MyBatis詳細解7.プラグイン

7931 ワード

クリックして私のブログに入ります.
MyBatis詳細解1.概説MyBatis詳細解2.MyBatis使用入門MyBatis詳細解3.MyBatis構成詳細解4.マッパーMapper MyBatis詳細解5.ダイナミックSQL MyBatis詳細解6.MyBatis技術内幕MyBatis詳細解7.プラグインMyBatis詳細解8.統合Spring
1プラグインのインターフェース
Configrationは、Exector、Sttement Handler、ResultSetHandler、ParameterHandlerを作成する方法で、InterceptorChinを通じてプラグインを追加することができます.これにより、これらのオブジェクトがスケジュールされている間に、コードを挿入して、特殊なシーンの需要を満たすために、いくつかの要求を実行することができます.ユーザー定義のプラグインを実現するには、org.apache.ibatis.plugin.Interceptorインターフェースが必要です.
public interface Interceptor {
  Object intercept(Invocation invocation) throws Throwable;
  Object plugin(Object target);
  void setProperties(Properties properties);
}
  • intercept方法:ブロックされているオブジェクトを直接カバーする方法は、最も主要な方法です.パラメータivocationオブジェクトは、反射によって元のオブジェクトをスケジュールする方法があります.
  • plugin方法:targetはブロックされたオブジェクトであり、ブロックされたオブジェクトにプロキシオブジェクトを生成して返します.使用を容易にするために、MyBatisはProxy.newProxyInstance方法をorg.apache.ibatis.plugin.Plugin#warp方法で包装し、代理先を作成します.
  • set Properties方法:plugin要素に必要なパラメータを設定することができます.方法はプラグイン初期化の時に一度呼び出しられました.その後、プラグインオブジェクトを設定に保存して、後で取り出すためです.
  • 2プラグインの初期化org.apache.ibatis.session.SqlSessionFactoryBuilder#build()方式でSql Session Factoryを構築する場合、org.apache.ibatis.builder.xml.XMLConfigBuilder#parse()方法を呼び出します.このとき、org.apache.ibatis.builder.xml.XMLConfigBuilder#pluginElement()方法を呼び出してプラグインの初期化を完了します.
      private void pluginElement(XNode parent) throws Exception {
        if (parent != null) {
          for (XNode child : parent.getChildren()) {
            String interceptor = child.getStringAttribute("interceptor");
            Properties properties = child.getChildrenAsProperties();
            Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
            interceptorInstance.setProperties(properties);
            //  configuration   Interceptor
            configuration.addInterceptor(interceptorInstance);
          }
        }
      }
    
    プロファイルを解析するとき、MyBatisのコンテキスト初期化プロセスでは、プラグインノードと私たちが設定したパラメータを読み込み始め、反射技術を使って対応するプラグインのインスタンスを生成し、プラグイン方法のset Properties方法を呼び出して、私たちが設定したパラメータを設定し、最後にプラグインのインスタンスを構成オブジェクトに保存して、それを読み取り、使用します.プラグインの例のオブジェクトは最初から初期化されていますので、それを使うときはそのまま取り出してもいいです.
      public void addInterceptor(Interceptor interceptor) {
        interceptorChain.addInterceptor(interceptor);
      }
    
    3プラグインのプロキシと反射設計
    プラグインのデザインモード
    プラグインは責任連鎖モードを使います.責任連鎖のモード、1つの対象が複数の役の中で伝達して、鎖の上のいかなる役がすべてその機会を処理することがいます.MyBatisの責任チェーンはInterceptorChainによって定義されており、ConfigrationでExector、Sttement Handler、Result SetHandler、ParameterHandlerを作成する方法では、interceptorChain.plugiAll()方法が実行されます.
    public class InterceptorChain {
      private final List interceptors = new ArrayList();
     
      public Object pluginAll(Object target) {
        for (Interceptor interceptor : interceptors) {
          target = interceptor.plugin(target);
        }
        return target;
      }
    }
    
    interceptor#plugin方法はプロキシオブジェクトを生成する方法であることを知っています.pluginAll方法では、まずinterceptorsを巡回してプラグインを取り出し、次いでExectorオブジェクトのためにプロキシを作成して返します.他の3つのオブジェクトの処理方法も同じです.
    工具類Plugin
    MyBatisは、プロキシオブジェクトを生成するためのツールタイプPluginを提供し、InvocationHandlerインターフェースを実現し、JDK動的エージェントを用いてプロキシ機能を実現しています.その一番の方法はwrap()とinvoke()です.
    public class Plugin implements InvocationHandler {
      //   target      
      public static Object wrap(Object target, Interceptor interceptor) {
        Map, Set> signatureMap = getSignatureMap(interceptor);
        Class> type = target.getClass();
        Class>[] interfaces = getAllInterfaces(type, signatureMap);
        if (interfaces.length > 0) {
          return Proxy.newProxyInstance(
              type.getClassLoader(),
              interfaces,
              new Plugin(target, interceptor, signatureMap));
        }
        return target;
      }
    
      //         
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
          Set methods = signatureMap.get(method.getDeclaringClass());
          if (methods != null && methods.contains(method)) {
            return interceptor.intercept(new Invocation(target, method, args));
          }
          return method.invoke(target, args);
        } catch (Exception e) {
          throw ExceptionUtil.unwrapThrowable(e);
        }
      }
    }
    
    Invocationオブジェクト
    public class Invocation {
      public Object proceed() throws InvocationTargetException, IllegalAccessException {
        return method.invoke(target, args);
      }
    }
    
    
    Invocationオブジェクトの中にプロシージャ方法があります.この方法はプロキシオブジェクトをスケジュールする本当の方法です.n個のプラグインがあると仮定して、最初の伝達パラメータは4つのオブジェクト自体であることを知っています.その後、wrap方法を呼び出して最初のプロキシオブジェクトを生成します.ここでの反射は4つのオブジェクト自体を反射する真実の方法です.第二のプラグインがある場合、第一のプロキシオブジェクトをwrap方法に渡し、第二のプロキシオブジェクトを生成します.ここでの反射は、最初のプロキシオブジェクトのinvoke方法を指します.これによって、最後のプロキシオブジェクトまで類推します.各プロキシオブジェクトがこのプロシージャ方法を呼び出すと、最後の4つのオブジェクト自体の方法も呼び出されますが、最後のプロキシオブジェクトのinvoke方法から最初のプロキシオブジェクトのinvoke方法まで実行されます.
    4工具類MetaObject
    MyBatisでは、4つのオブジェクトから提供されたpublic設定パラメータの方法は非常に少ないです.私たちは自身を通じて関連する属性情報を入手するのは難しいですが、MetaObjectを通じて、重要なオブジェクトの属性を有効に読み取ったり、修正したりできます.よく使われる3つの方法があります.
  • MetaObject forObject方法:包装対象に用いる.この方法はもう使わなくなりました.MyBatisで提供してくれたSystem MetaObject.forObjectです.
  • Object getValue方法:オブジェクト属性値を取得し、ONLをサポートする.
  • void setValue方法:オブジェクト属性値を修正し、ONLをサポートする.
  • 5プラグインの開発例
    今は自動的にSELECT文の後にLIMITを追加してSELECTの行数を制御すると仮定します.Sttement HandlerをブロックするprepareによってSELECT文自体を変更することで、下記のようになります.
    @Intercepts(
            @Signature(
                    type = StatementHandler.class,
                    method = "prepare",
                    args = {Connection.class, Integer.class}
            )
    )
    public class MyPlugin implements Interceptor {
        private Properties properties;
    
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
            //   StatementHandler  
            StatementHandler statementHandler = (StatementHandler)invocation.getTarget();
    
            //   SystemMetaObject     
            MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
    
            String sql = (String) metaObject.getValue("delegate.boundSql.sql");
    
            //   SQL    LIMIT
            if(sql != null && sql.startsWith("SELECT")) {
                sql = sql + "LIMIT " + properties.getProperty("row") ;
                metaObject.setValue("delegate.boundSql.sql", sql);
            }
    
            return invocation.proceed();
        }
    
        @Override
        public Object plugin(Object target) {
            //     , plugin              
            //   Plugin.wrap   @Intercepts                     
            return Plugin.wrap(target, this);
        }
    
        //     MyBatis             
        @Override
        public void setProperties(Properties properties) {
            //      properties
            properties.forEach((key, value) -> System.out.println("key=" + key + ", value=" + value));
            this.properties = properties;
        }
    }
    
        
            
                
            
        
    
    プラグインの開発プロセス
  • まずプラグインタイプMyPluginを作成し、Interceptorインターフェース
  • を継承する.
  • は、プラグ類に@Interceptsを用いてブロッキングする対象、方法、パラメータを決定する.
  • は、plugin方法にコードPlugin.wrap(target, this);を追加する.一般的には、plugin方法ではこのラインコードだけが必要です.Plugin.wrapは@Interceptsの注釈に基づいて元のオブジェクトに戻るかそれとも包装後のオブジェクトに戻るかを判断します.
  • は、intercept方法において具体的な論理を作成する.
  • は、設定ファイルにプラグインを導入する.
  • プラグイン作成の注意
  • は、プラグインを使用しないようにすることができます.
  • プラグインは、レイヤエージェントの責任チェーンモードを生成し、反射方法により動作し、性能が高くないので、プラグインを減らすことで、プロキシを低減し、システムの性能を向上させることができる.
  • プラグインを作成するには、MyBatisの動作原理を理解し、4つのオブジェクトとその方法の役割を理解し、どのオブジェクトをブロックする必要があるかを正確に判断する必要があります.どのような方法で、どのようなパラメータが必要ですか?
  • は、よくプラグインの中でMy Batisマッパーのオブジェクト属性を読み取り、修正する必要があります.MyBatisマッパーの内部構成についての知識を上手に把握する必要があります.
  • プラグインのコードの作成は、特に複数のプラグインの階層的なエージェントを考慮して、論理の正確性を保証します.