在任何无法理解的情况下,请编写脚本

脚本编写是使您的应用程序在运行时就可以根据客户需求进行调整的最流行的方法之一。 与往常一样,此方法不仅带来好处,例如,在灵活性和可管理性之间存在众所周知的折衷方案。 本文不是从理论上讨论优缺点的文章之一,而是从实践上展示了如何采用脚本的不同方法,并介绍了一个Spring库,该库提供了方便的脚本基础结构和有用的功能。

介绍

脚本(也称为插件体系结构)是使应用程序在运行时可自定义的最直接方法。 通常,脚本不是偶然进入设计的,而是偶然进入应用程序的。 说,您在功能规范中有一个非常不清楚的部分,因此为了避免浪费一天来进行额外的业务分析,我们决定创建一个扩展点并调用一个实现存根的脚本-将阐明以后的工作方式。

使用这种方法有很多众所周知的利弊:例如,在运行时定义业务逻辑的灵活性非常大,可以节省大量的重新部署时间,而无法进行全面的测试,因此,安全性,性能问题是无法预测的问题等等。

对于已经决定在其Java应用程序中坚持使用脚本插件的人,或者只是考虑将其添加到代码中的人,进一步讨论的脚本编写方法可能会有所帮助。

没什么特别的,只是脚本

使用Java的JSR-233 API,用Java评估脚本是一项简单的任务。 为此API实现了许多生产级评估引擎(Nashorn,JRuby,Jython等),因此向Java代码中添加一些脚本魔术不是问题,如下所示:

Map parameters = createParametersMap();ScriptEngineManager manager = new ScriptEngineManager();ScriptEngine scriptEngine = manager.getEngineByName("groovy");Object result = scriptEngine.eval(script.getScriptAsString("discount.groovy"), new SimpleBindings(parameters));

显然,当代码库中有多个脚本文件和一个调用时,将这样的代码散布在您的所有应用程序上并不是一个好主意,因此您可以将此代码段提取到放置到实用程序类的单独方法中。 有时,您甚至可以走得更远:您可以创建一个特殊的类(或类集),以基于业务域将脚本化的业务逻辑分组,例如PricingScriptService类。 这将使我们将对validateGroovy ()的调用包装到一个不错的强类型方法中,但是仍然有一些样板代码,所有方法都将包含参数映射,脚本文本加载逻辑和脚本评估引擎调用,类似于:

public BigDecimal applyCustomerDiscount(Customer customer, BigDecimal orderAmount) {Map params = new HashMap<>();params.put("cust", customer);params.put("amount", orderAmount);return (BigDecimal)scripting.evalGroovy(getScriptSrc("discount.groovy"), params);
}

这种方法在了解参数类型和返回值类型方面带来了更大的透明度。 并且不要忘记在您的编码标准文档中添加禁止“未包装”脚本引擎调用的规则!

类固醇脚本

尽管使用脚本引擎非常简单,但是如果代码库中有很多脚本,您可能会遇到一些性能问题。 举个例子–您使用Groovy模板进行报告并同时运行许多报告。 迟早您会发现“简单”脚本正在成为性能瓶颈。

这就是为什么某些框架在现有API上构建自己的脚本引擎的原因,并添加了一些不错的功能以提高性能,执行监视,多语言脚本等。

例如,在CUBA框架中,有一个相当复杂的脚本引擎,该引擎实现了一些功能来改善脚本的实现和执行,例如:

  1. 类缓存以避免重复的脚本编译。
  2. 能够使用Groovy和Java语言编写脚本。
  3. 用于脚本引擎管理的JMX bean。

所有这些都提高了性能和可用性,但是它们仍然是用于创建参数映射,获取脚本文本等的低级API,因此,我们仍然需要将它们分组为高阶模块,以在应用程序中有效地使用脚本。

而且,不提及新的实验性GraalVM引擎及其允许使用其他语言扩展Java应用程序的多语言API,这是不公平的。 因此,也许我们会看到Nashorn早晚退休,并能够在同一源文件中以不同的编程语言编写,但是将来仍然如此。

Spring框架:很难拒绝的提议?

在Spring Framework中,我们对JDK的API提供了内置的脚本支持,您可以在org.springframework.scripting。*包中找到很多有用的类。 这里有评估人员,工厂等。所有构建您自己的脚本支持所需的工具。

除了底层API之外,Spring Framework的实现还应简化应用程序中脚本的处理-您可以按照文档中的描述定义以动态语言实现的bean。

您需要做的就是使用动态语言(例如Groovy)实现一个类,并在配置XML中描述一个bean,如下所示:

<lang:groovy id="messenger" script-source="classpath:Messenger.groovy"><lang:property name="message" value="I Can Do The Frug" />
</lang:groovy>

之后,您可以使用XML config将Messenger Bean注入到应用程序类中。 可以在基础脚本更改的情况下自动“刷新”该bean,并与AOP等一起使用。

这种方法看起来不错,但是作为开发人员,如果您想利用动态语言支持的所有功能,则应为您的bean实现成熟的类。 在现实生活中,脚本可能是纯函数,因此您需要向脚本中添加一些额外的代码,以使其与Spring兼容。 如今也有一些开发人员认为XML配置与注解相比已“过时”,并试图避免使用它,因为bean定义和注入在Java代码和XML代码之间进行了划分。 尽管这更多是关于品味的问题,而不是性能/兼容性/可读性等问题,但我们可以考虑到它。

脚本:挑战和想法

因此,一切都有其代价,当您向应用程序中添加脚本时,您可能会遇到一些挑战:

  1. 可管理性–通常,脚本分散在应用程序中,因此很难管理大量的valuateGroovy (或类似)调用。
  2. 可发现性–如果调用脚本中出现问题,则很难在源代码中找到实际要点。 我们应该能够在我们的IDE中轻松找到所有脚本调用点。
  3. 透明度–编写脚本扩展不是一件容易的事,因为没有有关发送到脚本的变量的信息,也没有有关应返回的结果的信息。 最后,脚本只能由开发人员完成并且只能查看源代码。
  4. 测试和更新–部署(更新)新脚本总是很危险的,没有回滚的方法,也没有工具可以在生产前对其进行测试。

似乎在常规Java方法下隐藏脚本化方法调用可以解决其中的大多数难题。 首选方式–注入“脚本化” bean并使用有意义的名称调用其方法,而不是仅从实用工具类中调用另一个“ eval”方法。 因此,我们的代码正变得自我记录,开发人员无需查看文件“ disc_10_cl.groovy”即可确定参数名称,类型等。

另一个优势–如果所有脚本都有与之关联的唯一Java方法,则可以使用IDE中的“查找用法”功能轻松找到应用程序中的所有扩展点,以及了解该脚本的参数及其含义。返回。

这种执行脚本的方式也使测试变得更加简单–我们不仅能够“照常”测试这些类,而且还可以在需要时使用模拟框架。

所有这些都使我们想起了本文开头提到的方法–脚本方法的“特殊”类。 而且,如果我们更进一步,并隐藏开发人员对脚本引擎,参数创建等的所有调用,该怎么办?

脚本存储库概念

这个想法非常简单,并且所有使用Spring Framework的开发人员都应该熟悉。 我们只是创建一个Java接口并将其方法以某种方式链接到脚本。 例如,Spring Data JPA使用类似的方法,其中接口方法根据方法名称转换为SQL查询,然后由ORM引擎执行。

我们可能需要执行什么概念?

可能是一个类级别的注释,它将帮助我们检测脚本存储库接口并为其构造一个特殊的Spring bean。

方法级别的注释将帮助我们将方法链接到其脚本实现。

最好为该方法提供一个默认实现,它不是简单的存根,而是业务逻辑的有效部分。 在我们实现由业务分析师开发的算法之前,该方法将一直有效。 或者我们可以让他/她编写此脚本:-)

假设您需要创建一个服务来根据用户个人资料计算折扣。 而且业务分析师说,我们可以放心地假设默认情况下可以为所有注册客户提供10%的折扣。 对于这种情况,我们可能会考虑以下代码概念:

@ScriptRepository
public interface PricingRepository {@ScriptMethoddefault BigDecimal applyCustomerDiscount(Customer customer,BigDecimal orderAmount) {return orderAmount.multiply(new BigDecimal("0.9"));}
}

当涉及适当的折扣算法实现时,groovy脚本将如下所示:

-------- file discount.groovy --------
def age = 50
if ((Calendar.YEAR - cust.birthday.year) >= age) {return amount.multiply(0.75)
}
--------

这一切的最终目标–让开发人员仅实现唯一的接口和折扣算法脚本,而不要对所有这些“ getEngine”和“ eval”调用感到困惑。 脚本解决方案应该发挥所有魔力:当方法被调用时,拦截调用,查找并加载脚本文本,对其进行评估并返回结果(或者,如果找不到脚本文本,则执行默认方法)。 理想用法应与此类似:

@Service
public class CustomerServiceBean implements CustomerService {@Injectprivate PricingRepository pricingRepository;//Other injected beans here@Overridepublic BigDecimal applyCustomerDiscount(Customer cust, BigDecimal orderAmnt) {if (customer.isRegistered()) {return pricingRepository.applyCustomerDiscount(cust, orderAmnt);} else {return orderAmnt;}//Other service methods here}

脚本调用是可读的,我猜想任何Java开发人员都熟悉脚本的调用方式。

这些就是想法,它们被用来为使用Spring Framework的脚本存储库实现创建一个库。 该库具有用于从不同来源加载和评估脚本文本的功能,以及一些API,允许开发人员在需要时实现库的扩展。

怎么运行的

该库引入了一些注释(以及那些喜欢它的人的XML配置),这些注释在上下文初始化期间为所有标记有@ScriptRepository注释的存储库接口启动动态代理构建。 这些代理以实现存储库接口的单例Bean的形式发布,这意味着您可以使用@Autowired@Inject将这些代理完全注入到Bean中,如上一节中的代码片段所示。

在应用程序配置类之一上使用@EnableSpringRepositories批注可激活脚本存储库。 这种方法类似于其他熟悉的Spring注释,例如@EnableJpaRepositories或@EnableMongoRepositories。 并且对于此批注,您需要指定应类似于JPA存储库进行扫描的软件包名称数组。

@Configuration
@EnableScriptRepositories(basePackages = {"com.example", "com.sample"})
public class CoreConfig {
//More configuration here.
}

如前所示,我们需要使用@ScriptMethod标记脚本存储库中的每个方法(库也提供@GroovyScript@JavaScript ),以将元数据添加到这些调用中并指示这些方法已编写脚本。 当然,还支持脚本方法的默认实现。 解决方案的所有组件都显示在下图中。 蓝色形状与应用程序代码相关,白色形状与库相关。 Spring Bean标记有春天徽标。

脚本编写

调用接口的脚本化方法时,该代理类将拦截该代理方法,该代理类将对两个bean进行查找-一个提供程序以实现脚本文本,以及一个评估程序以获取结果。 脚本评估后,结果将返回到调用服务。 提供程序和评估程序都可以在@ScriptMethod批注属性以及执行超时中指定(尽管库为这些属性提供了默认值):

@ScriptRepository
public interface PricingRepository {@ScriptMethod (providerBeanName = "resourceProvider",evaluatorBeanName = "groovyEvaluator",timeout = 100)
default BigDecimal applyCustomerDiscount(@ScriptParam("cust") Customer customer,@ScriptParam("amount") BigDecimal orderAmount) {return orderAmount.multiply(new BigDecimal("0.9"));
}
}

您可能会注意到@ScriptParam批注-我们需要它们为方法的参数提供名称。 这些名称应在脚本中使用,因为Java编译器会在编译时删除实际的参数名称。 您可以省略这些注释,在这种情况下,您需要将脚本的参数命名为“ arg0”,“ arg1”等,这会影响代码的可读性。

默认情况下,该库具有提供程序,可以从两种脚本语言的文件系统和基于JSR-233的评估器读取groovy和javascript文件。 但是,您可以为不同的脚本存储和执行引擎创建自定义提供程序和评估程序。 所有这些功能都基于Spring框架接口( org.springframework.scripting.ScriptSourceorg.springframework.scripting.ScriptEvaluator ),因此您可以重用所有基于Spring的类,例如StandardScriptEvaluator而不是默认类。

提供程序(以及评估程序)以Spring Bean的形式发布,因为脚本存储库代理为了灵活性而按名称解析它们-您可以用新的executor代替默认执行程序,而无需更改应用程序代码,而是在应用程序上下文中替换一个Bean。

测试和版本控制

由于可以轻松更改脚本,因此我们需要确保在更改脚本时不会破坏生产服务器。 该库与JUnit测试框架兼容,没有什么特别的。 由于您在基于Spring的应用程序中使用脚本,因此可以将单元测试和集成测试作为应用程序的一部分来测试脚本,然后再将其上传到生产环境,因此还支持模拟。

另外,您可以创建一个脚本提供程序,以从数据库甚至Git或其他源代码控制系统中读取不同的脚本文本版本。 在这种情况下,如果生产中出现问题,则很容易切换到较新的脚本版本或回滚到以前的脚本版本。

结论

该库将帮助您安排代码中的脚本,提供以下内容:

  1. 通过引入Java接口,开发人员始终可以获得有关脚本参数及其类型的信息。
  2. 提供程序和评估程序可帮助您摆脱分散在应用程序代码中的脚本引擎调用。
  3. 通过使用“查找用法(引用)” IDE命令或仅通过方法名称进行简单的文本搜索,我们可以轻松地在应用程序代码中找到所有脚本用法。

在此Spring Boot的基础上,还支持自动配置,并且您还可以使用熟悉的单元测试和模拟技术,在将脚本部署到生产之前测试脚本。

该库具有用于在运行时获取脚本元数据(方法名称,参数等)的API,如果您希望避免编写try..catch块来处理脚本抛出的异常,则可以获取包装的执行结果,它还支持XML配置,如果您希望以这种格式存储配置。

同样,可以通过注释中的超时参数来限制脚本执行时间。

可以在https://github.com/cuba-rnd/spring-script-repositories找到库资源。

翻译自: https://www.javacodegeeks.com/2018/11/incomprehensible-situation-scripting.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/344793.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【渝粤教育】广东开放大学 公共关系实务 形成性考核 (37)

选择题 题目&#xff1a; 请指出以下哪些言论具有公共关系意识&#xff08; &#xff09; 选择一项或多项&#xff1a; 答案&#xff1a;看左侧 题目&#xff1a; 公共关系传播媒介&#xff0c;从其物质形式看有&#xff08; &#xff09; 选择一项或多项&#xff1a; 答案&a…

【渝粤教育】广东开放大学 地基与基础 形成性考核 (27)

选择题 题目&#xff1a;土的三相比例指标包括&#xff1a;土粒比重、含水量、密度、孔隙比、孔隙率和饱和度&#xff0c;其中为实测指标。 答案&#xff1a;看左侧 题目&#xff1a;条形均布荷载中心线下&#xff0c;附加应力随深度减小&#xff0c;其衰减速度与基础的宽度b有…

【渝粤教育】广东开放大学 社会调查与方法 形成性考核 (35)

选择题 题目&#xff1a;社会调查的本质是&#xff08; &#xff09; 题目&#xff1a;从所研究的总体屮&#xff0c;按照一定的方式选取一部分个体进行调查&#xff0c;并将在这部分个体中所得到的调查结果推广到总体中去&#xff0c;这种调查是&#xff08; &#xff09;…

下载anaconda时出现“Please make sure you are connected to the internet”警告

如题&#xff0c;在anaconda下载过程中下载VScode时出现下图的警告。 百度翻译&#xff1a; 顺着图中指定文件路径&#xff0c;找到vscode_inst.py.log文件&#xff08;注&#xff1a;有些人ProgramData文件夹可能找不到&#xff0c;打开任意文件夹&#xff0c;点击查看&…

【渝粤教育】广东开放大学 Linux操作系统 形成性考核 (55)

选择题 题目&#xff1a; 以下哪个不属于操作系统&#xff1f; 答案&#xff1a;看左侧 题目&#xff1a; 以下描述正确的是&#xff1f; 答案&#xff1a;看左侧 题目&#xff1a; 使用gcc编译后的目标文件一般以什么名称结尾&#xff1f; 答案&#xff1a;看左侧 题目&#x…

多个公证员提高网络吞吐量

您是否需要非常高吞吐量的Corda网络&#xff1f; 网络的吞吐量是否稳定&#xff1f; 您是否已经从其他领域挤出了所有可能的表现&#xff1f; 如果您对这些问题的回答是“是”&#xff0c;那么我可能会为您提供一些有用的信息。 我列出了这些问题&#xff0c;以减少您过早优化C…

初识FPGA(搬运)

原文链接1原文链接2 fpga简介 FPGA&#xff08;Field&#xff0d;Programmable Gate Array&#xff09;&#xff0c; 即现场可编程门阵列&#xff0c;它是在PAL&#xff08;可编程阵列逻辑&#xff09;、GAL&#xff08;通用阵列逻辑器件&#xff09;、CPL&#xff08;复杂可…

【渝粤教育】广东开放大学 会展营销 形成性考核 (56)

选择题 题目&#xff1a;以下哪一个选项不属于市场定位的内容&#xff1f; 答案&#xff1a;看左侧 题目&#xff1a;以下哪一个选项不是会展营销调研的程序&#xff1f; 答案&#xff1a;看左侧 题目&#xff1a;以下哪一个选项不是市场定位的原则&#xff1f; 答案&#xff1…

【渝粤教育】广东开放大学 劳动人事争议处理法 形成性考核 (51)

选择题 题目&#xff1a; 下列哪一选项属于劳动人事争议&#xff1f;&#xff08; &#xff09; 选择一项&#xff1a; 答案&#xff1a;看左侧 题目&#xff1a; 仲裁委员会的主任由&#xff08; &#xff09;担任。 选择一项&#xff1a; 答案&#xff1a;看左侧 题目…

【渝粤教育】广东开放大学 土地力学与地基基础 形成性考核 (32)

选择题 题目&#xff1a;7&#xff0c;地下科能(流入)与排泄(浦出)条件以及土中水的渗透速度都与土的有关 题目&#xff1a;8在非均质土层中,土的坚向自重应力沿深度的分布规律是( )。 题目&#xff1a;10某粘性土的I,≤0,该土的物理状态为( )。 题目&#xff1a;1地震时产生的…

蓝桥杯小白系列之汇编点亮led灯

蓝桥杯小白系列之汇编点亮led灯 1、源代码 2、逐条分析 (1)ORG 0000H (2)START: 伪指令,编译器可识别,单片机不可识别,可以随便起。 (3)蜂鸣器设置 蓝桥板插电以后蜂鸣器常会自动响起,故在程序前提前加上如下代码,让蜂鸣器不响: mov P2,#0A0H mov P0,#000H 参照…

带有Oracle Digital Assistant和Fn Project的会话式UI。 第二部分

在上一篇文章中&#xff0c;我使用Oracle Digital Assistant为FlexDeploy实现了一个对话式UI。 今天&#xff0c;我将用Fn Flow丰富它&#xff0c;以便聊天机器人接受发行名称而不是ID来创建快照。 完成后&#xff0c;对话听起来会更加自然&#xff1a; … “您可以建立快照吗…

小白系列之51单片机的入门速成法

51单片机的入门速成法 01-经典51内核资源全览浓缩图 02-重要外设特殊功能寄存器概览 03-程序开发流程与设计要点 04-三大外设的开发与可重用代码 05-应用程序设计入门一例通 01-经典51内核资源全览浓缩图 <1> 四组8位并行I/O端口&#xff1a; P0端口&#xff1a;PC门&a…

【渝粤教育】广东开放大学 工程经济 形成性考核

选择题 题目&#xff1a; 单选 下列是年金现值系数表示符号的是&#xff08;&#xff09; 答案&#xff1a;看左侧 题目&#xff1a; 单选 某施工企业现在对外投资200万元&#xff0c;5年后一次性收回本金和利息&#xff0c;若年基准收益率为8%&#xff0c;则总计可以收回资…

Hibernate事实:集成测试策略

我喜欢集成测试&#xff0c;这是检查Hibernate生成哪些幕后花絮SQL查询的好方法。 但是集成测试需要运行的数据库服务器&#xff0c;这是您必须要做的第一选择。 1.使用类似生产的本地数据库服务器进行集成测试 对于生产环境&#xff0c;我始终喜欢使用增量DDL脚本&#xff0c…

【渝粤教育】广东开放大学 插画与漫画 形成性考核 (27)

选择题 题目&#xff1a;先从整体开始绘制的一般顺序是 题目&#xff1a;正常头身比角色转化为Q版角色&#xff0c;基本可以用几个词说完&#xff0c;那就是 题目&#xff1a;影响衣服皱褶的的因素有 题目&#xff1a;关于女性漫画人物正面面部特征&#xff0c;以下说法正确的是…

蓝桥杯基础模块1:LED跑马灯

一、模块题目 二、原理简述 1、74HC138:三八译码器(3个输入,8个输出) 2、74HC573:锁存器(20个引脚,D1D8是数据输入端,Q1Q8是数据输出端&#

【渝粤教育】广东开放大学 文学概论 形成性考核 (31)

选择题 题目&#xff1a;对创作个性与文学风格这两个概念的关系理解正确的是&#xff08; &#xff09; 题目&#xff1a;审美意象总是以表达哲理和观念作为存在的目的和最高审美理想&#xff08; &#xff09; 题目&#xff1a;西方典型论发展的三个阶段是&#xff08; &#…

如何使用Hibernate将PostgreSQL枚举映射到JPA实体属性

介绍 开源的hibernate-types项目允许您映射JSON&#xff0c;ARRAY&#xff0c; YearMonth &#xff0c; Month或数据库特定的列&#xff08;例如INET地址&#xff09;。 在本文中&#xff0c;我们将看到使用JPA和Hibernate时如何将PostgreSQL Enum类型映射到Java数组。 Maven…

【渝粤教育】广东开放大学 物权法 形成性考核 (43)

选择题 题目&#xff1a;下列哪一选项不是民法上的物?&#xff08;&#xff09; 题目&#xff1a;甲有一套别墅&#xff0c;甲的下列何种行为不能体现物权的性质&#xff08;&#xff09; 题目&#xff1a;根据物权是否具有独立性不同&#xff0c;物权可以分为&#xff08;&am…