之前梳理了一下有关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作为containerIdthis.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为nullnewContainer = 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映射中没有该containerIdnewContainer = 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已经有当前containerIdnewContainer.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。