Read this article and you will learn

  • ingetBeanMethod,SpringHandle aliases as well asfactoryBeanname
  • SpringHow do I get data from a multilevel cachebeanNameTo obtainbean
  • SpringHow to deal with user acquisition commonbeanfactoryBean

The introduction

From the initialization of the Spring container, we learned how Spring converts XML files to BeanDefinition and registers them with BeanDefinitionRegstry.

Today we will continue our study of Spring bean loading

public static void main(String[] args) {
  Resource resource = new ClassPathResource("coderLi.xml");
  DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
  XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(defaultListableBeanFactory);
  xmlBeanDefinitionReader.loadBeanDefinitions(resource);
 } Copy the code

      

       
<beans>
 <bean class="com.demo.data.Person">
  <description>
Wechat search :CoderLi </description>  </bean> </beans> Copy the code

Familiar flavors, familiar recipes

Source code analysis

We can add the following code to the Java code above

System.out.println(defaultListableBeanFactory.getBean("com.demo.data.Person#0"));
Copy the code

We get the Person bean object from the Spring container according to the default beanName, though we can use the default alias

System.out.println(defaultListableBeanFactory.getBean("com.demo.data.Person"));
Copy the code

For those unfamiliar with Spring aliases, take a look at my article spring-AliasRegistry

We directly into the AbstractBeanFactory# getBean (String) method, the AbstractBeanFactory for DefaultListableBeanFactory parent class

@Override
public Object getBean(String name) throws BeansException {
   return doGetBean(name, null.null.false);
}
Copy the code

AbstractBeanFactory#doGetBean

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
                    @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

   // Find the bean name for this parameter
   final String beanName = transformedBeanName(name);
 Object bean;   // Eagerly check singleton cache for manually registered singletons.  // Check to see if the bean is in the buffer. Spring only holds the bean as a singleton  Object sharedInstance = getSingleton(beanName);  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);  } ..Copy the code

Because this method is too long, we cut it out and we go through it step by step

TransformedBeanName (name) converts our name to the real beanName, because the argument we pass in May bean alias or may be the beanName of a factoryBean (prefixed with &), The beanName of the factoryBean we store in Spring does not have an & prefix, so we need to get rid of that prefix

protected String transformedBeanName(String name) {
   return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}
public static String transformedBeanName(String name) {
  Assert.notNull(name, "'name' must not be null");
 // Whether it is a factory bean, if not, return it directly  if(! name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) { return name;  }  // If so, remove the & prefix  return transformedBeanNameCache.computeIfAbsent(name, beanName -> {  do {  beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());  }  while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));  return beanName;  });  }  / * ** Find the final bean Name for this alias, if none (* The name of the bean is the name of the bean. * * /  public String canonicalName(String name) {  String canonicalName = name;  // Handle aliasing...  String resolvedName;  do {  resolvedName = this.aliasMap.get(canonicalName);  if(resolvedName ! =null) {  canonicalName = resolvedName;  }  }  while(resolvedName ! =null);  return canonicalName;  } Copy the code

Let’s look again at the next method DefaultSingletonBeanRegistry# getSingleton (String)

public Object getSingleton(String beanName) {
   // allowEarlyReference allows early dependencies
   return getSingleton(beanName, true);
}
Copy the code
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {

   Object singletonObject = this.singletonObjects.get(beanName);
   // This bean is in the creation phase
 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {  // Concurrency control  synchronized (this.singletonObjects) {  // Whether the singleton cache exists  singletonObject = this.earlySingletonObjects.get(beanName);  // Whether to run the bean created by the GetBean Factory  if (singletonObject == null && allowEarlyReference) {  // Get the ObjectFactory in the cache ObjectFactory<? > singletonFactory =this.singletonFactories.get(beanName);  if(singletonFactory ! =null) {  singletonObject = singletonFactory.getObject();  // Cache the object in earlySingletonObject  this.earlySingletonObjects.put(beanName, singletonObject);  // Remove from the factory buffer  this.singletonFactories.remove(beanName);  }  }  }  }  return singletonObject; } Copy the code

The above code is Spring’s attempt to load a singleton from the cache. The singleton is created only once in the same Spring container, and then the bean is fetched directly from the cache.

Under the introduce this method, we first DefaultSingletonBeanRegistry the member variables in a class

  • Map<String, Object> singletonObjectsThe key is beanName and the value is the bean instance
  • Map<String, ObjectFactory<? >> singletonFactoriesKey is beanName and value is the factory where the bean was created
  • Map<String, Object> earlySingletonObjectsKey is beanName and value is bean. But andsingletonObjectsThe difference is that beans are added toearlySingletonObjectsWhile the bean is still in a state of creation, the purpose is simple, and Spring is used to solve the loop dependency in some scenarios

Let’s go back to the code and examine its logic

  1. Try to get beans from singletonObjects, where they are already created, and it would be nice to know that from there
  2. If not, we need to determine whether the beanName bean is being created
  3. If so, let’s see if the bean we are creating has been exposed. If not, let’s see if our parameters are allowed to rely on the earlier bean.
  4. If early dependencies are allowed, we try to get the corresponding bean from ObjectFactory, put it into earlySingletonObjects, and remove it from singletonFactories

A design similar to multi-level caching

We see in the above methods isSingletonCurrentlyInCreation (beanName) this method,

public boolean isSingletonCurrentlyInCreation(String beanName) {
   return this.singletonsCurrentlyInCreation.contains(beanName);
}
Copy the code

SingletonsCurrentlyInCreation this Set, when creating a beans before its corresponding beanName placed in the Set, the analysis in the later involves, here first a mouth

debug

It is normal for us to get the bean the first time and return null

So let’s say we getBean twice in our code

defaultListableBeanFactory.getBean("com.demo.data.Person#0")
defaultListableBeanFactory.getBean("com.demo.data.Person#0")
Copy the code

The value returned for the second call is not null

 Object sharedInstance = getSingleton(beanName);
   if(sharedInstance ! =null && args == null) {
      // 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);
   }
Copy the code

So let’s just assume that sharedInstance is not null which is the second time we call getBean, and we’re going into the getObjectForBeanInstance method

protected Object getObjectForBeanInstance(
      Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

    // We want a factory bean
   if (BeanFactoryUtils.isFactoryDereference(name)) {
  // If this is of type NullBean, this is a NULL instance  if (beanInstance instanceof NullBean) {  return beanInstance;  }  // Get beanInstance is not a factory, but your tm name has this & is confused  if(! (beanInstanceinstanceof FactoryBean)) {  throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());  }   if(mbd ! =null) {  mbd.isFactoryBean = true;  }  return beanInstance;  }   // Wang doesn't want the factory bean, and Spring finds a normal bean for him, and returns it directly  if(! (beanInstanceinstanceof FactoryBean)) {  return beanInstance;  }   Spring needs to do some extra processing to return a normal bean to Wang  Object object = null;   if(mbd ! =null) {  mbd.isFactoryBean = true;  } else {  // See if there is any in the cache  object = getCachedObjectForFactoryBean(beanName);  }   // If the bean factory does not exist  if (object == null) {  // Return bean instance from factory. FactoryBean<? > factory = (FactoryBean<? >) beanInstance; // Fetch the bean Definition from the cache  if (mbd == null && containsBeanDefinition(beanName)) {  mbd = getMergedLocalBeanDefinition(beanName);  }  // Whether the bean is user-defined or created by the application itself  booleansynthetic = (mbd ! =null && mbd.isSynthetic());  object = getObjectFromFactoryBean(factory, beanName, ! synthetic); }  return object; } Copy the code

Let’s take a step-by-step look at the code above

  1. We callgetBean(name)If the name is prefixed with &, we want to get a FactoryBean from Spring, Then we need to determine if the beanInstance we get from the cache is a FactoryBean and return it if it is and throw an exception if it is not
  2. We want a non-FactoryBean and find a non-FactoryBean bean in the Spring container, so return it
  3. We want a non-FactoryBean, but if we find a Bean of factoryBean in the Spring container, we need to entergetObjectFromFactoryBeanThe method of
protected Object getObjectFromFactoryBean(FactoryBean<? > factory, String beanName,boolean shouldPostProcess) {
      // It is a singleton and exists in the cache
   if (factory.isSingleton() && containsSingleton(beanName)) {

      synchronized (getSingletonMutex()) {
 // Get the specified bean from the cache (this bean is created from the factory bean)  Object object = this.factoryBeanObjectCache.get(beanName);   if (object == null) {  // Empty gets the object from the Factory bean  object = doGetObjectFromFactoryBean(factory, beanName);  // Fetch from the cache  Object alreadyThere = this.factoryBeanObjectCache.get(beanName);  if(alreadyThere ! =null) {  // It is already stored in the cache, and subsequent operations are not needed  object = alreadyThere;  } else {  // Need to do some post-processing  if (shouldPostProcess) {  // If the bean is being created,  if (isSingletonCurrentlyInCreation(beanName)) {  return object;  }  / / preposition Mainly to the bean to join singletonsCurrentlyInCreation is creating in the queue  beforeSingletonCreation(beanName);  try {  // Postprocess the objects obtained from factoryBean  // The generated object will be exposed to the bean reference and called back to beanPostProcessor  object = postProcessObjectFromFactoryBean(object, beanName);  } catch (Throwable ex) {  throw new BeanCreationException(beanName,  "Post-processing of FactoryBean's singleton object failed", ex);  } finally {  / / post processing From the singletonsCurrentlyInCreation removed  afterSingletonCreation(beanName);  }  }  // His factory bean already exists in the cache, so the beans generated by this factory bean should also be cached  if (containsSingleton(beanName)) {  this.factoryBeanObjectCache.put(beanName, object);  }  }  }   return object;  }  } else {  / / the singleton  Object object = doGetObjectFromFactoryBean(factory, beanName);   if (shouldPostProcess) {  try {  //  object = postProcessObjectFromFactoryBean(object, beanName);  } catch (Throwable ex) {  throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);  }  }  return object;  } } Copy the code

Ahh ahh ahh ahh, the code is very long long………. Let’s break it down a little bit

  1. The first step is to determine whether the factoryBean singleton, if not, and is the user’s own definition of beans, you need to call postProcessObjectFromFactoryBean method to do a follow-up processing

    1. The final callback is an interface we often useBeanPostProcessor
    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
        throws BeansException {
    
       Object result = existingBean;
       for (BeanPostProcessor processor : getBeanPostProcessors()) {
     Object current = processor.postProcessAfterInitialization(result, beanName);  if (current == null) {  return result;  }  result = current;  }  return result; } Copy the code
  2. If this beanFactory is a singleton, let’s look at factoryBeanObjectCache. BeanName beanName beanName beanName beanName beanName beanName beanName beanName beanName

  3. If they exist, and he returned directly, if it doesn’t exist, then doGetObjectFromFactoryBean, from this method used in FactoryBean# getObject produce beans

  4. In fact, the following code is really confusing

    Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
    if(alreadyThere ! =null) {
    // It is already stored in the cache, and subsequent operations are not needed
    object = alreadyThere;
    }
    Copy the code
  5. Then this method, we see beforeSingletonCreation is getSingleton above isSingletonCurrentlyInCreation judge whether a bean in is creating

    protected void beforeSingletonCreation(String beanName) {
      if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
       throw new BeanCurrentlyInCreationException(beanName);
      }
     }
     public boolean isSingletonCurrentlyInCreation(String beanName) {  return this.singletonsCurrentlyInCreation.contains(beanName);  } Copy the code
  6. Then call to postProcessObjectFromFactoryBean method, finally the callback is our commonly used a BeanPostProcessor interface

  7. It is best to call the afterSingletonCreation(beanName) method, remove it from the collection of beans being created, and last but not least, add it to the factoryBeanObjectCache collection

Today we first analysis here, the subsequent words we continue to discuss in the later articles. Today we looked at the three methods in getBean

image-20200530152154079

conclusion

  • Find the corresponding beanName based on the name in the parameter, whether the name is an alias or the beanName of a factoryBean
  • Check to see if the beanName object is in the cache
    • 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 a bean in the cache, we still need to deal with that bean
    • If the bean returned from the Spring cache is a factoryBean, and the user also wants a beanFactory (the prefix in the name argument is&), so we go straight back
    • If the bean returned from the Spring cache is a normal bean, and the user wants a normal bean, it will be returned
    • If the bean returned from the Spring cache is a factoryBean, and the user wants a plain bean, then we get the bean from the factoryBean
    • The process of getting this bean from a factoryBean calls pre-processing, post-processing, and the usual interface callbacksBeanPostProcessor

The above three methods are generally such a process, I hope to help you

Interested in group chat, communication and paddling together

image-20200530170736820
Surely this time?