3.庫文件介紹
概述
URule Pro整個(gè)産品由兩部分構成:一個(gè)是設計(jì)器(qì)部分;另一個(gè)是規則執行(xíng)引擎部分。設計(jì)器(qì)部分主要是由庫文件設計(jì)器(qì)以及具體(tǐ)的規則文件設計(jì)器(qì)兩部分構成。
庫文件設計(jì)器(qì)包括變量庫設計(jì)器(qì)、參數(shù)庫設計(jì)器(qì)、常量庫設計(jì)器(qì)以及動作(zuò)庫設計(jì)器(qì)四個(gè)部分,通(tōng)過這些(xiē)庫文件設計(jì)器(qì),可(kě)以将業務系統中使用的實體(tǐ)對象、枚舉值、常量以及需要在規則中調用的Java方法映射到引擎中備用。
規則文件設計(jì)器(qì)主要包括規則集、決策表、交叉決策表(決策矩陣)、決策樹(shù)、評分卡、複雜評分卡、規則流等八種類型的業務規則設計(jì)工具,這些(xiē)設計(jì)器(qì)全部是以可(kě)視(shì)化圖形方式提供,使用者隻需要通(tōng)過鼠标點擊或拖拽的方式即可(kě)完成業務規則的定義。通(tōng)過在這些(xiē)規則設計(jì)器(qì)中引入各種類型的已定義好的庫文件,就可(kě)以和(hé)業務系統結合起來(lái),從而設計(jì)出符合需求的業務規則。
這裏我們首先來(lái)了解一下URule Pro當中提供的庫文件,學習其配置及使用方法。
庫文件設計(jì)器(qì)中,對于新增數(shù)據,在沒保存之前,是可(kě)以直接點擊單元格進行(xíng)編輯的,一旦對數(shù)據進行(xíng)了保存,如果需要對保存後的數(shù)據進行(xíng)編輯,那(nà)麽隻能點擊數(shù)據所在行(xíng)中操作(zuò)列上(shàng)的“編輯”按鈕實現。
之所以這樣設計(jì),是因為(wèi)庫文件會(huì)被其它規則文件引用使用,一旦文件保存後,就存在被其它文件引用使用的可(kě)能性,這樣的話(huà),引用數(shù)據如果允許直接修改就會(huì)導緻引用數(shù)據與庫文件中定義數(shù)據不一緻的情況, 所以需要通(tōng)過操作(zuò)列上(shàng)的“編輯”按鈕才能實現對已保存的數(shù)據進行(xíng)編輯。
這裏的“編輯”操作(zuò)保存時(shí),實際上(shàng)會(huì)嘗試取到所有(yǒu)引用當前庫文件當前數(shù)據的規則文件,然後同步修改成最新的值。
變量庫文件
在業務系統開(kāi)發過程中,會(huì)用到大(dà)量包含Getter和(hé)Setter方法的簡單的Java對象,它們被稱之為(wèi)POJO(Plain Ordinary Java Object),或BOM(Business Object Model)對象,這些(xiē)對象在開(kāi)發中作(zuò)為(wèi)數(shù)據的載體(tǐ),負責數(shù)據的傳遞。在URule Pro當中,變量庫就是用來(lái)映射這些(xiē)POJO對象,從而使得(de)我們可(kě)以在具體(tǐ)的規則文件中使用它們,從而完成規則與業務數(shù)據的交互。
打開(kāi)URule Pro的操作(zuò)控制(zhì)台,創建一個(gè)項目,在項目的“庫”的節點上(shàng)點擊右鍵,在彈出的菜單中選擇“添加變量庫”就可(kě)以創建變量庫文件,如下圖所示:
果你(nǐ)創建的項目中資源節點下沒有(yǒu)“庫”節點,那(nà)是因為(wèi)你(nǐ)修改了項目知識庫內(nèi)容的展示方式,點擊左上(shàng)角,在彈出的菜單中選擇“分類展示”,這樣就可(kě)以看到“庫”節點。
在URule Pro當中,通(tōng)過這個(gè)功能可(kě)以修改項目知識庫內(nèi)容的展示方式,選擇“分類展示”,就可(kě)以看到上(shàng)圖所示效果,将項目中的庫文件、決策集文件、決策表文件、評分卡文件、決策流文件分類存放展示;如果選擇“集中展示”,那(nà)麽就不會(huì)對這些(xiē)文件進行(xíng)分類,統一放在資源節點下。
需要注意的是,在“分類展示”模式下,如果創建有(yǒu)目錄,那(nà)麽這個(gè)目錄會(huì)出現在所有(yǒu)的分類下,同樣如果在某個(gè)分類下删除某個(gè)目錄,那(nà)麽這個(gè)目錄也将會(huì)從所有(yǒu)的分類中删除,創建目錄也是一樣。在“集中展示”模式下,因為(wèi)不進行(xíng)分類,所以目錄就不存在這種現象,這點需要注意。
在添加文件或目錄時(shí),目錄或文件名隻能使用英文或中文,不支持其它非法字符。
創建好變量庫文件後,可(kě)以看到系統會(huì)用變量庫編輯器(qì)自動打開(kāi)這個(gè)文件。在這個(gè)編輯器(qì)中,首先需要添加變量的分類,然後再添加具體(tǐ)的變量字段。對應到Java實體(tǐ)對象,就是要添加對應的實體(tǐ)對象信息,再添加這個(gè)實體(tǐ)對象所擁有(yǒu)的屬性信息。如下圖所示:
添加一個(gè)分類,輸入名稱,這個(gè)名稱是對當前分類的描述,會(huì)在規則中直接引用顯示,所以一般我們會(huì)使用中文描述來(lái)作(zuò)為(wèi)名稱,類路徑,就是這個(gè)分類對應的實體(tǐ)類的完整路徑,比如上(shàng)圖中的“com.bstek.entity.Customer”。
變量的類路徑是規則引擎在執行(xíng)過程中查找對應對象的唯一标識,所以在定義時(shí)一定要讓其與實際業務中對應的POJO對象的完整類路徑一緻,否則在運行(xíng)時(shí)将會(huì)出現找不到類的情況,或者使得(de)規則在計(jì)算(suàn)時(shí)不會(huì)出現我們期望的結果。
這時(shí),變量的分類就定義好了,如果當前定義的類路徑對應的類在當前應用中存在的話(huà),那(nà)麽我們可(kě)以點擊“操作(zuò)列“中的第一個(gè)按鈕,這樣系統就會(huì)通(tōng)過Java的反射功能生(shēng)成當前類對應的所有(yǒu)字段信息。上(shàng)圖中com.bstek.entity.Customer類源碼如下所示:
package com.bstek.entity;
import java.util.Date;
import com.bstek.urule.model.Label;
/**
* @author Jacky.gao
* @since 2016年9月29日
*/
public class Customer {
@Label("名稱")
private String name;
@Label("年齡")
private int age;
@Label("出生(shēng)日期")
private Date birthday;
@Label("等級")
private int level;
@Label("手機号")
private String mobile;
@Label("性别")
private boolean gender;
@Label("是否有(yǒu)車(chē)")
private boolean car;
@Label("婚否")
private boolean married;
@Label("是否有(yǒu)房(fáng)")
private boolean house;
//省略上(shàng)述所有(yǒu)屬性對應的getter與setter方法......
}
在這個(gè)類當中,可(kě)以看到每個(gè)屬性都有(yǒu)一個(gè)名為(wèi)Label的annotation,它是一個(gè)由URule Pro提供的用來(lái)描述字段屬性的annotation,這裏描述的值,在生(shēng)成變量時(shí)會(huì)自動寫到變量的“标題”當中,這裏的标題将會(huì)在規則中直接引用,讓标題有(yǒu)意義,對于規則的清晰表達很(hěn)有(yǒu)價值。
如果這個(gè)類在當前所在的項目當中,所以可(kě)以直接通(tōng)過反射生(shēng)成所有(yǒu)的屬性,生(shēng)成後的效果如下:
可(kě)以看到上(shàng)圖中Label這個(gè)annotation對應的描述寫入到變量的“标題”當中。
在URule的服務器(qì)客戶端模式下,我們的規則都是在服務器(qì)上(shàng)定義的,這就有(yǒu)可(kě)能定義變量的時(shí)候變量分類對應的實體(tǐ)類在服務器(qì)上(shàng)不存在,而隻在客戶端上(shàng)存在,這種情況下就不能通(tōng)過反射來(lái)生(shēng)成對應的字段,這時(shí)我們可(kě)以在有(yǒu)這個(gè)實體(tǐ)類的客戶端應用中通(tōng)過URule Pro中提供的com.bstek.urule.ClassUtils來(lái)生(shēng)成目标實體(tǐ)類的xml描述文件,然後在到服務器(qì)上(shàng),點擊變量分類“操作(zuò)列”上(shàng)中間(jiān)那(nà)個(gè)上(shàng)傳圖标将這個(gè)xml描述文件上(shàng)傳,同樣可(kě)以生(shēng)成對應的字段信息。使用ClassUtils類生(shēng)成描述文件的代碼如下所示:
public static void main(String[] args) {
File file=new File("d:/customer.xml");
ClassUtils.classToXml(Customer.class, file);
}
運行(xíng)這個(gè)main方法,就會(huì)在D盤下生(shēng)成一個(gè)customer.xml的實體(tǐ)類描述文件,再上(shàng)傳這個(gè)文件即可(kě)。
在定義變量庫文件時(shí),對應的實體(tǐ)類不一定真實存在。在仿真測試中,在運行(xíng)規則時(shí),如果發現對應的實體(tǐ)類不在當前JVM的classpath中時(shí),引擎會(huì)使用一個(gè)名為(wèi)GeneralEntity的類來(lái)代替這目标實現類運行(xíng)。所以在URule Pro的客戶端服務器(qì)模式下,服務器(qì)上(shàng)定義變量庫時(shí),對應的實體(tǐ)類一般都不在服務器(qì)上(shàng),而位于調用規則運行(xíng)的客戶端上(shàng),但(dàn)對于服務器(qì)上(shàng)規則定義與測試是沒有(yǒu)影(yǐng)響的。
這到裏,變量庫文件就定義好了,可(kě)以根據需要在一個(gè)文件中添加多(duō)個(gè)變量分類,相應對應到多(duō)個(gè)POJO類。
變量定義好了之後,會(huì)被其它類型的規則文件引用,如果後期變量需要需要修改,在設計(jì)界面中提供了變更名的重構功能。在上(shàng)面的操作(zuò)中我們可(kě)以看到,無論是變量的分類名稱,還(hái)是具體(tǐ)的變量名在其操作(zuò)列上(shàng)都有(yǒu)一個(gè)圖标,點擊該圖标就可(kě)以對當前行(xíng)的變量名進行(xíng)修改,這樣就可(kě)以同步修改所有(yǒu)引用當前變量的規則文件,從而完成了變量名的重構。
在後面介紹的常量、參數(shù)、動作(zuò)庫文件中,對于它們的名稱的修改,同樣提供重構功能的支持,這裏不再贅述。
常量庫文件
在業務系統開(kāi)發過程中,常常會(huì)用到一個(gè)枚舉數(shù)據,比如用戶的性别、學曆等,在URule Pro當中,通(tōng)過定義常量庫文件,可(kě)以将系統中使用的這些(xiē)枚舉數(shù)據映射到規則中使用,這樣就可(kě)以避免規則定義過程中枚舉數(shù)據手工輸入存在錯誤的可(kě)能性。
選中我們的項目,在“庫”節點上(shàng)右鍵,創建一個(gè)常量庫文件,如下圖所示:
與變量庫文件類似,常量也是由分類和(hé)具體(tǐ)的常量值構成,比如性别有(yǒu)男女之分,那(nà)麽這裏的“性别”就屬性分類,“男”、“女”就屬性具體(tǐ)的常量值。在常量的分類中,“名稱”一般定義具體(tǐ)的分類名,“标題”是一段描述(比如“性别”是标題,“gender”是名稱),同樣這個(gè)标題也會(huì)出現在規則引用當中;加好分類後就可(kě)以添加這個(gè)分類下具體(tǐ)的常量值,常量值也有(yǒu)名稱和(hé)标題之分,名稱是具體(tǐ)的常量值,标題則是描述,比如“男”是标題,"true"是名稱,同樣“女”是标題,“false”是名稱,如下圖所示:
同樣,在一個(gè)變量庫文件中可(kě)以根據需要添加多(duō)個(gè)變量分類。
常量定義時(shí),其名稱值可(kě)使用Spring中加載的properties文件值,具體(tǐ)使用方法是将要引用的properties的key值用${...}包裹,這樣在具體(tǐ)規則運行(xíng)時(shí)會(huì)動态查找這個(gè)包裹的屬性值作(zuò)為(wèi)具體(tǐ)的常量,如:${app.title}表示取spring中properties文件的名為(wèi)app.title的屬性值。利用這一功能,可(kě)實現測試環境與生(shēng)産環境的動态切換。
從2.1.9版本開(kāi)始,常量庫文件在定義時(shí)支持從一個(gè)具體(tǐ)的Java枚舉類中直接導入,方法是點擊圖标,在彈出的窗口中輸入完整的枚舉類路徑即可(kě)實現将枚舉類裏的枚舉值導入到當前常量分類之下。
在通(tōng)過枚舉類導入具體(tǐ)常量時(shí),如果枚舉類中未定義getLabel方法,那(nà)麽導入的枚舉信息則隻采用其name屬性,如果定義了getLabel方法,那(nà)麽導入後的枚舉信息則包含name和(hé)label,包含getLabel方法的枚舉類示例如下所示:
public enum TestEnum { aaa("張三"),bbb("李四"); private String label; private TestEnum(String label) { this.label=label; } public String getLabel() { return label; } }
參數(shù)庫文件
在規則的條件判斷與計(jì)算(suàn)過程當中,難免會(huì)用到一些(xiē)臨時(shí)的變量來(lái)存儲值,這些(xiē)臨時(shí)變量數(shù)量和(hé)類型都可(kě)能是不固定的,對于這種類型的臨時(shí)變量,URule Pro以參數(shù)的形式提供,通(tōng)過參數(shù)庫就可(kě)以定義這些(xiē)在規則中要使用到的臨時(shí)變量。
在“庫”節點上(shàng)右鍵創建一個(gè)參數(shù)庫文件,從參數(shù)庫文件編輯器(qì)來(lái)看,參數(shù)因為(wèi)沒有(yǒu)了分類,配置要為(wèi)簡單許多(duō),如下圖所示:
參數(shù)庫在運行(xíng)時(shí)實際上(shàng)是存儲在HashMap當中,這裏的“名稱”的值将作(zuò)為(wèi)Map的key,“标題”則用在規則中顯示使用,定義參數(shù)庫時(shí)要保證“名稱”屬性的唯一性,因為(wèi)它是Map的key值; 同時(shí)如果一個(gè)規則文件裏引入多(duō)個(gè)參數(shù)庫文件,那(nà)麽每個(gè)參數(shù)庫文件裏定義的參數(shù)的名稱值也要唯一,否則就會(huì)存在相互覆蓋的情況。
在URule Pro當中,對于參數(shù)庫中定義的值,規則運行(xíng)時(shí),如果外部沒有(yǒu)對這些(xiē)參數(shù)進行(xíng)初始化,那(nà)麽引擎會(huì)自動為(wèi)部分沒有(yǒu)初始化的參數(shù)進行(xíng)初始化。下表中介紹了URule Pro中會(huì)自動初始化的數(shù)據類型自動初始化後的值。
數(shù)據類型 | 初始化值 |
---|---|
Integer | 0 |
Double | 0 |
Float | 0 |
Boolean | false |
List | new ArrayList() |
Set | new HashSet() |
Map | new HashMap() |
如果我們當前項目中已定義好若幹個(gè)變量庫文件,且文件中都定義了相應的變量對象,那(nà)麽在打開(kāi)參數(shù)庫文件,添加參數(shù)時(shí),我們會(huì)發現參數(shù)的數(shù)據類型除了固定的13種數(shù)據類型外, 還(hái)有(yǒu)當前項目中所有(yǒu)變量庫文件裏定義的變量分類值;如果在定義參數(shù)時(shí)數(shù)據類型選擇為(wèi)某個(gè)變量的分類名,那(nà)麽在使用這個(gè)參數(shù)時(shí),除了可(kě)以選擇參數(shù)自身,還(hái)能選擇參數(shù)對應的變量下的具體(tǐ)屬性值,如下圖所示:
因為(wèi)參數(shù)的數(shù)據類型可(kě)以指定為(wèi)某個(gè)變量類型,所以在設計(jì)一些(xiē)複雜規則的時(shí)候就顯得(de)特别有(yǒu)意義,比如我們可(kě)以通(tōng)過這種方式創建一個(gè)新的變量對象實例, 然後賦值給某個(gè)是這個(gè)變量數(shù)據類型的參數(shù),然後再通(tōng)過這個(gè)參數(shù)給這個(gè)新的對象實例的屬性進行(xíng)逐個(gè)賦值等。
動作(zuò)庫文件
動作(zuò)庫文件的作(zuò)用是對配置在spring中的bean方法進行(xíng)映射,使得(de)我們可(kě)以直接在規則當中調用這些(xiē)方法。同樣在項目的“庫”節點下創建一個(gè)動作(zuò)庫文件,可(kě)以看到動作(zuò)庫文件內(nèi)容有(yǒu)三列,分别是動作(zuò)名稱,bean的id定義列,方法名定義列,以及方法對應的參數(shù)定義列,如下圖所示:
為(wèi)了演示這裏定義方法的具體(tǐ)操作(zuò),下面是一個(gè)名為(wèi)MethodTest的類源碼:
package rete.test;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.bstek.urule.action.ActionId;
import com.bstek.urule.model.ExposeAction;
/**
* @author Jacky.gao
* @since 2015年1月14日
*/
public class MethodTest {
@ActionId("Hello")
public String hello(){
return "hello";
}
@ExposeAction(value="方法1",parameters={"用戶名"})
public boolean evalTest(String username){
if(username==null){
return false;
}else if(username.equals("張三")){
return true;
}
return false;
}
@ExposeAction(value="測試Int",parameters={"數(shù)字1","數(shù)字2"})
public int testInt(int a,int b){
return a+b;
}
public int testInteger(Integer a,int b){
return a+b*10;
}
@ExposeAction(value="打印內(nèi)容",parameters={"用戶名","出生(shēng)日期"})
public void printContent(String username,Date birthday){
SimpleDateFormat sd=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
if(birthday!=null){
System.out.println(username+"今年已經"+sd.format(birthday)+"歲了!");
}else{
System.out.println("Hello "+username+"");
}
}
@ExposeAction(value="打印Member",parameters={"Member對象"})
public void printUser(Member m){
System.out.println("Hello "+m.getUsername()+", has house:"+m.isHouse());
}
}
在這個(gè)MethodTest類中,我們對需要在動作(zuò)庫中引用的方法上(shàng)都添加了一個(gè)名為(wèi)ExposeAction的annotation,這就表示這個(gè)方法可(kě)以暴露給規則引用,反之如果不加這個(gè)annotation,那(nà)麽這個(gè)方法就不能在定義動作(zuò)時(shí)直接引用。 同樣我們也看到對于需要在動作(zuò)庫中引用的方法是不需要實現任何接口的,方法簽名也是任意的,隻需要在方法上(shàng)添加ExposeAction這個(gè)annotation即可(kě),接下來(lái),我們需要将這個(gè)類配置到spring中,讓其成為(wèi)一個(gè)标準的bean,spring中的配置如下:
<bean id="methodTest" class="rete.test.MethodTest"></bean>
定義動作(zuò)庫的Bean時(shí),一定不要忘記給Bean定義一個(gè)Id,這樣才能保證規則在任何地方運行(xíng)都不會(huì)出錯,這點很(hěn)關鍵。
回到動作(zuò)庫文件編輯器(qì),點擊“添加Bean”按鈕,添加一條Bean定義信息,将"Bean Id"屬性值改為(wèi)我們這裏的“methodTest”,點擊當前行(xíng)“操作(zuò)列”中第一個(gè)圖标選擇這個(gè)bean中的方法,以實現bean方法的映射,如下圖所示:
所有(yǒu)添加了ExposeAction的annotation的方法都出同在這個(gè)列表中,我們可(kě)以根據選擇添加即可(kě),可(kě)以看到添加的方法會(huì)自動将這個(gè)方法所需要的參數(shù)加載進去,添加成功後,為(wèi)了在規則中更好的可(kě)讀性,我們可(kě)以修改“動作(zuò)名稱”、“方法名稱”以及“參數(shù)名稱”。
可(kě)以看到,所有(yǒu)的庫文件除了可(kě)以保存外,還(hái)提供了一個(gè)名為(wèi)“保存為(wèi)版本”的按鈕,通(tōng)過這個(gè)按鈕,可(kě)以将當前文件內(nèi)容保存為(wèi)一個(gè)新的版本,這樣在規則中就可(kě)以引用特定版本,特定版本的庫文件不會(huì)被修改。實際上(shàng)在URule Pro中所有(yǒu)的文件都可(kě)以保存為(wèi)新版本。
“查看引用”按鈕,就是用來(lái)查看當前庫文件被哪些(xiē)規則文件使用了,會(huì)有(yǒu)個(gè)列表顯示出來(lái),方便我們後續維護。
在動作(zuò)庫定義的時(shí)候需要注意,如果我們規則運行(xíng)方式采用的是客戶端服務器(qì)模式(參見第16小(xiǎo)節內(nèi)容介紹),那(nà)麽必須要保證調用知識包的客戶端Spring環境裏有(yǒu)這個(gè)Bean,且Bean的Id要與動作(zuò)庫定義時(shí)的Id保證一至,否則調用會(huì)出現錯誤。
變量庫、參數(shù)庫、動作(zuò)庫、常量庫這些(xiē)庫文件定義好後,就可(kě)以在各種類型的規則文件中導入并使用它們,一旦某個(gè)庫文件在規則中被使用,我們就不能再随便修改這些(xiē)已定義好的庫文件的名稱、值或數(shù)據類型, 如果因為(wèi)業務調整需要必須要進行(xíng)修改,那(nà)麽可(kě)以通(tōng)過這些(xiē)變量、常量、參數(shù)、動作(zuò)定義的操作(zuò)列上(shàng)的修改圖标來(lái)對它們進行(xíng)修改,這樣可(kě)以保證引用文件同步更新。