本文主要研究下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