zoukankan      html  css  js  c++  java
  • ACTIVITI 源码研究之命令模式执行

    ACTIVITI 是一个优秀开源软件,通过阅读源码,我们不但可以了解工作流引擎执行的原理还可以增加个人的编码功力。

    ACTIVITI 所有执行过程都是采用命令模式进行执行。 

    本文主要描述流程引擎数据保存的过程。

    流程引擎所有的操作都采用命令模式,使用命令执行器进行执行,命令执行器是一个采用拦截器链式执行模式。

    1.命令执行器。

      代码为org.activiti.engine.impl.interceptor.CommandExecutor.

      命令执行器的构造代码如下:

      1.获取拦截器列表。

        1.获取客户自定义前置拦截器。

          这个需要实现CommandInterceptor接口,并配置到流程定义配置文件中。

        2.获取默认的拦截器。

     protected Collection< ? extends CommandInterceptor> getDefaultCommandInterceptors() {
        List<CommandInterceptor> interceptors = new ArrayList<CommandInterceptor>();
        interceptors.add(new LogInterceptor());
        
        CommandInterceptor transactionInterceptor = createTransactionInterceptor();
        if (transactionInterceptor != null) {
          interceptors.add(transactionInterceptor);
        }
        
        interceptors.add(new CommandContextInterceptor(commandContextFactory, this));
        return interceptors;
      }

           这个是获取默认的拦截器。

                 这里有四个拦截器。

                      1.LogInterceptor日志拦截器,拦截器打印执行的日志。

                      2.事务拦截器。

                      3.CommandContextInterceptor 拦截器。

                          这个拦截器功能如下:

                         1.流程定义。

                         2.注入命令上下文,命令上下文包括数据只有代码。

                        3.调用命令上下文close方法,执行数据保存。

                        4.命令执行拦截器,CommandInvoker 这个是拦截器最后的一个,为调用具体的命令。

        3.获取后置拦截器。

                 这个需要实现CommandInterceptor接口,并配置到流程定义配置文件中。

      2.构建拦截器链。

         基础拦截器代码: 

    public abstract class AbstractCommandInterceptor implements CommandInterceptor {
    
      /** will be initialized by the {@link org.activiti.engine.ProcessEngineConfiguration ProcessEngineConfiguration} */
      protected CommandInterceptor next;
    
      @Override
      public CommandInterceptor getNext() {
        return next;
      }
      
      @Override
      public void setNext(CommandInterceptor next) {
        this.next = next;
      }
    }

      拦截器调用setNext方法设置下一个拦截器。 

     protected CommandInterceptor initInterceptorChain(List<CommandInterceptor> chain) {
        if (chain==null || chain.isEmpty()) {
          throw new ActivitiException("invalid command interceptor chain configuration: "+chain);
        }
        for (int i = 0; i < chain.size()-1; i++) {
          chain.get(i).setNext( chain.get(i+1) );
        }
        return chain.get(0);
      }

      这里将拦截器列表构建成链的模式。

    2.现在介绍一下命令的执行模式顺序。

    比如我们设置流程变量,参考代码如下:

    RuntimeService setVariable(String executionId, String variableName, Object value);

    RuntimeServiceImpl 实现代码:

    Map<String, Object> variables = new HashMap<String, Object>();
        variables.put(variableName, value);
        commandExecutor.execute(new SetExecutionVariablesCmd(executionId, variables, false));

    这个commandExecutor到底是如何注入的。

    我们可以看到这个RuntimeServiceImpl 扩展了类ServiceImpl。

    我们在ProcessEngineConfigurationImpl 中看到如下代码:

    protected void initServices() {
        initService(repositoryService);
        initService(runtimeService);
        initService(historyService);
        initService(identityService);
        initService(taskService);
        initService(formService);
        initService(managementService);
      }
    
      protected void initService(Object service) {
        if (service instanceof ServiceImpl) {
          ((ServiceImpl)service).setCommandExecutor(commandExecutor);
        }
      }

    这里注入了commandExecutor。

    执行顺序为:

    1.执行前置拦截器,如果存在。

    2.日志执行。  

    public class LogInterceptor extends AbstractCommandInterceptor {
      
      private static Logger log = LoggerFactory.getLogger(LogInterceptor.class);
    
      public <T> T execute(CommandConfig config, Command<T> command) {
        if (!log.isDebugEnabled()) {
          // do nothing here if we cannot log
          return next.execute(config, command);
        }
        log.debug("                                                                                                    ");
        log.debug("--- starting {} --------------------------------------------------------", command.getClass().getSimpleName());
        try {
    
          return next.execute(config, command);
    
        } finally {
          log.debug("--- {} finished --------------------------------------------------------", command.getClass().getSimpleName());
          log.debug("                                                                                                    ");
        }
      }
    }

      参考日志代码记录日志,调用下一个拦截器(next.execute(config, command);),最后记录日志。

    3.执行事务。

    3.执行CommandContext拦截器。

      这个拦截器执行数据库持久化。

      

    try {
          // Push on stack
          Context.setCommandContext(context);
          Context.setProcessEngineConfiguration(processEngineConfiguration);
          
          return next.execute(config, command);
          
        } catch (Exception e) {
            
          context.exception(e);
          
        } finally {
          try {
              if (!contextReused) {
                  context.close();
              }
          } finally {
              // Pop from stack
              Context.removeCommandContext();
              Context.removeProcessEngineConfiguration();
          }
        }

    在命令中并不执行数据库持久化,持久化在此拦截器中调用context.close();执行。

    4.执行后置拦截器,如果存在。

    5.调用命令拦截器执行命令。

      

  • 相关阅读:
    Linux操作系统的介绍和安装教程(Centos6.4)
    通过漫画轻松掌握HDFS工作原理
    2018HUAS_ACM暑假比赛5题解
    codeforces #271D Good Substrings
    jQuery插件开发
    面试题及答案
    知乎日报 API 分析
    CSS值自动转REM的Sublime Text插件
    Sublime Text3常用插件以及安装方法(实用)
    移动开发的坑
  • 原文地址:https://www.cnblogs.com/yg_zhang/p/3676804.html
Copyright © 2011-2022 走看看