聊聊Autowired的常考面試題

ldzsl發表於2021-09-09

金三銀四,很快又到了招聘旺季了,最近經常需要去做各種面試,發現很多幾年工作經驗的候選人,對Spring瞭解也是知之甚少,更多的只是會用,比如一個@Autowired原理都可以問倒一大片。

為此,趁著女朋友狗澤今天加班,長話短說的聊聊這個話題吧!

@Autowired註解是如何實現自動裝配的

@Autowired註解之所以可以實現自動裝配,主要是依賴Spring提供的處理器AutowiredAnnotationBeanPostProcessor,該處理器在初始化的時候便加入了對@Autowired、@Inject、@Value三個註解的處理;

該處理器實現了介面InstantiationAwareBeanPostProcessor,因此可以在bean物件例項化的時候,對其使用了@Autowired的成員進行自動裝配。

原始碼參考如下:

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.
}
}

該處理器何時被加入的

當構造Spring容器的時候,Spring會向容器註冊幾個內建的處理器物件,其中就包括了AutowiredAnnotationBeanPostProcessor。

原始碼可以直接看AnnotationConfigUtils.registerAnnotationConfigProcessors方法。

原始碼參考如下:

public static Set 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;
}

該處理器是什麼時候被呼叫的

Spring在建立bean的時候會呼叫doCreateBean方法,在doCreateBean方法中會呼叫populateBean方法,該方法的作用便是先判斷是否該bean物件需要進行自動裝配,如果是的話再逐個遍歷呼叫Spring容器已經注入的處理器。

原始碼參考如下:

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;
}
}
}
}
}
}

自動裝配有幾種方式

沒有使用過xml配置進行注入的可能都無法回答這個問題,但是但凡看過Spring實戰類書籍的都應該知道這個知識點。

Spring目前支援三種方式,分別是

  • AUTOWIRE_BY_NAME 按名稱自動裝配bean屬性

  • AUTOWIRE_BY_TYPE 按型別自動裝配bean屬性

  • AUTOWIRE_CONSTRUCTOR 按構造器自動裝配

在xml配置注入的時候指定下型別即可進行切換,比如:

圖片描述

擴充:我司這邊不推薦使用在xml裡邊指定自動裝配型別,因為開發者無法對Spring應用中的所有Bean的情況都瞭如指掌,而透過這種方式指定會導致注入的物件也存在不確定性。

那麼使用@Autowired指定的是哪種自動裝配

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

但是實際上看原始碼的實現其實率先透過型別來裝配,如果匹配到的實現是多個的,才會額外採用其他策略。

如果@Autowried註解的介面有多個實現,Spring是如何處理的

大部分人遇見這種情況都是直接回答報錯,其實不是的。

如果介面有多個實現,Spring有自己的一套策略:

  • 會看看有沒有使用了@Primary註解的bean

  • 根據@Priority註解優先順序選擇優先順序高的。

  • 根據屬性的名稱和Spring中beanName來進行判斷。

再找不到才會報錯,也就是NoUniqueBeanDefinitionException異常。

具體實現原始碼如下:

圖片描述

@Autowired注入支援哪幾種型別

碼齡較短的或者沒有看過原始碼的都無法回答這個問題,基本上都只知道@Autowired注入一個物件。

而實際上@Autowired除了註冊單個物件外,還額外支援注入三種型別,分別是陣列、集合以及map型別。

具體參考DefaultListableBeanFactory.resolveMultipleBeans方法

總結

這幾個問題並非必考點,只是說如果考察@Autowired的話基本上問題就是這幾個了,好了,狗澤下班了,就先這樣吧。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2325/viewspace-2826986/,如需轉載,請註明出處,否則將追究法律責任。

相關文章