Spring的IOC,你真的能解釋清楚嗎?

一直以來,SpringFramework 做爲 Java 企業級開發的老大哥,面試中也常被問到。雖然說有些基礎性的問題可能不那麼特別被面試官和求職者重視,但若是真的問起來,能不能回答的準確、全面、有深度,仍是很容易體現出水平的。面試

在接下來的一個系列中,我會慢慢盤點一些 Spring 中常見但很差回答全面的問題,跟小夥伴們分享。編程

本文主題:Spring 的 IOC 如何回答的儘量全面、準確、有深度。架構

問:什麼是IOC?

這是一個看上去特別簡單、感受很無腦的問題,不少求職者在回答這個問題時會不加思考、快速回答出一個很簡單但同時也沒有什麼含量的答案。app

草率的回答

  • IOC 是控制反轉,Inverse of Control 。

試問一句,親,你在作名詞翻譯嗎?框架

就算真的是在作名詞翻譯,也不該該只是把這個簡稱的全稱解釋出來就完事吧,好歹的展開解釋點東西也好吧,做爲面試官不該該只想聽到這麼一點點吧。學習

方向偏了

  • IOC 是控制反轉,它把對象間的依賴關係的維護權利交給了 Spring ,程序自己再也不維護。

這裏面大致上把 IOC 的核心思想解釋出來了:對象間的依賴關係的維護權利發生了轉移,可是請小夥伴們注意,咱們在問 IOC ,這個問題僅僅是問 IOC 自己,與具體的技術無關IOC 不止有 Spring ,只是當下最強大的、使用最廣的是 Spring 而已。ui

因此小夥伴們在回答理論、概念等問題時,不要直接在概念解釋中提到具體的技術,技術都是概念和理論的落地實現,不止一種。單把一種拉出來,面試官可能會以爲:你是否是隻知道這個?翻譯

一種參考回答

該答案僅供參考,可根據自身的知識儲備動態調整。設計

IOC 全名控制反轉 Inverse of Control,它是一種編程原則,它的設計和架構能夠實現組件間的解耦,核心思想是將控制權轉移出去code

這裏面提到了幾個點:

  • 編程原則:它是一種理論,而非具體的某種技術落地
  • 組件間的解耦:所謂耦合,就是上面提到的對象間的依賴關係解耦,就是解除了對象間的依賴關係
    • 提到解耦,有可能面試官會繼續問「什麼是解耦」,也有可能不會問 「爲何用 Spring 」了
  • 控制權的轉移:IOC 爲了實現解耦,將原有的對象間的主動依賴改成被動接收型依賴(由直接 new 變爲 set )

問:IOC與DI的區別

若是對 IOC 的實現不是特別瞭解,或者只是用 SpringFramework 用的太習慣了,亦或是刻板的學習 SpringFramework ,那這個答案一般會是這樣的:

  • IOC 就是 DI 。

若是回答出這個答案,而面試官碰巧也跟你同樣,那恭喜你「瞎貓碰到死耗子」了!由於這個回答真的大錯特錯啊,IOC 不止有 DI 的!

正確的回答應該是:

  • IOC 是一種思想、編程原則,DI 是 IOC 思想的一種實現方式。
  • IOC 的實現方式有依賴查找( Dependency lookup )和依賴注入( Dependency Injection )。

上面已經介紹過了,IOC 僅僅是一種思想,它的意圖是想讓對象間的依賴控制發生轉換。用過 SpringFramework 的小夥伴都知道,你沒有在哪一個地方見過直接寫 IOC 的代碼,都是由一些實現方式來體現 IOC 的。

若是按照上面這樣回答,可能會引來下面一個問題:

依賴查找和依賴注入分別都是什麼?如何區分它們?

針對這個問題,最好不要一上來就搬出代碼解釋,最好是先理論後代碼。

通常狀況下,對比依賴查找和依賴注入,一般能夠從如下幾個維度對比:

依賴查找 依賴注入
實現方式 使用上下文(容器)主動獲取 依賴上下文被動接收
做用目標 一般是方法體內的局部變量,也能夠是對象成員 一般是對象成員
API依賴 依賴 IOC 框架的 API(必須操縱容器的 API ) 能夠不依賴(暴露 setter 方法便可)
applicationContext.getBean(beanName) public void setXXX() { ... }

問:SpringFramework中實現的IOC有什麼?

真的不會有小夥伴只能答出 ApplicationContext 吧,一開始學的時候應該知道還有個 BeanFactory 吧。這是 SpringFramework 中兩個核心的 IOC 容器的抽象。BeanFactory 僅僅是提供了一個容器管理的基本能力,ApplicationContext 在此基礎上作了更加完善、強大的擴展。具體的對比能夠參照下表:

Feature BeanFactory ApplicationContext
Bean instantiation/wiring —— Bean的實例化和屬性注入 Yes Yes
Integrated lifecycle management —— 生命週期管理 No Yes
Automatic BeanPostProcessor registration —— Bean後置處理器的支持 No Yes
Automatic BeanFactoryPostProcessor registration —— BeanFactory後置處理器的支持 No Yes
Convenient MessageSource access (for internalization) —— 消息轉換服務(國際化) No Yes
Built-in ApplicationEvent publication mechanism —— 事件發佈機制(事件驅動) No Yes

下面提供一個比較完整的示例答案,小夥伴們能夠根據本身的知識儲備和理解,調整這裏面的一些描述細節:

BeanFactory 接口提供了一個抽象的配置和對象的管理機制ApplicationContextBeanFactory 的子接口,它簡化了與 AOP 的整合、消息機制、事件機制,以及對 Web 環境的擴展WebApplicationContext 等),BeanFactory 是沒有這些擴展的。

ApplicationContext 主要擴展瞭如下功能:(括號內的部分是解釋擴展功能的一些簡單描述或者原理底層實現,能回答出來更好)

  • AOP的支持AnnotationAwareAspectJAutoProxyCreator 做用於 Bean 的初始化以後 )
  • 配置元信息BeanDefinitionEnvironment 、註解等 )
  • 資源管理Resource 抽象 )
  • 事件驅動機制ApplicationEventApplicationListener
  • 消息與國際化LocaleResolver
  • Environment 抽象(SpringFramework 3.1之後)

問:依賴注入的注入方式?有什麼區別?

注意這個問題也是與 SpringFramework 無關的,注入的方式自己就應該是依賴注入的實現,至於框架的代碼,那是人家對於這個方式的落地。

可從如下幾個維度對比:

注入方式 被注入成員是否可變 是否依賴IOC框架的API 使用場景
構造器注入 不可變 否(xml、編程式注入不依賴) 不可變的固定注入
參數注入 不可變 是(只能經過標註註解來侵入式注入) 一般用於不可變的固定注入
setter注入 可變 否(xml、編程式注入不依賴) 可選屬性的注入

基本上問這個問題的話,還可能會繼續問另外一個問題:

你以爲哪一種方式好?爲何?

「莽夫」應聘者一看終於來開放式問題了,趕忙開始自由發揮了:

我以爲參數注入好,由於我寫習慣了,給參數打註解多舒服啊!

你是這麼說了,面試官咋想:就這?就這???進而對你的印象可能就會有所減分了。

這種問題,除了要表述主觀見解以外,更多的是要根據一些既有的論述來輔助你的觀點,最好的論述那必定是官方文檔了。

SpringFramework 的官方文檔在不一樣的版本推薦的注入方式是不一樣的:

  • SpringFramework 4.0.2 及以前是推薦 setter 注入,理由是一個 Bean 有多個依賴時,構造器的參數列表會很長;並且若是 Bean 中依賴的屬性不都是必需的話,注入會變得更麻煩
  • 4.0.3 及之後官方推薦構造器注入,理由是構造器注入的依賴是不可變的、徹底初始化好的,且能夠保證不爲 null
  • 固然 4.0.3 及之後的官方文檔中也說了,若是真的出現構造器參數列表過長的狀況,多是這個 Bean 承擔的責任太多,應該考慮組件的責任拆解

問:組件注入的註解有什麼?有什麼區別?

相信大多數小夥伴都能答出 @Autowired@Resource 吧,若是答出這兩個,那證實你應該用過,也會用。但你能回答出 @Inject ,證實你對這些注入的註解確實有瞭解。做爲應聘者,在回答問題時必定是回答的儘量全面爲好,下面對這幾種註解做一個對比:

註解 注入方式 是否支持@Primary 來源 Bean不存在時處理
@Autowired 根據類型注入 SpringFramework原生註解 可指定 required=false 來避免注入失敗
@Resource 根據名稱注入 JSR250規範 容器中不存在指定Bean會拋出異常
@Inject 根據類型注入 JSR330規範 ( 須要導jar包 ) 容器中不存在指定Bean會拋出異常

跟上面差很少,若是問到了這個問題,那就有可能繼續被問到下面一個問題:

存在多個相同類型Bean時如何解決注入問題?

可能大多數小夥伴都能答出如下幾種解決方案:

  • @Resource :根據名稱指定注入的 Bean
  • @Qualifier :配合 @Autowired 註解使用,若是被標註的成員 / 方法在根據類型注入時發現有多個相同類型的 Bean ,則會根據該註解聲明的 name 尋找特定的 bean
  • @Primary :配合 @Bean 註解使用,若是有多個相同類型的 Bean 同時註冊到 IOC 容器中,使用 @Autowired@Inject 註解時會注入標註 @Primary 註解的 bean

其實你還能夠提另一種方案:把注入的字段名與 bean 的名稱保持一致,這樣也能夠解決注入時報不惟一 Bean 的問題。

以上幾個問題是關於 SpringFramework 與 IOC 部分的一些常見問題,卻是問題都不太陌生,可是小夥伴們想回答的全面、有深度,仍是須要下下功夫的。但願小夥伴們能有所收穫,在面試中流利回答,斬獲 offer !


問題整理不易,不點個贊支持一下做者嗎?(可憐巴巴)

相關文章
相關標籤/搜索