之前梳理了一下有关KieServices的获取,与获取中的代码走向,详情请见:
“万恶”之源的KieServices,获取代码就一行,表面代码越少里面东西就越多,本以为就是个简单的工厂方法,没想到里面弯弯绕绕这么多东西_zcrazy胡说八道的博客-CSDN博客
在我使用drools时,第一行的语句就是获取KieServices,紧接着就是获取KieContainer,就是下面这一句
代码1
KieContainer kContainer = kieServices.getKieClasspathContainer();
然后我就去看了这个getKieClasspathContainer方法的源码,源码如下:
代码2 KieServicesImpl类中的getKieClasspathContainer方法
/**
* 获取类路径容器
*
* @param containerId 容器Id
* @param classLoader 类加载器
* @return Kie容器
*/
public KieContainer getKieClasspathContainer(String containerId, ClassLoader classLoader) {
if (this.classpathKContainer == null) {
//如果是第一次调用该方法,这个classpathKContainer肯定是null的
//这个变量会在if处理中进行初始化
synchronized(this.lock) {
//下面的内容为同步内容
if (this.classpathKContainer == null) {
//将传入的类加载器赋值给当前实例
this.classpathClassLoader = classLoader;
if (containerId == null) {
//如果containerId是null的,则会给当前实例赋值一个UUID作为containerId
this.classpathKContainerId = UUID.randomUUID().toString();
} else {
this.classpathKContainerId = containerId;
}
//会调用KieServices的两个创建方法
this.classpathKContainer = this.newKieClasspathContainer(
this.classpathKContainerId, 、
classLoader);
} else if (classLoader != this.classpathClassLoader) {
throw new IllegalStateException("There's already another KieContainer created from a different ClassLoader");
}
}
} else if (classLoader != this.classpathClassLoader) {
throw new IllegalStateException("There's already another KieContainer created from a different ClassLoader");
}
if (containerId != null && !this.classpathKContainerId.equals(containerId)) {
throw new IllegalStateException("The default global singleton KieClasspathContainer was already created with id " + this.classpathKContainerId);
} else {
return this.classpathKContainer;
}
}
里面的注释是我添加的,如果在正常情况下,第一次调用该方法,会直接进入到newKieClasspathContainer方法中去,源码如下:
代码3 KieServicesImpl中的newKieClasspathContainer方法
public KieContainer newKieClasspathContainer(String containerId,
ClassLoader classLoader,
ReleaseId releaseId) {
KieContainerImpl newContainer;
if (containerId == null) {
//如果containerId为null
newContainer = new KieContainerImpl(UUID.randomUUID().toString(),
new ClasspathKieProject(classLoader, this.listener, releaseId),
(KieRepository)null);
return newContainer;
} else if (this.kContainers.get(containerId) == null) {
//containerId不为null,但是kContainers映射中没有该containerId
newContainer = new KieContainerImpl(containerId,
new ClasspathKieProject(classLoader, this.listener, releaseId),
(KieRepository)null,
releaseId);
KieContainer check =
(KieContainer)this.kContainers.putIfAbsent(containerId, newContainer);
if (check == null) {
//如果check为null,说明kContainers中没有当前的containerId,返回newContainer。
return newContainer;
} else {
//如果check不为null,说明kContainers已经有当前containerId
newContainer.dispose();
throw new IllegalStateException("There's already another KieContainer created with the id " + containerId);
}
} else {
throw new IllegalStateException("There's already another KieContainer created with the id " + containerId);
}
}
注释也是我自己添加的,最终就是会返回一个KieContainerImpl的实例,到这里,基本就已经返回一个KieContainer了,工作就结束了,但是凡事就怕琢磨,这个KieContainer到底是个什么呢?
KieContainer:是用于加载和管理规则资源的核心组件
规则资源又是什么呢?
规则资源(Rule Resources)指的是包含业务规则定义的文件,这些规则文件描述了系统在特定条件下应该如何进行推理或决策。
这些规则资源文件通常以.drl、.xls、.dslr、.bpmn等扩展名保存
简单来说,就是咱们项目里面的drl文件需要KieContainer加载,可能后续还有移除,更新,添加等等跟管理相关的操作。
回到代码1中我发现getKieClasspathContainer这个方法,方法名有问题,为什么不是getContainer,而是getKieClasspathContainer,这说明这个Container是由不同种类的,于是我在KieService中又发现了这个方法newKieContainer,为什么有了newKieClasspathContainer,还会有个newKieContainer方法呢?于是我就看了一下源码:
代码4 KieServicesImpl中的newKieContainer方法
public KieContainer newKieContainer(String containerId,
ReleaseId releaseId,
ClassLoader classLoader) {
InternalKieModule kieModule = (InternalKieModule)this.getRepository().
getKieModule(releaseId);
if (kieModule == null) {
throw new RuntimeException("Cannot find KieModule: " + releaseId);
} else {
if (classLoader == null) {
classLoader = kieModule.getModuleClassLoader();
}
KieProject kProject = new KieModuleKieProject(kieModule, classLoader);
if (classLoader != kProject.getClassLoader()) {
kProject.init();
}
KieContainerImpl newContainer;
if (containerId == null) {
newContainer = new KieContainerImpl(UUID.randomUUID().toString(),
kProject,
this.getRepository(),
releaseId);
return newContainer;
} else if (this.kContainers.get(containerId) == null) {
newContainer = new KieContainerImpl(containerId,
kProject,
this.getRepository(),
releaseId);
KieContainer check = (KieContainer)this.kContainers.putIfAbsent(containerId,
newContainer);
if (check == null) {
return newContainer;
} else {
newContainer.dispose();
throw new IllegalStateException("There's already another KieContainer created with the id " + containerId);
}
} else {
throw new IllegalStateException("There's already another KieContainer created with the id " + containerId);
}
}
}
总体来说newKieContainer方法和newKieClasspathContainer方法的代码很像,但是多出了几步,多出了KieModule的获取,以及KieProject 的获取,从代码里来看获取KieModule就是为了获取KieProject,然后在实例化KieContainerImpl时,让这个KieProject作为参数输入,在newKieClasspathContainer中也有出现KieProject,只不过在newKieClasspathContainer中出现的是ClasspathKieProject,而在newKieContainer中出现的是KieModuleKieProject。
KieServices类提供了两种方法来创建KieContainer对象,分别是newKieClasspathContainer()和newKieContainer(),他们的区别如下:
-
newKieClasspathContainer()
-
从类路径(classpath)加载规则资源。规则资源通常位于项目的src/main/resources目录下或其他在类路径中的位置。
-
可以自动扫描类路径中的规则文件并加载它们。
-
适用于需要将规则文件打包到应用程序中,并在运行时从类路径加载规则资源的场景。
-
-
newKieContainer(groupId, artifactId, version)
-
通过Maven坐标(groupId、artifactId和version)指定规则资源的位置。
-
需要在Maven仓库中存在相应的规则资源(JAR包)。
-
适用于从远程Maven仓库或本地Maven仓库加载规则资源的场景。
-
总结
在KieServices中,实例化KieContainer其实就两个方法,一个是从类路径加载规则资源的newKieClasspathContainer,一个是从Maven仓库中加载资源的newKieContainer,如果直接使用getKieClasspathContainer,第一次用会默认使用newKieClasspathContainer,之后再使用就是可以直接获取KieServicesImpl实例中对应的KieContainer。