概述
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脚本所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复