What quiet time is good, but someone is silently carrying on
1. Complexity of Spring
If you’re a Javaer, you’ve probably used Spring. While we are lamenting the incomparably powerful Spring, however, we are also lamenting the labyrinth-like complexity of Spring hidden behind its powerful appearance, just like the lost forest.
This is not the most desperate, but the most desperate is when you read a book or blog to try to understand why Spring is so powerful
Or something like this
Then, with sleepy eyes, you read (or are about to finish) the Book of “Spring XXX Inner Skills” and pretend that you have practiced the inner skills of Spring…
2. Look down at the Spring
I’m not saying that you don’t need to dig deep into Spring’s source code, but you don’t want to start with these fancy and confusing diagrams.
So what should we do?
A: You have to take it step by step, from big to small, and work your way through it. I call it onion learning, or more crudely, undressed learning
Let’s take a look at the full picture of Spring:
Yes, this is the only picture on the Spring-Framwork website that describes Spring.
Do you feel very clean and comfortable? In fact, this is the posture we should have in learning Spring: no nonsense, there is a picture and the truth.
From this diagram we can see that the average user needs only two things to use our Spring
- Pojo
- Configuration information
This is also the way most of us developers see Spring—- Powerful Spring, versatile Spring. All of Spring’s dark magic is hidden in the Spring container.
3. Analyze the Spring
Let’s take a closer look at Spring’s dark magic by peeling down the layers of his gorgeous coat to see how it works.
All dark magic, in fact, no matter how gorgeous it is presented, its internal implementation steps are actually step by step. As shown in the figure above, the Spring startup process is divided into two phases:
- Container startup phase (in order to get
BeanDefinition
) Bean
Instantiation phase (to get the assembledBean
)
BeanDefinition is very, very important.
BeanDefinition
What is it?In contrast, this is a bit like Class for Object, which describes the metadata of a Bean. For example (whether the Bean is a singleton, whether lazy loading is required, what is the name of the initialization method, which beans are relied on).
- One might ask why Spring doesn’t just use Class objects to produce beans.
One might argue that using Class alone does not fully describe all of Spring’s concepts: for example, you cannot describe Bean dependencies or call Bean initialization methods. In my opinion, this argument is not entirely correct. Since dependencies can be obtained from Class fields, methods, Constructor annotations (@autowired @Resource), and initialization methods (@postconstruct, etc.), So it is possible to describe beans using Class objects alone on both counts. But there are three things Class objects can’t do or will be difficult to do well.
- In the early days of Spring (the XML era), there were no annotations, and Bean dependencies could only be described through the configuration of XML
BeanDefinition
Is an intermediary that Spring allows itself and other developers to modify, whereas Class cannot- In the annotation era, using Class annotations to describe the metadata of all beans would require a lot of reflection operations in Spring to retrieve the metadata, which would be inefficient and cumbersome
- In general, if you ask a Class to describe a Bean,Class will probably just say: “I can’t do it!”
If you think this process is a bit abrupt, or a bit overwhelming, here’s an example:
It’s like when you go to a restaurant, you just sit there and wait for ten minutes and then you can enjoy the delicious food. But if you’ve ever actually cooked a meal at home, you know how long and cumbersome the process can be. But the kitchen has done it all for you. All you have to do is enjoy the food. But the long, complicated process, as chefs see it, consists of only two steps:
- Prepare ingredients (in order to obtain the ingredients of a washed, prepared dish)
- To cook (in order to get a dish ready to eat)
If you still don’t understand this metaphor, don’t worry, just remember that Spring container loading as a whole is divided into two parts:
- 1. Startup stage (to get BeanDefinition)
- 2.Bean instantiation phase (to get the assembled Bean)
Just remember in general, but keep reading for details of what happens in each section
4. Long Johns
Above we saw Spring’s gorgeous coat (stage 2). Now let’s move on, open the skin and take a look at Spring’s long coat, long Johns, and see what’s underneath the two stages.
Same old rule. Picture first
We can see from the picture above
-
The first phase (container startup phase) mainly uses the BeanFactoryPostProcessor to scan the raw material of the production Bean from an XML configuration file or Java code: BeanDefinition
-
The second phase (Bean instantiation phase) mainly uses the BeanDefinition obtained in the first step along with the BeanPostProcessor to produce the Bean
OK, so far, we know that these two characters are actually doing the work
BeanFactoryPostProcessor
(Some Chinese translation: Bean factory post-processor)BeanPostProcessor
(Some Chinese translation: Bean post-processor)
So what kind of gods are these two?
BeanFactoryPostProcessor
(Very, very important)
Spring BeanFactoryPostProcessor is an interface in the Spring, and it also has a very important sub BeanDefinitionRegistryPostProcessor. A brief understanding of its role: BeanFactoryPostProcessor allows the framework or developer to modify the BeanDefinition, While BeanDefinitionRegistryPostProcessor framework or developers to dynamically add more BeanDefinition container. For those who don’t understand, the BeanFactoryPostProcessor is the equivalent of once you’ve washed a dish, you can slice it or slice it or add a little pepper, which means you can change the shape of the ingredients. BeanDefinitionRegistryPostProcessor dish washing effect like in the basket, you can also add their own raw materials, such as add some raw mutton, beef, etc.
####BeanPostProcessor (very, very important) BeanPostProcessor is a cluster of interfaces. There are five of them. I’m not going to list them here. However, you should know that the BeanPostProcessor we draw in the figure above refers not only to the BeanPostProcessor itself, but also to all of its sub-interfaces. But these interfaces are actually used to produce assembled beans. The main function of BeanPostProcessor itself is to customize its logic before and after the Bean’s initialization, so when you open the source code you will see two methods, one called before initialization and one called after initialization. We’re not going to talk about initialization here, but we’ll talk about it later. If you don’t understand, for example, before the fish comes out of the pot can you put some soy sauce to freshen it up, and after the fish comes out of the pot can you put some coriander titian. ### Summary (very, very important) : Both BeanFactoryPostProcessor and BeanPostProcessor in the figure above refer not only to themselves, but to clusters of interfaces. BeanFactoryPostProcessor intervenes in the production process of Bean’s raw material, BeanDefinition, while BeanPostProcessor intervenes in the production process of Bean
5. The underwear of Spring
Above, we have opened Spring’s coat, long Johns and long Johns. Now we are going to be shy. Hey, let’s take a look at Spring’s underwear.
Attention, high energy ahead, the next to see a bit of source code. Of course the point of looking at the source code is not to look at the details, but to prove that our understanding is correct, and then draw some conclusions after looking at the source code. If you’re too lazy to look at the source code, just ignore it and go straight to the conclusion. It doesn’t affect your understanding.
The following Spring source code is based on the 5.1.9-release and only shows the annotation-based Spring loading process
Slightly seen Spring source students all know, the Spring core method is the most AbstractApplicationContext# refresh () method.
This method defines the entire flow that abstracts the Spring container load. However, some methods are empty implementations reserved for subclass Override. If you’ve studied design patterns, this is actually a template pattern. In fact, there are many patterns in Spring source code, which we will mention if we encounter them, but not if we encounter them
@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { prepareRefresh(); ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); prepareBeanFactory(beanFactory); try { postProcessBeanFactory(beanFactory); / / the first stage preparation stage (container) : the first method from above until the method is the first phase / / but the most important process is the main method invokeBeanFactoryPostProcessors (the beanFactory); / / excessive stage: registered BeanPostProcessor (including all of its child interface) registerBeanPostProcessors (the beanFactory); // Ignore the following four methods: initMessageSource(), the main flow for non-IOC containers; initApplicationEventMulticaster(); onRefresh(); registerListeners(); / / the second phase (Bean instantiation phase) : instantiate effect of lazy loading of Bean finishBeanFactoryInitialization (the beanFactory); finishRefresh(); } catch (BeansException ex) {// Ignore, non-main process destroyBeans(); cancelRefresh(ex); throw ex; } finally { resetCommonCaches(); // Ignore, not main process}}}Copy the code
Look not to understand? Never mind, you just need to read the comments roughly, mainly to verify that our previous Spring two-phase process is reflected in the source code. Let’s try to get a better understanding of Spring by graphing it.
### The first stage (container startup stage) analysis the essence of the first stage are in this method, the specific code is not posted here, you can have a look at the source code.
AbstractApplicationContext.invokeBeanFactoryPostProcessors(beanFactory);
Copy the code
Again to a layer, found that this process is delegated to the PostProcessorRegistrationDelegate
PostProcessorRegistrationDelegate#
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
Copy the code
To note here that normally beanFactoryPostProcessors parameters are empty, because this parameter is only in the developer manual call addBeanFactoryPostProcessor this method just can have value. Normally we don’t call this method manually when using Spring.
- There’s actually a problem here: if there are more than one
BeanFactoryPostProcessor
orBeanDefinitionRegistryPostProcessor
So who gets called first and who gets called next?Internally, Spring classifies objects into three main classes:
- To achieve the
PriorityOrdered, highest priority
The Bean of the interface- To achieve the
2. He was Ordered to take the first order.
The Bean of the interface- It didn’t happen
PriorityOrdered
andOrdered
Ordinary beans of the lowest priorityThis is then distinguished by implementing the getOrder() method in beans of the same level, such as those that have all implemented PriorityOrdered
This is the core of phase 1 (container startup). That’s damn easy, you might say. Are you misleading me? In fact, the main flow of Spring is that simple, but what makes Spring so complicated is that In the process of the Spring to join its own BeanDefinitionRegistryPostProcessor and Spring BeanFactoryPostProcessor implementation. If you’ve seen the implementation of Spring, you know that Spring is a deeply hidden evil. ####BeanDefinitionRegistryPostProcessor
BeanDefinitionRegistryPostProcessor in preparation for the container’s main function is to register all that needs to be loaded into the container Bean BeanDefinition metadata.
Here we introduce a Spring annotations drive mode of an epoch-making BeanDefinitionRegistryPostProcessor interface implementation class – ConfigurationClassPostProcessor, This class is the only Spring – framwork implements BeanDefinitionRegistryPostProcessor interface classes
If I had to describe the importance of * * ConfigurationClassPostProcessor, so can only use to describe the nuwa empress. * * is to the right, ConfigurationClassPostProcessor developers Pojo business class, just think of nuwa empress in human.
God said, there are no human beings on earth, so the Goddess Nu Wa came. Spring, said the container have not Bean, hence ConfigurationClassPostProcessor * * * *.
This kind of * * ConfigurationClassPostProcessor is mainly doing? Let’s take a look at ConfigurationClassPostProcessor * * documents:
BeanFactoryPostProcessor used for bootstrapping processing of @Configuration classes.
The point is to deal with all the configuration classes
Its internal implementation is not discussed here today. The main thing to remember is that it’s very, very important, and its role is to deal with configuration classes.
Here, do you have a little confused, I feel the Spring ConfigurationClassPostProcessor need only a * * * * is enough, why make so complicated, even PriorityOrdered and Ordered, all these have a purpose? In fact, there are some design principles that Spring follows:
Open closed principle: Closed for modification, open for extension
The main reason for the complexity of Spring is to make it easy for the Framework itself and developers to extend Spring. As Spring can not support annotations to configuration, can also support XML way, is it possible to realize a BeanDefinitionRegistryPostProcessor Spring to handle XML style? (Of course, by spring 4.x, this feature had been replaced by the @importResource annotation). That in addition to the Spring can be implemented, developers also can implement a BeanDefinitionRegistryPostProcessor to extend Spring?
Don’t you understand? Let me give you an example: the chef washed the fish, cut it, processed it and put it in the basket. But what if you say you want a drumstick? That is simple, as long as you go to the market to buy a chicken leg, and then wash it yourself, and then put it in the basket. At this time, the chef can not only do the fish well, but also do the chicken leg for you.
PS: Have you left the saliva
# # # # spring BeanFactoryPostProcessor said these chicken legs, oh, no, that’s all BeanDefinitionRegistryPostProcessor later, let’s talk about spring BeanFactoryPostProcessor
The main purpose of BeanFactoryPostProcessor is to intervene in the Bean generation process by modifying existing BeanDefinitions in the container. To put it bluntly, we’re here to screw things up.
So, now you can understand the figure above is why processing BeanDefinitionRegistryPostProcessor first and then deal with spring BeanFactoryPostProcessor? There’s another reason you need a BeanDefinition to change it: If you want to call the developer-defined BeanFactoryPostProcessor, you need to discover the BeanFactoryPostProcessor by the container itself, and then instantiate the BeanFactoryPostProcessor. Must first after nuwa empress ConfigurationClassPostProcessor * * * * custom spring BeanFactoryPostProcessor scan to find developers, then call spring BeanFactoryPostProcessor.
So what exactly can we change to BeanDefinition?
For example, the main implementation class is accomplished in the Spring, mainly to replace the ${} variable in the Bean properties for real property value. Of course, in the annotation-driven age, this class is not so important anymore. This class, mainly in the XML era, handles the ${} variable in Bean attributes, as follows
<bean id="hello" class="com.example.bean.User">
<property name="name" value="${name}"></property>
</bean>
Copy the code
If you ask if there are any other applications, so far, in the annotation-driven age, I really don’t have any other application scenarios, so please leave me a comment if you have one.
#### Phase 1 (Container startup Phase) Summarizes the container startup phase, where two awesome interfaces come into play. By BeanDefinitionRegistryPostProcessor interface implementation class to implement the scan of all Bean BeanDefinition is obtained, The BeanDefinition is then modified by the implementation class of the BeanFactoryPostProcessor interface. At this point, the raw material for the Bean is ready
### Transition phase (register BeanPostProcessor)
Note: this stage is not in many sources, this is just my own definition of a stage, but it doesn’t matter, as long as you know what I say this stage does, and then what effect.
The entry method for this phase is
registerBeanPostProcessors(beanFactory);
Copy the code
You’ll notice that you delegate directly to this method
PostProcessorRegistrationDelegate#registerBeanPostProcessors
Copy the code
Well, this is just to record the entry so that you can look at the source code later, we won’t analyze the source code directly here.
After we complete the first phase, we have the metadata BeanDefinitions of all the beans and the Instantiated BeanFactoryPostProcessor in our container. Now we’re actually going to instantiate the Bean with the BeanDefinition. However, the BeanPostProcessor is still missing in instantiation.
So the primary role of this transition phase is to register the instantiated and sorted BeanPostProcessor with the container.
The image above shows all the Sorted Beanpostprocessors. You’ll see that this is very similar to the sorting rules of the first BeanFactoryPostProcessor. Note, however, that this is just registering the sorted BeanPostProcessor into the container, and normally no methods of the BeanPostProcessor are called.
Note that this is just normal not to call any of the methods of the BeanPostProcessor. However, there is a special case, such as BeanPostProcessor relying on ordinary beans, which will lead to the instantiation of low-priority BeanPostProcessor, because the BeanPostProcessor relies on ordinary beans, The normal Bean is instantiated first and then injected into the instantiated BeanPostProcessor. This would cause our normal Bean to be initialized prematurely, which would result in the normal Bean not being handled by all the BeanPostProcessors and possibly missing some functionality (such as Spring’s asynchronicity, or not being handled by our own BeanPostProcessor). Specific can refer to my article in front
The Spring extension point (BeanPostProssor) is a deep diagnostic adventure
A lot of the magic of Spring is to pay attention to the principle behind the dark magic, what components are used to handle it, and what phase of the Spring container is loaded. There is a problem if the dark magic of the previous stage depends on the dark magic of the later stage. We’ll talk about that later.
If you want to talk about what these Beanpostprocessors are for, we won’t talk about it here. Just know that this is an interim phase, with a lot of sequential BeanpostProcessors in place for instantiating beans.
Phase 2: Instantiate the Bean
Okay, we’re finally in the instantiation Bean phase. With all of this in place, it’s time to move on to the topic of producing beans. The most exciting part of the kitchen is cooking.
Before going into the specific process of producing beans, let’s make it clear that there are two essential raw materials for producing beans
BeanDefinition
BeanPostProcessor
Interface (BeanPostProcessor
And its subinterfaces)
The BeanPostProcessor interface cluster actually has five interfaces and a total of 10 extension methods that participate in the entire Bean life cycle. Spring’s various dark arts are basically implemented through extensions that implement the BeanPostProcessor interface or its subinterfaces. Such as the @autowired @ Value annotation through AutowiredAnnotationBeanPostProcessor is implemented. Another well-known Aop feature is implemented through Abstracta To ProxyCreator.
So, all of Spring’s dark arts, including Aop, are actually implemented by extending Spring’s reserved extension interface. Thus, the Ioc container is fundamental to Spring, and Aop is just one of the Ioc container extension points. But that’s not to say that Aop isn’t important, but that the root of all the dark magic is the fancy application of the various extension points of the Spring IOC container.
Let’s record the location of the source code
AbstractApplicationContext{
refresh(){
// .....
finishBeanFactoryInitialization()
}
finishBeanFactoryInitialization(){
//...
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
}
}
DefaultListableBeanFactory{
preInstantiateSingletons(){}
}
AbstractBeanFactory{
getBean()
}
Copy the code
Now let’s look at how Spring creates beans
These are the steps of the bean production process. The most important is the first three steps:
- Instantiate the Bean
- Assembly attribute value
- Initialize the
Instantiate the Bean
Why, you might say, is there so much going on with a new date? This is easy to understand, because it is Spring, it does everything right, and it feels cool to do it.
Lu Xun once said: there are two trees in front of my house, one is jujube tree, the other is also jujube tree.
You taste, you taste.
Okay, let’s get back to Spring. Spring actually does a lot of things in the Bean instantiation phase.
- First, Spring gives BeanPostProcessor a chance to return the proxy object instead of the actual target object before instantiating the concrete object.
Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
Specific point is that you can call * * InstantiationAwareBeanPostProcessor postProcessBeforeInstantiation method to return an object you want to, if the return is not null, So can only call the BeanPostProcessor postProcessAfterInitialization method. The Bean then no longer performs initialization methods, dependency injection, etc. In other words, this is a short circuit callback method **. So what does that do? At present, the most important role is in the application of AOP, AOP base class Abstractauto xyCreator to achieve this method. There only needs to be one effect here.
-
Then, if the short circuit doesn’t work, you need to actually instantiate it. But instantiation is not that easy, because Spring uses constructors for dependency injection, so you have to decide which constructor to use when instantiating an object. The process to the * * * * SmartInstantiationAwareBeanPostProcessor determineCandidateConstructors method.
-
Then instantiated MergedBeanDefinitionPostProcessor will callback again * * * * postProcessMergedBeanDefinition method To further modify BeanDefinition or preprocess some annotations (e.g. preprocess class and cache element annotated @autowired)
-
Finally to handle loop applications, also call the SmartInstantiationAwareBeanPostProcessor getEarlyBeanReference method to expose reference in advance.
Assembly attribute value
- Call ** first
InstantiationAwareBeanPostProcessor
thepostProcessAfterInstantiation
** method to determine whether it is necessary to equip attribute values. - If it is necessary (and it is normally necessary), then it is necessary to perform **
InstantiationAwareBeanPostProcessor
thepostProcessProperties
orpostProcessPropertyValues
** to handle dependency assemblies.
Initialize the
-
InvokeAwareMethods: includes BeanNameAware, BeanClassLoaderAware, and BeanFactoryAware
-
Call before initialization callback methods: BeanPostProcessor postProcessBeforeInitialization
-
InvokeInitMethods afterPropertiesSet custom initialization method (initMethod) of InitializingBean
-
After initialization callback method BeanPostProcessor postProcessAfterInitialization
At this point you might understand the order of the initialization methods
Constructor > @PostConstruct > InitializingBean > init-method
It is easy to understand that Constructor is called first, instantiated, and then initialized. The afterPropertiesSet method of InitializingBean is called before init-method. Why is @PostConstruct called before the afterPropertiesSet method of the InitializingBean? In fact, very simple, because the work of the @ PostConstruct, are realized by using the approach of CommonAnnotationBeanPostProcessor postProcessBeforeInitialization. And in front of the life cycle of say very understand postProcessBeforeInitialization method is invoked before initialization. So @postconstruct is technically not an initialization method, but a pre-initialization method. Anyway, it doesn’t matter, there’s nothing else going on before or after initialization, so you can almost think of @PostConstruct as an initialization method.
At this point, we’ve covered the first three most important parts of the Bean life cycle. The article is already quite long, but there is still much that is not clear. 1. What are the specific implementation classes of various BeanPostProcessor and what are their functions? 2. How do I transition from Spring to SpringBoot
I’ll talk about them one by one, so stay tuned.