activiti学习笔记(十七)官方文档阅读

之前学习activiti时买过《activiti实战》,感觉这本书有些关键的细节并没有讲到,于是找了activiti的官方文档,想照着官方的英文文档系统学习一下activiti。本次分享的就是照着官方文档在spring boot环境下集成。

首先添加maven依赖:

<?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.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>

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

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.10.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-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-spring-boot-starter-basic</artifactId>
            <version>6.0.0</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>6.0.6</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Finchley.M9</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

接着在resouces目录下新建一个processes目录,这个目录下放置流程文件one-task-process.bpmn20.xml,内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<definitions
        xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
        xmlns:activiti="http://activiti.org/bpmn"
        targetNamespace="Examples">

    <process id="oneTaskProcess" name="The One Task Process">
        <startEvent id="theStart" />
        <sequenceFlow id="flow1" sourceRef="theStart" targetRef="theTask" />
        <userTask id="theTask" name="my task" />
        <sequenceFlow id="flow2" sourceRef="theTask" targetRef="theEnd" />
        <endEvent id="theEnd" />
    </process>

</definitions>

添加后,我们可以加一个配置类,在应用启动后打印流程相关信息:

@Configuration
@ComponentScan
@EnableAutoConfiguration
public class MyApplication {

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

    @Bean
    public CommandLineRunner init(final RepositoryService repositoryService,
                                  final RuntimeService runtimeService,
                                  final TaskService taskService) {

        return new CommandLineRunner() {
            @Override
            public void run(String... strings) throws Exception {
                System.out.println("Number of process definitions : "
                        + repositoryService.createProcessDefinitionQuery().count());
                System.out.println("Number of tasks : " + taskService.createTaskQuery().count());
                runtimeService.startProcessInstanceByKey("oneTaskProcess");
                System.out.println("Number of tasks after process start: " + taskService.createTaskQuery().count());
            }
        };
    }
}

启动应用后,结果如下:

Number of process definitions : 1
Number of tasks : 1
Number of tasks after process start: 1

可以看出已经自动找到流程文件,所以流程定义为1,启动后就创建了一个任务,说明环境搭建成功,接下来我们就可以继续学习activiti的相关概念了。

ProcessEngine

流程引擎(ProcessEngine)类是activiti进行流程相关api调用的中心点,通过ProcessEngine可以各种service类,结构如下:


屏幕快照 2018-07-15 上午11.40.50.png

流程引擎通过配置生成,而在spring boot 中引入activiti的依赖包后就可以自动生成流程引擎了。
流程引擎可以获取到的service有:

RuntimeService runtimeService = processEngine.getRuntimeService();
RepositoryService repositoryService = processEngine.getRepositoryService();
TaskService taskService = processEngine.getTaskService();
ManagementService managementService = processEngine.getManagementService();
IdentityService identityService = processEngine.getIdentityService();
HistoryService historyService = processEngine.getHistoryService();
FormService formService = processEngine.getFormService();
DynamicBpmnService dynamicBpmnService = processEngine.getDynamicBpmnService();

RepositoryService

这个类的功能涉及到下面几个方面:

  • RepositoryService是首先会使用到的service,它提供了管理和操作deployments和process definitions的方法。其中process definition是一个bpmn2.0流程的java副本,它表示了流程每一步的行为和结构。deployment是流程引擎进行打包的单元,它包含了流程的xml文件和其他相关资源。当创建一个xml格式的流程文件后,我们将其部署后,就会在数据库生成一份流程定义,部署后的流程就可以被启动了。
  • RepositoryService类基于流程定义启动流程实例 process instance。
  • RepositoryService类可以进行保存和获取流程变量 process variables,流程变量保存的是某个具体流程实例在流程流转时需要的变量。
  • RepositoryService可以查询流程实例和执行对象execution。关于流程实例和执行对象的区别可以自行百度,一般来说在没有分支的情况下,获取到的流程实例和执行对象的内容是一样的,而在有分支的情况下,获取到的流程实例和执行对象是有区别的。
  • 暂挂和激活流程定义,这部分内容将在后面描述。

TaskService

TaskService则是围绕着任务task相关的操作,任务是由有权限的处理人来执行的。借助TaskService可以实现的功能有:

  • 查询分配给某个人或组的任务
  • 创建一个单独的任务,这个任务不和某个流程实例挂钩。
  • 签收和完成任务。签收意味着某人已经决定要处理这个任务,其他人就不能再去做这个任务了。

IdentityService

IdentityService这个类则是用于对用户和组的管理。

FormService

FormService这个类不是必须的。它主要操作关于start form和task form两个观点。官网解释为:A start form is a form that is shown to the user before the process instance is started, while a task form is the form that is displayed when a user wants to complete a form.

HistoryService

HistoryService这个类则是拥有查询流程流转过程中保存的历史数据,从数据中可以得知每个节点的具体操作信息。
下面是一个获取历史数据例子:

HistoryService historyService = processEngine.getHistoryService();
HistoricProcessInstance historicProcessInstance =
historyService.createHistoricProcessInstanceQuery().processInstanceId(procId).singleResult();
System.out.println("Process instance end time: " + historicProcessInstance.getEndTime());

ManagementService和DynamicBpmnService

这两个类不常用到,这里就不再阐述了。

异常策略

activiti异常类的基类是org.activiti.engine.ActivitiException,这个异常类有几个子类,用于应对不同的异常,如果有某个异常被这几个子类范围内,就会归为ActivitiExceptions异常抛出。

启动流程实例

基于部署后生成的某个流程定义,我们可以启动多个流程实例。流程运行时的各种状态可以通过RuntimeService类来查询。在创建流程实例的时候,可以给特定的流程实例设置流程变量process variables,这也是不同流程实例的不同之处。创建流程实例有很多种方法,下面使用了RuntimeService类来创建,并且在启动的时候还添加了需要的流程参数。

Map<String, Object> variables = new HashMap<String, Object>();
variables.put("employeeName", "Kermit");
variables.put("numberOfDays", new Integer(4));
variables.put("vacationMotivation", "I'm really tired!");

RuntimeService runtimeService = processEngine.getRuntimeService();
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("vacationRequest", variables);

// Verify that we started a new process instance
Log.info("Number of process instances: " + runtimeService.createProcessInstanceQuery().count());

完成任务

流程中的用户组和用户有一个自己需要处理的任务列表,他可以通过下面这种方法查询:

TaskService taskService = processEngine.getTaskService();
List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("management").list();
for (Task task : tasks) {
  Log.info("Task available: " + task.getName());
}

获取到指定的任务后,我们可以使用TaskService类的complete方法完成任务:

Task task = tasks.get(0);

Map<String, Object> taskVariables = new HashMap<String, Object>();
taskVariables.put("vacationApproved", "false");
taskVariables.put("managerMotivation", "We have a tight deadline!");
taskService.complete(task.getId(), taskVariables);

暂挂和激活流程

我们可以将部署好的流程定义进行暂挂操作,使用的类是RepositoryService。流程定义被赞挂后就不能再创建流程实例了,只有重新激活以后才能再次正常创建流程实例。
下面是暂挂后再次创建流程跑出异常的例子:

repositoryService.suspendProcessDefinitionByKey("vacationRequest");
try {
  runtimeService.startProcessInstanceByKey("vacationRequest");
} catch (ActivitiException e) {
  e.printStackTrace();
}

激活流程定义的方法为:repositoryService.activateProcessDefinitionXXX。
此外,流程实例也支持暂挂和激活,使用的方法为:

runtimeService.suspendProcessInstance();
runtimeService.activateProcessInstance();

查询流程信息

有两种查询流程引擎中的信息的方法:query API 和 native queries。前者是使用activiti提供的固定格式的查询方法,如下:

List<Task> tasks = taskService.createTaskQuery()
    .taskAssignee("kermit")
    .processVariableValueEquals("orderId", "0815")
    .orderByDueDate().asc()
    .list();

由于这种查询方法封装了查询格式,对于复杂的sql查询并不支持,为此复杂查询需要用到native queries,即自己写sql语句进行查询:

List<Task> tasks = taskService.createNativeTaskQuery()
  .sql("SELECT count(*) FROM " + managementService.getTableName(Task.class) + " T WHERE T.NAME_ = #{taskName}")
  .parameter("taskName", "gonzoTask")
  .list();

变量

流程实例 process instance,执行对象execution 和 任务 task这三者都有各自作用范围的变量。这些变量存储在 ACT_RU_VARIABLE 表中。

  • 流程实例添加变量,在RuntimeService类中启动流程实例时添加
ProcessInstance startProcessInstanceByKey(String processDefinitionKey, Map<String, Object> variables);
  • execution中添加变量,RuntimeService类中设置:
void setVariable(String executionId, String variableName, Object value);
void setVariableLocal(String executionId, String variableName, Object value);
void setVariables(String executionId, Map<String, ? extends Object> variables);
void setVariablesLocal(String executionId, Map<String, ? extends Object> variables);
  • TaskService获取变量
Map<String, Object> getVariables(String executionId);
Map<String, Object> getVariablesLocal(String executionId);
Map<String, Object> getVariables(String executionId, Collection<String> variableNames);
Map<String, Object> getVariablesLocal(String executionId, Collection<String> variableNames);
Object getVariable(String executionId, String variableName);
<T> T getVariable(String executionId, String variableName, Class<T> variableClass);
  • execution获取变量
execution.getVariables();
execution.getVariables(Collection<String> variableNames);
execution.getVariable(String variableName);

execution.setVariables(Map<String, object> variables);
execution.setVariable(String variableName, Object value);

transient variable 局部变量

表达式

表达式有两种类型:value-expression 和 method-expression 。前者最终转化为值,如{xxx},后者转化为带参数或者不带参数的方法,如果不带参数,记得带上括号,以便和值表达式区分开,如{func()}

Spring 集成

ProcessEngineFactoryBean

在Spring 框架中使用activiti 引擎从ProcessEngineFactoryBean这个bean开始。这个bean实现了流程引擎的配置和创建。
spring的配置文件内容如下:

<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
    ...
</bean>

<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
  <property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>

现在ProcessEngineFactoryBean配置类已经改为org.activiti.spring.SpringProcessEngineConfiguration这个类了。

事务 Transactions

流程部署

在流程部署的时候,工作流会给流程定义版本号+1;对同一个流程文件多次部署,流程定义的版本号就会从当前版本号最大值加1后更新。启动流程实例的时候,就会使用版本号最大的流程定义去发起流程。

流程图 process diagram

BPMN2.0 介绍

一个简单的流程文件如下:

<definitions
  xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
  xmlns:activiti="http://activiti.org/bpmn"
  targetNamespace="Examples">

  <process id="myProcess" name="My First Process">
    ..
  </process>

</definitions>

根元素为definitions,其下可定义多个流程,为便于维护,一般定义一个流程。
process标签中的id元素就是我们使用runtimeService启动流程实例的时候指定的参数key:

ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myProcess");

而process标签中的name属性则不是必须的,只是显示看的。

参考文档
http://www.mossle.com/docs/activiti/index.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345