3.库文件介绍
概述
URule Pro整个产品由两部分构成:一个是设计器部分;另一个是规则执行引擎部分。设计器部分主要是由库文件设计器以及具体的规则文件设计器两部分构成。
库文件设计器包括变量库设计器、参数库设计器、常量库设计器以及动作库设计器四个部分,通过这些库文件设计器,可以将业务系统中使用的实体对象、枚举值、常量以及需要在规则中调用的Java方法映射到引擎中备用。
规则文件设计器主要包括规则集、决策表、交叉决策表(决策矩阵)、决策树、评分卡、复杂评分卡、规则流等八种类型的业务规则设计工具,这些设计器全部是以可视化图形方式提供,使用者只需要通过鼠标点击或拖拽的方式即可完成业务规则的定义。通过在这些规则设计器中引入各种类型的已定义好的库文件,就可以和业务系统结合起来,从而设计出符合需求的业务规则。
这里我们首先来了解一下URule Pro当中提供的库文件,学习其配置及使用方法。
库文件设计器中,对于新增数据,在没保存之前,是可以直接点击单元格进行编辑的,一旦对数据进行了保存,如果需要对保存后的数据进行编辑,那么只能点击数据所在行中操作列上的“编辑”按钮实现。
之所以这样设计,是因为库文件会被其它规则文件引用使用,一旦文件保存后,就存在被其它文件引用使用的可能性,这样的话,引用数据如果允许直接修改就会导致引用数据与库文件中定义数据不一致的情况, 所以需要通过操作列上的“编辑”按钮才能实现对已保存的数据进行编辑。
这里的“编辑”操作保存时,实际上会尝试取到所有引用当前库文件当前数据的规则文件,然后同步修改成最新的值。
变量库文件
在业务系统开发过程中,会用到大量包含Getter和Setter方法的简单的Java对象,它们被称之为POJO(Plain Ordinary Java Object),或BOM(Business Object Model)对象,这些对象在开发中作为数据的载体,负责数据的传递。在URule Pro当中,变量库就是用来映射这些POJO对象,从而使得我们可以在具体的规则文件中使用它们,从而完成规则与业务数据的交互。
打开URule Pro的操作控制台,创建一个项目,在项目的“库”的节点上点击右键,在弹出的菜单中选择“添加变量库”就可以创建变量库文件,如下图所示:
果你创建的项目中资源节点下没有“库”节点,那是因为你修改了项目知识库内容的展示方式,点击左上角,在弹出的菜单中选择“分类展示”,这样就可以看到“库”节点。
在URule Pro当中,通过这个功能可以修改项目知识库内容的展示方式,选择“分类展示”,就可以看到上图所示效果,将项目中的库文件、决策集文件、决策表文件、评分卡文件、决策流文件分类存放展示;如果选择“集中展示”,那么就不会对这些文件进行分类,统一放在资源节点下。
需要注意的是,在“分类展示”模式下,如果创建有目录,那么这个目录会出现在所有的分类下,同样如果在某个分类下删除某个目录,那么这个目录也将会从所有的分类中删除,创建目录也是一样。在“集中展示”模式下,因为不进行分类,所以目录就不存在这种现象,这点需要注意。
在添加文件或目录时,目录或文件名只能使用英文或中文,不支持其它非法字符。
创建好变量库文件后,可以看到系统会用变量库编辑器自动打开这个文件。在这个编辑器中,首先需要添加变量的分类,然后再添加具体的变量字段。对应到Java实体对象,就是要添加对应的实体对象信息,再添加这个实体对象所拥有的属性信息。如下图所示:
添加一个分类,输入名称,这个名称是对当前分类的描述,会在规则中直接引用显示,所以一般我们会使用中文描述来作为名称,类路径,就是这个分类对应的实体类的完整路径,比如上图中的“com.bstek.entity.Customer”。
变量的类路径是规则引擎在执行过程中查找对应对象的唯一标识,所以在定义时一定要让其与实际业务中对应的POJO对象的完整类路径一致,否则在运行时将会出现找不到类的情况,或者使得规则在计算时不会出现我们期望的结果。
这时,变量的分类就定义好了,如果当前定义的类路径对应的类在当前应用中存在的话,那么我们可以点击“操作列“中的第一个按钮,这样系统就会通过Java的反射功能生成当前类对应的所有字段信息。上图中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("出生日期")
private Date birthday;
@Label("等级")
private int level;
@Label("手机号")
private String mobile;
@Label("性别")
private boolean gender;
@Label("是否有车")
private boolean car;
@Label("婚否")
private boolean married;
@Label("是否有房")
private boolean house;
//省略上述所有属性对应的getter与setter方法......
}
在这个类当中,可以看到每个属性都有一个名为Label的annotation,它是一个由URule Pro提供的用来描述字段属性的annotation,这里描述的值,在生成变量时会自动写到变量的“标题”当中,这里的标题将会在规则中直接引用,让标题有意义,对于规则的清晰表达很有价值。
如果这个类在当前所在的项目当中,所以可以直接通过反射生成所有的属性,生成后的效果如下:
可以看到上图中Label这个annotation对应的描述写入到变量的“标题”当中。
在URule的服务器客户端模式下,我们的规则都是在服务器上定义的,这就有可能定义变量的时候变量分类对应的实体类在服务器上不存在,而只在客户端上存在,这种情况下就不能通过反射来生成对应的字段,这时我们可以在有这个实体类的客户端应用中通过URule Pro中提供的com.bstek.urule.ClassUtils来生成目标实体类的xml描述文件,然后在到服务器上,点击变量分类“操作列”上中间那个上传图标将这个xml描述文件上传,同样可以生成对应的字段信息。使用ClassUtils类生成描述文件的代码如下所示:
public static void main(String[] args) {
File file=new File("d:/customer.xml");
ClassUtils.classToXml(Customer.class, file);
}
运行这个main方法,就会在D盘下生成一个customer.xml的实体类描述文件,再上传这个文件即可。
在定义变量库文件时,对应的实体类不一定真实存在。在仿真测试中,在运行规则时,如果发现对应的实体类不在当前JVM的classpath中时,引擎会使用一个名为GeneralEntity的类来代替这目标实现类运行。所以在URule Pro的客户端服务器模式下,服务器上定义变量库时,对应的实体类一般都不在服务器上,而位于调用规则运行的客户端上,但对于服务器上规则定义与测试是没有影响的。
这到里,变量库文件就定义好了,可以根据需要在一个文件中添加多个变量分类,相应对应到多个POJO类。
变量定义好了之后,会被其它类型的规则文件引用,如果后期变量需要需要修改,在设计界面中提供了变更名的重构功能。在上面的操作中我们可以看到,无论是变量的分类名称,还是具体的变量名在其操作列上都有一个图标,点击该图标就可以对当前行的变量名进行修改,这样就可以同步修改所有引用当前变量的规则文件,从而完成了变量名的重构。
在后面介绍的常量、参数、动作库文件中,对于它们的名称的修改,同样提供重构功能的支持,这里不再赘述。
常量库文件
在业务系统开发过程中,常常会用到一个枚举数据,比如用户的性别、学历等,在URule Pro当中,通过定义常量库文件,可以将系统中使用的这些枚举数据映射到规则中使用,这样就可以避免规则定义过程中枚举数据手工输入存在错误的可能性。
选中我们的项目,在“库”节点上右键,创建一个常量库文件,如下图所示:
与变量库文件类似,常量也是由分类和具体的常量值构成,比如性别有男女之分,那么这里的“性别”就属性分类,“男”、“女”就属性具体的常量值。在常量的分类中,“名称”一般定义具体的分类名,“标题”是一段描述(比如“性别”是标题,“gender”是名称),同样这个标题也会出现在规则引用当中;加好分类后就可以添加这个分类下具体的常量值,常量值也有名称和标题之分,名称是具体的常量值,标题则是描述,比如“男”是标题,"true"是名称,同样“女”是标题,“false”是名称,如下图所示:
同样,在一个变量库文件中可以根据需要添加多个变量分类。
常量定义时,其名称值可使用Spring中加载的properties文件值,具体使用方法是将要引用的properties的key值用${...}包裹,这样在具体规则运行时会动态查找这个包裹的属性值作为具体的常量,如:${app.title}表示取spring中properties文件的名为app.title的属性值。利用这一功能,可实现测试环境与生产环境的动态切换。
从2.1.9版本开始,常量库文件在定义时支持从一个具体的Java枚举类中直接导入,方法是点击图标,在弹出的窗口中输入完整的枚举类路径即可实现将枚举类里的枚举值导入到当前常量分类之下。
在通过枚举类导入具体常量时,如果枚举类中未定义getLabel方法,那么导入的枚举信息则只采用其name属性,如果定义了getLabel方法,那么导入后的枚举信息则包含name和label,包含getLabel方法的枚举类示例如下所示:
public enum TestEnum { aaa("张三"),bbb("李四"); private String label; private TestEnum(String label) { this.label=label; } public String getLabel() { return label; } }
参数库文件
在规则的条件判断与计算过程当中,难免会用到一些临时的变量来存储值,这些临时变量数量和类型都可能是不固定的,对于这种类型的临时变量,URule Pro以参数的形式提供,通过参数库就可以定义这些在规则中要使用到的临时变量。
在“库”节点上右键创建一个参数库文件,从参数库文件编辑器来看,参数因为没有了分类,配置要为简单许多,如下图所示:
参数库在运行时实际上是存储在HashMap当中,这里的“名称”的值将作为Map的key,“标题”则用在规则中显示使用,定义参数库时要保证“名称”属性的唯一性,因为它是Map的key值; 同时如果一个规则文件里引入多个参数库文件,那么每个参数库文件里定义的参数的名称值也要唯一,否则就会存在相互覆盖的情况。
在URule Pro当中,对于参数库中定义的值,规则运行时,如果外部没有对这些参数进行初始化,那么引擎会自动为部分没有初始化的参数进行初始化。下表中介绍了URule Pro中会自动初始化的数据类型自动初始化后的值。
数据类型 | 初始化值 |
---|---|
Integer | 0 |
Double | 0 |
Float | 0 |
Boolean | false |
List | new ArrayList() |
Set | new HashSet() |
Map | new HashMap() |
如果我们当前项目中已定义好若干个变量库文件,且文件中都定义了相应的变量对象,那么在打开参数库文件,添加参数时,我们会发现参数的数据类型除了固定的13种数据类型外, 还有当前项目中所有变量库文件里定义的变量分类值;如果在定义参数时数据类型选择为某个变量的分类名,那么在使用这个参数时,除了可以选择参数自身,还能选择参数对应的变量下的具体属性值,如下图所示:
因为参数的数据类型可以指定为某个变量类型,所以在设计一些复杂规则的时候就显得特别有意义,比如我们可以通过这种方式创建一个新的变量对象实例, 然后赋值给某个是这个变量数据类型的参数,然后再通过这个参数给这个新的对象实例的属性进行逐个赋值等。
动作库文件
动作库文件的作用是对配置在spring中的bean方法进行映射,使得我们可以直接在规则当中调用这些方法。同样在项目的“库”节点下创建一个动作库文件,可以看到动作库文件内容有三列,分别是动作名称,bean的id定义列,方法名定义列,以及方法对应的参数定义列,如下图所示:
为了演示这里定义方法的具体操作,下面是一个名为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={"数字1","数字2"})
public int testInt(int a,int b){
return a+b;
}
public int testInteger(Integer a,int b){
return a+b*10;
}
@ExposeAction(value="打印内容",parameters={"用户名","出生日期"})
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());
}
}
在这个MethodTest类中,我们对需要在动作库中引用的方法上都添加了一个名为ExposeAction的annotation,这就表示这个方法可以暴露给规则引用,反之如果不加这个annotation,那么这个方法就不能在定义动作时直接引用。 同样我们也看到对于需要在动作库中引用的方法是不需要实现任何接口的,方法签名也是任意的,只需要在方法上添加ExposeAction这个annotation即可,接下来,我们需要将这个类配置到spring中,让其成为一个标准的bean,spring中的配置如下:
<bean id="methodTest" class="rete.test.MethodTest"></bean>
定义动作库的Bean时,一定不要忘记给Bean定义一个Id,这样才能保证规则在任何地方运行都不会出错,这点很关键。
回到动作库文件编辑器,点击“添加Bean”按钮,添加一条Bean定义信息,将"Bean Id"属性值改为我们这里的“methodTest”,点击当前行“操作列”中第一个图标选择这个bean中的方法,以实现bean方法的映射,如下图所示:
所有添加了ExposeAction的annotation的方法都出同在这个列表中,我们可以根据选择添加即可,可以看到添加的方法会自动将这个方法所需要的参数加载进去,添加成功后,为了在规则中更好的可读性,我们可以修改“动作名称”、“方法名称”以及“参数名称”。
可以看到,所有的库文件除了可以保存外,还提供了一个名为“保存为版本”的按钮,通过这个按钮,可以将当前文件内容保存为一个新的版本,这样在规则中就可以引用特定版本,特定版本的库文件不会被修改。实际上在URule Pro中所有的文件都可以保存为新版本。
“查看引用”按钮,就是用来查看当前库文件被哪些规则文件使用了,会有个列表显示出来,方便我们后续维护。
在动作库定义的时候需要注意,如果我们规则运行方式采用的是客户端服务器模式(参见第16小节内容介绍),那么必须要保证调用知识包的客户端Spring环境里有这个Bean,且Bean的Id要与动作库定义时的Id保证一至,否则调用会出现错误。
变量库、参数库、动作库、常量库这些库文件定义好后,就可以在各种类型的规则文件中导入并使用它们,一旦某个库文件在规则中被使用,我们就不能再随便修改这些已定义好的库文件的名称、值或数据类型, 如果因为业务调整需要必须要进行修改,那么可以通过这些变量、常量、参数、动作定义的操作列上的修改图标来对它们进行修改,这样可以保证引用文件同步更新。