Texte long Wanzi! Analyse approfondie du code source SpringAOP, à partir de zéro pour le faire en quelques minutes

1. Connaître l'AOP et son utilisation

Deuxièmement, les caractéristiques de l'AOP

2.1 Printemps AOP

2.1.1 Il est implémenté sur la base d'un proxy dynamique

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

Étudier l'utilisation des interfaces pour implémenter AOP, le but est de mieux comprendre les deux façons dont Spring utilise un proxy dynamique pour implémenter AOP

2.1.2 Spring prend en charge AspectJ, mais ne prend en charge que certaines fonctions: à savoir l'analyse (expression) et la mise en correspondance des points d'AspectJ

Lorsque nous écrivons des aspects, nous utilisons souvent @Aspect, @Before, @Pointcut, @After, @AfterReturning, @AfterThrowing, etc., qui sont fournis par AspectJ.

Nous savons qu'AspectJ est très utile et efficace. Alors pourquoi Spring n'utilise-t-il pas l'ensemble des éléments d'AspectJ, en particulier le tissage statique d'AspectJ.

Jetons d'abord un coup d'œil aux caractéristiques d'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是在实际代码运行前就完成了织入, 因此可以认为他生成的类是没有额外运行开销的.

Extension: Pourquoi le tissage statique d'AspectJ n'est-il pas utilisé ici? Parce que si vous introduisez le tissage statique, vous devez utiliser le propre analyseur d'AspectJ. Les fichiers AspectJ sont des fichiers se terminant par le suffixe aj. Spring n'a aucun moyen de le faire, donc AspectJ doit être utilisé Propre analyseur pour l'analyse, ce qui augmente le coût de Spring.

Trois, méthode de configuration AOP

Spring AOP et AspectJ mentionnés ci-dessus. Il a également indiqué qu'AspectJ définit de nombreuses annotations, telles que: @Aspect, @Pointcut, @Before, @After, etc. Cependant, nous utilisons Spring AOP pour écrire en pur code java. En d'autres termes Il appartient complètement à Spring, et n'a rien à voir avec AspectJ. Spring suit simplement les concepts d'AspectJ. Y compris les annotations du package jar fourni par AspectJ. Cependant, cela ne dépend pas de la fonctionnalité d'AspectJ.

Il existe trois façons de configurer Spring AOP.

  • Le premier: configuration basée sur l'interface. Dans la version Spring 1.2, ce qui est fourni est entièrement implémenté en fonction de l'interface
  • Le deuxième type: configuration basée sur un schéma. Après le printemps 2.0, xml est utilisé pour la configuration.
  • Le troisième type: basé sur l'annotation @Aspect. Cette méthode est la plus simple et la plus pratique. Bien qu'elle s'appelle AspectJ, elle n'a rien à voir avec AspectJ.

Parce que nous utilisons principalement des annotations pour configurer AOP dans notre travail quotidien, et que les annotations sont principalement implémentées sur la base de la première interface  , nous allons donc nous concentrer sur les première et troisième méthodes de configuration.

3.1 Configuration basée sur le mode interface. Dans la version Spring 1.2, ce qui est fourni est entièrement implémenté en fonction du mode interface

Cette méthode est la plus ancienne, mais comme Spring a fait une bonne compatibilité ascendante, il existe encore de nombreux codes utilisant cette méthode,  tels que les transactions déclaratives.

Alors, comment Spring implémente-t-il AOP quand AspectJ n'est pas introduit? Regardons un exemple:

1. Définissez une classe d'interface de logique métier

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. Définir les classes de logique métier

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. Définissez la classe de notification

Préavis

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

}

Message de notification

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

}

Notification 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 configuration

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

}

Comme je l'ai déjà dit, AOP s'appuie sur ioc, qui doit être enregistré en tant que bean pour atteindre la fonctionnalité AOP

Entrée de méthode

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

}

Résultats du:

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

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

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

Grâce à l'observation, nous avons constaté que l'ordre d'exécution est: pré-notification -> méthode avant de notification environnante -> logique cible -> méthode arrière de notification environnante -> notification arrière.

La pré-notification est-elle effectuée en premier, ou la méthode de pré-notification des notifications environnantes? Cela dépend de l'ordre de configuration du fichier de configuration

Ici, nous mettons la notification surround à la fin, de sorte que la notification surround est exécutée après la notification précédente.

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

Donc, si nous mettons la notification surround avant la pré-notification, la notification surround sera exécutée en premier

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

résultat de l'opération

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

执行目标方法: add

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

La chaîne de responsabilité a été mentionnée ci-dessus, qu'est-ce qu'une chaîne de responsabilité? Comme le montre la figure ci-dessous:

Analyse du code source Spring AOP

 

Il y a une ligne d'assemblage. Par exemple, une ligne de production. Elle contient de nombreux processus. Le processus 1 peut être terminé avant que le processus 2 ne puisse être exécuté, etc.

Combiné avec la démo ci-dessus, regardons une démo appelée par la chaîne de responsabilité.

Ci-dessus, nous avons défini deux méthodes. L'une est la notification avant BaseBeforeAdvice implémente MethodBeforeAdvice, et l'autre est la notification surround BaseAroundAdvice implémente MethodInterceptor. Si vous voulez mettre ces deux notifications sur une chaîne. Ensuite, ils doivent implémenter la même interface. Mais, c'est différent maintenant.

Nous savons que la notification surround comporte deux parties, l'une est la notification avant de la notification surround et l'autre est la notification arrière de la notification surround. Par conséquent, nous pouvons considérer la notification avant comme la partie de notification avant de la notification 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();
  }
}

Ce code encapsule la pré-notification et permet de l'étendre pour implémenter l'interface MethodInterceptor. Il s'agit d'une méthode qui étend l'interface.

Ensuite, nous devons créer une chaîne. Cette chaîne peut être comprise comme chaque travailleur sur la chaîne de montage. Chaque travailleur gère un processus. Afin de pouvoir appeler de manière unifiée. Tous les travailleurs doivent implémenter la même interface. La définition de la chaîne de responsabilité est la suivante:

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

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

Une chaîne de responsabilité est définie ici. Deux intervenants sont connectés. L'un est la pré-notification et l'autre la notification.

Les interfaces implémentées par la pré-notification et la notification environnante sont différentes. Afin de leur permettre de fonctionner en chaîne. Nous avons personnalisé un MethodBeforeAdviceInterceptor, ce qui équivaut à envelopper une couche de MethodInterceptor pour BaseBeforAdvice (), suivi de l'appel de chaîne de responsabilité.

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

}

Ici, nous nous concentrons sur la   méthode procède (). Nous recevons cycliquement la notification de la chaîne de responsabilité de la liste, puis exécutons la méthode invoke ()

Analyse du code source Spring AOP

 

La méthode proced () est une boucle en chaîne. Au début i = 0, list (0) est le préavis, lorsque le préavis est appelé, la méthode BeforeAdviceInterceptor.invoke () est appelée et l'invocation.proceed () est appelée, retour à la méthode MyMethodInvocation.proceed ().

Puis i = 1, list (1) est la notification environnante, lorsque la notification environnante est appelée, invocation.proceed () est à nouveau appelée; il y a un retour à la méthode MyMethodInvocation.proceed ().

Il s'agit déjà du dernier lien de la liste et la méthode invoke () ne sera pas appelée ultérieurement. La seconde consiste à exécuter la méthode cible. Une fois l'exécution terminée, l'appel entier se termine.

C'est une chaîne d'appels.

Il y a deux points à propos de la chaîne de responsabilité:

1. Il doit y avoir un appel unifié, c'est-à-dire une classe abstraite commune.

2. Utilisez la boucle ou la récursivité pour terminer l'appel de la chaîne de responsabilité

Pour résumer:

La méthode ci-dessus utilise la méthode de fabrique de bean proxy ProxyFactoryBean. Elle présente deux limitations:

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. Une seule classe peut être améliorée à la fois. Si vous améliorez plusieurs classes, vous devez définir plusieurs ProxyFactoryBeans.

2. La granularité améliorée ne peut être qu'au niveau de la classe et ne peut pas être affectée à une méthode à améliorer. Cela comporte encore certaines restrictions.

3.2 La méthode basée sur l'annotation @Aspect. Cette méthode est la plus simple et la plus pratique. Bien qu'elle s'appelle AspectJ, elle n'a rien à voir avec AspectJ.

3.2.1 Le principe analytique de l'aspect @Aspect

La première méthode ci-dessus est une étude détaillée du principe de mise en œuvre de la méthode d'interface AOP. La méthode d'annotation d'AOP, la dernière consiste à analyser les annotations @Befor, @After et d'autres dans la classe d'aspect @Aspect dans un conseiller. La classe avec @Before seront analysées dans un seul Advisor, les classes avec des méthodes @After seront également analysées dans un Advisor ... d'autres méthodes notifiées seront également analysées dans Advisor. Une logique améliorée est définie dans Advisor, c'est-à-dire une logique telle que @Befor et @After, Et les méthodes qui doivent être améliorées, telles que la méthode div.

Analysons le principe de mise en œuvre des annotations @Aspect, @Before, @After. Comme mentionné ci-dessus, il s'agit de générer Advisor à partir de @Before, @After

Il y a trois parties ici.

  • Partie 1: Analysez la méthode de notification avec @Before etc. sous @Aspect, et analysez-la en tant que conseiller
  • Partie 2: Lors de la création de Bean, créez un proxy dynamique
  • La troisième partie: appel. Lors de l'appel, exécutez la chaîne de responsabilité, bouclez toutes les notifications. Enfin, affichez le résultat.

Ci-dessous, nous analysons conformément à ces trois parties.

La première étape: analysez la méthode de notification avec @Before etc. sous @Aspect et analysez-la en tant que conseiller. Comme indiqué ci-dessous:

Analyse du code source Spring AOP

 

Quand la première étape a-t-elle été réalisée?

Lors de la création de Bean, de nombreux post-processeurs PostProcessor seront appelés, et ils seront exécutés lorsque le premier post-processeur sera appelé. Le processus d'exécution est à peu près le suivant: Obtenez toutes les BeanDefinitions et déterminez si la classe a des annotations @Aspect. Ensuite, allez à les méthodes annotées avec @Aspect pour trouver @Before, @After, @AfterReturning, @AfterThrowing, chaque notification générera un conseiller

Étape 2: lors de la création de Bean, créez un proxy dynamique

Analyse du code source Spring AOP

 

Il existe trois étapes dans createBean. À quelle étape le proxy dynamique est-il créé?

Le processus global est:

Lors de la création de Bean, le post-processeur du bean est appelé une fois l'initialisation terminée. Obtenez tous les conseillers, parcourez les conseillers en boucle, puis effectuez des correspondances en fonction des expressions de l'exécution. Procuration.

Il existe de nombreux types de pointcut. Les codes mentionnés ci-dessus sont:

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

Et nos annotations correspondent à la manière d'expressions d'exécution

Étape 3: Appel. Lors de l'appel, exécutez la chaîne de responsabilité, bouclez toutes les notifications. Enfin, affichez le résultat.

Analyse du code source Spring AOP

 

3.2.2 Analyse du code source de l'aspect AOP

L'analyse du code source est également divisée en trois parties

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

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

L'entrée de l'analyse du code source, les annotations 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 {

}

Pour introduire AOP, nous devons ajouter le proxy @EnableAspectJAutoProxy dans le fichier de configuration. Ensuite, si vous souhaitez supprimer l'introduction d'AOP, il vous suffit de commenter cette annotation. Cette annotation explique l'intégralité de l'entrée AOP.

Ensuite, entrez la classe d'annotation

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

}

Cela signifie que nous voyons que la classe EnableAspectJAutoProxy ajoute une classe d'annotation @Import, et nous savons que l'annotation Import peut ajouter un bean au conteneur IoC.

Entrez la classe AspectJAutoProxyRegistrar ci - dessous 

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

}

Nous voyons qu'un BeanDefinition est enregistré à l'aide de ImportBeanDefinitionRegistrar.

Il ne faut pas oublier que ImportBeanDefinitionRegistrar est généralement utilisé en combinaison avec @Import pour enregistrer un BeanDefinition dans le conteneur.

Comment s'inscrire Voir l'implémentation spécifique.

AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

Le nom enregistré est AnnotationAwareAspectJAutoProxyCreator internalAutoProxyCreator

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

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

La structure ci-dessus est la suivante:

Analyse du code source Spring AOP

 

Nous voyons que le bean de la classe AnnotationAwareAspectJAutoProxyCreator est enregistré. De quel type de classe s'agit-il? Jetons un coup d'œil à la structure de la classe. La structure d'héritage de cette classe est très énorme, nous ne regardons que la structure d'héritage liée à cela contenu

Analyse du code source Spring AOP

 

L'analyse des aspects et la création de proxies dynamiques sont toutes réalisées dans le post-processeur du bean, ce qui suit est une comparaison du principe de réalisation d'AOP et du processus de createBean (create bean).

Analyse du code source Spring AOP

 

L'image ci-dessus montre les 9 post-processeurs appelés pendant le processus de chargement du bean. Le post-processeur InstantiationAwareBeanPostProcessor est appelé avant la création du bean pour déterminer si un AOP doit être créé pour cette classe, qui est le processus d'analyse des aspects. Donc InstantiationAwareBeanPostProcessor est implémenté dans AnnotationAwareAspectJAutoProxyCreator L'interface du post-processeur La méthode postProcessBeforeInstantiation a été réécrite.

Après l'initialisation de la troisième étape de createBean, pour créer un proxy dynamique d'AOP, appelez le post-processeur BeanPostProcess, AnnotationAwareAspectJAutoProxyCreator implémente également l'interface BeanPostProcess. Remplacez postProcessAfterInitialization.

Dans le même temps, il est également nécessaire de gérer la dépendance circulaire d'AOP. Pour gérer la dépendance circulaire, le post-processeur SmartInstantiationAwareBeanPostProcessor est appelé avant l'attribution d'attribut, puis la méthode getEarlyBeanReference est réécrite. Nous voyons que AnnotationAwareAspectJAutoProxyCreator implémente également le Interface SmartInstantiationAwareBeanPostProcessor. Et réécrivez la méthode getEarlyBeanReference.

En raison du manque d'espace, seule une partie du contenu peut être partagée. Les amis qui ont besoin de la version complète peuvent aider à avancer et suivre, merci!

Je suppose que tu aimes

Origine blog.csdn.net/Ppikaqiu/article/details/112906786
conseillé
Classement