Groovy构建规则引擎
目录
Groovy模板引擎 API 构建动态脚本
可以利用Groovy提供的模板引擎, 在代码中编写脚本, 来完成一些动态化的需求, 案例如下, 当字符串长度等于4时, 执行打印
public class RuleEngine {
private static final ConcurrentHashMap<String, Template> SCRIPT_CACHE = new ConcurrentHashMap<>();
private static final SimpleTemplateEngine ENGINE = new SimpleTemplateEngine();
private static final String CONDITION_STR_TEMPLATE = "${%s ? true : false}";
private static final String EXECUTE_STR_TEMPLATE = "${%s}";
public static void main(String[] args) {
String request = "data";
String condition = "request.length() == 4";
String execute = "System.out.println(request)";
if (condition(condition, request)) {
execute(execute, request);
}
}
public static boolean condition(String condition, String request) {
boolean result;
String conditionTemplate = String.format(CONDITION_STR_TEMPLATE, condition);
Map<String, Object> context = new HashMap<>();
context.put("request", request);
try {
Template template;
if (SCRIPT_CACHE.containsKey(condition)) {
template = SCRIPT_CACHE.get(condition);
} else {
template = ENGINE.createTemplate(conditionTemplate);
SCRIPT_CACHE.put(condition, template);
}
Writer writer = new StringWriter();
template.make(context).writeTo(writer);
result = Boolean.parseBoolean(writer.toString());
} catch (Exception e) {
throw new RuntimeException("模板解析异常" + conditionTemplate);
}
return result;
}
public static void execute(String execute, String request) {
String executeTemplate = String.format(EXECUTE_STR_TEMPLATE, execute);
Map<String, Object> context = new HashMap<>();
context.put("request", request);
Template template;
try {
if (SCRIPT_CACHE.containsKey(execute)) {
template = SCRIPT_CACHE.get(execute);
} else {
template = ENGINE.createTemplate(executeTemplate);
SCRIPT_CACHE.put(execute, template);
}
template.make(context).writeTo(new StringWriter());
} catch (Exception e) {
throw new RuntimeException("模板解析异常" + executeTemplate);
}
}
}
Groovy动态对象注册为Spring IOC中的Bean
groovy script脚本
内容如下, 与Java语法完全兼容
package groovy;
import com.github.service.GroovyBeanCommand
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.lang.invoke.MethodHandles;
public class FoobarCommand implements GroovyBeanCommand {
private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@Override
String run() {
LOGGER.info("foobar command");
return "FoobarCommand Data";
}
}
约定的脚本所需实现的接口
public interface GroovyBeanCommand {
String run();
}
外部传入script的入口
@PostMapping("/command/add")
public void addGroovyCommand(@RequestParam String groovyBeanName, @RequestParam String script) {
GroovyContextUtils.autowireBean(groovyBeanName, script);
}
@GetMapping("/command/run")
public Object runGroovyCommand(@RequestParam String groovyBeanName) {
GroovyBeanCommand command = GroovyContextUtils.getBean(groovyBeanName, GroovyBeanCommand.class);
return command.run();
}
将script注入到IOC中的工具类
@Component
public class GroovyContextUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext;
private static final GroovyClassLoader CLASS_LOADER = new GroovyClassLoader();
@Override
public void setApplicationContext(@NonNull ApplicationContext context) {
applicationContext = context;
}
public static void autowireBean(String beanName, String script) {
Class<?> clazz = CLASS_LOADER.parseClass(script);
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
BeanDefinition beanDefinition = beanDefinitionBuilder.getRawBeanDefinition();
beanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
AutowireCapableBeanFactory autowireCapableBeanFactory = applicationContext.getAutowireCapableBeanFactory();
autowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(beanDefinition, beanName);
BeanDefinitionRegistry beanRegistry = (BeanDefinitionRegistry) autowireCapableBeanFactory;
if (beanRegistry.containsBeanDefinition(beanName)) {
beanRegistry.removeBeanDefinition(beanName);
}
beanRegistry.registerBeanDefinition(beanName, beanDefinition);
}
public static <T> T getBean(String beanName, Class<T> clazz) {
return applicationContext.getBean(beanName, clazz);
}
public static <T> T getBean(Class<T> clazz) {
return applicationContext.getBean(clazz);
}
}
动态脚本与规则编排
自定义一套规则组, 将脚本编号保存, 按照所需要顺序组合起来, 实现规则的编排和可插拔, 并按照流程处理数据流
伪代码如下
public void process(Data data) {
List<Handler> handlers = getHandlers(data.getBizCode());
for(Handler handler : handlers) {
handler.run(data);
}
}