本文主要研究下easy-rules。html
easy-rules是一款轻量级的规则引擎。
<dependency> <groupId>org.jeasy</groupId> <artifactId>easy-rules-core</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>org.jeasy</groupId> <artifactId>easy-rules-mvel</artifactId> <version>3.1.0</version> </dependency>
easy-rules首先集成了mvel表达式,后续可能集成SpEL
name: "alcohol rule" description: "children are not allowed to buy alcohol" priority: 2 condition: "person.isAdult() == false" actions: - "System.out.println(\"Shop: Sorry, you are not allowed to buy alcohol\");"
//create a person instance (fact) Person tom = new Person("Tom", 14); Facts facts = new Facts(); facts.put("person", tom); MVELRule alcoholRule = MVELRuleFactory.createRuleFrom(new File(getClass().getClassLoader().getResource("alcohol-rule.yml").getFile())); // create a rule set Rules rules = new Rules(); rules.register(alcoholRule); //create a default rules engine and fire rules on known facts RulesEngine rulesEngine = new DefaultRulesEngine(); System.out.println("Tom: Hi! can I have some Vodka please?"); rulesEngine.fire(rules, facts);
@Rule public class BuzzRule { @Condition public boolean isBuzz(@Fact("number") Integer number) { return number % 7 == 0; } @Action public void printBuzz() { System.out.println("buzz"); } @Priority public int getPriority() { return 2; } }
easy-rules-core-3.1.0-sources.jar!/org/jeasy/rules/api/Rule.javajava
/** * Abstraction for a rule that can be fired by the rules engine. * * Rules are registered in a rule set of type <code>Rules</code> in which they must have a <strong>unique</strong> name. * * @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com) */ public interface Rule extends Comparable<Rule> { /** * Default rule name. */ String DEFAULT_NAME = "rule"; /** * Default rule description. */ String DEFAULT_DESCRIPTION = "description"; /** * Default rule priority. */ int DEFAULT_PRIORITY = Integer.MAX_VALUE - 1; /** * Getter for rule name. * @return the rule name */ String getName(); /** * Getter for rule description. * @return rule description */ String getDescription(); /** * Getter for rule priority. * @return rule priority */ int getPriority(); /** * Rule conditions abstraction : this method encapsulates the rule's conditions. * <strong>Implementations should handle any runtime exception and return true/false accordingly</strong> * * @return true if the rule should be applied given the provided facts, false otherwise */ boolean evaluate(Facts facts); /** * Rule actions abstraction : this method encapsulates the rule's actions. * @throws Exception thrown if an exception occurs during actions performing */ void execute(Facts facts) throws Exception; }
实现这个接口,也是建立rule的一种形式。
easy-rules-core-3.1.0-sources.jar!/org/jeasy/rules/api/Rules.javagit
/** * Register a new rule. * * @param rule to register */ public void register(Object rule) { Objects.requireNonNull(rule); rules.add(RuleProxy.asRule(rule)); }
这里使用RuleProxy.asRule方法
easy-rules-core-3.1.0-sources.jar!/org/jeasy/rules/core/RuleProxy.javagithub
/** * Makes the rule object implement the {@link Rule} interface. * * @param rule the annotated rule object. * @return a proxy that implements the {@link Rule} interface. */ public static Rule asRule(final Object rule) { Rule result; if (rule instanceof Rule) { result = (Rule) rule; } else { ruleDefinitionValidator.validateRuleDefinition(rule); result = (Rule) Proxy.newProxyInstance( Rule.class.getClassLoader(), new Class[]{Rule.class, Comparable.class}, new RuleProxy(rule)); } return result; }
能够看到,若是有实现Rule接口,则直接返回,没有的话(
即基于注解的形式
),则利用JDK的动态代理进行包装。
@Override public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { String methodName = method.getName(); switch (methodName) { case "getName": return getRuleName(); case "getDescription": return getRuleDescription(); case "getPriority": return getRulePriority(); case "compareTo": return compareToMethod(args); case "evaluate": return evaluateMethod(args); case "execute": return executeMethod(args); case "equals": return equalsMethod(args); case "hashCode": return hashCodeMethod(); case "toString": return toStringMethod(); default: return null; } }
能够看到这里invoke对方法进行了适配
下面以getName为例看下如何根据注解来返回express
private String getRuleName() { org.jeasy.rules.annotation.Rule rule = getRuleAnnotation(); return rule.name().equals(Rule.DEFAULT_NAME) ? getTargetClass().getSimpleName() : rule.name(); }
能够看到这里对注解进行了解析
从本质上看,规则引擎的目的就是要以松散灵活的方式来替代硬编码式的if else判断,来达到解耦的目的,不过实际场景要额外注意规则表达式的安全问题。api