Read this article and you will learn

  • SpringprototypeThe type ofbeanHow to do cyclic dependency detection
  • SpringsingletonThe type ofbeanHow 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

image-20200530152154079

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 parametersnameFind the correspondingbeanNameWhatever thisnameIs an alias or is afactoryBeanbeanName
  • Check to see if this is included in the cachebeanNameobject
    • Start with level 1 cachingsingletonObjectsLet’s see if there are any
    • And then from level two cacheearlySingletonObjects
    • If not, then cache from level 3singletonFactoriesLet’s see if there are any
  • If there is one in the cachebeanSo we still need to deal with thisbean
    • ifSpringReturned from the cachebeanfactoryBean, and what users want is onebeanFactory(parameternameThe prefix in&), so we go straight back
    • ifSpringReturned from the cachebeanIs a commonbean, and users also want is a commonbean, then return directly
    • ifSpringReturned from the cachebeanIs afactoryBeanAnd what users want is an ordinary onebean, then we have to start fromfactoryBeanGet this frombean
    • And from afactoryBeanGet this frombeanCall to pre-processing, post-processing, and common interface callbacksBeanPostProcessor
  • If it’s not in cachebean, then determine whether it isprototypeType and loop dependent
  • If not, try to see if it can be found in the parent containerbean
  • Gets this if the parent container does not have one eitherbeanNameThe correspondingbeanDefinitionFind out what it depends onbeanName
  • Determine whether thebeanNameAnd rely onbeanNameIf no, register its dependencies and callgetBeanMethod to create a dependencybeanName

Surely this time?