(未完) [春のトピック] SringAOP の基礎原理の分析 - フェーズ 3 (AOP)

序文

予備知識

代理店パラダイム

プロキシモードとは何ですか? プロキシ モードの説明: 他のオブジェクトにプロキシを提供して、このオブジェクトへのアクセスを制御し、クラス内のメソッドを拡張し、プログラムを拡張します。
【エージェントパラダイム】って聞いたことありますか?その疑似コードは通常次のとおりです: (Cglibダイナミックプロキシで採用されている方式

class 被代理类 {
    
    
	function 方法A();
}

class 代理类 <<继承>> 被代理类 {
    
    
	被代理类 targetObject;
	
	@Override
	function 方法A() {
    
    
		// 这里写被加强的逻辑
		targetObject.方法A();
		// 也可以在这里写
	}
}

または、Java コードで次のように表現します。

// 被代理对象
public class ProxyTarget {
    
    
    public void run() {
    
    
        System.out.println("这是普通对象的run");
    }
}


// 代理对象
public class ProxyModel extends ProxyTarget {
    
    
    private ProxyTarget proxyTarget;

    public void setProxyTarget(ProxyTarget proxyTarget) {
    
    
        this.proxyTarget = proxyTarget;
    }

    @Override
    public void run() {
    
    
        System.out.println("我代理对象可以在这里做加强---1");
        super.run();
        System.out.println("我代理对象也可以在这里做加强---2");
    }
}

ただし、実際にはプロキシ モードを実装する別の方法がありますが、その前提となるのは、プロキシされるクラスにインターフェイスがあることです。そのプロキシ パラダイムの擬似コードは次のとおりです: (JDKダイナミックプロキシで採用されている方式

class 被代理类 <<实现>> 接口A {
    
    
	function 实现自接口A的方法();
}

class 代理类 <<实现>> 接口A {
    
    
	被代理类 targetObject;
	
	@Override
	function 实现自接口A的方法() {
    
    
		// 这里写被加强的逻辑
		targetObject.方法A();
		// 也可以在这里写
	}
}

上で説明した 2 つのプロキシ方法は細かい点です。後述する特定のパラメータは、プロキシ方法の選択に影響します。

Spring AOP の理解

OOP はプログラミングの考え方の一種であるオブジェクト指向プログラミングを意味し、AOP は同じくプログラミングの考え方の一種であるアスペクト指向プログラミングを意味します。 Spring は AOP の実行を容易にする一連のメカニズムを提供するため、このメカニズムを Spring AOP と呼ぶこともできます。

Spring動的プロキシの実装

あまり注目されないのではないかと思い、【授業内容】に記載させていただきました。

授業内容

1. ダイナミックプロキシの実現

経験豊富な友人なら、Java で一般的に使用される 2 つのプロキシ メソッド、つまり JDK ダイナミック プロキシと Cglib ダイナミック プロキシがあることを知っているはずだと思います。これら 2 つの動的エージェントの簡単な使用例を簡単に説明します。
言語が両方のプロキシになる前に、基本クラスが必要です。次のように:

@Component
public class UserService {
    
    

    public void test() {
    
    
        System.out.println("这是UserService的一个普通方法");
    }
}

プロキシを開始する前に、なぜ再度プロキシが必要なのでしょうか?
プロキシ モードの説明: 他のオブジェクトにプロキシを提供して、このオブジェクトへのアクセスを制御し、クラス内のメソッドを拡張し、プログラムを拡張します。
つまり、特定のメソッドを強化する必要があるが、元のコードを変更したくない場合 (設計原則、つまりオープンとクローズの原則に従う) には、通常、プロキシの使用を検討します。Spring では、このプロキシ モードを実装するために次の 2 つのソリューションが使用されます。

1.1 Cglib 動的プロキシ

上記の UserService で、新しい UserService オブジェクトを作成して test() メソッドを実行すると、結果は明らかです。UserService クラスのソース コードを変更せずに test() にロジックを追加する場合は、Cglib 動的プロキシ メカニズムを使用して UserService オブジェクトを作成できます。次に例を示します。

 public static void main(String[] args) throws IOException {
    
    
        UserService target = new UserService();
        target.test();


        System.out.println("下面开始是cglib之后的逻辑了");

        // cglib
        {
    
    
            // 使用cglib增强类
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(UserService.class);

            // 定义额外的增强逻辑,增强对象
            enhancer.setCallbacks(new Callback[]{
    
    new MethodInterceptor() {
    
    
                @Override
                public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    
    
                    System.out.println("before...");
                    Object result = methodProxy.invoke(target, objects);
                    System.out.println("after...");
                    return result;
                }
            }});

            // 动态代理所创建出来的UserService对象
            UserService userService = (UserService) enhancer.create();

            // 执行这个userService的test方法时,就会额外会执行一些其他逻辑
            userService.test();
        }
    }

取得されるのはすべて UserService オブジェクトですが、test() メソッドを実行した場合の効果は異なり、プロキシによってもたらされる効果です。
以上が親クラスと子クラスを元にcglibで実現されるプロキシオブジェクトの作成です プロキシクラス(UserService)が親クラス、プロキシクラスがサブクラス、プロキシオブジェクトがインスタンスオブジェクトになりますプロキシ クラス プロキシ クラスは cglib によって作成されるため、プログラマは気にしません。

1.2 JDK動的プロキシ

cglib テクノロジーに加えて、jdk 自体も、プロキシ オブジェクトを作成するための動的プロキシ メカニズムを提供します。ただし、プロキシ インターフェイスのみを使用できます。つまり、UserService には、JDK 動的プロキシ メカニズムを使用してプロキシ オブジェクトを生成するためのインターフェイスが最初に必要です。、例えば:

public interface UserInterface {
    
    
    void test();
}

public class UserService implements UserInterface{
    
    

    @Override
    public void test() {
    
    
        System.out.println("这是UserService的一个普通方法");
    }
}

JDK 動的プロキシを使用してプロキシ オブジェクトを生成する例は次のとおりです。

 public static void main(String[] args) throws IOException {
    
    
        UserService target = new UserService();
        target.test();

        System.out.println("下面开始是cglib之后的逻辑了");

        // jdk代理
        {
    
    
            // UserInterface接口的代理对象
            Object proxy = Proxy.newProxyInstance(UserService.class.getClassLoader(), new Class[]{
    
    UserInterface.class}, new InvocationHandler() {
    
    

                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
                    System.out.println("before...");
                    Object result = method.invoke(target, args);
                    System.out.println("after...");
                    return result;
                }
            });

            UserInterface userService = (UserInterface) proxy;
            userService.test();
        }
    }

new Class[]{UserInterface.class} を new Class[]{UserService.class} に置き換えると、コードは直接エラーを報告します (これは、JDK が 2 番目のプロキシ パラダイムを使用していることも証明します)

java.lang.IllegalArgumentException: UserService is not an interface

この制限により、生成されるプロキシ オブジェクトのタイプは UserService ではなく UserInterface となるため、注意が必要です。

1.3 ProxyFactory: Spring による 2 つのプロキシのカプセル化

上記では 2 つの動的プロキシ テクノロジを紹介しました。その後、それらは Spring でカプセル化されます。カプセル化されたクラスは ProxyFactory と呼ばれます。これは、プロキシ オブジェクトを作成するためのファクトリであることを意味し、上記よりも使いやすいです。たとえば、次のとおりです。

    public static void main(String[] args) throws IOException {
    
    
        UserService target = new UserService();

        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(target);
        proxyFactory.addAdvice(new MethodInterceptor() {
    
    
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
    
    
                System.out.println("before...");
                Object result = invocation.proceed();
                System.out.println("after...");
                return result;
            }
        });

        UserInterface userService = (UserInterface) proxyFactory.getProxy();
        userService.test();
    }

ProxyFactory を使用すると、cglib を使用するか jdk 動的プロキシを使用するかを気にする必要がなくなります。ProxyFactory は判断に役立ちます。UserService がインターフェイスを実装する場合、ProxyFactory の最下層は jdk 動的プロキシを使用します。インターフェイスが実装されていない場合は、ProxyFactory の最下層で jdk 動的プロキシが使用されます。 cglib テクノロジーを使用します。上記のコードは、UserService が UserInterface インターフェイスを実装しているため、生成される最終的なプロキシ オブジェクトは UserInterface タイプになります。

2. AOPの基礎知識

AOP の基本概念の確認

Spring AOP については誰もがよく知っていますが、AOP について話す前に、概念を整理することは、さらに勉強するのに非常に役立つと思います。また、実際に SpringAOP での制作経験があっても、以下の概念については理解できない部分もあると思います。なお、この後の【開発史】に登場するいくつかの概念は、言葉一つ違うように見えますが、実は別物です。

  • 切面(AspectJ): Aop ではアスペクトとは【】。強化されたパブリック動作コード[つまり通知]とカットインメソッド[つまりカットポイント]はアスペクトクラスで管理されます
  • 通知(Advice)アスペクトの特定の接続ポイントで実行される [アクション]通知には「前後」「前」「後」などさまざまな種類があります。Spring を含む多くの AOP フレームワークは、通知モデルとして [インターセプター] を使用し、接続ポイント中心の [インターセプター チェーン] を維持します。
  • 切点(PointCut): 結合ポイントに一致するアサーション。[アドバイス通知]は[ポイントカット]式に関連付けられており、この[ポイントカット]を満たす[接続ポイント]に対して動作します(例えば、特定の名前のメソッドを実行する場合)。AOP の中核は、ポイントカット式と結合点のマッチングです。Spring はデフォルトで AspectJ ポイントカット セマンティクスを使用します
  • 连接点(JoinPoint): Spring AOP では、ジョインポイントは常にメソッドの実行を表します。実際、これは強化された [メソッド] を表します。
  • 目标对象(Target): ターゲット オブジェクトは、強化されたオブジェクトつまり、メインのビジネス ロジックを含むクラスのオブジェクトです。
  • 顾问(Advisor): Adviser は、Advice をパッケージ化したものです。Advisor は、Pointcut と Advice を組み合わせたもので、Advice と Pointcut の管理に使用されます。、アプリケーションは気にする必要はありません (ただし、研究ソース コードは認識する必要があります)。
  • 织入(Weaving): アドバイスを結合点にカットするプロセスはウィービングと呼ばれます
  • 引入(Introductions): 他のインターフェースと実装を targetClass に動的に導入できます。

(追記: 特別に言わせてください。私の経験によると、[アドバイス] と [アドバイザー] を混同する人が多いです。結局、この 2 つの英語は似すぎています。)
(追記: 特別に言わせてください。私の経験によると、[アドバイス] と [アドバイザー] を混同する人が多いです。結局、この 2 つの英語は似すぎています。)
(追記: 特別に言わせてください。私の経験によると、[アドバイス] と [アドバイザー] を混同する人が多いです。確認してください。この 2 つの概念を誰もが明確に理解できるように、ここで 2 つのことについて話します。別途について。)

アドバイスの区分を通知する

上記の概念を読んで、親しみを感じていただけると思いますが、そうです、これらは私たちが普段使用しているアノテーションです。通常、これには次のカテゴリが含まれます。

  1. Before Advice: メソッドの前に実行されます。Spring AOP のアノテーションに対応します@Beforeさらに、対応する API インターフェイスもあります。MethodBeforeAdviceインターフェイスを継承します。BeforeAdvice
  2. アドバイスを返した後: メソッドが戻った後に実行されます。Spring AOP のアノテーションに対応します@AfterReturningさらに、対応する API インターフェイスもあります。AfterReturningAdvice
  3. アドバイスをスローした後: メソッドが例外をスローした後に実行されます。Spring AOP のアノテーションに対応します@AfterThrowingさらに、対応する API インターフェイスもあります。ThrowsAdvice
  4. After (finally) アドバイス: メソッドが最終的に実行された後に実行され、これが最後で、return よりも後で実行されます。Spring AOP のアノテーションに対応します@Afterさらに、対応する API インターフェイスもあります。AfterAdvice
  5. アドバイスの周り: これは最も強力なアドバイスであり、実行順序はカスタマイズできます。Spring AOP のアノテーションに対応します@Aroundさらに、対応する API インターフェイスもあります。MethodInterceptor

Spring は 5 つのアノテーションを対応する Advice クラスに解析します。

  1. @Before: AspectJMethodBeforeAdvice は実際には MethodBeforeAdvice です
  2. @AfterReturning: AspectJAfterReturningAdvice、実際には AfterReturningAdvice です
  3. @AfterThrowing: AspectJAfterThrowingAdvice、実際には MethodInterceptor です
  4. @After: AspectJAfterAdvice は実際には MethodInterceptor です
  5. @Around: AspectJAroundAdvice は実際には MethodInterceptor です

アドバイザーの理解

アドバイスと同様に、アドバイザの概念があり、アドバイザはポイントカットとアドバイスで構成され、ポイントカットはプロキシされるロジックを指定できます。そのモデル図は大まかに次のとおりです:
ここに画像の説明を挿入
たとえば、UserService クラスには 2 つのメソッドがあります。上記の例によれば、これら 2 つのメソッドはプロキシされ、拡張されます。これで、Advisor を使用してどのメソッドをプロキシするかを制御できるようになります。たとえば:

	public static void main(String[] args) {
    
    
        PointcutAdvisor pointcutAdvisor = new PointcutAdvisor() {
    
    

            @Override
            public Pointcut getPointcut() {
    
    
                return new StaticMethodMatcherPointcut() {
    
    
                    @Override
                    public boolean matches(Method method, Class<?> targetClass) {
    
    
                        return method.getName().equals("test");
                    }
                };
            }

            @Override
            public Advice getAdvice() {
    
    
                return new MethodInterceptor() {
    
    
                    @Override
                    public Object invoke(MethodInvocation invocation) throws Throwable {
    
    
                        System.out.println("before...");
                        Object result = invocation.proceed();
                        System.out.println("after...");
                        return result;
                    }
                };
            }

            @Override
            public boolean isPerInstance() {
    
    
                return false;
            }
        };

        // 注入advisor
        UserService target = new UserService();
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(target);
        proxyFactory.addAdvisor(pointcutAdvisor);
        UserInterface userService = (UserInterface) proxyFactory.getProxy();
        userService.test();
    }

    // 系统输出如下:
    // before...
    // 这是UserService的一个普通方法
    // after...

上記のコードは、生成されたプロキシ オブジェクトがtestこのメソッドが実行された場合にのみ拡張され、追加のロジックが実行されますが、他のメソッドが実行された場合には拡張されないことを示しています。

3. プロキシオブジェクトの作成方法

以上、Springが提供するProxyFactory、Advisor、Advice、PointCutといったプロキシオブジェクトの作成を実現する技術を紹介しましたが、Springを利用する場合、このようにProxyFactoryを直接利用することはありません。たとえば、ProxyFactory によって生成されたプロキシ オブジェクトが直接 Bean になること、UserSerivce のプロキシ オブジェクトが Spring コンテナから直接取得できることを望み、Spring はこれらをサポートしますが、開発者として Spring にこれらのクラスを伝える必要があります。プロキシする必要があるか、プロキシ ロジックとは何ですか。

3.1 ProxyFactoryBean: Spring AOP 初期実装バージョン

Spring の初期の段階では、UserService プロキシ Bean の作成など、AOP を実装する場合は、これを行う必要があります。

    @Bean
    public ProxyFactoryBean userServiceProxy() {
    
    
        UserService userService = new UserService();

        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        proxyFactoryBean.setTarget(userService);
        proxyFactoryBean.addAdvice(new MethodInterceptor() {
    
    

            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
    
    
                System.out.println("before...1111");
                Object result = invocation.proceed();
                System.out.println("after...1111");
                return result;
            }
        });
        return proxyFactoryBean;
    }

上記のコードからわかるように、初期段階で AOP を実装するには、次のことを行う必要があります。

  1. 上記のように、プロキシ オブジェクトを強力にバインドします。userService
  2. 通知を追加します。上記は匿名実装クラスですnew MethodInterceptorインターフェイスのサブインターフェイスMethodInterceptorですAdvice
  3. FactoryBean を使用してプロキシ オブジェクトを取得する

このメソッドは UserService Bean を定義するために使用され、AOP を通過しています。しかし明らかに、このメソッドは特定の Bean のみをターゲットにし、クラス レベルでのみ制御でき、クラス内のすべてのメソッドがインターセプトされます。これは、より多くの Bean に分割したい場合は、各 Bean に対応する FactoryBean を作成する必要があることを意味します。これは私たちにとって受け入れがたいことです。
ProxyFactoryBean には、Advise または Advisor を Bean として定義し、それを ProxyFactoryBean に設定するなどの追加機能もあります。

	@Bean
    public MethodInterceptor zhouyuAroundAdvice() {
    
    
        return new MethodInterceptor() {
    
    
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
    
    
                System.out.println("before...");
                Object result = invocation.proceed();
                System.out.println("after...");
                return result;
            }
        };
    }

    @Bean
    public ProxyFactoryBean userService() {
    
    
        UserService userService = new UserService();

        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        proxyFactoryBean.setTarget(userService);
        proxyFactoryBean.setInterceptorNames("zhouyuAroundAdvise");
        return proxyFactoryBean;
    }

3.2 NameMatchMethodPointcutAdvisor: すべてのメソッドをインターセプトする問題を改善するために Advisor の概念を導入します。

アドバイザーについて前に述べたことを覚えていますか? アドバイザーは「ポイントカット+アドバイス」を組み合わせたものです。全体としては比較的単純で、初期化時に内部でメソッドを指定しAdvice、どのメソッドをインターセプトするかAdvisorを決定する必要がありPointcut、インターセプト後に完了する必要がある作業は依然としてAdvice内部で行われます。

PS: AOP の経験がある学生は、Pointcut粒度が実際にはメソッド レベルであることを知っておく必要があります...

NameMatchMethodPointcutAdvisorこれはAdvisorのサブクラスであり、クラス名から、メソッド名によるエントリ ポイントと一致することがわかります。もちろん、いくつかの異なるサブクラスがあります。ここでは、これを説明するための簡単な使用法を示します。

@Bean
public MethodInterceptor zhouyuAroundAdvice() {
    
    
    return new MethodInterceptor() {
    
    
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
    
    
            System.out.println("before...");
            Object result = invocation.proceed();
            System.out.println("after...");
            return result;
        }
    };
}
	
@Bean
public NameMatchMethodPointcutAdvisor nameMethodAdvisor() {
    
    
    NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor();

    // 通知(Advice)  :是我们的通知类
    // 通知者(Advisor):是经过包装后的细粒度控制方式。
    advisor.setAdvice(zhouyuAroundAdvice());
    advisor.setMappedNames("test");
    return  advisor;
}

/**
 * FactoryBean方式单个: ProxyFactoryBean
 *  控制粒度到方法
 * @return
 */
@Bean
public ProxyFactoryBean calculateProxy(){
    
    
	
	UserService userService = new UserService();
    ProxyFactoryBean userService=new ProxyFactoryBean();
    userService.setInterceptorNames("nameMethodAdvisor");   
    userService.setTarget(userService);
    return userService;
}

tulingLogAspectこの Bean はアドバイザーを使用して構成されており、アドバイザー内にアドバイスがあることがわかります。アドバイザはメソッドのマッチングを担当し、内部アドバイスはメソッドのパッケージ化を実装します。
ここでの mappedNames 構成では、カンマで区切って複数を指定でき、異なるクラスのメソッドを指定できることに注意してください。ここでアドバイスを構成するとすべてのメソッドがインターセプトされるため、アドバイスを直接指定する場合と比較して、アドバイザはより詳細な制御を実現します。

3.3 BeanNameAutoProxyCreator: Beanが1つしか切り出せない問題を改善

AOP(クラス粒度)が小さすぎるという上記の問題を改善するために、 と呼ばれるAutoProxyインターフェイススキームが登場しました。名前からわかるように、これは自動プロキシ ソリューションです。ここでは、その実装クラスの 1 つを使用して、BeanNameAutoProxyCreator簡単なデモンストレーションを示します。
ProxyFactoryBean はプロキシされるオブジェクトをそれ自体で指定する必要があります。その後、BeanNameAutoProxyCreator を使用して Bean の名前を指定することで Bean をプロキシできます。

    @Bean
    public BeanNameAutoProxyCreator beanNameAutoProxyCreator() {
    
    
        BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
        beanNameAutoProxyCreator.setBeanNames("userSe*");
        beanNameAutoProxyCreator.setInterceptorNames("zhouyuAroundAdvice");
        beanNameAutoProxyCreator.setProxyTargetClass(true);
        return beanNameAutoProxyCreator;
    }

上記のコードからわかるように、AutoProxyAOP を実装するには、次のことを行う必要があります。

  1. オブジェクトをカットするためのルールを指定します。ファジーマッチングを使用した Bean 名は次のとおりです。userSe*
  2. 上記と同様に、通知を追加します。ここにありますzhouyuAroundAdvice
  3. 上記のプロキシ オブジェクトを取得するために FactoryBean を使用する必要があるのとは異なり、ここでは自動プロキシと言われているため、beanName に従って Bean を取得するときに、プロキシする必要があることが検出された場合、プロキシ オブジェクトはそれに対して自動的に生成される

明らかに、BeanNameAutoProxyCreator を介して、AOP は Bean のバッチに対して実行でき、プロキシ ロジックが指定され、Advice である InterceptorName が指定されます。前提条件として、Spring が見つけられるように Advice も Bean である必要があります。 BeanNameAutoProxyCreator だけでは不十分ですが、beanNameプロキシしたい Bean を指定するだけで十分です。

3.4 DefaultAdvisorAutoProxyCreator: より強力な AutoProxy ソリューション

現時点では、これは比較的成熟したソリューションです。

   @Bean
    public DefaultPointcutAdvisor defaultPointcutAdvisor() {
    
    
        NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
        pointcut.addMethodName("test");

        DefaultPointcutAdvisor defaultPointcutAdvisor = new DefaultPointcutAdvisor();
        defaultPointcutAdvisor.setPointcut(pointcut);
        defaultPointcutAdvisor.setAdvice(new ZhouyuAfterReturningAdvice());

        return defaultPointcutAdvisor;
    }

    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
    
    
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        return defaultAdvisorAutoProxyCreator;
    }

DefaultAdvisorAutoProxyCreator を通じて、すべての Advisor タイプの Bean が直接検索され、プロキシされる Bean とプロキシ ロジックが Advisor の PointCut および Advice 情報に従って決定されます。感じているかどうかはわかりませんが、この方法は現在使用しているアノテーション方法に非常に近いです。そう、先に言っておきますが、アノテーションベースのAOP実装スキームでは、アスペクトクラスを全て探し出し、[Pointcut]と[Advice]を解析して[Advisor]にパッケージングするだけです。 , 一連のプロキシ処理の後、プロキシ オブジェクトが作成されます。

最後に、プロジェクトでよく使用する AOP の例を貼り付けます。

@Aspect
@Component
public class MyAspect {
    
    

    @Pointcut("execution(* org.tuling.spring.aop..*(..))")
    public void pointCut() {
    
    

    }

    @Before("pointCut()")
    public void before() {
    
    
        System.out.println("方法执行之前,记录一下入口时间。" + System.currentTimeMillis());
    }

    @AfterReturning("pointCut()")
    public void afrer() {
    
    
        System.out.println("方法执行之后,记录一下出口时间。" + System.currentTimeMillis());
    }
}

上記のクラスを通じて、プロキシされるメソッド (式を通じて) とプロキシ ロジック (@Before によって変更されたメソッド) を直接定義します。これはシンプルで明確なので、Spring の場合はこれらを解析するだけで済みます。アノテーションは、解析後に、対応する Pointcut オブジェクトと Advice オブジェクトを取得し、Advisor オブジェクトを生成し、それらを ProxyFactory にスローして、対応するプロキシ オブジェクトを生成します。これらのアノテーションを解析する方法は、アノテーションが行う必要があることであり、で分析されます。詳細は後述します@EnableAspectJAutoProxy

4. AOP アノテーションスキームの基礎となるソースコードの分析

Spring がアスペクト クラスを通じてクラスに@Aspectアノテーションを追加し、Pointcutメソッドを定義し、最後に一連の拡張メソッドを定義することがわかりますAdviceこれでオブジェクトのアスペクト操作は完了です。あなたが Spring の作者である場合、上記の根拠に従って、どのような手順が必要だと考えてください。私の要約は次のとおりです。

  1. すべてのアスペクト Bean、つまりクラスに@Aspectアノテーションが付いた Beanを検索します。
  2. すべてのアドバイス、つまり@Before、、、、、、これら 5 つの注釈を解析し、それらを関数とともにカプセル化し、1 つずつパッケージ化して保存します@After@AfterReturning@AfterThrowing@AroundPointcutAdvisor
  3. 動的プロキシクラスを作成する
  4. プロキシ クラスのメソッドを呼び出すときに、すべてのエンハンサーを検索し、現在のメソッドを拡張します。

次に、ソース コードを調べて、それが私たちの推論と同じかどうかを確認してみましょう。

4.1 @EnableAspectJAutoProxy から始める

Springboot 時代に入った今、Spring AOP を導入するときにこのアノテーションを使用することを多くの学生が知らないか忘れているかもしれません。この注釈は何に使われるのでしょうか? 次のように、クリックしてソース コードを開きます。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
    
    

	/**
	 * 指示是否要创建基于子类(CGLIB)的代理,而不是基于标准Java接口的代理。默认为false。
	 */
	boolean proxyTargetClass() default false;

	/**
	 * 表明代理应该由AOP框架作为ThreadLocal公开,
	 * 以便通过org.springframework.aop.framework.AopContext类进行检索。
	 * 默认为关闭,即不保证AopContext访问将工作。
	 */
	boolean exposeProxy() default false;
}

上記のソースコードを見ると、アノテーションクラスで使用されていることがわかりますが@Import(AspectJAutoProxyRegistrar.class)、これは何を意味するのでしょうか? 次のように:

PS:@Import注釈については次のとおりです。
ここに画像の説明を挿入

上で述べたように、実際には BeanDefinition を Bean ファクトリに登録しているだけなので、クリックしてこのクラスが何を行うかを見てみましょう。ソースコードは次のとおりです。

フルクラスパス: org.springframework.context.annotation.AspectJAutoProxyRegistrar
クラスアノテーション: 指定された @EnableAspectJAutoProxy アノテーションに従って、現在の BeanDefinitionRegistry に AnnotationAwareAspectJAutoProxyCreator を登録します。

/**
* 根据给定的@EnableAspectJAutoProxy注释
* 在当前BeanDefinitionRegistry中注册一个AnnotationAwareAspectJAutoProxyCreator。
*/
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    
    

	/**
	 * 根据导入的@Configuration类上的@EnableAspectJAutoProxy.proxyTargetClass()
	 * 属性的值注册、升级和配置AspectJ自动代理创建器。
	 */
	@Override
	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    
    

		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

		AnnotationAttributes enableAspectJAutoProxy =
				AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
		if (enableAspectJAutoProxy != null) {
    
    
			if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
    
    
				AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
			}
			if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
    
    
				AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
			}
		}
	}
}

AnnotationAwareAspectJAutoProxyCreator正直に言うと、この時点で、Spring AOP のコア クラスの 1 つが上記のコメントで言及されたクラスであることが基本的にわかります。方法については多くはproxyTargetClass語りexposeProxyませんが、使ってみればそれが何を意味するかわかります。彼は私たちの中心的な研究対象ではありません。

ちょっとしたメモ:
proxyTargetClass: は @EnableAspectJAutoProxy アノテーションの属性です。標準 Java インターフェイスに基づく JDK プロキシの代わりにサブクラスベース (CGLIB) プロキシを作成するかどうかを示します。デフォルトは false です。(上で述べたように、Spring がプロキシを作成する一般的なプロセスは、プロキシ モードを選択します。そして、このパラメータは、これが true に設定されている場合、CGLIB プロキシがデフォルトで作成され、JDK プロキシは考慮されなくなることを意味します。) @EnableAspectJAutoProxy 属性による注釈が付けられます
exposeProxyorg.springframework.aop.framework.AopContext クラスを介した取得のために、プロキシが AOP フレームワークによって ThreadLocal として公開される必要があることを示します。デフォルトはオフです。つまり、AopContext アクセスが機能するという保証はありません。

4.2 AnnotationAwareAspectJAutoProxyCreator: 簡単な紹介

ここでは、このクラスの基本的な理解を得るために、このクラスの継承図、つまりクラス アノテーションを単に見ていきます。まずクラスのアノテーションを見てください。

/**
 * AspectJAwareAdvisorAutoProxyCreator子类,它处理当前应用程序上下文中的所有AspectJ注释方面,以及Spring Advisors。
 * 任何AspectJ注释的类都会被自动识别,如果Spring AOP的基于代理的模型能够应用它们,那么它们的建议就会被应用。这涵盖了方法执行连接点。
 * 如果使用了<aop:include>元素,那么只有名称与include模式匹配的@AspectJ bean才会被认为是定义了用于Spring自动代理的方面。
 * Spring advisor的处理遵循org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator中建立的规则。
 */
public class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator {
    
    
}

上記のインターフェイスのコメントから、いくつかのキーワードがわかります。Spring Advisor だけでなく、現在のアプリケーション コンテキスト内のすべての AspectJ アノテーション付きアスペクトも処理します。AspectJ アノテーションが付けられたクラスはすべて自動的に認識されますあ、これは冒頭で述べた【おおよその手順】の最初の2ステップではありません。継承図をもう一度見てみましょう。BeanPostProcessor
ここに画像の説明を挿入
とその 2 つのサブインターフェースなど、よく知られたインターフェースがたくさんあるかどうかを確認してください。
さらに、AOP の実装は [初期化後] 段階にあるとも前に述べました。次に、AOP 実装のソース コードを実際に分析し始めます。

4.3 コンセプトレビュー

BeanPostProcessor: [初期化]を経て完了するため、Spring AOPの実装はこのインターフェースと切り離せないはずです。ただし、これを実装するときは、彼の実装クラスの 1 つである を使用しましたAnnotationAwareAspectJAutoProxyCreatorこのクラスの実装の大部分は にありますAbstractAutoProxyCreator

4.4 手法の詳細説明

ソースコード分析エントリは次のとおりです。

フルパス: org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization
メソッドの説明: 初期化メソッド後に BeanPostProcessor を実行

	@Override
	public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
			throws BeansException {
    
    

		Object result = existingBean;
		for (BeanPostProcessor processor : getBeanPostProcessors()) {
    
    
			Object current = processor.postProcessAfterInitialization(result, beanName);
			if (current == null) {
    
    
				return result;
			}
			result = current;
		}
		return result;
	}

要約する

おすすめ

転載: blog.csdn.net/qq_32681589/article/details/132459769