spring5.x-AOP implementation principle and source code analysis

Above: spring5.x-listener principle and source code implementation


Articles in this series:

                spring5.x-listener principle and source code implementation

                spring5.x-solve circular dependency analysis

                spring5.x-IOC module source code learning

                Spring5.x introduction and matching spring source code reading environment


What is springAop?

Spring AOP (Aspect-Oriented Programming) is an important feature of the Spring framework, which provides a programming paradigm based on cross-cutting concerns. AOP allows developers to separate cross-cutting concerns (such as logging, transaction management, security, etc.) from core business logic, resulting in better code structure and maintainability.

In traditional object-oriented programming, functionality is usually scattered among the various modules of the application, such as access to the database, exception handling, and so on. This leads to a lot of repetitive code, which is difficult to maintain and extend. AOP provides a way to define cross-cutting concerns in a declarative way without modifying the original code, and apply them to multiple modules of the application.

In Spring AOP, the cross-cutting concern is called the aspect (Aspect), and the object being cut into is called the target object (Target Object). The implementation of AOP depends on proxy mode and dynamic proxy technology. Spring provides two main AOP implementation methods:

  1. Proxy-based AOP: This method uses JDK dynamic proxy or CGLIB dynamic proxy to create a proxy object and weave aspects into the method call of the target object. If the target object implements the interface, the JDK dynamic proxy is used; otherwise, the CGLIB dynamic proxy is used. Proxy-based AOP can weave aspect logic into the target object at runtime.

  2. Pure Java compile-time implanted AOP: This approach implements AOP through compile-time bytecode enhancement, which uses AspectJ's annotations and compiler to weave aspects into the target object's bytecode during compilation. Unlike proxy-based AOP, pure Java compile-time embedded AOP does not require dynamic proxies at runtime and therefore has higher performance.

Spring AOP supports the following common aspect functions:

  • Before advice: Actions performed before the target method is called.

  • After advice: Actions performed after the target method is called, regardless of whether an exception occurs.

  • After returning advice: Actions performed after the target method successfully returns a result.

  • Exception advice (After throwing advice): Actions performed after the target method throws an exception.

  • Around advice: surrounds the target method call and can perform custom operations before and after the call.

Spring AOP is a mechanism for implementing aspect-oriented programming, which can help developers better manage cross-cutting concerns, and provides flexible methods to handle common functions such as logging, transactions, and security, and improve code reusability and availability. Maintainability.

Basic use of spring AOP

Please refer to the article I originally wrote: spring's AOP

spring AOP source code learning

73725ee6b5bf7a7f3aa9e1c9d5cf9ef8.png

AOP initialization

d2951792ca3bf582edfe146fd4364dde.png

When spring is initialized to org.springframework.context.support.AbstractApplicationContext#refresh(), there is a notification processor initialization, which will call the following method for initialization (other ignore see the previous article)

6ae398dcd4be66d4aa0d0d6daee7b17c.png

When @EnableAspectJAutoProxy(proxyTargetClass = true) is annotated on a custom configuration class or startup class, it indicates that the project is enabled for AOP.

6bb2bd46d69ec1b1a2fd944ac1777b91.png

Configure startup class annotations:

@EnableAspectJAutoProxy

Enter @EnableAspectJAutoProxy and find that AspectJAutoProxyRegistrar.class is imported through @import.

14216ca2e0f96590ddb69d66f7bde529.png

Code location: org.springframework.context.annotation.AspectJAutoProxyRegistrar#registerBeanDefinitions

Class Diagram: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

That is to say, when spring starts, it will use the @EnableAspectJAutoProxy annotation to identify whether to start aop. If it is added by default, this aop will be started. The class AspectJAutoProxyRegistrar is imported through @import in this annotation for initialization, and there are A registerBeanDefinitions method is performed.

Then it will enter: org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

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

Note: In fact, the top layer here is AnnotationAwareAspectJAutoProxyCreator, but the bottom layer is AbstractAutowireCapableBeanFactory. Look at the class diagram below:

9e9352e4a7776620d7321e97ebdd9e89.png

Class location: org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator#initBeanFactory

Here is the factory class that initializes the bean. In fact, it is still in refresh here.

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

You have to pay attention here, and the next step is to adjust the IOC (you can read the original article if you don’t understand it).

Code location: 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

Then enter shouldSkip, this method is mainly used to determine whether to skip a specific bean

代码位置: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;
    }

Through findCandidateAdvisors above, find the findAdvisorBeans method used to build the vdisors of the aspect.

Code location: 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;
        }
    }

Then enter the isAspect method.

Code location: 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;
    }

代码位置: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;
    }

The following is how to obtain the aspect class. Of course, this is in order.

代码位置: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;
    }

The order is determined at the time of initialization, from head to head, that is, the order we execute: 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;
    }

Through the previous logic, we finally reach this InstantiationModelAwarePointcutAdvisorImpl to create a specific implementation of 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);
        }

    }

The above is ultimately the creation of the bean.

564b845659ead900113a275d01bc6ecc.png

Initialization alone is just such a big code (part of it is omitted~), it’s really~

dynamic proxy

e29c6ceb366f425e768d619082cbb7e3.png

Spring's default proxy method is JDK dynamic proxy, but if the target object does not implement the interface, it will automatically switch to CGLIB dynamic proxy. This ensures that the AOP function can be used in most cases without the need to manually configure the proxy method.

3492c461555ae83ee5b39a4344e1d4f1.png

After the previous initialization, when the method is called, spring will have an interception chain by default to connect the proxy and the aspect, and match the proxy through invoke reflection.

Code location: 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);
            }
        }
    }

The above method has always been executed recursively.ec39a62d91120810516859d1dc800c98.png

Then enter: invoke method

Code location: 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

You can see that this is the interceptor that is finally executed, and the chain of responsibility design pattern is used to make recursive calls. The order is: Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class} which is consistent with the initialization order.

As for students who don’t understand the chain of responsibility design model, please read: Design Pattern-Responsibility Chain Pattern

at last

    Spring aop is relatively difficult to debug, especially for various agents. If you don’t understand this article, please read the following reference articles for more reference. Of course, if you can understand it, it is recommended to look at the logs in some open source frameworks or some middleware or related components integrated by spring, springboot, and springcloud. It is worth seeing that these implementation logics and principles are actually the same. Of course, what I see more in my official work is the implementation of tools such as global logs and custom interceptors such as encryption.

reference:

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

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

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

Guess you like

Origin blog.csdn.net/qq_16498553/article/details/132267836