この記事は、Huawei クラウド コミュニティ「Spring Master's Road 18 - XML 構成の観点から Spring AOP を理解する」 (著者: Zhuan Yeyang__) から共有されたものです。
1. Spring AOP と動的プロキシ
1.1 Spring AOPと動的プロキシの関係
Spring AOP
動的プロキシを主要なメカニズムとして使用して、アスペクト指向プログラミングを実装します。このメカニズムによりSpring
、実行時にプロキシ オブジェクトを動的に作成できます。これらのプロキシ オブジェクトは、ターゲット オブジェクト (つまり、ビジネス コンポーネント) をラップして、ターゲット オブジェクトのメソッドの呼び出しの前後に追加の動作 (セキュリティ チェック、トランザクション管理、ロギングなど) を挿入します。 。
-
JDK 動的プロキシ:ターゲット オブジェクトが 1 つ以上のインターフェイスを実装する場合に
Spring AOP
デフォルトで使用される動的プロキシJDK
。JDK
動的プロキシは、リフレクション メカニズムを使用してインターフェイスのプロキシ オブジェクトを作成し、このプロキシ オブジェクトはターゲット インターフェイス メソッドへのすべての呼び出しをインターセプトします。 -
CGLIB プロキシ: ターゲット オブジェクトがインターフェイスを実装していない場合は、ライブラリ
Spring AOP
を使用してターゲット クラスのサブクラスを生成します。 ( ) は、実行時にクラスを拡張し、サブクラスのメソッドをオーバーライドしてメソッド インターセプトを実装する、強力で高性能なコード生成ライブラリです。CGLIB
CGLIB
Code Generation Library
Java
どのプロキシ メソッドが使用されるかに関係なく、目的は、元のビジネス ロジック コードを変更せずに、アスペクトによって定義された通知を通じてメソッド実行のさまざまな段階で追加の動作を挿入することです。
1.2 AOP の基本用語
アスペクト: アスペクトは、アスペクト指向プログラミングの中核であり、複数のクラス (ロギング、トランザクション管理など) にまたがる関心事項をモジュール化する構造です。アスペクトには、複数のタイプのアドバイス ( ) と、それらのアドバイスがいつどこで実行されるかを定義するAdvice
1 つ以上のポイントカット ( ) を含めることができます。Pointcut
結合ポイント: 結合ポイントはプログラム実行中の特定の位置を表し、Spring AOP
これらの位置をメソッド呼び出しに制限します。簡単に言えば、ジョインポイントはアスペクトアドバイスを挿入できるポイントです。
アドバイス: アドバイスは、接続ポイントでアスペクトによって実行されるアクションを定義します。通知の種類に応じて、これらのアクションは、メソッドの呼び出し前、呼び出し後、結果が返された後、または例外がスローされたときに実行できます。通知の種類には次のものがあります。
- 事前通知 (
Before advice
): メソッドが実行される前に実行されます。 - 事後通知 (
After advice
): 結果に関係なく、メソッドの実行後に実行されます。 - 復帰後通知 (
After-returning advice
): メソッドの実行が成功した後に実行されます。 - 例外後通知 (
After-throwing advice
): メソッドが例外をスローした後に実行されます。 - サラウンド アドバイス (
Around advice
): メソッドの実行の前後に実行され、メソッド呼び出しを完全に制御できます。
ポイントカット (ポイントカット) : ポイントカットは式です。ポイントカット式では、メソッド名、アクセス修飾子、および通知の実行時にどのメソッドをトリガーするかを決定するその他の条件を通じて接続ポイントを照合できます。
ターゲット オブジェクト: 1 つ以上のアスペクトによって通知されるオブジェクト。プロキシ オブジェクトとも呼ばれます。
AOP プロキシ:AOP
アスペクト コントラクト (アドバイスとポイントカットによって定義される) を実装するためにフレームワークによって作成されるオブジェクト。ではSpring AOP
、AOP
プロキシはJDK
ダイナミック プロキシまたはCGLIB
プロキシになります。
導入: 導入により、既存のクラスに新しいメソッドまたはプロパティを追加できます。これは、Introduction interfaces
1 つ以上の追加インターフェイス ( ) を定義することで実現され、AOP
フレームワークはこれらのインターフェイスを実装するターゲット オブジェクトのプロキシを作成します。
それでも抽象的だと感じる場合は、映画制作の別の例を例として挙げてみましょう。
側面
誰かが映画を撮影していると想像してください。映画内の特殊効果 (爆発や特殊照明効果など) は、アプリケーション (ロギングやトランザクション管理など) で処理する必要がある横断的な問題のようなものです。これらの特殊効果は、特定の 1 つのシーンだけでなく、映画のさまざまなシーンで使用されます。 PythonではAOP
、これらの「効果」は側面であり、実際のシーン (またはコード) を変更することなく、プログラムの複数の部分に適用できます。
結合ポイント
映画のメタファーを続けると、爆発が発生する瞬間など、各シーンの特定の瞬間が接続点と見なすことができます。プログラミングでは、これは通常、メソッド呼び出しに対応します。
アドバイス
通知とは、「このシーンが始まる前に爆発エフェクトを追加してください」「シーンが終了した後に消煙エフェクトを表示してください」など、監督から特殊効果チームに対する具体的な指示のようなものです。これらの指示は、映画の特定の瞬間にどの特定の効果を追加する必要があるかを特殊効果チームに指示します。ではAOP
、これらの「命令」は通知であり、アスペクト (特殊効果) がジョイン ポイント (特定のコード実行瞬間) の前、後、またはその前後で実行される必要があることを指定します。
ポイントカット
予告が監督から特撮チームへの指示だとすれば、その指示に含まれる「徹夜ロケ」などの具体的な条件が切り口となる。ポイントカットは、どの接続ポイント (どの特定のメソッド呼び出しなど) が通知 (影響命令) を受信するかを定義します。
対象オブジェクト
対象となるオブジェクトは、特殊効果を追加する必要があるシーンです。プログラミングの比喩では、これらはアスペクト ロジックの影響を受けるオブジェクト (ログを必要とするクラスなど) です。
AOPプロキシ
AOP
プロキシは、特殊効果チームが提供するシーンの制御可能な仮想コピーのようなものです。このコピーは、観客にはオリジナルのシーンと同じように見えますが、実際には、監督が必要に応じて自動的に特殊効果を追加します。プログラミングにおいて、プロキシはAOP
フレームワークによって自動的に作成されるオブジェクトであり、ターゲット オブジェクトをラップし、通知 (特殊効果命令) が正しいタイミングで実行されるようにします。
導入
イントロダクションは、元の脚本に存在しないまったく新しいキャラクターやシーンを映画に追加するようなものです。ではAOP
、導入により、既存のクラスに新しいメソッドまたはプロパティを追加できます。これは、元のスクリプトを変更せずに映画のコンテンツを拡張するようなものです。
2. XML 構成による Spring AOP の実装
Spring
豊富なAOP
サポートを提供し、構成を通じてXML
アスペクト、通知 ( advice
) およびポイントカット ( ) を定義できます。pointcuts
これにより、ソース コードを変更せずに追加の動作 (ロギング、トランザクション管理など) を追加できます。
実装手順:
-
Spring の依存関係を追加する:フレームワークと関連する依存関係をプロジェクトに
pom.xml
追加します。Spring
AOP
-
ビジネス インターフェイスと実装クラスを定義する: ビジネス ロジック インターフェイスとその実装 (単純なサービス クラスなど) を作成します。
-
アスペクト クラスを定義する: アスペクト クラスを作成して、事前通知、事後通知、およびラップアラウンド通知を定義します。
-
構成 XML :
applicationContext.xml
アスペクト、ビジネスbean
、およびAOP
関連タグを XML で構成します。
2.1 Spring 依存関係を追加する
pom.xml
ファイルに次の依存関係を追加します
<依存関係> <依存関係> <groupId>org.springframework</groupId> <artifactId>スプリングコンテキスト</artifactId> <バージョン>5.3.10</バージョン> </依存関係> <依存関係> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <バージョン>5.3.10</バージョン> </依存関係> <依存関係> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <バージョン>1.9.6</バージョン> </依存関係> </依存関係>
2.2 ビジネスインターフェースと実装クラスの定義
まず、ビジネス ロジック インターフェイスMyService
とその実装を定義しますMyServiceImpl
。
MyService.java:
パッケージ com.example.demo.aop; パブリック インターフェイス MyService { String PerformAction(String input) は例外をスローします。 }
MyServiceImpl.java:
パッケージ com.example.demo.aop; パブリック クラス MyServiceImpl は MyService を実装します { @オーバーライド public String PerformAction(String action) throws Exception { System.out.println("MyService でのアクションの実行: " + action); if ("throw".equals(action)) { throw new Exception("MyService からの例外"); } return "実行されたアクション: " + アクション; } }
2.3 アスペクトクラスの定義
次に、メソッドが実行される前に実行されるMyAspect
プレアドバイス ( advice
)を含むアスペクト クラスを定義します。MyService
performAction
MyAspect.java:
パッケージ com.example.demo.aop; org.aspectj.lang.ProceedingJoinPoint をインポートします。 パブリック クラス MyAspect { // 事前通知 public void beforeAdvice() { System.out.println("アドバイスを実行する前に!"); } // 通知を投稿する public void afterAdvice() { System.out.println("アドバイスの実行後!"); } // 復帰後に通知 public void afterReturningAdvice(Object retVal) { System.out.println("アドバイスを返した後は実行中です! 戻り値: " + retVal); } // 例外後の通知 public void afterThrowingAdvice(Throwable ex) { System.out.println("スロー後のアドバイスが実行中です! 例外: " + ex.getMessage()); } //サラウンド通知 public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("アドバイスの前後: メソッド実行前"); オブジェクトの結果 = null; 試す { 結果 = joinPoint.proceed(); } ついに { System.out.println("アドバイスの前後: メソッド実行後"); } 結果を返します。 } }
2.4 構成 XML
最後に、Spring
構成ファイルでapplicationContext.xml
上記bean
およびAOP
関連するコンテンツを構成する必要があります。
applicationContext.xml:
<?xml バージョン="1.0" エンコーディング="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 上記は XML ファイルのヘッダー宣言で、ファイルのバージョンとエンコーディング タイプを定義し、Spring Bean と AOP の名前空間を導入します。 これらの名前空間を通じて、XML で <bean> タグと <aop:*> タグを使用できます。 --> <!-- Bean 定義 --> <bean id="myService" class="com.example.demo.aop.MyServiceImpl"/> <bean id="myAspect" class="com.example.demo.aop.MyAspect"/> <!-- AOP 構成 --> <aop:config> <!-- アスペクトとその通知を定義する --> <aop:aspect id="myAspectRef" ref="myAspect"> <!-- ポイントカットを定義し、メソッドの実行時にトリガーされる通知を指定します --> <aop:pointcut id="serviceOperation"expression="execution(* com.example.demo.aop.MyService.performAction(..))"/> <!-- 事前通知を適用し、メソッド実行前の操作を指定します --> <aop:before method="beforeAdvice" pointcut-ref="serviceOperation"/> <!-- 事後通知を適用し、メソッドが正常に実行されたか例外がスローされたかにかかわらず、メソッド実行後の操作を指定します --> <aop:after method="afterAdvice" pointcut-ref="serviceOperation"/> <!-- アプリケーションが戻った後の通知、指定されたメソッドが正常に実行されて返された後の操作 --> <aop:after-returning method="afterReturningAdvice" pointcut-ref="serviceOperation" return="retVal"/> <!-- アプリケーション例外後の通知、指定されたメソッドが例外をスローした後のアクション --> <aop:after-throwing method="afterThrowingAdvice" pointcut-ref="serviceOperation" throw="ex"/> <!-- メソッド実行の前後に完全な制御を提供するために周囲の通知を適用します --> <aop:around メソッド="aroundAdvice" pointcut-ref="serviceOperation"/> </aop:アスペクト> </aop:config> </豆>
myService : これはビジネス ロジックであり、クラスのインスタンスbean
を指します。MyServiceImpl
myAspect : これは、クラスのインスタンスbean
を指すアスペクトです。MyAspect
<aop:config>
: これはAOP
構成のルート要素であり、AOP
アスペクト定義、ポイントカット、通知メソッドを含むすべての構成をこの要素内で定義する必要があります。
アスペクト:<aop:aspect>
要素を通じて定義され、一連のアドバイス ( advice
) と 1 つ以上のポイントカット ( pointcut
) が含まれます。この要素は、アスペクト クラス (通知ロジックを含むクラス) を特定の操作 (ターゲット オブジェクトをいつどのように強化するか) に関連付けます。
Pointcut :<aop:pointcut>
要素定義を通じて、実行時にどのメソッドが通知をトリガーするかを正確に制御する必要がある場合は、pointcut を定義する必要があります。ポイントカット式では、メソッド名、パラメータの型、注釈などによってメソッドを非常に正確に指定できます。expression
ポイントカットの式を定義し、ポイントカットの一致ルールを指定します。ここでの表現はexecution(* com.example.demo.aop.MyService.performAction(..))
、ポイントカットがMyService
インターフェイス内のメソッドの実行と一致しperformAction
、ポイントカットはJoin Point
通知が適用される接続ポイント (メソッド呼び出しなど) を指定するために使用されることを意味します。
式の解析についてexecution(* com.example.demo.aop.MyService.performAction(..))
実行: 最も一般的に使用されるポイントカット関数であり、メソッド実行の接続ポイントを照合するために使用されます。
※:メソッドの戻り値の型が任意であることを示します。
com.example.demo.aop.MyService.performAction : インターフェース名とメソッド名のフルパスを指定します。
(…) : メソッドのパラメータが任意であり、メソッドに含まれるパラメータの数に関係なく一致することを示します。
-
結合ポイント:結合ポイントは、メソッド呼び出しなど、プログラム実行中の特定のポイントを指します。 結合ポイントは、pointcut () 式によって
Pointcut
識別および照合されます。execution(* com.example.demo.aop.MyService.performAction(..))
この式は、結合ポイントの明示的なセット (つまり、MyService
インターフェイスのメソッドのすべての呼び出し) を指定するポイントカットを定義しますperformAction
。この例では、MyService
インターフェイスperformAction
メソッドの呼び出しが接続ポイントとなる可能性があります。performAction
メソッドが呼び出されるたびに、結合ポイントに到達します。この接続ポイントは、アプリケーションにタイミングが通知される場所です。 -
アドバイス: これは、
AOP
特定の時間にアクションを実行することでメソッドの実行を強化するアクションです。method
この属性は、上で定義されたポイントカットを参照して、ポイントカットが一致したときに実行されるアスペクトのメソッド名を指定しますpointcut-ref
。たとえば、beforeAdvice
ターゲット メソッドが実行されるperformAction
前に呼び出されるメソッドは次のとおりです。これは、MyService.performAction(..)
メソッドが呼び出されるたびに、beforeAdvice
そのメソッドが最初に実行されることを意味します。
一言でまとめると:Spring AOP
特定のメソッドをいつ(接続ポイント)、どのように(通知)強化するかのルール(カットポイント)を各面で定義することで、元のビジネスロジックを変更することなく、コードのモジュール化と懸念の分離を実現します。 。
このようにして、Spring AOP
元のビジネス ロジック コードを変更せずに、特定のメソッドの実行前、実行後、または実行前後に挿入されるカスタム ロジックを定義できます。これは、特にアプリケーションの複数の部分 (ロギング、トランザクション管理など) にまたがる横断的な懸念事項の分離を実現するための強力なメカニズムです。
<aop:config>
に設定されている場合は、
<aop:config proxy-target-class="true"> <!--その他の構成は変更されません--> </aop:config>
これを設定すると、ターゲット オブジェクトがインターフェイスを実装している場合でも、プロキシが優先的に使用されますproxy-target-class="true"
。デフォルトでは、このプロパティを設定する必要はありません。設定されている場合は、動的プロキシが使用されます。Spring AOP
CGLIB
proxy-target-class
false
JDK
主なプログラム:
デモアプリケーション.java:
パッケージ com.example.demo; com.example.demo.aop.MyService をインポートします。 org.springframework.context.support.ClassPathXmlApplicationContext をインポートします。 パブリック クラス DemoApplication { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); MyService myService = (MyService) context.getBean("myService"); 試す { System.out.println(myService.performAction("normal")); } catch (例外 e) { e.printStackTrace(); } System.out.println("=========================); 試す { System.out.println(myService.performAction("throw")); } catch (例外 e) { System.out.println("メインで例外がキャッチされました: " + e.getMessage()); } context.close(); } }
操作結果:
AOP
動的プロキシ テクノロジとこれらの概念を組み合わせることで、Spring AOP
アプリケーションに非侵入的な方法で横断的な懸念事項のサポートを提供できるため、開発者はこれらの懸念事項をモジュール化し、ビジネス ロジック コンポーネントに焦点を当ててシンプルに保つことができます。
動的プロキシに興味がある場合は、次のようなインターフェイスが実装されているJDK
ため、再度デバッグできます。public class MyServiceImpl implements MyService
ここで確認できる主要なクラスとインターフェイスについて簡単に説明しましょう。
ProxyFactory : これは、Spring AOP
プロキシ オブジェクトの作成に使用されるファクトリ クラスです。ターゲット オブジェクトがインターフェイスを実装しているかどうかに基づいて、JDK
動的プロキシを使用するかプロキシを使用するかを決定できますCGLIB
。
AopProxy : このインターフェイスは、プロキシ オブジェクトを取得するためのメソッドを定義します。これには、 JdkDynamicAopProxy
(JDK
動的プロキシ用) とCglibAopProxy
(CGLIB
プロキシ用)の2 つの主な実装があります。
JdkDynamicAopProxy :AopProxy
インターフェイスを実装し、JDK
動的プロキシ テクノロジを使用してプロキシを作成します。これはInvocationHandler
インターフェイスを実装し、プロキシ オブジェクトへのすべてのメソッド呼び出しをインターセプトします。
CglibAopProxy :AopProxy
インターフェイスも実装しますが、CGLIB
プロキシ オブジェクトの作成にライブラリを使用します。インターフェイスを実装していないクラスの場合、Spring
このメソッドがプロキシの作成に選択されます。
Spring AOP
ソース コードを詳しく理解したい場合は、これら 2 つのクラスの実装をJdkDynamicAopProxy
直接表示できます。CglibAopProxy
これはこの記事の焦点ではありません。以下について簡単に説明します。
たとえば、JdkDynamicAopProxy
次の動的プロキシの実装を参照してください。
-
JdkDynamicAopProxy
クラスは、動的プロキシの核とInvocationHandler
なるインターフェイスを実装します。JDK
そのinvoke
メソッドには、通話を傍受する必要があるかどうかを判断するロジックがあり、対応する通知が通話の前後に適用されます。 -
プロキシを作成するプロセスは主に、設定に従ってまたはのインスタンスを返すメソッドを
ProxyFactory
呼び出すことによって行われます。createAopProxy()
JdkDynamicAopProxy
CglibAopProxy
-
プロキシの使用: クライアント コードは
ProxyFactory
プロキシ オブジェクトを取得し、このプロキシ オブジェクトを通じてターゲット メソッドを呼び出します。プロキシ オブジェクトはJdkDynamicAopProxy
、 または を内部的に使用してCglibAopProxy
これらの呼び出しをインターセプトし、AOP
構成に基づいて通知を実行します。ProxyFactory
プロキシ オブジェクトを取得するプロセスは、通常、特にコンテナ管理をSpring
使用する場合、構成と使用法で暗黙的に行われます。このプロセスでは、開発者がクラスを直接呼び出す必要はありません。構成で定義され、アスペクトがそれに適用されると、コンテナーはエージェントの作成と通知の適用のプロセスを自動的に処理します。これは、ポストプロセッサーと名前空間のサポートによって実現され、開発者は通常、アスペクトとアドバイスを宣言的に構成するだけで済みます。Spring
AOP
ProxyFactory
Spring
bean
Spring
Spring
AOP
エージェントに会いたい場合は、次の方法がありますCGLIB
2
3 番目の1
方法は、MyServiceImpl
実装されたインターフェイスを削除し、MyService
メイン プログラムと式expression
の間の対応する場所を変更することですMyServiceImpl
。
3 番目の方法は、これを実現するために構成ファイルでラベルのプロパティを明示的に設定する2
ことです。次のように:Spring
<aop:config>
proxy-target-class="true"
<aop:config proxy-target-class="true"> <!--その他の構成は変更されません--> </aop:config>
デバッグは次のとおりです。
ワンクリックトリプル接続へようこそ~
ご質問がございましたら、メッセージを残してください。一緒に話し合って学びましょう。
RustDesk、詐欺横行のため 国内サービス淘宝網(taobao.com)を停止、ウェブバージョンの最適化作業を再開、 アップルがM4チップをリリース、 高校生が成人式として独自のオープンソースプログラミング言語を作成 - ネチズンのコメント:弁護側は、 ユンフェン氏がアリババを辞任し、将来的にはWindowsプラットフォーム上で Visual Studio Code 1.89を開発する予定であると、 独立系ゲームプログラマーの目標となる Yu Chengdong氏の雇用調整が 「FFmpegの恥の柱」に釘付けになったと正式に発表した。15年前、しかし今日彼は私たちに感謝しなければなりません - Tencent QQ Videoは以前の恥を晴らしますか?華中科技大学のオープンソースミラーステーションが外部ネットワークアクセスに正式にオープン