聊聊Autowired的常考面试题

金三银四,很快又到了招聘旺季了,最近经常需要去做各种面试,发现很多几年工作经验的候选人,对Spring了解也是知之甚少,更多的只是会用,比如一个@Autowired原理都可以问倒一大片。

为此,趁着女朋友狗泽今天加班,长话短说的聊聊这个话题吧!

@Autowired注解是如何实现自动装配的

@Autowired注解之所以可以实现自动装配,主要是依赖Spring提供的处理器AutowiredAnnotationBeanPostProcessor,该处理器在初始化的时候便加入了对@Autowired、@Inject、@Value三个注解的处理;

该处理器实现了接口InstantiationAwareBeanPostProcessor,因此可以在bean对象实例化的时候,对其使用了@Autowired的成员进行自动装配。

源码参考如下:

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" cid="n408" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public AutowiredAnnotationBeanPostProcessor() {
// 加入了对@Autowired、@Inject、@Value三个注解的处理
this.autowiredAnnotationTypes.add(Autowired.class);
this.autowiredAnnotationTypes.add(Value.class);
try {
this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
logger.info("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}</pre>

该处理器何时被加入的

当构造Spring容器的时候,Spring会向容器注册几个内置的处理器对象,其中就包括了AutowiredAnnotationBeanPostProcessor。

源码可以直接看AnnotationConfigUtils.registerAnnotationConfigProcessors方法。

源码参考如下:

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" cid="n437" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
BeanDefinitionRegistry registry, @Nullable Object source) {

// 省略代码...

if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}

// 省略代码...

return beanDefs;
}</pre>

该处理器是什么时候被调用的

Spring在创建bean的时候会调用doCreateBean方法,在doCreateBean方法中会调用populateBean方法,该方法的作用便是先判断是否该bean对象需要进行自动装配,如果是的话再逐个遍历调用Spring容器已经注入的处理器。

源码参考如下:

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" cid="n441" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
if (hasInstAwareBpps || needsDepCheck) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
}
PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
if (hasInstAwareBpps) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
// 执行后置处理器,填充属性,完成自动装配
pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvs == null) {
return;
}
}
}
}
}
}</pre>

自动装配有几种方式

没有使用过xml配置进行注入的可能都无法回答这个问题,但是但凡看过Spring实战类书籍的都应该知道这个知识点。

Spring目前支持三种方式,分别是

  • AUTOWIRE_BY_NAME 按名称自动装配bean属性

  • AUTOWIRE_BY_TYPE 按类型自动装配bean属性

  • AUTOWIRE_CONSTRUCTOR 按构造器自动装配

在xml配置注入的时候指定下类型即可进行切换,比如:

image-20210206191158324

拓展:我司这边不推荐使用在xml里边指定自动装配类型,因为开发者无法对Spring应用中的所有Bean的情况都了如指掌,而通过这种方式指定会导致注入的对象也存在不确定性。

那么使用@Autowired指定的是哪种自动装配

答案是AUTOWIRE_NO,也就是没有指定。

但是实际上看源码的实现其实率先通过类型来装配,如果匹配到的实现是多个的,才会额外采用其他策略。

如果@Autowried注解的接口有多个实现,Spring是如何处理的

大部分人遇见这种情况都是直接回答报错,其实不是的。

如果接口有多个实现,Spring有自己的一套策略:

  • 会看看有没有使用了@Primary注解的bean

  • 根据@Priority注解优先级选择优先级高的。

  • 根据属性的名称和Spring中beanName来进行判断。

再找不到才会报错,也就是NoUniqueBeanDefinitionException异常。

具体实现源码如下:

image-20210206160808690

@Autowired注入支持哪几种类型

码龄较短的或者没有看过源码的都无法回答这个问题,基本上都只知道@Autowired注入一个对象。

而实际上@Autowired除了注册单个对象外,还额外支持注入三种类型,分别是数组、集合以及map类型。

具体参考DefaultListableBeanFactory.resolveMultipleBeans方法

总结

这几个问题并非必考点,只是说如果考察@Autowired的话基本上问题就是这几个了,好了,狗泽下班了,就先这样吧。

知道大家需要,就整理了一份学Spring必备的电子书,有需要关注公众号自取,公众号回复:Spring

image-20210206190835933

有兴趣的关注我一波,Java面试官带你们跨过一个个的面试坑,保证不亏。

原文链接:https://mp.weixin.qq.com/s/yAFMGOh5WODueLVet-YUUA

谢谢点赞支持👍👍👍!

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容