Spring AOP source code analysis

We have previously analyzed the general flow of IOC. In this issue, we will take you to analyze the implementation principle of AOP, another major feature in Spring. It is recommended to understand the principle of IOC, the use of AOP and the basic concepts before reading this article, because the implementation of AOP is based on IOC.

When learning AOP, everyone has heard that the principle of AOP is dynamic agent more or less, but have you ever thought about the premise of dynamic agent? Whether it is a JDK or CGlib proxy, you have to pass in the proxy object to the proxy class, which means that the target object has been created at this time. Taking this into the IOC process, we can guess that the implementation of AOP must be after the bean is instantiated. So what are the processes that can perform some operations on beans in the life cycle of beans? That is, the subclass of BeanPostProcessor can be completed. With this in mind, we can go to the subclass of BeanPostProcessor to see which one is related to AOP. Here we'd better show what it is and find the specific implementation class.

To enable the AOP function with annotations, you need to add the @ EnableAspectJAutoProxy annotation. Follow up on the annotation

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
//Automatic proxy registrar, follow in
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

	boolean proxyTargetClass() default false;

	boolean exposeProxy() default false;

}

#AspectJAutoProxyRegistrar
@Override
public void registerBeanDefinitions(
		AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    //Keep following
	AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
	//......
}

public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
    	//You can see that a bean of type AnnotationAwareAspectJAutoProxyCreator is registered
		return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}

Let's take a look at the inheritance system of annotation awareaspectjauto proxycreator

It shows that it is the BeanPostProcessor we are looking for, and the method we want to analyze is in its base class AbstractAutoProxyCreator. Let's first look at the life cycle of beans in IOC.

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
		//......

		Object wrappedBean = bean;
		if (mbd == null || !mbd.isSynthetic()) {
            //postProcessBeforeInitialization callback for BeanPostProcessor
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}

		try {
            //Init method callback
			invokeInitMethods(beanName, wrappedBean, mbd);
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					(mbd != null ? mbd.getResourceDescription() : null),
					beanName, "Invocation of init method failed", ex);
		}
		if (mbd == null || !mbd.isSynthetic()) {
            //postProcessAfterInitialization callback for BeanPostProcessor
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}
		//Return wrapped Bean instance
		return wrappedBean;
	}

Friends familiar with IOC should know this method, and handle the callback method and init method of BeanPostProcessor here. For each singleton bean initialization, some processor callbacks are required, and the implementation of AOP starts from this.

#AbstractAutoProxyCreator

//Bean: bean instance after initialization
//beanName: the name of the bean
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
	if (bean != null) {
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
        //Prevent duplicate proxy of a bean
		if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            //Start of AOP
			return wrapIfNecessary(bean, beanName, cacheKey);
		}
	}
	return bean;
}

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    //The TargetSource scenario can be understood by yourself if you are interested. It will operate in the postprocessbeforeinstance of the current class
	if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
		return bean;
	}
	if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
		return bean;
	}
    
    //Isinstructureclass() determines whether it is the type of the basic framework
    //shouldSkip() determines whether beanName ends with. ORIGINAL
    //Neither of these can be enhanced
	if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}
    
    //Find the notification for the Class of the current bean
	Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    //DO_NOT_PROXY==null
	if (specificInterceptors != DO_NOT_PROXY) {
        //Indicates that enhancements are needed
		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 object
		return proxy;
	}
    //This indicates that the bean does not need to be enhanced and directly returns the original instance
	this.advisedBeans.put(cacheKey, Boolean.FALSE);
	return bean;
}

It can be seen from this that after the postProcessAfterInitialization() method of AbstractAutoProxyCreator, the bean to be proxied will eventually return the proxy object and put it into the container. Next, we should analyze how Spring parses the notification method we configured and how to create a proxy object.

There are two methods: one is to get the notification getAdvicesAndAdvisorsForBean(), and the other is to create the proxy object createProxy(). Let's look at them respectively.

getAdvicesAndAdvisorsForBean() looks for enhanced notifications suitable for the current bean

#AbstractAdvisorAutoProxyCreator
protected Object[] getAdvicesAndAdvisorsForBean(
		Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
    //Gets the notification appropriate for the current bean type
	List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
	if (advisors.isEmpty()) {
        //null returned if null
		return DO_NOT_PROXY;
	}
    //Convert to data return
	return advisors.toArray();
}

#AbstractAdvisorAutoProxyCreator
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    //Get all available advisors in the current container. This method is overridden in AnnotationAwareAspectJAutoProxyCreator
	List<Advisor> candidateAdvisors = findCandidateAdvisors();
    //Filter the Advisor obtained above and select the Advisor suitable for the current bean type
	List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    //An Advisor of type ExposeInvocationInterceptor will be added at the position where index==0
	extendAdvisors(eligibleAdvisors);
	if (!eligibleAdvisors.isEmpty()) {
        //Sort all notifications
		eligibleAdvisors = sortAdvisors(eligibleAdvisors);
	}
    //Returns the notification collection that matches successfully
	return eligibleAdvisors;
}

This method has analyzed the specific functions of internal methods. Because the call chain is complex, friends in need can choose to see it. Friends without needs can skip this part. Understand the functions of these methods and directly see the process of creating proxy objects.

Findcandidedadvisors() finds all notifications

#AnnotationAwareAspectJAutoProxyCreator
@Override
protected List<Advisor> findCandidateAdvisors() {
    //The following methods will parse Advisor in two ways
    
    //1. Get the bean instance of Advisor configured through bean
    //Here, the #beanfactory advisor retrievalhelper's findadvisor beans () is finally called
	List<Advisor> advisors = super.findCandidateAdvisors();
    
    //2. Extract the Advisor information defined in the @ Aspect annotation class
	advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
	return advisors;
}


//1. Get the bean instance of Advisor configured through bean
#BeanFactoryAdvisorRetrievalHelper
public List<Advisor> findAdvisorBeans() {
	String[] advisorNames = this.cachedAdvisorBeanNames;
	if (advisorNames == null) {
        //Query all beannames of subclasses of Advisor configured through beans
		advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
				this.beanFactory, Advisor.class, true, false);
		this.cachedAdvisorBeanNames = advisorNames;
	}
	if (advisorNames.length == 0) {
		return new ArrayList<>();
	}
	List<Advisor> advisors = new ArrayList<>();
    //Traversal beanName
	for (String name : advisorNames) {
		if (isEligibleBean(name)) {
			if (this.beanFactory.isCurrentlyInCreation(name)) {
				if (logger.isTraceEnabled()) {
					logger.trace("Skipping currently created advisor '" + name + "'");
				}
			}
			else {
				try {
                    //Take out the instance of Advisor from the container
					advisors.add(this.beanFactory.getBean(name, Advisor.class));
				}
				catch (BeanCreationException ex) {
					//......
				}
			}
		}
	}
    //Return to all Advisor s
	return advisors;
}

//2. Extract the Advisor information defined in the @ Aspect annotation class
#BeanFactoryAspectJAdvisorsBuilder
public List<Advisor> buildAspectJAdvisors() {
    //Get cache
	List<String> aspectNames = this.aspectBeanNames;
	if (aspectNames == null) {
		synchronized (this) {
			aspectNames = this.aspectBeanNames;
			if (aspectNames == null) {
                //Save Advisor defined by @ Aspect annotation
				List<Advisor> advisors = new LinkedList<Advisor>();
                //Save the beanName of the class annotated with @ Aspect
				aspectNames = new LinkedList<String>();
                //Get all beannames in the container
				String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
						this.beanFactory, Object.class, true, false);
                //Traverse all beannames
				for (String beanName : beanNames) {
					if (!isEligibleBean(beanName)) {
						continue;
					}
					Class<?> beanType = this.beanFactory.getType(beanName);
					if (beanType == null) {
						continue;
					}
                    //Query whether the current type, parent class, parent class of parent class... Have @ Aspect annotation. If yes, it indicates that the current type is Aspect
					if (this.advisorFactory.isAspect(beanType)) {
						aspectNames.add(beanName);
						AspectMetadata amd = new AspectMetadata(beanType, beanName);
						if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                            //Use factory pattern to manage Aspect metadata and associate instance objects of @ Aspect annotation
							MetadataAwareAspectInstanceFactory factory =
									new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
                            //Get the Advisor information in the @ Aspect class and follow it
							List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
							if (this.beanFactory.isSingleton(beanName)) {
								this.advisorsCache.put(beanName, classAdvisors);
							}
							else {
								this.aspectFactoryCache.put(beanName, factory);
							}
                            //Add all advisors in the current @ Aspect annotation class
							advisors.addAll(classAdvisors);
						}
						else {
							if (this.beanFactory.isSingleton(beanName)) {
								throw new IllegalArgumentException("Bean with name '" + beanName +
										"' is a singleton, but aspect instantiation model is not singleton");
							}
							MetadataAwareAspectInstanceFactory factory =
									new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
							this.aspectFactoryCache.put(beanName, factory);
							advisors.addAll(this.advisorFactory.getAdvisors(factory));
						}
					}
				}
                //Cache the beanName of the @ Aspect class just after cyclic processing, indicating that the class has extracted the Advisor
				this.aspectBeanNames = aspectNames;
				return advisors;
			}
		}
	}
	if (aspectNames.isEmpty()) {
		return Collections.emptyList();
	}
    //Get from cache
	List<Advisor> advisors = new LinkedList<Advisor>();
	for (String aspectName : aspectNames) {
		List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
		if (cachedAdvisors != null) {
			advisors.addAll(cachedAdvisors);
		}
		else {
			MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
			advisors.addAll(this.advisorFactory.getAdvisors(factory));
		}
	}
	return advisors;
}

#ReflectiveAspectJAdvisorFactory
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
    //Get the Class of @ Aspect
	Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
    //Get the beanName of @ Aspect
	String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
	validate(aspectClass);
	MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
			new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
	List<Advisor> advisors = new LinkedList<Advisor>();
    //getAdvisorMethods() will get @ Before @After @Around @AfterReturning @AfterThrowing these methods
    //Exclude @ PointCut
	for (Method method : getAdvisorMethods(aspectClass)) {
        //Wrap the current method as Advisor and follow it
		Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
		if (advisor != null) {
			advisors.add(advisor);
		}
	}
	//......
    //Returns all the advisors in the current @ Aspect annotation class
	return advisors;
}


#ReflectiveAspectJAdvisorFactory
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,int declarationOrderInAspect, String aspectName) {
	validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
    //Obtain the pointcut information. candidateAdviceMethod is the enhanced method in the @ Aspect class. Parse the pointcut from the annotation of the method
	AspectJExpressionPointcut expressionPointcut = getPointcut(
			candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
    //Description the corresponding Advisor cannot be created
	if (expressionPointcut == null) {
		return null;
	}
    
    //Wrap the method as an instance of type InstantiationModelAwarePointcutAdvisorImpl
    //expressionPointcut: tangent expression
    //Candidate advice method: enhancement method
    //This: build the factory of Advisor. Remember this
    //Follow in
	return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
			this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}

#InstantiationModelAwarePointcutAdvisorImpl
public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut,
		Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory,
		MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
    //A series of assignment operations
    //......
    //Assign an instance of reflective aspectJAdvisorFactory to aspectJAdvisorFactory
	this.aspectJAdvisorFactory = aspectJAdvisorFactory;
	if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
		//......
	}
	else {
        //Aspect handling single instances
		this.pointcut = this.declaredPointcut;
		this.lazy = false;
        //Enter this method
		this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
	}
}

#InstantiationModelAwarePointcutAdvisorImpl
private Advice instantiateAdvice(AspectJExpressionPointcut pcut) {
    //Call getAdvice through aspectJAdvisorFactory, which is reflective aspectJAdvisorFactory
    //Keep going
	return this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pcut,
			this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
}

#ReflectiveAspectJAdvisorFactory
//In this method, you can finally see that the enhancement method in @ Aspect is packaged as Advisor
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
		MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
    //Gets the Class of the @ Aspect to which the current enhancement method belongs
	Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
	validate(candidateAspectClass);
    //Gets the annotation information defined on the current enhancement method
	AspectJAnnotation<?> aspectJAnnotation =
			AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
	//......
	AbstractAspectJAdvice springAdvice;
	switch (aspectJAnnotation.getAnnotationType()) {
            //Before advice 
		case AtBefore:
			springAdvice = new AspectJMethodBeforeAdvice(
					candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
			break;
            //Post notification
		case AtAfter:
			springAdvice = new AspectJAfterAdvice(
					candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
			break;
            //Return notification
		case AtAfterReturning:
			springAdvice = new AspectJAfterReturningAdvice(
					candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
			AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
			if (StringUtils.hasText(afterReturningAnnotation.returning())) {
				springAdvice.setReturningName(afterReturningAnnotation.returning());
			}
			break;
            //Exception notification
		case AtAfterThrowing:
			springAdvice = new AspectJAfterThrowingAdvice(
					candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
			AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
			if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
				springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
			}
			break;
            //Around Advice 
		case AtAround:
			springAdvice = new AspectJAroundAdvice(
					candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
			break;
            //Pointcut failed to generate Advisor
		case AtPointcut:
			if (logger.isDebugEnabled()) {
				logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
			}
			return null;
		default:
			throw new UnsupportedOperationException(
					"Unsupported advice type on method: " + candidateAdviceMethod);
	}
	springAdvice.setAspectName(aspectName);
	springAdvice.setDeclarationOrder(declarationOrder);
	String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
	if (argNames != null) {
		springAdvice.setArgumentNamesFromStringArray(argNames);
	}
	springAdvice.calculateArgumentBindings();
    //Returns the wrapped Advisor of the current method
	return springAdvice;
}

Here, the whole call chain returns (#abstractadvisor autoproxycreator's findEligibleAdvisors() method). At present, all the advisors in the container have been created. The next step is to filter out the advisors suitable for the current bean.

findAdvisorsThatCanApply() filters out notifications suitable for the current bean

#AbstractAdvisorAutoProxyCreator
protected List<Advisor> findAdvisorsThatCanApply(
		List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
	ProxyCreationContext.setCurrentProxiedBeanName(beanName);
	try {
        //Keep following
		return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
	}
	finally {
		ProxyCreationContext.setCurrentProxiedBeanName(null);
	}
}

#AopUtils
//Candidate advisors: contains all the advisors
//clazz: Class of the current bean
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz){
	if (candidateAdvisors.isEmpty()) {
        //Return without notification
		return candidateAdvisors;
	}
	List<Advisor> eligibleAdvisors = new ArrayList<>();
    //Not considered
	for (Advisor candidate : candidateAdvisors) {
		if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
			eligibleAdvisors.add(candidate);
		}
	}
	boolean hasIntroductions = !eligibleAdvisors.isEmpty();
	for (Advisor candidate : candidateAdvisors) {
        //Not considered
		if (candidate instanceof IntroductionAdvisor) {
			continue;
		}
        //true, indicating that the current advisor matches clazz
		if (canApply(candidate, clazz, hasIntroductions)) {
            //Add advisor
			eligibleAdvisors.add(candidate);
		}
	}
	return eligibleAdvisors;
}

public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
    //Not considered
	if (advisor instanceof IntroductionAdvisor) {
		return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
	}
    //PointcutAdvisor is a sub interface of Advisor
	else if (advisor instanceof PointcutAdvisor) {
		PointcutAdvisor pca = (PointcutAdvisor) advisor;
        //Keep following
		return canApply(pca.getPointcut(), targetClass, hasIntroductions);
	}
	else {
		return true;
	}
}

public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
	Assert.notNull(pc, "Pointcut must not be null");
    //Judge whether the pointcut matches the current Class
	if (!pc.getClassFilter().matches(targetClass)) {
		return false;
	}
    //Get method matcher
	MethodMatcher methodMatcher = pc.getMethodMatcher();
    //A matcher of type TRUEMethodMatcher matches all methods
	if (methodMatcher == MethodMatcher.TRUE) {
		return true;
	}
	//......
    
    //Save the Class of the current object, the interface implemented by itself, the parent Class, the parent Class of the parent Class, and the interface implemented
	Set<Class<?>> classes = new LinkedHashSet<>();
    
    //Ensure that targetClass has not been proxied and is a native Class
	if (!Proxy.isProxyClass(targetClass)) {
		classes.add(ClassUtils.getUserClass(targetClass));
	}
    //Add interface
	classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
    
    //Traverse all classes and check whether all methods will be matched by the method matcher
    //As long as one method matches successfully, it indicates that the current bean needs to be enhanced and returns true
	for (Class<?> clazz : classes) {
		Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
		for (Method method : methods) {
			if (introductionAwareMethodMatcher != null ?
					introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
					methodMatcher.matches(method, targetClass)) {
				return true;
			}
		}
	}
	return false;
}

At this step, all the advisors suitable for the current bean are also filtered out, and we return to the main branch.

#AbstractAdvisorAutoProxyCreator
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
   //Get all available advisors in the current container
	List<Advisor> candidateAdvisors = findCandidateAdvisors();
    //Filter the Advisor obtained above and select the Advisor suitable for the current bean type
	List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    //An Advisor of type ExposeInvocationInterceptor will be added at the position where index==0
	extendAdvisors(eligibleAdvisors);
	if (!eligibleAdvisors.isEmpty()) {
        //Sort all notifications
		eligibleAdvisors = sortAdvisors(eligibleAdvisors);
	}
    //Returns the notification collection that matches successfully
	return eligibleAdvisors;
}

It took so much effort to filter out and return all the advisors suitable for the current bean in the container. The next step is to create an agent through these advisors.

createProxy() creates a proxy object

Remember where we came from? We called it from #AbstractAutoProxyCreator's postProcessAfterInitialization() method, and finally came to this method.

#AbstractAutoProxyCreator
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
	//......
    //Find the notification for the Class of the current bean
    //All the above processes begin with this method call
	Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    //DO_NOT_PROXY==null
	if (specificInterceptors != DO_NOT_PROXY) {
        //Indicates that enhancements are needed
		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 object
		return proxy;
	}
    //This indicates that the bean does not need to be enhanced and directly returns the original instance
	this.advisedBeans.put(cacheKey, Boolean.FALSE);
	return bean;
}

Next, let's look at how Spring creates proxy objects

#AbstractAutoProxyCreator
protected Object createProxy(
		Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
	if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
		AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
	}
    //Create a factory for proxy objects
	ProxyFactory proxyFactory = new ProxyFactory();
    //Copy some information to proxyFactory and assign some related attributes internally
	proxyFactory.copyFrom(this);
    //ProxyTargetClass is the configuration attribute of AOP. If it is true, it indicates that CGlib is used for proxy. The default is false
	if (!proxyFactory.isProxyTargetClass()) {
		if (shouldProxyTargetClass(beanClass, beanName)) {
			proxyFactory.setProxyTargetClass(true);
		}
		else {
			evaluateProxyInterfaces(beanClass, proxyFactory);
		}
	}
    //Specific interceptors: the Advisor created earlier and suitable for the current bean
    //Buildawisors () internally provides and adds a user-defined public Advisor
	Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
	for (Advisor advisor : advisors) {
        //Add Advisor to proxyFactory
		proxyFactory.addAdvisor(advisor);
	}
    //Add target object to proxyFactory
	proxyFactory.setTargetSource(targetSource);
	customizeProxyFactory(proxyFactory);
	proxyFactory.setFrozen(this.freezeProxy);
	if (advisorsPreFiltered()) {
        //It indicates that these advisors passed to proxyFactory have been matched on a basic basis and classFilter has been matched
		proxyFactory.setPreFiltered(true);
	}
    //proxyFactory creates proxy objects
	return proxyFactory.getProxy(getProxyClassLoader());
}

#ProxyFactory
public Object getProxy(ClassLoader classLoader) {
	return createAopProxy().getProxy(classLoader);
}

#ProxyCreatorSupport
protected final synchronized AopProxy createAopProxy() {
	if (!this.active) {
		activate();
	}
    //getAopProxyFactory() is proxyFactory
    //Look at createAopProxy()
	return getAopProxyFactory().createAopProxy(this);
}

#DefaultAopProxyFactory
//Here you can see how Spring selects JDK or CGlib for dynamic proxy
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    //config.isProxyTargetClass() corresponds to the ProxyTargetClass configuration
    //hasNoUserSuppliedProxyInterfaces(config) determines whether the proxy object has an implementation interface
	if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
		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.");
		}
        //Judge whether the Class of the proxied object is an interface, or whether it is already a proxied type
		if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
            //Only JDK dynamic proxy can be used
			return new JdkDynamicAopProxy(config);
		}
        //Using CGlib dynamic proxy
		return new ObjenesisCglibAopProxy(config);
	}
	else {
        //JDK dynamic agent
		return new JdkDynamicAopProxy(config);
	}
}

From here, we can know that proxyFactory.getProxy() is the getProxy() called through JdkDynamicAopProxy or ObjenesisCglibAopProxy. This article only analyzes the dynamic proxy process of JDK, so let's take a look at the source code of JDK dynamic aopproxy.

Source code analysis of JdkDynamicAopProxy

First look at the interface that this class inherits

final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable 

Is one of the interfaces familiar? InvocationHandler is the key to jdk dynamic proxy, so the invoke method will be rewritten. Therefore, for this class, we will focus on getProxy and invoke.

public Object getProxy(ClassLoader classLoader) {
	if (logger.isDebugEnabled()) {
		logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
	}
    //Gets the array of interfaces that require a proxy
	Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
    //Find all the interfaces that need proxy at present, see if there are equals and hashcode methods, and mark them if there are
	findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
    //Return proxy class object
	return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

Friends who have written jdk dynamic agent demo must be very familiar with it. They don't do too much analysis and look directly at the invoke method

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    //It is used to drive the execution chain of Advisor, which will be analyzed later
	MethodInvocation invocation;
	Object oldProxy = null;
	boolean setProxyContext = false;
    //Gets the target object inside ProxyFactory
	TargetSource targetSource = this.advised.targetSource;
	Class<?> targetClass = null;
	Object target = null;
	try {
        //Note: if equals is not defined in the interface implemented by the proxy class and the current method is equals, use the equals provided by JdkDynamicAopProxy
        //It's the tag in getProxy()
        //The same is true below
		if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
			return equals(args[0]);
		}
		else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
			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)) {
			return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
		}
        
        //Method return value
		Object retVal;
        //Do you need to expose the proxy object to the AOP context
		if (this.advised.exposeProxy) {
            //For special cases, for example, an object has both A and B methods that need to be enhanced, but B is also called inside A
            //If the proxy object is not exposed, the enhanced A calls B of the target object, and B is not enhanced
            //Therefore, after exposing the proxy object, enhancement A can call enhancement B of the proxy object
			oldProxy = AopContext.setCurrentProxy(proxy);
			setProxyContext = true;
		}
       	//Real target object
		target = targetSource.getTarget();
		if (target != null) {
            //Gets the Class of the target object
			targetClass = target.getClass();
		}
        //Find all method interceptors suitable for the current method, where the Advisor will be converted into the corresponding method interceptor
		List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
        //If there is no interceptor
		if (chain.isEmpty()) {
            //This indicates that the current method does not need to be enhanced
			Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            //Equivalent to method.invoke
			retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
		}
		else {
            //Description method needs to be enhanced
            //The reflective method invocation core passes in the proxy object, target object, current method and interceptor chain
			invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
            //procced() turns on the interceptor call link
			retVal = invocation.proceed();
		}
        //Method return value type
		Class<?> returnType = method.getReturnType();
        //If the return value type of method is the target object
		if (retVal != null && retVal == target &&
				returnType != Object.class && returnType.isInstance(proxy) &&
				!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
            //Replace here and return the proxy object
			retVal = proxy;
		}
		else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
			throw new AopInvocationException(
					"Null return value from advice does not match primitive return type for: " + method);
		}
        //Method return value
		return retVal;
	}
	finally {
		if (target != null && !targetSource.isStatic()) {
			targetSource.releaseTarget(target);
		}
		if (setProxyContext) {
			AopContext.setCurrentProxy(oldProxy);
		}
	}
}

Next, analyze the conversion process from Advisor to method interceptor

#AdvisedSupport
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) {
	MethodCacheKey cacheKey = new MethodCacheKey(method);
	List<Object> cached = this.methodCache.get(cacheKey);
	if (cached == null) {
        //Follow in
		cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
				this, method, targetClass);
		this.methodCache.put(cacheKey, cached);
	}
	return cached;
}

#DefaultAdvisorChainFactory
//config: proxyFactory, which internally holds all the advisors of the current bean
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
		Advised config, Method method, Class<?> targetClass) {
    
    //Interceptor list
	List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
    //Class of the target object
	Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
	boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
	AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
    //Traverse all the advisors of the current bean
	for (Advisor advisor : config.getAdvisors()) {
        //PointcutAdvisor contains pointcut information
		if (advisor instanceof PointcutAdvisor) {
			PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
            //isPreFiltered() has been set before. If it is true, it indicates that type matching has been done
			if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
                //Wrap Advisor as MethodInterceptor
				MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
                //Get method matcher
				MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
                //Static matching
				if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
                    //Involving runtime matching
					if (mm.isRuntime()) {
						for (MethodInterceptor interceptor : interceptors) {
							interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
						}
					}
					else {
                        //The match is successful. Add the interceptor to the list
						interceptorList.addAll(Arrays.asList(interceptors));
					}
				}
			}
		}
        //Not considered
		else if (advisor instanceof IntroductionAdvisor) {
			IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
			if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
				Interceptor[] interceptors = registry.getInterceptors(advisor);
				interceptorList.addAll(Arrays.asList(interceptors));
			}
		}
        //Describe all current Advisor matching methods
		else {
			Interceptor[] interceptors = registry.getInterceptors(advisor);
			interceptorList.addAll(Arrays.asList(interceptors));
		}
	}
    //Returns the list of interceptors
	return interceptorList;
}

The above is the process of converting Advisor into MethodInterceptor, and the next is the core interceptor call chain.

//The reflective method invocation core passes in the proxy object, target object, current method and interceptor chain
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
//procced() turns on the interceptor call link
retVal = invocation.proceed();

Let's take a look at #ReflectiveMethodInvocation, which is a class used to promote interceptor execution.

//Joinpoint, the top-level interface of ProxyMethodInvocation, will provide a processed () method. Focus on this method
public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable

protected ReflectiveMethodInvocation(
		Object proxy, Object target, Method method, Object[] arguments,
		Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {
    //Perform some assignment operations
	this.proxy = proxy;
	this.target = target;
	this.targetClass = targetClass;
	this.method = BridgeMethodResolver.findBridgedMethod(method);
	this.arguments = AopProxyUtils.adaptArgumentsIfNecessary(method, arguments);
	this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
}

public Object proceed() throws Throwable {
    //Interceptors anddynamicmethodmatchers: interceptor chain
    //The initial value of currentInterceptorIndex is - 1
    //This condition determines which subscript the current interceptor chain executes to. If the subscript is equal to the interceptor length - 1
    //It indicates that all interceptors have been executed
	if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        //Call target method
		return invokeJoinpoint();
	}
    //Get next interceptor
	Object interceptorOrInterceptionAdvice =
			this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    //Determine whether runtime matching is required
	if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
		InterceptorAndDynamicMethodMatcher dm =
				(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
		if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
			return dm.interceptor.invoke(this);
		}
		else {
            //Runtime matching failed. Skip the current interceptor and execute the next interceptor logic
			return proceed();
		}
	}
	else {
       	//It will be executed normally
        //Execute the logic of the interceptor and pass this in
        //this:ReflectiveMethodInvocation
        //invoke here is the interface method of MethodInterceptor. Let's take @ Before and @ After implementation classes for details
		return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
	}
}

@Before

#MethodBeforeAdviceInterceptor
//mi: ReflectiveMethodInvocation
public Object invoke(MethodInvocation mi) throws Throwable {
    //First execute the @ Before method
	this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
    //Then call the next interceptor method in the interceptor chain
	return mi.proceed();
}

@After

#AspectJAfterAdvice
//mi: ReflectiveMethodInvocation    
public Object invoke(MethodInvocation mi) throws Throwable {
	try {
        //Let the interceptor chain complete all interception execution
		return mi.proceed();
	}
	finally {
        //Execute @ After method again
		invokeAdviceMethod(getJoinPointMatch(), null, null);
	}
}

Other notification types are similar calling processes. Please check the source code and analyze the specific implementation. Seeing this, some friends may not know the call link of the interceptor, so let's customize a simple Spring AOP to help you sort out the interceptor process.

Custom AOP

AOP core interface and implementation class

//The execution flow used to drive the interceptor
public interface MethodInvocation {
    //Drive interceptor chain, execute enhanced logic + called by proxy method
    Object proceed()throws Throwable;
}
//Custom interceptor interface
public interface MethodInterceptor {
    //Method interceptor interface, enhanced logic, all written in it
    Object invoke(MethodInvocation mi) throws Throwable;
}
//MethodInvocation implementation class, corresponding to ReflectiveMethodInvocation
public class MethodInvocationImpl implements MethodInvocation{

    private Object target;
    private Method method;
    private Object[] args;
    private List<MethodInterceptor> interceptorList;
    //Subscript of current interceptor execution
    private int currentInterceptorIndex=-1;

    public MethodInvocationImpl(Object target, Method method,Object[] args,List<MethodInterceptor> interceptorList){
        this.target=target;
        this.method=method;
        this.args=args;
        this.interceptorList=interceptorList;
    }

    @Override
    public Object proceed() throws Throwable {
        //If interceptor chain execution is complete
        if(currentInterceptorIndex==interceptorList.size()-1){
            //Call target method
            return method.invoke(target,args);
        }
        //This indicates that there are still interceptors not executed
        MethodInterceptor interceptor=interceptorList.get(++currentInterceptorIndex);
        return interceptor.invoke(this);
    }
}
//Agent factory
public class ProxyFactory {
    //Target object
    private Object target;
    //Store interceptor
    private List<MethodInterceptor> interceptorList;

    public ProxyFactory(Object target) {
        this.target = target;
        this.interceptorList = new ArrayList<>();
    }

    //Adding Advisor just ensures that the method names are the same, so the transformation from Advisor to MethodInterceptor will not be realized
    public void addAdvisor(MethodInterceptor interceptor){
        this.interceptorList.add(interceptor);
    }

    public Object getProxy(){
        return new JdkDynamicAopProxy(this).getProxy();
    }

    public List<MethodInterceptor> getInterceptorList() {
        return interceptorList;
    }

    public Object getTarget() {
        return target;
    }

    public void setTarget(Object target) {
        this.target = target;
    }

}
//JDK dynamic proxy class
public class JdkDynamicAopProxy implements InvocationHandler {

    private ProxyFactory proxyFactory;

    public JdkDynamicAopProxy(ProxyFactory proxyFactory){
        this.proxyFactory=proxyFactory;
    }

    //Create proxy object
    public Object getProxy(){
        Object target=proxyFactory.getTarget();
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        List<MethodInterceptor> interceptorList = proxyFactory.getInterceptorList();
        Object target = proxyFactory.getTarget();
        MethodInvocation mi=new MethodInvocationImpl(target,method,args,interceptorList);
        return mi.proceed();
    }
}

Two custom interceptors are provided

//before
public class MethodBeforeInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        System.out.println("Method pre enhancement!");
        return mi.proceed();
    }
}
//after
public class MethodAfterInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        try{
            return mi.proceed();
        }finally {
            System.out.println("Method post enhancement!");
        }
    }
}

Related test classes

public interface Animal {
    public void eat();
}
public class Dog implements Animal{
    @Override
    public void eat() {
        System.out.println("Dogs eat bones!");
    }
}
public class Main {
    public static void main(String[] args) {
        //Create target object
        Dog dog=new Dog();
        //Create agent factory
        ProxyFactory proxyFactory=new ProxyFactory(dog);
        //Add Advisor to agent factory
        proxyFactory.addAdvisor(new MethodAfterInterceptor());
        proxyFactory.addAdvisor(new MethodBeforeInterceptor());
        //Get proxy object
        Animal proxy = (Animal)proxyFactory.getProxy();
        //Execution target method
        proxy.eat();
    }
}

Final execution result

Method pre enhancement!
Dogs eat bones!
Method post enhancement!

You can debug by yourself. This demo is equivalent to simplifying and implementing the core processes of AOP. I believe that after you understand this process, you can well understand the interceptor process of Spring AOP.

The above is the whole content of Spring AOP source code analysis. There are too many contents involved. You can only analyze some general processes, and you need to read the source code and study some details.

Keywords: Java Spring

Added by binarynomad on Wed, 08 Dec 2021 12:57:05 +0200