Activiti工作流整合CDI簡介
- activiti-cdi模組提供activiti的可配置型和cdi擴充套件
- activiti-cdi的特性:
- 支援 @BusinessProcessScoped beans, 繫結到流程例項的cdi bean
- 流程為cdi bean支援自定義EL處理器
- 使用註解為流程例項提供宣告式控制
- Activiti可以掛接在cdi事件匯流排上
- 支援Java EE和Java SE, 支援Spring
- 支援單元測試
- 要在maven專案中使用activiti-cdi,需要新增依賴:
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-cdi</artifactId>
<version>5.8</version>
</dependency>
- activiti-cdi 5.6以上的版本會自動加入activiti-entin和spring
設定activiti-cdi
- Activiti cdi可以安裝在不同環境中
查詢流程引擎
- cdi擴充套件需要訪問到ProcessEngine, 為了實現此功能:
- 使用org.activiti.cdi.spi.ProcessEngineLookup介面在執行期間進行查詢
- cdi模組使用預設的名為org.activiti.cdi.impl.LocalProcessEngineLookup的實現,使用ProcessEngines這個工具類來查詢ProcessEngine
- 預設配置下,使用ProcessEngines#NAME_DEFAULT來查詢ProcessEngine.這個類可能是使用自定義名稱的子類
- 注意: 需要把activiti.cfg.xml放在classpath下
- Activiti cdi使用java.util.ServiceLoader SPI處理org.activiti.cdi.spi.ProcessEngineLookup的例項
- 為了提供介面的自定義實現,需要建立一個文字檔案,名為META-INF/services/org.activiti.cdi.spi.ProcessEngineLookup, 在檔案中需要指定實現的全類名
- 如果你沒有提供自定義的org.activiti.cdi.spi.ProcessEngineLookup實現,activiti會使用預設的LocalProcessEngineLookup實現,需要做的就是把activiti.cfg.xml放到classpath下
配置Process Engine
- 實際的配置依賴於選用的ProcessEngineLookup策略
- 在這裡主要結合LocalProcessEngineLookup討論可用的配置,要求在classpath下提供一個spring的activiti.cfg.xml
- Activiti提供了不同的ProcessEngineConfiguration實現,主要是依賴實際使用的事務管理策略
- activiti-cdi模組對事務的要求不嚴格,意味著任何事務管理策略都可以使用,即便是spring事務抽象層
- cdi模組提供兩種自定義ProcessEngineConfiguration實現:
- org.activiti.cdi.CdiJtaProcessEngineConfiguration: activiti的JtaProcessEngineConfiguration的子類,用於在activiti使用JTA管理的事務環境
- org.activiti.cdi.CdiStandaloneProcessEngineConfiguration: activiti的StandaloneProcessEngineConfiguration的子類,用於在activiti使用簡單JDBC事務環境
- JBoss7下的activiti.cfg.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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">
<!-- lookup the JTA-Transaction manager -->
<bean id="transactionManager" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:jboss/TransactionManager"></property>
<property name="resourceRef" value="true" />
</bean>
<!-- process engine configuration -->
<bean id="processEngineConfiguration"
class="org.activiti.cdi.CdiJtaProcessEngineConfiguration">
<!-- lookup the default Jboss datasource -->
<property name="dataSourceJndiName" value="java:jboss/datasources/ExampleDS" />
<property name="databaseType" value="h2" />
<property name="transactionManager" ref="transactionManager" />
<!-- using externally managed transactions -->
<property name="transactionsExternallyManaged" value="true" />
<property name="databaseSchemaUpdate" value="true" />
</bean>
</beans>
- 在Glassfish 3.1.1,假設配置好名為jdbc/activiti的datasource:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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">
<!-- lookup the JTA-Transaction manager -->
<bean id="transactionManager" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:appserver/TransactionManager"></property>
<property name="resourceRef" value="true" />
</bean>
<!-- process engine configuration -->
<bean id="processEngineConfiguration"
class="org.activiti.cdi.CdiJtaProcessEngineConfiguration">
<property name="dataSourceJndiName" value="jdbc/activiti" />
<property name="transactionManager" ref="transactionManager" />
<!-- using externally managed transactions -->
<property name="transactionsExternallyManaged" value="true" />
<property name="databaseSchemaUpdate" value="true" />
</bean>
</beans>
- 注意: 上面的配置要引入spring-context模組依賴
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.0.3.RELEASE</version>
</dependency>
釋出流程
- 可以使用標準的activiti-api釋出流程-RepositoryService
- activiti-cdi也提供了自動釋出classpath下processes.xml中列出的流程的方式
- processes.xml:
<?xml version="1.0" encoding="utf-8" ?>
<!-- list the processes to be deployed -->
<processes>
<process resource="diagrams/myProcess.bpmn20.xml" />
<process resource="diagrams/myOtherProcess.bpmn20.xml" />
</processes>
基於CDI環境的流程執行
- BPMN業務流程通常是一個長時間執行的操作,包含了使用者和系統任務的操作
- 執行過程中,流程會分成多個單獨的工作單元,由使用者和應用邏輯執行
- 在activiti-cdi中,流程例項可以分配到cdi環境中,關聯展現成一個工作單元:
- 這是非常有用的,如果工作單元太複雜:比如如果實現的使用者任務是不同形式的複雜順序,可以在這個操作中保持non-process-scoped狀態
- 預設配置下,流程例項分配到broadest啟用環境,就會啟動互動,如果互動環境沒有啟用,就會返回到請求中
與流程例項進行關聯互動
- 處理 @BusinessProcessScoped beans, 或注入流程變數時,實現了啟用的cdi環境與流程例項的關聯
- Activiti-cdi提供了org.activiti.cdi.BusinessProcess bean來控制關聯:
- startProcessByXx(...): 對應activiti的RuntimeService中的相關方法,允許啟動和隨後向關聯的業務流程
- resumeProcessById(String processInstanceId): 允許通過提供的Id來關聯流程例項
- resumeTaskById(String taskId): 允許通過提供的Id來關聯任務,也可以擴充套件關聯流程例項
- 一個工作單元完成後 ,completeTask() 方法可以呼叫來解除流程例項和會話或請求的關聯.這會通知activiti當前任務已經完成,並讓流程例項繼續執行
- BusinessProcess bean是 @Named bean, 意思是匯出的方法可以通過表示式語言呼叫:
- 比如在JSF頁面中.下面的JSF 2 程式碼啟動一個新的互動,分配給一個使用者任務例項,Id作為一個請求引數傳遞:
<f:metadata>
<f:viewParam name="taskId" />
<f:event type="preRenderView" listener="#{businessProcess.startTask(taskId, true)}" />
</f:metadata>
宣告式流程控制
- Activiti-cdi允許通過註解宣告啟動流程例項和完成任務
- @org.activiti.cdi.annotation.StartProcess註解允許通過key或name啟動流程例項.流程例項會在註解的方法返回之後啟動:
@StartProcess("authorizeBusinessTripRequest")
public String submitRequest(BusinessTripRequest request) {
// do some work
return "success";
}
- 根據activiti的配置,註解方法的程式碼和啟動流程例項會在同一個事務中執行 .@org.activiti.cdi.annotation.CompleteTask事務的使用方式相同:
@CompleteTask(endConversation=false)
public String authorizeBusinessTrip() {
// do some work
return "success";
}
@CompleteTask註解可以結束當前會話.預設行為會在activiti返回後結束會話.可以禁用結束會話的功能
在流程中引用bean
- Activiti-cdi使用自定義解析器把CDI bean暴露到activiti El中,可以在流程中引用這些bean:
<userTask id="authorizeBusinessTrip" name="Authorize Business Trip"
activiti:assignee="#{authorizingManager.account.username}" />
- authorizingManager可以是生產者方法提供的bean:
@Inject @ProcessVariable Object businessTripRequesterUsername;
@Produces
@Named
public Employee authorizingManager() {
TypedQuery<Employee> query = entityManager.createQuery("SELECT e FROM Employee e WHERE e.account.username='"
+ businessTripRequesterUsername + "'", Employee.class);
Employee employee = query.getSingleResult();
return employee.getManager();
使用@BusinessProcessScoped beans
- 使用activiti-cdi,bean的生命週期可以繫結到流程例項上:
- 可以提供一個自定義的環境實現,命名為BusinessProcessContext.
- BusinessProcessScoped bean的例項會作為流程變數儲存到當前流程例項中
- BusinessProcessScoped bean需要是PassivationCapable,比如序列化
- 使用流程作用域bean的示例如下:
@Named
@BusinessProcessScoped
public class BusinessTripRequest implements Serializable {
private static final long serialVersionUID = 1L;
private String startDate;
private String endDate;
// ...
}
- 有時,需要使用流程作用域bean,沒有與流程例項關聯:
- 比如啟動流程之前.如果當前流程例項沒有啟用 ,BusinessProcessScoped bean例項會暫時儲存在區域性作用域裡:
- 會話
- 請求
- 依賴環境
- 比如啟動流程之前.如果當前流程例項沒有啟用 ,BusinessProcessScoped bean例項會暫時儲存在區域性作用域裡:
- 如果作用域後來與業務流程例項關聯了,bean例項會重新整理到流程例項裡
注入流程變數
- 流程變數可以實現用於注入
- Activiti-CDI支援以下注入流程變數的方式:
- @BusinessProcessScoped使用 @Inject [附加修飾] 型別 屬性名實現型別安全的流程變數的注入
- 使用@ProcessVariable(name)修飾符實現對型別不安全的流程變數的注入
@Inject @ProcessVariable Object accountNumber;
@Inject @ProcessVariable("accountNumber") Object account
- 為了通過EL引用流程變數, 可以使用如下方式:
- @Named @BusinessProcessScoped beans可以直接引用
- 其他流程變數可以使用ProcessVariables bean來使用
#{processVariables['accountNumber']}
接收流程事件
- Activiti可以掛在CDI的事件匯流排上,就可以使用標準CDI事件機制來監聽流程事件
- 為了啟用activiti的CDI事件支援,需要在配置中啟用對應的解析監聽器:
<property name="postBpmnParseHandlers">
<list>
<bean class="org.activiti.cdi.impl.event.CdiEventSupportBpmnParseHandler" />
</list>
</property>
- 這樣activiti就配置成了使用CDI事件匯流排釋出事件
- 在CDI bean中處理事件的方式:
- 使用@Observes註解宣告特定的事件監聽器
- 事件監聽是型別安全的
- 流程事件型別是org.activiti.cdi.BusinessProcessEvent
- 一個簡單事件監聽方法示例:
public void onProcessEvent(@Observes BusinessProcessEvent businessProcessEvent) {
// handle event
}
- 監聽器可以監聽所有事件.如果想限制監聽器接收的事件型別,可以新增修飾註解:
- @BusinessProcess: 限制指定流程定義的事件
- @Observes @BusinessProcess("billingProcess")
- @StartActivity: 限制指定進入環節的事件
- @Observes @StartActivity("shipGoods")
- @EndActivity: 限制指定結束環節的事件
- @Observes @EndActivity("shipGoods")
- @TakeTransition: 限制指定連線的事件
- @BusinessProcess: 限制指定流程定義的事件
- 修飾命名可以自由組合:
- 為了接收shipmentProcess流程中所有離開shipGoods環節的事件:
public void beforeShippingGoods(@Observes @BusinessProcess("shippingProcess") @EndActivity("shipGoods") BusinessProcessEvent evt) {
// handle event
}
- 預設配置下,事件監聽器是同步呼叫,並在同一個事務環境中
- CDI事務性監聽器可以控制監聽器什麼時候處理事件:
- 可以保證監聽器只在事件中的事務成功之後才處理
public void onShipmentSuceeded(@Observes(during=TransactionPhase.AFTER_SUCCESS) @BusinessProcess("shippingProcess") @EndActivity("shipGoods") BusinessProcessEvent evt) {
// send email to customer.
}
Activiti CDI中的更多功能
- 流程引擎和服務都可以注入: Inject ProcessEngine,RepositoryService,TaskService,...
- 當前流程例項和任務可以注入: @Inject ProcessInstance, Task
- 當前業務標識可以注入: @Inject @BusinessKey String businessKey
- 當前流程例項id可以注入: @Inject @ProcessInstanceId String pid