写点什么

Spring 集成使用工作流框架 Activiti!事务配置,注解配置以及流程资源自动化部署

发布于: 2021 年 05 月 28 日

Spring 使用 Activiti 提供了一些非常不错的集成特性,只在 Activiti 与 Spring 集成时使用

ProcessEngineFactoryBean

  • 可以把流程引擎(ProcessEngine)作为一个普通的 Spring bean 进行配置

  • org.activiti.spring.ProcessEngineFactoryBean 是集成的切入点,这个 bean 需要一个流程引擎配置来创建流程引擎

  • Spring 集成的配置和流程引擎 bean,使用的 processEngineConfiguration bean org.activiti.spring.SpringProcessEngineConfiguration 类:


<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">    ...</bean>
<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean"> <property name="processEngineConfiguration" ref="processEngineConfiguration" /></bean>
复制代码

事务

  • 使用这个例子的 Spring 配置文件 SpringTransactionIntegrationTest-context.xml:

  • dataSource: 数据源

  • transactionManager: 事务管理器

  • processEngine: 流程引擎

  • Activiti 引擎服务

  • 当把数据源(DataSource)传递给 SpringProcessEngineConfiguration(使用"dataSource"属性)之后,Activiti 内部使用了一个 org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy 代理来封装传递进来的数据源(DataSource).这样做是为了确保从数据源(DataSource)获取的 SQL 连接能够与 Spring 的事物结合在一起发挥得更出色.这意味不需要在 Spring 配置中代理数据源(dataSource). 但是仍然允许传递一个 TransactionAwareDataSourceProxy SpringProcessEngineConfiguration

  • 为了确保在 Spring 配置中声明的一个 TransactionAwareDataSourceProxy,不能把使用它的应用交给 Spring 事物控制的资源(例如 DataSourceTransactionManager JPATransactionManager 需要非代理的数据源)


<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">
<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" />
...
复制代码


  • Spring 配置文件的其余部分包含 beans 和在特有的例子中的配置:


<beans>  ...  <tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="userBean" class="org.activiti.spring.test.UserBean"> <property name="runtimeService" ref="runtimeService" /> </bean>
<bean id="printer" class="org.activiti.spring.test.Printer" />
</beans>
复制代码


  • 首先使用任意的一种 Spring 创建应用上下文的方式创建其 Spring 应用上下文.可以使用类路径下面的 XML 资源来配置我们的 Spring 应用上下文:


ClassPathXmlApplicationContext applicationContext =    new ClassPathXmlApplicationContext("org/activiti/examples/spring/SpringTransactionIntegrationTest-context.xml");
复制代码


或者


@ContextConfiguration("classpath:org/activiti/spring/test/transaction/SpringTransactionIntegrationTest-context.xml")
复制代码


  • 然后我们就可以得到 Activiti 的服务 beans 并且调用该服务上面的方法,ProcessEngineFactoryBean 将会对该服务添加一些额外的拦截器,在 Activiti 服务上面的方法使用的是 Propagation.REQUIRED 事物语义. 可以使用 repositoryService 去部署一个流程:


RepositoryService repositoryService = (RepositoryService) applicationContext.getBean("repositoryService");String deploymentId = repositoryService  .createDeployment()  .addClasspathResource("org/activiti/spring/test/hello.bpmn20.xml")  .deploy()  .getId();   
复制代码


  • 其他相同的服务也是同样可以这么使用.在这个例子中,Spring 的事务将会围绕在 userBean.hello()上,并且调用 Activiti 服务的方法也会加入到这个事务中


UserBean userBean = (UserBean) applicationContext.getBean("userBean");userBean.hello();
复制代码


  • 在上面 Spring bean 的配置中把 repositoryService 注入到 userBean 中


public class UserBean {
/** 由Spring注入 */ private RuntimeService runtimeService;
@Transactional public void hello() { //这里,你可以在你们的领域模型中做一些事物处理。 //当在调用Activiti RuntimeService的startProcessInstanceByKey方法时, //它将会结合到同一个事物中。 runtimeService.startProcessInstanceByKey("helloProcess"); }
public void setRuntimeService(RuntimeService runtimeService) { this.runtimeService = runtimeService; }}
复制代码

表达式

  • 当使用 ProcessEngineFactoryBean 时候,默认情况下,在 BPMN 流程中的所有表达式都将会"看见"所有的 Spring beans. 可以限制在表达式中暴露出的 beans 或者甚至可以在配置中使用一个 Map 不暴露任何 beans

  • 想要不暴露任何 beans,只需要在 SpringProcessEngineConfiguration 中传递一个空的 list 作为'beans'的属性. 当不设置'beans'的属性时,在应用上下文中 Spring beans 都是可以使用的

  • 下面的例子暴露了一个单例 bean(printer),可以把"printer"当作关键字使用:


<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" />
复制代码


  • 现在暴露出来的 beans 就可以在表达式中使用:例如,在 SpringTransactionIntegrationTest 中的 hello.bpmn20.xml 展示的是如何使用 UEL 方法表达式去调用 Spring bean 的方法


<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>
复制代码


  • 这里的 Printer 类似这样:


public class Printer {
public void printMessage() { System.out.println("hello world"); }}
复制代码


  • Spring bean 的配置类似这样:


<beans ...>  ...
<bean id="printer" class="org.activiti.examples.spring.Printer" />
</beans>
复制代码

资源自动部署

  • Spring 的集成有专门针对对资源部署的特性

  • 在流程引擎的配置中,可以指定一组资源,当流程引擎被创建的时候,所有在这里的资源都将会被自动扫描与部署

  • 在这里有过滤以防止资源重新部署,只有当这个资源真正发生改变的时候,它才会向 Activiti 使用的数据库创建新的部署.

  • 这对于很多用例来说,当 Spring 容器经常重启的情况下,使用是非常不错的选择


<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>
复制代码


默认情况下,上面的配置会把所有匹配的资源发布到 Activiti 引擎的一个单独发布包下.用来检测防止未修改资源重复发布的机制会作用到整个发布包中有时候,这可能不是想要的.比如,如果你发布了很多流程资源,但是只修改里其中某一个单独的流程定义,整个发布包都会被认为变更了,导致整个发布包下的所有流程定义都会被重新发布,结果就是每个流程定义都生成了新版本,虽然其中只有一个流程发生了改变


  • 为了定制发布方式, 可以为 SpringProcessEngineConfiguration 指定一个额外的参数 deploymentMode. 这个参数指定了匹配多个资源时的发布处理方式. 默认下这个参数支持设置三个值:

  • default: 把所有资源放在一个单独的发布包中,对这个发布包进行重复检测.这是默认值,如果你没有指定参数值,就会使用它

  • single-resource: 为每个单独的资源创建一个发布包,并对这些发布包进行重复检测.你可以单独发布每个流程定义,并在修改流程定义后只创建一个新的流程定义版本

  • resource-parent-folder: 把放在同一个上级目录下的资源发布在一个单独的发布包中,并对发布包进行重复检测.当需要多资源时需要创建发布包;但是需要根据共同的文件夹来组合一些资源时,可以使用

  • deploymentMode 参数配置为 single-resource 的情况:


<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">  ...  <property name="deploymentResources" value="classpath*:/activiti/*.bpmn" />  <property name="deploymentMode" value="single-resource" /></bean>
复制代码


  • 如果想使用上面三个值之外的参数值,你需要自定义处理发布包的行为.可以创建一个 SpringProcessEngineConfiguration 的子类,重写 getAutoDeploymentStrategy(String deploymentMode)方法. 这个方法中处理了对应 deploymentMode 的发布策略

单元测试

  • 当集成 Spring 时,使用标准的 Activiti 测试工具类是非常容易地对业务流程进行测试:


@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());
}}
复制代码


  • 对于这种方式,需要在 Spring 配置中定义一个 org.activiti.engine.test.ActivitiRulebean


<bean id="activitiRule" class="org.activiti.engine.test.ActivitiRule">  <property name="processEngine" ref="processEngine" /></bean>
复制代码

基于注解的配置

除了基于 XML 的配置以外,还可以选择基于注解的方式来配置 Spring 环境.这与使用 XML 的方法非常相似,除了要使用 @Bean 注解.而且配置是使用 java 编写的, 可以直接用于 Activiti-Spring 的集成


  • @EnableActiviti: 会创建一个 Spring 环境,并对 Activiti 流程引擎进行配置

  • 默认的内存 H2 数据库,启用数据库自动升级

  • 一个简单的 DataSourceTransactionManager

  • 一个默认的 SpringJobExecutor

  • 自动扫描 processes/ 目录下的 bpmn20.xml 文件


  @Configuration  @EnableActiviti  public static class SimplestConfiguration {
}
复制代码


  • 可以直接通过注入操作 Activiti 引擎:


 @Autowired  private ProcessEngine processEngine;
@Autowired private RuntimeService runtimeService;
@Autowired private TaskService taskService;
@Autowired private HistoryService historyService;
@Autowired private RepositoryService repositoryService;
@Autowired private ManagementService managementService;
@Autowired private FormService formService;
复制代码


  • 默认值都可以自定义:

  • 如果配置了 DataSource,就会代替默认创建的数据库配置

  • 事务管理器,ob 执行器和其他组件都与之相同


@Configuration  @EnableActiviti  public static class Config {
@Bean public DataSource dataSource() { BasicDataSource basicDataSource = new BasicDataSource(); basicDataSource.setUsername("sa"); basicDataSource.setUrl("jdbc:h2:mem:anotherDatabase"); basicDataSource.setDefaultAutoCommit(false); basicDataSource.setDriverClassName(org.h2.Driver.class.getName()); basicDataSource.setPassword(""); return basicDataSource; }
}
复制代码


其他数据库会代替默认的


  • 注意 AbstractActivitiConfigurer 用法,它暴露了流程引擎的配置,可以用来对它的细节进行详细的配置:


@Configuration@EnableActiviti@EnableTransactionManagement(proxyTargetClass = true)class JPAConfiguration {
@Bean public OpenJpaVendorAdapter openJpaVendorAdapter() { OpenJpaVendorAdapter openJpaVendorAdapter = new OpenJpaVendorAdapter(); openJpaVendorAdapter.setDatabasePlatform(H2Dictionary.class.getName()); return openJpaVendorAdapter; }
@Bean public DataSource dataSource() { BasicDataSource basicDataSource = new BasicDataSource(); basicDataSource.setUsername("sa"); basicDataSource.setUrl("jdbc:h2:mem:activiti"); basicDataSource.setDefaultAutoCommit(false); basicDataSource.setDriverClassName(org.h2.Driver.class.getName()); basicDataSource.setPassword(""); return basicDataSource; }
@Bean public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean( OpenJpaVendorAdapter openJpaVendorAdapter, DataSource ds) { LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean(); emf.setPersistenceXmlLocation("classpath:/org/activiti/spring/test/jpa/custom-persistence.xml"); emf.setJpaVendorAdapter(openJpaVendorAdapter); emf.setDataSource(ds); return emf; }
@Bean public PlatformTransactionManager jpaTransactionManager( EntityManagerFactory entityManagerFactory) { return new JpaTransactionManager(entityManagerFactory); }
@Bean public AbstractActivitiConfigurer abstractActivitiConfigurer( final EntityManagerFactory emf, final PlatformTransactionManager transactionManager) {
return new AbstractActivitiConfigurer() {
@Override public void postProcessSpringProcessEngineConfiguration(SpringProcessEngineConfiguration engine) { engine.setTransactionManager(transactionManager); engine.setJpaEntityManagerFactory(emf); engine.setJpaHandleTransaction(false); engine.setJobExecutorActivate(false); engine.setJpaCloseEntityManager(false); engine.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE); } }; }
// A random bean @Bean public LoanRequestBean loanRequestBean() { return new LoanRequestBean(); }}
复制代码

JPA 和 Hibernate

  • 在 Activiti 引擎的 serviceTask listener 中使用 Hibernate 4.2.x JPA 时,需要添加 Spring ORM 依赖,Hibernate 4.1.x 及以下版本是不需要的:


<dependency>  <groupId>org.springframework</groupId>  <artifactId>spring-orm</artifactId>  <version>${org.springframework.version}</version></dependency>
复制代码


发布于: 2021 年 05 月 28 日阅读数: 23
用户头像

一位攻城狮的自我修养 2021.04.06 加入

分享技术干货,面试题和攻城狮故事。 你的关注支持是我持续进步的最大动力! https://github.com/ChovaVea

评论

发布
暂无评论
Spring集成使用工作流框架Activiti!事务配置,注解配置以及流程资源自动化部署