实现了一个 Groovy 规则执行器 ,通过动态编译规则脚本并缓存执行对象(GroovyObject
)来提升性能。主要流程如下:
- 类名生成 :通过规则内容的哈希值生成唯一类名(
RuleScript_xxx
) - 缓存机制 :使用
ConcurrentHashMap
缓存已编译的规则对象 - 动态编译:首次执行时生成 Groovy 类并实例化,后续直接从缓存读取对象
- 规则执行 :调用
evaluate
方法传入状态参数,返回整型结果
版本依赖
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy</artifactId>
<version>3.0.19</version>
<scope>compile</scope>
</dependency>
1.第一种写法:首次执行加载到缓存,后续执行从缓存读取
package com.ruoyi.system.util;
import com.ruoyi.common.core.utils.uuid.UUID;
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyObject;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class GroovyRuleExecutor3 {
private static final GroovyClassLoader classLoader = new GroovyClassLoader();
private static final Map<String, GroovyObject> CACHE = Collections.synchronizedMap(
new LinkedHashMap<>(16, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<String, GroovyObject> eldest) {
return size() > 100;
}
});
private static String generateClassName(String rule) {
String uuid = UUID.nameUUIDFromBytes(rule.getBytes(StandardCharsets.UTF_8)).toString();
return "RuleScript_" + uuid.replace("-", "");
}
public static int parseRule(String rule, String state) {
try {
System.out.println(CACHE.toString());
return (int) CACHE.computeIfAbsent(rule, k -> {
long start = System.nanoTime();
String className = generateClassName(rule);
String script = "class " + className + " {\n" +
" int evaluate(String state) {\n" +
" " + rule + "\n" +
" }\n" +
"}";
synchronized (classLoader) {
Class<?> clazz = classLoader.parseClass(script);
try {
GroovyObject instance = (GroovyObject) clazz.newInstance();
return instance;
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException("实例化失败: " + e.getMessage(), e);
}
}
}).invokeMethod("evaluate", state);
} catch (Exception e) {
throw new IllegalArgumentException("规则执行失败: " + e.getMessage(), e);
}
}
public static void main(String[] args) {
String rule = "if(state == '男') {return 1} else if(state == '女') {return 2} else {return 0}";
long start = System.nanoTime();
System.out.println(parseRule(rule, "女")); // 输出2
long duration = System.nanoTime() - start;
System.out.println(" 首次执行结果: " + start + " (耗时: " + duration / 1_000_000 + "ms)");
long start2 = System.nanoTime();
System.out.println(parseRule(rule, "男")); // 输出1
long duration2 = System.nanoTime() - start2;
System.out.println(" 首次执行结果: " + start2 + " (耗时: " + duration2 / 1_000_000 + "ms)");
}
}
2.第二种写法,提前生成规则,项目启动加载规则,后续全部规则从缓存里面去,
package com.ruoyi.system.util;
import groovy.lang.GroovyClassLoader;
import java.util.HashMap;
import java.util.Map;
public class GroovyRuleExecutor2 {
private static final GroovyClassLoader classLoader = new GroovyClassLoader();
private static final Map<String, Class<?>> scriptCache = new HashMap<>();
// 预编译并缓存脚本
public static void loadScript(String scriptId, String code) {
scriptCache.put(scriptId, classLoader.parseClass(code));
}
// 安全执行方法
public static Object execute(String scriptId, Map<String, Object> params) throws Exception {
Class<?> scriptClass = scriptCache.get(scriptId);
Object scriptInstance = scriptClass.newInstance();
return scriptClass.getDeclaredMethod("execute", Map.class).invoke(scriptInstance, params);
}
public static void main(String[] args) throws Exception {
String rule = "if(s.equals('0')){return '男'} else if(s.equals('2')){return 3}else if(s.equals('4')){return 5}else{return '哈哈'}";
//String rule = "return new Date()";
String script = "def execute(Map params) { String s = params.get(\"s\").toString()" +
"\n"+rule+"}"; // 替换为上述修正脚本
loadScript("genderRule", script);
Map<String, Object> input = new HashMap<>();
input.put("s", "");
System.out.println(execute("genderRule", input)); // 明确抛出参数异常
}
}