In< What is the difference between @ Autowired and @ Resource in Spring? In this article, we mentioned that when Spring relies on injection, it first parses @Autowired annotations and @Resource annotated objects into dependency descriptor DependencyDescriptor, and then calls AutowireCapableBeanFactory#resolveDependency (DependencyDescriptor, String, Set<String>, TypeConverter) to resolve dependencies. Finally, the purpose of dependency injection is achieved through reflection setting fields or calling methods. Let's analyze how spring implements dependency resolution from the resolvedependency method.
Dependency analysis and implementation analysis
Spring's final implementation of the bottom layer of BeanFactory is DefaultListableBeanFactory. The source code of DefaultListableBeanFactory's implementation of resolveDependency method is as follows:
/** * @param descriptor Dependency description information for a field or method * @param requestingBeanName The name of the bean on which the dependency needs to be injected * @param autowiredBeanNames The collection of dependent bean names to be injected, and the resolution results will be stored in the collection * @param typeConverter Type conversion * @return * @throws BeansException */ @Override @Nullable public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { descriptor.initParameterNameDiscovery(getParameterNameDiscoverer()); if (Optional.class == descriptor.getDependencyType()) { // Resolving Optional dependencies return createOptionalDependency(descriptor, requestingBeanName); } else if (ObjectFactory.class == descriptor.getDependencyType() || ObjectProvider.class == descriptor.getDependencyType()) { // Resolve ObjectFactory or ObjectProvider dependencies return new DependencyObjectProvider(descriptor, requestingBeanName); } else if (javaxInjectProviderClass == descriptor.getDependencyType()) { // Parsing javax inject. Dependency of provider annotation annotation return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName); } else { // Lazy loaded objects return proxy objects Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary( descriptor, requestingBeanName); if (result == null) { // Resolve other objects result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter); } return result; } }
The dependency resolution method uses different resolution methods according to different dependency types. For Optional, ObjectFactory, and ObjectProvider dependent types, Spring repackages the dependency description information into another DependencyDescriptor, and then resolves it, since the actual type of its generic type needs to be resolved. The Java specification JSR-330 annotates javax inject. Provider only delegates the implementation to another class for processing. For different types of dependency resolution, the doResolveDependency method will eventually be called. Take createOptionalDependency as an example. The source code is as follows:
private Optional<?> createOptionalDependency( DependencyDescriptor descriptor, @Nullable String beanName, final Object... args) { // Wrap dependency description information. When resolving dependency types, the nesting level will be increased by 1 // For example, the original type to be resolved is the original type of optional < T >, and the actual type T of the generic type will be resolved after adding 1 to the nesting level DependencyDescriptor descriptorToUse = new NestedDependencyDescriptor(descriptor) { @Override public boolean isRequired() { return false; } @Override public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory) { return (!ObjectUtils.isEmpty(args) ? beanFactory.getBean(beanName, args) : super.resolveCandidate(beanName, requiredType, beanFactory)); } }; // Then really resolve the dependencies Object result = doResolveDependency(descriptorToUse, beanName, null, null); // Then wrap the resolved dependencies into Optional return (result instanceof Optional ? (Optional<?>) result : Optional.ofNullable(result)); }
The source code of the method doResolveDependency that tracks the real resolution dependency is as follows:
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { ... Omit some codes // Quick resolution dependency Object shortcut = descriptor.resolveShortcut(this); if (shortcut != null) { return shortcut; } Class<?> type = descriptor.getDependencyType(); // First resolve the dependent objects according to the @ Value annotation Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor); if (value != null) { if (value instanceof String) { // Then handle placeholders and expressions 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 { // Type the result return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor()); } catch (UnsupportedOperationException ex) { ... Omit some codes } } // An attempt was made to resolve a dependent object that contains more than one bean Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter); if (multipleBeans != null) { return multipleBeans; } // Find dependencies of the desired type Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor); if (matchingBeans.isEmpty()) { if (isRequired(descriptor)) { // If the dependency does not exist, an exception is thrown raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor); } return null; } String autowiredBeanName; Object instanceCandidate; if (matchingBeans.size() > 1) { // There are multiple dependencies of the same type. Determine which dependency to use autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor); 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 { // Only one dependency was found // 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) { 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; }
The code of the method doResolveDependency that really resolves dependencies is slightly longer, and some of the code is omitted above. We can see that the parsing process is as follows:
- Give priority to trying to resolve quickly according to the dependency descriptor. null is returned by default. The implementation class ShortcutDependencyDescriptor will obtain beans from BeanFactory according to the dependent name and type.
- If the quick parsing fails, try to parse the field marked with @ Value. The placeholder in the Value value Value of @ Value annotation will be parsed and the expression will be parsed. Finally, the parsed object will be type converted.
- If the @ Value annotation also fails to resolve, it will try to resolve the dependency types containing multiple bean objects, such as collection, array, Map, etc.
- If the dependency is a single type, all dependencies will be found according to the type. If the dependency does not exist and the dependency is necessary, an exception will be thrown.
- For a single type, if only one dependency is found, you can return it directly. Otherwise, you need to select the best dependency according to the Primary and priority.
For the collection type and single type, the findAutowireCandidates method will be called to find the candidate objects for automatic assembly. For the collection type, the found candidate objects will be packaged as the corresponding required type. For the single type, it is necessary to confirm which dependency is used in the end. The source code of the method findAutowireCandidates for tracking and finding dependent candidates is as follows:
protected Map<String, Object> findAutowireCandidates( @Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) { // Find the bean name of the given type in Spring String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this, requiredType, true, descriptor.isEager()); Map<String, Object> result = new LinkedHashMap<>(candidateNames.length); // First resolve the free objects in Spring, and add them to the result if the type matches the required type for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) { Class<?> autowiringType = classObjectEntry.getKey(); if (autowiringType.isAssignableFrom(requiredType)) { Object autowiringValue = classObjectEntry.getValue(); autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType); if (requiredType.isInstance(autowiringValue)) { result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue); break; } } } // Then resolve the name in the BeanDefinition registered in Spring for (String candidate : candidateNames) { if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) { // It does not depend on the bean itself, and the bean can be added to the result as a candidate addCandidateEntry(result, candidate, descriptor, requiredType); } } ... The fallback processing code is omitted, which is similar to the above candidate name loop return result; }
There are two main sources for obtaining candidate objects, one is a free object, and the other is a bean in spring. Spring obtains the corresponding instance according to the type and adds it to the returned result. This also confirms the dependency source of dependency injection mentioned in the previous article. It should be noted that the isAutowireCandidate method is called. The isAutowireCandidate method is used to judge whether an object can be used as a candidate. It has the identification to judge whether it can be used as a candidate in the bean definition, as well as generics, @ qualifier, etc. About @ Qualifier's processing, we will open another article for analysis later.
For single type dependency resolution, if multiple beans are found, you need to determine which bean is used as the result, and track the call of determineAutowireCandidate method. Its source code is as follows:
protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) { Class<?> requiredType = descriptor.getDependencyType(); // First judge according to the primary node String primaryCandidate = determinePrimaryCandidate(candidates, requiredType); if (primaryCandidate != null) { return primaryCandidate; } // If the primary does not exist, it is determined according to 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 the dependency matches the bean name of the free object or the specified dependency, it is returned directly if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) || matchesBeanName(candidateName, descriptor.getDependencyName())) { return candidateName; } } return null; }
Here, priority is given to beans marked with primary as true. If they are not marked, the bean with the highest priority will be selected. If there are multiple beans with the same priority, an exception will be thrown. Finally, the free object or bean matching the dependent name will be returned as the result.
summary
Spring dependency resolution will preferentially resolve according to the @ Value annotation, and supports multiple types. For dependencies of type Optional and ObjectFactory, spring will wrap the dependencies and return them. Other types are divided into collection type and single type. For collection type, spring can directly wrap all dependencies and return them as corresponding types. For single type, spring will give priority to beans with primary, highest priority and matching bean names.
Draught does not forget the well Digger: |