返回介绍

5.Spring 集成

发布于 2019-10-28 05:33:27 字数 14804 浏览 1082 评论 0 收藏 0

虽然没有 Spring 你也可以使用 Activiti,但是我们提供了一些非常不错的集成特性。这一章我们将介绍这些特性。

ProcessEngineFactoryBean

可以把流程引擎( ProcessEngine )作为一个普通的 Spring bean 进行配置。 类 org.activiti.spring.ProcessEngineFactoryBean 是集成的切入点。 这个 bean 需要一个流程引擎配置来创建流程引擎。这也意味着在文档的配置这一章的介绍属性的创建和配置对于 Spring 来说也是一样的。对于 Spring 集成的配置和流程引擎 bean 看起来像这样:

<bean class="org.activiti.spring.SpringProcessEngineConfiguration">
...
</bean>

<bean class="org.activiti.spring.ProcessEngineFactoryBean">
<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>

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

事务

我们将会一步一步地解释在 Spring examples 中公布的 SpringTransactionIntegrationTest 下面是我们使用这个例子的 Spring 配置文件(你可以在 SpringTransactionIntegrationTest-context.xml 找到它)以下展示的部分包括数据源(dataSource), 事务管理器(transactionManager),流程引擎(processEngine)和 Activiti 引擎服务。

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

为了确保在你的 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 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 class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>

<bean 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 class="org.activiti.spring.ProcessEngineFactoryBean">
<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>

<bean factory-bean="processEngine" factory-method="getRepositoryService" />
<bean factory-bean="processEngine" factory-method="getRuntimeService" />
<bean factory-bean="processEngine" factory-method="getTaskService" />
<bean factory-bean="processEngine" factory-method="getHistoryService" />
<bean factory-bean="processEngine" factory-method="getManagementService" />

...

Spring 配置文件的其余部分包含 beans 和我们将要在这个特有的例子中的配置:

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

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

<bean 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();

这个 UserBean 看起来像这样。记得在上面 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。下面的例子暴露了一个单例 bean(printer),可以把"printer"当作关键字使用. 想要不暴露任何 beans,仅仅只需要在 SpringProcessEngineConfiguration 中传递一个空的 list 作为'beans'的属性。当不设置'beans'的属性时,在应用上下文中 Spring beans 都是可以使用的。

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

<bean class="org.activiti.examples.spring.Printer" />

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

<definitions ...>

<process>

<startEvent />
<sequenceFlow sourceRef="start" targetRef="print" />

<serviceTask activiti:expression="#{printer.printMessage()}" />
<sequenceFlow sourceRef="print" targetRef="end" />

<endEvent />

</process>

</definitions>

这里的 Printer 看起来像这样:

public class Printer {

public void printMessage() {
System.out.println("hello world");
}
}

并且 Spring bean 的配置(如上文所示)看起来像这样:

<beans ...>
...

<bean class="org.activiti.examples.spring.Printer" />

</beans>

资源的自动部署

Spring 的集成也有一个专门用于对资源部署的特性。在流程引擎的配置中,你可以指定一组资源。当流程引擎被创建的时候, 所有在这里的资源都将会被自动扫描与部署。在这里有过滤以防止资源重新部署,只有当这个资源真正发生改变的时候,它才会向 Activiti 使用的数据库创建新的部署。 这对于很多用例来说,当 Spring 容器经常重启的情况下(例如 测试),使用它是非常不错的选择。

这里有一个例子:

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

<bean class="org.activiti.spring.ProcessEngineFactoryBean">
<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>

单元测试

当集成 Spring 时,使用标准的 Activiti 测试工具类是非常容易的对业务流程进行测试。 下面的例子展示了如何在一个典型的基于 Spring 单元测试测试业务流程:

@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 class="org.activiti.engine.test.ActivitiRule">
<property name="processEngine" ref="processEngine" />
</bean>

基于注解的配置

[试验] @EnableActiviti注解相对较新,未来可能会有变更。

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

首先介绍(需要 Spring 3.0+)的是@EnableActiviti注解。 最简单的用法如下所示:

  @Configuration
@EnableActiviti
public static class SimplestConfiguration {

}

它会创建一个 Spring 环境,并对 Activiti 流程引擎进行如下配置

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

  • 一个简单的 DataSourceTransactionManager

  • 一个默认的 SpringJobExecutor

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

在这样一个环境里,可以直接通过注入操作 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,它就会代替默认创建的数据库配置。 事务管理器,job 执行器和其他组件都与之相同。 比如如下配置:

  @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 4.2.x

在 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>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文