国内最全IT社区平台 联系我们 | 收藏本站
华晨云阿里云优惠2
您当前位置:首页 > php框架 > 框架设计 > 深入浅出Mybatis-插件原理

深入浅出Mybatis-插件原理

来源:程序员人生   发布时间:2017-02-03 14:52:59 阅读次数:3867次

Mybatis采取责任链模式,通过动态代理组织多个拦截器(插件),通过这些拦截器可以改变Mybatis的默许行动(诸如SQL重写之类的),由于插件会深入到Mybatis的核心,因此在编写自己的插件前最好了解下它的原理,以便写出安全高效的插件。

代理链的生成

Mybatis支持对ExecutorStatementHandlerPameterHandlerResultSetHandler进行拦截,也就是说会对这4种对象进行代理。下面以Executor为例。Mybatis在创建Executor对象时会履行下面1行代码:


[java] view plain copy
  1. executor =(Executor) interceptorChain.pluginAll(executor);  


InterceptorChain里保存了所有的拦截器,它在mybatis初始化的时候创建。上面这句代码的含义是调用拦截器链里的每一个拦截器顺次对executor进行plugin(插入?)代码以下:

[java] view plain copy
  1.  /** 
  2.   * 每个拦截器对目标类都进行1次代理 
  3.   * @paramtarget 
  4.   * @return 层层代理后的对象 
  5.   */  
  6.  public ObjectpluginAll(Object target) {  
  7.      for(Interceptor interceptor : interceptors) {  
  8.          target= interceptor.plugin(target);  
  9.      }  
  10.      returntarget;  
  11. }  

下面以1个简单的例子来看看这个plugin方法里到底产生了甚么。

[java] view plain copy
  1. @Intercepts({@Signature(type = Executor.class, method ="update", args = {MappedStatement.class, Object.class})})  
  2. public class ExamplePlugin implements Interceptor {  
  3.     @Override  
  4.     public Objectintercept(Invocation invocation) throws Throwable {  
  5.         returninvocation.proceed();  
  6.     }  
  7.   
  8.     @Override  
  9.     public Objectplugin(Object target) {  
  10.         returnPlugin.wrap(target, this);  
  11.     }  
  12.   
  13.     @Override  
  14.     public voidsetProperties(Properties properties) {  
  15.     }  
  16. }  


每个拦截器都必须实现上面的3个方法,其中:

1)       Object intercept(Invocation invocation)是实现拦截逻辑的地方,内部要通过invocation.proceed()显式地推动责任链前进,也就是调用下1个拦截器拦截目标方法。

2)       Object plugin(Object target) 就是用当前这个拦截器生成对目标target的代理,实际是通过Plugin.wrap(target,this) 来完成的,把目标target和拦截器this传给了包装函数。

3)       setProperties(Properties properties)用于设置额外的参数,参数配置在拦截器的Properties节点里。

注解里描写的是指定拦截方法的签名  [type,method,args] (即对哪一种对象的哪一种方法进行拦截),它在拦截前用于决断。

Plugin.wrap方法

从前面可以看出,每一个拦截器的plugin方法是通过调用Plugin.wrap方法来实现的。代码以下:

[java] view plain copy
  1. public staticObject wrap(Object target, Interceptor interceptor) {  
  2.    //从拦截器的注解中获得拦截的类名和方法信息  
  3.    Map, Set> signatureMap =getSignatureMap(interceptor);  
  4.    Class type = target.getClass();  
  5.    //解析被拦截对象的所有接口(注意是接口)  
  6.    Class[] interfaces = getAllInterfaces(type, signatureMap);  
  7.    if(interfaces.length > 0) {  
  8.         //生成代理对象, Plugin对象为该代理对象的InvocationHandler  (InvocationHandler属于java代理的1个重要概念,不熟习的请参考相干概念)  
  9.         returnProxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target,interceptor,signatureMap));  
  10.     }  
  11.     returntarget;  
  12. }   

 这个Plugin类有3个属性:

   private Object target;//被代理的目标类

   private Interceptor interceptor;//对应的拦截器

   private Map, Set> signatureMap;//拦截器拦截的方法缓存

我们再次结合(Executor)interceptorChain.pluginAll(executor)这个语句来看,这个语句内部对

executor履行了屡次plugin,第1次plugin后通过Plugin.wrap方法生成了第1个代理类,姑且就叫executorProxy1,这个代理类的target属性是该executor对象。第2次plugin后通过Plugin.wrap方法生成了第2个代理类,姑且叫executorProxy2,这个代理类的target属性是executorProxy1...这样通过每一个代理类的target属性就构成了1个代理链(从最后1个executorProxyN往前查找,通过target属性可以找到最原始的executor类)。

代理链上的拦截

代理链生成后,对原始目标的方法调用都转移到代理者的invoke方法上来了。Plugin作为InvocationHandler的实现类,他的invoke方法是怎样样的呢?

[java] view plain copy

<
生活不易,码农辛苦
如果您觉得本网站对您的学习有所帮助,可以手机扫描二维码进行捐赠
程序员人生
------分隔线----------------------------
分享到:
------分隔线----------------------------
关闭
程序员人生