摘要: 一、集成配置 <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource"> <property name="driverClass" value="org.
一、集成配置
<bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
<property name="driverClass" value="org.h2.Driver" />
<property name="url" value="jdbc:h2:mem:activiti;DB_CLOSE_DELAY=1000" />
<property name="username" value="sa" />
<property name="password" value="" />
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
<property name="dataSource" ref="dataSource" />
<property name="transactionManager" ref="transactionManager" />
<property name="databaseSchemaUpdate" value="true" />
<property name="jobExecutorActivate" value="false" />
</bean>
<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>
<bean id="repositoryService" factory-bean="processEngine" factory-method="getRepositoryService" />
<bean id="runtimeService" factory-bean="processEngine" factory-method="getRuntimeService" />
<bean id="taskService" factory-bean="processEngine" factory-method="getTaskService" />
<bean id="historyService" factory-bean="processEngine" factory-method="getHistoryService" />
<bean id="managementService" factory-bean="processEngine" factory-method="getManagementService" />
二、资源自动部署
为了定制发布方式,你可以为SpringProcessEngineConfiguration指定一个额外的参数deploymentMode。
这个参数指定了匹配多个资源时的发布处理方式。默认下这个参数支持设置三个值:
-
default
: 把所有资源放在一个单独的发布包中,对这个发布包进行重复检测。 这是默认值,如果你没有指定参数值,就会使用它。 -
single-resource
: 为每个单独的资源创建一个发布包,并对这些发布包进行重复检测。 你可以单独发布每个流程定义,并在修改流程定义后只创建一个新的流程定义版本。 -
resource-parent-folder
: 把放在同一个上级目录下的资源发布在一个单独的发布包中,并对发布包进行重复检测。 当需要多资源需要创建发布包,但是需要根据共同的文件夹来组合一些资源时,可以使用它。
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
...
</bean>
===============================================================================================
Spring和Activiti的整合:
在Spring和Activiti的整合中ProcessEngineFactoryBean成为了两者的整合点。ProcessEngineFactoryBean为org.activiti.spring.ProcessEngineFactoryBean。提供了ProcessEngine的配置和创建的功能。
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
...
</bean>
<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>
如果在包含事物的activiti配置如下:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<!-- 采用spring的数据源类创建一个数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
<property name="driverClass" value="org.h2.Driver" />
<property name="url" value="jdbc:h2:mem:activiti;DB_CLOSE_DELAY=1000" />
<property name="username" value="sa" />
<property name="password" value="" />
</bean>
<!-- 创建一个事物管理器 -->
<bean id="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 创建一个流程引擎的配置对象 -->
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
<property name="dataSource" ref="dataSource" />
<property name="transactionManager" ref="transactionManager" />
<!-- 设置数据库schema的更新方式 -->
<property name="databaseSchemaUpdate" value="true" />
<!-- 是否启动jobExecutor -->
<property name="jobExecutorActivate" value="false" />
</bean>
<!-- 创建一个流程引擎bean -->
<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>
<!-- 创建activiti提供的各种服务 -->
<!-- 工作流仓储服务 -->
<bean id="repositoryService" factory-bean="processEngine" factory-method="getRepositoryService" />
<!-- 工作流运行服务 -->
<bean id="runtimeService" factory-bean="processEngine" factory-method="getRuntimeService" />
<!-- 工作流任务服务-->
<bean id="taskService" factory-bean="processEngine" factory-method="getTaskService" />
<!-- 工作流历史数据服务-->
<bean id="historyService" factory-bean="processEngine" factory-method="getHistoryService" />
<!-- 工作流管理服务-->
<bean id="managementService" factory-bean="processEngine" factory-method="getManagementService" />
<!-- 工作流唯一服务 -->
<bean id="IdentityService" factory-bean="processEngine" factory-method="getIdentityService" />
...工作流中Expressions的使用
在使用spring的SpringProcessEngineConfiguration时,如果没有beans属性表示所有bean都可以暴露给activiti的流程文件xml访问。如果配置beans但是没有配置map中bean信息,则没有暴露的bean。如果map中配置部分bean表示只是暴露部分bean给activiti使用。
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
...
<property name="beans">
<map>
<entry key="printer" value-ref="printer" />
</map>
</property>
</bean>
<bean id="printer" class="org.activiti.examples.spring.Printer" />
暴露的bean可以在activiti的流程xml中使用。如下调用bean的方法
<bean id="printer" class="org.activiti.examples.spring.Printer" />
<definitions id="definitions" ...>
<process id="helloProcess">
<startEvent id="start" />
<sequenceFlow id="flow1" sourceRef="start" targetRef="print" />
<serviceTask id="print" activiti:expression="#{printer.printMessage()}" />
<sequenceFlow id="flow2" sourceRef="print" targetRef="end" />
<endEvent id="end" />
</process>
</definitions>
public class Printer {
public void printMessage() {
System.out.println("hello world");
}
}
自动资源部署(Automatic resource deploy)
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:org/activiti/spring/test/junit4/springTypicalUsageTest-context.xml")
public class MyBusinessProcessTest {
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Autowired
@Rule
public ActivitiRule activitiSpringRule;
@Test
@Deployment
public void simpleProcessTest() {
runtimeService.startProcessInstanceByKey("simpleProcess");
Task task = taskService.createTaskQuery().singleResult();
assertEquals("My Task", task.getName());
taskService.complete(task.getId());
assertEquals(0, runtimeService.createProcessInstanceQuery().count());
}
}
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
...
<property name="deploymentResources"value="classpath*:/org/activiti/spring/test/autodeployment/autodeploy.*.bpmn20.xml" />
</bean>
<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>
Spring和activit的整合测试
当在spring的配置中配置ActivitiRule时候,测试bean将自动被注入:
<bean id="activitiRule" class="org.activiti.engine.test.ActivitiRule">
<property name="processEngine" ref="processEngine" />
</bean>
======================================自动部署所有流程=========================================
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd">
<!--druid数据源-->
<!-- <bean id ="dataSourceActiviti" class="com.alibaba.druid.pool.DruidDataSource">-->
<!--采用内存数据库-->
<!-- <property name="url" value="jdbc:h2:mem:activiti;DB_CLOSE_DELAY=1000;MVCC=TRUE" />
<property name="driverClassName" value="org.h2.Driver" />
<property name="username" value="sa" />
<property name="password" value="" />-->
<!--自己本地的数据库-->
<!-- <property name="url" value="jdbc:mysql://localhost:3306/activiti8unit?useUnicode=true&characterEncoding=utf-8&usessl=false" />
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="username" value="root" />
<property name="password" value="root" />
<property name="initialSize" value="1"/>
<property name="maxActive" value="10"/>
<property name="filters" value="stat,slf4j"/>
</bean>-->
<!--=====================================================================-->
<!-- 配置数据源 -->
<!--<bean id="dataSourceActiviti" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/activiti10unit?useUnicode=true&characterEncoding=utf-8&usessl=false"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
<!– 初始化连接大小 –>
<property name="initialSize" value="1"></property>
<!– 连接池最大数量 –>
<property name="maxActive" value="10"></property>
<!– 连接池最大空闲 –>
<property name="maxIdle" value="10"></property>
<!– 连接池最小空闲 –>
<property name="minIdle" value="2"></property>
<!– 获取连接最大等待时间 –>
<property name="maxWait" value="10000"></property>
</bean>-->
<!-- <bean id="dataSourceActiviti" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/activiti10unit?useUnicode=true&characterEncoding=utf-8&usessl=false"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
<!– 初始化连接大小 –>
<property name="initialSize" value="1"></property>
<!– 连接池最大数量 –>
<property name="maxActive" value="10"></property>
<!– 连接池最大空闲 –>
<property name="maxIdle" value="10"></property>
<!– 连接池最小空闲 –>
<property name="minIdle" value="2"></property>
<!– 获取连接最大等待时间 –>
<property name="maxWait" value="10000"></property>
</bean>-->
<!-- 单独创建事物管理器-->
<!--<bean id="transactionManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!–<property name="dataSource" ref="dataSourceActiviti"/> 数据源分开–>
<property name="dataSource" ref="dataSource"/> <!–用同一个数据源–>
</bean>-->
<!--Spring集成-->
<!--要单独创建数据库,因为speing要单独对我们的事物做处理 事物是基于数据库的事物-->
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
<!--<property name="dataSource" ref="dataSourceActiviti"/> 数据源分开-->
<property name="dataSource" ref="dataSource"/> <!--用同一个数据源-->
<!--数据库策略-->
<property name="databaseSchemaUpdate" value="true" />
<!--注入事物管理器-->
<property name="transactionManager" ref="transactionManager"/>
<!--<property name="jobExecutorActivate" value="false" />-->
<!--如果配自动部署会导致每次启动都会部署一次,需要注意解决方法:具体见文章下面-->
<property name="deploymentResources" value="classpath:/activitit-bpmn20/*.*.xml"/>
<!--是否使用历史数据-->
<property name="dbHistoryUsed" value="true"/>
<!--是否使用身份数据-->
<property name="dbIdentityUsed" value="true"/>
<!--所有表结构加前缀 开头-->
<!--<property name="databaseTablePrefix" value="t_"/>-->
<!--默认属性,流程引擎可以根据传进去数据库的url 驱动等自动判断数据库,所以这个配置可以省略-->
<!--<property name="databaseType" value="mysql"/>-->
<!--记录历史详情的级别 共4个级别 -->
<!--<property name="history" value="none" /> --> <!-- 不记录历史流程,性能高,流程结束后不可读-->
<!--<property name="history" value="activity" />--> <!-- 归档流程实例和活动实例,流程变量不同步-->
<!--<property name="history" value="audit" />--> <!-- 默认值,在activiti基础上同步变量值,保持表单属性-->
<property name="history" value="full" /> <!--全部记录,性能较差,记录所有实例和变量的细节变化-->
<!--是否开启我们的事件日志 true开启 act_evt_log -->
<property name="enableDatabaseEventLogging" value="true"/>
<!--事件处理及监听器配置:1 list -->
<!--<property name="eventListeners">
<list>
<!–自定义监听类–>
<bean class="su.activiti.event.ProcessEventListener"/>
</list>
</property>-->
<!--事件处理及监听器配置:2 map -->
<!-- <property name="typedEventListeners">
<map>
<entry key="PROCESS_STARTED"> <!– 配置key以后只打印PROCESS_STARTED 对应的流程启动日志–>
<list>
<bean class="su.activiti.event.ProcessEventListener"/>
</list>
</entry>
</map>
</property>-->
<!-- 配置自定义MDCComandInvoder 拦截器 引入自己定义的bean -->
<!--<property name="commandInvoker" ref="comandInvoder" />-->
<!--命令拦截器-->
<!-- <property name="customPreCommandInterceptors">
<list> <!–这个配置好像有问题配置后 test 测试 流程启动报错 java.lang.IllegalStateException: Failed to load ApplicationContext–>
<!–定义一个拦截器统计这个命令执行的时间是多少–>
<bean class="su.activiti.interceptor.DurationCommandInterceptor" />
</list>
</property>-->
</bean>
<!-- <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<property name="dataSource" ref="dataSourceActiviti"/>
<property name="databaseSchemaUpdate" value="true"/>
</bean>-->
<bean id="comandInvoder" class="su.activiti.interceptor.MDCComandInvoder" />
<!--构造流程引擎对象-->
<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
<!--指定流程引擎配置对象-->
<property name="processEngineConfiguration" ref="processEngineConfiguration"/>
</bean>
<!--把服务暴露给Spring-->
<!-- 创建activiti提供的各种服务 -->
<!-- 工作流仓储服务 --> <!--部署流程定义文件-->
<bean id="repositoryService" factory-bean="processEngine"
factory-method="getRepositoryService" />
<!-- 工作流运行服务 -->
<bean id="runtimeService" factory-bean="processEngine"
factory-method="getRuntimeService" />
<!-- 工作流任务服务 -->
<bean id="taskService" factory-bean="processEngine"
factory-method="getTaskService" />
<!-- 工作流历史数据服务 -->
<bean id="historyService" factory-bean="processEngine"
factory-method="getHistoryService" />
<!-- 工作流管理服务 -->
<bean id="managementService" factory-bean="processEngine"
factory-method="getManagementService" />
<!-- 工作流唯一服务(用户服务) -->
<bean id="identityService" factory-bean="processEngine" factory-method="getIdentityService"/>
<!-- 表单服务 -->
<bean id="formService" factory-bean="processEngine" factory-method="getFormService"/>
<bean id="dynamicBpmnService" factory-bean="processEngine" factory-method="getDynamicBpmnService"/>
<!--配置测试 Rule-->
<!--
<bean id="activitiRule" class="org.activiti.engine.test.ActivitiRule">
<property name="processEngine" ref="processEngine"/>
</bean>
-->
<!-- <bean id="helloBean" class="com.su.test.HelloBean"></bean>-->
<!--=====================================================================-->
</beans>
========================注意:===============================
下面方法是转载过来的没经过验证;
转自: https://lucky16.iteye.com/blog/1740239
Activiti 整合spring的时候,提供了一个自动部署的特性:
这样当每次启动web容器的时候就会把指定路径的流程资源文件部署到Activiti DB上。不过这样会产生一个问题,资源文件在没经过任何改动的情况下,特别是我们在做Testing的时候,还是会重新部署一个新的版本到DB上,这样会造成不别要的重复部署。我们在部署之前,应该先判断资源文件是否有改动过,如果有,才部署新版本到DB上。
实现这个功能很简单,只需要建立一个实现了 InitializingBean 接口的 spring bean,在afterPropertiesSet()方法里面进行判断和部署就可以了。
Java代码:
/**
*
*/
package com.highcolu.dms.workflow;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.io.Resource;
/**
* @Description:结合Spring自动部署Activit流程定义文件
* @version:2012-12-20
* @author: <a href="mailto:zhouwentao16@gmail.com">周文滔</a>
*/
public class WorkflowDeployer implements InitializingBean,
ApplicationContextAware {
private static final Log LOGGER = LogFactory.getLog(WorkflowDeployer.class);
private Resource[] deploymentResources;
private String category;
ApplicationContext appCtx;
public void setDeploymentResources(Resource[] resources) {
this.deploymentResources = resources;
}
public void setCategory(String category) {
this.category = category;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.appCtx = applicationContext;
}
@Override
public void afterPropertiesSet() throws Exception {
if (category == null) {
throw new FatalBeanException("缺失属性 : category");
}
if (deploymentResources != null) {
RepositoryService repositoryService = appCtx
.getBean(RepositoryService.class);
for (Resource r : deploymentResources) {
String deploymentName = category + "_" + r.getFilename();
String resourceName = r.getFilename();
boolean doDeploy = true;
List<Deployment> deployments = repositoryService
.createDeploymentQuery().deploymentName(deploymentName)
.orderByDeploymenTime().desc().list();
if (!deployments.isEmpty()) {
Deployment existing = deployments.get(0);
try {
InputStream in = repositoryService.getResourceAsStream(
existing.getId(), resourceName);
if (in != null) {
File f = File.createTempFile(
"deployment",
"xml",
new File(System
.getProperty("java.io.tmpdir")));
f.deleteOnExit();
OutputStream out = new FileOutputStream(f);
IOUtils.copy(in, out);
in.close();
out.close();
doDeploy = (FileUtils.checksumCRC32(f) != FileUtils
.checksumCRC32(r.getFile()));
} else
throw new ActivitiException("不能读取资源 "
+ resourceName + ", 输入流为空");
} catch (ActivitiException ex) {
LOGGER.error("Unable to read " + resourceName
+ " of deployment " + existing.getName()
+ ", id: " + existing.getId()
+ ", will re-deploy");
}
}
if (doDeploy) {
repositoryService.createDeployment().name(deploymentName)
.addInputStream(resourceName, r.getInputStream())
.deploy();
LOGGER.warn("文件部署成功 : " + r.getFilename());
}
}
}
}
}
然后在spring的配置文件里面配置好这个bean就可以了.
<bean id="workflowDeployer"
class="com.jeemis.workflow.deployer.WorkflowDeployer">
<property name="category" value="TEST" />
<property name="deploymentResources" value="classpath*:process/TEST.bpmn20.xml" />
</bean>
=====================================================================================