Jbpm配置代码分析(一)
作者:吴大愚
Email:dywu_xa@sina.com
2006-11-1
版本 1.0
适用于jbpm3.1版本
1. 概述
Jbpm在从 3.0.2 版本升级到3.1版本之间,最大的变化在于增加了配置文件。使其使用的多个模块都可以进行配置。在其源代码开发包中目录jbpm/src/config.files下新增了一个jbpm.cfg.xml的配置文件。并且在目录jbpm/src/java.jbpm/org/jbpm下将原有的jbpm.properties文件除掉,添加了一个default.jbpm.cfg.xml配置文件。从而使得Jbpm3.1可以对各种功能模块可配置。
说明一点,我对Ioc理论了解甚少,只知道一点概念。在花费了将近一周的时间看完了相关内容后,感觉这一块的内容应该是jbpm在使用Ioc方面的重大举措。如果有那位朋友懂得Ioc理论的,在看了我下面的内容后,觉得我这个想法是对的话,最好能够用Ioc方面的知识来分析一下。如果觉得我这个想法不正确,也麻烦告知一二。谢谢:)
2. 从jbpm.cfg.xml开始
Jbpm3.1文档描述,使用jbpm包,需要在classpath下有一个名为jbpm.cfg.xml的配置文件。这个文件描述了jbpm需要的配置项。源代码中,这个文件在jbpm/src/config.files下,一般使用jbpm,只要将这个文件拷贝到我们的classpath下就可以了。如果jbpm在classpath下没有找到这个文件,那么它就会使用它自带的默认配置文件,就是default.jbpm.cfg.xml。
这两个配置文件不同之处在于,默认配置文件没有包括数据库、日志、消息、时间调度等功能模块,之包含了身份人中模块。是一个jbpm最小使用集合。这也就是为什么在3.1的最小配置中没有数据库部分的原因。
我们先来看一下jbpm.cfg.xml,(文件内容请参见jbpm程序包)。
<jbpm-configuration>作为根结点,包括一个<jbpm-context>节点,八个<string>节点,两个<bean>节点和一个<long>节点。
其中所有的<string>都包含不同的配置文件,<bean>包含两个配置类。<long>包含jbpm消息的timeout时间设定。 这些内容不是我们这篇文章的重点,在后续的分析中,会逐步提到。
<jbpm-context>节点中包含了五个<service>节点。通过名字我们可以看出,这是jbpm使用的五个功能模块的构造类。我们要关注的就是jbpm是如何加载他们的。
要走到Jbpm如何加载功能模块这一步,这是一个很漫长的过程,我们需要在加载模块之前搞清楚不少东西。首先就需要知道这些模块是怎么被Jbpm管理起来的。
Jbpm3.1中引入了一个新类,叫做JbpmContext。这个类在Jbpm的文档中被描述为一个包装持久化方法的类,同时它能使得Jbpm能够从特定的环境下剥离出来,它对Jbpm使用到的所有功能模块都设定了接口,并且管理这些满足接口的功能模块。(原文:A JbpmContext separates jBPM from a sprecific environment. For each service that jBPM uses, there is an interface specified in the jBPM codebase. jBPM also includes implementations that implement these services by using services in a specific environment. e.g. a hibernate session, a JMS asynchronous messaging system, ...)
因此,我们知道JbpmContext类负责了对所有功能模块的管理。那么在Jbpm启动过程中JbpmContext类是如何被创建的,它又是如何加载这些功能模块的呢?这里就要引出JbpmConfiguration类。这个类在Jbpm3.1版中被彻底重写,和 3.0.2 版有天壤之别。
在Jbpm应用中,只需要:
JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance();
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
两条语句就可以配置好一切。我们现在关心的就是这两条语句背后的东西。
3. ObjectFactory是如何创建对象的
3.1. Interface ObjectFactory
我们首先来看一个接口org.jbpm.configuration .ObjectFactory。
public interface ObjectFactory extends Serializable {
Object createObject(String name);
boolean hasObject(String name);
}
这个接口有一个实现类org.jbpm.configuration.ObjectFactoryImpl。而创建这个类的是一个辅助类org.jbpm.configuration.ObjectFactoryParser。这些类和接口之间的关系如图一。
图 一
在JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance();这条语句中,就创建了ObjectFactoryImpl类的实例。创建流程见图二。
图 二
Jbpmconfiguration类从有一个静态Map类型的变量instances。用来存放Jbpmconfiguration类的不同对象的。一般默认情况下,只需创建一个名字为null的Jbpmconfiguration对象(指一个Jbpmconfiguration类的实例,下类同),存放在instances中,每次调用getInstance()时返回。在最初要创建一个Jbpmconfiguration对象需要一个实现了ObjectFactory接口的参数。
ObjectFactory顾名思义,是一个创造Object的工厂类。自然我们会想到设计模式里面有关的抽象工厂模式,和工厂方法模式。我把这两个模式的相关图放在这里,对大家有些参考价值吧。
图 三 抽象工厂
图 四 工厂方法
3.2. Class ObjectFactoryImpl
Jbpm中的ObjectFactoryImpl类的思想和上面两种经典的设计模式有类似的地方,不同之处也是非常明显的。
既然是一个创造对象的工厂类,那么我们自然就要问问这样一个类能够创建什么类型的对象了。在ObjectFactoryImpl中有五个成员变量,分别是
List objectInfos;
Map namedObjectInfos;
Map singletons;
Map objects;
Collection objectsUnderConstruction;
搞清楚这五个成员的含义对理解ObjectFactory非常重要。但要明白它们的含义,我们还需要引入一个新的接口org.jbpm.configuration.ObjectInfo。有关ObjectInfo的详细分析,参见Interface ObjectInfo一节。
在ObjectInfo中有一个Object createObject(ObjectFactoryImpl objectFactory)接口。
简单的说,一个实现了ObjectInfo接口的类会对应一个类,能够通过createObjecct来创建相对应的类的对象。例如StringInfo对象就能创建String对象。ObjectFactoryImpl对象就是根据它所拥有的ObjectInfo对象来创建相应的类的对象的。
我们在jbpm.cfg.xml文件中每一个元素对应一个ObjectInfo对象。也就是说,通过对jbpm.cfg.xml文件进行解析,我们会创建一个JbpmContextInfo对象,八个StringInfo对象,两个BeanInfo对象和一个LongInfo对象。并且这些实例会存储在ObjectFactoryImpl对象里面。这些对象就存储在List objectInfos中。而Map namedObjectInfos的value中存放的内容和objectInfos一样,key中存放都是这些ObjectInfo对象的name属性的值。
ObjectFactoryParser类在createObjectFactory方法中首先创建好List objectInfos和Map namedObjectInfos,然后以这两个对象为参数,创建ObjectFactoryImpl对象。
通过调用ObjectFactoryImpl类的Object createObject(String name)方法,就可以创建相应类的对象。这里参数name是要使用的ObjectInfo对象的name,对应namedObjectInfos中key的值。
简单的说(这里只关注于创建对象方面,其他内容参见下文),首先会先查询namedObjectInfos中是否包含key为参数name的Entry,如果包含,那么就调用相应ObjectInfo对象的createObject方法,创建出所指定的相应类的对象来。
例如JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
就是通过jbpmConfiguration中的成员objectFactory的createObject方法,以JbpmContextInfo对象默认的名字作为参数,创建出来的JbpmContext对象。
顺序图如下:
图 五
在ObjectFactoryImpl中还有一个Registry的概念。所谓Registry就是用来存放ObjectFactoryImpl已经创建的对象集合的仓库。ObjectFactoryImpl是用成员singletons和objects来实现这个Registry概念的。
如果在配置文件中相应节点有属性singleton为true。那么由ObjectFactoryImpl创建出来的此类的对象也是唯一的。创建出来后存储在Map singletons中。如果没有属性singleton,或为false,那么多次创建就会得到多个不同的对象,存储在Map objects中。此外还有一个叫做objectsUnderConstruction的collection,这个collection用来存放当前正在创建的对象对应的ObjectInfo对象的名字属性,等到创建完毕就将此名字从objectsUnderConstruction中删除。
通过createObject方法创建对象前,会先将Registry清空(但不清除singletons中的对象),再创建。创建完后放入Registry中。只所以这么做,是因为通过hasObject方法和getObject方法回去查询Registry中的元素。
如果想要了解ObjectFactory如何使用,那么一个很好的建议就是去看看src/java.jbpm.test目录下的com.jbpm.configuration. ObjectFactoryUserGuideTest类。看名字你就能知道它是做什么的了吧。