前言:
如果对drools还不是特别熟悉的,可以看下 《规则引擎Drools 之 初识drools》这篇文章;
本文源码,github 传送门:https://github.com/vincent9309/drools:
系统架构如下:
一、项目搭建流程
二、项目目录结构
三、springboot集成drools
1.pom文件引入依赖
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-core</artifactId>
<version>7.0.0.Final</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-compiler</artifactId>
<version>7.0.0.Final</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-decisiontables</artifactId>
<version>7.0.0.Final</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-templates</artifactId>
<version>7.0.0.Final</version>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-api</artifactId>
<version>7.0.0.Final</version>
</dependency>
2. 在src/main/resources/ruls 新建规则文件:
package plausibcheck.adress
import com.neo.drools.model.Address;
import com.neo.drools.model.fact.AddressCheckResult;
rule "[address] Postcode should be filled with exactly 5 numbers"
agenda-group "address" // rule 分组
// activation-group "address" //group里面只能执行一个rule
// no-loop true // 避免循环调用
// lock-on-active true //ruleflow-group属性或agenda-group属性的时候, 避免循环调用
// salience 3 //权重
// dialect "java" //方言
// date-effective "20-Jul-2018" //生效日期
// date-expires "21-Jul-2019" //失效日期
// enabled true //是否可用
when
address : Address(postcode != null, postcode matches "([0-9]{5})")
checkResult : AddressCheckResult();
then
checkResult.setPostCodeResult(true);
System.out.println("[address]规则中打印日志:校验通过!");
end
rule "[copy-address] --> Postcode should be filled with exactly 5 numbers"
agenda-group "address"
activation-group "address"
no-loop true
lock-on-active true
salience 1
when
address : Address(postcode != null, postcode matches "([0-9]{5})")
checkResult : AddressCheckResult();
then
checkResult.setPostCodeResult(true);
System.out.println("[copy-address]规则中打印日志:校验通过!");
end
3、src/main/resources/META-INF新建配置文件kmodule.xml:
<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.drools.org/xsd/kmodule">
<kbase name="address-testKB" packages="rules">
<ksession name="address-testKS"/>
</kbase>
<kbase name="addressKB" packages="rules">
<ksession name="addressKS"/>
</kbase>
<kbase name="rules" packages="rules">
<ksession name="ksession-rule"/>
</kbase>
</kmodule>
4.创建tKieUitl
public class KieUtils {
private static KieContainer kieContainer;
private static KieSession kieSession;
public static KieContainer getKieContainer() {
return kieContainer;
}
public static void setKieContainer(KieContainer kieContainer) {
KieUtils.kieContainer = kieContainer;
kieSession = kieContainer.newKieSession();
}
public static KieSession getKieSession() {
return kieSession;
}
public static void setKieSession(KieSession kieSession) {
KieUtils.kieSession = kieSession;
}
public static KieServices getKieServices() {
return KieServices.Factory.get();
}
}
5.通过restful API即可调用
@RequestMapping("/address-test")
public void addressTest(int num){
Address address = new Address();
address.setPostcode(generateRandom(num));
KieServices ks = KieServices.Factory.get();
KieContainer kc = ks.getKieClasspathContainer();
KieSession kieSession = kc.newKieSession("address-testKS");
//选择agenda-group
kieSession.getAgenda().getAgendaGroup("address-test").setFocus();
//增加自定义AgendaFilter
AgendaFilter filter = new MyAgendaFilter("[Address-Test]");
AddressCheckResult result = new AddressCheckResult();
FactHandle faceHandle = kieSession.insert(address);
kieSession.insert(result);
//更新work memory 中的对象信息
// address.setPostcode("123");
// kieSession.update(faceHandle,address);
int ruleFiredCount = kieSession.fireAllRules(filter);
kieSession.destroy();
System.out.println("触发了" + ruleFiredCount + "条规则");
if(result.isPostCodeResult()){
System.out.println("规则校验通过");
}
}
/**
* 生成随机数
* @param num
* @return
*/
public String generateRandom(int num) {
String chars = "0123456789";
StringBuffer number=new StringBuffer();
for (int i = 0; i < num; i++) {
int rand = (int) (Math.random() * 10);
number=number.append(chars.charAt(rand));
}
return number.toString();
}
三、springboot热刷新drools 规则文件
1.增加config配置
@Configuration
public class DroolsAutoConfig {
public static final String RULES_PATH = "rules/";
public static final String BASE_RULES_PATH = "classpath*:";
@Bean
@ConditionalOnMissingBean(KieFileSystem.class)
public KieFileSystem kieFileSystem() throws IOException {
KieFileSystem kieFileSystem = getKieServices().newKieFileSystem();
for (Resource file : getRuleFiles()) {
kieFileSystem.write(ResourceFactory.newClassPathResource(RULES_PATH + file.getFilename(), "UTF-8"));
}
return kieFileSystem;
}
private Resource[] getRuleFiles() throws IOException {
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
return resourcePatternResolver.getResources(BASE_RULES_PATH + RULES_PATH + "**/*.*");
}
@Bean
@ConditionalOnMissingBean(KieContainer.class)
public KieContainer kieContainer() throws IOException {
final KieRepository kieRepository = getKieServices().getRepository();
kieRepository.addKieModule(new KieModule() {
public ReleaseId getReleaseId() {
return kieRepository.getDefaultReleaseId();
}
});
KieBuilder kieBuilder = getKieServices().newKieBuilder(kieFileSystem());
kieBuilder.buildAll();
KieContainer kieContainer = getKieServices().newKieContainer(kieRepository.getDefaultReleaseId());
KieUtils.setKieContainer(kieContainer);
return getKieServices().newKieContainer(kieRepository.getDefaultReleaseId());
}
private KieServices getKieServices() {
return KieServices.Factory.get();
}
@Bean
@ConditionalOnMissingBean(KieBase.class)
public KieBase kieBase() throws IOException {
return kieContainer().getKieBase();
}
@Bean
@ConditionalOnMissingBean(KieSession.class)
public KieSession kieSession() throws IOException {
KieSession kieSession = kieContainer().newKieSession();
KieUtils.setKieSession(kieSession);
return kieSession;
}
@Bean
@ConditionalOnMissingBean(KModuleBeanFactoryPostProcessor.class)
public KModuleBeanFactoryPostProcessor kiePostProcessor() {
return new KModuleBeanFactoryPostProcessor();
}
}
2. 增加两个API接口,一个是更新规则文件(此处就不详细写常规的上传功能);另一个是根据文件刷新规则
@RequestMapping("/reload")
public String reload(String drlName) throws Exception {
rules.reload(drlName);
return "ok";
}
@RequestMapping("/updateDrlFile")
public String updateDrlFile(MultipartFile file) throws Exception {
//更新drl后,再调用reload方法重载。即可热部署
return "ok";
}
3. 具体看service 实现 (此处也可以读取数据库,刷新规则)
@Service
public class ReloadDroolsRules {
public void reload(String drlName) throws Exception {
KieServices kieServices = KieUtils.getKieServices();
KieFileSystem kfs = kieServices.newKieFileSystem();
loadDBRules(drlName, kfs);
// loadFileRules(drlName, kfs);
KieBuilder kieBuilder = kieServices.newKieBuilder(kfs).buildAll();
Results results = kieBuilder.getResults();
if (results.hasMessages(Message.Level.ERROR)) {
System.out.println(results.getMessages());
throw new IllegalStateException("### errors ###");
}
KieUtils.setKieContainer(kieServices.newKieContainer(KieUtils.getKieServices().getRepository().getDefaultReleaseId()));
System.out.println("新规则重载成功");
}
private void loadDBRules(String drlName, KieFileSystem kfs) {
// String path = "src/main/resources/rules/address.drl";
String path = "src/main/resources/"+ DroolsAutoConfig.RULES_PATH + "/"+ drlName + ".drl";
// 从数据库加载的规则
kfs.write(path, "package plausibcheck.adress\n\n import com.neo.drools.model.Address;\n import com.neo.drools.model.fact.AddressCheckResult;\n\n rule \"Postcode 6 numbers\"\n\n when\n then\n System.out.println(\"打印日志:更新rules成功!\");\n end");
}
private void loadFileRules(String drlName, KieFileSystem kfs) throws IOException{
// 从classess/rules加载的规则
for (Resource file : getRuleFiles(drlName)) {
kfs.write(ResourceFactory.newClassPathResource(DroolsAutoConfig.BASE_RULES_PATH + DroolsAutoConfig.RULES_PATH + file.getFilename(), "UTF-8"));
}
}
private Resource[] getRuleFiles(String drlName) throws IOException {
if(StringUtils.isEmpty(drlName)){
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
return resourcePatternResolver.getResources(DroolsAutoConfig.BASE_RULES_PATH + DroolsAutoConfig.RULES_PATH + "**/*.*");
}
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
return resourcePatternResolver.getResources(DroolsAutoConfig.BASE_RULES_PATH + DroolsAutoConfig.RULES_PATH + "**/"+ drlName + ".*");
}
}
这样就可以实现规则热加载了,完整代码,可以参考文章头部 github链接, 谢谢!