前言:
如果對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鏈接, 謝謝!