Spring中擴(kuò)展機(jī)制

1.背景

在看一些框架源碼的時(shí)候越走,可以看見(jiàn)他們很多都會(huì)和Spring去做結(jié)合。舉個(gè)例子dubbo的配置:

很多人其實(shí)配置了也就配置了会傲,沒(méi)有去過(guò)多的思考:為什么這么配置spring就能識(shí)別错英,dubbo就能啟動(dòng)秉宿?

如果你也需要做一個(gè)框架和Spring結(jié)合,或者你想知道Spring其他框架是如何和Spring做結(jié)合的,那么你應(yīng)該了解一下Spring的擴(kuò)展機(jī)制践叠。

2.如何擴(kuò)展

本篇文章想從Spring的兩個(gè)流程去介紹如何擴(kuò)展言缤,一個(gè)是容器初始化流程,一個(gè)是Bean的創(chuàng)建流程進(jìn)行將禁灼。

2.1 容器的初始化

要想使用Spring管挟,第一步肯定是需要先讓容器初始化。在AbstractApplicationContext中有一個(gè)refresh方法定義了容器如何進(jìn)行刷新:

在refresh中的具體流程如下圖:

其中比較常見(jiàn)的擴(kuò)展在加載BeanDefinition中和執(zhí)行BeanPostProcessor弄捕。下面講述一下如何進(jìn)行這兩個(gè)的擴(kuò)展僻孝。

2.1.1 加載BeanDefinition

在介紹加載BeanDefinition之前,先讓我們了解一下什么是BeanDefinition,顧名思義BeanDefinition描述Bean的信息的守谓,比如他的class信息穿铆,屬性信息,是否是單例斋荞,是否延遲加載等悴务。

如何加載呢?一般有兩種手段譬猫,一個(gè)是通過(guò)我們的xml讯檐,一個(gè)是通過(guò)一些擴(kuò)展手段。

xml加載如下:

我們?cè)趕pring的XML中配置這樣一個(gè)bean的定義染服,他會(huì)進(jìn)行解析然后轉(zhuǎn)換成我們的BeanDefinition别洪。

還有種方式是通過(guò)XML schema擴(kuò)展的方式,關(guān)于xsd的一些詳細(xì)介紹可以參考這篇文章:Spring中的XML schema擴(kuò)展機(jī)制柳刮。有些同學(xué)會(huì)問(wèn)不是還有個(gè)注解的方式嗎挖垛?我們?cè)趯W(xué)的時(shí)候一般書上都寫XML和注解兩種方式,注解其實(shí)也是使用了XML schema的擴(kuò)展機(jī)制秉颗,等會(huì)我會(huì)細(xì)講痢毒。

2.1.1.1 XML schema擴(kuò)展

什么是XML schema的擴(kuò)展呢?

Spring允許你自己定義XML的的結(jié)構(gòu)并且可以用自己的bean解析器進(jìn)行解析蚕甥。這里參考一下Spring中的XML schema擴(kuò)展機(jī)制進(jìn)行自定義擴(kuò)展的4個(gè)步驟:

編寫一個(gè) XML schema 文件描述的你節(jié)點(diǎn)元素哪替。

在resources/META-INF/目錄下定義demo.xsd文件。這里定義了一個(gè)demo的節(jié)點(diǎn)元素菇怀,其中定義了一個(gè)name字段凭舶。

編寫一個(gè) NamespaceHandler 的實(shí)現(xiàn)類

編寫一個(gè)或者多個(gè) BeanDefinitionParser 的實(shí)現(xiàn) (關(guān)鍵步驟).

注冊(cè)上述的 schema 和 handler。 在resources/META-INF/ 目錄下面創(chuàng)建spring.handler文件輸入:

http\://www.demo.com/schema/demo = xsd.DemoNameSpaceHandler

爱沟,這一步將我們之前的標(biāo)簽的url映射到我們NamespaceHandler帅霜。

再創(chuàng)建一個(gè)spring.schemas文件,輸入:

http\://www.demo.me/schema/demo/demo.xsd= META-INF/demo.xsd

這一步將xsd的url進(jìn)行了映射。

回到注解呼伸,大家配置注解的時(shí)候一般都是使用下圖進(jìn)行配置:

但是可以看見(jiàn)其依然是使用XML schema擴(kuò)展進(jìn)行處理身冀,在Spring中有個(gè)叫ContextNamespaceHandler,注冊(cè)很多解析器:

其中有一個(gè)解析器是compnent-scan,在他的parse方法中定義了如何進(jìn)行注解掃描,獲取注解:

利用這個(gè)擴(kuò)展機(jī)制的還有AOP,MVC,Spring-Cache以及我們的一些開源框架比如Dubbo等。

2.1.1.2 BeanFactoryPostProcessor擴(kuò)展

這個(gè)機(jī)制可以讓我們?cè)谡嬲膶?shí)例化Bean之前對(duì)BeanDefinition進(jìn)行修改搂根。

這里我舉例一個(gè)實(shí)戰(zhàn)的例子珍促,想必大家很多都配置過(guò)數(shù)據(jù)庫(kù)連接池吧,這里拿Druid來(lái)舉例:

然后我們創(chuàng)建一個(gè)druid.properties輸入:

url=jdbc:mysql://localhost:3306/test

username=root

password=123456

對(duì)于這種配置自己玩玩已經(jīng)滿足兄墅,但是在公司有個(gè)問(wèn)題踢星,密碼放在項(xiàng)目中明碼存儲(chǔ),這樣是不行的隙咸,別人只要獲得了你項(xiàng)目的查看權(quán)限那么密碼就會(huì)被泄漏沐悦,所以一般的公司會(huì)有一個(gè)統(tǒng)一的密碼存儲(chǔ)服務(wù),只有足夠的權(quán)限才能夠使用五督,那么我們可以把密碼放在統(tǒng)一存儲(chǔ)服務(wù)中藏否,通過(guò)對(duì)服務(wù)的調(diào)用才能進(jìn)行密碼的使用,那么我們?cè)趺窗褟倪h(yuǎn)程服務(wù)中獲取到的密碼注入到我們Bean中呢?那么就要使用我們的BeanFactoryPostpRrocessor充包,下面的代碼繼承PropertyPlaceholderConfigurer(BeanFactoryPostpRrocessor的實(shí)現(xiàn)類):

在XML中有:

通過(guò)這種方式我們可以有幾個(gè)好處:

設(shè)置統(tǒng)一配置中心副签,那么我們不需要修改我們項(xiàng)目中的文件,只需要在配置中心頁(yè)面中修改即可基矮。

設(shè)置統(tǒng)一密碼中心淆储,那么我們不需要暴露明文在項(xiàng)目中,密碼如何保護(hù)那么就直接丟給密碼中心即可家浇。

2.2 Bean的創(chuàng)建

一般我們?cè)贏PI中獲取一個(gè)Bean都會(huì)如下操作:

通過(guò)GetBean操作進(jìn)行獲取本砰,前面我們講到過(guò)如果是非延遲加載的單例Bean那么會(huì)在容器刷新的時(shí)候進(jìn)行加載,如果是延遲加載的Bean那么會(huì)在我們獲取Bean的時(shí)候根據(jù)BeanDefinition進(jìn)行加載钢悲。首先在AbstractBeanFactory有兩個(gè)方法一個(gè)是doCreate,一個(gè)是create用來(lái)描述如何創(chuàng)建一個(gè)Bean点额。這里說(shuō)一下單例Bean是如何創(chuàng)建的:

doCreateBean操作流程如下圖:

可以看見(jiàn)真正的創(chuàng)建bean的操作在CreateBean中,對(duì)于真正的創(chuàng)建Bean有如下流程:

莺琳。

2.2.1 Aware接口

Spring提供了很多Aware接口用于進(jìn)行擴(kuò)展还棱,通過(guò)Aware我們可以設(shè)置很多想設(shè)置的東西:

invokeAwareMethod提供了三種最基本的Aware,如果是ApplicationContext的話那么在ApplicationContextAwareProcessor又進(jìn)行了一輪Aware注入。

BeanNameAware:如果Spring檢測(cè)到當(dāng)前對(duì)象實(shí)現(xiàn)了該接口惭等,會(huì)將該對(duì)象實(shí)例的beanName設(shè)置到對(duì)錢對(duì)象實(shí)例中珍手。

BeanClassLoaderAware:會(huì)將加載當(dāng)前Bean的ClassLoader注入進(jìn)去。

BeanFactoryAware:將當(dāng)前BeanFactory容器注入進(jìn)去咕缎。

如果使用ApplicaitonContext類型的容器的話又會(huì)有下面幾種:

EnvironmentAware:將上下文中Enviroment注入進(jìn)去珠十,一般獲取配置屬性時(shí)可以使用。

EmbeddedValueResolverAware:將上下文中EmbeddedValueResolver注入進(jìn)去凭豪,一般用于參數(shù)解析。 ResourceLoaderAware:將上下文設(shè)置進(jìn)去晒杈。

ApplicationEventPublisherAware:在ApplicationContext中實(shí)現(xiàn)了ApplicationEventPublisher接口嫂伞,所以可以將自己注入進(jìn)去。

MessageSourceAware:將自身注入。

ApplicationContextAware:這個(gè)是我們見(jiàn)的比較多的帖努,會(huì)將自身容器注入進(jìn)去撰豺。

2.2.2 BeanPostProcessor

在前面我們說(shuō)過(guò)BeanFactoryPostProcessor,這兩個(gè)名字很像,BeanFactoryPostProcessor是用來(lái)對(duì)我們BeanFactory中的BeanDefinition進(jìn)行處理拼余,此時(shí)Bean還未生成污桦。而BeanPostProcessor用來(lái)對(duì)我們生成的Bean進(jìn)行處理。

在BeanPostProcessor分為兩個(gè)方法匙监,一個(gè)是用于初始化前置處理凡橱,一個(gè)是初始化用于后置處理。

有一種特殊的BeanPostProcessor,InstantiationAwareBeanPostProcessor亭姥,其會(huì)在我們實(shí)例化流程之前稼钩,如果實(shí)現(xiàn)了這個(gè)接口,那么就會(huì)使用其返回的對(duì)象實(shí)例达罗,不會(huì)進(jìn)入后續(xù)流程坝撑。

實(shí)戰(zhàn):BeanPostProcessor有什么用呢?

如果你有一個(gè)需求粮揉,打點(diǎn)項(xiàng)目中方法每個(gè)方法的運(yùn)行時(shí)常巡李,你很容易想到用AOP去做,如果不用AOP的話那么你可以使用BeanPostProcessor的后置處理方法,將對(duì)應(yīng)的每個(gè)Bean都進(jìn)行動(dòng)態(tài)代理扶认。

2.2.3 InitializingBean/init-method

Spring提供了我們對(duì)Bean進(jìn)行初始化邏輯的擴(kuò)展:

實(shí)現(xiàn)InitalizingBean接口:

在afterPropertiesSet()方法中我們可以寫入我們的初始化邏輯侨拦。

通過(guò)xml方式:

在init-method中定義了我們初始化方法。

2.2.4 DisposableBean/destory-method

俗話說(shuō)蝠引,生與死輪回不止阳谍。那么我們有了生的擴(kuò)展,自然Spring提供了死的擴(kuò)展螃概。我們也可以通過(guò)下面兩個(gè)擴(kuò)展來(lái)實(shí)現(xiàn)我們銷毀的邏輯:

DisposableBean: 實(shí)現(xiàn)DisposableBean接口

實(shí)現(xiàn)destroy方法即可矫夯。

實(shí)現(xiàn)XML:

在destroy-method中定義銷毀方法。

PS:在我們Spring容器中如果要在JVM關(guān)閉時(shí)自動(dòng)調(diào)用關(guān)閉的方法那么我們可以((ClassPathXmlApplicationContext) applicationContext).registerShutdownHook();注冊(cè)關(guān)閉鉤子吊洼,這樣在關(guān)閉JVM的時(shí)候我們的Bean也能安全銷毀训貌。

3.總結(jié)

本篇文章從Spring容器啟動(dòng)原理,以及Bean的初始化原理介紹冒窍,引出了多個(gè)基本的擴(kuò)展點(diǎn)递沪。當(dāng)然這部分?jǐn)U展點(diǎn)還僅僅是Spring中的一部分,感興趣的可以閱讀Spring的文檔综液,或者閱讀Spring源碼款慨。如果能掌握這些擴(kuò)展,以后自己造輪子的時(shí)候和Spring結(jié)合這些擴(kuò)展是不能少的

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末谬莹,一起剝皮案震驚了整個(gè)濱河市檩奠,隨后出現(xiàn)的幾起案子桩了,更是在濱河造成了極大的恐慌,老刑警劉巖埠戳,帶你破解...
    沈念sama閱讀 211,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件井誉,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡整胃,警方通過(guò)查閱死者的電腦和手機(jī)颗圣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)屁使,“玉大人在岂,你說(shuō)我怎么就攤上這事∥莨啵” “怎么了洁段?”我有些...
    開封第一講書人閱讀 157,435評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)共郭。 經(jīng)常有香客問(wèn)我祠丝,道長(zhǎng),這世上最難降的妖魔是什么除嘹? 我笑而不...
    開封第一講書人閱讀 56,509評(píng)論 1 284
  • 正文 為了忘掉前任写半,我火速辦了婚禮,結(jié)果婚禮上尉咕,老公的妹妹穿的比我還像新娘叠蝇。我一直安慰自己,他們只是感情好年缎,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,611評(píng)論 6 386
  • 文/花漫 我一把揭開白布悔捶。 她就那樣靜靜地躺著,像睡著了一般单芜。 火紅的嫁衣襯著肌膚如雪蜕该。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,837評(píng)論 1 290
  • 那天洲鸠,我揣著相機(jī)與錄音堂淡,去河邊找鬼。 笑死扒腕,一個(gè)胖子當(dāng)著我的面吹牛绢淀,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播瘾腰,決...
    沈念sama閱讀 38,987評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼皆的,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了蹋盆?” 一聲冷哼從身側(cè)響起祭务,我...
    開封第一講書人閱讀 37,730評(píng)論 0 267
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤内狗,失蹤者是張志新(化名)和其女友劉穎怪嫌,沒(méi)想到半個(gè)月后义锥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,194評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡岩灭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,525評(píng)論 2 327
  • 正文 我和宋清朗相戀三年拌倍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片噪径。...
    茶點(diǎn)故事閱讀 38,664評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡柱恤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出找爱,到底是詐尸還是另有隱情梗顺,我是刑警寧澤,帶...
    沈念sama閱讀 34,334評(píng)論 4 330
  • 正文 年R本政府宣布车摄,位于F島的核電站寺谤,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏吮播。R本人自食惡果不足惜变屁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,944評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望意狠。 院中可真熱鬧粟关,春花似錦、人聲如沸环戈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)院塞。三九已至遮晚,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間迫悠,已是汗流浹背鹏漆。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留创泄,地道東北人艺玲。 一個(gè)月前我還...
    沈念sama閱讀 46,389評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像鞠抑,于是被迫代替她去往敵國(guó)和親饭聚。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,554評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容