参考原文A Really Simple But Powerful Rule Engine
MVEL 介绍
最近正好有做规则引擎相关的需求,预研了一下,并找到一些可以满足我需求的技术。引入一个东西——MVEL,怎么定义MVEL表达式,参看MVEL Guide。
来一段代码看一下MVEL的功能,首先引入MVEL解析库:
compile
'org.mvel:mvel2:2.3.2.Final'
下图显示的是本文所用到的Java Bean类:
MVEL example:
@Test
public void testMvel() {
String rule1 =
"(!(input.person.age < 26) && !(input.person.age > 59)) && input.account.ageInMonths > 24"
// compile表达式 异常表达式会报错
Serializable serializable = MVEL
.compileExpression(rule1)
TarifRequest request = new TarifRequest()
request
.setPerson(new Person())
request
.setAccount(new Account())
request
.getPerson()
.setAge(
30)
request
.getPerson()
.setName(
"jack")
request
.getAccount()
.setAgeInMonths(
25)
Map<String, TarifRequest> vars = new HashMap<>()
vars
.put(
"input", request)
boolean res = (boolean) MVEL
.executeExpression(serializable, vars)
System
.out.println(res)
String rule2 =
"input.person.name == 'tom'"
boolean res1 = (boolean) MVEL
.executeExpression(MVEL
.compileExpression(rule2), vars)
System
.out.println(res1)
String rule3 =
"['jack', 'tom', 'hanson'].contains(input.person.name)"
boolean res2 = (boolean) MVEL
.executeExpression(MVEL
.compileExpression(rule3), vars)
System
.out.println(res2)
// MVEL
.eval() 直接计算出表达式的值
System
.out.println(MVEL
.eval(rule2, vars))
}
基于MVEL的 简单规则引擎
github源码地址 引入依赖:
compile
'ch.maxant:rules:2.2.1'
1. 执行规则
这个规则引擎比较简单,我居然都能看懂里面的源码,看测试代码:
@Test
public void testRules()
throws Exception {
Rule r1 =
new Rule(
"one",
"input.person.age < 26",
"YT2011",
3,
"ch.maxant.someapp.tarifs",
null);
Rule r2 =
new Rule(
"two",
"input.person.age > 59",
"ST2011",
3,
"ch.maxant.someapp.tarifs",
null);
Rule r3 =
new Rule(
"three",
"!#one && !#two",
"DT2011",
3,
"ch.maxant.someapp.tarifs",
null);
Rule r4 =
new Rule(
"four",
"#three && input.account.ageInMonths > 24",
"LT2011",
4,
"ch.maxant.someapp.tarifs",
null);
Rule defaultRule =
new Rule(
"five",
"true",
"default", -
1,
"ch.maxant.someapp.tarifs",
null);
List<Rule> rules = Arrays.asList(r1, r2, r3, r4, defaultRule);
Engine engine =
new Engine(rules,
true);
TarifRequest request =
new TarifRequest();
request.setPerson(
new Person());
request.setAccount(
new Account());
request.setDefaultRuleFlag(
true);
request.getPerson().setAge(
30);
request.getAccount().setAgeInMonths(
5);
String tarif = engine.getBestOutcome(request);
System.out.println(tarif);
}
执行大概流程图如下: 当没有规则匹配的时候,就返回默认规则的结果。
2. 使用SubRule编写更复杂的规则
@Test
public void testSubRules()
throws Exception {
Rule rule1 =
new SubRule(
"longdistance",
"input.distance > 100",
"ch.maxant.produkte",
null);
Rule rule2 =
new SubRule(
"firstclass",
"input.map[\"travelClass\"] == 1",
"ch.maxant.produkte",
null);
Rule rule3 =
new Rule(
"productA",
"#longdistance && #firstclass",
"productA",
3,
"ch.maxant.produkte",
null);
List<Rule> rules = Arrays.asList(rule1, rule2, rule3);
Engine e =
new Engine(rules,
true);
TravelRequest request =
new TravelRequest(
150);
request.put(
"travelClass",
1);
List<Rule> rs = e.getMatchingRules(request);
System.out.println(rs);
}
规则引擎在解析的时候,SubRule的规则都将被丢弃,只执行rule3的规则,rule3被替换成了:
Rule rule3 =
new Rule(
"productA",
"(input.distance > 100) && (input.map["travelClass
"] == 1)",
"productA",
3,
"ch.maxant.produkte",
null);
3. 根据规则执行具体的操作
@Test
public void testRulesAction() throws Exception {
Rule r1
= new Rule(
"SendEmailToUser",
"input.config.sendUserEmail == true",
"SendEmailToUser",
1,
"ch.maxant.someapp.config",
null);
Rule r2
= new Rule(
"SendEmailToModerator",
"input.config.sendAdministratorEmail == true and input.user.numberOfPostings < 5",
"SendEmailToModerator",
2,
"ch.maxant.someapp.config",
null);
List<Rule
> rules
= Arrays
.asList(r1, r2);
final
List<String> log = new ArrayList
<>();
AbstractAction
<ForumSetup,
Void> a1
= new AbstractAction
<ForumSetup,
Void>(
"SendEmailToUser") {
@Override
public Void execute(ForumSetup input) {
log.add(
"Sending email to user!");
return null;
}
};
AbstractAction
<ForumSetup,
Void> a2
= new AbstractAction
<ForumSetup,
Void>(
"SendEmailToModerator") {
@Override
public Void execute(ForumSetup input) {
log.add(
"Sending email to moderator!");
return null;
}
};
Engine engine
= new Engine(rules,
true);
ForumSetup setup
= new ForumSetup();
Config config
= new Config();
setup
.setConfig(config);
User user
= new User();
setup
.setUser(user);
setup
.getConfig()
.setSendUserEmail(
true);
setup
.getConfig()
.setSendAdministratorEmail(
true);
setup
.getUser()
.setNumberOfPostings(
2);
engine
.executeAllActions(setup, Arrays
.asList(a1, a2));
执行操作也挺简单的,rule的outcome和action的name是一一对应的关系,从匹配到的规则里获取outcome,然后通过outcome获取action,最后action执行具体的操作,这个逻辑没毛病,但感觉rule和action有辣么一点耦合关系,有值得改进的地方,等大伙看源码去深挖吧。
其他的规则引擎: easy-rules Rulette