规则引擎groovy

规则引擎选型
重量级方案:Acitivities、drools,适合复杂业务场景的规则引擎。

轻量级方案:groovy脚本。

groovy
特点:

动态性
单纯的java语言是不具有动态性的,groovy恰恰弥补了这一缺憾,有了groovy你可以在程序运行时任意修改代码逻辑,不需要重新发布。
语法糖
groovy在语法上兼具java 语言和脚本语言特点,大大简化了语法。

优点:

学习曲线平缓,有丰富的语法糖,对于Java开发者非常友好;
技术成熟,功能强大,易于使用维护,性能稳定,被业界看好;
和Java兼容性强,可以无缝衔接Java代码,可以调用Java所有的库。可以在Groovy脚本中使用Java代码,兼容率高达90%,除了lambda、数组语法,其他Java语法基本都能兼容。

原理:

        Groovy编译器先将.groovy文件编译成.class文件,然后调用JVM执行*.class文件,可以在Java项目中集成Groovy并充分利用Groovy的动态功能;

        Groovy兼容几乎所有的java语法,开发者完全可以将groovy当做Java来开发,甚至可以不使用Groovy的特有语法,仅仅通过引入Groovy并使用它的动态能力;

        Groovy可以直接调用项目中现有的Java类(通过import导入),通过构造函数构造对象并直接调用其方法并返回结果。

适用场景:

        Groovy适合在业务变化较多、较快的情况下进行一些可配置化的处理。适合规则数量相对较小的且不会频繁更新规则的规则引擎。

Groovy与java集成
GroovyClassLoader
用 Groovy 的 GroovyClassLoader ,它会动态地加载一个脚本并执行它。GroovyClassLoader是一个Groovy定制的类装载器,负责加载解析Groovy脚本类。

GroovyShell
GroovyShell允许在Java类中(甚至Groovy类)求任意Groovy表达式的值。您可使用Binding对象输入参数给表达式,并最终通过GroovyShell返回Groovy表达式的计算结果。

GroovyScriptEngine
GroovyShell多用于推求对立的脚本或表达式,如果换成相互关联的多个脚本,使用GroovyScriptEngine会更好些。GroovyScriptEngine从您指定的位置(文件系统,URL,数据库,等等)加载Groovy脚本,并且随着脚本变化而重新加载它们。

以GroovyClassLoader为例
原理:GroovyClassLoader支持从文件、url或字符串中加载解析Groovy Class,实例化对象,反射调用指定方法。主要负责运行时处理Groovy脚本,将其编译、加载为Class对象的工作。

1、引入jar包
        <!-- groovy-all -->
        <dependency>
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy-all</artifactId>
            <version>3.0.12</version>
            <type>pom</type>
        </dependency>
2、规则缓存工厂RuleCacheFactory
核心代码:

    private static Class buildGroovyClass(String groovyScript) throws RuleException {
        // 每个class都new一个loader,便于垃圾回收
        GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
        try {
            // groovy脚本解析为class
            return groovyClassLoader.parseClass(groovyScript);
        } catch (CompilationFailedException e) {
            log.error("groovy脚本解析为class异常!groovyScript={}", groovyScript, e);
            throw new RuleException("groovy脚本解析为class异常!");
        } finally {
            try {
                groovyClassLoader.close();
            } catch (IOException e) {
                log.error("GroovyClassLoader.close()异常!", e);
            }
        }
    }
RuleCacheFactory全部代码 :

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
 
import groovy.lang.GroovyClassLoader;
import lombok.extern.slf4j.Slf4j;
import org.codehaus.groovy.control.CompilationFailedException;
import yzh.exception.RuleException;
 
/**
 * 规则缓存工厂
 *
 * @author yangzihe
 * @date 2022/8/7
 */
@Slf4j
public final class RuleCacheFactory {
 
    /**
     * 规则脚本类缓存map  key-规则id value-规则脚本类
     */
    private static final Map<Long, Class> ruleClassMap = new ConcurrentHashMap<>();
 
    /**
     * 添加或更新规则脚本
     *
     * @param ruleId       规则id
     * @param groovyScript 规则的groovy脚本
     */
    public static void addOrUpdateRuleClass(Long ruleId, String groovyScript) {
        Class groovyClass = buildGroovyClass(groovyScript);
        ruleClassMap.put(ruleId, groovyClass);
    }
 
    /**
     * 获取规则脚本类
     *
     * @param ruleId 规则id
     *
     * @return 规则脚本类
     */
    public static Class getRuleClass(Long ruleId) {
        return ruleClassMap.get(ruleId);
    }
 
    /**
     * 删除规则脚本
     *
     * @param ruleId 规则id
     */
    public static void deleteRuleClass(Long ruleId) {
        ruleClassMap.remove(ruleId);
    }
 
    /**
     * 构建groovy类
     *
     * @param groovyScript groovy脚本
     *
     * @return groovy类
     *
     * @throws RuleException
     */
    private static Class buildGroovyClass(String groovyScript) throws RuleException {
        // 每个class都new一个loader,便于垃圾回收
        GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
        try {
            // groovy脚本解析为class
            return groovyClassLoader.parseClass(groovyScript);
        } catch (CompilationFailedException e) {
            log.error("groovy脚本解析为class异常!groovyScript={}", groovyScript, e);
            throw new RuleException("groovy脚本解析为class异常!");
        } finally {
            try {
                groovyClassLoader.close();
            } catch (IOException e) {
                log.error("GroovyClassLoader.close()异常!", e);
            }
        }
    }
 
    private RuleCacheFactory() {}
}
 

3、规则引擎RuleEngine
/**
 * groovy实现的规则引擎
 *
 * @author yangzihe
 * @date 2022/8/7
 */
@Slf4j
public class RuleEngine {
 
    /**
     * 规则执行
     *
     * @param ruleId  规则id
     * @param context 规则上下文
     *
     * @return 执行结果
     */
    public static RuleResult execute(Long ruleId, Map<String, Object> context) {
        Class ruleClass = RuleCacheFactory.getRuleClass(ruleId);
        if (ruleClass == null) {
            log.error("规则class缓存中不存在!ruleId={}", ruleId);
            return null;
        }
 
        // 获取groovy对象
        GroovyObject groovyObject = null;
        try {
            groovyObject = (GroovyObject) ruleClass.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            log.error("创建GroovyObject实例异常!ruleId={}", ruleId, e);
            throw new RuleException("创建GroovyObject实例异常");
        }
 
        // 调用脚本的run方法且传参
        Object result = null;
        try {
            result = groovyObject.invokeMethod("run", new Object[]{ruleId, context});
        } catch (Exception e) {
            log.error("规则执行异常!ruleId={}, context={}", ruleId, context, e);
            throw new RuleException("规则执行异常");
        }
 
        return (RuleResult) result;
    }
}
4、规则执行测试
import java.util.HashMap;
import java.util.Map;
 
import lombok.extern.slf4j.Slf4j;
import yzh.engine.bo.Rule;
import yzh.engine.bo.RuleResult;
 
/**
 * @author yangzihe
 * @date 2022/8/21
 */
@Slf4j
public class RuleEngineTest {
    /**
     * 规则执行测试
     */
    public static void main(String[] args) {
        // 获取规则
        Rule rule = new Rule();
        rule.setRuleId(1L);
        rule.setRuleCode("code1");
        rule.setRuleName("规则1");
 
        // 获取规则脚本
        RuleScript ruleScript = new RuleScript();
 
        ruleScript.setRule(rule);
 
        String runBody = RuleScriptUtils.buildRunBody();
        ruleScript.setRunBody(runBody);
 
        String allMethod = RuleScriptUtils.buildAllMethod();
        ruleScript.setAllMethod(allMethod);
 
        String groovyScript = ruleScript.getFullScript();
        log.info("脚本:\n{}", groovyScript);
        // 缓存规则class对象
        RuleCacheFactory.addOrUpdateRuleClass(rule.getRuleId(), groovyScript);
 
        // 获取规则上下文
        Map<String, Object> context = new HashMap<>();
        context.put("name", "程序员");
        context.put("age", 18);
 
        // 执行规则
        RuleResult ruleResult = RuleEngine.execute(rule.getRuleId(), context);
 
        context.put("name", "程序员");
        context.put("age", 40);
        RuleResult ruleResult2 = RuleEngine.execute(rule.getRuleId(), context);
    }
}

执行结果:

21:26:48.886 [main] INFO Rule_code1 - 规则执行,入参:{name=程序员, age=18}, 结果:RuleResult(ruleId=1, isHit=false, hitExpList=[1])
21:26:48.887 [main] INFO Rule_code1 - 规则执行,入参:{name=程序员, age=40}, 结果:RuleResult(ruleId=1, isHit=true, hitExpList=[1, 2])
规则脚本:
 

import java.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import yzh.engine.bo.RuleResult;
 
@groovy.transform.CompileStatic
public class Rule_code1 {
	private static final Logger log = LoggerFactory.getLogger(Rule_code1.class);
	// 上下文
	private Map<String, Object> context = null;
	// 命中的表达式id集合
	private List<Long> hitExpList = new ArrayList<>();
	// 执行入口
	public RuleResult run(Long ruleId, Map<String, Object> context) {
		this.context = context;
 
		boolean isHit = exp_1() && exp_2();
 
		RuleResult ruleResult = new RuleResult();
		ruleResult.setRuleId(ruleId);
		ruleResult.setIsHit(isHit);
		ruleResult.setHitExpList(hitExpList);
		log.info("规则执行,入参:{}, 结果:{}", context, ruleResult);
		return ruleResult;
	}
	// 表达式1
	private boolean exp_1() {
		String leftVar = (String) context.get("name");
		String rightVar = "程序员";
		boolean expHit = equalsString(leftVar, rightVar);
		if (expHit) {
			hitExpList.add(1L);
		}
		return expHit;
	}
	// 表达式2
	private boolean exp_2() {
		Long leftVar = (Long) context.get("age");
		Long rightVar = 35L;
		boolean expHit = greaterThanLong(leftVar, rightVar);
		if (expHit) {
			hitExpList.add(2L);
		}
		return expHit;
	}
	// 操作符函数
	private boolean equalsString(String leftVar, String rightVar) {
		if (leftVar == null) {
			return rightVar == null;
		}
		return leftVar.equals(rightVar);
	}
	// 操作符函数
	private boolean greaterThanLong(Long leftVar, Long rightVar) {
		if (leftVar == null) {
			return false;
		}
		return leftVar.compareTo(rightVar) > 0;
	}
 
}
5、规则脚本RuleScript
import java.util.List;
 
import lombok.Data;
import yzh.engine.bo.Rule;
 
/**
 * 规则脚本
 *
 * @author yangzihe
 * @date 2022/8/20
 */
@Data
public class RuleScript {
 
    /**
     * 包名集合
     */
    private List<String> packageList;
    /**
     * 规则
     */
    private Rule rule;
    /**
     * run方法体
     */
    private String runBody;
    /**
     * 所有方法
     */
    private String allMethod;
 
    /**
     * 获取完整规则脚本
     *
     * @return 规则脚本
     */
    public String getFullScript() {
        String scriptTemplate = RuleScriptUtils.buildScriptTemplate();
        String importPackage = RuleScriptUtils.buildImportPackage(packageList);
        String ruleCode = rule.getRuleCode();
        return String.format(scriptTemplate, importPackage, ruleCode, ruleCode, runBody, allMethod);
    }
 
}
 规则脚本工具类RuleScriptUtils
package yzh.engine;
 
import java.util.List;
 
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
 
import static yzh.engine.bo.ScriptConstant.CONTEXT;
import static yzh.engine.bo.ScriptConstant.HIT_EXP_LIST;
import static yzh.engine.bo.ScriptConstant.NEWLINE;
import static yzh.engine.bo.ScriptConstant.RULE_RESULT_CLASS_NAME;
import static yzh.engine.bo.ScriptConstant.TAB;
import static yzh.engine.bo.ScriptConstant.TAB2;
import static yzh.engine.bo.ScriptConstant.TAB3;
 
/**
 * 规则脚本工具类
 *
 * @author yangzihe
 * @date 2022/8/20
 */
@Slf4j
public final class RuleScriptUtils {
 
    /**
     * 构建脚本模板
     *
     * @return 脚本模板
     */
    public static String buildScriptTemplate() {
        // %s
        // @groovy.transform.CompileStatic
        // public class Rule_%s {
        //  private static final Logger log = LoggerFactory.getLogger(Rule_%s.class);
        // 	// 上下文
        // 	private Map<String, Object> context = null;
        //  // 命中的表达式id集合
        //  private List<Long> hitExpList = new ArrayList<>();
        // 	// 执行入口
        // 	public RuleResult run(Long ruleId, Map<String, Object> context) {
        // %s
        // 	}
        // %s
        // }
        StringBuilder stringBuilder = new StringBuilder();
 
        // 1-%s:导包语句占位符
        stringBuilder.append("%s").append(NEWLINE);
 
        // 使用groovy的静态编译:禁用了Groovy动态编程特征,例如:生成的字节码文件中不再含有实现动态编程的字节码指令"invoke dynamic";
        // 字节码文件也更小;生成的字节码将会和javac生成的字节码很相似,jvm执行性能接近。https://www.oschina.net/translate/new-groovy-20?cmp&p=2
        stringBuilder.append("@groovy.transform.CompileStatic").append(NEWLINE);
 
        // 类名,2-%s:规则code占位符
        stringBuilder.append("public class Rule_%s {").append(NEWLINE);
 
        // 成员变量声明,3-%s:规则code占位符
        stringBuilder.append(TAB).append("private static final Logger log = LoggerFactory.getLogger(Rule_%s.class);").append(NEWLINE);
        stringBuilder.append(TAB).append("// 上下文").append(NEWLINE);
        stringBuilder.append(TAB).append("private Map<String, Object> ").append(CONTEXT).append(" = null;").append(NEWLINE);
        stringBuilder.append(TAB).append("// 命中的表达式id集合").append(NEWLINE);
        stringBuilder.append(TAB).append("private List<Long> ").append(HIT_EXP_LIST).append(" = new ArrayList<>();").append(NEWLINE);
 
        // 执行入口 run()方法声明
        stringBuilder.append(TAB).append("// 执行入口").append(NEWLINE);
        stringBuilder.append(TAB).append("public ").append(RULE_RESULT_CLASS_NAME).append(" run(Long ruleId, Map<String, Object> context) {").append(NEWLINE);
        // 4-%s:run()方法体占位符
        stringBuilder.append("%s").append(NEWLINE);
        stringBuilder.append(TAB).append("}").append(NEWLINE);
 
        // 5-%s:所有方法的占位符
        stringBuilder.append("%s").append(NEWLINE);
 
        stringBuilder.append("}").append(NEWLINE);
 
        return stringBuilder.toString();
    }
 
    /**
     * 构建导包语句
     *
     * @param packageList 包名集合
     *
     * @return 导包语句
     */
    public static String buildImportPackage(List<String> packageList) {
        StringBuilder stringBuilder = new StringBuilder();
        // 公共类库
        stringBuilder.append("import java.util.*;").append(NEWLINE);
        stringBuilder.append("import org.slf4j.Logger;").append(NEWLINE);
        stringBuilder.append("import org.slf4j.LoggerFactory;").append(NEWLINE);
        stringBuilder.append("import yzh.engine.bo.RuleResult;").append(NEWLINE);
        if (CollectionUtils.isEmpty(packageList)) {
            return stringBuilder.toString();
        }
        for (String packageName : packageList) {
            stringBuilder.append("import ").append(packageName).append(";").append(NEWLINE);
        }
        return stringBuilder.toString();
    }
 
    /**
     * 构建run()方法体
     *
     * @return run()方法体
     */
    public static String buildRunBody() {
        StringBuilder stringBuilder = new StringBuilder();
 
        stringBuilder.append(TAB2).append("this.").append(CONTEXT).append(" = context;").append(NEWLINE);
 
        stringBuilder.append(NEWLINE);
        stringBuilder.append(TAB2).append("boolean isHit = exp_1() && exp_2();").append(NEWLINE);
        stringBuilder.append(NEWLINE);
 
        stringBuilder.append(TAB2).append("RuleResult ruleResult = new RuleResult();").append(NEWLINE);
        stringBuilder.append(TAB2).append("ruleResult.setRuleId(ruleId);").append(NEWLINE);
        stringBuilder.append(TAB2).append("ruleResult.setIsHit(isHit);").append(NEWLINE);
        stringBuilder.append(TAB2).append("ruleResult.setHitExpList(hitExpList);").append(NEWLINE);
        stringBuilder.append(TAB2).append("log.info(\"规则执行,入参:{}, 结果:{}\", context, ruleResult);").append(NEWLINE);
        stringBuilder.append(TAB2).append("return ruleResult;");
 
        return stringBuilder.toString();
    }
 
    /**
     * 构建除run()方法外的其它所有方法
     *
     * @return 所有方法
     */
    public static String buildAllMethod() {
        StringBuilder stringBuilder = new StringBuilder();
 
        String expMethod = buildExpMethod(1L);
        String expMethod2 = buildExpMethod2(2L);
        String operatorMethod = buildOperatorMethod();
        String operatorMethod2 = buildOperatorMethod2();
 
        stringBuilder.append(expMethod);
        stringBuilder.append(expMethod2);
        stringBuilder.append(operatorMethod);
        stringBuilder.append(operatorMethod2);
 
        return stringBuilder.toString();
    }
 
    public static String buildExpMethod(Long expId) {
        StringBuilder stringBuilder = new StringBuilder();
 
        stringBuilder.append(TAB).append("// 表达式").append(expId).append(NEWLINE);
        stringBuilder.append(TAB).append("private boolean exp_").append(expId).append("() {").append(NEWLINE);
        stringBuilder.append(TAB2).append("String leftVar = (String) context.get(\"name\");").append(NEWLINE);
        stringBuilder.append(TAB2).append("String rightVar = \"程序员\";").append(NEWLINE);
        stringBuilder.append(TAB2).append("boolean expHit = equalsString(leftVar, rightVar);").append(NEWLINE);
        stringBuilder.append(TAB2).append("if (expHit) {").append(NEWLINE);
        stringBuilder.append(TAB3).append("hitExpList.add(1L);").append(NEWLINE);
        stringBuilder.append(TAB2).append("}").append(NEWLINE);
        stringBuilder.append(TAB2).append("return expHit;").append(NEWLINE);
        stringBuilder.append(TAB).append("}").append(NEWLINE);
 
        return stringBuilder.toString();
    }
 
    public static String buildExpMethod2(Long expId) {
        StringBuilder stringBuilder = new StringBuilder();
 
        stringBuilder.append(TAB).append("// 表达式").append(expId).append(NEWLINE);
        stringBuilder.append(TAB).append("private boolean exp_").append(expId).append("() {").append(NEWLINE);
        stringBuilder.append(TAB2).append("Long leftVar = (Long) context.get(\"age\");").append(NEWLINE);
        stringBuilder.append(TAB2).append("Long rightVar = 35L;").append(NEWLINE);
        stringBuilder.append(TAB2).append("boolean expHit = greaterThanLong(leftVar, rightVar);").append(NEWLINE);
        stringBuilder.append(TAB2).append("if (expHit) {").append(NEWLINE);
        stringBuilder.append(TAB3).append("hitExpList.add(2L);").append(NEWLINE);
        stringBuilder.append(TAB2).append("}").append(NEWLINE);
        stringBuilder.append(TAB2).append("return expHit;").append(NEWLINE);
        stringBuilder.append(TAB).append("}").append(NEWLINE);
 
        return stringBuilder.toString();
    }
 
    public static String buildOperatorMethod() {
        StringBuilder stringBuilder = new StringBuilder();
 
        stringBuilder.append(TAB).append("// 操作符函数").append(NEWLINE);
        stringBuilder.append(TAB).append("private boolean equalsString(String leftVar, String rightVar) {").append(NEWLINE);
        stringBuilder.append(TAB2).append("if (leftVar == null) {").append(NEWLINE);
        stringBuilder.append(TAB3).append("return rightVar == null;").append(NEWLINE);
        stringBuilder.append(TAB2).append("}").append(NEWLINE);
        stringBuilder.append(TAB2).append("return leftVar.equals(rightVar);").append(NEWLINE);
        stringBuilder.append(TAB).append("}").append(NEWLINE);
 
        return stringBuilder.toString();
    }
 
    public static String buildOperatorMethod2() {
        StringBuilder stringBuilder = new StringBuilder();
 
        stringBuilder.append(TAB).append("// 操作符函数").append(NEWLINE);
        stringBuilder.append(TAB).append("private boolean greaterThanLong(Long leftVar, Long rightVar) {").append(NEWLINE);
        stringBuilder.append(TAB2).append("if (leftVar == null) {").append(NEWLINE);
        stringBuilder.append(TAB3).append("return false;").append(NEWLINE);
        stringBuilder.append(TAB2).append("}").append(NEWLINE);
        stringBuilder.append(TAB2).append("return leftVar.compareTo(rightVar) > 0;").append(NEWLINE);
        stringBuilder.append(TAB).append("}").append(NEWLINE);
 
        return stringBuilder.toString();
    }
}

规则对象Rule
package yzh.engine.bo;
 
import lombok.Data;
 
/**
 * @author yangzihe
 * @date 2022/8/7
 */
@Data
public class Rule {
 
    /**
     * 规则id
     */
    private Long ruleId;
 
    /**
     * 规则编码
     */
    private String ruleCode;
 
    /**
     * 规则名
     */
    private String ruleName;
 

————————————————
版权声明:本文为CSDN博主「yzh_1346983557」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yzh_1346983557/article/details/126211611

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
方案是为解决特定问题或达成特定目标而制定的一系列计划或步骤。它的作用是提供一种系统性的方法,以有效地应对挑战、优化流程或实现目标。以下是方案的主要作用: 问题解决: 方案的核心目标是解决问题。通过系统性的规划和执行,方案能够分析问题的根本原因,提供可行的解决方案,并引导实施过程,确保问题得到合理解决。 目标达成: 方案通常与明确的目标相关联,它提供了一种达成这些目标的计划。无论是企业战略、项目管理还是个人发展,方案的制定都有助于明确目标并提供达成目标的路径。 资源优化: 方案在设计时考虑了可用资源,以最大化其效用。通过明智的资源分配,方案可以在有限的资源条件下实现最大的效益,提高效率并减少浪费。 风险管理: 方案通常会对潜在的风险进行评估,并制定相应的风险管理策略。这有助于减轻潜在问题的影响,提高方案的可行性和可持续性。 决策支持: 方案提供了决策者所需的信息和数据,以便做出明智的决策。这种数据驱动的方法有助于减少不确定性,提高决策的准确性。 团队协作: 复杂的问题通常需要多个人的协同努力。方案提供了一个共同的框架,帮助团队成员理解各自的职责和任务,促进协作并确保整个团队朝着共同的目标努力。 监控与评估: 方案通常包括监控和评估的机制,以确保实施的有效性。通过定期的评估,可以及时调整方案,以适应变化的环境或新的挑战。 总体而言,方案的作用在于提供一种有序、有计划的方法,以解决问题、实现目标,并在实施过程中最大化资源利用和风险管理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值