我是靠谱客的博主 真实烤鸡,最近开发中收集的这篇文章主要介绍springboot集成groovy执行代码一.springboot中执行groovy文件二,通过数据库保存groovy内容,动态执行groovy脚本,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

springboot集成groovy动态执行代码

springboot版本号:2.2.2.RELEASE

在pom.xml中集成groovy

<dependency>
   <groupId>org.codehaus.groovy</groupId>
   <artifactId>groovy-all</artifactId>
   <version>1.8.9</version>
   <scope>compile</scope>
</dependency>

一.springboot中执行groovy文件

main文件直接调用groovy文件,有三种方式

方式一生成Test.groovy:

package com.example.demo.groovy

public class Test {

    public static String test(String id) {
        return "younger--->" + id;
    }
}

方式二生成test1.groovy

package com.example.demo.groovy.groovyscript

def test(id){
    return "test2 id:"+ id;
}

方法三生成test2.groovy

package com.example.demo.groovy.groovyscript

output = "test3 id: ${id}, name: ${name}"

通过Java的main方法调用groovy文件,得到执行结束

package com.example.demo;

import com.example.demo.controller.CalculateController;
import groovy.lang.Binding;
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyObject;
import groovy.lang.Script;
import groovy.util.GroovyScriptEngine;
import groovy.util.ResourceException;
import groovy.util.ScriptException;
import java.io.File;
import java.io.IOException;

/**
 * @program: demo
 * @description: 本地main方法执行groovy文件
 * @author: younger
 * @create: 2021-05-07 15:42
 **/
public class GroovyTest {

    public static void main(String[] args) throws IOException, IllegalAccessException, InstantiationException, ResourceException, ScriptException {
        //方式一调用groovy文件
        ClassLoader parent = CalculateController.class.getClassLoader();
        GroovyClassLoader loader = new GroovyClassLoader(parent);
        Class groovyClass = loader.parseClass(new File("src/main/java/com/example/demo/groovy/Test.groovy"));
        //得到groovy对象
        GroovyObject groovyObject= (GroovyObject)groovyClass.newInstance();
        //执行对象的test方法,并传参数-"id---1"
        String result = (String) groovyObject.invokeMethod("test", "id---1");
        System.out.println("result--------->" + result);

        //方式二调用groovy文件,找到groovy脚本所在文件夹
        GroovyScriptEngine engine = new GroovyScriptEngine("src/main/java/com/example/demo/groovy/groovyscript");
        //得到Script对象
        Script script = engine.createScript("test1.groovy", new Binding());
        //执行Script对象的test方法,并传参数-"id---1"
        result = (String) script.invokeMethod("test", "id----2");
        System.out.println("result--------->" + result);

        //方式三调用groovy文件
//        GroovyScriptEngine engine = new GroovyScriptEngine("src/main/java/com/example/demo/groovy/groovyscript");
        Binding binding = new Binding();
        //封装参数
        binding.setVariable("id","id---3");
        binding.setVariable("name", "younger");
        //执行test2.groovy脚本
        engine.run("test2.groovy", binding);
        //返回output
        result = binding.getVariable("output").toString();
        System.out.println("result--------->" + result);
    }
}

二,通过数据库保存groovy内容,动态执行groovy脚本

数据库mysql,创建groovy规则表:calculate_rule

CREATE TABLE `calculate_rule` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `interface_id` varchar(128) NOT NULL COMMENT '接口id',
  `bean_name` varchar(64) NOT NULL COMMENT 'bean_name',
  `calculate_rule` text NOT NULL COMMENT 'groovy脚本内容',
  `calculate_type` varchar(64) NOT NULL COMMENT '状态',
  `status` varchar(16) NOT NULL DEFAULT 'ENABLE' COMMENT 'ENABLE-启用/DISENABLE-停用',
  `extend_info` varchar(4096) DEFAULT NULL,
  `created_time` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
  `modified_time` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COMMENT='calculate rule';

insert into `calculate_rule` (`id`, `interface_id`, `bean_name`, `calculate_rule`, `calculate_type`, `status`, `extend_info`, `created_time`, `modified_time`) values('1','B.integration.A.calculate.reward','rewardCalculateParser','package com.example.demo.groovy.calculate.impl;nimport com.example.demo.entity.request.CalculateRequest;nimport com.example.demo.entity.response.CalculateResponse;nimport com.example.demo.groovy.calculate.CalculateParsernimport com.example.demo.service.CalculateInterestRuleService;nimport org.apache.commons.lang3.StringUtilsnimport org.springframework.beans.factory.annotation.Autowirednimport org.springframework.stereotype.Service;nimport java.math.RoundingMode;n/**n * 计算推广奖金n */npublic class RewardCalculateParser implements CalculateParser {n    @Autowiredn    private CalculateInterestRuleService calculateInterestRuleService;n    @Overriden    public CalculateResponse parse(CalculateRequest request) {n		String result = calculateInterestRuleService.test("younger");n        System.out.println("calculateInterestRuleService.test-------->" + result);n        Map<String, Object> extendInfo = request.getExtendInfo();n        String interfaceId = request.getInterfaceId();n        BigDecimal totalAmount = BigDecimal.ZERO;n        if (StringUtils.isNotBlank((String) extendInfo.get("totalAmount"))) {n            totalAmount = new BigDecimal((String) extendInfo.get("totalAmount"));n        }n        int refererNumber = 0;n        if (StringUtils.isNotBlank((String) extendInfo.get("refererNumber"))) {n            refererNumber = Integer.parseInt((String) extendInfo.get("refererNumber"));n        }n        System.out.println("进入奖金计算逻辑,总金额为:" + totalAmount + ",邀请人数为:" + refererNumber);n        n        BigDecimal reward = totalAmount.multiply(new BigDecimal(String.valueOf(refererNumber)))n                .divide(new BigDecimal("100")).divide(new BigDecimal("365"),4, RoundingMode.HALF_DOWN);n        CalculateResponse response = new CalculateResponse();n        response.setInterfaceId(interfaceId);n        Map<String, Object> map = new HashMap<>();n        map.put("reward", reward);n        response.setExtendInfo(map);n        System.out.println("退出奖金计算逻辑,总奖金为:" + reward);n        return response;n    }n}n','reward','ENABLE',NULL,'2020-07-06 09:27:58.279144','2021-05-07 17:17:19.771010');
insert into `calculate_rule` (`id`, `interface_id`, `bean_name`, `calculate_rule`, `calculate_type`, `status`, `extend_info`, `created_time`, `modified_time`) values('2','B.integration.A.calculate.sum','sumCalculateParser','package com.example.demo.groovy.calculate.impl;nnimport com.example.demo.entity.request.CalculateRequest;nimport com.example.demo.entity.response.CalculateResponse;nimport com.example.demo.groovy.calculate.CalculateParser;nimport org.apache.commons.lang3.StringUtils;nnimport java.math.BigDecimal;nimport java.math.RoundingMode;nimport java.util.HashMap;nimport java.util.Map;nn/**n * 计算推广奖金n */npublic class SumCalculateParser implements CalculateParser {nn    @Overriden    public CalculateResponse parse(CalculateRequest request) {nn        Map<String, Object> extendInfo = request.getExtendInfo();nn        String interfaceId = request.getInterfaceId();nn        BigDecimal totalAmount = BigDecimal.ZERO;n        if (StringUtils.isNotBlank((String) extendInfo.get("totalAmount"))) {n            totalAmount = new BigDecimal((String) extendInfo.get("totalAmount"));n        }nn        int refererNumber = 0;n        if (StringUtils.isNotBlank((String) extendInfo.get("refererNumber"))) {n            refererNumber = Integer.parseInt((String) extendInfo.get("refererNumber"));n        }nnn        System.out.println("进入奖金计算逻辑,总金额为:" + totalAmount + ",邀请人数为:" + refererNumber);n        n        BigDecimal sum = totalAmount.multiply(new BigDecimal(refererNumber));n        CalculateResponse response = new CalculateResponse();nn        response.setInterfaceId(interfaceId);n        Map<String, Object> map = new HashMap<>();n        map.put("sum", sum);nn        response.setExtendInfo(map);nn        System.out.println("退出奖金计算逻辑,总奖金为:" + sum);n        return response;n    }n}','reward','ENABLE',NULL,'2020-07-06 09:27:58.279144','2020-07-06 13:23:51.311882');

原理:把groovy脚本放在表calculate_rule中的calculate_rule,spring-boot启动加载calculate_rule中启用的数据,动态成生spring.xml文件把groovy脚本加载至spring容器中。

1.创建GroovyDynamicConfiguration类,在spring-boot启动时加载groovy脚本至spring容器中:

注:请看重点方法

package com.example.demo.groovy.core;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.demo.entity.CalculateRuleDO;
import com.example.demo.groovy.cache.BeanName;
import com.example.demo.groovy.cache.BeanNameCache;
import com.example.demo.groovy.cache.GroovyInfo;
import com.example.demo.groovy.cache.GroovyInnerCache;
import com.example.demo.service.CalculateInterestRuleService;
import groovy.lang.GroovyClassLoader;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.ResourceEntityResolver;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 * @program: demo
 * @description: 动态加载groovy脚本至spring容器中
 * @author: younger
 * @create: 2021-05-07 15:42
 **/
@Configuration
public class GroovyDynamicConfiguration implements ApplicationContextAware, InitializingBean {

    private ConfigurableApplicationContext applicationContext;

    private static final GroovyClassLoader groovyClassLoader = new GroovyClassLoader(GroovyDynamicConfiguration.class.getClassLoader());

    @Resource
    private CalculateInterestRuleService calculateInterestRuleService;

    @Override
    public void afterPropertiesSet() throws Exception {
        long start = System.currentTimeMillis();
        System.out.println("开始从数据库解析groovy脚本...");
        init();
        long cost = System.currentTimeMillis() - start;
        System.out.println("结束从数据库解析groovy脚本...,耗时:" + cost);
    }

    /**
     * 启动spring-boot就加载数据库中groovy脚本至spring容器管理
     */
    private void init() {
        //从mysql中获取groovy脚本规则
        List<CalculateRuleDO> calculateRuleDOS = calculateInterestRuleService.list(new QueryWrapper<CalculateRuleDO>().eq("status", "ENABLE"));
        List<BeanName> beanNameList = new ArrayList<>();
        List<GroovyInfo> groovyInfos = convert(calculateRuleDOS, beanNameList);
        init(groovyInfos, beanNameList);
    }

    /**
     * 重点方法
     */
    private void init(List<GroovyInfo> groovyInfos, List<BeanName> beanNameList) {
        if (CollectionUtils.isEmpty(groovyInfos)) {
            return;
        }
        ConfigurationXMLWriter config = new ConfigurationXMLWriter();
        //生成配置文件内容
        addConfiguration(config, groovyInfos);
        //把groovy规则加载至内存
        put2map(groovyInfos, beanNameList);
        //加载至spring容器中
        loadBeanDefinitions(config);
    }


    /**
     * 重新从mysql加载groovy脚本
     */
    public void refresh() {
        List<CalculateRuleDO> calculateRuleDOS = calculateInterestRuleService.list(new QueryWrapper<CalculateRuleDO>().eq("status", "ENABLE"));
        List<BeanName> beanNameList = new ArrayList<>();
        List<GroovyInfo> groovyInfos = convert(calculateRuleDOS, beanNameList);
        if (CollectionUtils.isEmpty(groovyInfos)) {
            return;
        }

        // loadBeanDefinitions 之后才会生效
        destroyBeanDefinition(groovyInfos);
        destroyScriptBeanFactory();
        ConfigurationXMLWriter config = new ConfigurationXMLWriter();
        addConfiguration(config, groovyInfos);
        put2map(groovyInfos, beanNameList);
        loadBeanDefinitions(config);
    }

    private List<GroovyInfo> convert(List<CalculateRuleDO> calculateRuleDOS, List<BeanName> beanNameList) {
        List<GroovyInfo> groovyInfos = new LinkedList<>();

        if (CollectionUtils.isEmpty(calculateRuleDOS)) {
            return groovyInfos;
        }

        for (CalculateRuleDO calculateRuleDO : calculateRuleDOS) {
            GroovyInfo groovyInfo = new GroovyInfo();
            groovyInfo.setClassName(calculateRuleDO.getBeanName());
            groovyInfo.setGroovyContent(calculateRuleDO.getCalculateRule());
            groovyInfos.add(groovyInfo);

            BeanName beanName = new BeanName();
            beanName.setInterfaceId(calculateRuleDO.getInterfaceId());
            beanName.setBeanName(calculateRuleDO.getBeanName());
            beanNameList.add(beanName);
        }

        return groovyInfos;
    }


    private void addConfiguration(ConfigurationXMLWriter config, List<GroovyInfo> groovyInfos) {
        for (GroovyInfo groovyInfo : groovyInfos) {
            writeBean(config, groovyInfo);
        }
    }

    private void loadBeanDefinitions(ConfigurationXMLWriter config) {
        /**
         * contextString=
         * <?xml version="1.0" encoding="UTF-8" standalone="no"?>
         * <beans xmlns="http://www.springframework.org/schema/beans"
         * xmlns:lang="http://www.springframework.org/schema/lang"
         * xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         * default-autowire="byName"
         * xsi:schemaLocation="http://www.springframework.org/schema/beans
         * http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
         * http://www.springframework.org/schema/lang
         * http://www.springframework.org/schema/lang/spring-lang-2.5.xsd">
         * <lang:groovy id="rewardCalculateParser" script-source="database:rewardCalculateParser"/>
         * <lang:groovy id="sumCalculateParser" script-source="database:sumCalculateParser"/>
         * </beans>
         * 生成加载至spring容器的xml,为了把groovy对应的对象交给spring容器来管理。
         */
        String contextString = config.getContent();
        if(StringUtils.isBlank(contextString)) {
            return ;
        }

        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader((BeanDefinitionRegistry) this.applicationContext.getBeanFactory());
        beanDefinitionReader.setResourceLoader(this.applicationContext);
        beanDefinitionReader.setBeanClassLoader(applicationContext.getClassLoader());
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this.applicationContext));
        beanDefinitionReader.loadBeanDefinitions(new InMemoryResource(contextString));

        String[] postProcessorNames = applicationContext.getBeanFactory().getBeanNamesForType(CustomScriptFactoryPostProcessor.class, true, false);
        for (String postProcessorName : postProcessorNames) {
            applicationContext.getBeanFactory().addBeanPostProcessor((BeanPostProcessor) applicationContext.getBean(postProcessorName));
        }
    }

    private void destroyBeanDefinition(List<GroovyInfo> groovyInfos) {
        DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
        for (GroovyInfo groovyInfo : groovyInfos) {
            try {
                beanFactory.removeBeanDefinition(groovyInfo.getClassName());
            } catch (Exception e) {
                System.out.println("【Groovy】delete groovy bean definition exception. skip:" + groovyInfo.getClassName());
            }
        }
    }

    private void destroyScriptBeanFactory() {
        String[] postProcessorNames = applicationContext.getBeanFactory().getBeanNamesForType(CustomScriptFactoryPostProcessor.class, true, false);
        for (String postProcessorName : postProcessorNames) {
            CustomScriptFactoryPostProcessor processor = (CustomScriptFactoryPostProcessor) applicationContext.getBean(postProcessorName);
            processor.destroy();
        }
    }

    private void writeBean(ConfigurationXMLWriter config, GroovyInfo groovyInfo) {
        if (checkSyntax(groovyInfo)) {
            DynamicBean bean = composeDynamicBean(groovyInfo);
            config.write(GroovyConstant.SPRING_TAG, bean);
        }
    }

    private boolean checkSyntax(GroovyInfo groovyInfo) {
        try {
            groovyClassLoader.parseClass(groovyInfo.getGroovyContent());
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    private DynamicBean composeDynamicBean(GroovyInfo groovyInfo) {
        DynamicBean bean = new DynamicBean();
        String scriptName = groovyInfo.getClassName();
        Assert.notNull(scriptName, "parser className cannot be empty!");

        //设置bean的属性,这里只有id和script-source。
        bean.put("id", scriptName);
        bean.put("script-source", GroovyConstant.SCRIPT_SOURCE_PREFIX + scriptName);
        return bean;
    }

    private void put2map(List<GroovyInfo> groovyInfos, List<BeanName> beanNameList) {
        GroovyInnerCache.put2map(groovyInfos);
        BeanNameCache.put2map(beanNameList);
    }

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

2.调用Controller测试groovy动态脚本

package com.example.demo.controller;

import java.util.Map;
import java.util.HashMap;
import javax.annotation.Resource;
import com.example.demo.entity.request.CalculateRequest;
import com.example.demo.entity.response.CalculateResponse;
import com.example.demo.groovy.calculate.CalculateParser;
import com.example.demo.groovy.calculate.GroovyParserEngine;
import com.example.demo.groovy.core.GroovyDynamicConfiguration;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class CalculateController {

    @Resource
    private GroovyParserEngine groovyParserEngine;

    @Resource
    private GroovyDynamicConfiguration groovyDynamicLoader;

    @Resource
    private CalculateParser rewardCalculateParserGroovy;

    @RequestMapping("/calculate")
    public Map<String, Object> calculate() {
        String interfaceId = "B.integration.A.calculate.reward";
        Map<String, Object> map = new HashMap<>();
        map.put("totalAmount", "10");
        map.put("refererNumber", "5");

        CalculateRequest request = new CalculateRequest();
        request.setInterfaceId(interfaceId);
        request.setExtendInfo(map);

        CalculateResponse response = groovyParserEngine.parse(request);
        return response.getExtendInfo();
    }

    @RequestMapping("/refresh")
    public void refresh() {
        groovyDynamicLoader.refresh();
    }
}
package com.example.demo.groovy.calculate;

import com.example.demo.entity.request.CalculateRequest;
import com.example.demo.entity.response.CalculateResponse;

public interface GroovyParserEngine {

    CalculateResponse parse(CalculateRequest request);
}
package com.example.demo.groovy.calculate.impl;

import com.example.demo.groovy.cache.BeanNameCache;
import com.example.demo.groovy.calculate.CalculateParser;
import com.example.demo.groovy.calculate.GroovyParserEngine;
import com.example.demo.entity.request.CalculateRequest;
import com.example.demo.entity.response.CalculateResponse;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;

@Service
public class GroovyParserEngineImpl implements GroovyParserEngine, ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Override
    public CalculateResponse parse(CalculateRequest request) {
        String beanName = BeanNameCache.getByInterfaceId(request.getInterfaceId());
        CalculateParser parser = (CalculateParser) applicationContext.getBean(beanName);
        return parser.parse(request);
    }

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

启动项目,调用http://localhost:8888/calculate,测试结果。
代码下载地址:https://download.csdn.net/download/yangxiang_Younger/18445827

最后

以上就是真实烤鸡为你收集整理的springboot集成groovy执行代码一.springboot中执行groovy文件二,通过数据库保存groovy内容,动态执行groovy脚本的全部内容,希望文章能够帮你解决springboot集成groovy执行代码一.springboot中执行groovy文件二,通过数据库保存groovy内容,动态执行groovy脚本所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(33)

评论列表共有 0 条评论

立即
投稿
返回
顶部