咱們知道 Spring Boot 能快速的搭建起一個應用,簡化了大量的配置過程,那到底有多"簡"呢?
咱們經過一個例子來講明,平時咱們經過 Spring
和Spring MVC
搭建一個helloword
的 Web 應用,須要作如下工做:web
Spring
、Spring MVC
框架的依賴,同時還須要考慮這些不一樣的框架的不一樣版本是否存在不兼容的問題。而使用 Spring Boot 搭建的話,須要作如下工做:redis
pom.xml
,添加 Web 起步依賴。單從步驟數量上看就知道經過 Spring、Spring MVC 搭建比經過 Spring Boot
搭建更復雜,須要編寫大量的配置,這還僅僅是在不多框架和 Spring 整合狀況下,若是須要將多個第三方框架和 Spring 整合,恐怕就要陷入"配置地獄"了,此外這些配置基本都是固化的,也就是搭建新的應用,你仍然須要再次編寫相同的配置信息,特別是在微服務這麼火的當下,一個應用可能由十幾個甚至幾十個小型服務無組成,若是每一個小型服務都重複的作着這些配置工做......。spring
那有沒有什麼辦法解決這個局面呢?答案是有的,那就是使用 Spring Boot
,上從上面的例子就能夠發現,使用 Spring Boot
的最大優勢就是減小了配置的工做,那麼是否是說使用 Spring Boot
就不須要這些配置過程了?固然不是,而是 Spring Boot
幫咱們把這些工做給作了。json
那 Spring Boot 是如何幫咱們把這些配置工做給作了呢?這就是本文須要探討的問題了,在探討以前,咱們須要瞭解兩個概念起步依賴和自動配置,這裏暫且知道這兩個東西是Spring Boot
的核心、是Spring Boot
的精華所在、是咱們不須要再進行大量配置工做的緣由所在就好了。tomcat
起步依賴說白了就是Spring Boot
經過對經常使用的依賴進行再一次封裝,例如咱們平時須要搭建一個Web
應用的時候,通常都會導入如下幾個依賴:springboot
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
複製代碼
也就是須要將spring-web
和spring mvc
分別導入,而使用Spring Boot
的話只須要導入一個:bash
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
複製代碼
也就是隻須要導入一個名爲web
的起步依賴便可,咱們點spring-boot-starter-web
進去能夠看到,其實這個起步依賴集成了經常使用的 web 依賴,以下:mvc
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.1.4.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
<version>2.1.4.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.1.4.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.16.Final</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.1.6.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.6.RELEASE</version>
<scope>compile</scope>
</dependency>
複製代碼
也就是前面所說的,Spring Boot的起步依賴說白了就是對經常使用的依賴進行再一次封裝,方便咱們引入,簡化了 pom.xml 配置,可是更重要的是將依賴的管理交給了 Spring Boot,咱們無需關注不一樣的依賴的不一樣版本是否存在衝突的問題,Spring Boot 都幫咱們考慮好了,咱們拿來用便可!app
在使用 Spring Boot 的起步依賴以前,咱們須要在pom.xml
中添加配置:框架
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
複製代碼
即讓pom.xml
繼承 Spring Boot 的pom.xml
,而 Spring Boot 的pom.xml
裏面定義了經常使用的框架的依賴以及相應的版本號。
總結一下 Spring Boot 的起步依賴的優勢:
pom.xml
配置。若是將開發一個應用比喻成裝修房子的過程,那麼 Spring Boot 就像是一個全能型公司同樣存在,而起步依賴能夠比喻成購買裝修用品的過程,自動配置比喻成用裝修用品進行裝修的過程。
咱們能夠經過 Spring Boot 的起步依賴獲取到你想要的塗料、瓷磚、裝飾品等, Spring Boot 公司會根據最佳的組合將這些裝修用品打包好給咱們,咱們無需考慮各類裝修用品是否搭配、是否衝突等問題。
經過起步依賴咱們獲取到了想要的裝修用品,那接下來咱們須要作的就是進行裝修了,前面咱們說過 Spring Boot 就像一個全能型公司同樣,因此咱們在他那裏購買裝修用品以後,他不只將裝修用品送上門還會幫咱們完成裝修(自動配置),讓咱們享受一站式的服務,從購買裝飾品(起步依賴)到裝修完成(自動配置)都不用咱們考慮,咱們只須要在裝修完成以後入住(編寫本身的業務邏輯代碼)便可。
說了這麼多,咱們還不知道Spring Boot
是如何完成自動配置的,接下來咱們來分析 Spring Boot 神奇的自動配置!
首先咱們知道 Spring Boot 啓動須要有一個啓動引導類,這個類除了是應用的入口以外,還發揮着配置的 Spring Boot 的重要做用。下面是一個簡單的啓動引導類:
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
複製代碼
咱們發現有一個名爲@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}
)}
)
複製代碼
這裏簡要的說下@SpringBootConfiguration
和@ComponentScan
註解。前者實質爲@Configuration
註解,這個註解相比你們都接觸過,也就是起到聲明這個類爲配置類的做用,然後者起到開啓自動掃描組件的做用。
這裏須要重點分析的是@EnableAutoConfiguration
這個註解,這個註解的做用是開啓 Spring Boot 的自動配置功能,咱們來分析一下它是如何開啓的,點擊進去能夠看到:
@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 {};
}
複製代碼
@EnableAutoConfiguration
這個註解一樣發揮着多個註解的功能,咱們重點分析@Import({AutoConfigurationImportSelector.class})
這個註解,咱們知道@import
的做用是將組件添加到 Spring 容器中,而在這裏便是將AutoConfigurationImportSelector
這個組件添加到 Spring 容器中。
咱們進一步分析AutoConfigurationImportSelector
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.filter(configurations, autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
}
複製代碼
有一個名爲getAutoConfigurationEntry
的方法,這個方法發揮的做用是掃描ClassPath
下的全部jar
包的spring.factories
文件,將spring.factories
文件key
爲EnableAutoConfiguration
的全部值取出,而後這些值實際上是類的全限定名,也就是自動配置類的全限定名,而後 Spring Boot 經過這些全限定名進行類加載(反射),將這些自動配置類添加到 Spring 容器中。
那這些自動配置類有哪些?發揮什麼做用呢?咱們接着往下看,咱們找到一個名爲spring-boot-autoconfigure-2.1.4.RELEASE.jar
的 jar 包,打開它的spring.factories
文件,發現這個文件有key
爲EnableAutoConfiguration
的鍵值對
# 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,\
......
複製代碼
也就是這個jar
包有自動配置類,能夠發現這些自動配置配都是以xxxAutoConfiguration
的命名規則來取名的,這些自動配置類包含我了們經常使用的框架的自動配置類,好比aop
、elasticsearch
、redis
和web
等等,基本能知足咱們平常開發的需求。
那這些自動配置類又是如何發揮配置做用的呢,咱們取一個較爲簡單的配置類進行分析,名爲HttpEncodingAutoConfiguration
,它的部分代碼以下:
@Configuration //聲明這個類爲配置類
@EnableConfigurationProperties({HttpProperties.class}) //開啓ConfigurationProperties功能,同時將配置文件和HttpProperties.class綁定起來
@ConditionalOnWebApplication( //只有在web應用下自動配置類才生效
type = Type.SERVLET
)
@ConditionalOnClass({CharacterEncodingFilter.class}) //只有存在CharacterEncodingFilter.class狀況下 自動配置類才生效
@ConditionalOnProperty( //判斷配置文件是否存在某個配置spring.http.encoding,若是存在其值爲enabled才生效,若是不存在這個配置類也生效。
prefix = "spring.http.encoding",
value = {"enabled"},
matchIfMissing = true
)
//將字符編碼過濾器組件添加到 Spring 容器中
@Bean
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.RESPONSE));
return filter;
}
@Bean
public HttpEncodingAutoConfiguration.LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
return new HttpEncodingAutoConfiguration.LocaleCharsetMappingsCustomizer(this.properties);
}
複製代碼
首先它一樣有許多註解,咱們一個一個分析:
ConfigurationProperties
功能,也就是將配置文件和HttpProperties.class
這個類綁定起來,將配置文件的相應的值和HttpProperties.class
的變量關聯起來,能夠點擊HttpProperties.class
進去看看,下面截取了部分代碼進行分析:@ConfigurationProperties(
prefix = "spring.http"
)
public static final Charset DEFAULT_CHARSET;
private Charset charset;
private Boolean force;
private Boolean forceRequest;
private Boolean forceResponse;
private Map<Locale, Charset> mapping;
複製代碼
經過ConfigurationProperties
指定前綴,將配置文件application.properties
前綴爲spring.http
的值和HttpProperties.class
的變量關聯起來,經過類的變量能夠發現,咱們能夠設置的屬性是charset
、force
、forceRequest
、forceResponse
和mapping
。也就是咱們除了使用 Spring Boot 默認提供的配置信息以外,咱們還能夠經過配置文件指定配置信息。
CharacterEncodingFilter
這個類的狀況下自動配置類纔會生效。能夠發現後面幾個註解都是ConditionalXXXX
的命名規則,這些註解是 Spring 制定的條件註解,只有在符合條件的狀況下自動配置類纔會生效。
接下來的characterEncodingFilter
方法,建立一個CharacterEncodingFilter
的對象,也就是字符編碼過濾器,同時設置相關屬性,而後將對象返回,經過@Bean
註解,將返回的對象添加到 Spring 容器中。這樣字符編碼過濾器組件配置好了,而平時的話,咱們須要在 web.xml 進行以下配置:
<filter>
<filter-name>springUtf8Encoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>springUtf8Encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
複製代碼
到這裏是否是感覺到了 Spring Boot 自動配置帶來的好處了?
接下來的localeCharsetMappingsCustomizer
方法同理,就不分析了。
最後咱們用一句話總結一下 Spring Boot 的自動配置:Spring Boot 啓動的時候,會掃描ClassPath
下的全部 jar 包,將其spring.factories
文件中key
爲EnableAutoConfiguration
的全部值取出,而後這些值實際上是類的全限定名,也就是自動配置類的全限定名,而後 Spring Boot 經過這些全限定名進行類加載(反射),將這些自動配置類添加到 Spring 容器中。這些自動配置類根據不一樣的條件(@ConditionalXXX)決定自動配置類是否生效,生效的話自動配置類會將相關組件添加到 Spring 容器中,也就不用咱們再進行配置!
看了網上挺多的文章都說的不是很清楚,因此按照本身的理解寫下了這篇總結,有誤之處歡迎指出。
原文地址:ddnd.cn/2019/05/10/…