• 规则引擎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包

    1. <!-- groovy-all -->
    2. <dependency>
    3. <groupId>org.codehaus.groovy</groupId>
    4. <artifactId>groovy-all</artifactId>
    5. <version>3.0.12</version>
    6. <type>pom</type>
    7. </dependency>

    2、规则缓存工厂RuleCacheFactory

    核心代码:

    1. private static Class buildGroovyClass(String groovyScript) throws RuleException {
    2. // 每个class都new一个loader,便于垃圾回收
    3. GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
    4. try {
    5. // groovy脚本解析为class
    6. return groovyClassLoader.parseClass(groovyScript);
    7. } catch (CompilationFailedException e) {
    8. log.error("groovy脚本解析为class异常!groovyScript={}", groovyScript, e);
    9. throw new RuleException("groovy脚本解析为class异常!");
    10. } finally {
    11. try {
    12. groovyClassLoader.close();
    13. } catch (IOException e) {
    14. log.error("GroovyClassLoader.close()异常!", e);
    15. }
    16. }
    17. }

    RuleCacheFactory全部代码 :

    1. import java.io.IOException;
    2. import java.util.Map;
    3. import java.util.concurrent.ConcurrentHashMap;
    4. import groovy.lang.GroovyClassLoader;
    5. import lombok.extern.slf4j.Slf4j;
    6. import org.codehaus.groovy.control.CompilationFailedException;
    7. import yzh.exception.RuleException;
    8. /**
    9. * 规则缓存工厂
    10. *
    11. * @author yangzihe
    12. * @date 2022/8/7
    13. */
    14. @Slf4j
    15. public final class RuleCacheFactory {
    16. /**
    17. * 规则脚本类缓存map key-规则id value-规则脚本类
    18. */
    19. private static final Map ruleClassMap = new ConcurrentHashMap<>();
    20. /**
    21. * 添加或更新规则脚本
    22. *
    23. * @param ruleId 规则id
    24. * @param groovyScript 规则的groovy脚本
    25. */
    26. public static void addOrUpdateRuleClass(Long ruleId, String groovyScript) {
    27. Class groovyClass = buildGroovyClass(groovyScript);
    28. ruleClassMap.put(ruleId, groovyClass);
    29. }
    30. /**
    31. * 获取规则脚本类
    32. *
    33. * @param ruleId 规则id
    34. *
    35. * @return 规则脚本类
    36. */
    37. public static Class getRuleClass(Long ruleId) {
    38. return ruleClassMap.get(ruleId);
    39. }
    40. /**
    41. * 删除规则脚本
    42. *
    43. * @param ruleId 规则id
    44. */
    45. public static void deleteRuleClass(Long ruleId) {
    46. ruleClassMap.remove(ruleId);
    47. }
    48. /**
    49. * 构建groovy类
    50. *
    51. * @param groovyScript groovy脚本
    52. *
    53. * @return groovy类
    54. *
    55. * @throws RuleException
    56. */
    57. private static Class buildGroovyClass(String groovyScript) throws RuleException {
    58. // 每个class都new一个loader,便于垃圾回收
    59. GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
    60. try {
    61. // groovy脚本解析为class
    62. return groovyClassLoader.parseClass(groovyScript);
    63. } catch (CompilationFailedException e) {
    64. log.error("groovy脚本解析为class异常!groovyScript={}", groovyScript, e);
    65. throw new RuleException("groovy脚本解析为class异常!");
    66. } finally {
    67. try {
    68. groovyClassLoader.close();
    69. } catch (IOException e) {
    70. log.error("GroovyClassLoader.close()异常!", e);
    71. }
    72. }
    73. }
    74. private RuleCacheFactory() {}
    75. }

    3、规则引擎RuleEngine

    1. /**
    2. * groovy实现的规则引擎
    3. *
    4. * @author yangzihe
    5. * @date 2022/8/7
    6. */
    7. @Slf4j
    8. public class RuleEngine {
    9. /**
    10. * 规则执行
    11. *
    12. * @param ruleId 规则id
    13. * @param context 规则上下文
    14. *
    15. * @return 执行结果
    16. */
    17. public static RuleResult execute(Long ruleId, Map context) {
    18. Class ruleClass = RuleCacheFactory.getRuleClass(ruleId);
    19. if (ruleClass == null) {
    20. log.error("规则class缓存中不存在!ruleId={}", ruleId);
    21. return null;
    22. }
    23. // 获取groovy对象
    24. GroovyObject groovyObject = null;
    25. try {
    26. groovyObject = (GroovyObject) ruleClass.newInstance();
    27. } catch (InstantiationException | IllegalAccessException e) {
    28. log.error("创建GroovyObject实例异常!ruleId={}", ruleId, e);
    29. throw new RuleException("创建GroovyObject实例异常");
    30. }
    31. // 调用脚本的run方法且传参
    32. Object result = null;
    33. try {
    34. result = groovyObject.invokeMethod("run", new Object[]{ruleId, context});
    35. } catch (Exception e) {
    36. log.error("规则执行异常!ruleId={}, context={}", ruleId, context, e);
    37. throw new RuleException("规则执行异常");
    38. }
    39. return (RuleResult) result;
    40. }
    41. }

    4、规则执行测试

    1. import java.util.HashMap;
    2. import java.util.Map;
    3. import lombok.extern.slf4j.Slf4j;
    4. import yzh.engine.bo.Rule;
    5. import yzh.engine.bo.RuleResult;
    6. /**
    7. * @author yangzihe
    8. * @date 2022/8/21
    9. */
    10. @Slf4j
    11. public class RuleEngineTest {
    12. /**
    13. * 规则执行测试
    14. */
    15. public static void main(String[] args) {
    16. // 获取规则
    17. Rule rule = new Rule();
    18. rule.setRuleId(1L);
    19. rule.setRuleCode("code1");
    20. rule.setRuleName("规则1");
    21. // 获取规则脚本
    22. RuleScript ruleScript = new RuleScript();
    23. ruleScript.setRule(rule);
    24. String runBody = RuleScriptUtils.buildRunBody();
    25. ruleScript.setRunBody(runBody);
    26. String allMethod = RuleScriptUtils.buildAllMethod();
    27. ruleScript.setAllMethod(allMethod);
    28. String groovyScript = ruleScript.getFullScript();
    29. log.info("脚本:\n{}", groovyScript);
    30. // 缓存规则class对象
    31. RuleCacheFactory.addOrUpdateRuleClass(rule.getRuleId(), groovyScript);
    32. // 获取规则上下文
    33. Map context = new HashMap<>();
    34. context.put("name", "程序员");
    35. context.put("age", 18);
    36. // 执行规则
    37. RuleResult ruleResult = RuleEngine.execute(rule.getRuleId(), context);
    38. context.put("name", "程序员");
    39. context.put("age", 40);
    40. RuleResult ruleResult2 = RuleEngine.execute(rule.getRuleId(), context);
    41. }
    42. }

    执行结果:

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

    规则脚本:

    1. import java.util.*;
    2. import org.slf4j.Logger;
    3. import org.slf4j.LoggerFactory;
    4. import yzh.engine.bo.RuleResult;
    5. @groovy.transform.CompileStatic
    6. public class Rule_code1 {
    7. private static final Logger log = LoggerFactory.getLogger(Rule_code1.class);
    8. // 上下文
    9. private Map context = null;
    10. // 命中的表达式id集合
    11. private List hitExpList = new ArrayList<>();
    12. // 执行入口
    13. public RuleResult run(Long ruleId, Map context) {
    14. this.context = context;
    15. boolean isHit = exp_1() && exp_2();
    16. RuleResult ruleResult = new RuleResult();
    17. ruleResult.setRuleId(ruleId);
    18. ruleResult.setIsHit(isHit);
    19. ruleResult.setHitExpList(hitExpList);
    20. log.info("规则执行,入参:{}, 结果:{}", context, ruleResult);
    21. return ruleResult;
    22. }
    23. // 表达式1
    24. private boolean exp_1() {
    25. String leftVar = (String) context.get("name");
    26. String rightVar = "程序员";
    27. boolean expHit = equalsString(leftVar, rightVar);
    28. if (expHit) {
    29. hitExpList.add(1L);
    30. }
    31. return expHit;
    32. }
    33. // 表达式2
    34. private boolean exp_2() {
    35. Long leftVar = (Long) context.get("age");
    36. Long rightVar = 35L;
    37. boolean expHit = greaterThanLong(leftVar, rightVar);
    38. if (expHit) {
    39. hitExpList.add(2L);
    40. }
    41. return expHit;
    42. }
    43. // 操作符函数
    44. private boolean equalsString(String leftVar, String rightVar) {
    45. if (leftVar == null) {
    46. return rightVar == null;
    47. }
    48. return leftVar.equals(rightVar);
    49. }
    50. // 操作符函数
    51. private boolean greaterThanLong(Long leftVar, Long rightVar) {
    52. if (leftVar == null) {
    53. return false;
    54. }
    55. return leftVar.compareTo(rightVar) > 0;
    56. }
    57. }

    5、规则脚本RuleScript

    1. import java.util.List;
    2. import lombok.Data;
    3. import yzh.engine.bo.Rule;
    4. /**
    5. * 规则脚本
    6. *
    7. * @author yangzihe
    8. * @date 2022/8/20
    9. */
    10. @Data
    11. public class RuleScript {
    12. /**
    13. * 包名集合
    14. */
    15. private List packageList;
    16. /**
    17. * 规则
    18. */
    19. private Rule rule;
    20. /**
    21. * run方法体
    22. */
    23. private String runBody;
    24. /**
    25. * 所有方法
    26. */
    27. private String allMethod;
    28. /**
    29. * 获取完整规则脚本
    30. *
    31. * @return 规则脚本
    32. */
    33. public String getFullScript() {
    34. String scriptTemplate = RuleScriptUtils.buildScriptTemplate();
    35. String importPackage = RuleScriptUtils.buildImportPackage(packageList);
    36. String ruleCode = rule.getRuleCode();
    37. return String.format(scriptTemplate, importPackage, ruleCode, ruleCode, runBody, allMethod);
    38. }
    39. }

     规则脚本工具类RuleScriptUtils

    1. package yzh.engine;
    2. import java.util.List;
    3. import lombok.extern.slf4j.Slf4j;
    4. import org.apache.commons.collections4.CollectionUtils;
    5. import static yzh.engine.bo.ScriptConstant.CONTEXT;
    6. import static yzh.engine.bo.ScriptConstant.HIT_EXP_LIST;
    7. import static yzh.engine.bo.ScriptConstant.NEWLINE;
    8. import static yzh.engine.bo.ScriptConstant.RULE_RESULT_CLASS_NAME;
    9. import static yzh.engine.bo.ScriptConstant.TAB;
    10. import static yzh.engine.bo.ScriptConstant.TAB2;
    11. import static yzh.engine.bo.ScriptConstant.TAB3;
    12. /**
    13. * 规则脚本工具类
    14. *
    15. * @author yangzihe
    16. * @date 2022/8/20
    17. */
    18. @Slf4j
    19. public final class RuleScriptUtils {
    20. /**
    21. * 构建脚本模板
    22. *
    23. * @return 脚本模板
    24. */
    25. public static String buildScriptTemplate() {
    26. // %s
    27. // @groovy.transform.CompileStatic
    28. // public class Rule_%s {
    29. // private static final Logger log = LoggerFactory.getLogger(Rule_%s.class);
    30. // // 上下文
    31. // private Map context = null;
    32. // // 命中的表达式id集合
    33. // private List hitExpList = new ArrayList<>();
    34. // // 执行入口
    35. // public RuleResult run(Long ruleId, Map context) {
    36. // %s
    37. // }
    38. // %s
    39. // }
    40. StringBuilder stringBuilder = new StringBuilder();
    41. // 1-%s:导包语句占位符
    42. stringBuilder.append("%s").append(NEWLINE);
    43. // 使用groovy的静态编译:禁用了Groovy动态编程特征,例如:生成的字节码文件中不再含有实现动态编程的字节码指令"invoke dynamic";
    44. // 字节码文件也更小;生成的字节码将会和javac生成的字节码很相似,jvm执行性能接近。https://www.oschina.net/translate/new-groovy-20?cmp&p=2
    45. stringBuilder.append("@groovy.transform.CompileStatic").append(NEWLINE);
    46. // 类名,2-%s:规则code占位符
    47. stringBuilder.append("public class Rule_%s {").append(NEWLINE);
    48. // 成员变量声明,3-%s:规则code占位符
    49. stringBuilder.append(TAB).append("private static final Logger log = LoggerFactory.getLogger(Rule_%s.class);").append(NEWLINE);
    50. stringBuilder.append(TAB).append("// 上下文").append(NEWLINE);
    51. stringBuilder.append(TAB).append("private Map ").append(CONTEXT).append(" = null;").append(NEWLINE);
    52. stringBuilder.append(TAB).append("// 命中的表达式id集合").append(NEWLINE);
    53. stringBuilder.append(TAB).append("private List ").append(HIT_EXP_LIST).append(" = new ArrayList<>();").append(NEWLINE);
    54. // 执行入口 run()方法声明
    55. stringBuilder.append(TAB).append("// 执行入口").append(NEWLINE);
    56. stringBuilder.append(TAB).append("public ").append(RULE_RESULT_CLASS_NAME).append(" run(Long ruleId, Map context) {").append(NEWLINE);
    57. // 4-%s:run()方法体占位符
    58. stringBuilder.append("%s").append(NEWLINE);
    59. stringBuilder.append(TAB).append("}").append(NEWLINE);
    60. // 5-%s:所有方法的占位符
    61. stringBuilder.append("%s").append(NEWLINE);
    62. stringBuilder.append("}").append(NEWLINE);
    63. return stringBuilder.toString();
    64. }
    65. /**
    66. * 构建导包语句
    67. *
    68. * @param packageList 包名集合
    69. *
    70. * @return 导包语句
    71. */
    72. public static String buildImportPackage(List packageList) {
    73. StringBuilder stringBuilder = new StringBuilder();
    74. // 公共类库
    75. stringBuilder.append("import java.util.*;").append(NEWLINE);
    76. stringBuilder.append("import org.slf4j.Logger;").append(NEWLINE);
    77. stringBuilder.append("import org.slf4j.LoggerFactory;").append(NEWLINE);
    78. stringBuilder.append("import yzh.engine.bo.RuleResult;").append(NEWLINE);
    79. if (CollectionUtils.isEmpty(packageList)) {
    80. return stringBuilder.toString();
    81. }
    82. for (String packageName : packageList) {
    83. stringBuilder.append("import ").append(packageName).append(";").append(NEWLINE);
    84. }
    85. return stringBuilder.toString();
    86. }
    87. /**
    88. * 构建run()方法体
    89. *
    90. * @return run()方法体
    91. */
    92. public static String buildRunBody() {
    93. StringBuilder stringBuilder = new StringBuilder();
    94. stringBuilder.append(TAB2).append("this.").append(CONTEXT).append(" = context;").append(NEWLINE);
    95. stringBuilder.append(NEWLINE);
    96. stringBuilder.append(TAB2).append("boolean isHit = exp_1() && exp_2();").append(NEWLINE);
    97. stringBuilder.append(NEWLINE);
    98. stringBuilder.append(TAB2).append("RuleResult ruleResult = new RuleResult();").append(NEWLINE);
    99. stringBuilder.append(TAB2).append("ruleResult.setRuleId(ruleId);").append(NEWLINE);
    100. stringBuilder.append(TAB2).append("ruleResult.setIsHit(isHit);").append(NEWLINE);
    101. stringBuilder.append(TAB2).append("ruleResult.setHitExpList(hitExpList);").append(NEWLINE);
    102. stringBuilder.append(TAB2).append("log.info(\"规则执行,入参:{}, 结果:{}\", context, ruleResult);").append(NEWLINE);
    103. stringBuilder.append(TAB2).append("return ruleResult;");
    104. return stringBuilder.toString();
    105. }
    106. /**
    107. * 构建除run()方法外的其它所有方法
    108. *
    109. * @return 所有方法
    110. */
    111. public static String buildAllMethod() {
    112. StringBuilder stringBuilder = new StringBuilder();
    113. String expMethod = buildExpMethod(1L);
    114. String expMethod2 = buildExpMethod2(2L);
    115. String operatorMethod = buildOperatorMethod();
    116. String operatorMethod2 = buildOperatorMethod2();
    117. stringBuilder.append(expMethod);
    118. stringBuilder.append(expMethod2);
    119. stringBuilder.append(operatorMethod);
    120. stringBuilder.append(operatorMethod2);
    121. return stringBuilder.toString();
    122. }
    123. public static String buildExpMethod(Long expId) {
    124. StringBuilder stringBuilder = new StringBuilder();
    125. stringBuilder.append(TAB).append("// 表达式").append(expId).append(NEWLINE);
    126. stringBuilder.append(TAB).append("private boolean exp_").append(expId).append("() {").append(NEWLINE);
    127. stringBuilder.append(TAB2).append("String leftVar = (String) context.get(\"name\");").append(NEWLINE);
    128. stringBuilder.append(TAB2).append("String rightVar = \"程序员\";").append(NEWLINE);
    129. stringBuilder.append(TAB2).append("boolean expHit = equalsString(leftVar, rightVar);").append(NEWLINE);
    130. stringBuilder.append(TAB2).append("if (expHit) {").append(NEWLINE);
    131. stringBuilder.append(TAB3).append("hitExpList.add(1L);").append(NEWLINE);
    132. stringBuilder.append(TAB2).append("}").append(NEWLINE);
    133. stringBuilder.append(TAB2).append("return expHit;").append(NEWLINE);
    134. stringBuilder.append(TAB).append("}").append(NEWLINE);
    135. return stringBuilder.toString();
    136. }
    137. public static String buildExpMethod2(Long expId) {
    138. StringBuilder stringBuilder = new StringBuilder();
    139. stringBuilder.append(TAB).append("// 表达式").append(expId).append(NEWLINE);
    140. stringBuilder.append(TAB).append("private boolean exp_").append(expId).append("() {").append(NEWLINE);
    141. stringBuilder.append(TAB2).append("Long leftVar = (Long) context.get(\"age\");").append(NEWLINE);
    142. stringBuilder.append(TAB2).append("Long rightVar = 35L;").append(NEWLINE);
    143. stringBuilder.append(TAB2).append("boolean expHit = greaterThanLong(leftVar, rightVar);").append(NEWLINE);
    144. stringBuilder.append(TAB2).append("if (expHit) {").append(NEWLINE);
    145. stringBuilder.append(TAB3).append("hitExpList.add(2L);").append(NEWLINE);
    146. stringBuilder.append(TAB2).append("}").append(NEWLINE);
    147. stringBuilder.append(TAB2).append("return expHit;").append(NEWLINE);
    148. stringBuilder.append(TAB).append("}").append(NEWLINE);
    149. return stringBuilder.toString();
    150. }
    151. public static String buildOperatorMethod() {
    152. StringBuilder stringBuilder = new StringBuilder();
    153. stringBuilder.append(TAB).append("// 操作符函数").append(NEWLINE);
    154. stringBuilder.append(TAB).append("private boolean equalsString(String leftVar, String rightVar) {").append(NEWLINE);
    155. stringBuilder.append(TAB2).append("if (leftVar == null) {").append(NEWLINE);
    156. stringBuilder.append(TAB3).append("return rightVar == null;").append(NEWLINE);
    157. stringBuilder.append(TAB2).append("}").append(NEWLINE);
    158. stringBuilder.append(TAB2).append("return leftVar.equals(rightVar);").append(NEWLINE);
    159. stringBuilder.append(TAB).append("}").append(NEWLINE);
    160. return stringBuilder.toString();
    161. }
    162. public static String buildOperatorMethod2() {
    163. StringBuilder stringBuilder = new StringBuilder();
    164. stringBuilder.append(TAB).append("// 操作符函数").append(NEWLINE);
    165. stringBuilder.append(TAB).append("private boolean greaterThanLong(Long leftVar, Long rightVar) {").append(NEWLINE);
    166. stringBuilder.append(TAB2).append("if (leftVar == null) {").append(NEWLINE);
    167. stringBuilder.append(TAB3).append("return false;").append(NEWLINE);
    168. stringBuilder.append(TAB2).append("}").append(NEWLINE);
    169. stringBuilder.append(TAB2).append("return leftVar.compareTo(rightVar) > 0;").append(NEWLINE);
    170. stringBuilder.append(TAB).append("}").append(NEWLINE);
    171. return stringBuilder.toString();
    172. }
    173. }

    规则对象Rule

    1. package yzh.engine.bo;
    2. import lombok.Data;
    3. /**
    4. * @author yangzihe
    5. * @date 2022/8/7
    6. */
    7. @Data
    8. public class Rule {
    9. /**
    10. * 规则id
    11. */
    12. private Long ruleId;
    13. /**
    14. * 规则编码
    15. */
    16. private String ruleCode;
    17. /**
    18. * 规则名
    19. */
    20. private String ruleName;
    21. }

    规则结果RuleResult

    1. package yzh.engine.bo;
    2. import java.util.List;
    3. import lombok.Data;
    4. /**
    5. * 规则结果
    6. *
    7. * @author yangzihe
    8. * @date 2022/8/7
    9. */
    10. @Data
    11. public class RuleResult {
    12. /**
    13. * 规则id
    14. */
    15. private Long ruleId;
    16. /**
    17. * 是否命中
    18. */
    19. private Boolean isHit;
    20. /**
    21. * 命中的表达式id集合
    22. */
    23. private List hitExpList;
    24. }

     脚本常量类ScriptConstant

    1. /**
    2. * 脚本常量类
    3. *
    4. * @author yangzihe
    5. * @date 2022/8/21
    6. */
    7. public class ScriptConstant {
    8. /**
    9. * 换行符
    10. */
    11. public static final String NEWLINE = System.getProperty("line.separator");
    12. public static final String TAB = "\t";
    13. public static final String TAB2 = "\t\t";
    14. public static final String TAB3 = "\t\t\t";
    15. /**
    16. * 上下文变量名
    17. */
    18. public static final String CONTEXT = "context";
    19. /**
    20. * 命中的表达式集合
    21. */
    22. public static final String HIT_EXP_LIST = "hitExpList";
    23. /**
    24. * 规则结果类名
    25. */
    26. public static final String RULE_RESULT_CLASS_NAME = RuleResult.class.getSimpleName();
    27. }

    RuleException

    1. public class RuleException extends RuntimeException {
    2. private static final long serialVersionUID = 6799975089755988273L;
    3. public RuleException() {
    4. super("规则异常");
    5. }
    6. public RuleException(String message) {
    7. super(message);
    8. }
    9. public RuleException(String message, Throwable e) {
    10. super(message, e);
    11. }
    12. public RuleException(Throwable e) {
    13. super(e);
    14. }
    15. }

    参考:

    http://www.groovy-lang.org/integrating.html

    https://juejin.cn/post/6844903682639659015

    https://www.oschina.net/translate/new-groovy-20?cmp&p=2

  • 相关阅读:
    听书项目总结
    Mybatis常用代码
    包装类知识点
    21天学习挑战赛--第四天打卡(横竖屏切换)
    SSL知识讲解
    Springboot学生成绩管理系统idea开发mysql数据库web结构java编程计算机网页源码maven项目
    关于QTableWidget的it所占内存的释放问题
    HTTPS的加密原理
    全志G2D实现屏幕旋转,开机logo实现手动旋转。
    Python中的函数
  • 原文地址:https://blog.csdn.net/yzh_1346983557/article/details/126211611