In the previous article on loading Spring Ioc beans (I), we looked at the doGetBean() method of loading Spring Ioc beans in 2.2 fetching a singleton Bean from the cache and 2.3 fetching the final Bean instance object. We then looked at the remaining steps. Directly on the code:

// The ability to actually fetch beans from the IOC container is where dependency injection is triggered
	protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

		// Get the name of the managed Bean with the specified name, stripping container dependencies from the specified name
		// If an alias is specified, the alias is converted to the canonical Bean name
<1>		final String beanName = transformedBeanName(name);
		Object bean;

		// Eagerly check singleton cache for manually registered singletons.
		// Get the created singleton Bean from the cache
<2>		Object sharedInstance = getSingleton(beanName);
		// If there is one in the cache
		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 + "'"); }}// Note: BeanFactory is the factory that manages beans in the container
			// FactoryBeans are factory beans that create objects. There is a difference between the two

			// Gets the instance object of the given Bean, either the Bean instance itself or the Bean object created by FactoryBean
			SharedInstance = sharedInstance = sharedInstance;
<3>			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}

		else {
			// Fail if we're already creating this bean instance:
			// We're assumably within a circular reference.
			// Because Spring only addresses loop dependencies in singleton mode, in prototype mode an exception is thrown if a loop dependency exists.
<4>			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}

			// Check if bean definition exists in this factory.
			// Check if there is a BeanDefinition with the specified name in the IOC container
			// Can get the desired Bean from the current BeanFactory, if not delegate to the current container
			// Find the parent of the container. If the parent cannot be found, find the parent of the container along the inheritance system of the container
			BeanFactory parentBeanFactory = getParentBeanFactory();
			// The parent of the current container exists, and the specified Bean does not exist in the current container
			if(parentBeanFactory ! =null && !containsBeanDefinition(beanName)) {
				// Not found -> check parent.
				// Resolve the original name of the specified Bean name
				String nameToLookup = originalBeanName(name);
				// If it is an AbstractBeanFactory type, delegate to the parent class
				if (parentBeanFactory instanceof AbstractBeanFactory) {
					return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
							nameToLookup, requiredType, args, typeCheckOnly);
				}
				else if(args ! =null) {
					// Delegation to parent with explicit args.
					Delegate the parent container to look up by the specified name and explicit arguments
					return (T) parentBeanFactory.getBean(nameToLookup, args);
				}
				else {
					// No args -> delegate to standard getBean method.
					Delegate the parent container to look up the specified name and type
					returnparentBeanFactory.getBean(nameToLookup, requiredType); }}// Whether the created Bean requires type validation
<5>			if(! typeCheckOnly) {// The Bean specified to the container tag has been created
				markBeanAsCreated(beanName);
			}

			try {
				// Get the corresponding GenericBeanDefinition object from the container and convert it to a RootBeanDefinition object
				// The main solution is to merge the public attributes of the parent class when the Bean is inherited
<6>				final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				// Check the given merged BeanDefinition.
				checkMergedBeanDefinition(mbd, beanName, args);

				// Guarantee initialization of beans that the current bean depends on.
				$DependsOn(); $DependsOn();
				// Get the names of all the dependent beans of the current Bean
<7>				String[] dependsOn = mbd.getDependsOn();
				// If there are dependencies
				if(dependsOn ! =null) {
					for (String dep : dependsOn) {
						// Verify that the dependency has been registered with the current Bean
						if (isDependent(beanName, dep)) {
							// Registered, exception thrown
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
						}
						// If no, register the dependent bean first
						registerDependentBean(dep, beanName);
						// Recursively call getBean(), which becomes the dependent beangetBean(dep); }}// Create bean instance.
				// Create a singleton Bean
<8>				if (mbd.isSingleton()) {
					// An anonymous inner class is used to create Bean instance objects and register them with dependent objects
					sharedInstance = getSingleton(beanName, () -> {
						try {
							// Create a specified Bean instance object and merge the subclass and superclass definitions if there is parent inheritance
							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 clear instance objects from the container singleton pattern Bean cache
							destroySingleton(beanName);
							throwex; }});// Get the instance object of the given Bean
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

				// Create a multiinstance Bean
				else if (mbd.isPrototype()) {
					// Prototype creates a new object each time
					Object prototypeInstance = null;
					try {
						The default function is to register the currently created prototype object
						beforePrototypeCreation(beanName);
						// Create the specified Bean object instance
						prototypeInstance = createBean(beanName, mbd, args);
					}
					finally {
						The default function tells the IOC container that the prototype object for the specified Bean is no longer created
						afterPrototypeCreation(beanName);
					}
					// Get the instance object of the given Bean
					bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
				}

				// The Bean to be created is neither Singleton nor Prototype
				// For example, the lifecycle of request, Session, and Application
				else {
					String scopeName = mbd.getScope();
					final Scope scope = this.scopes.get(scopeName);
					// The Bean definition is invalid if the lifecycle scope is not configured in the Bean definition resource
					if (scope == null) {
						throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
					}
					try {
						// An anonymous inner class is used to get an instance of the specified lifecycle range
						Object scopedInstance = scope.get(beanName, () -> {
							// preprocessing
							beforePrototypeCreation(beanName);
							try {
								return createBean(beanName, mbd, args);
							}
							finally {
								// post-processingafterPrototypeCreation(beanName); }});// Get the instance object of the given Bean
						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);
				throwex; }}// Check if required type matches the type of the actual bean instance.
		// Type check the created Bean instance object
<9>		if(requiredType ! =null && !requiredType.isInstance(bean)) {
			try {
				T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
				if (convertedBean == null) {
					throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
				}
				return convertedBean;
			}
			catch (TypeMismatchException ex) {
				if (logger.isDebugEnabled()) {
					logger.debug("Failed to convert bean '" + name + "' to required type '" +
							ClassUtils.getQualifiedName(requiredType) + "'", ex);
				}
				throw newBeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); }}return (T) bean;
	}
Copy the code

The code is long and requires some patience, so let’s walk through it step by step:

  • <1> : for specific analysis, see 2.1 Obtaining original beanName

  • <2> : for detailed analysis, see 2.2 Obtaining singleton bean from cache

  • <3> : for detailed analysis, see 2.3 Obtaining the final bean instance object

  • <4> : see 2.4 Prototype Dependency Check and getting Bean from parentBeanFactory for detailed analysis

  • <5> : for detailed analysis, see 2.5 marking bean as created or to be created

  • <6> : For detailed analysis, see 2.6 Obtaining BeanDefinition

  • <7> : for detailed analysis, see 2.7 Bean dependency Processing

  • <8> : for detailed analysis, see 2.8 instantiation of beans with different scopes

  • <9> : for detailed analysis, see 2.9 Type Conversion

2.4. Prototype dependency check and get Bean from parentBeanFactory

Prototype mode dependency check, corresponding code is as follows:

if (isPrototypeCurrentlyInCreation(beanName)) {
	throw new BeanCurrentlyInCreationException(beanName);
	}
Copy the code

Track in:

        /** Names of beans that are currently in creation */
	private final ThreadLocal<Object> prototypesCurrentlyInCreation =
			new NamedThreadLocal<>("Prototype beans currently in creation");

protected boolean isPrototypeCurrentlyInCreation(String beanName) {
		// Retrieve the prototype being created from ThreadLocal
		Object curVal = this.prototypesCurrentlyInCreation.get();
		return(curVal ! =null &&
				(curVal.equals(beanName) || (curVal instanceofSet && ((Set<? >) curVal).contains(beanName)))); }Copy the code

Spring only handles singleton loop dependencies and throws exceptions for prototype loop dependencies. Spring stores the prototype pattern Bean being created into ThreadLoacl, where ThreadLoacl is used to determine whether the current Bean has been created.

Get the Bean from parentBeanFactory as follows:

// Check if bean definition exists in this factory.
	// Check if there is a BeanDefinition with the specified name in the IOC container
	// Can get the desired Bean from the current BeanFactory, if not delegate to the current container
	// Find the parent of the container. If the parent cannot be found, find the parent of the container along the inheritance system of the container
	BeanFactory parentBeanFactory = getParentBeanFactory();
	// The parent of the current container exists, and the specified Bean does not exist in the current container
	if(parentBeanFactory ! =null && !containsBeanDefinition(beanName)) {
		// Not found -> check parent.
		// Resolve the original name of the specified Bean name
		String nameToLookup = originalBeanName(name);
		// If it is an AbstractBeanFactory type, delegate to the parent class
		if (parentBeanFactory instanceof AbstractBeanFactory) {
			return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
					nameToLookup, requiredType, args, typeCheckOnly);
		}
		else if(args ! =null) {
			// Delegation to parent with explicit args.
			Delegate the parent container to look up by the specified name and explicit arguments
			return (T) parentBeanFactory.getBean(nameToLookup, args);
		}
		else {
			// No args -> delegate to standard getBean method.
			Delegate the parent container to look up the specified name and type
			returnparentBeanFactory.getBean(nameToLookup, requiredType); }}Copy the code

If there is no corresponding BeanDefinition object in the current container cache, it tries to load it from the parentBeanFactory, and then recursively calls getBean(…). methods

2.5. Mark the bean as created or about to be created

The corresponding code is as follows:

// Whether the created Bean requires type validation
			if(! typeCheckOnly) {// The Bean specified to the container tag has been created
				markBeanAsCreated(beanName);
			}
Copy the code

TypeCheckOnly is doGetBean(final String Name,@Nullable Final Class requiredType,@Nullable final Object[] args, Boolean typeCheckOnly) a parameter in the method. Generally, this parameter is passed false

Then trace the markBeanAsCreated() method:

protected void markBeanAsCreated(String beanName) {
		// Not created
		if (!this.alreadyCreated.contains(beanName)) {
			synchronized (this.mergedBeanDefinitions) {
				// Double check for DCL
				if (!this.alreadyCreated.contains(beanName)) {
					clearMergedBeanDefinition(beanName);
					// Add to the created bean collection
					this.alreadyCreated.add(beanName); }}}}Copy the code

The familiar double check from the singleton pattern is used here

2.6 get BeanDefinition

The corresponding code is as follows:

        // Get the corresponding GenericBeanDefinition object from the container and convert it to a RootBeanDefinition object
	// The main solution is to merge the public attributes of the parent class when the Bean is inherited
	final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
	// Check the given merged BeanDefinition.
	checkMergedBeanDefinition(mbd, beanName, args);
Copy the code

This code is commented in detail and won’t be explained much.

2.7 bean dependency processing

The corresponding code is as follows:

// Guarantee initialization of beans that the current bean depends on.
	$DependsOn(); $DependsOn();
	// Get the names of all the dependent beans of the current Bean
<1>	String[] dependsOn = mbd.getDependsOn();
	// If there are dependencies
	if(dependsOn ! =null) {
		for (String dep : dependsOn) {
			// Verify that the dependency has been registered with the current Bean
<2>			if (isDependent(beanName, dep)) {
				// Registered, exception thrown
				throw new BeanCreationException(mbd.getResourceDescription(), beanName,
						"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
			}
			// If no, register the dependent bean first
<3>			registerDependentBean(dep, beanName);
			// Recursively call getBean(), which becomes the dependent bean
<4> getBean(dep); }}Copy the code

A @dependson (value = “B”) annotation is available in Spring. If the object A is not loaded until the object B is loaded, A @dependson (value = “B”) annotation is available. The @dependson implementation is based on this code.

  • <1> Call mbd.getDependson () on the BeanDefinition we obtained from the IoC container to obtain all dependencies of the current bean.

  • <2>, iterate over these dependencies to determine if the dependency is registered with the current Bean

  • <3>, if no, the dependent Bean is registered first

  • <4>, recursively call getBean(), which becomes the dependent bean

<2>, iterate over these dependencies to determine if the dependency is registered with the current Bean

Code:

	
	// Save the mapping between the bean and its dependencies: B - > A
	private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);

        // Save the mapping between the bean and its dependencies: A - > B
	private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64);

private boolean isDependent(String beanName, String dependentBeanName, @Nullable Set<String> alreadySeen) {
		if(alreadySeen ! =null && alreadySeen.contains(beanName)) {
			return false;
		}
		// Get the current original beanName
		String canonicalName = canonicalName(beanName);
		// Get a collection of other beans that the bean depends on
		Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
		if (dependentBeans == null) {
			return false;
		}
		If the dependency exists, the dependency is registered with the bean
		if (dependentBeans.contains(dependentBeanName)) {
			return true;
		}
		// Recursively detect dependencies
		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

DependentBeans this code is simple: obtain all dependentBeans for the current bean using a dependentBeanMap, check whether the bean is registered, and recursively check whether the dependent bean has dependencies. If so, recursively call isDependent() to check

<3>, if no, the dependent Bean is registered first

RegisterDependentBean (DEP, beanName) if no dependent Bean is registered to the Bean:

	// Save the mapping between the bean and its dependencies: B - > A
	private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);

        // Save the mapping between the bean and its dependencies: A - > B
	private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64);

// Inject the dependent Bean into the specified Bean
	public void registerDependentBean(String beanName, String dependentBeanName) {
	// A quick check for an existing entry upfront, avoiding synchronization...
	// Get the original beanName
	String canonicalName = canonicalName(beanName);
	Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
	if(dependentBeans ! =null && dependentBeans.contains(dependentBeanName)) {
		return;
	}

	// No entry yet -> fully synchronized manipulation of the dependentBeans Set
	// Find the dependent beans of the given named bean from the container: bean name --> All dependent bean names collection
	synchronized (this.dependentBeanMap) {
		// Get all the dependent Bean names for the Bean with the given name
		dependentBeans = this.dependentBeanMap.get(canonicalName);
		if (dependentBeans == null) {
			// Set the dependency Bean information for the Bean
			dependentBeans = new LinkedHashSet<>(8);
			this.dependentBeanMap.put(canonicalName, dependentBeans);
		}
		// Put the mapping into the collection
		dependentBeans.add(dependentBeanName);
	}
	// Find the dependent beans of the given named bean from the container: bean name --> collection of dependent beans of the specified named bean
	synchronized (this.dependenciesForBeanMap) {
		Set<String> dependenciesForBean = this.dependenciesForBeanMap.get(dependentBeanName);
		if (dependenciesForBean == null) {
			dependenciesForBean = new LinkedHashSet<>(8);
			this.dependenciesForBeanMap.put(dependentBeanName, dependenciesForBean);
		}
		// Put the mapping into the collectiondependenciesForBean.add(canonicalName); }}Copy the code

If A @dependson (value = “B”) registerDependentBean(dep, beanName) registerDependentBean(dep, beanName) registerDependentBean(dep, beanName) registerDependentBean(dep, beanName) What this code does is register the dependencies between beans into two maps.

  • DependentBeanMap deposit (B, A)

  • DependenciesForBeanMap deposit (A, B)

<4>, recursive call getBean(DEP), the dependent bean

At this point, a recursive call to the getBean(beanName) method, doGetBean(beanName), reruns the current process to instantiate the dependent Bean first. After the dependent Bean is instantiated, the current Bean continues execution.

2.8 instantiation of beans of different scopes

Code:

        // Create bean instance.
	// Create a singleton Bean
	if (mbd.isSingleton()) {
		// An anonymous inner class is used to create Bean instance objects and register them with dependent objects
		sharedInstance = getSingleton(beanName, () -> {
			try {
				// Create a specified Bean instance object and merge the subclass and superclass definitions if there is parent inheritance
				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 clear instance objects from the container singleton pattern Bean cache
				destroySingleton(beanName);
				throwex; }});// Get the instance object of the given Bean
		bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
	}

	// Create a multiinstance Bean
	else if (mbd.isPrototype()) {
		// Prototype creates a new object each time
		Object prototypeInstance = null;
		try {
			The default function is to register the currently created prototype object
			beforePrototypeCreation(beanName);
			// Create the specified Bean object instance
			prototypeInstance = createBean(beanName, mbd, args);
		}
		finally {
			The default function tells the IOC container that the prototype object for the specified Bean is no longer created
			afterPrototypeCreation(beanName);
		}
		// Get the instance object of the given Bean
		bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
	}

	// The Bean to be created is neither Singleton nor Prototype
	// For example, the lifecycle of request, Session, and Application
	else {
		String scopeName = mbd.getScope();
		final Scope scope = this.scopes.get(scopeName);
		// The Bean definition is invalid if the lifecycle scope is not configured in the Bean definition resource
		if (scope == null) {
			throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
		}
		try {
			// An anonymous inner class is used to get an instance of the specified lifecycle range
			Object scopedInstance = scope.get(beanName, () -> {
				// preprocessing
				beforePrototypeCreation(beanName);
				try {
					return createBean(beanName, mbd, args);
				}
				finally {
					// post-processingafterPrototypeCreation(beanName); }});// Get the instance object of the given Bean
			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); }}Copy the code

The code is clearly divided into three parts:

  • Instantiate the Singleton Bean

  • Instantiate the Prototype Bean

  • Instantiation of other Bean types (session, Request, etc.)

Let’s start with singleton Bean instantiation:

if (mbd.isSingleton()) {
	// An anonymous inner class is used to create Bean instance objects and register them with dependent objects
	sharedInstance = getSingleton(beanName, () -> {
	try {
		// Create a specified Bean instance object and merge the subclass and superclass definitions if there is parent inheritance
		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 clear instance objects from the container singleton pattern Bean cache
		destroySingleton(beanName);
		throwex; }});// Get the instance object of the given Bean
	bean = getObjectForBeanInstance(sharedInstance, name,beanName, mbd);
	}
Copy the code

The Spring Bean’s scope defaults to Singleton. There are other scopes, such as Prototype, Request, Session, etc. Different scopes have different initialization strategies. See Spring Ioc’s Bean loading (3) : Bean creation for each scope.

2.9. Type conversion

Code:

// Check if required type matches the type of the actual bean instance.
	// Type check the created Bean instance object
	if(requiredType ! =null && !requiredType.isInstance(bean)) {
	try {
	        // Perform the conversion
		T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
		// Failed to convert, throw exception
		if (convertedBean == null) {
			throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
		}
		return convertedBean;
	}
	catch (TypeMismatchException ex) {
		if (logger.isDebugEnabled()) {
			logger.debug("Failed to convert bean '" + name + "' to required type '" +
					ClassUtils.getQualifiedName(requiredType) + "'", ex);
		}
		throw newBeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); }}return (T) bean;
Copy the code

RequiredType is an argument that the getBean() method can pass in, that is, the Bean can be fetched based on the specified beanName and requiredType.

RequiredType is null, such as getBean(beanName).

This logic is used when requiredType is not null.

Conclusion:

At this point, spring loads beans, also known as getBean(), and we’re done with the overview, and we’ll write a few more articles detailing some of these steps.

Reference: impression channel source code