There are several important steps to loading and instantiating beans:

Scan the classes that need to be managed according to the package. 2. Encapsulate these classes into BeanDefinitions and register them with the BeanFactory container

The chart below shows when these three key steps are triggered in the order in which Springboot starts the call chain:

1. How does SpringBoot scan the classes that need to be managed according to the package?

Springboot is to start the class by the @ComponentScan annotation specified by the package path to scan, if there is no annotation, the default from the start class where the package path to scan, according to the class annotation to determine whether to be managed by the container. The code for the ConfigurationClassParser parse method is From the above we can see this method call stack is SpringApplication# run () – > refreshContext (context) – > invokeBeanFactoryPostProcessors (the beanFactory); ->ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry(BeanDefinitionRegistry ConfigurationClassParser#parse

Here is the code for the parse method:

public void parse(Set<BeanDefinitionHolder> configCandidates) {
		for (BeanDefinitionHolder holder : configCandidates) {
			BeanDefinition bd = holder.getBeanDefinition();
			try {
				if (bd instanceof AnnotatedBeanDefinition) {
				// Parse the logic in the method from steps 1.1 to 1.8
					parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
				}
				else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
					parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
				}
				else{ parse(bd.getBeanClassName(), holder.getBeanName()); }}catch (BeanDefinitionStoreException ex) {
				throw ex;
			}
			catch (Throwable ex) {
				throw new BeanDefinitionStoreException(
						"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex); }}// Core step 1.9
		this.deferredImportSelectorHandler.process();
	}
Copy the code

The parse method calls to the following method:

The core steps are as follows:

  • 1.1: If the class is annotated by @Component (even if it is annotated by @Component annotations, such as @Configuration annotated by @Component, and if the class is annotated by @Configuration, then it is annotated by @Component), Then deal with the first class in the definition of some internal classes, recursive calls to these inner class processConfigurationClass (ConfigurationClass configClass) method, analytical inner class first, then parse the outer class
  • 1.2: Check if the annotation is annotated by @propertysource. If it is annotated, parse the key and value in the annotation’s property configuration file into the Context’s Environment object for future use
  • 1.3: To determine if @ComponentScans are marked (which tells the Spring container to traverse the package path to load the bean), scan all classes against the configured package path, and recursively call the following method for classes that need to be managed by the Spring container (classes annotated by @Component). Knowing that all classes under the package path specified in the @ComponentScans annotation on this class are scanned and resolved into the Spring container (default is the package path of the class if no package path is configured), The scanning is implemented by ClassPathBeanDefinitionScanner doScan method of a class. The debug diagram is as follows:

  • 1.4: Start parsing the @import annotation, which specifies that certain classes are managed by the Spring container
  • 1.5: Scan the class for the @importResource annotation. If it has an annotation, parse the annotation. This annotation is used to import an XML configuration file, such as a Spring project XML file containing configuration bean information. Load the configuration information in these files
  • 1.6: Parse the methods annotated by @bean in the class
  • The @bean annotation can be used on the default method of the interface
  • 1.8: check to see if the class has a parent class and interface of the father, if any, recursive calls processConfigurationClass to parse () method
  • 1.9: Finally, execute the DeferredImportSelector class scanned in the previous steps, For example, we automatically loaded @ EnableAutoConfiguration annotations in @ Import used to load the introduction of automatic component AutoConfigurationImportSelector class (principle selectImports method of this class is to view the META – I NF/spring.factory find the EnableAutoConfiguration for some automatically loaded components. The interface class is executed last because many of these auto-loaded classes use @Conditional-related annotations, such as @conditionalonmissingBean, so you have to wait until the other beans have been scanned before executing the logic of this class

The codes for 1.1 through 1.8 are as follows:

/**
	 * Apply processing and build a complete {@link ConfigurationClass} by reading the
	 * annotations, members and methods from the source class. This method can be called
	 * multiple times as relevant sources are discovered.
	 * @param configClass the configuration class being build
	 * @param sourceClass a source class
	 * @return the superclass, or {@code null} if none found or previously processed
	 */
	@Nullable
	protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
			throws IOException {
    / / 1.1
		if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
			// Recursively process any member (nested) classes first
			processMemberClasses(configClass, sourceClass);
		}
    / / 1.2
		// Process any @PropertySource annotations
		for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), PropertySources.class,
				org.springframework.context.annotation.PropertySource.class)) {
			if (this.environment instanceof ConfigurableEnvironment) {
				processPropertySource(propertySource);
			}
			else {
				logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
						"]. Reason: Environment must implement ConfigurableEnvironment"); }}/ / 1.3
		// Process any @ComponentScan annotations
		Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
		if(! componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
			for (AnnotationAttributes componentScan : componentScans) {
				// The config class is annotated with @ComponentScan -> perform the scan immediately
				Set<BeanDefinitionHolder> scannedBeanDefinitions =
						this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
				// Check the set of scanned definitions for any further config classes and parse recursively if needed
				for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
					BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
					if (bdCand == null) {
						bdCand = holder.getBeanDefinition();
					}
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { parse(bdCand.getBeanClassName(), holder.getBeanName()); }}}}/ / 1.4
		// Process any @Import annotations
		processImports(configClass, sourceClass, getImports(sourceClass), true);
    / / 1.5
		// Process any @ImportResource annotations
		AnnotationAttributes importResource =
				AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
		if(importResource ! =null) {
			String[] resources = importResource.getStringArray("locations");
			Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
			for (String resource : resources) {
				String resolvedResource = this.environment.resolveRequiredPlaceholders(resource); configClass.addImportedResource(resolvedResource, readerClass); }}/ / 1.6
		// Process individual @Bean methods
		Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
		for (MethodMetadata methodMetadata : beanMethods) {
			configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
		}
    / / 1.7
		// Process default methods on interfaces
		processInterfaces(configClass, sourceClass);
    / / 1.8
		// Process superclass, if any
		if (sourceClass.getMetadata().hasSuperClass()) {
			String superclass = sourceClass.getMetadata().getSuperClassName();
			if(superclass ! =null && !superclass.startsWith("java") &&!this.knownSuperclasses.containsKey(superclass)) {
				this.knownSuperclasses.put(superclass, configClass);
				// Superclass found, return its annotation metadata and recurse
				returnsourceClass.getSuperClass(); }}// No superclass -> processing is complete
		return null;
	}
Copy the code

2. How does SpringBoot encapsulate these classes as BeanDefinitions and register them with the BeanFactory container

Through ConfigurationClassBeanDefinitionReader to parse the parser in the third step in configurationClasses ConfigurationClass in the Map, And the BeanDefinition is parsed and registered in the container. The debug diagram is as follows:

/**
	 * Read a particular {@link ConfigurationClass}, registering bean definitions
	 * for the class itself and all of its {@link Bean} methods.
	 */
	private void loadBeanDefinitionsForConfigurationClass( ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
    // Check whether there is an @conditional annotation; if there is, check whether the condition is met; otherwise, delete it from the container
		if (trackedConditionEvaluator.shouldSkip(configClass)) {
			String beanName = configClass.getBeanName();
			if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
				this.registry.removeBeanDefinition(beanName);
			}
			this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
			return;
		}
    // Encapsulate the classes introduced through the @import annotation as BeanDefinitions and register them in the container
		if (configClass.isImported()) {
			registerBeanDefinitionForImportedConfigurationClass(configClass);
		}
		// Encapsulate the @bean annotation as a BeanDefinition and register it in the container
		for (BeanMethod beanMethod : configClass.getBeanMethods()) {
			loadBeanDefinitionsForBeanMethod(beanMethod);
		}
    // Load the beanDefinition in the configuration file specified by @importResource
		loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
		/ / perform introduced ImportBeanDefinitionRegistrar registerBeanDefinitions method of a class, Such as @ EnableConfigurationProperties annotations on EnableConfigurationPropertiesRegistrar. Class
		loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
	}
Copy the code

Here TrackedConditionEvaluator analytical @ Conditional class is annotated, such as @ ConditionalOnClass, @ ConditionalOnBean annotations, ConditionalOnClass () {ConditionalOnClass (); ConditionalOnClass (); ConditionalOnClass (); ConditionalOnClass ();

The OnClassCondition class diagram is as follows:

The logic behind the @conditionAlonBean annotation is that the bean must already be loaded, and the matches method needs to wait until all the beans have been scanned, so there is a phase concept. Matches specifies the phase in which the current matches method matches, such as OnBeanCondition, which matches only during the registration phase:

There are two enumerations at this stage:

PARSE_CONFIGURATION: refers to the PARSE_CONFIGURATION phase of the parser phase above. REGISTER_BEAN is scanned for package paths and encapsulated in ConfigurationClass: Refers to the current of this step loadBeanDefinitionsForConfigurationClass stage of reader, namely: to transfer ConfigurationClass beanDefinition registered with the container

3. How does SpringBoot instantiate all scanned BeanDefinitions, including solving cyclic dependencies and lazy loading

The bean is instantiated using the getBean method of the BeanFactory container.

This provides methods to get beans by bean name and type information. According to the diagram of the call chain at the beginning, We debug into finishBeanFactoryInitialization (the beanFactory) method is the beanFactory. PreInstantiateSingletons () method, the code is as follows:

@Override
	public void preInstantiateSingletons(a) throws BeansException {
		if (logger.isTraceEnabled()) {
			logger.trace("Pre-instantiating singletons in " + this);
		}

		// Iterate over a copy to allow for init methods which in turn register new bean definitions.
		// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
		List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

		// Trigger initialization of all non-lazy singleton beans...
		for (String beanName : beanNames) {
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
			if(! bd.isAbstract() && bd.isSingleton() && ! bd.isLazyInit()) {if (isFactoryBean(beanName)) {
					Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
					if (bean instanceof FactoryBean) {
						finalFactoryBean<? > factory = (FactoryBean<? >) bean;boolean isEagerInit;
						if(System.getSecurityManager() ! =null && factory instanceofSmartFactoryBean) { isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) ((SmartFactoryBean<? >) factory)::isEagerInit, getAccessControlContext()); }else {
							isEagerInit = (factory instanceofSmartFactoryBean && ((SmartFactoryBean<? >) factory).isEagerInit()); }if(isEagerInit) { getBean(beanName); }}}else{ getBean(beanName); }}}// Trigger post-initialization callback for all applicable beans...
		for (String beanName : beanNames) {
			Object singletonInstance = getSingleton(beanName);
			if (singletonInstance instanceof SmartInitializingSingleton) {
				final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
				if(System.getSecurityManager() ! =null) {
					AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
						smartSingleton.afterSingletonsInstantiated();
						return null;
					}, getAccessControlContext());
				}
				else{ smartSingleton.afterSingletonsInstantiated(); }}}}Copy the code

This code does: 1, through the getBean (beanName) method to instantiate all the lazy loading of bean (lazy loading will bean bean for injection generated when a proxy class methods to achieve) 2, traverse all SmartInitializingSingleton class has been instantiated, And call the class afterSingletonsInstantiated () method

GetBean (beanName)

During bean instantiation, Spring breaks the process into two phases:

  • In the instantiation phase, Spring uses reflection to call an empty object from the constructor of the class. All dependencies in the object are null (unless the property is injected by the constructor). After this phase is complete, the reference to the empty object is exposed in a Map. Will get the reference in the Map and inject the reference to solve the circular dependency problem. I’ll describe how to solve the circular dependency problem in more detail. I’ll call this kind of object the semi-instantiated bean.
  • In the dependency injection phase, dependency injection is performed on empty objects, and if it is found that there is no dependent bean in the Spring container, the dependent bean is instantiated. I will call this type of injected bean a fully instantiated bean.

The core steps are as follows:

  • Check if the singletonObjects Map (which stores beans that have been fully instantiated) already exists
  • Check earlySingletonObjects for a Map that stores pre-exposed semi-instantiated beans and resolves loop dependencies
  • Check the singletonFactories Map (which stores pre-exposed semi-instantiated beans and resolves loop dependencies). If it exists, the Factory’s getObject method is called to get the bean and then put the bean into earlySingletonObjects
  • If a bean has already been created in the previous three steps, check if the bean is a FactoryBean, and if so, call the FactoryBean’s getObject method to get the bean
  • Scope =”prototype”; scope=” scope “; scope=”prototype”; scope=” scope “; scope=”prototype”; Each time it’s a new reference, it can’t be solved by exposing the reference in advance
  • Check to see if the parent BeanFactory exists, and if so, instantiate it by calling getBean of the parent BeanFactory
  • The token bean has been created, that is, adding the beanName of the bean to the alreadyCreated set
  • Remerge the beanDefinition. The merge means that if the bean has a parent, the beanDefinition of the parent bean and the beanDefinition of the current bean need to be merged into one beanDefinition. For example, when instantiating son, we need to merge the beanDefinition of father with the beanDefinition of father. Because the parent bean may define some spring features (such as injecting some properties, etc.), we need to instantiate the bean together. Otherwise, some of the injected attributes in father inherited by son will be null
  • Check if the current bean is tagged with an @dependson annotation. This annotation means that the current bean needs to depend on other beans before being instantiated. The class specified in the @dependson annotation needs to be instantiated first
  • Check whether the current bean is a singleton bean (that is, only one object is generated). Singleton beans follow the logic of singleton beans, as described in the singleton bean analysis below
  • Check if the current bean is a prototype bean (that is, a new object is generated each time), and the prototype bean follows the logic of the prototype bean, as described below
  • That is, non-singleton beans and non-prototype beans follow the logic of other scopes, as detailed in the following analysis
  • If the resulting bean type does not match, the type conversion is performed
/**
	 * Return an instance, which may be shared or independent, of the specified bean.
	 * @param name the name of the bean to retrieve
	 * @param requiredType the required type of the bean to retrieve
	 * @param args arguments to use when creating a bean instance using explicit arguments
	 * (only applied when creating a new instance as opposed to retrieving an existing one)
	 * @param typeCheckOnly whether the instance is obtained for a type check,
	 * not for actual use
	 * @return an instance of the bean
	 * @throws BeansException if the bean could not be created
	 */
	@SuppressWarnings("unchecked")
	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 bean, which filters the ampersand of the FactoryBean
		final String beanName = transformedBeanName(name);
		Object bean;

		// Eagerly check singleton cache for manually registered singletons.
		// Check whether a singleton has been created
		Object sharedInstance = getSingleton(beanName);
		if(sharedInstance ! =null && args == null) {
			if (logger.isTraceEnabled()) {
				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 + "'"); }}// If there is a bean already created, the bean is checked to see if it is a FactoryBean. If so, the FactoryBean's getObject method is called to get the bean
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}

		else {
			// Fail if we're already creating this bean instance:
			// We're assumably within a circular reference.
			// If the bean is multimodal, and the multimodal loop depends on itself, this cannot be resolved because infinite new objects are required
			// The singleton loop dependency can be resolved. After all, there is only one object, only one reference, and no need for infinite new objects
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}

			// Check if bean definition exists in this factory.
			// Use the parent beanFactory to instantiate the bean first
			BeanFactory parentBeanFactory = getParentBeanFactory();
			if(parentBeanFactory ! =null && !containsBeanDefinition(beanName)) {
				// Not found -> check parent.
				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); }}// The tag bean has been created
			if(! typeCheckOnly) { markBeanAsCreated(beanName); }try {
			  // If a bean has a parent, e.g. 
       Merge the parent bean chain into a beanDefinition, because the parent bean may also define some Spring features that need to be instantiated together, such as dependency injection, etc
				checkMergedBeanDefinition(mbd, beanName, args);

				// Guarantee initialization of beans that the current bean depends on.
				If the current bean is tagged with an @dependson annotation, the class specified in the @dependson annotation must be instantiated first
				String[] dependsOn = mbd.getDependsOn();
				if(dependsOn ! =null) {
					for (String dep : dependsOn) {
						if (isDependent(beanName, dep)) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
						}
						registerDependentBean(dep, beanName);
						try {
							getBean(dep);
						}
						catch (NoSuchBeanDefinitionException ex) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"'" + beanName + "' depends on missing bean '" + dep + "'", ex); }}}// Create bean instance.
				// The singleton pattern is instantiated
				if (mbd.isSingleton()) {
					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.
							destroySingleton(beanName);
							throwex; }}); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }// Instantiation of the prototype pattern (new one object at a time)
				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);
				}
        // Other custom Scope instantiation, such as session, request Scope instantiation
				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, () -> {
							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);
				throwex; }}// Perform a type conversion if the types are inconsistent
		// Check if required type matches the type of the actual bean instance.
		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.isTraceEnabled()) {
					logger.trace("Failed to convert bean '" + name + "' to required type '" +
							ClassUtils.getQualifiedName(requiredType) + "'", ex);
				}
				throw newBeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); }}return (T) bean;
	}
Copy the code

3.1 Instantiation of singleton beans

Spring defaults to singleton beans, i.e. : scope=”single”

The core steps are as follows:

  • Check if the Map: singletonObjects used to store the created singleton bean exists, and return it if it does
  • If the container is being destroyed, stop creation and throw an exception
  • Put beanName singletonsCurrentlyInCreation this set, mark the current bean is created
  • Call the createBean method to instantiate the bean, as described in the source code below
  • From singletonsCurrentlyInCreation instantiation, after the completion of the set to remove the beanName, identify the bean creation process has been completed (but failed to complete the bean lock depend on the properties of the injection, can’t use)
  • Place the instantiated bean into the singletonObjects Map
  • Remove the bean from the singletonFactories and earlySingletonObjects caches, which address the problem of loop dependencies, because they are fully instantiated and added to singletonObjects
  • RegisteredSingletons is a linkedSet, in the order they are instantiated.
if (mbd.isSingleton()) {
		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.
				destroySingleton(beanName);
				throwex; }}); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }public Object getSingleton(String beanName, ObjectFactory
        singletonFactory) {
		Assert.notNull(beanName, "Bean name must not be null");
		synchronized (this.singletonObjects) {
		  // Check if the singleton bean has been created, and return if it has been created
			Object singletonObject = this.singletonObjects.get(beanName);
			if (singletonObject == null) {
				if (this.singletonsCurrentlyInDestruction) {
					throw new BeanCreationNotAllowedException(beanName,
							"Singleton bean creation not allowed while singletons of this factory are in destruction " +
							"(Do not request a bean from a BeanFactory in a destroy method implementation!) ");
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
				}
				/ / check whether there is the current class, in the singletonsCurrentlyInCreation this set exists, explain the current class is original in the creation process, will throw an error, so in singleton bean cycle depend on the scene, if is injected through the constructor of the circular dependencies will throw an error, Because the constructor injected cyclic dependencies, the bean construction process cannot be completed and only an error can be reported. (The cyclic dependencies injected through properties does not prevent the object from being new and then injecting references to each other.)
				beforeSingletonCreation(beanName);
				boolean newSingleton = false;
				boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
				if (recordSuppressedExceptions) {
					this.suppressedExceptions = new LinkedHashSet<>();
				}
				try {
				CreateBean (beanName, MBD, args); // Call the function passed in when this method is called: createBean(beanName, MBD, args)
					singletonObject = singletonFactory.getObject();
					newSingleton = true;
				}
				catch (IllegalStateException ex) {
					// Has the singleton object implicitly appeared in the meantime ->
					// if yes, proceed with it since the exception indicates that state.
					singletonObject = this.singletonObjects.get(beanName);
					if (singletonObject == null) {
						throwex; }}catch (BeanCreationException ex) {
					if (recordSuppressedExceptions) {
						for (Exception suppressedException : this.suppressedExceptions) { ex.addRelatedCause(suppressedException); }}throw ex;
				}
				finally {
					if (recordSuppressedExceptions) {
						this.suppressedExceptions = null;
					}
					/ / instantiate completed, delete the beanName from singletonsCurrentlyInCreation this set, identify the bean creation process has been completed (but failed to complete the bean lock depend on the properties of the injection, can't use)
					afterSingletonCreation(beanName);
				}
				if (newSingleton) {
				  // Once created, the bean is cached in the singletonObjectsMap, removing the previously exposed references from several caches to resolve loop dependencies (more on that later)addSingleton(beanName, singletonObject); }}returnsingletonObject; }}Copy the code

CreateBean ();

The core logic of the createBean method is to call the doCreateBean() method:

The core steps are as follows:

  • Check if a FactoryBean exists in the factoryBeanInstanceCache Map that caches the Bean
  • If not, createBeanInstance() is called to instantiate the object
  • Pointing to the rear MergedBeanDefinitionPostProcessor logic processor, These processors, including responsible @ Resource annotation parsing and injection CommonAnnotationBeanPostProcessor and responsible for the @autowired and @ Value annotation AutowiredAnnotationBeanPostProce parsing and injection Ssor rear processor
  • Semi-instantiated beans (beans that have not yet been injected with dependency properties) are pre-exposed in the singletonFactories map to solve the problem of circular dependencies
  • see your source code analysis later
  • Check whether the bean implements the BeanNameAware, BeanClassLoaderAware, and BeanFactoryAware interfaces, and if so, execute the set method of the corresponding interface
  • Perform BeanPostProcessor post processor postProcessBeforeInitialization method, this method allows returns an object to replace the current bean, such as the return to an agent
  • Execute the InitMethods methods of the bean, including the afterPropertiesSet method that implements the InitializingBean interface, and the init-method method specified by the @bean annotation
  • Perform BeanPostProcessor post processor postProcessAfterInitialization method, this method allows returns an object to replace the current bean, such as the return to an agent
  • Check that if the BeanPostProcessor post-processor returns a new object to replace the original bean, the references exposed in the singletonFactories Map are invalidated and objects that depend on the current bean need to be re-instantiated
  • A method that must be executed to register a bean when it is destroyed, such as the Destroy method that implements the DisposableBean interface, which is distinguished by the bean’s scope, which indicates the bean’s declaration cycle, For example, scope= Request means that the life of the current bean lasts until the end of a request, so the bean’s destroy method needs to be executed at the end of the request
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {

		// Instantiate the bean.
		BeanWrapper instanceWrapper = null;
		// Whether the FactoryBean exists in the cache
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
		// Instantiate the bean
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		// Get the real bean
		finalObject bean = instanceWrapper.getWrappedInstance(); Class<? > beanType = instanceWrapper.getWrappedClass();if(beanType ! = NullBean.class) { mbd.resolvedTargetType = beanType; }// Allow post-processors to modify the merged bean definition.
		/ / execution MergedBeanDefinitionPostProcessor post processor, preparing for the bean dependency injection
		synchronized (mbd.postProcessingLock) {
			if(! mbd.postProcessed) {try {
					applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
				}
				catch (Throwable ex) {
					throw new BeanCreationException(mbd.getResourceDescription(), beanName,
							"Post-processing of merged bean definition failed", ex);
				}
				mbd.postProcessed = true; }}// Eagerly cache singletons to be able to resolve circular references
		// even when triggered by lifecycle interfaces like BeanFactoryAware.
		// The reference to the instantiated bean is pre-cached in the singletonFactories Map to solve the circular dependency dependency problem. The dependency properties in the bean have not yet been injected (except for properties injected through the constructor), so they are incomplete
		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			if (logger.isTraceEnabled()) {
				logger.trace("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		// Initialize the bean instance.
		Object exposedObject = bean;
		try {
		 // Inject properties
			populateBean(beanName, mbd, instanceWrapper);
			// Execute the bean's init method (the method labeled @postconstruct)
			// Execute the BeanPostProcessor post-processor method
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		catch (Throwable ex) {
			if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
				throw (BeanCreationException) ex;
			}
			else {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); }}// If the previously exposed bean has already been injected, it is also necessary to check whether the bean has been re-instantiated. If it has been re-instantiated, the previously exposed reference has expired and needs to be re-injected
		if (earlySingletonExposure) {
			Object earlySingletonReference = getSingleton(beanName, false);
			if(earlySingletonReference ! =null) {
				if (exposedObject == bean) {
					exposedObject = earlySingletonReference;
				}
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
					String[] dependentBeans = getDependentBeans(beanName);
					Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
					for (String dependentBean : dependentBeans) {
						if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
							actualDependentBeans.add(dependentBean);
						}
					}
					if(! actualDependentBeans.isEmpty()) {throw new BeanCurrentlyInCreationException(beanName,
								"Bean with name '" + beanName + "' has been injected into other beans [" +
								StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
								"] in its raw version as part of a circular reference, but has eventually been " +
								"wrapped. This means that said other beans do not use the final version of the " +
								"bean. This is often the result of over-eager type matching - consider using " +
								"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example."); }}}}// Register bean as disposable.
		// Register a method that needs to be executed when the bean is destroyed, such as the destroy method that implements the DisposableBean interface
		try {
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
		}

		return exposedObject;
	}
Copy the code

CreateBeanInstance method source

The core logic is as follows:

  • Resolve the bean’s Class information by bean name
  • Check to see if any Supplier is configured to generate the object. If so, this Supplier is used to generate the object
  • Check whether the bean is configured with factory-method, which is to configure a factory class to generate an object, for example
  • Check to see if the constructor or factory-method has been parsed before, and do not re-parse if it has
  • Perform SmartInstantiationAwareBeanPostProcessor post processor determineCandidateConstructors method, the rear handler returns bean constructors, Such as rear AutowiredAnnotationBeanPostProcessor processor will return the constructor with parameters
  • If a parameter constructor exists, it executes and takes care of the injection of arguments into the constructor
  • The no-parameter constructor is executed if none exists
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
		// Make sure bean class is actually resolved at this point.
		// The Class information of the bean is resolved by the bean nameClass<? > beanClass = resolveBeanClass(mbd, beanName);if(beanClass ! =null&&! Modifier.isPublic(beanClass.getModifiers()) && ! mbd.isNonPublicAccessAllowed()) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,
					"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
		}
    // Check whether any Supplier is configured for the generated objectSupplier<? > instanceSupplier = mbd.getInstanceSupplier();if(instanceSupplier ! =null) {
			return obtainFromSupplier(instanceSupplier, beanName);
		}
    
      
		if(mbd.getFactoryMethodName() ! =null) {
			return instantiateUsingFactoryMethod(beanName, mbd, args);
		}
    
		// Shortcut when re-creating the same bean...
		// Check if the constructor or factory-method has been parsed before
		boolean resolved = false;
		boolean autowireNecessary = false;
		if (args == null) {
			synchronized (mbd.constructorArgumentLock) {
				if(mbd.resolvedConstructorOrFactoryMethod ! =null) {
					resolved = true; autowireNecessary = mbd.constructorArgumentsResolved; }}}// instantiate if parsed
		if (resolved) {
			if (autowireNecessary) {
				return autowireConstructor(beanName, mbd, null.null);
			}
			else {
				returninstantiateBean(beanName, mbd); }}// Candidate constructors for autowiring?
		/ / execution SmartInstantiationAwareBeanPostProcessor post processor determineCandidateConstructors method, the post processor returns the constructor of the beanConstructor<? >[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);if(ctors ! =null|| mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || ! ObjectUtils.isEmpty(args)) {// Execute the constructor with arguments
			return autowireConstructor(beanName, mbd, ctors, args);
		}

		// Preferred constructors for default construction?
		ctors = mbd.getPreferredConstructors();
		if(ctors ! =null) {
			return autowireConstructor(beanName, mbd, ctors, null);
		}
    // Execute the no-argument constructor
		// No special handling: simply use no-arg constructor.
		return instantiateBean(beanName, mbd);
	}
	
Copy the code

PopulateBean source code analysis

The core steps are as follows:

  • Perform InstantiationAwareBeanPostProcessor post processor postProcessAfterInstantiation method, this method returns a fase said don’t need to do the follow-up di action, returned directly
  • Check to see if the current BeanDefinition is configured with dependency injection objects
  • Get the corresponding object to be injected according to the injection type. The default value is 0, that is: Is not specified by the name or type to injection, a post processor is used to finish injection, this will be done by using the approach of CommonAnnotationBeanPostProcessor postProcessProperties @ Resourse of injection, Done through the AutowiredAnnotationBeanPostProcessor postProcessProperties @autowired and @ the Value of injection, classmates are interested can look at the source code of two methods
  • Perform InstantiationAwareBeanPostProcessor post processor postProcessProperties method, Here will be executed CommonAnnotationBeanPostProcessor and AutowiredAnnotationBeanPostProcessor injection logic to complete the corresponding annotations, this method returns a PropertyValues object at the same time, If the PropertyValues object has a value, it will be injected again based on that object, and these two post-processors will handle the Lazy loading logic of the @lazy annotation flag when processing the injection. See Lazy loading source code. If the PropertyValues object has a value, it will find the properties in the bean based on the key in the object. The value injection is complete
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
		if (bw == null) {
			if (mbd.hasPropertyValues()) {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
			}
			else {
				// Skip property population phase for null instance.
				return; }}// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
		// state of the bean before properties are set. This can be used, for example,
		// to support styles of field injection.
		boolean continueWithPropertyPopulation = true;
    / rear/execute InstantiationAwareBeanPostProcessor processor postProcessAfterInstantiation method, this method returns a fase said don't need to do the follow-up di action, returned directly
		if(! mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof InstantiationAwareBeanPostProcessor) {
					InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
					if(! ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { continueWithPropertyPopulation =false;
						break; }}}}if(! continueWithPropertyPopulation) {return;
		}
    // Check whether the current BeanDefinition is configured with attributes
		PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

		int resolvedAutowireMode = mbd.getResolvedAutowireMode();
		The default value is 0, i.e., no name or type is specified for injection. A post-processor is used for injection
		if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
			MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
			// Add property values based on autowire by name if applicable.
			if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
				autowireByName(beanName, mbd, bw, newPvs);
			}
			// Add property values based on autowire by type if applicable.
			if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
				autowireByType(beanName, mbd, bw, newPvs);
			}
			pvs = newPvs;
		}

		boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
		booleanneedsDepCheck = (mbd.getDependencyCheck() ! = AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); PropertyDescriptor[] filteredPds =null;
		if (hasInstAwareBpps) {
			if (pvs == null) {
				pvs = mbd.getPropertyValues();
			}
			/ / execution InstantiationAwareBeanPostProcessor post processor postProcessProperties method, Here will be executed CommonAnnotationBeanPostProcessor and AutowiredAnnotationBeanPostProcessor injection logic to complete the corresponding annotations, this method returns a PropertyValues object at the same time, If the object has a value, it is injected again based on that object
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof InstantiationAwareBeanPostProcessor) {
					InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
					PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
					if (pvsToUse == null) {
						if (filteredPds == null) {
							filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
						}
						pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
						if (pvsToUse == null) {
							return; } } pvs = pvsToUse; }}}if (needsDepCheck) {
			if (filteredPds == null) {
				filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
			}
			checkDependencies(beanName, mbd, filteredPds, pvs);
		}
   // Inject according to the PVS object
		if(pvs ! =null) { applyPropertyValues(beanName, mbd, bw, pvs); }}Copy the code

Lazy load source code analysis

I’m here to rear CommonAnnotationBeanPostProcessor code to analyze the principle of the processor, Code position in CommonAnnotationBeanPostProcessor type of inner class ResourceElement getResourceToInject buildLazyResourceProxy method in the way, The idea is not to actually instantiate the bean, but to return a reference to a proxy class that generates a TargetSource object. When the bean’s method is actually called during execution, The getTarget method of the TargetSource object is first called to instantiate the bean.


protected Object buildLazyResourceProxy(final LookupElement element, final @Nullable String requestingBeanName) {
		TargetSource ts = new TargetSource() {
			@Override
			publicClass<? > getTargetClass() {return element.lookupType;
			}
			@Override
			public boolean isStatic(a) {
				return false;
			}
			@Override
			public Object getTarget(a) {
				return getResource(element, requestingBeanName);
			}
			@Override
			public void releaseTarget(Object target) {}}; ProxyFactory pf =new ProxyFactory();
		pf.setTargetSource(ts);
		if (element.lookupType.isInterface()) {
			pf.addInterface(element.lookupType);
		}
		ClassLoader classLoader = (this.beanFactory instanceof ConfigurableBeanFactory ?
				((ConfigurableBeanFactory) this.beanFactory).getBeanClassLoader() : null);
		return pf.getProxy(classLoader);
	}

Copy the code

Analysis of cyclic dependency problems

Spring can only solve the problem of loop dependency for singleton beans, but not for multi-instance beans (scope=”prototype”). The solution is when the bean is half-instantiated (when the bean’s injection logic is not complete, it is just an empty object from the reflection constructor new). The reference to the bean is exposed in a Map ahead of time, and the bean that depends on the object is created by first iterating through the Map and injecting the references it finds into the bean. This solves the problem of loop dependencies, but not when the bean loop depends on an object that is injected through a constructor. Since there is not even a semi-instantiated object (because the object is instantiated by the constructor’s reflection), here is the distribution of several cached Get and set points during bean creation:

3.2 Prototype Bean creation (scope=”prototype”)

A prototype bean creates a new object each time, so it calls the createBean method directly each time. Unlike a singleton bean, which puts the created bean into a Map and queries the Map each time, the prototype bean calls the beforePrototypeCreation() method before creation. Add prototypesCurrentlyInCreation beanName this set, the set deposited in creating a singleton beans, prototype bean will traverse the set before creating, if there is an error directly, so for the singleton bean cycle depend on the problem, Spring can’t solve it


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);
				}

Copy the code

3.3 Creation of beans of other scopes

For example, scope=”request”, scope=”session” life cycle bean creation process, the logic is to call the scope get method to get the bean, For example, the Scope get method of the request fetches the bean from a requestAttribute object, and then generates the bean from a beanFactory if none exists:

  {
					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, () -> {
							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); }}Copy the code