Implementation principle of spring AOP

Time:2022-5-4

Through the previous articleSpring bean creation process and related extension pointsWe know the process of creating bean instances with getbean(), which has the following extension points:

  • Implement the instantiaawarebeanpostprocessor interface before the bean instance is created
  • After the bean instance is created successfully, a factory method is created immediately when the property is not set. Bean can be enhanced by implementing the smartinstantiationawarebeanpostprocessor interface
  • In the life cycle of bean initialization, implement the beanpostprocessor interface to enhance the bean

How springboot implements AOP:

1. Start with / meta-inf / spring. Under the spring boot autoconfigure package Starting from factories, you can find the class aopautoconfiguration, which indicates that aopautoconfiguration will be loaded automatically after introducing the jar package spring boot autoconfigure.

@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(Advice.class)
    static class AspectJAutoProxyingConfiguration {

        @Configuration(proxyBeanMethods = false)
        @EnableAspectJAutoProxy(proxyTargetClass = false)
        @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",
                matchIfMissing = false)
        static class JdkDynamicAutoProxyConfiguration {

        }

        @Configuration(proxyBeanMethods = false)
        @EnableAspectJAutoProxy(proxyTargetClass = true)
        @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
                matchIfMissing = true)
        static class CglibAutoProxyConfiguration {

        }
    }

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnMissingClass("org.aspectj.weaver.Advice")
    @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
            matchIfMissing = true)
    static class ClassProxyingConfiguration {

        ClassProxyingConfiguration(BeanFactory beanFactory) {
            if (beanFactory instanceof BeanDefinitionRegistry) {
                BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
                AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
            }
        }
    }
}

I mainly look at the annotation enableaspectjautoproxy. In any case, this annotation will take effect

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

    /**
     * Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
     * to standard Java interface-based proxies. The default is {@code false}.
     */
    boolean proxyTargetClass() default false;

    /**
     * Indicate that the proxy should be exposed by the AOP framework as a {@code ThreadLocal}
     * for retrieval via the {@link org.springframework.aop.framework.AopContext} class.
     * Off by default, i.e. no guarantees that {@code AopContext} access will work.
     * @since 4.3.1
     */
    boolean exposeProxy() default false;

}

Take a closer look

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) {
    //This is the point - this is the point - this is the point------
    //Here is the entry to inject the annotationawareaspectjautoproxycreator object
        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);
            }
        }
    }
}

UML diagram of annotationawareaspectjautoproxycreator class

Implementation principle of spring AOP

image-20210701142934998.png

As you can see, annotationawareaspectjautoproxycreator class implements the interfaces of instantiationawarebeanpostprocessor, samrtistantiawarebeanpostprocessor and beanpostprocessor, which shows that this class can intercept and enhance the bean creation process during the bean instance creation process.

Similarly, the aspectjawareadvisorautoproxycreator class actually intercepts and enhances beans based on XML configuration.

1. Before bean instantiation:

@Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
        Object cacheKey = getCacheKey(beanClass, beanName);

        if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
            if (this.advisedBeans.containsKey(cacheKey)) {
                return null;
            }
            if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
                this.advisedBeans.put(cacheKey, Boolean.FALSE);
                return null;
            }
        }
    //That's the point -- that's the point -- that's the point
    //From here, we can see that this is another way to create bean instances
    //Create a bean instance through a custom Targetsource. It belongs to unconventional usage.
        // Create proxy here if we have a custom TargetSource.
        // Suppresses unnecessary default instantiation of the target bean:
        // The TargetSource will handle target instances in a custom fashion.
        TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
        if (targetSource != null) {
            if (StringUtils.hasLength(beanName)) {
                this.targetSourcedBeans.add(beanName);
            }
            Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
      //After collecting all the advisors, create an agent through methodinterceptor. The specific implementation can be JDK or cglib
            Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }

        return null;
    }

On this basis, we can customize a beanpostprocessor to intercept annotationawareaspectjautoproxycreator. In the initialization life cycle of annotationawareaspectjautoproxycreator, we can create a Targetsource object and set it in, so as to make the getcustomtargetsource () method take effect.

2. The other is to put the bean object into the third level cache after the bean is instantiated

//587 lines in abstractautowirecapablebeanfactory class
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
        Object exposedObject = bean;
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                    SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
          //****************************************************************************************
                    exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
                }
            }
        }
        return exposedObject;
    }

//There are 237 lines in the abstractautoproxycreator class. Abstractautoproxycreator is a subclass of smartinstantiationawarebeanpostprocessor and an entry to implement AOP
@Override
    public Object getEarlyBeanReference(Object bean, String beanName) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        this.earlyProxyReferences.put(cacheKey, bean);
    //****************************************************************************************
        return wrapIfNecessary(bean, beanName, cacheKey);
    }

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }

        //Obtain all advisor instances and filter out the matching advisor instances
    //The way to obtain the advisor is also through getBean (), which is the advisor according to the specified target Class lookup
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
      //Create a proxy object based on the filtered advisor instance
      //How to create a proxy is actually implemented by using JDK dynamic proxy or cglib dynamic proxy
            Object proxy = createProxy(
                    bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            this.proxyTypes.put(cacheKey, proxy.getClass());
      //Returns the enhanced bean object
            return proxy;
        }

        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

3. After bean initialization

//295 lines in abstractautoproxycreator class
@Override
    public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            if (this.earlyProxyReferences.remove(cacheKey) != bean) {
        //********* this is the key point *******************************************************************
        //Same as wrapifnecessary() above
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }

According to abstractautoproxycreator, AOP has three entry points:

1. Postprocessbeforeinstance(): before the bean is created, this point is left to the developer to extend. For the custom Targetsource, check whether there is a matching advisor. If so, create the corresponding proxy object.

2. Postprocessafterinitialization(): this means that the bean has been initialized and the bean is ready for publishing. Then check whether there is a matching advisor for the bean object. If so, create the corresponding proxy object.

3. Getearlybeanreference(): This is the bean that has just been created and has not set the attribute value, so it cannot be published. However, in order to solve the problem of circular dependency, you need to publish the reference of this bean first. Then for this bean, check whether there is a matching advisor. If so, create a proxy object.

In these three extension points, the following things will be done:

1. The findcandidateadvisors () method obtains all advisor instances and stores them in an array; The principle is to find advisor by getBean () Class all instances

2. The findadvisors thatcanapply() method filters out the advisors intercepting this bean from the array, including intercepting class and method; It is mainly divided into introduction advisor and pointcutadvisor;

  • 2.1: introduction advisor can only intercept classes, while pointcutadvisor can intercept methods and classes.

3. Sort the filtered advisor

4. According to the filtered advisor objects, through proxyfactory Getproxy() creates a proxy.

In addition, two classes for creating proxy objects by manual programming are introduced:

  • AspectJProxyFactory
    • This class contains two functions: the function of creating an agent; Handle the functions of advisor and advice
  • ProxyFactory
    • This class can only create agents. Advisor and advice need to be created manually

The difference between the above two classes is that there is a reflective aspectjadvisorfactory object attribute in aspectjproxyfactory, which is specially used to process and generate advisor and advice.

The common point is that both proxy objects can be created, mainly because these two classes inherit from proxycreator support.

Recommended Today

Modify user information changeinfo

When judging the persistence layer: Problem: there is such a problem when modifying user information. For example: the user’s email is not required. It was not empty originally. At this time, the user deletes the mailbox information and submits it. At this time, if it is not empty to judge whether it needs to be […]