SpringBoot系列二:SpringBoot自動(dòng)配置原理

原文出處: 晴楓

1 SpringBoot運(yùn)作原理

上一章中我們提到主程序類的注解 @SpringBootApplication 注解拌蜘,它其實(shí)是個(gè)組合注解预麸,源碼如下:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
           @Filter(
             type = FilterType.CUSTOM,
             classes = {TypeExcludeFilter.class}
           ), 
           @Filter(
              type = FilterType.CUSTOM,
              classes = {AutoConfigurationExcludeFilter.class}
           )}
)
public @interface SpringBootApplication {
    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    Class<?>[] exclude() default {};
 
    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    String[] excludeName() default {};
 
    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackages"
    )
    String[] scanBasePackages() default {};
 
    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackageClasses"
    )
    Class<?>[] scanBasePackageClasses() default {};
}

最主要的還是三個(gè)配置 @SpringBootConfiguration于置、@EnableAutoConfigration、@ComponentScan 三個(gè)注解,下面我們來(lái)一一分析。

1.1 @SpringBootConfiguration

查看@SpringBootConfiguration源碼惫叛,其實(shí)它也就是@Configuration注解:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}

對(duì)于 @Configuration,我們并不陌生逞刷,它就是 JavaConfig 形式的 SpringIOC 容器的配置類挣棕,也是 SpringBoot 推薦使用配置形式,所以主程序類標(biāo)注了 @SpringBootConfiguration 注解亲桥,其本身也就是一個(gè)配置類。更多有關(guān) JavaConfig 形式的配置以及有關(guān)它和 XML 形式的配置的區(qū)別與聯(lián)系固耘,請(qǐng)參考后續(xù)有關(guān) Spring 注解版相關(guān)文章题篷。

1.2 @ComponentScan

@ComponentScan 注解在 Spring 的注解中也起到到相當(dāng)重要的作用,它可以自定義 Spring 掃描的包厅目,也就是它默認(rèn)會(huì)掃描標(biāo)注了 @Controller番枚、@Service法严、@Component 以及 @Repository 注解的類,并實(shí)例化這些組件到 SpringIOC 容器中葫笼,它有個(gè)配置屬性: basePackages深啤,也就是指定掃描的包,如果不知道路星,它會(huì)默認(rèn)掃描配置了該注解的類的包所在的路徑(包括子包)溯街。我們看 @SpringBootConfiguration 注解的源碼中有段代碼:

@AliasFor(
  annotation = ComponentScan.class,
  attribute = "basePackages"
)
String[] scanBasePackages() default {};

scanBasePackages 屬性,指定到了 @ComponentScan 注解的 basePackages 屬性洋丐,所有在 SpringBoot 中呈昔,我們同樣可以通過(guò) scanBasePackages 屬性指定包掃描的路徑(如部指定,會(huì)默認(rèn)掃描主程序類所在的包路徑以及子包下的類):

@SpringBootApplication(scanBasePackages = "com.seagetech.springbootdemo")

1.3 @EnableAutoConfigration

關(guān)于 SpringBoot 的運(yùn)作原理友绝,它的核心功能還是由 @EnableAutoConfigration 注解提供堤尾,所有把它放到最后來(lái)講,我們來(lái)看下它的源碼:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
 
    Class<?>[] exclude() default {};
 
    String[] excludeName() default {};
}

1.3.1 @AutoConfigurationPackage

這個(gè)注解的主要功能自動(dòng)配置包迁客,它會(huì)獲取主程序類所在的包路徑郭宝,并將包路徑(包括子包)下的所有組件注冊(cè)到 SpringIOC 容器中。

1.3.2 @Import({AutoConfigurationImportSelector.class})

@EnableAutoConfiguration 的關(guān)鍵功能也是這個(gè) @Import 導(dǎo)入的配置功能掷漱,使用 SpringFactoriesLoader.loadFactoryNames 方法來(lái)掃描具有 META-INF/spring.factories 文件的jar包粘室,我們看看在 spring-boot-autoconfigure-2.10.RELEASE.jar 包的 META-INF 下正好有個(gè) spring.factories 文件:

打開 spring.factories 文件,找到 org.springframework.boot.autoconfigure.EnableAutoConfiguration 指定的自動(dòng)配置類:

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
 
......
......
 
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\

每一個(gè) xxxAutoConfiguration 類都是容器中的一個(gè)組件切威,都加入到容器中育特,用它們來(lái)做自動(dòng)配置。

2 自動(dòng)配置分析

在上一節(jié)基礎(chǔ)上先朦,我們知道每一個(gè)自動(dòng)配置類進(jìn)行自動(dòng)配置功能缰冤,下面我們以 HttpEncodingAutoConfiguration 為例來(lái)分析:

2.1 配置參數(shù)

@ConfigurationProperties(
    prefix = "spring.http"http://①
)
public class HttpProperties {
    private boolean logRequestDetails;
    private final HttpProperties.Encoding encoding = new HttpProperties.Encoding();//②
 
    ......
 
    public static class Encoding {
        public static final Charset DEFAULT_CHARSET;
        private Charset charset;
        private Boolean force;
        private Boolean forceRequest;
        private Boolean forceResponse;
        private Map<Locale, Charset> mapping;
 
        public Encoding() {
            this.charset = DEFAULT_CHARSET;//②
        }
        ......
        static {
            DEFAULT_CHARSET = StandardCharsets.UTF_8;//②
        }
        ......
}

代碼解析:

  1. 在 application.properties 文件中配置的時(shí)候指定前綴是 spring.http;
  2. 指定默認(rèn)的編碼方式是 UTF-8喳魏,如果需要修改棉浸,可使用 spring.http.encoding.charset=編碼。

2.2 自動(dòng)配置類

@Configuration//①
@EnableConfigurationProperties({HttpProperties.class})//②
@ConditionalOnWebApplication(
    type = Type.SERVLET
)//③
@ConditionalOnClass({CharacterEncodingFilter.class})//④
@ConditionalOnProperty(
    prefix = "spring.http.encoding",
    value = {"enabled"},
    matchIfMissing = true
)//⑤
public class HttpEncodingAutoConfiguration {
    private final Encoding properties;
 
    public HttpEncodingAutoConfiguration(HttpProperties properties) {
        this.properties = properties.getEncoding();
    }
 
    @Bean
    @ConditionalOnMissingBean
    public CharacterEncodingFilter characterEncodingFilter() {
        ......
    }
    .......
}

源碼解析:

  1. 表示這是一個(gè)配置類刺彩,以前編寫的配置文件一樣迷郑,也可以給容器中添加組件。
  2. 啟動(dòng)指定類的 ConfigurationProperties 功能创倔,將配置文件中對(duì)應(yīng)的值和 HttpEncodingProperties 綁定起來(lái)嗡害;并把 HttpEncodingProperties 加入到 IOC 容器中;
  3. Spring 底層 @Conditional 注解(有關(guān) @Conditional 注解的詳解請(qǐng)參考后續(xù) Spring 注解相關(guān)文章)畦攘,根據(jù)不同的條件霸妹,如果滿足指定的條件,整個(gè)配置類里面的配置就會(huì)生效知押,該注解是判斷當(dāng)前應(yīng)用是否是web應(yīng)用叹螟,如果是鹃骂, HttpEncodingAutoConfiguration 配置類生效;
  4. 判斷當(dāng)前項(xiàng)目有沒有這個(gè)類 CharacterEncodingFilter罢绽;SpringMVC 中進(jìn)行亂碼解決的過(guò)濾器畏线,如果有則 HttpEncodingAutoConfiguration 配置生效;
  5. 判斷 application.properties 配置文件中是否存在 spring.http.encoding.enabled良价,如果不存在寝殴,判斷也是成立的,因?yàn)?matchIfMissing=true棚壁,即缺省時(shí)默認(rèn)為 true杯矩。

根據(jù)當(dāng)前不同的條件判斷,決定 HttpEncodingAutoConfiguration 這個(gè)配置類是否生效袖外?一但這個(gè)配置類生效史隆;這個(gè)配置類就會(huì)給容器中添加各種組件,這些組件的屬性是從對(duì)應(yīng)的 HttpEncodingProperties 類中獲取的曼验,這些類里面的每一個(gè)屬性又是和配置文件綁定的泌射。

2.3 @Conditional擴(kuò)展注解

除了以上解析到的注解,SpringBoot 還為我們提供了更多的有關(guān) @Conditional 的派生注解鬓照。它們的作用:必須是 @Conditional 指定的條件成立熔酷,才給容器中添加組件,配置配里面的所有內(nèi)容才生效:

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末豺裆,一起剝皮案震驚了整個(gè)濱河市拒秘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌臭猜,老刑警劉巖躺酒,帶你破解...
    沈念sama閱讀 216,919評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異蔑歌,居然都是意外死亡羹应,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門次屠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)园匹,“玉大人,你說(shuō)我怎么就攤上這事劫灶÷阄ィ” “怎么了?”我有些...
    開封第一講書人閱讀 163,316評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵本昏,是天一觀的道長(zhǎng)供汛。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么紊馏? 我笑而不...
    開封第一講書人閱讀 58,294評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮蒲犬,結(jié)果婚禮上朱监,老公的妹妹穿的比我還像新娘。我一直安慰自己原叮,他們只是感情好赫编,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,318評(píng)論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著奋隶,像睡著了一般擂送。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上唯欣,一...
    開封第一講書人閱讀 51,245評(píng)論 1 299
  • 那天嘹吨,我揣著相機(jī)與錄音,去河邊找鬼境氢。 笑死蟀拷,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的萍聊。 我是一名探鬼主播问芬,決...
    沈念sama閱讀 40,120評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼寿桨!你這毒婦竟也來(lái)了此衅?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,964評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤亭螟,失蹤者是張志新(化名)和其女友劉穎挡鞍,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體媒佣,經(jīng)...
    沈念sama閱讀 45,376評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡匕累,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,592評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了默伍。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片欢嘿。...
    茶點(diǎn)故事閱讀 39,764評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖也糊,靈堂內(nèi)的尸體忽然破棺而出炼蹦,到底是詐尸還是另有隱情,我是刑警寧澤狸剃,帶...
    沈念sama閱讀 35,460評(píng)論 5 344
  • 正文 年R本政府宣布掐隐,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏虑省。R本人自食惡果不足惜匿刮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,070評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望探颈。 院中可真熱鬧熟丸,春花似錦、人聲如沸伪节。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,697評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)怀大。三九已至纱兑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間化借,已是汗流浹背潜慎。 一陣腳步聲響...
    開封第一講書人閱讀 32,846評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留屏鳍,地道東北人勘纯。 一個(gè)月前我還...
    沈念sama閱讀 47,819評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像钓瞭,于是被迫代替她去往敵國(guó)和親驳遵。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,665評(píng)論 2 354

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

  • Spring Web MVC Spring Web MVC 是包含在 Spring 框架中的 Web 框架山涡,建立于...
    Hsinwong閱讀 22,394評(píng)論 1 92
  • https://github.com/cuzz1/springboot-learning 一堤结、Spring Boo...
    cuzz_閱讀 3,435評(píng)論 1 6
  • SpringMVC原理分析 Spring Boot學(xué)習(xí) 5、Hello World探究 1鸭丛、POM文件 1竞穷、父項(xiàng)目...
    jack_jerry閱讀 1,287評(píng)論 0 1
  • 對(duì)孩子,不再急躁鳞溉,耐心溝通瘾带,這是我的一小步。以往讓甜甜洗澡她不愿意熟菲,我會(huì)抓狂看政,今天就給她講為什么,她同意了抄罕。其實(shí)很...
    溫明春曉閱讀 62評(píng)論 0 0
  • 在我的一生當(dāng)中有多少花兒開放允蚣,為什么我挽起的,只是香呆贿。錯(cuò)過(guò)太多花開嚷兔,看過(guò)太多花落。別的什么也不能說(shuō),只能說(shuō)我曾愛過(guò)
    西決和南音的家閱讀 175評(píng)論 0 0