十分鐘認識Activiti6.0工作流引擎

概述

瞭解Activiti

假如你還不知道Activiti是什麼,那麼工作流引擎對你來說一定也是新的,解決Activiti和工作流知識盲點,所以看到到這裏就對了,還有Android同學會誤以爲是“Activity”,工作流引擎Activiti和Android開發的Activity這是兩碼事兒,Activiti是一個開源的自動化業務工作流引擎,具體概念就不碎碎念介紹了,

使用工作流引擎

工作流引擎最常見用於審批流程中,現在一線互聯網公司也開始使用,並有快速推廣的趨勢,複雜繁多的業務流程如果採用if else實現那將是崩潰的,代碼不可維護,業務流程在代碼中可讀性很差,所以高人設計了業務流程模型圖示BPMN2.0,我們要做到就是把業務場景抽象爲標準流程圖,把流程圖丟到流程引擎中按流程定義約定逐步流轉,很顯然擴展性和業務可描述性會好很多,所以工作流引擎主要用於解決複雜的業務,目前經常被提起的中臺系統抽象業務爲服務,也涉及大量智能的業務流程引擎做支撐。

讓工作流引擎運行

我們快速體驗一個流程的運行過程

  1. 搭建流程引擎

我們在IDEA環境中使用 Spring Initializr引導我們創建一個標準工程,採用最新穩定版spring boot 2.0.2,Activiti 6.0,通過引入H2內存數據庫,方便我們快速啓動程序演示,而不用費心在創建數據庫相關工作,當程序啓動Spring boot會基於自動配置原理給我們隱式的創建一個工作流引擎對象ProcessEngine,並把RuntimeService(控制流程運行時數據流轉)等核心服務註冊到 Spring 容器中,我們只需要 依賴注入使用即可。

  1. 繪製流程圖

繪製流程圖建議大家通過Eclipse + Activiti插件來繪製,如果大家對流程圖元素還不瞭解,可以直接拷貝流程定義文件運行,這樣方便快速的運行我們的快速程序

  1. 部署流程圖

我們代碼裏面並沒有體現流程部署的過程,也得益於Spring Boot提供的自動部署功能,也是在Spring 容器啓動的過程通過 activiti-spring組件,把Class path對應的/processes目錄的流程文件自動加載部署,約定大於配置,快速演示代碼推薦大家把流程圖放到/processes 資源包下面,線上系統則不建議這樣做

  1. 啓動流程

啓動流程是通過流程定義文件Key創建一個流程實例的過程,類似於我們根據類名new出一個對象的原理,一個流程定義文件可以生成許許多多的流程實例

  1. 多用戶協助審批流程

在命令行下體驗流程審批的效果,對Java後端開發來說,更多的傾向於瞭解底層的原理,而對UI效果興趣不高,其實Activiti也跟我們提供了還不錯的activiti-app來更好的體驗流程的流轉。我們這裏在命令行下運行工作流程,可以滿足一下極客青年小小的虛榮心。

開發

創建工程

基於spring Initializr創建一個標準的maven工程,並添加相關依賴 spring boot 2.0.2最新穩定版,Activiti 6最新穩定版

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

// pom.xml

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

 

    <groupId>com.imooc.activiti</groupId>

    <artifactId>activiti-demo</artifactId>

    <version>0.0.1-SNAPSHOT</version>

    <packaging>jar</packaging>

 

    <name>activiti-demo</name>

    <description>Demo project for Spring Boot</description>

 

    <parent>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-parent</artifactId>

        <version>2.0.2.RELEASE</version>

        <relativePath/> <!-- lookup parent from repository -->

    </parent>

 

    <properties>

        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

        <java.version>1.8</java.version>

    </properties>

 

    <dependencies>

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter</artifactId>

        </dependency>

        <dependency>

            <groupId>org.projectlombok</groupId>

            <artifactId>lombok</artifactId>

        </dependency>

        <dependency>

            <groupId>com.google.guava</groupId>

            <artifactId>guava</artifactId>

            <version>24.0-jre</version>

        </dependency>

        <dependency>

            <groupId>org.activiti</groupId>

            <artifactId>activiti-spring-boot-starter-basic</artifactId>

            <version>6.0.0</version>

        </dependency>

        <dependency>

            <groupId>com.h2database</groupId>

            <artifactId>h2</artifactId>

            <scope>runtime</scope>

        </dependency>

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-test</artifactId>

            <scope>test</scope>

        </dependency>

    </dependencies>

 

    <build>

        <plugins>

            <plugin>

                <groupId>org.springframework.boot</groupId>

                <artifactId>spring-boot-maven-plugin</artifactId>

            </plugin>

        </plugins>

    </build>

</project>

Spring Initializr會自動創建程序啓動入口類ActivitiDemoApplication

/src/main/java/com.imooc.activiti.demo.ActivitiDemoApplicationpackage 

com.imooc.activiti.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplicationpublic class ActivitiDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(ActivitiDemoApplication.class, args);
    }
}

設計流程定義文件

https://img4.mukewang.com/5b1fde170001823818100620.jpg

剛開始設計流程定義文件推薦大家使用 Eclipse+ Activiti插件的方式, Activiti也提供了基於瀏覽器的web 流程設計器,都可以分別體驗一下,這個流程定義文件就是普通的xml,大家應該能很容易看明白這個xml文件

// src/main/resources/processes/second_approve.bpmn

<?xml version="1.0" encoding="UTF-8"?><definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" xmlns:tns="http://www.activiti.org/test" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test" id="m1528728906382" name="">
  <process id="second_approve" name="二級審批流程" isExecutable="true" isClosed="false" processType="None">
    <startEvent id="startEvent" name="開始"></startEvent>
    <userTask id="submitForm" name="填寫審批信息">
      <extensionElements>
        <activiti:formProperty id="message" name="申請信息" type="string" required="true"></activiti:formProperty>
        <activiti:formProperty id="name" name="申請人姓名" type="string" required="true"></activiti:formProperty>
        <activiti:formProperty id="submitTime" name="提交時間" type="date" datePattern="yyyy-MM-dd" required="true"></activiti:formProperty>
        <activiti:formProperty id="submitType" name="確認申請" type="string" required="true"></activiti:formProperty>
      </extensionElements>
    </userTask>
    <sequenceFlow id="flow1" sourceRef="startEvent" targetRef="submitForm"></sequenceFlow>
    <exclusiveGateway id="decideSubmit" name="提交OR取消"></exclusiveGateway>
    <sequenceFlow id="flow2" sourceRef="submitForm" targetRef="decideSubmit"></sequenceFlow>
    <userTask id="tl_approve" name="主管審批">
      <extensionElements>
        <activiti:formProperty id="tlApprove" name="主管審批結果" type="string"></activiti:formProperty>
        <activiti:formProperty id="tlMessage" name="主管備註" type="string" required="true"></activiti:formProperty>
      </extensionElements>
    </userTask>
    <sequenceFlow id="flow3" sourceRef="decideSubmit" targetRef="tl_approve">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${submitType == "y" || submitType == "Y"}]]></conditionExpression>
    </sequenceFlow>
    <exclusiveGateway id="decideTLApprove" name="主管審批校驗"></exclusiveGateway>
    <sequenceFlow id="flow4" sourceRef="tl_approve" targetRef="decideTLApprove"></sequenceFlow>
    <endEvent id="endEvent" name="結束"></endEvent>
    <endEvent id="endEventCancel" name="取消"></endEvent>
    <sequenceFlow id="flow8" sourceRef="decideSubmit" targetRef="endEventCancel">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${submitType == "n" || submitType == "N"}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow9" sourceRef="decideTLApprove" targetRef="submitForm">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${tlApprove == "n" || tlApprove == "N"}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="_2" sourceRef="decideTLApprove" targetRef="endEvent">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${tlApprove == "y" || tlApprove == "Y"}]]></conditionExpression>
    </sequenceFlow>
  </process>
 </definitions>

開發可以根據spring boot自動裝配的流程樣例

流程執行我們使用了CommandLineRunner,這個類的事例會在Spring boot容器啓動加載完成後直接運行

//src/main/java/com.imooc.activiti.demo.AutoConfigurationProcessDemo

@Configuration
@Slf4jpublic class AutoConfigurationProcessDemo {
    @Autowired    private RuntimeService runtimeService;
    @Autowired    private TaskService taskService;
    @Autowired    private FormService formService;   
     /**
     * 命令行執行
     *
     * @return
     */
    @Bean  
    public CommandLineRunner commandLineRunner() {        
        return new CommandLineRunner() {
            @Override            
            public void run(String... args) throws Exception {                
                log.info("啓動我們的程序");                
                //啓動運行流程
                ProcessInstance processInstance = startProcessInstance("second_approve");                
                //處理流程任務
                processTask(processInstance);                
                log.info("結束我們的程序");
            }
        };
    }    /**
     * 處理流程任務
     *
     * @param processInstance
     * @throws ParseException
     */
    private void processTask(ProcessInstance processInstance) throws ParseException {
        Scanner scanner = new Scanner(System.in);        
        while (processInstance != null && !processInstance.isEnded()) {
            List<Task> list = taskService.createTaskQuery().list();            
            log.info("待處理任務數量 [{}]", list.size());            
            for (Task task : list) {                
            log.info("待處理任務 [{}]", task.getName());
                Map<String, Object> variables = buildVariablesByScanner(scanner, task);
                taskService.complete(task.getId(), variables);
                processInstance = runtimeService
                        .createProcessInstanceQuery()
                        .processInstanceId(processInstance.getId())
                        .singleResult();
            }
        }
        scanner.close();
    }    /**
     * 從控制檯獲取變量
     *
     * @param scanner
     * @param task
     * @return
     * @throws ParseException
     */
    private Map<String, Object> buildVariablesByScanner(Scanner scanner, Task task) throws ParseException {
        TaskFormData taskFormData = formService.getTaskFormData(task.getId());
        List<FormProperty> formProperties = taskFormData.getFormProperties();        
        return buildVariablesByScanner(scanner, formProperties);
    }    
    
    public static Map<String, Object> buildVariablesByScanner(Scanner scanner, List<FormProperty> formProperties) throws ParseException {
        Map<String, Object> variables = Maps.newHashMap();        
        for (FormProperty property : formProperties) {            
        String line = null;            
        if (StringFormType.class.isInstance(property.getType())) {                
        log.info("請輸入 {} ?", property.getName());                
        line = scanner.nextLine();
                variables.put(property.getId(), line);
            } else if (DateFormType.class.isInstance(property.getType())) {                
            log.info("請輸入 {} ? 格式 (yyyy-MM-dd)", property.getName());               
            line = scanner.nextLine();
                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
                Date date = dateFormat.parse(line);
                variables.put(property.getId(), date);
            } else {                
                log.info("類型暫不支持 {}", property.getType());
            }            
            log.info("您輸入的內容是 [{}]", line);

        }        
        return variables;
    }    /**
     * 獲取流程實例
     *
     * @param processDefinitionId
     * @return
     */
    private ProcessInstance startProcessInstance(String processDefinitionId) {
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionId);        log.info("啓動流程 [{}]", processInstance.getProcessDefinitionKey());        return processInstance;
    }

}

命令行運行效果

可以看到我簡單3個文件就可以讓工作流程序運行起來,爲了讓控制檯輸出更清晰直觀,讓控制檯日誌僅僅輸出message日誌信息,忽略默認格式中日誌級別時間信息等等

//src/main/resources/application.yml
logging:  
    pattern:    
        console: "%m%n"

我們可以直接在控制檯運行 mvn spring-boot:run,當然也可以mvn package打出一個Jar包後,運行獨立jar包

mvn spring-boot:run
// 輸入正常流程審批通過
啓動我們的程序
啓動流程 [second_approve]
待處理任務數量 [1]
待處理任務 [填寫審批信息]
請輸入 申請信息 ?
$辭職,世界那麼大,我想去看看
您輸入的內容是 [辭職,世界那麼大,我想去看看]
請輸入 申請人姓名 ?
$Jimmy
您輸入的內容是 [Jimmy]
請輸入 提交時間 ? 格式 (yyyy-MM-dd)
$2018-05-21
您輸入的內容是 [2018-05-21]
請輸入 確認申請 ?
$Y
您輸入的內容是 [Y]
待處理任務數量 [1]
待處理任務 [主管審批]
請輸入 主管審批結果 ?
$Y
您輸入的內容是 [Y]
請輸入 主管備註 ?
$錢包那麼小,勸你別作妖
您輸入的內容是 [錢包那麼小,勸你別作妖]
結束我們的程序
 

總節

我們簡單演示了一下Activiti6.0工作流的使用方式,希望能幫助到大家,Activiti是非常受歡迎的工作流引擎,很多公司的工作流平臺技術選型都選了Activiti,工作流技術相對複雜一些,如果想深入瞭解工作流技術是需要下一番功夫的,正如說所Activiti工作流是一個有技術門檻的事兒,掌握有門檻的技術,提升自己的競爭力,相信大家可以做到的。


作者:JimmyNo1
鏈接:https://www.imooc.com/article/35590
來源:慕課網
本文原創發佈於慕課網 ,轉載請註明出處,謝謝合作

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章