盘点Flow:Activiti如何使用命令模式执行流

这是我参与更文挑战的第12天,活动详情查看: 更文挑战

首先分享之前的所有文章 , 欢迎点赞收藏转发三连下次一定 >>>> 😜😜😜
文章合集 : 🎁 juejin.cn/post/694164…
Github : 👉 github.com/black-ant

一 . 前言

在深入 Activiti 的使用者 , 发现他对命令模式进行了很深入的使用 , 这里来看一下他是如何灵活使用该模式的 :

二 . 命令模式简述

原理 : 命令模式的原理在于发送者发送命令 , 执行者执行命令 ,传递者传递命令 , 命令描述执行的动作

角色成员 :

  • Command(抽象命令类):抽象命令类一般是一个抽象类或接口,在其中声明了用于执行请求的execute()等方法
  • ConcreteCommand(具体命令类):具体命令类是抽象命令类的子类,实现了在抽象命令类中声明的方法,它对应具体的接收者对象,将接收者对象的动作绑定其中
  • Invoker(调用者):调用者即请求发送者,它通过命令对象来执行请求。
    • invoke 更像是一个传令兵 , 而不是调用者
  • Receiver(接收者):接收者执行与请求相关的操作,它具体实现对请求的业务处理。

我们来看一个命令模式的简单 Demo >> 👉

Step 1 : 构建 Command 接口

每个行为 (Command) 都会对应一个具体的 Command 对象 , 但是他们都会通过实现一个具体的 Command 接口来实现 , 对应整个流程中 ,中间对象都面向接口行为 ,而不会关心具体的实现类型

public interface Command {  
    // 每个具体的 Command 都应该实现该方法
    public void exe();  
}  
复制代码

Step 2 : 构建一个具体的 Command 实现类

Command 单元 , 该对象用于调用 具体的 Command 业务类

public class MyCommand implements Command {  

    private Receiver receiver;  

    public MyCommand(Receiver receiver) {  
        this.receiver = receiver;  
    }  

    @Override  
    public void exe() {  
        receiver.action();  
    }  
}  

复制代码

Step 3 : 命令接收人执行具体的命令

准确来说 , 该对象不算在命令模式体系中 , 他是具体的的业务对象.

public class Receiver {  
    public void action(){  
        System.out.println("command received!");  
    }  
}  

复制代码

Step 4 : 代理器用于传递命令

总代理类 , 获取 Command 命令 , 进行判断并且调用

public class Invoker {  

    private Command command;  

    public Invoker(Command command) {  
        this.command = command;  
    }  

    public void action(){  
        command.exe();  
    }  
}  
复制代码

Step End : 测试

public class Test {  

    public static void main(String[] args) {  
        Receiver receiver = new Receiver();  
        Command cmd = new MyCommand(receiver);  
        Invoker invoker = new Invoker(cmd);  
        invoker.action();  
    }  
}  

复制代码

三 . Activiti 中的命令模式

image.png

3.1 命令对象

Activiti 中的命令对象接口为 Command , 他有很多实现类 :

Command-system.png

PS:因为太多 , 这里仅仅展示了一部分 , 这些对象共同实现了 Command 接口

public interface Command<T> {
  T execute(CommandContext commandContext);
}
复制代码

还是以 DeleteTaskCmd 为例 :

3.2 Command 实现类

public class DeleteTaskCmd implements Command<Void>, Serializable {

  public Void execute(CommandContext commandContext) {
    // 简单魔改 . 省略主要逻辑
    deleteTask(commandContext, taskId);
    return null;
  }

  protected void deleteTask(CommandContext commandContext, String taskId) {
    // 调用其他的业务类执行业务  
    commandContext.getTaskEntityManager().deleteTask(taskId, deleteReason, cascade, cancel);
  }
}
复制代码

3.3 CommandInvoker 执行Command 代理

该类是最后得分发器 , 通过该方法创建新线程执行 Command 命令

public class CommandInvoker extends AbstractCommandInterceptor {

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

  @Override
  @SuppressWarnings("unchecked")
  public <T> T execute(final CommandConfig config, final Command<T> command) {
    final CommandContext commandContext = Context.getCommandContext();

    // 执行命令
    commandContext.getAgenda().planOperation(new Runnable() {
      @Override
      public void run() {
        commandContext.setResult(command.execute(commandContext));
      }
    });

    // 循环执行操作
    executeOperations(commandContext);

    // 最后,调用执行树更改监听器
    if (commandContext.hasInvolvedExecutions()) {
      Context.getAgenda().planExecuteInactiveBehaviorsOperation();
      executeOperations(commandContext);
    }

    // resultStack.pollLast() 获取结果
    return (T) commandContext.getResult();
  }

  protected void executeOperations(final CommandContext commandContext) {
    while (!commandContext.getAgenda().isEmpty()) {
      // 构建线程 , 并且调用 executeOperation 执行
      Runnable runnable = commandContext.getAgenda().getNextOperation();
      executeOperation(runnable);
    }
  }

  public void executeOperation(Runnable runnable) {
    if (runnable instanceof AbstractOperation) {
      AbstractOperation operation = (AbstractOperation) runnable;

      // 操作没有执行或者操作执行了一次,并且没有结束
      if (operation.getExecution() == null || !operation.getExecution().isEnded()) {
        runnable.run();
      }
    } else {
      runnable.run();
    }
  }

  @Override
  public CommandInterceptor getNext() {
    return null;
  }

  @Override
  public void setNext(CommandInterceptor next) {
    throw new UnsupportedOperationException("CommandInvoker must be the last interceptor in the chain");
  }

}
复制代码

PS : CommandInvoke 整体数据

image.png

这里可以看到 , 实际上 Command 对象已经设置为了 DeleteTaskCmd , 下面看一下 Command 命令谁生成的

3.4 Command 的调用

以上基本上就能看出命令模式的主要成员就已经说清楚了 , 这里还补充一下 Command 的调用

  • Step1 : C- ActivitiTaskRuntimeService # deleteTask
  • Step2 : C- TaskRuntimeImpl # delete
  • Step3 : C- TaskServiceImpl # deleteTask : 生成 Command 命令
  • Step4 : C- CommandExecutorImpl # execute : 核心方法 , 调用拦截器链
  • Step5 : C- CommandContextInterceptor # execute : 命令容器执行 Command

Step 3 : 在其中可以看到创建了 command 对象

public void deleteTask(String taskId, String deleteReason, boolean cancel) {
    commandExecutor.execute(new DeleteTaskCmd(taskId, deleteReason, false, cancel));
}
复制代码

Step 5 : CommandContextInterceptor 处理 Command 消息体

public class CommandContextInterceptor extends AbstractCommandInterceptor {
  
  private static final Logger log = LoggerFactory.getLogger(CommandContextInterceptor.class);

  // 工程类及配置对象
  protected CommandContextFactory commandContextFactory;
  protected ProcessEngineConfigurationImpl processEngineConfiguration;

  public CommandContextInterceptor() {
  }

  public CommandContextInterceptor(CommandContextFactory commandContextFactory, ProcessEngineConfigurationImpl processEngineConfiguration) {
    this.commandContextFactory = commandContextFactory;
    this.processEngineConfiguration = processEngineConfiguration;
  }

  public <T> T execute(CommandConfig config, Command<T> command) {
   
    CommandContext context = Context.getCommandContext();

    boolean contextReused = false;
    // 检查异常,事务可能处于回滚状态,并且会触发一些其他命令进行补偿
    if (!config.isContextReusePossible() || context == null || context.getException() != null) {
      context = commandContextFactory.createCommandContext(command);
    } else {
   
      contextReused = true;
      context.setReused(true);
    }

    try {
      
      // 将操作推到堆栈中 ( getStack(commandContextThreadLocal).push(commandContext);)
      Context.setCommandContext(context);
      Context.setProcessEngineConfiguration(processEngineConfiguration);
      if (processEngineConfiguration.getActiviti5CompatibilityHandler() != null) {
        Context.setActiviti5CompatibilityHandler(processEngineConfiguration.getActiviti5CompatibilityHandler());
      }
        
      // 执行操作  
      return next.execute(config, command);

    } catch (Throwable e) {

      context.exception(e);
      
    } finally {
      try {
        if (!contextReused) {
          // 关闭容器
          context.close();
        }
      } finally {
        
        // 同时移除相关数据
        Context.removeCommandContext();
        Context.removeProcessEngineConfiguration();
        Context.removeBpmnOverrideContext();
        Context.removeActiviti5CompatibilityHandler();
      }
    }

    return null;
  }
  
}  

复制代码

总结

目的: 达到命令的发出者和执行者之间解耦 , 实现请求和执行分开
本质: 一个请求对应于一个命令,将发出命令的责任和执行命令的责任分割开。每一个命令都是一个操作
结果: 由于引用了抽象类 , 请求发送者 , 具体的命令与请求接收者互相解耦

最后总结一下使用命令模式对 Activiti 的好处 :

  • 在业务中 , 创建一个 command 命令
  • 构建容器后 ,让 command 命令在其中流转
    • 对于容器 , 只需要关注命令的实现方法 , 不需要关系命令的内部逻辑
    • 发送者不需要知道中间发生了什么 , 也不需要关心性能处理
  • 最后 , 命令代理类收到命令 (此时命令中包含让谁去执行)
    • 命令代理类叫来执行对象(对象的引用) ,让其执行
    • 实际上 ,此处可以通过反射来获得执行对象 , 这样命令才真的只是个命令