[spring] dependency injection @ Autowired find beans that can be injected: findAutowireCandidates

Posted by dude81 on Tue, 22 Feb 2022 16:54:25 +0100

@Bean s that Autowired can inject

  • The source code of this article is based on spring-framework-5.3.10.
  • Source location: org springframework. beans. factory. support. DefaultListableBeanFactory. findAutowireCandidates(String, Class<?>, DependencyDescriptor)
  • This article focuses on how @ Autowired can select those beans for dependency injection during dependency injection.
  • This method is the core of dependency injection!
  • ByType before ByName, so @ Autowired is now considered to be injected by ByType!

findAutowireCandidates source code process

  • Find out the names of all beans with type in BeanFactory. Note that it is the name, not the Bean object, because we can judge whether it matches the current type according to the BeanDefinition without generating a Bean object
  • Find the object whose key is type in resolvable dependencies and add it to the result
  • Traverse the beanName found by type to determine whether the Bean corresponding to the current beanName can be automatically injected
  • Judge the autowireCandidate attribute in the BeanDefinition corresponding to beanName first. If it is false, it means it cannot be used for automatic injection. If it is true, continue to judge
  • Judge whether the current type is generic. If it is generic, all beannames in the container will be found. If this is the case, the real type of the generic must be obtained in this step, and then match. If the current beanName matches the real type corresponding to the current generic, continue to judge
  • If there is a @ Qualifier annotation on the current DependencyDescriptor, judge whether a Qualifier is defined on the current beanName and whether it is equal to the Qualifier on the current DependencyDescriptor. If it is equal, it will match
  • After the above verification, the current beanName can become an injectable and be added to the result

Source code analysis of findAutowireCandidates

protected Map<String, Object> findAutowireCandidates(
		@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {

	// Find the beanName that matches the requiredType from the BeanFactory. It is only beanName. These beans do not have to be instantiated. Only when a bean is finally determined will it be instantiated if it has not been instantiated
	String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
			this, requiredType, true, descriptor.isEager());
	Map<String, Object> result = CollectionUtils.newLinkedHashMap(candidateNames.length);

	// Match beans from resolvableDependencies according to type. resolvableDependencies store type: Bean object, such as beanfactory Class: beanfactory object, which is set when Spring starts.
	for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {
		// Get the type of the current Bean
		Class<?> autowiringType = classObjectEntry.getKey();
		if (autowiringType.isAssignableFrom(requiredType)) {
			// Gets the value in the cache
			Object autowiringValue = classObjectEntry.getValue();
			// A Bean name will be generated here, and there is no Bean name in the cache
			autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);

			// Type match, add the current value
			if (requiredType.isInstance(autowiringValue)) {
				result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
				break;
			}
		}
	}

	// It is understood here that when injecting the same Bean, other beans of the same type should be considered first
	for (String candidate : candidateNames) {
		// If not, judge whether the candidate can be used for automatic injection (@ Bean (autowireCandidate = true)) defaults to true
		if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
			// When you are not yourself and can inject, call this Code: add a candidate
			addCandidateEntry(result, candidate, descriptor, requiredType);
		}
	}

	// If it is empty, either there is no match or it matches itself
	if (result.isEmpty()) {
		// Is the type to be matched Map or array
		boolean multiple = indicatesMultipleBeans(requiredType);
		// Consider fallback matches if the first pass failed to find anything...
		// If you can't find anything the first time, consider fallback matching.
		DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
		// Traverse each candidate
		for (String candidate : candidateNames) {
			// It is not itself and can be injected (not Map, array or @ Qualifier specifies BeanName)
			if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor) &&
					(!multiple || getAutowireCandidateResolver().hasQualifier(descriptor))) {
				// Add real candidates
				addCandidateEntry(result, candidate, descriptor, requiredType);
			}
		}

		// Match yourself and add yourself to the result.
		if (result.isEmpty() && !multiple) {
			// Consider self references as a final pass...
			// but in the case of a dependency collection, not the very same bean itself.
			for (String candidate : candidateNames) {
				if (isSelfReference(beanName, candidate) &&
						(!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) &&
						isAutowireCandidate(candidate, fallbackDescriptor)) {
					addCandidateEntry(result, candidate, descriptor, requiredType);
				}
			}
		}
	}
	return result;
}

/**
 * Add candidate
 */
private void addCandidateEntry(Map<String, Object> candidates, String candidateName,
		DependencyDescriptor descriptor, Class<?> requiredType) {

	// Logic of MultiElementDescriptor
	if (descriptor instanceof MultiElementDescriptor) {
		Object beanInstance = descriptor.resolveCandidate(candidateName, requiredType, this);
		if (!(beanInstance instanceof NullBean)) {
			candidates.put(candidateName, beanInstance);
		}
	}
	// Singleton logic
	else if (containsSingleton(candidateName) || (descriptor instanceof StreamDependencyDescriptor &&
			((StreamDependencyDescriptor) descriptor).isOrdered())) {
		// If it exists in the singleton pool, put it directly into the bean object
		Object beanInstance = descriptor.resolveCandidate(candidateName, requiredType, this);
		candidates.put(candidateName, (beanInstance instanceof NullBean ? null : beanInstance));
	}
	else {
		// Save the matched beanName and beanClass into
		candidates.put(candidateName, getType(candidateName));
	}
}

Get all beannames corresponding to a type

public static String[] beanNamesForTypeIncludingAncestors(
		ListableBeanFactory lbf, Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {

	Assert.notNull(lbf, "ListableBeanFactory must not be null");
	// Find from this container
	String[] result = lbf.getBeanNamesForType(type, includeNonSingletons, allowEagerInit);
	// Find and put result from parent container
	if (lbf instanceof HierarchicalBeanFactory) {
		HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
		if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
			// Recursive call
			String[] parentResult = beanNamesForTypeIncludingAncestors(
					(ListableBeanFactory) hbf.getParentBeanFactory(), type, includeNonSingletons, allowEagerInit);
			// Merge and de duplicate the values found from the child and parent Bean factories (those with reusable subclasses)
			result = mergeNamesWithParent(result, parentResult, hbf);
		}
	}
	// Returns all beannames found
	return result;
}

/**
 * The real external method to get the BeanName according to the type
 */
public String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {

	// If it is not frozen, go to BeanFactory according to the type. If it is frozen, you may skip this if and get it from the cache
	if (!isConfigurationFrozen() || type == null || !allowEagerInit) {
		return doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, allowEagerInit);
	}

	// Cache the beanName matched by the current type
	Map<Class<?>, String[]> cache =
			(includeNonSingletons ? this.allBeanNamesByType : this.singletonBeanNamesByType);
	// Get the value of BeanName in the buffer
	String[] resolvedBeanNames = cache.get(type);
	// The obtained value is not null and is returned directly
	if (resolvedBeanNames != null) {
		return resolvedBeanNames;
	}
	// The value is null. Go to get the value again
	resolvedBeanNames = doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, true);
	// Cache
	if (ClassUtils.isCacheSafe(type, getBeanClassLoader())) {
		cache.put(type, resolvedBeanNames);
	}
	// Return all beannames
	return resolvedBeanNames;
}

/**
 * The real internal method of obtaining BeanName according to type
 */
private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
	List<String> result = new ArrayList<>();

	// Check all bean definitions.
	// Traverse all BeanDefinitions
	for (String beanName : this.beanDefinitionNames) {
		// Only consider bean as eligible if the bean name is not defined as alias for some other bean.
		// Skip alias
		if (!isAlias(beanName)) {
			try {
				// Get the merged BeanDefinition of the current Bean
				RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				// Only check bean definition if it is complete.
				// Judge whether mbd is allowed to obtain the corresponding type
				// First, the mdb cannot be abstract, and then alloweegerinit is true, you can directly infer the type of mdb and match it
				// If alloweegerinit is false, continue to judge. If the mdb has not loaded the class and is lazy and does not allow the class to be loaded in advance, the mbd cannot be used for matching (because the class is not allowed to be loaded in advance, the class can only be created when the mdb creates the bean object itself)
				// If alloweegerinit is false, and mbd has loaded the class, or it is not lazy to load, or it is allowed to load the class in advance, and you do not have to initialize in advance to obtain the type, you can match it
				// This condition is a little complicated, but if you only consider most processes, you can ignore this judgment, because the data passed in by allowEagerInit is basically true
				if (!mbd.isAbstract() && (allowEagerInit ||
						(mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading()) &&
								!requiresEagerInitForType(mbd.getFactoryBeanName()))) {
					// Get whether it is FactoryBean
					boolean isFactoryBean = isFactoryBean(beanName, mbd);
					// Get BeanDefinitionHolder
					BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
					// Match flag false
					boolean matchFound = false;
					// Allow FactoryBean initialization
					boolean allowFactoryBeanInit = (allowEagerInit || containsSingleton(beanName));
					// Non lazy loading
					boolean isNonLazyDecorated = (dbd != null && !mbd.isLazyInit());

					// The current BeanDefinition is either a FactoryBean or an ordinary Bean
					if (!isFactoryBean) {
						// When filtering beans, if only a singleton is included, but the corresponding beanName is not a singleton, it is ignored
						if (includeNonSingletons || isSingleton(beanName, mbd, dbd)) {
							// Judge whether the types match
							matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
						}
					}
					else {
						if (includeNonSingletons || isNonLazyDecorated ||
								(allowFactoryBeanInit && isSingleton(beanName, mbd, dbd))) {
							// Type matching - lazy loading
							matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
						}
						if (!matchFound) {
							// In case of FactoryBean, try to match FactoryBean instance itself next.
							beanName = FACTORY_BEAN_PREFIX + beanName;
							// Type matching - FactoryBean
							matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
						}
					}
					if (matchFound) {
						// Add the matching to the result set
						result.add(beanName);
					}
				}
			}
			catch (CannotLoadBeanClassException | BeanDefinitionStoreException ex) {
				if (allowEagerInit) {
					throw ex;
				}
				// Probably a placeholder: let's ignore it for type matching purposes.
				LogMessage message = (ex instanceof CannotLoadBeanClassException ?
						LogMessage.format("Ignoring bean class loading failure for bean '%s'", beanName) :
						LogMessage.format("Ignoring unresolvable metadata in bean definition '%s'", beanName));
				logger.trace(message, ex);
				// Register exception, in case the bean was accidentally unresolvable.
				onSuppressedException(ex);
			}
			catch (NoSuchBeanDefinitionException ex) {
				// Bean definition got removed while we were iterating -> ignore.
			}
		}
	}

	// Check manually registered singletons too.
	// The logic here is to check whether there are qualified beans in the manually registered beans
	for (String beanName : this.manualSingletonNames) {
		try {
			// In case of FactoryBean, match object created by FactoryBean.
			if (isFactoryBean(beanName)) {
				if ((includeNonSingletons || isSingleton(beanName)) && isTypeMatch(beanName, type)) {
					result.add(beanName);
					// Match found for this bean: do not match FactoryBean itself anymore.
					continue;
				}
				// In case of FactoryBean, try to match FactoryBean itself next.
				beanName = FACTORY_BEAN_PREFIX + beanName;
			}
			// Match raw bean instance (might be raw FactoryBean).
			if (isTypeMatch(beanName, type)) {
				result.add(beanName);
			}
		}
		catch (NoSuchBeanDefinitionException ex) {
			// Shouldn't happen - probably a result of circular reference resolution...
			logger.trace(LogMessage.format(
					"Failed to check manually registered singleton with name '%s'", beanName), ex);
		}
	}

	// Return result array
	return StringUtils.toStringArray(result);
}

Judge whether the types match

protected boolean isTypeMatch(String name, ResolvableType typeToMatch, boolean allowFactoryBeanInit)
		throws NoSuchBeanDefinitionException {
	// Judge whether the type of Bean corresponding to name is typeToMatch
	// allowFactoryBeanInit indicates whether the FactoryBean object is allowed to be instantiated here

	// If name is & xxx, beanName is xxx
	String beanName = transformedBeanName(name);
	// Is name & XXX (factorybean)
	boolean isFactoryDereference = BeanFactoryUtils.isFactoryDereference(name);

	// Check manually registered singletons.
	// Get danliBean
	Object beanInstance = getSingleton(beanName, false);
	// The singleton Bean is obtained, and it is not an empty Bean
	if (beanInstance != null && beanInstance.getClass() != NullBean.class) {
		// Special logic of FactoryBean
		if (beanInstance instanceof FactoryBean) {
			// Class outside FactoryBean
			if (!isFactoryDereference) {
				// Call factorybean getObjectType()
				Class<?> type = getTypeForFactoryBean((FactoryBean<?>) beanInstance);
				// Returns whether the current type is not empty and whether the types match
				return (type != null && typeToMatch.isAssignableFrom(type));
			}
			else {
				// Whether the return types match
				return typeToMatch.isInstance(beanInstance);
			}
		}
		// Either FactoryBean or ordinary Bean
		else if (!isFactoryDereference) {
			// Match directly to and return true. In fact, the common method is to see this line
			if (typeToMatch.isInstance(beanInstance)) {
				// Direct match for exposed instance?
				return true;
			}
			// Generic related processing
			else if (typeToMatch.hasGenerics() && containsBeanDefinition(beanName)) {
				// Generics potentially only match on the target class, not on the proxy...
				RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				Class<?> targetType = mbd.getTargetType();
				if (targetType != null && targetType != ClassUtils.getUserClass(beanInstance)) {
					// Check raw class match as well, making sure it's exposed on the proxy.
					Class<?> classToMatch = typeToMatch.resolve();
					if (classToMatch != null && !classToMatch.isInstance(beanInstance)) {
						return false;
					}
					if (typeToMatch.isAssignableFrom(targetType)) {
						return true;
					}
				}
				ResolvableType resolvableType = mbd.targetType;
				if (resolvableType == null) {
					resolvableType = mbd.factoryMethodReturnType;
				}
				return (resolvableType != null && typeToMatch.isAssignableFrom(resolvableType));
			}
		}
		return false;
	}
	// The singleton pool has, but not in the BeanDefinitionMap. false is returned. Empty instances registered
	else if (containsSingleton(beanName) && !containsBeanDefinition(beanName)) {
		// null instance registered
		return false;
	}

	// No singleton instance found -> check bean definition.
	BeanFactory parentBeanFactory = getParentBeanFactory();
	// If there is a parent factory, call the isTypeMatch method of the parent factory
	if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
		// No bean definition found in this factory -> delegate to parent.
		return parentBeanFactory.isTypeMatch(originalBeanName(name), typeToMatch);
	}

	// If there is no Bean object corresponding to name in the singleton pool, the type can only be determined according to the BeanDefinition

	// Retrieve corresponding bean definition.
	RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
	BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();

	// Setup the types that we want to match against
	Class<?> classToMatch = typeToMatch.resolve();
	if (classToMatch == null) {
		classToMatch = FactoryBean.class;
	}
	// To judge whether the current beanName is classToMatch or FactoryBean
	Class<?>[] typesToMatch = (FactoryBean.class == classToMatch ?
			new Class<?>[] {classToMatch} : new Class<?>[] {FactoryBean.class, classToMatch});


	// Attempt to predict the bean type
	Class<?> predictedType = null;

	// We're looking for a regular reference but we're a factory bean that has
	// a decorated bean definition. The target bean should be the same type
	// as FactoryBean would ultimately return.  Gnawing
	if (!isFactoryDereference && dbd != null && isFactoryBean(beanName, mbd)) {
		// We should only attempt if the user explicitly set lazy-init to true
		// and we know the merged bean definition is for a factory bean.
		if (!mbd.isLazyInit() || allowFactoryBeanInit) {
			RootBeanDefinition tbd = getMergedBeanDefinition(dbd.getBeanName(), dbd.getBeanDefinition(), mbd);
			// The type of method used by getObjectType
			Class<?> targetType = predictBeanType(dbd.getBeanName(), tbd, typesToMatch);
			if (targetType != null && !FactoryBean.class.isAssignableFrom(targetType)) {
				predictedType = targetType;
			}
		}
	}

	// If we couldn't use the target type, try regular prediction.
	if (predictedType == null) {
		predictedType = predictBeanType(beanName, mbd, typesToMatch);
		if (predictedType == null) {
			return false;
		}
	}

	// Attempt to get the actual ResolvableType for the bean.
	ResolvableType beanType = null;

	// If it's a FactoryBean, we want to look at what it creates, not the factory class.
	// The type of BeanDefinition is not FactoryBean. If you want to instantiate the object of FactoryBean first, then call the getObjectType method to know the specific type, provided that allowFactoryBeanInit is true.
	if (FactoryBean.class.isAssignableFrom(predictedType)) {
		if (beanInstance == null && !isFactoryDereference) {
			beanType = getTypeForFactoryBean(beanName, mbd, allowFactoryBeanInit);
			predictedType = beanType.resolve();
			if (predictedType == null) {
				return false;
			}
		}
	}
	else if (isFactoryDereference) {
		// Special case: A SmartInstantiationAwareBeanPostProcessor returned a non-FactoryBean
		// type but we nevertheless are being asked to dereference a FactoryBean...
		// Let's check the original bean class and proceed with it if it is a FactoryBean.
		predictedType = predictBeanType(beanName, mbd, FactoryBean.class);
		if (predictedType == null || !FactoryBean.class.isAssignableFrom(predictedType)) {
			return false;
		}
	}

	// We don't have an exact type but if bean definition target type or the factory
	// method return type matches the predicted type then we can use that.
	if (beanType == null) {
		ResolvableType definedType = mbd.targetType;
		if (definedType == null) {
			definedType = mbd.factoryMethodReturnType;
		}
		if (definedType != null && definedType.resolve() == predictedType) {
			beanType = definedType;
		}
	}

	// If we have a bean type use it so that generics are considered
	if (beanType != null) {
		return typeToMatch.isAssignableFrom(beanType);
	}

	// If we don't have a bean type, fallback to the predicted type
	return typeToMatch.isAssignableFrom(predictedType);
}

Concluding remarks

  • Get more pre knowledge articles of this article and new valuable articles. Let's become architects together!
  • Paying attention to the official account enables you to have an in-depth understanding of MySQL, concurrent programming and spring source code.
  • Pay attention to the official account and follow the continuous and efficient learning of JVM!
  • This official account is not advertising!!! Update daily!!!

Topics: Java Spring Back-end