Spring source code series -- AOP dynamic proxy source code analysis

Posted by mschrank on Sun, 07 Nov 2021 07:17:32 +0100

The principle of AOP as like as two peas and Mybatis integration into Spring are exactly the same principle. Before reading this article, I suggest reading the last one first.

Spring source code series (8) -- how Mybatis is integrated into spring source code analysis

https://blog.csdn.net/zxd1435513775/article/details/121180974

1, Spring startup process reanalysis

Configure the class and open the annotation @ EnableAspectJAutoProxy of AOP

@Configuration
@ComponentScan("com.scorpios")
@EnableAspectJAutoProxy
public class AppConfig {
 
}

Section class

@Aspect
@Component
public class AspectJScorpios {

    @Pointcut("execution(* com.scorpios.service..*.*(..))")
    public void pointCut(){

    }

    @Before("pointCut()")
    public void before(){
        System.out.println(" proxy before ... ");
    }
}

Startup class

public static void main( String[] args )
{
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
    ac.register(AppConfig.class);
    ac.refresh();
    XService xService = (XService) ac.getBean("XService");
    xService.print();
}

1. Handle the Import process

For the processing of Import, you can take a look at chapter 4 of source code analysis, processing @ Import annotation in package scanning.

Spring source code series (IV) -- function analysis of configuration class postprocessor

https://blog.csdn.net/zxd1435513775/article/details/120935494

@The EnableAspectJAutoProxy annotation source code uses the @ Import annotation to Import the aspectjautoproxyregister class into the Spring container, and the aspectjautoproxyregister class implements the ImportBeanDefinitionRegistrar interface.

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

	boolean proxyTargetClass() default false;
    
	boolean exposeProxy() default false;
    
}

For an introduction to the @ Import annotation Import ImportBeanDefinitionRegistrar implementation class, see the Spring source code series (8),

The principle of AOP is the same as that of Mybatis by implementing the ImportBeanDefinitionRegistrar interface. The implementation class of Mybatis is mappercannerregister, and the implementation class of AOP is aspectjautoproxyregister.

// Mybatis

class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar

// AOP

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar

2. Execute Import class

In parser.parse(candidates); In this line of code, the @ Import annotation is imported, the class implementing the ImportBeanDefinitionRegistrar interface is instantiated, and the created real column is placed in the property importBeanDefinitionRegistrars of the ConfigurationClass class. You can see the following broken point diagram.

this.reader.loadBeanDefinitions() method completes the call to the ImportBeanDefinitionRegistrar interface method.

Let's take a look at the methods that implement the ImportBeanDefinitionRegistrar interface in the aspectjautoproxyregister class. The entrance of AOP is right here!!!!

2, AOP source code analysis

1. Aspectjautoproxyregister class

Take a look at the ImportBeanDefinitionRegistrar interface method implemented in the aspectjautoproxyregister class.

The function of this method is to add a BeanDefinition to the Spring container. The beanName is org.springframework.aop.config.internalAutoProxyCreator. beanClass is AnnotationAwareAspectJAutoProxyCreator.class.

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(
        AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        // Core method
        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);
            }
        }
    }
}
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
    return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
}


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

    return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}


private static BeanDefinition registerOrEscalateApcAsRequired(
    Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {

    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
        BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
        if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
            int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
            int requiredPriority = findPriorityForClass(cls);
            if (currentPriority < requiredPriority) {
                apcDefinition.setBeanClassName(cls.getName());
            }
        }
        return null;
    }

    // The bean class here is AnnotationAwareAspectJAutoProxyCreator.class
    RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
    beanDefinition.setSource(source);
    beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
    beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    // Register a BeanDefinition with beanName org.springframework.aop.config.internalAutoProxyCreator in beanDefinitionMap
    registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
    return beanDefinition;
}

2. internalAutoProxyCreator class

Take another look at internalAutoProxyCreator. Its implementation class is org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator

After the registerbanpostprocessors () method in the refresh() method, the BeanPostProcessor has been instantiated and added to the beanPostProcessors property in the beanFactory factory.


3. BeanPostProcessor method execution

Let's take a look at the execution time of the BeanPostProcessor. In the initializeBean() method after the assignment of the populateBean() attribute, we call the Bean post processor method.

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    // Permission check
    if (System.getSecurityManager() != null) {
        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
            invokeAwareMethods(beanName, bean);
            return null;
        }, getAccessControlContext());
    } else {
        // Perform setBeanName/setBeanFactory assignment
        invokeAwareMethods(beanName, bean);
    }

    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        // Call the postProcessBeforeInitialization() method in the BeanPostProcessor interface
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }

    try {
        // Execute the custom init method method
        invokeInitMethods(beanName, wrappedBean, mbd);
    } catch (Throwable ex) {
       // Throw exception code omitted
    }
    if (mbd == null || !mbd.isSynthetic()) {
        // Call the postProcessAfterInitialization() method in the BeanPostProcessor interface
        // The AOP proxy is in this method of the AnnotationAwareAspectJAutoProxyCreator class
        // The parent class AbstractAutoProxyCreator of AnnotationAwareAspectJAutoProxyCreator implements this method
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }

    return wrappedBean;
}

After the postProcessAfterInitialization() method in the AnnotationAwareAspectJAutoProxyCreator class above is executed, the XService instance becomes a Cglib proxy object.

Let's analyze the source code:

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}
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;
    }

    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // Create proxy object
        Object proxy = createProxy(
            bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }

    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
                             @Nullable Object[] specificInterceptors, TargetSource targetSource) {

    if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
        AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
    }

    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.copyFrom(this);

    if (!proxyFactory.isProxyTargetClass()) {
        if (shouldProxyTargetClass(beanClass, beanName)) {
            proxyFactory.setProxyTargetClass(true);
        }
        else {
            evaluateProxyInterfaces(beanClass, proxyFactory);
        }
    }

    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    proxyFactory.addAdvisors(advisors);
    proxyFactory.setTargetSource(targetSource);
    customizeProxyFactory(proxyFactory);

    proxyFactory.setFrozen(this.freezeProxy);
    if (advisorsPreFiltered()) {
        proxyFactory.setPreFiltered(true);
    }

    return proxyFactory.getProxy(getProxyClassLoader());
}


public Object getProxy(@Nullable ClassLoader classLoader) {
    return createAopProxy().getProxy(classLoader);
}

protected final synchronized AopProxy createAopProxy() {
    if (!this.active) {
        activate();
    }
    return getAopProxyFactory().createAopProxy(this);
}

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    // This judgment is very important. It can be configured through the configuration file to determine what dynamic agent to use
    if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
        Class<?> targetClass = config.getTargetClass();
        if (targetClass == null) {
			// Throwing anomaly
        }
        if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
            // Create JDK dynamic proxy
            return new JdkDynamicAopProxy(config);
        }
        // Create Cglib dynamic proxy
        return new ObjenesisCglibAopProxy(config);
    }
    else {
        return new JdkDynamicAopProxy(config);
    }
}

3, Summary

The principle of AOP is the same as that of Mybatis. It uses the @ Import import ImportBeanDefinitionRegistrar extension point in Spring. First add a BeanDefinition to the container, and then call the method at the appropriate time.

The two articles can be compared.

Topics: Spring source code analysis