Spring 集成
虽然你完全可以不使用 Spring 来使用 Flowable,但我们也提供了一些非常好用的集成特性,本章将对此进行讲解。
CmmnEngineFactoryBean
CmmnEngine 可以配置为一个普通的 Spring Bean。集成的起点是 org.flowable.cmmn.spring.CmmnEngineFactoryBean 这个类。该 Bean 接收一个 CMMN 引擎配置并创建 CMMN 引擎。这意味着面向 Spring 的属性创建与配置方式与配置小节中记录的相同。对于 Spring 集成,配置和引擎 Bean 将如下所示:
<bean id="cmmnEngineConfiguration" class="org.flowable.cmmn.spring.SpringCmmnEngineConfiguration">
...
</bean>
<bean id="cmmnEngine" class="org.flowable.cmmn.spring.CmmnEngineFactoryBean">
<property name="cmmnEngineConfiguration" ref="cmmnEngineConfiguration" />
</bean>
注意,cmmnEngineConfiguration Bean 现在使用的是 org.flowable.cmmn.spring.SpringCmmnEngineConfiguration 类。
默认 Spring 配置
下面展示的配置包含 dataSource、transactionManager、cmmnEngine 以及 Flowable 引擎服务。
当把 DataSource 传递给 SpringCmmnEngineConfiguration(使用 "dataSource" 属性)时,Flowable 内部会使用 org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy 包装所传入的 DataSource。这样做是为了确保从 DataSource 获取的 SQL 连接与 Spring 事务能够良好配合。这意味着你不再需要在 Spring 配置中自己代理 dataSource,不过仍然可以将一个 TransactionAwareDataSourceProxy 传给 SpringCmmnEngineConfiguration。在这种情况下,不会再进行额外的包装。
请务必注意,当你自己在 Spring 配置中声明 TransactionAwareDataSourceProxy 时,不要将它用于已经感知 Spring 事务的资源(例如 DataSourceTransactionManager 需要未被代理的 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="cmmnEngineConfiguration" class="org.flowable.cmmn.spring.SpringCmmnEngineConfiguration">
<property name="dataSource" ref="dataSource" />
<property name="transactionManager" ref="transactionManager" />
<property name="databaseSchemaUpdate" value="true" />
</bean>
<bean id="cmmnEngine" class="org.flowable.cmmn.spring.CmmnEngineFactoryBean">
<property name="cmmnEngineConfiguration" ref="cmmnEngineConfiguration" />
</bean>
<bean id="cmmnRepositoryService" factory-bean="cmmnEngine" factory-method="getCmmnRepositoryService" />
<bean id="cmmnRuntimeService" factory-bean="cmmnEngine" factory-method="getCmmnRuntimeService" />
<bean id="cmmnTaskService" factory-bean="cmmnEngine" factory-method="getCmmnTaskService" />
<bean id="cmmnHistoryService" factory-bean="cmmnEngine" factory-method="getCmmnHistoryService" />
<bean id="cmmnManagementService" factory-bean="cmmnEngine" factory-method="getCmmnManagementService" />
...
首先,使用 Spring 支持的任意一种方式创建应用上下文。在本例中,你可以使用一个 classpath XML 资源来配置 Spring 应用上下文:
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(
"org/flowable/cmmn/examples/spring/SpringIntegrationTest-context.xml");
或者,由于这是一个测试:
@ContextConfiguration(
"classpath:org/flowable/cmmn/spring/test/SpringIntegrationTest-context.xml")
表达式
当使用 CmmnEngineFactoryBean 时,默认情况下 CMMN 流程中的所有表达式也都能"看到"所有 Spring Bean。你可以通过配置一个 map 来限制希望在表达式中暴露的 Bean(甚至一个都不暴露)。下面的例子只暴露了一个 Bean(printer),可以在 key "printer" 下使用。要完全不暴露任何 Bean,只需在 SpringCmmnEngineConfiguration 上传入一个空列表作为 'beans' 属性。当未设置 'beans' 属性时,上下文中的所有 Spring Bean 都将可用。
<bean id="cmmnEngineConfiguration" class="org.flowable.cmmn.spring.SpringCmmnEngineConfiguration">
...
<property name="beans">
<map>
<entry key="printer" value-ref="printer" />
</map>
</property>
</bean>
<bean id="printer" class="org.flowable.cmmn.examples.spring.Printer" />
现在,这些暴露的 Bean 就可以在表达式中使用了:
...
<case id="myCase">
<casePlanModel id="myPlanModel" name="My CasePlanModel">
<planItem id="planItem1" name="Task One" definitionRef="serviceTask" />
<planItem id="planItem2" name="The Case" definitionRef="task">
<entryCriterion sentryRef="sentry1" />
</planItem>
<sentry id="sentry1">
<planItemOnPart sourceRef="planItem1">
<standardEvent>complete</standardEvent>
</planItemOnPart>
</sentry>
<task id="serviceTask" flowable:type="java" flowable:expression="${printer.printMessage(var1)}" flowable:resultVariableName="customResponse" />
<task id="task" name="The Task" isBlocking="true" />
</casePlanModel>
</case>
其中 Printer 类如下所示:
public class Printer {
public void printMessage(String var) {
System.out.println("hello " + var);
}
}
Spring Bean 配置(如上面所示)如下所示:
<beans>
...
<bean id="printer" class="org.flowable.cmmn.examples.spring.Printer" />
</beans>
资源自动部署
Spring 集成还提供了一个用于部署资源的特殊特性。在 CMMN 引擎配置中,你可以指定一组资源。当 CMMN 引擎创建时,会扫描并部署所有这些资源。其中设有过滤机制,以防止重复部署。只有当资源确实发生变化时,才会向 Flowable 数据库执行新的部署。这在很多使用场景中都很有意义,例如 Spring 容器频繁重启的场景(如测试)。
下面是一个例子:
<bean id="cmmnEngineConfiguration" class="org.flowable.cmmn.spring.SpringCmmnEngineConfiguration">
...
<property name="deploymentResources"
value="classpath*:/org/flowable/cmmn/spring/test/autodeployment/autodeploy.*.cmmn" />
</bean>
<bean id="cmmnEngine" class="org.flowable.cmmn.spring.CmmnEngineFactoryBean">
<property name="cmmnEngineConfiguration" ref="cmmnEngineConfiguration" />
</bean>
默认情况下,上述配置会将所有匹配过滤器的资源分组为一个单一部署部署到 Flowable 引擎。用于防止重复部署未变更资源的去 重过滤适用于整个部署。在某些情况下,这可能不是你想要的。例如,如果你以这种方式部署一组流程资源,而其中只有一个用例定义发生了变化,那么整个部署都会被视为新部署,该部署中的所有用例定义都会被重新部署,从而为每个用例定义生成新版本,尽管实际上只有一个发生了变化。
为了能够自定义部署的判定方式,你可以在 SpringCmmnEngineConfiguration 中指定一个额外的属性 deploymentMode。该属性定义了如何根据匹配过滤器的资源集合来判定部署。该属性默认支持 3 个取值:
- default:将所有资源分组为一个单一部署,并对该部署应用去重过滤。这是默认值,当你不指定取值时将使用该值。
- single-resource:为每个单独的资源创建一个独立的部署,并对该部署应用去重过滤。如果你希望每个流程定义单独部署,并且只在用例定义发生变化时才创建新版本,就使用这个取值。
- resource-parent-folder:为共享同一父文件夹的资源创建一个独立的部署,并对该部署应用去重过滤。该取值可以为大多数资源创建独立部署,同时仍能通过将部分资源放在共享文件夹中来对其分组。下面是一个为 deploymentMode 指定 single-resource 配置的例子:
<bean id="cmmnEngineConfiguration"
class="org.flowable.cmmn.spring.SpringCmmnEngineConfiguration">
...
<property name="deploymentResources" value="classpath*:/flowable/*.cmmn" />
<property name="deploymentMode" value="single-resource" />
</bean>
除了为 deploymentMode 使用上面列出的取值之外,你可能还需要自定义部署的判定行为。如果是这样,你可以创建 SpringCmmnEngineConfiguration 的子类,并重写 getAutoDeploymentStrategy(String deploymentMode) 方法。该方法决定对于某个 deploymentMode 配置取值使用哪种部署策略。
单元测试
与 Spring 集成时,可以使用标准的 Flowable 测试设施 非常方便地测试业务用例。下面的例子展示了如何在典型的基于 Spring 的 JUnit Jupiter 测试中测试一个用例定义:
JUnit Jupiter 测试。
@ExtendWith(FlowableCmmnSpringExtension.class)
@SpringJUnitConfig(CmmnSpringJunitJupiterTest.TestConfiguration.class)
class MyBusinessCaseTest {
@Autowired
private CmmnRepositoryService cmmnRepositoryService;
@Autowired
private CmmnRuntimeService cmmnRuntimeService;
@Test
@CmmnDeployment
public void simpleCaseTest() {
CaseInstance caseInstance = cmmnRuntimeService.createCaseInstanceBuilder()
.caseDefinitionKey("simpleCase")
.variable("var1", "John Doe")
.start();
Assertions.assertNotNull(caseInstance);
}
}