SpringIOC之依赖自动注入六层筛选源码剖析
Spring IOC 中的依赖自动注入功能是通过 BeanFactory 的 AutowireCapableBeanFactory 接口实现的。当 Spring IOC 容器在创建 Bean 的时候,会检查 Bean 中的依赖属性,如果这些属性没有被赋值,那么 Spring 会尝试自动注入这些属性。 这些规则包括以下六个层次:
- Spring 会根据类型来确定要注入的 Bean 实例。如果存在多个符合类型的 Bean 实例,会抛出 NoUniqueBeanDefinitionException 异常,提示存在多个相同类型的 Bean。
- 如果存在多个符合类型的 Bean 实例,但是其中有一个 Bean 实例被标记为 @Primary,那么 Spring 会选择被标记为 @Primary 的 Bean 实例。
- 如果存在多个符合类型的 Bean 实例,但是其中有一个 Bean 实例被标记为 @Qualifier,那么 Spring 会选择被标记为 @Qualifier 的 Bean 实例。
- 如果存在多个符合类型的 Bean 实例,但是其中只有一个 Bean 实例的名称与属性名称匹配,那么 Spring 会选择这个 Bean 实例。
- 如果存在多个符合类型的 Bean 实例,但是其中只有一个 Bean 实例的名称与属性名称不匹配,那么 Spring 会尝试使用属性名称的驼峰式大小写规则来匹配 Bean 实例的名称,如果匹配成功,那么 Spring 会选择这个 Bean 实例。
- 如果以上五个层次都无法选择要注入的 Bean 实例,那么 Spring 会抛出 NoSuchBeanDefinitionException 异常,提示找不到符合条件的 Bean 自动注入依赖的源码剖析:
Spring IOC 会调用 BeanFactory 的 getBean() 方法来获取要注入的 Bean 实例。在获取 Bean 实例时,Spring 会根据属性的类型或名称来查找符合条件的 Bean 实例,如果存在多个符合条件的 Bean 实例,那么 Spring 会根据上述六个层次的规则来选择要注入的 Bean 实例。 在选择要注入的 Bean 实例时,Spring 会调用 AutowiredAnnotationBeanPostProcessor 类的 getInjectedObject() 方法来进行依赖注入。在这个方法中,Spring 会调用 BeanFactory 的 resolveDependency() 方法来获取要注入的依赖项。 在 resolveDependency() 方法中,Spring 会根据要注入的依赖项的类型、名称、注解等信息来查找符合条件的 Bean 实例。如果存在多个符合条件的 Bean 实例,那么 Spring 会根据上述六个层次的规则来选择要注入的 Bean 实例。 最终,Spring 会将选择的 Bean 实例注入到目标对象中的依赖属性中,完成依赖注入的过程。
Spring IOC 自动注入依赖的六层筛选是通过 BeanFactory 的 getBean() 方法和 AutowiredAnnotationBeanPostProcessor 类的 getInjectedObject() 方法来实现的。在选择要注入的 Bean 实例时,Spring 会根据类型、名称、注解等信息来查找符合条件的 Bean 实例,并根据上述六个层次的规则来进行选择。
我们通过源码分析一下,每一层筛选是如何实现的,并给出相应的源码分析。
spring IOC bean注入源码分析
- 通过类型匹配查找候选 Bean
这一步是通过类型来查找所有可能的候选 Bean,Spring 会从容器中查找所有与依赖类型兼容(包括实现了接口和继承了父类)的 Bean 实例,并将这些 Bean 实例保存在一个候选列表中。
protected <T> Map<String, T> findAutowireCandidates(
String beanName, Class<T> requiredType, DependencyDescriptor descriptor) {
// 从容器中查找所有与依赖类型兼容的 Bean 实例
String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, requiredType, true, descriptor.isEager());
Map<String, T> result = new LinkedHashMap<>(candidateNames.length);
for (String candidateName : candidateNames) {
// 只添加非抽象、非懒加载的 Bean 实例
if (!candidateName.equals(beanName) && !containsBeanDefinition(candidateName)) {
result.put(candidateName, getBean(candidateName, requiredType));
}
}
return result;
}
2. 通过名称匹配查找候选 Bean
如果第一步找到的候选 Bean 不止一个,那么 Spring 会继续通过名称来匹配 Bean,如果依赖属性的名称与 Bean 的名称匹配,那么 Spring 会优先选择这个 Bean。
protected Object autowireByName(
String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
for (String propertyName : propertyNames) {
// 查找与属性名称匹配的 Bean
if (containsBean(propertyName)) {
Object bean = getBean(propertyName);
// 注入依赖属性
pvs.add(propertyName, bean);
registerDependentBean(propertyName, beanName);
if (logger.isTraceEnabled()) {
logger.trace("Autowiring by name from bean name '{}' via property '{}' to bean named '{}'",
beanName, propertyName, propertyName);
}
}
}
return null;
}
3. 通过主要标识查找 Bean
如果第二步找到的候选 Bean 不止一个,那么 Spring 会继续查找主要标识的 Bean,主要标识是在 Bean 定义中通过 @Primary 注解指定的 Bean。
protected Object autowireByType(
String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, TypeConverter converter) {
Object value = null;
Class<?> type = bw.getWrappedClass();
// 按类型查找所有候选 Bean
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, mbd);
if (!matchingBeans.isEmpty()) {
// 查找主要标识的 Bean
String primaryBeanName = determinePrimaryCandidate(matchingBeans, type);
if (primaryBeanName != null) {
Object primaryBean = matchingBeans.get(primaryBeanName);
// 注入依赖属性
value = primaryBean;
bw.setPropertyValue(new PropertyValue(propertyDescriptor.getName(), value));
}
}
return value;
}
4. 通过限定符查找
如果前面的步骤还没有找到合适的 Bean,那么 Spring 会检查依赖属性上是否有限定符(@Qualifier 注解),如果有,那么 Spring 会根据限定符来选择对应的 Bean。
protected Object resolveQualifier(
String beanName, AbstractBeanDefinition mbd, String typeBeanName) {
Object value = null;
// 查找 BeanFactory 中所有匹配的 Bean
String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, this.descriptor.getResolvableType().getType(), true, false);
for (String candidateName : candidateNames) {
if (candidateName.equals(beanName)) {
continue;
}
if (this.beanFactory instanceof
5. 通过构造函数查找
如果前面的步骤还没有找到合适的 Bean,那么 Spring 会尝试通过构造函数来注入依赖。Spring IOC 会检查 Bean 中的构造函数,然后查找所有参数类型与依赖属性类型匹配的 Bean,如果有且仅有一个 Bean 与之匹配,那么 Spring 会使用该 Bean 来注入依赖属性。
protected Object autowireConstructor(
String beanName, RootBeanDefinition mbd, @Nullable Constructor<?>[] chosenCtors,
Object[] explicitArgs) {
if (chosenCtors == null) {
// 选择最匹配的构造函数
chosenCtors = determineConstructorsFromBeanPostProcessors(mbd.getBeanClass(), beanName);
if (chosenCtors == null) {
// 没有找到匹配的构造函数
throw new UnsatisfiedDependencyException(
getResourceDescription(), beanName, "No suitable constructor found", null);
}
}
Constructor<?> constructorToUse = null;
Object[] argsToUse = null;
int minNrOfArgsResolved = Integer.MAX_VALUE;
Map<String, Object> candidateBeans = findAutowireCandidates(beanName, Object.class, new LinkedMultiValueMap<>());
for (Constructor<?> ctor : chosenCtors) {
Class<?>[] paramTypes = ctor.getParameterTypes();
// 检查构造函数参数类型是否与依赖属性类型匹配
if (constructorToUse != null || paramTypes.length != explicitArgs.length) {
continue;
}
Object[] args = new Object[paramTypes.length];
int nrOfArgsResolved = 0;
for (int i = 0; i < args.length; i++) {
Class<?> paramType = paramTypes[i];
Object autowiredArgument = null;
String autowiredBeanName = null;
if (explicitArgs != null && i < explicitArgs.length) {
// 如果指定了构造函数参数,则使用指定的参数
autowiredArgument = explicitArgs[i];
} else {
// 如果没有指定构造函数参数,则从 BeanFactory 中查找匹配的 Bean
DependencyDescriptor desc = new DependencyDescriptor(new MethodParameter(ctor, i), true);
desc.setContainingClass(mbd.getBeanClass());
Object[] argsToResolve = new Object[]{desc};
try {
autowiredArgument = this.beanFactory.resolveDependency(desc, beanName, candidateBeans, this.typeConverter);
} catch (NoUniqueBeanDefinitionException ex) {
throw ex;
} catch (NoSuchBeanDefinitionException ex) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Ignoring NoSuchBeanDefinitionException for optional dependency on constructor: " + ex);
}
}
}
if (autowiredArgument != null) {
nrOfArgsResolved++;
args[i] = autowiredArgument;
}
}
if (nrOfArgsResolved < minNrOfArgsResolved) {
// 选择参数最少的构造函数
constructorToUse = ctor;
argsToUse = args;
minNrOfArgsResolved = nrOfArgsResolved;
}
}
if (constructorToUse == null) {
// 没有找到匹配的构造函数
throw new UnsatisfiedDependencyException(
getResourceDescription(), beanName, "No suitable constructor found", null);
}
return this.beanFactory.instantiateBean(beanName, mbd, constructorToUse, argsToUse);
}
Spring IOC 容器通过构造函数查找 Bean 的实现方法。Spring 先通过 determineConstructorsFromBeanPostProcessors 方法选择最匹配的构造函数,然后使用 findAutowireCandidates 方法查找所有参数类型与依赖属性类型匹配的 Bean,最后返回使用该构造函数实例化的 Bean。如果没有找到匹配的构造函数或 Bean,那么 Spring 会抛出 UnsatisfiedDependencyException 异常。
转载自:https://juejin.cn/post/7220425595770683452