Spring Bean creation process

1. Entry created by bean

  • There is this in the refresh() method of AbstractApplicationContext Finishbeanfactoryinitialization (beanfactory) method

  • In this method, the preInstantiateSingletons() method of DefaultListableBeanFactory is invoked, which initializes all non delayed loading Bean instances.

  • The preInstantiateSingletons() method will call the getBean() method to create an example

    if (isEagerInit) {
        this.getBean(beanName);
    }
    
  • The creation of Bean by calling the doGetBean() method of AbstractBeanFactory in the getBean() method.

    public Object getBean(String name) throws BeansException {
        return this.doGetBean(name, (Class)null, (Object[])null, false);
    }
    
  • An anonymous inner class is used in the doGetBean() method to create a Bean

    // Here, an anonymous inner class is used to create a Bean instance object and register it with the dependent object
    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.
    		// Explicitly delete the bean instance from the singleton cache
    		// In the singleton mode, in order to solve the circular dependency, it may already exist, so it is destroyed
    		destroySingleton(beanName);
    		throw ex;
    	}
    });
    
  • AbstractAutowireCapableBeanFactory implements the createBean() method, which provides automatic assembly services

  • doCreateBean() is found in the createBean() method, which is the entry to create the Bean

    Object beanInstance = doCreateBean(beanName, mbdToUse, args);
    

2 learning route

3 doGetBean()

  • AbstractBeanFactory provides the doGetBean() method, which can perform different processing according to the Scope. Here we mainly study Singleton
  • For the circular dependency of prototype, spring has no solution and throws an exception directly

3.1 trying to get a Bean from the cache

3.1. 1 get beanname - transformatedbeanname (name)

3.1. 2 try to get Bean instance - getSingleton()

  • doGetBean() calls the getSingleton() method of the DefaultSingletonBeanRegistry class to try to get the singleton instance from the cache
  • This method is in DefaultSingletonBeanRegistry, which involves three-level cache
    • Bean s that inject attributes are stored in singletonObjects
    • Incomplete beans are stored in earlySingletonObjects or singletonFactories
    /** Cache of singleton objects: bean name to bean instance. */
    //L1 cache: singleton object cache pool, beanname - > bean, which stores singleton objects after instantiation and successful attribute assignment
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    
    /** Cache of singleton factories: bean name to ObjectFactory. */
    //Level 3 cache: the cache of a single instance factory, beanname - > objectfactory. When it is added, the instance does not have attributes
    // It is used to save the relationship map between the beanName and the Factory that created the bean. The Factory that was prematurely exposed at the beginning of the creation of the singleton bean,
    // The reason why the factory method is adopted is that some beans need to be represented. It is meaningless to expose the information before the agent
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
    
    /** Cache of early singleton objects: bean name to bean instance. */
    //L2 cache: the early singleton object, beanname - > bean, which stores the singleton object whose attribute is not assigned after instantiation
    // The bean produced by the factory method is executed. After the bean is put in,
    // Then, when the bean is created, it can be obtained through the getBean method
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
    

Process analysis

  • First, try to get the Bean instance from the cache and singletonObjects. It must not be found
    • The ConcurrentHashMap stores the beans of the final form (filled with attributes)
    //Try to get a complete Bean from the L1 cache, and the attribute has been injected
    Object singletonObject = this.singletonObjects.get(beanName);
    
  • At this time, singletonObject == null, and then judge whether issingletoncurrentyincreation (beanname), that is, whether it is a singleton Bean instance being created. Because the method is executed without judging the Scope attribute of the Bean, it is not false here, and if is skipped
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {}
    
  • Finally, null is returned
3.1. 2.2 L3 cache

if process analysis

  • If the Bean is singleton and is being created, synchronize the lock on singleObjects

    • After locking, it is safe to operate L2 cache and L3 cache, so both L2 cache and L3 cache use HashMap
    synchronized (this.singletonObjects)
    
  • Try to get the Bean instance from the L2 cache earlySingletonObjects, which stores the Bean instance that has not yet been added

    singletonObject = this.earlySingletonObjects.get(beanName);
    
  • If it is obtained, it will not enter if and return directly

    if (singletonObject == null && allowEarlyReference)
    
  • If it is not obtained and allowrearlyreference = true, enter if and try to obtain the singleton factory instance that created this Bean from the L3 cache singletonFactories according to beanName

    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
    
    • ObjectFactory is a function interface. There is only one getObject() method in it. The subsequent sequence can call getObject() to create Bean instances
    • The purpose of not using FactoryBean is to distinguish users from frameworks. FactoryBean has its own processing logic
  • If the object factory instance corresponding to the singleton is not obtained, null is returned

  • After obtaining the object, the getObject() method of the obtained object factory instance will be called to create the Bean instance. Since the Bean property may not be injected at this time, it will be put into the secondary cache earlySingletonObjects first. In order to ensure that only one level in the L3 cache has Bean instances, the object factory instances in the L3 cache will be removed after the Bean instances are created

    if (singletonFactory != null) {
    	//Call the getObject method of the singleton factory to return the object instance
    	singletonObject = singletonFactory.getObject();
    	//Put the instance into the L2 cache
    	this.earlySingletonObjects.put(beanName, singletonObject);
    	//Remove from L3 cache
    	// Ensure that only one layer of the three-tier cache consists of beans, that is, singleton
    	this.singletonFactories.remove(beanName);
    }
    

3.1.3 getObjectForBeanInstance() - TODO

  • Listen again 8 - 2
  • The function of this method is to judge whether it is FactoryBean
    • If it is an ordinary bean, it returns directly. If it is a FactoryBean, it returns the bean instance generated by its getObject
    // If an instance of a singleton Bean has been created previously, and the parameter passed in by the called getBean method is null
    // Then execute the logic in if
    // Args is required to be null because if there is args, further assignment is required, so it cannot be returned directly
    if (sharedInstance != null && args == null) {
    	if (logger.isTraceEnabled()) {
    		//If the Bean is still being created, it indicates a circular reference
    		if (isSingletonCurrentlyInCreation(beanName)) {
    			logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
    					"' that is not fully initialized yet - a consequence of a circular reference");
    		}
    		else {
    			logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
    		}
    	}
    	// The function of this method is to judge whether it is FactoryBean
    	// If it is an ordinary bean, it returns directly. If it is a FactoryBean, it returns the bean instance generated by its getObject
    	bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }
    

3.2 judgment of prototype circular dependency

  • 3.1 describes the logic of obtaining beans from the cache. This section explains that the scope is not a singleton (a prototype) or the scope is a signature, but the Bean is not created (the value cannot be obtained from the cache)

3.2.1 isPrototypeCurrentlyInCreation()

  • If the scope is prototype and the display is still being created, it is basically a circular dependency

    • For the circular dependency of prototype, spring has no solution and directly throws a beancurrentyincreationexception exception. The circular dependency of prototype is not supported by spring
    if (isPrototypeCurrentlyInCreation(beanName)) {
    	throw new BeanCurrentlyInCreationException(beanName);
    }
    
  • This method attempts to get from the prototypesCurrentlyInCreation

    Object curVal = this.prototypesCurrentlyInCreation.get();
    
    • prototypesCurrentlyInCreation is of ThreadLocal type, that is, only Bean instances with a Scope of prototype created by this thread will be placed in the name list of beans being created
      /** Names of beans that are currently in creation. */
      private final ThreadLocal<Object> prototypesCurrentlyInCreation =
      		new NamedThreadLocal<>("Prototype beans currently in creation");
      

3.3 returning to parent container to obtain Bean instance - TODO

  • If it is not the case since the loop, return to the parent container and try to get the Bean

  • Get parent container

    BeanFactory parentBeanFactory = getParentBeanFactory();
    
  • Recursively trying to get a Bean from the parent container

    //If the parent container is still an instance of AbstractBeanFactory
    //instanceof returns a Boolean value to indicate whether the object is an instance of the specific class or its subclass
    if (parentBeanFactory instanceof AbstractBeanFactory) {
    	//Direct recursive call method to find
    	return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
    			nameToLookup, requiredType, args, typeCheckOnly);
    }
    

3.4 merge beandefinition of subclasses and parent classes

  • Merge and overwrite the parent class's BeanDefinition with the child class's BeanDefinition

    //Merge and overwrite the parent class's BeanDefinition with the child class's BeanDefinition
    final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
    
    • Try to get the merged beandefinition from mergedBeanDefinitions
      • Get direct return
      • Can't get. Merge again
    protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
    	// Quick check on the concurrent map first, with minimal locking.
    	RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
    	if (mbd != null && !mbd.stale) {
    		return mbd;
    	}
    	return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
    }
    
  • Verify the merged BeanDefinition, mainly depending on whether the attribute is abstract

    checkMergedBeanDefinition(mbd, beanName, args);
    

3.5 recursive instantiation to display dependent beans - dependencies on

Dependencies on is used to specify the order of Bean initialization and destruction

<bean id=a Class="com.njupt.A" depends-on="b" />
<bean id=b Class="com.njupt.B" />
  • Note: Spring does not support defining displayed circular dependencies
    <bean id="beanA" class="BeanA" depends-on="beanB">
    <bean id="beanB" class="BeanB" depends-on="beanA">
    
    • If yes, an exception will be thrown directly
    if (isDependent(beanName, dep)) {
    	throw new BeanCreationException(mbd.getResourceDescription(), beanName,
    			"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
    }
    

Execution process

  • Find out whether there is a dependent on from the obtained beandefinition

    String[] dependsOn = mbd.getDependsOn();
    
  • Throw an exception if there is a circular dependency displayed

    if (isDependent(beanName, dep)) {
    	throw new BeanCreationException(mbd.getResourceDescription(), beanName,
    			"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
    }
    
  • If there are dependencies on and it is not a circular dependency, the dependent Bean will be registered first

    registerDependentBean(dep, beanName);
    
    public void registerDependentBean(String beanName, String dependentBeanName) {
    	// Get the real name of the Bean
    	String canonicalName = canonicalName(beanName);
    	// dual registration
    	synchronized (this.dependentBeanMap) {
    		/*
    		1.Map<String, Set<String>> dependentBeanMap: A collection of beans that hold the referenced beans of the current Bean
    			String: The name of the dependent Bean
    			Set<String>: value All beans that depend on the bean
    			  For example, the name of the Bean to be instantiated is userinfo, and there is a human attribute in userinfo,
    		      Then there is the relationship human=[userInfo] referenced by userInfo
    	    2.computeIfAbsent:If the value corresponding to the key is empty, the returned value of the second parameter will be stored and returned
    		 */
    		Set<String> dependentBeans =
    				this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8));
    		if (!dependentBeans.add(dependentBeanName)) {
    			return;
    		}
    	}
    
    	synchronized (this.dependenciesForBeanMap) {
    		// Dependencies forbeanmap stores the collection of beans on which the current Bean depends
    		Set<String> dependenciesForBean =
    				this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8));
    		dependenciesForBean.add(canonicalName);
    	}
    }
    
  • Recursively call the get Bean method to register dependencies between beans

    // Recursively call the get Bean method to register the dependencies between beans (for example, C needs to be initialized later than B, and B needs to be initialized later than A)
    // Initialize dependent bean s
    getBean(dep);
    

3.6 create Bean instances for different scopes

3.6.1 singleton

3.6. 1.1 overall process analysis
  • Here, an anonymous inner class is used to create a Bean instance object and register it with the dependent object
    if (mbd.isSingleton()) {
    	// Here, an anonymous inner class is used to create a Bean instance object and register it with the dependent object
    	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.
    			// Explicitly delete the bean instance from the singleton cache
    			// In the singleton mode, in order to solve the circular dependency, it may already exist, so it is destroyed
    			destroySingleton(beanName);
    			throw ex;
    		}
    	});
    	// If it is an ordinary bean, it returns directly. If it is a FactoryBean, it returns its getObject
    	bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }
    

getSingleton() method flow

  • getSingleton() is implemented by the DefaultSingletonBeanRegistry class. It is found that the anonymous inner class is also ObjectFactory

    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {}
    
    • First try to get the Bean from the L1 cache

      Object singletonObject = this.singletonObjects.get(beanName);
      
  • If not, judge whether the container is destroying the single instance. If not, start the real creation

  • Call beforeSingletonCreation(beanName) to add the current beanName to the list of beans being created

    this.singletonsCurrentlyInCreation.add(beanName)
    
  • Call getObject of ObjectFactory to create the Bean, that is, the anonymous inner class at the beginning. After creation, set newSingleton to true

    singletonObject = singletonFactory.getObject();
    newSingleton = true;
    
    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.
    		// Explicitly delete the bean instance from the singleton cache
    		// In the singleton mode, in order to solve the circular dependency, it may already exist, so it is destroyed
    		destroySingleton(beanName);
    		throw ex;
    	}
    });
    
  • Call afterSingletonCreation(beanName); Remove beanName from the list of names being created

    this.singletonsCurrentlyInCreation.remove(beanName)
    
  • Call the addSingleton(beanName, singletonObject) method

    • Add the newly created beans to the L1 cache and remove the beans from the L2 and L3 cache
    • Add beanName to the list of singleton beans
    protected void addSingleton(String beanName, Object singletonObject) {
    	synchronized (this.singletonObjects) {
    		//After the Bean instance is created, only the first level cache and the order of registering beanName are retained, and the rest are cleared
    		this.singletonObjects.put(beanName, singletonObject);
    		this.singletonFactories.remove(beanName);
    		this.earlySingletonObjects.remove(beanName);
    		this.registeredSingletons.add(beanName);
    	}
    }
    
  • Finally, the Bean instance is returned

getObjectForBeanInstance()

  • Calling this method returns the bean instance itself. If it is an ordinary bean, it returns directly. If it is a FactoryBean, it returns its getObject
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    

3.6.2 prototype

  • First, register the beanName of the current Bean into the name list of the Bean whose type is threadloadcl, indicating the prototype being created by the current thread, so as to prevent circular dependency

    beforePrototypeCreation(beanName);
    
    • This explains why we judge whether a Bean is being created at the beginning
    if (isPrototypeCurrentlyInCreation(beanName)) {
    	throw new BeanCurrentlyInCreationException(beanName);
    }
    
  • Call the createBean() method to create an object

    prototypeInstance = createBean(beanName, mbd, args);
    
  • Clearly save the cache of beanName

    // The default function is to erase the previously registered Bean information being created
    afterPrototypeCreation(beanName);
    
  • Get the created Bean instance or the Bean instance created by BeanFactory

    bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
    

3.6.3 !singleton && !prototype

  • The Bean to be created is neither a singleton pattern nor a prototype pattern. Select the appropriate method to instantiate the Bean according to the life cycle range configured in the Bean definition resource. This is commonly used in Web applications, such as request, session, application and other life cycles
    • Request: create a new instance for each request
    • Session: the same Bean instance is used within the validity period of the session
  • Calling createBean() method in anonymous parameter
    Object scopedInstance = scope.get(beanName, () -> {
    	beforePrototypeCreation(beanName);
    	try {
    		return createBean(beanName, mbd, args);
    	}
    	finally {
    		afterPrototypeCreation(beanName);
    	}
    });
    
  • The anonymous parameter implements the getObject() logic of the ObjectFactory interface
    Object get(String name, ObjectFactory<?> objectFactory);
    

3.7 type check the created Bean

supplement

Why consider that beans may have been created

  • Because the container obtains bean instances from the container through the getBean method, some beans are created when the container is initialized, and some are loaded late.
  • For example, one of our Service classes was created in advance by the container through the getBean method when the container was initialized. Under one of our controller classes, if we have the Service class as a member variable and mark it with the @ Autowired tag, and the controller class is set to delay loading. At this time, the controller class will create the corresponding Bean instance only when it is called for the first time. During the creation process, getBean will be called to obtain the Bean of the Service. At this time, the Bean of the Service can be directly returned from the container cache because it has been created

Keywords: Java Spring Singleton pattern

Added by jandrews3 on Thu, 23 Dec 2021 17:36:27 +0200