Texto longo Wanzi! Análise aprofundada do código-fonte SpringAOP, do zero para fazê-lo em minutos

1. Conheça AOP e seu uso

Em segundo lugar, as características do AOP

2.1 Spring AOP

2.1.1 É implementado com base em proxy dinâmico

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

Estude o uso de interfaces para implementar AOP, o objetivo é entender melhor as duas maneiras de spring usa proxy dinâmico para implementar AOP

2.1.2 Spring fornece suporte para AspectJ, mas apenas fornece suporte para algumas funções: a saber, análise de pontos de AspectJ (expressão) e correspondência

Quando escrevemos aspectos, geralmente usamos @Aspect, @Before, @Pointcut, @After, @AfterReturning, @AfterThrowing, etc., que são fornecidos por AspectJ.

Sabemos que AspectJ é muito útil e eficiente. Então, por que o Spring não usa todo o conjunto de coisas de AspectJ? Especialmente a tecelagem estática de AspectJ.

Vamos primeiro dar uma olhada nas características do 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是在实际代码运行前就完成了织入, 因此可以认为他生成的类是没有额外运行开销的.

Extensão: por que a tecelagem estática de AspectJ não é usada aqui? Porque se você introduzir a tecelagem estática, você precisa usar o próprio analisador de AspectJ. Os arquivos AspectJ são arquivos que terminam com o sufixo aj. O Spring não tem como fazer isso, então AspectJ deve ser usado. para análise. Isso aumenta o custo do Spring.

Três, método de configuração AOP

O Spring AOP e AspectJ mencionados acima. Ele também disse que AspectJ define muitas anotações, como: @Aspect, @Pointcut, @Before, @After, etc. No entanto, usamos Spring AOP para escrever em código Java puro. Em outras palavras Ele pertence completamente ao Spring e não tem nada a ver com AspectJ. O Spring apenas segue os conceitos de AspectJ. Incluindo as anotações do pacote jar fornecido por AspectJ. No entanto, não depende da funcionalidade de AspectJ.

Existem três maneiras de configurar Spring AOP.

  • A primeira: configuração baseada na interface. Na versão Spring 1.2, o que é fornecido é totalmente implementado com base na interface
  • O segundo tipo: configuração baseada em esquema.Após a primavera 2.0, xml é usado para configuração.
  • O terceiro tipo: baseado na anotação @Aspect.Este método é o mais simples e conveniente.Embora seja chamado de AspectJ, não tem nada a ver com AspectJ.

Porque usamos principalmente anotações para configurar AOP em nosso trabalho diário, e as anotações são implementadas principalmente com base na primeira interface.  Portanto, vamos nos concentrar no primeiro e terceiro métodos de configuração.

3.1 Configuração baseada no modo de interface. Na versão Spring 1.2, o que é fornecido é implementado totalmente baseado no modo de interface

Este método é o método mais antigo, mas como o spring tem uma boa compatibilidade com versões anteriores, ainda existem muitos códigos usando este método,  como transações declarativas.

Então, como Spring implementa AOP quando AspectJ não é introduzido? Vejamos um exemplo:

1. Defina uma classe de interface de lógica de negócios

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. Definir classes de lógica de negócios

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. Defina a classe de notificação

Aviso prévio

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");
    }

}

Postar notificação

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() 结束=========== ");
    }

}

Notificação surround

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;
    }

}

Classe de configuração

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;
    }

}

Como eu disse antes, o AOP depende do ioc, que deve ser registrado como um bean para atingir a funcionalidade AOP

Entrada de método

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);
    }

}

Resultados de:

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

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

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

Por meio da observação, descobrimos que a ordem de execução é: pré-notificação -> método frontal de notificação circundante -> lógica de destino -> método posterior de notificação circundante -> notificação posterior.

Então, a pré-notificação é realizada primeiro ou o método de pré-notificação das notificações ao redor? Depende da ordem de configuração do arquivo de configuração

Aqui, colocamos a notificação surround no final, para que a notificação surround seja executada após a notificação anterior.

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

Então, se colocarmos a notificação surround antes da pré-notificação, a notificação surround será executada primeiro

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

resultado da operação

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

执行目标方法: add

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

A cadeia de responsabilidade foi mencionada acima, então o que é uma cadeia de responsabilidade? Conforme mostrado na figura abaixo:

Análise de código-fonte Spring AOP

 

Há uma linha de montagem. Por exemplo, uma linha de produção. Há muitos processos nela. O processo 1 pode ser concluído antes que o processo 2 possa ser executado e assim por diante.

Combinado com a demonstração acima, vamos dar uma olhada em uma demonstração chamada pela cadeia de responsabilidade.

Acima, definimos dois métodos. Um é a notificação de encaminhamento que BaseBeforeAdvice implementa MethodBeforeAdvice e o outro é a notificação surround que BaseAroundAdvice implementa MethodInterceptor. Se você quiser colocar essas duas notificações em uma cadeia. Então, elas devem implementar a mesma interface. Mas, é diferente agora.

Sabemos que a notificação surround tem duas partes, uma é a notificação frontal da notificação surround e a outra é a notificação traseira da notificação surround. Portanto, podemos considerar a notificação frontal como a parte da notificação frontal da notificação surround.

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();
  }
}

Este código envolve a pré-notificação e permite que ela seja estendida para implementar a interface MethodInterceptor, um método que estende a interface.

Em seguida, temos que criar uma cadeia. Esta cadeia pode ser entendida como cada trabalhador na linha de montagem. Cada trabalhador lida com um processo. Para poder ligar de forma unificada. Todos os trabalhadores devem implementar a mesma interface. A definição de a cadeia de responsabilidade é a seguinte:

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

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

Uma cadeia de responsabilidade é definida aqui. Há dois trabalhadores conectados. Um é a pré-notificação. O outro é a notificação circundante.

As interfaces implementadas por pré-notificação e notificação circundante são diferentes. Para permitir que funcionem em cadeia. Personalizamos um MethodBeforeAdviceInterceptor, que é equivalente a envolver uma camada de MethodInterceptor para BaseBeforAdvice (), seguido pela chamada de cadeia de responsabilidade.

/**
   * 责任链调用
   */
  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];
    }
  }

}

Aqui, nos concentramos no   método proced (). Ciclicamente, obtemos a notificação da cadeia de responsabilidade da lista e, em seguida, executamos o método invoke ()

Análise de código-fonte Spring AOP

 

O método proced () é um loop em cadeia. No início i = 0, list (0) é o aviso prévio, quando o aviso prévio é chamado, o método BeforeAdviceInterceptor.invoke () é chamado e o método invocation.proceed () método é chamado, Voltar para o método MyMethodInvocation.proceed ().

Então i = 1, list (1) é a notificação envolvente, quando a notificação envolvente é chamada, invocation.proceed () é chamado novamente; há um retorno para o método MyMethodInvocation.proceed ().

Este já é o último link da lista, e o método invoke () não será chamado depois. O segundo é executar o método de destino. Após o término da execução, toda a chamada termina.

Esta é uma cadeia de chamadas.

Existem dois pontos sobre a cadeia de responsabilidade:

1. Deve haver uma chamada unificada, ou seja, uma classe abstrata comum.

2. Use loop ou recursão para completar a chamada da cadeia de responsabilidade

Resumindo:

O método acima usa o método de fábrica do proxy bean ProxyFactoryBean. Ele tem duas limitações:

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. Apenas uma classe pode ser aprimorada por vez. Se você aprimorar várias classes, será necessário definir vários ProxyFactoryBeans

2. A granularidade aprimorada só pode estar no nível da classe e não pode ser atribuída a um método de aprimoramento. Isso ainda tem certas restrições

3.2 A forma baseada na anotação @Aspect.Esta forma é a mais simples e conveniente.Embora se denomine AspectJ, não tem nada a ver com AspectJ.

3.2.1 O princípio analítico do aspecto @Aspect

O primeiro método acima é um estudo detalhado do princípio de implementação do método de interface AOP. O método de anotação de AOP, o último é analisar @Befor, @After e outras anotações na classe de aspecto @Aspect em um Consultor. A classe com @Before serão analisados ​​em um Advisor, classes com métodos @After também serão analisados ​​em um Advisor ... outros métodos notificados também serão analisados ​​em Advisor. A lógica aprimorada é definida no Advisor, ou seja, lógica como @Befor e @After, And métodos que precisam ser aprimorados, como o método div.

Vamos analisar o princípio de implementação do uso de anotações @Aspect, @Before, @After. Conforme mencionado acima, é para gerar o Advisor de @Before, @After

Existem três partes aqui.

  • Parte 1: Analise o método de notificação com @Before etc. em @Aspect, e analise-o como Consultor
  • Parte 2: Ao criar o Bean, crie um proxy dinâmico
  • A terceira parte: chamar. Quando chamar, execute a cadeia de responsabilidade, faça um loop em todas as notificações. Por fim, produza o resultado.

Abaixo analisamos de acordo com essas três partes.

A primeira etapa: analise o método de notificação com @Before etc. em @Aspect e analise-o como Consultor. Conforme mostrado abaixo:

Análise de código-fonte Spring AOP

 

Quando foi realizada a primeira etapa?

Ao criarBean, muitos pós-processadores PostProcessor serão chamados e serão executados quando o primeiro pós-processador for chamado. O processo de execução é aproximadamente o seguinte: Obtenha todas as BeanDefinitions e determine se a classe tem anotações @Aspect. Em seguida, vá para os métodos anotados com @Aspect para encontrar @Before, @After, @AfterReturning, @AfterThrowing, cada notificação irá gerar um Consultor

Etapa 2: Ao criar o Bean, crie um proxy dinâmico

Análise de código-fonte Spring AOP

 

Existem três estágios no createBean. Em qual estágio o proxy dinâmico é criado?

O processo geral é:

Ao criarBean, o pós-processador do bean é chamado após a inicialização ser concluída. Obtenha todos os Consultores, faça um loop através dos Consultores e, em seguida, execute correspondências com base nas expressões na execução. Corresponda com o bean criado atualmente e corresponda, Crie uma dinâmica proxy.

Existem muitos tipos de pointcut. Os códigos acima mencionados são:

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();
  }
}

E nossas anotações são correspondidas na forma de expressões de execução

Etapa 3: Chame. Ao chamar, execute a cadeia de responsabilidade, faça um loop em todas as notificações e, por fim, envie o resultado.

Análise de código-fonte Spring AOP

 

3.2.2 Análise do código-fonte do aspecto AOP

A análise do código-fonte também é dividida em três partes

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

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

A entrada para a análise do código-fonte, anotações 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 {

}

Para introduzir o AOP, precisamos adicionar o proxy @EnableAspectJAutoProxy no arquivo de configuração. Em seguida, se você quiser remover a introdução do AOP, você só precisa comentar esta anotação. Esta anotação explica toda a entrada do AOP.

Em seguida, insira a classe de anotação

/**
   * 责任链调用
   */
  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];
    }
  }

}

Isto é, vemos que a classe EnableAspectJAutoProxy adiciona uma classe de anotação @Import e sabemos que a anotação Import pode adicionar um bean ao contêiner IoC.

Digite a classe AspectJAutoProxyRegistrar abaixo 

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);
            }
        }
    }

}

Vemos que um BeanDefinition é registrado usando ImportBeanDefinitionRegistrar.

Deve ser lembrado que ImportBeanDefinitionRegistrar é geralmente usado em combinação com @Import para registrar um BeanDefinition no contêiner.

Como se inscrever? Veja a implementação específica.

AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

O nome registrado é AnnotationAwareAspectJAutoProxyCreator internalAutoProxyCreator

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

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

A estrutura acima é a seguinte:

Análise de código-fonte Spring AOP

 

Vemos que o bean da classe AnnotationAwareAspectJAutoProxyCreator está registrado. Que tipo de classe é essa? Vamos dar uma olhada na estrutura da classe. A estrutura de herança desta classe é muito grande, olhamos apenas para a estrutura de herança relacionada a isso conteúdo

Análise de código-fonte Spring AOP

 

A análise de aspectos e a criação de proxies dinâmicos são todas realizadas no pós-processador do bean. A seguir, uma comparação do princípio de realização do AOP e o processo de createBean (criar bean).

Análise de código-fonte Spring AOP

 

A imagem acima mostra os 9 pós-processadores chamados durante o processo de carregamento do bean. O pós-processador InstantiationAwareBeanPostProcessor é chamado antes de o bean ser criado para determinar se um AOP precisa ser criado para esta classe, que é o processo de análise de aspectos. InstantiationAwareBeanPostProcessor é implementado em AnnotationAwareAspectJAutoProxyCreator A interface do pós-processador.O método postProcessBeforeInstantiation foi reescrito.

Após a inicialização do terceiro estágio de createBean, para criar um proxy dinâmico de AOP, chame o pós-processador BeanPostProcess, AnnotationAwareAspectJAutoProxyCreator também implementa a interface BeanPostProcess. Substitua postProcessAfterInitialization.

Ao mesmo tempo, também é necessário lidar com a dependência circular do AOP. Para lidar com a dependência circular, o pós-processador SmartInstantiationAwareBeanPostProcessor é chamado antes da atribuição do atributo e, em seguida, o método getEarlyBeanReference é reescrito. Vemos que AnnotationAwareAspectJAutoProxyCreator também implementa o Interface SmartInstantiationAwareBeanPostProcessor. E reescrever o método getEarlyBeanReference.

Devido a limitações de espaço, apenas parte do conteúdo pode ser compartilhado. Amigos que precisam da versão completa podem ajudar a encaminhar + seguir, obrigado!

Acho que você gosta

Origin blog.csdn.net/Ppikaqiu/article/details/112906786
Recomendado
Clasificación