わんじロングテキスト!SpringAOPソースコードの詳細な分析。ゼロから数分で完了します。

1.AOPとその使用法を知る

第二に、AOPの特徴

2.1春のAOP

2.1.1動的プロキシに基づいて実装されます

Spring 提供了很多的实现AOP的方式:Spring 接口方式,schema配置方式和注解的方式. 
如果使用接口方式引入AOP, 就是用JDK提供的动态代理来实现.
如果没有使用接口的方式引入. 那么就是使用CGLIB来实现的

AOPを実装するためのインターフェースの使用を研究します。目的は、Springが動的プロキシを使用してAOPを実装する2つの方法をよりよく理解することです。

2.1.2 SpringはAspectJのサポートを提供しますが、一部の機能、つまりAspectJのポイントカット分析(式)とマッチングのみをサポートします。

アスペクトを作成するときは、AspectJが提供する@ Aspect、@ Before、@ Pointcut、@ After、@ AfterReturning、@ AfterThrowingなどをよく使用します。

AspectJが非常に便利で効率的であることはわかっていますが、SpringがAspectJのすべての機能を使用しないのはなぜですか?特にAspectJの静的な織り方です。

まず、AspectJの特徴を見てみましょう

AspectJ的特点
1. AspectJ属于静态织入. 他是通过修改代码实现的. 它的织入时机有三种
    1) Compile-time weaving: 编译期织入. 例如: 类A使用AspectJ增加了一个属性. 类B引用了类A, 这个场景就需要在编译期的时候进行织入, 否则类B就没有办法编译, 会报错.
    2) Post-compile weaving: 编译后织入.也就是已经生成了.class文件了, 或者是都已经达成jar包了. 这个时候, 如果我们需要增强, 就要使用到编译后织入
    3) Loading-time weaving: 指的是在加载类的时候进行织入. 

2. AspectJ实现了对AOP变成完全的解决方案. 他提供了很多Spring AOP所不能实现的功能
3. 由于AspectJ是在实际代码运行前就完成了织入, 因此可以认为他生成的类是没有额外运行开销的.

拡張機能:AspectJの静的ウィービングがここで使用されないのはなぜですか?静的ウィービングを導入する場合は、AspectJ独自のパーサーを使用する必要があるためです。AspectJファイルはajサフィックスで終わるファイルです。Springにはこれを行う方法がないため、AspectJを独自のパーサーとして使用する必要があります。解析用。これにより、Springのコストが増加します。

3、AOP構成方法

上記のSpringAOPとAspectJ。また、AspectJは、@ Aspect、@ Pointcut、@ Before、@ Afterなどの多くのアノテーションを定義するとも述べています。ただし、SpringAOPを使用して純粋なJavaコードを記述します。これは完全にSpringに属し、AspectJとは関係ありません。SpringはAspectJの概念に従うだけです。AspectJが提供するjarパッケージのアノテーションを含みます。ただし、AspectJの機能には依存しません。

SpringAOPを構成する方法は3つあります。

  • 1つ目:インターフェースに基づく構成Spring 1.2バージョンでは、提供されるものはインターフェースに基づいて完全に実装されます。
  • 2番目のタイプ:スキーマベースの構成。Spring2.0以降、構成にはxmlが使用されます。
  • 3番目のタイプ:アノテーション@Aspectに基づく。このメソッドは最も単純で最も便利です。AspectJと呼ばれますが、AspectJとは関係ありません。

日常業務では主にアノテーションを使用してAOPを構成し、アノテーションは主に最初のインターフェースに基づいて実装される ため、最初と3番目の構成方法に焦点を当てます。

3.1インターフェースモードに基づく構成Spring1.2バージョンでは、提供されるものは完全にインターフェースモードに基づいて実装されます。

この方法は最も古い方法ですが、Springは下位互換性が優れているため、宣言型トランザクションなど、この方法を使用するコードはまだたくさんあります 

では、AspectJが導入されていない場合、SpringはどのようにAOPを実装しますか?例を見てみましょう。

1.ビジネスロジックインターフェイスクラスを定義します

package com.lxl.www.aop.interfaceAop;

/**
 * 使用接口方式实现AOP, 默认通过JDK的动态代理来实现. 非接口方式, 使用的是cglib实现动态代理
 *
 * 业务接口类-- 计算器接口类
 *
 * 定义三个业务逻辑方法
 */
public interface IBaseCalculate {

    int add(int numA, int numB);

    int sub(int numA, int numB);

    int div(int numA, int numB);

    int multi(int numA, int numB);

    int mod(int numA, int numB);

}

2.ビジネスロジッククラスを定義します

package com.lxl.www.aop.interfaceAop;//业务类,也是目标对象

import com.lxl.www.aop.Calculate;

import org.springframework.aop.framework.AopContext;
import org.springframework.stereotype.Service;

/**
 * 业务实现类 -- 基础计算器
 */

public class BaseCalculate implements IBaseCalculate {

    @Override
    public int add(int numA, int numB) {
        System.out.println("执行目标方法: add");
        return numA + numB;
    }

    @Override
    public int sub(int numA, int numB) {
        System.out.println("执行目标方法: sub");
        return numA - numB;
    }

    @Override
    public int multi(int numA, int numB) {
        System.out.println("执行目标方法: multi");
        return numA * numB;
    }

    @Override
    public int div(int numA, int numB) {
        System.out.println("执行目标方法: div");
        return numA / numB;
    }

    @Override
    public int mod(int numA, int numB) {
        System.out.println("执行目标方法: mod");

        int retVal = ((Calculate) AopContext.currentProxy()).add(numA, numB);
        return retVal % numA;
    }
}

3.通知クラスを定義します

予告

package com.lxl.www.aop.interfaceAop;

import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * 定义前置通知
 * 实现MethodBeforeAdvice接口
 */
public class BaseBeforeAdvice implements MethodBeforeAdvice {

    /**
     *
     * @param method 切入的方法
     * @param args 切入方法的参数
     * @param target 目标对象
     * @throws Throwable
     */
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("===========进入beforeAdvice()============");
        System.out.println("前置通知--即将进入切入点方法");
        System.out.println("===========进入beforeAdvice() 结束============\n");
    }

}

通知を投稿する

package com.lxl.www.aop.interfaceAop;

import org.aspectj.lang.annotation.AfterReturning;
import org.springframework.aop.AfterAdvice;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * 后置通知
 * 实现AfterReturningAdvice接口
 */
public class BaseAfterReturnAdvice implements AfterReturningAdvice {

    /**
     *
     * @param returnValue 切入点执行完方法的返回值,但不能修改
     * @param method 切入点方法
     * @param args 切入点方法的参数数组
     * @param target 目标对象
     * @throws Throwable
     */
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("\n==========进入afterReturning()===========");
        System.out.println("后置通知--切入点方法执行完成");
        System.out.println("==========进入afterReturning() 结束=========== ");
    }

}

サラウンド通知

package com.lxl.www.aop.interfaceAop;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * 环绕通知
 * 实现MethodInterceptor接口
 */
public class BaseAroundAdvice implements MethodInterceptor {

    /**
     * invocation :连接点
     */
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("===========around环绕通知方法 开始===========");
        // 调用目标方法之前执行的动作
        System.out.println("环绕通知--调用方法之前: 执行");
        // 执行完方法的返回值:调用proceed()方法,就会触发切入点方法执行
        Object returnValue = invocation.proceed();
        System.out.println("环绕通知--调用方法之后: 执行");
        System.out.println("===========around环绕通知方法  结束===========");
        return returnValue;
    }

}

構成クラス

package com.lxl.www.aop.interfaceAop;

import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.context.annotation.Bean;

/**
 * 配置类
 */
public class MainConfig {

    /**
     * 被代理的对象
     * @return
     */
    @Bean
    public IBaseCalculate baseCalculate() {
        return new BaseCalculate();
    }

    /**
     * 前置通知
     * @return
     */
    @Bean
    public BaseBeforeAdvice baseBeforeAdvice() {
        return new BaseBeforeAdvice();
    }

    /**
     * 后置通知
     * @return
     */
    @Bean
    public BaseAfterReturnAdvice baseAfterReturnAdvice() {
        return new BaseAfterReturnAdvice();
    }

    /**
     * 环绕通知
     * @return
     */
    @Bean
    public BaseAroundAdvice baseAroundAdvice() {
        return new BaseAroundAdvice();
    }

    /**
     * 使用接口方式, 一次只能给一个类增强, 如果想给多个类增强, 需要定义多个ProxyFactoryBean
     * 而且, 曾增强类的粒度是到类级别的. 不能指定对某一个方法增强
     * @return
     */
    @Bean
    public ProxyFactoryBean calculateProxy() {
        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        proxyFactoryBean.setInterceptorNames("baseAfterReturnAdvice", "baseBeforeAdvice", "baseAroundAdvice");
        proxyFactoryBean.setTarget(baseCalculate());
        return proxyFactoryBean;
    }

}

前述のように、AOPはiocに依存しており、AOP機能を実現するにはBeanとして登録する必要があります

メソッドエントリ

package com.lxl.www.aop.interfaceAop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class InterfaceMainClass{

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
        IBaseCalculate calculate = context.getBean("calculateProxy", IBaseCalculate.class);
        System.out.println(calculate.getClass());
        calculate.add(1, 3);
    }

}

結果:

===========进入beforeAdvice()============
前置通知--即将进入切入点方法
===========进入beforeAdvice() 结束============

===========around环绕通知方法 开始===========
环绕通知--调用方法之前: 执行
执行目标方法: add
环绕通知--调用方法之后: 执行
===========around环绕通知方法  结束===========

==========进入afterReturning()===========
后置通知--切入点方法执行完成
==========进入afterReturning() 结束=========== 

観察の結果、実行順序は、事前通知->周辺通知のフロント方式->ターゲットロジック->周辺通知のリア方式->リア通知であることがわかりました。

それでは、事前通知が最初に実行されますか、それとも周囲の通知の事前通知方法ですか?それは構成ファイルの構成順序によって異なります

ここでは、サラウンド通知を最後に置くので、サラウンド通知は前の通知の後に実行されます。

  @Bean
    public ProxyFactoryBean calculateProxy() {
        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        proxyFactoryBean.setInterceptorNames( "baseAfterReturnAdvice", "baseBeforeAdvice", "baseAroundAdvice");
        proxyFactoryBean.setTarget(baseCalculate());
        return proxyFactoryBean;
    }

したがって、事前通知の前にサラウンド通知を配置すると、サラウンド通知が最初に実行されます

  @Bean
    public ProxyFactoryBean calculateProxy() {
        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        proxyFactoryBean.setInterceptorNames("baseAroundAdvice", "baseAfterReturnAdvice", "baseBeforeAdvice");
        proxyFactoryBean.setTarget(baseCalculate());
        return proxyFactoryBean;
    }

運用結果

===========around环绕通知方法 开始===========
环绕通知--调用方法之前: 执行
===========进入beforeAdvice()============
前置通知--即将进入切入点方法
===========进入beforeAdvice() 结束============

执行目标方法: add

==========进入afterReturning()===========
后置通知--切入点方法执行完成
==========进入afterReturning() 结束=========== 
环绕通知--调用方法之后: 执行
===========around环绕通知方法  结束===========

責任の連鎖は上で述べたので、責任の連鎖とは何ですか?下の図に示すように:

SpringAOPソースコード分析

 

組立ラインがあります。たとえば、生産ラインです。その中には多くのプロセスがあります。プロセス1は、プロセス2を実行する前に完了することができます。

上記のデモと組み合わせて、責任の連鎖によって呼び出されるデモを見てみましょう。

上記で2つのメソッドを定義しました。1つは転送通知BaseBeforeAdviceがMethodBeforeAdviceを実装し、もう1つはサラウンド通知BaseAroundAdviceがMethodInterceptorを実装します。これら2つの通知をチェーンに配置する場合は、同じインターフェイスを実装する必要があります。今は違う。

サラウンド通知には、サラウンド通知のフロント通知とサラウンド通知のリア通知の2つの部分があることがわかっているため、フロント通知はサラウンド通知のフロント通知部分と見なすことができます。

package com.lxl.www.aop.interfaceAop.chainDemo;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.MethodBeforeAdvice;

/**
 * 为什么 我们可以让MethodBeforeAdvice 前置通知继承自环绕通知的接口呢?
 * 主要原因是, 环绕通知的前半部分, 就是前置通知
 */
public class BeforeAdviceInterceptor implements MethodInterceptor {

  // 前置通知
  MethodBeforeAdvice methodBeforeAdvice;

  public BeforeAdviceInterceptor(MethodBeforeAdvice methodBeforeAdvice) {
    this.methodBeforeAdvice = methodBeforeAdvice;
  }

  /**
   * 使用了环绕通知的前半部分. 就是一个前置通知
   * @param invocation the method invocation joinpoint
   * @return
   * @throws Throwable
   */
  @Override
  public Object invoke(MethodInvocation invocation) throws Throwable {
    methodBeforeAdvice.before(invocation.getMethod(), invocation.getArguments(), invocation.getClass());
    return invocation.proceed();
  }
}

このコードは事前通知をラップし、MethodInterceptorインターフェースを実装するために拡張できるようにします。これはインターフェースを拡張するメソッドです。

次に、チェーンを作成する必要があります。このチェーンは、組立ラインの各ワーカーとして理解できます。各ワーカーはプロセスを処理します。統一された方法で呼び出すことができるようにするには、すべてのワーカーが同じインターフェイスを実装する必要があります。責任の連鎖は次のとおりです。

    /**
     * 把一条链上的都初始化
     *
     * 有一条链, 这条链上都有一个父类接口 MethodInterceptor.
     * 也就是说, 链上都已一种类型的工人. 但每种工人的具体实现是不同的. 不同的工人做不同的工作
     *
     * 这里定义了一个责任链. 连上有两个工人. 一个是前置通知. 一个是环绕通知.
     * 前置通知和环绕通知实现的接口是不同的. 为了让他们能够在一条链上工作. 我们自定义了一个MethodBeforeAdviceInterceptor
     * 相当于为BaseBeforeAdvice()包装了一层MethodInterceptor
     */

    List<MethodInterceptor> list = new ArrayList<>();
    list.add(new BeforeAdviceInterceptor(new BaseBeforeAdvice()));
    list.add(new BaseAroundAdvice());

ここで責任チェーンを定義します。2人のワーカーが接続されています。1つは事前通知です。もう1つは通知を取り巻くものです。

事前通知と周囲の通知によって実装されるインターフェースは異なります。チェーンで機能できるようにするために、MethodBeforeAdviceInterceptorをカスタマイズしました。これは、BaseBeforAdvice()のMethodInterceptorのレイヤーをラップした後、責任チェーン。

/**
   * 责任链调用
   */
  public static class MyMethodInvocation implements MethodInvocation {

    // 这是责任链
    protected List<MethodInterceptor> list;
    // 目标类
    protected final BaseCalculate target;

    public MyMethodInvocation(List<MethodInterceptor> list) {
      this.list = list;
      this.target = new BaseCalculate();
    }

    int i = 0;

    public Object proceed() throws Throwable {
      if (i == list.size()) {
        /**
         * 执行到责任链的最后一环, 执行目标方法
         */
        return target.add(2, 2);
      }
      MethodInterceptor interceptor = list.get(i);
      i++;
      /**
       * 执行责任链调用
       * 这个调用链第一环是: 包装后的前置通知
       * 调用链的第二环是: 环绕通知.
       * 都执行完以后, 执行目标方法.
       */
      return interceptor.invoke(this);
    }

    @Override
    public Object getThis() {
      return target;
    }

    @Override
    public AccessibleObject getStaticPart() {
      return null;
    }

    @Override
    public Method getMethod() {
      try {
        return target.getClass().getMethod("add", int.class, int.class);
      } catch (NoSuchMethodException e) {
        e.printStackTrace();
      }
      return null;
    }

    @Override
    public Object[] getArguments() {
      return new Object[0];
    }
  }

}

ここでは、proceed  ()メソッドに焦点を当て ます。リストの責任チェーンの通知を周期的に取得してから、invoke()メソッドを実行します。

SpringAOPソースコード分析

 

proceed()メソッドはチェーンループです。最初のi = 0では、list(0)が事前通知であり、事前通知が呼び出されると、BeforeAdviceInterceptor.invoke()メソッドが呼び出され、invocation.proceed()が呼び出されます。メソッドが呼び出され、MyMethodInvocation.proceed()メソッドに戻ります。

次に、i = 1、list(1)は周囲の通知であり、周囲の通知が呼び出されると、invocation.proceed()が再度呼び出され、MyMethodInvocation.proceed()メソッドに戻ります。

これはすでにリストの最後のリンクであり、invoke()メソッドは後で呼び出されません。2つ目はターゲットメソッドを実行することです。実行が終了すると、呼び出し全体が終了します。

これはコールチェーンです。

責任の連鎖については2つのポイントがあります。

1.統一された呼び出し、つまり共通の抽象クラスが必要です。

2.ループまたは再帰を使用して、責任の連鎖の呼び出しを完了します

総括する:

上記のメソッドはProxyFactoryBeanプロキシBeanファクトリメソッドを使用します。2つの制限があります。

public class ProxyFactoryBean extends ProxyCreatorSupport
        implements FactoryBean<Object>, BeanClassLoaderAware, BeanFactoryAware {
......
   @Override
    @Nullable
    public Object getObject() throws BeansException {
        /**
         * 初始化通知链: 将通知放入链中
         * 后面初始化的时候, 是通过责任链的方式调用这些通知链的的. 
         * 那么什么是责任链呢?
         */
        initializeAdvisorChain();
        if (isSingleton()) {
            /**
             * 创建动态代理
             */
            return getSingletonInstance();
        }
        else {
            if (this.targetName == null) {
                logger.info("Using non-singleton proxies with singleton targets is often undesirable. " +
                        "Enable prototype proxies by setting the 'targetName' property.");
            }
            return newPrototypeInstance();
        }
    }
......
}

1.一度に拡張できるクラスは1つだけです。複数のクラスを拡張する場合は、複数のProxyFactoryBeanを定義する必要があります。

2.拡張された粒度はクラスレベルでのみ可能であり、拡張するメソッドに割り当てることはできません。これにはまだ特定の制限があります。

3.2アノテーション@Aspectに基づく方法。この方法は最も簡単で便利です。AspectJと呼ばれますが、AspectJとは関係ありません。

3.2.1@Aspectアスペクトの分析原理

上記の最初のメソッドは、インターフェイスメソッドAOPの実装原理の詳細な調査です。AOPのアノテーションメソッド、最後は、@ Aspectアスペクトクラスの@ Befor、@ After、およびその他のアノテーションを解析してアドバイザにします。クラス@Beforeを使用すると1つのAdvisorに解析され、@ Afterメソッドを含むクラスもAdvisorに解析されます...他の通知されたメソッドもAdvisorに解析されます。拡張ロジックはAdvisorで定義されます。つまり、@ Beforなどのロジックです。および@After、およびdivメソッドなどの拡張が必要な​​メソッド。

アノテーション@ Aspect、@ Before、@ Afterを使用する実装原理を分析しましょう。前述のように、@ Before、@ AfterからAdvisorを生成します。

ここには3つの部分があります。

  • パート1:@Aspectの下の@Beforeなどで通知方法を分析し、Advisorとして解析します
  • パート2:createBeanの場合、動的プロキシを作成します
  • 3番目の部分:呼び出し。呼び出したら、責任チェーンを実行し、すべての通知をループします。最後に結果を出力します。

以下では、これら3つの部分に従って分析します。

最初のステップ:@Aspectの下の@Beforeなどで通知メソッドを解析し、Advisorとして解析します。以下に示すように:

SpringAOPソースコード分析

 

最初のステップはいつ実行されましたか?

Beanを作成すると、多くのPostProcessorポストプロセッサが呼び出され、最初のポストプロセッサが呼び出されたときに実行されます。実行プロセスは、おおまかに次のとおりです。すべてのBeanDefinitionsを取得し、クラスに@Aspectアノテーションがあるかどうかを確認します。 @Aspectアノテーションが付けられたメソッドで@ Before、@ After、@ AfterReturning、@ AfterThrowingを見つけると、各通知でアドバイザが生成されます

ステップ2:createBeanの場合、動的プロキシを作成します

SpringAOPソースコード分析

 

createBeanには3つの段階がありますが、動的プロキシはどの段階で作成されますか?

全体的なプロセスは次のとおりです。

createBeanの場合、初期化が完了した後にBeanのポストプロセッサが呼び出されます。すべてのアドバイザを取得し、アドバイザをループして、実行中の式に基づいて一致を実行します。現在作成されているBeanと一致し、一致して、動的を作成します。プロキシ。

ポイントカットには多くの種類があります。上記のコードは次のとおりです。

package com.lxl.www.aop.interfaceAop.chainDemo;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.MethodBeforeAdvice;

/**
 * 为什么 我们可以让MethodBeforeAdvice 前置通知继承自环绕通知的接口呢?
 * 主要原因是, 环绕通知的前半部分, 就是前置通知
 */
public class BeforeAdviceInterceptor implements MethodInterceptor {

  // 前置通知
  MethodBeforeAdvice methodBeforeAdvice;

  public BeforeAdviceInterceptor(MethodBeforeAdvice methodBeforeAdvice) {
    this.methodBeforeAdvice = methodBeforeAdvice;
  }

  /**
   * 使用了环绕通知的前半部分. 就是一个前置通知
   * @param invocation the method invocation joinpoint
   * @return
   * @throws Throwable
   */
  @Override
  public Object invoke(MethodInvocation invocation) throws Throwable {
    methodBeforeAdvice.before(invocation.getMethod(), invocation.getArguments(), invocation.getClass());
    return invocation.proceed();
  }
}

そして、私たちのアノテーションは実行式の方法で一致しています

ステップ3:呼び出す。呼び出すときは、責任チェーンを実行し、すべての通知をループします。最後に結果を出力します。

SpringAOPソースコード分析

 

3.2.2AOPアスペクトのソースコード分析

ソースコード分析も3つの部分に分かれています

  • 1.分析的側面
  • 2.動的プロキシを作成します
  • 3.電話する
    /**
     * 把一条链上的都初始化
     *
     * 有一条链, 这条链上都有一个父类接口 MethodInterceptor.
     * 也就是说, 链上都已一种类型的工人. 但每种工人的具体实现是不同的. 不同的工人做不同的工作
     *
     * 这里定义了一个责任链. 连上有两个工人. 一个是前置通知. 一个是环绕通知.
     * 前置通知和环绕通知实现的接口是不同的. 为了让他们能够在一条链上工作. 我们自定义了一个MethodBeforeAdviceInterceptor
     * 相当于为BaseBeforeAdvice()包装了一层MethodInterceptor
     */

    List<MethodInterceptor> list = new ArrayList<>();
    list.add(new BeforeAdviceInterceptor(new BaseBeforeAdvice()));
    list.add(new BaseAroundAdvice());

ソースコード分析、AOPアノテーションへの入り口:

package com.lxl.www.aop;

import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configurable
// 使用注解的方式引入AOP
@EnableAspectJAutoProxy
@ComponentScan("com.lxl.www.aop")
public class MainConfig {

}

AOPを導入するには、構成ファイルに@EnableAspectJAutoProxyプロキシを追加する必要があります。その後、AOPの導入を削除する場合は、このアノテーションをコメントアウトするだけで済みます。このアノテーションは、AOPエントリ全体を説明します。

次に、アノテーションクラスを入力します

/**
   * 责任链调用
   */
  public static class MyMethodInvocation implements MethodInvocation {

    // 这是责任链
    protected List<MethodInterceptor> list;
    // 目标类
    protected final BaseCalculate target;

    public MyMethodInvocation(List<MethodInterceptor> list) {
      this.list = list;
      this.target = new BaseCalculate();
    }

    int i = 0;

    public Object proceed() throws Throwable {
      if (i == list.size()) {
        /**
         * 执行到责任链的最后一环, 执行目标方法
         */
        return target.add(2, 2);
      }
      MethodInterceptor interceptor = list.get(i);
      i++;
      /**
       * 执行责任链调用
       * 这个调用链第一环是: 包装后的前置通知
       * 调用链的第二环是: 环绕通知.
       * 都执行完以后, 执行目标方法.
       */
      return interceptor.invoke(this);
    }

    @Override
    public Object getThis() {
      return target;
    }

    @Override
    public AccessibleObject getStaticPart() {
      return null;
    }

    @Override
    public Method getMethod() {
      try {
        return target.getClass().getMethod("add", int.class, int.class);
      } catch (NoSuchMethodException e) {
        e.printStackTrace();
      }
      return null;
    }

    @Override
    public Object[] getArguments() {
      return new Object[0];
    }
  }

}

つまり、EnableAspectJAutoProxyクラスが@Importアノテーションクラスを追加し、ImportアノテーションがBeanをIoCコンテナに追加できることがわかります。

以下にAspectJAutoProxyRegistrarクラスを入力します 

package org.springframework.context.annotation;

import org.springframework.aop.config.AopConfigUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

    /**
     * Register, escalate, and configure the AspectJ auto proxy creator based on the value
     * of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing
     * {@code @Configuration} class.
     */
    @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);
            }
        }
    }

}

BeanDefinitionがImportBeanDefinitionRegistrarを使用して登録されていることがわかります。

ImportBeanDefinitionRegistrarは通常、@ Importと組み合わせて使用​​され、BeanDefinitionをコンテナーに登録することに注意してください。

登録方法は?具体的な実装をご覧ください。

AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

登録名はAnnotationAwareAspectJAutoProxyCreatorinternalAutoProxyCreatorです。

@Nullable
    public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
            BeanDefinitionRegistry registry, @Nullable Object source) {

        /**
         * 注册一个AnnotationAwareAspectJAutoProxyCreator类型的bean定义
         */
        return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
    }

上記の構造は次のとおりです。

SpringAOPソースコード分析

 

AnnotationAwareAspectJAutoProxyCreatorクラスのBeanが登録されていることがわかります。これはどのようなクラスですか?クラスの構造を見てみましょう。このクラスの継承構造は非常に巨大で、これに関連する継承構造のみを確認します。コンテンツ

SpringAOPソースコード分析

 

アスペクトの分析と動的プロキシの作成はすべて、Beanのポストプロセッサで実行されます。以下は、AOPの実現原理とcreateBean(Beanの作成)のプロセスの比較です。

SpringAOPソースコード分析

 

上の図は、Beanのロードプロセス中に呼び出される9つのポストプロセッサを示しています。InstantiationAwareBeanPostProcessorポストプロセッサは、Beanが作成される前に呼び出され、アスペクトを解析するプロセスであるこのクラスに対してAOPを作成する必要があるかどうかを判断します。 InstantiationAwareBeanPostProcessorは、AnnotationAwareAspectJAutoProxyCreatorに実装されています。ポストプロセッサのインターフェイス。postProcessBeforeInstantiationメソッドが書き直されました。

createBeanの第3段階の初期化の後、AOPの動的プロキシを作成するには、BeanPostProcessポストプロセッサを呼び出します。AnnotationAwareAspectJAutoProxyCreatorもBeanPostProcessインターフェイスを実装します。postProcessAfterInitializationをオーバーライドします。

同時に、AOPの循環依存関係を処理する必要もあります。循環依存関係を処理するには、属性割り当ての前にSmartInstantiationAwareBeanPostProcessorポストプロセッサを呼び出してから、getEarlyBeanReferenceメソッドを書き換えます。AnnotationAwareAspectJAutoProxyCreatorも実装していることがわかります。 SmartInstantiationAwareBeanPostProcessorインターフェース。getEarlyBeanReferenceメソッドを書き直します。

スペースに限りがあるため、共有できるのはコンテンツの一部のみです。フルバージョンが必要な友達は、転送とフォローをサポートできます。ありがとうございます。

おすすめ

転載: blog.csdn.net/Ppikaqiu/article/details/112906786