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();
相關文章
相關標籤/搜索