分析版本 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()方法。
|