金三銀四,很快又到了招聘旺季了,最近經(jīng)常需要去做各種面試,發(fā)現(xiàn)很多幾年工作經(jīng)驗的候選人,對Spring了解也是知之甚少,更多的只是會用,比如一個@Autowired原理都可以問倒一大片。
為此,趁著女朋友狗澤今天加班,長話短說的聊聊這個話題吧!
@Autowired注解是如何實現(xiàn)自動裝配的
@Autowired注解之所以可以實現(xiàn)自動裝配,主要是依賴Spring提供的處理器AutowiredAnnotationBeanPostProcessor,該處理器在初始化的時候便加入了對@Autowired、@Inject、@Value三個注解的處理;
該處理器實現(xiàn)了接口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會向容器注冊幾個內(nèi)置的處理器對象,其中就包括了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>
該處理器是什么時候被調(diào)用的
Spring在創(chuàng)建bean的時候會調(diào)用doCreateBean方法,在doCreateBean方法中會調(diào)用populateBean方法,該方法的作用便是先判斷是否該bean對象需要進行自動裝配,如果是的話再逐個遍歷調(diào)用Spring容器已經(jīng)注入的處理器。
源碼參考如下:
<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;
// 執(zhí)行后置處理器,填充屬性,完成自動裝配
pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvs == null) {
return;
}
}
}
}
}
}</pre>
自動裝配有幾種方式
沒有使用過xml配置進行注入的可能都無法回答這個問題,但是但凡看過Spring實戰(zhàn)類書籍的都應該知道這個知識點。
Spring目前支持三種方式,分別是
AUTOWIRE_BY_NAME 按名稱自動裝配bean屬性
AUTOWIRE_BY_TYPE 按類型自動裝配bean屬性
AUTOWIRE_CONSTRUCTOR 按構造器自動裝配
在xml配置注入的時候指定下類型即可進行切換,比如:

拓展:我司這邊不推薦使用在xml里邊指定自動裝配類型,因為開發(fā)者無法對Spring應用中的所有Bean的情況都了如指掌,而通過這種方式指定會導致注入的對象也存在不確定性。
那么使用@Autowired指定的是哪種自動裝配
答案是AUTOWIRE_NO,也就是沒有指定。
但是實際上看源碼的實現(xiàn)其實率先通過類型來裝配,如果匹配到的實現(xiàn)是多個的,才會額外采用其他策略。
如果@Autowried注解的接口有多個實現(xiàn),Spring是如何處理的
大部分人遇見這種情況都是直接回答報錯,其實不是的。
如果接口有多個實現(xiàn),Spring有自己的一套策略:
會看看有沒有使用了@Primary注解的bean
根據(jù)@Priority注解優(yōu)先級選擇優(yōu)先級高的。
根據(jù)屬性的名稱和Spring中beanName來進行判斷。
再找不到才會報錯,也就是NoUniqueBeanDefinitionException異常。
具體實現(xiàn)源碼如下:

@Autowired注入支持哪幾種類型
碼齡較短的或者沒有看過源碼的都無法回答這個問題,基本上都只知道@Autowired注入一個對象。
而實際上@Autowired除了注冊單個對象外,還額外支持注入三種類型,分別是數(shù)組、集合以及map類型。
具體參考DefaultListableBeanFactory.resolveMultipleBeans方法
總結(jié)
這幾個問題并非必考點,只是說如果考察@Autowired的話基本上問題就是這幾個了,好了,狗澤下班了,就先這樣吧。
知道大家需要,就整理了一份學Spring必備的電子書,有需要關注公眾號自取,公眾號回復:Spring

有興趣的關注我一波,Java面試官帶你們跨過一個個的面試坑,保證不虧。
原文鏈接:https://mp.weixin.qq.com/s/yAFMGOh5WODueLVet-YUUA
謝謝點贊支持??????!