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更加靈活,方便,簡單的緣由了========>無非就是大量使用默認自動配置+靈活的自動配置設計
主要從spring boot是如何啓動後自動檢測自動配置與spring boot如何靈活處理自動配置兩個方面講解,前者主要分析源碼,從簡單的註解入手到最後的讀取spring.facoties文件。後者主要舉例RedisAutoConfiguration自動配置來講明spring boot對於相關的配置類bean的經過註解的靈活處理。
都知道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應用啓動讀取自動配置以後,從上到下的相關調用棧(文字簡單描述)
從spring.factories文件中咱們選取RedisAutoConfiguration舉例說明,至於爲何選這個呢?這是由於以前我有寫過關於Spring Boot整合Redis的相關博文。因此好入手!
首先,仍是同樣進入RedisAutoConfiguration(Idea大法好,點擊進入便可查看代碼)
源碼以下,主要關注如下幾個註解:
1)@ConditionalOnClass:在當前classpath下是否存在指定類,如果則將當前的配置註冊到spring容器。可見其靈活性。相關的@ConditionalXXX註解是spring 4.x推出的靈活註冊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。
...........剩下的還有不少,在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來實現靈活配置。
那麼實現一個自動配置有哪些重要的環節呢?
繼續往下看第二部分!
上面只是對自動配置中幾個重要的註解作了簡單介紹,可是並無介紹要開發一個簡單的自動配置須要哪些環節(部分),一樣地咱們仍是以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 註解:
那麼,能夠簡單總結獲得一個自動配置類主要有如下幾部分組成(單純是根據我的理解總結出來的,沒有官方說明)
========================================未完待續(下一篇補充自定義starter會涉及自動配置組成部分)==========================================