Spring's getBean process (source level explanation + core process summary)

Process of getBean

Source code

@Override
public <T> T getBean(String name, Class<T> requiredType) throws BeansException { 
    // getBean doesn't actually do anything. It's all in doGetBean
	return doGetBean(name, requiredType, null, false);
}
protected <T> T doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
		throws BeansException {     
    
    // Process alias BeanName, process factory BeanName with & character
	final String beanName = transformedBeanName(name);
	Object bean;  

	// First, try to get the Bean instance from the cache. This location is the way for the L3 cache to solve the circular dependency
	Object sharedInstance = getSingleton(beanName);   

	if (sharedInstance != null && args == null) {
		if (logger.isDebugEnabled()) {
			if (isSingletonCurrentlyInCreation(beanName)) {
				logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
						"' that is not fully initialized yet - a consequence of a circular reference");
			}
			else {
				logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
			}
		}        
        
        // 1. If sharedInstance is an ordinary Bean instance, the following method will directly return
        // 2. If sharedInstance is a factory Bean type, you need to obtain the getObject method. You can refer to the implementation class of FactoryBean 
		bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
	}
	else {
		
        // There are three types of circular dependencies: setter injection, multi instance and constructor. Spring can only solve setter injection, so Prototype will throw an exception
		if (isPrototypeCurrentlyInCreation(beanName)) {
			throw new BeanCurrentlyInCreationException(beanName);
		}    

		// 1. Parent bean factory exists
        // 2. If the current bean does not exist in the current bean factory, go to the parent factory to find the bean instance
		BeanFactory parentBeanFactory = getParentBeanFactory();
		if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
			// Get the beanName corresponding to name. If the name starts with &, return & + beanName
			String nameToLookup = originalBeanName(name);         
            
            // Depending on whether the args parameter is empty, call different parent container methods to obtain the bean instance
			if (args != null) {
				return (T) parentBeanFactory.getBean(nameToLookup, args);
			}
			else {
				return parentBeanFactory.getBean(nameToLookup, requiredType);
			}
		}       

        // 1. typeCheckOnly is used to judge whether only type checking is performed when calling getBean method
        // 2. If you don't just check the type, you will call markBeanAsCreated to record
		if (!typeCheckOnly) {
			markBeanAsCreated(beanName);
		}
		try {    
    
            // Get the GenericBeanDefinition corresponding to beanName from the container getMergedLocalBeanDefinition and convert it to RootBeanDefinition
			final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); 
            // Check whether the currently created bean definition is an abstract bean definition
			checkMergedBeanDefinition(mbd, beanName, args);
			
            // Handle dependency creation bean instances that use the dependencies on annotation
			String[] dependsOn = mbd.getDependsOn();
			if (dependsOn != null) {
				for (String dep : dependsOn) {   
                    // Monitor whether there are dependencies on circular dependencies. If so, an exception will be thrown
					if (isDependent(beanName, dep)) {
						throw new BeanCreationException(mbd.getResourceDescription(), beanName,
								"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
					}       
                    
                    // Register dependency records
					registerDependentBean(dep, beanName);
					try {    
					    // Load dependencies on dependencies (dep is short for dependencies on)
						getBean(dep);
					}
					catch (NoSuchBeanDefinitionException ex) {
						throw new BeanCreationException(mbd.getResourceDescription(), beanName,
								"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
					}
				}
			}  

			// Create a singleton bean instance
			if (mbd.isSingleton()) {    

			    // Pass the beanName and new ObjectFactory anonymous inner classes into the callback
				sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
					@Override
					public Object getObject() throws BeansException {
						try {    
                            // Create bean
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							// Destroy if creation fails
							destroySingleton(beanName);
							throw ex;
						}
					}
				});
				bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
			}      
            // Create other types of bean instances
			else if (mbd.isPrototype()) {
				// It's a prototype -> create a new instance.
				Object prototypeInstance = null;
				try {
					beforePrototypeCreation(beanName);
					prototypeInstance = createBean(beanName, mbd, args);
				}
				finally {
					afterPrototypeCreation(beanName);
				}
				bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
			}
			else {
				String scopeName = mbd.getScope();
				final Scope scope = this.scopes.get(scopeName);
				if (scope == null) {
					throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
				}
				try {
					Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
						@Override
						public Object getObject() throws BeansException {
							beforePrototypeCreation(beanName);
							try {
								return createBean(beanName, mbd, args);
							}
							finally {
								afterPrototypeCreation(beanName);
							}
						}
					});
					bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
				}
				catch (IllegalStateException ex) {
					throw new BeanCreationException(beanName,
							"Scope '" + scopeName + "' is not active for the current thread; consider " +
							"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
							ex);
				}
			}
		}
		catch (BeansException ex) {
			cleanupAfterBeanCreationFailure(beanName);
			throw ex;
		}
	}
	// If type conversion is required, operations will be performed here
	if (requiredType != null && bean != null && !requiredType.isInstance(bean)) {
		try {
			return getTypeConverter().convertIfNecessary(bean, requiredType);
		}
		catch (TypeMismatchException ex) {
			if (logger.isDebugEnabled()) {
				logger.debug("Failed to convert bean '" + name + "' to required type '" +
						ClassUtils.getQualifiedName(requiredType) + "'", ex);
			}
			throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
		}
	}    

    // Return Bean
	return (T) bean;
}

Several important functions:

	protected String transformedBeanName(String name) {
		//If beanName starts with &, intercept the beginning&
		return canonicalName(BeanFactoryUtils.transformedBeanName(name));
	}
	public String canonicalName(String name) {
		String canonicalName = name;
		// Loop to get the alias until you get the real beanName
		//aliasA->aliasB->beanName
		String resolvedName;
		do {
			resolvedName = this.aliasMap.get(canonicalName);
			if (resolvedName != null) {
				canonicalName = resolvedName;
			}
		}
		while (resolvedName != null);
		return canonicalName;
	}
 
	protected Object getObjectForBeanInstance(
			Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {
 
		//If the name starts with & but is not a FactoryBean, an exception is thrown directly
		if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
			throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
		}
 
		// beanInstance may be an ordinary bean or a FactoryBean
		// If it is an ordinary bean, it returns directly
		if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
			return beanInstance;
		}
 
		//FactoryBean creates a bean instance and returns
		Object object = null;
		if (mbd == null) {
			object = getCachedObjectForFactoryBean(beanName);
		}
		if (object == null) {
			FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
			if (mbd == null && containsBeanDefinition(beanName)) {
				mbd = getMergedLocalBeanDefinition(beanName);
			}
			boolean synthetic = (mbd != null && mbd.isSynthetic());
			object = getObjectFromFactoryBean(factory, beanName, !synthetic);
		}
		return object;
	}

Process summary

  1. Calling the transformatedbeanname function to process the Name of a Bean has two main purposes: one is to process the Name of a FactoryBean (usually a & symbol is added before the Name of a FactoryBean to distinguish it from the BeanName of an ordinary Bean, but there is no difference in storage); the other is to process aliases and replace them with real beannames.
  2. Call the getSingleton function to search the Bean to be found from the L3 cache:
    1. If it is not null, it indicates that it has been created or is being created (circular dependency is solved here). At this time, if the obtained Bean is an ordinary Bean, it will return directly and the getBean process ends. If the obtained FactoryBean is a FactoryBean, it will first find out whether the FactoryBean corresponding to the BeanName has produced objects in the cache. If so, it will return directly and the getBean process ends; If the object cannot be obtained from the cache, it means that the FactoryBean has not produced an object, then the getObject method of the FactoryBean will be called to produce a Bean, and then the Bean created by the FactoryBean will be added to the cache (Note: the cache here is not any of the L3 cache, Instead, a cache dedicated to storing factory beans, that is, beans created by factorybeans, is located in FactoryBeanRegistrySupport, that is, factorybeans are stored in the level-3 cache, and the created beans are not in the level-3 cache, and the two are not cached in the same place). Then, the produced beans are returned and the getBean process ends.
    2. If you get null from the cache, if the parent factory of the current factory exists and there is no definition information of the Bean in the current factory, you will go to the parent factory to perform the getBean operation (at this time, if you create an object, it will also be saved in the parent factory), and then directly return the result of the getBean of the parent factory, and the getBean process ends. If there is no parent factory or the definition information of the Bean exists in the current factory, the Bean will be created: first, obtain the definition information of the Bean. Then process the beans specified by the Bean through the @ DependsOn annotation (the annotation can specify the loading order of beans and load the specified beans first), traverse the names of these specified beans, and call the getBean function to load these beans. Then execute the beforeSingletonCreation method to mark the Bean as being created; Then it will execute the logic of the createBean method (see the following for detailed analysis: Process of creating Bean ); Execute the afterSingletonCreation method to remove the bean from the state being created; Then the addSingleton method is called to add the Bean to the first level cache and delete its contents in the two or three level cache. After the bean is created, the getObjectForBeanInstance method will be executed. The function is to obtain the object to be created by the FactoryBean if the bean is of FactoryBean type, and then take it as the final object and return it (Note: the creation logic here is that of a single instance bean).

Tags: Java Spring

Posted on Fri, 03 Sep 2021 21:19:43 -0400 by dksmarte