SpringBoot系列二:SpringBoot自動配置原理
1 SpringBoot運作原理
上一章中我們提到主程式類的註解 @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 { @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 {}; }
最主要的還是三個配置 @SpringBootConfiguration、@EnableAutoConfigration、@ComponentScan 三個註解,下面我們來一一分析。
1.1 @SpringBootConfiguration
檢視@SpringBootConfiguration原始碼,其實它也就是@Configuration註解:
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration public @interface SpringBootConfiguration { }
對於 @Configuration,我們並不陌生,它就是 JavaConfig 形式的 SpringIOC/">IOC 容器的配置類,也是 SpringBoot 推薦使用配置形式,所以主程式類標註了 @SpringBootConfiguration 註解,其本身也就是一個配置類。更多有關 JavaConfig 形式的配置以及有關它和 XML 形式的配置的區別與聯絡,請參考後續有關 Spring 註解版相關文章。
1.2 @ComponentScan
@ComponentScan 註解在 Spring 的註解中也起到到相當重要的作用,它可以自定義 Spring 掃描的包,也就是它預設會掃描標註了 @Controller、@Service、@Component 以及 @Repository 註解的類,並例項化這些元件到 SpringIOC 容器中,它有個配置屬性: basePackages,也就是指定掃描的包,如果不知道,它會預設掃描配置了該註解的類的包所在的路徑(包括子包)。我們看 @SpringBootConfiguration 註解的原始碼中有段程式碼:
@AliasFor( annotation = ComponentScan.class, attribute = "basePackages" ) String[] scanBasePackages() default {};
scanBasePackages 屬性,指定到了 @ComponentScan 註解的 basePackages 屬性,所有在 SpringBoot 中,我們同樣可以通過 scanBasePackages 屬性指定包掃描的路徑(如部指定,會預設掃描主程式類所在的包路徑以及子包下的類):
@SpringBootApplication(scanBasePackages = "com.seagetech.springbootdemo")
1.3 @EnableAutoConfigration
關於 SpringBoot 的運作原理,它的核心功能還是由 @EnableAutoConfigration 註解提供,所有把它放到最後來講,我們來看下它的原始碼:
@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
這個註解的主要功能自動配置包,它會獲取主程式類所在的包路徑,並將包路徑(包括子包)下的所有元件註冊到 SpringIOC 容器中。
1.3.2 @Import({AutoConfigurationImportSelector.class})
@EnableAutoConfiguration 的關鍵功能也是這個 @Import 匯入的配置功能,使用 SpringFactoriesLoader.loadFactoryNames 方法來掃描具有 META-INF/spring.factories 檔案的jar包,我們看看在 spring-boot-autoconfigure-2.10.RELEASE.jar 包的 META-INF 下正好有個 spring.factories 檔案:
開啟 spring.factories 檔案,找到 org.springframework.boot.autoconfigure.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.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.Socket/">WebSocketReactiveAutoConfiguration,\
每一個 xxxAutoConfiguration 類都是容器中的一個元件,都加入到容器中,用它們來做自動配置。
2 自動配置分析
在上一節基礎上,我們知道每一個自動配置類進行自動配置功能,下面我們以 HttpEncodingAutoConfiguration 為例來分析:
2.1 配置引數
@ConfigurationProperties( prefix = "spring.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;//② } ...... }
程式碼解析:
- 在 application.properties 檔案中配置的時候指定字首是 spring.http;
- 指定預設的編碼方式是 UTF-8,如果需要修改,可使用 spring.http.encoding.charset=編碼。
2.2 自動配置類
@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() { ...... } ....... }
原始碼解析:
- 表示這是一個配置類,以前編寫的配置檔案一樣,也可以給容器中新增元件。
- 啟動指定類的 ConfigurationProperties 功能,將配置檔案中對應的值和 HttpEncodingProperties 繫結起來;並把 HttpEncodingProperties 加入到 IOC 容器中;
- Spring 底層 @Conditional 註解(有關 @Conditional 註解的詳解請參考後續 Spring 註解相關文章),根據不同的條件,如果滿足指定的條件,整個配置類裡面的配置就會生效,該註解是判斷當前應用是否是web應用,如果是, HttpEncodingAutoConfiguration 配置類生效;
- 判斷當前專案有沒有這個類 CharacterEncodingFilter;SpringMVC 中進行亂碼解決的過濾器,如果有則 HttpEncodingAutoConfiguration 配置生效;
- 判斷 application.properties 配置檔案中是否存在 spring.http.encoding.enabled,如果不存在,判斷也是成立的,因為 matchIfMissing=true,即預設時預設為 true。
根據當前不同的條件判斷,決定 HttpEncodingAutoConfiguration 這個配置類是否生效?一但這個配置類生效;這個配置類就會給容器中新增各種元件,這些元件的屬性是從對應的 HttpEncodingProperties 類中獲取的,這些類裡面的每一個屬性又是和配置檔案繫結的。
2.3@Conditional擴充套件註解
除了以上解析到的註解,SpringBoot 還為我們提供了更多的有關 @Conditional 的派生註解。它們的作用:必須是 @Conditional 指定的條件成立,才給容器中新增元件,配置配裡面的所有內容才生效: