review
In the previous article, we analyzed how to create a regular bean. We returned to the doCreateBean method in our AbstractAutowireCapableBeanFactory. Now we have generated the beanWrapper
//First, it depends on whether a singleton is allowed, whether circular dependency is allowed, and whether the bean is being created //To determine whether exposure is required boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { //If exposure is required, turn on the log if (logger.isTraceEnabled()) { logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } //Put beanName and its ObjectFactory into SingleFactory, that is, cache addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); }
addSingletonFactory method
The source code is as follows
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(singletonFactory, "Singleton factory must not be null"); //Lock synchronized (this.singletonObjects) { //Determine whether the cache exists //If it doesn't exist if (!this.singletonObjects.containsKey(beanName)) { //If not in the L1 cache, add beanName and its beanfactory to the L3 cache this.singletonFactories.put(beanName, singletonFactory); //Remove beanName from L2 cache this.earlySingletonObjects.remove(beanName); //The beanName is stored in the registered singleton this.registeredSingletons.add(beanName); } } }
As you can see, the function of this method is to add the generated singleton bean to the L3 cache for exposure, and identify that the bean has been registered
The reason why the bean is exposed in advance here is that the bean has not been created yet. The beanWrapper is only generated through the constructor. The dependent bean has not been created yet, so the bean here has not been created yet (lack of creation of dependent bean). Therefore, it is necessary to expose in advance to detect whether circular dependency occurs to the dependent bean
populateBean method
At this point, we have exposed the beanWrapper. Next, we need to use the populateBean method to inject attributes
The source code is as follows
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { //If beanWrapper is null //If the instance at this time is null, property injection cannot be performed if (bw == null) { //Judge whether there are prototype values waiting for injection in RootBeanDefinition //If yes, throw it wrong because it cannot be injected if (mbd.hasPropertyValues()) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance"); } else { // Skip property population phase for null instance. //If not, return directly 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. // In the instance aware bean postprocessors, give one last chance to change the beanwrapper if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) { if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { return; } } } //Start attribute injection //Gets the property to inject PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null); //Gets the pattern of RootBeanDefinition injection int resolvedAutowireMode = mbd.getResolvedAutowireMode(); //You can see the familiar byName and byType if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) { MutablePropertyValues newPvs = new MutablePropertyValues(pvs); // Add property values based on autowire by name if applicable. //Property injection based on byName if (resolvedAutowireMode == AUTOWIRE_BY_NAME) { autowireByName(beanName, mbd, bw, newPvs); } // Add property values based on autowire by type if applicable. //Attribute injection according to byType if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) { autowireByType(beanName, mbd, bw, newPvs); } //Store the extracted dependent bean s in newPvs pvs = newPvs; } //Determine whether the subsequent processor has been initialized boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors(); //Determine whether dependency check is required boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); PropertyDescriptor[] filteredPds = null; if (hasInstAwareBpps) { //If the subsequent processor has been initialized if (pvs == null) { pvs = mbd.getPropertyValues(); } // for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) { PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName); if (pvsToUse == null) { if (filteredPds == null) { filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); } //Post process all attributes requiring dependency check, but there is no dependency check at this time pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); if (pvsToUse == null) { return; } } pvs = pvsToUse; } } //Type check if required if (needsDepCheck) { if (filteredPds == null) { filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); } //Dependency check corresponds to the dependencies on attribute, which has been deprecated in 3.0 checkDependencies(beanName, mbd, filteredPds, pvs); } if (pvs != null) { //Apply properties to bean s applyPropertyValues(beanName, mbd, bw, pvs); } }
Summarize the function of the populateBan method
- Call the postprocessafterinstance function of the instantiaawarebeanpostprocessor processor
- Extract the dependent bean s according to the injected type, byType or byName, and store them in PropertyValues
- Apply the postProcessPropertyValues method processed by InstantiatinAwareBeanPostProcessor to process the injected properties again
- Fill the found properties into BeanWrapper, that is, PropertyValues
The focus here is on the injection of the second and fourth steps. Other feelings are too difficult to see...
Extract dependent bean s according to the injected type
There are two types of injection
- byType
- byName
We can set the injection type for the bean
<bean id = "" class = "" autowire="byType|byName|constructor|default"/>
The automatic injection here is to automatically inject the dependent bean, not itself...
The corresponding code is as follows
AUTOWIRE_BY_NAME
This is an automatic injection method based on the name, that is, the dependent name attribute
The source code is as follows
protected void autowireByName( String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) { //Get the dependency attributes that need to be injected. Note that this is not a simple type, that is, the basic data type String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw); //Traverse these dependent properties for (String propertyName : propertyNames) { //Determine whether this bean exists in the IOC container of Spring //You can see that this is the ByName form if (containsBean(propertyName)) { //Call the getBean method to create //You can see how dependency is created recursively in singleton //And how to detect cyclic dependencies Object bean = getBean(propertyName); //Add the generated bean to the new PropertyValues pvs.add(propertyName, bean); //Register the bean 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"); } } } }
The steps are as follows
- Get the properties that you want to depend on
- Traverse the properties that you need to depend on
- Check whether the bean exists in the IOC container according to the bean name of the dependent attribute, reflecting ByName
- If the bean does not exist, an error is thrown, indicating that no matching bean is found
- If the bean exists, call the getBean method to create the dependent bean (go through that process again, so in the final analysis, creating a bean is a recursive process)
- Put the created bean into PropertyValues
- Register the bean
The overall logic is nothing. Let's see how to obtain the dependent attributes
unsatisfiedNonSimpleProperties
Corresponding to the specific method of obtaining dependent attributes, this method will actively exclude simple attributes, namely reference types and basic data types
The source code is as follows
protected String[] unsatisfiedNonSimpleProperties(AbstractBeanDefinition mbd, BeanWrapper bw) { //Create a treeSet for de duplication Set<String> result = new TreeSet<>(); //Get all dependencies PropertyValues pvs = mbd.getPropertyValues(); PropertyDescriptor[] pds = bw.getPropertyDescriptors(); // for (PropertyDescriptor pd : pds) { //Only the set method is written //Is not in a collection that does not check dependencies //Not a simple type if (pd.getWriteMethod() != null && !isExcludedFromDependencyCheck(pd) && !pvs.contains(pd.getName()) && !BeanUtils.isSimpleProperty(pd.getPropertyType())) { //Add it after meeting the conditions result.add(pd.getName()); } } return StringUtils.toStringArray(result); }
The code is more abstract, so I decided to take chestnuts as an example
A configuration file is simply an injection type
Class is also very simple. It simply provides a set method injection
Let's look directly at what those two sets are
PropertyDescriptor[] pds = bw.getPropertyDescriptors();
give the result as follows
There are two objects in it
One is the bean's own class without writeMethod
Another one is about twoPojo, and there is a formal set method called writeMethod!!!!
Therefore, it can be inferred that this collection obtains the attribute details that need to be relied on
PropertyValues pvs = mbd.getPropertyValues();
The contents are as follows
As you can see, it is completely the information in the configuration file....
Therefore, this collection stores the dependent attribute information in the configuration file
Now, it's easy to see what this for loop eliminates
Back to our for loop
for (PropertyDescriptor pd : pds) { //Only the set method is written //Is not in a collection that does not check dependencies //Not a simple type //And there is no beanName in the configuration file, //If there is in the configuration file, just go to the IOC container and get it. There is no need for automatic injection //Instead of injection if (pd.getWriteMethod() != null && !isExcludedFromDependencyCheck(pd) && !pvs.contains(pd.getName()) && !BeanUtils.isSimpleProperty(pd.getPropertyType())) { //Add it after meeting the conditions result.add(pd.getName()); } }
You can see the result. The result is 0
Suppose we remove the property of the configuration file
You can see the result with twoPOJO
Let's take a look at what BeanUtils filters out
You can see some types of filtering
-
void type
-
isPrimitiveOrWrapper: judge whether it is a wrapper class, that is, a filter wrapper class
-
Enum type
-
Date type
-
URI type
-
URL type
-
Locale type
-
Class type
-
CharSequence type
To summarize, which dependent bean s will be used
- Dependencies that do not write the Set method are filtered
- Dependencies in collections that are not checked are filtered
- The type of injection that has been specified in the configuration file will be filtered
- Simple types are also filtered
AUTOWIRE_BY_TYPE
There is also an automatic injection type, which is to find according to the type
The corresponding codes are as follows
The corresponding method is autowireByType
The source code is as follows
protected void autowireByType( String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) { //Get type processor TypeConverter converter = getCustomTypeConverter(); if (converter == null) { converter = bw; } //Use a LinkedHashSet to store the bean name of the bean that needs to be automatically injected //Previously, beanName was stored using a set for de duplication, and then converted into a string array Set<String> autowiredBeanNames = new LinkedHashSet<>(4); //You can see that filtering is also required here, so I won't repeat it String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw); //Traversal of dependencies to be injected for (String propertyName : propertyNames) { try { 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. //Judge whether the dependency is of Object type (Note: Object type is not filtered in filtering) if (Object.class != pd.getPropertyType()) { //Get the parameters of the bean's set method.. //It is equivalent to getting the type.. Reflects ByType 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); DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager); //Resolve the to that matches the property of the specified beanName, and store the resolved property name in autowiredBeanNames Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter); if (autowiredArgument != null) { pvs.add(propertyName, autowiredArgument); } //Traverse the bean to be injected for (String autowiredBeanName : autowiredBeanNames) { //Registration dependency 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 new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex); } } }
Two details can be seen here
- Spring does not inject ByType for Object type
- The Type is obtained through the parameters of the Set method in the past. The parameter is actually Type, which can be seen as the embodiment of ByType
- Create dependencies through resolveDependency
resolveDependency
It can be seen that the creation of ByType dependency is implemented by DefaultListableBeanFactory, while the creation of ByName dependency is implemented by AbstractAutowireCapableFactory
The source code is as follows
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { // descriptor.initParameterNameDiscovery(getParameterNameDiscoverer()); //Extended support for the Optional class if (Optional.class == descriptor.getDependencyType()) { //The Optional class is created using methods alone return createOptionalDependency(descriptor, requestingBeanName); } //ObjectFactory and ObjectProvider are also created by separate methods else if (ObjectFactory.class == descriptor.getDependencyType() || ObjectProvider.class == descriptor.getDependencyType()) { return new DependencyObjectProvider(descriptor, requestingBeanName); } //Handle JavaInjectProviderClass separately else if (javaxInjectProviderClass == descriptor.getDependencyType()) { return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName); } else { //For other general types //Use autowirecandidate resolver for injection Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary( descriptor, requestingBeanName); if (result == null) { //If autpwirecandideresolver injection fails, use doResolveDependency for injection result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter); } return result; } }
It can be seen that for some specific types, other types will take the same steps (most of them)
getLazyResollutionProxyIfNecessary
For general types, this method will be used first
From the annotation of this method, it feels like adding a proxy to the pointcut..
However, its default implementation directly returns Null, so don't look at it first.
doResolveDependency
Next, execute this method for processing
The source code is as follows. You can see that it is very scary...
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor); try { Object shortcut = descriptor.resolveShortcut(this); if (shortcut != null) { return shortcut; } //Get the class object, that is, the parameters obtained through the set method Class<?> type = descriptor.getDependencyType(); //New @ Value annotation is supported //@The Value annotation can be used directly on the class, so the annotation is obtained according to the class //Then go to the corresponding configuration for injection Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor); //Judge whether @ Value is injected //So ByName will not be injected by @ Value???? if (value != null) { //If yes, the following actions will be performed if (value instanceof String) { String strVal = resolveEmbeddedValue((String) value); BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null); 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())); } } //Consider whether this attribute is of multiple beans, that is, to handle collection types Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter); if (multipleBeans != null) { return multipleBeans; } //Find all candidates 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; //Judge the number of candidates if (matchingBeans.size() > 1) { //If there are two or more meanings, do the following //Decide which candidate to use autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor); //Judge whether it can be decided if (autowiredBeanName == null) { // if (isRequired(descriptor) || !indicatesMultipleBeans(type)) { 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 { //If there is only one candidate value, proceed to the following processing // We have exactly one match. //To get the only candidate Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next(); //Use candidate's properties //beanName is key //The dependent object is the candidate's value autowiredBeanName = entry.getKey(); instanceCandidate = entry.getValue(); } //Add to collection if (autowiredBeanNames != null) { autowiredBeanNames.add(autowiredBeanName); } if (instanceCandidate instanceof Class) { instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this); } //At this time, let result be the candidate Object result = instanceCandidate; //Then judge whether it is a NullBean type 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 results return result; } finally { ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint); } }
It's so complicated. I'm going to vomit......
Summarize the general process
-
Get the Class type by parsing the parameters obtained from the set method
-
Use this Class type to determine whether it is identified by the @ Value annotation
- If so, take another logic
- If not, the representative still needs to inject
- Judge whether it is a collection type. If it is a collection type, go through the processing of collection type
- If it is not a collection type, get all candidates
- Judge the number of candidates
- If there are more than two candidates
- Judging which candidate to use is another way if you can't decide
- If the decision is made, the candidate depends on the Bean instance
- If there is only one candidate, use the candidate directly
- If there are more than two candidates
- Then do some space judgment on the generated candidates
-
Return results
The general idea is like this.... It's too difficult to study it carefully next time
After that, we have completed the acquisition of all injected properties, but the obtained properties exist in the form of PropertyValue and have not been applied to the instantiated bean
Injection properties
Back to the populate method, now all dependencies have been created and waiting to be injected into the bean. The corresponding execution method is the applyPropertyValues method, which is also the last execution method of the populateBean
The source code is as follows
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) { //If the dependency is empty, return directly 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 dependency has been converted to the corresponding type if (mpvs.isConverted()) { // Shortcut: use the pre-converted values as-is. try { //If it is converted to the corresponding type, it is directly loaded into BeanWrapper bw.setPropertyValues(mpvs); //End return return; } catch (BeansException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Error setting property values", ex); } } original = mpvs.getPropertyValueList(); } else { //Put dependencies in original original = Arrays.asList(pvs.getPropertyValues()); } // TypeConverter converter = getCustomTypeConverter(); if (converter == null) { converter = bw; } //Get the corresponding parser BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter); // Create a deep copy, resolving any references for values. //Use an ArrayList to store dependent deep copy objects List<PropertyValue> deepCopy = new ArrayList<>(original.size()); boolean resolveNecessary = false; //Traverse all dependencies for (PropertyValue pv : original) { //Judge whether the dependency has been converted if (pv.isConverted()) { //If it has been converted, it will be directly added to the collection deepCopy.add(pv); } else { //Get dependent name, variable name String propertyName = pv.getName(); //Get the dependent value, which is already an object Object originalValue = pv.getValue(); //If the value is AutowiredPropertyMarker //I'm not sure what's going on 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); } //Parsing using a parser Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue); //Put the parsing result in the convertedValue variable Object convertedValue = resolvedValue; boolean convertible = bw.isWritableProperty(propertyName) && !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName); if (convertible) { //Transform 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); } //Add to deep copy collection 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(); } // Set our (possibly massaged) deep copy. try { //Finally, save all the dependencies of the deep copy into the beanWrapper bw.setPropertyValues(new MutablePropertyValues(deepCopy)); } catch (BeansException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Error setting property values", ex); } }
The injection attribute is also difficult to understand. The general process is
- Judge whether all dependencies have been transformed
- If they are all converted, they can be directly added to beanWrapper
- If the conversion is not good, it is necessary to make a deep copy of the dependency
- Get the dependent variable name and dependent value. The value is usually a Spring customized RuntimeBeanReference
- Transform using parser
- Add the converted value to the deepCopy set, which is an ArrayList
- Finally, save the deepCopy collection into beanWrapper
Therefore, the focus is on parsing and saving the parsed dependencies into beanWrapper. Let's see how to parse them
valueResolver.resolveValueIfNecessary(pv, originalValue);
This method is used for parsing
The source code is as follows
@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. //Determine whether it is a RuntimeBeanReference if (value instanceof RuntimeBeanReference) { RuntimeBeanReference ref = (RuntimeBeanReference) value; return resolveReference(argName, ref); } //Determine whether it is a RuntimeBeanNameReference else if (value instanceof RuntimeBeanNameReference) { String refName = ((RuntimeBeanNameReference) value).getBeanName(); refName = String.valueOf(doEvaluate(refName)); if (!this.beanFactory.containsBean(refName)) { throw new BeanDefinitionStoreException( "Invalid bean name '" + refName + "' in bean reference for " + argName); } return refName; } //Judge whether it is BeanDefinitinHolder else if (value instanceof BeanDefinitionHolder) { // Resolve BeanDefinitionHolder: contains BeanDefinition with name and aliases. BeanDefinitionHolder bdHolder = (BeanDefinitionHolder) value; return resolveInnerBean(argName, bdHolder.getBeanName(), bdHolder.getBeanDefinition()); } //Judge whether it is BeanDefinition else if (value instanceof BeanDefinition) { // Resolve plain BeanDefinition, without contained name: use dummy name. BeanDefinition bd = (BeanDefinition) value; String innerBeanName = "(inner bean)" + BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(bd); return resolveInnerBean(argName, innerBeanName, bd); } //Determine whether it is a DependencyDescriptor else if (value instanceof DependencyDescriptor) { Set<String> autowiredBeanNames = new LinkedHashSet<>(4); Object result = this.beanFactory.resolveDependency( (DependencyDescriptor) value, this.beanName, autowiredBeanNames, this.typeConverter); for (String autowiredBeanName : autowiredBeanNames) { if (this.beanFactory.containsBean(autowiredBeanName)) { this.beanFactory.registerDependentBean(autowiredBeanName, this.beanName); } } return result; } //Determine whether it is ManagedArray 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); } //Determine whether it is ManagedList else if (value instanceof ManagedList) { // May need to resolve contained runtime references. return resolveManagedList(argName, (List<?>) value); } //Judge whether it is ManagedSet else if (value instanceof ManagedSet) { // May need to resolve contained runtime references. return resolveManagedSet(argName, (Set<?>) value); } //Judge whether it is ManagedMap else if (value instanceof ManagedMap) { // May need to resolve contained runtime references. return resolveManagedMap(argName, (Map<?, ?>) value); } //Determine whether it is ManagedProperties 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; } //Determine whether it is TypeStringValue else if (value instanceof TypedStringValue) { // Convert value to target type here. TypedStringValue typedStringValue = (TypedStringValue) value; Object valueObject = evaluate(typedStringValue); try { Class<?> resolvedTargetType = resolveTargetType(typedStringValue); if (resolvedTargetType != null) { return this.typeConverter.convertIfNecessary(valueObject, resolvedTargetType); } else { 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); } } //Determine whether it is a NullBean else if (value instanceof NullBean) { return null; } //If none of the above types else { return evaluate(value); } }
As you can see, the injected attribute is distinguished according to the type of the value passed in
- Of type RuntimeBeanReference
- Of type RuntimeBeanNameReference
- Of type BeanDefinitionHolder
- Of type BeanDefiniton
- Of type DependencyDescriptor
- Of type ManagedArray
- Amount of ManagedList type
- Of type ManagedSet
- ManagedMap type
- Of type ManagedProperty
- Of NullBean type
- Other types. Other types here are already the corresponding class types
Generally, an already corresponding type is passed in
That will directly correspond to the final evaluate method
The source code is as follows
@Nullable protected Object evaluate(@Nullable Object value) { //Processing strings //The main processing is to parse the string into a Bean object if (value instanceof String) { return this.doEvaluate((String)value); } //Processing string arrays else if (value instanceof String[]) { String[] values = (String[])((String[])value); boolean actuallyResolved = false; Object[] resolvedValues = new Object[values.length]; //In fact, it is to traverse the string array and process each string for(int i = 0; i < values.length; ++i) { String originalValue = values[i]; Object resolvedValue = this.doEvaluate(originalValue); if (resolvedValue != originalValue) { actuallyResolved = true; } resolvedValues[i] = resolvedValue; } return actuallyResolved ? resolvedValues : values; } //It is neither a string nor a string array. It directly returns value, that is, the created object //It usually returns directly here else { return value; } }
You can see that this method does special processing for String and String array, but String type and String array type are impossible here... Because the front has been filtered out
bw.setPropertyValues
This method is to save the dependency into beanWrapper
You can see that all the dependencies to be injected are stored in deepCopy
After entering this method, you can see that this method is implemented by AbstractPropertyAccessor, and the overloaded method is called
The source code is as follows
@Override public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid) throws BeansException { //Storage exception List<PropertyAccessException> propertyAccessExceptions = null; //Turn the passed dependency into a List List<PropertyValue> propertyValues = (pvs instanceof MutablePropertyValues ? ((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues())); //This parameter is false, regardless of the following steps if (ignoreUnknown) { this.suppressNotWritablePropertyException = true; } try { //Traverse all dependencies passed in for (PropertyValue pv : propertyValues) { // setPropertyValue 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. try { //Inject setPropertyValue(pv); } //The following are all exception handling. I don't want to see it catch (NotWritablePropertyException ex) { if (!ignoreUnknown) { throw ex; } // Otherwise, just ignore it and continue... } catch (NullValueInNestedPathException ex) { if (!ignoreInvalid) { throw ex; } // Otherwise, just ignore it and continue... } //Statistics PropertyAccessException exception catch (PropertyAccessException ex) { if (propertyAccessExceptions == null) { propertyAccessExceptions = new ArrayList<>(); } propertyAccessExceptions.add(ex); } } } finally { if (ignoreUnknown) { this.suppressNotWritablePropertyException = false; } } //Here is the statistics of PropertyAccessException //Finally, all are thrown // If we encountered individual exceptions, throw the composite exception. if (propertyAccessExceptions != null) { PropertyAccessException[] paeArray = propertyAccessExceptions.toArray(new PropertyAccessException[0]); throw new PropertyBatchUpdateException(paeArray); } }
It can be seen that this method is to traverse all dependencies, and then execute setProperty method for each dependency. The key is setProperty method
After clicking in, you will find that IDEA will jump here
But in fact, this method is rewritten by AbstractNestablePropertyAccessor!!! So it's not the logic here. It won't come in here
So we should click here to continue breakpoint debugging
The source code of this method is as follows
@Override public void setPropertyValue(PropertyValue pv) throws BeansException { PropertyTokenHolder tokens = (PropertyTokenHolder) pv.resolvedTokens; //Judge whether there is a token. Ordinary injections will not come here if (tokens == null) { String propertyName = pv.getName(); 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); } tokens = getPropertyNameTokens(getFinalPath(nestedPa, propertyName)); if (nestedPa == this) { pv.getOriginalPropertyValue().resolvedTokens = tokens; } nestedPa.setPropertyValue(tokens, pv); } else { //Usually I come in here setPropertyValue(tokens, pv); } }
After coming in, there is another overloaded method
protected void setPropertyValue(AbstractNestablePropertyAccessor.PropertyTokenHolder tokens, PropertyValue pv) throws BeansException { //The tokens.key is usually 0 if (tokens.keys != null) { this.processKeyedProperty(tokens, pv); } else { //So it's usually this way this.processLocalProperty(tokens, pv); } }
processLocalProperty
The source code of the method is as follows
private void processLocalProperty(PropertyTokenHolder tokens, PropertyValue pv) { //Get the injection dependency information according to the actualName of tokens //For example, the previous beanWrapper and some Class information //It can be understood that ph is the bean to be injected into 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; } if (this.suppressNotWritablePropertyException) { // Optimization for common ignoreUnknown=true scenario since the // exception would be caught and swallowed higher up anyway... return; } throw createNotWritablePropertyException(tokens.canonicalName); } Object oldValue = null; try { //Get the dependent value, which is an object at this time Object originalValue = pv.getValue(); Object valueToApply = originalValue; if (!Boolean.FALSE.equals(pv.conversionNecessary)) { if (pv.isConverted()) { valueToApply = pv.getConvertedValue(); } else { if (isExtractOldValueForEditor() && ph.isReadable()) { try { oldValue = ph.getValue(); } catch (Exception ex) { if (ex instanceof PrivilegedActionException) { ex = ((PrivilegedActionException) ex).getException(); } if (logger.isDebugEnabled()) { logger.debug("Could not read previous value of property '" + this.nestedPath + tokens.canonicalName + "'", ex); } } } valueToApply = convertForProperty( tokens.canonicalName, oldValue, originalValue, ph.toTypeDescriptor()); } pv.getOriginalPropertyValue().conversionNecessary = (valueToApply != originalValue); } //Inject dependencies into bean s ph.setValue(valueToApply); } catch (TypeMismatchException ex) { throw ex; } catch (InvocationTargetException ex) { PropertyChangeEvent propertyChangeEvent = new PropertyChangeEvent( getRootInstance(), this.nestedPath + tokens.canonicalName, oldValue, pv.getValue()); if (ex.getTargetException() instanceof ClassCastException) { throw new TypeMismatchException(propertyChangeEvent, ph.getPropertyType(), ex.getTargetException()); } else { Throwable cause = ex.getTargetException(); if (cause instanceof UndeclaredThrowableException) { // May happen e.g. with Groovy-generated methods cause = cause.getCause(); } throw new MethodInvocationException(propertyChangeEvent, cause); } } catch (Exception ex) { PropertyChangeEvent pce = new PropertyChangeEvent( getRootInstance(), this.nestedPath + tokens.canonicalName, oldValue, pv.getValue()); throw new MethodInvocationException(pce, ex); } }
ph.setValue
Finally, the injection step is completed by BeanWrapperImpl
The source code is as follows
public void setValue(@Nullable Object value) throws Exception { //Gets the Method object of the Set Method Method writeMethod = (this.pd instanceof GenericTypeAwarePropertyDescriptor ? ((GenericTypeAwarePropertyDescriptor) this.pd).getWriteMethodForActualAccess() : this.pd.getWriteMethod()); //System security manager 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 { //Use the reflection tool to make the set method object accessible ReflectionUtils.makeAccessible(writeMethod); //getWrappedInstance obtains the bean to be dependency injected, and value is the dependency to be injected //Execute the set method of the bean to be dependency injected to complete dependency injection writeMethod.invoke(getWrappedInstance(), value); } } }
After executing the invoke method, you can see that twoPojo has successfully injected
Therefore, it can be understood that the essence of the injection attribute is that BeanWrapperImpl uses the created dependency and Set method reflection to inject
Attribute injection is now complete
However, at this time, only the attributes are injected into the beanWrapper. The Bean has basically been created, but the overall Bean creation process is still in the doCreateBean method of AbstractAutowireCapableBeanFactory. Only by completing this method can the whole Bean be created. There are still some things in the life cycle, such as the init method
The next step is to execute the initializeBean method
initializeBean method
Although the method is called initializeBean, in fact, the whole Bean has been basically completed. Before studying the Bean configuration file parsing, it encountered a init-method attribute. The function of this attribute is to call the init-method specified method after bean instantiation to complete the action before instantiation. The function of this attribute lies in the step of initializing the bean
The corresponding source code is as follows
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { //Get the security manager of the system, its zu'yong'shi //When running an unknown Java program, the program may have malicious code (deleting system files, restarting the system, etc.), //In order to prevent the running of malicious code from affecting the system //You need to control the permissions of the running code. At this time, you need to enable the Java security manager. //However, the key here is to execute the invokeAwareMethods method if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { invokeAwareMethods(beanName, bean); return null; }, getAccessControlContext()); } else { //Execute the invokeAwareMethods method invokeAwareMethods(beanName, bean); } // Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { //Pre initialization of post application processor wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } try { //Execute init method invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) { throw new BeanCreationException( (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); } if (mbd == null || !mbd.isSynthetic()) { //Call the post initialization of the post processor wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; }
The overall process is clear
- Execute the invokeAwareMethods method
- Call the post processor's processing logic before initialization
- Execute the invokeinitialmethods method
- Call the post processor's processing logic after initialization
The logic of the post processor is set aside here
invokeAwareMethods method
People who have used Spring know that beans are not Aware of the IOC container and cannot obtain the services of the IOC container. If you want beans to obtain IOC services, you must make beans Aware of the existence of the IOC container. Therefore, there will be a series of Aware interfaces corresponding to different IOC services. As long as beans implement this interface, they can obtain the corresponding IOC services, I won't go into detail about Aware interface
The source code is as follows
private void invokeAwareMethods(String beanName, Object bean) { //Judge whether it is Aware interface if (bean instanceof Aware) { //Determine whether it is the BeanNameAware interface inside //This interface allows the bean to get its name in the IOC container if (bean instanceof BeanNameAware) { //Inject your own BeanName into the bean ((BeanNameAware) bean).setBeanName(beanName); } //Determine whether it is the BeanClassLoaderAware interface //This interface allows the bean to obtain the class loader service of IOC if (bean instanceof BeanClassLoaderAware) { ClassLoader bcl = getBeanClassLoader(); if (bcl != null) { //Inject BeanClassLoader into Bean ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl); } } //Determine whether it is BeanFactoryAware interface //This interface can obtain the BeanFactory service of IOC if (bean instanceof BeanFactoryAware) { //AbstractAutowireCapableBeanFactory.this in this step can be regarded as a this pointer //Inject Bean to create its own Factory ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this); } } }
It can be seen that this method simply enumerates all Aware interfaces, forcibly converts them into interface types, and then injects corresponding services. From here, we can see that there are actually three corresponding Aware interfaces
- BeanNameAware
- BeanClassLoaderAware
- BeanFactoryAware
After waking up the IOC service for the Bean, the next step is to activate the method init method
invokeAwareMethods method
This method is used to activate the method init method
The source code is as follows
protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd) throws Throwable { //Determine whether InitializingBean is of type //This is done only when the bean implements the InitializingBean interface boolean isInitializingBean = (bean instanceof InitializingBean); if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) { //If so, you need to call the afterpropertieset method //The security processor of the system is also ignored here, except that the afterpropertieset method will be called here if (logger.isTraceEnabled()) { logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'"); } if (System.getSecurityManager() != null) { try { AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> { ((InitializingBean) bean).afterPropertiesSet(); return null; }, getAccessControlContext()); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { //Call the afterPropertySet method //And you can see that it is called by the bean itself ((InitializingBean) bean).afterPropertiesSet(); } } //Determine whether it is NullBean.Class if (mbd != null && bean.getClass() != NullBean.class) { //Gets the method name specified by method init String initMethodName = mbd.getInitMethodName(); //The afterPropertiesSet method is also filtered here if (StringUtils.hasLength(initMethodName) && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) && !mbd.isExternallyManagedInitMethod(initMethodName)) { //Execution method invokeCustomInitMethod(beanName, bean, mbd); } } }
I also saw a knowledge left by Spring, the InitializingBean interface
- Determine whether the Bean implements the InitializingBean interface
- If implemented, the afterpropertieset method in the finite execution interface
- Go to RootBeanDefinitin to find the method init attribute
- Find the corresponding method and filter out the afterpropertieset method. Of course, the premise of this filtering is that the Bean implements the InitializingBean interface
- Call the invokeCustomInitMethod method to implement
invokeCustomInitMethod method
The source code is as follows
protected void invokeCustomInitMethod(String beanName, Object bean, RootBeanDefinition mbd) throws Throwable { //Get method init attribute String initMethodName = mbd.getInitMethodName(); //Assert Assert.state(initMethodName != null, "No init method set"); //Use reflection to get the Method object of the Method Method initMethod = (mbd.isNonPublicAccessAllowed() ? BeanUtils.findMethod(bean.getClass(), initMethodName) : ClassUtils.getMethodIfAvailable(bean.getClass(), initMethodName)); //If the obtained Method object is empty if (initMethod == null) { //Judge whether enforcement is set in RootBeanDefinition if (mbd.isEnforceInitMethod()) { //If it is empty and enforced, an error will be reported throw new BeanDefinitionValidationException("Could not find an init method named '" + initMethodName + "' on bean with name '" + beanName + "'"); } else { //If not enforced, ignore and return directly if (logger.isTraceEnabled()) { logger.trace("No default init method named '" + initMethodName + "' found on bean with name '" + beanName + "'"); } // Ignore non-existent default lifecycle methods. return; } } //If it is not empty, the following processing will be performed if (logger.isTraceEnabled()) { logger.trace("Invoking init method '" + initMethodName + "' on bean with name '" + beanName + "'"); } //I don't quite understand this method. The comment says that I should try to get the interface method Method methodToInvoke = ClassUtils.getInterfaceMethodIfPossible(initMethod); //Security manager, don't think about it first if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { ReflectionUtils.makeAccessible(methodToInvoke); return null; }); try { AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> methodToInvoke.invoke(bean), getAccessControlContext()); } catch (PrivilegedActionException pae) { InvocationTargetException ex = (InvocationTargetException) pae.getException(); throw ex.getTargetException(); } } else { try { //This method can be called through the reflection tool ReflectionUtils.makeAccessible(methodToInvoke); //Execute this method through reflection methodToInvoke.invoke(bean); } catch (InvocationTargetException ex) { throw ex.getTargetException(); } } }
The general logic here is also very simple
- Judge whether the method init attribute is empty
- If it is empty, the assertion will throw an error
- If it is not empty, get the Method object of the Method through reflection
- If the Method object is empty, judge whether the configuration file is set to enforce. If enforced, an error will be thrown
- If the Method object is empty and the configuration file does not set enforcement, it is ignored directly
- Execute the Method by reflection
So far, the object has completed property injection and initialization