easy Rule就是一个轻量级的规则引擎,它可以直接注入spring bean类,可以查看github上对它的介绍:GitHub - j-easy/easy-rules: The simple, stupid rules engine for Java
easy rules是一个简单而强大的java规则引擎,它有以下特性:
它主要包括几个主要的类或接口:Rule,RulesEngine,RuleListener,Facts
还有几个主要的注解:@Action,@Condition,@Fact,@Priority,@Rule
使用easy rules有两种方式:
目录
查看下源码:
public interface Rule extends Comparable<Rule> {
/**
* 默认的规则名称
*/
String DEFAULT_NAME = "rule";
/**
* 默认的规则描述
*/
String DEFAULT_DESCRIPTION = "description";
/**
* 默认的规则优先级
*/
int DEFAULT_PRIORITY = Integer.MAX_VALUE - 1;
getter and setter...
/**
* 规则引擎判断条件
* 如果提供的facts被应用到规则上返回true,否则返回false
*/
boolean evaluate(Facts facts);
/**
* 规则引擎判断条件返回true后,执行此方法
*/
void execute(Facts facts) throws Exception;
}
由上面代码可以知道:一个规则由名称、描述、优先级三个属性和判断、执行两个方法组成,实现Rule接口,和使用@Rule,@Condition,@Action,@Priority,@Fact注解的效果是一样的。
public interface RulesEngine {
/**
* 返回规则引擎的参数
*/
RulesEngineParameters getParameters();
/**
* 返回已注册的规则监听器的列表
*/
List<RuleListener> getRuleListeners();
/**
* 在给定的因素上开启所有已注册的规则
*/
void fire(Rules rules, Facts facts);
/**
* 检查规则和因素是否符合
*/
Map<Rule, Boolean> check(Rules rules, Facts facts);
}
RulesEngine负责检查和开启规则,同时可以得到规则引擎的参数和规则监听器列表
public interface RuleListener {
/**
* 规则条件判断之前的触发器
*/
boolean beforeEvaluate(Rule rule, Facts facts);
/**
* 规则条件判断之后的触发器
*/
void afterEvaluate(Rule rule, Facts facts, boolean evaluationResult);
/**
* 规则执行之前的触发器
*/
void beforeExecute(Rule rule, Facts facts);
/**
* 规则执行成功之后的触发器
*/
void onSuccess(Rule rule, Facts facts);
/**
* 规则执行失败之后的触发器
*/
void onFailure(Rule rule, Facts facts, Exception exception);
}
RuleListener 规则执行过程的监听器,可以监听规则是否触发,规则执行的结果
package org.jeasy.rules.api;
public interface RulesEngineListener {
//该方法用于判断规则 参数。在RuleListener之前执行
void beforeEvaluate(org.jeasy.rules.api.Rules rules, org.jeasy.rules.api.Facts facts);
// 规则执行成功后。在RuleListener之后执行
void afterExecute(org.jeasy.rules.api.Rules rules, org.jeasy.rules.api.Facts facts);
}
public class Facts implements Iterable<Map.Entry<String, Object>> {
private final Set<Fact<?>> facts = new HashSet<>();
/**
* 在工作空间放置一个因素
*/
public <T> void put(String name, T value) {
Objects.requireNonNull(name, "fact name must not be null");
Objects.requireNonNull(value, "fact value must not be null");
Fact<?> retrievedFact = getFact(name);
if (retrievedFact != null) {
remove(retrievedFact);
}
add(new Fact<>(name, value));
}
public <T> void add(Fact<T> fact) {
Objects.requireNonNull(fact, "fact must not be null");
Fact<?> retrievedFact = getFact(fact.getName());
if (retrievedFact != null) {
remove(retrievedFact);
}
facts.add(fact);
}
/**
* 删除因素
*/
public void remove(String factName) {
Objects.requireNonNull(factName, "fact name must not be null");
Fact<?> fact = getFact(factName);
if (fact != null) {
remove(fact);
}
}
/**
* 通过name得到因素
*/
@SuppressWarnings("unchecked")
public <T> T get(String factName) {
Objects.requireNonNull(factName, "fact name must not be null");
Fact<?> fact = getFact(factName);
if (fact != null) {
return (T) fact.getValue();
}
return null;
}
/**
* 以map形式返回因素
*/
public Map<String, Object> asMap() {
Map<String, Object> map = new HashMap<>();
for (Fact<?> fact : facts) {
map.put(fact.getName(), fact.getValue());
}
return map;
}
...
}
Facts就是一个hashmap,通过注解@Fact(String value),其中的value是map的key,可以拿到Facts中的value。
大致的步骤:
Rule weatherRule = new RuleBuilder()
.name("weather rule")
.description("if it rains then take an umbrella")
.when(facts -> facts.get("rain").equals(true))
.then(facts -> System.out.println("It rains, take an umbrella!"))
.build();
Rule weatherRule = new MVELRule()
.name("weather rule")
.description("if it rains then take an umbrella")
.when("rain == true")
.then("System.out.println(\"It rains, take an umbrella!\");");
@Rule(name = "weather rule", description = "if it rains then take an umbrella")
@Rule
public class FizzRule {
@Condition
public boolean isFizz(@Fact("number") Integer number) {
return number % 5 == 0;
}
@Action
public void printFizz() {
System.out.print("fizz");
}
@Priority
public int getPriority() {
return 1;
}
}
weather-rule.yml文件
name: "weather rule"
description: "if it rains then take an umbrella"
condition: "rain == true"
actions:
- "System.out.println(\"It rains, take an umbrella!\");"
解析yml规则文件
MVELRuleFactory ruleFactory = new MVELRuleFactory(new YamlRuleDefinitionReader());
Rule weatherRule = ruleFactory.createRule(new FileReader("weather-rule.yml"));
public class HighTemperatureCondition implements Condition {
@Override
public boolean evaluate(Facts facts) {
Integer temperature = facts.get("temperature");
return temperature > 25;
}
static HighTemperatureCondition itIsHot() {
return new HighTemperatureCondition();
}
}
public class DecreaseTemperatureAction implements Action {
@Override
public void execute(Facts facts) throws Exception {
System.out.println("It is hot! cooling air..");
Integer temperature = facts.get("temperature");
facts.put("temperature", temperature - 1);
}
static DecreaseTemperatureAction decreaseTemperature() {
return new DecreaseTemperatureAction();
}
}
Rule airConditioningRule = new RuleBuilder()
.name("air conditioning rule")
.when(itIsHot())
.then(decreaseTemperature())
.build();
加载规则到规则引擎,并注入参数触发规则
Facts facts = new Facts();
facts.put("rain", true);
// define rules
Rule weatherRule = ...
Rules rules = new Rules();
rules.register(weatherRule);
// fire rules on known facts
RulesEngine rulesEngine = new DefaultRulesEngine();
rulesEngine.fire(rules, facts);
下面给出自己的一个使用easy rule规则引擎调用业务类的例子:
1:封装 规则引擎
@Component
public class RuleUtil {
DefaultRulesEngine rulesEngine = null;
Facts facts = null;
Rules rules = null;
public RulesEngine getRuleEngine(){return rulesEngine;}
public Rules getRules() {
return rules;
}
public void setRules(Rules rules) {
this.rules = rules;
}
public Facts getFacts() {
return facts;
}
public void setFacts(Facts facts) {
this.facts = facts;
}
public void addFact(String name,Object vule){
facts.put(name,vule);
}
public void registRule(CvbRule cvbRule){
MVELRule rule = creatRule(cvbRule);
rules.register(rule);
}
public void deleteRule(CvbRule cvbRule) {
String ruleName = cvbRule.getCvbRuleName();
rules.unregister(ruleName);
}
public void putRule(Rules rules1,CvbRule cvbRule){
MVELRule rule = creatRule(cvbRule);
rules1.register(rule);
}
/**
* 初始化基本参数信息
*/
public void init() {
RulesEngineParameters parameters = new RulesEngineParameters();
//匹配第一个后面就不在匹配
//parameters.skipOnFirstAppliedRule(true);
parameters.skipOnFirstAppliedRule(false);
parameters.skipOnFirstFailedRule(false);
parameters.skipOnFirstFailedRule(false);
parameters.skipOnFirstNonTriggeredRule(false);
rulesEngine = new DefaultRulesEngine(parameters);
rulesEngine.registerRuleListener(myRuleListener);
// rulesEngine.registerRulesEngineListener(myRuleEngineListener);
facts = new Facts();
rules = new Rules();
}
/**
* 触发指定规则,并赋予参数
* @param rules1
* @param facts1
*/
public void triggerRule(Rules rules1,Facts facts1) {
rulesEngine.fire(rules1, facts1);
}
/**
* 触发规则引擎所有规则
*/
public void triggerAllRule(){rulesEngine.fire(rules, facts);}
/**
* 流式创建规则
* @param cvbRule
* @return
*/
public MVELRule creatRule(CvbRule cvbRule) {
MVELRule rule = new MVELRule().name(cvbRule.getCvbRuleName())
.description(cvbRule.getCvbRuleDesc())
.priority(RandomUtils.generateRandomNumber(5))
.when("mqService.doRuleConditions(conditionList,tmpParam)")
.then("mqService.doRuleOperation(cvbOperation,tmpParam)");
return rule;
}
}
可以看到上面我是直接注入的参数是业务类,Spring管理的业务类,并且直接调用业务类方法,并且传入参数。
具体的注入参数,以及业务类:
这里我写了一个线程池,通过阻塞队列来并发执行规则:
@Slf4j
@Component
public class Consumer implements Runnable {
@Autowired
private RuleUtil ruleUtil;
@Autowired
private MqService mqService;
private BlockingQueue queue;
public void setQueue(BlockingQueue queue) {
this.queue = queue;
}
private static AtomicLong count = new AtomicLong(0);
@Override
public void run() {
Lock lock = new ReentrantLock();
try {
boolean rs = lock.tryLock(10, TimeUnit.SECONDS);
if (rs) {
TmpParam message = JSONObject.parseObject(JSONObject.toJSONString(queue.poll(2, TimeUnit.SECONDS)), TmpParam.class);
log.info("----消费者拿到消息:{}----", message);
if (message.getData() != null) {
Long num = count.getAndIncrement();
Facts facts = new Facts();
//直接注入参数 在监听的地方根据规则注入 规则的条件 规则的操作
facts.put("tmpParam", message);
facts.put("mqService", mqService);
ruleUtil.setFacts(facts);
ruleUtil.triggerAllRule();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
log.info("-----消费者处理完成----");
lock.unlock();
}
}
}
我们通过上面注入的规则监听器去监听规则的执行情况 ,下面是我自己定义的监听类,实现类 RuleListener接口
public class MyRuleListener implements RuleListener {
@Override
public boolean beforeEvaluate(Rule rule, Facts facts) {
log.info("-----{}-----Condition之前的执行----", rule.getName());
String ruleName = rule.getName();
//拿到规则信息
//这里是可以对参数进行判断校验的
return true;
}
/**
* 规则条件判断之后的触发器
* b表示规则是否触发
*/
@Override
public void afterEvaluate(Rule rule, Facts facts, boolean b) {
log.info("----{} ---Condition执行之后 可以得到规则是否触发----", rule.getName());
if (b) {
log.info("----{} 规则被触发-----", rule.getName());
} else {
log.info("-----{} 规则没有被触发----", rule.getName());
}
}
/**
* 规则执行之前的触发器
*/
@Override
public void beforeExecute(Rule rule, Facts facts) {
log.info("-----{} ----在执行Action之前执行----", rule.getName());
}
/**
* 规则执行成功之后的触发器
*/
@Override
public void onSuccess(Rule rule, Facts facts) {
log.info("-----{}---- 规则Action执行完并且是成功的---", rule.getName());
String ruleName = rule.getName();
}
/**
* 规则执行失败之后的触发器
*/
@Override
public void onFailure(Rule rule, Facts facts, Exception e) {
log.info("-----{} ----Action执行失败 ----", rule.getName());
String ruleName = rule.getName();
}
}
public class MyRuleEngineListener implements RulesEngineListener {
@Override
public void beforeEvaluate(Rules rules, Facts facts) {
//先执行
log.info("----- 该方法用于判断规则 参数。在RuleListener之前执行----");
}
@Override
public void afterExecute(Rules rules, Facts facts) {
log.info("----- 规则执行成功后。在RuleListener之后执行 ----");
}
}
以上就是使用easy Rule 实现规则引擎,并通过监听接口监听规则使用结果情况,具体其他的用法大家可以参考github 上的源码,也可以定制自己的规则引擎。