欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

fixflow与spring的集成示例

程序员文章站 2022-05-31 21:00:28
...

最近不少用户在找fixflow与spring的集成示例,相信也有不少用户自己摸索有了自己的集成方式。所以抽了点时间做了个fixflow与spring集成的Demo.

 

此示例适用于对fixflow有一定了解的用户。

此示例存在不少bug,仅供参考,完整逻辑还是参考官方示例。

 

此示例功能:

  1. 完成了与spring的集成示例,此项目集成的是fixflow-core和fixflow-expand内核项目,与bpmcenter示例项目无关。
  2. 完成了fixflow-expand打成jar包,并能在设计器中正常运行。
  3. 模拟请假流程演示业务系统与fixflow引擎的集成,框架用springmvc,业务数据的持久化用jdbcTemplate

首先附上项目代码结构:

 


fixflow与spring的集成示例
            
    
    博客分类: Fixflow fixflowspring集成事务 
 

 

         提到集成,首先考虑到的是事务以及引擎的操作方式,spring提供了很好的依赖注入,确实省了不少事情。废话不说,直接上spring配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation=" 
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd 
             http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd 
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
	<context:component-scan base-package="com.ych.demo.service"/>
	<!-- 配置文件加载 -->
	<bean id="propertyConfigurer"
		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="location" value="classpath:jdbc.properties" />
	</bean>
	<!-- 业务系统数据源 -->
	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="${idbase.driverClassName}" />
		<property name="url" value="${idbase.url}" />
		<property name="username" value="${idbase.username}" />
		<property name="password" value="${idbase.password}" />
	</bean>
	
	<bean id="springBeanUtil" class="com.ych.util.SpringBeanUtil" scope="singleton" />
	
	<!-- 使用jdbcTemplate -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource">
			<ref local="dataSource" />
		</property>
	</bean>
	
	<bean id="abstrJdbcTemplate" abstract="true">
		<property name="jdbcTemplate" ref="jdbcTemplate" />
	</bean>
	<bean id="leaveDao" class="com.ych.demo.dao.LeaveDao" parent="abstrJdbcTemplate" />
	
    <!-- fixflow事物管理 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
	</bean>
   
	<!-- 流程引擎 -->
	<bean id="processEngine" class="com.ych.factory.ProcessEngineFactoryBean" />

	<!-- 任务服务 -->
	<bean id="taskService" factory-bean="processEngine" factory-method="getTaskService" />
	<!-- 模型服务 -->
	<bean id="modelService" factory-bean="processEngine" factory-method="getModelService" />
	<!-- 运行时服务 -->
	<bean id="runtimeService" factory-bean="processEngine" factory-method="getRuntimeService" />
	
	<bean id="fixflowInterceptor" class="com.ych.aop.FixflowInterceptor" />
	<!-- 事务通知 -->
	<tx:advice id="transactionAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<tx:method name="query*" propagation="REQUIRED" read-only="true" />
			<tx:method name="get*" propagation="REQUIRED" read-only="true" />
			<tx:method name="create*" propagation="REQUIRED" />
			<tx:method name="del*" propagation="REQUIRED" />
			<tx:method name="save*" propagation="REQUIRED" />
			<tx:method name="update*" propagation="REQUIRED" />
			<tx:method name="execute*" propagation="REQUIRED" />
			<tx:method name="ask*" propagation="REQUIRED" />
		</tx:attributes>
	</tx:advice>
	<!-- aop配置 -->
	<aop:config>
		<!-- 事物切入点 -->
		<aop:pointcut id="txPointcut" expression="execution(* com.ych.demo.service.*.*(..))" />
		<aop:advisor advice-ref="transactionAdvice" pointcut-ref="txPointcut" />
		<aop:advisor advice-ref="fixflowInterceptor" pointcut-ref="txPointcut"  />
	</aop:config>
</beans> 

 由于引擎获取不能用new的方法,所以新建了processEngineFactoryBean类,用来创建引擎,继承spring的FactoryBean工厂,这样就可以用注入的方式获取processEngine了。

package com.ych.factory;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.ApplicationContext;

import com.founder.fix.fixflow.core.ProcessEngine;
import com.founder.fix.fixflow.core.ProcessEngineManagement;
import com.founder.fix.fixflow.core.impl.ProcessEngineImpl;

/**
 * fixflow引擎 spring工厂
 * @author ych
 *
 */
public class ProcessEngineFactoryBean implements FactoryBean<ProcessEngine> ,DisposableBean{

	protected ApplicationContext applicationContext;
	protected ProcessEngineImpl processEngine;
	
	public ProcessEngine getObject() throws Exception {
		return ProcessEngineManagement.getDefaultProcessEngine();
	}

	public void destroy() throws Exception {
		if (processEngine != null) {
			processEngine.closeEngine();
		}
	}

	public Class<?> getObjectType() {
		return ProcessEngine.class;
	}
	public boolean isSingleton() {
		return true;
	}
}

 

         业务系统(例子中是模拟请假操作)的事务正常配置,有transactionManager和相关配置,此处使用了tx标签对service进行了事务配置。

         细心的朋友会发现,此处并没有对fixflow进行事务配置,也没有注入数据源,别急,后面我们还做了一件事情,重写了com.founder.fix.fixflow.expand.database.FixFlowConnectionResultImpl类,重写了getConnection()方法,为什么这么做? 因为fixflow在外界不提供数据源的时候,默认是从这个类获取数据源的,修改后代码如下:

public Connection getConnection() {
		try {
			if (this.connection == null) {
				//通过spring取数据源,此处可以自己扩展,用spring注入测方式封装,保证灵活
				//此方法获取的数据源可以保证在spring的事务上下文中
				this.connection = DataSourceUtils.getConnection((DataSource)SpringBeanUtil.getBean("dataSource"));
				return this.connection;
			} else {
				return this.connection;

			}
		} catch (Exception e) {
			throw new FixFlowDbException(e.getMessage(), e);
		}
	}

 

         熟悉spring事务的用户看到这里就明白了,因为DataSourceUtils.getConnection(DataSource dataSource)工具类获取的connection默认是在spring的事务上下文的。所以只要在上面配置事务的service中调用fixflow的任何方法,都是会走spring的事务管理的。

 

         到这里,fixflow和我们的业务系统事务就统一了!!

 

         注:其实这是山寨的做法,其实fixflow是提供了数据源管理器功能的,在fixflowconfig.xml的connectionManagementConfig节点,官方推荐的是重写这两个管理器类的,有兴趣的可以自己实现。

 

 

然后业务service(LeaveService)里面我们怎么做呢

看业务LeaveService代码:

/**
	 * 请假处理,包括请假开始和审批
	 * 如果taskId为空,则保存业务数据,进行发起操作,否则,不处理业务数据,进行审批操作
	 * @param params
	 */
	public void saveLeave(Map<String,Object> params){
		String qjbh = params.get("qjbh").toString();
		String nextAssgine = StringUtil.getString(params.get("nextAssgine"));
		Object taskId = params.get("taskId");
		//不存在taskId,表示第一次启动流程,需要保存业务数据
		if(taskId == null || taskId.equals("")){
			String qjr = params.get("qjr").toString();
			String qjsj = params.get("qjsj").toString();
			LeaveModel leave = new LeaveModel();
			leave.setQjbh(qjbh);
			leave.setQjr(qjr);
			leave.setQjsj(qjsj);
			leaveDao.saveLeave(leave);
		}
		
		//将主键作为关连建给流程引擎
		params.put("bizKey", qjbh);
		//给流程设置变量,nextAssignee在流程定义中被设置为审批节点的处理人,详见流程定义
		
		if(StringUtil.isNotEmpty(nextAssgine)){
			Map<String,Object> taskVariable = new HashMap<String,Object>();
			taskVariable.put("nextAssignee", nextAssgine);
			params.put("taskVariable", taskVariable);
		}	
		workFlowService.executeCommand(params);
	}

 

上面是业务系统service调用dao层保存业务数据,下面调用流程service进行流程驱动处workFlowService代码如下:

// 任务服务
	@Autowired
	protected TaskService taskService;
	
	@Autowired
	protected ProcessEngine processEngine;
	
	/**
	 * 这里接口应该接收查询参数和分页参数的,详细参考bpmcenter中的示例,时间关系,简单处理了
	 * @return
	 */
	public List<Map<String,Object>> getToDoTask(){
		List<Map<String,Object>> taskResult = new ArrayList<Map<String,Object>>();
		TaskQuery taskQuery = taskService.createTaskQuery();
		//查询admin的共享和独占任务,此处应该从session里拿当前登录用户
		taskQuery.taskAssignee("1200119390");
		taskQuery.taskCandidateUser("1200119390");
		taskQuery.taskNotEnd();
		List<TaskInstance> tasks = taskQuery.list();
		
		for(TaskInstance task : tasks){
			taskResult.add(task.getPersistentState());
		}
		return taskResult;
	}

通过spring注入的方式,将引擎及相关service注入到workFlowService中,代码看着也比较简洁。

 

需要注意的两点:

  1. processEngine调用的时候是要设置当前登陆人的,如官方示例中的getProcessEngine(userId)
  2. processEngine调用结束之后是要清空线程副本的,不然会造成线程混乱,如官方示例中的:
finally{
   processEngine.contextClose(true,false);
}

 

对于这两个问题,既然用了spring,那我们肯定要用非常漂亮的、很强大的spring aop来做了,所以我做了个拦截器,用来拦截workFlowService中的方法,代码如下:

/**
 * fixflow的拦截器,拦截所有用到ProcessEngine的方法
 * 1.方法执行前,设置当前引擎的操作人
 * 2.方法执行后,清空线程副本
 * @author ych
 *
 */
public class FixflowInterceptor implements AfterReturningAdvice,
		MethodBeforeAdvice {


	@Override
	public void before(Method arg0, Object[] arg1, Object arg2)
			throws Throwable {
		//设置当前操作人,这里应该从当前session读取,在每次执行的时候都要设置
		//如果有多个service用到fixflow引擎,则应该将service抽象成接口进行判断,这里只有一个,所以省事了,直接与事务用同一个切入点,用if判断的偷懒做法
		if(arg2 instanceof WorkFlowService){
			Authentication.setAuthenticatedUserId("1200119390");
		}
	}

	@Override
	public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
		//如果有多个service用到fixflow引擎,则应该将service抽象成接口进行判断,这里只有一个,所以省事了
		if(arg3 instanceof WorkFlowService){
			WorkFlowService serivce = (WorkFlowService)arg3;
			serivce.closeEngine();
		}
	}

 

这样,整个集成基本就结束了,包含事务处理,引擎初始化操作,引擎清理操作,都交给spring处理,业务service的代码也变的非常简洁,非常干净。至于功能的扩展和完善,还是需要参考官方示例中的逻辑及相关代码的。

 

fixflow官方提供的示例也是一种集成方式,对于不同的代码风格,集成方式也不同,不论是fixflow,还是其他项目,只要理解了原理,集成的方式是多种多样,所谓“条条大路通罗马”,说的也是这个道理。

 

本人也是spring的初学者,如果大神有更好的做法,或者有什么潜在的bug,欢迎提出来共同探讨。

最后附上项目地址:

https://github.com/yangchenhui/fixflowSpringDemo-noMaven

 

 

 

  • fixflow与spring的集成示例
            
    
    博客分类: Fixflow fixflowspring集成事务 
  • 大小: 26.8 KB