We have seen how bean instances are created. We have seen how bean instances are created. Today we’ll look at the implementation of the populateBean method on one of the key points: dependency injection resolution.
The profile
Mainly from today to explore the parsing of dependency injection to see AbstractAutowireCapableBeanFactory# populateBean method of implementation, the method is mainly divided into the following four points:
- Support for external custom property injection, which controls whether to continue to set property values for the Bean
- Inject properties into PropertyValues (assemble by name or assemble by type)
- Properties of parsing out but not set reprocessing (postProcessPropertyValues)
- Set the PropertyValues in PropertyValues to BeanWrapper
Source code analysis
AbstractAutowireCapableBeanFactory#populateBean
Without further ado, take a look at the source implementation of the populateBean method:
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {.../ / to InstantiationAwareBeanPostProcessors last chance before the property into modified Bean attribute value, can also control whether to fill the Bean
/ / by calling the specific postProcessAfterInstantiation method, if the call returns false, said don't need to continue the dependency injection, returned directly
// Attribute injection can be customized by the user. Implement a InstantiationAwareBeanPostProcessor types such as user's post processor,
/ / and through postProcessAfterInstantiation method to the bean's member variables into the custom information.
if(! mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
if(! ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {return; }}}}// PVS is a MutablePropertyValues instance that implements the PropertyValues interface,
// Provides read/write operations on properties, and deep copy can be implemented by calling constructors
BeanDefinition = BeanDefinition = BeanDefinition = BeanDefinition
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
// The default is 0, which means that all dependency injection needs to be explicitly configured in the XML file
// If the related dependency assembly mode is set, the properties of the Bean will be traversed to complete the corresponding injection by type or name, no additional configuration is required
int resolvedAutowireMode = mbd.getResolvedAutowireMode();
if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
// Set autowiring based on beanName
// ps:
if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
// Autowiring is processed automatically based on the type of the Bean
// ps:
if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}
Whether registered InstantiationAwareBeanPostProcessor / / container
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
// Whether to check dependencies. Default is false
booleanneedsDepCheck = (mbd.getDependencyCheck() ! = AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); PropertyDescriptor[] filteredPds =null;
if (hasInstAwareBpps) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
}
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
// This is where dependency injection is performed on the @autowired tag properties
PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
// Parsed properties that are not set are processed again
pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return; } } pvs = pvsToUse; }}}// This attribute is deprecated in 3.0
if (needsDepCheck) {
// Filter out all attribute editors that need dependency checking
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
checkDependencies(beanName, mbd, filteredPds, pvs);
}
if(pvs ! =null) {
// Finally, the property is injected into the Bean Wrapper instance. The injection is for properties explicitly configured with autowiredbyName or ByType.
/ / for comments, because the AutowiredAnnotationBeanPostProcessor have already completed the injection,
// So it is not executed hereapplyPropertyValues(beanName, mbd, bw, pvs); }}Copy the code
Ok, let’s look at the implementation logic one by one
Support for external custom attribute injection
if(! mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
if(! ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {return; }}}}Copy the code
As you can see, before the set properties, make any implementation InstantiationAwareBeanPostProcessor interface class to have the opportunity to modify the state of the bean. Specific by calling postProcessAfterInstantiation method, if the call returns false, said don’t need to continue the dependency injection, returned directly
The main point here is to allow the user to customize the attribute injection. Implement a InstantiationAwareBeanPostProcessor types such as user’s post processor, and through postProcessAfterInstantiation method to the bean’s member variables into the custom information.
Inject properties into PropertyValues
Note here: Regular, according to the dependency injection pattern complete injection Bean configuration resolvedAutowireMode default is 0, is not to walk the following logic autowireByName or autowireByType, In this injection can see doCreateBean applyMergedBeanDefinitionPostProcessors, detailed in the previous article. The autowireByName method is applied only if autowire=”byName” is declared in the configuration file.
// PVS is a MutablePropertyValues instance that implements the PropertyValues interface,
// Provides read/write operations on properties, and deep copy can be implemented by calling constructors
BeanDefinition = BeanDefinition = BeanDefinition = BeanDefinition
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
// The default is 0, which means that all dependency injection needs to be explicitly configured in the XML file
// If the related dependency assembly mode is set, the properties of the Bean will be traversed to complete the corresponding injection by type or name, no additional configuration is required
int resolvedAutowireMode = mbd.getResolvedAutowireMode();
if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
// Set autowiring based on beanName
// ps:
if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
// Set autowiring based on BeanType
// ps:
if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}
Copy the code
So let’s follow up with the autowireByName method
protected void autowireByName( String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
// Get the name of the non-simple type attribute to inject
String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
for (String propertyName : propertyNames) {
// Check whether there is a bean or BeanDefinition associated with propertyName.
// If so, call the beanFactory.getBean method to get the bean instance
if (containsBean(propertyName)) {
// Get the corresponding bean instance from the container
Object bean = getBean(propertyName);
// Store the parsed bean in the attribute value list PVS
pvs.add(propertyName, bean);
// Register dependencies
registerDependentBean(propertyName, beanName);
// logger
}
// logger}}Copy the code
As you can see, this method mainly gets the name of the property of the non-simple type to be injected, gets the corresponding bean instance from the container based on the property name, and then registers the corresponding dependency.
Now let’s look at getting to the injection of a simple type of attribute names unsatisfiedNonSimpleProperties method
protected String[] unsatisfiedNonSimpleProperties(AbstractBeanDefinition mbd, BeanWrapper bw) {
Set<String> result = new TreeSet<>();
PropertyValues pvs = mbd.getPropertyValues();
PropertyDescriptor[] pds = bw.getPropertyDescriptors();
for (PropertyDescriptor pd : pds) {
if(pd.getWriteMethod() ! =null&&! isExcludedFromDependencyCheck(pd) && ! pvs.contains(pd.getName()) && ! BeanUtils.isSimpleProperty(pd.getPropertyType())) { result.add(pd.getName()); }}return StringUtils.toStringArray(result);
}
Copy the code
This method mainly gets the name of a non-simple type attribute that is not configured in the configuration file.
So what are simple types? What Spring considers simple type attributes are:
- The implementation class of the CharSequence interface, such as String
- Enum
- Date
- URI/URL
- An inherited class of Number, such as Integer/Long
- byte/short/int… Isobasic type
- Locale
- All of the above types of arrays, such as String[], Date[], int[], and so on
In addition to requiring properties of non-simple types, the property is not configured in the configuration file, that is, pvs.contains(pd.getName()) = false. This is the judgment condition in the above source code if.
Let’s look at the autowireByType method for autowiring based on BeanType:
protected void autowireByType( String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
// Get the attribute type converter
TypeConverter converter = getCustomTypeConverter();
if (converter == null) {
converter = bw;
}
// Used to hold the name of the parsed property to be injected
Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
// Get the name of the attribute to inject (non-simple attribute (8 primitive types, characters, URLS, etc.))
String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
for (String propertyName : propertyNames) {
try {
// 获取指定属性名称的属性 Descriptor(Descriptor用来记载属性的getter setter type等情况)
PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
// If the attribute type is Object, it will be ignored and not parsed
if(Object.class ! = pd.getPropertyType()) {// Get the setter method for the property
MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
// For a post-processor that inherits PriorityOrdered, no immediate initialization (hot loading) is allowed
booleaneager = ! PriorityOrdered.class.isInstance(bw.getWrappedInstance());// Create a dependency description to be injected to provide uniform access
DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
// Return all Bean instances to be injected by resolving dependencies based on BeanDefinition of the container
Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
if(autowiredArgument ! =null) {
// Store the parsed bean in the attribute value list PVS
pvs.add(propertyName, autowiredArgument);
}
for (String autowiredBeanName : autowiredBeanNames) {
// Register dependencies
registerDependentBean(autowiredBeanName, beanName);
if (logger.isTraceEnabled()) {
logger.trace("Autowiring by type from bean name '" + beanName + "' via property '" +
propertyName + "' to bean named '" + autowiredBeanName + "'"); }}// Clear records of injected propertiesautowiredBeanNames.clear(); }}catch (BeansException ex) {
throw newUnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex); }}}Copy the code
This method is slightly more complex than the autowireByName method in that the name of the bean corresponding to the attribute is uncertain and is parsed accordingly.
This method also starts by getting the name of the non-simple type attribute, creating a description of the dependency to be injected based on the name, providing consistent access, resolving the dependency based on the BeanDefinition of the container, and returning all the Bean instances to be injected. Finally, the resolved bean is stored in the attribute value list PVS and the registerDependentBean method is executed to register the dependency.
Reprocess parsed but unset properties
@autowired: @autowired: @autowired: @autowired: @autowired: @autowired: @autowired
Whether registered InstantiationAwareBeanPostProcessor / / container
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
// Whether to check the dependencies. The default value is false, which determines whether to check the dependencies later.
booleanneedsDepCheck = (mbd.getDependencyCheck() ! = AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); PropertyDescriptor[] filteredPds =null;
if (hasInstAwareBpps) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
}
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
// Do dependency injection for the @autoWired tag attribute
PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
// Parsed properties that are not set are processed again
pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return; } } pvs = pvsToUse; }}}Copy the code
We follow up postProcessProperties dependency injection method, the realization of his class for: AutowiredAnnotationBeanPostProcessor
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
// Get the meta information for the @autowired annotation in the specified class
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
// Automatically inject the Bean properties
metadata.inject(bean, beanName, pvs);
}
/ / catch...
return pvs;
}
Copy the code
Inject () ¶ We are familiar with the inject method, which is to iterate over each field and inject it, and finally call the Invoke method for reflection processing. Let’s take a look at the meta information of @autowired related annotation in the specified class:
private InjectionMetadata findAutowiringMetadata(String beanName, Class<? > clazz,@Nullable PropertyValues pvs) {
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
// Look in the container to see if there is an Autowire for the given class
InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
if(metadata ! =null) {
metadata.clear(pvs);
}
// Parse the given class @autowired to focus on the meta information
metadata = buildAutowiringMetadata(clazz);
// Store the obtained information about the given class autowire in the container cache
this.injectionMetadataCache.put(cacheKey, metadata); }}}return metadata;
}
Copy the code
As you can see, this method mainly parses the information of the given class @Autowired and stores the information of the given class Autowire in the container cache.
Set the PropertyValues in PropertyValues to BeanWrapper
Here we can see that there is a prejudgment condition PVS! = null, so the method (applyPropertyValues) will only be called if an autowireByName or autowireByType property injection is performed, and the property will eventually be injected into the Wrapper instance of the Bean. The source code is as follows:
if(pvs ! =null) {
applyPropertyValues(beanName, mbd, bw, pvs);
}
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
// Attribute list zise = 0
if (pvs.isEmpty()) {
return;
}
if(System.getSecurityManager() ! =null && bw instanceof BeanWrapperImpl) {
// Set the security context, JDK security mechanism
((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext());
}
MutablePropertyValues mpvs = null;
List<PropertyValue> original;
if (pvs instanceof MutablePropertyValues) {
mpvs = (MutablePropertyValues) pvs;
if (mpvs.isConverted()) {
// If the attribute value has already been converted, assign the value directly
try {
bw.setPropertyValues(mpvs);
return;
}
catch (BeansException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Error setting property values", ex); }}// If it is not converted, record the value of the original type before the conversion
original = mpvs.getPropertyValueList();
}
else {
// If PVS is not of type MutablePropertyValues, use the primitive type directly
original = Arrays.asList(pvs.getPropertyValues());
}
// Get the user's custom type conversion
TypeConverter converter = getCustomTypeConverter();
if (converter == null) {
converter = bw;
}
// Create a Bean definition property value parser that resolves the property value of the Bean definition to the actual value of the Bean instance object
BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);
// Create a copy of the value of the property to be resolved and inject the copy data into the Bean instance
List<PropertyValue> deepCopy = new ArrayList<>(original.size());
boolean resolveNecessary = false;
for (PropertyValue pv : original) {
// Attribute values that do not need conversion are added directly
if (pv.isConverted()) {
deepCopy.add(pv);
}
else {
String propertyName = pv.getName();
// Retain the value of the property before the transformation
Object originalValue = pv.getValue();
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
if (originalValue == AutowiredPropertyMarker.INSTANCE) {
Method writeMethod = bw.getPropertyDescriptor(propertyName).getWriteMethod();
if (writeMethod == null) {
throw new IllegalArgumentException("Autowire marker for property without write method: " + pv);
}
originalValue = new DependencyDescriptor(new MethodParameter(writeMethod, 0), true);
}
// Convert property values, such as a reference to an object instantiated in the container
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
Object convertedValue = resolvedValue;
// Whether the attribute value can be converted
booleanconvertible = bw.isWritableProperty(propertyName) && ! PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);if (convertible) {
// Convert property values using a user-defined type converter
convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
}
// Store the converted property value, avoiding the transformation every time the property is injected
if (resolvedValue == originalValue) {
if (convertible) {
pv.setConvertedValue(convertedValue);
}
deepCopy.add(pv);
}
else if (convertible && originalValue instanceofTypedStringValue && ! ((TypedStringValue) originalValue).isDynamic() && ! (convertedValueinstanceof Collection || ObjectUtils.isArray(convertedValue))) {
pv.setConvertedValue(convertedValue);
deepCopy.add(pv);
}
else {
resolveNecessary = true;
deepCopy.add(newPropertyValue(pv, convertedValue)); }}}if(mpvs ! =null && !resolveNecessary) {
// Tag attributes have been transformed
mpvs.setConverted();
}
try {
// Use the Java reflection mechanism to inject properties into the bean using the set method.
bw.setPropertyValues(new MutablePropertyValues(deepCopy));
}
catch (BeansException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Error setting property values", ex); }}Copy the code
The collective operation is marked above, which is to generate an object of the type that needs to be injected by performing a transformation operation, and then call the setPropertyValues method to inject the property into the bean using Java’s reflection mechanism according to the set method.
This article is based on the spring framework 5.2.10.RELEASE version for analysis