• Activiti快速入门


    1.什么是Activiti

    在解释activiti之前我们看一下什么是工作流。
    工作流(Workflow),就是“业务过程的部分或整体在计算机应用环境下的自动化”,它主要解决的是“使在多个参与者之间按照某种预定义的规则传递文档、信息或任务的过程自动进行,从而实现某个预期的业务目标,或者促使此目标的实现”。
    我的理解是,工作流将一套大的业务逻辑分解成业务逻辑段, 并统一控制这些业务逻辑段的执行条件,执行顺序以及相互通信。 实现业务逻辑的分解和解耦。
    Activiti是一个开源的工作流引擎,它实现了BPMN 2.0规范,可以发布设计好的流程定义,并通过api进行流程调度。
    BPMN即业务流程建模与标注(Business Process Model and Notation,BPMN) ,描述流程的基本符号,包括这些图元如何组合成一个业务流程图(Business Process Diagram)。

    BPMN的流程图长这样子

    activiti5.13使用了23张表支持整个工作流框架,底层使用mybatis操作数据库。这些数据库表为


    1)ACT_RE_*: 'RE'表示repository。 这个前缀的表包含了流程定义相关的静态资源(图片,规则等)。
    2)ACT_RU_*: 'RU'表示runtime。 运行时表,包含流程实例,任务,变量,异步任务等运行中的数据。流程结束时这些记录会被删除。
    3)ACT_ID_*: 'ID'表示identity。 这些表包含用户和组的信息。
    4)ACT_HI_*: 'HI'表示history。 这些表包含历史数据,比如历史流程实例,变量,任务等。
    5)ACT_GE_*: 通用数据,bytearray表保存文件等字节流对象。

    工作流进行的基本过程如下:
    定义流程(框架外) -> 部署流程定义 -> 启动流程实例, 框架移动到任务1 -> 拾取组任务 -> 办理个人任务, 框架移动到任务2 -> 拾取组任务 -> 办理个人任务...

    组任务是多个用户都可以完成的任务。没有组任务直接办理个人任务; 有组任务需先通过拾取将组任务变成个人任务, 然后再办理。

    个人任务/组任务在表中的区别

    个人任务: 表act_ru_task的ASSIGNEE段即指定的办理人

    组任务: 表act_ru_task的ASSIGNEE段为null, 相关信息在表act_ru_identitylink中, 组任务1见userid段;  组任务2见groupid段, 当然还需查询act_id_xxx表才能精确到人.

    2.Activiti的使用

    2.1 创建processEngine

    processEngine控制着工作流整个流程

    public class processEngine {
        @Test
        public void createProcessEngine1() {
            String resource = "activiti-context.xml";    // 配置文件
            String beanName = "processEngineConfiguration";  // 配置文件中bean name
            // 从配置文件创建配置对象
            ProcessEngineConfiguration config = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource(resource, beanName);
            // 根据配置创建引擎对象
            ProcessEngine processEngine = config.buildProcessEngine();
        }
    
        /**
         *  一条语句创建processEngine, 要求:
         * 1、配置文件必须在classpath根目录下
         * 2、配置文件名必须为activiti-context.xml或activiti.cfg.xml
         * 3、工厂对象的id必须为processEngine
         */
        @Test
        public void createProcessEngine2() {
            ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        }
    }
    <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">
        <!-- 配置 -->
        <bean id="processEngineConfiguration"
             class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
            <property name="jdbcDriver"  value="com.mysql.jdbc.Driver"/>
            <property name="jdbcUrl"  value="jdbc:mysql:///test_activiti"/>
            <property name="jdbcUsername"  value="root"/>
            <property name="jdbcPassword"  value="root"/>
            <!-- 创建processEngine时, activiti自动创建23张表 -->
            <property name="databaseSchemaUpdate" value="true"/>
        </bean>
        <!-- 使用配置创建引擎对象 -->
        <bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
            <property name="processEngineConfiguration" ref="processEngineConfiguration"/>
        </bean>
    </beans>

    当然, 可以与spring进一步整合, 使用spring方式获取processEngine.  applicationContext.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">
        <bean id="dataSource"
            class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="com.mysql.jdbc.Driver" />
            <property name="url" value="jdbc:mysql:///activiti_day2" />
            <property name="username" value="root" />
            <property name="password" value="root" />
        </bean>
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"/>
        </bean>
        <!-- 流程引擎配置对象 -->
        <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
            <!-- 注入数据源 -->
            <property name="dataSource" ref="dataSource"/>
            <!-- 注入事务管理器对象 -->
            <property name="transactionManager" ref="transactionManager"/>
            <property name="databaseSchemaUpdate" value="true" />
        </bean>
        <bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
            <property name="processEngineConfiguration" ref="processEngineConfiguration" />
        </bean>
    </beans>

    2.2 部署流程定义

    流程是由用户通过bpmn等文件(底层xml)定义的, 即上面列举的的bpmn流程图

    定义好的流程需要部署给activiti才能被其使用

       /**
         * 部署流程定义 
         * 一套定义文件只有一个流程定义Key, 但可以被部署多次形成多个版本(部署表里多个id和流程定义表里多个id)
         * 涉及的表:act_re_deployment(部署表)、act_re_procdef(流程定义表)、act_ge_bytearray(二进制表)
         */
        @Test
        public void test() throws FileNotFoundException {
            DeploymentBuilder deploymentBuilder = processEngine.getRepositoryService().createDeployment();
            // 逐个文件部署
            // deploymentBuilder.addClasspathResource("qjlc.bpmn");
            // deploymentBuilder.addClasspathResource("qjlc.png");
            // 压缩文件打包部署, 推荐
            ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(new File("d:\processDef.zip")));
            deploymentBuilder.addZipInputStream(zipInputStream );
            
            Deployment deployment = deploymentBuilder.deploy();
        }

     

    2.3 启动流程实例

         /**
         * 启动一个流程实例
         * 涉及的表:
         * act_ru_execution(流程实例表), 管理流程进度
         * act_ru_task(任务表), 进行到哪一个流程的哪一个任务, 该由谁完成
         */
        @Test
        public void test() throws Exception{
            String processDefinitionKey = "qjlc";
            //方式一:根据流程定义id启动流程实例
            //String processDefinitionId = "qjlc:6:904";
            //ProcessInstance processInstance = processEngine.getRuntimeService().startProcessInstanceById(processDefinitionId);
            
            //方式二:根据流程定义Key启动流程实例   推荐!流程定义有多个版本时会选择最新版本
            ProcessInstance processInstance = processEngine.getRuntimeService().startProcessInstanceByKey(processDefinitionKey);
        }        

    2.4 办理任务

      /**
        * 办理任务, 办理后框架自动移动到下一任务
        * 涉及的表: act_ru_execution(流程实例表)、act_ru_task(任务表)
        */
        @Test
        public void test() throws Exception{
            String taskId = "1304";
            processEngine.getTaskService().complete(taskId);
        }

    2.5 其他操作

       /**
        * 查询流程定义
        * 涉及的表:act_re_procdef
        */
        @Test
        public void test(){
            ProcessDefinitionQuery query = processEngine.getRepositoryService().createProcessDefinitionQuery();
            // 查询条件过滤
            query.processDefinitionKey("qjlc");
            query.orderByProcessDefinitionVersion().asc();
            List<ProcessDefinition> list = query.listPage(0, 10);
            for (ProcessDefinition processDefinition : list) {
                System.out.println(processDefinition.getId());
            }
        }

    activiti中查询的套路:  processEngine.getXXXService().createXXXQuery().list()/singleResult()
    processEngine.getRepositoryService().createDeploymentQuery().list(); // 查询部署
    processEngine.getRuntimeService().createProcessInstanceQuery().list(); // 查询流程实例
    processEngine.getTaskService().createTaskQuery().list(); // 查询个人任务
    processEngine.getIdentityService().createUserQuery().list(); // 查询用户
    processEngine.getHistoryService().createHistoricActivityInstanceQuery().list(); //查询历史
    过滤条件
    查询个人任务 query.taskAssignee()
    查询组任务    query.taskCandidate()

    几个javabean(和表对应):
    Deployment------act_re_deployment
    ProcessDefinition-----act_re_procdef
    ProcessInstance------act_ru_execution
    Task-----act_ru_task
    几个Query对象
    DeploymentQuery------act_re_deployment
    ProcessDefinitionQuery-----act_re_procdef
    ProcessInstanceQuery------act_ru_execution
    TaskQuery-----act_ru_task
    几个Service
    RepositoryService----操作部署表、流程定义表等静态资源信息表
    RuntimeService----操作流程实例表、任务表等动态信息表
    TaskService-----操作任务表
    HistoryService----操作历史表
    IdentityService----操作用户表、组表、关系表

      // 删除流程定义
        @Test
        public void test1(){
            String deploymentId = "101";  //部署id
            boolean cascade = false;  // 级联删除, 设置为true的话, 有正在跑的流程实例及任务也会被删除
            processEngine.getRepositoryService().deleteDeployment(deploymentId, cascade);
        }
        // 删除流程实例
        @Test
        public void test2() throws Exception{
            String processInstanceId = "1201";
            String deleteReason = "不请假了";  // 可以添加删除原因
            processEngine.getRuntimeService().deleteProcessInstance(processInstanceId, deleteReason);
        }
      // 根据部署id, 获取定义文件 @Test public void test3() throws Exception{ String deploymentId = "201"; //部署id // 先获得定义文件的名字 List<String> names = processEngine.getRepositoryService().getDeploymentResourceNames(deploymentId); for (String name : names) { InputStream in = processEngine.getRepositoryService().getResourceAsStream(deploymentId, name); FileUtils.copyInputStreamToFile(in, new File("d:\"+name)); in.close(); } } // 根据流程定义id, 获取定义文件 @Test public void test4() throws Exception{ String processDefinitionId = "qjlc:6:904"; //流程定义id InputStream pngStream = processEngine.getRepositoryService().getProcessDiagram(processDefinitionId); FileUtils.copyInputStreamToFile(pngStream, new File("d:\abc.png")); }

    通过javabean能访问到某些需要的字段, 例如

    processInstance.getActivityId() -> 当前执行的任务名

    processDefinition.getDiagramResourceName() -> 定义文件中图片的名字

    2.6  流程变量

    多个任务间可以通过流程变量通信.

    流程变量以key-value形式存放, 存于表 act_ru_variable. 在同一流程实例里, 不同方式设置变量, key相同时会覆盖

        // 启动流程实例时 设置流程变量
        @Test
        public void test1() {
            String processDefinitionKey = "bxlc";
            Map<String, Object> variables = new HashMap<String, Object>();
            variables.put("key", "value");
            ProcessInstance pi = processEngine.getRuntimeService().startProcessInstanceByKey(processDefinitionKey, variables);
        }
        // 办理任务时 设置流程变量, 更实用!
        @Test
        public void test2() {
            String taskId = "206";
            Map<String, Object> variables = new HashMap<>();
            variables.put("key", "value");
            processEngine.getTaskService().complete(taskId, variables);
        }
        // 通过RuntimeService 设置流程变量
        @Test
        public void test3() {
            String executionId = "201"; // 流程实例id
            Map<String, Object> variables = new HashMap<>();
            variables.put("key", "value");
            //processEngine.getRuntimeService().setVariable(executionId, variableName, value);
            processEngine.getRuntimeService().setVariables(executionId, variables);
        }
        // 通过TaskService 设置流程变量
        @Test
        public void test4() {
            String taskId = "304";
            String key = "key";
            Object value = "value";
            processEngine.getTaskService().setVariable(taskId , key, value);
        }
    // 通过RuntimeService 获取流程变量
        @Test
        public void test5() {
            String executionId = "201";
            Object value = processEngine.getTaskService().getVariable(executionId, "user");
            System.out.println(value);
        }
         // 通过TaskService 获取流程变量
        @Test
        public void test6() {
            String taskId = "304";
            Object value = processEngine.getTaskService().getVariable(taskId, "user");
            System.out.println(value);
        }

    流程变量还可以通过在定义流程用表达式${}.  框架在该段任务执行前从act_ru_variable表里动态获取

    另外, 启动流程实例还有一个重载函数, 除了流程变量variables还能指定业务主键businessKey

    processEngine.getRuntimeService().startProcessInstanceByKey(processDefinitionKey, businessKey, variables);

    businessKey一般设置为业务表的主键值, 在使用activiti的时候, 通过查询业务表主键, 能方便地查询出业务的最新状态

    2.7  组任务

    组任务1

      // 查询组任务
        @Test
        public void test1() {
            TaskQuery query = processEngine.getTaskService().createTaskQuery();
            // 使用候选人查询组任务
            String candidateUser = "财务二";
            query.taskCandidateUser(candidateUser);
            List<Task> list = query.list();
            for (Task task : list) {
                System.out.println(task.getId());
            }
        }
        // 拾取组任务
        @Test
        public void test2() {
            String taskId = "1102";
            processEngine.getTaskService().claim(taskId , "财务二");
        }
        // 办理组任务, 无需指定办理人
        @Test
        public void test3() throws Exception{
            String taskId = "1102";
            processEngine.getTaskService().complete(taskId);
        }

    组任务2

      // activiti使用自己的用户与组的权限表, 因此需要设置. 但需注意要与框架外用户/组同步设置
        @Test
        public void test2() {
            // 创建组
            Group group = new GroupEntity();
            group.setId("财务组");
            processEngine.getIdentityService().saveGroup(group);
            // 创建用户
            User user = new UserEntity();
            user.setId("2");
            processEngine.getIdentityService().saveUser(user);
            // 维护用户与组的关系
            processEngine.getIdentityService().createMembership("2", "财务组");
        }
        // 查询组任务
        @Test
        public void test2() {
            TaskQuery query = processEngine.getTaskService().createTaskQuery();
            String candidateUser = "2";
            // 使用候选人过滤
            query.taskCandidateUser(candidateUser);
            // 使用组过滤
            //query.taskCandidateGroup("财务组");
            List<Task> list = query.list();
            for (Task task : list) {
                System.out.println(task.getId());
            }
        }
        // 拾取组任务
        @Test
        public void test3() {
            String taskId = "1902";
            processEngine.getTaskService().claim(taskId , "2");
        }
        // 办理组任务略

    2.8 排他网关

    设置分支条件

    3. 一些使用经验

    1)

    考虑到工作流中的一个任务, 对应一个业务段, 可以将taskDefinitionKey设置成strus action类的method, 使之具有一定的通用性

    2)

    两种对流程定义的查询, 后者能获得更多定义的细节信息  processDefinitionEntity.findActivity(taskId) 工作流中某任务的信息

    repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult()

    (ProcessDefinitionEntity) repositoryService.getProcessDefinition(processDefinitionId)

  • 相关阅读:
    tomcat2章1
    tomcat1章1
    线程安全的CopyOnWriteArrayList
    Java Security: Illegal key size or default parameters?
    struct和typedef struct
    C可变参数函数 实现
    C和C++混合编程(__cplusplus 与 external "c" 的使用)
    WebRTC之带宽控制部分学习(1) ------基本demo的介绍
    WebRTC代码走读(八):代码目录结构
    webrtc中的带宽自适应算法
  • 原文地址:https://www.cnblogs.com/myJavaEE/p/6676226.html
Copyright © 2020-2023  润新知