前言
因为项目中需要用到Groovy
语言,所以对它一直有做了解,本文主要总结一下Groovy
在Java
与Spring
中的用法。希望本文对准备学习使用或者对 Groovy 感兴趣的同学有所帮助,如有不对之处还望指出哈,对这门语言的理解还是比较肤浅的。
简介
Groovy 是 Apache 旗下的一门基于 JVM 平台的动态/敏捷编程语言,在语言的设计上它吸纳了 Python、Ruby 和 Smalltalk 语言的优秀特性,语法非常简练和优美,开发效率也非常高(编程语言的开发效率和性能是相互矛盾的,越高级的编程语言性能越差,因为意味着更多底层的封装,不过开发效率会更高,需结合使用场景做取舍)。并且,Groovy 可以与 Java 语言无缝对接,在写 Groovy 的时候如果忘记了语法可以直接按Java的语法继续写,也可以在 Java 中调用 Groovy 脚本,都可以很好的工作,这有效的降低了 Java 开发者学习 Groovy 的成本。Groovy 也并不会替代 Java,而是相辅相成、互补的关系,具体使用哪门语言这取决于要解决的问题和使用的场景。
入门
Groovy的简单入门这里就不作讲述,想了解的同学可以参考下面这篇博客:
Spring 支持
从 Spring 2.0 版本开始对动态脚本语言进行了支持(Spring 官方文档该部分地址),其中便包括 Groovy ,Spring 提供了<lang:groovy/>
标签来定义 Groovy Bean 。Groovy Bean 可以通过script-source
属性加载脚本文件,脚本源文件可以来自本地或者网络,并且可以通过refresh-check-delay
属性监听脚本内代码变更重新装载 Bean 实现动态 Bean 。
Calculator.java
public interface Calculator {
int add(int x, int y);
}
CalculatorGroovyImpl.groovy
class CalculatorGroovyImpl implements Calculator {
int add(int x, int y) {
x + y
}
}
#### beans.xml 省略 xmlns
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<lang:groovy id="calculator" script-source="classpath:CalculatorGroovyImpl.groovy" refresh-check-delay="1000"/>
</beans>
Test.java
public static void main(String[] args) throws InterruptedException {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Calculator calculator = (Calculator)context.getBean("calculator");
//尝试修改CalculatorGroovyImpl.groovy,将 x + y,修改为x * y。
while (true){
System.out.println(calculator.add(1, 1));
TimeUnit.SECONDS.sleep(1);
}
}
与Java集成
Groovy非常容易集成在Java环境中,利用其动态性来做规则引擎、流程引擎、动态脚本环境,非常适合那些不需要经常发布但又经常变更的场景下使用。在Java中集成(调用)Groovy 代码有下面几种方式。
Eval
groovy.util.Eval
类是最简单的用来在运行时动态执行 Groovy 代码的类,提供了几个静态工厂方法供使用,内部其实就是对GroovyShell的封装。
//执行Groovy代码
Eval.me("println 'hello world'");
//绑定自定义参数
Object result = Eval.me("age", 22, "if(age < 18){'未成年'}else{'成年'}");
assertEquals(result, "成年");
//绑定一个名为 x 的参数的简单计算
assertEquals(Eval.x(4, "2*x"), 8);
//带有两个名为 x 与 y 的绑定参数的简单计算
assertEquals(Eval.xy(4, 5, "x*y"), 20);
//带有三个绑定参数(x、y 和 z)的简单计算
assertEquals(Eval.xyz(4, 5, 6, "x*y+z"), 26);
GroovyShell
groovy.lang.GroovyShell
除了可以执行 Groovy 代码外,提供了更丰富的功能,比如可以绑定更多的变量,从文件系统、网络加载代码等。
GroovyShell shell = new GroovyShell();
//可以绑定更多变量
shell.setVariable("age",22);
//直接求值
shell.evaluate("if(age < 18){'未成年'}else{'成年'}");
//解析为脚本,延迟执行或者缓存起来
Script script = shell.parse("if(age < 18){'未成年'}else{'成年'}");
assertEquals(script.run(), "成年");
//可以从更多位置加载/执行脚本
//shell.evaluate(new File("script.groovy"));
//shell.evaluate(new URI("http://wwww.a.com/script.groovy"));
GroovyClassLoader
groovy.lang.GroovyClassLoader
是一个定制的类加载器,可以在运行时加载 Groovy 代码,生成 Class 对象。
GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
String scriptText = "class Hello { void hello() { println 'hello' } }";
//将Groovy脚本解析为Class对象
Class clazz = groovyClassLoader.parseClass(scriptText);
//Class clazz = groovyClassLoader.parseClass(new File("script.groovy"));
assertEquals(clazz.getName(),"Hello");
clazz.getMethod("hello").invoke(clazz.newInstance());
GroovyScriptEngine
groovy.util.GroovyScriptEngine
能够处理任何 Groovy 代码的动态编译与加载,可以从统一的位置加载脚本,并且能够监听脚本的变化,当脚本发生变化时会重新加载。
/script/groovy/hello.groovy
println "hello $name"
测试类
GroovyScriptEngine scriptEngine = new GroovyScriptEngine("script/groovy");
Binding binding = new Binding();
binding.setVariable("name", "groovy");
while (true){
scriptEngine.run("hello.groovy", binding);
TimeUnit.SECONDS.sleep(1);
}
输出
hello groovy
hello groovy
....
//将hello.groovy内代码修改为println "hi $name", GroovyScriptEngine会重新进行加载
hi groovy
hi groovy
JSR 223 javax.script API
JSR-223 是 Java 中调用脚本语言的标准 API。从 Java 6 开始引入进来,主要目的是用来提供一种统一的框架,以便在 Java 中调用多种脚本语言。JSR-223 支持大部分流行的脚本语言,比如JavaScript、Scala、JRuby、Jython和Groovy等。
ScriptEngine engine = new ScriptEngineManager().getEngineByName("groovy");
Bindings bindings = new SimpleBindings();
bindings.put("age", 22);
Object value = engine.eval("if(age < 18){'未成年'}else{'成年'}",bindings);
assertEquals(value,"成年");
//script/groovy/hello.groovy
//println "hello world"
engine.eval(new FileReader("script/groovy/hello.groovy"));
//hello world
由于 Groovy 自身已经提供了更丰富的集成机制,如果在 Java 应用中只是使用 Groovy 一种脚本语言的话,使用 Groovy 提供的集成机制可能会更合适一点。
后记
感谢以下博客的分享:
—— Ariescat 记于 2019.01.23