【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>
简介
官网地址:http://drools.org/
关于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依赖
<pro..>
<drools-version>6.4.0.Final</drools-version>
</pro..>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-core</artifactId>
<version>${drools-version}</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-compiler</artifactId>
<version>${drools-version}</version>
</dependency>
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
@getter
public 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)) {
List<Message> errors = 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);
}
public <T extends SessionClock> T getSessionClock() {
return kieSession.getSessionClock();
}
public int fireAllRules(int max) {
return kieSession.fireAllRules(max);
}
public Collection<RuleRuntimeEventListener> getWorkingMemoryEventListeners() {
return kieSession.getRuleRuntimeEventListeners();
}
public Collection<ProcessEventListener> 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<String, Object> 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<AgendaEventListener> getAgendaEventListeners() {
return kieSession.getAgendaEventListeners();
}
public String getEntryPointId() {
return kieSession.getEntryPointId();
}
public void unregisterChannel(String name) {
kieSession.unregisterChannel(name);
}
public Map<String, Channel> 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<String, Object> parameters) {
return kieSession.createProcessInstance(processId, parameters);
}
@Deprecated
public void retract(FactHandle handle) {
kieSession.retract(handle);
}
public Collection<? extends EntryPoint> getEntryPoints() {
return kieSession.getEntryPoints();
}
public void fireUntilHalt() {
kieSession.fireUntilHalt();
}
public <T> T execute(Command<T> 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<? extends Object> 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<? extends Object> getObjects(ObjectFilter filter) {
return kieSession.getObjects(filter);
}
public <T extends FactHandle> Collection<T> getFactHandles() {
return kieSession.getFactHandles();
}
public <T extends FactHandle> Collection<T> getFactHandles(
ObjectFilter filter) {
return kieSession.getFactHandles(filter);
}
public Collection<ProcessInstance> 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<RuleRuntimeEventListener> 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<T> {
List<T> userRegister( List<User> users );
List<T> remUsers( List<User> users );
List<T> checkUserStatus();
}
@Service
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON , proxyMode = ScopedProxyMode.INTERFACES)
public class UserRuleServiceImpl implements UserRuleService<User> ,Serializable {
private Logger logger = LoggerFactory.getLogger(UserRuleServiceImpl.class);
public KieSessionBean kieSession;
private TrackingAgendaEventListener agendaEventListener;
private TrackingWorkingMemoryEventListener workingMemoryEventListener;
private Map<String , FactHandle> fact2User = Maps.newHashMap();
private FactFinder<User> userFactFinder = new FactFinder<User>(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<User> userRegister(List<User> 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<User> results = userFactFinder.findFacts(kieSession);
pause();
kieSession.dispose();
return results;
}
@Override
public List<User> remUsers(List<User> 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<User> result = userFactFinder.findFacts(kieSession);
return result;
}
@Override
public List<User> 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<userCnt+5) ; i ++ )
{
String salt = UidUtils.UID();
String format = String.format("%06d" , i);
User user = MockUtils.buildMockEntity(User.class);
user.setAuthUid("19999"+format);
user.setRealName("用户" + format);
user.setNickName("昵称" + format);
user.setAuthUuid(salt);
user.setAuthType(User.AuthTypeEnum.SYS);
user.setPassword(passwordService.encryptPassword("123456" , salt));
user.setIdCardNo("110010" + String.format("%012d" , i));
user.setAccessToken(StringUtils.EMPTY);
//user.setAccessToken(salt);
users.add(user);
}
if(CollectionUtils.isEmpty(users))
{
users = userService.findAll();
}
//规则引擎服务处理
users = (List<User>) userRuleService.userRegister(users);
//存储用户信息
userService.save(users);
定义规则文件(user-register.drl)
//User-Level规则
package rules
import 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() + "]");
end
rule 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
//获取KieFileSystem
this.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)) {
List<Message> errors = 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);
时间匆忙写的比较乱,基本贴的代码。有空整理
来源:oschina
链接:https://my.oschina.net/u/1159856/blog/690074