JBPM3源代码剖析
作者:wocsok
常用JBPM3的朋友们,也许已经对下面几行代码很熟悉了。这篇文章主要是对JBPM里的源代码进行一些简要的说明,让我们来看看这些方法里到底做了些什么。
JbpmConfiguration configuration = JbpmConfiguration.getInstance();
JbpmContext jbpmContext = configuration.createJbpmContext();
InputStream is = new FileInputStream("test.par/processdefinition.xml");
ProcessDefinition processDefinition = ProcessDefinition.parseXmlInputStream(is);
jbpmContext.deployProcessDefinition(processDefinition);
引用
在剖析之前,大家先明确几个概念。
1. JbpmConfiguration顾名思义,JBPM的配置,也就是说这个类会包含JBPM的配置信息。
2. ObjectInfo。 JBPM会把配置文件中的对象都封装成ObjectInfo以便进行统一的操作。
3. ObjectFactory。创建ObjectInfo的工厂接口,它只有一个实现类ObjectFactoryImpl。
4. ObjectFactoryParser。解析器,负责解析XML配置文件,校验(判断配置文件标签中的命名,是否所支持的。),以及在创建ObjectInfo实现类的时候,会保存ObjectInfo的名字和对应的对象到一个map里,以后用的时候可以通过名字,直接取出对应的对象。
言归正传,我们先来看看JbpmConfiguration configuration = JbpmConfiguration.getInstance();
里面的秘密。
public static JbpmConfiguration getInstance() {
return getInstance(null);
}
public static synchronized JbpmConfiguration getInstance(String resource) {
// 默认去寻找jbpm.cfg.xml
if (resource == null) {
resource = "jbpm.cfg.xml";
}
// 从map里去取
JbpmConfiguration instance = (JbpmConfiguration) instances
.get(resource);
// 如果是空的,进行构造,然后缓存到instances这个map里
if (instance == null) {
// 判断objectFactory是否存在
if (defaultObjectFactory != null) {
instance = new JbpmConfiguration(defaultObjectFactory);
} else {
try {
InputStream jbpmCfgXmlStream = ClassLoaderUtil
.getStream(resource);
// 构造一个对象工厂
ObjectFactory objectFactory = parseObjectFactory(jbpmCfgXmlStream);
// 构造一个JbpmConfiguration实例
instance = new JbpmConfiguration(objectFactory);
} catch (RuntimeException e) {
throw new JbpmException(
"couldn't parse jbpm configuration from resource '"
+ resource + "'", e);
}
}
// 把构建成功的实例,缓存到map里。
instances.put(resource, instance);
}
return instance;
}
来看一下构建对象工厂的过程。
protected static ObjectFactory parseObjectFactory(InputStream inputStream) {
// 构造一个工厂解析器
ObjectFactoryParser objectFactoryParser = new ObjectFactoryParser();
// ObjectFactoryImpl对象工厂接口的实现类
ObjectFactoryImpl objectFactoryImpl = new ObjectFactoryImpl();
// 解析器对默认的配置文件进行解析,解析成对象,放入ObjectFactoryImpl。
objectFactoryParser.parseElementsFromResource(
"org/jbpm/default.jbpm.cfg.xml", objectFactoryImpl);
// 解析传入的需要解析的资源
if (inputStream != null) {
objectFactoryParser.parseElementsStream(inputStream,
objectFactoryImpl);
}
return objectFactoryImpl;
}
看下objectFactoryParser.parseElementsFromResource()做了什么。
public void parseElementsFromResource(String resource, ObjectFactoryImpl objectFactoryImpl) {
//取得XML的根元素
Element rootElement = XmlUtil.parseXmlResource(resource).getDocumentElement();
parseElements(rootElement, objectFactoryImpl);
}
把XML里的配置信息,解析成ObjectInfo放入objectFactoryImpl里
public void parseElements(Element element,
ObjectFactoryImpl objectFactoryImpl) {
List objectInfoElements = XmlUtil.elements(element);
for (int i = 0; i < objectInfoElements.size(); i++) {
Element objectInfoElement = (Element) objectInfoElements.get(i);
ObjectInfo objectInfo = parse(objectInfoElement);
objectFactoryImpl.addObjectInfo(objectInfo);
}
}
// 具体如何解析的
// 通过反射 生成objectInfo对象
public ObjectInfo parse(Element element) {
ObjectInfo objectInfo = null;
String elementTagName = element.getTagName().toLowerCase();
// 注: 在构造ObjectFactoryParser对象时,构造函数里进行了
// 把对应的XML里的标签名 和 所对应应该封装成的类型的构造器(java.lang.reflect.Constructor),
// 以key-value的形式,存放到mappings里,所以在此处取出Constructor进行实例化。
Constructor constructor = (Constructor) mappings.get(elementTagName);
if (constructor == null) {
throw new JbpmException(
"no ObjectInfo class specified for element '"
+ elementTagName + "'");
}
try {
// 注意 之所以在这里采用了回调的方式,把解析器的引用又传给了objectInfo是因为,在objectInfo构造器中,
// 会把创建成功的 ObjectInfo的实现类的 名字 和 构造完的对象 以KEY-VALUE的形式
// 存放到解析器的一个MAP里,以便日后直接可以取出
// 名字对应的对象(没构造之前,只是有一个MAP保存 名字 和 对应的Construct)。
objectInfo = (ObjectInfo) constructor.newInstance(new Object[] {
element, this });
} catch (Exception e) {
throw new JbpmException("couldn't parse '" + elementTagName
+ "' into a '" + constructor.getDeclaringClass().getName()
+ "': " + XmlUtil.toString(element), e);
}
return objectInfo;
}
至此,JbpmConfiguration的创建过程完毕。JbpmConfiguration的创建过程,其实就是把jbpm.cfg.xml配置文件,和default.jbpm.cfg.xml配置文件
进行了解析,然后封装成了对应的ObjectInfo的实现类,放进objectFactoryImpl里,然后所创建的JbpmConfiguration对象里埋了下对objectFactoryImpl的引用
以后我无论再需要创建什么ObjectInfo的实现类,只需要用这个对象工程进行创建就OK了(工厂对象的create()方法,实际上是调用了对应ObjectInfo的create方法)。
现在我们来看看 default.jbpm.cfg.xml都有些什么。
<jbpm-configuration>
<!--
This configuration is used when there is no jbpm.cfg.xml file found in the
root of the classpath. It is a very basic configuration without persistence
and message services. Only the authorization service installed.
You can parse and create processes, but when you try to use one of the
unavailable services, you'll get an exception.
-->
<jbpm-context>
<service name="persistence" factory="org.jbpm.persistence.db.DbPersistenceServiceFactory" />
<service name="message" factory="org.jbpm.msg.db.DbMessageServiceFactory" />
<service name="scheduler" factory="org.jbpm.scheduler.db.DbSchedulerServiceFactory" />
<service name="logging" factory="org.jbpm.logging.db.DbLoggingServiceFactory" />
<service name="authentication" factory="org.jbpm.security.authentication.DefaultAuthenticationServiceFactory" />
</jbpm-context>
<!-- configuration resource files pointing to default configuration files in jbpm-{version}.jar -->
<string name="resource.hibernate.cfg.xml" value="hibernate.cfg.xml" />
//还有好多,省略。
<long name="jbpm.msg.wait.timout" value="5000" singleton="true" />
<int name="jbpm.byte.block.size" value="1024" singleton="true" />
<string name="mail.smtp.host" value="localhost" />
<bean name="jbpm.task.instance.factory" class="org.jbpm.taskmgmt.impl.DefaultTaskInstanceFactoryImpl" singleton="true" />
//还有好多,省略。
</jbpm-configuration>
//咱们先以简单的 StringInfo 为例,看构造的时候干了些什么。
public StringInfo(Element stringElement, ObjectFactoryParser configParser) {
//父类中 这个构造器是 在解析器里map里注册一下,然后判断下是否是单例的(例如:<long name="a" value="5000" singleton="true" />
就是判断这个标签解析出来的 singleton存在不,如果存在是否是true )
super(stringElement, configParser);
s = getValueString(stringElement);
}
protected String getValueString(Element element) {
String value = null;
if (element.hasAttribute("value")) {
value = element.getAttribute("value");
} else {
value = XmlUtil.getContentText(element);
}
return value;
}
//StringInfo里 构造函数主要就是解析出对应value的值,看看StringInfo的create();
public Object createObject(ObjectFactoryImpl objectFactory) {
//简单吧 就是把解析的value对应的字符串,返回
return s;
}
下篇文章我们会看一下 复杂点的JbpmContext的创建过程。
JbpmContext jbpmContext = configuration.createJbpmContext();