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);
4
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 }
14
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 }
28
29 return null;
30 }
postProcessBeforeInstantiation
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 }
19
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 }
29
30 this.advisedBeans.put(cacheKey, Boolean.FALSE);
31 return bean;
32 }
wrapIfNecessary
Line 9-18: or judge first, whether it's a basic type, etc. we don't judge here
24-25: pass in the obtained information such as the intensifier and create a proxy object
Enter findEligibleAdvisors, and call findCandidateAdvisors to get all candidate enhancers
Then judge that the selected booster queue is not empty, order it, and finally return to the selected booster queue
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
As can be seen in the figure below, after the final sorting, the enhancer queue with five enhancers will be returned
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