SpringAOPの実装プロセスとソースコードの分析

序文

AOPの文字通りの解釈はアスペクト指向プログラミングです。アスペクト指向プログラミングはプログラミングモデルです。JAVAはオブジェクト指向、つまりOOPであることがわかっています。OOPのオブジェクト指向プログラミングは垂直関係の定義に適していますが、水平関係の定義には適していません。したがって、このOOPの存在のこれらの欠点に対処するために、AOPアスペクト指向プログラミングモデルは、オブジェクト指向の補足として使用されます。これは、ビジネスに関係のない一般的な動作とロジックを抽出してカプセル化するために使用されますが、再利用可能なモジュールとして、このモジュールは「アスペクト」と呼ばれ、システム内の反復コードを減らし、モジュール間の結合を減らし、システムの保守性を向上させます。権限認証、ログ、およびトランザクション処理に使用できます。

AOPプロキシの紹介

これがAOPの実装です。SpringAOPではないことに注意してください。まず、AOP実装の鍵はプロキシモードにあります。AOPプロキシは動的プロキシと静的プロキシに分けられます。ご存知のとおり、SpringAOPは動的プロキシに属します。プロキシであり、静的プロキシの代表はAspectJです。

静的プロキシ

AspectJは静的プロキシの拡張機能です。いわゆる静的プロキシは、AOPフレームワークがコンパイルフェーズ中にAOPプロキシクラスを生成することを意味するため、これもコンパイル時の拡張機能です。静的プロキシはAspectJ(アスペクト)をに織り込みます。コンパイルフェーズ中のjavaバイトコード。、実行すると、拡張AOPオブジェクトになります。

動的プロキシ

SpringAOPは動的プロキシです。いわゆる動的プロキシとは、AOPフレームワークがバイトコードファイルを変更せず、実行するたびにメモリ内にゼロ時間でメソッドのAOPオブジェクトを生成することを意味します。このAOPオブジェクトには、すべてのメソッドが含まれています。ターゲットオブジェクトの。また、特定の側面での処理の強化、および元のオブジェクトのコールバックメソッド。

違い

静的プロキシと動的プロキシの違いは、AOPプロキシオブジェクトを生成するタイミングが異なることです。AspectJの静的プロキシメソッドと比較すると、パフォーマンスは向上しますが、AspectJは処理に特定のコンパイラを必要としますが、SpringAOPは特定のコンパイラを必要としません。処理する。

SpringAOP動的プロキシ

SpringAOPが動的プロキシによって実装されることはすでに知っています。動的プロキシには、JDK動的プロキシとCGLIB動的プロキシの2つの実装方法があります。プロキシクラスがJDK動的プロキシにInvocationHandlerインターフェイスを実装していない場合、SpringAOPはCGLIBを使用してターゲットクラスを動的にプロキシすることを選択します。デフォルトでは、@ EnableAspectJAutoProxyはproxyTargetClass = trueを指定せず(デフォルトはfalseとして記述されていません)、ビジネスクラスはInvocationHandlerインターフェイスインターフェイスを実装します。AOPはJDK動的プロキシを使用します。InvocationHandlerインターフェイスを実装していない場合は、 CGLIBプロキシ、@ EnableAspectJAutoProxyにSpecifyproxyTargetClass = trueがある場合、ビジネスクラスがInvocationHandlerインターフェイスを実装するかどうかに関係なく、CGLIBを使用する必要があります。また、exposeProxy =の場合は参照拡張(メソッドBはメソッドAで呼び出されます)も使用する必要があります。 trueが@EnableAspectJAutoProxyアノテーションで指定されている場合(デフォルトはfalse)、プロキシオブジェクトはスレッド変数に公開されるため、呼び出すときは、プロキシオブジェクトをスレッド変数から取り出し、プロキシオブジェクトを介して呼び出します。メソッドが強化され、Bメソッドも強化されました!トランザクションAと同様に、メソッドはトランザクションメソッドBを呼び出します

  • JDK動的プロキシはインターフェイスプロキシのみを提供し、クラスプロキシをサポートしません。コアはProxyクラスです。InvocationHandleはinvoke()メソッドリフレクションを介してターゲットクラスのコードを呼び出し、横断的なロジックとビジネスを動的に組み合わせます。次にプロキシInvocationHandleを使用して、特定のインターフェイスに準拠するインスタンスを動的に作成し、ターゲットクラスのプロキシオブジェクトを生成します

  • CGLIB(コード生成ライブラリ)は、コード生成用のクラスライブラリであり、実行時に指定されたクラスのサブクラスオブジェクトを動的に生成し、特定のメソッドをオーバーライドし、拡張コードを追加してAOPを実現できます。CGLIBは統合された方法による動的プロキシであるため、クラスがfinalとしてマークされている場合、CGLIBを動的プロキシとして使用することはできません。
    InvocationHandler

AOPの用語

ジョインポイント

強化が必要な方法です

ポイントカット

接続ポイントのセットは、接点と呼ばれます

側面

カットポイント、接続ポイント、通知で構成されるクラスです。

助言

  • 事前通知(前):接続オブジェクトの取得など、ビジネスコードを実行する前にいくつかの操作を実行します
  • 通知後(後):ビジネスコードの実行後に何かを行うと、接続オブジェクトを閉じるなどの例外が発生したかどうかに関係なく実行されます
  • 例外通知(afterThrowing):トランザクションのロールバックなど、ビジネスコードの実行後に例外が発生したときに実行する必要のある操作
  • 返品通知(returning後)、ビジネスコードの実行後に例外なく実行される操作
  • 通知の前後(前後)、現在話しているトランザクションに対応する操作がないため、当面は話しません


強化が必要なビジネスオブジェクトをターゲットにする

織り

ウィービングは、ターゲットクラスへの特定の接続ポイントに拡張機能を追加するプロセスです。ウィービングは鮮やかな用語です。具体的には、プロキシオブジェクトを生成し、アスペクトコンテンツをビジネスプロセスに統合するプロセスです。

プロキシ

クラスがAOPによって作成および拡張された後、プロキシクラスが作成されます。

SpringAOPサンプルコード

目標

@Service
public class A {
    
    
    public void f(){
    
    
        System.out.println("我被执行了");
    }
}

セクション

@Component
@Aspect
public class MyAspect {
    
    
	
	//切点
    @Pointcut("execution(* com.xxx.xxx..*(..))")
    private void anyOldTransfcr(){
    
    }
	//com.xxx.xxx..*(..))这个中描述的每个方法为织入点

	//通知
    @Before("anyOldTransfcr()")
    public void advice(JoinPoint joinPoint){
    
    
        //String methodName=joinPoint.getSignature().getName();
        //System.out.println("执行的方法--->"+methodName);
        //System.out.println(Arrays.asList(joinPoint.getArgs()));
        System.out.println("--------开始执行-------");
    }

	//通知
    @After("anyOldTransfcr()")
    public void advice2(){
    
    
        System.out.println("--------结束执行-------");
    }

}

メインスタートクラス

@EnableAspectJAutoProxy(proxyTargetClass = true)

补充:这里在切面中有个@Aspect这个注解,和上面提到的AspectJ静态代理其实是有点关系的,因为Spring在刚开始实现AOP的时候写的语法是不太友好的,然后Spring后面在迭代的过程中实现AOP就参照了AspectJ的语法,注意是语法!

起動後に結果を実行する
ここに画像の説明を挿入

SpringAOPソースコード実装プロセスの分析

ここでは、SpringIOCのプロセス全体を実行する必要があります。率直に言って、SpringAOPはIOCの特定の部分によって実装される関数です。SpringIOCプロセスについては、Springソースコード分析(プロセスコーミング)を参照してくださいこの記事には、次の図があります。
ここに画像の説明を挿入
プロキシオブジェクトを作成するAOPのプロセスは、上の図に示されています。 Bean作成のプロセス。AOPプロセスは次のように分けられます。

  1. ファセットルックアップ
  2. キャッシュアスペクト通知
  3. ポイントカット式を使用して、現在のBeanがウィービングポイントに属しているかどうかを判別します。
  4. プロキシオブジェクトを作成するための織りのポイントです
  5. 実行対象方式

1.断面検索

Beanが作成されるたびに、AbstractAutowireCapableBeanFactoryクラスのcreateBeanメソッドに入ります。このメソッドにはAOPに関する非常に重要なコードがあります。
ここに画像の説明を挿入

		try {
    
    
			// 给BeanPostProcessors一个返回代理而不是目标bean实例的机会
			Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
			if (bean != null) {
    
    
				return bean;
			}
		}

このコード行を入力した後、ここ
ここに画像の説明を挿入
に入力し続けること
ここに画像の説明を挿入
が特に重要です。AOP関数アノテーションを有効にすると、@ EnableAspectJAutoProxy(proxyTargetClass = true)

このアノテーションは、AspectJAutoProxyRegistrarクラスをインポートするのに役立ちます。AspectJAutoProxyRegistrarクラスを
ここに画像の説明を挿入
入力します。このクラスは、ImportBeanDefinitionRegistrarインターフェイスを実装し、registerBeanDefinitionsを書き換えます。したがって、これは間違いなくBeanの登録に役立ち、このメソッドを入力し
ここに画像の説明を挿入
ここに画像の説明を挿入
てAnnotationAwareAspectJAutoProxyCreator登録します。はい、この豆は非常に重要です!

AOPプロセスコードに戻る

Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);

このコード行は、ibpが上記で挿入されたAnnotationAwareAspectJAutoProxyCreatorである場合、AbstractAutoProxyCreatorクラスのpostProcessBeforeInstantiationに入ります。これは、AnnotationAwareAspectJAutoProxyCreatorのトップレベルがAbstractAutoProxyCreatorを継承するため、AbstractAutoProxyCreatorクラスでpostProcessBeforeInstantiationメソッドを呼び出すことができるためです。
ここに画像の説明を挿入
このメソッドshouldSkip(beanClass、beanName)は、AspectJAwareAdvisorAutoProxyCreatorクラスのshouldSkipメソッドを
ここに画像の説明を挿入
ここに画像の説明を挿入
呼び出しますここで呼び出しは、BeanFactoryAspectJAdvisorsBuilderクラスのbuildAspectJAdvisorsに移動します。このメソッドは、アスペクトクラスを検索し、アスペクトクラスで宣言された通知タイプセットを返します。 。

2.キャッシュアスペクト通知

List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);

このコード行では、通知タイプのオブジェクトコレクションが生成され、アスペクトクラスがキーとして使用され、通知オブジェクトコレクションが現在のアスペクトクラスの値としてキーとして格納されます。
ここに画像の説明を挿入
このステップでは、アスペクトクラスを取得し、アスペクトクラスの通知に従って通知オブジェクトを生成するプロセスに時間がかかるため、解析された通知オブジェクトがキャッシュに追加されます。その後のBeanの作成が​​ここに来ると、最初にaspectBeanNamesが空かどうかを判断します。空の場合は、キャッシュ内のデータをフェッチして、

アスペクトクラスの通知タイプに
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);応じて、通知オブジェクトを生成するコード行がエントリとして使用され、最後にReflectiveAspectJAdvisorFactoryクラスのgetAdviceメソッドに入ります。
ここに画像の説明を挿入

戻るメイン処理に、メインプロセスは、アスペクトクラスを発見したと通知オブジェクトは、元の道と戻り始める、その後、ここではキャッシュされた
ここに画像の説明を挿入
メインプロセスをして豆を作成するプロセスを入力して開始し、
ここに画像の説明を挿入
我々は、スキップのプロセスを属性の割り当て。見て、AOPはそれほど重要ではありません。
ここに画像の説明を挿入
初期化が完了した後、Bean初期化プロセス直接実行さ
ここに画像の説明を挿入
ここに画像の説明を挿入
ます。ここで実行されます。@ EnableAspectJAutoProxyコメントの後にここで開かれるキーがあります。上記のBeanPostProcessorは
ここに画像の説明を挿入
、プロセッサがAnnotationAwareAspectJAutoProxyCreatorになります。ここでAnnotationAwareAspectJAutoProxyCreatorオブジェクトの場合、AbstractAutoProxyCreatorクラスにpostProcessAfterInitializationメソッドを入力します。
ここに画像の説明を挿入
ここに画像の説明を挿入
このコード行は次のようになります。1。アスペクトクラスを検索します。2。アスペクトクラスに通知オブジェクトをキャッシュします。ただし、この操作のみを実行します。最初にshouldSkipメソッドが呼び出されるのはすでにです。1。アスペクトクラスを見つけると2.アスペクトクラスに通知オブジェクトをキャッシュするという2つの操作を完了した後、ロジックはここでは再度実行されませんが、buildAspectJAdvisors()に入った後です。メソッドの場合、buildAspectJAdvisors()の最初の実行に直接戻ります。メソッドによってキャッシュされた通知オブジェクトの結果セット!

3.ポイントカット式を使用して、現在のBeanがウィービングポイントに属しているかどうかを判別します。

ここに画像の説明を挿入
戻り値がnullの場合は、初期化する必要のあるBeanがプロキシオブジェクトを作成する必要がないことを意味します。戻り値がnullでない場合は、プロキシオブジェクトの作成を開始します。

4.プロキシオブジェクトを作成するための織り込みポイントですか

ここに画像の説明を挿入
ここに画像の説明を挿入
ここに画像の説明を挿入
ここに画像の説明を挿入
CGLIBを使用してプロキシオブジェクトを生成ここに画像の説明を挿入
する記事の冒頭にあるSpringAOP紹介では、動的プロキシを完成させるためにCGLIBが使用される理由を説明しています。

5.ターゲットメソッドの実装

ここに画像の説明を挿入
fメソッドが呼び出されると、プロキシオブジェクトに入るのはfメソッドです。
ここに画像の説明を挿入
動的プロキシを完了するためにCGLIBが使用されるため、実行はサポートするプロキシオブジェクトエグゼキュータによっても完了されます。CGLIBはCglibAopProxyとJDKはJdkDynamicAopProxyによって実行されます!実際、ReflectiveMethodInvocationクラスのproceed()メソッドを介して最終的に実行されます!
ここに画像の説明を挿入

//当前拦截器的的索引
this.currentInterceptorIndex

//拦截器集合
this.interceptorsAndDynamicMethodMatchers

//当拦截器索引等于拦截器集合数组长度-1时,执行目标方法
this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1

//执行目标方法
return invokeJoinpoint();

//完成当前拦截器获取和当前拦截器索引++操作
Object interceptorOrInterceptionAdvice =
				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);

//使用当前索引获取到的拦截器执行invoke方法逻辑
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);

这里在invoke方法中传入的是this就是为了递归循环调用proceed()方法自己

ここに画像の説明を挿入
各再帰は、実際には論理コードブロックをコードブロックに追加してから実行します。完全な再帰スケルトンコードは上記のとおりです。上から下に実行してください!

  1. 以前のAOPAronud…
  2. アドバイス前のAOP…
  3. ターゲットメソッドが実行されます...
  4. AOPAronud後…
  5. アドバイス後のAOP…
  6. AOP AfterReturning Advice :(通常の実行が完了したら、現在のメソッドを実行します)
  7. AOP AfterThrowing Advice ...(例外がスローされた場合、現在のメソッドが実行されます)

このコールフローは、責任と再帰の連鎖によって完了します

おすすめ

転載: blog.csdn.net/CSDN877425287/article/details/114328007