10、Groovy实现代码热载的机制和原理
我们知道,Groovy在很大一部分的用途是在java工程里面穿插使用的,本文的主题是Groovy实现代码热载,,其他大背景是java实现主干代码,,groovy实现易变动的逻辑代码. 先来看下java是如何调用的groovy脚本的。
Groovy代码热载例子
import groovy.lang.Binding;
import groovy.lang.GroovyShell;
public class GroovyTest {
public static void main(String[] args) {
// *) groovy 代码
String script = "println 'hello'; 'name = ' + name;";
// *) 传入参数
Binding binding = new Binding();
binding.setVariable("name", "lilei");
// *) 执行脚本代码
GroovyShell shell = new GroovyShell(binding);
Object res = shell.evaluate(script);
System.out.println(res);
}
}
这个例子的输出结果即为其中groovy代码的输出结果:
接下来我们来分析一下传入参数和执行脚本代码的源码。
//groovy.lang.binding
//一个收集参数的map属性
private Map variables;
public Binding() {
}
//构造函数
public Binding(Map variables) {
this.variables = variables;
}
public Binding(String[] args) {
this();
this.setVariable("args", args);
}
//余下的是对参数数据map的一些操作,就不列出来了
所以Binging里面其实就是用一个map存储其传来的参数而已,我们再来看GroovyShell类,对于其中的函数evaluate, 我们追踪进去, 会有不少的新认识。
public Object evaluate(GroovyCodeSource codeSource) throws CompilationFailedException {
Script script = this.parse(codeSource);
return script.run();
}
public Script parse(GroovyCodeSource codeSource) throws CompilationFailedException {
return InvokerHelper.createScript(this.parseClass(codeSource), this.context);
}
其大致的思路, 为Groovy脚本代码包装生成class, 然后产生该类实例对象, 在具体执行其包装的逻辑代码。而在parse方法的parseClass源码如下所示:
public Class parseClass(String text) throws CompilationFailedException {
return this.parseClass(text, "script" + System.currentTimeMillis() + Math.abs(text.hashCode()) + ".groovy");
}
对于groovy脚本, 它默认会生成名字为script + System.currentTimeMillis() + Math.abs(text.hashCode())的class类, 也就是说传入脚本, 它都会生成一个新类, 就算同一段groovy脚本代码, 每调用一次, 都会生成一个新类。
总结
对于Groovy脚本,先用Binding传入参数到map数组里面,再根据Binding创建GroovyShell对象, 然后为Groovy脚本代码包装生成class, 然后产生该类实例对象, 在具体执行其包装的逻辑代码。而在parse方法的parseClass源码如下所示。