preface
An in-depth understanding of Spring source code is divided into seven sections. This section is the second section of Spring source code.
- The scanning process
2. Bean creation process
- Container extension
- AOP source code analysis
- Transactional source code analysis
- Spring JDBC source code analysis
- Spring common design patterns
Bean secondary collection
The previous section covered the bean collection process, but that was only Spring’s first collection. There is a second collection phase in which some beans must be instantiated. This process is also reserved for framework extensions, as you might have seen with the @enableXXXX annotation, These annotations often add new beans to the container behind the scenes, so it’s important to know.
Look at the constructor of the AnnotationConfigApplicationContext, scan we no longer need to say, it is important to refresh, this method is the core, is very complex.
public AnnotationConfigApplicationContext(String... basePackages) {
this(a); scan(basePackages); refresh(); }Copy the code
There are several methods under Refresh, as follows.
@Override
public void refresh(a) throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
/** ** do not need to see */
prepareRefresh();
/ * * * get in this class constructor execution phase DefaultListableBeanFactory instantiated, all stages around the he * /
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
/ * * * to add some other auxiliary ConfigurableListableBeanFactory classes, such as the post-processor * /
prepareBeanFactory(beanFactory);
try {
/** * this method is used to extend, this process does not subclass it, do not need to see */
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process")
/ * * * here also implements the Bean's collection, mainly executes ConfigurationClassPostProcessor collection process * / * here is a cycle
invokeBeanFactoryPostProcessors(beanFactory);
/** * Collect all BeanPostProcessors */
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
initMessageSource();
initApplicationEventMulticaster();
onRefresh();
registerListeners();
// Instantiate the remaining beans
finishBeanFactoryInitialization(beanFactory);
finishRefresh();
} catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
} finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...resetCommonCaches(); contextRefresh.end(); }}}Copy the code
We start from the first useful methods, namely invokeBeanFactoryPostProcessors (), this method is very important, He will call all realized BeanDefinitionRegistryPostProcessor interface class postProcessBeanDefinitionRegistry () method, An implementation of the most core classes are ConfigurationClassPostProcessor, he might be to add new bean container, because the class may have @ Import, @ bean annotations, so there is always a class will be responsible for the interpretation of these annotations, that this class is he.
The interface comment says, “More bean definition can be done”, that is, in addition to adding annotations to the class, more beans can be added to the container through the interface, as shown below.
@Component
public class TestA implements BeanDefinitionRegistryPostProcessor{
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(TestB.class) .getBeanDefinition(); registry.registerBeanDefinition("testBean",beanDefinition); }}Copy the code
ConfigurationClassPostProcessor as the main work is to collect Bean, then look at the specific do below.
Perform all BeanDefinitionRegistryPostProcessor
From invokeBeanFactoryPostProcessors () to enter directly to PostProcessorRegistrationDelegate# invokeBeanFactoryPostProcessors (), Here to do the first thing is collected at the present stage find realized BeanDefinitionRegistryPostProcessor interface beans beans list, and also implements the PriorityOrdered interface (used to), at this time, The only one in this condition is ConfigurationClassPostProcessor, then invokes the getBean () instantiation, instantiated call postProcessBeanDefinitionRegistry new bean collection.
After collection, the container may increase the class also implements BeanDefinitionRegistryPostProcessor interface, so the Spring also judgment, if these classes are to realize Ordered interface (used to), then do the same thing, instantiation, And call the postProcessBeanDefinitionRegistry.
So the question comes, if this time there is a new class is added to the container, and there is some kind of is to realize the BeanDefinitionRegistryPostProcessor interface, that how to do?
The idea here is that as soon as you enter the loop, the condition next time will be false. Then repeat the above operation from the existing bean. If you find that a bean has not been processed, set the condition next time to true. And then sort, and then collect.
boolean reiterate = true;
while (reiterate) {
reiterate = false;
/ * * * again get realized BeanDefinitionRegistryPostProcessor interface classes, and instantiate the * here is a call, until all finished * / bean were collected
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true.false);
for (String ppName : postProcessorNames) {
if(! processedBeans.contains(ppName)) { currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); processedBeans.add(ppName); reiterate =true; }}/ / sorting
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
/ / call postProcessBeanDefinitionRegistry new bean collection.
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
currentRegistryProcessors.clear();
}
Copy the code
Once collected, all classes that implement the BeanFactoryPostProcessor interface are fetched, their postProcessBeanFactory methods are called in the order of their PriorityOrdered, Ordered interfaces, Under this method in spring BeanFactoryPostProcessor interface, and BeanDefinitionRegistryPostProcessor succeed him again.
When the method is called, all bean definitions are loaded, but not instantiated. We can modify the bean properties, as well as add new beans, such as the following.
@Component
public class TestA implements BeanDefinitionRegistryPostProcessor{
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(TestB.class) .getBeanDefinition(); ((DefaultListableBeanFactory) beanFactory).registerBeanDefinition("testBean",beanDefinition);
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {}}Copy the code
Parameter ConfigurableListableBeanFactory only implementation class is DefaultListableBeanFactory, take after him, we can do whatever you want, because DefaultListableBeanFactory is a core class, You can also register beans.
ConfigurationClassPostProcessor
Said to the above will call ConfigurationClassPostProcessor postProcessBeanDefinitionRegistry class, here is the key.
The code is too long to post.
Collect classes where new beans may exist
The bean class is marked with Configuration, Component, ComponentScan, Import, and ImportResource annotations. The bean class is marked with Configuration, Component, Import, and ImportResource annotations. If it doesn’t, it checks to see if the method in the class is annotated with an @bean annotation, and if it is, it puts it in the collection as well.
If the Configuration annotation is marked and proxyBeanMethods is true, it will be enhanced in the future, that is, dynamic proxy will be required.
If this is the case, the original class will be instantiated, but if proxyBeanMethods is true, the final class will be propped up by CGLIB and printed like this: TestC$$EnhancerBySpringCGLIB$$8d32c824@6c2d4cc6
@Configuration(proxyBeanMethods = false)
public class TestC {}Copy the code
Now that Spring has identified the classes that need further processing, the next step is to parse them one by one. This part is resolved by ConfigurationClassParser, which is named ConfigurationClassParser.
And it’s a recursive process.
Working with inner classes
If it is marked with @Component or its child annotation, it will process its inner class first. If it is marked with @Component or its child annotation, that class will eventually be included in Spring’s container, as well as inner classes. This nesting form, which Spring takes into account, is handled recursively.
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
processMemberClasses(configClass, sourceClass, filter);
}
Copy the code
To collect property
PropertySources annotations are then checked and added to the context’s environment as PropertySource, which holds values in files such as application.properties. Finally, it is used for dependency injection.
There is also a knowledge is very important, the environment is created in the AnnotationConfigApplicationContext, how this variable is passed to the ConfigurationClassPostProcessor? This is where the Aware interface comes in.
Scanning for new paths
Then the annotation of ComponentScans and ComponentScan will be processed. After Spring gets the value inside, it will re-use the method of the previous chapter and scan it into doScan. After scanning, the new Bean scanned will go through these processes.
To deal with the Import
Then we’ll deal with the @import annotation, which is a bit more, because the classes in @import can fall into one of three categories.
-
Implements the ImportSelector
-
Implements the ImportBeanDefinitionRegistrar
-
Independent class
In short it can collect all three new bean, if is the third, then the new bean name is his full path, such as com. XXX. TestA, this process is also a recursive same collection process, the second case will temporarily first instantiated into a collection, do not call any of these methods, will call in the future, The first and third will not be put into a container for now, but into a collection.
If @import (testb.class) is marked on TestA and @import (testa.class) is marked on TestB, an infinite loop will occur, and Spring must know that it does this through the ImportStack, for example when dealing with TestA. We put TestA on the stack, and if we have to deal with TestA in the future, because it’s already in the stack, we throw an exception.
Gets a method with an @bean annotation
The next step is to collect the @bean annotated method from this class, which will eventually be encapsulated as a BeanMethod object and added to the collection. The method will be called in the future and the return value will be put into the container.
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
Copy the code
Collect the default method in the interface
Even better, if the class implements an interface that has a default method annotated with the @bean annotation, Spring still collects it, and this is also a recursive process, which means that the interface inherits the interface and drills through to the end.
@Bean
default public TestB print(a) {
return new TestB();
}
Copy the code
private void processInterfaces(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
// Get all the interfaces for this class
for (SourceClass ifc : sourceClass.getInterfaces()) {
// Extract the default method marked with @bean
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(ifc);
for (MethodMetadata methodMetadata : beanMethods) {
if(! methodMetadata.isAbstract()) { configClass.addBeanMethod(newBeanMethod(methodMetadata, configClass)); }}// Recursively retrieve all the default methods marked with @bean in this interfaceprocessInterfaces(configClass, ifc); }}Copy the code
To deal with the parent class
When this class is done, it may have the same requirements in its parent class that Spring needs to handle. But this is not a recursive process, a process, while doProcessConfigurationClass is said all of the above process, the return value is his parent, no words will return null.
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
while(sourceClass ! =null);
Copy the code
So this is the end of the process, and we start the next process
Add a new bean to the container
At this point, the ConfigurationClass has been parsed, all the @bean and other information has been extracted and encapsulated into a ConfigurationClass, and the next step is to read that information and put everything that can fit into the container.
Load BeanDefinitions from ConfigurationClass. Note the “s”, indicating that a ConfigurationClass may have multiple BeanDefinitions ready to be added to the container.
private void loadBeanDefinitionsForConfigurationClass( ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
/** * Check whether */ is skipped
if (trackedConditionEvaluator.shouldSkip(configClass)) {
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
return;
}
/** * Determine if this class is being used@ImportImport, * if it is, then it will be wrapped as BeanDefinitionHolder, added to the container */
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
/** * traversal this class all added@BeanAnnotation method */
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
Call ImportBeanDefinitionRegistrar * / / * * *
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
Copy the code
The @bean annotation contains a lot of information about initMethod and destroyMethod, and also needs to extract Scope annotations to determine whether a proxy is needed. But in short, Each of these efforts collects information from various sources and encapsulates it into a BeanDefinition, although the implementation class of each BeanDefinition may differ.
Here to see the last line calls, we have said above, @ Import class if implemented ImportBeanDefinitionRegistrar interface, so temporarily will put his instantiated in the collection, the future will call, so here is it.
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
registrars.forEach((importBeanDefinitionRegistrar, annotationMetadata) -> {
importBeanDefinitionRegistrar.registerBeanDefinitions(annotationMetadata, this.registry, this.importBeanNameGenerator);
});
}
Copy the code
ImportBeanDefinitionRegistrar also used to gather new Bean
BeanPostProcessor
The following is a collection of all the classes that implement the BeanPostProcessor interface. This interface is very important, and it’s probably a little bit easier to understand if you know the hook function. It’s like a hook. The return value is passed to the original next function.
Had A and C two functions, for example, A processing might be passed to the results of C, but suddenly in B, which increased the function function execution sequence into A – > B > C, and B also have the input and output, the processing result is passed to the B, B in which you can modify A result, finally passed to C, C at this time may not be aware of the data has been corrected.
And that’s what BeanPostProcessor does, playing the role of B in it.
RegisterBeanPostProcessors below the refresh () () method is to collect all implements the BeanPostProcessor interface classes, that is to collect all hooks, the future will be called before, during, and after creating objects.
The following code gets the class that implements the BeanPostProcessor interface from all the beans. The details of how to get the class are not covered in this chapter.
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true.false);
Copy the code
finishBeanFactoryInitialization
This is basically the last step, which calls the following paragraph. The comment means to instantiate the remaining beans that are not lazily loaded. Because some logic needs to be instantiated earlier, for example, this Bean implements an interface that Spring must use and was instantiated earlier, lazy loaded beans are instantiated when needed.
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
Copy the code
The logic in the above code is to iterate through all Bean names and determine whether the Bean is eligible for instantiation. For example, lazy loaded beans are not eligible for instantiation. If so, call getBean() to instantiate the Bean.
getBean
This process is very complex, mainly to resolve the dependency problem, and the constructor problem, the simplest is no parameter construction, if there is a parameter, then the trouble, Spring to see if the existing bean can find the bean that matches the constructor parameter, plus possibly at Value.
Let’s start directly with AbstractBeanFactor #doGetBean, which is the start of getting the bean.
The first step is to convert the name, because if you use @bean, then the Bean may have multiple aliases, such as name1 or name2. The first of these names will be placed in beanDefinitionMap as the final Bean name, and an aliasMap will hold the aliasMap. For example, name2->name1, when name2 is fetched, it will be converted to name1.
This next step is the key, addressing the core of the dependency problem.
// If there is already an instance
Object sharedInstance = getSingleton(beanName);
if(sharedInstance ! =null && args == null) {
/** * If the Bean implements a FactoryBean, it may return its getObject method */ here
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
Copy the code
Take a closer look at getSingleton()
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
/** * If there is a complete Bean object, it returns * all objects created and populated with properties will be put into this */
Object singletonObject = this.singletonObjects.get(beanName);
/** * If the object does not exist, determine whether the object is in the creation process of the loop dependency. * /
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
/** * generally Null */
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
/** * gets the result of the early exposure of this object */
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
/** * get the pre-exposed ObjectFactory of this object * if there is an ObjectFactory here, get the object from the factory, which is the result of the previous pre-exposed */ObjectFactory<? > singletonFactory =this.singletonFactories.get(beanName);
if(singletonFactory ! =null) {
// Instance object
singletonObject = singletonFactory.getObject();
// Put it into earlySingletonObjects, which may be used later
this.earlySingletonObjects.put(beanName, singletonObject);
// Delete object factory, it already has objects, no longer need
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
Copy the code
No dependency creation process
Now, if you want to get testA from the cache the first time you get testA, you’re not going to get testA from the cache the first time you get testA, so you’re not going to get testA from the cache.
DependsOn (@dependson) is then instantiated by invoking getBean(), as in the current process.
And then determine if it’s a singleton, and in general it is.
And then we go in here, and the second argument to getSingleton is ObjectFactory, which is used to create the object.
sharedInstance = getSingleton(beanName, () -> {
try {
/** * this is called in getSingleton */
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throwex; }});Copy the code
GetSingleton does three important things. The first thing is to put the bean currently being created into a collection for loop dependency detection. The second thing is to call getObject() of the passed ObjectFactory to get the object. You can fetch it directly from the cache later.
So let’s look at the second most important thing, going into the createBean() method.
In createBean(), Spring does not instantiate the object itself, but hands it over to the extension interface.
/** * attempts to initialize after instantiation, if the extension interface is not instantiated, then Spring instantiates it itself, otherwise returns */
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if(bean ! =null) {
return bean;
}
Copy the code
This interface is called InstantiationAwareBeanPostProcessor, we can achieve, when creating objects, Spring will be handed over to him, if he returns null, then the Spring can only be instantiated, Otherwise call all implements the BeanPostProcessor interface postProcessAfterInitialization after () method returns, this also is Spring leaves us extension.
Now if no class wants to instantiate the bean, Spring will have to do it itself, going into doCreateBean().
/** * instantiate */
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
return beanInstance;
Copy the code
The first sentence of doCreateBean() is to instantiate the object.
/** * create object, initialize object */
instanceWrapper = createBeanInstance(beanName, mbd, args);
Copy the code
This step is tricky because Spring has many types to infer. But now we know that our object is successfully created, but it could also be a proxy object. Spring does not proxy the object after it is created, but instead replaces the instantiated class, such as com.test. Test$$EnhancerBySpringCGLIB$$dded467a is instantiated by reflection. This determines that if the BeanDefinition needs a proxy, it should generate one for it first.
Create is called after the completion of all MergedBeanDefinitionPostProcessor interface.
The second argument is also ObjectFactory. This is called early exposure. It’s not useful for classes that don’t have any dependencies, but it’s very useful for dependency loops.
/** * After an object is created, add it to singletonFactories with no properties filled in, and return */ from getEarlyBeanReference when other objects depend on it
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
Copy the code
The next step is populating properties, also known as dependency injection, which doesn’t work if you don’t have any properties.
/** * fill the property */
populateBean(beanName, mbd, instanceWrapper);
/** * Call object BeanNameAware, BeanClassLoaderAware, BeanFactoryAware */
exposedObject = initializeBean(beanName, exposedObject, mbd);
Copy the code
Finally, initialize the bean.
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
/** * Call object BeanNameAware, BeanClassLoaderAware, BeanFactoryAware(if implemented) */
invokeAwareMethods(beanName, bean);
Object wrappedBean = bean;
if (mbd == null| |! mbd.isSynthetic()) {/** * Call the preprocessor in BeanPostProcessors to try to modify */
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
/** * Calls the bean's init method */
invokeInitMethods(beanName, wrappedBean, mbd);
} catch (Throwable ex) {
throw newBeanCreationException( (mbd ! =null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null| |! mbd.isSynthetic()) {/** * Call the back processor of BeanPostProcessors to try to modify */
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
Copy the code
That’s the end of this part.
Dependency situation
So now we have dependencies, we have dependencies and what we do is we populate properties, so we need to look at the populateBean() method, PopulateBean () will call all realized InstantiationAwareBeanPostProcessor interface postProcessProperties () method, And specific the implementation class is finish fill AutowiredAnnotationBeanPostProcessor, other implementation class will be filled with other annotations property,
Enter AutowiredAnnotationBeanPostProcessor# postProcessProperties (), first will call findAutowiringMetadata find need automatic injection field, This object contains a collection that holds InjectedElement, which represents a single field that needs to be injected.
InjectedElement an implementation class is located in AutowiredAnnotationBeanPostProcessor, called AutowiredFieldElement, when calling his inject method injection on this field.
Internally, if the field is of Class type, it’s easier to call the leading getBean() and try to get it from the container.
The logic for injecting @Value is also here, but it is more complicated and will be examined separately.
Dependent cyclic case
Now suppose TestB needs to be injected into TestA, and TestA needs to be injected into TestB.
Starting with getBean(“testA”), the populateBean() method again populates testB, and returns to create testB again. After testB is successfully created, populateBean() is executed again to populate testA.
You can’t create testA at this point, so it’s an endless loop.
Remember “key point 1”? When an object is successfully created, an ObjectFactory is stored in singletonFactories. GetObject () of this ObjectFactory can be called to retrieve the newly created object anywhere.
If a singletonFactories (” singletonFactories “, “singletonFactories”, “singletonFactories”, “singletonFactories”, “singletonFactories”, “singletonFactories”, “singletonFactories”, “singletonFactories”
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if(! mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { List<SmartInstantiationAwareBeanPostProcessor> smartInstantiationAware = getBeanPostProcessorCache().smartInstantiationAware;for(SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) { exposedObject = bp.getEarlyBeanReference(exposedObject, beanName); }}return exposedObject;
}
Copy the code
He will call all achieved SmartInstantiationAwareBeanPostProcessor interface class getEarlyBeanReference () try to do the last modification, at the moment, I found that he only used at AOP, so this chapter temporarily don’t consider him.
So this method just returns the object that was passed in.
This solves the dependency loop.
FactoryBean
Let’s go back to the beginning, and we have this next paragraph to parse.
/** * If the Bean implements a FactoryBean, it may return its getObject method */ here
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
Copy the code
This is the case when getBean() returns the value of getObject() for the interface when the bean implements the FactoryBean interface.