Spring source code analysis - Bean instantiation - attribute injection

Posted by cpjok on Thu, 03 Feb 2022 03:11:04 +0100

preface

If you like my article, please give me a favorable comment. Yours must be the biggest driving force for me to insist on writing. Come on, brothers, give me some driving force

In this chapter, we will discuss the attribute injection in the process of creating beans. During the startup of Spring IOC container, the defined beans will be encapsulated as BeanDefinition and registered in a ConcurrentHashMap. After the Bean registration is completed, the simple and lazy init = false beans will be instantiated. The code for creating the Bean is in AbstractAutowireCapableBeanFactory#doCreateBean. When the Bean is created successfully, the AbstractAutowireCapableBeanFactory#populateBean method will be called for attribute injection. This article mainly analyzes how this method realizes Bean attribute injection.

Here is the flow chart of attribute injection. You can see the code according to this chart later

Attribute injection: AbstractAutowireCapableBeanFactory#populateBean

The main function of AbstractAutowireCapableBeanFactory#populateBean method is attribute filling. The source code is as follows

//Populates the bean instance in a given BeanWrapper with the property values in the bean definition.
	@SuppressWarnings("deprecation")  // for postProcessPropertyValues
	protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
		if (bw == null) {
			//Judge whether there is property attribute
			if (mbd.hasPropertyValues()) {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
			}
			else {
				//No attributes can be populated
				// 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.
		//Let instantiaawarebeanpostprocessors also change the state of the Bean before property injection
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof InstantiationAwareBeanPostProcessor) {
					InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
					if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
						return;
					}
				}
			}
		}
		//Get all PropertyValues from RootBeanDefinition
		PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

		int resolvedAutowireMode = mbd.getResolvedAutowireMode();
		//Get type injection by name
		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 applicable, add auto assemble based attribute values by name.
			if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
				autowireByName(beanName, mbd, bw, newPvs);
			}
			// Add property values based on autowire by type if applicable.
			//If applicable, add auto assemble based attribute values based on the type
			if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
				autowireByType(beanName, mbd, bw, newPvs);
			}
			pvs = newPvs;
		}
		//Has the post processor been registered and initialized
		boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
		//Whether to check the dependency. The default is false
		boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);

		PropertyDescriptor[] filteredPds = null;
		if (hasInstAwareBpps) {
			if (pvs == null) {
				pvs = mbd.getPropertyValues();
			}
			//Get post processor
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				//If Bean instantiates the post processor
				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);
						}
						//Post process the attributes that need dependency check
						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);
		}

		if (pvs != null) {
			//Dependency injection entry: apply attributes to beans
			applyPropertyValues(beanName, mbd, bw, pvs);
		}
	}

Important code in method

  • autowireByName: inject according to attribute name
  • autowireByType: inject beans according to type
  • InstantiationAwareBeanPostProcessor.postProcessPropertyValues: this method processes the given property value before the factory applies it to the given bean, such as the verification of the property in the RequiredAnnotationBeanPostProcessor class.
  • applyPropertyValues: fill in of properties

AbstractAutowireCapableBeanFactory#autowireByName

autowireByName is injected according to the name, and the source code is as follows

protected void autowireByName(
			String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
		//Find the dependent properties in BeanWrapper
		String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
		for (String propertyName : propertyNames) {
			if (containsBean(propertyName)) {
				//Recursively instantiated Bean
				Object bean = getBean(propertyName);
				pvs.add(propertyName, bean);
				//Register the dependent Bean and add it to the dependentBeanMap
				registerDependentBean(propertyName, beanName);
				if (logger.isTraceEnabled()) {
					logger.trace("Added autowiring by name from bean name '" + beanName +
							"' via property '" + propertyName + "' to bean named '" + propertyName + "'");
				}
			}
			else {
				if (logger.isTraceEnabled()) {
					logger.trace("Not autowiring property '" + propertyName + "' of bean '" + beanName +
							"' by name: no matching bean found");
				}
			}
		}
	}

This method is very simple, that is, first find the dependent Bean, recursively initialize it, and then add it to pvs

//An abstract method that defines the behavior of "auto assemble by type" (bean properties by type)
	protected void autowireByType(
			String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
		//Type converter
		TypeConverter converter = getCustomTypeConverter();
		if (converter == null) {
			converter = bw;
		}

		Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
		//Find the attribute to be injected
		String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
		for (String propertyName : propertyNames) {
			try {
				//Attribute description
				PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
				// Don't try autowiring by type for type Object: never makes sense,
				// even if it technically is a unsatisfied, non-simple property.
				if (Object.class != pd.getPropertyType()) {
					//Get the set method of the object
					MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
					// Do not allow eager init for type matching in case of a prioritized post-processor.

					boolean eager = !(bw.getWrappedInstance() instanceof PriorityOrdered);
					//Dependency description
					DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
					//[important] get the value of the dependent attribute and store it in the autowiredBeanNames collection
					//It provides a pair of sets, such as: @ Autowired private list < a > as; Support, according to the type, go to all beans and inject them
					Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
					if (autowiredArgument != null) {
						//Add to pvs
						pvs.add(propertyName, autowiredArgument);
					}
					for (String autowiredBeanName : autowiredBeanNames) {
						//Inject dependent beans
						registerDependentBean(autowiredBeanName, beanName);
						if (logger.isTraceEnabled()) {
							logger.trace("Autowiring by type from bean name '" + beanName + "' via property '" +
									propertyName + "' to bean named '" + autowiredBeanName + "'");
						}
					}
					//Clean up dependencies
					autowiredBeanNames.clear();
				}
			}
			catch (BeansException ex) {
				throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);
			}
		}
	}

Seeing this, we probably know that in fact, in the populateBean method, we will first obtain the Bean's properties (PropertyValues) from the RootBeanDefinition. At the same time, we will automatically inject the mode according to the autowireMode of the RootBeanDefinition to find the dependency of the master Bean according to the name or type.

Both type injection and name injection are to find the dependent attributes of the Bean from the BeanWrapper, and then find the matching Bean according to the attribute type to realize dependency injection. It also provides a pair of sets, such as: @ Autowired private list < a > as; Support for collection injection.

After the property is searched, it will be encapsulated into PropertyValues, and then passed to applyPropertyValues to be applied to the Bean.

AbstractAutowireCapableBeanFactory#applyPropertyValues

We can think that the previous code is looking for the dependent properties for the current Bean and encapsulating them in PropertyValues. In applyPropertyValues, the properties are applied to the current Bean.

//Handle references between objects, using deep copy
	protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
		if (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;
			//Judge whether the value in mpvs has been converted to the corresponding type. After that, you can directly set the value to BeanWrapper
			if (mpvs.isConverted()) {
				// Shortcut: use the pre-converted values as-is.
				try {
					//Set properties for instanced objects
					bw.setPropertyValues(mpvs);
					return;
				}
				catch (BeansException ex) {
					throw new BeanCreationException(
							mbd.getResourceDescription(), beanName, "Error setting property values", ex);
				}
			}
			//Gets the original type of the property value
			original = mpvs.getPropertyValueList();
		}
		else {
			//If the type is not MutablePropertyValues, the native property acquisition method is used
			original = Arrays.asList(pvs.getPropertyValues());
		}
		//Get user-defined type converter
		TypeConverter converter = getCustomTypeConverter();
		if (converter == null) {
			converter = bw;
		}
		//Parser: the Helper class used for bean factory implementation, which parses the value contained in the bean definition object into the actual value applied to the target bean instance.
		BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);

		// Create a deep copy, resolving any references for values.
		//Store the converted attributes, create a new copy of the attribute resolution value of the Bean, and inject the copied data into the object
		List<PropertyValue> deepCopy = new ArrayList<>(original.size());
		boolean resolveNecessary = false;
		//Type conversion, which converts the attribute to the corresponding type
		for (PropertyValue pv : original) {
			if (pv.isConverted()) {
			//The attribute value does not need to be converted
				deepCopy.add(pv);
			}
			else {
				//Attribute name
				String propertyName = pv.getName();
				//The original attribute value, whose type is a reference type such as runtimebeanreference < otherbean >
				Object originalValue = pv.getValue();
				//Convert the attribute value and convert the reference to the instantiated object reference OtherBean in the IOC container
				Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
				Object convertedValue = resolvedValue;
				
				boolean convertible = bw.isWritableProperty(propertyName) &&
						!PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
				if (convertible) {
					//Convert using user-defined converters
					convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
				}
				// Possibly store converted value in merged bean definition,
				// in order to avoid re-conversion for every created bean instance.
				if (resolvedValue == originalValue) {
					if (convertible) {
						//Set the converted value to PV, and set the dependent Bean to PropertyValue
						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;
					//The converted dependent attributes are finally placed in an ArrayList
					deepCopy.add(new PropertyValue(pv, convertedValue));
				}
			}
		}
		if (mpvs != null && !resolveNecessary) {
			mpvs.setConverted();
		}

		// Set our (possibly massaged) deep copy.
		try {
			//Set the parsed property to BeanWrapper
			bw.setPropertyValues(new MutablePropertyValues(deepCopy));
		}
		catch (BeansException ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Error setting property values", ex);
		}
	}

Attribute conversion is mainly carried out here and then applied to beans. For example, in BeanDefinition, attributes may be described by string type, and attributes need to be converted into real original attribute types.

  • First, judge whether the attribute needs to be converted. If not, it is directly applied to the Bean. For example: < property name = "otherbean" ref = "otherbean" / > this property value is actually a string "otherbean", which needs to be resolved into a reference to the otherbean instance in the container.
  • If the attribute value needs type conversion, for example, if the attribute value is another Bean in the container, the referenced object needs to be resolved according to the attribute value, and then injected into the attribute of the object and applied to the Bean.

Resolve the attribute value through the resolveValueIfNecessary() method in the BeanDefinitionValueResolver class, and inject the attribute value through BW Setpropertyvalues() method completed

Resolution: BeanDefinitionValueResolver#resolveValueIfNecessary

Given a PropertyValue, type resolution is performed according to the property value, and references to other bean s in the factory are resolved if necessary

@Nullable
	public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
		// We must check each value to see whether it requires a runtime reference
		// to another bean to be resolved.
		//Resolution of property values that are reference types
		if (value instanceof RuntimeBeanReference) {
			//For example, < property name = "XX" ref = "xxbean" is the reference type, which will go here
			RuntimeBeanReference ref = (RuntimeBeanReference) value;
			//Resolve reference type properties
			return resolveReference(argName, ref);
		}
		///The property value is the resolution of the name of another Bean in the reference container
		else if (value instanceof RuntimeBeanNameReference) {
			String refName = ((RuntimeBeanNameReference) value).getBeanName();
			refName = String.valueOf(doEvaluate(refName));
			//Determine whether this Bean exists in the container
			if (!this.beanFactory.containsBean(refName)) {
				throw new BeanDefinitionStoreException(
						"Invalid bean name '" + refName + "' in bean reference for " + argName);
			}
			return refName;
		}
		else if (value instanceof BeanDefinitionHolder) {
			// Resolve BeanDefinitionHolder: contains BeanDefinition with name and aliases.
			//Resolve BeanDefinitionHolder: contains BeanDefinition with name and alias
			BeanDefinitionHolder bdHolder = (BeanDefinitionHolder) value;
			//Resolve internal Bean
			return resolveInnerBean(argName, bdHolder.getBeanName(), bdHolder.getBeanDefinition());
		}
		else if (value instanceof BeanDefinition) {
			// Resolve plain BeanDefinition, without contained name: use dummy name.
			//Resolve pure BeanDefinition without name
			BeanDefinition bd = (BeanDefinition) value;
			String innerBeanName = "(inner bean)" + BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR +
					ObjectUtils.getIdentityHexString(bd);
			return resolveInnerBean(argName, innerBeanName, bd);
		}
		//Parsing array types
		else if (value instanceof ManagedArray) {
			// May need to resolve contained runtime references.
			ManagedArray array = (ManagedArray) value;
			Class<?> elementType = array.resolvedElementType;
			if (elementType == null) {
				String elementTypeName = array.getElementTypeName();
				if (StringUtils.hasText(elementTypeName)) {
					try {
						elementType = ClassUtils.forName(elementTypeName, this.beanFactory.getBeanClassLoader());
						array.resolvedElementType = elementType;
					}
					catch (Throwable ex) {
						// Improve the message by showing the context.
						throw new BeanCreationException(
								this.beanDefinition.getResourceDescription(), this.beanName,
								"Error resolving array type for " + argName, ex);
					}
				}
				else {
					elementType = Object.class;
				}
			}
			return resolveManagedArray(argName, (List<?>) value, elementType);
		}
		//Resolve collection type
		else if (value instanceof ManagedList) {
			// May need to resolve contained runtime references.
			return resolveManagedList(argName, (List<?>) value);
		}
		//Resolve Set type
		else if (value instanceof ManagedSet) {
			// May need to resolve contained runtime references.
			return resolveManagedSet(argName, (Set<?>) value);
		}
		//Resolve Map type
		else if (value instanceof ManagedMap) {
			// May need to resolve contained runtime references.
			return resolveManagedMap(argName, (Map<?, ?>) value);
		}
		//Parsing Properties
		else if (value instanceof ManagedProperties) {
			Properties original = (Properties) value;
			Properties copy = new Properties();
			original.forEach((propKey, propValue) -> {
				if (propKey instanceof TypedStringValue) {
					propKey = evaluate((TypedStringValue) propKey);
				}
				if (propValue instanceof TypedStringValue) {
					propValue = evaluate((TypedStringValue) propValue);
				}
				if (propKey == null || propValue == null) {
					throw new BeanCreationException(
							this.beanDefinition.getResourceDescription(), this.beanName,
							"Error converting Properties key/value pair for " + argName + ": resolved to null");
				}
				copy.put(propKey, propValue);
			});
			return copy;
		}
		//Resolve attribute values of string type
		else if (value instanceof TypedStringValue) {
			// Convert value to target type here.
			TypedStringValue typedStringValue = (TypedStringValue) value;
			Object valueObject = evaluate(typedStringValue);
			try {
				//Target type
				Class<?> resolvedTargetType = resolveTargetType(typedStringValue);
				if (resolvedTargetType != null) {
					//Resolve the target type
					return this.typeConverter.convertIfNecessary(valueObject, resolvedTargetType);
				}
				else {
					//If the type is not obtained, the Object type is returned
					return valueObject;
				}
			}
			catch (Throwable ex) {
				// Improve the message by showing the context.
				throw new BeanCreationException(
						this.beanDefinition.getResourceDescription(), this.beanName,
						"Error converting typed String value for " + argName, ex);
			}
		}
		else if (value instanceof NullBean) {
			return null;
		}
		else {
			return evaluate(value);
		}
	}


In this method, it is resolved according to the type of attribute value, such as String, array, list, set and map. More complex is that the attribute value depends on a Bean, so you need to find the instance of the Bean in the container according to the name of the dependent Bean, as shown below:

/**
	 * Resolve a reference to another bean in the factory.
	 */
	 //Resolution of associated objects
	@Nullable
	private Object resolveReference(Object argName, RuntimeBeanReference ref) {
		try {
			Object bean;
			//Name of the reference object
			String refName = ref.getBeanName();
			refName = String.valueOf(doEvaluate(refName));
			//If the object is in the parent container, get it from the parent container
			if (ref.isToParent()) {
				if (this.beanFactory.getParentBeanFactory() == null) {
					throw new BeanCreationException(
							this.beanDefinition.getResourceDescription(), this.beanName,
							"Can't resolve reference to bean '" + refName +
									"' in parent factory: no parent factory available");
				}
				//If the object is in the parent container, get it from the parent container
				bean = this.beanFactory.getParentBeanFactory().getBean(refName);
			}
			else {
				//[important] obtain the Bean from the current container according to the name of the dependent Bean
				bean = this.beanFactory.getBean(refName);
				//Suggest the dependency relationship between the instance of the dependent Bean and the current object, and use the dependentBeanMap to maintain the relationship
				this.beanFactory.registerDependentBean(refName, this.beanName);
			}
			if (bean instanceof NullBean) {
				bean = null;
			}
			return bean;
		}
		catch (BeansException ex) {
			throw new BeanCreationException(
					this.beanDefinition.getResourceDescription(), this.beanName,
					"Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex);
		}
	}



 * For each element in the managed array, resolve reference if necessary.
	 */
	 //Parse array
	private Object resolveManagedArray(Object argName, List<?> ml, Class<?> elementType) {
		Object resolved = Array.newInstance(elementType, ml.size());
		for (int i = 0; i < ml.size(); i++) {
			Array.set(resolved, i, resolveValueIfNecessary(new KeyedArgName(argName, i), ml.get(i)));
		}
		return resolved;
	}

	/**
	 * For each element in the managed list, resolve reference if necessary.
	 */
	 //Parse List
	private List<?> resolveManagedList(Object argName, List<?> ml) {
		List<Object> resolved = new ArrayList<>(ml.size());
		for (int i = 0; i < ml.size(); i++) {
			resolved.add(resolveValueIfNecessary(new KeyedArgName(argName, i), ml.get(i)));
		}
		return resolved;
	}

	/**
	 * For each element in the managed set, resolve reference if necessary.
	 */
	 //Parse set
	private Set<?> resolveManagedSet(Object argName, Set<?> ms) {
		Set<Object> resolved = new LinkedHashSet<>(ms.size());
		int i = 0;
		for (Object m : ms) {
			resolved.add(resolveValueIfNecessary(new KeyedArgName(argName, i), m));
			i++;
		}
		return resolved;
	}

	/**
	 * For each element in the managed map, resolve reference if necessary.
	 */
	 //Parse Map
	private Map<?, ?> resolveManagedMap(Object argName, Map<?, ?> mm) {
		Map<Object, Object> resolved = new LinkedHashMap<>(mm.size());
		mm.forEach((key, value) -> {
			Object resolvedKey = resolveValueIfNecessary(argName, key);
			Object resolvedValue = resolveValueIfNecessary(new KeyedArgName(argName, key), value);
			resolved.put(resolvedKey, resolvedValue);
		});
		return resolved;
	}

After the property value is parsed, it is encapsulated into a MutablePropertyValues through BeanWrapperImpl The setpropertyvalues () method completes the value injection, and the injection method in BeanWrapperImpl is completed by AbstractPropertyAccessor#setPropertyValue(java.lang.String, java.lang.Object).

AbstractPropertyAccessor#setPropertyValue

@Override
	public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
			throws BeansException {

		List<PropertyAccessException> propertyAccessExceptions = null;
		//Get all the attribute lists
		List<PropertyValue> propertyValues = (pvs instanceof MutablePropertyValues ?
				((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));
		for (PropertyValue pv : propertyValues) {
			try {
				// This method may throw any BeansException, which won't be caught
				// here, if there is a critical failure such as no matching field.
				// We can attempt to deal only with less serious exceptions.
				//Set attribute value
				setPropertyValue(pv);
			}
			...ellipsis...

@Override
	public void setPropertyValue(String propertyName, @Nullable Object value) throws BeansException {
		//Property accessor
		AbstractNestablePropertyAccessor nestedPa;
		try {
			nestedPa = getPropertyAccessorForPropertyPath(propertyName);
		}
		catch (NotReadablePropertyException ex) {
			throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName,
					"Nested property in path '" + propertyName + "' does not exist", ex);
		}
		//Property assistant
		PropertyTokenHolder tokens = getPropertyNameTokens(getFinalPath(nestedPa, propertyName));
		//Setting values for properties through property accessors
		nestedPa.setPropertyValue(tokens, new PropertyValue(propertyName, value));
	}

It can be seen here that the property injection is completed by the AbstractNestablePropertyAccessor property accessor

protected void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException {
		if (tokens.keys != null) {
			processKeyedProperty(tokens, pv);
		}
		else {
			//Come here
			processLocalProperty(tokens, pv);
		}
	}

private void processLocalProperty(PropertyTokenHolder tokens, PropertyValue pv) {
		//Attribute processor
		PropertyHandler ph = getLocalPropertyHandler(tokens.actualName);
		if (ph == null || !ph.isWritable()) {
			if (pv.isOptional()) {
				if (logger.isDebugEnabled()) {
					logger.debug("Ignoring optional value for property '" + tokens.actualName +
							"' - property not found on bean class [" + getRootClass().getName() + "]");
				}
				return;
			}
			else {
				throw createNotWritablePropertyException(tokens.canonicalName);
			}
		}

		Object oldValue = null;
		try {
			//Native value
			Object originalValue = pv.getValue();
			Object valueToApply = originalValue;
			...ellipsis...
			//This is the key point. Set the property value to the object through PropertyHandler
			ph.setValue(valueToApply);
		}
		catch (TypeMismatchException ex) {
			throw ex;
		}

The property value here is set through PropertyHandler

@Override
	public void setValue(@Nullable Object value) throws Exception {
		//Get the set method of the attribute
		Method writeMethod = (this.pd instanceof GenericTypeAwarePropertyDescriptor ?
				((GenericTypeAwarePropertyDescriptor) this.pd).getWriteMethodForActualAccess() :
				this.pd.getWriteMethod());
		if (System.getSecurityManager() != null) {
			AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
				ReflectionUtils.makeAccessible(writeMethod);
				return null;
			});
			try {
				AccessController.doPrivileged((PrivilegedExceptionAction<Object>)
						() -> writeMethod.invoke(getWrappedInstance(), value), acc);
			}
			catch (PrivilegedActionException ex) {
				throw ex.getException();
			}
		}
		else {
			//Set access rights
			ReflectionUtils.makeAccessible(writeMethod);
			//Call the set method to set the attribute value
			writeMethod.invoke(getWrappedInstance(), value);
		}
	}

Here is the set method to get the attribute, and then call the set method to inject the value into it.

Constructor injection parameters

When analyzing the creation of Bean before, we said that in AbstractAutowireCapableBeanFactory#createBeanInstance, the constructor of the Bean will be obtained through reflection. If it is a parametric construction, we will use the autowireConstructor method to create an instance through parametric construction

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
		// Make sure bean class is actually resolved at this point.
		Class<?> beanClass = resolveBeanClass(mbd, beanName);

		...ellipsis...
		// Need to determine the constructor...
		Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
		if (ctors != null ||
				mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
				mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args))  {
				//[important] constructor injection parameters
			return autowireConstructor(beanName, mbd, ctors, args);
		}

		// No special handling: simply use no-arg constructor.
		return instantiateBean(beanName, mbd);
	}

In the autowireConstructor method, the constructor parameters will be resolved through the ConstructorResolver

protected BeanWrapper autowireConstructor(
			String beanName, RootBeanDefinition mbd, @Nullable Constructor<?>[] ctors, @Nullable Object[] explicitArgs) {
		//Constructor, parser, injection
		return new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs);
	}


public BeanWrapper autowireConstructor(final String beanName, final RootBeanDefinition mbd,
			@Nullable Constructor<?>[] chosenCtors, @Nullable final Object[] explicitArgs) {

		...ellipsis...
			else {
				//Get constructor parameters
				ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
				//Get constructor parameter value
				resolvedValues = new ConstructorArgumentValues();
				//Analytical parameter value [important]
				minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
			}
		...ellipsis...
try {
			//Instantiation strategy
			final InstantiationStrategy strategy = beanFactory.getInstantiationStrategy();
			Object beanInstance;

			if (System.getSecurityManager() != null) {
				final Constructor<?> ctorToUse = constructorToUse;
				final Object[] argumentsToUse = argsToUse;
				beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
						strategy.instantiate(mbd, beanName, beanFactory, ctorToUse, argumentsToUse),
						beanFactory.getAccessControlContext());
			}
			else {
				//Instantiate an object and create an instance using reflection according to the parameterized constructor
				beanInstance = strategy.instantiate(mbd, beanName, this.beanFactory, constructorToUse, argsToUse);
			}

			bw.setBeanInstance(beanInstance);
			return bw;
		}

The resolveConstructorArguments method uses BeanDefinitionValueResolver to resolve the attribute value. If there is a parameter value, it will go through reflection, and the instance will be created and returned according to the parameter constructor.

private int resolveConstructorArguments(String beanName, RootBeanDefinition mbd, BeanWrapper bw,
			ConstructorArgumentValues cargs, ConstructorArgumentValues resolvedValues) {

		TypeConverter customConverter = this.beanFactory.getCustomTypeConverter();
		TypeConverter converter = (customConverter != null ? customConverter : bw);
		//Parser for attribute values
		BeanDefinitionValueResolver valueResolver =
				new BeanDefinitionValueResolver(this.beanFactory, beanName, mbd, converter);

		...ellipsis...
		for (ConstructorArgumentValues.ValueHolder valueHolder : cargs.getGenericArgumentValues()) {
			if (valueHolder.isConverted()) {
				resolvedValues.addGenericArgumentValue(valueHolder);
			}
			else {
			//Parsing the constructor parameter value is the same as the attribute value parsing of set injection previously analyzed
				Object resolvedValue =
						valueResolver.resolveValueIfNecessary("constructor argument", valueHolder.getValue());
				ConstructorArgumentValues.ValueHolder resolvedValueHolder = new ConstructorArgumentValues.ValueHolder(
						resolvedValue, valueHolder.getType(), valueHolder.getName());
				resolvedValueHolder.setSource(valueHolder);
				resolvedValues.addGenericArgumentValue(resolvedValueHolder);
			}
		}

		return minNrOfArgs;
	}

Because the resolveValueIfNecessary method has been analyzed before, I won't talk about it here.

summary

Here, the process of attribute injection is analyzed and summarized in this figure

If you like it, give me a high praise. Your must be the biggest driving force for me to insist on writing. Come on, brothers, give me some driving force

Topics: Spring DI