本文如有任何紕漏、錯誤,還請不吝指出!html
注:本文提到的Spring容器或者Bean容器,或者Spring Bean容器,都是指同一個事情,那就是代指BeanFactory。關於BeanFactory,後面有機會會再說下。java
幾年前接觸過SpringBoot
,跑過Demo,當時剛入行,連Spring
都沒搞明白,更別說SpringBoot
了,就是以爲,哇塞,好厲害,而後一臉懵逼。web
工做中沒有用到,又沒有去主動學習它。以爲很恐懼,這麼厲害的東西,確定是很深奧,很複雜吧!。spring
這種心理也形成了必定程度上,對某些事物的望而卻步,其實只要向前邁出了步子,一步步慢慢來,才發現,之前的那種恐懼心理是多麼的幼稚、膽怯、好笑!編程
SpringBoot
自己並無多大的花樣,全部的知識點其實還都是Spring Framework
的。數組
在SpringBoot
以前,使用Spring
能夠說,並非那麼的方便,其實也主要是在搭建一個基於Spring Framework
的項目時這個困擾。Spring
自己的配置,整合SpringMVC
,整合Struts2
,整合mybatis
,整合Hibernate
,整合SpringSecurity
等等,若是是Web
應用還有個web.xml
須要配置。什麼都要你去配置一下,第一步就是去找怎麼配置,記住這麼配置是如何配的,其實並無切實的意義,畢竟又不是常常須要去搭建一個項目。正由於不常這麼配置,不值得記住如何配置,致使每次實際用到時,很麻煩,處處去找如何配置的XML
配置文件。mybatis
SpringBoot
的出現,正是爲了解決這個問題,讓你能夠不去作任何配置的狀況下,運行一個Spring
應用,或者Web
應用。須要作的僅僅是引入SpringBoot
的maven
或者gradle
依賴便可。app
SpringBoot
要作的就是,讓你開箱即用!框架
將使用Spring
的成本降到儘量低,爲用戶帶來了極大的便利。maven
固然SpringBoot
作的也不只僅只有這些,不過這裏僅討論下它的自動化配置,不討論其餘的。
若是瞭解Spring
對@Configuration
這個註解的處理過程,會更加容易理解SpringBoot
的自動化配置。
若是沒有,能夠參考這篇解釋
這第一件事,就是找門,門都找不到,那不是沒門
嗎!
既然想找門,就得從程序的啓動入口去找,任何SpringBoot
程序都會用到這麼兩個
@SpringBootApplication public class Application{ public static void main(String[] args){ SpringApplication.run(Application.class, args); } }
看到這個後,若是好奇其實現,應該會首先查看SpringApplication#run
方法,實際調用的是這個重載的靜態方法。
// org.springframework.boot.SpringApplication public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); } public ConfigurableApplicationContext run(String... args) { ···省略··· try { ···省略··· context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context); // 真正啓動應用程序上下文前的一些準備動做 // 這裏會去將Application.class,註冊到org.springframework.context.annotation.AnnotatedBeanDefinitionReader // 也就是去把Application.class註冊成一個BeanDefinition實例 // 不過Application必需要是一個@Component prepareContext(context, environment, listeners, applicationArguments, printedBanner); // 刷新上下文,這個過程當中主要就是Bean的實例化和屬性賦值綁定 // 若是是Web環境,涉及到Web相關的一些東西,可是本質上仍是各類Bean的實例化 // 和Bean之間依賴關係的處理,代理Bean的生成(涉及到AspectJ的Advice處理)等等 refreshContext(context); } return context; }
BeanDefinition
實例有了,就能去啓動上下文,處理Bean
容器了,容器啓動完成後,整個SpringBoot
程序基本啓動完成!
等等! 是否是少了什麼?
這裏就註冊了一個BeanDefinition
,那麼多@Component
、@Configuration
、@Service
、@Controller
怎麼辦?
先留着疑問,且待後面解答!
林盡水源,便得一山,山有小口,彷彿如有光。
注意到上面的準備階段,被註冊的Bean
必需要被@Component
註解,如今Application.class
僅有一個註解@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) }) public @interface SpringBootApplication { ···省略··· }
挨個查看幾個註解的定義後,會發現@SpringBootConfiguration
被@Component
所註解,這就解釋了爲何被@SpringBootApplication
所註解的Application.class
類能夠被做爲一個Bean
註冊到BeanDefinitionRegistry
。
除此以外,還有個使人驚喜的名稱:@EnableAutoConfiguration
,看名字就看出來它是作啥的了。
沒錯,SpringBoot
的所謂自動配置,就是它在起做用。
這裏暫時不討論@ComponentScan
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { ···省略··· }
這個註解又使用了兩個註解,分別是@AutoConfigurationPackage
和@Import
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage { }
能夠發現,這兩個註解最終都指向了同一個註解@Import
@Import
是Annotation
時代的<import/>
,做用是向BeanDefinitionRegistry
註冊Bean
的。
因此@EnableAutoConfiguration
這個註解一共註冊了兩個Bean
,分別是:AutoConfigurationPackages.Registrar.class
和AutoConfigurationImportSelector.class
先說說AutoConfigurationPackages.Registrar
的用處
這個類就幹一個事,註冊一個Bean
,這個Bean
就是org.springframework.boot.autoconfigure.AutoConfigurationPackages.BasePackages
,它有一個參數,這個參數是使用了@AutoConfigurationPackage
這個註解的類所在的包路徑。有了這個包路徑後,就會掃描這個包下的全部class
文件,而後將須要註冊到Bean
容器的類,給註冊進去。
具體能夠參見這裏
org.springframework.boot.autoconfigure.AutoConfigurationPackages#register
這裏就解釋了爲何有時候主配置類放的位置不對,致使有些類沒被Spring容器歸入管理
經歷了一番折騰,就要進入桃花源了
AutoConfigurationImportSelector
就是那最後一層窗戶紙
// org.springframework.boot.autoconfigure.AutoConfigurationImportSelector public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } // 爲了加載spring-boot-autoconfiguration包下的配置文件META-INF/spring-autoconfigure-metadata.properties // 這裏配置的主要是一些SpringBoot啓動時用到的一些@ConditionOnClass的配置 AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); // 這裏的AutoConfigurationEntry,就包含了全部的導入的須要被實例化的Bean AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata); // 返回這些被導入Bean的類全限定名數組 return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } ··· 省略 ··· // 獲取全部的須要導入的Bean,這些被導入的Bean就是各個組件須要自動化配置的啓動點 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); ··· 省略 ··· return new AutoConfigurationEntry(configurations, exclusions); } protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { // 使用SpringFactoriesLoader#loadFactoryNames方法,從全部的包及classpath目錄下, // 查找META-INF/spring.factories文件,且名稱爲org.springframework.boot.autoconfigure.EnableAutoConfiguration的配置 List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you " + "are using a custom packaging, make sure that file is correct."); return configurations; } }
最終這些配置在META-INF/spring.factories
中須要自動配置的類,就會被註冊到Spring Bean
容器中,而後被實例化,調用初始化方法等!
這些作自動配置的類,基本都會經過實現各類Aware
接口,獲取到Spring Framework
中的BeanFactory
,ApplicationContext
等等全部的一些框架內的組件,用於後面使用。
以後完成本身框架的一些初始化工做,主要就是將原先和Spring
整合時,須要手動配置的那些,在這裏經過編程式的方式
,給作了。
這樣,就完成了所謂的自動化配置,全程不須要咱們的任何參與。
PS: 這個僅僅是作了一個通用的配置,讓用戶能夠在不作任何配置的狀況下能直接使用。可是一些個性化的配置,仍是須要經過配置文件的方式,寫入配置。對於這部分配置的處理,
SpringBoot
也都給攬下了
總體看下來,SpringBoot
乾的這些,更像是一個體力活,將於Spring
集成的那麼多三方庫的配置,使用代碼所有實現了一遍,其使用的核心功能,依然是Spring Framework
的那些東西。
可是這個體力活是爲使用者省下的,也讓Spring Framework
更加的具備活力了。
同時微服務的興起,也是Spring
爲了順勢而必須做出的一個改變,也能夠說爲Spring
在微服務領域立下了汗馬功勞!