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.調用命令攔截器執行命令。

 

 

  

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM