在我的最终产品中,我提供了使用小型Groovy脚本在运行时扩展应用程序代码的功能,这些脚本可通过表单进行编辑并将其代码保存在SQL数据库中。
这些“自定义代码”摘要所遵循的方案通常是根据输入参数返回一个值。例如,在服务开票期间,评分系统可能会通过自定义常规代码使用已发布的预定费率表或应用程序中合同中定义的值,如果返回了“覆盖”值,则应为用过的。
在确定速率的“替代”值的逻辑中,我合并了类似这些常规代码片段的示例,这些片段返回一个值,或者如果它们返回null,则使用默认值。例如。
class GroovyRunner {
static final GroovyClassLoader classLoader = new GroovyClassLoader()
static final String GROOVY_CODE = MyDatabase().loadCustomCode()
static final String GROOVY_CLASS = MyDatabase().loadCustomClassName()
static final String TEMPDIR = System.getProperty("java.io.tmpdir")
double getOverrideRate(Object inParameters) {
def file = new File(TEMPDIR+GROOVY_CLASS+".groovy")
BufferedWriter bw = new BufferedWriter(new FileWriter(file))
bw.write(GROOVY_CODE)
bw.close()
Class gvy = classLoader.parseClass(file)
GroovyObject obj = (GroovyObject) gvy.getDeclaredConstructor().newInstance()
return Double.valueOf(obj.invokeMethod("getRate",inParameters)
}
}
然后,在用户创建的自定义常规代码中:
class RateInterceptor {
def getRate(Object inParameters) {
def businessEntity = (SomeClass) inParameters
return businessEntity.getDiscount() == .5 ? .5 : null
}
}
问题在于,上面的GROOVY_CODE中的这些“自定义代码”位是在运行时从数据库中提取的,并且包含复杂的groovy类。由于此方法将被连续调用多次,因此每次运行时都要创建一个新的File对象是不切实际的。
无论我使用GroovyScriptEngine还是GroovyClassLoader,这两个都需要java.io.File对象。这使得代码执行非常缓慢,因为必须在从数据库中检索自定义常规代码后创建文件。有什么方法可以运行可返回值而无需创建临时文件来执行它的常规代码?
最佳答案
针对您的情况的直接解决方案是使用GroovyClassLoader.parseClass(String text)
http://docs.groovy-lang.org/latest/html/api/groovy/lang/GroovyClassLoader.html#parseClass(java.lang.String)
类缓存应该不是问题,因为每次创建新的GroovyClassLoader
时
但是请考虑使用groovy脚本而不是类
您的速率拦截器代码可能是这样的:
def businessEntity = (SomeClass) context
return businessEntity.getDiscount() == .5 ? .5 : null
甚至像这样:
context.getDiscount() == .5 ? .5 : null
在脚本中,您可以声明函数,内部类等
因此,如果您需要以下脚本,也可以使用:
class RateInterceptor {
def getRate(SomeClass businessEntity) {
return businessEntity.getDiscount() == .5 ? .5 : null
}
}
return new RateInterceptor().getRate(context)
执行这些脚本的Java代码:
import groovy.lang.*;
...
GroovyShell gs = new GroovyShell();
Script script = gs.parse(GROOVY_CODE);
// bind variables
Binding binding = new Binding();
binding.setVariable("context", inParams);
script.setBinding(binding);
// run script
Object ret = script.run();
请注意,解析常规代码(类或脚本)是一项繁重的操作。而且,如果您需要加快代码的速度,可以考虑将已解析的类缓存到某些内存缓存中,甚至缓存到映射中
Map<String, Class<groovy.lang.Script>>