当某个服务的需求经常变的时候,如果使用了硬编码的方式进行开发会是一件非常麻烦的事。
最近在对项目的积分模块进行改造的时候想到了规则引擎,使用规则引擎处理复杂而且多变的业务逻辑有其非常大的优势,包括实时更新、性能等方面。
不多说,直接上代码:
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>