XML 構成の観点から Spring AOP を理解する

この記事は、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デフォルトで使用される動的プロキシJDKJDK動的プロキシは、リフレクション メカニズムを使用してインターフェイスのプロキシ オブジェクトを作成し、このプロキシ オブジェクトはターゲット インターフェイス メソッドへのすべての呼び出しをインターセプトします。

  • CGLIB プロキシ: ターゲット オブジェクトがインターフェイスを実装していない場合は、ライブラリSpring AOPを使用してターゲット クラスのサブクラスを生成します。 ( ) は、実行時にクラスを拡張し、サブクラスのメソッドをオーバーライドしてメソッド インターセプトを実装する、強力で高性能なコード生成ライブラリです。CGLIBCGLIBCode Generation LibraryJava

どのプロキシ メソッドが使用されるかに関係なく、目的は、元のビジネス ロジック コードを変更せずに、アスペクトによって定義された通知を通じてメソッド実行のさまざまな段階で追加の動作を挿入することです。

1.2 AOP の基本用語

アスペクト: アスペクトは、アスペクト指向プログラミングの中核であり、複数のクラス (ロギング、トランザクション管理など) にまたがる関心事項をモジュール化する構造です。アスペクトには、複数のタイプのアドバイス ( ) と、それらのアドバイスがいつどこで実行されるかを定義するAdvice1 つ以上のポイントカット ( ) を含めることができます。Pointcut

結合ポイント: 結合ポイントはプログラム実行中の特定の位置を表し、Spring AOPこれらの位置をメソッド呼び出しに制限します。簡単に言えば、ジョインポイントはアスペクトアドバイスを挿入できるポイントです。

アドバイス: アドバイスは、接続ポイントでアスペクトによって実行されるアクションを定義します。通知の種類に応じて、これらのアクションは、メソッドの呼び出し前、呼び出し後、結果が返された後、または例外がスローされたときに実行できます。通知の種類には次のものがあります。

  • 事前通知 ( Before advice): メソッドが実行される前に実行されます。
  • 事後通知 ( After advice): 結果に関係なく、メソッドの実行後に実行されます。
  • 復帰後通知 ( After-returning advice): メソッドの実行が成功した後に実行されます。
  • 例外後通知 ( After-throwing advice): メソッドが例外をスローした後に実行されます。
  • サラウンド アドバイス ( Around advice): メソッドの実行の前後に実行され、メソッド呼び出しを完全に制御できます。

ポイントカット (ポイントカット) : ポイントカットは式です。ポイントカット式では、メソッド名、アクセス修飾子、および通知の実行時にどのメソッドをトリガーするかを決定するその他の条件を通じて接続ポイントを照合できます。

ターゲット オブジェクト: 1 つ以上のアスペクトによって通知されるオブジェクト。プロキシ オブジェクトとも呼ばれます。

AOP プロキシ:AOPアスペクト コントラクト (アドバイスとポイントカットによって定義される) を実装するためにフレームワークによって作成されるオブジェクト。ではSpring AOPAOPプロキシはJDKダイナミック プロキシまたはCGLIBプロキシになります。

導入: 導入により、既存のクラスに新しいメソッドまたはプロパティを追加できます。これは、Introduction interfaces1 つ以上の追加インターフェイス ( ) を定義することで実現され、AOPフレームワークはこれらのインターフェイスを実装するターゲット オブジェクトのプロキシを作成します。

それでも抽象的だと感じる場合は、映画制作の別の例を例として挙げてみましょう。

側面

誰かが映画を撮影していると想像してください。映画内の特殊効果 (爆発や特殊照明効果など) は、アプリケーション (ロギングやトランザクション管理など) で処理する必要がある横断的な問題のようなものです。これらの特殊効果は、特定の 1 つのシーンだけでなく、映画のさまざまなシーンで使用されます。 PythonではAOP、これらの「効果」は側面であり、実際のシーン (またはコード) を変更することなく、プログラムの複数の部分に適用できます。

結合ポイント

映画のメタファーを続けると、爆発が発生する瞬間など、各シーンの特定の瞬間が接続点と見なすことができます。プログラミングでは、これは通常、メソッド呼び出しに対応します。

アドバイス

通知とは、「このシーンが始まる前に爆発エフェクトを追加してください」「シーンが終了した後に消煙エフェクトを表示してください」など、監督から特殊効果チームに対する具体的な指示のようなものです。これらの指示は、映画の特定の瞬間にどの特定の効果を追加する必要があるかを特殊効果チームに指示します。ではAOP、これらの「命令」は通知であり、アスペクト (特殊効果) がジョイン ポイント (特定のコード実行瞬間) の前、後、またはその前後で実行される必要があることを指定します。

ポイントカット

予告が監督から特撮チームへの指示だとすれば、その指示に含まれる「徹夜ロケ」などの具体的な条件が切り口となる。ポイントカットは、どの接続ポイント (どの特定のメソッド呼び出しなど) が通知 (影響命令) を受信するかを定義します。

対象オブジェクト

対象となるオブジェクトは、特殊効果を追加する必要があるシーンです。プログラミングの比喩では、これらはアスペクト ロジックの影響を受けるオブジェクト (ログを必要とするクラスなど) です。

AOPプロキシ

AOPプロキシは、特殊効果チームが提供するシーンの制御可能な仮想コピーのようなものです。このコピーは、観客にはオリジナルのシーンと同じように見えますが、実際には、監督が必要に応じて自動的に特殊効果を追加します。プログラミングにおいて、プロキシはAOPフレームワークによって自動的に作成されるオブジェクトであり、ターゲット オブジェクトをラップし、通知 (特殊効果命令) が正しいタイミングで実行されるようにします。

導入

イントロダクションは、元の脚本に存在しないまったく新しいキャラクターやシーンを映画に追加するようなものです。ではAOP、導入により、既存のクラスに新しいメソッドまたはプロパティを追加できます。これは、元のスクリプトを変更せずに映画のコンテンツを拡張するようなものです。

2. XML 構成による Spring AOP の実装

Spring豊富なAOPサポートを提供し、構成を通じてXMLアスペクト、通知 ( advice) およびポイントカット ( ) を定義できます。pointcutsこれにより、ソース コードを変更せずに追加の動作 (ロギング、トランザクション管理など) を追加できます。

実装手順:

  1. Spring の依存関係を追加する:フレームワークと関連する依存関係をプロジェクトにpom.xml追加します。SpringAOP

  2. ビジネス インターフェイスと実装クラスを定義する: ビジネス ロジック インターフェイスとその実装 (単純なサ​​ービス クラスなど) を作成します。

  3. アスペクト クラスを定義する: アスペクト クラスを作成して、事前通知、事後通知、およびラップアラウンド通知を定義します。

  4. 構成 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)を含むアスペクト クラスを定義しますMyServiceperformAction

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 AOPCGLIBproxy-target-classfalseJDK

主なプログラム:

デモアプリケーション.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次の動的プロキシの実装を参照してください。

  1. JdkDynamicAopProxyクラスは、動的プロキシの核とInvocationHandlerなるインターフェイスを実装します。JDKそのinvokeメソッドには、通話を傍受する必要があるかどうかを判断するロジックがあり、対応する通知が通話の前後に適用されます。

  2. プロキシを作成するプロセスは主に、設定に従ってまたはのインスタンスを返すメソッドをProxyFactory呼び出すことによって行われますcreateAopProxy()JdkDynamicAopProxyCglibAopProxy

  3. プロキシの使用: クライアント コードはProxyFactoryプロキシ オブジェクトを取得し、このプロキシ オブジェクトを通じてターゲット メソッドを呼び出します。プロキシ オブジェクトはJdkDynamicAopProxy、 または を内部的に使用してCglibAopProxyこれらの呼び出しをインターセプトし、AOP構成に基づいて通知を実行します。ProxyFactoryプロキシ オブジェクトを取得するプロセスは、通常、特にコンテナ管理をSpring使用する場合、構成と使用法で暗黙的に行われます。このプロセスでは、開発者がクラスを直接呼び出す必要はありません。構成で定義され、アスペクトがそれに適用されると、コンテナーはエージェントの作成と通知の適用のプロセスを自動的に処理します。これは、ポストプロセッサーと名前空間のサポートによって実現され、開発者は通常、アスペクトとアドバイスを宣言的に構成するだけで済みます。SpringAOPProxyFactorySpringbeanSpringSpringAOP

エージェントに会いたい場合は、次の方法がありますCGLIB2

3 番目の1方法は、MyServiceImpl実装されたインターフェイスを削除し、MyServiceメイン プログラムと式expressionの間の対応する場所を変更することですMyServiceImpl
3 番目の方法は、これを実現するために構成ファイルでラベルのプロパティを明示的に設定する2ことです。次のように:Spring<aop:config>proxy-target-class="true"

<aop:config proxy-target-class="true">
    <!--その他の構成は変更されません-->
</aop:config>

デバッグは次のとおりです。

 

ワンクリックトリプル接続へようこそ~

ご質問がございましたら、メッセージを残してください。一緒に話し合って学びましょう。

クリックしてフォローし、できるだけ早くHuawei Cloudの新しいテクノロジーについて学びましょう~

 

RustDesk、詐欺横行のため 国内サービス淘宝網(taobao.com)を停止、ウェブバージョンの最適化作業を再開、 アップルがM4チップをリリース、 高校生が成人式として独自のオープンソースプログラミング言語を作成 - ネチズンのコメント:弁護側は、 ユンフェン氏がアリババを辞任し、将来的にはWindowsプラットフォーム上で Visual Studio Code 1.89を開発する予定であると、 独立系ゲームプログラマーの目標となる Yu Chengdong氏の雇用調整が 「FFmpegの恥の柱」に釘付けになったと正式に発表した15年前、しかし今日彼は私たちに感謝しなければなりません - Tencent QQ Videoは以前の恥を晴らしますか?華中科技大学のオープンソースミラーステーションが外部ネットワークアクセスに正式にオープン
{{名前}}
{{名前}}

おすすめ

転載: my.oschina.net/u/4526289/blog/11106069