写在最前
前两篇文章讲述了流程定义、解析以及节点设计,接下来我们要介绍工作流中最重要的工作流引擎如何工作,如何设计节点的流转。可以先看一下前两篇文章的介绍,因为本期内容有关联性。文章-流程定义,文章-流程定义解析
工作流引擎
介绍
工作流引擎其实就是设计如何让流程中的节点运转起来,在通俗一点简化之后就是这样一个执行过程
开始-》执行节点动作-》寻找连线-》执行连线动作-》寻找下一个节点 -》执行节点动作-》 ---》结束
就是这样周而复始,直到最后流程结束,其中的分离和合并节点也只不过是执行节点动作的实现不同。所以我们只要定义好了这样的流程,那我们就可以实现整个工作流引擎的运转。
执行动作
刚刚的描述中反复提到的执行动作是我们首先需要考虑的,可以发现不论是节点还是连线,都需要处理节点动作,所以我们定义一个接口,使节点和连线都has这个接口,也就是实现这个接口。
public interface Action {
/**
* 执行对象中包含执行上下文信息
*
* @param execution 执行对象
*/
void execute(Execution execution);
}
节点对象和连线对象都实现这个接口
public abstract class NodeModel extends BaseModel implements Action
public class LineModel extends BaseModel implements Action
节点动作的实现
节点元素NodeModel
定义的是抽象类,实现Action
的方法,通过模板方法定义标准流程,执行前动作、执行动作、执行后动作来可扩展化节点的执行。exec()
是抽象方法,让具体实现类自己实现,来区分不同节点需要执行的动作。
@Override
public void execute(Execution execution) {
// 前置拦截器
intercept(preInterceptorList, execution);
// 核心执行处理
exec(execution);
// 后置拦截器
intercept(postInterceptorList, execution);
}
/**
* 具体节点模型需要完成的执行逻辑
*
* @param execution 执行对象
*/
protected abstract void exec(Execution execution);
连线对象执行动作
连线对象执行就比较简单了,因为一条连线有一个明确的唯一的节点,所以直接执行目标节点的动作就可以了。
@Override
public void execute(Execution execution) {
// 不可执行,则返回
if (!enabled || target == null) {
return;
}
// 执行目标节点
target.execute(execution);
}
如何串联
刚刚已经介绍了节点执行动作和连线执行动作,那节点如何寻找到要执行的线呢?
在节点对象里定义方法,供子类调用
/**
* 后继执行
*
* @param execution 执行对象
*/
protected void runOutTransition(Execution execution) {
// 遍历连线节点,执行连线节点动作
for (LineModel line : getOutputs()) {
line.setEnabled(true);
line.execute(execution);
}
}
这样我们就把一个基本的工作流引擎就可以运行起来了。
前、后置拦截器
在NodeModel
中定义的execute()方法中定义了pre、post拦截器的执行。这两个拦截器分为用于系统级别、业务级别的拦截器,系统级别的拦截器比如节点history的记录、日志的记录等。业务级别的拦截器用于为业务实现定制化的需求而设计,业务方通过选择执行类来执行特定的业务需求。
public interface EngineInterceptor {
/**
* 从执行对象中获得上下文执行数据
*
* @param execution
*/
void intercept(Execution execution);
}
在具体实现类中实现intercept
方法来完成系统级别和业务级别的需求。
写在最后
工作流引擎简单的实现就已经讲完了,接下来会就不同节点的exec(),即节点执行动作展开讨论。希望大家能多支持支持点个赞,更有动力把这些内容分享给大家。