Interpretation of Spring's IOC from Source Code

concept

IOC (Inversion Of Control), namely control inversion, or DI (Dependency Injection), is a feature of Spring, which can be collectively called IOC.

  • Inversion of control, i.e. inversion of control, means that the control created by built-in objects is given to third-party containers rather than to their own objects.
  • Dependency injection, i.e. injection of dependent objects, i.e. injection of dependent objects through third-party containers instead of creating objects on its own initiative

In Spring, objects in these containers are collectively referred to as beans, which can be easily declared as beans in containers by xml or Java configuration, and can be annotated by @Autowire annotation. To inject these beans where they are needed, let's take a closer look at how IOC is implemented in Spring.

BeanFactory

As the name implies, BeanFactory is the factory of Bean, that is, the container of Bean. BeanFactory is the container interface. We don't care about its implementation class for the moment. First, we understand its own characteristics and point into the source code of BeanFactory. We just look at the following important methods, and then we will talk about the other methods when they are used.

	/** Getting bean instances by name */
	Object getBean(String name) throws BeansException;

	/** Get bean instances by name and object type */
	<T> T getBean(String name, Class<T> requiredType) throws BeansException;

	/** Get the alias of the bean, and if indexed by the alias, the original name will also be retrieved. */
	String[] getAliases(String name);

These methods seem very simple. Don't worry. They are just definitions in interfaces. Spring provides many BeanFactory implementation classes, such as ApplicationContext.

BeanDefinition

Beans, of course, cannot be described as ordinary objects. In spring, beans are encapsulated as bean Definition, as follows:

Loading process of Bean resources

The loading of resources, or the initialization of containers, can be divided into three parts:

  • Location resources
  • Load Resource
  • Registered resources

For example, xml Web Application Context loads resources from xml files. Let's take ClassPath xml Application Context as an example to see how configurations in xml files are loaded into Spring containers.

Firstly, the construction method is as follows:

	/**
	 * @param configLocations Resource path
	 * @param refresh Whether to refresh containers automatically
	 * @parent parent Parent class of container
	 */
	public ClassPathXmlApplicationContext(
			String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
			throws BeansException {

		super(parent);
		setConfigLocations(configLocations);
		if (refresh) {
			refresh();
		}
	}

Let's look at this super(parent) first. We find that each layer calls super(parent) until we enter the AbstractApplicationContext class. We find that we call this() method. This method is detailed as follows:

	public AbstractApplicationContext() {
		this.resourcePatternResolver = getResourcePatternResolver();
	}

Literally, it should be something like setting up a resource parser. When we go into this method, we find that it actually creates a PathMatching Resource PatternResolver object and sets our top container as resourceLoader resource loader. As you can see, super(parent) is actually setting up a resource loader for bean s.

Let's look at the setConfigLocations(configLocations) method with the following source code:

	public void setConfigLocations(@Nullable String... locations) {
		if (locations != null) {
			Assert.noNullElements(locations, "Config locations must not be null");
			this.configLocations = new String[locations.length];
			for (int i = 0; i < locations.length; i++) {
				this.configLocations[i] = resolvePath(locations[i]).trim();
			}
		}
		else {
			this.configLocations = null;
		}
	}

This method is inherited and is a method of AbstractRefreshable Config Application Context. Inside this method, the value of configLocations is set as a resource path (filling and removing spaces for environment variables), which can be understood as locating resources.

That is to say, when the container is created, it does the following two things (excluding refreshing the container):

  • Setting up resource parser
  • Setting up resource paths and locating resources

Then let's look at this optional refresh() method, which is inherited from AbstractApplicationContext. The source code is as follows:

	@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Get the current time and set the synchronization identifier to avoid multi-threaded conflicts
			prepareRefresh();

			// The refreshBeanFactory method of the subclass is actually called, and the beanFactory of the subclass is returned at the same time.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Setting Container Properties
			prepareBeanFactory(beanFactory);

			try {
				// Specify BeanPost event handlers for subclass beanFactory
				postProcessBeanFactory(beanFactory);

				// Call event handlers registered as bean s
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register BeanPost event handlers to listen for container creation
				registerBeanPostProcessors(beanFactory);

				// Initialize message source
				initMessageSource();

				// Initialize event propagator
				initApplicationEventMulticaster();

				// Initialize other special bean s in specific subclasses
				onRefresh();

				// Check and register listeners
				registerListeners();

				// Initialize the remaining singletons
				finishBeanFactoryInitialization(beanFactory);

				// Last step: Initialize the container lifecycle processor and publish container lifecycle events
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy the created singletons to avoid hanging resources
				destroyBeans();

				// Reset Synchronization Identity
				cancelRefresh(ex);

				throw ex;
			}

			finally {
				// Because no metadata is required in a single bean, reset spring's self-checking cache
				resetCommonCaches();
			}
		}
	}

With annotations, you can almost understand the execution process. In fact, it is the process of initializing and registering a series of processors and listeners. Some people may find that there is no process of loading resources. Don't worry. We enter the obtainFreshBeanFactory() method, which has a refreshBeanFactory() method. We click on the implementation in AbstractRefreshable Application Context:

	@Override
	protected final void refreshBeanFactory() throws BeansException {
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());
			customizeBeanFactory(beanFactory);
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}

Did you find that there is a loadBeanDefinitions() method that calls methods in subclasses based on xml parsing or annotation parsing, and then the next parsing process is not the focus of our analysis. If I have time later, I will write another article to specifically analyze the parsing process.

At this point, the whole loading process is clear.

Dependency Injection Process

At the beginning, we introduced the getBean(name) method, then we will go on to analyze in detail how this method actually creates and gives us the bean s we need.

There are two common implementations of this method, AbstractBeanFactory and Abstract Application Context. In fact, AbstractApplication Context also calls AbstractBeanFactory's method, so let's just look at AbstractBeanFactory.

The doGetBean method is called inside this method, and we go inside the method as follows:

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

		final String beanName = transformedBeanName(name);
		Object bean;

		// Eagerly check singleton cache for manually registered singletons.
		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 + "'");
				}
			}
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}

		else {
			// Fail if we're already creating this bean instance:
			// We're assumably within a circular reference.
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}

			// Check if bean definition exists in this factory.
			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);
				}
			}

			if (!typeCheckOnly) {
				markBeanAsCreated(beanName);
			}

			try {
				final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				checkMergedBeanDefinition(mbd, beanName, args);

				// Guarantee initialization of beans that the current bean depends on.
				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.
				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);
							throw ex;
						}
					});
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

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

		// 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 new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
			}
		}
		return (T) bean;
	}

The whole method is quite long. Let's look at the first part in part.

		// Convert to a canonical name (mainly for aliases)
		final String beanName = transformedBeanName(name);
		Object bean;

		// Check the cache to avoid creating singletons repeatedly
		Object sharedInstance = getSingleton(beanName);
		// If not empty, return the singleton in the cache
		if (sharedInstance != null && args == null) {
			// If the trace log is turned on, the log is printed according to its current status
			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 + "'");
				}
			}
			// Return from the cache
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}

This part is equivalent to a preview operation. If there are singletons in the cache, they are returned directly, avoiding repeated creation.

Next is the real creation process, as follows

		else {
			// The discovery that the bean is being created indicates that the prototype bean already exists in the cache.
			// Probably due to circular references, exceptions are thrown here
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}

			// Find out if there is a definition of a specified bean in the container
			BeanFactory parentBeanFactory = getParentBeanFactory();
			if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
				// ...
			}

			// Determine whether type validation is required, which defaults to false
			if (!typeCheckOnly) {
				// Marking the specified bean in the container has been created
				markBeanAsCreated(beanName);
			}

			try {
				// Get the parent bean definition and merge common properties
				final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				checkMergedBeanDefinition(mbd, beanName, args);

				// Get the dependencies of beans to ensure that their dependent beans are initialized properly in advance
				String[] dependsOn = mbd.getDependsOn();
				// If the dependency has other beans, initialize the dependent beans first
				if (dependsOn != null) {
					// ...
				}

				// Create in singleton mode
				if (mbd.isSingleton()) {
					// ...
				}

				// Create in prototype mode
				else if (mbd.isPrototype()) {
					// ...
				}
				
				// If there are other patterns, use bean s to define the life cycle scope (request, session, application, etc.) of the allocation of resources.
				else {
					// ...
				}
			}
			catch (BeansException ex) {
				cleanupAfterBeanCreationFailure(beanName);
				throw ex;
			}
		}

With the annotations, the core part is divided into the following parts:

  • Pre check
  • Front-end work
  • Instance by specified pattern

It seems very simple, indeed. If you just want to understand a rough creation process, the next part can be skipped and go directly to the next part. If you want to know the whole creation process in detail, please follow me to further analyze the omitted parts in the code.

Find the definition of bean s in containers
			// Find out if there is a definition of a specified bean in the container
			BeanFactory parentBeanFactory = getParentBeanFactory();
			// If there is no definition of bean in the current container and the parent container is not empty, go to the parent container to find it.
			if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
				String nameToLookup = originalBeanName(name);
				// If the parent container is AbstractBeanFactory, it is at the top of the container.
				// Call its doGetBean method directly
				if (parentBeanFactory instanceof AbstractBeanFactory) {
					return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
							nameToLookup, requiredType, args, typeCheckOnly);
				}
				// If you specify a parameter, look for it based on the explicit parameter
				else if (args != null) {
					return (T) parentBeanFactory.getBean(nameToLookup, args);
				}
				// If no parameter is specified, the search is based on the specified type name
				else if (requiredType != null) {
					return parentBeanFactory.getBean(nameToLookup, requiredType);
				}
				// Otherwise, default lookup will be used.
				else {
					return (T) parentBeanFactory.getBean(nameToLookup);
				}
			}

The process of finding bean definitions is actually a recursive operation. If bean definitions do not exist in subclasses, they are found in the parent class. If the parent class does not exist, they are searched in the parent class of the parent class. Until you reach the top-most parent class

Getting bean dependencies
				// Get the dependencies of beans to ensure that their dependent beans are initialized properly in advance
				String[] dependsOn = mbd.getDependsOn();
				if (dependsOn != null) {
					// Traverse and initialize all dependencies
					for (String dep : dependsOn) {
						// If there is a cyclic dependency, an exception is thrown
						if (isDependent(beanName, dep)) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
						}
						// Registration dependence
						registerDependentBean(dep, beanName);
						try {
							// Call the getBean method to create dependent bean s
							getBean(dep);
						}
						catch (NoSuchBeanDefinitionException ex) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
						}
					}
				}

There's nothing to say about this method, and it should be easy to understand with annotations. The point is that it contains a process for checking cyclic dependencies.

Create bean s in singleton mode

Next is the core of the whole method. The three creation modes here are much the same. Here we only talk about the most classic single mode, and the rest can be consulted by ourselves.

				// Create in singleton mode
				if (mbd.isSingleton()) {
					// Create singleton objects
					sharedInstance = getSingleton(beanName, () -> {
						try {
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							// Delete the singleton from the cache and delete the bean that received the temporary reference of the bean
							destroySingleton(beanName);
							throw ex;
						}
					});
					// Get an instance of a given bean
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

The whole method looks very clear and understandable, except to create a singleton and then return to (do not understand lambda expressions like'()->{}'), refer to my last post: Functional Programming: lambda Expressions in Java)

This code is short, but it contains the core content of the whole method: create bean instances, we click on the createBean method, which is an abstract method. The implementation part is in AbstractAutowire Capable BeanFactory, the specific source code is as follows:

	@Override
	protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {

		if (logger.isTraceEnabled()) {
			logger.trace("Creating instance of bean '" + beanName + "'");
		}
		RootBeanDefinition mbdToUse = mbd;

		Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
		if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
			mbdToUse = new RootBeanDefinition(mbd);
			mbdToUse.setBeanClass(resolvedClass);
		}

		try {
			mbdToUse.prepareMethodOverrides();
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
					beanName, "Validation of method overrides failed", ex);
		}

		try {
			Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
			if (bean != null) {
				return bean;
			}
		}
		catch (Throwable ex) {
			throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
					"BeanPostProcessor before instantiation of bean failed", ex);
		}

		try {
			Object beanInstance = doCreateBean(beanName, mbdToUse, args);
			if (logger.isTraceEnabled()) {
				logger.trace("Finished creating instance of bean '" + beanName + "'");
			}
			return beanInstance;
		}
		catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
			throw ex;
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
		}
	}

It looks very bulky. In order to facilitate analysis, we delete the logs and exceptions. Let's see again:

	@Override
	protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {
		RootBeanDefinition mbdToUse = mbd;
		
		// Determine whether a given bean can be instantiated (i.e., loaded by the current class loader)
		Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
		// If not, delegate to its parent class for lookup
		if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
			mbdToUse = new RootBeanDefinition(mbd);
			mbdToUse.setBeanClass(resolvedClass);
		}
		// Prepare to override methods in bean s
		mbdToUse.prepareMethodOverrides();
		
		// If the processor before and after initialization is set, a proxy object is returned.
		Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
		if (bean != null) {
			return bean;
		}
		
		// Create bean
		Object beanInstance = doCreateBean(beanName, mbdToUse, args);
		return beanInstance;
	}

Is it a lot simpler at once? This is also a common way to analyze the source code. It is more convenient to understand the program structure. Well, let's look at this program. First, we pass in three parameters: bean name, parent bean, and parameter list. Then, we just look at the core methods. We find that there is no code to create beans. After all, there is no new code. Don't worry. Click into the doCreateBean method. Then we can see:

	protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {
			
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			// Remove the mapping of the bean Name in the registry (an implementation of the singleton pattern) and return the bean
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
			// If the bean is not in the registry, an instance of the bean is created
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		// Encapsulating bean s
		final Object bean = instanceWrapper.getWrappedInstance();
		// Get the bean type
		Class<?> beanType = instanceWrapper.getWrappedClass();
		if (beanType != NullBean.class) {
			mbd.resolvedTargetType = beanType;
		}

		// Synchronization Lock on Post Processor
		// Allow post-processor to modify merged bean definitions
		synchronized (mbd.postProcessingLock) {
			// Determine whether the post-processor has completed the processing, and if not, perform the post-processing operation after the merge bean definition.
			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;
			}
		}

		// Cache singletons immediately to facilitate circular references to dependent objects
		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");
			}
			// Allow containers to hold references to objects as early as possible to facilitate circular references to dependent objects
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		// Initialize the bean instance, where the dependencies actually trigger
		Object exposedObject = bean;
		try {
			// Filling bean instances with parameters
			populateBean(beanName, mbd, instanceWrapper);
			// Initialize bean objects
			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);
			}
		}
		
		// The parent class is a singleton object & the bean allows circular references & the bean is being created
		if (earlySingletonExposure) {
			// Get registered singleton bean s
			Object earlySingletonReference = getSingleton(beanName, false);
			if (earlySingletonReference != null) {
				// If the registered bean is the same as the bean being created, the bean is returned directly.
				if (exposedObject == bean) {
					exposedObject = earlySingletonReference;
				}
				// If the bean relies on other beans and is not allowed to inject beans in the case of cyclic dependencies
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
					String[] dependentBeans = getDependentBeans(beanName);
					Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
					for (String dependentBean : dependentBeans) {
						// Check types and add dependencies
						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.");
					}
				}
			}
		}

		// Adding beans to the list of disposable beans in the factory applies only to the singleton pattern
		try {
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
		}

		return exposedObject;
	}

Specific execution process has been annotated in detail in the code, I do not intend to repeat it again, I believe you can read carefully, we are concerned about a very interesting point here, you can see that there are three places in modern code involving the loading of bean s:

			// ...
			instanceWrapper = createBeanInstance(beanName, mbd, args);
			// ...
			populateBean(beanName, mbd, instanceWrapper);
			exposedObject = initializeBean(beanName, exposedObject, mbd);
			// ...

Let's first look at the createBeanInstance method. In order to avoid many people losing sight of it, we don't want to put the source code. Let me briefly describe the process of the method.

  • Pre check
  • Call instantiateUsingFactoryMethod factory method to instantiate bean s
  • Instantiation using automatic assembly method
    Setting Synchronization Markers
    If the automatic assembly property is set, the autowireConstructor method is called to automatically match the construction method according to the parameter type.
    - Otherwise, use the default parametric constructor
  • If no automatic assembly is set up, use the construction method to instantiate it.

Surely someone will ask, this method has not been instantiated object, then what is the latter method? Don't worry, we ignore the method of populateBean injecting attributes and go directly to initializeBean method. If you are interested in the source code, you can consult it by yourself. I'll also briefly talk about the process here.

  • Obtain security management interface
  • Create objects according to the set instantiation strategy

/ / unfinished

Keywords: Spring xml Java Lambda

Added by peacedesigns on Thu, 16 May 2019 07:54:02 +0300