相信看過Drools的人一定看到過它最簡單的HelloWorld的例子,接下來讓我們看下它究竟爲我們做了那些事情。
例子如下:
HelloWorld.java
public class HelloWorldExample {
public static final void main(final String[] args) {
KieServices ks = KieServices.Factory.get();
KieContainer kc = ks.getKieClasspathContainer();
KieSession ksession = kc.newKieSession("HelloWorldKS");
final Message message = new Message();
message.setMessage( "Hello World" );
message.setStatus( Message.HELLO );
ksession.insert( message );
ksession.fireAllRules();
ksession.dispose();
}
public static class Message {
public static final int HELLO = 0;
public static final int GOODBYE = 1;
private String message;
private int status;
//....
}
helloWorld.drl
package org.drools.examples.helloworld
import org.drools.examples.helloworld.HelloWorldExample.Message;
rule "Hello World"
dialect "mvel"
when
m : Message( status == Message.HELLO, message : message )
then
System.out.println( message );
modify ( m ) { message = "Goodbye cruel world",
status = Message.GOODBYE };
end
rule "Good Bye"
dialect "java"
when
Message( status == Message.GOODBYE, message : message )
then
System.out.println( message );
end
<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://jboss.org/kie/6.0.0/kmodule">
<kbase name="HelloWorldKB" packages="org.drools.examples.helloworld">
<ksession name="HelloWorldKS"/>
</kbase>
</kmodule>
關於Drools規則如何編寫,KModule.xml如何寫,如何調用規則這些請參考Drools-6.1.0.Final_入門文檔。
我想通過對源碼的解析來介紹KIE API爲我們方便的操控引擎做了哪些事情。
KIE的API中有以下的幾個類和接口需要介紹下:
KieServices
該接口提供了很多方法,可以通過這些方法訪問KIE關於構建和運行的相關對象,比如說可以獲取KieContainer,利用KieContainer來訪問KBase和KSession等信息;可以獲取KieRepository對象,利用KieRepository來管理KieModule等。
KieServices就是一箇中心,通過它來獲取的各種對象來完成規則構建、管理和執行等操作。KieContainer
可以理解KieContainer就是一個KieBase的容器。KieBase
KieBase就是一個知識倉庫,包含了若干的規則、流程、方法等,在Drools中主要就是規則和方法,KieBase本身並不包含運行時的數據之類的,如果需要執行規則KieBase中的規則的話,就需要根據KieBase創建KieSession。KieSession
KieSession就是一個跟Drools引擎打交道的會話,其基於KieBase創建,它會包含運行時數據,包含“事實 Fact”,並對運行時數據事實進行規則運算。我們通過KieContainer創建KieSession是一種較爲方便的做法,其實他本質上是從KieBase中創建出來KieRepository
KieRepository是一個單例對象,它是一個存放KieModule的倉庫,KieModule由kmodule.xml文件定義(當然不僅僅只是用它來定義)。KieProject
KieContainer通過KieProject來初始化、構造KieModule,並將KieModule存放到KieRepository中,然後KieContainer可以通過KieProject來查找KieModule定義的信息,並根據這些信息構造KieBase和KieSession。ClasspathKieProject
ClasspathKieProject實現了KieProject接口,它提供了根據類路徑中的META-INF/kmodule.xml文件構造KieModule的能力,也就是我們能夠基於Maven構造Drools組件的基本保障之一。
KieServices ks = KieServices.Factory.get()
KieServices實際上是一個接口,但是接口中有一個靜態工廠用來得到實例化的KieServicesImpl.
KieContainer kc = ks. getKieClasspathContainer()
得到的KieServices 通過調用方法讀取配置文件然後得到Kie容器。
public KieContainer getKieClasspathContainer () {
return getKieClasspathContainer( findParentClassLoader() );
}
ClassLoader是類加載器,用來加載Java類到Java虛擬機。首先根據findParentClassLoader()獲得類加載器然後把相關的父類父接口都放進類加載器中。
然後調用getKieClasspathContainer(ClassLoader loader),返回當前存在的KieContainer或者實例化一個KieContainer返回。
實例化新的Container返回:
public KieContainer newKieClasspathContainer(ClassLoader classLoader) {
return new KieContainerImpl(new ClasspathKieProject(classLoader, listener), null);
}
ClasspathKieProject主要作用是根據根據類路徑中的META-INF/kmodule.xml文件構造KieModule。他是什麼時候進行構造呢?
是在ClasspathKieProject實例化然後傳入KieContainerImpl的構造函數中:
public KieContainerImpl(KieProject kProject, KieRepository kr) {
this.kr = kr;
this.kProject = kProject;
kProject.init();//這纔是關鍵
}
調用的是ClasspathKieProject實現的KieProject的init()方法.
public void init() {
this.classLoader = createProjectClassLoader(parentCL);
discoverKieModules();
indexParts(kieModules.values(), kJarFromKBaseName);
}
其中this.classLoader = createProjectClassLoader(parentCL)因爲classLoader沒怎麼深入的瞭解,所以有點不清不楚。
discoverKieModules()
很關鍵的一個方法,他通過兩個路徑KMODULE_JAR_PATH、KMODULE_SPRING_JAR_PATH來查找相應的META-INF下的kmodule.xml或者是kmodule-spring.xml,通過這兩個配置文件來構造相應的kieModules。然後將得到的KieModules放入Map
String[] configFiles = {KieModuleModelImpl.KMODULE_JAR_PATH, KieModuleModelImpl.KMODULE_SPRING_JAR_PATH};
比如我在META-INF中放了一個kmodule.xml,Jar包中引入了一個drools-examples-6.2.0.CR4.jar,因此裏面也有一個kmodule.xml。所以在運行完後kieModules如下:
{org.drool:droolDemo:0.0.1-SNAPSHOT=FileKieModule[ ReleaseId=org.drool:droolDemo:0.0.1-SNAPSHOTfile=E:\java\workspace2\droolDemo\target\classes], org.drools:drools-examples:6.2.0.CR4=ZipKieModule[ ReleaseId=org.drools:drools-examples:6.2.0.CR4file=C:\Users\Administrator.m2\repository\org\drools\drools-examples\6.2.0.CR4\drools-examples-6.2.0.CR4.jar]}
可以看出一個是Jar包中讀取到的配置文件,一個是項目中的classpath下配置文件。
indexParts(..)
protected void indexParts(Collection<InternalKieModule> kieModules,Map<String, InternalKieModule> kJarFromKBaseName);
源碼太長不引人了,主要功能是根據上一步的到的kieModules,獲得相應KieModuleModel、KieBaseModel、KieSessionModel然後放入AbstractKieProject所對應的Map以及對象中。
private KieBaseModel defaultKieBase = null;
private KieSessionModel defaultKieSession = null;
private KieSessionModel defaultStatelessKieSession = null;
這些用來放Kmodule.xml中定義爲default的KieBase、KieSession、StatelessKieSession。而這邊只有一個對象,所以爲什麼默認的只有一個的緣故。當然要是有多個,newKieSession()怎麼知道哪一個。
protected final Map<String, KieBaseModel> kBaseModels;
Map<KieBaseModel, Set<String>> includesInKieBase;
protected final Map<String, KieSessionModel> kSessionModels;
之後我們都是通過KieSession的name找到相應KieSessionModel,通過KieBase的name找到相應的KieBaseModel。然後通過相應的KieSessionModel、KieBaseModel來得到相應的KieSession以及KieBase。
KieSession ksession = kc.newKieSession(“HelloWorldKS”);
KieSession ksession = kc.newKieSession("HelloWorldKS");
KieBase kiebase = kc.getKieBase("kieBase");
public KieSession newKieSession(String kSessionName, Environment environment, KieSessionConfiguration conf) {
//傳入的environment、conf都是null。KieSessionConfiguration用法在Drools6新特性中有提到過。
KieSessionModelImpl kSessionModel = (KieSessionModelImpl) getKieSessionModel(kSessionName);
//DO1 驗證是否找到相應的Model
//DO2 驗證生成Model是否是StateFul,默認生成的KieSession都是stateFul
//DO3 找到相應的KieBase
//DO4 根絕KieBase以及KSessionModel生成相應的KieSession
//DO5 放入一個存儲有狀態的KieSession的Map,之後可以在需要的時候先從Map裏面找。
}
ksession.insert( message );
public FactHandle insert(final Object object) {
return insert( object, /* Not-Dynamic */
null,false,false,null,null );
}
public FactHandle insert(final Object object,final Object tmsValue,final boolean dynamic,boolean logical,final RuleImpl rule,final Activation activation) {
checkAlive();
return this.defaultEntryPoint.insert( object,
tmsValue,dynamic,logical,rule,activation );
}
在向KieSession插入fact,規則引擎首先檢查的是KieSession是否還能使用。