Activiti 基础知识

1、基本概念 工作流:业务过程的部分或整体在计算机应用环境下的自动化 工作流管理系统:工作流的定义和管理,按照在系统中预定义好的工作流规则进行工作流实例的执行。 工作流管理系统的目标:管理工作流程以确保工作在正确的时间被期望的人员执行–在自动化进行的业务过程中插入人工的执行和干预。 Activiy是什么 Activiti是一个针对企业用户、开发人员、系统管理员的轻量级工作流业务管理平台,其核心是使用Java开发的快速、稳定的BPMN2.0流程引擎。 Activiti的特点 数据持久化,底层使用MyBatis 引擎Service接口 流程设计器 原生支持Spring 分离运行时与历史数据

什么是 Activiti7

Actviti7 简介

简介

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

创始人Tom Baeyens是JBoss jBPM的项目架构师,以及另一位架构师Joram Barrez,一起加入到创建Alfresco这项首次实现Apache开源许可的BPMN 2.0引擎开发中来。

Activiti是一个独立运作和经营的开源项目品牌,并将独立于Alfresco开源ECM系统运行。 Activiti将是一种轻量级,可嵌入的BPM引擎,而且还设计适用于可扩展的云架构。 Activiti将提供宽松的Apache许可2.0,以便这个项目可以广泛被使用,同时促进Activiti BPM引擎和BPMN 2.0的匹配,该项目现正由OMG通过标准审定。 加入Alfresco Activiti项目的是VMware的SpringSource分支,Alfresco的计划把该项目提交给Apache基础架构,希望吸引更多方面的BPM专家和促进BPM的创新。

特色

架构师Tom Baeyens说:“ Activiti有非常大的影响力来改变目前BPM的生态。Activiti的Apache授权,完整的功能,将使Activiti到达一个新的水平。Activiti将推动业界的创新,因为BPM技术可以广泛而自由地被应用。通过实现这些想法以及开源社区的努力,也让Activiti成为事实上的 BPM和BPMN标准执行“。

SpringSource的首席技术官Adrian Coyler说道:”这是一个对Spring开发人员和Java社区总体的发展非常令人兴奋的事情,长期以来一直需要一个Apache许可的流程引擎,这对许多应用系统非常实用的需求。我们认为,Activiti作为新的应用领域扩展到的Java和开源的发展,特别是在云架构上”。

Alfresco软件公司的首席技术官John Newton表示"我们发起这个项目,使内容和过程技术的使用可以更广泛和普及,我们这样做是因为,像其他开源项目,我们需要一个更宽松授权的流程引擎。我们相信,这可以改变业务流程处理领域,就像Alfresco公司已经为企业内容管理ECM领域所做的那样。”

Activiti将成为Alfresco的默认的业务流程引擎,Alfresco公司将继续支持jBPM,以及目前与其他业务流程的企业内容管理软件集成的引擎。 Alfresco公司也将与Alfresco企业版一起,提供对Activiti的支持,维护和技术保证。

1.工作流:工作的一个流程,事物发展的一个业务过程

流程:
请假流程:员工申请----部门经理-----总经理-----人事存档
传统方式下? 请假条的传递来实现
无纸化办公? 线上申请----线上审批----一条请假记录
 在计算机的帮助下,能够实现流程的自动化控制,就称为工作流.

2.工作流引擎

 为了实现自动化控制,Acitviti引擎就产生了。

作用:实现流程自动化控制

3.工作流系统:具有工作流的系统。

       如果一个系统具备流程的自动化管理功能,这个系统就可以称为工作流系统

工作流系统,有哪些手段可以实现?

      工作流系统,如何来实现流程的自动化管理?
      流程自动化管理:程序员编码来实现
      请假:员工申请----部门经理-----总经理-----人事存档
       1,工号,部门号,姓名,日期,天数,原因,状态3


       员工:0未提交1提交
       部门经理:部门号=部门经理的部门编号相同,状态=1
             2同意  3不同意   
       
       总经理    状态=2
             4同意  5不同意


       人事存档  状态=4
             6同意   7不同意  

       问题:业务流程变更后,程序不能使用
             以不变应万变

如何解决,以不变应万变?

    -----Activiti就可以实现业务流程变化后,程序代码不需要改动。

使用场景,及相关业务.

SaaS-人力资源管理系统 行政审批(调薪)

为什么Activiti就可以解决业务需求变更时,源代码不需要更新,更新的是业务流程图?

原理?

图片附件

holiday.png

Activiti 框架原理

本文基于一个简单的Demo流程介绍了Activiti框架启动、部署、运行过程。

Demo准备

流程图文件:

<?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" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
   <process id="hello" name="hello" isExecutable="true">
        <!-- 流程开始节点 -->
        <startEvent id="start" name="Start" ></startEvent>
        <!-- serviceTask:执行me.likeyao.activiti.demo.HelloWorld的execute方法,打印hello world -->
        <serviceTask id="helloworld" name="helloworld" activiti:class="me.likeyao.activiti.demo.HelloWorld"/>
        <!-- 流程结束节点 -->
        <endEvent id="end" name="End"></endEvent>
        <!-- 流程迁移线:开始节点到serviceTask节点 -->
        <sequenceFlow id="sid-1" sourceRef="start" targetRef="helloworld"></sequenceFlow>
        <!-- 流程迁移线:serviceTask节点到结束节点 -->
        <sequenceFlow id="sid-3" sourceRef="helloworld" targetRef="end"></sequenceFlow>
    </process>
</definitions>

流程图: 2242271-c5438bc5dc6b468a.png demo流程

代码:

public class App {
    public static void main(String[] args) {
        //创建流程引擎
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //部署流程图
        processEngine.getRepositoryService().createDeployment().addClasspathResource("hello.bpmn20.xml").deploy();
        //发起流程
        processEngine.getRuntimeService().startProcessInstanceByKey("hello");
    }
}


public class HelloWorld implements JavaDelegate{
    public void execute(DelegateExecution execution) throws Exception {
        System.out.println("Hello world!");
    }
}

Demo实现的功能是发起一个流程,执行到流程的serviceTask节点时,打印Hello world!,然后流程结束。

源码版本:5.22.0

框架初始化2242271-4becaf2056d3c51f.webp

ProcessEngine类图

ProcessEngine

ProcessEngine是Activiti框架的门面,ProcessEngine本身不提供任何功能,通过getXXXService方法可以获取到对应的Service对象执行操作。Demo中涉及到的两个Service:

ProcessEngineConfiguration

ProcessEngineConfiguration负责Activiti框架的属性配置、初始化工作,初始化入口是buildProcessEngine方法,所有Activiti框架运行时需要用到的组件基本都在这里初始化:

public ProcessEngine buildProcessEngine() {
    init();
    return new ProcessEngineImpl(this);
}

protected void init() {
    initConfigurators();
    configuratorsBeforeInit();
    initProcessDiagramGenerator();
    initHistoryLevel();
    initExpressionManager();
    initDataSource();
    initVariableTypes();
    initBeans();
    initFormEngines();
    initFormTypes();
    initScriptingEngines();
    initClock();
    initBusinessCalendarManager();
    initCommandContextFactory();
    initTransactionContextFactory();
    initCommandExecutors();
    initServices();
    initIdGenerator();
    initDeployers();
    initJobHandlers();
    initJobExecutor();
    initAsyncExecutor();
    initTransactionFactory();
    initSqlSessionFactory();
    initSessionFactories();
    initJpa();
    initDelegateInterceptor();
    initEventHandlers();
    initFailedJobCommandFactory();
    initEventDispatcher();
    initProcessValidator();
    initDatabaseEventLogging();
    configuratorsAfterInit();
}

这里有一个扩展点:ProcessEngineConfigurator。

public interface ProcessEngineConfigurator {
    //组件初始化前
    void beforeInit(ProcessEngineConfigurationImpl processEngineConfiguration);
    //组件初始化后
    void configure(ProcessEngineConfigurationImpl processEngineConfiguration);
    //优先级
    int getPriority();
}

在init初始化方法中,initConfigurators方法通过ServiceLoader加载ProcessEngineConfigurator。随后在configuratorsBeforeInit和configuratorsAfterInit方法中分别调用ProcessEngineConfigurator的beforeInit和configure方法,使用户可以在ProcessEngineConfiguration初始化前后编程式的修改属性,替换Activiti默认组件。

流程部署

流程部署实现的功能是将xml格式的流程图,转化为Activiti框架运行时依赖的流程定义对象。

RepositoryService

2242271-bc6f111998288acc.webp RepositoryService类图

Demo中通过以下代码部署了一个流程:

processEngine.getRepositoryService().createDeployment().addClasspathResource("hello.bpmn20.xml").deploy();

createDeployment方法中创建了DeploymentBuilder对象,DeploymentBuilder对象负责读取指定路径的流程图xml文件的内容(byte数组),并缓存在DeploymentEntity对象中:

public DeploymentBuilder addInputStream(String resourceName, InputStream inputStream) {
    ...
    byte[] bytes = IoUtil.readInputStream(inputStream, resourceName);
    ResourceEntity resource = new ResourceEntity();
    resource.setName(resourceName);
    resource.setBytes(bytes);
    deployment.addResource(resource);
    return this;
}

最终DeploymentBuilder的deploy方法会调用RepositoryService的deploy方法,完成流程部署:

public Deployment deploy() {
    return repositoryService.deploy(this);
}

CommandExecutor

在RepositoryService的deploy方法中,使用了CommandExecutor对象:

public Deployment deploy(DeploymentBuilderImpl deploymentBuilder) {
    return commandExecutor.execute(new DeployCmd<Deployment>(deploymentBuilder));
}

2242271-e6a84ab68b549bf4.webp CommandExecutor类图

在Activiti中,大部分操作都以Command模式实现,例如部署流程图的DeployCmd。CommandExecutor封装了一系列的CommandInterceptor,在内部形成CommandInterceptor链,在命令执行前后做了拦截。Activiti框架提供了一些

CommandInterceptor实现:

名称 作用
CommandContextInterceptor 用于生成命令执行的上下文(CommandContext)。
LogInterceptor 开启日志Debug级别后,打印日志。
JtaTransactionInterceptor 开启Jta事务

引入activiti-spring包,通过SpringTransactionInterceptor引入Spring的事务支持。

CommandExecutor在ProcessEngineConfigurationImpl的initCommandExecutors方法中初始化:

protected void initCommandExecutors() {
    initDefaultCommandConfig();
    initSchemaCommandConfig();
    initCommandInvoker();
    initCommandInterceptors();
    initCommandExecutor();
}

可以设置ProcessEngineConfigurationImpl的customPreCommandInterceptors和customPostCommandInterceptors属性,添加自定义的CommandInterceptor:

protected void initCommandInterceptors() {
    if (commandInterceptors==null) {
        commandInterceptors = new ArrayList<CommandInterceptor>();
        if (customPreCommandInterceptors!=null) {
            commandInterceptors.addAll(customPreCommandInterceptors);
        }
        commandInterceptors.addAll(getDefaultCommandInterceptors());
        if (customPostCommandInterceptors!=null) {
            commandInterceptors.addAll(customPostCommandInterceptors);
        }
        commandInterceptors.add(commandInvoker);
    }
}

这里的pre和post是指Activiti框架getDefaultCommandInterceptors()的前后。

CommandInvoker是CommandInterceptor链的最后一个对象,负责调用Command:

public class CommandInvoker extends AbstractCommandInterceptor {
    @Override
    public <T> T execute(CommandConfig config, Command<T> command) {
        return command.execute(Context.getCommandContext());
    }
}

CommandContext

2242271-dbf9c4a66c79d3c9.webp CommandContext类图

CommandContext是Activit框架Command执行的上下文,主要包含各种SessionFactory:

sessionFactories = processEngineConfiguration.getSessionFactories();

SessionFactory负责生成Session,Session是Activiti操作持久化对象的统一接口:

名称 作用
ProcessDefinitionEntityManager 流程定义相关读写操作。
ExecutionEntityManager 流程实例相关读写操作。
DefaultHistoryManager 历史记录相关读写操作

CommandContext的生命周期

CommandConext在CommandContextInterceptor中创建,在finally代码块中销毁:

public <T> T execute(CommandConfig config, Command<T> command) {
    //首先尝试从线程上下文的栈中获取CommandContext
    CommandContext context = Context.getCommandContext();
    boolean contextReused = false;
    //什么时候创建新的CommandContext?
    //1、CommandConfig中指定了不复用CommandContext
    //2、当前线程上下文中不存在CommandConext
    //3、当前线程上下文中的CommandConext已经抛出异常
    if (!config.isContextReusePossible() || context == null || context.getException() != null) { 
        context = commandContextFactory.createCommandContext(command);      }  
    else {
        contextReused = true;
    }

    try {
        //将前面获取到的CommandContext入栈
        Context.setCommandContext(context);
        Context.setProcessEngineConfiguration(processEngineConfiguration);
        //执行下一个interceptor,在CommandInvoker中可以通过Context.getCommandContext()获取线程上下文中的CommandContext
        return next.execute(config, command);
    } catch (Exception e) {
        //记录异常信息
        context.exception(e);
    } finally {
        try {
            //如果CommandContext不可复用,用完直接关闭
            if (!contextReused) {
                context.close();
            }
        } finally {
            //出栈操作
            Context.removeCommandContext();
            Context.removeProcessEngineConfiguration();
            Context.removeBpmnOverrideContext();
        }
    }
    
    return null;
}

Activiti的框架可以在一个Command的执行过程中,调用另外一个Command,所以会出现是否需要复用CommandContext的选项,默认值为true。

流程的解析

在DeployCmd中,首先调用DeploymentEntityManager持久化存储DeploymentEntity对象:

commandContext.getDeploymentEntityManager().insertDeployment(deployment);

然后调用DeploymentManager部署流程(流程解析):

commandContext.getProcessEngineConfiguration().getDeploymentManager().deploy(deployment, deploymentSettings);

DeploymentEntityManager

2242271-eb4810b9b4a64a04.webp DeploymentEntityManager类图

DeploymentEntityManager的deploy方法中循环调用Deployer对象的deploy方法,Activiti默认的Deployer是BpmnDeployer。

另外DeploymentEntityManager中还缓存了解析好的流程定义对象和Bpmn模型对象。

Activiti持久化的是流程图xml文件,每次系统重新启动都要执行一次“deploy”操作,生成ProcessDefinitionEntity对象。

BpmnDeployer

2242271-68d44c88944fc225.webp BpmnDeployer类图

BpmnDeployer的deploy方法中包含几个操作(代码缩略版):

public void deploy(DeploymentEntity deployment, Map<String, Object> deploymentSettings) {
    ...
    BpmnParse bpmnParse = bpmnParser.createParse().sourceInputStream(inputStream).setSourceSystemId(resourceName).deployment(deployment).name(resourceName);
    bpmnParse.execute();
    for (ProcessDefinitionEntity processDefinition: bpmnParse.getProcessDefinitions()) {
        if (deployment.isNew()) {
            ProcessDefinitionEntity latestProcessDefinition = ...
            if (latestProcessDefinition != null) {
                processDefinitionVersion = latestProcessDefinition.getVersion() + 1;
            }else{
                processDefinitionVersion = 1;
            }
            processDefinition.setId(idGenerator.getNextId());
            dbSqlSession.insert(processDefinition);
        }
        ...
    }
}

BpmnParse

2242271-c0d8c16d9fbf42fc.webp BpmnParse类图

在BpmnParse的execute中完成了xml文件到ProcessDefinitionEntity对象的转化:

public BpmnParse execute() {
    //xml->bpmnModel
    bpmnModel = converter.convertToBpmnModel(streamSource, validateSchema, enableSafeBpmnXml, encoding);
    //bpmnModel-> ProcessDefinitionEntity
    transformProcessDefinitions();
}

protected void transformProcessDefinitions() {
    for (Process process : bpmnModel.getProcesses()) {
        bpmnParserHandlers.parseElement(this, process);
    }
}

在流程定义解析过程中,会涉及到两套模型:

Bpmn模型

2242271-0d7054f46acfb7dd.webp Bpmn模型

PVM模型

2242271-ce5026e930258c97.webp PVM模型

Bpmn模型更偏向于xml节点的描述,PVM模型是运行时模型。Bpmn模型中的ServiceTask、StartEvent等会统一映射转换为PVM的ActivityImpl对象,ServiceTask和StartEvent等节点行为上的差别,体现在ActivityImpl对象持有的不同的ActivityBehavior上。

运行流程

创建流程实例

在demo中通过RuntimeService发起流程实例:

processEngine.getRuntimeService().startProcessInstanceByKey("hello");

在startProcessInstanceByKey方法中执行StartProcessInstanceCmd命令:

public class StartProcessInstanceCmd<T> implements Command<ProcessInstance>, Serializable {
    ...
    public ProcessInstance execute(CommandContext commandContext) {
        //获取流程定义
        ProcessDefinitionEntity processDefinition = ...
        //创建流程实例
        ExecutionEntity processInstance = processDefinition.createProcessInstance(businessKey);
        //开始流程
        processInstance.start();
        return processInstance;
    }
    ...
}

在StartProcessInstanceCmd方中通过流程定义ProcessDefinitionEntity创建了流程实例 ExecutionEntity:

2242271-5a8bc87dc9fd0f46.webp ExecutionEntity

ExecutionEntity实现了一些重要接口:

在ExecutionEntity中维护类一个属性:activity。activity属性代表当前执行到哪个节点,在创建ExecutionEntity过程中会设置activity,使流程从某一个节点开始,默认是开始节点。

最后StartProcessInstanceCmd还调用ExecutionEntity的start方法开始驱动流程:

public void start() {
    performOperation(AtomicOperation.PROCESS_START);
}

驱动流程

Activiti框架的流程运行于PVM模型之上,在流程运行时主要涉及到PVM中几个对象:ActivityImpl、TransitionImpl和ActivityBehavior。

ActivityImpl、TransitionImpl和ActivityBehavior只是描述了流程的节点、迁移线和节点行为,真正要让ExecutionEntity流转起来,还需要AtomicOperation的驱动:

AtomicOperation PROCESS_START = new AtomicOperationProcessStart();
AtomicOperation PROCESS_START_INITIAL = new AtomicOperationProcessStartInitial();
AtomicOperation PROCESS_END = new AtomicOperationProcessEnd();
AtomicOperation ACTIVITY_START = new AtomicOperationActivityStart();
AtomicOperation ACTIVITY_EXECUTE = new AtomicOperationActivityExecute();
AtomicOperation ACTIVITY_END = new AtomicOperationActivityEnd();
AtomicOperation TRANSITION_NOTIFY_LISTENER_END = new AtomicOperationTransitionNotifyListenerEnd();
AtomicOperation TRANSITION_DESTROY_SCOPE = new AtomicOperationTransitionDestroyScope();
AtomicOperation TRANSITION_NOTIFY_LISTENER_TAKE = new AtomicOperationTransitionNotifyListenerTake();
AtomicOperation TRANSITION_CREATE_SCOPE = new AtomicOperationTransitionCreateScope();
AtomicOperation TRANSITION_NOTIFY_LISTENER_START = new AtomicOperationTransitionNotifyListenerStart();
    
AtomicOperation DELETE_CASCADE = new AtomicOperationDeleteCascade();
AtomicOperation DELETE_CASCADE_FIRE_ACTIVITY_END = new AtomicOperationDeleteCascadeFireActivityEnd();

在ExecutionEntity的start方法中,调用了PROCESS_START,PROCESS_START做了几件事:

PROCESS_START_INITIAL也实现了类似的功能:

在Demo流程执行中涉及的AtomicOperation的链路主要包括:

以Demo流程中的ServiceTask节点helloworld为例,在执行ACTIVITY_EXECUTE时,会获取activity关联的behavior:

public class AtomicOperationActivityExecute implements AtomicOperation {
    public void execute(InterpretableExecution execution) {
        ...
        ActivityImpl activity = (ActivityImpl) execution.getActivity();
        ActivityBehavior activityBehavior = activity.getActivityBehavior();
        activityBehavior.execute(execution);
        ...
    }
}

ServiceTask解析时关联的是ServiceTaskJavaDelegateActivityBehavior,execution方法:

public void execute(ActivityExecution execution) throws Exception {
    //execution中调用了me.likeyao.activiti.demo.HelloWorld
    execute((DelegateExecution) execution);
    //离开当前节点
    leave(execution);
}

在leave方法中调用了:

bpmnActivityBehavior.performDefaultOutgoingBehavior(execution);

performDefaultOutgoingBehavior方法会在当前activity的 出线中选择一条,使流程流向下一个节点。在Demo中只有一条线存在:

protected void performOutgoingBehavior(ActivityExecution execution, 
          boolean checkConditions, boolean throwExceptionIfExecutionStuck, List<ActivityExecution> reusableExecutions) {

    if (transitionsToTake.size() == 1) {
        execution.take(transitionsToTake.get(0));
    }         
          
}

最终take方法会将流程驱动权交还到AtomicOperation中:

public class ExecutionEntity{
    ...
    public void take(PvmTransition transition, boolean fireActivityCompletionEvent) {
        ...
        setActivity((ActivityImpl)transition.getSource());
        setTransition((TransitionImpl) transition);
        performOperation(AtomicOperation.TRANSITION_NOTIFY_LISTENER_END);
        ...
    }
    ...
}

AtomicOperation的问题

按照AtomicOperation的驱动模式,只有当遇到UserTask等需要等待single信号的节点,调用才会返回。这意味着当调用RuntimeService启动一个流程实例时,要一直等到流程运行到一个UserTask节点调用才会返回,如果流程比较长耗时非常验证。

另一个问题是当流程图比较复杂,ExecutionListener数量比较多时,AtomicOperation之间的互相调用会导致调用栈非常深。

AtomicOperation驱动模式与ExecutionEntity、Behavior等绑定的比较紧密,暂时没有特别好的办法替换掉。

小结

本文主要介绍了Activiti框架的启动、部署、运行的主链路,并没有深入BPMN规范和Activit功能的具体实现,后续打算根据Activiti的用户手册,详细分析每个功能的使用和实现。

Activiti 核心 API


Activiti 核心 API

7大接口

核心API

.1: ProcessEngine

说明:

  1. 在Activiti中最核心的类,其他的类都是由他而来。

  2. 产生方式:

在前面看到了两种创建ProcessEngine(流程引擎)的方式,而这里要简化很多,调用ProcessEngines的getDefaultProceeEngine方法时会自动加载classpath下名为activiti.cfg.xml文件。

  1. 可以产生RepositoryService

  2. 可以产生RuntimeService

  3. 可以产生TaskService

各个Service的作用:

RepositoryService 管理流程定义
RuntimeService 执行管理,包括启动、推进、删除流程实例等操作
TaskService 任务管理
HistoryService 历史管理(执行完的数据的管理)
IdentityService 组织机构管理
FormService 一个可选服务,任务表单管理
ManagerService 使用Activiti的定制环境中基本上不会用到。 它可以查询数据库的表和表的元数据。另外,它提供了查询和管理异步操作的功能。

.2:RepositoryService

是Activiti的仓库服务类。所谓的仓库指流程定义文档的两个文件:bpmn文件和流程图片。

  1. 产生方式

  2. 可以产生DeploymentBuilder,用来定义流程部署的相关参数

  3. 删除流程定义

.3:RuntimeService

是activiti的流程执行服务类。可以从这个服务类中获取很多关于流程执行相关的信息。

.4:TaskService

是activiti的任务服务类。可以从这个类中获取任务的信息。

.5:HistoryService

是activiti的查询历史信息的类。在一个流程执行完成后,这个对象为我们提供查询历史信息。

.6:ProcessDefinition

流程定义类。可以从这里获得资源文件等。

.7:ProcessInstance

代表流程定义的执行实例。如范冰冰请了一天的假,她就必须发出一个流程实例的申请。一个流程实例包括了所有的运行节点。我们可以利用这个对象来了解当前流程实例的进度等信息。流程实例就表示一个流程从开始到结束的最大的流程分支,即一个流程中流程实例只有一个。

.8:Execution

Activiti用这个对象去描述流程执行的每一个节点。在没有并发的情况下,Execution就是同ProcessInstance。流程按照流程定义的规则执行一次的过程,就可以表示执行对象Execution。

如图为ProcessInstance的源代码:

从源代码中可以看出ProcessInstance就是Execution。但在现实意义上有所区别:

在单线流程中,如上图的贷款流程,ProcessInstance与Execution是一致的。

这个例子有一个特点:wire money(汇钱)和archive(存档)是并发执行的。 这个时候,总线路代表ProcessInstance,而分线路中每个活动代表Execution。

总结:

Activiti 用户手册

Activiti 5.16 用户手册

官方用户手册

Activiti 数据库表结构

——文档适用于 Activiti 5-6


Activiti 数据库表结构设计说明

Activiti 工作流总共包含 23 张数据表(现在是25张,新增了 ACT_EVT_LOGACT_PROCDEF_INFO

Activiti 7 的数据库表结构设计

# Activiti 7 的数据库表结构设计
ACT_EVT_LOG, ACT_GE_BYTEARRAY, ACT_GE_PROPERTY, ACT_HI_ACTINST, ACT_HI_ATTACHMENT, ACT_HI_COMMENT, ACT_HI_DETAIL, ACT_HI_IDENTITYLINK, ACT_HI_PROCINST, ACT_HI_TASKINST, ACT_HI_VARINST, ACT_PROCDEF_INFO, ACT_RE_DEPLOYMENT, ACT_RE_MODEL, ACT_RE_PROCDEF, ACT_RU_DEADLETTER_JOB, ACT_RU_EVENT_SUBSCR, ACT_RU_EXECUTION, ACT_RU_IDENTITYLINK, ACT_RU_INTEGRATION, ACT_RU_JOB, ACT_RU_SUSPENDED_JOB, ACT_RU_TASK, ACT_RU_TIMER_JOB, ACT_RU_VARIABLE

Activiti 各大版本表对比

以下只是对表名做了简单的对比,并没有涉及到数据库表字段及数据类型

编号 表名 Activiti 5.23.0 Acticiti 6.0.0 Activiti 7.1.0.M6
1 ACT_EVT_LOG
2 ACT_GE_BYTEARRAY
3 ACT_GE_PROPERTY
4 ACT_HI_ACTINST
5 ACT_HI_ATTACHMENT
6 ACT_HI_COMMENT
7 ACT_HI_DETAIL
8 ACT_HI_IDENTITYLINK
9 ACT_HI_PROCINST
10 ACT_HI_TASKINST
11 ACT_HI_VARINST
12 ACT_ID_GROUP
13 ACT_ID_INFO
14 ACT_ID_MEMBERSHIP
15 ACT_ID_USER
16 ACT_PROCDEF_INFO
17 ACT_RE_DEPLOYMENT
18 ACT_RE_MODEL
19 ACT_RE_PROCDEF
20 ACT_RU_DEADLETTER_JOB
21 ACT_RU_EVENT_SUBSCR
22 ACT_RU_EXECUTION
23 ACT_RU_IDENTITYLINK
24 ACT_RU_INTEGRATION
25 ACT_RU_JOB
26 ACT_RU_SUSPENDED_JOB
27 ACT_RU_TASK
28 ACT_RU_TIMER_JOB
29 ACT_RU_VARIABLE

表名规则

Activiti 使用到的表都是 ACT_ 开头的。表名的第二部分用两个字母表明表的用途。

25 张表详情

一般数据 (ACT_GE_)


通用数据表

表名 解释
ACT_GE_BYTEARRAY 二进制数据表,存储通用的流程定义和流程资源。
ACT_GE_PROPERTY 系统相关属性,属性数据表存储整个流程引擎级别的数据,初始化表结构时,会默认插入三条记录。

流程历史记录 (ACT_HI_)

表名 解释
ACT_HI_ACTINST 历史节点表
ACT_HI_ATTACHMENT 历史附件表
ACT_HI_COMMENT 历史意见表
ACT_HI_DETAIL 历史详情表,提供历史变量的查询
ACT_HI_IDENTITYLINK 历史流程人员表
ACT_HI_PROCINST 历史流程实例表
ACT_HI_TASKINST 历史任务实例表
ACT_HI_VARINST 历史变量表

用户用户组表 (ACT_ID_)


身份数据表

表名 解释
ACT_ID_GROUP 用户组信息表
ACT_ID_INFO 用户扩展信息表
ACT_ID_MEMBERSHIP 用户与用户组对应信息表
ACT_ID_USER 用户信息表

流程定义表 (ACT_RE_)


流程存储数据表

表名 解释
ACT_RE_DEPLOYMENT 部署信息表
ACT_RE_MODEL 流程设计模型部署表
ACT_RE_PROCDEF 流程定义数据表

运行实例表 (ACT_RU_)

表名 解释
ACT_RU_EVENT_SUBSCR 运行时事件 throwEvent、catchEvent 时间监听信息表
ACT_RU_EXECUTION 运行时流程执行实例
ACT_RU_IDENTITYLINK 运行时流程人员表,主要存储任务节点与参与者的相关信息
ACT_RU_JOB 运行时定时任务数据表
ACT_RU_TASK 运行时任务节点表
ACT_RU_VARIABLE 运行时流程变量数据表

其它

表名 解释
ACT_EVT_LOG 事件日志
ACT_PROCDEF_INFO 流程定义的动态变更信息

一般数据 (ACT_GE_)

ACT_GE_BYTEARRAY 二进制数据表

二进制数据表,存储通用的流程定义和流程资源。(act_ge_bytearray)

保存流程定义图片和xml、Serializable(序列化)的变量,即保存所有二进制数据,特别注意类路径部署时候,不要把svn等隐藏文件或者其他与流程无关的文件也一起部署到该表中,会造成一些错误(可能导致流程定义无法删除)

用于保存与流程引擎相关的资源,只要调用了 Activiti 存储服务的 API ,涉及的资源均会被转换为 byte 数组保存到这个表中。在资源表中设计了一个 BYTES宇段,用来保存资源的内容,因此理论上其可以用于保存任何类型的资源(文件或者其他来源的输入流)。一般情况下, Activiti 使用这个表来保存字符串、流程文件的内容、流程图片内容

字段名称 字段描述 数据类型 主键 为空 取值说明
ID_ ID_ nvarchar(64) Y 主键ID
REV_ 乐观锁 int Y Version(版本)
NAME_ 名称 nvarchar(255) Y 部署的文件名称,如:leave.bpmn.png,leave.bpmn20.xml
DEPLOYMENT_ID_ 部署ID nvarchar(64) Y 部署表ID
BYTES_ 字节 varbinary(max) Y 部署文件
GENERATED_ 是否是引擎生成 tinyint Y 0为用户生成,1为activiti生成
字段 类型 NULL 备注
ID_ varchar(64) NO
REV_ int(11) YES 数据版本, Activiti为一些有可能会被频繁修改的数据表,加入该字段,用来表示该数据被操作的次数
NAME_ varchar(255) YES 资源名称
DEPLOYMENT_ID_ varchar(64) YES 一次部署可以添加多个资源,该字段与部署表ACT_RE_DEPLOYMENT的主键相关联
BYTES_ longblob YES 资源内容,最大可存储4G数据
GENERATED_ tinyint(4) YES 是否由Activiti自动产生的资源,0

ACT_GE_PROPERTY 属性数据表

属性数据表(act_ge_property)

属性数据表。存储整个流程引擎级别的数据。

Activiti 将全部的属性抽象为 key-value 对,每个属性都有名称和值, 使用

ACT GE PROPERTY 来保存这些属性

字段名称 字段描述 数据类型 主键 为空 取值说明
NAME_ 名称 nvarchar(64) schema.versionschema.historynext.dbid
VALUE_ nvarchar(300) 5.create(5.)
REV_ 乐观锁 int version
字段 类型 NULL 备注
NAME_ varchar(64) NO 名称
VALUE_ varchar(300) YES
REV_ int(11) YES 数据的版本号

流程历史记录 (ACT_HI_)

历史数据表就好像流程引擎的日志表,操作过的流程元素将会被记录到历史表中。

ACT_HI_ACTINST 历史节点表

历史节点表(act_hi_actinst)

历史活动信息。这里记录流程流转过的所有节点,与HI_TASKINST不同的是,taskinst只记录usertask内容

字段名称 字段描述 数据类型 主键 为空 取值说明
ID_ ID_ nvarchar(64)
PROC_DEF_ID_ 流程定义ID nvarchar(64)
PROC_INST_ID_ 流程实例ID nvarchar(64)
EXECUTION_ID_ 执行实例ID nvarchar(64)
ACT_ID_ 节点ID nvarchar(225) 节点定义ID
TASK_ID_ 任务实例ID nvarchar(64) 任务实例ID 其他节点类型实例ID在这里为空
CALL_PROC_INST_ID_ 调用外部的流程实例ID nvarchar(64) 调用外部流程的流程实例ID’
ACT_NAME_ 节点名称 nvarchar(225) 节点定义名称
ACT_TYPE_ 节点类型 nvarchar(225) 如startEvent、userTask
ASSIGNEE_ 签收人 nvarchar(64) 节点签收人
START_TIME_ 开始时间 datetime 2013-09-15 11:30:00
END_TIME_ 结束时间 datetime 2013-09-15 11:30:00
DURATION_ 耗时 numeric(19,0) 毫秒值
字段 类型 NULL 备注
ID_ varchar(64) NO
PROC_DEF_ID_ varchar(64) NO
PROC_INST_ID_ varchar(64) NO
EXECUTION_ID_ varchar(64) NO
ACT_ID_ varchar(255) NO
TASK_ID_ varchar(64) YES
CALL_PROC_INST_ID_ varchar(64) YES
ACT_NAME_ varchar(255) YES
ACT_TYPE_ varchar(255) NO
ASSIGNEE_ varchar(255) YES
START_TIME_ datetime NO
END_TIME_ datetime YES
DURATION_ bigint(20) YES
DELETE_REASON_ varchar(4000) YES
TENANT_ID_ varchar(255) YES

ACT_HI_ATTACHMENTA 历史附件表

历史附件表( act_hi_attachment )

字段名称 字段描述 数据类型 主键 为空 取值说明
ID_ ID_ nvarchar(64) 主键ID
REV_ 乐观锁 integer Version
USER_ID_ 用户ID nvarchar(255) 用户ID
NAME_ 名称 nvarchar(255) 附件名称
DESCRIPTION_ 描述 nvarchar(4000) 描述
TYPE_ 类型 nvarchar(255) 附件类型
TASK_ID_ 任务实例ID nvarchar(64) 节点实例ID
PROC_INST_ID_ 流程实例ID nvarchar(64) 流程实例ID
URL_ URL_ nvarchar(4000) 附件地址
CONTENT_ID_ 字节表的ID nvarchar(64) ACT_GE_BYTEARRAY的ID
字段 类型 NULL 备注
ID_ varchar(64) NO
REV_ int(11) YES
USER_ID_ varchar(255) YES 附件对应的用户ID
NAME_ varchar(255) YES 附件名称
DESCRIPTION_ varchar(4000) YES 附件描述
TYPE_ varchar(255) YES 附件类型
TASK_ID_ varchar(64) YES 附件对应的任务ID
PROC_INST_ID_ varchar(64) YES 对应的流程实例ID
URL_ varchar(4000) YES 链接到该附件的URL
CONTENT_ID_ varchar(64) YES 附件内容ID,附件内容会保存到资源表中,该字段记录资源数据ID
TIME_ datetime YES 数据产生的时间

ACT_HI_COMMENT 历史意见表

历史意见表( act_hi_comment )

表实际不只保存评论数据,它还会保存某些事件数据

字段名称 字段描述 数据类型 主键 为空 取值说明
ID_ ID_ nvarchar(64) 主键ID
TYPE_ 类型 nvarchar(255) 类型:event(事件)comment(意见)
TIME_ 时间 datetime 填写时间’
USER_ID_ 用户ID nvarchar(64) 填写人
TASK_ID_ 节点任务ID nvarchar(64) 节点实例ID
PROC_INST_ID_ 流程实例ID nvarchar(255) 流程实例ID
ACTION_ 行为类型 nvarchar(64) 见备注1
MESSAGE_ 基本内容 nvarchar(4000) 用于存放流程产生的信息,比如审批意见
FULL_MSG_ 全部内容 varbinary(max) 附件地址
字段 类型 NULL 备注
ID_ varchar(64) NO
TYPE_ varchar(255) YES 评论的类型可以设值为event或comment表示事件记录数据或者评论数据
TIME_ datetime NO 数据产生的时间
USER_ID_ varchar(255) YES 产生评论数据的用户ID
TASK_ID_ varchar(64) YES 评论数据的任务ID
PROC_INST_ID_ varchar(64) YES 评论数据对应的流程实例ID
ACTION_ varchar(255) YES 该评论数据的操作标识
MESSAGE_ varchar(4000) YES 该评论数据的信息
FULL_MSG_ longblob YES 该字段同样记录评论数据的信息

ACT_HI_DETAIL 流程明细表

历史详情表( act_hi_detail )

流程中产生的变量详细,包括控制流程流转的变量,业务表单中填写的流程需要用到的变量等。

流程明细表(ACT HI DETAlL 会记录流程执行过程中的参数或者表单数据,由于在流程执行过程中,会产生大量这类数据,因此默认情况下, Activiti 不会保存流程明细数据,除非将流程引擎的历史数据( hist。可〉配置为 full

字段名称 字段描述 数据类型 主键 为空 取值说明
ID_ ID_ nvarchar(64) 主键
TYPE_ 类型 nvarchar(255) 见备注2
PROC_INST_ID_ 流程实例ID nvarchar(64) 流程实例ID
EXECUTION_ID_ 执行实例ID nvarchar(64) 执行实例ID
TASK_ID_ 任务实例ID nvarchar(64) 任务实例ID
ACT_INST_ID_ 节点实例ID nvarchar(64) ACT_HI_ACTINST表的ID
NAME_ 名称 nvarchar(255) 名称
VAR_TYPE_ 参数类型 nvarchar(255) 见备注3
REV_ 乐观锁 int Version
TIME_ 时间戳 datetime 创建时间
BYTEARRAY_ID_ 字节表ID nvarchar ACT_GE_BYTEARRAY表的ID
DOUBLE_ DOUBLE_ double precision 存储变量类型为Double
LONG_ LONG_ numeric 存储变量类型为long
TEXT_ TEXT_ nvarchar 存储变量值类型为String
TEXT2_ TEXT2_ nvarchar 此处存储的是JPA持久化对象时,才会有值。此值为对象ID
字段 类型 NULL 备注
ID_ varchar(64) NO
TYPE_ varchar(255) NO
PROC_INST_ID_ varchar(64) YES
EXECUTION_ID_ varchar(64) YES
TASK_ID_ varchar(64) YES
ACT_INST_ID_ varchar(64) YES
NAME_ varchar(255) NO
VAR_TYPE_ varchar(255) YES
REV_ int(11) YES
TIME_ datetime NO
BYTEARRAY_ID_ varchar(64) YES
DOUBLE_ double YES
LONG_ bigint(20) YES
TEXT_ varchar(4000) YES
TEXT2_ varchar(4000) YES

历史流程人员表( act_ru_identitylink )

任务参与者数据表。主要存储历史节点参与者的信息

字段名称 字段描述 数据类型 主键 为空 取值说明
ID_ ID_ nvarchar(64) ID_
GROUP_ID_ 组ID nvarchar(255) 组ID
TYPE_ 类型 nvarchar(255) 备注4
USER_ID_ 用户ID nvarchar(255) 用户ID
TASK_ID_ 节点实例ID nvarchar(64) 节点实例ID
PROC_INST_ID_ 流程实例ID nvarchar(64) 流程实例ID

ACT_HI_PROCINST 流程实例表

历史流程实例表(act_hi_procinst)

只要流程被启动,就会将流程实例的数据写入 ACT HI PROC ST 表中 。除了基本的流程字段外,与运行时数据表不同的是,历史流程实例表还会记录流程的开始活动ID,结束活动ID等信息

字段名称 字段描述 数据类型 主键 为空 取值说明
ID_ ID_ nvarchar(64) 主键ID
PROC_INST_ID_ 流程实例ID nvarchar(64) 流程实例ID
BUSINESS_KEY_ 业务主键 nvarchar(255) 业务主键,业务表单的ID
PROC_DEF_ID_ 流程定义ID nvarchar(64) 流程定义ID
START_TIME_ 开始时间 datetime 开始时间
END_TIME_ 结束时间 datetime 结束时间
DURATION_ 耗时 Numeric(19) 耗时
START_USER_ID_ 起草人 nvarchar(255) 起草人
START_ACT_ID_ 开始节点ID nvarchar(255) 起草环节ID
END_ACT_ID_ 结束节点ID nvarchar(255) 结束环节ID
SUPER_PROCESS_INSTANCE_ID_ 父流程实例ID nvarchar(64) 父流程实例ID
DELETE_REASON_ 删除原因 nvarchar(4000) 删除原因
字段 类型 NULL 备注
ID_ varchar(64) NO
PROC_INST_ID_ varchar(64) NO
BUSINESS_KEY_ varchar(255) YES
PROC_DEF_ID_ varchar(64) NO
START_TIME_ datetime NO
END_TIME_ datetime YES
DURATION_ bigint(20) YES
START_USER_ID_ varchar(255) YES
START_ACT_ID_ varchar(255) YES 开始活动的 ID 一般是流程开始事件的ID,在流程文件中定义
END_ACT_ID_ varchar(255) YES 流程最后一个活动的 ID一般是流程结束事件的ID,在流程文件中定义。
SUPER_PROCESS_INSTANCE_ID_ varchar(64) YES
DELETE_REASON_ varchar(4000) YES 该流程实例被删除的原因
TENANT_ID_ varchar(255) YES
NAME_ varchar(255) YES

ACT_HI_TASKINST 历史任务表

历史任务实例表( act_hi_taskinst )

字段名称 字段描述 数据类型 主键 为空 取值说明
ID_ ID_ nvarchar(64) 主键ID
PROC_DEF_ID_ 流程定义ID nvarchar(64) 流程定义ID
TASK_DEF_KEY_ 节点定义ID nvarchar(255) 节点定义ID
PROC_INST_ID_ 流程实例ID nvarchar(64) 流程实例ID
EXECUTION_ID_ 执行实例ID nvarchar(64) 执行实例ID
NAME_ 名称 varchar(255) 名称
PARENT_TASK_ID_ 父节点实例ID nvarchar(64) 父节点实例ID
DESCRIPTION_ 描述 nvarchar(400) 描述
OWNER_ 实际签收人 任务的拥有者 nvarchar(255) 签收人(默认为空,只有在委托时才有值)
ASSIGNEE_ 签收人或被委托 nvarchar(255) 签收人或被委托
START_TIME_ 开始时间 datetime 开始时间
CLAIM_TIME_ 提醒时间 datetime 提醒时间
END_TIME_ 结束时间 datetime 结束时间
DURATION_ 耗时 numeric(19) 耗时
DELETE_REASON_ 删除原因 nvarchar(4000) 删除原因(completed,deleted)
PRIORITY_ 优先级别 int 优先级别
DUE_DATE_ 过期时间 datetime 过期时间,表明任务应在多长时间内完成
FORM_KEY_ 节点定义的formkey nvarchar(255) desinger节点定义的form_key属性
字段 类型 NULL 备注
ID_ varchar(64) NO
PROC_DEF_ID_ varchar(64) YES
TASK_DEF_KEY_ varchar(255) YES
PROC_INST_ID_ varchar(64) YES
EXECUTION_ID_ varchar(64) YES
NAME_ varchar(255) YES
PARENT_TASK_ID_ varchar(64) YES
DESCRIPTION_ varchar(4000) YES
OWNER_ varchar(255) YES
ASSIGNEE_ varchar(255) YES
START_TIME_ datetime NO
CLAIM_TIME_ datetime YES
END_TIME_ datetime YES
DURATION_ bigint(20) YES
DELETE_REASON_ varchar(4000) YES
PRIORITY_ int(11) YES
DUE_DATE_ datetime YES
FORM_KEY_ varchar(255) YES
CATEGORY_ varchar(255) YES
TENANT_ID_ varchar(255) YES

ACT_HI_VARINST 历史变量表

历史变量表( act_hi_varinst )

字段名称 字段描述 数据类型 主键 为空 取值说明
ID_ ID_ nvarchar(64) ID_
PROC_INST_ID_ 流程实例ID nvarchar(64) 流程实例ID
EXECUTION_ID_ 执行实例ID nvarchar(255) 执行实例ID
TASK_ID_ 任务实例ID nvarchar(64) 任务实例ID
NAME_ 名称 nvarchar(64) 参数名称(英文)
VAR_TYPE_ 参数类型 varchar(255) 备注5
REV_ 乐观锁 nvarchar(64) 乐观锁 Version
BYTEARRAY_ID_ 字节表ID nvarchar(400) ACT_GE_BYTEARRAY表的主键
DOUBLE_ DOUBLE_ nvarchar(255) 存储DoubleType类型的数据
LONG_ LONG_ nvarchar(255) 存储LongType类型的数据
TEXT_ TEXT_ datetime 备注6
TEXT2_ TEXT2_ datetime 此处存储的是JPA持久化对象时,才会有值。此值为对象ID

用户用户组表 (ACT_ID_)

Activiti 整个身份数据模块,可以独立于流程引擎而存在 有关身份数据的几张表没有保存与流程相关的数据及关联。身份表名称以 ACT_ID 开头,表名中的 是单词 identity的缩写。

ACT_ID_GROUP 用户组信息表

用户组信息表( act_id_group )

字段名称 字段描述 数据类型 主键 为空 取值说明
ID_ ID_ nvarchar(64) 主键ID
REV_ 乐观锁 int 乐观锁Version
NAME_ 名称 nvarchar(255) 组名称
TYPE_ 类型 nvarchar(255) 类型
字段 类型 NULL 备注
ID_ varchar(64) NO
REV_ int(11) YES
NAME_ varchar(255) YES 用户组名称
TYPE_ varchar(255) YES 用户组类型,类型不由activiti提供,但是在某些业务中, Activiti 会根据该字段的值进行查询,字段值由 Activiti 定如 Activiti webService

ACT_ID_INFO 用户扩展信息表

用户扩展信息表( act_id_info )

Activiti 将用户、用户账号和用户信息分为三种数据,其中用户表保存用户的数据,而用

户账号和用户信息,则被保存到 ACT_ID_INFO 表中

字段名称 字段描述 数据类型 主键 为空 取值说明
ID_ ID_ nvarchar(64) 主键ID
REV_ 乐观锁 int 乐观锁Version
USER_ID_ 用户ID nvarchar(64)
TYPE_ 类型 nvarchar(64)
KEY_ nvarchar(255)
VALUE_ nvarchar(255)
PASSWORD_ Image
PARENT_ID_ nvarchar(255)
字段 类型 NULL 备注
ID_ varchar(64) NO
REV_ int(11) YES
USER_ID_ varchar(64) YES 对应用户表的数据ID(没有强制做外键关联)
TYPE_ varchar(64) YES 信息类型,当前可以设置用户账号(account)、用户信息(userinfo)和NULL三种值
KEY_ varchar(255) YES 数据的键。可以根据改键来查找用户信息的值
VALUE_ varchar(255) YES 数据的值
PASSWORD_ longblob YES 用户账号的密码字段
PARENT_ID_ varchar(255) YES 该信息的父信息ID,如果一条数据设置了父信息ID,则表示该数据是用户账号(信息)的明细数据,例如一个账号有激活日期,那么激活日期就是该账号的明细数据,此处使用了自关联来实现。

ACT_ID_MEMBERSHIP 用户与分组对应信息表

用户与分组对应信息表( act_id_membership )

用来保存用户的分组信息。

一个用户组下有多个用户, 一个用户可以属于不同的用户组,那么这种多对多的关系,就

使用关系表来进行描述,关系表为 ACT ID ME IBERSHIP ,只有两个字段。需要注意的是, ACT_ID_MEMBERSHI的两个字段均做了外键约束 写入该表的数据,必须要有用户和用户组数据与之关联。

字段名称 字段描述 数据类型 主键 为空 取值说明
USER_ID 用户ID nvarchar(64)
GROUP_ID 用户组ID nvarchar(64)
字段 类型 NULL 备注
USER_ID_ varchar(64) NO
GROUP_ID_ varchar(64) NO

ACT_ID_USER 用户信息表

用户信息表( act_id_user )

字段名称 字段描述 数据类型 主键 为空 取值说明
ID_ ID_ nvarchar(64) 主键ID
REV_ 乐观锁 int 乐观锁Version
FIRST_ nvarchar(255)
LAST_ nvarchar(255)
EMAIL_ EMAIL_ nvarchar(255)
PWD_ 密码 nvarchar(255)
PICTURE_ID_ 图片ID nvarchar(64)
字段 类型 NULL 备注
ID_ varchar(64) NO
REV_ int(11) YES
FIRST_ varchar(255) YES 人名
LAST_ varchar(255) YES 姓式
EMAIL_ varchar(255) YES 用户邮箱
PWD_ varchar(255) YES 用户密码
PICTURE_ID_ varchar(64) YES 用户图片,对应资源中的数据ID

流程定义表 (ACT_RE_)

ACT_RE_DEPLOYMENT 流程部署表

部署信息表( act_re_deployment )

部署流程定义时需要被持久化保存下来的信息。

在 Activiti 中,一次部署可以添加多个资源,资源会被保存到资源表中(act_ge_bytearray),而对于部署,则部署信息会被保存到部署表中

字段名称 字段描述 数据类型 主键 为空 取值说明
ID_ ID_ nvarchar(64) 主键ID
NAME_ 部署名称 nvarchar(255) 部署文件名
CATEGORY_ 分类 nvarchar(255) 类别
DEPLOY_TIME_ 部署时间 datetime 部署时间
字段 类型 NULL 备注
ID_ varchar(64) NO
NAME_ varchar(255) YES 部署的名称
CATEGORY_ varchar(255) YES
KEY_ varchar(255) YES
TENANT_ID_ varchar(255) YES
DEPLOY_TIME_ timestamp YES 部署的时间
ENGINE_VERSION_ varchar(255) YES

ACT_RE_MODEL 流程设计模型部署表

流程设计模型部署表( act_re_model )

流程设计器设计流程后,保存数据到该表。

字段名称 字段描述 数据类型 主键 为空 取值说明
ID_ ID_ nvarchar(64) ID_
REV_ 乐观锁 int 乐观锁
NAME_ 名称 nvarchar(255) 名称
KEY_ KEY_ nvarchar(255) 分类
CATEGORY_ 分类 nvarchar(255) 分类
CREATE_TIME_ 创建时间 datetime 创建时间
LAST_UPDATE_TIME_ 最新修改时间 datetime 最新修改时间
VERSION_ 版本 int 版本
META_INFO_ META_INFO_ nvarchar(255) 以json格式保存流程定义的信息
DEPLOYMENT_ID_ 部署ID nvarchar(255) 部署ID
EDITOR_SOURCE_VALUE_ID_ datetime
EDITOR_SOURCE_EXTRA_VALUE_ID_ datetime

ACT_RE_PROCDEF 流程定义表

流程定义数据表( act_re_procdef )

业务流程定义数据表。此表和 ACT_RE_DEPLOYMENT 是多对一的关系,即,一个部署的bar包里可能包含多个流程定义文件,每个流程定义文件都会有一条记录在 ACT_REPROCDEF 表内,每个流程定义的数据,都会对于 ACT_GE_BYTEARRAY 表内的一个资源文件和 PNG 图片文件。和 ACT_GE_BYTEARRAY 的关联是通过程序用ACT_GE_BYTEARRAY.NAME 与 ACT_RE_PROCDEF.NAME 完成的,在数据库表结构中没有体现。

Activiti 在部署添加资源时,如果发布部署的文件是流程文件( .bpnn 或者.BPMN 20.xml),

则除了会解析这些流程文件,将内容保存到资源表外,还会解析流程文件的内容,形成特定的流程定义数据,写入流程定义表( ACT_RE_PROCDEF )中

字段名称 字段描述 数据类型 主键 为空 取值说明
ID_ ID_ nvarchar(64) ID_
REV_ 乐观锁 int 乐观锁
CATEGORY_ 分类 nvarchar(255) 流程定义的Namespace就是类别
NAME_ 名称 nvarchar(255) 名称
KEY_ 定义的KEY nvarchar(255) 流程定义ID
VERSION_ 版本 int 版本
DEPLOYMENT_ID_ 部署表ID nvarchar(64) 部署表ID
RESOURCE_NAME_ bpmn文件名称 nvarchar(4000) 流程bpmn文件名称
DGRM_RESOURCE_NAME_ png图片名称 nvarchar(4000) 流程图片名称
DESCRIPTION_ 描述 nvarchar(4000) 描述
HAS_START_FORM_KEY_ 是否存在开始节点formKey tinyint start节点是否存在formKey 0否 1是
SUSPENSION_STATE_ 是否挂起 tinyint 1 激活 2挂起
字段 类型 NULL 备注
ID_ varchar(64) NO
REV_ int(11) YES
CATEGORY_ varchar(255) YES 流程定义的分类
NAME_ varchar(255) YES 流程定义名称
KEY_ varchar(255) NO 流程定义的key
VERSION_ int(11) NO
DEPLOYMENT_ID_ varchar(64) YES 流程定义对应的部署数据ID
RESOURCE_NAME_ varchar(4000) YES 流程定义对应的资源名称
DGRM_RESOURCE_NAME_ varchar(4000) YES 流程定义对应的流程图资源名称
DESCRIPTION_ varchar(4000) YES
HAS_START_FORM_KEY_ tinyint(4) YES
HAS_GRAPHICAL_NOTATION_ tinyint(4) YES
SUSPENSION_STATE_ int(11) YES 表示流程定义的状态是激活还是终止,1:激活,2:终止,如果流程定义状态是终止状态则不能启动该流程
TENANT_ID_ varchar(255) YES
ENGINE_VERSION_ varchar(255) YES

运行实例表 (ACT_RU_)

运行时数据表用来保存流程在运行过程中所产生的数据,例如流程实例、执行流、任务等

运行时数据表的名称以 ACT_RU 开头,“RU ”是单词 runtime 的缩写。

ACT_RU_EVENT_SUBSCR 事件描述表

事件描述表( act_ru_event_subscr )

如果流程到达某类事件节点, Activiti 会往ACT_RUN_EVENT_SUBSCR表中加入事件描述数据,这些事件描述数据将会决定流程事件的触发。

字段名称 字段描述 数据类型 主键 为空 取值说明
ID_ 事件ID nvarchar(64) 事件ID
REV_ 版本 int 乐观锁Version
EVENT_TYPE_ 事件类型 nvarchar(255) 事件类型
EVENT_NAME_ 事件名称 nvarchar(255) 事件名称
EXECUTION_ID_ 执行实例ID nvarchar(64) 执行实例ID
PROC_INST_ID_ 流程实例ID nvarchar(64) 流程实例ID
ACTIVITY_ID_ 活动实例ID nvarchar(64) 活动实例ID
CONFIGURATION_ 配置 nvarchar(255) 配置
CREATED_ 是否创建 datetime 默认值 当前系统时间戳CURRENT_TIMESTAMP
字段 类型 NULL 备注
ID_ varchar(64) NO
REV_ int(11) YES
EVENT_TYPE_ varchar(255) NO 事件类型,不同事件会产生不同类型的事件描述,并不是所有的事件都会产生时间描述
EVENT_NAME_ varchar(255) YES 事件名称,在流程文件中定义
EXECUTION_ID_ varchar(64) YES 事件所在的执行流ID
PROC_INST_ID_ varchar(64) YES 时间所在的流程实例ID
ACTIVITY_ID_ varchar(64) YES 具体事件的ID,在流程文件中定义
CONFIGURATION_ varchar(255) YES 事件的配置属性,该字段中有可能存放流程定义ID,执行流ID,或者其他数据
CREATED_ timestamp NO
PROC_DEF_ID_ varchar(64) YES
TENANT_ID_ varchar(255) YES

ACT_RU_EXECUTION 运行时流程执行实例表

运行时流程执行实例表( act_ru_execution )

流程启动后,会产生一个流程实例,同时会产生相应的执行流,流程实例和执行流数据均

被保存在 ACT_RU_EXECUTION 表中,如果一个流程实例只有一条执行流,那么该表中只产

生一条数据,该数据既表示执行流,也表示流程实例。

字段名称 字段描述 数据类型 主键 为空 取值说明
ID_ ID_ nvarchar(64) ID_
REV_ 乐观锁 int 乐观锁
PROC_INST_ID_ 流程实例ID nvarchar(64) 流程实例ID
BUSINESS_KEY_ 业务主键ID nvarchar(255) 业务主键ID
PARENT_ID_ 父节点实例ID nvarchar(64) 父节点实例ID
PROC_DEF_ID_ 流程定义ID nvarchar(64) 流程定义ID
SUPER_EXEC_ SUPER_EXEC_ nvarchar(64) SUPER_EXEC_
ACT_ID_ 节点实例ID nvarchar(255) 节点实例ID即ACT_HI_ACTINST中ID
IS_ACTIVE_ 是否存活 tinyint 是否存活
IS_CONCURRENT_ 是否并行 tinyint 是否为并行(true/false)
IS_SCOPE_ IS_SCOPE_ tinyint IS_SCOPE_
IS_EVENT_SCOPE_ IS_EVENT_SCOPE_ tinyint IS_EVENT_SCOPE_
SUSPENSION_STATE_ 是否挂起 tinyint 挂起状态 1激活 2挂起
CACHED_ENT_STATE_ int
字段 类型 NULL 备注
ID_ varchar(64) NO
REV_ int(11) YES
PROC_INST_ID_ varchar(64) YES 流程实例ID,一个流程实例可能产生多个执行流,该字段表示执行流所属的流程实例
BUSINESS_KEY_ varchar(255) YES 启动流程时指定的业务主键
PARENT_ID_ varchar(64) YES 父执行流的ID,一个流程实例可能会产生多个执行流,该字段表示父执行ID
PROC_DEF_ID_ varchar(64) YES 流程定义数据ID
SUPER_EXEC_ varchar(64) YES
ROOT_PROC_INST_ID_ varchar(64) YES
ACT_ID_ varchar(255) YES 当前执行流行为的ID,ID在流程文件中定义
IS_ACTIVE_ tinyint(4) YES 该执行流是否活跃的标识
IS_CONCURRENT_ tinyint(4) YES 执行流是否正在并行
IS_SCOPE_ tinyint(4) YES
IS_EVENT_SCOPE_ tinyint(4) YES
IS_MI_ROOT_ tinyint(4) YES
SUSPENSION_STATE_ int(11) YES 标识流程的中断状态
CACHED_ENT_STATE_ int(11) YES
TENANT_ID_ varchar(255) YES
NAME_ varchar(255) YES
START_TIME_ datetime YES
START_USER_ID_ varchar(255) YES
LOCK_TIME_ timestamp YES
IS_COUNT_ENABLED_ tinyint(4) YES
EVT_SUBSCR_COUNT_ int(11) YES
TASK_COUNT_ int(11) YES
JOB_COUNT_ int(11) YES
TIMER_JOB_COUNT_ int(11) YES
SUSP_JOB_COUNT_ int(11) YES
DEADLETTER_JOB_COUNT_ int(11) YES
VAR_COUNT_ int(11) YES
ID_LINK_COUNT_ int(11) YES

运行时流程人员表( act_ru_identitylink )

任务参与者数据表。主要存储当前节点参与者的信息。

流程与身份关系,用户或者用户组与流程数据之间的关系

字段名称 字段描述 数据类型 主键 为空 取值说明
ID_ ID_ nvarchar(64) ID_
REV_ 乐观锁 int 乐观锁
GROUP_ID_ 组ID nvarchar(64) 组ID
TYPE_ 类型 nvarchar(255) 备注7
USER_ID_ 用户ID nvarchar(64) 用户ID
TASK_ID_ 节点实例ID nvarchar(64) 节点实例ID
PROC_INST_ID_ 流程实例ID nvarchar(64) 流程实例ID
PROC_DEF_ID_ 流程定义ID nvarchar(255) 流程定义ID
字段 类型 NULL 备注
ID_ varchar(64) NO
REV_ int(11) YES
GROUP_ID_ varchar(255) YES 该关系数据中的用户组ID
TYPE_ varchar(255) YES 该关系数据的类型,assignee(指派人或组),candidate(候选人或组),owner(拥有人)
USER_ID_ varchar(255) YES 关系数据中的用户ID
TASK_ID_ varchar(64) YES 关系数据中的任务ID
PROC_INST_ID_ varchar(64) YES 关系数据中的流程实例ID
PROC_DEF_ID_ varchar(64) YES 关系数据中的流程定义ID

ACT_RU_JOB 运行时定时任务数据表

运行时定时任务数据表( act_ru_job )

字段名称 字段描述 数据类型 主键 为空 取值说明
ID_ 标识 nvarchar(64) 标识
REV_ 版本 int 版本
TYPE_ 类型 nvarchar(255) 类型
LOCK_EXP_TIME_ 锁定释放时间 datetime 锁定释放时间
LOCK_OWNER_ 挂起者 nvarchar(255) 挂起者
EXCLUSIVE_ bit
EXECUTION_ID_ 执行实例ID nvarchar(64) 执行实例ID
PROCESS_INSTANCE_ID_ 流程实例ID nvarchar(64) 流程实例ID
PROC_DEF_ID_ 流程定义ID nvarchar(64) 流程定义ID
RETRIES_ int
EXCEPTION_STACK_ID_ 异常信息ID nvarchar(64) 异常信息ID
EXCEPTION_MSG_ 异常信息 nvarchar(4000) 异常信息
DUEDATE_ 到期时间 datetime 到期时间
REPEAT_ 重复 nvarchar(255) 重复
HANDLER_TYPE_ 处理类型 nvarchar(255) 处理类型
HANDLER_CFG_ nvarchar(4000) 标识
字段 类型 NULL 备注
ID_ varchar(64) NO
REV_ int(11) YES
TYPE_ varchar(255) NO
LOCK_EXP_TIME_ timestamp YES
LOCK_OWNER_ varchar(255) YES
EXCLUSIVE_ tinyint(1) YES
EXECUTION_ID_ varchar(64) YES
PROCESS_INSTANCE_ID_ varchar(64) YES
PROC_DEF_ID_ varchar(64) YES
RETRIES_ int(11) YES
EXCEPTION_STACK_ID_ varchar(64) YES
EXCEPTION_MSG_ varchar(4000) YES
DUEDATE_ timestamp YES
REPEAT_ varchar(255) YES
HANDLER_TYPE_ varchar(255) YES
HANDLER_CFG_ varchar(4000) YES
TENANT_ID_ varchar(255) YES

ACT_RU_TASK 运行时任务节点表

运行时任务节点表( act_ru_task )

流程在运行过程中所产生的任务数据保存在 ACT_RU_TASK 表中

字段名称 字段描述 数据类型 主键 为空 取值说明
ID_ ID_ nvarchar(64) ID_
REV_ 乐观锁 int 乐观锁
EXECUTION_ID_ 执行实例ID nvarchar(64) 执行实例ID
PROC_INST_ID_ 流程实例ID nvarchar(64) 流程实例ID
PROC_DEF_ID_ 流程定义ID nvarchar(64) 流程定义ID
NAME_ 节点定义名称 nvarchar(255) 节点定义名称
PARENT_TASK_ID_ 父节点实例ID nvarchar(64) 父节点实例ID
DESCRIPTION_ 节点定义描述 nvarchar(4000) 节点定义描述
TASK_DEF_KEY_ 节点定义的KEY nvarchar(255) 任务定义的ID
OWNER_ 实际签收人 nvarchar(255) 拥有者(一般情况下为空,只有在委托时才有值)
ASSIGNEE_ 签收人或委托人 nvarchar(255) 签收人或委托人
DELEGATION_ 委托类型 nvarchar(64) 备注8
PRIORITY_ 优先级别 int 优先级别,默认为:50
CREATE_TIME_ 创建时间 datetime 创建时间
DUE_DATE_ 过期时间 datetime 耗时
SUSPENSION_STATE_ 是否挂起 int 1代表激活 2代表挂起
字段 类型 NULL 备注
ID_ varchar(64) NO
REV_ int(11) YES
EXECUTION_ID_ varchar(64) YES 任务所在的执行流ID
PROC_INST_ID_ varchar(64) YES 对应的流程实例ID
PROC_DEF_ID_ varchar(64) YES 对应流程定义数据ID
NAME_ varchar(255) YES 任务名称,在流程文件中定义
PARENT_TASK_ID_ varchar(64) YES
DESCRIPTION_ varchar(4000) YES 任务描述,在流程文件中配置
TASK_DEF_KEY_ varchar(255) YES 任务定义的ID值,在流程文件中定义
OWNER_ varchar(255) YES 任务拥有人,没有做外键关联
ASSIGNEE_ varchar(255) YES 被指派执行该任务的人,没有做外键关联
DELEGATION_ varchar(64) YES
PRIORITY_ int(11) YES 任务优先级数值
CREATE_TIME_ timestamp YES
DUE_DATE_ datetime YES 任务预定日期
CATEGORY_ varchar(255) YES
SUSPENSION_STATE_ int(11) YES
TENANT_ID_ varchar(255) YES
FORM_KEY_ varchar(255) YES
CLAIM_TIME_ datetime YES

ACT_RU_VARIABLE 运行时流程变量数据表

运行时流程变量数据表( act_ru_variable )

Activiti 提供了ACT_RU_VAEIABLE表来存放流程中的参数,这类参数包括流程实例参数、执行流参数和任务参数,参数有可能会有多种类型

字段名称 字段描述 数据类型 主键 为空 取值说明
ID_ ID_ nvarchar(64) 主键标识
REV_ 乐观锁 int 乐观锁
TYPE_ 类型 nvarchar(255) 备注9
NAME_ 名称 nvarchar(255) 变量名称
EXECUTION_ID_ 执行实例ID nvarchar(64) 执行的ID
PROC_INST_ID_ 流程实例ID nvarchar(64) 流程实例ID
TASK_ID_ 节点实例ID nvarchar(64) 节点实例ID(Local)
BYTEARRAY_ID_ 字节表ID nvarchar(64) 字节表的ID(ACT_GE_BYTEARRAY)
DOUBLE_ DOUBLE_ float 存储变量类型为Double
LONG_ LONG_ numeric(19) 存储变量类型为long
TEXT_ TEXT_ nvarchar(4000) ‘存储变量值类型为String 如此处存储持久化对象时,值jpa对象的class
TEXT2_ TEXT2_ nvarchar(4000) 此处存储的是JPA持久化对象时,才会有值。此值为对象ID
字段 类型 NULL 备注
ID_ varchar(64) NO
REV_ int(11) YES
TYPE_ varchar(255) NO 参数类型,该字段值可以为:boolean、bytes、serializable、date、double、integer、jpa-entity、long、null、short、string,这些字段值均为activiti提供,还可以通过扩展来自定义参数类型
NAME_ varchar(255) NO 参数名称
EXECUTION_ID_ varchar(64) YES 该参数对应的执行ID,NULL
PROC_INST_ID_ varchar(64) YES 该参数对应的流程实例ID,NULL
TASK_ID_ varchar(64) YES 如果该参数是任务参数,就需要设置任务ID
BYTEARRAY_ID_ varchar(64) YES 如果参数值是序列化对象,那么可以将该对象作为资源保存到资源表中,该字段保存资源表中的数据ID
DOUBLE_ double YES 参数类型为double的话则值会保存在该字段中
LONG_ bigint(20) YES 参数类型为long的话则值会保存在该字段中
TEXT_ varchar(4000) YES 保存文本类型的参数
TEXT2_ varchar(4000) YES

工作数据表(以下表结构暂时未整理)

ACT_RU_DEADLETTER 无法执行工作表

字段 类型 NULL 备注
ID_ varchar(64) NO
REV_ int(11) YES
TYPE_ varchar(255) NO
EXCLUSIVE_ tinyint(1) YES
EXECUTION_ID_ varchar(64) YES
PROCESS_INSTANCE_ID_ varchar(64) YES
PROC_DEF_ID_ varchar(64) YES
EXCEPTION_STACK_ID_ varchar(64) YES
EXCEPTION_MSG_ varchar(4000) YES
DUEDATE_ timestamp YES
REPEAT_ varchar(255) YES
HANDLER_TYPE_ varchar(255) YES
HANDLER_CFG_ varchar(4000) YES
TENANT_ID_ varchar(255) YES

ACT_RU_SUSPENDED 中断工作表

字段 类型 NULL 备注
ID_ varchar(64) NO
REV_ int(11) YES
TYPE_ varchar(255) NO
EXCLUSIVE_ tinyint(1) YES
EXECUTION_ID_ varchar(64) YES
PROCESS_INSTANCE_ID_ varchar(64) YES
PROC_DEF_ID_ varchar(64) YES
RETRIES_ int(11) YES
EXCEPTION_STACK_ID_ varchar(64) YES
EXCEPTION_MSG_ varchar(4000) YES
DUEDATE_ timestamp YES
REPEAT_ varchar(255) YES
HANDLER_TYPE_ varchar(255) YES
HANDLER_CFG_ varchar(4000) YES
TENANT_ID_ varchar(255) YES

ACT_RU_TIMER_JOB 定时器工作表

字段 类型 NULL 备注
ID_ varchar(64) NO
REV_ int(11) YES
TYPE_ varchar(255) NO
LOCK_EXP_TIME_ timestamp YES
LOCK_OWNER_ varchar(255) YES
EXCLUSIVE_ tinyint(1) YES
EXECUTION_ID_ varchar(64) YES
PROCESS_INSTANCE_ID_ varchar(64) YES
PROC_DEF_ID_ varchar(64) YES
RETRIES_ int(11) YES
EXCEPTION_STACK_ID_ varchar(64) YES
EXCEPTION_MSG_ varchar(4000) YES
DUEDATE_ timestamp YES
REPEAT_ varchar(255) YES
HANDLER_TYPE_ varchar(255) YES
HANDLER_CFG_ varchar(4000) YES
TENANT_ID_ varchar(255) YES

其他

ACT_EVT_LOG 事件日志表

Activiti 事件日志表( act_evt_log )

字段名称 字段描述 数据类型 为空 取值说明
LOG_NR_ 主键 bigint(20) NO 自增长
TYPE_ 类型 varchar(64) YES 类型
PROC_DEF_ID_ 流程定义ID varchar(64) YES 流程定义ID
PROC_INST_ID_ 流程实例ID varchar(64) YES 流程实例ID
EXECUTION_ID_ 执行实例ID varchar(64) YES 执行实例ID
TASK_ID_ 节点实例ID varchar(64) YES 节点实例ID
TIME_STAMP_ 时间戳 timestamp(3) NO 时间戳
USER_ID_ 用户ID varchar(255) YES 用户ID
DATA_ 一些额外参数 longblob YES 例如 IP地址 等。
LOCK_OWNER_ 挂起者 varchar(255) YES 暂时没有用到
LOCK_TIME_ 挂起时间 timestamp(3) YES 暂时没有用到
IS_PROCESSED_ 是否处理过了 tinyint(4) YES 暂时没有用到

ACT_PROCDEF_INFO 流程定义扩展表

关联ACTGE_BYTEARRAY与PROC_DEF_ID表。

字段名称 字段描述 数据类型 为空 取值说明
ID_ 主键 varchar(64) NO 主键ID
PROC_DEF_ID_ 流程定义ID varchar(64) NO 流程定义ID
REV_ 乐观锁 int(11) YES 默认值 NULL,version版本
INFO_JSON_ID_ 主键 varchar(64) YES ACT_GE_BYTEARRAY的ID

DMN规则引擎数据表

决策部署表,act_dmn_deployment

保存决策数据,类似于流程定义部署,每一次部署,可以添加多份决策文件,向部署表中写入一条部署数据。

只启动流程引擎,并不会创建规则引擎表。

字段 注释
NAME_ 部署名称
CATEGORY_ 部署的目录名称
PARENT_DEPLOYMENT_ID_ 父部署ID

决策表,act_dmn_decision_table

可以先将决策看做流程定义,决策文件中保存着决策表,部署时会解析决策文件中的决策模型并将其保存到act_dmn_decision_table中。

字段 注释
KEY_ 决策业务主键
DEPLOYMENT_ID_ 所属的部署数据ID

部署资源表,act_dmn_deployment_resource

规则引擎相关的资源,例如决策文件、图片等,被保存在act_dmn_deployment_resource表中,该表类似于流程引擎的资源表。

字段 注释
NAME_ 资源名称
DEPLOYMENT_ID_ 所属的部署数据ID
RESOURCE_BYTES_ 资源内容,longblob类型。

结语

Activiti6.0的表相较于Activiti5有所不同,但核心的内容实质上还是一样,数据库这一块没有太大差别,对于这么多表,大体过一遍有个印象即可,Activiti6.0的重点仍在核心API。

Activiti 依赖事务监听器(上)


Activiti 依赖事务监听器

Activiti5.x

Activiti5.x版本中可以通过监听器辅助自身的业务操作,比如自定义一个执行监听器,则只需要定义一个类,然后实现org.activiti.engine.delegate.ExecutionListener接口即可。任务监听器需要实现org.activiti.engine.delegate.TaskListene。当然也可以自定义表达式以及委托表达式的方式实现。

当监听器被引擎触发的时候,会自动触发所有不同类型的听众(自定义监听器、内置监听器、历史监听器等)。但是自定义的监听器如果出现异常或者错误,那么这些监听器的结果并不依赖事务,通俗一点的描述就是事务回滚之后监听器会重复的执行,这就完全不对了。用下面的例子对其进行说明:

上图的流程文档如下所示:

   <process id="bookflight" name="Book Flight" isExecutable="true">

   <startEvent id="start-event-1" />

   <sequenceFlow id="flow1" sourceRef="start-event-1" targetRef="book-flight" />

   <serviceTask id="book-flight" name="Book flight" activiti:delegateExpression="${bookFlightBean}" activiti:async="true">

   <extensionElements>

   <activiti:executionListener event="end" delegateExpression="${emailBean}"/>

   </extensionElements>

  </serviceTask>

  <sequenceFlow id="flow2" sourceRef="book-flight" targetRef="charge-credit-card">

  </sequenceFlow>

  <serviceTask id="charge-credit-card" name="Charge credit card" activiti:delegateExpression="${chargeCCBean}">

  </serviceTask>

  <sequenceFlow id="flow3" sourceRef="charge-credit-card" targetRef="do-something-else" />

  <serviceTask id="do-something-else" name="Do something else" activiti:delegateExpression="${doSomethingBean}" activiti:async="true">

  </serviceTask>

  <sequenceFlow id="flow4" sourceRef="do-something-else" targetRef="end-event-1" />

  <endEvent id="end-event-1" />

  </process>

假设我们想发送电子邮件预订航班时,信用卡被指控成功。这当然可以以不同的方式来完成的。但是对于本例的缘故我们会通过实现执行侦听器和配置它的‘结束’事件的书飞行活动。

部署并启动上述的流程文档,活动成功。配置的执行侦听器会实现触发和发送电子邮件。

然后收取信用卡的活动执行。这导致一个例外。事务回滚和“书飞行”和“收取信用卡”将再次执行。这意味着我们执行侦听器也将再次执行。

这只是一个简单的例子。并且有许多用例中,您想要侦听器每次执行。但在有些情况下,比如在上面的示例中需要有可能让监听器基于整个事务的结果。比如事务成功,则监听器中的业务逻辑触发,事务失败则监听器中的业务逻辑不应该被触发。

Activiti 6.x

Activiti6中已经提供了事务监听器,以下代码片段显示了一个适应上面的例子,现在与事务相关的执行侦听器配置。

   <serviceTask id="book-flight" name="Book flight" activiti:delegateExpression="${bookFlightBean}" activiti:async="true">

   <extensionElements>

   <activiti:executionListener event="end" delegateExpression="${emailBean}" onTransaction="committed" />

   </extensionElements>

   </serviceTask>

上述代码中,可以通过设置executionListener元素中的onTransaction属性进行事务状态的的定义,即事务的状态监听器的执行以来事务的状态。onTransaction属性可以有如下三个值:

&middot;           Committed(提交)

&middot;           rolled-back(回滚)

&middot;           before-commit(提交前)

注意:Activiti5.X设计器不支持设置onTransaction属性的设置。

新接口

依赖事务执行监听器

依赖事务监听器必须实现一个不同的接口而不是“普通”执行侦听器。TransactionDependentExecutionListener接口提供了一种方法,需要自行实现。该接口的定义如下所示:

public interface TransactionDependentExecutionListener extends BaseExecutionListener {

  String ON_TRANSACTION_BEFORE_COMMIT = "before-commit";

  String ON_TRANSACTION_COMMITTED = "committed";

  String ON_TRANSACTION_ROLLED_BACK = "rolled-back";

 

  void notify(String processInstanceId, String executionId, FlowElement flowElement,

              MapexecutionVariables, MapcustomPropertiesMap);

}

依赖事务任务监听器

对于依赖事务任务监听器TransactionDependentTaskListener接口的定义如下:

   public interface TransactionDependentTaskListener extends BaseTaskListener {

     String ON_TRANSACTION_COMMITTING = "before-commit";

     String ON_TRANSACTION_COMMITTED = "committed";

     String ON_TRANSACTION_ROLLED_BACK = "rolled-back";

     void notify(String processInstanceId, String executionId, Task task, MapexecutionVariables, MapcustomPropertiesMap);

   }

对于最后一个输入参数customPropertiesMap,他是可选的。可以提供一个configureable属性解析器。(参考下文的ReceiveTask示例)。

Activiti 依赖事务监听器(下)

Activiti 依赖事务监听器(下)


Activiti 依赖事务监听器

Activiti依赖事务监听器(上)讲解了Activiti依赖事务监听器的概念,接下来看一下该如何使用TransactionDependentExecutionListener。

当引擎解析BPMN XML模型的Java模型的“常规”执行/任务听众会转换为ActivitiListeners事务相关的监听器。如果引擎操作的执行期间,遇到ActivitiListener要检查它是否一个事务依赖。如果是这样,它不执行它那一刻,就像它与其他听众,它计划为以后处理。

这种延迟处理需要拍摄快照依此当前的执行状态,因为事情可以改变实际执行侦听器之前。甚至更重要的是,执行本身执行侦听器时不再可用。

(关于这一点涉及到了源码的讲解,后续章节会详细的讲解)。

让我们看一下如何使用该接口。

首先定义一个流程文档,该流程文档对应的图如下所示:

上图对应的流程文档XML内容如下所示:

 <process id="transaction-dependent-listeners">

     <startEvent id="start">

         <extensionElements>

             <activiti:executionListener delegateExpression="${myActivityLogger}" event="start" />

         </extensionElements>

     </startEvent>



     <sequenceFlow id="flow1" sourceRef="start" targetRef="script-task-1"/>

     <scriptTask id="script-task-1" name="Script Task One" activiti:async="true" scriptFormat="groovy">

         <extensionElements>

             <activiti:executionListener delegateExpression="${myActivityLogger}" event="start" />

             <activiti:executionListener delegateExpression="${myMessageProducer}" event="start" onTransaction="committed" />

         </extensionElements>

         <script>

            println 'script task one; start new transaction'

         </script>

     </scriptTask>

     <sequenceFlow id="flow2" sourceRef="script-task-1" targetRef="receive-task-1"/>

     <receiveTask id="receive-task-1" name="Wait">

         <extensionElements>

             <activiti:executionListener delegateExpression="${myActivityLogger}" event="start" />

         </extensionElements>

     </receiveTask>

     <sequenceFlow id="flow3" sourceRef="receive-task-1" targetRef="script-task-2"/>

     <scriptTask id="script-task-2" name="Script Task Two" activiti:async="true" scriptFormat="groovy">

         <extensionElements>

             <activiti:executionListener delegateExpression="${myActivityLogger}" event="start" />

         </extensionElements>

         <script>

            println 'script task two; start new transaction'

         </script>

     </scriptTask>

     <sequenceFlow id="flow4" sourceRef="script-task-2" targetRef="end"/>

     <endEvent id="end">

         <extensionElements>

             <activiti:executionListener delegateExpression="${myActivityLogger}" event="start" />

         </extensionElements>

     </endEvent>

 </process>

当Activiti遇到ReceiveTask执行进入等待状态。这意味着流程实例逗留在在ACT_RU_EXECUTION表中。

假设在上面的示例脚本任务一个的将JMS队列上的消息。和消费者的消息将会发送一个信号流程实例(继续从等待状态)。如果发送信号发生的非常快,等待状态尚未就可能依然存在。这种情况是例外,但它可能发生。

下面看一下JMS的配置:

生产者:

/**

 * @author www.shareniu.com

 */

@Component("myMessageProducer")

public class MyMessageProducer implements TransactionDependentExecutionListener {

    private static final Logger logger = LoggerFactory.getLogger(MyMessageProducer.class);

    @Autowired

    private JmsTemplate jmsTemplate;

    public void notify(String processInstanceId, String executionId, FlowElement currentFlowElement, MapexecutionVariables, MapcustomPropertiesMap) {

        logger.debug("Sending message <{}> to queue", executionId);

        jmsTemplate.convertAndSend("receive_task_signal", executionId);

    }

}

消费者:

/**

 * @author www.shareniu.com

 */

@Component

public class MyMessageConsumer {

    private static final Logger logger = LoggerFactory.getLogger(MyMessageConsumer.class);

    @Autowired

    private RuntimeService runtimeService;

    @JmsListener(destination = "receive_task_signal", containerFactory = "myFactory")

    public void receiveMessage(String executionId) {

        logger.debug("Received message: <" + executionId + ">");

        logger.debug("Signaling execution with id: <" + executionId + ">");

        runtimeService.trigger(executionId);

    }

}

在上面的示例脚本任务一个只是一个占位符标记的开始一个事务。这同样适用于“脚本任务两个”。的脚本任务一个和接收任务“等待”是相同的事务的一部分。这意味着尽管myMessageProducer的侦听器配置的脚本任务一个侦听器将在“等待”是坚持执行。

运行上述的代码,执行如下的脚本即可。

mvn spring-boot:run

控制台的输出如下:

2017-02-07 15:18:52.229 DEBUG 1243900 --- [cTaskExecutor-1] o.a.demo.listener.MyActivityLogger       : Current activity id:

script task one; start new transaction

2017-02-07 15:18:52.653 DEBUG 1243900 --- [cTaskExecutor-1] o.a.demo.listener.MyActivityLogger       : Current activity id:

2017-02-07 15:18:52.657 DEBUG 1243900 --- [cTaskExecutor-1] o.a.demo.listener.MyMessageProducer      : Sending message <5> to queue

2017-02-07 15:18:52.689 DEBUG 1243900 --- [enerContainer-1] org.activiti.demo.jms.MyMessageConsumer  : Received message: <5>

2017-02-07 15:18:52.689 DEBUG 1243900 --- [enerContainer-1] org.activiti.demo.jms.MyMessageConsumer  : Signaling execution with id: <5>

2017-02-07 15:18:52.696 DEBUG 1243900 --- [cTaskExecutor-2] o.a.demo.listener.MyActivityLogger       : Current activity id:

script task two; start new transaction

2017-02-07 15:18:52.703 DEBUG 1243900 --- [cTaskExecutor-2] o.a.demo.listener.MyActivityLogger       : Current activity id:

Activiti6 特性


了解 Activiti6 新增特性

本文重点分析 Activiti6 新增的一些特性,从而可以更好的了解 Activiti6 的走向。

Activiti6最大的变化点就是对代码进行了重构,该版本修复以往的Bug并不多,但内部实现相对来说变化比较大。其突出的变化如下所示:

Activiti 6.0 工作流入门


Activiti 6.0 工作流入门学习

工作流介绍

工作流: 是对工作流程及其各操作步骤之间业务规则的抽象、概括描述

工作流建模: 即将工作流程中的工作如何前后组织在一起的逻辑和规则,在计算机中以恰当的模型表达并对其实施计算

要解决的问题: 是为实某个业务目标,利用 计算机在多个参与者之间按某种预定规则自动传递文档、信息或者任务

数据模型设计

数据表分类 描述
ACT_GE_* 通用数据表
ACT_RE_* 流程定义存储表
ACT_ID_* 身份信息表
ACT_RU_* 运行时数据表
ACT_HI_* 历史数据表

BPM2.0元素

审批流程模型化

购物工作流程模型化

部署Activiti

准备环境:

  1. 压缩activiti-6.0.0.zip找到wars把里面的activiti-admin.war,activiti-app.war复制到tomcat的webapps下面并启动tomcat

  2. 浏览器打开访问地址http://127.0.0.1:8080/activiti-app/账号amdin密码test

流程设计体验

创建用户

创建了三个用户

创建流程

画流程图并指定user

选择对应的用户

创建app

选择创建的流程保存

流程执行

切换userdev账号启动流程

切换到usertl账号进行审批

切换到userhr账号审批

登录管理员后台查看

登录http://127.0.0.1:8080/activiti-admin用管理员账号登录账号密码都是admin


修改端口号跟activiti-app项目的端口号一致这里是8080

Activiti 6.0 源码分析 helloword


Activiti 6.0 源码分析 helloword 学习

获取源码

  1. 然后从自己目录下把项目克隆到本地
  2. 切换分支>git checkout -b study6 activiti-6.0.0
  3. 编译>mvn clean test-compile
  4. 导入到编辑器

Activiti6.0模块介绍

基于源码activiti-app运行

启动cativiti-app

cd modules/activiti-ui/activiti-app
mvn clean tomcat7:run

打开浏览器访问http://127.0.0.1:9999/activiti-app/

activiti-ui

activiti-app 集成发布的war工程

activiti-app-conf UI独立于业务外的配置

activiti-app-logic UI的业务逻辑

activiti-app-rest 提供接口的rest api

Activiti6.0初体验helloword

通过activiti-app画好流程图并设置属性

设置form表单信息

填写审批信息表单

主管审批表单

hr审批表单

设置流转条件

提交or取消:

主管审批校验:

人事审批校验:

导出工作流xml文件创建demoActiviti项目

  1. 把导入文件放入项目里
  2. 引入jar包
<?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.guosh.activiti</groupId>
    <artifactId>guosh-activiti</artifactId>
    <version>1.0-SNAPSHOT</version>


    <dependencies>
        <!--activiti核心模块-->
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-engine</artifactId>
            <version>6.0.0</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <!--日志-->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.1.11</version>
        </dependency>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>20.0</version>
        </dependency>
        <!--h2内存数据库-->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.3.176</version>
        </dependency>
    </dependencies>

</project>
  1. 流程demo
public class DemoMain {
    private static  final Logger logger= LoggerFactory.getLogger(DemoMain.class);

    public static void main(String[] args) throws ParseException {
        logger.info("启动程序");

        //创建流程引擎(基于内存数据库的流程引擎对象)
        ProcessEngine processEngine = getProcessEngine();

        //部署流程定义文件
        ProcessDefinition processDefinition = getProcessDefinition(processEngine);

        //启动流程
        ProcessInstance processInstance = getProcessInstance(processEngine, processDefinition);


        //处理流程任务
        processTask(processEngine, processInstance);
        logger.info("结束程序");
    }

    private static void processTask(ProcessEngine processEngine, ProcessInstance processInstance) throws ParseException {
        Scanner scanner = new Scanner(System.in);

        //流程不为空并且流程没有结束
        while (processInstance!=null && !processInstance.isEnded()){
            //任务Service用于管理、查询任务,例如签收、办理、指派等
            TaskService taskService = processEngine.getTaskService();
            List<Task> list = taskService.createTaskQuery().list();
            for (Task task:list) {
                logger.info("待处理任务 [{}]",task.getName());
                Map<String, Object> variables = getStringObjectMap(processEngine, scanner, task);
                //提交
                taskService.complete(task.getId(),variables);
                //获取流程对象最新状态
                processInstance=processEngine.getRuntimeService().createProcessInstanceQuery()
                        .processInstanceId(processInstance.getId()).singleResult();


            }
            logger.info("待处理任务数量 [{}]",list.size());
        }
        scanner.close();
    }

    private static Map<String, Object> getStringObjectMap(ProcessEngine processEngine, Scanner scanner, Task task) throws ParseException {
        //表单Service用于读取和流程、任务相关的表单数据
        FormService formService = processEngine.getFormService();
        //获取任务表单
        TaskFormData taskFormData = formService.getTaskFormData(task.getId());
        //获取属性集合
        List<FormProperty> formProperties = taskFormData.getFormProperties();
        //存储要提交的表单
        Map<String, Object> variables=new HashMap<String, Object>();
        String line=null;
        for (FormProperty property:formProperties){
            //如果是string类型
            if(StringFormType.class.isInstance(property.getType())){
                logger.info("请输入 {}",property.getName());
                line=scanner.nextLine();
                logger.info("您输入的内容是 [{}]",line);
                variables.put(property.getId(),line);
            }else if(DateFormType.class.isInstance(property.getType())){
                logger.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{
                logger.info("类型不支持");
            }
        }
        return variables;
    }

    private static ProcessInstance getProcessInstance(ProcessEngine processEngine, ProcessDefinition processDefinition) {
        //运行时Service可以处理所有运行状态的流程实例流程控制(开始,暂停,挂起等)
        RuntimeService runtimeService = processEngine.getRuntimeService();
        ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinition.getId());
        logger.info("启动流程[{}]",processInstance.getProcessDefinitionKey());
        return processInstance;
    }

    private static ProcessDefinition getProcessDefinition(ProcessEngine processEngine) {
        //流程仓库Service,可以管理流程仓库例如部署删除读取流程资源
        RepositoryService repositoryService = processEngine.getRepositoryService();
        Deployment deployment = repositoryService.createDeployment()
                .addClasspathResource("second_approve.bpmn20.xml")
                .deploy();

        //部署id
        String deploymentId = deployment.getId();

        //流程对应对象
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deploymentId)
                .singleResult();

        logger.info("流程名称 [{}],流程ID [{}],流程KEY [{}]",processDefinition.getName(),processDefinition.getId(),processDefinition.getKey());
        return processDefinition;
    }

    private static ProcessEngine getProcessEngine() {
        ProcessEngineConfiguration cfg = ProcessEngineConfiguration.createStandaloneInMemProcessEngineConfiguration();
        //构造流程引擎
        ProcessEngine processEngine = cfg.buildProcessEngine();
        String name = processEngine.getName();
        String version = processEngine.VERSION;
        logger.info("流程引擎名称 [{}],版本 [{}]",name,version);
        return processEngine;
    }
}

Activiti 框架原理

本文基于一个简单的Demo流程介绍了Activiti框架启动、部署、运行过程。

Demo准备

流程图文件:

<?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" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
   <process id="hello" name="hello" isExecutable="true">
        <!-- 流程开始节点 -->
        <startEvent id="start" name="Start" ></startEvent>
        <!-- serviceTask:执行me.likeyao.activiti.demo.HelloWorld的execute方法,打印hello world -->
        <serviceTask id="helloworld" name="helloworld" activiti:class="me.likeyao.activiti.demo.HelloWorld"/>
        <!-- 流程结束节点 -->
        <endEvent id="end" name="End"></endEvent>
        <!-- 流程迁移线:开始节点到serviceTask节点 -->
        <sequenceFlow id="sid-1" sourceRef="start" targetRef="helloworld"></sequenceFlow>
        <!-- 流程迁移线:serviceTask节点到结束节点 -->
        <sequenceFlow id="sid-3" sourceRef="helloworld" targetRef="end"></sequenceFlow>
    </process>
</definitions>

流程图: 2242271-c5438bc5dc6b468a.png demo流程

代码:

public class App {
    public static void main(String[] args) {
        //创建流程引擎
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //部署流程图
        processEngine.getRepositoryService().createDeployment().addClasspathResource("hello.bpmn20.xml").deploy();
        //发起流程
        processEngine.getRuntimeService().startProcessInstanceByKey("hello");
    }
}


public class HelloWorld implements JavaDelegate{
    public void execute(DelegateExecution execution) throws Exception {
        System.out.println("Hello world!");
    }
}

Demo实现的功能是发起一个流程,执行到流程的serviceTask节点时,打印Hello world!,然后流程结束。

源码版本:5.22.0

框架初始化2242271-4becaf2056d3c51f.webp

ProcessEngine类图

ProcessEngine

ProcessEngine是Activiti框架的门面,ProcessEngine本身不提供任何功能,通过getXXXService方法可以获取到对应的Service对象执行操作。Demo中涉及到的两个Service:

ProcessEngineConfiguration

ProcessEngineConfiguration负责Activiti框架的属性配置、初始化工作,初始化入口是buildProcessEngine方法,所有Activiti框架运行时需要用到的组件基本都在这里初始化:

public ProcessEngine buildProcessEngine() {
    init();
    return new ProcessEngineImpl(this);
}

protected void init() {
    initConfigurators();
    configuratorsBeforeInit();
    initProcessDiagramGenerator();
    initHistoryLevel();
    initExpressionManager();
    initDataSource();
    initVariableTypes();
    initBeans();
    initFormEngines();
    initFormTypes();
    initScriptingEngines();
    initClock();
    initBusinessCalendarManager();
    initCommandContextFactory();
    initTransactionContextFactory();
    initCommandExecutors();
    initServices();
    initIdGenerator();
    initDeployers();
    initJobHandlers();
    initJobExecutor();
    initAsyncExecutor();
    initTransactionFactory();
    initSqlSessionFactory();
    initSessionFactories();
    initJpa();
    initDelegateInterceptor();
    initEventHandlers();
    initFailedJobCommandFactory();
    initEventDispatcher();
    initProcessValidator();
    initDatabaseEventLogging();
    configuratorsAfterInit();
}

这里有一个扩展点:ProcessEngineConfigurator。

public interface ProcessEngineConfigurator {
    //组件初始化前
    void beforeInit(ProcessEngineConfigurationImpl processEngineConfiguration);
    //组件初始化后
    void configure(ProcessEngineConfigurationImpl processEngineConfiguration);
    //优先级
    int getPriority();
}

在init初始化方法中,initConfigurators方法通过ServiceLoader加载ProcessEngineConfigurator。随后在configuratorsBeforeInit和configuratorsAfterInit方法中分别调用ProcessEngineConfigurator的beforeInit和configure方法,使用户可以在ProcessEngineConfiguration初始化前后编程式的修改属性,替换Activiti默认组件。

流程部署

流程部署实现的功能是将xml格式的流程图,转化为Activiti框架运行时依赖的流程定义对象。

RepositoryService

2242271-bc6f111998288acc.webp RepositoryService类图

Demo中通过以下代码部署了一个流程:

processEngine.getRepositoryService().createDeployment().addClasspathResource("hello.bpmn20.xml").deploy();

createDeployment方法中创建了DeploymentBuilder对象,DeploymentBuilder对象负责读取指定路径的流程图xml文件的内容(byte数组),并缓存在DeploymentEntity对象中:

public DeploymentBuilder addInputStream(String resourceName, InputStream inputStream) {
    ...
    byte[] bytes = IoUtil.readInputStream(inputStream, resourceName);
    ResourceEntity resource = new ResourceEntity();
    resource.setName(resourceName);
    resource.setBytes(bytes);
    deployment.addResource(resource);
    return this;
}

最终DeploymentBuilder的deploy方法会调用RepositoryService的deploy方法,完成流程部署:

public Deployment deploy() {
    return repositoryService.deploy(this);
}

CommandExecutor

在RepositoryService的deploy方法中,使用了CommandExecutor对象:

public Deployment deploy(DeploymentBuilderImpl deploymentBuilder) {
    return commandExecutor.execute(new DeployCmd<Deployment>(deploymentBuilder));
}

2242271-e6a84ab68b549bf4.webp CommandExecutor类图

在Activiti中,大部分操作都以Command模式实现,例如部署流程图的DeployCmd。CommandExecutor封装了一系列的CommandInterceptor,在内部形成CommandInterceptor链,在命令执行前后做了拦截。Activiti框架提供了一些

CommandInterceptor实现:

名称 作用
CommandContextInterceptor 用于生成命令执行的上下文(CommandContext)。
LogInterceptor 开启日志Debug级别后,打印日志。
JtaTransactionInterceptor 开启Jta事务

引入activiti-spring包,通过SpringTransactionInterceptor引入Spring的事务支持。

CommandExecutor在ProcessEngineConfigurationImpl的initCommandExecutors方法中初始化:

protected void initCommandExecutors() {
    initDefaultCommandConfig();
    initSchemaCommandConfig();
    initCommandInvoker();
    initCommandInterceptors();
    initCommandExecutor();
}

可以设置ProcessEngineConfigurationImpl的customPreCommandInterceptors和customPostCommandInterceptors属性,添加自定义的CommandInterceptor:

protected void initCommandInterceptors() {
    if (commandInterceptors==null) {
        commandInterceptors = new ArrayList<CommandInterceptor>();
        if (customPreCommandInterceptors!=null) {
            commandInterceptors.addAll(customPreCommandInterceptors);
        }
        commandInterceptors.addAll(getDefaultCommandInterceptors());
        if (customPostCommandInterceptors!=null) {
            commandInterceptors.addAll(customPostCommandInterceptors);
        }
        commandInterceptors.add(commandInvoker);
    }
}

这里的pre和post是指Activiti框架getDefaultCommandInterceptors()的前后。

CommandInvoker是CommandInterceptor链的最后一个对象,负责调用Command:

public class CommandInvoker extends AbstractCommandInterceptor {
    @Override
    public <T> T execute(CommandConfig config, Command<T> command) {
        return command.execute(Context.getCommandContext());
    }
}

CommandContext

2242271-dbf9c4a66c79d3c9.webp CommandContext类图

CommandContext是Activit框架Command执行的上下文,主要包含各种SessionFactory:

sessionFactories = processEngineConfiguration.getSessionFactories();

SessionFactory负责生成Session,Session是Activiti操作持久化对象的统一接口:

名称 作用
ProcessDefinitionEntityManager 流程定义相关读写操作。
ExecutionEntityManager 流程实例相关读写操作。
DefaultHistoryManager 历史记录相关读写操作

CommandContext的生命周期

CommandConext在CommandContextInterceptor中创建,在finally代码块中销毁:

public <T> T execute(CommandConfig config, Command<T> command) {
    //首先尝试从线程上下文的栈中获取CommandContext
    CommandContext context = Context.getCommandContext();
    boolean contextReused = false;
    //什么时候创建新的CommandContext?
    //1、CommandConfig中指定了不复用CommandContext
    //2、当前线程上下文中不存在CommandConext
    //3、当前线程上下文中的CommandConext已经抛出异常
    if (!config.isContextReusePossible() || context == null || context.getException() != null) { 
        context = commandContextFactory.createCommandContext(command);      }  
    else {
        contextReused = true;
    }

    try {
        //将前面获取到的CommandContext入栈
        Context.setCommandContext(context);
        Context.setProcessEngineConfiguration(processEngineConfiguration);
        //执行下一个interceptor,在CommandInvoker中可以通过Context.getCommandContext()获取线程上下文中的CommandContext
        return next.execute(config, command);
    } catch (Exception e) {
        //记录异常信息
        context.exception(e);
    } finally {
        try {
            //如果CommandContext不可复用,用完直接关闭
            if (!contextReused) {
                context.close();
            }
        } finally {
            //出栈操作
            Context.removeCommandContext();
            Context.removeProcessEngineConfiguration();
            Context.removeBpmnOverrideContext();
        }
    }
    
    return null;
}

Activiti的框架可以在一个Command的执行过程中,调用另外一个Command,所以会出现是否需要复用CommandContext的选项,默认值为true。

流程的解析

在DeployCmd中,首先调用DeploymentEntityManager持久化存储DeploymentEntity对象:

commandContext.getDeploymentEntityManager().insertDeployment(deployment);

然后调用DeploymentManager部署流程(流程解析):

commandContext.getProcessEngineConfiguration().getDeploymentManager().deploy(deployment, deploymentSettings);

DeploymentEntityManager

2242271-eb4810b9b4a64a04.webp DeploymentEntityManager类图

DeploymentEntityManager的deploy方法中循环调用Deployer对象的deploy方法,Activiti默认的Deployer是BpmnDeployer。

另外DeploymentEntityManager中还缓存了解析好的流程定义对象和Bpmn模型对象。

Activiti持久化的是流程图xml文件,每次系统重新启动都要执行一次“deploy”操作,生成ProcessDefinitionEntity对象。

BpmnDeployer

2242271-68d44c88944fc225.webp BpmnDeployer类图

BpmnDeployer的deploy方法中包含几个操作(代码缩略版):

public void deploy(DeploymentEntity deployment, Map<String, Object> deploymentSettings) {
    ...
    BpmnParse bpmnParse = bpmnParser.createParse().sourceInputStream(inputStream).setSourceSystemId(resourceName).deployment(deployment).name(resourceName);
    bpmnParse.execute();
    for (ProcessDefinitionEntity processDefinition: bpmnParse.getProcessDefinitions()) {
        if (deployment.isNew()) {
            ProcessDefinitionEntity latestProcessDefinition = ...
            if (latestProcessDefinition != null) {
                processDefinitionVersion = latestProcessDefinition.getVersion() + 1;
            }else{
                processDefinitionVersion = 1;
            }
            processDefinition.setId(idGenerator.getNextId());
            dbSqlSession.insert(processDefinition);
        }
        ...
    }
}

BpmnParse

2242271-c0d8c16d9fbf42fc.webp BpmnParse类图

在BpmnParse的execute中完成了xml文件到ProcessDefinitionEntity对象的转化:

public BpmnParse execute() {
    //xml->bpmnModel
    bpmnModel = converter.convertToBpmnModel(streamSource, validateSchema, enableSafeBpmnXml, encoding);
    //bpmnModel-> ProcessDefinitionEntity
    transformProcessDefinitions();
}

protected void transformProcessDefinitions() {
    for (Process process : bpmnModel.getProcesses()) {
        bpmnParserHandlers.parseElement(this, process);
    }
}

在流程定义解析过程中,会涉及到两套模型:

Bpmn模型

2242271-0d7054f46acfb7dd.webp Bpmn模型

PVM模型

2242271-ce5026e930258c97.webp PVM模型

Bpmn模型更偏向于xml节点的描述,PVM模型是运行时模型。Bpmn模型中的ServiceTask、StartEvent等会统一映射转换为PVM的ActivityImpl对象,ServiceTask和StartEvent等节点行为上的差别,体现在ActivityImpl对象持有的不同的ActivityBehavior上。

运行流程

创建流程实例

在demo中通过RuntimeService发起流程实例:

processEngine.getRuntimeService().startProcessInstanceByKey("hello");

在startProcessInstanceByKey方法中执行StartProcessInstanceCmd命令:

public class StartProcessInstanceCmd<T> implements Command<ProcessInstance>, Serializable {
    ...
    public ProcessInstance execute(CommandContext commandContext) {
        //获取流程定义
        ProcessDefinitionEntity processDefinition = ...
        //创建流程实例
        ExecutionEntity processInstance = processDefinition.createProcessInstance(businessKey);
        //开始流程
        processInstance.start();
        return processInstance;
    }
    ...
}

在StartProcessInstanceCmd方中通过流程定义ProcessDefinitionEntity创建了流程实例 ExecutionEntity:

2242271-5a8bc87dc9fd0f46.webp ExecutionEntity

ExecutionEntity实现了一些重要接口:

在ExecutionEntity中维护类一个属性:activity。activity属性代表当前执行到哪个节点,在创建ExecutionEntity过程中会设置activity,使流程从某一个节点开始,默认是开始节点。

最后StartProcessInstanceCmd还调用ExecutionEntity的start方法开始驱动流程:

public void start() {
    performOperation(AtomicOperation.PROCESS_START);
}

驱动流程

Activiti框架的流程运行于PVM模型之上,在流程运行时主要涉及到PVM中几个对象:ActivityImpl、TransitionImpl和ActivityBehavior。

ActivityImpl、TransitionImpl和ActivityBehavior只是描述了流程的节点、迁移线和节点行为,真正要让ExecutionEntity流转起来,还需要AtomicOperation的驱动:

AtomicOperation PROCESS_START = new AtomicOperationProcessStart();
AtomicOperation PROCESS_START_INITIAL = new AtomicOperationProcessStartInitial();
AtomicOperation PROCESS_END = new AtomicOperationProcessEnd();
AtomicOperation ACTIVITY_START = new AtomicOperationActivityStart();
AtomicOperation ACTIVITY_EXECUTE = new AtomicOperationActivityExecute();
AtomicOperation ACTIVITY_END = new AtomicOperationActivityEnd();
AtomicOperation TRANSITION_NOTIFY_LISTENER_END = new AtomicOperationTransitionNotifyListenerEnd();
AtomicOperation TRANSITION_DESTROY_SCOPE = new AtomicOperationTransitionDestroyScope();
AtomicOperation TRANSITION_NOTIFY_LISTENER_TAKE = new AtomicOperationTransitionNotifyListenerTake();
AtomicOperation TRANSITION_CREATE_SCOPE = new AtomicOperationTransitionCreateScope();
AtomicOperation TRANSITION_NOTIFY_LISTENER_START = new AtomicOperationTransitionNotifyListenerStart();
    
AtomicOperation DELETE_CASCADE = new AtomicOperationDeleteCascade();
AtomicOperation DELETE_CASCADE_FIRE_ACTIVITY_END = new AtomicOperationDeleteCascadeFireActivityEnd();

在ExecutionEntity的start方法中,调用了PROCESS_START,PROCESS_START做了几件事:

PROCESS_START_INITIAL也实现了类似的功能:

在Demo流程执行中涉及的AtomicOperation的链路主要包括:

以Demo流程中的ServiceTask节点helloworld为例,在执行ACTIVITY_EXECUTE时,会获取activity关联的behavior:

public class AtomicOperationActivityExecute implements AtomicOperation {
    public void execute(InterpretableExecution execution) {
        ...
        ActivityImpl activity = (ActivityImpl) execution.getActivity();
        ActivityBehavior activityBehavior = activity.getActivityBehavior();
        activityBehavior.execute(execution);
        ...
    }
}

ServiceTask解析时关联的是ServiceTaskJavaDelegateActivityBehavior,execution方法:

public void execute(ActivityExecution execution) throws Exception {
    //execution中调用了me.likeyao.activiti.demo.HelloWorld
    execute((DelegateExecution) execution);
    //离开当前节点
    leave(execution);
}

在leave方法中调用了:

bpmnActivityBehavior.performDefaultOutgoingBehavior(execution);

performDefaultOutgoingBehavior方法会在当前activity的 出线中选择一条,使流程流向下一个节点。在Demo中只有一条线存在:

protected void performOutgoingBehavior(ActivityExecution execution, 
          boolean checkConditions, boolean throwExceptionIfExecutionStuck, List<ActivityExecution> reusableExecutions) {

    if (transitionsToTake.size() == 1) {
        execution.take(transitionsToTake.get(0));
    }         
          
}

最终take方法会将流程驱动权交还到AtomicOperation中:

public class ExecutionEntity{
    ...
    public void take(PvmTransition transition, boolean fireActivityCompletionEvent) {
        ...
        setActivity((ActivityImpl)transition.getSource());
        setTransition((TransitionImpl) transition);
        performOperation(AtomicOperation.TRANSITION_NOTIFY_LISTENER_END);
        ...
    }
    ...
}

AtomicOperation的问题

按照AtomicOperation的驱动模式,只有当遇到UserTask等需要等待single信号的节点,调用才会返回。这意味着当调用RuntimeService启动一个流程实例时,要一直等到流程运行到一个UserTask节点调用才会返回,如果流程比较长耗时非常验证。

另一个问题是当流程图比较复杂,ExecutionListener数量比较多时,AtomicOperation之间的互相调用会导致调用栈非常深。

AtomicOperation驱动模式与ExecutionEntity、Behavior等绑定的比较紧密,暂时没有特别好的办法替换掉。

小结

本文主要介绍了Activiti框架的启动、部署、运行的主链路,并没有深入BPMN规范和Activit功能的具体实现,后续打算根据Activiti的用户手册,详细分析每个功能的使用和实现。