Viewing AOP source code from @ EnableAspectJAutoProxy

Posted by jvrothjr on Mon, 25 Oct 2021 10:53:31 +0200

background

In fact, springboot is enabled by default.
@The annotation "EnableAspectJAutoProxy" is an annotation for manually starting AOP.
If you want to see what Spring has done behind the scenes, you have to start with the source code.

EnableAspectJAutoProxy

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

}

@In the EnableAspectJAutoProxy annotation, there are two parameters: proxyTargetClass and exposeProxy

  1. proxyTargetClass
    The specific implementation of AOP agent. The English comment is very simple. Whether to turn on the CGLIB (subclass based) proxy. If it is true, CGLIB will be used, and if it is false, the proxy based on the standard Java interface will be used. The dynamic proxy of JDK is used by default.
  2. exposeProxy
    Controls how agents are exposed. Whether to expose the current proxy object to ThreadLocal mode. It can solve the problem that the internal call cannot use the aspect. If it is true, you can obtain the proxy object and call the method to enter the aspect through the proxy object.
		//Instance pseudo code
        T proxy=(T) AopContext.currentProxy();

The core here is @ import (aspectjautoproxyregister. Class)

AspectJAutoProxyRegistrar

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) {
		//Load bean
		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);
			}
		}
	}

}

Register the bean instance in the Spring container through the registerBeanDefinitions method.
The focus is AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); This sentence.

@Nullable
    public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
        return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, (Object)null);
    }

    @Nullable
    public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, @Nullable Object source) {
        return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
    }

The main function of the tool class AopConfigUtils is to define the class AnnotationAwareAspectJAutoProxyCreator as BeanDefinition and put it into the spring container. For details on how to implement loading, please refer to the interface ImportBeanDefinitionRegistrar. When spring officially registers beans dynamically, most of the routines actually use the ImportBeanDefinitionRegistrar interface.

All classes that implement this interface will be processed by ConfigurationClassPostProcessor. ConfigurationClassPostProcessor implements the beanfactoryprocessor interface. Therefore, beans dynamically registered in ImportBeanDefinitionRegistrar are initialized prior to those that depend on them, and can also be processed by aop, validator and other mechanisms.

The key class here is the AnnotationAwareAspectJAutoProxyCreator registered in the container. It is also the core class of Spring AOP.

AnnotationAwareAspectJAutoProxyCreator


From the class diagram, you can get a general idea of what the annotationawareaspectjauto proxycreator is used for.

  • A series of Aware interfaces are implemented
    Aware, translated as aware, perceived and aware, so these interfaces literally can perceive the meaning in front of all aware.
    When loading a Bean, obtain the BeanFactory(Bean container) and the Bean ClassLoader
  • The Ordered interface is implemented
    Sorts the facets on the tangent point
  • Inherited PorxyConfig
    ProxyConfig mainly encapsulates the general processing logic of the proxy, such as setting the target class, setting whether to use cglib or java proxy and other basic configurations
  • Implements the BeanPostProcessor interface
    BeanPostProcessor is an extension interface provided to us by the Spring IOC container. It is also the core of AOP implementation agent.
public interface BeanPostProcessor {
    @Nullable
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Nullable
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

The BeanPostProcessor interface has two callback methods. After the implementation class of a BeanPostProcessor is registered in the Spring IOC container, for each bean instance created by the Spring IOC container, the postProcessBeforeInitialization method in BeanPostProcessor will be called before the initialization method (such as afterpropertieset and any declared init method) is called. After the bean instance initialization method is called, The postProcessAfterInitialization method in BeanPostProcessor is called

AbstractAutoProxyCreator

Take another look at the top abstract class, which mainly abstracts the logic of implementing the agent. That is, the postProcessBeforeInitialization method.

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

    //1. There is no need to create an agent
    if (beanName == null || !this.targetSourcedBeans.contains(beanName)) {
        if (this.advisedBeans.containsKey(cacheKey)) {
            return null;
        }
        //2. If it is an infrastructure or a class that should be skipped, it means that this class does not need to create a proxy and cache it
        //The default shouldSkip is false and should not be skipped
        //However, AspectJAwareAdvisorAutoProxyCreator implements this method
        if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return null;
        }
    }

    //2. For customized TargetSource, the proxy will be created immediately and cached
    if (beanName != null) {
        //Custom TargetSource
        TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
        //If it's not TargetSource, it won't come here
        if (targetSource != null) {
            this.targetSourcedBeans.add(beanName);
            //Get slice
            Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
            Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }
    }

    return null;
}

Targetsource is the source of the target instance being proxied. Targetsource is used to obtain the target object required by the current MethodInvocation (method call). In other words, the proxy is not the target, but the Targetsource object.

Usually, a proxy object can only proxy one target, and the target of each method call is also the only target. However, if you let the proxy proxy proxy TargetSource, you can make the target instance of each method call different. (of course, it depends on the implementation of TargetSource). This mechanism can make the method call more flexible.

After we open the EbableAspectJAutoProxy, this logic will be executed every time the bean is assembled. The first part is mainly to verify whether the bean needs to be proxied (special classes and proxied). The core logic is the getAdvicesAndAdvisorsForBean method in the next few lines to obtain all qualified aspects. The specific implementation is in the subclass. Here is the abstract method, After obtaining the slice, the proxy is created:

    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();
        //Copy the properties of the current class
        proxyFactory.copyFrom(this);
        //If false is configured in the annotation
        if (!proxyFactory.isProxyTargetClass()) {
            if (this.shouldProxyTargetClass(beanClass, beanName)) {
                proxyFactory.setProxyTargetClass(true);
            } else {
                this.evaluateProxyInterfaces(beanClass, proxyFactory);
            }
        }

        Advisor[] advisors = this.buildAdvisors(beanName, specificInterceptors);
        //Add intensifier
        proxyFactory.addAdvisors(advisors);
        //Set targetSource to proxy
        proxyFactory.setTargetSource(targetSource);
        //User defined agent
        this.customizeProxyFactory(proxyFactory);
        proxyFactory.setFrozen(this.freezeProxy);
        if (this.advisorsPreFiltered()) {
            proxyFactory.setPreFiltered(true);
        }
		//Create proxy
        return proxyFactory.getProxy(this.getProxyClassLoader());
    }

The object to be proxied is stored in the TargetSource. This code is mainly used to build the ProxyFactory. The configuration information (whether to use java proxy, threadlocal, etc.), target class and section are passed into the ProxyFactory. In the ProxyFactory, the proxy factory DefaultAopProxyFactory will be created through the createAopProxy() method, and the proxy factory will generate a specific proxy to proxy the target class.

    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        //    //Here, the creation method of the agent is preliminarily judged. If it does not meet the conditions, the JDK dynamic agent is directly used. If it meets the conditions, it is further judged whether to use JKD dynamic agent or CGLIB agent
        if (!config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) {
            return new JdkDynamicAopProxy(config);
        } else {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");
            } else {
                // If the proxy interface or the proxy setting class is the current class, JDK dynamic proxy is used; otherwise, CGLIB proxy is used
                return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config));
            }
        }
    }

Let's mainly look at the implementation of JdkDynamicAopProxy.

public Object getProxy(@Nullable ClassLoader classLoader) {
        if (logger.isTraceEnabled()) {
            logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
        }
        // Gets the interface of the proxy class
        Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
        // Handling equals, hashcode methods
        this.findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
        return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
    }

JdkDynamicAopProxy also implements InvocationHandler to execute the target method. Spring official statement:

The calling handler of each dynamic proxy class must implement the InvocationHandler interface, and the instance of each proxy class is associated with the dynamic proxy class calling handler that implements the interface. When we call a method through the dynamic proxy object, the call of this method will be forwarded to the invoke method that implements the InvocationHandler interface class

Take a look at the invoke method of JDK dynamic agent

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    MethodInvocation invocation;
    Object oldProxy = null;
    boolean setProxyContext = false;
    // Target class
    TargetSource targetSource = this.advised.targetSource;
    Object target = null;

    try {
        // If the interface does not define an equals method and the current method is an equals method, it will not be enhanced and will be returned directly
        if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
            return equals(args[0]);
        }
        else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
            // If the interface does not define a hashCode method and the current method is a hashCode method, it will not be enhanced and will be returned directly
            return hashCode();
        }
        else if (method.getDeclaringClass() == DecoratingProxy.class) {
            return AopProxyUtils.ultimateTargetClass(this.advised);
        }
        else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                method.getDeclaringClass().isAssignableFrom(Advised.class)) {
            // If the class in which the method is located is the same as the Advised class or is a parent subclass relationship, it is executed directly
            return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
        }
        // Return value
        Object retVal;

        // This corresponds to the application of the expose proxy attribute, which exposes the proxy
        // The self invocation inside the target method will not implement the enhancement in the aspect, so the proxy needs to be exposed here
        if (this.advised.exposeProxy) {
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }

        target = targetSource.getTarget();
        Class<?> targetClass = (target != null ? target.getClass() : null);

        // Gets the interceptor for the method
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

        //If the interceptor of the method is empty, execute the target method directly to avoid creating a MethodInvocation object
        if (chain.isEmpty()) {
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            // Execute target method: method.invoke(target, args)
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
        }
        else {
            // Encapsulate all interceptors in reflective method invocation to facilitate chain invocation 
            invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
            // Execute interceptor chain
            retVal = invocation.proceed();
        }
        // ...... Omit some codes
        return retVal;
    }
    finally {
      // .            
    }
}

Execute the enhancement method in the executed interceptor method. For example, the pre enhancement is executed before the method, and the post enhancement is executed after the method. The processed method is as follows:

public Object proceed() throws Throwable {
    //When all enhancement methods are executed, the target method is executed
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        // method.invoke(target, args)
        return invokeJoinpoint();
    }
     // Gets the next interceptor to execute
    Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);

    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        // Dynamic matching
        InterceptorAndDynamicMethodMatcher dm = interceptorOrInterceptionAdvice;
        Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
        // If it can match, execute the interceptor's method,
        if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
            return dm.interceptor.invoke(this);
        }
        else {
            // If the dynamic match fails, skip the interceptor and execute the next interceptor
            return proceed();
        }
    }
    else {
        // Ordinary interceptor, direct call
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

In this method, the implantation of enhancement is completed. The main logic is that each method will have an interceptor chain, which is called enhancement in AOP, and then each interceptor chain will be executed circularly. When all interceptors are executed, the target method will be executed. For example, the intensifier AspectJAfterAdvice corresponding to @ After and the intensifier AspectJAroundAdvice corresponding to @ Around.

summary

The above is a series of things spring does behind @ EnableAspectJAutoProxy. In fact, there are still some details that have not been deducted, such as how spring obtains the section, how the creator is loaded, and so on. You can see that spring has really done a lot for us behind the scenes.

In fact, a large section above is simply

@EnableAspectJAutoProxy imports aspectjautoproxyregister.class through @ Import,
Then aspectjautoproxyregister is the implementation class of the importbeandefinitionregister interface,
The core class AnnotationAwareAspectJAutoProxyCreator is imported;

PS: irrelevant essays. Reading the source code is really boring, but it can really let you see a lot of things you couldn't see before. There are many flaws in the first learning record. I look forward to becoming better and better myself.

Topics: Java AOP source code