javascript
Spring启动慢?提速利器SpringFu来了
函數(shù)是應(yīng)用在Serverless世界里的一種極輕量形態(tài),每個(gè)函數(shù)通常專注提供單一功能的服務(wù)。它們相互串聯(lián),井然有序,時(shí)而成群結(jié)對(duì)的出現(xiàn),在完成既定任務(wù)后又很快的消失,此起彼伏,輝映成趣,將云的靈巧與飄逸發(fā)揮得淋漓盡致。
與此同時(shí),扎根在中后臺(tái)辛勤耕耘了十多年的Spring,早已是一位心寬體闊、中年發(fā)福了的明星大叔。倘若將他邀請(qǐng)到函數(shù)的舞臺(tái)上,那就成了絕佳的娛樂(lè)新聞?lì)}材。其實(shí)Spring本尊也確曾出演過(guò)一部收視率不太高的“古裝情景喜劇”《Spring Cloud Function 1.0》,盡管使出了反應(yīng)式(Reactive)架構(gòu)、云平臺(tái)集成等等經(jīng)典絕技,無(wú)奈臃腫的身材難以跟上輕快的舞步,被Nodejs和Python等晚輩小生調(diào)戲得窘態(tài)頻現(xiàn)。于是,Spring立下宏志,即刻減肥,不瘦30斤,不回江湖。如今,Spring終于功成歸來(lái),不僅習(xí)得新技能Functional Bean,還為社區(qū)帶來(lái)了兩位新伙伴:SpringInit和SpringFu。
根據(jù)非官方實(shí)驗(yàn)數(shù)據(jù),將SpringMVC應(yīng)用改造為SpringFu結(jié)構(gòu),啟動(dòng)時(shí)間大約能縮短50%。這點(diǎn)提速當(dāng)然不算矚目,然而SpringFu的關(guān)鍵價(jià)值在于,它使Spring框架能夠擺脫JVM動(dòng)態(tài)特性束縛,從而適配AOT編譯模式。倘若搭配上GraalVM編譯器,應(yīng)用啟動(dòng)速度就能直線下降到原先的大約1%,徹底甩掉“起跑永遠(yuǎn)慢半拍”的帽子。
一 Spring 5.0的創(chuàng)新
關(guān)于SpringFu的故事,還得從Spring 5.0版本說(shuō)起。
縱觀Spring的演化過(guò)程,Bean對(duì)象的定義方式是一條脈絡(luò)清晰的主線。早期“SSH時(shí)代”的Spring框架只能使用xml文件定義Bean,寫一個(gè)Java項(xiàng)目,一半是xml文件。即便如此,Spring 1.0依然帶領(lǐng)著這支“尖括號(hào)加代碼”大軍擊潰了更加笨拙繁瑣的EJB框架。平定天下以后,Spring內(nèi)部開始了自我進(jìn)化,Spring 2.0推出了基于注解的Bean定義機(jī)制,逐步替換過(guò)去的xml文件。最終在SpringBoot項(xiàng)目的強(qiáng)力助攻下,注解全面取代xml,完成大國(guó)一統(tǒng)。眼看太平盛世剛剛到來(lái),可就在最近幾年里,Spring社區(qū)又刮起了一股更年輕的“去注解”風(fēng)潮。
這股風(fēng)潮的興起與云原生以及GraalVM項(xiàng)目蓬勃發(fā)展有著不可忽視的聯(lián)系。了解過(guò)AOT編譯的讀者應(yīng)該知道,反射、動(dòng)態(tài)代理等運(yùn)行時(shí)特性都是妨礙Java代碼進(jìn)行AOT編譯的首要因素,然而基于注解的Bean掃描過(guò)程恰恰大量依賴了這些Java動(dòng)態(tài)特性。這就相當(dāng)于說(shuō),曾經(jīng)代表著先進(jìn)生產(chǎn)力的Spring注解,現(xiàn)在正在成為制約Spring繼續(xù)演進(jìn)的束腳石。
事已至此,Spring自個(gè)也不含糊。“注解不要也罷,直接暴露接口”,2017年底,正趕在Spring 5.0新品發(fā)布會(huì)的節(jié)骨眼上,Spring當(dāng)機(jī)立斷的拿出了布局未來(lái)的一張牌:Functional Bean。
發(fā)展才是硬道理。
這項(xiàng)肩負(fù)重任的Functional Bean究竟是何神技呢?不瞞您說(shuō),其實(shí)關(guān)鍵的修改就是加了一個(gè)成員方法。
對(duì)于Spring系統(tǒng)而言,IoC容器是其中最核心的部分,在代碼上就是BeanFactory和各種ApplicationContext。這當(dāng)中,最功績(jī)顯赫的要數(shù)能從xml文件裝載Bean的ClassPathXmlApplicationContext,和能從代碼上下文掃描Bean的
AnnotationConfigApplicationContext,它倆的光輝事跡早已在網(wǎng)上被廣泛傳誦,無(wú)需多述。比較有意思的地方在于,若從繼承關(guān)系來(lái)看,這兩位老爺子頂多也就算得上是遠(yuǎn)房親戚,雖然都繼承自ApplicationContext,在中間隔卻了好幾代血緣。而與Functional Bean相關(guān)的“改進(jìn)”發(fā)生在AnnotationConfigApplicationContext的父類,也就是GenericApplicationContext類型里。
在Spring的歷史上,GenericApplicationContext并不是一位經(jīng)常拋頭露面的IoC容器。據(jù)文檔記載,一直到Spring 5.0之前,GenericApplicationContext的適用場(chǎng)景都是配合其他DefinitionReader對(duì)象完成非標(biāo)準(zhǔn)來(lái)源的Bean注冊(cè),比如從properties文件加載Bean定義。它有一個(gè)registerBeanDefinition()方法,但實(shí)際的代碼實(shí)現(xiàn)卻只是將傳入的Bean定義直接轉(zhuǎn)交給代管的beanFactory對(duì)象。
就是這么個(gè)低調(diào)的小副手,在Spring 5.0里忽然被賦予了如同AnnotationConfigApplicationContext一樣獨(dú)立加載和操控Bean的權(quán)力,就像78歲高齡的前副總統(tǒng)被提名大選,雖是情理之中,卻也有些意料之外。新增加的方法叫做registerBean(),共有6種重載,其中前5種都是最后1種的參數(shù)簡(jiǎn)化版本,因此本質(zhì)上就是一個(gè)方法,其完整定義如下:
<T> void registerBean(String beanName, Class<T> beanClass, Supplier<T> supplier, BeanDefinitionCustomizer... customizers)一共4個(gè)參數(shù):
- beanName:給Bean取的名字
- beanClass:注冊(cè)Bean的類型
- supplier:關(guān)鍵參數(shù),用于生成Bean對(duì)象
- customizers:可選參數(shù),對(duì)生成的Bean對(duì)象進(jìn)行配置
其中supplier和customizers參數(shù)所屬類型都是只有一個(gè)方法的接口,在Java 8以上版本里,這種接口參數(shù)可以直接傳入Lambda函數(shù)作為匿名類來(lái)使用。例如下面這個(gè)Bean定義:
context.registerBean("myService", MyService.class, () -> new MyService(), // supplier參數(shù),定義Bean對(duì)象的創(chuàng)建方法(bean) -> bean.setLazyInit(true) // customizers參數(shù),配置Bean );用Lambda函數(shù)定義Bean,沒(méi)有注解,沒(méi)有反射,沒(méi)有動(dòng)態(tài)特性,這就是Functional Bean。
由于AnnotationConfigApplicationContext本身也是一種GenericApplicationContext,因此在代碼里訪問(wèn)它一點(diǎn)也不困難。比如繼承ApplicationContextInitializer接口,然后通過(guò)其提供的initialize()回調(diào)方法參數(shù)拿到GenericApplicationContext對(duì)象。或者在Spring容器里的任意地方用@Autowired注解直接拿到全局GenericApplicationContext對(duì)象等等。然而對(duì)于早已習(xí)慣了“Spring == 各種注解”的現(xiàn)有開發(fā)者來(lái)說(shuō),相比寫@Component、@Configuration等等注解,要自己獲取應(yīng)用容器,再調(diào)用registerBean()方法來(lái)注冊(cè)Bean,適應(yīng)成本實(shí)在有點(diǎn)高。即使在最適用的Serverless函數(shù)場(chǎng)景下,愿意折騰的開發(fā)者早就投奔了Nodejs陣營(yíng),不愿折騰的開發(fā)者繼續(xù)Spring注解將就用,這種“丑陋”的Bean注冊(cè)方式即便官方博客多次宣傳,在發(fā)布過(guò)后的近一年里依然幾乎無(wú)人問(wèn)津。
眼看不溫不火的Spring Cloud Function 2.0同樣難以扛起推廣Functional Bean的大旗,Spring此時(shí)亟待一位像當(dāng)年SpringBoot那樣席卷全球的網(wǎng)紅節(jié)目來(lái)反轉(zhuǎn)自己在Serverless戰(zhàn)線上一度低迷的票房。為此,一個(gè)嶄新的項(xiàng)目,SpringFu出現(xiàn)在了大家的視線里。
二 SpringFu vs SpringMVC
SpringFu項(xiàng)目的發(fā)起人Deleuze先生來(lái)自法國(guó),已經(jīng)為Pivotal公司的Spring團(tuán)隊(duì)效力超過(guò)6年,我猜他大概是位中國(guó)迷。根據(jù)項(xiàng)目作者的闡述,Fu有三種含義,首先是Functional的前兩個(gè)字母縮寫,其次是來(lái)源于單詞Kong-Fu(功夫),最后則是諧音中文的“賦”(函數(shù)聲明式的定義寫起來(lái)像是有節(jié)奏韻律的詩(shī)歌)。
這款項(xiàng)目的設(shè)計(jì)也正如其名所述,將Spring Bean的定義過(guò)程編制得如同有行云流水般的順滑。來(lái)看個(gè)例子:
public class Application {public static void main (String[] args) {JafuApplication jafu = webApplication(app -> app.beans(def -> def.bean(DemoHandler.class).bean(DemoService.class)).enable(webMvc(server -> server.port(server.profiles().contains("test") ? 8181 : 8080).router(router -> {DemoHandler handler = server.ref(DemoHandler.class);router.GET("/", handler::hello).GET("/api", handler::json);}).converters(converter -> converter.string().jackson()))));jafu.run(args);} }相比我們印象中Spring項(xiàng)目里東一個(gè)@Service西一個(gè)@Controller的松散型結(jié)構(gòu),基于SpringFu編寫的代碼非常緊湊,信息密度極高。事實(shí)上SpringFu的大部分核心能力依然直接來(lái)自于Spring的各個(gè)子項(xiàng)目,但它與SpringMVC項(xiàng)目用各種注解區(qū)分不同Bean的方式完全不同,SpringFu讓用戶顯式的調(diào)用不同的注冊(cè)接口來(lái)將所需的不同Bean對(duì)象注冊(cè)到Spring上下文容器,整個(gè)機(jī)制完全不依賴反射和其他Java動(dòng)態(tài)特性。因此只要用戶自己沒(méi)有故意使用Java動(dòng)態(tài)語(yǔ)法,采用SpringFu編寫的程序就能天然支持GraalVM的AOT編譯,生成啟動(dòng)速度極快的二進(jìn)制文件。其效果要比提供大量運(yùn)行時(shí)信息給GraalVM編譯器更簡(jiǎn)潔而顯著,這也是SpringFu能帶來(lái)近百倍提速的主要原因。
為了更直觀的感受SpringFu這種聲明式代碼的獨(dú)特Feeling,下面以SpringMVC的幾種典型注解為線索,對(duì)比一下二者的差異。
首先是普通的Bean定義,在SpringMVC通過(guò)@Configuration類注解和@Bean方法注解來(lái)表示。
@Configuration public class MyConfiguration {@Beanpublic Foo foo() {return new Foo();}@Beanpublic Bar bar(Foo foo) {return new Bar(foo);} }在SpringFu里對(duì)應(yīng)的是ConfigurationDsl類型的beans方法,該方法接收一個(gè)Consumer接口對(duì)象作為參數(shù),按照慣例,Consumer接口的實(shí)現(xiàn)通常采用Lambda方法來(lái)定義,因而在代碼中不會(huì)顯式的見(jiàn)到BeanDefinitionDsl的身影。
ConfigurationDsl config = beans(def -> def.bean(Foo.class).bean(Bar.class) // 隱含使用構(gòu)造函數(shù)注入其他Bean )在實(shí)際的代碼里,一般也極少出現(xiàn)單獨(dú)的ConfigurationDsl,它總是以Consumer的形式出現(xiàn),并且隱藏在Lambda方法里面被傳遞給需要它的對(duì)象。
像SpringMVC中使用@Component、@Service等注解的地方,在SpringFu里的處理方法與前例是相同的,只需將原類型的注解去掉,然后通過(guò)beans()方法來(lái)注冊(cè)。例如下面這兩個(gè)定義:
@Component public class XxComponent {// ... }@Service public class YyService {// ... }在SpringFu里大致是這個(gè)樣子:
public class XxComponent {// ... }public class YyService {// ... }beans(def -> def.bean(XxComponent.class).bean(YyService.class) )稍有特殊的是@Controller注解(以及@RestController注解),由于它是業(yè)務(wù)請(qǐng)求的入口,與API的路由息息相關(guān),因此在SpringFu中有專門的DSL類型與之對(duì)應(yīng),比如WebMvcServerDsl和WebFluxServerDsl,它們提供諸如port()、router()等方法來(lái)定義與HTTP監(jiān)聽(tīng)相關(guān)的屬性。例如這兩個(gè)SpringMVC的接口:
@RestController @RequestMapping("/api/demo") public class MyController {@Autowiredprivate MyService myService;@GetMapping("/")public List<Data> findAll() {return myService.findAll();}@GetMapping("/{id}")public Data findOne(@PathVariable Long id) {return myService.findById(id);} }在SpringFu里,通常將處理請(qǐng)求的入口類命名為Handler(而不是Controller,這種命名更符合函數(shù)定義的慣例),如果將上述代碼“直譯”為SpringFu結(jié)構(gòu),大概會(huì)長(zhǎng)這樣:
public class MyHandler {private MyService myService;public MyHandler(MyService myService) {this.myService = myService;}public List<Data> findAll() {return myService.findAll();}public Data findOne(ServerRequest request) {val id = request.pathVariable("id");return myService.findById(id);} }router(r -> {MyHandler handler = server.ref(MyHandler.class);r.GET("/", handler::findAll).GET("/{id}", handler::findOne); }實(shí)際更符合慣例的SpringFu寫法則是將MyHandler類型本身也匿名掉,這樣上述代碼可以進(jìn)一步精簡(jiǎn)成:
router(r -> {MyService myService = server.ref(MyService.class);r.GET("/", myService::findAll).GET("/{id}", request -> {return myService.findById(request.pathVariable("id"));}); }這種流暢的聲明式代碼對(duì)于天生小巧的Serverless函數(shù)十分契合,尤其搭配上同樣簡(jiǎn)潔的Kotlin語(yǔ)言時(shí),往往僅需一個(gè)文件就能完成許多中等復(fù)雜程度函數(shù)的定義。
不過(guò)從SpringFu的業(yè)務(wù)范圍來(lái)看,它的目標(biāo)并非替代SpringMVC。在Spring布局的Functional Bean藍(lán)圖里,SpringFu更像是一支精銳的突擊小分隊(duì),專攻以Serverless函數(shù)為典型的新型輕應(yīng)用場(chǎng)景。對(duì)于Spring框架的傳統(tǒng)優(yōu)勢(shì)領(lǐng)域,中后臺(tái)大型服務(wù)而言,把所有Bean集中定義在一個(gè)地方畢竟過(guò)于理想。在通往Serverless的道路上,其實(shí)SpringFu并不孤單,它還有一個(gè)姐妹項(xiàng)目叫SpringInit,該項(xiàng)目立足于通過(guò)編譯期代碼增強(qiáng),將用戶編寫的SpringMVC注解偷偷換成Functional Bean的方式注冊(cè),從而實(shí)現(xiàn)大型JVM服務(wù)的Serverless適配。而這個(gè)同樣異想天開的項(xiàng)目作者正是大名鼎鼎的SpringBoot和SpringCloud項(xiàng)目創(chuàng)始人Dave Syer。由于篇幅所限,本文不對(duì)SpringInit項(xiàng)目再做展開,有興趣的同學(xué)可移步Github圍觀。
三 源碼解讀
在設(shè)計(jì)之初,SpringFu就是奔著DSL(特定領(lǐng)域語(yǔ)言)的思路去的,項(xiàng)目分為JaFu(Java-Fu)和KoFu(Kotlin-Fu)兩個(gè)部分,分別對(duì)應(yīng)了符合Java和Kotlin語(yǔ)言語(yǔ)法的新型高效Spring服務(wù)編寫方式。由于開發(fā)人手不足,其中JaFu子項(xiàng)目在0.1.0版本后曾暫停過(guò)一段時(shí)間,代碼也從倉(cāng)庫(kù)里移除了,后來(lái)社區(qū)呼聲強(qiáng)烈,在0.3.0版本里又再次復(fù)出。兩者的本質(zhì)原理大差不差,以下的源碼分析以JaFu的最新發(fā)布版本v0.4.3作為參考。
項(xiàng)目結(jié)構(gòu)十分簡(jiǎn)潔,一共3個(gè)模塊:
autoconfigure-adapter:公共的ApplicationContextInitializer對(duì)象
jafu:SpringFu的Java DSL實(shí)現(xiàn)
kofu:SpringFu的Kotlin DSL實(shí)現(xiàn)
其中autoconfigure-adapter模塊被jafu和kofu共用,它實(shí)現(xiàn)了許多ApplicationContextInitializer對(duì)象,這些對(duì)象用于在SpringBoot初始化過(guò)程中通過(guò)Functional Bean機(jī)制預(yù)注冊(cè)某些系統(tǒng)的Bean。這當(dāng)中,有些是為了從而加速服務(wù)的啟動(dòng)過(guò)程,比如ServletWebServerInitializer中注冊(cè)的TomcatServletWebServerFactoryCustomizer,在這里直接注冊(cè)會(huì)比讓SpringBoot自己去掃描快許多;有些是為了改變服務(wù)行為,比如在JacksonJsonConverterInitializer會(huì)注冊(cè)一個(gè)名稱為mappingJackson2HttpMessageConverter的Bean,它會(huì)影響json對(duì)象通過(guò)HTTP接口返回時(shí)的序列化方式。總之這個(gè)模塊屬于針對(duì)函數(shù)場(chǎng)景的Spring定制優(yōu)化,涉及很多Spring內(nèi)部細(xì)節(jié),我們點(diǎn)到為止。
在jafu模塊下的源文件并不多,放在頂層目錄最顯眼位置的JaFu.java是用戶程序的發(fā)動(dòng)機(jī),提供了進(jìn)入SpringFu世界的三種入口方法application()、webApplication()和reactiveWebApplication()。它們負(fù)責(zé)創(chuàng)建出Spring的ApplicationContext容器,然后將其包裝成一個(gè)匿名的JafuApplication對(duì)象返回,在之后在各種DSL里傳遞的context成員都是這個(gè)容器的引用。
幾種入口的區(qū)別在于創(chuàng)建的ApplicationContext容器類型,application()生成的是原始的GenericApplicationContext類型容器(能夠提供Functional Bean所需的registerBean()方法的最基礎(chǔ)容器類型),而webApplication()和reactiveWebApplication()生成的是功能更豐富的ServletWebServerApplicationContext和ReactiveWebServerApplicationContext容器,它倆都來(lái)自SpringBoot項(xiàng)目,從這里已經(jīng)可以看出,SpringFu麻雀雖小,卻是站在巨人肩膀上起飛的。
在入口方法上還有一個(gè)關(guān)鍵細(xì)節(jié),是創(chuàng)建JafuApplication對(duì)象時(shí)候要接收一個(gè)ApplicationDsl類型的參數(shù)。這個(gè)ApplicationDsl是SpringFu整套DSL機(jī)制的總指揮艙,里面琳瑯滿目的裝載著其他所有DSL元素。
至此,SpringFu的外觀輪廓已經(jīng)出來(lái)了。任何SpringFu程序的最外層代碼都可以概括成下面這種三部曲模式:
application( // 構(gòu)造,也可以是webApplication()或reactiveWebApplication()ApplicationDsl // 配置 ).run() // 啟動(dòng)第一步“構(gòu)造”完成,接下來(lái)是內(nèi)容最豐富的一個(gè)部分:“配置”。
從繼承關(guān)系來(lái)看,ApplicationDsl是一種ConfigurationDsl,而ConfigurationDsl以及其他各種DSL元素都來(lái)自AbstractDsl。
在所有類型中,孤零零的LoggingDsl是唯一沒(méi)有繼承AbstractDsl的漏網(wǎng)之魚。作為SpringFu項(xiàng)目里最簡(jiǎn)單的DSL元素,去掉空行和注釋,LoggingDsl類型的有效代碼只有20行。不過(guò),即便如此特立獨(dú)行,在LoggingDsl身上依然保留著一項(xiàng)與其他DSL元素相同的特征:構(gòu)造方法接收以自身類型為模板的Consumer對(duì)象作為參數(shù)。
LoggingDsl(Consumer<LoggingDsl> dsl) {dsl.accept(this); }構(gòu)造函數(shù)里只有一行dsl.accept(this),這行代碼在所有DSL元素里都會(huì)出現(xiàn)。只是在繼承了AbstractDsl類型的DSL元素中,它是被放在實(shí)現(xiàn)initialize抽象方法的地方,而LoggingDsl類型沒(méi)有繼承過(guò)來(lái)的initialize方法,就直接將它擺在構(gòu)造函數(shù)里了。“把自己傳遞給構(gòu)造方法接收的Consumer對(duì)象”,關(guān)于DSL的這個(gè)神秘行為,我們?cè)诤竺嬷v“啟動(dòng)”的環(huán)節(jié)里再來(lái)解釋。
回到ApplicationDsl上來(lái),這個(gè)類型只是在ConfigurationDsl的基礎(chǔ)上,通過(guò)一個(gè)MessageSourceInitializer對(duì)象額外注冊(cè)了幾個(gè)Spring自用的Bean,主要功能都是直接繼承自ConfigurationDsl。再看ConfigurationDsl類型,這里有幾個(gè)比較常用的方法:
- configurationProperties(Class clazz):注冊(cè)屬性配置類,相當(dāng)于@ConfigurationProperties注解。
- logging(Consumer dsl) : 提供函數(shù)的輸出日志配置。
- beans(Consumer dsl) :提供定義Bean的地方,相當(dāng)于@Configuration注解。
- enable(Consumer configuration) → 為其他DSL提供擴(kuò)展能力的萬(wàn)能配置入口,比如增加Web監(jiān)聽(tīng)。
為了符合流式聲明結(jié)構(gòu)的要求,這些方法都返回ConfigurationDsl類型,并且使用使用return this讓下一個(gè)配置可以串聯(lián)起來(lái)。然后就可以寫出像下面這樣的配置代碼:
conf -> conf.beans(...).logging(...).enable(...);beans()方法接收的是一個(gè)消費(fèi)BeanDefinitionDsl的匿名函數(shù),這個(gè)DSL提供bean()方法,其效果類似SpringMVC程序里的@Bean注解,但在內(nèi)部會(huì)通過(guò)GenericApplicationContext的registerBean()方法直接注冊(cè)Bean到IoC容器,沒(méi)有反射和掃描的過(guò)程。logging()方法接收一個(gè)消費(fèi)LoggingDsl的匿名函數(shù),后者提供level()方法,可以動(dòng)態(tài)調(diào)整任意包路徑的輸出日志級(jí)別。enable()方法需要結(jié)合其他DSL元素一起使用,其用途非常廣泛,比如開頭示例里的webMvc()方法會(huì)返回一個(gè)WebMvcServerDsl對(duì)象,可以配置HTTP監(jiān)聽(tīng)、路由等屬性并通過(guò)該DSL對(duì)象自動(dòng)注冊(cè)相關(guān)的Bean到Spring上下文。
在整個(gè)ApplicationDsl對(duì)象定義完以后,就進(jìn)入到最后的一個(gè)環(huán)節(jié)“啟動(dòng)”了。前面提到過(guò),application()接收一個(gè)ApplicationDsl對(duì)象,會(huì)返回一個(gè)JafuApplication對(duì)象。接下來(lái)就要調(diào)用這個(gè)返回對(duì)象里的點(diǎn)火器方法run()。
SpringFu的DSL是聲明式的,開發(fā)者通過(guò)ApplicationDsl對(duì)象定義的所有配置信息,此時(shí)都還藏在ApplicationDsl自己的肚子里,真正的Spring容器里面依然空空如也。在JafuApplication的run()方法里,SpringFu創(chuàng)建出由SpringBoot框架封裝的SpringApplication應(yīng)用對(duì)象,并將傳入的ApplicationDsl對(duì)象指定為該應(yīng)用對(duì)象的initializer,然后調(diào)用應(yīng)用對(duì)象的run()。這之后就進(jìn)入了SpringBoot的劇本,SpringApplication會(huì)完成Spring運(yùn)行所需的所有前序工作,然后調(diào)用所有initializer對(duì)象的initialize()方法,包括此前傳入的ApplicationDsl對(duì)象。
在ApplicationDsl的initialize()方法里,首先通過(guò)super.initialize(context)調(diào)用祖父類型AbstractDsl的initialize()方法,將傳入的context容器引用保留下來(lái)。然后執(zhí)行dsl.accept(this)進(jìn)入構(gòu)造時(shí)傳入的回調(diào)方法。在接下來(lái)的beans()和enable()方法里,又會(huì)顯式的調(diào)用子級(jí)DSL元素的initialize()方法,從而將這個(gè)初始化過(guò)程一級(jí)一級(jí)的迭代下去。就像是層層嵌套的遞歸調(diào)用,直到所有子級(jí)元素都構(gòu)造完畢。至此,初始化過(guò)程結(jié)束,程序返回到SpringBoot的啟動(dòng)流程。
從源碼不難得出結(jié)論,SpringFu程序的本質(zhì)就是規(guī)避了JVM動(dòng)態(tài)特性的SpringBoot程序。在卸掉過(guò)去spring-boot-starter-web和spring-boot-starter-webflux沉重的外殼之后,換上了一身輕便的戰(zhàn)袍。
看似離經(jīng)叛道,實(shí)則一脈相承。
四 總結(jié)
SpringFu的到來(lái)是Spring面向Serverless時(shí)代的一次主動(dòng)出擊,顛覆自己,重獲新生。為了讓Spring在Serverless函數(shù)的舞臺(tái)上也能輕盈起舞,SpringFu選擇了一條前人從未走過(guò)的路,將Spring不符合AOT編譯的東西統(tǒng)統(tǒng)去掉,做極簡(jiǎn)主義的減法。
事實(shí)證明,沒(méi)有負(fù)擔(dān)的SpringFu能夠跑得更快、飛得更高。
隨著云原生漸漸滲入到開發(fā)者日常的方方面面,相信在前往Serverless的旅途上,我們終將再次遇見(jiàn)Spring那高挑的身影,因?yàn)樗缇偷诌_(dá)了這里,迎接著大家的到來(lái)。
原文鏈接:https://developer.aliyun.com/article/780595?
版權(quán)聲明:本文內(nèi)容由阿里云實(shí)名注冊(cè)用戶自發(fā)貢獻(xiàn),版權(quán)歸原作者所有,阿里云開發(fā)者社區(qū)不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。具體規(guī)則請(qǐng)查看《阿里云開發(fā)者社區(qū)用戶服務(wù)協(xié)議》和《阿里云開發(fā)者社區(qū)知識(shí)產(chǎn)權(quán)保護(hù)指引》。如果您發(fā)現(xiàn)本社區(qū)中有涉嫌抄襲的內(nèi)容,填寫侵權(quán)投訴表單進(jìn)行舉報(bào),一經(jīng)查實(shí),本社區(qū)將立刻刪除涉嫌侵權(quán)內(nèi)容。總結(jié)
以上是生活随笔為你收集整理的Spring启动慢?提速利器SpringFu来了的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: AD管控下的弹性云桌面和文件共享最佳实践
- 下一篇: Spring Boot参数校验以及分组校