Java Grovy 动态代码执行

/**
 * groovy 脚本执行
 * @auth zzj
 */
@Component
@Slf4j
public class GroovyRunner {

    private final String DEFAULT_METHOD = "run";

    /** 缓存脚本对象
     * TODO 后续需要添加一个计时器清空cache缓存,避免GC无法回收已经不用的对象
     */
    private Map<String, GroovyObject> cacheObj = new ConcurrentHashMap();
    private Map<String, String> cacheKey = new ConcurrentHashMap<>();

    /**
     * groovy动态脚本执行, 执行java class 对象
     * @param script 要执行脚本的字符串
     * @param methodName 执行的方法名
     * @param params 方法参数
     * @return Object
     * @throws Exception
     */
    public Object runner(String script, String methodName, Object ...params) throws Exception {
         boolean checkJavaScript = checkJavaScript(script);
         Checks.checkFalse(checkJavaScript, "脚本文件不是Java class类型");

        String cacheObjName = getCacheObjName(script);
        String keyName = getJavaScriptName(script);
        // bean = SpringContextUtils.getBean(javaScriptBeanName);
        GroovyObject obj = getCacheObj(cacheObjName);

        if (null == obj) {
            ApplicationContext applicationContext = SpringContextUtils.getApplicationContext();
            AutowireCapableBeanFactory autowireCapableBeanFactory = applicationContext.getAutowireCapableBeanFactory();
            GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
            Class aClass = groovyClassLoader.parseClass(script);
            // 频繁使用ClassLoader会导致内存泄露,交给容器缓存对象 @zzj 2023.4.20
            // 因动态代码修改会重新加载代码,但是Spring 容器重新加载bean 异常困难 后续再研究下 @zzj 2023.4.20
            // bean = createSpringBean(aClass, autowireCapableBeanFactory);
            // 注入bean
            obj = (GroovyObject) aClass.newInstance();
            injectSpringBean(obj, autowireCapableBeanFactory);
            doClearCacheObj(keyName);
            doCacheObj(cacheObjName, obj);
            doCacheKey(keyName, cacheObjName);
        }

        Object run = obj.invokeMethod(methodName, params);
        return run;
    }

    private GroovyObject getCacheObj(String cacheObjName) {
        return cacheObj.get(cacheObjName);
    }

    private void doCacheObj(String cacheObjName, GroovyObject obj) {
        cacheObj.put(cacheObjName, obj);
    }

    private void doCacheKey(String className, String cacheObjName) {
        cacheKey.put(className, cacheObjName);
    }

    private void doClearCacheObj(String className) {
        String cacheObjName = cacheKey.get(className);
        if (null != cacheObjName) {
            GroovyObject groovyObject = cacheObj.get(cacheObjName);
            cacheObj.remove(cacheObjName);
            groovyObject = null;
        }
    }

    private String getCacheObjName(String script) {
        Checks.checkNull(script, "脚本不能为空");
        String cacheName = MD5Util.MD5Encode(script, null);
        return cacheName;
    }

    private String getJavaScriptName(String script) {
        String regex = "public\\s+class\\s+\\S+";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(script);
        boolean b = matcher.find();
        if (!b) {
            return null;
        }

        String group = matcher.group();
        group = group.replaceAll("public\\s+class\\s+", "");
        group = group.replace("[\\{\\}]", "");
        return group.trim();
    }

    private boolean checkJavaScript(String script) {
        String regex = "public\\s+class\\s+.+\\s+\\{[\\s\\S]*\\}";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(script);
        return matcher.find();
    }

    private Object createSpringBean(Class aClass, AutowireCapableBeanFactory autowireCapableBeanFactory) {
        Object bean = autowireCapableBeanFactory.createBean(aClass, AutowireCapableBeanFactory.AUTOWIRE_BY_NAME, Boolean.TRUE);
        return bean;
    }

    /**
     * 注入Spring管理的bean
     * @param o
     */
    private void injectSpringBean(GroovyObject o, AutowireCapableBeanFactory autowireCapableBeanFactory) {
        autowireCapableBeanFactory.autowireBean(o);
    }

    /**
     * groovy动态脚本执行, 默认执行run方法
     * @param script 要执行脚本的字符串
     * @param params 方法参数
     * @return
     * @throws Exception
     */
    public Object runner(String script, Object ...params) throws Exception {
        return runner(script, DEFAULT_METHOD, params);
    }

    /**
     * groovy动态脚本执行, 默认执行run方法
     * @param script 要执行脚本的字符串
     * @return
     * @throws Exception
     */
    public Object runner(String script) throws Exception {
        return runner(script, DEFAULT_METHOD, null);
    }

    /**
     * groovy脚本执行,通用脚本执行, 支持非class结构代码
     * @param script
     * @return Object
     * @throws Exception
     */
    @Deprecated
    public Object runnerSimple(String script) throws Exception {
        GroovyShell groovyShell = new GroovyShell();
        Object evaluate = groovyShell.evaluate(script);
        return evaluate;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值