Activiti介紹
什麼是Activiti?
Activiti5是由Alfresco軟體在2010年5月17日釋出的業務流程管理(BPM)框架,它是覆蓋了業務流程管理、工作流、服務協作等領域的一個開源的、靈活的、易擴充套件的可執行流程語言框架。Activiti基於Apache許可的開源BPM平臺,創始人Tom Baeyens是JBoss jBPM的專案架構師,它特色是提供了eclipse外掛,開發人員可以通過外掛直接繪畫出業務 流程圖。.
我們即將學習的是一個業務流程管理框架, 常見開源工作流引擎框架 : OSWorkFlow、jBPM(jboss business process management),Activiti工作流(是對jBPM升級)。一般我們稱作為工作流框架..
為什麼要學習Activiti
那我們為什麼要學習業務流程管理框架呢???學習它幹嘛???工作流(Workflow),就是“業務過程的部分或整體在計算機應用環境下的自動化”
我們來提出一個常見的需求來更好地理解:
我們在學生時代,肯定會遇到請假寫請假條的情況,如果學校的請假抓得非常嚴,就需要經過多層的同意才能確定能不能請假..
班主任->任課老師->教學總監->校長這麼一個流程,首先我們先明確一點:我們作為一個學生,不可能直接跳過老師,去找校長申請請假的【校長是你隨便找的嗎??】
因此我們請假的流程是一步一步的,並不能做跳躍
也就是說,當班主任沒有批准請假的時候,即使你去找任課老師了,任課老師會讓你回去找班主任的,作為任課老師了,只關注班主任有沒有批准你請假,同理,作為校長,只關注教學總監有沒有批准你請假!
進一步說:當教學總監還沒有批准你請假時,你請假的請求是不會出現在校長的範圍裡的。
其實是非常好理解的,就是一步步往下執行,當還沒有執行到自己處理的點子上時,是不會有對應的處理請求的。分工有序
對上面的請假流程進行分析**,如果我們沒有使用框架,而把這麼一個請假流程寫到我們的網站中,我們會怎麼做呢**??
我們需要維護一個變數,來不斷傳遞過去給下一個處理者...如果一切正常,需求不會變,並沒有條件的處理。這是我們非常希望看到的...但是,如果有條件判斷【請假三天以下、請假三天以上的處理方式不一樣】,需求會變【不需要校長批准了,教學總監批准完,你就能夠請假了】,那麼我們的程式碼就會變得亂
基於這麼一個原因,我們是需要學習一個框架來幫我們完成工作流的...
採用工作流管理系統的優點
- 1、提高系統的柔性,適應業務流程的變化
- 2、實現更好的業務過程控制,提高顧客服務質量
- 3、降低系統開發和維護成本
一、快速入門Activiti
首先我們來梳理一下Activiti的開發步驟:
- 我們要用到一個工作流,首先就要把這個工作流定義出來【也就是工作流的步驟的怎麼樣的】,Activiti支援以“圖”的方式來定義工作流
- 定義完工作流,就要部署到起來【我們可以聯想到Tomcat,我們光下載了Tomcat是沒有用的,要把它部署起來】
- 隨後我們就執行該工作流,該工作流就隨著我們定義的步驟來一一執行!
1.1BPMN
業務流程建模與標註(Business Process Model and Notation,BPMN) ,描述流程的基本符號,包括這些圖元如何組合成一個業務流程圖(Business Process Diagram)
BPMN這個就是我們所謂把工作流定義出來的流程圖..
1.2資料庫相關
我們在執行工作流步驟的時候會涉及到很多資料【執行該流程的人是誰、所需要的引數是什麼、包括想檢視之前流程執行的記錄等等】,因此我們會需要用到資料庫的表來儲存資料...
由於我們使用的是Activiti框架,這個框架會自動幫我們把對應的資料庫表建立起來,它涉及的表有23個,但是常用的並不是很多,因此也不用很慌...
下面就列舉一下表的情況
Activiti的後臺是有資料庫的支援,所有的表都以ACT_開頭。 第二部分是表示表的用途的兩個字母標識。 用途也和服務的API對應。
ACT_RE_*: 'RE'表示repository。 這個字首的表包含了流程定義和流程靜態資源 (圖片,規則,等等)。
ACT_RU_*: 'RU'表示runtime。 這些執行時的表,包含流程例項,任務,變數,非同步任務,等執行中的資料。 Activiti只在流程例項執行過程中儲存這些資料, 在流程結束時就會刪除這些記錄。 這樣執行時表可以一直很小速度很快。
ACT_ID_*: 'ID'表示identity。 這些表包含身份資訊,比如使用者,組等等。
ACT_HI_*: 'HI'表示history。 這些表包含歷史資料,比如歷史流程例項, 變數,任務等等。
ACT_GE_*: 通用資料, 用於不同場景下,如存放資原始檔。
複製程式碼
1.3搭建配置環境
我這裡使用的Intellij idea來使用Activiti,首先,我們得下載外掛來使用Activiti【因為定義流程圖需要用到外掛】
詳情可以看這篇博文:blog.sina.com.cn/s/blog_4b31…
Activiti外掛中文亂碼問題:
流程之前的連線是通過圖中的藍色小點點拖動來進行連線的...
匯入對應的jar包
- activation-1.1.jar
- activiti-bpmn-converter-5.13.jar
- activiti-bpmn-layout-5.13.jar
- activiti-bpmn-model-5.13.jar
- activiti-common-rest-5.13.jar
- activiti-engine-5.13.jar
- activiti-json-converter-5.13.jar
- activiti-rest-5.13.jar
- activiti-simple-workflow-5.13.jar
- activiti-spring-5.13.jar
- aopalliance-1.0.jar
- commons-dbcp-1.4.jar
- commons-email-1.2.jar
- commons-fileupload-1.2.2.jar
- commons-io-2.0.1.jar
- commons-lang-2.4.jar
- commons-pool-1.5.4.jar
- h2-1.3.170.jar
- hamcrest-core-1.3.jar
- jackson-core-asl-1.9.9.jar
- jackson-mapper-asl-1.9.9.jar
- javaGeom-0.11.0.jar
- jcl-over-slf4j-1.7.2.jar
- jgraphx-1.10.4.2.jar
- joda-time-2.1.jar
- junit-4.11.jar
- log4j-1.2.17.jar
- mail-1.4.1.jar
- mybatis-3.2.2.jar
- mysql-connector-java.jar
- org.restlet.ext.fileupload-2.0.15.jar
- org.restlet.ext.jackson-2.0.15.jar
- org.restlet.ext.servlet-2.0.15.jar
- org.restlet-2.0.15.jar
- slf4j-api-1.7.2.jar
- slf4j-log4j12-1.7.2.jar
- spring-aop-3.1.2.RELEASE.jar
- spring-asm-3.1.2.RELEASE.jar
- spring-beans-3.1.2.RELEASE.jar
- spring-context-3.1.2.RELEASE.jar
- spring-core-3.1.2.RELEASE.jar
- spring-expression-3.1.2.RELEASE.jar
- spring-jdbc-3.1.2.RELEASE.jar
- spring-orm-3.1.2.RELEASE.jar
- spring-tx-3.1.2.RELEASE.jar
1.4開發步驟
上面已經說過了,我們要想使用Activiti就需要有資料庫的支援,雖然Activiti是自動幫我們建立對應的資料庫表,但是我們是需要配置資料庫的資訊的。我們配置資料庫的資訊,接著拿到Activiti最重要的API------Activiti引擎
1.4.1得到工作流引擎
Activiti提供使用程式碼的方式來配置資料庫的資訊:
@Test
public void createActivitiEngine(){
/* *1.通過程式碼形式建立
* - 取得ProcessEngineConfiguration物件
* - 設定資料庫連線屬性
* - 設定建立表的策略 (當沒有表時,自動建立表)
* - 通過ProcessEngineConfiguration物件建立 ProcessEngine 物件*/
//取得ProcessEngineConfiguration物件
ProcessEngineConfiguration engineConfiguration=ProcessEngineConfiguration.
createStandaloneProcessEngineConfiguration();
//設定資料庫連線屬性
engineConfiguration.setJdbcDriver("com.mysql.jdbc.Driver");
engineConfiguration.setJdbcUrl("jdbc:mysql://localhost:3306/activitiDB?createDatabaseIfNotExist=true"
+ "&useUnicode=true&characterEncoding=utf8");
engineConfiguration.setJdbcUsername("root");
engineConfiguration.setJdbcPassword("root");
// 設定建立表的策略 (當沒有表時,自動建立表)
// public static final java.lang.String DB_SCHEMA_UPDATE_FALSE = "false";//不會自動建立表,沒有表,則拋異常
// public static final java.lang.String DB_SCHEMA_UPDATE_CREATE_DROP = "create-drop";//先刪除,再建立表
// public static final java.lang.String DB_SCHEMA_UPDATE_TRUE = "true";//假如沒有表,則自動建立
engineConfiguration.setDatabaseSchemaUpdate("true");
//通過ProcessEngineConfiguration物件建立 ProcessEngine 物件
ProcessEngine processEngine = engineConfiguration.buildProcessEngine();
System.out.println("流程引擎建立成功!");
}
複製程式碼
Activiti也可以通過配置檔案來配置資料庫的資訊,載入配置檔案從而得到工作流引擎
/**2. 通過載入 activiti.cfg.xml 獲取 流程引擎 和自動建立資料庫及表
*
ProcessEngineConfiguration engineConfiguration=
ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml");
//從類載入路徑中查詢資源 activiti.cfg.xm檔名可以自定義
ProcessEngine processEngine = engineConfiguration.buildProcessEngine();
System.out.println("使用配置檔案Activiti.cfg.xml獲取流程引擎");
*/
複製程式碼
activiti.cfg.xml
<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">
<!-- 配置 ProcessEngineConfiguration -->
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<!-- 配置資料庫連線 -->
<property name="jdbcDriver" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/activitiDB?createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=utf8"></property>
<property name="jdbcUsername" value="root"></property>
<property name="jdbcPassword" value="root"></property>
<!-- 配置建立表策略 :沒有表時,自動建立 -->
<property name="databaseSchemaUpdate" value="true"></property>
</bean>
</beans>
複製程式碼
上面的那種載入配置檔案方式,配置檔案的名字是可以自定義的,如果我們配置檔案的名字預設就是activiti.cfg.xml
的話,也是放在類路徑下,我們就可以使用預設的方式來進行載入了!
@Test
public void createActivitiEngine(){
/**
* 3. 通過ProcessEngines 來獲取預設的流程引擎
*/
// 預設會載入類路徑下的 activiti.cfg.xml
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
System.out.println("通過ProcessEngines 來獲取流程引擎");
}
複製程式碼
1.4.2定義工作流
定義工作流就需要我們剛才下載的外掛了,我們是使用圖形的方式來定義工作流的....
在每個流程中,我們都可以指定對應的處理人是誰,交由誰處理
1.4.3部署工作流
我們上面已經定義了工作流了,工作流引擎我們也已經拿到了,接下來就是把工作流部署到工作流引擎中了
@Test
public void deploy() {
//獲取倉庫服務 :管理流程定義
RepositoryService repositoryService = processEngine.getRepositoryService();
Deployment deploy = repositoryService.createDeployment()//建立一個部署的構建器
.addClasspathResource("LeaveActiviti.bpmn")//從類路徑中新增資源,一次只能新增一個資源
.name("請求單流程")//設定部署的名稱
.category("辦公類別")//設定部署的類別
.deploy();
System.out.println("部署的id"+deploy.getId());
System.out.println("部署的名稱"+deploy.getName());
}
複製程式碼
相對應的資料庫表就會插入資料、涉及到的資料庫表後面會詳細說明。現在我們只要瞭解到,我們工作流引擎執行操作會有資料庫表記錄
1.4.4執行工作流
指定指定工作流就是我們定義時工作流程表的id
@Test
public void startProcess(){
//指定執行我們剛才部署的工作流程
String processDefiKey="leaveBill";
//取執行時服務
RuntimeService runtimeService = processEngine.getRuntimeService();
//取得流程例項
ProcessInstance pi = runtimeService.startProcessInstanceByKey(processDefiKey);//通過流程定義的key 來執行流程
System.out.println("流程例項id:"+pi.getId());//流程例項id
System.out.println("流程定義id:"+pi.getProcessDefinitionId());//輸出流程定義的id
}
複製程式碼
1.4.5根據代理人查詢當前任務的資訊
剛才我們已經開始了工作流了,隨後工作流應該去到了申請請假的流程,申請請假的處理人是鍾福成,我們可以查詢出對應的資訊:
//查詢任務
@Test
public void queryTask(){
//任務的辦理人
String assignee="鍾福成";
//取得任務服務
TaskService taskService = processEngine.getTaskService();
//建立一個任務查詢物件
TaskQuery taskQuery = taskService.createTaskQuery();
//辦理人的任務列表
List<Task> list = taskQuery.taskAssignee(assignee)//指定辦理人
.list();
//遍歷任務列表
if(list!=null&&list.size()>0){
for(Task task:list){
System.out.println("任務的辦理人:"+task.getAssignee());
System.out.println("任務的id:"+task.getId());
System.out.println("任務的名稱:"+task.getName());
}
}
}
複製程式碼
1.4.6處理任務
我們現在處理流程去到“申請請假”中,處理人是鍾福成...接著就是鍾福成去處理任務,根據任務的id使得流程繼續往下走
任務的id剛才我們已經查詢出來了【上面】,我們如果是在web端運算元據的話,那麼只要傳遞過去就行了!
//完成任務
@Test
public void compileTask(){
String taskId="304";
//taskId:任務id
processEngine.getTaskService().complete(taskId);
System.out.println("當前任務執行完畢");
}
複製程式碼
當我們處理完該任務的時候,就到了批准【班主任】任務了,我們查詢一下是不是如我們想象的效果:
我們按照定義的工作流程圖一步一步往下走,最終把流程走完
二、流程定義細講
管理流程定義主要涉及到以下的4張表:
-- 流程部署相關的表
SELECT * FROM act_ge_bytearray # 通用位元組資源表
SELECT * FROM act_ge_property # 通用屬性表,可以生成部署id
SELECT * FROM act_re_deployment #部署表
SELECT * FROM act_re_procdef # 流程定義表
複製程式碼
2.1PNG資源
在Eclipse中Activiti外掛會自動生成一個BPMN與之對應的PNG圖片,是需要通過載入PNG圖片的
@Test
public void deploy() {
//獲取倉庫服務 :管理流程定義
RepositoryService repositoryService = processEngine.getRepositoryService();
Deployment deploy = repositoryService.createDeployment()//建立一個部署的構建器
.addClasspathResource("LeaveActiviti.bpmn")//從類路徑中新增資源,一次只能新增一個資源
.name("請求單流程")//設定部署的名稱
.category("辦公類別")//設定部署的類別
.deploy();
System.out.println("部署的id"+deploy.getId());
System.out.println("部署的名稱"+deploy.getName());
}
複製程式碼
而我們的Intellij idea外掛不會自動生成PNG圖片,但是我們在載入BPMN檔案的時候,好像會自動插入PNG圖片,資料庫是有對應的記錄的【我們是沒有手動載入PNG圖片資源的】
我們檢視一下
//檢視bpmn 資源圖片
@Test
public void viewImage() throws Exception{
String deploymentId="201";
String imageName=null;
//取得某個部署的資源的名稱 deploymentId
List<String> resourceNames = processEngine.getRepositoryService().getDeploymentResourceNames(deploymentId);
// buybill.bpmn buybill.png
if(resourceNames!=null&&resourceNames.size()>0){
for(String temp :resourceNames){
if(temp.indexOf(".png")>0){
imageName=temp;
}
}
}
/**
* 讀取資源
* deploymentId:部署的id
* resourceName:資源的檔名
*/
InputStream resourceAsStream = processEngine.getRepositoryService()
.getResourceAsStream(deploymentId, imageName);
//把檔案輸入流寫入到檔案中
File file=new File("d:/"+imageName);
FileUtils.copyInputStreamToFile(resourceAsStream, file);
}
複製程式碼
可惜的是,檢視出來的圖片的中文資料會亂碼...
2.2檢視流程定義
我們當時候查詢流程定義是通過我們設定流程圖的id來檢視的....其實我們可以通過其他的屬性來查詢...並且可以查詢出更加詳細的資料
//檢視流程定義
@Test
public void queryProcessDefination(){
String processDefiKey="buyBill";//流程定義key
//獲取流程定義列表
List<ProcessDefinition> list = processEngine.getRepositoryService().createProcessDefinitionQuery()
//查詢 ,好比where
// .processDefinitionId(proDefiId) //流程定義id
// 流程定義id : buyBill:2:704 組成 : proDefikey(流程定義key)+version(版本)+自動生成id
.processDefinitionKey(processDefiKey)//流程定義key 由bpmn 的 process 的 id屬性決定
// .processDefinitionName(name)//流程定義名稱 由bpmn 的 process 的 name屬性決定
// .processDefinitionVersion(version)//流程定義的版本
.latestVersion()//最新版本
//排序
.orderByProcessDefinitionVersion().desc()//按版本的降序排序
//結果
// .count()//統計結果
// .listPage(arg0, arg1)//分頁查詢
.list();
//遍歷結果
if(list!=null&&list.size()>0){
for(ProcessDefinition temp:list){
System.out.print("流程定義的id: "+temp.getId());
System.out.print("流程定義的key: "+temp.getKey());
System.out.print("流程定義的版本: "+temp.getVersion());
System.out.print("流程定義部署的id: "+temp.getDeploymentId());
System.out.println("流程定義的名稱: "+temp.getName());
}
}
}
複製程式碼
2.3資源來自ZIP
我們還可以載入的是ZIP型別的資源資料
//部署流程定義,資源來自zip格式
@Test
public void deployProcessDefiByZip(){
InputStream in=getClass().getClassLoader().getResourceAsStream("BuyBill.zip");
Deployment deploy = processEngine.getRepositoryService()
.createDeployment()
.name("採購流程")
.addZipInputStream(new ZipInputStream(in))
.deploy();
System.out.println("部署名稱:"+deploy.getName());
System.out.println("部署id:"+deploy.getId());
}
複製程式碼
2.4刪除流程定義
//刪除流程定義
@Test
public void deleteProcessDefi(){
//通過部署id來刪除流程定義
String deploymentId="101";
processEngine.getRepositoryService().deleteDeployment(deploymentId);
}
複製程式碼
再次查詢的時候,已經沒有101這個流程定義的資料了。
三、流程例項與任務執行細講
流程例項與任務執行的常用表有以下幾個:
-- 流程例項與任務
SELECT * FROM act_ru_execution # 流程執行物件資訊
SELECT * FROM act_ru_task # 正在執行的任務表
SELECT * FROM act_hi_procinst # 歷史流程例項表
SELECT * FROM act_hi_taskinst # 歷史流程任務表
複製程式碼
這裡就簡單簡述一下流程例項與流程物件的區別:
- (1)如果是單例流程,執行物件ID就是流程例項ID
- (2)如果一個流程有分支和聚合,那麼執行物件ID和流程例項ID就不相同
- (3)一個流程中,流程例項只有1個,執行物件可以存在多個。
3.1開始流程##
@Test
public void startProcess(){
String processDefiKey="leaveActiviti";//bpmn 的 process id屬性
ProcessInstance pi = processEngine.getRuntimeService()
.startProcessInstanceByKey(processDefiKey);
System.out.println("流程執行物件的id:"+pi.getId());//Execution 物件
System.out.println("流程例項的id:"+pi.getProcessInstanceId());//ProcessInstance 物件
System.out.println("流程定義的id:"+pi.getProcessDefinitionId());//預設執行的是最新版本的流程定義
}
複製程式碼
3.2檢視正在執行的任務
//查詢正在執行任務
@Test
public void queryTask(){
//取得任務服務
TaskService taskService = processEngine.getTaskService();
//建立一個任務查詢物件
TaskQuery taskQuery = taskService.createTaskQuery();
//辦理人的任務列表
List<Task> list = taskQuery.list();
//遍歷任務列表
if(list!=null&&list.size()>0){
for(Task task:list){
System.out.println("任務的辦理人:"+task.getAssignee());
System.out.println("任務的id:"+task.getId());
System.out.println("任務的名稱:"+task.getName());
}
}
}
複製程式碼
查詢SELECT * FROM act_ru_task 表
3.3獲取流程例項的狀態
有的時候,我們需要判斷它是在該流程,還是該流程已經結束了。我們可以根據獲取出來的物件是否為空來進行判斷
//獲取流程例項的狀態
@Test
public void getProcessInstanceState(){
String processInstanceId="605";
ProcessInstance pi = processEngine.getRuntimeService()
.createProcessInstanceQuery()
.processInstanceId(processInstanceId)
.singleResult();//返回的資料要麼是單行,要麼是空 ,其他情況報錯
//判斷流程例項的狀態
if(pi!=null){
System.out.println("該流程例項"+processInstanceId+"正在執行... "+"當前活動的任務:"+pi.getActivityId());
}else{
System.out.println("當前的流程例項"+processInstanceId+" 已經結束!");
}
}
複製程式碼
3.4檢視歷史流程例項的資訊
//檢視歷史執行流程例項資訊
@Test
public void queryHistoryProcInst(){
List<HistoricProcessInstance> list = processEngine.getHistoryService()
.createHistoricProcessInstanceQuery()
.list();
if(list!=null&&list.size()>0){
for(HistoricProcessInstance temp:list){
System.out.println("歷史流程例項id:"+temp.getId());
System.out.println("歷史流程定義的id:"+temp.getProcessDefinitionId());
System.out.println("歷史流程例項開始時間--結束時間:"+temp.getStartTime()+"-->"+temp.getEndTime());
}
}
}
複製程式碼
查詢表:
3.5檢視歷史例項執行任務資訊
@Test
public void queryHistoryTask(){
String processInstanceId="605";
List<HistoricTaskInstance> list = processEngine.getHistoryService()
.createHistoricTaskInstanceQuery()
.processInstanceId(processInstanceId)
.list();
if(list!=null&&list.size()>0){
for(HistoricTaskInstance temp:list){
System.out.print("歷史流程例項任務id:"+temp.getId());
System.out.print("歷史流程定義的id:"+temp.getProcessDefinitionId());
System.out.print("歷史流程例項任務名稱:"+temp.getName());
System.out.println("歷史流程例項任務處理人:"+temp.getAssignee());
}
}
}
複製程式碼
給予對應的例項id就可以查詢出執行到哪個任務了...
3.6執行任務
根據任務的id,就可以把該任務執行了。
@Test
public void compileTask(){
String taskId="608";
//taskId:任務id
processEngine.getTaskService().complete(taskId);
System.out.println("當前任務執行完畢");
}
複製程式碼
四、流程變數細講
流程變數涉及到的資料庫表:
act_ru_variable:正在執行的流程變數表
act_hi_varinst:流程變數歷史表
複製程式碼
流程變數在工作流中扮演著一個非常重要的角色。例如:請假流程中有請假天數、請假原因等一些引數都為流程變數的範圍**。流程變數的作用域範圍是只對應一個流程例項**。也就是說各個流程例項的流程變數是不相互影響的。流程例項結束完成以後流程變數還儲存在資料庫中(存放到流程變數的歷史表中)。
4.1設定流程變數
我們有兩種服務可以設定流程變數,TaskService【任務服務】和RuntimeService【執行時服務】
場景
- 在流程開始的時候設定流程變數
- 在完成某個任務的時候設定流程變數
- 使用TaskService設定服務
- 使用RuntimeService設定服務
作用:
- 傳遞業務引數
- 動態指定代理人【我們快速入門的例子是固定在流程定義圖上寫上代理人的】
- 指定連線【決定流程往哪邊走】
4.2流程變數支援型別
如果我們使用JavaBean來作為流程的變數,那麼我們需要將JavaBean實現Serializable介面。
Javabean型別設定獲取流程變數,除了需要這個javabean實現了Serializable介面外,還要求流程變數物件的屬性不能發生變化,否則丟擲異常。解決方案,固定序列化ID
4.3setVariable和setVariableLocal的區別
4.4例子
//模擬流程變數設定
@Test
public void getAndSetProcessVariable(){
//有兩種服務可以設定流程變數
// TaskService taskService = processEngine.getTaskService();
// RuntimeService runtimeService = processEngine.getRuntimeService();
/**1.通過 runtimeService 來設定流程變數
* executionId: 執行物件
* variableName:變數名
* values:變數值
*/
// runtimeService.setVariable(executionId, variableName, values);
// runtimeService.setVariableLocal(executionId, variableName, values);
//設定本執行物件的變數 ,該變數的作用域只在當前的execution物件
// runtimeService.setVariables(executionId, variables);
//可以設定多個變數 放在 Map<key,value> Map<String,Object>
/**2. 通過TaskService來設定流程變數
* taskId:任務id
*/
// taskService.setVariable(taskId, variableName, values);
// taskService.setVariableLocal(taskId, variableName, values);
//// 設定本執行物件的變數 ,該變數的作用域只在當前的execution物件
// taskService.setVariables(taskId, variables); //設定的是Map<key,values>
/**3. 當流程開始執行的時候,設定變數引數
* processDefiKey: 流程定義的key
* variables: 設定多個變數 Map<key,values>
*/
// processEngine.getRuntimeService()
// .startProcessInstanceByKey(processDefiKey, variables)
/**4. 當任務完成時候,可以設定流程變數
* taskId:任務id
* variables: 設定多個變數 Map<key,values>
*/
// processEngine.getTaskService().complete(taskId, variables);
/** 5. 通過RuntimeService取變數值
* exxcutionId: 執行物件
*
*/
// runtimeService.getVariable(executionId, variableName);//取變數
// runtimeService.getVariableLocal(executionId, variableName);//取本執行物件的某個變數
// runtimeService.getVariables(variablesName);//取當前執行物件的所有變數
/** 6. 通過TaskService取變數值
* TaskId: 執行物件
*
*/
// taskService.getVariable(taskId, variableName);//取變數
// taskService.getVariableLocal(taskId, variableName);//取本執行物件的某個變數
// taskService.getVariables(taskId);//取當前執行物件的所有變數
}
複製程式碼
//設定流程變數值
@Test
public void setVariable(){
String taskId="1804";//任務id
//採用TaskService來設定流程變數
//1. 第一次設定流程變數
// TaskService taskService = processEngine.getTaskService();
// taskService.setVariable(taskId, "cost", 1000);//設定單一的變數,作用域在整個流程例項
// taskService.setVariable(taskId, "申請時間", new Date());
// taskService.setVariableLocal(taskId, "申請人", "何某某");//該變數只有在本任務中是有效的
//2. 在不同的任務中設定變數
// TaskService taskService = processEngine.getTaskService();
// taskService.setVariable(taskId, "cost", 5000);//設定單一的變數,作用域在整個流程例項
// taskService.setVariable(taskId, "申請時間", new Date());
// taskService.setVariableLocal(taskId, "申請人", "李某某");//該變數只有在本任務中是有效的
/**
* 3. 變數支援的型別
* - 簡單的型別 :String 、boolean、Integer、double、date
* - 自定義物件bean
*/
TaskService taskService = processEngine.getTaskService();
//傳遞的一個自定義bean物件
AppayBillBean appayBillBean=new AppayBillBean();
appayBillBean.setId(1);
appayBillBean.setCost(300);
appayBillBean.setDate(new Date());
appayBillBean.setAppayPerson("何某某");
taskService.setVariable(taskId, "appayBillBean", appayBillBean);
System.out.println("設定成功!");
}
複製程式碼
//查詢流程變數
@Test
public void getVariable(){
String taskId="1804";//任務id
// TaskService taskService = processEngine.getTaskService();
// Integer cost=(Integer) taskService.getVariable(taskId, "cost");//取變數
// Date date=(Date) taskService.getVariable(taskId, "申請時間");//取本任務中的變數
//// Date date=(Date) taskService.getVariableLocal(taskId, "申請時間");//取本任務中的變數
// String appayPerson=(String) taskService.getVariableLocal(taskId, "申請人");//取本任務中的變數
//// String appayPerson=(String) taskService.getVariable(taskId, "申請人");//取本任務中的變數
//
// System.out.println("金額:"+cost);
// System.out.println("申請時間:"+date);
// System.out.println("申請人:"+appayPerson);
//讀取實現序列化的物件變數資料
TaskService taskService = processEngine.getTaskService();
AppayBillBean appayBillBean=(AppayBillBean) taskService.getVariable(taskId, "appayBillBean");
System.out.println(appayBillBean.getCost());
System.out.println(appayBillBean.getAppayPerson());
}
複製程式碼
public class AppayBillBean implements Serializable{
private Integer id;
private Integer cost;//金額
private String appayPerson;//申請人
private Date date;//申請日期
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getCost() {
return cost;
}
public void setCost(Integer cost) {
this.cost = cost;
}
public String getAppayPerson() {
return appayPerson;
}
public void setAppayPerson(String appayPerson) {
this.appayPerson = appayPerson;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
複製程式碼
五、連線
上面我們已將學過了流程變數了,可以在【任務服務、執行時服務、流程開始、完成某個任務時設定流程變數】,而我們的連線就是流程變數的實際應用了....
5.1定義流程圖
我們並不是所有的流程都是按一條的路徑來走的,我們有的時候會根據條件來走不同的路。當然了,最終該流程是會一步步走完....
例子:
重要的資訊交由老闆來處理,不重要的資訊交由經理來處理
表示式的結果必須是布林型
#{variable=='value'}
- ${variable==value}
5.2測試
我在任務完成時設定流程變數為不重要,那麼跳到下一個流程時就是經理來進行處理
當我設定為重要的時候,那麼就是交由老闆來處理
六、排他閘道器
上面我們使用連線的時候用了兩個條件 : 要麼條件是“重要”,要麼條件是“不重要”....如果有另一種情況呢???就是使用者把條件輸入錯了,寫成“不知道重不重要”,那麼我們的流程怎麼走???豈不是奔潰了???
因此,我們要有一條預設的路來走,就是當該變數不符合任何的條件時,我們也有一條預設的路
值得注意的是:如果是在Eclipse中使用外掛的BPMN流程圖,如果使用了排他閘道器,那麼在Idea下是解析不了的...
解決:
我們只要重新定義BPMN流程圖的排他閘道器就行了,idea中的Activiti外掛是不用制定預設流程的,只要我們不設定條件,那就是預設的連線線
6.1測試
public class ExclusiveGetWay {
private ProcessEngine processEngine = ProcessEngines
.getDefaultProcessEngine();
// 部署流程定義,資源來在bpmn格式
@Test
public void deployProcessDefi() {
Deployment deploy = processEngine.getRepositoryService()
.createDeployment().name("排他閘道器流程")
.addClasspathResource("ExclusiveGateway.bpmn")
.deploy();
System.out.println("部署名稱:" + deploy.getName());
System.out.println("部署id:" + deploy.getId());
}
// 執行流程,開始跑流程
@Test
public void startProcess() {
String processDefiKey = "bankBill";// bpmn 的 process id屬性
ProcessInstance pi = processEngine.getRuntimeService()
.startProcessInstanceByKey(processDefiKey);
System.out.println("流程執行物件的id:" + pi.getId());// Execution 物件
System.out.println("流程例項的id:" + pi.getProcessInstanceId());// ProcessInstance
// 物件
System.out.println("流程定義的id:" + pi.getProcessDefinitionId());// 預設執行的是最新版本的流程定義
}
// 查詢正在執行任務
@Test
public void queryTask() {
// 取得任務服務
TaskService taskService = processEngine.getTaskService();
// 建立一個任務查詢物件
TaskQuery taskQuery = taskService.createTaskQuery();
// 辦理人的任務列表
List<Task> list = taskQuery.list();
// 遍歷任務列表
if (list != null && list.size() > 0) {
for (Task task : list) {
System.out.println("任務的辦理人:" + task.getAssignee());
System.out.println("任務的id:" + task.getId());
System.out.println("任務的名稱:" + task.getName());
}
}
}
// 完成任務
@Test
public void compileTask() {
String taskId = "2404";
Map<String,Object> params=new HashMap<String, Object>();
params.put("visitor", 6);
// taskId:任務id
processEngine.getTaskService().complete(taskId, params);
// processEngine.getTaskService().complete(taskId);
System.out.println("當前任務執行完畢");
}
}
複製程式碼
我們指定的值並不是VIP也不是後臺,那麼就會自動去普通視窗中處理
七、擴充閱讀
並行閘道器:
等待活動:
使用者任務:
- 使用流程變數指定處理人:
我們在快速入門的例子中,是在定義流程圖中硬性指定處理人,其實這麼幹是不夠靈活的,我們學了流程變數之後,我們是可以靈活地指定處理人的....
@Test
public void deployProcessDefi() {
Deployment deploy = processEngine.getRepositoryService()
.createDeployment().name("使用者任務指定流程")
.addClasspathResource("AppayBill.bpmn")
.deploy();
System.out.println("部署名稱:" + deploy.getName());
System.out.println("部署id:" + deploy.getId());
}
// 執行流程,開始跑流程
@Test
public void startProcess() {
String processDefiKey = "appayBill";// bpmn 的 process id屬性
Map<String,Object> params=new HashMap<String, Object>();
params.put("userID", "王某某");
ProcessInstance pi = processEngine.getRuntimeService()
.startProcessInstanceByKey(processDefiKey, params);
System.out.println("流程執行物件的id:" + pi.getId());// Execution 物件
System.out.println("流程例項的id:" + pi.getProcessInstanceId());// ProcessInstance
// 物件
System.out.println("流程定義的id:" + pi.getProcessDefinitionId());// 預設執行的是最新版本的流程定義
}
// 查詢正在執行任務
@Test
public void queryTask() {
String assignee="王某某";//指定任務處理人
// 取得任務服務
TaskService taskService = processEngine.getTaskService();
// 建立一個任務查詢物件
TaskQuery taskQuery = taskService.createTaskQuery();
// 辦理人的任務列表
List<Task> list = taskQuery
.taskAssignee(assignee)
.list();
// 遍歷任務列表
if (list != null && list.size() > 0) {
for (Task task : list) {
System.out.println("任務的辦理人:" + task.getAssignee());
System.out.println("任務的id:" + task.getId());
System.out.println("任務的名稱:" + task.getName());
}
}
}
複製程式碼
使用類指定:
組任務:
- 直接指定辦理人
- 使用流程變數
- 使用類
總結
- 如果一個業務需要多方面角色進行處理的話,那麼我們最好就是用工作流框架。因為如果其中一個環節的需求發生了變化的話,我們要是沒有用到工作流。那就需要修改很多的程式碼。十分麻煩。
- Activiti工作流框架快速入門:
- 定義工作流,使用外掛來把我們的流程圖畫出來。這個流程圖就是我們定義的工作流。
- 工作流引擎是工作流的核心,能夠讓我們定義出來的工作流部署起來。
- 由於我們使用工作流的時候是有很多資料產生的,因此Activiti是將資料儲存到資料庫表中的。這些資料庫表由Actitviti建立,由Activiti維護。
- 部署完的工作流是需要手動去執行該工作流的。
- 根據由誰處理當前任務,我們就可以查詢出具體的任務資訊。
- 根據任務的id,我們就可以執行任務了。
- 流程定義涉及到了四張資料庫表
- 我們可以通過API把我們的流程定義圖讀取出來
- 可以根據查詢最新版本的流程定義
- 刪除流程定義
- 部署流程定義的時候也可以是ZIP檔案
- 流程在執行中,涉及到兩個物件,四張資料庫表:
- 流程例項
- 流程任務
- 流程例項可以有多個,流程物件只能有一個。
- 如果流程沒有分支的話,那麼流程例項就等於流程物件
- 基於這麼兩個物件,我們就可以做很多事情了
- 獲取流程例項和任務的歷史資訊
- 判斷流程例項是否為空來判斷流程是否結束了
- 檢視正在執行服務的詳細資訊
- 通過流程例項來開啟流程
- 流程變數:它涉及到了兩張表。流程變數實際上就是我們的條件。
- 流程變數的作用域只在流程例項中。
- 我們可以在流程開始的時候設定流程變數,在任務完成的時候設定流程變數。
- 執行時服務和流程任務都可以設定流程變數。
- 通過連線我們可以在其中設定條件,根據不同的條件流程走不同的分支
- 如果沒有設定預設的條件,當條件不吻合的時候,那麼流程就走不下去了,因此需要排他閘道器來設定一條預設的路徑。
如果文章有錯的地方歡迎指正,大家互相交流。習慣在微信看技術文章,想要獲取更多的Java資源的同學,可以關注微信公眾號:Java3y