當某個服務的需求經常變的時候,如果使用了硬編碼的方式進行開發會是一件非常麻煩的事。
最近在對項目的積分模塊進行改造的時候想到了規則引擎,使用規則引擎處理復雜而且多變的業務邏輯有其非常大的優勢,包括實時更新、性能等方面。
不多說,直接上代碼:
1、第一步先寫好工具類,有了工具類,只需在應用的業務場景中調用相應方法就可以了
@Component public class KieSessionUtils {private static KieBase kieBase;//定義規則文件的包名,與drl文件里的package對應private static final String drlPackage = "rules";//定義drl文件的存放路徑,靜態變量需要通過在其set方法上打@Value注解,才可實現配置注入private static String drlPath;//通過配置拉取路徑,這里推薦一下apollo配置中心,使用apollo可以實時更改通過@Value拉取的配置@Value("${drools.points.drlPath}")public void setDrlPath(String drlPath){KieSessionUtils.drlPath = drlPath;}/*** 生成kieSeesion會話* @param ruleName* @return* @throws Exception*/public static KieSession newKieSession(String ruleName) throws Exception {//無狀態的kieSession,和有狀態相比,區別在于不維持會話,即使用完后自動釋放資源,不需要手動調dispose//StatelessKieSession kieSession = getKieBase(ruleName).newStatelessKieSession();//有狀態的kieSessionKieSession kieSession = getKieBase(ruleName).newKieSession();//添加監聽器,這里加的是對規則文件運行debug監聽器,測試時最好加上,用于排查問題,生產上可視情況去掉kieSession.addEventListener(new DebugRuleRuntimeEventListener());return kieSession;}/*** 生成kieBase* @param ruleName 規則文件名* @return* @throws Exception*/protected static KieBase getKieBase(String ruleName) throws Exception {//判斷kieBase和需要獲取的規則文件是否存在,不存在則重新初始化kieBaseif (kieBase ==null || kieBase.getRule(drlPackage,ruleName)==null) {KieServices kieServices = KieServices.Factory.get();KieFileSystem kfs = kieServices.newKieFileSystem();//獲取規則數據源,這里由于本人項目使用的是springboot,打包會打成jar包,如果想做實時更新,drl文件需要放在jar包外面//獲取resource的方式很多,不一定要用讀取文件的方式,可根據自己的設計和業務場景采取不同方案Resource resource = kieServices.getResources().newFileSystemResource(new File(drlPath+"/"+ruleName));resource.setResourceType(ResourceType.DRL);kfs.write(resource);KieBuilder kieBuilder = kieServices.newKieBuilder(kfs).buildAll();if (kieBuilder.getResults().getMessages(Message.Level.ERROR).size() > 0) {throw new Exception();}KieContainer kieContainer = kieServices.newKieContainer(kieServices.getRepository().getDefaultReleaseId());kieBase = kieContainer.getKieBase();}return kieBase;}/*** 更新規則* @param ruleName 規則名和規則文件名* @throws Exception*/public static void refreshRules(String ruleName) throws Exception {//判斷規則不為null,則移除規則if (kieBase !=null && kieBase.getRule(drlPackage,ruleName)!=null){//為了方便,本人把規則名和drl文件名稱統一定義了 kieBase.removeRule(drlPackage,ruleName);//重新初始化kieBase getKieBase(ruleName);}} }
2、編寫規則文件,這里只給出和規則引擎格式有關的代碼
package rules; //包名import com.jiuair.dto.AddObject import java.util.List import java.util.HashMap import java.util.Map import java.util.ArrayList import java.util.Date import java.util.Iterator import java.util.Setglobal com.demo.dto.AddObject addObject //傳入的對象,同時也是返回值對象 rule "add.drl" //規則名,為了方便,設為何drl文件名一樣,可以不一樣 when$s : AddObject();then。。。。。//這一段加自己業務代碼邏輯,支持jdk$s.setResult(X); //執行完邏輯后將結果設置到對象中 end
3、在業務場景中調用工具類里的方法
private AddObject executeAddRule(Object data) {AddObject addObject = new AddObject();addObject.setJsonObject(data);try {//獲取會話KieSession kieSession = KieSessionUtils.newKieSession("add.drl");//設置傳入參數 kieSession.insert(addObject);//設置全局參數kieSession.setGlobal("addObject",addObject);//執行規則 kieSession.fireAllRules();//釋放會話資源 kieSession.dispose();} catch (Exception e) {e.printStackTrace();}return addObject;}
4、實現實時更新drl文件
/*** 更新規則文件,這里只給出service層的代碼了,相信controller大家都會寫。。。* @param name 名稱為drl的文件名* @param is 由于dubbo不支持流的方式傳輸,文件需在controller轉為byte數組,再傳到service*/@Overridepublic void refreshRule(String name, byte[] is) {try {FileOutputStream fos = new FileOutputStream(drlPath+"/"+name);fos.write(is);fos.close();KieSessionUtils.refreshRules(name);} catch (Exception e) {e.printStackTrace();}}
附maven引包:
<properties><runtime.version>7.20.0.Final</runtime.version></properties><dependency><groupId>org.kie</groupId><artifactId>kie-api</artifactId><version>${runtime.version}</version></dependency><dependency><groupId>org.kie</groupId><artifactId>kie-internal</artifactId><version>${runtime.version}</version></dependency><dependency><groupId>org.drools</groupId><artifactId>drools-core</artifactId><version>${runtime.version}</version></dependency> <dependency><groupId>org.drools</groupId><artifactId>drools-decisiontables</artifactId><version>${runtime.version}</version></dependency>
?
?