Spring IOC dependency injection

preface

The initialization process of IoC container is analyzed in detail. The main work of this initialization process is to establish BeanDefinition data mapping in IoC container.
However, the IoC container does not inject Bean dependencies. This article analyzes how to inject Bean dependencies when the IoC container.

The main method call sequence is getBean → doGetBean → createBean → doCreateBean → createBeanInstance → populateBean

1, getBean()

First, note that the dependency injection process is triggered when the user obtains the Bean from the IoC container for the first time. Of course, there are exceptions (set the lazy init attribute in the BeanDefinition).
It is the getBean() method of the BeanFactory interface. Let's see the implementation of the getBean() method from the base class AbstractBeanFactory of DefaultListableBeanFactory.
Some codes are shown below.

	public Object getBean(String name) throws BeansException {
			return doGetBean(name, null, null, false);
	}
	
// This is the place to actually obtain beans, and this is also the place to start dependency injection!
	protected <T> T doGetBean(
			final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
			throws BeansException {
	    // Get specification beanName
		final String beanName = transformedBeanName(name);
		Object bean;
     // Check the singleton cache for manually registered singleton objects
     // First, get the beans from the cache singletonObjects and process the beans of the singleton that have been created
     // private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
		Object sharedInstance = getSingleton(beanName);
		if (sharedInstance != null && args == null) {
			if (logger.isDebugEnabled()) {
				if (isSingletonCurrentlyInCreation(beanName)) {
					// Returns a cache instance of an uninitialized singleton bean, which is the result of a circular reference
				}
				else {
					// Returns the cache instance of the singleton bean
				}
			}
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}
		else {
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}
			//Here, check whether the BeanDefinition in the Ioc container exists. Check whether the required Bean can be obtained from the current BeanFactory. If it cannot be obtained from the current factory, go to the parent BeanFactory. If the current parent factory cannot be obtained, go up along the parent BeanFactory
			BeanFactory parentBeanFactory = getParentBeanFactory();
			// Omit
			}
			try {
				// Here, the BeanDefinition is obtained according to the name of the Bean
				final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				checkMergedBeanDefinition(mbd, beanName, args);
				// Get all dependent beans of the current Bean. This will start the recursive call of getBean until a Bean without any dependencies is obtained
				String[] dependsOn = mbd.getDependsOn();
				// Omit
				}
				//Here we call the createBean method to create an instance of Singleton Bean. Here is a callback function getObject that calls createBean of ObjectFactory in getSingleton.
				if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
						@Override
						public Object getObject() throws BeansException {
							try {
								return createBean(beanName, mbd, args);
							}
							catch (BeansException ex) {
								destroySingleton(beanName);
								throw ex;
							}
						}
					});
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}
				//  Omit
		return (T) bean;
	}

In particular, getBean is the starting point of dependency injection, and then createBean will be called. Let's take a look at the createBean code.
In this process, the Bean object will be generated according to the requirements defined by BeanDefinition. createBean is implemented in AbstractAutowireCapableBeanFactory. createBean not only generates beans, but also handles Bean initialization, such as the definition of init method attribute in BeanDefinition, Bean post processor, etc.

2, createBean()

protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
		if (logger.isDebugEnabled()) {
			logger.debug("Creating instance of bean '" + beanName + "'");
		}
		RootBeanDefinition mbdToUse = mbd;
     // Here, judge whether the Bean to be created can be instantiated and whether this class can be loaded through the class loader
		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 {
       // If the Bean is configured with a PostProcessor, name returns a Proxy here
			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);
		}
     // Here we start creating beans
		Object beanInstance = doCreateBean(beanName, mbdToUse, args);
		if (logger.isDebugEnabled()) {
			logger.debug("Finished creating instance of bean '" + beanName + "'");
		}
		return beanInstance;
	}

Then enter the doCreateBean method to see how it is created.

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
			throws BeanCreationException {
     // Used to hold the created Bean object
		BeanWrapper instanceWrapper = null;
		// If it is a Singleton, clear the Bean object with the same name in the cache
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
			//Where the bean is instantiated
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
		Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
		mbd.resolvedTargetType = beanType;
		// Allows the post processor to modify the merged bean definition. 
		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;
			}
		}

		///Cache singletons quickly so that circular references can be resolved, even if triggered by a lifecycle interface such as beanfactoryaware.  
		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			if (logger.isDebugEnabled()) {
				logger.debug("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			//Add the obtained objects that need to be exposed to singletonFactories to solve the circular dependency
			addSingletonFactory(beanName, new ObjectFactory<Object>() {
				@Override
				public Object getObject() throws BeansException {
					return getEarlyBeanReference(beanName, mbd, bean);
				}
			});
		}
        // Here is the initialization of the Bean. Dependency injection takes place here. After initialization, the exposed object will be returned as the Bean after dependency injection
		Object exposedObject = bean;
		try {
			//Attribute filling
			populateBean(beanName, mbd, instanceWrapper);
			if (exposedObject != null) {
				//aop post processor
				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 it is early exposure, use early exposure objects
		if (earlySingletonExposure) {
			Object earlySingletonReference = getSingleton(beanName, false);
			if (earlySingletonReference != null) {
				//In the case of aop proxy, the object passes through the early proxy, and the exposed object is the original bean, which directly obtains the proxy object from the L2 cache
				//In the case of async proxy, exposedObject is the object after the proxy, and the two are not equal
				if (exposedObject == bean) {
					exposedObject = earlySingletonReference;
				}
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
					String[] dependentBeans = getDependentBeans(beanName);
					Set<String> actualDependentBeans = new LinkedHashSet<String>(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 " +
								"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
					}
				}
			}
		}
		//Register bean s as one-time
		try {
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
		}
		return exposedObject;
	}

In doCreateBean, we mainly look at two methods: one is the method of instantiating a Bean: createBeanInstance(), and the other is the method of injecting attributes into a Bean: populateBean().

1.createBeanInstance()

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
		// Ensure that the bean class has actually resolved at this time.
		Class<?> 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());
		}
		// Factory instantiation is used here
		if (mbd.getFactoryMethodName() != null) {
			return instantiateUsingFactoryMethod(beanName, mbd, args);
		}
		// Shortcut when the same bean is recreated
		boolean resolved = false;
		boolean autowireNecessary = false;
		if (args == null) {
			synchronized (mbd.constructorArgumentLock) {
				if (mbd.resolvedConstructorOrFactoryMethod != null) {
					resolved = true;
					autowireNecessary = mbd.constructorArgumentsResolved;
				}
			}
		}
		if (resolved) {
			if (autowireNecessary) {
				return autowireConstructor(beanName, mbd, null, null);
			}
			else {
				return instantiateBean(beanName, mbd);
			}
		}
		// Instantiate using constructor
		Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
		if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
				mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
			return autowireConstructor(beanName, mbd, ctors, args);
		}
		// Instantiate using the parameterless construction method
		// No special handling: simply use no-arg constructor.
		return instantiateBean(beanName, mbd);
	}

Of course, the most common is to instantiate using the parameterless constructor instantiateBean().

	protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
		//Instantiate the Bean using the default instantiation policy
		// The default instantiation strategy is cglibsubclassing instantiation strategy, which is to instantiate beans using CGLIB, and then look at its implementation
		try {
			Object beanInstance;
			final BeanFactory parent = this;
			if (System.getSecurityManager() != null) {
				beanInstance = AccessController.doPrivileged(new PrivilegedAction<Object>() {
					@Override
					public Object run() {
						return getInstantiationStrategy().instantiate(mbd, beanName, parent);
					}
				}, getAccessControlContext());
			}
			else {
				//Constructor construction using reflection calls
				beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
			}
			BeanWrapper bw = new BeanWrapperImpl(beanInstance);
			initBeanWrapper(bw);
			return bw;
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
		}
	}

The instantiate() method is as follows:

public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {
		// If there is no override, do not override the class with CGLIB.
		if (bd.getMethodOverrides().isEmpty()) {
			// Here, get the specified constructor or the factory method of the generated object to instantiate the Bean
			Constructor<?> constructorToUse;
			synchronized (bd.constructorArgumentLock) {
				constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
				if (constructorToUse == null) {
					final Class<?> clazz = bd.getBeanClass();
					if (clazz.isInterface()) {
						throw new BeanInstantiationException(clazz, "Specified class is an interface");
					}
					try {
						if (System.getSecurityManager() != null) {
							constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor<?>>() 
								@Override
								public Constructor<?> run() throws Exception {
									return clazz.getDeclaredConstructor((Class[]) null);
								}
							});
						}
						else {
							constructorToUse =	clazz.getDeclaredConstructor((Class[]) null);
						}
						bd.resolvedConstructorOrFactoryMethod = constructorToUse;
					}
					catch (Throwable ex) {
						throw new BeanInstantiationException(clazz, "No default constructor found", ex);
					}
				}
			}
			// Instantiate through Bean Utils, which instantiates beans through the Constructor
			// You can see the specific call ctor.newInstance(args) in BeanUtils
			return BeanUtils.instantiateClass(constructorToUse);
		}
		else {
			// Instantiating objects using CGLIB
			return instantiateWithMethodInjection(bd, beanName, owner);
		}
	}

2.populateBean()

Let's take another look at populateBean(). This process is designed to handle the properties of various Bean objects. One of these dependencies is the resolved BeanDefinition.

protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
		PropertyValues pvs = mbd.getPropertyValues();

		if (bw == null) {
			if (!pvs.isEmpty()) {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
			}
			else {
				// Skip the property fill phase of an empty instance. 
				return;
			}
		}
		// Modify the state before setting the property for any instantiaawarebeanpostprocessor. For example, it can be used to support the style of field injection. 
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof InstantiationAwareBeanPostProcessor) {
					InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
					if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
						return;
					}
				}
			}
		}
		int resolvedAutowireMode = mbd.getResolvedAutowireMode();
		if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
			MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
			// If applicable, add name based auto assembly attribute values.  
			if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
				autowireByName(beanName, mbd, bw, newPvs);
			}
			// If applicable, add attribute values for type based auto assemblies.  
			if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
				autowireByType(beanName, mbd, bw, newPvs);
			}
			pvs = newPvs;
		}
		boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
		boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
		if (hasInstAwareBpps || needsDepCheck) {
			PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
			if (hasInstAwareBpps) {
				for (BeanPostProcessor bp : getBeanPostProcessors()) {
					if (bp instanceof InstantiationAwareBeanPostProcessor) {
						//@ Value@Autowired The postProcessPropertyValues method of autowiredannotationbeanpostprocessor handles
						InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
						pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
						if (pvs == null) {
							return;
						}
					}
				}
			}
			if (needsDepCheck) {
				checkDependencies(beanName, mbd, filteredPds, pvs);
			}
		}
		applyPropertyValues(beanName, mbd, bw, pvs);
	}

Enter the applyPropertyValues() method

protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
		if (pvs == null || pvs.isEmpty()) {
			return;
		}
		if (System.getSecurityManager() != null && bw instanceof BeanWrapperImpl) {
			((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext());
		}
		MutablePropertyValues mpvs = null;
		List<PropertyValue> original;
		if (pvs instanceof MutablePropertyValues) {
			mpvs = (MutablePropertyValues) pvs;
			if (mpvs.isConverted()) {
				try {
					bw.setPropertyValues(mpvs);
					return;
				}
				catch (BeansException ex) {
					throw new BeanCreationException(
							mbd.getResourceDescription(), beanName, "Error setting property values", ex);
				}
			}
			original = mpvs.getPropertyValueList();
		}
		else {
			original = Arrays.asList(pvs.getPropertyValues());
		}
		TypeConverter converter = getCustomTypeConverter();
		if (converter == null) {
			converter = bw;
		}
		//  Note that the BeanDefinitionValueResolver resolves the BeanDefinition in this valueResolver
		BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);
		// Here, a copy of the parsed value is created, and the data of the copy will be injected into the Bean
		List<PropertyValue> deepCopy = new ArrayList<PropertyValue>(original.size());
		boolean resolveNecessary = false;
		for (PropertyValue pv : original) {
			if (pv.isConverted()) {
				deepCopy.add(pv);
			}
			else {
				String propertyName = pv.getName();
				Object originalValue = pv.getValue();
				Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
				Object convertedValue = resolvedValue;
				boolean convertible = bw.isWritableProperty(propertyName) &&
						!PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
				if (convertible) {
					convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
				}
				// The converted value may be stored in the merged bean definition,
				// In order to avoid re converting each created bean instance. 
				if (resolvedValue == originalValue) {
					if (convertible) {
						pv.setConvertedValue(convertedValue);
					}
					deepCopy.add(pv);
				}
				else if (convertible && originalValue instanceof TypedStringValue &&
						!((TypedStringValue) originalValue).isDynamic() &&
						!(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
					pv.setConvertedValue(convertedValue);
					deepCopy.add(pv);
				}
				else {
					resolveNecessary = true;
					deepCopy.add(new PropertyValue(pv, convertedValue));
				}
			}
		}
		if (mpvs != null && !resolveNecessary) {
			mpvs.setConverted();
		}
		try {
			//This is where dependency injection occurs and will be completed in BeanWrapperImpl
			bw.setPropertyValues(new MutablePropertyValues(deepCopy));
		}
		catch (BeansException ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Error setting property values", ex);
		}
	}

summary

In the process of Bean creation and object dependency injection, dependency injection needs to be completed recursively according to the information in BeanDefinition. As can be seen from the above recursive processes, these recursions take getBean as the entry. One recursion is to find the required Bean in the context system and create the recursive call of Bean, and the other recursion is during dependency injection, By recursively calling the getBean method of the container, the dependent Bean of the current Bean is obtained, and the creation and injection of the dependent Bean are also started. During dependency injection of Bean attributes, the parsing process is also a recursive process. In this way, the creation and injection of beans are completed layer by layer according to the dependencies until the creation of the current Bean is finally completed.
After Bean creation and dependency injection are completed, a series of beans linked by dependencies are established in the IoC container. This Bean is no longer a simple Java object. After the Bean series and dependencies between beans are established, it can be easily used by upper-level applications through the relevant interface methods of the IoC container.

Keywords: Java Spring Back-end

Added by westminster86 on Wed, 03 Nov 2021 23:20:57 +0200