使用Drools规则引擎的代码,最简单的主要有以下几部分:
//这一部分的连接:“万恶”之源的KieServices,获取代码就一行,表面代码越少里面东西就越多,本以为就是个简单的工厂方法,没想到里面弯弯绕绕这么多东西_zcrazy胡说八道的博客-CSDN博客
KieServices ks = KieServices.Factory.get();
//这一部分的理解:规则的加载与管理者——KieContainer的获取与其类型的区别(虽然标题是KieContainer,其实说的还是KieServices)_zcrazy胡说八道的博客-CSDN博客
KieContainer kContainer = ks.getKieClasspathContainer();
//今天讲解这一部分
KieSession kSession = kContainer.newKieSession();
目录
获取KieSessionModel
获取KieBase
根据KieBaseModel名字获取KieBase
获取KieBaseModel
创建KieBase
获取KieSession
总结
会话分为有状态会话和无状态会话,本篇主要是有状态会话的获取的源码过程,就按照上面的代码段来讲,没有传入任何的参数,全部使用默认的值来获取会话。源代码如下:
代码段1 KieContainerImpl类中的newKieSession方法
public KieSession newKieSession() {
return this.newKieSession(
(Environment)((Environment)null),
(KieSessionConfiguration)null);
}
public KieSession newKieSession(Environment environment, KieSessionConfiguration conf) {
return this.newKieSession((String)null, environment, conf);
}
//按照上面两个方法,传进去的参数全部是null
public KieSession newKieSession(String kSessionName,
Environment environment,
KieSessionConfiguration conf) {
//因为传进来的参数全是null,所以会走findKieSessionModel(false)这一条线
//获取KieSessionModel,用来创建KieSession的模型
KieSessionModelImpl kSessionModel = kSessionName != null ?
(KieSessionModelImpl)this.getKieSessionModel(kSessionName) :
(KieSessionModelImpl)this.findKieSessionModel(false);
if (kSessionModel == null) {
log.error("Unknown KieSession name: " + kSessionName);
return null;
} else {
//方法名已经很直接了,从kieSessionModel中获取kieBase
//将上一步获取的kieSessionModel作为参数传入进去
KieBase kBase = this.getKieBaseFromKieSessionModel(kSessionModel);
if (kBase == null) {
return null;
} else {
//conf是null,所以会调用 this.getKieSessionConfiguration((KieSessionModel)kSessionModel), environment);
KieSession kSession = kBase.newKieSession(conf != null ? conf :
this.getKieSessionConfiguration((KieSessionModel)kSessionModel),
environment);
this.registerNewKieSession(
kSessionModel,
(InternalKnowledgeBase)kBase, kSession);
return kSession;
}
}
}
从上面的方法来看,想要获取到有状态的会话,先要获取一个KieSessionModel,然后通过KieSessionModel获取KieBase,再从KieBase中获取KieSession,再将获取到的KieSession放入到缓存中,方便频繁的获取KieSession。
获取KieSessionModel
代码段2 KieContainerImpl类中的findKieSessionModel方法
//因为是有状态会话,所以stateless传入的是false
//会调用this.kProject.getDefaultKieSession()
private KieSessionModel findKieSessionModel(boolean stateless) {
KieSessionModel defaultKieSessionModel = stateless ?
this.kProject.getDefaultStatelessKieSession() :
this.kProject.getDefaultKieSession();
if (defaultKieSessionModel == null) {
throw new RuntimeException(stateless ? "Cannot find a default StatelessKieSession" : "Cannot find a default KieSession");
} else {
return defaultKieSessionModel;
}
}
这一段没什么好解释的,就是根据通过传入的参数来返回默认的会话,因为当前是有状态会话,所以会去getDefaultKieSession方法,这个方法就如其名字一样,至于内部的代码,之后讨论KieProject的时候再去研究。
获取KieBase
代码段3 KieContainerImpl类中的getKieBaseFromKieSessionModel方法
private KieBase getKieBaseFromKieSessionModel(KieSessionModel kSessionModel) {
if (kSessionModel.getType() == KieSessionType.STATELESS) {
//从findKieSessionModel获取的默认kieSessionModel,是有状态的,所以不会进入到这里来
throw new RuntimeException("Trying to create a stateful KieSession from a stateless KieSessionModel: " + kSessionModel.getName());
} else {
//通过kieSessionModel获取kieBaseModel
KieBase kBase = this.getKieBase(kSessionModel.getKieBaseModel().getName());
if (kBase == null) {
log.error("Unknown KieBase name: " + kSessionModel.getKieBaseModel().getName());
return null;
} else {
return kBase;
}
}
}
将上一步获取到的KieSessionModel作为参数,传入到这个方法中去,这个方法里面会通过KieSessionModel来获取建立KieBase的KieBaseModel,然后通过KieBaseModel的名字来获取对应的KieBase
根据KieBaseModel名字获取KieBase
代码段4 KieContainerImpl类中的getKieBase方法
public KieBase getKieBase(String kBaseName) {
//从缓存中获取kieBase
KieBase kBase = (KieBase)this.kBases.get(kBaseName);
if (kBase == null) {
//如果缓存中没有kieBase,就先获取kieBasemodel
KieBaseModelImpl kBaseModel = this.getKieBaseModelImpl(kBaseName);
synchronized(kBaseModel) {
kBase = (KieBase)this.kBases.get(kBaseName);
if (kBase == null) {
BuildContext buildContext = new BuildContext();
//创建一个kieBase
kBase = this.createKieBase(kBaseModel,
this.kProject,
buildContext,
(KieBaseConfiguration)null);
if (kBase == null) {
throw new RuntimeException("Error while creating KieBase" + buildContext.getMessages().filterMessages(new Level[]{Level.ERROR}));
}
//将创建的kieBase放入缓存中,下一次就可以直接获取到了
this.kBases.put(kBaseName, kBase);
}
}
}
return kBase;
}
从这段代码里面看出,进入该方法后,会先通过KieBase的名字从缓存中查找对应的KieBase,但是如果是第一次调用,肯定是查找不到对应的KieBase,所以就会先获取KieBaseModel用来建立KieBase,建立KieBase是一个同步操作,也就是说同一时间只能有一个线程执行该操作,在创建之前,也要先从缓存中先获取一次,防止有其他线程先执行了新建操作。如果此时KieBase还是空,就说明没有其他线程新建过,缓存中也不存在该KieBase,可以着手新建了。
获取KieBaseModel
代码段5 KieContainerImpl类中的getKieBaseModelImpl
private KieBaseModelImpl getKieBaseModelImpl(String kBaseName) {
//从当前的kieProject中获取kieBaseModel
KieBaseModelImpl kBaseModel = (KieBaseModelImpl)
this.kProject.getKieBaseModel(kBaseName);
if (kBaseModel == null) {
throw new RuntimeException("The requested KieBase \"" + kBaseName + "\" does not exist");
} else {
return kBaseModel;
}
}
简单的来说就是直接从KieProject中获取KieBaseModel,这个KieProject会在之后详细分析。
创建KieBase
代码段6 KieContainerImpl类中的createKieBase
private KieBase createKieBase(KieBaseModelImpl kBaseModel,
KieProject kieProject,
BuildContext buildContext,
KieBaseConfiguration conf) {
if (log.isInfoEnabled()) {
log.info("Start creation of KieBase: " + kBaseModel.getName());
}
//获取创建KieBase的KieModel
InternalKieModule kModule = kieProject.getKieModuleForKBase(kBaseModel.getName());
//通过KieModel的方法创建KieBase,具体怎么创建的等到了KieModel在说
InternalKnowledgeBase kBase = kModule.createKieBase(kBaseModel, kieProject, buildContext, conf);
//这个方法是在KieBase建立之后调用,可以用于执行一些与模型相关的操作
//例如更新模型的配置、添加或删除规则等
kModule.afterKieBaseCreationUpdate(kBaseModel.getName(), kBase);
if (kBase == null) {
return null;
} else {
kBase.setResolvedReleaseId(this.containerReleaseId);
kBase.setContainerId(this.containerId);
kBase.setKieContainer(this);
kBase.initMBeans();
if (log.isInfoEnabled()) {
log.info("End creation of KieBase: " + kBaseModel.getName());
}
return kBase;
}
}
获取KieSession
之前的步骤需要的代码大部分都是在Container中就可以看到,这一步的创建KieSession是在KieBase的代码中才能看到,需要先获取到KieSession的配置
代码段7 KieContainerImpl类中的getKieSessionConfiguration
private KieSessionConfiguration getKieSessionConfiguration(KieSessionModel kSessionModel) {
KieSessionConfiguration ksConf =
(KieSessionConfiguration)this.sessionConfsCache
.computeIfAbsent(kSessionModel.getName(),
(k) -> {
return
new SessionConfigurationImpl(
(Properties)null,
this.kProject.getClassLoader()
);
});
ksConf.setOption(kSessionModel.getClockType());
ksConf.setOption(kSessionModel.getBeliefSystem());
return ksConf;
}
代码段8 KnowledgeBaseImpl类中的newKieSession方法
//environment传入为null
public KieSession newKieSession(KieSessionConfiguration conf, Environment environment) {
return this.newKieSession(conf, environment, false);
}
KieSession newKieSession(KieSessionConfiguration conf, Environment environment, boolean fromPool) {
//通过代码段7来看,这地方不会为null
if (conf == null) {
conf = this.getSessionConfiguration();
}
SessionConfiguration sessionConfig = (SessionConfiguration)conf;
//传入的参数为null,所以会从这里获取Environment
if (environment == null) {
environment = EnvironmentFactory.newEnvironment();
}
if (this.getConfiguration().isSequential()) {
throw new RuntimeException("Cannot have a stateful rule session, with sequential configuration set to true");
} else {
//整了一个重入读写锁
this.readLock();
StatefulKnowledgeSessionImpl var5;
try {
//获取KieSession
var5 = this.internalCreateStatefulKnowledgeSession(environment, sessionConfig, fromPool);
} finally {
//解锁重入读写锁
this.readUnlock();
}
return var5;
}
}
这样KieSession就获取成功了,获取的详细代码,在之后的内容中可能会涉及,请持续关注,感谢!~
总结
获取KieSession是一个非常复杂的过程,根据用户手册来说,KieSession是从KieBase中获取的,KieBase是从KieContainer中获取的,而且都会有默认的,也可以在kmodul.xml文件中配置。文章中出现了很多KiePaoject等相关控件,会在之后的内容中陆续解析。