乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      深入淺出Mybatis

       aaie_ 2016-05-19

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

      代理鏈的生成

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

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

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

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

      下面以一個簡單的例子來看看這個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. }  


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

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

      2)       Object plugin(Object target) 就是用當前這個攔截器生成對目標target的代理,實際是通過Plugin.wrap(target,this) 來完成的,把目標target和攔截器this傳給了包裝函數(shù)。

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

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

      Plugin.wrap方法

      從前面可以看出,每個攔截器的plugin方法是通過調(diào)用Plugin.wrap方法來實現(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.    //解析被攔截對象的所有接口(注意是接口)  
      6.    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);  
      7.    if(interfaces.length > 0) {  
      8.         //生成代理對象, Plugin對象為該代理對象的InvocationHandler  (InvocationHandler屬于java代理的一個重要概念,不熟悉的請參考相關概念)  
      9.         returnProxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target,interceptor,signatureMap));  
      10.     }  
      11.     returntarget;  
      12. }   

       這個Plugin類有三個屬性:

         private Object target;//被代理的目標類

         private Interceptor interceptor;//對應的攔截器

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

      我們再次結合(Executor)interceptorChain.pluginAll(executor)這個語句來看,這個語句內(nèi)部對

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

      代理鏈上的攔截

      代理鏈生成后,對原始目標的方法調(diào)用都轉移到代理者的invoke方法上來了。Plugin作為InvocationHandler的實現(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)用攔截器的攔截方法。我們看到傳遞給攔截器的是一個Invocation對象,這個對象是什么樣子的,他的功能又是什么呢?

      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類保存了代理對象的目標類,執(zhí)行的目標類方法以及傳遞給它的參數(shù)。

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

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

      總結

      我們假設在MyBatis配置了一個插件,在運行時會發(fā)生什么?

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

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

      3)       執(zhí)行插接中的攔截方法后,推進目標的執(zhí)行

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

      因此,在編寫插件時需注意以下幾個原則:

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

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

       

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

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多