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));  // 明确抛出参数异常
    }
}
相关推荐
步步为营DotNet28 分钟前
深度解析CancellationToken:.NET中的优雅取消机制
java·前端·.net
冷雨夜中漫步8 小时前
Python快速入门(6)——for/if/while语句
开发语言·经验分享·笔记·python
JH30739 小时前
SpringBoot 优雅处理金额格式化:拦截器+自定义注解方案
java·spring boot·spring
m0_7369191010 小时前
C++代码风格检查工具
开发语言·c++·算法
Coder_Boy_10 小时前
技术让开发更轻松的底层矛盾
java·大数据·数据库·人工智能·深度学习
2501_9449347310 小时前
高职大数据技术专业,CDA和Python认证优先考哪个?
大数据·开发语言·python
invicinble10 小时前
对tomcat的提供的功能与底层拓扑结构与实现机制的理解
java·tomcat
较真的菜鸟10 小时前
使用ASM和agent监控属性变化
java
黎雁·泠崖11 小时前
【魔法森林冒险】5/14 Allen类(三):任务进度与状态管理
java·开发语言
2301_7634724611 小时前
C++20概念(Concepts)入门指南
开发语言·c++·算法