UML软件工程组织

 

 

Jbpm Delegation机制源代码分析和实例
 
作者:吴大愚 来源:.csdn.
 


分析版本 3.1.3

1. 什么是Delegation

在分析Jbpm的Delegation机制之前,我们要先搞明白什么是Delegation。在这里我不细写什么是Delegation,而给出两个链接,这两个连接都是csdn上面的博客文章,讲解了什么是Delegation机制。

《Delegation模式》:http://blog.csdn.net/mildwind/archive/2004/12/15/217553.aspx

《delegation(委托)vs. composition(复合)?》:http://blog.csdn.net/fantasylu/archive/2004/07/22/44901.aspx

如果这两个链接失效的话,大家也可以在我的博客(http://blog.csdn.net/dust_bug)上面找一篇我转载并总结的有关Delegation机制的文章。

简单的说一下什么是Delegation。

在Jbpm的user guide第16.2节中说道“Delegation is the mechanism used to include the users' custom code in the execution of processes.”。

有人这么说“委托(Delegation)模式是一种技术,一个对象在外界来看好像实现了一些行为,但实际上是委托给相关的其他类来实现行为的”(见《Delegation模式》)。还有人说真正的Delegation不是这样的,还必须有更高的要求才是Delegation,他们这么说“To achieve the same effect with delegation, the receiver passes itself to the delegate to let the delegated operation refer to the receiver”(见《delegation(委托)vs. composition(复合)?》)

在Jbpm中的Delegation应该是第一种观点中的Delegation。

2. Jbpm在何处使用Delegation

就像Jbpm的文档中自己写的那样,Jbpm使用Delegation机制是为了在流程执行中能够使用用户自己定制的代码(user’s custom code)。

在jPDL的schema定义中,有四个元素是可以添加用户自己定制的类。那就是action,assignment,controller和handler。这四个元素都有两个共同的属性,class和config-type。其中,action元素中class所指定的类必须实现org.jbpm.graph.def.ActionHandler接口。Assignment元素的class所指定的类必须实现org.jbpm.taskmgmt.def.AssignmentHandler接口。controller元素的class所指定的类必须实现org.jbpm.taskmgmt.def.TaskControllerHandler接口。handler元素的class所指定的类必须实现org.jbpm.graph.node.DecisionHandler接口。

在这四个元素的schema定义中还都有一个没有Name属性的成员“{content}”。Jbpm对它的解释是“the content of the handler can be used as configuration information for your custom handler implementations. This allows the creation of reusable delegation classes.”我会在Delegation的配置类型部分,详细讲解这个内容。

图 一 Jbpm Delegation类图

图一是Delegation的相关的类图。我们本文关心的所有的类都在这里。

下面让我们先来分析一下Jbpm是如何实现在流程执行中使用用户自己定制的代码的吧。

3. Delegation类和它所使用到的类

我们先来看一下在Jbpm中Delegation是被谁委托的,又能代表谁做什么事情呢?

3.1. org.jbpm.instantiation. Instantiator接口

instantiation是什么意思呢?查了金山词霸,原来是实例化的意思。那实例化什么呢?

这个接口就一个方法Object instantiate(Class clazz, String configuration);Jbpm并没有给这个方法说明。通过后面的分析,发现实现这个方法的类,在这个方法中都是使用反射机制创建了客户自定义的类的对象。方法的第一个参数clazz就是要创建的类的Class,第二个参数是用来传递流程定义xml文件中相应的部分,用来对客户自定义的类进行配置的。

3.2. 实现Instantiator接口的类

实现这个接口的一共有六个类,分别是FieldInstantiator,BeanInstantiator,ConstructorInstantiator,ConfigurationPropertyInstantiator,DefaultInstantiator和XmlInstantiator。以上六个类都是在org.jbpm.instantiation包中,其中后两个类虽然定义了,但是在jbpm3.1.3中没有被使用。

在DefaultInstantiator的instantiate方法中就一条核心语句:

return clazz.newInstance();

这条语句也都出现在其他5个类的instantiate()方法中。可以看出这六个类其核心就是采用不同的方式创建客户自定义的类。

下面我以FieldInstantiator类作为例子进行分析。

3.3. FieldInstantiator类

在FieldInstantiator类的instantiate()方法中。以下用伪代码表示:

Object instantiate(Class clazz, String configuration){

根据参数clazz创建一个对象;

If(参数configuration有内容){

将configuration转化成为xml节点形式;

}

遍历xml节点{

调用setPropertyValue()方法,将xml节点的值注入到新创建的对象中去。

}

返回新创建的对象;

}

setPropertyValue(Class clazz, Object newInstance, String propertyName, Element propertyElement)有四个参数。第一个参数clazz是第二个参数newInstance的Class对象。第三个参数propertyName是newInstance的一个成员名称。第四个参数是一个xml对象,其中具有要付给propertyName这个成员的值。

3.4. org.jbpm.instantiation.Delegation类

简单的说Delegation类的作用就是代理上面所提到的四个实现了Instantiator接口的类(FieldInstantiator,BeanInstantiator,ConstructorInstantiator,ConfigurationPropertyInstantiator)来创建客户定制的类的实例。

Delegation类最主要的方法是instantiate()。这个方法功能是调用上面四个类中一个的instantiate()方法,创建一个客户化对象,然后返回这个对象。

下面我们来具体分析一下。

3.4.1. instantiatorCache成员

Delegation类有一个map成员instantiatorCache。在初始化阶段,这个成员会被添加五个元素。这些元素的key为string类型,value为分别实现Instantiator接口的对象。分别为(null, new FieldInstantiator()),("field", new FieldInstantiator()),("bean", new BeanInstantiator()),("constructor", new ConstructorInstantiator()),("configuration-property", new ConfigurationPropertyInstantiator())。instantiatorCache作甚么用呢?它就是用来存储Delegation所有代理的对象。

3.4.2. Instantiate()方法

instantiate()方法主要步骤如下:

Object instantiate(){

根据类的成员className,创建相应的Class对象clazz;

根据类的成员configType,从instantiatorCache找到相应Instantiator接口的的对象,存放在instantiator变量中。

以clazz和类的成员configuration作为参数,调用instantiator的instantiate方法。创建客户化对象;(Delegation模式就是这儿:)

返回创建的对象;

}

有一点要说明,就是Jbpm还支持自定义的实现instantiator接口的类来创建自己的客户化对象。也就是说,我们可以来定义自己的instantiator类,自己实现如何创建客户化类的过程。

3.4.3. read()方法

instantiate()方法中使用到的类的成员className,configType,configuration都是在类的方法read(Element delegateElement, JpdlXmlReader jpdlReader)中得到的。其参数delegateElement就是我们所说的Jbpm中能够使用客户化类的四种元素(action,assignment,controller和handler)之一的xml节点。

我们通过delegateElement可以得到xml节点的class、config-type属性,分别付给类的className和configType成员。而类的configuration成员,存放的就是xml节点内的所有字符 串。

例如,对于一个action类型的xml节点:

<action class=”dust.action.action1Handler” config-type=”field”>

<name>dust</name>

<count>5</count>

</action>

那么经过read()方法,成员class=”dust.action.action1Handler”,成员config-type=”field”,而configuration=” <name>dust</name><count>5</count>”

我们现在基本知道了Delegation类是干什么用的,那么Jbpm是怎么用Delegation类的呢?我下面就分析一下Action中如何使用Delegation创建客户化类的对象并调用的。

4. 在Action中实例分析如何使用Delegation

在org.jbpm.graph.def .Action类中有一个成员actionDelegation,类型为Delegation。在read()方法中,如果<action>元素包含一个class属性,那么就会创建一个Delegation的对象,并赋给成员actionDelegation。然后调用actionDelegation的read()方法。

在Action. execute()方法中,如果actionDelegation不为null,那么就会调用如下的语句:

ActionHandler actionHandler = (ActionHandler)actionDelegation.getInstance();

actionHandler.execute(executionContext);

看明白了吧,首先通过getInstance()创建一个实现了ActionHandler接口的客户化对象,然后调用这个对象的execute()方法。

Yes!We get it! 我们对上了!通过上面的分析,我们终于把jPDL语言中,一个Action元素所指定的客户化类创建出来,并且调用执行客户代码进行了分析。路修通啦J

同样,在org.jbpm.graph.node.Decision类有一个成员Delegation decisionDelegation;在org.jbpm.taskmgmt.def. TaskController类有一个成员Delegation taskControllerDelegation;在org.jbpm.taskmgmt.def. Task类有一个成员Delegation assignmentDelegation;在org.jbpm.taskmgmt.def. Swimlane类有一个成员Delegation assignmentDelegation。这些类型为Delegation的成员的作用都等同于Action类中的actionDelegation。

到此为止,我们把Delegation的内容基本上说完了。但是我前面一直在回避一个方面,那就是四种xml元素的config-type属性所起到的作用。下面我们就来看看在Jbpm中Delegation的配置类型。

5. Delegation的配置类型

在Jbpm的userguide的第16.2.3节中,讲述了Delegation的四种配置机制。相对应的也就是config-type属性的四种可能的值。分别是“field,bean,constructor,configuration-property”。我建议读者先读一下userguide的第16.2.3节。

回忆一下,Delegation类有一个map成员instantiatorCache。它五个元素的key,除了null,其余四个正是我们上面列出的四个值。也就是说,在建模的时候,对于<action class=”dust.action.Action1Handler” config-type=”field”>元素, Action类的actionDelegation成员的instantiate()方法就会选择instantiatorCache中的FieldInstantiator对象来实例化dust.action.Action1Handler类。如果config-type=”bean”,那么就会选择BeanInstantiator。依次类推。

如果你不写config-type那么这个时候instantiatorCache的null就起作用了,所以默认还是FieldInstantiator。

那么FieldInstantiator,BeanInstantiator,ConfigurationPropertyInstantiator ,ConstructorInstantiator,四个类创建客户化对象有什么不同呢?

我们先写个类dust.action.Action1Handler,来看看。

public Action1Handler implements ActionHandler{

String name;

int count;

public Action1Handler(){}

public Action1Handler(String configuration){

System.out.println("==Action1Handler contstructor==");

System.out.println("==configuration is:"+configuration+"==");

}

public void setConfiguration(String configuration){

System.out.println("==Action1Handler setConfiguration==");

System.out.println("==configuration is:"+configuration+"==");

}

// getters and setters //////////////////////////////////////////////////////

public void setName(String name){

this.name = name;

}

public String getName(){

return this.name ;

}

public void setCount(int count){

this.count = count;

}

public int getCount(){

return count;

}

public void execute(ExecutionContext executionContext) throws Exception {

// TODO Auto-generated method stub

}

}

下面我们假设流程定义为中有

<action class=”dust.action.Action1Handler” config-type=”field”>

<name>dust</name>

<count>5</count>

</action>

5.1. FieldInstantiator类

当config-type为field时,或者没有config-type属性,会使用FieldInstantiator类。

FieldInstantiator类使用反射机制直接对类的成员进行赋值。

也就是说FieldInstantiator会直接将dust付给name,5付给count。

5.2. BeanInstantiator类

当config-type为bean时,会使用BeanInstantiator类。

BeanInstantiator类必须使用setter属性,来对成员赋值。

即,BeanInstantiator使用setName()和setCount()。如果没有这两个方法,那么就会在日志中报错。

5.3. ConstructorInstantiator类

当config-type为constructor时,会使用ConstructorInstantiator类。

ConstructorInstantiator类会调用客户类的具有一个String类型参数的构造函数。并且将<action>元素的所有内容作为字符串类型的参数,传给构造函数。客户可以在这个构造函数中自己处理这段xml文档。

也就是说Jbpm会调用 public Action1Handler(String configuration){}。

5.4. ConfigurationPropertyInstantiator类

当config-type为configuration-property时,会使用ConfigurationPropertyInstantiator类。

ConfigurationPropertyInstantiator类会调用客户类的具有一个String类型参数的一个名叫setConfiguration()方法。并且将<action>元素的所有内容作为字符串类型的参数,传给setConfiguration方法。客户可以在这个方法中自己处理这段xml文档。

也就是说Jbpm会调用 public void setConfiguration(String configuration){}方法。

在Jbpm执行一个流程实例过程中,当遇到一个Action时,会首先实例化流程定义中的Action元素指定的客户化类。如果没有指定config-type的话,那么会调用客户化类的默认构造函数。然后调用execute()方法。

如果config-type为field或bean的话,那么就会在调用execute()前进行属性赋值;如果config-type为constructor,那么就会调用有参数的构造函数;如果config-type为configuration-property是,会先调用默认构造函数,然后调用setConfiguration()方法。

 

组织简介 | 联系我们 |   Copyright 2002 ®  UML软件工程组织 京ICP备10020922号

京公海网安备110108001071号