用脚本应对业务不清析的情况

原文:In any incomprehensible situation go scripting

翻译CUBA China

CUBA-Platform:https://cuba-platform.com

CUBA-China:

 

  让应用程序在运行时适应客户需求最常用的方法之一就是使用脚本。但是事物总有两面性,无一例外。脚本这种方法并非只有好的一面,我们需要在灵活性和可管理性之间权衡。本文不是在理论上讨论优缺点的文章,而是从实际出发,展示使用脚本的几种不同方式,并介绍了一个Spring库,这个库提供了方便的脚本基础设施和一些其他的有用功能。

介绍

  脚本(也称为插件架构)是使应用程序在运行时可自定义的最直接的方法。很多时候,脚本并不是设计到应用程序中的,只是偶尔会用到。比如说,在功能描述中有一部分描述地非常模糊,我们不能再耗费额外一天时间去分析这个描述模糊的业务。这时我们决定创建一个扩展点并调用一个脚本存根,然后在晚些时候再详细定义这个脚本的业务逻辑。

  使用这种方法有很多众所周知的优点和缺点:例如可以非常灵活地在运行时定义业务逻辑并且可以在重新部署方面节省大量时间,但是使用脚本也会导致不能对系统进行全面地测试,因此,会给系统在安全性、性能等方面带来不可预测的问题。

  后续对使用脚本的方式的讨论可能对已经决定在Java应用程序中坚持使用脚本插件的用户或正在考虑将其添加到代码中的用户都会有所帮助。

直接编写脚本

  使用Java JSR-233 API在Java中执行脚本是很简单的任务。有许多实现了此API的产品级脚本执行引擎,比如Nashorn、JRuby、Jython等。因此在java添加一些脚本的魔法很容易实现,如下所示:

用脚本应对业务不清析的情况

  显然,如果你的代码库中的脚本文件很多,那么将这些调用代码分散在整个应用程序并不好。因此,你可能会将此代码段提取到一个工具类的单独方法中。也可能会考虑地更远一些:创建一个专用的类(或一组类),根据业务领域对脚本化业务逻辑进行分组,比如:PricingScriptService类。这样我们就可以将对evaluateGroovy() 方法的调用封装到一个强类型的方法中,但这里仍然存在一些重复的代码:所有方法都包含构建参数Map、加载脚本文本以及调用脚本引擎的代码,类似于:

用脚本应对业务不清析的情况

  这种方法在了解参数类型和返回值类型方面有更大的透明度。但是别忘了,需要在编码标准文档中添加一条:禁止调用“解包”了的脚本引擎!

CUBA平台中增强版的脚本引擎

  尽管使用脚本引擎非常简单,但如果代码库中有很多脚本,就可能会遇到一些性能问题。比如,在报表中使用groovy模板,并且同时运行大量报表。那么,你迟早会发现“简单”脚本成为性能瓶颈。

  这就是为什么有些框架在现有API上构建自己的脚本引擎 :添加一些功能来改善性能、监控脚本的执行、支持多种脚本语言,等等。

  例如,在CUBA框架中,有一个相当复杂的Scripting引擎,提供一些用于改善脚本实现和执行的功能,例如:

对类进行缓存, 避免脚本的重复编译。

使开发人员能够使用Groovy和Java语言编写脚本。

提供用于脚本引擎管理的JMX bean。

  所有这些都改善了性能和可用性,但这些还只是用于创建参数Map、获取脚本文本等低级API,因此我们仍然需要将这些脚本分组到高阶模块,以便在应用程序中更高效地使用脚本。

  在这里,有必要提一下GraalVM引擎,这是一个由Oracle开源实验性产品。GraalVM引擎及其多语言API允许开发人员使用其他语言扩展Java应用程序。所以,我们也许能看到Nashorn退役之时,我们也可以在同一个源文件中使用不同的编程语言。但是,这些还需要等待。

Spring 框架对脚本的支持

  Spring框架在JDK API之上内置了对脚本的支持,可以在org.springframework.scripting.* 包中找到很多有用的类。有执行器(evaluators)、工厂(factories)等所有用于构建脚本支持所需的工具。

  除了低级别API之外,Spring 框架还有一个可以在应用程序中简化脚本处理的实现 - 可以使用动态语言定义Bean,文档。

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

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zypzds.html