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));  // 明确抛出参数异常
    }
}
相关推荐
望获linux17 分钟前
【实时Linux实战系列】Linux 内核的实时组调度(Real-Time Group Scheduling)
java·linux·服务器·前端·数据库·人工智能·深度学习
Never_Satisfied20 分钟前
在 JavaScript 中,删除数组中内容为xxx的元素
java·前端·javascript
MC丶科28 分钟前
【SpringBoot常见报错与解决方案】端口被占用?Spring Boot 修改端口号的 3 种方法,第 3 种 90% 的人不知道!
java·linux·spring boot
怪兽201432 分钟前
Redis常见性能问题和解决方案
java·数据库·redis·面试
zz-zjx33 分钟前
JVM 内存结构与 GC 机制详解( 实战优化版)
java·jvm·tomcat
mjhcsp37 分钟前
MATLAB 疑难问题诊疗:从常见报错到深度优化的全流程指南
开发语言·matlab
Lynnxiaowen44 分钟前
今天我们开始学习python语句和模块
linux·运维·开发语言·python·学习
nvvas1 小时前
Android Studio JAVA开发按钮跳转功能
android·java·android studio
逐步前行1 小时前
C标准库--浮点<float.h>
c语言·开发语言
CV工程师丁Sir1 小时前
Rokid设备连接全解析:蓝牙与Wi-Fi通信源码深度剖析
java