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);
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM