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);
    }
}
posted @ 2021-01-06 00:51  梅子酒zZ  阅读(3217)  评论(0编辑  收藏  举报