test beans
1. Container startup
There are many ways for Spring to create a container. It can be based on configuration classes or based on xml configuration files. Here, we use XML configuration to create Spring containers.
Debug starts and sets the first breakpoint
Come to org.springframework.context.support.AbstractApplicationContext.refresh() method refresh is the core method of spring container startup
A total of 12 other methods are called in this method. This article is to look at the life cycle of Beans. All unrelated methods are briefly explained.
The first method prepareRefresh()
// Prepare this context for refreshing.
prepareRefresh();
Next, this line of code is executed to do a very important thing, create a BeanFactory
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
2. Create BeanFactory
Let's see how to get the bean factory
debug跟进 org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory
return new DefaultListableBeanFactory(getInternalParentBeanFactory());
The returned bean factory type is DefaultListableBeanFactory
If you download the source code, you can see the full set of bean lifecycle interfaces annotated in the BeanFactory interface, etc. We can see them all in the source code
Bean factory implementations should support standard bean lifecycle interfaces whenever possible. The full set of initialization methods and their standard sequence are:
-
BeanNameAware's setBeanName
-
BeanClassLoaderAware’s setBeanClassLoader
-
BeanFactoryAware’s setBeanFactory
-
EnvironmentAware’s setEnvironment
-
EmbeddedValueResolverAware's setEmbeddedValueResolver
-
ResourceLoaderAware’s setResourceLoader (only applicable when running in an application context)
-
ApplicationEventPublisherAware’s setApplicationEventPublisher (only applicable when running in an application context)
-
MessageSourceAware’s setMessageSource (only applicable when running in an application context)
-
ApplicationContextAware’s setApplicationContext (only applicable when running in an application context)
-
ServletContextAware’s setServletContext (only applicable when running in a web application context)
-
postProcessBeforeInitialization methods of BeanPostProcessors
-
InitializingBean’s afterPropertiesSet
-
a custom init-method definition
-
postProcessAfterInitialization methods of BeanPostProcessors
When closing the bean factory, the following lifecycle methods are applied:
- postProcessBeforeDestruction methods of DestructionAwareBeanPostProcessors
- DisposableBean’s destroy
- a custom destroy-method definition
回到 org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory
After creating the bean factory, three more methods are called
Method 1: Set serialization ID
beanFactory.setSerializationId(getId());
Method 2: Customize the bean factory (whether the bean definition can be overridden, whether to run circular dependencies)
customizeBeanFactory(beanFactory);
Method 3: Parse the bean configuration information and generate BeanDefinitions (hereinafter referred to as bd)
loadBeanDefinitions(beanFactory);
3. Parse and generate BeanDefinitions
debug跟进 来到 org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions()
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
Create an xml parser, we use the xml configuration file to start the container, and the bean definition information is configured in xml
After the parsing is complete, call the loadBeanDefinitions(beanDefinitionReader) method and add it to the BeanDefinition
Next, get all the configuration information files and load them
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
Finally, the xml configuration file is parsed into a Document object through JAXP in the JDK, and then registered as bd
After registering bd, the beanDefinitionMap in the beanFactory already has the definition information of bean A. In the future, through these definition information, a bean A can be initialized and placed in the container by reflecting the instance.
Next, come to the invokeBeanFactoryPostProcessors(beanFactory) method to enter the post-processor process of executing the Bean Factory
4. Execute the post-processor method of the Bean factory
The execution time of the post-processor method of the bean factory is to initialize bean factory, and after parsing and generating bd, but before the bean is instantiated
Then the refresh method goes down, and the eleventh method begins to enter the instantiation phase of the bean
finishBeanFactoryInitialization(beanFactory)
5. ApplyBeanPostProcessorsBeforeInstantiation extension point before instantiating the bean
来到 org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization
Called beanFactory.preInstantiateSingletons(); to initialize all single instance beans
org.springframework.beans.factory.support.DefaultListableBeanFactory. preInstantiateSingletons()
First copy all beanNames and put them in beanNames
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
Then start traversing all beanNames
for (String beanName : beanNames) {
.....
}
The first step of traversal is to convert bd to RootBeanDefinition to unify
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
Then there is a triple judgment that it cannot be an abstract class, it must be a singleton, and it is not lazy loading, and continue to go down
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
Determine whether it is a factory bean test bean A is a common bean, go else
if (isFactoryBean(beanName))
Call getBean(beanName); method
又调用 org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean
First get the real beanName, remove the & prefix of the factory bean name and resolve the bean alias
String beanName = transformedBeanName(name);
Then try to get the bean object from the cache
Object sharedInstance = getSingleton(beanName)
来到 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton
First note the parameters of a method call
allowEarlyReference 这个参数的值,默认就是true,用来解决循环依赖的,很重要
Then look at this method from the first-level cache, the first-level cache is full and initialized singleton bean, if you can get it, return directly
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256) Singleton cache pool (first level cache)
Object singletonObject = this.singletonObjects.get(beanName); //singletonObjects 单例缓存池
If not, and the bean corresponding to the current beanName is being created
private final Set singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16)) saves the beanName being created
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
Find in earlySingletonObjects from the second level cache
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16) Second-level cache, instantiated, but not filled with properties
singletonObject = this.earlySingletonObjects.get(beanName);
If not found, continue to search from the third-level cache singletonObjects
**private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16) stores the lombda expression that generates the proxy object**
Note that the value ObjectFactory<?> of this map is a functional interface, which will be used to save lombda expressions in the future to solve the problem of circular dependencies of beans proxied by AOP
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
If the singletonFactory is not empty, the lombda expression will be executed to generate a proxy class, that is, AOP in advance
singletonObject = singletonFactory.getObject();
After AOP in advance, two interesting methods are executed
Put the current proxy class object into the second-level cache, and then delete the entry in the third-level cache. These two steps are very important. Later, I will write a circular dependency blog post, which will explain in detail.
If you don't get it from the cache, return an empty object directly
Circular dependency judgment, there is no circular dependency problem, go down, objects with circular dependencies first create dependent objects
The current bean is a singleton, enter if
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
A getSingleton method is called here. The method has two parameters, a beanName, and a lombda expression,
执行org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(String beanName, ObjectFactory<?> singletonFactory)方法
First verify that the name of the bean cannot be empty
Assert.notNull(beanName, "Bean name must not be null");
Then try to get the object from the cache again and see it
Object singletonObject = this.singletonObjects.get(beanName);
I didn't get to enter the if judgment to execute the lombda expression
Execute createBean(beanName, mbd, args); method
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
Before the bean is actually instantiated, there is also such a method to return the current proxy object instead of the target bean object
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd)
Determine whether there are InstantiationAwareBeanPostProcessors in the container, and if so, execute the InstantiationAwareBeanPostProcessors.applyBeanPostProcessorsBeforeInstantiation method
InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation is called before instantiation
Object result = bp.postProcessBeforeInstantiation(beanClass, beanName);
If there is a return object, the changed object will be placed in the spring container, and the instantiation process of the bean to which the current bd package definition information belongs will be terminated early, and the InstantiationAwareBeanPostProcessor.applyBeanPostProcessorsAfterInitialization method will be executed.
If null is returned, the current bean instantiation process continues down
We did not implement the applyBeanPostProcessorsBeforeInstantiation extension point, and continue to go down
Come to the doCreateBean(beanName, mbdToUse, args) method
doCreateBean(beanName, mbdToUse, args)
The process before the bean instantiation has been completed, and the actual bean instantiation has begun to be placed in the next blog post