具体的代码参照 示例项目 https://github.com/qihaiyan/springcamp/tree/main/spring-easy-rule
一、概述
通过将业务规则配置的配置文件中,可以精简代码,同时已于维护,当规则修改时,只需要修改配置文件即可。easy-rules是一个小巧的规则引擎,支持spring的SPEL表达式,同时还支持 Apache JEXL 表达式和 MVL 表达式。
二、项目中加入依赖
在项目的gradle中增加依赖关系。
build.gradle:
plugins {
<span>id</span> <span>'org.springframework.boot'</span> version <span>'3.0.5'</span>
<span>id</span> <span>'io.spring.dependency-management'</span> version <span>'1.1.0'</span>
<span>id</span> <span>'java'</span>
}
group = <span>'cn.springcamp'</span>
version = <span>'0.0.1-SNAPSHOT'</span>
sourceCompatibility = <span>'17'</span>
configurations {
compileOnly {
extendsFrom annotationProcessor
}
testCompileOnly {
extendsFrom testAnnotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation <span>"org.springframework.boot:spring-boot-starter-json"</span>
implementation <span>'org.springframework.boot:spring-boot-starter-validation'</span>
implementation <span>'org.jeasy:easy-rules-core:4.1.0'</span>
implementation <span>'org.jeasy:easy-rules-spel:4.1.0'</span>
implementation <span>'org.jeasy:easy-rules-support:4.1.0'</span>
annotationProcessor <span>'org.projectlombok:lombok'</span>
testAnnotationProcessor <span>'org.projectlombok:lombok'</span>
testImplementation <span>"org.springframework.boot:spring-boot-starter-test"</span>
testImplementation <span>'org.junit.vintage:junit-vintage-engine'</span>
testImplementation <span>'org.junit.vintage:junit-vintage-engine'</span>
}
dependencyManagement {
imports {
mavenBom <span>"org.springframework.cloud:spring-cloud-dependencies:2022.0.1"</span>
}
}
<span>test</span> {
useJUnitPlatform()
}
三、配置文件
示例程序将业务规则放到配置文件中,业务规则配置文件(demo-rule.yml)代码:
<span>name:</span> <span>"age rule"</span>
<span>description:</span> <span>""</span>
<span>priority:</span> <span>1</span>
<span>condition:</span> <span>"#person.getAdult() == false"</span>
<span>actions:</span>
- <span>"T(java.lang.System).out.println("</span>Shop: Sorry, you are <span>not</span> allowed<span>")"</span>
- <span>"#person.setAdult(true)"</span>
- <span>"#person.setAge(18)"</span>
---
<span>name:</span> <span>"alcohol rule"</span>
<span>description:</span> <span>""</span>
<span>priority:</span> <span>1</span>
<span>condition:</span> <span>"#person.getAdult() == true"</span>
<span>actions:</span>
- <span>"T(java.lang.System).out.println("</span>Shop: you are now allowed<span>")"</span>
配置文件中的规则通过 condition 进行配置,当满足规则时,会调用 actions 中配置的动作。 示例项目使用了spring的SPEL表达式进行规则配置,配置文件中配置了2个规则,第一个规则通过 person 这个spring bean中的getAdult()判断是否满足规则,满足规则时调用三个方法。
在spring-boot本身的配置文件中 application.yml 配置规则文件:
<span>rule:</span>
<span>skip</span>-<span>on</span>-first-failed-rule: <span>true</span>
<span>skip</span>-<span>on</span>-first-applied-rule: <span>false</span>
<span>skip</span>-<span>on</span>-first-non-triggered-rule: <span>true</span>
rules:
- rule-id: <span>"demo"</span>
rule-file-location: <span>"classpath:demo-rule.yml"</span>
四、代码中对规则引擎进行配置
通过 RuleEngineConfig 这个spring的配置类对规则引擎进行配置:
<span>@Slf4j</span>
<span>@EnableConfigurationProperties</span>(<span>RuleEngineConfigProperties</span>.<span>class</span>)
<span>@Configuration</span>
<span>public</span> <span>class</span> <span>RuleEngineConfig</span> <span>implements</span> <span>BeanFactoryAware</span> {
<span>@Autowired</span>(required = <span>false</span>)
<span>private</span> <span>List</span><<span>RuleListener</span>> ruleListeners;
<span>@Autowired</span>(required = <span>false</span>)
<span>private</span> <span>List</span><<span>RulesEngineListener</span>> rulesEngineListeners;
<span>private</span> <span>BeanFactory</span> beanFactory;
<span>@Bean</span>
<span>public</span> <span>RulesEngineParameters</span> <span>rulesEngineParameters</span>(<span>RuleEngineConfigProperties properties</span>) {
<span>RulesEngineParameters</span> parameters = <span>new</span> <span>RulesEngineParameters</span>();
parameters.<span>setSkipOnFirstAppliedRule</span>(properties.<span>isSkipOnFirstAppliedRule</span>());
parameters.<span>setSkipOnFirstFailedRule</span>(properties.<span>isSkipOnFirstFailedRule</span>());
parameters.<span>setSkipOnFirstNonTriggeredRule</span>(properties.<span>isSkipOnFirstNonTriggeredRule</span>());
<span>return</span> parameters;
}
<span>@Bean</span>
<span>public</span> <span>RulesEngine</span> <span>rulesEngine</span>(<span>RulesEngineParameters rulesEngineParameters</span>) {
<span>DefaultRulesEngine</span> rulesEngine = <span>new</span> <span>DefaultRulesEngine</span>(rulesEngineParameters);
<span>if</span> (!<span>CollectionUtils</span>.<span>isEmpty</span>(ruleListeners)) {
rulesEngine.<span>registerRuleListeners</span>(ruleListeners);
}
<span>if</span> (!<span>CollectionUtils</span>.<span>isEmpty</span>(rulesEngineListeners)) {
rulesEngine.<span>registerRulesEngineListeners</span>(rulesEngineListeners);
}
<span>return</span> rulesEngine;
}
<span>@Bean</span>
<span>public</span> <span>BeanResolver</span> <span>beanResolver</span>(<span></span>) {
<span>return</span> <span>new</span> <span>BeanFactoryResolver</span>(beanFactory);
}
<span>@Bean</span>
<span>public</span> <span>RuleEngineTemplate</span> <span>ruleEngineTemplate</span>(<span>RuleEngineConfigProperties properties, RulesEngine rulesEngine</span>) {
<span>RuleEngineTemplate</span> ruleEngineTemplate = <span>new</span> <span>RuleEngineTemplate</span>();
ruleEngineTemplate.<span>setBeanResolver</span>(<span>beanResolver</span>());
ruleEngineTemplate.<span>setProperties</span>(properties);
ruleEngineTemplate.<span>setRulesEngine</span>(rulesEngine);
<span>return</span> ruleEngineTemplate;
}
<span>@Bean</span>
<span>public</span> <span>RuleListener</span> <span>defaultRuleListener</span>(<span></span>) {
<span>return</span> <span>new</span> <span>RuleListener</span>() {
<span>@Override</span>
<span>public</span> <span>boolean</span> <span>beforeEvaluate</span>(<span>Rule rule, Facts facts</span>) {
<span>return</span> <span>true</span>;
}
<span>@Override</span>
<span>public</span> <span>void</span> <span>afterEvaluate</span>(<span>Rule rule, Facts facts, <span>boolean</span> b</span>) {
log.<span>info</span>(<span>"-----------------afterEvaluate-----------------"</span>);
log.<span>info</span>(rule.<span>getName</span>() + rule.<span>getDescription</span>() + facts.<span>toString</span>());
}
<span>@Override</span>
<span>public</span> <span>void</span> <span>beforeExecute</span>(<span>Rule rule, Facts facts</span>) {
log.<span>info</span>(<span>"-----------------beforeExecute-----------------"</span>);
log.<span>info</span>(rule.<span>getName</span>() + rule.<span>getDescription</span>() + facts.<span>toString</span>());
}
<span>@Override</span>
<span>public</span> <span>void</span> <span>onSuccess</span>(<span>Rule rule, Facts facts</span>) {
log.<span>info</span>(<span>"-----------------onSuccess-----------------"</span>);
log.<span>info</span>(rule.<span>getName</span>() + rule.<span>getDescription</span>() + facts.<span>toString</span>());
}
<span>@Override</span>
<span>public</span> <span>void</span> <span>onFailure</span>(<span>Rule rule, Facts facts, Exception e</span>) {
log.<span>info</span>(<span>"-----------------onFailure-----------------"</span>);
log.<span>info</span>(rule.<span>getName</span>() + <span>"----------"</span> + rule.<span>getDescription</span>() + facts.<span>toString</span>() + e.<span>toString</span>());
}
};
}
<span>@Override</span>
<span>public</span> <span>void</span> <span>setBeanFactory</span>(<span>BeanFactory</span> beanFactory) throws <span>BeansException</span> {
<span>this</span>.<span>beanFactory</span> = beanFactory;
}
}
配置文件中配置了 ruleEngineTemplate 这个spring bean,通过ruleEngineTemplate触发规则引擎的执行。
五、执行规则引擎
ruleEngineTemplate 配置好后,我们可以在业务代码中执行规则引擎,处理配置文件中配置的业务规则:
最为演示,我们将规则引擎的执行代码放到了 Application 的 run 方法中,程序启动后立即执行规则引擎:
<span>@SpringBootApplication</span>
<span>public</span> <span>class</span> <span>Application</span> <span>implements</span> <span>CommandLineRunner</span> {
<span>@Autowired</span>
<span>RuleEngineTemplate</span> ruleEngineTemplate;
<span>public</span> <span>static</span> <span>void</span> <span>main</span>(<span><span>String</span>[] args</span>) {
<span>SpringApplication</span>.<span>run</span>(<span>Application</span>.<span>class</span>, args);
}
<span>@Override</span>
<span>public</span> <span>void</span> <span>run</span>(<span><span>String</span>... args</span>) {
<span>Person</span> person = <span>new</span> <span>Person</span>();
<span>Facts</span> facts = <span>new</span> <span>Facts</span>();
facts.<span>put</span>(<span>"person"</span>, person);
ruleEngineTemplate.<span>fire</span>(<span>"demo"</span>, facts);
}
}
程序执行后可以看到控制台里打印了 Shop: Sorry, you are not allowed,这个内容对应的是我们在规则文件中的actions中配置的 "T(java.lang.System).out.println("Shop: Sorry, you are not allowed")" ,说明规则成功执行了。
轻量、易集成、配置化驱动,适合 Spring 项目中的简单规则场景;若需要决策表、复杂流控或大规模规则治理,仍建议评估 Drools 等重型方案。