SpringBoot快速構(gòu)建微服務(wù)體系_第1頁
SpringBoot快速構(gòu)建微服務(wù)體系_第2頁
SpringBoot快速構(gòu)建微服務(wù)體系_第3頁
SpringBoot快速構(gòu)建微服務(wù)體系_第4頁
SpringBoot快速構(gòu)建微服務(wù)體系_第5頁
已閱讀5頁,還剩172頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

SpringBoot快速構(gòu)建微服務(wù)體系目錄\h第1章了解微服務(wù)\h1.1什么是微服務(wù)\h1.2微服務(wù)因何而生\h1.3微服務(wù)會帶來哪些好處\h1.3.1獨立,獨立,還是獨立\h1.3.2多語言生態(tài)\h1.4微服務(wù)會帶來哪些挑戰(zhàn)\h1.5本章小結(jié)\h第2章飲水思源:回顧與探索Spring框架的本質(zhì)\h2.1Spring框架的起源\h2.2SpringIoC其實很簡單\h2.3了解一點兒JavaConfig\h2.3.1那些高曝光率的Annotation\h2.4本章小結(jié)\h第3章SpringBoot的工作機制\h3.1SpringBoot初體驗\h3.2@SpringBootApplication背后的秘密\h3.2.1@Configuration創(chuàng)世紀(jì)\h3.2.2@EnableAutoConfiguration的功效\h3.2.3可有可無的@ComponentScan\h3.3SpringApplication:SpringBoot程序啟動的一站式解決方案\h3.3.1深入探索SpringApplication執(zhí)行流程\h3.3.2SpringApplicationRunListener\h3.3.3ApplicationListener\h3.3.4ApplicationContextInitializer\h3.3.5CommandLineRunner\h3.4再談自動配置\h3.4.1基于條件的自動配置\h3.4.2調(diào)整自動配置的順序\h3.5本章小結(jié)\h第4章了解紛雜的spring-boot-starter\h4.1應(yīng)用日志和spring-boot-starter-logging\h4.2快速Web應(yīng)用開發(fā)與spring-boot-starter-web\h4.2.1項目結(jié)構(gòu)層面的約定\h4.2.2SpringMVC框架層面的約定和定制\h4.2.3嵌入式Web容器層面的約定和定制\h4.3數(shù)據(jù)訪問與spring-boot-starter-jdbc\h4.3.1SpringBoot應(yīng)用的數(shù)據(jù)庫版本化管理\h4.4spring-boot-starter-aop及其使用場景說明\h4.4.1spring-boot-starter-aop在構(gòu)建spring-boot-starter-metrics自定義模塊中的應(yīng)用\h4.5應(yīng)用安全與spring-boot-starter-security\h4.5.1了解SpringSecurity基本設(shè)計\h4.5.2進(jìn)一步定制spring-boot-starter-security\h4.6應(yīng)用監(jiān)控與spring-boot-starter-actuator\h4.6.1自定義應(yīng)用的健康狀態(tài)檢查\h4.6.2開放的endpoints才真正“有用”\h4.6.3用還是不用,這是個問題\h4.7本章小結(jié)\h第5章SpringBoot微服務(wù)實踐探索\h5.1使用SpringBoot構(gòu)建微服務(wù)\h5.1.1創(chuàng)建基于Dubbo框架的SpringBoot微服務(wù)\h5.1.2使用SpringBoot快速構(gòu)建WebAPI\h5.1.3使用SpringBoot構(gòu)建其他形式的微服務(wù)\h5.2SpringBoot微服務(wù)的發(fā)布與部署\h5.2.1spring-boot-starter的發(fā)布與部署方式\h5.2.2基于RPM的發(fā)布與部署方式\h5.2.3基于Docker的發(fā)布與部署方式\h5.3SpringBoot微服務(wù)的注冊與發(fā)現(xiàn)\h5.4SpringBoot微服務(wù)的監(jiān)控與運維\h5.4.1推還是拉,這一直是個問題\h5.4.2從局部性觸發(fā)式報警到系統(tǒng)性智能化報警\h5.5SpringBoot微服務(wù)的安全與防護(hù)\h5.6SpringBoot微服務(wù)體系的脊梁:發(fā)布與部署平臺\h5.7本章小結(jié)\h第6章SpringBoot與Scala\h6.1使用Maven構(gòu)建和發(fā)布基于SpringBoot的Scala應(yīng)用\h6.1.1進(jìn)一步簡化基于Maven的Scala項目創(chuàng)建\h6.1.2進(jìn)一步簡化基于Scala的WebAPI開發(fā)\h6.2使用SBT構(gòu)建和發(fā)布基于SpringBoot的Scala應(yīng)用\h6.2.1探索基于SBT的SpringBoot應(yīng)用開發(fā)模式\h6.2.2探索基于SBT的SpringBoot應(yīng)用發(fā)布策略\h6.3本章小結(jié)\h第7章SpringBoot總結(jié)與展望第1章了解微服務(wù)SpringBoot是一個可使用Java構(gòu)建微服務(wù)的微框架,所以在了解SpringBoot之前,我們需要先了解什么是微服務(wù)。1.1什么是微服務(wù)微服務(wù)(Microservice)雖然是當(dāng)下剛興起的比較流行的新名詞,但本質(zhì)上來說,微服務(wù)并非什么新的概念。實際上,很多SOA實施成熟度比較好的企業(yè),已經(jīng)在使用和實施微服務(wù)了。只不過,它們只是在悶聲發(fā)大財,并不介意是否有一個比較時髦的名詞來明確表述SOA的這個發(fā)展演化趨勢罷了。微服務(wù)其實就是服務(wù)化思路的一種最佳實踐方向,遵循SOA的思路,各個企業(yè)在服務(wù)化治理的道路上走的時間長了,踩的坑多了,整個軟件交付鏈路上各個環(huán)節(jié)的基礎(chǔ)設(shè)施逐漸成熟了,微服務(wù)自然而然就誕生了。當(dāng)然,之所以叫微服務(wù),是與之前的服務(wù)化思路和實踐相比較而來的。早些年的服務(wù)實現(xiàn)和實施思路是將很多功能從開發(fā)到交付都打包成一個很大的服務(wù)單元(一般稱為Monolith),而微服務(wù)實現(xiàn)和實施思路則更強調(diào)功能趨向單一,服務(wù)單元小型化和微型化。如果用“茶壺煮餃子”來打比方的話,原來我們是在一個茶壺里煮很多個餃子,現(xiàn)在(微服務(wù)化之后)則基本上是在一個茶壺煮一個餃子,而這些餃子就是服務(wù)的功能,茶壺則是將這些服務(wù)功能打包交付的服務(wù)單元,如圖1-1所示。圖1-1論茶壺里煮“餃子”的不同形式所以,從思路和理念上來講,微服務(wù)就是要倡導(dǎo)大家盡量將功能進(jìn)行拆分,將服務(wù)粒度做小,使之可以獨立承擔(dān)對外服務(wù)的職責(zé),沿著這個思路開發(fā)和交付的軟件服務(wù)實體就叫作“微服務(wù)”,而圍繞著這個思路和理念構(gòu)建的一系列基礎(chǔ)設(shè)施和指導(dǎo)思想,筆者將它稱為“微服務(wù)體系”。1.2微服務(wù)因何而生微服務(wù)的概念我們應(yīng)該大體了解了,那么微服務(wù)又是怎么來的?原來將很多功能打包為一個很大的服務(wù)單元進(jìn)行交付的做法不能滿足需求嗎?實際上,并非原來“大一統(tǒng)”(Monolith)的服務(wù)化實踐不能滿足要求,也不是不好,只是,它有自己存在的合理場景。對于Monolith服務(wù)來說,如果團(tuán)隊不大,軟件復(fù)雜度不高,那么,使用Monolith的形式進(jìn)行服務(wù)化治理是比較合適的,而且,這種方式對運維和各種基礎(chǔ)設(shè)施的要求也不高。但是,隨著軟件系統(tǒng)的復(fù)雜度持續(xù)飆升,軟件交付的效率要求更高,投入的人力以及各項資源越來越多,基于Monolith的服務(wù)化思路就開始“捉襟見肘”。在開發(fā)階段,如果我們遵循Monolith的服務(wù)化理念,通常會將所有功能的實現(xiàn)都統(tǒng)一歸到一個開發(fā)項目下,但隨著功能的膨脹,這些功能一定會分發(fā)給不同的研發(fā)人員進(jìn)行開發(fā),造成的后果就是,大家在提交代碼的時候頻繁沖突并需要解決這些沖突,單一的開發(fā)項目成為了開發(fā)期間所有人的工作瓶頸。為了減輕這種苦惱,我們自然會將項目按照要開發(fā)的功能拆分為不同的項目,從而負(fù)責(zé)不同功能的研發(fā)人員就可以在自己的代碼項目上進(jìn)行開發(fā),從而解決了大家無法在開發(fā)階段并行開發(fā)的苦惱。到了軟件交付階段,如果我們遵循Monolith的服務(wù)化理念,那么,我們一定是將所有這些開發(fā)階段并行開發(fā)的項目集合到一起進(jìn)行交付,這就涉及服務(wù)化早期實踐中比較有名的“火車模型”,即交付的服務(wù)就像一輛火車,而這個服務(wù)相關(guān)的所有功能對應(yīng)的項目成果,就是要裝上火車車廂的一件件貨物,交付的列車只有等到所有項目都開發(fā)測試完成后才可以裝車出發(fā),完成整個服務(wù)的交付。很顯然,只要有一個車廂沒有準(zhǔn)備好貨物(即功能項目未開發(fā)測試完成),火車就不能發(fā)車,服務(wù)就不能交付,這大大降低了服務(wù)的交付效率。如果每個功能項目可以各自獨立交付,那么就不需要都等同一輛火車,各自出發(fā)就可以了。順著這個思路,自然而然地,大家逐漸各自獨立,每一個功能或者少數(shù)相近的功能作為單一項目開發(fā)完成后將作為一個獨立的服務(wù)單元進(jìn)行交付,從而在服務(wù)交付階段,大家也能夠并行不悖,各自演化而不受影響。所以,隨著服務(wù)和系統(tǒng)的復(fù)雜度逐漸飆升,為了能夠在整個軟件的交付鏈路上高效擴(kuò)展,將獨立的功能和服務(wù)單元進(jìn)行拆分,從而形成一個一個的微服務(wù)是自然而然發(fā)生的事情。這就像打不同的戰(zhàn)役一樣,在雙方兵力不多、戰(zhàn)場復(fù)雜度不高的情況下,Monolith的統(tǒng)一指揮調(diào)度方式是合適的;而一旦要打大的戰(zhàn)役(類似于系統(tǒng)復(fù)雜度提升),雙方一定會投入大量的兵力(軟件研發(fā)團(tuán)隊的規(guī)模增長),如果還是在狹小甚至固定的戰(zhàn)場上進(jìn)行廝殺,顯然施展不開!所以,小戰(zhàn)役有小戰(zhàn)役的打法,大戰(zhàn)役有大戰(zhàn)役的戰(zhàn)法,而微服務(wù)實際上就是一種幫助擴(kuò)展組織能力、提升團(tuán)隊效率的應(yīng)對“大戰(zhàn)役”的方法,它幫助我們從軟件開發(fā)到交付,進(jìn)而到團(tuán)隊和組織層面多方位進(jìn)行擴(kuò)展??偟膩碚f,一方面微服務(wù)可以幫助我們應(yīng)對飆升的系統(tǒng)復(fù)雜度;另一個方面,微服務(wù)可以幫助我們進(jìn)行更大范圍的擴(kuò)展,從開發(fā)階段項目并行開發(fā)的擴(kuò)展,到交付階段并行交付的擴(kuò)展,再到相應(yīng)的組織結(jié)構(gòu)和組織能力的擴(kuò)展,皆因微服務(wù)而受惠。1.3微服務(wù)會帶來哪些好處顯然,隨著系統(tǒng)復(fù)雜度的提升,以及對系統(tǒng)擴(kuò)展性的要求越來越高,微服務(wù)化是一個很好的方向,但除此之外,微服務(wù)還會給我們帶來哪些好處?1.3.1獨立,獨立,還是獨立我們說微服務(wù)打響的是各自的獨立戰(zhàn)爭,所以,每一個微服務(wù)都是一個小王國,這些微服務(wù)跳出了“大一統(tǒng)”(Monolith)王國的統(tǒng)治,開始從各個層面打造自己的獨立能力,從而保障自己的小王國可以持續(xù)穩(wěn)固的運轉(zhuǎn)。首先,在開發(fā)層面,每個微服務(wù)基本上都是各自獨立的項目(project),而對應(yīng)各自獨立項目的研發(fā)團(tuán)隊基本上也是獨立對應(yīng),這樣的結(jié)構(gòu)保證了微服務(wù)的并行研發(fā),并且各自快速迭代,不會因為所有研發(fā)都投入一個近乎單點的項目,從而造成開發(fā)階段的瓶頸。開發(fā)階段的獨立,保證了微服務(wù)的研發(fā)可以高效進(jìn)行。服務(wù)開發(fā)期間的形態(tài),跟服務(wù)交付期間的形態(tài)原則上是不需要完全高度統(tǒng)一的,即使我們在開發(fā)的時候都是各自進(jìn)行,但交付的時候還是可以一起交付,不過這不是微服務(wù)的做法。在微服務(wù)治理體系下,各個微服務(wù)交付期間也是各自獨立交付的,從而使得每個微服務(wù)從開發(fā)到交付整條鏈路上都是獨立進(jìn)行,這大大加快了微服務(wù)的迭代和交付效率。服務(wù)交付之后需要部署運行,對微服務(wù)來說,它們運行期間也是各自獨立的。微服務(wù)獨立運行可以帶來兩個比較明顯的好處,第一個就是可擴(kuò)展性。我們可以快速地添加服務(wù)集群的實例,提升整個微服務(wù)集群的服務(wù)能力,而在傳統(tǒng)Monolith模式下,為了能夠提升服務(wù)能力,很多時候必須強化和擴(kuò)展單一結(jié)點的服務(wù)能力來達(dá)成。如果單結(jié)點服務(wù)能力已經(jīng)擴(kuò)展到了極限,再尋求擴(kuò)展的話,就得從軟件到硬件整體進(jìn)行重構(gòu)。軟件行業(yè)有句話:“Threadsdon'tscale,Processesdo!”,很明確地道出了原來Monolith服務(wù)與微服務(wù)在擴(kuò)展(Scale)層面的差異。對于Java開發(fā)者來說,早些年(當(dāng)然現(xiàn)在也依然存在),我們遵循JavaEE規(guī)范開發(fā)的Web應(yīng)用,都需要以WAR包的形式部署到TOMCAT、Jetty、RESIN等Web容器中運行,即使每個WAR包提供的都是獨立的微服務(wù),但因為它們都是統(tǒng)一部署運行在一個Web容器中,所以擴(kuò)展能力受限于Web容器作為一個進(jìn)程(process)的現(xiàn)狀。無論如何調(diào)整Web容器內(nèi)部實現(xiàn)的線程(thread)設(shè)置,還是會受限于Web容器整體的擴(kuò)展能力。所以,現(xiàn)在很多情況下,大家都是一個TOMCAT只部署一個WAR,然后通過復(fù)制和擴(kuò)展多個TOMCAT實例來擴(kuò)展整個應(yīng)用服務(wù)集群。當(dāng)然,說到在TOMCAT實例中只部署一個WAR包這樣的做法,實際上不單單只是因為擴(kuò)展的因素,還涉及微服務(wù)運行期間給我們帶來的第二個好處,即隔離性。隔離性實際上是可擴(kuò)展性的基礎(chǔ),當(dāng)我們將每個微服務(wù)都隔離為獨立的運行單元之后,任何一個或者多個微服務(wù)的失敗都將只影響自己或者少量其他微服務(wù),而不會大面積地波及整個服務(wù)運行體系。在架構(gòu)設(shè)計上有一種實踐模式,即隔板模式(BulkheadPattern),這種架構(gòu)設(shè)計模式的首要目的就是為了隔離系統(tǒng)中的各個功能單元和實體,使得系統(tǒng)不會因為一個單元或者服務(wù)的失敗而導(dǎo)致整體失敗。這種思路在造船行業(yè)、兵工行業(yè)都有類似的應(yīng)用場景?,F(xiàn)在任何大型船舶在設(shè)計上都會有隔艙,目的就是即使有少量進(jìn)水,也可以只將進(jìn)水部位隔離在小范圍,不會擴(kuò)散而導(dǎo)致船舶大面積進(jìn)水,從而沉沒。當(dāng)年泰坦尼克號雖然沉了,但不意味著他們沒有做隔艙設(shè)計,只能說,傷害度已經(jīng)遠(yuǎn)遠(yuǎn)超出隔艙可以提供的基礎(chǔ)保障范圍。在坦克的設(shè)計上,現(xiàn)在一般也會將彈藥艙和乘員艙隔離,從而可以保障當(dāng)坦克受創(chuàng)之后,將傷害盡量限定在指定區(qū)域,盡量減少對車乘成員的傷害。前面我們提到,現(xiàn)在大家基本上弱化了JavaEE的Web容器早期采用的“一個Web容器部署多個WAR包”的做法,轉(zhuǎn)而使用“一個Web容器只部署一個WAR包”的做法,這實際上正是綜合考慮了Web容器的設(shè)計和實現(xiàn)現(xiàn)狀與真實需求之后做出的合理實踐選擇。這些Web容器內(nèi)部大多通過類加載器(Classloader)以及線程來實現(xiàn)一定程度上的依賴和功能隔離,但這些機制從基因上決定了這些做法不是最好的隔離手段。而進(jìn)程(Process)擁有天然的隔離特性,所以,一個WAR包只部署運行在一個Web容器進(jìn)程中才是最好的隔離方式?,F(xiàn)在回想一下,好像自從各個微服務(wù)打響?yīng)毩?zhàn)爭并且獨立之后,無論從哪個層面來看,各自“活”得都挺好。1.3.2多語言生態(tài)微服務(wù)獨立之后,給了對應(yīng)的團(tuán)隊和組織快速迭代和交付的能力,同時,也給團(tuán)隊和組織帶來了更多的靈活性,實際上,對應(yīng)交付不同微服務(wù)的團(tuán)隊或者組織來說,現(xiàn)在可以基于不同的計算機語言生態(tài)構(gòu)建這些微服務(wù),如圖1-2所示。微服務(wù)的提供者既可以使用Java或者Go等靜態(tài)語言完成微服務(wù)的開發(fā)和交付,也可以使用Python或者Ruby等動態(tài)語言完成微服務(wù)的開發(fā)和交付,對于團(tuán)隊內(nèi)部擁有繁榮且有差異的語言文化來說,多語言生態(tài)下的微服務(wù)開發(fā)和交付將可以最大化的發(fā)揮團(tuán)隊和組織內(nèi)部各成員的優(yōu)勢。當(dāng)然,對于多語言生態(tài)下的微服務(wù)研發(fā)來說,有一點需要注意:為了讓服務(wù)的訪問者可以用統(tǒng)一的接口訪問所有這些用不同語言開發(fā)和交互的微服務(wù),應(yīng)該盡量統(tǒng)一微服務(wù)的服務(wù)接口和協(xié)議。在微服務(wù)的生態(tài)下,互通性應(yīng)該是需要重點關(guān)注的因素,沒有互通,不但服務(wù)的訪問者和用戶無法很好地使用這些微服務(wù),微服務(wù)和微服務(wù)之間也無法相互信賴和互助,這將大大損耗微服務(wù)研發(fā)體系帶來的諸多好處,而多語言生態(tài)也會變成一種障礙和負(fù)累,而不是益處。記得時任黑貓宅急便社長的小倉昌男在其所著的《黑貓宅急便的經(jīng)營學(xué)》中提到一個故事,日本國鐵曾經(jīng)采用不同于國際標(biāo)準(zhǔn)的集裝箱和鐵路規(guī)格,然后發(fā)現(xiàn)貨物的運輸效率很低,經(jīng)過考察發(fā)現(xiàn),原來是貨物從國際標(biāo)準(zhǔn)集裝箱卸載之后,在通過日本國鐵運輸之前,需要先拆箱,重新裝入日本國鐵規(guī)格的集裝箱,然后裝載到日本國鐵上進(jìn)行運輸。但是,如果日本國鐵采用國際標(biāo)準(zhǔn)的集裝箱規(guī)格,那么貨物集裝箱從遠(yuǎn)洋輪船上卸載之后就可以直接裝上國鐵,這將大大加快運輸效率(日本,國鐵改革后也證明確實如此)。日本國鐵在前期采用私有方案時,只關(guān)注了自己的利益和效率,舍棄了互通,也帶來了效率的低下。所以,在開發(fā)和交付微服務(wù)的時候,尤其是在多語言生態(tài)下開發(fā)和交付微服務(wù),我們從一開始就要將互通性作為首要考慮因素,從而不會因為執(zhí)迷于某些服務(wù)或者系統(tǒng)的單點效率而失去了整個微服務(wù)體系的整體效率。圖1-2多語言的微服務(wù)生態(tài)1.4微服務(wù)會帶來哪些挑戰(zhàn)微服務(wù)給我們帶來的并非只有好處,還有相應(yīng)的一些挑戰(zhàn)。服務(wù)“微”化之后,一個顯著的特點就是服務(wù)的數(shù)量增多了。如果將軟件開發(fā)和交付也作為一種生產(chǎn)模式看待,那么數(shù)量眾多的微服務(wù)實際上就類似于傳統(tǒng)生產(chǎn)線上的產(chǎn)品,而在傳統(tǒng)生產(chǎn)模型下,為了能夠高效地生產(chǎn)大量產(chǎn)品,通常采用的就是標(biāo)準(zhǔn)化生產(chǎn)。比如在汽車產(chǎn)業(yè),在福特T型車沒有出來之前,大多汽車企業(yè)的生產(chǎn)效率都不高,而福特在引入標(biāo)準(zhǔn)化生產(chǎn)線之后,福特T型車得以大量生產(chǎn)并以低成本優(yōu)勢快速普及。在其他行業(yè)也是同樣的道理,個性化生產(chǎn)雖然會深得個別用戶的喜歡,但生產(chǎn)成本通常也會很高,生產(chǎn)效率因為受限于個性化需求,也無法從“熟能生巧”中獲益,所以,最終用戶需要為生產(chǎn)成本和效率付出更多的溢價才能獲得最終產(chǎn)品。而相對于個性化生產(chǎn)來說,標(biāo)準(zhǔn)化生產(chǎn)走的是另一條路,通過生產(chǎn)標(biāo)準(zhǔn)產(chǎn)品,使得整條生產(chǎn)鏈路可重復(fù),從而提升了生產(chǎn)效率,可以為更廣層面的用戶提供大量“物美價廉”的標(biāo)準(zhǔn)產(chǎn)品。微服務(wù)的研發(fā)和交付其實就類似于產(chǎn)品的生產(chǎn)鏈路,而數(shù)量大這一特點則決定了,我們無法通過個性化的生產(chǎn)模式來支撐整個微服務(wù)的交付鏈路和研發(fā)體系,雖然微服務(wù)化之后,我們可以投入相應(yīng)的人力和團(tuán)隊對應(yīng)各個微服務(wù)的開發(fā)和交付,可擴(kuò)展性上絕對沒有問題,但這不意味著現(xiàn)實情況下我們就能這樣做,因為這些都涉及人力和資源成本,而這往往是受限的。所以,使用標(biāo)準(zhǔn)化的思路來開發(fā)和交付微服務(wù)就變成了自然而然的選擇:·通過標(biāo)準(zhǔn)化,我們可以重復(fù)使用開發(fā)階段打造的一系列環(huán)境和工具支持?!ねㄟ^標(biāo)準(zhǔn)化,我們可以復(fù)用支持整個微服務(wù)交付鏈路的各項基礎(chǔ)設(shè)施?!ねㄟ^標(biāo)準(zhǔn)化,我們可以減少采購差異導(dǎo)致的成本上升,同時更加高效地利用硬件資源?!ねㄟ^標(biāo)準(zhǔn)化,我們可以用標(biāo)準(zhǔn)的協(xié)議和格式來治理和維護(hù)數(shù)量龐大的微服務(wù)。如果你還對使用標(biāo)準(zhǔn)化的思路來構(gòu)建微服務(wù)體系存有疑惑,那么,不妨再結(jié)合微服務(wù)的多語言生態(tài)特性思考一番:·增加一種語言生態(tài)用于微服務(wù)的開發(fā)和交付,我們是否要圍繞著這種語言生態(tài)和微服務(wù)的需求重新搭建一套研發(fā)/測試環(huán)境?·我們是否還要圍繞著這種語言生態(tài)打造一系列的工具來提升日常開發(fā)的效率?·增加一種語言生態(tài),我們是不是還要圍繞這種語言生態(tài)搭建一套針對微服務(wù)的交付鏈路基礎(chǔ)設(shè)施?·增加一種語言生態(tài),我們是否還要圍繞它提供特定的硬件環(huán)境以及運維支撐工具和平臺?多語言生態(tài)雖然靈活度高了,不同語種和思路的團(tuán)隊成員也能夠百花齊放了,但是不是也同樣帶來了以上一系列的成本?所以,很多事情你能做,并不意味著你一定要做。適度的收縮語言生態(tài)的選擇范圍,并圍繞主要的語言生態(tài)構(gòu)建一套標(biāo)準(zhǔn)化的微服務(wù)交付體系,或許是更為合理的做法。要實施高效可重復(fù)的標(biāo)準(zhǔn)化微服務(wù)生產(chǎn),我們需要有類似傳統(tǒng)行業(yè)生產(chǎn)線的基礎(chǔ)設(shè)施。否則,高效可重復(fù)的開發(fā)和交付大量的微服務(wù)就無從談起,所以,完備的微服務(wù)研發(fā)和交付體系基礎(chǔ)設(shè)施建設(shè)就成為了實施微服務(wù)的終極挑戰(zhàn)。一個公司或者組織要很好地或者說成熟地實施微服務(wù)化戰(zhàn)略,為交付鏈路提供完備支撐的基礎(chǔ)設(shè)施建設(shè)必不可少!1.5本章小結(jié)在帶領(lǐng)大家探索本書的主角SpringBoot微框架之前,本章首先為大家介紹了SpringBoot微框架服務(wù)的核心場景,即微服務(wù)。然后一起探索了微服務(wù)的概念以及由來,并探討了微服務(wù)可以為我們帶來哪些好處,以及同時又為我們帶來哪些挑戰(zhàn)??偟膩碚f,微服務(wù)化雖然是當(dāng)下流行的趨勢,但并非任何場景都合適,我們還是要審慎地在“大一統(tǒng)”(Monolith)服務(wù)架構(gòu)和微服務(wù)架構(gòu)之間做出選擇,而一旦確定選擇了微服務(wù)化之路,那么,就應(yīng)該圍繞團(tuán)隊和組織的主要語言生態(tài)以及微服務(wù)方向積極探索高效的微服務(wù)開發(fā)和交付模式。SpringBoot微框架實際上就是為Java語言生態(tài)而生的一種微服務(wù)最佳實踐,在第2章中我們將從回顧SpringBoot的起源開始,逐步揭開SpringBoot微框架的神秘面紗。第2章飲水思源:回顧與探索Spring框架的本質(zhì)SpringBoot框架的命名關(guān)鍵在“Boot”上,或許BootSpring更能說明這個微框架設(shè)計的初衷,也就是快速啟動一個Spring應(yīng)用!所以,自始至終,SpringBoot框架都是為了能夠幫助使用Spring框架的開發(fā)者快速高效地構(gòu)建一個個基于Spring框架以及Spring生態(tài)體系的應(yīng)用解決方案。要深刻理解SpringBoot框架,首先我們需要深刻理解Spring框架,所以讓我們先來讀讀歷史吧!2.1Spring框架的起源雖然筆者在自己的上一本著作《Spring揭秘》中對Spring框架進(jìn)行了十分詳盡的介紹和剖析,但這里還是要再啰嗦幾句。Spring框架誕生于“黑暗”的EJB1的時代(如果你沒有聽說過,恭喜你,說明你還年輕),那是一個J2EE規(guī)范統(tǒng)治的時代,基于各種容器和J2EE規(guī)范的軟件解決方案是唯一的“正道”,沉重的研發(fā)模式和生態(tài)讓那個時代的開發(fā)者痛苦不堪。隨著經(jīng)典巨著《ExpertOne-on-OneJ2EEDesignandDevelopment》的誕生,重規(guī)范時代終于迎來了一線曙光,該書的作者RodJohnson在書中闡述了輕量級框架的研發(fā)理念,對原有笨重的規(guī)范進(jìn)行了抨擊,并基于書中的理念推出了最初版的Spring框架,并延續(xù)至今已達(dá)10多年之久。Spring框架是構(gòu)建高效Java研發(fā)體系的一種最佳實踐,它通過一系列統(tǒng)一而簡潔的設(shè)計,為廣大Java開發(fā)者開拓了一條光明的Java應(yīng)用最佳實踐之路。大家熟知的SpringIoC與AOP自不必說,Spring更是對Java應(yīng)用開發(fā)中常用的技術(shù)進(jìn)行了合理的設(shè)計和封裝,使得Java應(yīng)用開發(fā)者可以避免昔日因API和系統(tǒng)設(shè)計不當(dāng)而易犯的錯誤,又能夠高效地完成相應(yīng)問題領(lǐng)域的研發(fā)工作,真可說是Java開發(fā)必備良器!當(dāng)然,因為這不是一本專門介紹Spring框架的書,所以,這里不會詳細(xì)展開對Spring框架的細(xì)節(jié)回顧。不過,一些核心的實踐以及與SpringBoot相關(guān)的概念,還是有必要說在前的,比如SpringIoC!2.2SpringIoC其實很簡單有部分Java開發(fā)者對IoC(InversionOfControl)和DI(DependencyInjection)的概念有些混淆,認(rèn)為二者是對等的,實際上我在之前的著作中已經(jīng)說過了,IoC其實有兩種方式,一種就是DI,而另一種是DL,即DependencyLookup(依賴查找),前者是當(dāng)前軟件實體被動接受其依賴的其他組件被IoC容器注入,而后者則是當(dāng)前軟件實體主動去某個服務(wù)注冊地查找其依賴的那些服務(wù),概念之間的關(guān)系如圖2-1所示可能更貼切些。圖2-1IoC相關(guān)概念示意圖我們通常提到的SpringIoC,實際上是指Spring框架提供的IoC容器實現(xiàn)(IoCContainer),而使用SpringIoC容器的一個典型代碼片段就是:publicclassApp{

publicstaticvoidmain(String[]args){

ApplicationContextcontext=newFileSystemXmlApplication-Context("...");

//...

MockServiceservice=context.getBean(MockService.class);

service.doSomething();

}

}

任何一個使用Spring框架構(gòu)建的獨立的Java應(yīng)用(StandaloneJavaApplication),通常都會存在一行類似于“context.getBean(..);”的代碼,實際上,這行代碼做的就是DL的工作,而構(gòu)建的任何一種IoC容器背后(比如BeanFactory或者ApplicationContext)發(fā)生的事情,則更多是DI的過程(也可能有部分DL的邏輯用于對接遺留系統(tǒng))。Spring的IoC容器中發(fā)生的事情其實也很簡單,總結(jié)下來即兩個階段:(1)采摘和收集“咖啡豆”(bean)(2)研磨和烹飪咖啡哦,不對,這是一本技術(shù)書,差點兒寫成咖啡文化雜志。那我們還是回過頭來繼續(xù)說SpringIoC容器的依賴注入流程吧!SpringIoC容器的依賴注入工作可以分為兩個階段:階段一:收集和注冊第一個階段可以認(rèn)為是構(gòu)建和收集bean定義的階段,在這個階段中,我們可以通過XML或者Java代碼的方式定義一些bean,然后通過手動組裝或者讓容器基于某些機制自動掃描的形式,將這些bean定義收集到IoC容器中。假設(shè)我們以XML配置的形式來收集并注冊單一bean,一般形式如下:<beanid="mockService"class="..MockServiceImpl">

...

</bean>

如果嫌逐個收集bean定義麻煩,想批量地收集并注冊到IoC容器中,我們也可以通過XMLSchema形式的配置進(jìn)行批量掃描并采集和注冊:<context:component-scanbase-package="com.keevol">

注意基于JavaConfig形式的收集和注冊,不管是單一還是批量,后面我們都會單獨提及。階段二:分析和組裝當(dāng)?shù)谝浑A段工作完成后,我們可以先暫且認(rèn)為IoC容器中充斥著一個個獨立的bean,它們之間沒有任何關(guān)系。但實際上,它們之間是有依賴關(guān)系的,所以,IoC容器在第二階段要干的事情就是分析這些已經(jīng)在IoC容器之中的bean,然后根據(jù)它們之間的依賴關(guān)系先后組裝它們。如果IoC容器發(fā)現(xiàn)某個bean依賴另一個bean,它就會將這另一個bean注入給依賴它的那個bean,直到所有bean的依賴都注入完成,所有bean都“整裝待發(fā)”,整個IoC容器的工作即算完成。至于分析和組裝的依據(jù),Spring框架最早是通過XML配置文件的形式來描述bean與bean之間的關(guān)系的,隨著Java業(yè)界研發(fā)技術(shù)和理念的轉(zhuǎn)變,基于Java代碼和Annotation元信息的描述方式也日漸興盛(比如@Autowired和@Inject),但不管使用哪種方式,都只是為了簡化綁定邏輯描述的各種“表象”,最終都是為本階段的最終目的服務(wù)。提示很多Java開發(fā)者一定認(rèn)為spring的XML配置文件是一種配置(Configuration),但本質(zhì)上,這些配置文件更應(yīng)該是一種代碼形式,XML在這里其實可以看作一種DSL,它用來表述的是bean與bean之間的依賴綁定關(guān)系,諸君還記得沒有IoC容器的年代要自己寫代碼新建(new)對象并配置(set)依賴的吧?2.3了解一點兒JavaConfigJava5的推出,加上當(dāng)年基于純JavaAnnotation的依賴注入框架Guice的出現(xiàn),使得Spring框架及其社區(qū)也“順應(yīng)民意”,推出并持續(xù)完善了基于Java代碼和Annotation元信息的依賴關(guān)系綁定描述方式,即JavaConfig項目?;贘avaConfig方式的依賴關(guān)系綁定描述基本上映射了最早的基于XML的配置方式,比如:(1)表達(dá)形式層面基于XML的配置方式是這樣的:<?xmlversion="1.0"encoding="UTF-8"?>

<beansxmlns="/schema/beans"

xmlns:xsi="/2001/XMLSchema-instance"

xmlns:context="/schema/context"

xsi:schemaLocation="/schema/beans

/schema/beans/spring-beans.xsd/schema/context/schema/context/spring-context.xsd">

<!--bean

定義

-->

</beans>

而基于JavaConfig的配置方式是這樣的:@Configuration

publicclassMockConfiguration{

//bean

定義

}

任何一個標(biāo)注了@Configuration的Java類定義都是一個JavaConfig配置類。(2)注冊bean定義層面基于XML的配置形式是這樣的:<beanid="mockService"class="..MockServiceImpl">

...

</bean>

而基于JavaConfig的配置形式是這樣的:@Configuration

publicclassMockConfiguration{

@Bean

publicMockServicemockService(){

returnnewMockServiceImpl();

}

}

任何一個標(biāo)注了@Bean的方法,其返回值將作為一個bean定義注冊到Spring的IoC容器,方法名將默認(rèn)成為該bean定義的id。(3)表達(dá)依賴注入關(guān)系層面為了表達(dá)bean與bean之間的依賴關(guān)系,在XML形式中一般是這樣的:<beanid="mockService"class="..MockServiceImpl">

<propertyname="dependencyService"ref="dependencyService"/>

</bean>

<beanid="dependencyService"class="DependencyServiceImpl"/>

而在JavaConfig中則是這樣的:@Configuration

publicclassMockConfiguration{

@Bean

publicMockServicemockService(){

returnnewMockServiceImpl(dependencyService());

}

@Bean

publicDependencyServicedependencyService(){

returnnewDependencyServiceImpl();

}

}

如果一個bean的定義依賴其他bean,則直接調(diào)用對應(yīng)JavaConfig類中依賴bean的創(chuàng)建方法就可以了。注意在JavaConfig形式的依賴注入過程中,我們使用方法調(diào)用的形式注入依賴,如果這個方法返回的對象實例只被一個bean依賴注入,那也還好,如果多于一個bean需要依賴這個方法調(diào)用返回的對象實例,那是不是意味著我們就會創(chuàng)建多個同一類型的對象實例?從代碼表述的邏輯來看,直覺上應(yīng)該是會創(chuàng)建多個同一類型的對象實例,但實際上最終結(jié)果卻不是這樣,依賴注入的都是同一個Singleton的對象實例,那這是如何做到的?筆者一開始以為Spring框架會通過解析JavaConfig的代碼結(jié)構(gòu),然后通過解析器轉(zhuǎn)換加上反射等方式完成這一目的,但實際上Spring框架的設(shè)計和實現(xiàn)者采用了另一種更通用的方式,這在Spring的參考文檔中有說明,即通過攔截配置類的方法調(diào)用來避免多次初始化同一類型對象的問題,一旦擁有攔截邏輯的子類發(fā)現(xiàn)當(dāng)前方法沒有對應(yīng)的類型實例時才會去請求父類的同一方法來初始化對象實例,否則直接返回之前的對象實例。所以,原來SpringIoC容器中有的特性(features)在JavaConfig中都可以表述,只是換了一種形式而已,而且,通過聲明相應(yīng)的JavaAnnotation反而“內(nèi)聚”一處,變得更加簡潔明了了。2.3.1那些高曝光率的Annotation至于@Configuration,我想前面已經(jīng)提及過了,這里不再贅述,下面我們看幾個其他比較常見的Annotation,便于為后面更好地理解SpringBoot框架的奧秘做準(zhǔn)備。1.@ComponentScan@ComponentScan對應(yīng)XML配置形式中的<context:component-scan>元素,用于配合一些元信息JavaAnnotation,比如@Component和@Repository等,將標(biāo)注了這些元信息Annotation的bean定義類批量采集到Spring的IoC容器中。我們可以通過basePackages等屬性來細(xì)粒度地定制@ComponentScan自動掃描的范圍,如果不指定,則默認(rèn)Spring框架實現(xiàn)會從聲明@ComponentScan所在類的package進(jìn)行掃描。@ComponentScan是SpringBoot框架魔法得以實現(xiàn)的一個關(guān)鍵組件,大家可以重點關(guān)注,我們后面還會遇到它。2.@PropertySource與@PropertySources@PropertySource用于從某些地方加載*.properties文件內(nèi)容,并將其中的屬性加載到IoC容器中,便于填充一些bean定義屬性的占位符(placeholder),當(dāng)然,這需要PropertySourcesPlaceholderConfigurer的配合。如果我們使用Java8或者更高版本開發(fā)(本書寫作期間Java9還沒發(fā)布),那么,我們可以并行聲明多個@PropertySource:@Configuration

@PropertySource("classpath:1.properties")

@PropertySource("classpath:2.properties")

@PropertySource("...")

publicclassXConfiguration{

...

}

如果我們使用低于Java8版本的Java開發(fā)Spring應(yīng)用,又想聲明多個@PropertySource,則需要借助@PropertySources的幫助了:@PropertySources({

@PropertySource("classpath:1.properties"),

@PropertySource("classpath:2.properties"),

...

})

publicclassXConfiguration{

...

}

3.@Import與@ImportResource在XML形式的配置中,我們通過<importresource="XXX.xml"/>的形式將多個分開的容器配置合到一個配置中,在JavaConfig形式的配置中,我們則使用@Import這個Annotation完成同樣目的:@Configuration

@Import(MockConfiguration.class)

publicclassXConfiguration{

...

}

@Import只負(fù)責(zé)引入JavaConfig形式定義的IoC容器配置,如果有一些遺留的配置或者遺留系統(tǒng)需要以XML形式來配置(比如dubbo框架),我們依然可以通過@ImportResource將它們一起合并到當(dāng)前JavaConfig配置的容器中:@Configuration

@Import(MockConfiguration.class)

@ImportResource("...")

publicclassXConfiguration{

...

}

2.4本章小結(jié)“磨刀不誤砍柴工”,本章我們主要回顧了一下Spring框架的歷史,并對Spring框架的一些核心功能和特性進(jìn)行了精煉的剖析,在把我們的思維之刀磨礪快了之后,讓我們開始解一下SpringBoot這頭小牛兒吧!第3章SpringBoot的工作機制我們說SpringBoot是Spring框架對“約定優(yōu)先于配置(ConventionOverConfiguration)”理念的最佳實踐的產(chǎn)物,一個典型的SpringBoot應(yīng)用本質(zhì)上其實就是一個基于Spring框架的應(yīng)用,而如果大家對Spring框架已經(jīng)了如指掌,那么,在我們一步步揭開SpringBoot微框架的面紗之后,大家就會發(fā)現(xiàn)“陽光之下,并無新事”。不信?那我們一起走著瞧唄!3.1SpringBoot初體驗一個典型的SpringBoot應(yīng)用長什么樣子呢?如果我們使用\hhttp://start.spring.io/創(chuàng)建一個最簡單的依賴Web模塊的SpringBoot應(yīng)用,一般情況下,我們會得到一個SpringBoot應(yīng)用的啟動類,如下面代碼所示:importorg.springframework.boot.SpringApplication;

importorg.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication

publicclassDemoApplication{

publicstaticvoidmain(String[]args){

SpringApplication.run(DemoApplication.class,args);

}

}

所有的SpringBoot無論怎么定制,本質(zhì)上與上面的啟動類代碼是一樣的,而以上代碼示例中,Annotation定義(@SpringBootApplication)和類定義(SpringApplication.run)最為耀眼,那么,要揭開SpringBoot應(yīng)用的奧秘,很明顯的,我們只要先從這兩位開始就可以了。3.2@SpringBootApplication背后的秘密@SpringBootApplication是一個“三體”結(jié)構(gòu),實際上它是一個復(fù)合Annotation:@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

@Configuration

@EnableAutoConfiguration

@ComponentScan

public@interfaceSpringBootApplication{

...

}

雖然它的定義使用了多個Annotation進(jìn)行元信息標(biāo)注,但實際上對于SpringBoot應(yīng)用來說,重要的只有三個Annotation,而“三體”結(jié)構(gòu)實際上指的就是這三個Annotation:·@Configuration·@EnableAutoConfiguration·@ComponentScan所以,如果我們使用如下的SpringBoot啟動類,整個SpringBoot應(yīng)用依然可以與之前的啟動類功能對等:@Configuration

@EnableAutoConfiguration

@ComponentScan

publicclassDemoApplication{

publicstaticvoidmain(String[]args){

SpringApplication.run(DemoApplication.class,args);

}

}

但每次都寫三個Annotation顯然過于繁瑣,所以寫一個@SpringBoot-Application這樣的一站式復(fù)合Annotation顯然更方便些。3.2.1@Configuration創(chuàng)世紀(jì)這里的@Configuration對我們來說并不陌生,它就是JavaConfig形式的SpringIoC容器的配置類使用的那個@Configuration,既然SpringBoot應(yīng)用骨子里就是一個Spring應(yīng)用,那么,自然也需要加載某個IoC容器的配置,而SpringBoot社區(qū)推薦使用基于JavaConfig的配置形式,所以,很明顯,這里的啟動類標(biāo)注了@Configuration之后,本身其實也是一個IoC容器的配置類!很多SpringBoot的代碼示例都喜歡在啟動類上直接標(biāo)注@Configuration或者@SpringBootApplication,對于初接觸SpringBoot的開發(fā)者來說,其實這種做法不便于理解,如果我們將上面的SpringBoot啟動類拆分為兩個獨立的Java類,整個形勢就明朗了:@Configuration

@EnableAutoConfiguration

@ComponentScan

publicclassDemoConfiguration{

@Bean

publicControllercontroller(){

returnnewController();

}

}

publicclassDemoApplication{

publicstaticvoidmain(String[]args){

SpringApplication.run(DemoConfiguration.class,args);

}

}

所以,啟動類DemoApplication其實就是一個標(biāo)準(zhǔn)的Standalone類型Java程序的main函數(shù)啟動類,沒有什么特殊的。而@Configuration標(biāo)注的DemoConfiguration定義其實也是一個普通的JavaConfig形式的IoC容器配置類,沒啥新東西,全是Spring框架里的概念!3.2.2@EnableAutoConfiguration的功效@EnableAutoConfiguration其實也沒啥“創(chuàng)意”,各位是否還記得Spring框架提供的各種名字為@Enable開頭的Annotation定義?比如@EnableScheduling、@EnableCaching、@EnableMBeanExport等,@EnableAutoConfiguration的理念和“做事方式”其實一脈相承,簡單概括一下就是,借助@Import的支持,收集和注冊特定場景相關(guān)的bean定義:·@EnableScheduling是通過@Import將Spring調(diào)度框架相關(guān)的bean定義都加載到IoC容器?!EnableMBeanExport是通過@Import將JMX相關(guān)的bean定義加載到IoC容器。而@EnableAutoConfiguration也是借助@Import的幫助,將所有符合自動配置條件的bean定義加載到IoC容器,僅此而已!@EnableAutoConfiguration作為一個復(fù)合Annotation,其自身定義關(guān)鍵信息如下:@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

@AutoConfigurationPackage

@Import(EnableAutoConfigurationImportSelector.class)

public@interfaceEnableAutoConfiguration{

...

}

其中,最關(guān)鍵的要屬@Import(EnableAutoConfigurationImportSelector.class),借助EnableAutoConfigurationImportSelector,@EnableAutoConfiguration可以幫助SpringBoot應(yīng)用將所有符合條件的@Configuration配置都加載到當(dāng)前SpringBoot創(chuàng)建并使用的IoC容器,就跟一只“八爪魚”一樣(如圖3-1所示)。借助于Spring框架原有的一個工具類:SpringFactoriesLoader的支持,@EnableAutoConfiguration可以“智能”地自動配置功效才得以大功告成!圖3-1EnableAutoConfiguration得以生效的關(guān)鍵組件關(guān)系圖自動配置的幕后英雄:SpringFactoriesLoader詳解SpringFactoriesLoader屬于Spring框架私有的一種擴(kuò)展方案(類似于Java的SPI方案java.util.ServiceLoader),其主要功能就是從指定的配置文件META-INF/spring.factories加載配置,spring.factories是一個典型的javaproperties文件,配置的格式為Key=Value形式,只不過Key和Value都是Java類型的完整類名(Fullyqualifiedname),比如\h[1]:example.MyService=example.MyServiceImpl1,example.MyServiceImpl2

然后框架就可以根據(jù)某個類型作為Key來查找對應(yīng)的類型名稱列表了:publicabstractclassSpringFactoriesLoader{

//...

publicstatic<T>List<T>loadFactories(Class<T>factoryClass,ClassLoaderclassLoader){

...

}

publicstaticList<String>loadFactoryNames(Class<?>factoryClass,ClassLoaderclassLoader){

...

}

//...

}

對于@EnableAutoConfiguration來說,SpringFactoriesLoader的用途稍微不同一些,其本意是為了提供SPI擴(kuò)展的場景,而在@EnableAutoConfiguration的場景中,它更多是提供了一種配置查找的功能支持,即根據(jù)@EnableAutoConfiguration的完整類名org.springframework.boot.autoconfigure.EnableAutoConfiguration作為查找的Key,獲取對應(yīng)的一組@Configuration類:org.springframework.boot.autoconfigure.EnableAutoConfiguration=\

org.springframework.boot.autoconfigure.admin.SpringApplicationAdmin-JmxAutoConfiguration,\

org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\

org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\

org.springframework.boot.autoconfigure.MessageSourceAutoConfiguration,\

org.springframework.boot.autoconfigure.PropertyPlaceholderAuto-Configuration,\

org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\

org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\

org.springframework.boot.autoconfigure.cassandra.CassandraAuto-Configuration,\

org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\

org.springframework.boot.autoconfigure.context.ConfigurationProperties-AutoConfiguration,\

org.springframework.boot.autoconfigure.dao.PersistenceException-TranslationAutoConfiguration,\

org.springframework.boot.autoconfigure.data.cassandra.Cassandra-DataAutoConfiguration,\

org.springframework.boot.autoconfigure.data.cassandra.Cassandra-RepositoriesAutoConfiguration,\

...

以上是從SpringBoot的autoconfigure依賴包中的META-INF/spring.factories配置文件中摘錄的一段內(nèi)容,可以很好地說明問題。所以,@EnableAutoConfiguration自動配置的魔法其實就變成了:從classpath中搜尋所有META-INF/spring.factories配置文件,并將其中org.spring-framework.boot.autoconfigure.EnableAutoConfiguration對應(yīng)的配置項通過反射(JavaReflection)實例化為對應(yīng)的標(biāo)注了@Configuration的JavaConfig形式的IoC容器配置類,然后匯總為一個并加載到IoC容器。目前為止,還是Spring框架的原有概念和支持,依然沒有“新鮮事”!\h[1]摘自SpringFactoriesLoader的Javadoc。3.2.3可有可無的@ComponentScan為啥說@ComponentScan是可有可無的?因為原則上來說,作為Spring框架里的“老一輩革命家”,@ComponentScan的功能其實就是自動掃描并加載符合條件的組件或bean定義,最終將這些bean定義加載到容器中。加載bean定義到Spring的IoC容器,我們可以手工單個注冊,不一定非要通過批量的自動掃描完成,所以說@ComponentScan是可有可無的。對于SpringBoot應(yīng)用來說,同樣如此,比如我們本章的啟動類:@Configuration

@EnableAutoConfiguration

@ComponentScan

publicclassDemoApplication{

publicstaticvoidmain(String[]args){

SpringApplication.run(DemoApplication.class,args);

}

}

如果我們當(dāng)前應(yīng)用沒有任何bean定義需要通過@ComponentScan加載到當(dāng)前SpringBoot應(yīng)用對應(yīng)使用的IoC容器,那么,除去@ComponentScan的聲明,當(dāng)前SpringBoot應(yīng)用依然可以照常運行,功能對等!看,還是沒有啥新東西!3.3SpringApplication:SpringBoot程序啟動的一站式解決方案如果非說SpringBoot微框架提供了點兒自己特有的東西,在核心類層面(各種場景下的自動配置一站式插拔模塊,我們下一章再重點介紹),也就是SpringApplication了。SpringApplication將一個典型的Spring應(yīng)用啟動的流程“模板化”(這里是動詞),在沒有特殊需求的情況下,默認(rèn)模板化后的執(zhí)行流程就可以滿足需求了;但有特殊需求也沒關(guān)系,SpringApplication在合適的流程結(jié)點開放了一系列不同類型的擴(kuò)展點,我們可以通過這些擴(kuò)展點對SpringBoot程序的啟動和關(guān)閉過程進(jìn)行擴(kuò)展。最“膚淺”的擴(kuò)展或者配置是SpringApplication通過一系列設(shè)置方法(setters)開放的定制方式,比如,我們之前的啟動類的main方法中只有一句:SpringApplication.run

DemoApplication.class

args

);

但如果我們想通過SpringApplication的一系列設(shè)置方法來擴(kuò)展啟動行為,則可以用如下方式進(jìn)行:publicclassDemoApplication{

publicstaticvoidmain(String[]args){

//SpringApplication.run(DemoConfiguration.class,args);

SpringApplicationbootstrap=newSpringApplication(Demo-Configuration.class);

bootstrap.setBanner(newBanner(){

@Override

publicvoidprintBanner(Environmentenvironment,Class<?>aClass,PrintStreamprintStream){

//

比如打印一個我們喜歡的

ASCIIArts

字符畫

}

});

bootstrap.setBannerMode(Banner.Mode.CONSOLE);

//

其他定制設(shè)置

...

bootstrap.run(args);

}

}

提示設(shè)置自定義banner最簡單的方式其實是把ASCIIArt字符畫放到一個資源文件,然后通過ResourceBanner來加載:bootstrap.setBanner(newResourceBanner(newClassPathResource("banner.txt")));大部分情況下,SpringApplication已經(jīng)提供了很好的默認(rèn)設(shè)置,所以,我們不再對這些表層進(jìn)行探究了,因為對表層之下的東西進(jìn)行探究才是我們的最終目的。3.3.1深入探索SpringApplication執(zhí)行流程SpringApplication的run方法的實現(xiàn)是我們本次旅程的主要線路,該方法的主要流程大體可以歸納如下\h[1]:1)如果我們使用的是SpringApplication的靜態(tài)run方法,那么,這個方法里面首先需要創(chuàng)建一個SpringApplication對象實例,然后調(diào)用這個創(chuàng)建好的SpringApplication的實例run方法。在SpringApplication實例初始化的時候,它會提前做幾件事情:·根據(jù)classpath里面是否存在某個特征類(org.springframework.web.context.ConfigurableWebApplicationContext)來決定是否應(yīng)該創(chuàng)建一個為Web應(yīng)用使用的ApplicationContext類型,還是應(yīng)該創(chuàng)建一個標(biāo)準(zhǔn)Standalone應(yīng)用使用的ApplicationContext類型?!な褂肧pringFactoriesLoader在應(yīng)用的classpath中查找并加載所有可用的ApplicationContextInitializer?!な褂肧pringFactoriesLoader在應(yīng)用的classpath中查找并加載所有可用的ApplicationListener。·推斷并設(shè)置main方法的定義類。2)SpringApplication實例初始化完成并且完成設(shè)置后,就開始執(zhí)行run方法的邏輯了,方法執(zhí)行伊始,首先遍歷執(zhí)行所有通過SpringFactoriesLoader可以查找到并加載的SpringApplicationRunListener,調(diào)用它們的started()方法,告訴這些SpringApplicationRunListener,“嘿,SpringBoot應(yīng)用要開始執(zhí)行咯!”。3)創(chuàng)建并配置當(dāng)前SpringBoot應(yīng)用將要使用的Environment(包括配置要使用的PropertySource以及Profile)。4)遍歷調(diào)用所有SpringApplicationRunListener的environmentPrepared()的方法,告訴它們:“當(dāng)前SpringBoot應(yīng)用使用的Environment準(zhǔn)備好咯!”。5)如果SpringApplication的showBanner屬性被設(shè)置為true,則打印banner(SpringBoot1.3.x版本,這里應(yīng)該是基于Banner.Mode決定banner的打印行為)。這一步的邏輯其實可以不關(guān)心,我認(rèn)為唯一的用途就是“好玩”(JustForFun)。6)根據(jù)用戶是否明確設(shè)置了applicationContextClass類型以及初始化階段的推斷結(jié)果,決定該為當(dāng)前SpringBoot應(yīng)用創(chuàng)建什么類型的ApplicationContext并創(chuàng)建完成,然后根據(jù)條件決定是否添加ShutdownHook,決定是否使用自定義的BeanNameGenerator,決定是否使用自定義的ResourceLoader,當(dāng)然,最重要的,將之前準(zhǔn)備好的Environment設(shè)置給創(chuàng)建好的ApplicationContext使用。7)ApplicationContext創(chuàng)建好之后,SpringApplication會再次借助Spring-FactoriesLoader,查找并加載classpath中所有可用的ApplicationContext-Initializer,然后遍歷調(diào)用這些ApplicationContextInitializer的initialize(applicationContext)方法來對已經(jīng)創(chuàng)建好的ApplicationContext進(jìn)行進(jìn)一步的處理。8)遍歷調(diào)用所有SpringApplicationRunListener的contextPrepared()方法,通知它們:“SpringBoot應(yīng)用使用的ApplicationContext準(zhǔn)備好啦!”9)最核心的一步,將之前通過@EnableAutoConfiguration獲取的所有配置以及其他形式的IoC容器配置加載到已經(jīng)準(zhǔn)備完畢的ApplicationContext。10)遍歷調(diào)用所有SpringApplicationRunListener的contextLoaded()方法,告知所有SpringApplicationRunListener,ApplicationContext"裝填完畢"!11)調(diào)用ApplicationContext的refresh()方法,完成IoC容器可用的最后一道工序。12)查找當(dāng)前ApplicationContext中是否注冊有CommandLineRunner,如果有,則遍歷執(zhí)行它們。13)正常情況下,遍歷執(zhí)行SpringApplicationRunListener的finished()方法,告知它們:“搞定!”。(如果整個過程出現(xiàn)異常,則依然調(diào)用所有SpringApplicationRunListener的finished()方法,只不過這種情況下會將異常信息一并傳入處理)。至此,一個完整的SpringBoot應(yīng)用啟動完畢!整個過程看起來冗長無比,但其實很多都是一些事件通知的擴(kuò)展點,如果我們將這些邏輯暫時忽略,那么,其實整個SpringBoot應(yīng)用啟動的邏輯就可以壓縮到極其精簡的幾步,如圖3-2所示。圖3-2SpringBoot應(yīng)用啟動步驟簡要示意圖前后對比我們就可以發(fā)現(xiàn),其實SpringApplication提供的這些各類擴(kuò)展點近乎“喧賓奪主”,占據(jù)了一個Spring應(yīng)用啟動邏輯的大部分“江山”,除了初始化并準(zhǔn)備好ApplicationContext,剩下的大部分工作都是通過這些擴(kuò)展點完成的,所以,我們有必要對各類擴(kuò)展點進(jìn)行逐一剖析,以便在需要的時候可以信手拈來,為我所用。\h[1]本流程說明參考的是SpringBoot1.2.6版本代碼的實現(xiàn)。3.3.2SpringApplicationRunListenerSpringApplicationRunListener是一個只有SpringBoot應(yīng)用的main方法執(zhí)行過程中接收不同執(zhí)行時點事件通知的監(jiān)聽者:publicinterfaceSpringApplicationRunListener{

voidstarted();

voidenvironmentPrepared(ConfigurableEnvironmentenvironment);

voidcontextPrepared(ConfigurableApplicationContextcontext);

voidcontextLoaded(ConfigurableApplicationContextcontext);

voidfinished(ConfigurableApplicationContextcontext,Throwableexception);

}

對于我們來說,基本沒什么常見的場景需要自己實現(xiàn)一個Spring-ApplicationRunListener,即使SpringBoot默認(rèn)也只是實現(xiàn)了一個org.spring-framework.boot.context.event.EventPublishingRunListener,用于在SpringBoot啟動的不同時點發(fā)布不同的應(yīng)用事件類型(ApplicationEvent),如果有哪些ApplicationListener對這些應(yīng)用事件感興趣,則可以接收并處理。(還記得SpringApplication實例初始化的時候加載了一批ApplicationListener,但是在run方法執(zhí)行流程中卻沒有被使用的絲毫痕跡嗎?EventPublishingRunListener就是答案?。┘僭O(shè)我們真的有場景需要自定義一個SpringApplicationRunListener實現(xiàn),那么有一點需要注意,即任何一個SpringApplicationRunListener實現(xiàn)類的構(gòu)造方法(Constructor)需要有兩個構(gòu)造參數(shù),一個構(gòu)造參數(shù)的類型就是我們的org.springframework.boot.SpringApplication,另外一個就是args參數(shù)列表的String[]:publicclassDemoSpringApplicationRunListenerimplementsSpringApplicationRunListener{

@Override

publicvoidstarted(){

//dowhateveryouwanttodo

}

@Override

publicvoidenvironmentPrepared(ConfigurableEnvironmentenvironment){

//dowhateveryouwanttodo

}

@Override

publicvoidcontextPrepared(ConfigurableApplicationContextcontext){

//dowhateveryouwanttodo

}

@Override

publicvoidcontextLoaded(ConfigurableApplicationContextcontext){

//dowhateveryouwanttodo

}

@Override

publicvoidfinished(ConfigurableApplicationContextcontext,Throwableexception){

//dowhateveryouwanttodo

}

}

之后,我們可以通過SpringFactoriesLoader立下的規(guī)矩,

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論