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));  // 明确抛出参数异常
    }
}
相关推荐
嘵奇4 分钟前
深入解析 Spring Boot 测试核心注解
java·spring boot·后端
癞皮狗不赖皮8 分钟前
Java安全基础-反射机制
java·反射机制·java安全基础
别惊鹊14 分钟前
(三)安装和使用Maven
java·maven
兢兢业业的小白鼠28 分钟前
Java高级JVM知识点记录,内存结构,垃圾回收,类文件结构,类加载器
java·开发语言·jvm·tomcat
Niuguangshuo38 分钟前
Python设计模式:代理模式
开发语言·python·代理模式
能来帮帮蒟蒻吗1 小时前
GO语言学习(16)Gin后端框架
开发语言·笔记·学习·golang·gin
落榜程序员1 小时前
Java 基础-29-final关键字-详解
java·开发语言
用户3315489111071 小时前
【零停机】一次400万用户数据的双写迁移技术详解
java·面试
JavaPub-rodert1 小时前
一道go面试题
开发语言·后端·golang
6<71 小时前
【go】静态类型与动态类型
开发语言·后端·golang