最近在继续研究Groovy,同时也在团队内部推动Groovy作为大家学习的第二语言。目前能想到Groovy在应用中的作用有两点:
- 利用Groovy语法的简练,用它来写测试用例,减少测试的代码量。
- 实现一个动态执行Groovy脚本的引擎,将一些业务规则用Groovy实现,通过对Groovy脚本可动态更改来满足运营的一些需求。
其中第一点其实比较容易搞,也不会有性能问题,第二个需求则需要考虑到性能的问题。针对这个方案,上周六写了一个简单的GroovyEngine用来从数据库中加载Groovy脚本并执行,顺便对其做了一下性能测试。GroovyEngine的核心方法代码如:
public Map< string ,Object> execute(Map< string , Object> context,
Map< string , Object> params, Source source) {
// Script script = scripts.get(source.getkey());
// if (script == null) {
GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
Class scriptClass = groovyClassLoader.parseClass(source.getSource(),
source.getkey());
Script script = InvokerHelper.createScript(scriptClass,
groovySpringApplicationContextBinding);
// if (script != null) {
// scripts.put(source.getkey(), script);
// }
//}
return (Map< string , Object>) script.invokeMethod("execute",
new Object[]{context, params});
}
其中Source是定义的一个包含Groovy脚本的对象,groovySpringApplicationContextBinding是Binding的一个实例,目的是将spring容器的对象可以被Groovy脚本方法。
测试代码如下:
class TestGroovyExecutor extends BaseTestCase{
@Autowired
GroovyExecutor groovyExecutor
@Test
public void test_execute() {
def script = """def execute(Map context,Map param){
return ["result":"hello world"]
}
"""
def code = new Source(source: script,type: "new",name: "hello")
def context = new HashMap< string ,Object>()
def params = ["name":"Groovy"]
def start = System.currentTimeMillis()
1000.times{
def result = groovyExecutor.execute(context, params, code)
}
def time = System.currentTimeMillis() - start
System.out.println("take time: " + time)
}
}
上面的测试代码运行结果如下:
take time: 23843
这段很短的Groovy脚本大约每次的执行需要23ms。
如果将Script加入缓存,如将第一段代码中的注释都打开,如下:
public Map< string ,Object> execute(Map< string , Object> context,
Map< string , Object> params, Source source) {
Script script = scripts.get(source.getkey());
if (script == null) {
GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
Class scriptClass = groovyClassLoader.parseClass(source.getSource(),
source.getkey());
script = InvokerHelper.createScript(scriptClass,
groovySpringApplicationContextBinding);
if (script != null) {
scripts.put(source.getkey(), script);
}
}
}
这次将执行次数加大到100000次,测试执行结果如下:
take time: 1678
每一次执行时间少于0.1ms,这个执行效率已经可以接受了。
测试的最终结论是要利用Groovy的动态特性来实现一些需要动态变化的业务需求,如果性能上有要求的话,必须对Script做缓存。