小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

深入淺出Mybatis

 aaie_ 2016-05-19

Mybatis采用責(zé)任鏈模式,通過(guò)動(dòng)態(tài)代理組織多個(gè)攔截器(插件),通過(guò)這些攔截器可以改變Mybatis的默認(rèn)行為(諸如SQL重寫之類的),由于插件會(huì)深入到Mybatis的核心,因此在編寫自己的插件前最好了解下它的原理,以便寫出安全高效的插件。

代理鏈的生成

Mybatis支持對(duì)ExecutorStatementHandler、PameterHandlerResultSetHandler進(jìn)行攔截,也就是說(shuō)會(huì)對(duì)這4種對(duì)象進(jìn)行代理。下面以Executor為例。Mybatis在創(chuàng)建Executor對(duì)象時(shí)會(huì)執(zhí)行下面一行代碼:

  1. executor =(Executor) interceptorChain.pluginAll(executor);  

InterceptorChain里保存了所有的攔截器,它在mybatis初始化的時(shí)候創(chuàng)建。上面這句代碼的含義是調(diào)用攔截器鏈里的每個(gè)攔截器依次對(duì)executor進(jìn)行plugin(插入?)代碼如下:

  1.  /** 
  2.   * 每一個(gè)攔截器對(duì)目標(biāo)類都進(jìn)行一次代理 
  3.   * @paramtarget 
  4.   * @return 層層代理后的對(duì)象 
  5.   */  
  6.  public ObjectpluginAll(Object target) {  
  7.      for(Interceptor interceptor : interceptors) {  
  8.          target= interceptor.plugin(target);  
  9.      }  
  10.      returntarget;  
  11. }  

下面以一個(gè)簡(jiǎn)單的例子來(lái)看看這個(gè)plugin方法里到底發(fā)生了什么。

  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. }  


每一個(gè)攔截器都必須實(shí)現(xiàn)上面的三個(gè)方法,其中:

1)       Object intercept(Invocation invocation)是實(shí)現(xiàn)攔截邏輯的地方,內(nèi)部要通過(guò)invocation.proceed()顯式地推進(jìn)責(zé)任鏈前進(jìn),也就是調(diào)用下一個(gè)攔截器攔截目標(biāo)方法。

2)       Object plugin(Object target) 就是用當(dāng)前這個(gè)攔截器生成對(duì)目標(biāo)target的代理,實(shí)際是通過(guò)Plugin.wrap(target,this) 來(lái)完成的,把目標(biāo)target和攔截器this傳給了包裝函數(shù)。

3)       setProperties(Properties properties)用于設(shè)置額外的參數(shù),參數(shù)配置在攔截器的Properties節(jié)點(diǎn)里。

注解里描述的是指定攔截方法的簽名  [type,method,args] (即對(duì)哪種對(duì)象的哪種方法進(jìn)行攔截),它在攔截前用于決斷。

Plugin.wrap方法

從前面可以看出,每個(gè)攔截器的plugin方法是通過(guò)調(diào)用Plugin.wrap方法來(lái)實(shí)現(xiàn)的。代碼如下:

  1. public staticObject wrap(Object target, Interceptor interceptor) {  
  2.    //從攔截器的注解中獲取攔截的類名和方法信息  
  3.    Map<Class<?>, Set<Method>> signatureMap =getSignatureMap(interceptor);  
  4.    Class<?> type = target.getClass();  
  5.    //解析被攔截對(duì)象的所有接口(注意是接口)  
  6.    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);  
  7.    if(interfaces.length > 0) {  
  8.         //生成代理對(duì)象, Plugin對(duì)象為該代理對(duì)象的InvocationHandler  (InvocationHandler屬于java代理的一個(gè)重要概念,不熟悉的請(qǐng)參考相關(guān)概念)  
  9.         returnProxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target,interceptor,signatureMap));  
  10.     }  
  11.     returntarget;  
  12. }   

 這個(gè)Plugin類有三個(gè)屬性:

   private Object target;//被代理的目標(biāo)類

   private Interceptor interceptor;//對(duì)應(yīng)的攔截器

   private Map<Class<?>, Set<Method>> signatureMap;//攔截器攔截的方法緩存

我們?cè)俅谓Y(jié)合(Executor)interceptorChain.pluginAll(executor)這個(gè)語(yǔ)句來(lái)看,這個(gè)語(yǔ)句內(nèi)部對(duì)

executor執(zhí)行了多次plugin,第一次plugin后通過(guò)Plugin.wrap方法生成了第一個(gè)代理類,姑且就叫executorProxy1,這個(gè)代理類的target屬性是該executor對(duì)象。第二次plugin后通過(guò)Plugin.wrap方法生成了第二個(gè)代理類,姑且叫executorProxy2,這個(gè)代理類的target屬性是executorProxy1...這樣通過(guò)每個(gè)代理類的target屬性就構(gòu)成了一個(gè)代理鏈(從最后一個(gè)executorProxyN往前查找,通過(guò)target屬性可以找到最原始的executor類)。

代理鏈上的攔截

代理鏈生成后,對(duì)原始目標(biāo)的方法調(diào)用都轉(zhuǎn)移到代理者的invoke方法上來(lái)了。Plugin作為InvocationHandler的實(shí)現(xiàn)類,他的invoke方法是怎么樣的呢?

  1. public Objectinvoke(Object proxy, Method method, Object[] args) throws Throwable {  
  2.     try {  
  3.        Set<Method> methods = signatureMap.get(method.getDeclaringClass());  
  4.         if(methods != null && methods.contains(method)) {  
  5.             //調(diào)用代理類所屬攔截器的intercept方法,  
  6.            return interceptor.intercept(new Invocation(target, method, args));  
  7.         }  
  8.         returnmethod.invoke(target, args);  
  9.     } catch(Exception e) {  
  10.         throwExceptionUtil.unwrapThrowable(e);  
  11.     }  
  12.    

invoke里,如果方法簽名和攔截中的簽名一致,就調(diào)用攔截器的攔截方法。我們看到傳遞給攔截器的是一個(gè)Invocation對(duì)象,這個(gè)對(duì)象是什么樣子的,他的功能又是什么呢?

  1. public class Invocation {  
  2.   
  3.   private Object target;  
  4.   private Methodmethod;  
  5.   private Object[]args;  
  6.    
  7.   publicInvocation(Object target, Method method, Object[] args) {  
  8.     this.target =target;  
  9.     this.method =method;  
  10.     this.args =args;  
  11.   }  
  12.   ...  
  13.   
  14.   public Objectproceed() throws InvocationTargetException, IllegalAccessException {  
  15.     returnmethod.invoke(target, args);  
  16.   }  
  17. }  


可以看到,Invocation類保存了代理對(duì)象的目標(biāo)類,執(zhí)行的目標(biāo)類方法以及傳遞給它的參數(shù)。

在每個(gè)攔截器的intercept方法內(nèi),最后一個(gè)語(yǔ)句一定是returninvocation.proceed()(不這么做的話攔截器鏈就斷了,你的mybatis基本上就不能正常工作了)。invocation.proceed()只是簡(jiǎn)單的調(diào)用了下target的對(duì)應(yīng)方法,如果target還是個(gè)代理,就又回到了上面的Plugin.invoke方法了。這樣就形成了攔截器的調(diào)用鏈推進(jìn)。

  1. public Object intercept(Invocation invocation) throws Throwable {  
  2.   
  3.      //完成代理類本身的邏輯  
  4.      ...  
  5.      //通過(guò)invocation.proceed()方法完成調(diào)用鏈的推進(jìn)  
  6.      return invocation.proceed();  
  7.   }   

總結(jié)

我們假設(shè)在MyBatis配置了一個(gè)插件,在運(yùn)行時(shí)會(huì)發(fā)生什么?

1)       所有可能被攔截的處理類都會(huì)生成一個(gè)代理

2)       處理類代理在執(zhí)行對(duì)應(yīng)方法時(shí),判斷要不要執(zhí)行插件中的攔截方法

3)       執(zhí)行插接中的攔截方法后,推進(jìn)目標(biāo)的執(zhí)行

如果有N個(gè)插件,就有N個(gè)代理,每個(gè)代理都要執(zhí)行上面的邏輯。這里面的層層代理要多次生成動(dòng)態(tài)代理,是比較影響性能的。雖然能指定插件攔截的位置,但這個(gè)是在執(zhí)行方法時(shí)動(dòng)態(tài)判斷,初始化的時(shí)候就是簡(jiǎn)單的把插件包裝到了所有可以攔截的地方。

因此,在編寫插件時(shí)需注意以下幾個(gè)原則:

1)       不編寫不必要的插件;

2)       實(shí)現(xiàn)plugin方法時(shí)判斷一下目標(biāo)類型,是本插件要攔截的對(duì)象才執(zhí)行Plugin.wrap方法,否者直接返回目標(biāo)本省,這樣可以減少目標(biāo)被代理的次數(shù)。

 

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多