之前梳理了一下有关KieServices的获取,与获取中的代码走向,详情请见:

“万恶”之源的KieServices,获取代码就一行,表面代码越少里面东西就越多,本以为就是个简单的工厂方法,没想到里面弯弯绕绕这么多东西_zcrazy胡说八道的博客-CSDN博客

规则的加载与管理者——KieContainer的获取与其类型的区别(虽然标题是KieContainer,其实说的还是KieServices)-LMLPHP 在我使用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到底是个什么呢?

规则资源又是什么呢?

简单来说,就是咱们项目里面的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仓库加载规则资源的场景。

总结

规则的加载与管理者——KieContainer的获取与其类型的区别(虽然标题是KieContainer,其实说的还是KieServices)-LMLPHP

在KieServices中,实例化KieContainer其实就两个方法,一个是从类路径加载规则资源的newKieClasspathContainer,一个是从Maven仓库中加载资源的newKieContainer,如果直接使用getKieClasspathContainer,第一次用会默认使用newKieClasspathContainer,之后再使用就是可以直接获取KieServicesImpl实例中对应的KieContainer。 

08-17 16:12