Jbpm Delegation机制源代码分析和实例

来源:互联网 发布:老子西出函谷关 知乎 编辑:程序博客网 时间:2024/06/06 23:56

 

 Jbpm Delegation机制源代码分析和实例 

 

分析版本 3.1.3 

 

 

 

作者:吴大愚

Email:dywu_xa@sina.com

1.   什么是Delegation

在分析JbpmDelegation机制之前,我们要先搞明白什么是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

Jbpmuser guide16.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)。

jPDLschema定义中,有四个元素是可以添加用户自己定制的类。那就是actionassignmentcontrollerhandler。这四个元素都有两个共同的属性,classconfig-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类和它所使用到的类

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

3.1. org.jbpm.instantiation. Instantiator接口

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

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

3.2. 实现Instantiator接口的类

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

DefaultInstantiatorinstantiate方法中就一条核心语句:

       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是第二个参数newInstanceClass对象。第三个参数propertyNamenewInstance的一个成员名称。第四个参数是一个xml对象,其中具有要付给propertyName这个成员的值。

3.4. org.jbpm.instantiation.Delegation

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

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

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

3.4.1.    instantiatorCache成员

Delegation类有一个map成员instantiatorCache。在初始化阶段,这个成员会被添加五个元素。这些元素的keystring类型,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作为参数,调用instantiatorinstantiate方法。创建客户化对象;(Delegation模式就是这儿:)

返回创建的对象;

}

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

3.4.3.    read()方法

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

我们通过delegateElement可以得到xml节点的classconfig-type属性,分别付给类的classNameconfigType成员。而类的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。然后调用actionDelegationread()方法。

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属性所起到的作用。下面我们就来看看在JbpmDelegation的配置类型。

5.   Delegation的配置类型

Jbpmuserguide的第16.2.3节中,讲述了Delegation的四种配置机制。相对应的也就是config-type属性的四种可能的值。分别是“fieldbeanconstructorconfiguration-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那么这个时候instantiatorCachenull就起作用了,所以默认还是FieldInstantiator

那么FieldInstantiatorBeanInstantiatorConfigurationPropertyInstantiator 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-typefield时,或者没有config-type属性,会使用FieldInstantiator类。

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

也就是说FieldInstantiator会直接将dust付给name5付给count

5.2. BeanInstantiator

config-typebean时,会使用BeanInstantiator类。

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

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

5.3. ConstructorInstantiator

config-typeconstructor时,会使用ConstructorInstantiator类。

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

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

5.4. ConfigurationPropertyInstantiator

config-typeconfiguration-property时,会使用ConfigurationPropertyInstantiator类。

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

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

 

 

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

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

 
原创粉丝点击