跳到主要内容

Spring 集成

Doubao 中英对照 Spring integration

虽然你完全可以在不使用 Spring 的情况下使用 Flowable,但我们提供了一些非常好的集成特性,本章将对其进行讲解。

ProcessEngineFactoryBean​

ProcessEngine 可以像常规 Spring bean 一样配置。集成的起点是 org.flowable.spring.ProcessEngineFactoryBean 类。该 bean 接收一个流程引擎配置并创建流程引擎。这意味着 Spring 中属性的创建和配置方式与配置章节中所述相同。对于 Spring 集成,配置和引擎 bean 如下所示:

<bean id="processEngineConfiguration" class="org.flowable.spring.SpringProcessEngineConfiguration">
...
</bean>

<bean id="processEngine" class="org.flowable.spring.ProcessEngineFactoryBean">
<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>

请注意,processEngineConfiguration bean 现在使用的是 org.flowable.spring.SpringProcessEngineConfiguration 类。

事务​

我们将逐步讲解发行版 Spring 示例中的 SpringTransactionIntegrationTest。下面是本示例使用的 Spring 配置文件(你可以在 SpringTransactionIntegrationTest-context.xml 中找到它)。所示部分包含 dataSource、transactionManager、processEngine 以及 Flowable 引擎服务。

在将 DataSource 传给 SpringProcessEngineConfiguration(使用 "dataSource" 属性)时,Flowable 内部会使用 org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy,对传入的 DataSource 进行包装。这样做是为了确保从 DataSource 获取的 SQL 连接与 Spring 事务能够良好配合。这意味着不再需要在 Spring 配置中自行代理 dataSource,不过仍然可以将一个 TransactionAwareDataSourceProxy 传给 SpringProcessEngineConfiguration。在这种情况下,不会再进行额外包装。

请务必注意,如果你自行在 Spring 配置中声明 TransactionAwareDataSourceProxy,不要将它用于已经感知 Spring 事务的资源(例如 DataSourceTransactionManager 和 JPATransactionManager 需要未代理的 dataSource)。

<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:flowable;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.flowable.spring.SpringProcessEngineConfiguration">
<property name="dataSource" ref="dataSource" />
<property name="transactionManager" ref="transactionManager" />
<property name="databaseSchemaUpdate" value="true" />
<property name="asyncExecutorActivate" value="false" />
</bean>

<bean id="processEngine" class="org.flowable.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 配置文件的其余部分包含我们在这个特定示例中使用的 bean 和配置:

<beans>
...
<tx:annotation-driven transaction-manager="transactionManager"/>

<bean id="userBean" class="org.flowable.spring.test.UserBean">
<property name="runtimeService" ref="runtimeService" />
</bean>

<bean id="printer" class="org.flowable.spring.test.Printer" />

</beans>

首先,使用 Spring 支持的任意一种方式创建应用上下文。在本例中,你可以使用一个 classpath XML 资源来配置 Spring 应用上下文:

ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(
"org/flowable/examples/spring/SpringTransactionIntegrationTest-context.xml");

或者,由于这是一个测试:

@ContextConfiguration(
"classpath:org/flowable/spring/test/transaction/SpringTransactionIntegrationTest-context.xml")

然后我们就可以获取服务 bean 并调用它们的方法。ProcessEngineFactoryBean 会为这些服务额外添加一个拦截器,在 Flowable 服务方法上应用 Propagation.REQUIRED 事务语义。因此,例如我们可以这样使用 repositoryService 来部署流程:

RepositoryService repositoryService =
(RepositoryService) applicationContext.getBean("repositoryService");
String deploymentId = repositoryService
.createDeployment()
.addClasspathResource("org/flowable/spring/test/hello.bpmn20.xml")
.deploy()
.getId();

反过来同样可行。在这种情况下,Spring 事务将围绕 userBean.hello() 方法,而 Flowable 服务方法调用将加入同一个事务。

UserBean userBean = (UserBean) applicationContext.getBean("userBean");
userBean.hello();

UserBean 如下所示。请回忆上面的 Spring bean 配置,我们将 repositoryService 注入到了 userBean 中。

public class UserBean {

/** injected by Spring */
private RuntimeService runtimeService;

@Transactional
public void hello() {
// here you can do transactional stuff in your domain model
// and it will be combined in the same transaction as
// the startProcessInstanceByKey to the Flowable RuntimeService
runtimeService.startProcessInstanceByKey("helloProcess");
}

public void setRuntimeService(RuntimeService runtimeService) {
this.runtimeService = runtimeService;
}
}

表达式​

使用 ProcessEngineFactoryBean 时,默认情况下 BPMN 流程中的所有表达式也会"看到"所有 Spring bean。可以通过配置一个 map 来限制你想在表达式中暴露的 bean 数量(甚至一个都不暴露)。下面的示例暴露了单个 bean(printer),可通过 key "printer" 使用。如果完全不想暴露任何 bean,只需在 SpringProcessEngineConfiguration 上以 'beans' 属性传入一个空列表。当未设置 'beans' 属性时,上下文中的所有 Spring bean 都将可用。

<bean id="processEngineConfiguration" class="org.flowable.spring.SpringProcessEngineConfiguration">
...
<property name="beans">
<map>
<entry key="printer" value-ref="printer" />
</map>
</property>
</bean>

<bean id="printer" class="org.flowable.examples.spring.Printer" />

现在暴露的 bean 可以在表达式中使用:例如,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" flowable: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.flowable.examples.spring.Printer" />

</beans>

自动资源部署​

Spring 集成还提供了用于部署资源的特殊功能。在流程引擎配置中,可以指定一组资源。创建流程引擎时,所有这些资源都会被扫描并部署。内置了过滤机制以防止重复部署。只有当资源实际发生变化时,才会将新的部署部署到 Flowable 数据库。这在很多用例中都很有意义,例如 Spring 容器频繁重启的场景(如测试)。

下面是一个示例:

<bean id="processEngineConfiguration" class="org.flowable.spring.SpringProcessEngineConfiguration">
...
<property name="deploymentResources"
value="classpath*:/org/flowable/spring/test/autodeployment/autodeploy.*.bpmn20.xml" />
</bean>

<bean id="processEngine" class="org.flowable.spring.ProcessEngineFactoryBean">
<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>

默认情况下,上面的配置会将匹配该过滤器的所有资源分组到一个部署中,部署到 Flowable 引擎。用于防止未变更资源重复部署的重复过滤作用于整个部署。在某些情况下,这可能不是你想要的。例如,如果你以这种方式部署一组流程资源,而这些资源中只有一个流程定义发生了变化,那么整个部署都将被视为新部署,该部署中的所有流程定义都会被重新部署,从而导致每个流程定义都生成新版本,尽管实际上只有一个发生了变化。

为了能够自定义部署的判定方式,可以在 SpringProcessEngineConfiguration 中指定一个额外属性 deploymentMode。该属性定义了如何根据匹配过滤器的资源集合来确定部署。该属性默认支持以下 3 个值:

  • default:将所有资源分组到单个部署中,并对该部署应用重复过滤。这是默认值,如果你不指定值,就会使用它。
  • single-resource:为每个独立资源创建单独的部署,并对该部署应用重复过滤。如果你希望每个流程定义单独部署、并且只有在其发生变化时才创建新的流程定义版本,就应使用这个值。
  • resource-parent-folder:为共享同一父文件夹的资源创建单独的部署,并对该部署应用重复过滤。这个值可以为大多数资源创建单独部署,同时仍能通过将某些资源放在共享文件夹中来对其分组。下面是为 deploymentMode 指定 single-resource 配置的示例:
<bean id="processEngineConfiguration"
class="org.flowable.spring.SpringProcessEngineConfiguration">
...
<property name="deploymentResources" value="classpath*:/flowable/*.bpmn" />
<property name="deploymentMode" value="single-resource" />
</bean>

除了使用上面列出的 deploymentMode 值之外,你可能还需要自定义确定部署的行为。如果是这样,可以创建 SpringProcessEngineConfiguration 的子类并重写 getAutoDeploymentStrategy(String deploymentMode) 方法。该方法决定了针对某个 deploymentMode 配置值使用哪种部署策略。

单元测试​

与 Spring 集成时,可以使用标准的 Flowable 测试工具非常方便地测试业务流程。
以下示例展示了在典型的基于 Spring 的 JUnit Jupiter 测试中如何测试业务流程:

JUnit 5 测试。

@ExtendWith(FlowableSpringExtension.class)
@SpringJUnitConfig(SpringJunitJupiterTest.TestConfiguration.class)
public class MyBusinessProcessTest {

@Autowired
private RuntimeService runtimeService;

@Autowired
private TaskService taskService;

@Test
@Deployment
void simpleProcessTest() {
runtimeService.startProcessInstanceByKey("simpleProcess");
Task task = taskService.createTaskQuery().singleResult();
assertEquals("My Task", task.getName());

taskService.complete(task.getId());
assertEquals(0, runtimeService.createProcessInstanceQuery().count());

}
}

使用 FlowableSpringExtension 后,就可以使用 Deployment 注解。

使用 Hibernate 4.2.x 的 JPA​

在 Flowable 引擎的服务任务或监听器逻辑中使用 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>