当前位置: 首页 > news >正文

Groovy 规则执行器,加载到缓存

实现了一个 Groovy 规则执行器,通过动态编译规则脚本并缓存执行对象(GroovyObject)来提升性能。主要流程如下:

  1. 类名生成:通过规则内容的哈希值生成唯一类名(RuleScript_xxx
  2. 缓存机制:使用 ConcurrentHashMap 缓存已编译的规则对象
  3. 动态编译:首次执行时生成 Groovy 类并实例化,后续直接从缓存读取对象
  4. 规则执行:调用 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));  // 明确抛出参数异常
    }
}

相关文章:

  • Minio集群部署
  • 大数据在金融服务中的中阶应用:从洞察到决策的技术进阶
  • 蓝桥杯 2023 省赛 B 组 I 题 - 景区导游题解(LCA最近公共祖先)
  • 水下成像机理分析
  • 输电线路在线监测通信规约,即I1协议
  • 医院信息系统建设:大数据平台、集成平台、数据治理平台,该如何选择?
  • 常用shell命令
  • NodeTextFileCollectorScrapeError 报警原因及解决方法
  • Linux中的权限管理(附加详细实验示例)
  • swe-bench环境安装踩坑
  • 如何利用 AI 进行 A/B 测试的优化?
  • 【设计模式】过滤器模式
  • 手机显示5GA图标的条件
  • Oracle中文一二三四排序【失败】
  • Linux防火墙的iptables命令示例与详细解释
  • deepseek v3-0324实现数学方程式绘制曲线功能
  • Redis-14.在Java中操作Redis-Spring Data Redis使用方式-操作列表类型的数据
  • 西门子TCP通讯过程中硬件连接突然断开
  • 轻帆云智能ITSM应用最佳实践,助力IT共享服务中心高效运营
  • Redis:集群
  • 中国证券监督管理委员会党委委员、副主席王建军接受审查调查
  • 关于新冠疫情防控与病毒溯源的中方行动和立场
  • 体坛联播|欧冠半决赛阿森纳主场不敌巴黎,北京男篮险胜山西
  • 匈牙利国会通过退出国际刑事法院的决定
  • 烟花、美食和购物优惠都安排上了,上海多区开启热闹模式
  • 丁俊晖连续7年止步世锦赛16强,中国军团到了接棒的时候