工作流引擎Activiti系列一——初识[通俗易懂]

大家好,又见面了,我是你们的朋友全栈君。

1、介绍

几乎任何一个公司的软件开发都会涉及到流程,以往我们可能是这么实现的:业务表添加标志位标识流程的节点状态,关联批注表实现审核意见,根据一些业务数据分析处理逻辑,分配任务到用户,节点的调度,审批等…..这其实是很繁琐的,且不说开发起来比较混乱,维护起来更是难上加难:

Activiti刚好就能解决几乎所有的这些问题,当流程开发变得简单有趣。

官网:https://www.activiti.org/

官方文档:https://www.activiti.org/userguide/

Activiti项目是一项新的基于Apache许可的开源BPM平台,从基础开始构建,旨在提供支持新的BPMN 2.0标准,包括支持对象管理组(OMG),面对新技术的机遇,诸如互操作性和云架构,提供技术实现。

作为开发者,使用Activiti带给我的直接好处:

  1. 天然支持Spring(Spring需要在配置文件中自己定义Activiti的Bean,Spring Boot则不需要)
  2. 流程定义通过画流程图实现(官方提供了相关工具,Eclipse也有插件支持),简单直观,易维护,易修改。
  3. 审批条件参数化,流程分支简单实现。
  4. 审批人可使用多种方式设置(支持用户、用户组、角色、候选组以及监听器动态设置),灵活,简单。
  5. 统一的审批接口,并不需要判断流程当前节点和走向。
  6. 提供强大的JPA查询,同时支持Name Query和Native Query。
  7. 流程数据与业务数据分离。

后续文章会一步步介绍Activiti的功能,主要使用基于Spring Boot的工程,也会提供单纯的Spring工程Demo。

2、示例

此处演示一个小示例,暂不解释代码,仅仅看看是怎样用activiti实现的。

场景就是请假,不过这里稍稍多了点内容,就是请假由请假人对应部门的领导审批,而不是统一的某一部分人。

2.1、使用Spring Boot工程

首先创建Spring boot工程,为了演示方便,使用内存数据库,完整pom文件如下:

<?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.anxpp</groupId>
<artifactId>ActivitiDemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>ActivitiDemo</name>
<description>ActivitiDemo</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.3.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.mybatis.spring.boot</groupId> -->
<!-- <artifactId>mybatis-spring-boot-starter</artifactId> -->
<!-- <version>1.1.1</version> -->
<!-- </dependency> -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>spring-boot-starter-basic</artifactId>
<version>5.17.0</version>
</dependency>
<!-- 内存数据库  -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<!-- <dependency> -->
<!-- <groupId>mysql</groupId> -->
<!-- <artifactId>mysql-connector-java</artifactId> -->
<!-- <scope>runtime</scope> -->
<!-- </dependency> -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<distributionManagement>
<repository>
<id>Releases</id>
<name>Nexus Release Repository</name>
<url>http://anxpp.com/nexus/content/repositories/releases/</url>
</repository>
<snapshotRepository>
<id>Snapshots</id>
<name>Nexus Snapshot Repository</name>
<url>http://anxpp.com/nexus/content/repositories/snapshots/</url>
</snapshotRepository>
</distributionManagement>
<repositories>
<repository>
<id>nexus</id>
<name>Nexus</name>
<url>http://anxpp.com/nexus/content/groups/public/</url>
<layout>default</layout>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
</project>

2.2、流程定义

这里使用eclipse的activiti designer插件,插件安装方法另见:Eclipse安装Activiti Designer插件

如我所愿,这里以请假流程,使用流程设计器得到如下流程定义:

这个流程相当简单。下面是定义的xml文件:

<?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: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" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
<process id="myProcess" name="My process" isExecutable="true">
<extensionElements>
<activiti:executionListener event="end" class="com.anxpp.demo.activiti.simple.listener.SimpleProcessEndListener"></activiti:executionListener>
</extensionElements>
<startEvent id="startevent_simple" name="Start"></startEvent>
<userTask id="usertask1" name="领导审批">
<extensionElements>
<activiti:taskListener event="create" class="com.anxpp.demo.activiti.simple.listener.LeaderCheckListener"></activiti:taskListener>
</extensionElements>
</userTask>
<endEvent id="endevent_simple" name="End"></endEvent>
<sequenceFlow id="flow_toCheck" sourceRef="startevent_simple" targetRef="usertask_leadercheck"></sequenceFlow>
<sequenceFlow id="flow_toEnd" sourceRef="usertask_leadercheck" targetRef="endevent_simple"></sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_myProcess">
<bpmndi:BPMNPlane bpmnElement="myProcess" id="BPMNPlane_myProcess">
<bpmndi:BPMNShape bpmnElement="startevent_simple" id="BPMNShape_startevent_simple">
<omgdc:Bounds height="35.0" width="35.0" x="170.0" y="290.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1">
<omgdc:Bounds height="55.0" width="105.0" x="290.0" y="280.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="endevent_simple" id="BPMNShape_endevent_simple">
<omgdc:Bounds height="35.0" width="35.0" x="500.0" y="290.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>

里面包含一个用户任务、一个流程监听器(流程结束后回写业务数据状态)、一个任务监听器(以为审批是由申请员工对应部门的领导审核的,使用监听器可以灵活的设置任务审批候选人)。

2.3、监听器

监听器分任务监听器和流程监听器。

任务监听器

/**
* 领导审核监听器
* @author anxpp.com
* 2016年12月24日 下午12:10:01
*/
public class LeaderCheckListener implements TaskListener{
private static final long serialVersionUID = 4285398130708457006L;
private final static Logger log = LoggerFactory.getLogger(LeaderCheckListener.class);
@Override
public void notify(DelegateTask task) {
log.info("领导审核监听器...");
//设置任务处理候选人
UserService userService = SpringUtil.getBean(UserService.class);
List<String> leaders = userService.getSimpleCheckerByDept(Long.valueOf(task.getVariable("dept").toString()));
log.info(leaders.toString());
log.info(task.getVariable("dept").toString());
task.addCandidateUsers(leaders);
}
}

流程监听器(此处并无实际代码,只给写法):

/**
* 流程监听器
* @author anxpp.com
* 2016年12月24日 下午12:33:58
*/
public class SimpleProcessEndListener implements ExecutionListener{
private static final long serialVersionUID = 5212042435691138021L;
private final static Logger log = LoggerFactory.getLogger(SimpleProcessEndListener.class);
@Override
public void notify(DelegateExecution arg0) throws Exception {
log.info("流程结束监听器...");
//TODO 修改业务数据状态
}
}

单元测试

package com.anxpp.demo.activiti;
import java.util.Iterator;
import java.util.List;
import org.activiti.engine.task.Task;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.anxpp.demo.activiti.core.entity.User;
import com.anxpp.demo.activiti.core.service.UserService;
import com.anxpp.demo.activiti.simple.Config.Constant;
import com.anxpp.demo.activiti.simple.core.entity.ApplySimple;
import com.anxpp.demo.activiti.simple.core.service.ApplySimpleService;
@RunWith(SpringRunner.class)
@SpringBootTest
public class ActivitiDemoApplicationTests {
private final static Logger log = LoggerFactory.getLogger(ActivitiDemoApplicationTests.class);
private static Long DEPT_TINY_SOFTWARE_STUDIO = 1L;
private static Long DEPT_OTHER = 2L;
@Autowired
UserService userService;
@Autowired
ApplySimpleService simpleService;
/**
* 测试程序启动
*/
@Test
public void contextLoads() {
}
/**
* 测试数据库操作
*/
@Test
public void testCURD(){
Long countUser = userService.countUser();
User user = new User();
user.setName("anxpp0");
user.setDept(DEPT_TINY_SOFTWARE_STUDIO);
userService.save(user);
Assert.assertEquals(new Long(countUser+1), userService.countUser());
}
/**
* 测试简单流程
*/
@Test
public void testSimpleActiviti(){
long startAt = System.currentTimeMillis();
//添加申请用户
User user = new User();
user.setName("anxpp");
user.setDept(DEPT_TINY_SOFTWARE_STUDIO);
user.setPosition(Constant.POSITION_GENERAL);
userService.save(user);
//添加审核用户
User userLeader1 = new User();
userLeader1.setName("anxpp1");
userLeader1.setDept(DEPT_TINY_SOFTWARE_STUDIO);
userLeader1.setPosition(Constant.POSITION_LEADER);
userService.save(userLeader1);
User userLeader2 = new User();
userLeader2.setName("anxpp2");
userLeader2.setDept(DEPT_OTHER);
userLeader2.setPosition(Constant.POSITION_LEADER);
userService.save(userLeader2);
Long countHis = simpleService.countProcess();
Long countLeader1Task = simpleService.countTask(userLeader1.getId());
Long countLeader2Task = simpleService.countTask(userLeader2.getId());
//创建请假申请
ApplySimple applySimple = new ApplySimple();
applySimple.setInsertBy(user.getId());
applySimple.setComtent("有事请假...");
//启动请假流程
simpleService.startProcess(applySimple);
/**断言历史流程+1*/
Assert.assertEquals(simpleService.countProcess(), new Long(countHis+1));
/**断言领导任务变化*/
Assert.assertEquals(simpleService.countTask(userLeader1.getId()), new Long(countLeader1Task+1));
Assert.assertEquals(countLeader2Task, simpleService.countTask(userLeader2.getId()));
//获取用户任务
List<Task> taskUserLeader1 = simpleService.getTaskByUid(userLeader1.getId());
//处理任务
Iterator<Task> iterator = taskUserLeader1.iterator();
while(iterator.hasNext()){
Task task = iterator.next();
/**断言任务节点名称*/
Assert.assertEquals("领导审批", task.getName());
simpleService.completeSimpleCheck(task.getId(), ApplySimpleService.STATE_PASS);
}
/**断言领导任务变化*/
Assert.assertEquals(countLeader1Task, simpleService.countTask(userLeader1.getId()));
Assert.assertEquals(countLeader2Task, simpleService.countTask(userLeader2.getId()));
System.err.println("asdf");
log.info("测试完成,花费时间:"+(System.currentTimeMillis()-startAt));
}
}

运行单元测试,OK!通过!

完整实例源码地址:https://github.com/anxpp/activitiSimpleDemo.git

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/215504.html原文链接:https://javaforall.cn

相关文章

学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习...
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面...
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生...
Can’t connect to local MySQL server through socket \'/v...
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 ...
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服...