AOP プロセスのソース コード分析 - プロセス全体の呼び出しを要求します

今回は、残りのすべての AOP コンテンツについて説明します。実際、プロセス全体を呼び出すリクエストは 1 つだけです。フォローアップの Spring シリーズ記事では、ブートとクラウドのみが残っています。ブートについて説明できるはずです。面接の前に、クラウドについて説明しましたが、私はここ数年保険業界で働いているため、今はあまり明確ではありません。Distributed が今覚えているのは、ダボ システムです。これについては後でのみ説明します。必要がある場合は、インタビューに行きます、クラウドを見て整理します、該当の記事はダメならインタビューが終わるまで待つしかありませんが、今年中には終わるはずです。

ソースコード分析

くだらない話をした後、今度はソース コードの解析を開始します。老人の運転手は車を発進させ、しっかりと座りました。

前回の aop の記事では、エージェントの作成について説明しました (aop は合計で 2 つだけなので、再度リンクをマークする必要はありません)。これは、cglib と jdk の 2 つの方法に分かれています。通常、作成には jdk を使用します。 cglib は大文字と小文字の区別に慣れていないため、使用はお勧めしません。今回お話しするのも、jdkがプロキシを生成した後のリクエスト呼び出しまでのプロセス全体です。

最初に確認する必要があるのは、JdkDynamicAopProxy オブジェクトです。プロキシ オブジェクトは InvocationHandler オブジェクトを継承する必要があるため、invoke メソッドを書き換える必要があります (これについてはダボ サービス呼び出しプロセスで後述します。ここで注意してください)。 request プロキシオブジェクトのメソッドを呼び出す場合は、このinvokeメソッドに行くことになるので、このメソッドを直接フォローします。

インターセプタチェーン、プロキシオブジェクトインスタンス、エグゼキュータ呼び出しメソッドの取得と構築

フォローアップ方法。

まず、これらの基本メソッドはプロキシされないため、メソッドが equals、hashCode などの Object メソッドであるかどうかは無視されます。

次に、プロキシ オブジェクトのインスタンスを取得します。

// 2.拿到我们被代理的对象实例
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);

次に、前に解析したインターセプタ チェーンを取得します。

// 【*】3.获取拦截器链:例如使用@Around注解时会找到AspectJAroundAdvice,还有ExposeInvocationInterceptor
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

この時点で、私は他のコンテンツにはもう注意を払わず、主に通話を傍受することにします。

ここでは、まず ReflectiveMethodInvocation オブジェクト (実行オブジェクトであると理解しています) を構築し、次にそのオブジェクトの continue メソッドを直接呼び出します。

// 6.如果存在拦截器,则创建一个ReflectiveMethodInvocation:代理对象、被代理对象、方法、参数、
// 被代理对象的Class、拦截器链作为参数创建ReflectiveMethodInvocation
MethodInvocation invocation =
  new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
// 7.触发ReflectiveMethodInvocation的执行方法(逐一调用chain中的每一个拦截方法的proceed)
retVal = invocation.proceed();

インターセプタを呼び出し、呼び出された object-proceed メソッドのメソッドを実行します。

続行方法に従ってください。

次に、非常に興味深いロジックが表示されます。ここで説明します。ここでの currentInterceptorIndex 属性の値は -1 で、それに対応するセットはインターセプター チェーンです。これはアドバイザーのセットのサイズ - 1 です。 invokeJoinpointメソッド プロキシオブジェクトが実際に呼び出すメソッドを実行します。

以下は、インターセプター チェーンの添字インデックスに従って特定のインターセプターを取得するものです。このコードの実行後、 currentInterceptorIndex プロパティの値が +1 になることに注意してください。1. プロパティ値がインターセプター チェーンのサイズと等しい場合、プロキシ オブジェクトによって呼び出されるメソッドが実行されます; 2. 特定のインターセプターが取得される限り、プロパティの値はcurrentInterceptorIndex プロパティは +1 になります。

// 1.如果所有拦截器都执行完毕(index是从-1开始,所以跟size - 1比较),则直接使用反射调用连接点(也就是我们原本的方法)
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
    
    
  return invokeJoinpoint();
}

// 2.每次调用时,将索引的值递增,并通过索引拿到要执行的拦截器
Object interceptorOrInterceptionAdvice =
  this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);

それから私たちは見下ろして、迎撃機の転送を直接見ました。

return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);

現時点では、インターセプターは前の記事の構成のままです。つまり、サラウンド、フロント、リアの 3 つのインターセプターですが、スプリングによってデフォルトで最初のインターセプターが追加されます。これは今のところ確認する必要はありません。 。

画像-20220428215745492

ちなみに、インターセプタの順序は、サラウンド、プリポジション、ポストポジションです。同レベルのインターセプタについては、構成解析の順序を先に見てください。最初の構成が最初に実行されますが、ポストポジションはその理由については、引き続きコードを見てみましょう。

インターセプター オブジェクト: AspectJAroundAdvice オブジェクト

ここで、サラウンド インターセプタ オブジェクト、AspectJAroundAdvice オブジェクトの invoke メソッドに直接進みます。

ここでは、最初に 2 つのオブジェクトがカプセル化され、次に invokeAdviceMethod メソッドが直接呼び出されます。このメソッドは非常に曲がりくねっていますが、目的は以前に書いた周囲のインターセプトメソッドを呼び出すことです。

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
    
    
  // 1.这边的mi就是我们的ReflectiveMethodInvocation,
  // ReflectiveMethodInvocation实现了ProxyMethodInvocation接口,所以这边肯定通过校验
  if (!(mi instanceof ProxyMethodInvocation)) {
    
    
    throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
  }
  // 2.将mi直接强转成ProxyMethodInvocation,mi持有代理类实例proxy、被代理类实例target、被代理的方法method等
  ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
  // 3.将pmi封装成MethodInvocationProceedingJoinPoint(直接持有入参mi,也就是ReflectiveMethodInvocation的引用)
  ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
  // 4.拿到pointcut的表达式
  JoinPointMatch jpm = getJoinPointMatch(pmi);
  // 5.调用增强方法
  return invokeAdviceMethod(pjp, jpm, null, null);
}

サラウンド インターセプト メソッドに戻りましょう。ここには、上記の ReflectiveMethodInvocation オブジェクトのカプセル化である ProceedingJoinPoint オブジェクトがあり、ここで呼び出されるプロシージャ メソッドは上記の呼び出しと同じです。

これは、現在のサラウンド インターセプトが実行されていない場合、プロシージャ メソッドに戻り、次のインターセプタに進むことを意味します。この呼び出しがないと、後続のすべてのインターセプターへの呼び出しが行われないことに注意してください。

@Around("pointcut()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws InterruptedException {
    
    
  System.out.println("around advice start");
  try {
    
    
    Object result = proceedingJoinPoint.proceed();
    System.out.println("around advice end");
    return result;
  } catch (Throwable throwable) {
    
    
    throwable.printStackTrace();
    return null;
  }
}

プレインターセプター: MethodBeforeAdviceInterceptor オブジェクト

次に、プレブロッカーについて見ていきます。

プレインターセプター: MethodBeforeAdviceInterceptor オブジェクト。内容は比較的単純です。前に作成したプレメソッドを実行し、その後、プロシージャ メソッドのコールバックを継続します。これは、次のインターセプターの呼び出しに戻るのと同じです。

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
    
    
  this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
  return mi.proceed();
}

ポストインターセプター: AspectJAfterAdvice オブジェクト

引き続き、ポストインターセプター: AspectJAfterAdvice オブジェクトを見ていきます。これもシンプルですが、まず、proceed メソッドに戻り、次のインターセプター呼び出しが成功した後に独自のメソッド呼び出しを実行します。

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
    
    
  try {
    
    
    return mi.proceed();
  }
  finally {
    
    
    invokeAdviceMethod(getJoinPointMatch(), null, null);
  }
}

プロセスの概要

ここで処理を整理すると、1つ目はspringが提供するインターセプタの呼び出しで、インターセプタが実行されるとproceedメソッドが呼び出され、次のサラウンドインターセプタの次にサラウンドインターセプトが行われ、先頭にproceedメソッドが呼び出されます。プレインターセプターは、ポストインターセプターに対して進行メソッドを呼び出します。ここでのインターセプターは完成しているので、以前の currentInterceptorIndex 属性値は 3 になっているはずで、インターセプター チェーンのサイズ -1 も 3 であることに注意してください。その後、前述の invokeJoinpoint メソッドが使用され、プロキシはなくなります。オブジェクトによって呼び出されるメソッド。同時に、呼び出しが完了すると、以下のインターセプタを取得できなくなることに注意してください。これは、ポストインターセプタによって呼び出されるプロシージャメソッドが終了することを意味します。現在、ポストインターセプタは、そのまま進みますので、後方迎撃機による迎撃方法となります。

ここまで言うと少し複雑なので簡単に説明すると、進行メソッド呼び出しインターセプタ 1 (最初の呼び出しを識別し、後続の蓄積) -> Spring デフォルト インターセプタ -> 進行メソッド呼び出しインターセプタ 2 -> サラウンド インターセプタ メソッド -> 進行メソッドインターセプタ 3 の呼び出し -> インターセプタ前メソッド -> メソッドの続行 インターセプタ 4 の呼び出し -> インターセプタ後メソッド -> メソッドの続行 インターセプタ 5 の呼び出し -> ここにはインターセプタがないので、invokeJoinpoint メソッドに進みます。

プロセスは完了しておらず、まだ戻っています。続行メソッド呼び出しインターセプター 5 -> ポストインターセプターメソッド -> 続行メソッド呼び出しインターセプター 4 -> フロントインターセプターメソッド -> 続行メソッド呼び出しインターセプター 3 -> サラウンドインターセプターメソッド -> 続行メソッド呼び出しインターセプター 2 -> Spring デフォルト インターセプター -> メソッド呼び出しインターセプター 1 を続行 -> 最後にインターセプター 1 が終了します。

このようにして、ポストインターセプターの構成が前の構成の後に実行される理由を知ることができます。ポストインターセプターは最初に独自のインターセプトメソッドを実行せず、次のインターセプターを呼び出すため、構成は最後に行われるためです。インターセプターは、前のポストインターセプターの前に実行されます。

要約する

実際の呼び出し処理はそれほど複雑ではありませんが、非常に複雑です。理解すると、これが非常に興味深いことがわかります。ループを使用していませんが、ループの効果も得られ、より賢明です。 . タスクチェーンの設定モードです。

今回はここで終わり、伏線は全て過去にやっていたので内容はかなり減りましたが、ここで参考にした部分も多いです春の続報は様子を見ましょう次の段階で, we should first look at jdk Source code. 今回は面接の質問がたくさんあります。面接が後で行われる場合は、最初に一連の面接の質問を作成します。ターミナルはここです、降りてください。

付録 Spring ソースコード分析シリーズの記事

IOC

時間 記事
2022-03-09 Spring と IOC プロセスの基本概念の簡単な説明
2022-03-11 IOCプロセス分析 - BeanFactoryの作成
2022-03-14 IOC プロセス分析 - BeanFactoyPostProcessor および BeanPostProcessor
2022-03-15 IOC プロセス分析 - インスタンス化と初期化
2022-03-17 IOC プロセス分析 - 循環依存関係

AOP

時間 記事
2022-03-19 Spring と IOC プロセスの基本概念の簡単な説明
2022-03-20 AOP プロセスのソース コード分析 - プロセス全体の呼び出しを要求します

おすすめ

転載: blog.csdn.net/qq_39339965/article/details/124489143