栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring @Autowired 属性注入过程分析

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring @Autowired 属性注入过程分析

先来看一个例子

public interface MyService {
}
@Service
public class MyServiceImpl implements MyService {
}
@RestController
public class MyController {
    @Autowired
    private MyService myService;
}

这种情况是我们常用的方式,一个 Service 接口只有一个实现,一点问题没有。

但是如果此时又有一个实现类实现了 Service 接口后,会怎样呢?

@Service
public class MyServiceImpl2 implements MyService {
}

此时 MyController 如果还是原来的,那么就会报错了。

Field myService in MyController required a single bean, but 2 were found.

错误显示有两个 myService 导致 MyController 不知道该使用哪一个。

我先给出解决方案,只需要将属性名和 Bean 名称匹配即可。

@RestController
public class MyController {
    @Autowired
    private MyService myServiceImpl;
    @Autowired
    private MyService myServiceImpl2;
}

由于实现类的 Bean 名称就是它们的类名首字母小写,所以将属性名改成类名首字母小写形式,明确指定我要使用哪一个实现类。

接下来,我们来分析下源码,看看这里面的前因后果。

首先,一个 MyController 这个 Bean 的创建是由 AbstractAutowireCapableBeanFactory#createBeanInstance来完成的,构造出了 MyController 实例后,会调用 AbstractAutowireCapableBeanFactory#populate来填充属性,这里填充的就是 myService。

填充过程的关键就是执行各种 BeanPostProcessor 处理器,当这个 bp 表示 AutowiredAnnotationBeanPostProcessor 时,就表示进行 @Autowired 属性注入了。

我们从调用栈可以看出,是调用了 AutowiredAnnotationBeanPostProcessor#postProcessProperties

public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
  // 寻找要注入的字段和方法
  Injectionmetadata metadata = this.findAutowiringmetadata(beanName, bean.getClass(), pvs);

  try {
    // 注入
    metadata.inject(bean, beanName, pvs);
    return pvs;
  } catch (BeanCreationException var6) {
    throw var6;
  } catch (Throwable var7) {
    throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", var7);
  }
}

inject 方法最终调用的是 AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject

protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
  Field field = (Field)this.member;
  Object value;
  if (this.cached) {
    try {
      value = AutowiredAnnotationBeanPostProcessor.this.resolvedCachedArgument(beanName, this.cachedFieldValue);
    } catch (NoSuchBeanDefinitionException var7) {
      value = this.resolveFieldValue(field, bean, beanName);
    }
  } else {
    // 寻找依赖
    value = this.resolveFieldValue(field, bean, beanName);
  }

  if (value != null) {
    ReflectionUtils.makeAccessible(field);
    field.set(bean, value);
  }

}

我们看第 12 行的 resolveFieldValue 方法

@Nullable
private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {
  //...
  try {
    // 解析依赖
    value = AutowiredAnnotationBeanPostProcessor.this.beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
  } catch (BeansException var12) {
    throw new UnsatisfiedDependencyException((String)null, beanName, new InjectionPoint(field), var12);
  }
  //...
}

第 6 行我们继续跟进去

@Nullable
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
//...
      if (result == null) {
        // 跟进去
        result = this.doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
      }

      return result;
    }
  } else {
    return new DefaultListableBeanFactory.DependencyObjectProvider(descriptor, requestingBeanName);
  }
}

在 doResolveDependency 方法内我们注意到这行代码

Map matchingBeans = this.findAutowireCandidates(beanName, type, descriptor);

这行代码是用来找到 MyService 接口的所有实现类,跟进去看下

通过 BeanFactoryUtils.beanNamesForTypeIncludingAncestors 找到所有的实现类的 beanName,这里我们发现有两个。

好,现在我们知道了 @Autowired 是根据类型来获取所有 bean 的,获取完后接下来就是匹配了,那之前发生的错误必然就出现在了匹配阶段。没错,我们继续往下看。

我们找到如下代码片段

matchingBeans 的值为 2 我们已经知道了,那么该值如果大于 1,这时候就需要通过 determineAutowireCandidate 方法来判断到底选择哪一个了。

protected String determineAutowireCandidate(Map candidates, DependencyDescriptor descriptor) {
  Class requiredType = descriptor.getDependencyType();
  // 1.@Primary 优先
  String primaryCandidate = this.determinePrimaryCandidate(candidates, requiredType);
  if (primaryCandidate != null) {
    return primaryCandidate;
  } else {
    // 2.@Priority 其次
    String priorityCandidate = this.determineHighestPriorityCandidate(candidates, requiredType);
    if (priorityCandidate != null) {
      return priorityCandidate;
    } else {
      // 3. 根据 beanName 匹配
      Iterator var6 = candidates.entrySet().iterator();

      String candidateName;
      Object beanInstance;
      do {
        if (!var6.hasNext()) {
          return null;
        }

        Entry entry = (Entry)var6.next();
        candidateName = (String)entry.getKey();
        beanInstance = entry.getValue();
      } while((beanInstance == null || !this.resolvableDependencies.containsValue(beanInstance)) && !this.matchesBeanName(candidateName, descriptor.getDependencyName()));

      // 4.都没有返回 null
      return candidateName;
    }
  }
}

我们从 determineAutowireCandidate 方法中可以看出,确定使用哪个 bean 的判断标准有三个:

  • 标注了 @Primary 注解的就是被 Spring 选中的幸运儿
  • 如果没有标注 @Primary,但是标注了 @Priority,会获取优先级最高的 bean 作为幸运儿
  • 如果 @Primary 和 @Priority 都没有标注,会匹配属性名和 beanName 相同的 bean 作为幸运儿
  • 要是以上三个条件,一个都不满足,返回 null

我们这里就比较悲催了,一个都不满足,返回了 null,紧接着就返回错误异常信息了。

以上就是 @Autowired 的注入过程分析了,网上说的不清不楚,不如自己调试一下就明白了。

我是知识不加糖,关注我,后续还会分享更多有意思的文章!

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/666620.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号