activiti源码分析(一)设计模式

对activiti有基本了解的朋友都知道,activiti暴露了七个接口来提供工作流的相关服务,这些接口具体是如何实现的呢?查看源码发现其实现的形式大体如下: 

1
2
3
4
5
6
7
8
9
10
11
12
public  class  RuntimeServiceImpl  extends  ServiceImpl  implements  RuntimeService {
   
   public  ProcessInstance startProcessInstanceByKey(String processDefinitionKey) {
     return  commandExecutor.execute( new  StartProcessInstanceCmd<ProcessInstance>(processDefinitionKey,  null null null ));
   }
 
   public  ProcessInstance startProcessInstanceByKey(String processDefinitionKey, String businessKey) {
     return  commandExecutor.execute( new  StartProcessInstanceCmd<ProcessInstance>(processDefinitionKey,  null , businessKey,  null ));
   }
 
   ...
}

  service中的大部分方法都是通过调用commandExecutor.execute()完成的,然而点进去看则会发现什么都没有:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public  class  CommandExecutorImpl  implements  CommandExecutor {
 
   private  final  CommandConfig defaultConfig;
   private  final  CommandInterceptor first;
   
   public  CommandExecutorImpl(CommandConfig defaultConfig, CommandInterceptor first) {
     this .defaultConfig = defaultConfig;
     this .first = first;
   }
   
   public  CommandInterceptor getFirst() {
     return  first;
   }
 
   @Override
   public  CommandConfig getDefaultConfig() {
     return  defaultConfig;
   }
   
   @Override
   public  <T> T execute(Command<T> command) {
     return  execute(defaultConfig, command);
   }
 
   @Override
   public  <T> T execute(CommandConfig config, Command<T> command) {
     return  first.execute(config, command);
   }
 
}

  看到这里就会发现并不能看出这条语句究竟做了什么,那么究竟是如何提供服务的呢?其实activiti中大部分操作都是基于设计模式中的命令模式完成的(这里还使用了职责链模式,构造了命令拦截器链,用于在命令真正被执行之前做一系列操作)。下面结合源码详细介绍一下这些设计思路:

  命令模式的本质在于将命令进行封装,发出命令和执行命令分离。职责链模式只需要将请求放入职责链上,其处理细节和传递都不需要考虑。activiti将这两个模式整合在一起,构成了其服务主要的实现方式。其核心只有三个部分:CommandExecutor(命令执行器,用于执行命令),CommandInterceptor(命令拦截器,用于构建拦截器链),Command(命令自身)。这三个接口是整个核心的部分,还会涉及到其它的关键类,之后会一一说明,这三个类都在activiti-engine.jar这个activiti实现的核心包下,具体位置是:org.activiti.engine.impl.interceptor。下面由这三个接口逐步介绍相关的类和具体实现:

  三个接口源码:

1
2
3
4
5
public  interface  Command <T> {
 
   T execute(CommandContext commandContext);
   
}

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
  * The command executor for internal usage.
  */
public  interface  CommandExecutor {
   
   /**
    * @return the default {@link CommandConfig}, used if none is provided.
    */
   CommandConfig getDefaultConfig();
 
   /**
    * Execute a command with the specified {@link CommandConfig}.
    */
   <T> T execute(CommandConfig config, Command<T> command);
 
   /**
    * Execute a command with the default {@link CommandConfig}.
    */
   <T> T execute(Command<T> command);
   
}

  

1
2
3
4
5
6
7
8
9
public  interface  CommandInterceptor {
 
   <T> T execute(CommandConfig config, Command<T> command);
  
   CommandInterceptor getNext();
 
   void  setNext(CommandInterceptor next);
 
}

  Command的接口中只有一个execute方法,这里才是写命令的具体实现,而CommandExecutor的实现类在上面已经给出,其包含了一个CommandConfig和一个命令拦截器CommandInterceptor,而执行的execute(command)方法,实际上调用的就是commandInterceptor.execute(commandConfig,command)。CommandInterceptor中包含了一个set和get方法,用于设置next(实际上就是下一个CommandInterceptor)变量。想象一下,这样就能够通过这种形式找到拦截器链的下一个拦截器链,就可以将命令传递下去。

  简单梳理一下:Service实现服务的其中一个标准方法是在具体服务中调用commandExecutor.execute(new command())(这里的command是具体的命令)。其执行步骤就是命令执行器commandExecutor.execute调用了其内部变量CommandInterceptor first(第一个命令拦截器)的execute方法(加上了参数commandConfig)。CommandInterceptor类中包含了一个CommandInterceptor对象next,用于指向下一个CommandInterceptor,在拦截器的execute方法中,只需要完成其对应的相关操作,然后执行一下next.execute(commandConfig,command),就可以很简单的将命令传递给下一个命令拦截器,然后在最后一个拦截器中执行command.execute(),调用这个命令最终要实现的内容就行了。

  实现一个自定义的命令只需要实现Command<T>接口,在execute中做相应的操作就行了,而实现一个自定义的命令拦截器需要继承AbstractCommandInterceptor,在execute中做相应的处理,最后调用next.execute()即可,而命令执行器虽然也可以自己实现,但是没有多大意义,非常麻烦。前面说过,命令执行器会先执行命令拦截器链的execute方法,但命令拦截器链是如何构建的,命令又是在哪里调用的,第一个拦截器是如何添加到命令执行器的,这些都要关注于Activiti工作流引擎的初始化。

  初始化的方法主要写在了org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl类的init()方法中,这里主要关注于其中的initCommandExecutors(),如果对activiti的配置不清楚的,可以好好的了解一下这个初始化过程。

  initCommandExecutors():

  这五个方法名很清楚地说明了初始化步骤,前两步都是初始化CommandConfig,第一个就是命令执行器的defaultConfig,主要用在transaction拦截器。第三步初始化命令执行者,这也是一个拦截器,不过其放在拦截器的尾端,最后一个执行,它的execute方法就是调用了command.execute()。第四步就是初始化命令拦截器了。最后一步初始化命令执行器。

  前三步相关的类:

  CommandInvoker:

  接下来看看关键的第四步:

  这段代码可以看出,activiti提供了默认的命令拦截器,其顺序是LogInterceptor->TransactionInterceptor->CommandContextInterceptor,也能看出activiti提供了配置自定义的拦截器可能,customPreCommandInterceptors和customPostCommandInterceptors,只需要set进入配置就行了。一个在默认拦截器之前,一个在之后,最后一个添加的就是commandInvoker。最终的命令拦截器链就是customPreCommandInterceptors->LogInterceptor->TransactionInterceptor->CommandContextInterceptor->customPostCommandInterceptors->commandInvoker。

  最后一步初始化命令执行器代码包括了构建拦截器链:

  最后我们看一看默认提供的三个拦截器都做了一些什么操作(不包括最后CommandInvoker,上面已给出)。

  LogInterceptor.execute():

  TransactionInterceptor.execute()(这是一个抽象的方法,需要自己实现,下面以与spring集成后所给的实现为例)

  SpringTransactionInterceptor:

  最后一个CommandContextInterceptor.execute():

  这里值得注意的是context.close()方法,这里将调用session.flush();,真正执行完成数据库操作。Context也是一个比较重要的类,有兴趣可以研究一下。

猜你喜欢

转载自blog.csdn.net/dhdhxgx/article/details/80978420