務實優化:將本地單元測試Spring啓動速度從1分半優化到16秒,全流程詳解

引子

某個平凡熟悉的早上,傳來測試同窗的一陣哀嚎:那個誰!你提測的代碼連運行都不能運行,苦澀。java

我默不做聲,由於主項目尚未徹底服務化,主項目的總體war包太大,加上歷來沒有講究過,開發同窗跑一個測試用例,每每啓動Spring就要花一分半鐘,哪裏有心情按規範跑單測呢?同問了幾個開發同窗也都有一樣的痛點,感受解決單測環境刻不容緩,古人云:工欲善其事,必先利其器,對吧?spring

來了,老弟

將日誌級別調至Debug級別

沒錯,這是咱們須要作的第一步最重要的步驟,開啓單測,把日誌打到一個文件裏,從頭擼到尾,究竟你這加載的一分半鐘究竟幹嗎了~數據庫

一些Spring配置初始化
根據ComponentScan掃路徑的Class類,加入待注入候選列表
根據Mapper掃路徑的Class類,加入待注入候選列表
對掃出來的Mapper建立MapperFactoryBean
建立注入@Configuration裏@Bean註解的Bean的BeanDefinitions
預加載一些Bean
一些組件例如,PostProcessor、Advisor初始化
一些中間件例如數據庫、緩存、消息隊列加載
掃描的Bean的初始化,依賴注入。。
Bean的PostConstruct開始跑
....//可能不是很全,列舉了其中一部分
複製代碼

默默的看了眼日誌,20M,媽耶,引了一個 ApplicationContextAware看了一下 BeanFactory,好吧,加載了1500個 BeanSpring默認的Ioc容器會把全部的Bean在啓動時,都加載成功,首先想到的措施是讓 Bean懶加載,按需加載,不用的就不加載嘛,很簡單!

坑來了:如何對Bean進行懶加載?

簡單的網上衝浪了一下,咱們須要將緩存

註解法:@ComponentScan(value = "com.evanyz",lazyInit = true) //將這個配置設爲true
//xml裏是beans裏有一個default-lazy-init標籤
複製代碼

但是設置成功以後,徹底不生效,仍是1500個,嘗試許久,仍是沒生效,感受很懵逼,甚至都有點開始懷疑Spring了。bash

排查許久後,忽然發現爲何Debug的時候,會報一些該Bean重複已存在忽略的錯,忽然靈光一現。 MD,咱們項目裏寫代碼根本不講究,每個子項目裏,例如commonbiz 都含有Spring的初始化文件。app

也就是說,從這個初始化配置以後,繼續掃其餘的配置文件,仍是會繼續加載,致使以前的配置失效。。工具

舉例:
我在test的初始化類裏配置了 @ComponentScan(value = "com.evanyz",lazyInit = true) ,
當他掃到biz子項目的時候,發現另一個Spring的配置文件是
@ComponentScan(value = "com.evanyz")
這時候懶加載會被覆蓋掉,就不生效了。。
複製代碼

這時候想到的辦法就是 在test配置里加上一些操做,解釋見註解測試

@ComponentScan(
    //掃包
    value = {com.evanyz"},
   //排除一些Bean
    excludeFilters = {
        //作點小優化,讓他把一些在跑單測時的擴展點不要注入
        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {
            SmsServicePostProcessor.class, CatUrlPostProcessor.class,
        }),
        //按政策排除,把一些其餘項目裏的Spring配置去掉
        @ComponentScan.Filter(type = FilterType.REGEX, pattern = {
            "com\\.evanyz\\.test\\..*",
            "com\\.evanyz\\.biz\\.springconfig..*",
            "com\\.evanyz\\.common\\.springconfig..*",
            "com\\.evanyz\\.common\\.cat..*"
        }),
        //最後清掉了發現,仍是有一些配置被加載了
        //一不作二不休,我全乾掉,搞白名單還不行嗎?哭哭
        @ComponentScan.Filter(type = FilterType.ANNOTATION, value = {
            ComponentScan.class, Configuration.class, ImportResource.class
        }),
    }, lazyInit = true
)

複製代碼

通過以上的一頓操做,Spring終於懶加載了,直觀的看一下以後的Debug,瘦身了3倍,爽! 優化

繼續優化,Mock一些毒瘤依賴

雖然Spring已懶加載,可是依賴很亂,每每依賴一個服務,又要注入不少的類,並且不少的業務的類,都寫了@PostConstruct,若是裏面包含業務代碼,例如查庫啊之類的,你就呵呵吧。一次Spring啓動能給你跑100條Sql,能不慢嗎?ui

來,壯士斷腕,把這些毒瘤,會預先加載的類,選一些不重要的在單測不須要用的都作個Mock,不要讓這個拖垮咱們的環境!

精益求精,搜尋日誌,發現異常類加載超過10s,來魔改

這時候,其實一次單測已經在30s就能夠搞定了,可是本着有點追求的想法,仍是想再優化一下。

忽然發現有一個可疑的日誌

咱們用的Cat作監控,咱們項目裏有不少Cat打點的工具類,只要跑到一個打點上,Cat就會開始加載(明明連不上),可是這一步驟估計是IO之類的東西,加載一下竟然花了10幾秒,個人天,確定要幹掉!

怎麼幹呢?由於這些打點是耦合在代碼裏的,很差動,這時候想到的解決方案就是看看Cat能不能關掉,後來衝浪了一番,發現咱們這個舊版本沒辦法關。怎麼辦呢?被逼出來的

在代碼目錄裏,搞個同名類,覆蓋原有類,讓他默認打點的時候不要初始化。 魔改一把,生效!美滋滋!

classLoader默認會讀同名最近的那個類
複製代碼

最後跑了一下單測,16s,好爽!

總結一把

自從單測優化以後,後面制定規範讓你們交付測試的時候本身先跑遍單測,這樣就能有效的避免由於一些小錯誤返工 四、5次的尷尬,並且單測測的更全,更不容易出錯,利已利民。

這裏貼一下單測的核心類的註釋:

多點時間陪陪家人

我:爽嗎?我問對面的開發。
他:太爽了!
我笑了笑,深藏功與名(:
複製代碼
相關文章
相關標籤/搜索