Read this article and you will learn
Spring
中prototype
The type ofbean
How to do cyclic dependency detectionSpring
中singleton
The type ofbean
How to do cyclic dependency detection
preface
Following on from the previous article, Spring gets the singleton process (1), let’s take a look at the following process this time
In the previous article, we said that first we find the beanName corresponding to the name, and then we go to the cache to see if the corresponding bean has been created/created. If the bean is found in the cache, then we need to do some processing for the bean. For example, if the user wants a plain bean, but finds a factoryBean in the Spring cache, the getObject method of the fatoryBean is called with its corresponding preloading methods and callbacks.
So what about the process if we can’t find the bean in the cache? This is what this article is going to share with you
Source code analysis
if(sharedInstance ! =null && args == null) {
// I deleted some spring logs here
// Handle the factory bean case, including fetching from the Factory Beans cache, or re-calling the Factory bean's get bean method, including some callbacks
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
} else {
// The object's bean cannot be retrieved from the getSingleton above, the scope indicating that the bean is either not a Singleton requires that the bean bea Singleton but does not initialize a sentence // Because Spring only addresses cyclic dependencies in singleton mode, in prototype mode an exception is thrown if a cyclic dependency exists // The loop dependency check uses threadLocal because prototype only if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } // If it is not found in the container, it is loaded from the parent container BeanFactory parentBeanFactory = getParentBeanFactory(); // parentBeanFactory is not empty and there is no beanDefinitionMap for this name if(parentBeanFactory ! =null && !containsBeanDefinition(beanName)) { // Not found -> check parent. // This is just to find the real beanName, without removing the prefix of the Factory bean String nameToLookup = originalBeanName(name); if (parentBeanFactory instanceof AbstractBeanFactory) { return ((AbstractBeanFactory) parentBeanFactory).doGetBean( nameToLookup, requiredType, args, typeCheckOnly); } else if(args ! =null) { // Delegation to parent with explicit args. return (T) parentBeanFactory.getBean(nameToLookup, args); } else if(requiredType ! =null) { // No args -> delegate to standard getBean method. return parentBeanFactory.getBean(nameToLookup, requiredType); } else { return (T) parentBeanFactory.getBean(nameToLookup); } } ...} Copy the code
The first step is to determine if this is a Prototypebean and throw a loop-dependent exception if it is and is being created
protected boolean isPrototypeCurrentlyInCreation(String beanName) {
/ / prototypesCurrentlyInCreation is a threadLocal
Object curVal = this.prototypesCurrentlyInCreation.get();
return(curVal ! =null &&
(curVal.equals(beanName) || (curVal instanceofSet && ((Set<? >) curVal).contains(beanName))));} Copy the code
This method is called when each Prototype bean is created
protected void beforePrototypeCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get();
if (curVal == null) {
this.prototypesCurrentlyInCreation.set(beanName);
} else if (curVal instanceof String) {
Set<String> beanNameSet = new HashSet<>(2); beanNameSet.add((String) curVal); beanNameSet.add(beanName); this.prototypesCurrentlyInCreation.set(beanNameSet); } else { Set<String> beanNameSet = (Set<String>) curVal; beanNameSet.add(beanName); } } Copy the code
The curVal is either a String or a Set after creating the Prototype bean
protected void afterPrototypeCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get();
if (curVal instanceof String) {
this.prototypesCurrentlyInCreation.remove();
} else if (curVal instanceof Set) {
Set<String> beanNameSet = (Set<String>) curVal; beanNameSet.remove(beanName); if (beanNameSet.isEmpty()) { this.prototypesCurrentlyInCreation.remove(); } } } Copy the code
As you can see, Spring uses ThreadLocal to check for loop dependencies, as mentioned in the source code analysis of Spring resource loading, and to check for loop references to a resource using ThreadLocal to initialize the Spring container
The second step is to determine whether the current beanFactory has a parent (parent beanFactory). If it does and the beanDefinition of beanName does not exist in the current beanFactory, then try to fetch the bean from the parent
Let’s move on to the code below
// Instead of just doing type checking to create a bean, mark that the bean has been created or will be created
if(! typeCheckOnly) { markBeanAsCreated(beanName);
}
try { // Get beanName's corresponding GenericBeanDefinition from the container and convert it to RootBeanDefinition final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); // Check the given merged BeanDefinition checkMergedBeanDefinition(mbd, beanName, args); // Guarantee initialization of beans that the current bean depends on. // Process the dependent bean String[] dependsOn = mbd.getDependsOn(); if(dependsOn ! =null) { for (String dep : dependsOn) { // If it is a cyclic dependency if (isDependent(beanName, dep)) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'"); } / / register registerDependentBean(dep, beanName); try { // See if I rely on the big guy is ready getBean(dep); } catch (NoSuchBeanDefinitionException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", ex); } } } ..Copy the code
The third step is to get the registered BeanDefinitionDefinition from the BeanDefinitionRegistry and then other beans that this bean depends on, iterate over the beans that it depends on, and determine whether it is cyclically dependent
protected boolean isDependent(String beanName, String dependentBeanName) {
synchronized (this.dependentBeanMap) {
return isDependent(beanName, dependentBeanName, null);
}
}
private boolean isDependent(String beanName, String dependentBeanName, @Nullable Set<String> alreadySeen) { if(alreadySeen ! =null && alreadySeen.contains(beanName)) { return false; } String canonicalName = canonicalName(beanName); // Find the collection that depends on this beanName Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName); // No one depends on this beanName if (dependentBeans == null) { return false; } // Ho, beanName depends on bean, also depends on beanName, die if (dependentBeans.contains(dependentBeanName)) { return true; } DependentBeanName is not dependentBeanName // A wants to depend on F, BCDE depends on A, so we are now at this stage, we have determined that F does not depend on A, so we need to see if F depends on BCDE, if so, then it is cyclic dependent for (String transitiveDependency : dependentBeans) { if (alreadySeen == null) { alreadySeen = new HashSet<>(); } alreadySeen.add(beanName); if (isDependent(transitiveDependency, dependentBeanName, alreadySeen)) { return true; } } return false; } Copy the code
Before each bean is created, it registers its dependencies. It consists of two maps, one for key and value, and the other for key and value. For example, beanA depends on beanB and beanC
Key is the dependents and value is the set of dependentsbeanB ---> beanA
beanC ---> beanA
Key is a dependent and value is a collection of dependentsbeanA ---> beanB,beanC
Copy the code
The fourth step is to unregister dependencies, which store data in the two maps above
public void registerDependentBean(String beanName, String dependentBeanName) {
String canonicalName = canonicalName(beanName);
// Add this dependent person to this
synchronized (this.dependentBeanMap) {
Set<String> dependentBeans =
this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8)); if(! dependentBeans.add(dependentBeanName)) { return; } } // Put the big guy I depend on in my dependency list here synchronized (this.dependenciesForBeanMap) { Set<String> dependenciesForBean = this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8)); dependenciesForBean.add(canonicalName); } } Copy the code
The final getBean goes back to where we started
getBean(dep);
Copy the code
Today we first analysis here, the subsequent words we continue to discuss in the later articles. Today we have a general analysis
conclusion
- According to the parameters
name
Find the correspondingbeanName
Whatever thisname
Is an alias or is afactoryBean
的beanName
- Check to see if this is included in the cache
beanName
object- Start with level 1 caching
singletonObjects
Let’s see if there are any - And then from level two cache
earlySingletonObjects
- If not, then cache from level 3
singletonFactories
Let’s see if there are any
- Start with level 1 caching
- If there is one in the cache
bean
So we still need to deal with thisbean
- if
Spring
Returned from the cachebean
是factoryBean
, and what users want is onebeanFactory
(parametername
The prefix in&
), so we go straight back - if
Spring
Returned from the cachebean
Is a commonbean
, and users also want is a commonbean
, then return directly - if
Spring
Returned from the cachebean
Is afactoryBean
And what users want is an ordinary onebean
, then we have to start fromfactoryBean
Get this frombean
- And from a
factoryBean
Get this frombean
Call to pre-processing, post-processing, and common interface callbacksBeanPostProcessor
- if
- If it’s not in cache
bean
, then determine whether it isprototype
Type and loop dependent - If not, try to see if it can be found in the parent container
bean
- Gets this if the parent container does not have one either
beanName
The correspondingbeanDefinition
Find out what it depends onbeanName
- Determine whether the
beanName
And rely onbeanName
If no, register its dependencies and callgetBean
Method to create a dependencybeanName