在JBPM的Handle类中调用由Spring管理的类原理方法详解

我们在使用JBPM定义流程的时候经常要在流程定义文件中加入一个继承xxxHandler的类来实现我们的业务逻辑判断或者其他的需求,在这个类中我们默认情况下只能通过JBPM提供的API拿到Hibernate的Session,如果我们想要执行一条hql查询语句的话要使用session进行查询这样不太好。最好是我们将要执行的代码封装到一个Service类中,然后进行调用 如果想要调用这个Service类中的方法,首先要通过Spring得到这个类的对象。如何才能得到呢?下面我们来探讨这个问题。(以UserService为例)

第一种方法:将UserService定义成static的(不推荐)
//实现了JBPM中AssignmentHandler的类
public class RoleAssignmentHandler03 implements AssignmentHandler {

@Resource
private UserService userService;

@Override
public void assign(Assignable assignable, ExecutionContext executionContext) throws Exception {
userSerivce.xxxx //调用业务逻辑
}
}


如果我们按照上面的方法来写userSerivice是不会被Spring来注入的,因为如果你想向一个类中注入一个属性的话,那么这个类必须也要交给Spring来管理所以在Spring的配置文件中:
 <bean id="roleAssignmentHandler03" class="com.bjsxt.oa.service.handler.RoleAssignmentHandler03">
<property name="userService" ref="userService"></property>
</bean>

即便我们在Spring的配置文件中定义了这个类,那么userSerivce还是不会有值的,为什么呢?当web服务器启动的时候Spring的确将这个类进行了实例化,但是当我们的系统在运行调用这个类的时候JBPM会再次的创建一个这个类的一个对象,所以他不会使用Spring给我们创建的那个对象。那么怎么办呢? 我们可以将它定义成静态的:
@Resource
private static UserService userService

为什么定义成静态的就可以呢?我们都知道静态变量时类级别的变量,所有这个类的实例共享一份,那么第一次Spring给我们创建这个对象的时候,userService有值了等到第二次JBPM给我们创建这个对象的时候由于UserService是static的,所以他依然会有值 所以通过这种方法我们可以得到UserService对象,但是我们并不推荐这种方法,因为确实有点浪费内存资源 Spring明明已经把这个对象创建好了但是我们却没有去使用这个对象而是去使用了另一个由JBPM给我们创建的一个对象,但这种方法是可行的

第二种方法:通过创建一个BeanFactory来得到相应的Bean对象(错误的做法)
我们知道如果能够得到一个BeanFactory对象然后通过getBean(对象id)就可以拿到一个对象,所以就想当然的使用下面这种方法来得到一个Beanfactory对象:
BeanFacotory factory = new ClassPathXmlApplicationContext("xxx.xml);

但是请注意:如果你这样写相当于重新创建了一个容器环境,这个与当前我们tomcat启动的时候自动创建的那个容器环境完全的就是两套东西,他们的事务控制、包括bean对象的获取都完全是处在两个不同的环境中了而且如果这样写,程序会报错的。

第三种方法:通过相应的辅助类来得到tomcat启动时创建的BeanFactory对象(不推荐)
上面那种方法无法得到一个BeanFactory对象。那么我们可以通过一些辅助类来得到tomcat启动时创建的BeanFactory对象:因为我们在web.xml的配置文件中配置了Spring的Listener以及Spring的配置文件的位置等等,那么在tomcat启动的 时候就会初始化这个容器环境创建一个BeanFactory对象,放置到
ServletContext中。我们如何才能拿到tomcat创建的这个BeanFactory呢?要用到一个辅助类

WebApplicationContextUtils.getRequiredWebApplicationContext(ServletActionContext.getServletActionContxt());

但是我们不推荐这样做为什么?因为ServletActionContext是struts的东西,这样spring或者说JBPM都依赖于Struts,我们在业务逻辑层不要依赖于呈现层的东西。但是这种方法也是可行的。

第四种方法:用JBPM给我们提供的方法
这个时候我们要用到Spring和Jbpm集成时候的一个第三方的类库:spring-modules 在他的文档中(spring-modules-0.8\docs\reference\html\jbpm31.html)他有一个题目叫做:9.3. Accessing Spring beans from jBPM actions,就是在JBPM的Action中访问Spring的Bean对象的方法,正好是我们想解决的问题。按照他的意思我们这个例子应该这样来做:
Spring的配置文件中还是要加入:
 <bean id="roleAssignmentHandler03" class="com.bjsxt.oa.service.handler.RoleAssignmentHandler03">
<property name="userService" ref="userService"></property>
</bean>

然后在JBPM的流程定义文件中:
<task-node name="部门领导审批">
<task>
<assignment class="org.springmodules.workflow.jbpm31.JbpmHandlerProxy">
<targetBean>roleAssignmentHandler03</targetBean>
</assignment>
</task>
<transition name="结束审批" to="结束"></transition>
</task-node>


在JBPM的Handler类中:
public class RoleAssignmentHandler03 implements AssignmentHandler {

//由JBPM来进行注入
private String roleName;

@Resource
private UserService userService;

public void setUserService(UserService userService) {
this.userService = userService;
}
}

这样就可以注入了。这是什么意思呢?在JBPM的流程定义文件中
	<assignment class="org.springmodules.workflow.jbpm31.JbpmHandlerProxy">
<targetBean>roleAssignmentHandler03</targetBean>
</assignment>

class所指定的这个类是JBPM提供给我们的,然后targetBean所指定的类就是我们在Spring文件中让Spring给我们实例化的那个Handler类在上面的spring配置文件中可以看到而且我们要把我们想要注入的属性Userservice写到里面,然后我们在Handler类中定义UserService的set方法,他就可以帮我们注入了
原理:在这种的解决方法中是由org.springmodules.workflow.jbpm31.JbpmHandlerProxy这个类和Spring打交道,他可以拿到Beanfactory,然后通过 <targetBean>所指定的Spring容器中的那个bean对象的id他就可以帮我们注入进去了。但是这样方法有一个很大的缺点:在Handler类中你看到我们还有一个roleName属性他是我们查询条件的参数的院线我们通过:
<assignment class="com.bjsxt.oa.service.handler.RoleAssignmentHandler02">
<roleName>部门领导</roleName>
</assignment>
这种方式传递进去,但是现在class换成了org.springmodules.workflow.jbpm31.JbpmHandlerProxy这个类他是不支持比如你在下面加一个<roleName>部门领导</roleName>这种方法传值的。当然我们可以把这个属性定义到spring配置文件中:
 <bean id="roleAssignmentHandler03" class="com.bjsxt.oa.service.handler.RoleAssignmentHandler03">
<property name="userService" ref="userService"></property>
<property name="roleName" ref="系统管理员"></property>

</bean>

但是这种方法把属性就写死了,而且如果我有很多的属性要进行传递,那么spring配置文件就太臃肿了。所以这种方法我们依然是不推荐的,但是如果你不需要进行属性的传递,这种方法依然是可行的。

第五种方法:我们的Handler类实现BeanFactoryAware这个接口:
这个接口里面有一个方法:
@Override
public void setBeanFactory(BeanFactory arg0) throws BeansException {

}

通过这个方法我们就可以拿到这个Beanfactory对象了。上面这种方法我们有点想当然了,因为beanFactory是没有值的 为什么呢?我们说上面这种情况成立的前提必须是实现了BeanFactroyAware这个接口的类也要交给Spring管理但是我们这个类并没有交给Spring来管理它还是由JBPM来创建的。所以这种方式依然行不通。

第六种方法:最终可行方案
我们定义自己的一个BeanFactoryHelper 类这个类中有一个静态变量Beanfactory,然后我们让spring帮助我们生成这个类,那么这个时候Beanfactory就有值了我们向外提供一个静态的方法返回Beanfactory就可以了。
public class BeanFactoryHelper implements BeanFactoryAware {

private static BeanFactory beanFactory;

@Override
public void setBeanFactory(BeanFactory f) throws BeansException {
this.beanFactory = f;
}

public static BeanFactory getBeanfactory() {
return beanFactory;
}

}


spring配置文件:
<bean id="beanFactoryHelper" class="com.bjsxt.oa.service.handler.BeanFactoryHelper">
</bean>

在我们的Handler类中,我们可以这样使用:
public class RoleAssignmentHandler03 implements AssignmentHandler {

//由JBPM来进行注入
private String roleName;

@Resource
private UserService userService;

public void setUserService(UserService userService) {
this.userService = userService;
}


@Override
public void assign(Assignable assignable, ExecutionContext executionContext) throws Exception {
if(roleName == null) {
throw new RuntimeException("必须首先指定角色");
}

BeanFactory bf = BeanFactoryHelper.getBeanfactory();
UserService userService = (UserService)bf.getBean("userService");
UserService.xxxxx//调用业务逻辑方法
}

用这个方法是最好的,但是这个方法不用我们自己写有人已经帮我们写好了。

我们先借鉴一下JbpmHandlerProxy这个类是怎么拿到Beanfactory对象的。我们要关联上源代码了解一下他的运行机制:
JbpmHandlerProxy的运行机制:
jbpmhandlerProxy通过一个JbpmFactoryLocator来得到一个Beanfactory对象,那么他是怎么得到的呢,jbpmfactoryLocator实现了一个BeanFactoryAwre接口,所以有个
setBeanfacotry(BeanFactory factory) 方法,那么是哪个类来调用的这个方法 呢?是LocalJbpmConfigurationFactoryBean他也实现了BeanFactoryAwre接口所以他也有一个
setBeanfacotry(BeanFactory factory) 方法,因为这个类的对象我们是让spring帮我们生成的,所以在tomcat启动的时候spring会把Beanfactory对象放作为参数传递给
LocalJbpmConfigurationFactoryBean实现的setBeanfacotry(BeanFactory factory) 中,然后再这个方法中LocalJbpmConfigurationFactoryBean又去调用jbpmfactoryLocator
类的setBeanfacotry(BeanFactory factory) 关键就在这里,JbpmFactoryLocator中有一个protected static BeanFactory defaultFactory = null; 他把setFactory方法传递给他的
Beanfactory对象赋值给了静态变量defaultFactory。
然后在JbpmHandlerProxy类的retrieveBeanFactory方法中new JbpmFaotoryLocator对象,因为他里面的Beanfactory属性是静态的所以不管你怎么new他都是有值的,然后返回这个值
protected BeanFactory retrieveBeanFactory() {
BeanFactoryLocator factoryLocator = new JbpmFactoryLocator();
BeanFactoryReference factory = factoryLocator.useBeanFactory(factoryKey);
if (factory == null)
throw new IllegalArgumentException("no beanFactory found under key=" + factoryKey);

try {
return factory.getFactory();
}
finally {
factory.release();
}
}

这就是JbpmHandlerProxy的运行机制


[img]http://dl.iteye.com/upload/attachment/232445/b9893e7f-130b-3124-aa3f-5defdd3b3c24.jpg[/img]


那我们怎么做的,我们可以把这个方法拷贝到我们的类中,然后直接调用就可以拿到Beanfactory对象了,但是这样有点麻烦我们要把这个方法封装一下

自定义BaseAutowire类:
public class BaseAutowire {
//arg1:向哪个类进行属性注入
//arg2:按照那种方式注入:按类型、或者名称....此处按照类型
//arg2:是否检查依赖关系,一般情况下为true要检查依赖关系。
public BaseAutowire() {
((AutowireCapableBeanFactory)retrieveBeanFactory())
.autowireBeanProperties(this, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true);
}

protected BeanFactory retrieveBeanFactory() {
BeanFactoryLocator factoryLocator = new JbpmFactoryLocator();
BeanFactoryReference factory = factoryLocator.useBeanFactory(null);
if (factory == null)
throw new IllegalArgumentException("no beanFactory found under key=" + null);

try {
return factory.getFactory();
}
finally {
factory.release();
}
}
}


public class RoleAssignmentHandler03 extends BaseAutowire implements AssignmentHandler {
//由JBPM来进行注入
private String roleName;

@Resource
private UserService userService;

@Override
public void assign(Assignable assignable, ExecutionContext executionContext) throws Exception {
if(roleName == null) {
throw new RuntimeException("必须首先指定角色");
userService.xxxx//调用业务逻辑
}
}

我们说如果你想要往一个类的对象中注入一个spring管理的对象的时候,这个类也必须要交给spring管理,其实这种说法也不是完全正确为什么我们在Junit单元测试中的相关属性上面定义@Resource注解就会将对象注入进去呢?都是使用了;类似我们下面讲解的这种方法: 我们可以不让spring帮助我们注入,我们自己来注入:在jbpm来构建RoleAssignmentHandler03对象的时候因为他继承了BaseAutowire类所以首先会调用BaseAutowire类的构造方法,在这个类的构造方法中我们调用retrieveBeanFactory()方法返回个Beanfactory对象,然后将它强制转换成AutowireCapableBeanFactory对象,然后调用autowireBeanProperties()方法,这样我们在RoleAssignmentHandler03类中:
@Resource
private UserService userService;
就有用了加上@Resource就表明了一种依赖关系。我们在BaseAutowire类的构造方法中定义了自动注入的特性。因为RoleAssignmentHandler03继承了他,而且在UserService上加上了@Resource注解,表明了依赖关系所以会自动的将UserService这个类的对象注入进来。然后我们直接使用就可以了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值