Spring使用Groovy动态脚本化编程

本文介绍了一种在Spring环境中动态编译和执行Groovy脚本的方法,并将其无缝整合到Spring容器中,使得开发者能轻松利用Groovy脚本的灵活性进行业务逻辑的定制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

总结一套groovy脚本语言与spring容器完美结合的动态执行能力
1.groovy脚本
groovy 是JVM脚本语言,可以在JVM环境中编译运行,基于这个特性,我们只需要在项目中封装好了对脚本的编译逻辑-运行接口,那么在JVM中动态运行groovy脚本就顺利成章了,可喜的是,groovy 提供了GroovyShell 和 Script 类专门提供这样的逻辑,所以代码写起来就简单的多了

	/**
     * 动态执行groovy脚本代码
     * @param scriptStr
     * @return
     */
    public Object runScript (String scriptStr){
        GroovyClassLoader groovyClassLoader = new GroovyClassLoader(this.getClass().getClassLoader());
        CompilerConfiguration compilerConfiguration = new CompilerConfiguration();
        compilerConfiguration.setSourceEncoding("utf-8");
        //Binding 可以将变量传递进Script当中,类似可以传递任何参数
        Binding binding = new Binding();
        binding.setVariable("scriptEngine",this);
        GroovyShell groovyShell = new GroovyShell(groovyClassLoader,binding,compilerConfiguration);
        Script scriptObj = groovyShell.parse(scriptStr);
        Object result = scriptObj.run();
        return result;
    }

如此:我们提交一段groovy代码片段,就可以执行对应逻辑了,但是这样有几个问题:

  1. Script对象本身是非常大的,使用不当容易导致OOM
  2. 没有与项目中的spring相融合,脚本中无法方便的使用spring bean 那么这样的脚本实用性就不强
    为解决以上两个问题,请往下看

2.groovy动态bean
项目工程一般情况下都会继承spring,此方案就是将groovy 动态脚本方便快捷的与spring容器结合,便于开发,更实用

这里仅仅使用了Groovy的编译,将编译后的class注入的spring容器中,所以并没有使用的Groovy的Script所以内存占用是非常有限的,随后以bean的形式注入spring容器

    /**
     * 全局scriptBean注册锁,注册bean是串行的
     */
    private static final String REGISTER_LOCK = "scriptLock";

    /**
     * spring 容器
     */
    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    /**
     * 动态注册GroovyBean
     * 因为是单例使用锁控制串行就行
     * @param contextFunction
     * @param scriptName
     * @return
     */
    public BaseScript registerIfAbsent (String scriptName, ScriptContextFunction contextFunction){
        synchronized (REGISTER_LOCK){
            BaseScript bean  = getBean(scriptName);
            if (bean == null){
                return register(scriptName,contextFunction);
            }
            return bean;
        }
    }

    /**
     * 刷新注册GroovyBean
     * @param contextFunction
     * @param scriptName
     * @return
     */
    public void refresh (String scriptName, ScriptContextFunction contextFunction){
        synchronized (REGISTER_LOCK){
            BaseScript bean  = getBean(scriptName);
            if (bean == null){
                logger.info("刷新ScriptBean失败:脚本{}未注册bean",scriptName);
                return;
            }
            register(scriptName,contextFunction);
            logger.info("刷新ScriptBean成功:{}",scriptName);
        }
    }

    /**
     * 注册bean
     * @param scriptName
     * @param contextFunction
     * @return
     */
    private BaseScript register(String scriptName, ScriptContextFunction contextFunction) {
        String finalGroovyBeanName = "groovy" + scriptName + "Bean";
        GroovyClassLoader groovyClassLoader = new GroovyClassLoader(this.getClass().getClassLoader());
        String scriptContext = contextFunction.getScript(scriptName);
        Class scriptCls = groovyClassLoader.parseClass(scriptContext,finalGroovyBeanName);
        //获取BeanFactory
        DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory)applicationContext.getAutowireCapableBeanFactory();
        //创建bean信息
        BeanDefinitionBuilder beanDefinitionBuilder =BeanDefinitionBuilder.genericBeanDefinition(scriptCls);
        //动态注册bean
        defaultListableBeanFactory.registerBeanDefinition(finalGroovyBeanName,beanDefinitionBuilder.getBeanDefinition());
        Object bean = applicationContext.getBean(finalGroovyBeanName);
        logger.info("注册ScriptBean:{}",scriptName);
        return (BaseScript) bean;
    }

    /**
     * 获取Bean的工具类
     * @param beanName
     * @return
     */
    public BaseScript getBean(String beanName){
        String finalGroovyBeanName = "groovy" + beanName + "Bean";
        BaseScript script = null;
        try {
            script = (BaseScript) applicationContext.getBean(finalGroovyBeanName);
        } catch (NoSuchBeanDefinitionException e) {
        }
        return script;
    }

工具类

@FunctionalInterface
public interface ScriptContextFunction {
    /**
     * 获取脚本内容
     * @param name
     * @return
     */
    String getScript(String name);
}

基于以上对于groovy脚本的动态编译和注入,以及刷新能力,业务的使用场景可以自由定制实现,比如如何获取脚本内容,如何使用动态脚本bean等

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值