In the Spring Bean creation method, there is the following code:
AbstractAutowireCapableBeanFactory#createBean:
@Override protected Object createBean (String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { //... try { // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance. Object bean = resolveBeforeInstantiation (beanName, mbdToUse); if (bean != null ) { return bean; } } try { Object beanInstance = doCreateBean(beanName, mbdToUse, args); return beanInstance; } //... }
The Bean creation logic we usually talk about refers to the logic in the doCreateBean method. In Song Ge’s previous articles, whenever it comes to the Bean creation process, I am also talking about the process of the doCreateBean method.
But friends, please note that before the doCreateBean method is executed, there is actually a resolveBeforeInstantiation method that will be executed first, and this method may directly generate a Bean! If this method directly generates a Bean, the logic in the doCreateBean method will not take effect.
So what is the significance of the resolveBeforeInstantiation method? In fact, you can probably get some clues from the comments on this method:
Gives the BeanPostProcessor a chance to create a proxy object and use this proxy object in place of the target Bean.
Contents
1. resolveBeforeInstantiation
Friends who look at the source code below must first understand the two similar words, otherwise they will be confused after reading:
- Instantiation: Instantiation, from Class to Bean is instantiation.
- Initialization: Initialization, making various configurations for the Bean is initialization.
To understand these two words, let’s look at the source code.
First, let me sort out the resolveBeforeInstantiation method with my friends.
AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation:
@Nullable protected Object resolveBeforeInstantiation (String beanName, RootBeanDefinition mbd) { Object bean = null ; if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) { // Make sure bean class is actually resolved at this point. if (!mbd .isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { Class<?> targetType = determineTargetType(beanName, mbd); if (targetType != null ) { bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName); if (bean != null ) { bean = applyBeanPostProcessorsAfterInitialization(bean, beanName); } } } mbd.beforeInstantiationResolved = (bean != null ); } return bean; }
Friends, take a look, here is a judgment condition, mbd.isSynthetic is to judge whether it is a synthetic bean, this is generally defined by Spring, our custom beans generally do not fall into this category, and then the hasInstantiationAwareBeanPostProcessors method follows It is to determine whether there is currently a post-processor of type InstantiationAwareBeanPostProcessor. If it exists, enter the if branch.
If we want to complete the Bean processing in the resolveBeforeInstantiation method, then we need to provide a post-processor of type InstantiationAwareBeanPostProcessor ourselves.
Next, two methods will be called:
- applyBeanPostProcessorsBeforeInstantiation: As you can see from the name, this is a method that is triggered before instantiation, so the parameter of this method is still Class because it has not been instantiated yet.
- applyBeanPostProcessorsAfterInitialization: As you can see from the name, this is a method that starts after initialization, so the parameter of this method is Bean, because the initialization has been completed at this time.
1.1 applyBeanPostProcessorsBeforeInstantiation
@Nullable protected Object applyBeanPostProcessorsBeforeInstantiation (Class<?> beanClass, String beanName) { for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) { Object result = bp.postProcessBeforeInstantiation(beanClass, beanName); if (result != null ) { return result ; } } return null ; }
This place is very simple, it is to execute the postProcessBeforeInstantiation method of the post-processor of type InstantiationAwareBeanPostProcessor.
1.2 applyBeanPostProcessorsAfterInitialization
@Override public Object applyBeanPostProcessorsAfterInitialization (Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor processor : getBeanPostProcessors()) { Object current = processor.postProcessAfterInitialization(result, beanName); if (current == null ) { return result; } result = current; } return result; }
This is the postProcessAfterInitialization method that executes BeanPostProcessor.
So the source code of this piece is actually not difficult, the reason is very simple.
1.3 Case
Brother Song wrote a simple case, let’s take a look.
Suppose I have a BookService as follows:
public class BookService { public void hello () { System.out.println( "hello javaboy" ); } }
Then I create a post-processor of type InstantiationAwareBeanPostProcessor and override the postProcessBeforeInstantiation and postProcessAfterInitialization methods mentioned earlier:
public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor { @Override public Object postProcessBeforeInstantiation (Class<?> beanClass, String beanName) throws BeansException { if (beanClass == BookService.class) { Enhancer enhancer = new Enhancer (); enhancer.setSuperclass(beanClass); enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> { String name = method.getName(); System.out.println(name + "The method started executing..." ); Object invoke = proxy.invokeSuper(obj, args); System.out.println(name + "Method execution ended..." ); return invoke; }); BookService bookService = (BookService) enhancer.create(); return bookService; } return InstantiationAwareBeanPostProcessor. super .postProcessBeforeInstantiation(beanClass, beanName); } @Override public Object postProcessAfterInitialization (Object bean, String beanName) throws BeansException { System.out.println( "bean.getClass() ========= " + bean.getClass()); return InstantiationAwareBeanPostProcessor. super .postProcessAfterInitialization(bean, beanName); } }
In the postProcessBeforeInstantiation method, if the bean to be created is BookService, a CGLIB proxy object is created through Enhancer. If it is the creation of other beans, just call the parent class method. After rewriting in this way, the result obtained in section 1.1 will be a proxy BookService object.
In the postProcessAfterInitialization method, I did not do any additional processing, just printed the Bean we got. At this time, the Bean we got is actually the proxy object generated by the previous postProcessBeforeInstantiation method, and then the parent class method is called here to return. In fact, Just return the parameter Bean intact.
Finally, register these two beans into the Spring container:
<?xml version= "1.0" encoding= "UTF-8" ?> < beans xmlns = "http://www.springframework.org/schema/beans" xmlns:xsi = "http://www.w3.org /2001/XMLSchema-instance" xsi:schemaLocation = "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > < bean class = "org.javaboy.bean.aop2.BookService" id = "bookService" /> < bean class = "org.javaboy.bean.aop2.MyInstantiationAwareBeanPostProcessor" id = "myInstantiationAwareBeanPostProcessor" /> </ beans >
Then initialize the Spring container:
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext ( "aop2.xml" ); BookService bs = ctx.getBean(BookService.class); System.out.println( "bs.getClass() = " + bs.getClass()); bs.hello();
The final execution result is as follows:
It can be seen that the BookService finally obtained is a proxy object. From the source code level, this proxy object is generated in the resolveBeforeInstantiation method, and the subsequent doCreateBean method is not actually executed.
This is the role of the resolveBeforeInstantiation method. It actually gives the BeanPostProcessor a chance to create a proxy object and use this proxy object to replace the target Bean.
2. Source code practice
Why does Brother Song pay attention to this method?
If any friends have studied the Spring AOP source code, they will find that this method has a place to use when dealing with Spring AOP.
When we are in Spring AOP, we often define aspects through the following code:
@Component @Aspect @EnableAspectJAutoProxy public class LogAspect { //... }
There is an @Aspect annotation on this class, so the question is, how does Spring identify that this is an aspect rather than an ordinary Bean?
The answer is in the applyBeanPostProcessorsBeforeInstantiation method in Section 1.1. This method will traverse all post-processors of the InstantiationAwareBeanPostProcessor type. InstantiationAwareBeanPostProcessor has a subclass called AnnotationAwareAspectJAutoProxyCreator. In this processor, LogAspect is recognized as an aspect.
The specific identification method is as follows:
First call the postProcessBeforeInstantiation method of AnnotationAwareAspectJAutoProxyCreator (actually the method in the parent class AbstractAutoProxyCreator of AnnotationAwareAspectJAutoProxyCreator):
@Override public Object postProcessBeforeInstantiation (Class<?> beanClass, String beanName) { Object cacheKey = getCacheKey(beanClass, beanName); if (!StringUtils.hasLength(beanName) || ! this .targetSourcedBeans.contains(beanName)) { if ( isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) { this .advisedBeans.put(cacheKey, Boolean.FALSE); return null ; } } //... }
Starting from this place, it is divided into two lines:
- If it is an aspect bean, executing the first method isInfrastructureClass can return true.
- If it is a normal Bean, the first method will return false, and the second method shouldSkip will be executed (although this method will also return false), but this method has some other values in it.
2.1 Section Bean
Let’s first look at the isInfrastructureClass method, and first look at how aspect beans are processed.
I extracted part of this method. We focus on the isInfrastructureClass method. This method is used to determine whether the current class is an Aspect:
@Override protected boolean isInfrastructureClass (Class<?> beanClass) { return ( super .isInfrastructureClass(beanClass) || ( this .aspectJAdvisorFactory != null && this .aspectJAdvisorFactory.isAspect(beanClass))); }
The judgment here is mainly based on two aspects:
- Call the method of the parent class to determine whether the current class is related to AOP:
protected boolean isInfrastructureClass (Class<?> beanClass) { boolean retVal = Advice.class.isAssignableFrom(beanClass) || Pointcut.class.isAssignableFrom(beanClass) || Advisor.class.isAssignableFrom(beanClass) || AopInfrastructureBean.class.isAssignableFrom(beanClass); return retVal; }
I don’t need to explain this, these are all our old acquaintances in AOP.
- Call
aspectJAdvisorFactory.isAspect
the method to determine whether the current class contains the @Aspect annotation:
AbstractAspectJAdvisorFactory#isAspect
@Override public boolean isAspect (Class<?> clazz) { return (hasAspectAnnotation(clazz) && !compiledByAjc(clazz)); } private boolean hasAspectAnnotation (Class<?> clazz) { return (AnnotationUtils.findAnnotation(clazz, Aspect.class) != null ); }
This code is easy to understand, it just checks whether the current class has @Aspect
annotations. The latter compiledByAjc method is to check whether ajc compilation is required. We generally do not need this, so as long as the hasAspectAnnotation method returns true, it will return true as a whole.
If our class contains @Aspect
annotations, the current class name will eventually be added to the advisedBeans Map. In the advisedBeans Map, the key is the name of the current Bean, and the value is false, which is a mark indicating that the current class does not need to be generated. Agent class.
This is the general logic executed by the isInfrastructureClass method.
2.2 Common Beans
If it is an ordinary Bean, it is obvious that the isInfrastructureClass method will return false, which will cause the shouldSkip method to be executed. Although this method is called shouldSkip, it does a lot of practical things.
I will explain this method in detail when I share the AOP creation process with my friends in the next article. Let me start by saying that this method will collect various Aspect Beans and generate Advisor based on these Beans in the future.
Okay, this is the role of the resolveBeforeInstantiation method. Interested friends can DEBUG and see some by themselves~