最近不少用户在找fixflow与spring的集成示例,相信也有不少用户自己摸索有了自己的集成方式。所以抽了点时间做了个fixflow与spring集成的Demo.
此示例适用于对fixflow有一定了解的用户。
此示例存在不少bug,仅供参考,完整逻辑还是参考官方示例。
此示例功能:
- 完成了与spring的集成示例,此项目集成的是fixflow-core和fixflow-expand内核项目,与bpmcenter示例项目无关。
- 完成了fixflow-expand打成jar包,并能在设计器中正常运行。
- 模拟请假流程演示业务系统与fixflow引擎的集成,框架用springmvc,业务数据的持久化用jdbcTemplate
首先附上项目代码结构:
提到集成,首先考虑到的是事务以及引擎的操作方式,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中,代码看着也比较简洁。
需要注意的两点:
- processEngine调用的时候是要设置当前登陆人的,如官方示例中的getProcessEngine(userId)
- 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
相关推荐
在这个"rabbitmq与spring集成示例demo"中,我们将探讨如何将RabbitMQ集成到基于Spring的应用程序中,以便利用其消息传递能力。以下是一些关键知识点: 1. **Maven集成**:Maven是Java项目管理工具,用于构建、依赖...
当我们谈到“activityMQ调用示例,spring集成示例”,这意味着我们将探讨如何将ActivityMQ与Spring框架整合,以实现高效、可扩展的系统架构。 首先,我们需要理解ActivityMQ的核心概念。ActivityMQ是一个基于Apache...
- **Spring集成示例**:说明如何配置DWR与Spring的集成,以及如何在JavaScript中调用Spring的Bean。 通过理解并实践这些示例,开发者可以更好地掌握DWR的使用,并将其应用于实际的Web应用中,实现高效、便捷的前后...
Redis缓存+Spring的集成示例 Redis缓存+Spring的集成示例Redis缓存+Spring的集成示例 Redis缓存+Spring的集成示例
solr-5.5.4与spring集成,增加了账号密码身份验证,增加了zk中文分词,增加了增量导入,mysql数据同步,增加了git链接地址
spring-boot-jpa-thymeleaf-curd:spring boot + jpa + thymeleaf 增删改查示例 spring-boot-rabbitmq:spring boot和rabbitmq各种消息应用案例 spring-boot-scheduler:spring boot和定时任务案例 spring-boot-web...
总的来说,这个 drools7.5.0 和 spring 4.3.12 的集成示例提供了一个实用的方法,让开发者可以在 Spring 应用程序中灵活地运用 drools 的规则处理能力,以实现更复杂的业务逻辑。通过理解和应用这个示例,你可以更好...
在本文中,我们将深入探讨SpringJMS的基本概念、如何与ActiveMQ集成,以及如何通过示例代码理解其工作原理。 1. **SpringJMS简介** SpringJMS是Spring框架对JMS API的包装,它简化了生产者和消费者之间的消息通信...
Redis 缓存 + Spring 的集成示例 源码分享! Redis 缓存 + Spring 的集成示例。 本资源是一个最新 spring4 + mybatis3 + Redis 缓存集成的一个简单的 demo,也是博客《Redis 缓存 + Spring 的集成示例》的配套示例...
这是一个web程序的后台代码,整合了spring mvc和mybatis的配置,实现了各地方的电量增删改查,该代码持久层利用mybatis框架,简化了dao层的实现,spring实现了controller层、service层。
Kafka与Spring集成指南 Kafka分布式消息系统是一种高吞吐量、可扩展、基于发布订阅模式的消息系统,广泛应用于大数据处理、实时数据处理和日志处理等领域。Spring Framework是一种流行的Java应用程序框架,提供了一...
DUBBO与Spring Cloud的集成通讯示例
文件名 `spring-mvc-showcase-master` 提示这是一个Spring MVC的示例项目,可能包含了DWR的集成示例。在这个项目中,你可以找到如何将DWR与Spring MVC整合的代码实例,包括控制器定义、DWR配置、前端页面和...
SpringCloud是中国开发者广泛使用的分布式系统微服务框架,它集成了众多优秀的开源项目,提供了一站式的微服务解决方案。本示例代码将带你深入理解SpringCloud的核心功能,包括服务的注册与发现、负载均衡等关键概念...
Java_Apache Camel Spring Boot示例是一个综合性的项目,展示了如何在Spring Boot应用程序中集成Apache Camel框架。Apache Camel是一个流行的开源框架,它简化了企业级集成(EIP,Enterprise Integration Patterns)...
【SpringCamp2015】Spring集成示例基础 Spring Camp 2015 - Spring 集成示例(基础) 使用 Spring 集成 4.1.3.RELEASE 示例是基于Gradle build ㄴ 克隆源代码后,无需任何特殊工作,执行gradlew build工作 ##...
"spring简单示例"可能包含了一些基本的Spring配置和使用案例,让我们来深入探讨一下Spring框架的关键概念和功能。 1. **依赖注入**:Spring框架的核心特性之一是依赖注入,它允许开发者在运行时动态地将依赖关系...
5. **RESTful Web服务**:Spring 3.0支持创建RESTful风格的API,使得服务可以通过HTTP协议暴露,易于与其他系统集成。petstore示例可能会包含处理HTTP请求的控制器,展示了如何构建RESTful端点。 6. **国际化与本地...
spring boot demo 是一个Spring Boot、Spring Cloud的项目示例,根据市场主流的后端技术,共集成了30+个demo,未来将持续更新。该项目包含helloworld(快速入门)、web(ssh项目快速搭建)、aop(切面编程)、data-redis...
介绍Spring Cloud Stream与RabbitMQ集成的代码示例。Spring Cloud Stream是一个建立在Spring Boot和Spring Integration之上的框架,有助于创建事件驱动或消息驱动的微服务。