activiti里的命令、拦截器原理介绍和源码分析

简介

activiti里的api服务核心是它的命令和拦截器,所以我们有必要了解它的运行机制。

activiti里用到的设计模式

activiti的整体架构采用了外观模式(Facede)命令模式(Command)拦截器模式(Interceptor)这三种主要的设计模式,activiti里的所有API均是在此基础上实现的。

外观模式的体现:activiti的api设计指导思想是“面向接口编程”,我们常说的七大Service(activiti6里是七个service,activiti7里有所改动)都是用抽象定义的方式来提供,但引擎的内部实现不对外公开,开发人员只要调用相应的接口实现功能即可。如果有自定义的需求实现,可以实现对应的接口来覆盖引擎内部默认的实现。

activiti的七大服务

命令模式的体现:翻开activiti的源码,我们可以看到activiti的每一个操作都对应一个命令接口实现类,这样把不同的功能分散在N个命令中,易于维护和扩展。当调用某个XXXService的时候,其实是调用的对应的Command,但接口并不是直接调用命令,而是把命令交给CommandExecutor统一执行,因为通过它可以在命令执行过程中执行若干拦截器(类似j2ee里的Filter链)。

这边以最常用的taskService.complete(taskId)来看看源码。

拦截器的体现:拦截器大家应该都不会陌生,spring里经常会用到拦截器,例如spring的事务管理。activiti里也是类似,通过拦截器可以为所有的Command准备好命令上下文(CommandContext)对象,从而可以在Command实现类中获取到引擎配置对象、会话对象以及其它扩展属性,还可以为Command提供事务管理器、乐观锁自动重试等功能。

命令与拦截器的源码分析

通过上面的源码查看,可以知道,每个命令都实现了org.activiti.engine.impl.interceptor.Command接口,就是下图。

可以看到该接口只有一个方法execute(CommandContext commandContext),里面的参数CommandContext却非常重要,里面提供了获取数据库、事务管理器、扩展属性等资源。

activiti引擎在初始化时会初始化一系列命令拦截器,其中最重要的是CommandContextInterceptor,接口Command的CommandContext参数就是由它来提供。

若要分析它是如何来提供的,可以看源码里的这个类ProcessEngineConfigurationImpl

默认拦截器的代码片段

默认拦截器的代码片段

读取所有拦截器的方法

读取所有拦截器的方法

commandInvoker是最终调用命令的处理类,在调用命令时会同时把CommandContext对象传递给具体的命令处理类。

commandInvoker就是一个CommandInterceptor,所有的拦截器都要实现CommandInterceptor接口。

可以看到CommandInterceptor接口里面有getNext、setNext两个方法,这个Next是下一个拦截器的对象引用,这和j2ee里的Filter链有异曲同工之妙,N个拦截器通过Next就串联成了一个拦截器链。

那么,请问,第一个拦截器是什么?

上面getDefaultCommandInterceptors()源码截图有写明,是日志拦截器,然后如果有事务拦截器,它就是第二个,最后才轮到上下文拦截器。

那么,再请问,第一个拦截器是如何被调用的呢?

还记得前面有说所有的命令最后都是交给CommandExecutor来执行的,该类在初始化时把first变量作为构造参数传递。

所以CommandExecutor是命令执行的入口。

实战

《 自定义命令实现任务的回退、跳转 》

+2

《activiti里的命令、拦截器原理介绍和源码分析》有2个想法

发表评论

您的电子邮箱地址不会被公开。