简介
官网地址:
关于Drools(官网简介直接Copy过来)
Drools is a Business Rules Management System (BRMS) solution.
It provides a core Business Rules Engine (BRE), a web authoring and rules management application (Drools Workbench) and an Eclipse IDE plugin for core development.
最近有个消费返现和后付费保险类型项目,要求根据不同规则进行消费返现及保险静默投保etc. ,为避免过多硬编码ifelse逻辑判断,影响程序可读性及削弱程序可扩展性,因此引入了Drools规则引擎。
至于规则引擎到底是啥,在这里就不赘述了,google一下,你就知道。
Code实现
下面基于一个简单的Mock User Register模拟流程,简单介绍一下关于Spring+Drools集成实现流程。
需求说明
新用户规则:1.系统Mock生成用户注册数据,并进入Drools规则引擎处理;2.对于新注册用户设定用户锁定状态,并初始用户等级为3级,覆盖新用户标识为非;非新用户规则:1.设定用户非锁定状态,每次Mock Code执行,用户等级+1(规则比较随意);
实现过程
添加Drools Pom依赖
6.4.0.Final org.drools drools-core ${drools-version} org.drools drools-compiler ${drools-version}
User实体Bean定义
@Getter@Setter@Access(AccessType.FIELD)@Accessors(chain = true)@MetaData(value = "登陆/认证用户信息" , comments = "monster-web / monster-web-admin模块使用统一的auth信息")@Entity@Table(name = "auth_MsUser" , uniqueConstraints = @UniqueConstraint(columnNames ={"authUid","authType"}))@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)public class User extends BaseNativeEntity { …… …… @MetaData(value="用户全局唯一标识" , comments = "同时作为系统用户密码Salt值,Salt形式 [authUuid]") @Column(length = 64 , nullable = false) private String authUuid; @MetaData(value="authUid,登陆账号") @Column(length = 128 , nullable = false) private String authUid; @MetaData(value="密码",comments = "MD5加密串,格式:MD5({Salt}+sourcePassword)") @Column(length = 128 , nullable = false) private String password; @MetaData(value="用户类型") @Column(length = 32 , nullable = false) @Enumerated(EnumType.STRING) private AuthTypeEnum authType; @MetaData(value = "身份证号码") @Column(length = 32) private String idCardNo; @MetaData(value="性别") @Column(length = 32) @Enumerated(EnumType.STRING) private GenderEnum gender; @MetaData(value = "REST访问Token") @Column private String accessToken; @MetaData(value = "访问Token过期时间") @DateTimeFormat(pattern = DateUtils.DEFAULT_TIME_FORMAT) @JsonFormat(pattern = DateUtils.DEFAULT_TIME_FORMAT) private Date accessTokenExpireTime; @MetaData(value = "是否新用户",tooltips = "用于新用户相关业务规则") @Column private Boolean isNew = Boolean.TRUE; @MetaData(value = "账户未锁定标志", tooltips = "账号锁定后无法登录") @Column private Boolean accountNonLocked = Boolean.TRUE; …… …… }
Drools相关Bean定义
提供Drools资源辅助类
package org.monster.drools.technorage;import org.kie.api.io.ResourceType;@setter@getterpublic class DroolsResource { private String path; private ResourcePathType pathType; private ResourceType type; private String username = null; private String password = null; /** * * @param path * The path to this resource. * @param pathType * The type of path (FILE, URL, etc). * @param type * The type of resource (DRL, Binary package, DSL, etc) */ public DroolsResource(String path, ResourcePathType pathType, ResourceType type) { this.path = path; this.pathType = pathType; this.type = type; } /** * * @param path * The path to this resource. * @param pathType * The type of path (FILE, URL, etc). * @param type * The type of resource (DRL, Binary package, DSL, etc) * @param username * The user name for connecting to the resource. * @param password * The password for connecting to the resource. */ public DroolsResource(String path, ResourcePathType pathType, ResourceType type, String username, String password) { this.path = path; this.pathType = pathType; this.type = type; this.username = username; this.password = password; } }
定义本地接口,并实现Drools提供的KieServices与KieContainer、KieSession接口
import org.kie.api.KieServices;public interface KieServicesBean extends KieServices {}
package org.monster.drools.technorage.spring;import org.kie.api.runtime.KieContainer;public interface KieContainerBean extends KieContainer {}
package org.monster.drools.technorage.spring;import org.kie.api.runtime.KieSession;public interface KieSessionBean extends KieSession {}
及其分别实现类
public class DefaultKieServicesBean implements KieServicesBean { private static Logger log = LoggerFactory.getLogger(DefaultKieServicesBean.class); private DroolsResource[] resources; private KieServices kieServices; private KieFileSystem kfs; public DefaultKieServicesBean(DroolsResource[] resources) throws KieBuildException { log.info("Initialising KnowledgeEnvironment with resources: " + this.resources); this.resources = resources; createAndBuildKieServices(resources); } /** * Initialises the {@link org.kie.api.KieServices} by downloading the package from the * Guvnor REST interface, at the location defined in the URL. * * @param url The URL of the package via the Guvnor REST API. * @throws KieBuildException */ public DefaultKieServicesBean(String url) throws KieBuildException { log.info("Initialising KnowledgeEnvironment with resources: " + this.resources); this.resources = new DroolsResource[] { new DroolsResource(url, ResourcePathType.URL, ResourceType.PKG )}; createAndBuildKieServices(resources); } /** * 根据提供的URL生成{@link org.kie.api.KieServices} * * @param url The URL of the package via the Guvnor REST API. * @param username The Guvnor user name. * @param password The Guvnor password. * @throws KieBuildException */ public DefaultKieServicesBean(String url, String username, String password) throws KieBuildException { this.resources = new DroolsResource[] { new DroolsResource(url, ResourcePathType.URL, ResourceType.PKG, username, password )}; createAndBuildKieServices(resources); } /** * 根据提供的DroolsResource创建 {@link org.kie.api.KieServices} * * @param resources * An array of {@link DroolsResource} indicating where the * various resources should be loaded from. These could be * classpath, file or URL resources. * @return A new {@link org.kie.api.runtime.KieContainer}. * @throws KieBuildException */ private void createAndBuildKieServices(DroolsResource[] resources) throws KieBuildException { this.kieServices = KieServices.Factory.get(); this.kfs = newKieFileSystem(); for (DroolsResource resource : resources) { log.info("Resource: " + resource.getType() + ", path type=" + resource.getPathType() + ", path=" + resource.getPath()); switch (resource.getPathType()) { case CLASSPATH: this.kfs.write(ResourceFactory.newClassPathResource(resource.getPath())); break; case FILE: this.kfs.write(ResourceFactory.newFileResource(resource.getPath())); break; case URL: UrlResource urlResource = (UrlResource) ResourceFactory .newUrlResource(resource.getPath()); if (resource.getUsername() != null) { log.info("Setting authentication for: " + resource.getUsername()); urlResource.setBasicAuthentication("enabled"); urlResource.setUsername(resource.getUsername()); urlResource.setPassword(resource.getPassword()); } this.kfs.write(urlResource); break; default: throw new IllegalArgumentException( "Unable to build this resource path type."); } } KieBuilder kieBuilder = kieServices.newKieBuilder(kfs).buildAll(); if (kieBuilder.getResults().hasMessages(Level.ERROR)) { Listerrors = kieBuilder.getResults().getMessages(Level.ERROR); StringBuilder sb = new StringBuilder("Errors:"); for (Message msg : errors) { sb.append("\n " + prettyBuildMessage(msg)); } throw new KieBuildException(sb.toString()); } log.info("KieServices built: " + toString()); } private static String prettyBuildMessage(Message msg) { return "Message: {" + "id="+ msg.getId() + ", level=" + msg.getLevel() + ", path=" + msg.getPath() + ", line=" + msg.getLine() + ", column=" + msg.getColumn() + ", text=\"" + msg.getText() + "\"" + "}"; } //省略相关实现方法 }
public class DefaultKieContainerBean implements KieContainerBean { private KieServicesBean kieServices; private KieContainer kieContainer; public DefaultKieContainerBean(KieServicesBean kieServicesBean) { this.kieServices = kieServicesBean; this.kieContainer = this.kieServices.newKieContainer(kieServices.getRepository().getDefaultReleaseId()); } /** …… …… */}
public class DefaultKieSessionBean implements KieSessionBean { private static Logger log = LoggerFactory.getLogger(DefaultKieSessionBean.class); private KieSession kieSession; public DefaultKieSessionBean(KieServicesBean kieServices, KieContainerBean kieContainer) { this(kieServices, kieContainer, null); } public DefaultKieSessionBean(KieServicesBean kieServices, KieContainerBean kieContainer, Properties droolsProperties) { log.info("Initialising session..."); KieSessionConfiguration conf; if (droolsProperties == null) { conf = SessionConfiguration.getDefaultInstance(); } else { conf = SessionConfiguration.newInstance(droolsProperties);//new SessionConfiguration(droolsProperties); } this.kieSession = kieContainer.newKieSession(conf); } public void addEventListener(RuleRuntimeEventListener listener) { kieSession.addEventListener(listener); } public void addEventListener(ProcessEventListener listener) { kieSession.addEventListener(listener); } public ProcessInstance startProcess(String processId) { return kieSession.startProcess(processId); } public void removeEventListener(RuleRuntimeEventListener listener) { kieSession.removeEventListener(listener); } public int fireAllRules() { return kieSession.fireAllRules(); } public void removeEventListener(ProcessEventListener listener) { kieSession.removeEventListener(listener); } publicT getSessionClock() { return kieSession.getSessionClock(); } public int fireAllRules(int max) { return kieSession.fireAllRules(max); } public Collection getWorkingMemoryEventListeners() { return kieSession.getRuleRuntimeEventListeners(); } public Collection getProcessEventListeners() { return kieSession.getProcessEventListeners(); } public void setGlobal(String identifier, Object value) { kieSession.setGlobal(identifier, value); } public void halt() { kieSession.halt(); } public ProcessInstance startProcess(String processId, Map parameters) { return kieSession.startProcess(processId, parameters); } public void addEventListener(AgendaEventListener listener) { kieSession.addEventListener(listener); } public Object getGlobal(String identifier) { return kieSession.getGlobal(identifier); } public Globals getGlobals() { return kieSession.getGlobals(); } public Calendars getCalendars() { return kieSession.getCalendars(); } public void removeEventListener(AgendaEventListener listener) { kieSession.removeEventListener(listener); } public Environment getEnvironment() { return kieSession.getEnvironment(); } public KieBase getKieBase() { return kieSession.getKieBase(); } public int fireAllRules(AgendaFilter agendaFilter) { return kieSession.fireAllRules(agendaFilter); } public void registerChannel(String name, Channel channel) { kieSession.registerChannel(name, channel); } public Collection getAgendaEventListeners() { return kieSession.getAgendaEventListeners(); } public String getEntryPointId() { return kieSession.getEntryPointId(); } public void unregisterChannel(String name) { kieSession.unregisterChannel(name); } public Map getChannels() { return kieSession.getChannels(); } public Agenda getAgenda() { return kieSession.getAgenda(); } public int fireAllRules(AgendaFilter agendaFilter, int max) { return kieSession.fireAllRules(agendaFilter, max); } public FactHandle insert(Object object) { return kieSession.insert(object); } public KieSessionConfiguration getSessionConfiguration() { return kieSession.getSessionConfiguration(); } @Override public EntryPoint getEntryPoint(String name) { return kieSession.getEntryPoint(name); } public ProcessInstance createProcessInstance(String processId, Map parameters) { return kieSession.createProcessInstance(processId, parameters); } @Deprecated public void retract(FactHandle handle) { kieSession.retract(handle); } public Collection getEntryPoints() { return kieSession.getEntryPoints(); } public void fireUntilHalt() { kieSession.fireUntilHalt(); } public T execute(Command command) { return kieSession.execute(command); } public void delete(FactHandle handle) { kieSession.delete(handle); } @Override public void delete(FactHandle handle, FactHandle.State fhState) { } public QueryResults getQueryResults(String query, Object... arguments) { return kieSession.getQueryResults(query, arguments); } public void update(FactHandle handle, Object object) { kieSession.update(handle, object); } public void fireUntilHalt(AgendaFilter agendaFilter) { kieSession.fireUntilHalt(agendaFilter); } public FactHandle getFactHandle(Object object) { return kieSession.getFactHandle(object); } public LiveQuery openLiveQuery(String query, Object[] arguments, ViewChangedEventListener listener) { return kieSession.openLiveQuery(query, arguments, listener); } public ProcessInstance startProcessInstance(long processInstanceId) { return kieSession.startProcessInstance(processInstanceId); } public Object getObject(FactHandle factHandle) { return kieSession.getObject(factHandle); } public int getId() { return kieSession.getId(); } @Override public long getIdentifier() { return 0; } public void signalEvent(String type, Object event) { kieSession.signalEvent(type, event); } public void dispose() { kieSession.dispose(); } public Collection getObjects() { return kieSession.getObjects(); } public void destroy() { kieSession.destroy(); } public void signalEvent(String type, Object event, long processInstanceId) { kieSession.signalEvent(type, event, processInstanceId); } public Collection getObjects(ObjectFilter filter) { return kieSession.getObjects(filter); } public Collection getFactHandles() { return kieSession.getFactHandles(); } public Collection getFactHandles( ObjectFilter filter) { return kieSession.getFactHandles(filter); } public Collection getProcessInstances() { return kieSession.getProcessInstances(); } public long getFactCount() { return kieSession.getFactCount(); } public ProcessInstance getProcessInstance(long processInstanceId) { return kieSession.getProcessInstance(processInstanceId); } public ProcessInstance getProcessInstance(long processInstanceId, boolean readonly) { return kieSession.getProcessInstance(processInstanceId, readonly); } public void abortProcessInstance(long processInstanceId) { kieSession.abortProcessInstance(processInstanceId); } public WorkItemManager getWorkItemManager() { return kieSession.getWorkItemManager(); } @Override public KieRuntimeLogger getLogger() { return kieSession.getLogger(); } @Override public Collection getRuleRuntimeEventListeners() { return kieSession.getRuleRuntimeEventListeners(); } }
集成Spring,交由spring托管
@Configuration//@Profile("drools")public class BaseKieConfig { @Bean(name="kieServices") public KieServicesBean kieServices() throws KieBuildException { DroolsResource [] droolsResources = new DroolsResource[]{ new DroolsResource("rules/user-level.drl", ResourcePathType.CLASSPATH , ResourceType.DRL) }; KieServicesBean kieServicesBean = new DefaultKieServicesBean(droolsResources); return kieServicesBean; } @Bean(name="kieContainer") public KieContainerBean kieContainer(KieServicesBean kieServices){ KieContainerBean kieContainer = new DefaultKieContainerBean(kieServices); return kieContainer; }}
定义相关业务接口
public interface UserRuleService{ List userRegister( List users ); List remUsers( List users ); List checkUserStatus();}
@Service@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON , proxyMode = ScopedProxyMode.INTERFACES)public class UserRuleServiceImpl implements UserRuleService,Serializable { private Logger logger = LoggerFactory.getLogger(UserRuleServiceImpl.class); public KieSessionBean kieSession; private TrackingAgendaEventListener agendaEventListener; private TrackingWorkingMemoryEventListener workingMemoryEventListener; private Map fact2User = Maps.newHashMap(); private FactFinder userFactFinder = new FactFinder (User.class); @Autowired public UserRuleServiceImpl(@Qualifier("kieServices") KieServicesBean kieServices , @Qualifier("kieContainer") KieContainerBean kieContainer){ System.setProperty("drools.negatable", "on"); kieSession = new DefaultKieSessionBean(kieServices , kieContainer); agendaEventListener = new TrackingAgendaEventListener(); workingMemoryEventListener = new TrackingWorkingMemoryEventListener(); kieSession.addEventListener(agendaEventListener); kieSession.addEventListener(workingMemoryEventListener); } @Override public List userRegister(List users) { for ( User user : users ) { if(logger.isDebugEnabled()){ logger.debug("执行用户注册规则模板,对应用户:{}",user.toString()); } if(!fact2User.containsKey(user.getId())){ FactHandle userFireHandle = kieSession.insert(user); fact2User.put(String.valueOf(user.getId()) , userFireHandle); } } kieSession.fireAllRules(); List results = userFactFinder.findFacts(kieSession); pause(); kieSession.dispose(); return results; } @Override public List remUsers(List users) { for( User user : users ) { if(fact2User.containsKey(String.valueOf(user.getId()))) { kieSession.delete(fact2User.get(String.valueOf(user.getId()))); fact2User.remove(user); } } kieSession.fireAllRules(); List result = userFactFinder.findFacts(kieSession); return result; } @Override public List checkUserStatus() { return null; } public static void pause() { System.out.println( "Pressure enter to continue" ); Scanner keyboard = new Scanner(System.in); keyboard.nextLine(); }}
Mock代码
…… …… …… int userCnt = countTable(User.class); for( int i = userCnt ; (i < INIT_USER_CNT && i) userRuleService.userRegister(users); //存储用户信息 userService.save(users);
定义规则文件(user-register.drl)
//User-Level规则package rulesimport org.monster.core.auth.entity.User;rule RegistNewUserRule//ruleflow-group "Basic-User-Group"when $user:User(isNew == Boolean.TRUE)then //1.设置解除锁定状态 $user.setAccountNonLocked(false); //2.用户注册送积分 $user.setUserLevel(3); $user.setIsNew(Boolean.FALSE); //3.根据性别 //System.out.println("++++++++++ Is New Flg True , [User:" + $user.toString() + "]");endrule RegistNewUserRuleNotNew//ruleflow-group "Basic-User-Group"when $user:User(isNew == false)then //1.设置锁定状态 $user.setAccountNonLocked(true); //2.老用户统一设定每次启动增加一级 $user.setUserLevel($user.getUserLevel()+1); //System.out.println("---------- Is New Flg False, [User:" + $user.toString() +"]");end
————————————————————————————————————————————————————————————————————————————————
Drools中,其三者关系
1.KieServices
1.1 容器通过ClassPathResource获取KieFileSystem
//获取KieFileSystemthis.kfs.write(ResourceFactory.newClassPathResource(resource.getPath()));
1.2.并通过Factory.get()方法获取Service实例,进行相关装载校验
//获取kieServices实例this.kieServices = KieServices.Factory.get();……KieBuilder kieBuilder = kieServices.newKieBuilder(kfs).buildAll(); if (kieBuilder.getResults().hasMessages(Level.ERROR)) { Listerrors = kieBuilder.getResults().getMessages(Level.ERROR); StringBuilder sb = new StringBuilder("Errors:"); for (Message msg : errors) { sb.append("\n " + prettyBuildMessage(msg)); } throw new KieBuildException(sb.toString()); }
2.KieContainer
通过KieServices实例,构造KieContainer
this.kieContainer = this.kieServices.newKieContainer(kieServices.getRepository().getDefaultReleaseId());
3.KieSession
通过KieContainer及相关KieSessionConfig获取current working Session对象
KieSessionConfiguration conf; if (droolsProperties == null) { conf = SessionConfiguration.getDefaultInstance(); } else { conf = SessionConfiguration.newInstance(droolsProperties);//new SessionConfiguration(droolsProperties); } this.kieSession = kieContainer.newKieSession(conf);
时间匆忙写的比较乱,基本贴的代码。有空整理