Activiti工做流学习-----基于5.19.0版本(5)

5、与Spring集成

实际项目中通常都有Spring的身影,与Spring集成使得Activiti的实用性获得提升。activiti和Spring整合须要activiti-spring的jar在类路径下面,整合类是:org.activiti.spring.ProcessEngineFactoryBean,它将加载配置对象和产生流程引擎对象。例以下面代码:html

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

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

5.1 事务的配置

 在xml配置SpringProcessEngineConfiguration的dataSource的时候,实际上工做流使用的是org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy,它是dataSource的一个代理类,它的做用就是肯定从dataSource中获取的数据库链接已经绑定了Spring的事务,因此你无需在进行配置dataSource的代理类,以防失去TransactionAwareDataSourceProxy的效果。java

确保在配置中没有配置TransactionAwareDataSourceProxy,由于它在Spring的事务中不会有做用(好比DataSourceTransactionManager和JPATransactionManager不须要代理的dataSource)。git

 1 <beans xmlns="http://www.springframework.org/schema/beans"
 2        xmlns:context="http://www.springframework.org/schema/context"
 3        xmlns:tx="http://www.springframework.org/schema/tx"
 4        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 5        xsi:schemaLocation="http://www.springframework.org/schema/beans   http://www.springframework.org/schema/beans/spring-beans.xsd
 6                            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
 7                            http://www.springframework.org/schema/tx      http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
 8 
 9   <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
10     <property name="driverClass" value="org.h2.Driver" />
11     <property name="url" value="jdbc:h2:mem:activiti;DB_CLOSE_DELAY=1000" />
12     <property name="username" value="sa" />
13     <property name="password" value="" />
14   </bean>
15 
16   <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
17     <property name="dataSource" ref="dataSource" />
18   </bean>
19 
20   <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
21     <property name="dataSource" ref="dataSource" />
22     <property name="transactionManager" ref="transactionManager" />
23     <property name="databaseSchemaUpdate" value="true" />
24     <property name="jobExecutorActivate" value="false" />
25   </bean>
26 
27   <bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
28     <property name="processEngineConfiguration" ref="processEngineConfiguration" />
29   </bean>
30 
31   <bean id="repositoryService" factory-bean="processEngine" factory-method="getRepositoryService" />
32   <bean id="runtimeService" factory-bean="processEngine" factory-method="getRuntimeService" />
33   <bean id="taskService" factory-bean="processEngine" factory-method="getTaskService" />
34   <bean id="historyService" factory-bean="processEngine" factory-method="getHistoryService" />
35   <bean id="managementService" factory-bean="processEngine" factory-method="getManagementService" />
36 
37 ...

 在其余的Spring配置文件中配置其余的,这样功能清晰,后期容易维护,例如:spring

 1 <beans>
 2   ...
 3   <tx:annotation-driven transaction-manager="transactionManager"/>
 4 
 5   <bean id="userBean" class="org.activiti.spring.test.UserBean">
 6     <property name="runtimeService" ref="runtimeService" />
 7   </bean>
 8 
 9   <bean id="printer" class="org.activiti.spring.test.Printer" />
10 
11 </beans>

在Spring中启动容器(application context)的方法有不少,好比下面利用类路径加载配置文件:数据库

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

而在单元测试中,咱们能够这样:express

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

在Spring的IOC容器中咱们能够轻易的获取咱们须要的工做流的Service,ProcessEngineFactoryBean实现了对这些Service的Propagation.REQUIRED的事务级别的控制,例以下面这段代码:tomcat

1 RepositoryService repositoryService =
2   (RepositoryService) applicationContext.getBean("repositoryService");
3 String deploymentId = repositoryService
4   .createDeployment()
5   .addClasspathResource("org/activiti/spring/test/hello.bpmn20.xml")
6   .deploy()
7   .getId();

在其余方面也有一样的效果,在下面这种状况中,Spring的事务是环绕在serBean.hello()中,这时Activiti的Service会加入事务当中。app

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

而在具体的UserBean中,咱们经过前面的配置是注入了RuntimeService:dom

 1 public class UserBean {
 2 
 3   /** Spring注入 */
 4   private RuntimeService runtimeService;
 5 
 6   @Transactional
 7   public void hello() {
 8     // here you can do transactional stuff in your domain model
 9     // and it will be combined in the same transaction as
10     // the startProcessInstanceByKey to the Activiti RuntimeService
11     runtimeService.startProcessInstanceByKey("helloProcess");
12   }
13 
14   public void setRuntimeService(RuntimeService runtimeService) {
15     this.runtimeService = runtimeService;
16   }
17 }

5.2 Spring中使用表达式

 在Spring的配置中也有像activiti的表达式使用,有可能表达式中的bean有限制,在使用Map配置发现没有bean可用,在SpringProcessEngineConfiguration的beans属性配置Map时,若是里面的bean不存在也会经过,只不过bean为空而已。下面这段代码配置beans属性:分布式

<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" />

如今咱们能够在xxxbpmn.xml文件中能够直接使用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>

而咱们的Printer类代码:

public class Printer {

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

在Spring中配置:

<beans>
  ...

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

</beans>

5.3 使用Spring实现自动发布

 自动发布流程定义是整合Spring后的功能,在流程引擎被建立的时候就会将spring配置的资源加载发布。为了防止重复发布,整合的jar中添加了过滤功能,只有加载的资源真正的被改变,才会从新发布。好处在于一些场景(junit 测试)下,Spring容器常常重启。

例以下面配置,其中deploymentResources属性设置资源路径。

<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提供了自定义发布的功能,在bean节点:SpringProcessEngineConfiguration添加属性deploymentMode,目前它有三种取值策略:

  • default:没有定义deploymentMode时,系统默认设置为default.
  • single-resource:每一个资源文件单独发布,若是资源文件改变,单独发布。
  • resource-parent-folder:每一个资源文件夹单独发布,也就是说在不一样文件夹下的资源文件将会以不一样的文件夹为单位,进行防重复发布功能的发布。它介于default(批量发布)和single-resource(单个发布)之间。

以上就是全部的deploymentMode取值,若是你以为不可以知足你的需求,你能够继承SpringProcessEngineConfiguration,重写其中的getAutoDeploymentStrategy(String deploymentMode)方法实现本身的功能。

5.4 在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());

  }
}

不过值得注意的是你测试以前须要将org.activiti.engine.test.ActivitiRule配置注册到Spring的文件中,它会在上述例子中自动注入到测试中。

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

目前本人使用Spring-Boot很少(就没怎么用),至于Spring-Boot和Activiti的集成,感兴趣的同窗能够本身去了解,反正我以为太过于封装的东西不要太过于追求。

6、 工做流动态发布

将流程定义等资源文件以zip压缩格式打包后,activiti引擎将会去扫描含有.bpmn20.xml 或者 .bpmn扩展名的文件,这些文件将会被识别解析。但注意不要将java文件以及编译后文件也加到打包文件中,流程引擎只会加载当前类路径下面的类文件,同时加到打包文件的class文件也不会被加到类路径下面去。

这时后台须要代码实现发布功能,好比这样:

String barFileName = "path/to/process-one.bar";
ZipInputStream inputStream = new ZipInputStream(new FileInputStream(barFileName));

repositoryService.createDeployment()
    .name("process-one.bar")
    .addZipInputStream(inputStream)
    .deploy();

在官方提供的Activiti Explorer项目中也有动态发布功能集成,将其项目部署到tomcat后,我使用了管理员Kermit登陆系统,点击管理-->部署包--->添加新的部署包

弹出一个上传文件的弹出框:

 

 而咱们资源打包文件引用到的类须要放到流程引擎的classpath下面.

6.1 流程定义的版本

BPMN是没有版本的概念的,bpmn文件交给版本控制软件(好比SVN,git,Mercurial)进行管理,这样子设计方便咱们对项目的集中管理,而存入数据库的流程定义的版本是工做流在发布期间标识的版本。每个流程定义写进数据库须要初始化这些属性:key, version, name 和id。

id的值来自流程定义文件的key值。name值来自于流程定义文件的name值,第一次发布默认version是1,随后的发布会将版本递增。id的取值是{流程定义的key值}:{流程定义的版本}:{随机数},而这个随机数在分布式环境中也是保证是惟一的。

好比下面这个流程定义文件:

1 <definitions id="myDefinitions" >
2   <process id="myProcess" name="My important process" >

发布事后在数据库中保存的数据就是这样:

id key name version

myProcess:1:676

myProcess

My important process

1

假使咱们从新发布了流程定义文件,但文件中id的值并无修改,数据库中的数据变成了这样子:

myProcess:1:676

myProcess

My important process

1

myProcess:2:870

myProcess

My important process

2

若是咱们修改了id,再次发布数据库数据就成这样子:

id key name version

myProcess:1:676

myProcess

My important process

1

myProcess:2:870

myProcess

My important process

2

myNewProcess:1:1033

myNewProcess

My important process

1

能够看出发布区分新的流程定义会根据key进行判断,Activiti仅仅会根据key值的不一样进行区分,因此流程版本变成了1。

6.2 添加流程图发布

发布过程当中能够添加流程图进去, 流程图会被视为资源而保存起来,流程图主要用在Activiti Explorer的页面中显示给用户查看。假定咱们有一个流程的xml文件在类路径下面,activiti使用命名规则进行获取流程图:

  • 若是流程图已经存在而且使用流程定义文件名做为图片名称,固然后缀名是git、jpg、png均可以,将会被获取加载,若是有其余名称的图片文件,将会被忽视。
  • 若是图片文件在当前流程定义文件的同一个目录里面不存在。

人工添加流程图代码实现就是以下所示:

1 repositoryService.createDeployment()
2   .name("expense-process.bar")
3   .addClasspathResource("org/activiti/expenseProcess.bpmn20.xml")
4   .addClasspathResource("org/activiti/expenseProcess.png")
5   .deploy();

取出图片资源代码实现:

1 ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
2   .processDefinitionKey("expense")
3   .singleResult();
4 
5 String diagramResourceName = processDefinition.getDiagramResourceName();
6 InputStream imageStream = repositoryService.getResourceAsStream(
7     processDefinition.getDeploymentId(), diagramResourceName);

若是bpmn文件中含有必要的信息,activiti将会生成流程图片,若是不想生成能够设置:

<property name="createDiagramOnDeploy" value="false" />

bpmn文件能够被用户进行自定义分类,帮助用户本身识别使用,在bpmn文件的:<definitions …​ targetNamespace="yourCategory" 能够设置。

若是使用代码实现这个功能能够这样:

1 repositoryService
2     .createDeployment()
3     .category("yourCategory")
4     ...
5     .deploy();
相关文章
相关标签/搜索