Groovy脚本高级特征总结

网友投稿 1013 2022-05-29

上一篇博文提到,Groovy是一种基于JVM,功能强大、类型可选、动态的,可与Java无缝衔接的编程语言。该语言易于学习,可以大大提高开发人员的工作效率,可为Java程序添加强大的功能,其中就包含脚本功能(scripting capabilities)。合理的使用脚本特征功能,可以大大提高程序的灵活性,这非常适合做一些补丁功能,或者个性化极强的功能,比如促销活动,这个活动规则会经常的变动,因此,如果不借助某种机制,当促销规则发生变化时,则需要重新进行编码调整并发布程序,这样的程序维护起来过于麻烦。

针对类似促销规则多变的编程场景,用脚本引擎来实现规则的维护和运行,是一种很好的方案。由于脚本本质上就是一段文本代码,因此,可以非常方便的在后台进行及时的维护,当运行时,则加载并运行新脚本即可实现新促销方案的发布工作。本篇就重点对脚本特征进行介绍。

根据官方的介绍,Groovy语言提供了多种机制,可以很好的与Java语言或者Groovy本身进行脚本特征的整合工作。官方的文档地址为:https://docs.groovy-lang.org ,可根据需要自行阅读相关文档进行学习。

首先,在Groovy语言框架中,提供了一个groovy.util.Eval 类,可以动态的执行脚本,它的使用也非常的简单,其中有一个Eval.me方法,可执行脚本。下面给出示例代码:

import groovy.util.Eval y = Eval.me('2.5 * 3') println y //7.5 println Eval.x(7, '2*x') // 14 println Eval.xyz(3, 2, 6, 'x*y+z') //12 println Eval.me('m', 3, 'm**2') //9

Groovy脚本高级特征总结

从示例代码中可知,首先需要导入包,然后就可以用Eval.me或者其他方法来实现简单的脚本执行。特别的,它支持动态参数功能,如一个表达式中参数x,y,z,那么可以使用Eval.xyz()方法来执行。

注意:Eval类执行脚本是没有缓存功能的,且不适合处理多行的脚本。因此只适合于简单的脚本执行。

其次,在Groovy语言框架中,还提供了一个groovy.lang.GroovyShell类,也可以动态的执行脚本,它可以对脚本进行缓存,并处理多种数据来源的脚本,如多行文本String,文件File,Reader和InputStream等。下面给出示例代码:

def shell = new GroovyShell() def y = shell.evaluate '2.5 * 3' println y // 7.5 y = shell.evaluate(new StringReader('2.5 * 3')) println y // 7.5 def script = shell.parse '2.5 * 3' println script instanceof groovy.lang.Script //true println script.run() // 7.5 /////////////////////////// def sharedData = new Binding() def shell = new GroovyShell(sharedData) sharedData.setProperty('name', 'Jack') sharedData.setProperty('age', 33) //Jack age is 33 println shell.evaluate('"$name age is $age"') shell.evaluate('x = 7') println sharedData.getProperty('x') //7 //显性类型,局部变量,无法外部访问 shell.evaluate('int y = 7') println sharedData.getProperty('y') //Exception //def局部变量,无法外部访问 shell.evaluate('def z = 7') println sharedData.getProperty('z') //Exception /////////////////////////// def shell = new GroovyShell() shell.evaluate('x = 7') shell.evaluate('y = 2 * x') println shell.evaluate('"$y"') //14

注意:共享变量是全局变量,多线程下是有点问题的,不是线程安全的。

除此外,GroovyShell下还有一个parse方法,也可以支持脚本特征,示例如下:

def shell = new GroovyShell() def b1 = new Binding(x:3) def script1 = shell.parse('x = 2*x ; x = x + 1;def sum(x,y){return x+y};x = sum(x,1)') script1.binding = b1 script1.run() println b1.getProperty('x') //8

从示例中可知,它可以解析多条语句,用分号分隔,并支持函数定义等。

其次,还有一种就是groovy.lang.GroovyClassLoader类,可以更加灵活的执行脚本,示例如下:

import groovy.lang.GroovyClassLoader def cloader = new GroovyClassLoader() def clazz = cloader.parseClass('class Utils { void say(String msg) { println "Hello,"+msg } }') println clazz.name // Utils def o = clazz.newInstance() o.say("Jack") //Hello,Jack //////////////////////////// import groovy.lang.GroovyClassLoader def gcl = new GroovyClassLoader() def clazz1 = gcl.parseClass('class Utils { void say(String msg) { println "Hello,"+msg } }') def clazz2 = gcl.parseClass('class Utils { void say(String msg) { println "Hello,"+msg } }') println clazz1 == clazz2 //false ///////////////////////////////////// def clazz3 = gcl.parseClass(new File("K:\\Utils.groovy")) def clazz4 = gcl.parseClass(new File("K:\\Utils.groovy")) println clazz3 == clazz4 //true

注意:GroovyClassLoader默认会引用所有创建的类对象,且文本定义的类,即使是同名的,也是不同的对象,因此,同样的脚本,多次执行会创建多个对象,可能会导致内存泄漏。当采用File传入脚本后,Groovy可以缓存同一个文件脚本,并只创建同一个对象。

最后,还有一个groovy.util.GroovyScriptEngine类,也可以支持脚本特征,它的功能更加的强大和灵活。它是建立在GroovyClassLoader类之上,可以处理类的依赖问题以及重加载的问题,即可以实现热更新功能。示例如下:

def binding = new Binding() def engine = new GroovyScriptEngine("K:\\") while (true) { //Demo02.groovy 文件名不能和类名Demo一致,否则类重复 def utils = engine.run('Demo02.groovy', binding) println utils.say("Jack") Thread.sleep(500) } //Demo02.groovy class Demo { String say(String msg) { "Hello, $msg" //"Hello2, $msg" } } new Demo()

Groovy Java

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:ModelArts问题索引_old
下一篇:测试从零开始-No.2-了解计算机基础知识
相关文章