spring -- principle and source code of AOP

Keywords: Java Spring

Previous situation review:

  Above We analyzed the whole process of AnnotationAwareAspectJAutoProxyCreator executing the postprocessbeforeinstance() method, which is the core component from container creation to AOP annotation import

The analysis shows that before all bean s are created, the resolvebeforeinstance method will be called to try to return a proxy object


This notice

As you can see in the figure below, the resolvebeforeinstance method contains

applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);

applyBeanPostProcessorsAfterInitialization(bean, beanName);

Two methods


In this article, we will go through the whole process of resolvebeforeinstance and return the proxy object



Starting point of debugging

Start debugging, or jump all the way to the next breakpoint until abstractautoproxycreator. Postprocessbeforeinstance()


You can see that the current bean is org.springframework.context.event.internalEventListenerProcessor, which has nothing to do with the AOP we want to test.

Because the current method has a breakpoint, we move to the next breakpoint until class aop.MathCalculator

 1 @Override
 2     public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
 3         Object cacheKey = getCacheKey(beanClass, beanName);
 5         if (beanName == null || !this.targetSourcedBeans.contains(beanName)) {
 6             if (this.advisedBeans.containsKey(cacheKey)) {
 7                 return null;
 8             }
 9             if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
10                 this.advisedBeans.put(cacheKey, Boolean.FALSE);
11                 return null;
12             }
13         }
15         // Create proxy here if we have a custom TargetSource.
16         // Suppresses unnecessary default instantiation of the target bean:
17         // The TargetSource will handle target instances in a custom fashion.
18         if (beanName != null) {
19             TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
20             if (targetSource != null) {
21                 this.targetSourcedBeans.add(beanName);
22                 Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
23                 Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
24                 this.proxyTypes.put(cacheKey, proxy.getClass());
25                 return proxy;
26             }
27         }
29         return null;
30     }


step voer step by step from the top, and the following is the explanation of the corresponding line

3. Get the cache of bean s in the container

5. Determine whether the cache information of the current bean exists in the target source bean.

(you can see the operation of adding the target source bean in line 21, and the proxy object is created in line 23. So this step is also equivalent to judging whether the current bean has created a proxy object.)

Because it is the first time to execute the bean MathCalculator, here we enter the judgment

Next, this.advisedBeans.containsKey(cacheKey) determines whether there is a cache in the advisedBeans (here we call the advisedBeans an intensifier)

We are not satisfied with our judgment here, and then we go down.

9. Perform isinstructionclass (beanclass) 𞓜 shouldskip (beanclass, beanname) judgment

Let's go to the two methods in line 9 to check.


I. is the infrastructure class (bean class) the basic type

After entering, we will find two ways

    super.isInfrastructureClass(beanClass) || this.aspectJAdvisorFactory.isAspect(beanClass)



The isinstructionclass method, which enters the parent class first, returns false after a series of judgments, indicating that the current bean is not the basic type.




Then I go to isaspect (bean class), and judge whether the class is tangent by @ Aspect annotation on the class (MathCalculator is obviously not a tangent here)



It can be seen from the return that the judgment of isinstructionclass (beanclass) is false



II. Should skip



Traverse all the intensifiers in the method, and the intensifier obtained from the surface in the red box is the log method.   

And judge whether the intensifier is of the type of AspectJPointcutAdvisor. We judge that it is not satisfied

At the end, go to the shouldSkip method of the parent class, and you can see that it directly returns false




Finally, when we come to the outer layer of judgment, we can see that false is returned


Exit the applybeanpostprocessorsbeforeinstance method

Next, go to the code below, and see the comments that if there is a custom target Source, we will create a proxy object here

step voer, judge that targetSource is null in line 259, so there is no custom target Source here

Let's go on and on until we get back to the resolvebeforeinstance method. The returned bean is null

So we will not enter the applyBeanPostProcessorsAfterInitialization method


So far, our resolvebeforeinstance method has been executed. From the above, we can see that the method does not return the proxy object to us

As shown in the figure below, we will continue to execute the createBean process, and then we will call doCreateBean


Research on postProcessAfterInitialization method

We execute the doCreateBean method and come to the bean method of the configuration class


Then jump to the next breakpoint until the postProcessAfterInitialization method. We are familiar with the following method stack

From finishBeanFactoryInitialization to initializeBean


But now we are going to post process after initialization

It can also be seen from the process of initializeBean method in the figure below

Two methods, applybeanpostprocessors before initialization and invokeinitialization, have been executed


Next, enter the postProcessAfterInitialization method:

If the current bean object does not exist in the previous proxy reference, wrap if necessary (bean, bean name, cachekey) is called and its result is returned



Enter wrap if necessary:

 1 /**
 2      * Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
 3      * @param bean the raw bean instance
 4      * @param beanName the name of the bean
 5      * @param cacheKey the cache key for metadata access
 6      * @return a proxy wrapping the bean, or the raw bean instance as-is
 7      */
 8     protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
 9         if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
10             return bean;
11         }
12         if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
13             return bean;
14         }
15         if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
16             this.advisedBeans.put(cacheKey, Boolean.FALSE);
17             return bean;
18         }
20         // Create proxy if we have advice.
21         Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
22         if (specificInterceptors != DO_NOT_PROXY) {
23             this.advisedBeans.put(cacheKey, Boolean.TRUE);
24             Object proxy = createProxy(
25                     bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
26             this.proxyTypes.put(cacheKey, proxy.getClass());
27             return proxy;
28         }
30         this.advisedBeans.put(cacheKey, Boolean.FALSE);
31         return bean;
32     }


Line 9-18: or judge first, whether it's a basic type, etc. we don't judge here

21: get the intensifier and put it into Object[] specificInterceptors

24-25: pass in the obtained information such as the intensifier and create a proxy object

Let's review the process of getting advicesandadvisorsforbean's enhancer:

Call findEligibleAdvisors first to find the qualified enhancer (log method)



Enter findEligibleAdvisors, and call findCandidateAdvisors to get all candidate enhancers

Then call findadvisors that can apply to select the

Then judge that the selected booster queue is not empty, order it, and finally return to the selected booster queue



Find candidate advisors let's not talk

Focus on findadvisors that can apply

As shown in the figure below, you can see that AOP utils, or AOP tool class, is used for filtering



Enter the tool class method, see that the method makes another layer of judgment, and put the final qualified enhancer (log method) into the selection queue



After getting it, we went all the way back to find eligible advisors

As can be seen in the figure below, after the final sorting, the enhancer queue with five enhancers will be returned




Create proxy object

Finally, we get the required intensifier (log method) and put it into an array called special interceptor (here it is called interceptor array temporarily)

After the judgment is not empty, put the cache of the current bean into the adviseBeans

Then call createProxy to create the proxy object




 1 /**
 2      * Create an AOP proxy for the given bean.
 3      * @param beanClass the class of the bean
 4      * @param beanName the name of the bean
 5      * @param specificInterceptors the set of interceptors that is
 6      * specific to this bean (may be empty, but not null)
 7      * @param targetSource the TargetSource for the proxy,
 8      * already pre-configured to access the bean
 9      * @return the AOP proxy for the bean
10      * @see #buildAdvisors
11      */
12     protected Object createProxy(
13             Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
15         if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
16             AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
17         }
19         ProxyFactory proxyFactory = new ProxyFactory();
20         proxyFactory.copyFrom(this);
22         if (!proxyFactory.isProxyTargetClass()) {
23             if (shouldProxyTargetClass(beanClass, beanName)) {
24                 proxyFactory.setProxyTargetClass(true);
25             }
26             else {
27                 evaluateProxyInterfaces(beanClass, proxyFactory);
28             }
29         }
31         Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
32         proxyFactory.addAdvisors(advisors);
33         proxyFactory.setTargetSource(targetSource);
34         customizeProxyFactory(proxyFactory);
36         proxyFactory.setFrozen(this.freezeProxy);
37         if (advisorsPreFiltered()) {
38             proxyFactory.setPreFiltered(true);
39         }
41         return proxyFactory.getProxy(getProxyClassLoader());
42     }

From the top down
Line 19 creates proxy object factory proxyFactory, 31-34 adds properties such as intensifier and target Source to proxy object factory
Line 41 calls proxyFactory.getProxy(getProxyClassLoader()) to get the proxy object of the current bean



Create Aop proxy object first


You can see that after a series of calls to the result, we come to the following figure. There are three kinds of dynamic proxy objects that may return. Here we return Cglib dynamic proxy objects


Step by step, return the proxy object, execute the applyBeanPostProcessorsAfterInitialization method of the current bean, and return its proxy object




As you can see, the returned proxy object will be stored in the container instead of the bean object

So far, our proxy object creation is complete.



When initializeBean method initializes the bean, it will create and return the proxy object of the target bean through applybeanprocessorsafterinitialization and store it in the container.

So far, the process has been completed before the bean is initialized

The code flow in the following two figures is the key


initializeBean process:



refresh process (what we need to know in AOP):

  1. postProcessBeanFactory(beanFactory);
  2. invokeBeanFactoryPostProcessors(beanFactory);
  3. finishBeanFactoryInitialization(beanFactory);

Refer to the following for the complete process of refresh:

 1 @Override
 2     public void refresh() throws BeansException, IllegalStateException {
 3         synchronized (this.startupShutdownMonitor) {
 4             // Prepare this context for refreshing.
 5             prepareRefresh();
 7             // Tell the subclass to refresh the internal bean factory.
 8             ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
10             // Prepare the bean factory for use in this context.
11             prepareBeanFactory(beanFactory);
13             try {
14                 // Allows post-processing of the bean factory in context subclasses.
15                 postProcessBeanFactory(beanFactory);
17                 // Invoke factory processors registered as beans in the context.
18                 invokeBeanFactoryPostProcessors(beanFactory);
20                 // Register bean processors that intercept bean creation.
21                 registerBeanPostProcessors(beanFactory);
23                 // Initialize message source for this context.
24                 initMessageSource();
26                 // Initialize event multicaster for this context.
27                 initApplicationEventMulticaster();
29                 // Initialize other special beans in specific context subclasses.
30                 onRefresh();
32                 // Check for listener beans and register them.
33                 registerListeners();
35                 // Instantiate all remaining (non-lazy-init) singletons.
36                 finishBeanFactoryInitialization(beanFactory);
38                 // Last step: publish corresponding event.
39                 finishRefresh();
40             }
42             catch (BeansException ex) {
43                 if (logger.isWarnEnabled()) {
44                     logger.warn("Exception encountered during context initialization - " +
45                             "cancelling refresh attempt: " + ex);
46                 }
48                 // Destroy already created singletons to avoid dangling resources.
49                 destroyBeans();
51                 // Reset 'active' flag.
52                 cancelRefresh(ex);
54                 // Propagate exception to caller.
55                 throw ex;
56             }
58             finally {
59                 // Reset common introspection caches in Spring's core, since we
60                 // might not ever need metadata for singleton beans anymore...
61                 resetCommonCaches();
62             }
63         }
64     }


In the next article, the last in this series, we'll explore how the enhancer (log method) works when the proxy object method executes through the proxy object.

Posted by KingWylim on Sat, 07 Mar 2020 03:43:01 -0800