An overview of the
Spring’s property padding is done after the Bean is created by assigning object properties to the populateBean method to initialize the Bean step by step.
Spring attribute padding
The core of the Spring attribute padding process is the implementation of the injection of the dependencies of @value, @Autowired, @Resource and other attributes or method annotations or the lookup and padding process for dependent objects.
@Value
,@Autowired
The processing class is passedAutowiredAnnotationBeanPostProcessor
To deal with.@Resource
Is through theCommonAnnotationBeanPostProcessor
To deal with
The above two classes are also the core classes covered in this article. Fill the entry method DefaultListableBeanFactory# populateBean, step by step, let’s start from entry method of analytic Spring IOC Bean properties filling process.
PopulateBean method
Below is process of populateBean InstantiationAwareBeanPostProcessor processor postProcessAfterinstantiation method (1) line, This method executes to determine whether to continue with the default property padding processing. (2) According to the injection type (byName/byType), extract the dependent bean, And unified in the PropertyValues InstantiationAwareBeanPostProcessor processor postProcessProperties method (3) application, between fill the attributes of the comb again, Typical line AutowiredAnnotationBeanPostProcessor implementation @ Autowried annotation of parsing. (4) Fill the properties in PropertyValues into BeanWrapper. Here is the process in the source code
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
/ /...
/ / in front of the attribute is populated, to rear InstantiationAwareBeanPostProcessor type of processor a chance to modify the state of the bean.
/ / define the type of post processor, can be done through InstantiationAwareBeanPostProcessorAdapter abstract class.
if(! mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
if(! ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {return; }}}}// Extract the prepared values from the Bean definition
// It is a MutablePropertyValues, holding N multiple property values ~~~
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
// Automatic injection mode
int resolvedAutowireMode = mbd.getResolvedAutowireMode();
if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
// by_name looks up the bean by its property name
// By_type looks for beans by attribute type
// After the bean is found, set injection is performed
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
// Add property values based on autowire by name if applicable.
if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
// Add property values based on autowire by type if applicable.
if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
/ / summary:
// Spring automatic injection looks for the bean by the set method of a class. ByName looks for the bean by the property name of the set method
// byType is to find the bean based on the parameters of a set method
// If the current use of annotations, rarely used
}
// After the auto-injection of Spring is completed, we start parsing @autowired. This is called the instantiation callback
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
booleanneedsDepCheck = (mbd.getDependencyCheck() ! = AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);/ / the @autowired annotation AutowiredAnnotationBeanPostProcessor
/ / @ Resource CommonAnnotationBeanPostProcessor annotations
PropertyDescriptor[] filteredPds = null;
if (hasInstAwareBpps) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
}
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
// Call BeanPostProcessor to parse @Autowired, @Resource, and @value for the attributes
// This is where we get the attributes and values of the dependencies from the post-processing. AutowiredAnnotationBeanPostProcessor is out value here ~ ~ ~
PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
if (filteredPds == null) {
filteredPds =filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return; } } pvs = pvsToUse; }}}if (needsDepCheck) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
checkDependencies(beanName, mbd, filteredPds, pvs);
}
if(pvs ! =null) {
// Complete the PVS assignmentapplyPropertyValues(beanName, mbd, bw, pvs); }}Copy the code
autowireByName
Automatic injection is achieved by using the beanName method, which is unique in Spring IOC, so it is most efficient and least complicated.
protected void autowireByName( String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
// Gets the name of a non-simple type attribute that is not configured in the configuration file
String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
for (String propertyName : propertyNames) {
if (containsBean(propertyName)) {
// Look for the bean by its property name, which is called byName
Object bean = getBean(propertyName);
// Assign a value to the attribute
pvs.add(propertyName, bean);
registerDependentBean(propertyName, beanName);
//.... print log
}
else {
//.... print log}}}Copy the code
autowireByType
Beans are obtained by type for injection, and since more than one Bean of the same type can exist in Spring IOC, processing is more complicated than autowireByName.
protected void autowireByType( String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
TypeConverter converter = getCustomTypeConverter();
if (converter == null) {
converter = bw;
}
Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
// Find the property with the corresponding set method
String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
for (String propertyName : propertyNames) {
try {
// Attributes are described
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()) {// The parameters in the set method
MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
// Do not allow eager init for type matching in case of a prioritized post-processor.
// Whether the current bean implements PriorityOrdered
booleaneager = ! (bw.getWrappedInstance()instanceof PriorityOrdered);
DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
// Look for the bean type, in this case, byType
Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
if(autowiredArgument ! =null) {
pvs.add(propertyName, autowiredArgument);
}
for (String autowiredBeanName : autowiredBeanNames) {
registerDependentBean(autowiredBeanName, beanName);
if (logger.isTraceEnabled()) {
logger.trace("Autowiring by type from bean name '" + beanName + "' via property '" +
propertyName + "' to bean named '" + autowiredBeanName + "'"); } } autowiredBeanNames.clear(); }}catch (BeansException ex) {
throw newUnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex); }}}Copy the code
From the above code we can see that the core method here is resolveDependency.
resolveDependency
The resolveDependency method first determines the type of dependency, and then performs corresponding processing. The core method is doResolveDependency.
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
DependencyDescriptor is a property field, a constructor parameter, or a set parameter
BeanFactory = BeanFactory; // Descriptor = BeanFactory
descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
// If this is Optional
if (Optional.class == descriptor.getDependencyType()) {
return createOptionalDependency(descriptor, requestingBeanName);
} else if (ObjectFactory.class == descriptor.getDependencyType() ||
ObjectProvider.class == descriptor.getDependencyType()) {
return new DependencyObjectProvider(descriptor, requestingBeanName);
} else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
} else {
// When using @autowired annotation, you can also use @lazy annotation, which will inject a proxy object
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
descriptor, requestingBeanName);
if (result == null) {
// Parse the descriptor to find the bean object
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
returnresult; }}Copy the code
Let’s move on to the doResolveDependency method
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
try {
/ / if DependencyDescriptor is a ShortcutDependencyDescriptor
BeanName gets a bean from the beanFactory
/ / when using the @autowired annotation to dependency injection using ShortcutDependencyDescriptor to cache for dependency injection
// Cache the beanName of the dependent bean when the dependency information is resolved
Object shortcut = descriptor.resolveShortcut(this);
if(shortcut ! =null) {
return shortcut;
}
// Specify the name of the descriptorClass<? > type = descriptor.getDependencyType();// 1. Obtain the Value configured in the @value annotation
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor); // Check whether it is specified by @value
if(value ! =null) {
if (value instanceof String) {
// First fill the placeholder and parse the "$" symbolString strVal = resolveEmbeddedValue((String) value); BeanDefinition bd = (beanName ! =null && containsBean(beanName) ?
getMergedBeanDefinition(beanName) : null);
// Parse the Spring EL expression, parse the "#" symbol (either an operation or the name of a bean)value = evaluateBeanDefinitionString(strVal, bd); } TypeConverter converter = (typeConverter ! =null ? typeConverter : getTypeConverter());
try {
return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
} catch (UnsupportedOperationException ex) {
// A custom TypeConverter which does not support TypeDescriptor resolution...
return(descriptor.getField() ! =null? converter.convertIfNecessary(value, type, descriptor.getField()) : converter.convertIfNecessary(value, type, descriptor.getMethodParameter())); }}// The @value annotation is not used
//2. The type to be injected is not a Map, Array or Collection
Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
if(multipleBeans ! =null) {
return multipleBeans;
}
// In this case, the value may be a specific instance object, or it may be a Class object temporarily
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
if (matchingBeans.isEmpty()) {
if (isRequired(descriptor)) {
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
}
return null;
}
String autowiredBeanName;
Object instanceCandidate;
// Multiple types are found according to type
if (matchingBeans.size() > 1) {
// If more than one is found, try to identify the only one
autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
if (autowiredBeanName == null) {
if(isRequired(descriptor) || ! indicatesMultipleBeans(type)) {// If multiple dependencies are found and the dependencies are required, or are not arrays or collections or maps
return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
} else {
// In case of an optional Collection/Map, silently ignore a non-unique case:
// possibly it was meant to be an empty collection of multiple regular beans
// (before 4.3 in particular when we didn't even look for collection beans).
return null;
}
}
instanceCandidate = matchingBeans.get(autowiredBeanName);
} else {
// We have exactly one match.
Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
autowiredBeanName = entry.getKey();
instanceCandidate = entry.getValue();
}
if(autowiredBeanNames ! =null) {
autowiredBeanNames.add(autowiredBeanName);
}
if (instanceCandidate instanceof Class) {
// Call beanFactory.getBean () to create the bean object
instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
}
Object result = instanceCandidate;
if (result instanceof NullBean) {
if (isRequired(descriptor)) {
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
}
result = null;
}
if(! ClassUtils.isAssignableValue(type, result)) {throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
}
return result;
} finally{ ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint); }}Copy the code
With our common injection @ the Value, the processing of the @autowired, when we have the same type of Bean multiple results to find the time, let’s look at how to choose the determineAutowireCandidate method implementation.
protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) { Class<? > requiredType = descriptor.getDependencyType();// Select @primary from bean
String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
if(primaryCandidate ! =null) {
return primaryCandidate;
}
// The Bean with the highest Priority is defined by @priority. The smaller the number, the higher the Priority
String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
if(priorityCandidate ! =null) {
return priorityCandidate;
}
// Fallback
for (Map.Entry<String, Object> entry : candidates.entrySet()) {
String candidateName = entry.getKey();
Object beanInstance = entry.getValue();
if((beanInstance ! =null && this.resolvableDependencies.containsValue(beanInstance)) ||
// Based on the attribute name
matchesBeanName(candidateName, descriptor.getDependencyName())) {
returncandidateName; }}return null;
}
Copy the code
DoResolveDependency summary
(1) the first call DefaultListableBeanFactory# doResolveDependency method through property to obtain the corresponding need to inject a Bean. (2) The first step is to parse the @value annotation, and parse the EL expression to get the Bean if it exists. (3) The second step is to parse the @autowired autoinjection annotation. If it exists, parse the annotation first. (4) To find all the candidate beans that meet the conditions through the dependent types. (5) When the number of qualified beans is 0, check whether @autowired’s required attribute is true. If it is true, throw an exception, indicating that no beans can be found. (6) If the match to 1, we directly use. (7) if the match to more than one, we need to make a choice: iterate over all candidate beanName first, determine whether have @ Primary notes, if you have a return, if there are multiple throws an exception NoUniqueBeanDefinitionException; Then iterate through all the candidate beanName, judge whether there is @ Priority annotations, if there is a will, in accordance with the natural return to the first order, if there are multiple Bean Priority is same throws an exception NoUniqueBeanDefinitionException (note: The @order annotation is confusing here; Finally, all the candidate beanName are traversed, and if a match is not selected in either case, the parameter name is read to match the Bean by byName. (8) Put all the fields and attributes that need to be injected into the Bean to complete the attribute injection. (9) Finally, if the attribute PVS exists, then do the assignment
applyPropertyValues
This method mainly implements the application of the value of PVS (1) to detect whether the attribute value list has been transformed. If the attribute value list has been transformed, the attribute is directly populated without the need to transform again. (2) to traverse the attribute value list PVS and parse the originalValue. (4) set the converted PropertyValue to the PropertyValue object. (4) set the converted PropertyValue to the PropertyValue object. (4) set the converted PropertyValue to the PropertyValue object. The PropertyValue object is stored in the deepCopy collection and the property information in the deepCopy is injected into the bean object
// This method passes in beanName and bean definition information, along with its corresponding BeanWrapper and value values
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;
// Check whether the type is MutablePropertyValues
if (pvs instanceof MutablePropertyValues) {
mpvs = (MutablePropertyValues) pvs;
// If all property values in the MPVS have been converted to the corresponding type, set MPVS to BeanWrapper and return
if (mpvs.isConverted()) {
// Shortcut: use the pre-converted values as-is.
try {
bw.setPropertyValues(mpvs);
return;
}
catch (BeansException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Error setting property values", ex); }}// Otherwise, get the property value inside
original = mpvs.getPropertyValueList();
}
else {
original = Arrays.asList(pvs.getPropertyValues());
}
// If the caller does not have a custom converter, BeanWrapper itself is used (because BeanWrapper implements the TypeConverter interface)
TypeConverter converter = getCustomTypeConverter();
if (converter == null) {
converter = bw;
}
/ / get BeanDefinitionValueResolver, the Bean used in Bean definition object contains the value of the resolution is applied to the actual value target Bean instance.
BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);
// Create a deep copy, resolving any references for values.
List<PropertyValue> deepCopy = new ArrayList<>(original.size());
boolean resolveNecessary = false;
// Iterate over the original value that has not been parsed
for (PropertyValue pv : original) {
if (pv.isConverted()) {
deepCopy.add(pv);
}
else {// The parsed PropertyValue will be parsed step by step
String propertyName = pv.getName();
Object originalValue = pv.getValue();
// Parse a variety of values
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
Object convertedValue = resolvedValue;
// Properties are writable and not nested (such as foo.bar, Java with getFoo().getbar ()) or indexed (such as person.addresses[0]) properties
booleanconvertible = bw.isWritableProperty(propertyName) && ! PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);if (convertible) {
// Use a type converter to convert
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) {
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)); }}}// Mark that MPVS has been converted
if(mpvs ! =null && !resolveNecessary) {
mpvs.setConverted();
}
// Set our (possibly massaged) deep copy.
// Fill with the converted values
try {
// Inject the value of the attribute
bw.setPropertyValues(new MutablePropertyValues(deepCopy));
}
catch (BeansException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Error setting property values", ex); }}Copy the code
Spring source parsing
- Spring Startup Process
- The life cycle of Spring Beans
- Spring Attribute Injection
- Spring loop dependencies
- Spring Aop
- Spring transactions
- Spring integration MyBatis
- Spring FAQ