Flowable 命令执行框架

664 阅读1分钟

CommandExecutor

命令执行器使用了命令模式和职责链模式。

classDiagram
 class CommandInterceptor{
  <<Interface>>
  execute(CommandConfig cfg, CommandContext ctx, CommandExecutor executor)
  setNext(CommandInterceptor arg0)
  getNext(): CommandInterceptor
 }
 class CommandContext {
 
 }
 class CommandExecutor {
  execute(Command<T> command)
 }
 class Command {
  execute(CommandContext ctx)
 }
 Command ..> CommandContext : uses
 CommandExecutor ..> Command : uses
 CommandExecutor o--> CommandInterceptor : owns
 CommandInterceptor --> CommandInterceptor : links

Flowable中流程引擎的CommandExecutor实例的拦截器链如下:

相关代码查看AbstractEngineConfiguration类的initCommandInterceptors()方法。

flowchart TD
LogInterceptor --> CommandContextInterceptor --> TransactionContextInterceptor 
--> CommandInvoker
  • LogInterceptor 打印日志
  • CommandContextInterceptor 负责为要执行的 Command 设置及清理线程本地存储 CommandContext,以及执行CommandContext 的 close 操作
  • TransactionContextInterceptor 负责为要执行的 Command 设置及清理本地存储 TransactionContext
  • CommandInvoker 开始执行 Command

CommandContext

classDiagram
  class CommandContext {
      close()
      flushSessions()
      addCloseListener(CommandContextCloseListener commandContextCloseListener)
  }
  class TransactionContext {
      <<Interface>>
      commit()
      rollback()
      addTransactionListener(TransactionState s, TransactionListener l)
  }
  class CommandContextCloseListener {
      <<Interface>>
      closing(CommandContext ctx)
      afterSessionFlush(CommandContext ctx)
      closed(CommandContext ctx)
      closeFailure(CommandContext ctx)
      Integer order()
  }
  class TransactionCommandContextCloseListener {
    +TransactionContext transactionContext
  }
  class StandaloneMybatisTransactionContext {
    +CommandContext commandContext
    +DbSqlSession dbSqlSession
    +Map<TransactionState, List<TransactionListener>> listeners
  }
  class TransactionListener {
      <<Interface>>
      execute(CommandContext commandContext): void
  }
  
  CommandContext o-- CommandContextCloseListener : has
  TransactionCommandContextCloseListener ..|> CommandContextCloseListener : implements
  TransactionCommandContextCloseListener o--> TransactionContext : has
  StandaloneMybatisTransactionContext ..|> TransactionContext : implements
  TransactionContext ..> TransactionListener : uses

CommandContextInterceptor

public class CommandContextInterceptor {
    @Override
    public <T> T execute(Command<T> command) {
        CommandContext commandContext = Context.getCommandContext();
        boolean contextReused = false;
        /*
        * 命令可以执行服用调用,甚至是深度嵌套的服用调用。
        * 当开始执行命令时,‘originalContextReusedState’原样存储CommandContext中的‘reused’标记。
        * 对于一个嵌套的命令,将会是‘true'。只有在执行根命令上下文时,这个标记才会是’false‘。
        * 当执行完嵌套命令时,恢复CommandContext中的`reused`标记,这样允许在CommandInvloker中检测哪一个真正的根命令。
        */
        boolean originalContextReusedState = false;
        if(commandContext==null || commandContext.getException()!=null){
            commandContext = commandContextFactory.createCommandContext();
            ...
        }else{
            // 当前存在一个有效的上下文对象,执行命令时将复用这个上下文对象
            contextReused = true; // 表明当前上下文对象是被复用的,需要再finally中关闭
            originalContextReusedState = commandContext.isReused(); // 存储当前上下的状态
            commandContext.setReused(true);
        }

        try{
            Context.setCommandContext(commandContext);
            return next.execute(command);
        }catch(Exception e){
            commandContext.exception(e);
        }finally{
            try{
                if(!contextReused){
                    commandContext.close(); // 这里很关键
                }
                commandContext.setResued(originalContxtReusedState); // 恢复执行命令前上下文中的reused状态
            }finally{
                Context.removeCommandContext();
            }
        }
        if(contextReused && commandContext.getException()!=null){ // 执行当前命令的上下文对象是复用的,而且执行过程中还产生了异常,需要将异常抛出给到上层命令
        Throwable exception = commandContext.getException();
        commandContext.resetException();

        throw new FlowableException("Exception during command execution", exception);
        }
        return null;
    }
}

CommandInvoker

CommandContext 的 close 方法