摘要:依賴注入是向某個(gè)類或方法注入一個(gè)值,其中所用到的原理就是控制反轉(zhuǎn)。但發(fā)現(xiàn)更多時(shí)間是在調(diào)和的源碼。里面就是從中取出這個(gè),完成控制反轉(zhuǎn)的??刂品崔D(zhuǎn)的優(yōu)點(diǎn)最后來(lái)以我個(gè)人觀點(diǎn)談?wù)効刂品崔D(zhuǎn)的優(yōu)點(diǎn)吧??刂品崔D(zhuǎn)為了降低項(xiàng)目耦合,提高延伸性。
本章開始來(lái)學(xué)習(xí)下Spring的源碼,看看Spring框架最核心、最常用的功能是怎么實(shí)現(xiàn)的。
網(wǎng)上介紹Spring,說源碼的文章,大多數(shù)都是生搬硬推,都是直接看來(lái)的觀點(diǎn)換個(gè)描述就放出來(lái)。這并不能說有問題,但沒有從一個(gè)很好的、容易切入的角度去了解學(xué)習(xí)。博主來(lái)嘗試拋棄一些所知,從使用上入手,步步回溯源碼去了解學(xué)習(xí)。
很多人會(huì)混亂IOC和DI的兩個(gè)概念,其實(shí)這兩者是層面的不同。
具體的區(qū)別的區(qū)別:IOC是DI的原理。依賴注入是向某個(gè)類或方法注入一個(gè)值,其中所用到的原理就是控制反轉(zhuǎn)。
所以說到操作層面的時(shí)候用DI,原理層的是說IOC,下文亦同。
對(duì)于DI最新使用方法,現(xiàn)在都是建議用Java注解去標(biāo)識(shí)。但是相信筆者,不要用這種方式去看源碼。筆者本來(lái)是想從Java注解入手去一步步看源碼,debug看看發(fā)生什么了。但發(fā)現(xiàn)更多時(shí)間是在調(diào)SpringBoot和AOP的源碼。在看了一天后,還是換一種思路吧,因?yàn)锳OP是打算在下一章再講的。
所以我用XML的方式,搭了一個(gè)最簡(jiǎn)單的Spring項(xiàng)目來(lái)學(xué)習(xí)其中IOC的源碼。建議大家把代碼拉下來(lái),跟著筆者思路來(lái)一起看。
源碼在此:https://github.com/Zack-Ku/sp...
maven的依賴,只添加了spring-context模板,用的是4.3.11版本(部分代碼)
org.springframework spring-context 4.3.11.RELEASE
作為Bean的Service(部分代碼)
public class TestBeanServiceImpl implements TestBeanService { public String getBean() { return "a test bean"; } }
配置XML(部分代碼)
啟動(dòng)類。只是加載了下spring的xml配置,然后從context中拿出Bean,這就是完整IOC的過程了。(部分代碼)
public class Application { public static void main(String[] args) { // 加載xml配置 ApplicationContext context = new ClassPathXmlApplicationContext("classpath:application.xml"); // IOC獲取Bean TestBeanService testBeanService = context.getBean(TestBeanService.class); System.out.println(testBeanService.getBean()); } }
最后啟動(dòng)就能獲取這個(gè)bean,看到getMessage()打印的內(nèi)容了。
這樣就是一個(gè)比較純粹的Spring-IOC的項(xiàng)目了。我們直接從啟動(dòng)類開始看起
Bean的含義前置先解釋下這個(gè)Bean的含義,因?yàn)闀?huì)貫穿整個(gè)流程。
通俗地講,Bean就是IOC的容器。如上面的例子,將TestBeanService注冊(cè)到Spring里,那么TestBeanService就是Spring的里面的一個(gè)Bean。Demo里面context.getBean()就是從Spring中取出這個(gè)Bean,完成控制反轉(zhuǎn)的。
所以我們的重點(diǎn)就是要看看Spring到底是怎么生成管理這些Bean的。
ClassPathXmlApplicationContext啟動(dòng)類中,加載配置的ClassPathXmlApplicationContext肯定就是完成IOC的核心。不知道它到底是怎么做的,怎么入手呢?
先來(lái)看看它的類圖
先分析下這個(gè)類圖,
ClassPathXmlApplicationContext類是AbstractApplicationContext抽象類的子類
AbstractApplicationContext類是ApplicaionContext接口的實(shí)現(xiàn)。
ApplicaionContext接口集合了非常多的內(nèi)容,其中和IOC比較相關(guān)的就是ListableBeanFactory接口和HierarchicalBeanFactory接口
ListableBeanFactory接口和HierarchicalBeanFactory接口是繼承BeanFactory
從此分析可以看出,ClassPathXmlApplicationContext是什么,了解下ApplicaionContext;它怎么和IOC有關(guān),要了解BeanFactory。
所以后面我們先來(lái)看看ApplicaionContext與BeanFactory。
從該接口的注解描述可知,ApplicationContext是整個(gè)項(xiàng)目的配置,Spring項(xiàng)目在啟動(dòng)或運(yùn)行的時(shí)候都需要依賴到它。
其中Bean管理相關(guān)的則是ListableBeanFactory和HierarchicalBeanFactory。
BeanFactoryListableBeanFactory和HierarchicalBeanFactory都是繼承BeanFactory的。
先看看BeanFactory的文件注解
從上圖可知,BeanFactory就是獲取Bean容器的地方。而且他可以提供單例的對(duì)象或者是獨(dú)立的對(duì)象
從這段可以得知,HierarchicalBeanFactory是一個(gè)分層的Bean,如果實(shí)現(xiàn)了這個(gè)接口,所有方法都會(huì)經(jīng)過父類的工廠。所以這個(gè)是個(gè)拓展的類,暫時(shí)先不看它。
接下來(lái)看看ListableBeanFactory注解說明
這個(gè)接口是要實(shí)現(xiàn)預(yù)先加載Bean的配置,生成好實(shí)例,直接管理Bean的實(shí)例,而不是來(lái)一個(gè)請(qǐng)求,生成一個(gè)。
好了,以上就是基本的概念和認(rèn)知,現(xiàn)在帶著這些概念,我們回頭看看ClassPathXmlApplicationContext的執(zhí)行流程,看看它到底怎么的生成管理Bean的。
初始化IOC容器從ClassPathXmlApplicationContext的構(gòu)造函數(shù)看,最核心的就是refresh()函數(shù),其他只是設(shè)一些值。
而這個(gè)refresh()是調(diào)用父類AbstractApplicationContext中的refresh()。
根據(jù)它的注解可知它是加載刷新了整個(gè)context,并且加載所有Bean定義和創(chuàng)建對(duì)應(yīng)的單例。
看下這個(gè)方法做了什么
里面有許多步驟,重點(diǎn)看下obtainFreshBeanFactory()(重新獲取一個(gè)BeanFactory)。
它里面有個(gè)核心的方法refreshBeanFactory()
如果已有BeanFactory,先刪除所有Bean,然后關(guān)閉BeanFactory。
然后創(chuàng)建一個(gè)新的ListableBeanFactory,上面說到這個(gè)工廠里會(huì)預(yù)先加載所有的Bean。
最后核心的就是loadBeanDefinitions(beanFactory),它是加載Bean的定義。實(shí)現(xiàn)交給了子類。
用的是XmlBeanDefinitionReader直接讀配置文件加載Bean Definition(Bean定義)到BeanFactory。它里面一步步把xml的配置文件拆解讀取,把一個(gè)個(gè)Bean Definition加載到BeanFactory里。
至此,已經(jīng)有用一個(gè)加載好Bean Definition的BeanFactory了。
其他方法也是圍繞BeanFactory后置處理和Context的配置準(zhǔn)備。內(nèi)容太多,想更深入了解的話建議順著以上思路,找到對(duì)應(yīng)代碼閱讀以下。
依賴注入回到啟動(dòng)類中,看看怎么從context中獲取bean的。
context.getBean(TestBeanService.class)
是根據(jù)類去拿bean的,當(dāng)然也可以根據(jù)id。
其對(duì)應(yīng)的源碼實(shí)現(xiàn),在DefaultListableBeanFactory中,上文有說到對(duì)應(yīng)的BeanFactory選型。
NamedBeanHolder是里面包含一個(gè)實(shí)例化的對(duì)象,和bean的名字。resolveNamedBean()是怎么拿出Bean的關(guān)鍵。
一步步Debug,可以看到,它是遍歷BeanFactory里面維護(hù)的beanDefinitionNames和manualSingletonNames成員變量,找出命中的beanName返回。
然后拿著這個(gè)beanName去找具體的bean實(shí)例。這里的代碼比較長(zhǎng),在AbstractBeanFactory里面的doGetBean()中實(shí)現(xiàn)。
大意是先嘗試去找手動(dòng)添加bean的單例工廠里找有沒有對(duì)應(yīng)的實(shí)例,沒有的話就往父類beanFactory里面找,最后沒有的話就生成一個(gè)。
spring中一個(gè)bean是如何加載和如何注入大致如此,更細(xì)節(jié)的內(nèi)容,可以自己debug看看源碼。
控制反轉(zhuǎn)的優(yōu)點(diǎn)最后來(lái)以我個(gè)人觀點(diǎn)談?wù)効刂品崔D(zhuǎn)的優(yōu)點(diǎn)吧。
舉個(gè)例子,我要裝修房子,需要門、浴具、廚具、油漆、玻璃等材料。
decorateHouse(Door,BathThing,CookThing,....)
但是我作為一個(gè)裝修工人,我需要去制造門、制造浴具,合成玻璃油漆嗎?
不需要,也不關(guān)心其建造的過程,對(duì)應(yīng)的會(huì)有人去做這些東西。
door = buildDoor(); glass = buildGlass();
所有材料放到建材商城里面,裝修工人需要什么材料就去建材商城里面取。
對(duì)應(yīng)Spring的IOC,門、玻璃等材料就是Bean,建材商城就是IOC容器,把材料放到建材商城就是Bean加載,去商城拿材料就是依賴注入的過程。
程序開發(fā)發(fā)展至今,一個(gè)簡(jiǎn)答的項(xiàng)目或許也要分幾個(gè)模板,幾個(gè)人去開發(fā)。劃分好職責(zé),設(shè)計(jì)好接口,面向接口編程。每個(gè)人只需要完成好自己那部分的工作,依賴調(diào)用就可以了。這樣做同時(shí)有助于降低項(xiàng)目的耦合度,讓項(xiàng)目有更好的延伸性。由此Spring的IOC就是基于以上的需求所誕生的。
總結(jié)回顧下全文的內(nèi)容
ApplicationContext是Spring項(xiàng)目的核心配置,項(xiàng)目運(yùn)行依賴于它,其中包含許多方面的內(nèi)容。
BeanFactory是Context包含的內(nèi)容之一,它負(fù)責(zé)管理Bean的加載,生成,注入等內(nèi)容。
Spring控制反轉(zhuǎn)為了降低項(xiàng)目耦合,提高延伸性。
本文講Spring IOC還比較淺顯,僅僅講了如何加載的重點(diǎn)和注入的重點(diǎn),關(guān)于生命周期,BeanFactory的處理由于篇幅問題并沒有細(xì)講。有興趣的讀者可以用Demo跑起來(lái),一步步Debug看看。因?yàn)镈emo基本是最小化的Spring IOC了,所以這個(gè)Debug不會(huì)太難,很容易就能看清楚整個(gè)流程做了什么。
Demo:https://github.com/Zack-Ku/sp...
如果覺得還不錯(cuò),請(qǐng)關(guān)注公眾號(hào):Zack說碼
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/77117.html
摘要:?jiǎn)栴}來(lái)了,我們到底還在用嗎答案是,不全用。后者是初始化的配置,主要是的配置。啟動(dòng)類測(cè)試啟動(dòng)項(xiàng)目后,在瀏覽器里面輸入。通過查詢已裝載的,并且支持該而獲取的。按照前面對(duì)的描述,對(duì)于而言,這個(gè)必定是。的核心在的方法中。 之前已經(jīng)分析過了Spring的IOC(《零基礎(chǔ)帶你看Spring源碼——IOC控制反轉(zhuǎn)》)與AOP(《從源碼入手,一文帶你讀懂Spring AOP面向切面編程》)的源碼,本次...
摘要:,,面向切面編程。,切點(diǎn),切面匹配連接點(diǎn)的點(diǎn),一般與切點(diǎn)表達(dá)式相關(guān),就是切面如何切點(diǎn)。例子中,注解就是切點(diǎn)表達(dá)式,匹配對(duì)應(yīng)的連接點(diǎn),通知,指在切面的某個(gè)特定的連接點(diǎn)上執(zhí)行的動(dòng)作。,織入,將作用在的過程。因?yàn)樵创a都是英文寫的。 之前《零基礎(chǔ)帶你看Spring源碼——IOC控制反轉(zhuǎn)》詳細(xì)講了Spring容器的初始化和加載的原理,后面《你真的完全了解Java動(dòng)態(tài)代理嗎?看這篇就夠了》介紹了下...
摘要:動(dòng)態(tài)地代理,可以猜測(cè)一下它的含義,在運(yùn)行時(shí)動(dòng)態(tài)地對(duì)某些東西代理,代理它做了其他事情。所以動(dòng)態(tài)代理的內(nèi)容重點(diǎn)就是這個(gè)。所以下一篇我們來(lái)細(xì)致了解下的到底是怎么使用動(dòng)態(tài)代理的。 之前講了《零基礎(chǔ)帶你看Spring源碼——IOC控制反轉(zhuǎn)》,本來(lái)打算下一篇講講Srping的AOP的,但是其中會(huì)涉及到Java的動(dòng)態(tài)代理,所以先單獨(dú)一篇來(lái)了解下Java的動(dòng)態(tài)代理到底是什么,Java是怎么實(shí)現(xiàn)它的。 ...
摘要:對(duì)象之間耦合度過高的系統(tǒng),必然會(huì)出現(xiàn)牽一發(fā)而動(dòng)全身的情形??刂票环崔D(zhuǎn)之后,獲得依賴對(duì)象的過程由自身管理變?yōu)榱擞扇萜髦鲃?dòng)注入。于是,他給控制反轉(zhuǎn)取了一個(gè)更合適的名字叫做依賴注入。 Spring還可以這么學(xué)--IoC(控制反轉(zhuǎn)) / DI(依賴注入)理解 聲明:文章的前三部分參考博文:https://www.cnblogs.com/Nouno...這篇文章首發(fā)是在我的個(gè)人微信訂閱號(hào)每天學(xué)編...
摘要:學(xué)習(xí)總結(jié)學(xué)習(xí)整理的一些筆記,很簡(jiǎn)單。大部分認(rèn)為和只是不同的叫法而已。依賴注入的兩種方式和注解使用注釋驅(qū)動(dòng)的功能源碼剖析 Spring IoC學(xué)習(xí)總結(jié) 學(xué)習(xí)spring Ioc整理的一些筆記,很簡(jiǎn)單。分享給大家。 IoC 基本概念 在這之前,我們先記住一句話。好萊塢原則:Dont call us, we will call you.其實(shí)這句話很恰當(dāng)?shù)匦稳萘朔崔D(zhuǎn)的意味;Ioc, Inve...
閱讀 2949·2021-11-24 09:39
閱讀 2857·2021-09-29 09:34
閱讀 3549·2021-09-24 10:23
閱讀 1731·2021-09-22 15:41
閱讀 1690·2019-08-30 15:55
閱讀 3506·2019-08-30 13:58
閱讀 2614·2019-08-30 13:11
閱讀 1661·2019-08-29 12:31