Spring Boot自動配置原理與實踐(一)

前言

  Spring Boot衆所周知是爲了簡化Spring的配置,省去XML的複雜化配置(雖然Spring官方推薦也使用Java配置)採用Java+Annotation方式配置。以下幾個問題是我剛開始接觸Spring Boot的時候常常遇到的一些疑問,如今總結出來但願能幫助到更多的人理解Spring Boot,固然這只是我的的理解,稍微顯得膚淺但易懂!當時我明白瞭如下幾個問題後,以爲Spring Boot也不過如此,沒有啥花裏胡哨的,但願能幫到你們!java

  本博文主要由兩個部分組成:第一篇經過源碼等形式介紹自動配置的原理與組成部分,第二篇經過實現自定義的starter實現自動配置加深對自動配置的理解。mysql

  注:創做不易啊,雖然寫只寫了5個小時左右,可是整個準備過程很早就開始了,固然這樣的記錄方式主要是督促本身對Spring Boot要深刻理解,順帶但願能幫到更多人redis

  問題一:那麼Spring Boot是靠什麼樣的手段簡化配置呢?spring

  從廣義上講,從軟件設計範式來講。Spring Boot提供一種「約定優於配置」來簡化配置;減小開發人員去開發不必的配置模塊時間,採用約定,而且對不符合約定的部分採用靈活配置知足便可!sql

  問題二:那麼什麼叫約定優於配置呢?服務器

  好比:咱們知道Spring Boot都是內嵌Tomcat、Jetty等服務器的,其中默認的Tomcat服務器,相應的監聽端口也默認綁定,這些默認即爲約定大部分狀況下咱們都會使用默認默認的Spring Boot自帶的內嵌的默認自動配置約定,不會大量去自定義配置,即這就是我理解的約定優於配置。那麼問題三來了。當咱們以爲不符合咱們實際生產環境的時候纔會去改變默認配置,那麼須要很麻煩的手段嗎?app

  問題三:更改默認的端口或者服務器(即上述提到的約定)須要很麻煩的代碼編寫嗎?甚至須要更改源碼嗎?spring-boot

  答案確定是否認的,只須要更改application.yml/properties(application-*.yml/properties)配置文件便可。固然也能夠從源頭上開始從新建立本身須要的模塊(準確來講能夠是starter),須要的註冊的配置類Bean。從而達到靈活的自動配置目的,這就是本篇要介紹的Spring Boot的自動配置。spa

  明白以上三個問題後,其實就能理解爲何Spring Boot相對於Spring更加靈活,方便,簡單的緣由了========>無非就是大量使用默認自動配置+靈活的自動配置設計

 

 


 

1、Spring Boot自動配置的原理介紹

 

  主要從spring boot是如何啓動後自動檢測自動配置與spring boot如何靈活處理自動配置兩個方面講解,前者主要分析源碼,從簡單的註解入手到最後的讀取spring.facoties文件。後者主要舉例RedisAutoConfiguration自動配置來講明spring boot對於相關的配置類bean的經過註解的靈活處理。

一、Spring Boot是如何發現自動配置的?

都知道Spring Boot程序入口處(xxxApplication.java)都會有 @SpringBootApplication 的註解。這個註解代表該程序是主程序入口,也是SpringBoot應用

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

查看 @SpringBootApplication 註解,不難發現是一個組合註解,包括了 @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan 。換句話說這三個註解能夠替代 @SpringBootApplication 

@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}
)}
)

其中註解  @EnableAutoConfiguration  就是實現SpringBoot自動配置的關鍵所在,值得注意的是@EnableXXXX註解並非SpringBoot特有,在Spring 3.x中就已經引出,好比:@EnableWebMvc用來啓用Spring MVC而不須要XML配置,@EnableTransactionManagement註釋用來聲明事務管理而不須要XML配置。如此可見,@EnableXXXX是用來開啓某一個特定的功能,從而使用Java來配置,省去XML配置。在SpringBoot中自動配置的開啓就是依靠註解  @EnableAutoConfiguration  ,繼續查看註解接口 @EnableAutoConfiguration 

@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 {};
}

其中關鍵是 @Import({AutoConfigurationImportSelector.class}) ,表示選擇全部SpringBoot的自動配置。廢話很少說,還得繼續看該選擇器源碼,其中最主要的方法是 getAutoConfiguationEntry 方法,該方法的主要功能是:

得到自動配置---->刪除重複自動配置----->刪除排除掉的自動配置---->刪除過濾掉的自動配置------>最終的自動配置

這裏咱們主要關注如何獲取自動配置,即圖中紅圈部分,即 getCandidateConfigurations 方法,該方法經過SpringFactoresLoader.loadFactoryNames方法讀取某個classpath下的文件獲取全部的configurations

注意到: No auto configuration classes found in META-INF/spring.factories..... ,它應該是從META-INF/spring.factories文件中下讀取configurations,咱們再往下查看方法 SpringFactoresLoader.loadFactoryNames ,該方法果真是讀取META-INF/spring.factories文件的,從中獲取自動配置鍵值對。

 

最後,咱們找到META-INF/spring.factories文件(位於spring-boot-autoconfigure下)

查看其內容:

...
# 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.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
...

 這時候才恍然大悟,原來如此。簡單總結起來就是,當SpringBoot應用啓動讀取自動配置以後,從上到下的相關調用棧(文字簡單描述)

  • @EnableAutoConfiguration開啓啓動配置功能
  • 經過@Import導入AutoConfigurationImportSelector選擇器發現(註冊)自動配置
  • getAutoConfigurationEntry方法獲取並處理全部的自動配置
  • SpringFactoriesLoader.loadFactoryNames讀取spring.factories文件並加載自動配置鍵值對。

 

 二、Spring Boot是如何靈活處理自動配置的?

 

  從spring.factories文件中咱們選取RedisAutoConfiguration舉例說明,至於爲何選這個呢?這是由於以前我有寫過關於Spring Boot整合Redis的相關博文。因此好入手!

首先,仍是同樣進入RedisAutoConfiguration(Idea大法好,點擊進入便可查看代碼)

源碼以下,主要關注如下幾個註解:

1)@ConditionalOnClass在當前classpath下是否存在指定類,如果則將當前的配置註冊到spring容器。可見其靈活性。相關的@ConditionalXXX註解是spring 4.x推出的靈活註冊bean的註解,稱之爲「條件註解」。有以下一系列條件註解:

  • @ConditionalOnMissingBean: 當spring容器中不存在指定的bean時,纔會註冊該Bean。
  • @ConditionalOnMissingClass:當不存在指定的類時,纔會註冊該bean。
  • @ConditionalOnProperty:根據配置文件中某個屬性來決定是否註冊該Bean,相對於其它條件註解較爲複雜。主要有如下兩種狀況應用:

  一種是@ConditionalOnProperty(prefix = 「mysql」, name = 「enable」, havingValue = 「true」)表示覺得mysql爲前綴的mysql.enable屬性若是爲空或者不是true時不會註冊指定Bean,只有mysql.enable屬性爲true時候纔會註冊該指定Bean

    一種是@ConditionalOnProperty(prefix = 「mysql」, name = 「enable」, havingValue = 「true」, matchIfMissing = true)matchIfMissing表示若是沒有以mysql爲前綴的enable屬性,則爲true表示符合條件,能夠註冊該指定Bean,默認爲false。

  • @ConditionalOnJava:是不是Java應用
  • @ConditionalOnWebApplication:是不是Web應用
  • @ConditionalOnExpression:根據表達式判斷是否註冊Bean

  ...........剩下的還有不少,在autoconfigure.condition包下...........

    

 

2)@EnableConfigurationProperties讓 @ConfigurationProperties 註解的properties類生效並使用。如讓RedisProperties生效並註冊到Spring容器中,而且使用!

3)@ConditionalOnMissingBean當前Spring容器中沒有該bean(name或者class指定),則註冊該bean到spring容器。

@Configuration
@ConditionalOnClass({RedisOperations.class})
@EnableConfigurationProperties({RedisProperties.class})
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {
    public RedisAutoConfiguration() {
    }

    @Bean
    @ConditionalOnMissingBean(
        name = {"redisTemplate"}
    )
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        RedisTemplate<Object, Object> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    @Bean
    @ConditionalOnMissingBean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
}

 因而可知Spring Boot對於自動配置的靈活性,能夠傳遞給咱們一種信息,Spring Boot是如何靈活處理自動配置的?

往大了說,就是你能夠選擇約定使用大量默認配置簡化開發過程,你也能夠自定義開發過程當中須要的自動配置。往細了說,就是經過各式各樣的條件註解註冊須要的Bean來實現靈活配置。

那麼實現一個自動配置有哪些重要的環節呢?

繼續往下看第二部分!

 

2、Spring Boot自動配置的重要組成部分

 

  上面只是對自動配置中幾個重要的註解作了簡單介紹,可是並無介紹要開發一個簡單的自動配置須要哪些環節(部分),一樣地咱們仍是以RedisAutoConfiguration與RabbitAutoConfiguration爲例,主要查看註解頭:

RedisAutoConfiguration:

@Configuration
@ConditionalOnClass({RedisOperations.class})
@EnableConfigurationProperties({RedisProperties.class})
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})

RabbitAutoConfiguration:

@Configuration
@ConditionalOnClass({RabbitTemplate.class, Channel.class})
@EnableConfigurationProperties({RabbitProperties.class})
@Import({RabbitAnnotationDrivenConfiguration.class})

你會發現一般的通常的AutoConfiguration都有@Configuration,@ConditionalOnClass,@EnableConfigurationProperties,@Import 註解:

  • @Configuration:天然沒必要多說,表示是配置類,是必須存在的!
  • @ConditionalOnClass:表示該配置依賴於某些Class是否在ClassPath中,若是不存在那麼這個配置類是毫無心義的。也能夠說是該class在配置類中充當重要的角色,是必須存在的!。在後面的自定義spring boot starter中咱們能夠簡單統稱爲服務類
  • @EnableConfigurationProperties:讀取配置文件,須要讓相關的Properties生效,每一個自動配置都離不開properties屬性,是必須存在的!
  • @Import:導入配置類中須要用到的bean,一般是一些配置類。表示此時的自動配置類須要基於一些配置類而實現自動配置功能。固然不是必須的,有些是沒有這個註解的,是不須要基於某些配置類的!

那麼,能夠簡單總結獲得一個自動配置類主要有如下幾部分組成(單純是根據我的理解總結出來的,沒有官方說明)

  • properties bean配置屬性:用來讀取spring配置文件中的屬性,@EnableConfigurationProperties與@ConfigurationProperties結合使用,具體請看下一篇實踐stater例子。
  • 該配置類依賴的一些Class,使用@ConditionalOnClass判斷;
  • 該配置類依賴的一些配置Bean,使用@Import導入。
  • 可能還有加載順序的控制,如@AutoConfigureAfter,@AutoConfigureOrder等
  • 一些Bean的加載,每每經過方法返回Object,加@Bean以及一些條件註解來實現

========================================未完待續(下一篇補充自定義starter會涉及自動配置組成部分)==========================================

相關文章
相關標籤/搜索