《完爆面試官》系列之Spring源碼篇(上)

前言

​ HR小姐姐帶領着我來到一間洽談室中,眼前只見一張長長的會議桌,環顧四周,我當即找了個面向窗戶的位子,這樣有利於面試官能夠清楚地看見索大的刷臉,而後緩緩地坐下來。HR和我簡單的寒暄幾句後,遞給我一杯水,讓我稍等一會,面試官立刻就來了。java

​ 索大暗暗思道:這個小姐姐人還挺好,有禮貌,聲音也很溫柔,面試官應該也不賴吧!web

面試開始

​ 沒過多久,一位身着格子衫+牛仔褲搭配,體型略顯瘦高,戴着一個黑框框眼鏡的男士,推開洽談室的門。面試

​ 索大禮貌性地起身問候:帥氣的面試官,您好呀。spring

面試官仔細地翻閱索大的簡歷,來來回回翻了幾回,內心暗道:數據庫

這傢伙,整整寫了五頁紙,不知道是真有點東西仍是湊字數的,待我來考考他吧

小夥子,看你簡歷寫的挺多的,並且不少項目都是使用Spring框架,那你說下你對Spring的理解吧?

幸虧索大以前有對Spring框架進行一個全面的複習,爽道:編程

Spring 是個java企業級應用的開源開發框架。Spring主要用來開發Java應用,可是有些擴展是針對構建J2EE平臺的web應用。Spring 框架目標是簡化Java企業級應用開發,並經過POJO爲基礎的編程模型促進良好的編程習慣。而他主要是經過:控制反轉(IOC)、依賴注入(DI)以及面向切面(AOP)這三種方式來達成的。緩存

spring之因此強大,是由於Spring有衆多模塊爲咱們提供了開發企業應用的一切安全

Spring的七個核心模塊
Spring的七個核心模塊
  • Spring Core:核心類庫提供spring框架的基本功能IOC服務。Spring以bean的方式組織和管理Java應用中的各個組件及其關係。Spring使用BeanFactory來產生和管理Bean,它是工廠模式的實現。框架

  • Spring Context:Spring上下文是一個配置文件,向Spring框架提供上下文信息。Spring上下文包括企業服務,如JNDI、EJB、電子郵件、國際化、校驗和調度功能。提供框架式Bean訪問方式,其餘程序能夠經過Context訪問Spring的Bean資源。編輯器

  • Spring AOP:Spring在它的AOP模塊中提供了對面向切面編程的豐富支持。例如方法攔截器(method-interceptors)和切點(pointcuts),能夠有效的防止代碼上功能的耦合,這個模塊是在Spring應用中實現切面編程的基礎。

  • Spring DAO:DAO模塊主要目的是將持久層相關問題與通常的的業務規則和工做流隔離開來。Spring 中的DAO提供一致的方式訪問數據庫,無論採用何種持久化技術,Spring都提供一致的編程模型。Spring的DAO模塊對JDBC進行了再封裝,隱藏了Connection、Statement、ResultSet等JDBC API,使DAO模塊直接繼承JdbcDaoSupport類。

  • Spring ORM:對現有的ORM框架的支持;

  • Spring Web:提供了基本的面向Web的綜合特性,例如多方文件上傳;

  • Spring MVC:MVC框架是一個全功能的構建Web應用程序的MVC實現。經過策略接口,MVC框架變成爲高度可配置的。Spring的MVC框架提供清晰的角色劃分:控制器、驗證器、命令對象、表單對象和模型對象、分發器、處理器映射和視圖解析器。Spring支持多種視圖技術。

你說下使用Spring框架的好處是什麼?

索大回答這類問題,通常喜歡從他的特色來分析,好比:

(1)spring屬於低侵入式設計,代碼的污染極低;

(2)spring的DI機制將對象之間的依賴關係交由框架處理,減低組件的耦合性;

(3)Spring提供了AOP技術,支持將一些通用任務,如安全、事務、日誌、權限等進行集中式管理,從而提供更好的複用。

(4)spring對於主流的應用框架提供了集成支持。

你說下什麼是Spring IOC 容器?

​ IOC就是控制反轉,是指建立對象的控制權的轉移,之前建立對象的主動權和時機是由本身把控的,而如今這種權力轉移到Spring容器中,並由容器根據配置文件去建立實例和管理各個實例之間的依賴關係,對象與對象之間鬆散耦合,也利於功能的複用。DI依賴注入,和控制反轉是同一個概念的不一樣角度的描述,即 應用程序在運行時依賴IoC容器來動態注入對象須要的外部資源。

​ 索大這樣回答其實還不能打動面試官,還要進行深刻分析。在Spring容器中,

誰控制誰?:固然是IoC 容器控制了對象

控制了什麼?:主要控制了外部資源獲取(不僅是對象包括好比文件等)

什麼方面反轉了?:得到依賴對象的方式反轉了

經過一張圖來解釋從IOC容器獲取對象的過程:

咱們來總結下實現原理 Spring的IOC容器是經過反射機制+工廠模式實現的,在實例化一個類時,它經過反射調用類中set方法將事先保存在HashMap中的類屬性注入到類中。

*回答到這裏,若是不順便把**依賴注入(DI)*說下,彷佛不能體現索大的逼格啊

依賴注入(Dependency Injection):就是說組件bean之間依賴關係由容器在運行期決定,形象的說,即由容器動態的將某個依賴關係注入到組件bean之中。經過依賴注入機制,咱們只須要經過簡單的配置,而無需任何代碼就可指定目標須要的資源,完成自身的業務邏輯,而不須要關心具體的資源來自何處,由誰實現。

不過咱們須要注意兩個細節:

  1. 依賴注入發生的時間 (1).用戶第一次經過getBean方法向IoC容索要Bean時,IoC容器觸發依賴注入。 (2).當用戶在Bean定義資源中爲元素配置了lazy-init屬性,即讓容器在解析註冊Bean定義時進行預實例化,觸發依賴注入。
  2. 依賴注入實如今如下兩個方法中: (1).createBeanInstance:生成Bean所包含的java對象實例。 (2).populateBean :對Bean屬性的依賴注入進行處理。

那你知道Spring的注入方式有哪幾種?

​ 常見的注入方式有三種:setter 屬性注入、構造方法注入和註解方式注入。

  • Setter方法注入:Setter方法注入是容器經過調用無參構造器或無參static工廠 方法實例化bean以後,調用該bean的setter方法,即實現了基於setter的依賴注入。

  • 構造器依賴注入:構造器依賴注入經過容器觸發一個類的構造器來實現的,該類有一系列參數,每一個參數表明一個對其餘類的依賴。

  • 註解方式注入:使用@Autowired註解來自動裝配指定的bean。@Autowired可用於:構造函數、成員變量、Setter方法。

    若是查詢結果恰好爲一個,就將該bean裝配給@Autowired指定的數據;

    若是查詢的結果不止一個,那麼@Autowired會根據名稱來查找;

    若是上述查找的結果爲空,那麼會拋出異常。解決方法時,使用required=false。

注意:@Autowired和@Resource之間的區別

  • @Autowired默認是按照類型裝配注入的,默認狀況下它要求依賴對象必須存在(能夠設置它required屬性爲false)。

  • @Resource默認是按照名稱來裝配注入的,只有當找不到與名稱匹配的bean纔會按照類型來裝配注入。

你知道spring循環依賴是怎麼解決的?

索大先假設場景以下,A->B->A

一、實例化A,並將未注入屬性的A暴露出去,即提早曝光給容器Wrap 二、開始爲A注入屬性,發現須要B,調用getBean(B) 三、實例化B,並注入屬性,發現須要A的時候,從單例緩存中查找,沒找到時繼而從Wrap中查找,從而完成屬性的注入 四、遞歸完畢以後回到A的實例化過程,A將B注入成功,並注入A的其餘屬性值,自此即完成了循環依賴的注入

spring循環依賴流程圖
spring循環依賴流程圖

首先咱們要清楚Spring單例對象的初始化能夠分爲三步:

  1. 【createBeanInstance】, 實例化, 實際上就是調用對應的構造方法構造對象, 此時只是調用了構造方法,spring xml中指定的property並無進行populate
  2. 【populateBean】,填充屬性, 這步對spring xml中指定的property進行populate
  3. 【initializeBean】,調用spring xml中指定的init方法, 或者AfterPropertiesSet方法

會發生循環依賴的步驟集中在第一步和第二步

而後咱們還要對Spring的三級緩存有所瞭解

​ 對於單例對象來講,在Spring的整個容器的生命週期內, 有且只存在一個對象,很容易想到這個對象應該存在Cache中,Spring大量運用了Cache的手段,在循環依賴問題的解決過程當中甚至使用了「三級緩存」。

  1. singletonObjects:指單例對象的cache
  2. earlySingletonObjects:指提早曝光的單例對象的cache
  3. singletonFactories:指單例對象工廠的cache

Spring解決循環依賴的步驟:

首先Spring會嘗試從緩存中獲取,這個緩存就是指singletonObjects;

  1. Spring首先從singletonObjects(一級緩存)中嘗試獲取,
  2. 若是獲取不到而且對象在建立中,則嘗試從earlySingletonObjects(二級緩存)中獲取;
  3. 若是仍是獲取不到而且容許從singletonFactories經過getObject獲取,則經過singletonFactory.getObject()(三級緩存)獲取
  4. 若是獲取到了則移除對應的singletonFactory, 將singletonObject放入到earlySingletonObjects, 其實就是將三級緩存提高到二級緩存中!

解決循環依賴的關鍵, 單例對象此時已經被建立出來的。這個對象已經被生產出來了,雖然還不完美(尚未進行初始化的第二步和第三步),可是已經能被人認出來了(根據對象引用能定位到堆中的對象),因此Spring此時將這個對象提早曝光出來讓你們認識,讓你們使用。

最後索大來個小結:

​ Spring循環依賴的理論依據實際上是Java基於值傳遞,傳遞引用, 當咱們獲取到對象的引用時,對象的field或者或屬性是能夠延後設置的。

​ Spring經過三級緩存加上「提早曝光」機制,配合Java的對象引用原理,比較完美地解決了某些狀況下的循環依賴問題!

面試結束

面試官上下打量着我,絕不吝嗇地誇了一句:

小夥子,不賴呀。

這個時候,必定不要激動,能夠給個低調奢華的表情

總結

​ Spring 是一個主流的 Java Web 開發框架,該框架是一個輕量級的應用框架,具備很高的凝聚力和吸引力。Spring 框架因其強大的功能以及卓越的性能而受到衆多開發人員的喜好。涉及到的知識遠不止於此,咱們要腳踏實地地去實踐,而後理解並掌握其中的原理。

參考資料:

  1. Spring官網 https://spring.io/
  2. 計文柯 《深刻理解Spring技術內幕》
  3. 郝佳 《Spring源碼深度解析》

花絮

以上就是本篇文章的所有內容了,謝謝你們的閱覽,各位的支持和鼓勵是索大前進的動力,下一篇我

們不見不散。

碼字不易啊,喜歡的話不妨點贊👍 關注💓分享給朋友👥,這對我真的很重要呀!

相關文章
相關標籤/搜索