Principe de mise en œuvre de spring5.x-AOP et analyse du code source

Ci-dessus : principe de spring5.x-listener et implémentation du code source


Articles de cette série :

                Principe de spring5.x-listener et implémentation du code source

                analyse de dépendance circulaire spring5.x-solve

                Apprentissage du code source du module spring5.x-IOC

                Introduction à spring5.x et à son environnement de lecture de code source Spring correspondant


Qu’est-ce que springAop ?

Spring AOP (Aspect-Oriented Programming) est une fonctionnalité importante du framework Spring, qui fournit un paradigme de programmation basé sur des préoccupations transversales. AOP permet aux développeurs de séparer les préoccupations transversales (telles que la journalisation, la gestion des transactions, la sécurité, etc.) de la logique métier de base, ce qui se traduit par une meilleure structure et maintenabilité du code.

Dans la programmation orientée objet traditionnelle, les fonctions sont généralement dispersées entre différents modules de l'application, comme l'accès aux bases de données, la gestion des exceptions, etc. Cela entraîne beaucoup de code répétitif, difficile à maintenir et à étendre. AOP permet de définir des préoccupations transversales de manière déclarative et de les appliquer à plusieurs modules de l'application sans modifier le code d'origine.

Dans Spring AOP, la préoccupation transversale est appelée l'aspect (Aspect), et l'objet découpé est appelé l'objet cible (Target Object). L'implémentation d'AOP dépend du mode proxy et de la technologie de proxy dynamique. Spring propose deux méthodes principales d'implémentation d'AOP :

  1. AOP basé sur un proxy : cette approche utilise des proxys dynamiques JDK ou des proxys dynamiques CGLIB pour créer des objets proxy et intégrer des aspects dans des appels de méthode sur l'objet cible. Si l'objet cible implémente l'interface, utilisez le proxy dynamique JDK ; sinon, utilisez le proxy dynamique CGLIB. L'AOP basé sur un proxy peut intégrer une logique d'aspect dans les objets cibles au moment de l'exécution.

  2. AOP implanté en Java pur au moment de la compilation : Cette méthode implémente AOP via l'amélioration du bytecode au moment de la compilation. Elle utilise les annotations et le compilateur d'AspectJ pour intégrer des aspects dans le bytecode de l'objet cible pendant le processus de compilation. Contrairement à l'AOP basé sur un proxy, l'AOP intégré au moment de la compilation Java pur ne nécessite pas de proxy dynamique au moment de l'exécution et offre donc des performances plus élevées.

Spring AOP prend en charge les fonctions d'aspect communes suivantes :

  • Avant conseil : actions effectuées avant l'appel de la méthode cible.

  • Après conseil : actions effectuées après l'appel de la méthode cible, qu'une exception se produise ou non.

  • Après avoir renvoyé un conseil : L'action à effectuer après que la méthode cible ait renvoyé avec succès un résultat.

  • Conseil d'exception (après avoir lancé un conseil) : actions effectuées après que la méthode cible a levé une exception.

  • Autour des conseils : entoure l'appel de la méthode cible et peut effectuer des opérations personnalisées avant et après l'appel.

Spring AOP est un mécanisme de mise en œuvre d'une programmation orientée aspect, qui peut aider les développeurs à mieux gérer les problèmes transversaux et fournit des méthodes flexibles pour gérer les fonctions courantes telles que la journalisation, les transactions et la sécurité, améliorant ainsi la réutilisabilité et la disponibilité du code.

Utilisation de base de l'AOP printanière

Veuillez vous référer à l'article que j'ai écrit à l'origine : Spring's AOP

Apprentissage du code source Spring AOP

73725ee6b5bf7a7f3aa9e1c9d5cf9ef8.png

Initialisation AOP

d2951792ca3bf582edfe146fd4364dde.png

Lorsque Spring est initialisé sur org.springframework.context.support.AbstractApplicationContext#refresh(), il existe un processeur de notification d'initialisation et la méthode suivante sera appelée pour l'initialisation (les autres sont ignorées et lisent l'article précédent)

6ae398dcd4be66d4aa0d0d6daee7b17c.png

Lorsque @EnableAspectJAutoProxy(proxyTargetClass = true) est annoté sur une classe de configuration personnalisée ou une classe de démarrage, cela indique que le projet est activé pour AOP.

6bb2bd46d69ec1b1a2fd944ac1777b91.png

Configurez les annotations de la classe de démarrage :

@EnableAspectJAutoProxy

Entrez @EnableAspectJAutoProxy et constatez que AspectJAutoProxyRegistrar.class est importé via @import.

14216ca2e0f96590ddb69d66f7bde529.png

Emplacement du code : org.springframework.context.annotation.AspectJAutoProxyRegistrar#registerBeanDefinitions

Diagramme de classes :bc4eac3aef8a3606539faba83358f5c8.png

@Override
  public void registerBeanDefinitions(
      AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    //通过反射方式进行注入

    AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

    AnnotationAttributes enableAspectJAutoProxy =
        AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
    //cglib代理
    if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
      AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
    }
    //jdk代理(同理)
    if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
      AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
    }
  }

b85d3086524b4b9bca19a269e53f290e.png

C'est-à-dire qu'au démarrage du printemps, il utilisera l'annotation @EnableAspectJAutoProxy pour identifier s'il faut démarrer aop. S'il est ajouté par défaut, l'aop sera démarré. La classe AspectJAutoProxyRegistrar est importée via @import dans cette annotation pour l'initialisation. , et la classe a Une méthode registerBeanDefinitions est exécutée.

Ensuite, vous entrerez : org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

exposedObject = this.initializeBean(beanName, exposedObject, mbd);

Remarque : La couche supérieure ici est en fait AnnotationAwareAspectJAutoProxyCreator, mais la couche inférieure est AbstractAutowireCapableBeanFactory. Regardez le diagramme de classes ci-dessous :

9e9352e4a7776620d7321e97ebdd9e89.png

Emplacement de la classe : org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator#initBeanFactory

Voici la classe d'usine qui initialise le bean. En fait, il est encore en cours de rafraîchissement ici.

6a6aa7f745722b26e011d13fca0cacdc.png

protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        super.initBeanFactory(beanFactory);
        if (this.aspectJAdvisorFactory == null) {
            this.aspectJAdvisorFactory = new ReflectiveAspectJAdvisorFactory(beanFactory);
        }
//用于管理切面通知的构建和配置,用于实现横切关注点的处理。
        this.aspectJAdvisorsBuilder = new AnnotationAwareAspectJAutoProxyCreator.BeanFactoryAspectJAdvisorsBuilderAdapter(beanFactory, this.aspectJAdvisorFactory);
    }

Il faut faire attention ici, et la prochaine étape consiste à ajuster le CIO (vous pouvez lire l'article original si vous ne le comprenez pas).

Emplacement du code : org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessBeforeInstantiation

public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
    //缓存获取key
        Object cacheKey = this.getCacheKey(beanClass, beanName);
    //为空或不包含
        if (beanName == null || !this.targetSourcedBeans.contains(beanName)) {
      //如果有有解析直接返回
            if (this.advisedBeans.containsKey(cacheKey)) {
                return null;
            }
      //如果不是基础bean就直接跳过
            if (this.isInfrastructureClass(beanClass) || this.shouldSkip(beanClass, beanName)) {
                this.advisedBeans.put(cacheKey, Boolean.FALSE);
                return null;
            }
        }
    //如果beanName不是空
        if (beanName != null) {
            TargetSource targetSource = this.getCustomTargetSource(beanClass, beanName);
      //获取目标路劲不为空 通过工理方式获取并放到代理缓存中
            if (targetSource != null) {
                this.targetSourcedBeans.add(beanName);
                Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
                Object proxy = this.createProxy(beanClass, beanName, specificInterceptors, targetSource);
                this.proxyTypes.put(cacheKey, proxy.getClass());
                return proxy;
            }
        }

        return null;
    }

b3845d9c1e387fe415a3aed38a831d28.png

Saisissez ensuite ShouldSkip, cette méthode est principalement utilisée pour déterminer s'il faut ignorer un bean spécifique

Fichier:org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator#shouldSkip

protected boolean shouldSkip(Class<?> beanClass, String beanName) {
        List<Advisor> candidateAdvisors = this.findCandidateAdvisors();
        Iterator var4 = candidateAdvisors.iterator();

        Advisor advisor;
        do {
            if (!var4.hasNext()) {
                return super.shouldSkip(beanClass, beanName);
            }

            advisor = (Advisor)var4.next();
        } while(!(advisor instanceof AspectJPointcutAdvisor) || !((AbstractAspectJAdvice)advisor.getAdvice()).getAspectName().equals(beanName));

        return true;
    }

Recherchez la méthode findAdvisorBeans via les findCandidateAdvisors ci-dessus pour créer les vdisors de l'aspect.

Emplacement du code : org.springframework.aop.framework.autoproxy.BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans

//获取所有通知者
public List<Advisor> findAdvisorBeans() {
    //通知者名称列表
        String[] advisorNames = null;
    //同步锁
        synchronized(this) {
      //获取缓存列表
            advisorNames = this.cachedAdvisorBeanNames;
      //如果为空
            if (advisorNames == null) {
        //通过从工厂类中去获取(这里不深入前端ioc类似)
                advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Advisor.class, true, false);
        //然后进行赋值给缓存
                this.cachedAdvisorBeanNames = advisorNames;
            }
        }
    //长度为空直接返回空
        if (advisorNames.length == 0) {
            return new LinkedList();
        } else {
      //初始化列表
            List<Advisor> advisors = new LinkedList();
            String[] var3 = advisorNames;
            int var4 = advisorNames.length;
      //从ioc容器中获取所有的bean名称
            for(int var5 = 0; var5 < var4; ++var5) {
                String name = var3[var5];
                if (this.isEligibleBean(name)) {
                    if (this.beanFactory.isCurrentlyInCreation(name)) {
                        if (logger.isDebugEnabled()) {
                            logger.debug("Skipping currently created advisor '" + name + "'");
                        }
                    } else {
                        try {
                            advisors.add(this.beanFactory.getBean(name, Advisor.class));
                        } catch (BeanCreationException var11) {
                            Throwable rootCause = var11.getMostSpecificCause();
                            if (rootCause instanceof BeanCurrentlyInCreationException) {
                                BeanCreationException bce = (BeanCreationException)rootCause;
                                String bceBeanName = bce.getBeanName();
                                if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) {
                                    if (logger.isDebugEnabled()) {
                                        logger.debug("Skipping advisor '" + name + "' with dependency on currently created bean: " + var11.getMessage());
                                    }
                                    continue;
                                }
                            }

                            throw var11;
                        }
                    }
                }
            }

            return advisors;
        }
    }

Entrez ensuite la méthode isAspect.

Emplacement du code : org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactory#isAspect

public boolean isAspect(Class<?> clazz) {
        return this.hasAspectAnnotation(clazz) && !this.compiledByAjc(clazz);
    }

    private boolean hasAspectAnnotation(Class<?> clazz) {
        return AnnotationUtils.findAnnotation(clazz, Aspect.class) != null;
    }

Fichier:org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getAdvisors

public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
    //获取aspect类
        Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
    //获取切面类的名称
        String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
    //校验切面类
        this.validate(aspectClass);
    //通过包装类包装
        MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory = new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
        List<Advisor> advisors = new ArrayList();
    //获取所有切面类 @PointCut注解的方法
        Iterator var6 = this.getAdvisorMethods(aspectClass).iterator();

        while(var6.hasNext()) {
            Method method = (Method)var6.next();
      //循环解析切面中的方法
            Advisor advisor = this.getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
            if (advisor != null) {
                advisors.add(advisor);
            }
        }

        if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
            Advisor instantiationAdvisor = new ReflectiveAspectJAdvisorFactory.SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
            advisors.add(0, instantiationAdvisor);
        }

        Field[] var12 = aspectClass.getDeclaredFields();
        int var13 = var12.length;

        for(int var14 = 0; var14 < var13; ++var14) {
            Field field = var12[var14];
            Advisor advisor = this.getDeclareParentsAdvisor(field);
            if (advisor != null) {
                advisors.add(advisor);
            }
        }

        return advisors;
    }

Ce qui suit est d'obtenir la classe d'aspect grâce à cela, bien sûr, c'est dans l'ordre.

Fichier:org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getAdvisorMethods

//获取一个切面类(aspectClass)中的方法列表
private List<Method> getAdvisorMethods(Class<?> aspectClass) {
        final List<Method> methods = new ArrayList();
        ReflectionUtils.doWithMethods(aspectClass, new MethodCallback() {
            public void doWith(Method method) throws IllegalArgumentException {
                if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {
                    methods.add(method);
                }

            }
        });
        Collections.sort(methods, METHOD_COMPARATOR);
        return methods;
    }

L'ordre du début à la fin est déterminé lors de l'initialisation, qui est l'ordre lorsque nous exécutons : Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class}

static {
        CompoundComparator<Method> comparator = new CompoundComparator();
        comparator.addComparator(new ConvertingComparator(new InstanceComparator(new Class[]{Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class}), new Converter<Method, Annotation>() {
            public Annotation convert(Method method) {
                AspectJAnnotation<?> annotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method);
                return annotation != null ? annotation.getAnnotation() : null;
            }
        }));
        comparator.addComparator(new ConvertingComparator(new Converter<Method, String>() {
            public String convert(Method method) {
                return method.getName();
            }
        }));
        METHOD_COMPARATOR = comparator;
    }

Grâce à la logique précédente, nous atteignons finalement cet InstantiationModelAwarePointcutAdvisorImpl pour créer une implémentation spécifique d'aop.

public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut, Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
    //切面定义切点
        this.declaredPointcut = declaredPointcut;
    //切面对象
        this.declaringClass = aspectJAdviceMethod.getDeclaringClass();
    //切面方法名称
        this.methodName = aspectJAdviceMethod.getName();
    //切面参数类型
        this.parameterTypes = aspectJAdviceMethod.getParameterTypes();
    //切面方法对象
        this.aspectJAdviceMethod = aspectJAdviceMethod;
    //asperctj通知工厂
        this.aspectJAdvisorFactory = aspectJAdvisorFactory;
    //aspect实例工厂
        this.aspectInstanceFactory = aspectInstanceFactory;
    //切面具体顺序
        this.declarationOrder = declarationOrder;
    //切面名称
        this.aspectName = aspectName;
    //判断是否需要延迟加载
        if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
            Pointcut preInstantiationPointcut = Pointcuts.union(aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);
            this.pointcut = new InstantiationModelAwarePointcutAdvisorImpl.PerTargetInstantiationModelPointcut(this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory);
            this.lazy = true;
        } else {
            this.pointcut = this.declaredPointcut;
            this.lazy = false;
            this.instantiatedAdvice = this.instantiateAdvice(this.declaredPointcut);
        }

    }

Ce qui précède parvient enfin à la création de haricots.

564b845659ead900113a275d01bc6ecc.png

L'initialisation seule est un si gros code (une partie est omise ~), c'est vraiment ~

proxy dynamique

e29c6ceb366f425e768d619082cbb7e3.png

La méthode de proxy par défaut de Spring est le proxy dynamique JDK, mais si l'objet cible n'implémente pas l'interface, il passera automatiquement au proxy dynamique CGLIB. Cela garantit que la fonction AOP peut être utilisée dans la plupart des cas sans qu'il soit nécessaire de configurer manuellement la méthode proxy.

3492c461555ae83ee5b39a4344e1d4f1.png

Après l'initialisation précédente, lorsque la méthode est appelée, spring aura par défaut une chaîne d'interception pour connecter l'agent et l'aspect, et faire correspondre l'agent via la réflexion d'invocation.

Emplacement du code : org.springframework.aop.framework.ReflectiveMethodInvocation#proceed

//AOP 拦截器链可以按照顺序依次执行拦截器或拦截器通知的逻辑
public Object proceed() throws Throwable {
    //最后一个拦截器会进入
        if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
      //返回切点
            return this.invokeJoinpoint();
        } else {
      //获取需要运行的拦截器
            Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
            if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
                InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice;
                return dm.methodMatcher.matches(this.method, this.targetClass, this.arguments) ? dm.interceptor.invoke(this) : this.proceed();
            } else {
        //执行拦截器方法
                return ((MethodInterceptor)interceptorOrInterceptionAdvice).invoke(this);
            }
        }
    }

La méthode ci-dessus a toujours été exécutée de manière récursive.ec39a62d91120810516859d1dc800c98.png

Saisissez ensuite : méthode d'invocation

Emplacement du code : org.springframework.aop.interceptor.ExposeInvocationInterceptor#invoke

public Object invoke(MethodInvocation mi) throws Throwable {
        MethodInvocation oldInvocation = (MethodInvocation)invocation.get();
        invocation.set(mi);

        Object var3;
        try {
            var3 = mi.proceed();
        } finally {
            invocation.set(oldInvocation);
        }

        return var3;
    }

4cc800cccc03edba5f32c0cc9f13db04.png

On peut voir qu'il s'agit de l'intercepteur d'exécution final, où le modèle de conception de chaîne de responsabilité est utilisé pour les appels récursifs. L'ordre est : Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class} est cohérent avec l'ordre d'initialisation.

Quant aux étudiants qui ne comprennent pas le modèle de conception de chaîne de responsabilité, veuillez lire : Modèle de conception-Modèle de chaîne de responsabilité

enfin

    Spring AOP est relativement difficile à déboguer, en particulier divers agents. Si vous ne comprenez pas cet article, veuillez lire l'article de référence ci-dessous pour plus de référence. Bien sûr, si vous pouvez le comprendre, il est recommandé de jeter un œil aux journaux de certains frameworks open source ou de certains middlewares ou composants associés intégrés par spring, springboot et springcloud. Cela vaut la peine d'y jeter un coup d'œil. les principes sont en fait les mêmes. Bien sûr, ce que j'ai vu le plus dans mes travaux publics, c'est la mise en œuvre d'outils tels que les journaux globaux et les intercepteurs personnalisés tels que le cryptage.

référence:

https://cloud.tencent.com/developer/article/1512235

https://www.cnblogs.com/FatalFlower/p/15572344.html

https://blog.csdn.net/pengjianglilive/article/details/109608986

Je suppose que tu aimes

Origine blog.csdn.net/qq_16498553/article/details/132267836
conseillé
Classement