规则引擎适合于做业务规则频繁变化的场景,我们的业务在应用过程中,也经常要处理大量的业务规则,当然,也希望能有一套规则引擎来支撑,这样是再好不过的。
对一些常用的商业规则引擎做一下了解,感觉非常不错,但是太贵了。看一些开源的引擎吧,也不错,但是感觉相对于我们自己这么简单的需求,太复杂了。
于是就想着自己做个,试试看能不能解决了自己的这些简单的业务规则频繁变化的业务场景,嗯嗯,脑子里大概过了一下电影,感觉路是通的,主要有如下业务需求:
- 业务规则执行器需要支持多种,也应该支持业务人员自行扩展,原因是我自己设计的业务规则再完美,也不可能完美的适应所有人的胃口,所以这个默认可以有支持的,但是一定是可以扩展的
- 业务规则要支持优先级,也就是说有的业务规则先执行,有的业务规则后执行
- 业务规则允许排他规则,也就是说只要执行到排他规则,就可以马上结束
- 业务规则可以允许重复执行,这样才可以方便的进行循环处理
- 在规则引擎中,可以方便的使用Spring中的业务对象
于是就可以开始设计了:
规则执行器接口
由于业务规则执行器需要支持扩展,当然需要设计一个接口了:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
/**
* 规则执行器,可以有多种实现
*/
public
interface
RuleExecutor<T
extends
Rule> {
/**
* 返回执行器类型
*
* @return
*/
String getType();
/**
* 执行规则,并把结果放到上下文上
*
* @param context
* @return 返回条件是否成立
*/
boolean
execute(Context context, T rule);
}
|
execute方法用来执行规则,执行的结果是一个布尔值,表示此条规则是否有执行。
规则引擎接口
接下来就是设计规则引擎的接口了:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
public
interface
RuleEngine {
/**
* 对指定上下文执行指定类型的规则
*
* @param context
* @param ruleSetName
*/
void
execute(Context context, String ruleSetName);
/**
* 添加一组规则
*
* @param ruleSet
*/
void
addRules(RuleSet ruleSet);
/**
* 删除一组规则
*
* @param ruleSet
*/
void
removeRules(RuleSet ruleSet);
/**
* 添加规则执行器列表
*
* @param ruleExecutors
*/
void
addRuleExecutors(List<RuleExecutor> ruleExecutors);
/**
* 添加一个规则执行器
*
* @param ruleExecutor
*/
void
addRuleExecutor(RuleExecutor ruleExecutor);
/**
* 删除规则执行器列表
*
* @param ruleExecutors
*/
void
removeRuleExecutors(List<RuleExecutor> ruleExecutors);
/**
* 删除一个规则执行器
*
* @param ruleExecutor
*/
void
removeRuleExecutor(RuleExecutor ruleExecutor);
/**
* 设置一批规则执行器
* @param ruleExecutors
*/
void
setRuleExecutors(List<RuleExecutor> ruleExecutors);
}
|
execute用来执行一个规则集,其它的方法就是对规则集和规则执行器的管理,只要看一遍就一清二楚了。
规则集对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
@XStreamAlias
(
"rule-set"
)
public
class
RuleSet {
/**
* 同种名称的规则集会自动合并
*/
@XStreamAsAttribute
private
String name;
@XStreamImplicit
private
List<Rule> rules;
public
String getName() {
return
name;
}
public
void
setName(String name) {
this
.name = name;
}
public
List<Rule> getRules() {
if
(rules==
null
){
rules =
new
ArrayList<Rule>();
}
return
rules;
}
public
void
setRules(List<Rule> rules) {
this
.rules = rules;
}
}
|
规则抽象类
根据上面的业务需求,抽象类Rule的结构如下:
它里面只有基本的几个属性:优先级,标识,是否排他,是否允许重复,描述,标题,类型,有效性。
说好的业务规则呢,怎么描述?
由于不同的规则执行器,它可以支持的规则也不一样,因此这里的规则抽象类只有基本的一些属性,怎么样描述规则由其子类决定。
MVEL方式的规则及其执行器
Mvel规则
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
/**
* 采用MVEL表达式作为条件实现
* @author yancheng11334
*
*/
@XStreamAlias
(
"mvel-rule"
)
public
class
MvelRule
extends
Rule{
//匹配条件
private
String condition;
//后续操作
private
String action;
public
String getCondition() {
return
condition;
}
public
void
setCondition(String condition) {
this
.condition = condition;
}
public
String getAction() {
return
action;
}
public
void
setAction(String action) {
this
.action = action;
}
public
String getType(){
return
"mvel"
;
}
public
String toString() {
return
"MvelRule [condition="
+ condition +
", action="
+ action
+
", type="
+ getType() +
", id="
+ getId() +
", priority="
+ getPriority() +
", multipleTimes="
+isMultipleTimes()+
",exclusive="
+isExclusive()+
"]"
;
}
/**
* 验证mvel规则的合法性
*/
public
boolean
isVaild() {
if
(StringUtil.isEmpty(getCondition())){
throw
new
RuntimeException(String.format(
"规则[%s]的匹配条件为空"
, getId()));
}
if
(StringUtil.isEmpty(getAction())){
throw
new
RuntimeException(String.format(
"规则[%s]的后续操作为空"
, getId()));
}
return
true
;
}
}
|
Mvel规则执行器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
|
public
class
MvelRuleExecutor
implements
RuleExecutor<MvelRule>{
private
EL el;
public
EL getEl() {
return
el;
}
public
void
setEl(EL el) {
this
.el = el;
}
public
String getType() {
return
"mvel"
;
}
public
boolean
execute(Context context, MvelRule rule) {
try
{
if
(executeCondition(rule.getCondition(),context)){
executeAction(rule.getAction(),context);
return
true
;
}
else
{
return
false
;
}
}
catch
(RuntimeException e){
throw
e;
}
catch
(Exception e){
throw
new
RuntimeException(
"Mvel规则引擎执行器发生异常:"
,e);
}
}
/**
* 判断条件是否匹配
* @param condition
* @param context
* @return
*/
protected
boolean
executeCondition(String condition,Context context){
try
{
MvelContext mvelContext=
null
;
if
(context
instanceof
MvelContext){
mvelContext=(MvelContext) context;
}
else
{
mvelContext=
new
MvelContext(context);
}
return
(Boolean)el.execute(condition, mvelContext);
}
catch
(Exception e){
throw
new
RuntimeException(String.format(
"条件[%s]匹配发生异常:"
, condition),e);
}
}
/**
* 执行条件匹配后的操作
* @param action
* @param context
*/
protected
void
executeAction(String action,Context context) {
try
{
MvelContext mvelContext =
null
;
if
(context
instanceof
MvelContext) {
mvelContext = (MvelContext) context;
}
else
{
mvelContext =
new
MvelContext(context);
}
el.execute(action, mvelContext);
}
catch
(Exception e) {
throw
new
RuntimeException(String.format(
"后续操作[%s]执行发生异常:"
, action), e);
}
}
}
|
呵呵,这个逻辑也太简单了。对,tiny框架的一大特点就是用非常简单的逻辑来实现相对复杂的处理。
Mvel上下文
前面讲到,要方便的在表达式中调用Spring中托管的对象,这个的实现就要从上下文上作文章了:
1
2
3
4
5
6
7
8
9
10
|
public
<T> T get(String name) {
if
(context.exist(name)){
return
(T)context.get(name);
}
else
{
//必须保存到上下文,否则每次返回不一定是同一个对象(scope可能是属性)
T t = (T)beanContainer.getBean(name);
context.put(name, t);
return
t;
}
}
|
规则引擎实现类
到上面为止,相关的准备工作都就绪了,规则引擎的实现类也可以现身了。其实这个类不贴吧,看文章的同学们一定说我藏着掖着,但是贴出来吧,真的没有啥技术含量:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|