MyBatisインターセプターの紹介
MyBatisはプラグイン機能を提供します。プラグインと呼ばれますが、実際にはインターセプター機能です。では、インターセプターはMyBatisを傍受するものは何ですか?
公式ウェブサイトに行って見てみましょう。
MyBatisを使用すると、マッピングされたステートメントの実行中に特定の時点でコールを傍受できます。デフォルトでは、メソッドコールMyBatisでプラグインをインターセプトできます。
更新、クエリ、コミット、ロールバック、その他の方法、他のインターフェイスの方法など、エグゼキューターインターフェイスを傍受できるいくつかの方法を見てきました
待って。
全体的な要約は次のとおりです。
インターセプターの使用
インターセプターの紹介と構成
まず、MyBatisインターセプターのインターフェイス定義を見てみましょう。
パブリックインターフェイスインターセプター{Object Intercept(Invocation Invocation)Throws Throwable;オブジェクトプラグイン(オブジェクトターゲット); void setProperties(プロパティプロパティ);}比較的簡単で、3つの方法しかありません。 MyBatisには、デフォルトでインターセプターインターフェイスの実装クラスがなく、開発者はニーズを満たすインターセプターを実装できます。
MyBatisの公式Webサイトからのインターセプターの例は次のとおりです。
@intercepts({@signature(type = executor.class、method = "update"、args = {mappedstatement.class、object.class})})public class exhomplugin emplugent interpector {public object intercept(招集の呼び出し)スロー可能{return volation.proceed.proceed(); } public Object Plugin(Object Target){return plugin.wrap(ターゲット、this); } public void setProperties(プロパティプロパティ){}}グローバルXML構成:
<プラグイン> <プラグインInterceptor = "org.format.mybatis.cache.interceptor.exampleplugin"> </plugin> </plugins>
このインターセプターは、エグゼキューターインターフェイスの更新方法をインターセプトします(実際、それはSQLSessionの追加、削除、および変更操作です)。実行者を実行するすべての更新方法は、インターセプターによって傍受されます。
ソースコード分析
このコードの背後にあるソースコードを分析しましょう。
まず、ソース - > configファイルから分析を開始します。
XMLCONFIGBUILDERは、MyBatisグローバル構成ファイルのプラグインセレメントプライベートメソッドを解析します。
private void pluginelement(xnode parent)スロー例外{if(parent!= null){for(xnode child:parent.getChildren()){string interceptor = child.getStringAttribute( "interceptor");プロパティプロパティ= child.getChildRenasProperties(); Interceptor InterceptorInstance =(Interceptor)ResolveClass(Interceptor).NewInstance(); InterceptorInstance.setProperties(プロパティ); configuration.adddInterceptor(interceptorInstance); }}}特定の解析コードは実際には比較的単純なので、投稿しません。主に、リフレクションによってプラグインノードのインターセプター属性で表されるクラスをインスタンス化します。次に、グローバル構成クラス構成のAddInterceptorメソッドを呼び出します。
public void addInterceptor(Interceptor Interceptor){Interceptorchain.AddInterceptor(Interceptor); }このInterceptorchainは構成の内部プロパティであり、そのタイプはインターセプターチェーンであるInterceptorchainです。その定義を見てみましょう。
public class interceptorchain {private final list <interceptor> interceptors = new ArrayList <Interceptor>(); public Object Plaginall(Object Target){for(interceptor interceptor:interceptor){ターゲット= interceptor.plugin(ターゲット); }ターゲットを返します。 } public void addInterceptor(interceptor interceptor){interceptors.add(interceptor); } public List <Interceptor> getInterceptors(){return collections.unmodifiablelist(interceptors); }}インターセプター構成の解析とインターセプターの所有権を理解したので、インターセプターがこれらの方法をインターセプトする理由を振り返ってください(エグゼクタ、パラメーターハンドラー、結果の部分):
public ParameterHandler newParameterHandler(MappedStatement MappedStatement、Object ParameterObject、BoundsQl BundsQl){parameterHandler parameterHandler = mappedStatement.getLang()。createparameterhandler(MappedStatement、parameterObject、Boundsql); parameterHandler =(parameterhandler)interceptorchain.pluginall(parameterhandler); parameterhandler;} public resultsethrler newresultsetherler(エグゼキューターエグゼキューター、マッピングステートメントマッピングステートメント、rowbounds rowbounds、parameterhandler parameterhandler、resulthandler resulthandler、boundsql boundsql) Resulthandler、Boundsql、Rowbounds); resterShandler =(resultsethandler)interceptorchain.pluginall(resultethandler); return resultsEthandler;} public StatementHandler newStatementHandler(エグゼキューターエグゼキューター、マッピングステートメントマッピングステートメント、オブジェクトパラメーターオブジェクト、ロウバウンドロウバウンド、再rthandler resultherler、boundsql boundsql) Boundsql); StatementHandler =(StatementHandler)InterceptOrchain.Pluginall(StatementHandler); return StatementHandler;} public executor newexecutor(トランザクショントランザクション、executortype executortype、boolean autocommit){executortype = executortype == null? DefaultExeCutortype:ExecutorType; executortype = executortype == null? executortype.simple:executortype;執行者の執行者。 if(executortype.batch == executortype){executor = new batchexecutor(this、transaction); } else if(executortype.reuse == executortype){executor = new reuseexecutor(this、transaction); } else {executor = new SimpleExecutor(this、transaction); } if(cacheenabled){executor = new cachingexecutor(executor、autocommit); } executor =(executor)interceptorchain.pluginall(executor);執行者を返す;}上記の4つの方法はすべて構成方法です。これらのメソッドは、MyBatisの操作で実行されます(追加、削除、変更、およびクエリ)。実行の順序は、執行者、parameterhandler、resterethandler、statementhandler(parameterhandlerおよびresultesandlerがStatementhandler [3つの利用可能な実装クラスCallableStatementhandler、preatableStatementementementhandler]を作成するときに作成されます。
これらの4つのメソッドが対応するオブジェクトをインスタンス化すると、インターセプトマーシュのプラグインールメソッドを呼び出します。前述のように、Interceptorchainのプラグインールが導入されました。これは、すべてのインターセプターをトラバースし、各インターセプターのプラグインメソッドを呼び出すことです。注:インターセプターのプラグインメソッドの返品値は、元のオブジェクトに直接割り当てられます。
StatementHandlerは傍受できるため、このインターフェイスは主にSQL構文の構築を扱います。したがって、たとえば、ページングの機能はインターセプターで実装できます。インターセプターのプラグインメソッドのStatementHandlerインターフェイス実装クラスでSQLを処理するだけで、リフレクションを使用して実装できます。
MyBatisは、インターセプターに関する@interceptsと@signatureの注釈も提供します。公式ウェブサイトの例は、プラグインクラスの使用を含むこれら2つの注釈を使用することです。
@overridepublicオブジェクトプラグイン(オブジェクトターゲット){return plugin.wrap(ターゲット、this);}これら3つの「新しい組み合わせ」のソースコードを分析しましょう。まず、プラグインクラスのラップメソッドを見てみましょう。
public staticオブジェクトラップ(オブジェクトターゲット、インターセプターインターセプター){map <class <?>、set <method >> signaturemap = getsignatureMap(interceptor); class <?> type = target.getClass(); class <? if(interfaces.length> 0){proxy.newproxyinstance(type.getClassLoader()、インターフェイス、新しいプラグイン(ターゲット、インターフェイス、SignatureMap)); }ターゲットを返します;}プラグインクラスは、InvocationHandlerインターフェイスを実装します。明らかに、JDK自体が提供する動的プロキシクラスがここに返されることがわかります。この方法で呼ばれる他の方法を分析しましょう。
GetSignAtureMapメソッド:
private static Map <class <? if(interceptSannotation == null){// Issue#251 Throw new PlugineXception( "no @intercepts annotationはinterceptor" + interceptor.getClass()。getName()); } signature [] sigs = interceptsannotation.value(); map <class <? for(signature sig:sigs){set <method> method = signaturemap.get(sig.type()); if(methods == null){method = new Hashset <method>(); signaturemap.put(sig.type()、method); } try {method method = sig.type()。getMethod(sig.method()、sig.args()); methods.add(method); } catch(nosuchmethodexception e){throw new pluginexception( "" + sig.type() + "named" + sig.method() + "。原因:" + e、e); }} signaturemapを返します;}GetSignAtureMapメソッド説明:最初に、インターセプタークラスの@Interceptorsアノテーションを取得し、次にこのアノテーションの属性の@Signatureアノテーションコレクションを取得し、このコレクションをトラバースし、@Signature Annotationの型属性(クラスタイプ)を取り出し、次にこのタイプに基づくメソッド属性に基づいたメソッド属性を取得します。 @Signature属性は@Interceptorsによって注釈されているため、最終的にkeyとvalueとしてタイプのマップをset <メソッド>として返します。
@intercepts({@signature(type = executor.class、method = "update"、args = {mappedstatement.class、object.class})}))))たとえば、@Interceptorsアノテーションは、エグゼキューターとしてキーをコレクションとしてバリューとして返します(このコレクションには1つの要素のみがあります。つまり、メソッドインスタンス、このメソッドインスタンスはエグゼキューターインターフェイスの更新メソッドであり、このメソッドにはタイプマッピングステートメントとオブジェクトのパラメーターがあります)。このメソッドインスタンスは、 @signatureのメソッドおよびargs属性に基づいて取得されます。 argsパラメーターがタイプタイプのメソッドメソッドに対応していない場合、例外がスローされます。
GetAllInterFacesメソッド:
private static class <? while(type!= null){for(class <?> c:type.getInterfaces()){if(signaturemap.containskey(c)){interfaces.add(c); }} type = type.getSuperClass(); } return interfaces.toarray(new class <?> [interfaces.size()]);}getAllInterfacesメソッド説明:ターゲットインスタンスターゲット(このターゲットは、MyBatisインターセプターが前述のようにインターセプトできるクラス、エグゼキューター、パラメーターハンドラー、結果のハンドラー、ステートメントハンドラー)とその親クラスを、SignatureMapのターゲット実装を含むインターフェイスアレイを返します。
したがって、プラグインクラスの関数は、@interceptorsアノテーションに基づいた注釈付き属性の属性@Signatureアレイを取得し、リフレクションを使用して、各@Signature Annotedのタイプ、方法、およびARGS属性に従って対応するメソッドを見つけます。最後に、呼び出されたターゲットオブジェクトによって実装されたインターフェイスに基づいて、元のターゲットオブジェクトを置き換えるためにプロキシオブジェクトを返すかどうかを決定します。
たとえば、MyBatisの公式Webサイトでは、ConfigurationがnewExecutorメソッドを呼び出す場合、exectorインターフェイスの更新(マッピングステートメントMS、オブジェクトパラメーター)メソッドがインターセプターによって傍受されます。したがって、エグゼキューターではなく、プロキシクラスプラグインで終了が返されます。この方法でメソッドを呼び出すと、プロキシクラスの場合、実行されます。
パブリックオブジェクトInvoke(Object Proxy、Method Method、Object [] Args)Throws {try {set <method> method = signaturemap.get(method.getDeclaringClass()); if(method!= null && methods.contains(method)){return interceptor.intercept(new Invocation(Target、Method、Args)); } return method.invoke(ターゲット、args); } catch(Exception e){exceptionutil.unwrapthrowable(e); }}そうです、対応するメソッドが見つかり、プロキシ化されている場合、インターセプターインターフェイスのインターセプターメソッドが実行されます。
この呼び出しクラスは次のとおりです。
パブリッククラスの呼び出し{プライベートオブジェクトターゲット。プライベートメソッドメソッド。プライベートオブジェクト[] args; public Invocation(Object Target、Method Method、Object [] args){this.target =ターゲット; this.method = method; this.args = args; } public Object getTarget(){return target; } public Method getMethod(){return method; } public Object [] getArgs(){return args; } public object roce()throws InvocationTargetException、IllegalaccesSexception {return method.invoke(ターゲット、args); }}そのプロセス方法は、元の方法(プロキシなし)を呼び出すことです。
要約します
MyBatisインターセプターインターフェイスによって提供される3つの方法のうち、プラグイン法は、特定のプロセッサ(ハンドラー)の構築プロセスで使用されます。インターセプター法は、プロキシクラスの実行を処理するために使用されます。 SetPropertiesメソッドは、インターセプタープロパティを設定するために使用されます。
実際、@interceptorsと@signature annotationsおよびプラグインクラスを使用するためにMybatisの公式Webサイトが提供する方法は、この方法で必ずしも直接使用されるわけではありません。また、これら3つのクラスを放棄し、プラグインメソッド内のターゲットインスタンスのタイプに基づいて、対応する操作を直接実行することもできます。
全体として、MyBatisインターセプターはまだ非常に単純です。インターセプター自体はあまり多くの知識ポイントを必要としませんが、インターセプターを学習するには、インターセプターには各インターフェイスの知識ポイントが含まれるため、MyBatisの各インターフェイスに精通する必要があります。
要約します
上記は、編集者によって導入されたMyBatisインターセプターの原則の調査です。私はそれが誰にでも役立つことを願っています。ご質問がある場合は、メッセージを残してください。編集者は、すべての人に時間内に返信します。 wulin.comのウェブサイトへのご支援ありがとうございます!