Spring Boot自動配置

Spring boot 特性:

1、能夠快速創(chuàng)建基于Spring的應用程序; 2、能夠直接使用java main方法啟動內(nèi)嵌的Tomcat,Jetty服務器運行Spring boot程序,不需要部署war包文件; 3、提供約定的starter POM來簡化Maven配置,讓Maven的配置變得簡單; 4、根據(jù)項目的Maven依賴配置,Spring boot自動配置Spring、Spring mvc等; 5、提供了程序的健康檢查等功能; 6、基本可以完全不使用XML配置文件,采用注解配置。

Spring boot四大核心:

1、自動配置:針對很多Spring應用程序和常見的應用功能,Spring Boot能自動提供相關(guān)配置; 2、起步依賴:告訴Spring Boot需要什么功能,它就能引入需要的依賴庫; 3、Actuator:讓你能夠深入運行中的應用程序,一探Spring Boot程序的內(nèi)部信息; 4、命令行界面:這是Spring Boot的可選特性,主要針對Groovy語言使用。

Spring boot Actuator 見 Spring Boot微服務監(jiān)控告警簡單介紹

1.1 自動配置簡介

SpringBoot的自動配置就是當Spring容器啟動后,一些自動配置類(只是自動配置類,并不是當前的組件配置到IOC容器中,自動配置類通過@Conditional注解來按需配置)就自動裝配的IOC容器中,不需要我們手動注入,從而簡化了開發(fā),省去繁瑣的配置。

一個SpringBoot工程如果想運行成功,就必須有一個主程序類,所有 Spring Boot 項目的主啟動程序類上都使用了一個 @SpringBootApplication 注解,該注解是 Spring Boot 中最重要的注解之一 ,也是 Spring Boot 實現(xiàn)自動化配置的關(guān)鍵。

當我們進入@SpringBootApplication注解的源碼當中,可以發(fā)現(xiàn)它是一個復合注解,它由@SpringBootConfiguration@EnableAutoConfiguration、@ComponentScan這三個注解組成。

  • @EnableAutoConfiguration:啟用 SpringBoot 的自動配置機制。
  • @SpringBootConfiguration(實際是:@Configuration):聲明當前類是一個配置類,然后Spring會自動掃描到添加了@Configuration的類,并且讀取其中的配置信息,允許在上下文中注冊額外的 bean 或?qū)肫渌渲妙悾?/li>
  • @ComponentScan: 掃描被@Component (@Service,@Controller)注解的 bean,注解默認會掃描啟動類所在的包下所有的類 ,可以自定義不掃描某些 bean

1.2 自動配置原理

@EnableAutoConfiguration會開啟SpringBoot的自動配置,并且根據(jù)你引入的依賴來生效對應的默認配置。那么問題來了:

  • 這些默認配置是在哪里定義的呢?
  • 為何依賴引入就會觸發(fā)配置呢?

Spring Factories 機制

Spring Boot 的自動配置是基于 Spring Factories 機制實現(xiàn)的。
Spring Factories 機制是 Spring Boot 中的一種服務發(fā)現(xiàn)機制,這種擴展機制與 Java SPI 機制十分相似。Spring Boot 會自動掃描所有 Jar 包類路徑下 META-INF/spring.factories 文件,并讀取其中的內(nèi)容,進行實例化,這種機制也是 Spring Boot Starter 的基礎。

在 spring-boot-autoconfigure-xxx.jar 類路徑下的 META-INF/spring.factories 中設置了 Spring Boot 自動配置的內(nèi)容,其中定義了大量自動配置類,幾乎涵蓋了現(xiàn)在主流的開源框架,例如:redis、amqp、jdbc、jackson、mongodb、jpa、elasticsearch等等,如下圖

image.png

SpringBoot內(nèi)部對大量的第三方庫或Spring內(nèi)部庫進行了默認配置,這些配置是否生效,取決于我們是否引入了對應庫所需的依賴,如果有那么默認配置就會生效(通過@ConditionalOnBean注解實現(xiàn),@ConditionalOnBean的作用是 容器中存在指定的 Bean 時才生效)。

所以,我們使用SpringBoot構(gòu)建一個項目,只需要引入所需框架的依賴,配置就可以交給SpringBoot處理了。除非你不希望使用SpringBoot的默認配置,它也提供了自定義配置的入口。

# 1.3 自動配置加載過程

@EnableAutoConfiguration 注解 用于開啟 Spring Boot 的自動配置功能, 它使用 Spring 框架提供的 @Import 注解通過
AutoConfigurationImportSelector類(選擇器)給容器中導入自動配置組件。

image.png

1.3.1 @AutoConfigurationPackage注解

默認將主配置類( @SpringBootApplication )所在的包及其子包里面的所有組件掃描到IOC容器中。

在@AutoConfigurationPackage注解中存在一個@Import(AutoConfigurationPackages.Registrar.class)注解,自動配置包就是通過這個Registrar類的方法來完成的。

1.3.2 AutoConfigurationImportSelector 類

AutoConfigurationImportSelector 加載所有符合條件的META-INF/spring.factorie類

AutoConfigurationImportSelector 接口何時被執(zhí)行?

SpringBoot 啟動時會使用 ConfigurationClassParser 來解析被 @Configuration 標識的配置類, 然后再處理這個類內(nèi)部被其他注解修飾的情況, 比如 @Import 注解, @ComponentScan 注解,@Bean 注解等

如果發(fā)現(xiàn)注解中存在 @Import(ImportSelector) 的情況下,就會創(chuàng)建一個相應的 ImportSelector 對象,并調(diào)用其 process 方法

process() 方法,該方法通過調(diào)用 getAutoConfigurationEntry() 方法讀取 spring.factories 文件中的內(nèi)容,獲得自動配置類的集合,代碼如下

public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
    Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, () -> {
        return String.format("Only %s implementations are supported, got %s", AutoConfigurationImportSelector.class.getSimpleName(), deferredImportSelector.getClass().getName());
    });
    //拿到 META-INF/spring.factories中的EnableAutoConfiguration,并做排除、過濾處理
    //AutoConfigurationEntry里有需要引入配置類和排除掉的配置類,最終只要返回需要配置的配置類
    AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector)deferredImportSelector).getAutoConfigurationEntry(annotationMetadata);
    //加入緩存,List<AutoConfigurationEntry>類型
    this.autoConfigurationEntries.add(autoConfigurationEntry);
    Iterator var4 = autoConfigurationEntry.getConfigurations().iterator();

    while(var4.hasNext()) {
        String importClassName = (String)var4.next();
        //加入緩存,Map<String, AnnotationMetadata>類型
        this.entries.putIfAbsent(importClassName, annotationMetadata);
    }
}

getAutoConfigurationEntry() 方法通過調(diào)用 getCandidateConfigurations() 方法來獲取自動配置類的完全限定名,并在經(jīng)過排除、過濾等處理后,將其緩存到成員變量中,具體代碼如下。

protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if (!this.isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    } else {
        //獲取注解元數(shù)據(jù)中的屬性設置
        AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
        //獲取自動配置類
        //通過getCandidateConfigurations得到待配置的class的類名集合,這個集合就是所有需要進行自動配置的類名,而是是否配置的關(guān)鍵在于META-INF/spring.factories文件中是否存在該配置信息
        List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
        //刪除list 集合中重復的配置類
        configurations = this.removeDuplicates(configurations);
        //獲取排除導入的配置類
        Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
        //檢查是否還存在排除配置類
        this.checkExcludedClasses(configurations, exclusions);
        //刪除排除的配置類
        configurations.removeAll(exclusions);
        //獲取過濾器,過濾配置類
        configurations = this.getConfigurationClassFilter().filter(configurations);
        //觸發(fā)自動化配置導入事件
        this.fireAutoConfigurationImportEvents(configurations, exclusions);
        return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
    }
}

通過getCandidateConfigurations得到待配置的class的類名集合,這個集合就是所有需要進行自動配置的類名,而是是否配置的關(guān)鍵在于META-INF/spring.factories文件中是否存在該配置信息

在 getCandidateConfigurations() 方法中,根據(jù) Spring Factories 機制調(diào)用 SpringFactoriesLoader 的 loadFactoryNames() 方法(后面Spring Factories 實現(xiàn)原理中有介紹這個方法),根據(jù) EnableAutoConfiguration.class (自動配置接口)獲取其實現(xiàn)類(自動配置類)的類名的集合,如下圖。

image.png
image.png

2 Spring Factories 原理

2.1 spring.factories

spring.factories 文件本質(zhì)上與 properties 文件相似,其中包含一組或多組鍵值對(key=vlaue),其中,key 的取值為接口的完全限定名;value 的取值為接口實現(xiàn)類的完全限定名,一個接口可以設置多個實現(xiàn)類,不同實現(xiàn)類之間使用“,”隔開,例如:\

org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

spring.factories 文件可以將 spring-boot 項目包以外的 bean(即在 pom 文件中添加依賴中的 bean)注冊到 spring-boot 項目的 spring 容器。由于@ComponentScan 注解只能掃描 spring-boot 項目包內(nèi)的 bean 并注冊到 spring 容器中,因此需要 @EnableAutoConfiguration 注解來注冊項目包外的bean。而 spring.factories 文件,則是用來記錄項目包外需要注冊的bean類名

2.2 Spring Factories 實現(xiàn)原理

spring-core 包里定義了 SpringFactoriesLoader 類,這個類會掃描所有 Jar 包類路徑下的 META-INF/spring.factories 文件,并獲取指定接口的配置。在 SpringFactoriesLoader 類中定義了兩個對外的方法,如下表。

返回值 方法 描述
List loadFactories(Class factoryType, @Nullable ClassLoader classLoader) 靜態(tài)方法; 根據(jù)接口獲取其實現(xiàn)類的實例; 該方法返回的是實現(xiàn)類對象列表。
List loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) 公共靜態(tài)方法; 根據(jù)接口l獲取其實現(xiàn)類的名稱; 該方法返回的是實現(xiàn)類的類名的列表

以上兩個方法的關(guān)鍵都是從指定的 ClassLoader中獲取spring.factories 文件,并解析得到類名列表,具體代碼如下。 loadFactories() 方法能夠獲取指定接口的實現(xiàn)類對象,具體代碼如下。

public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
    // 調(diào)用loadFactoryNames獲取接口的實現(xiàn)類
    List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
    // 遍歷 factoryNames 數(shù)組,創(chuàng)建實現(xiàn)類的對象
    List<T> result = new ArrayList(factoryImplementationNames.size());
    Iterator var5 = factoryImplementationNames.iterator();
    //排序
    while(var5.hasNext()) {
        String factoryImplementationName = (String)var5.next();
        result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
    }

    AnnotationAwareOrderComparator.sort(result);
    return result;
}

loadFactoryNames() 方法能夠根據(jù)接口獲取其實現(xiàn)類類名的集合,具體代碼如下。

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    String factoryTypeName = factoryType.getName();
    //獲取自動配置類
    return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}

loadSpringFactories() 方法能夠讀取該項目中所有 Jar 包類路徑下 META-INF/spring.factories 文件的配置內(nèi)容,并以 Map 集合的形式返回,具體代碼如下。

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
    Map<String, List<String>> result = (Map)cache.get(classLoader);
    if (result != null) {
        return result;
    } else {
        HashMap result = new HashMap();

    try {
        //掃描所有 Jar 包類路徑下的 META-INF/spring.factories 文件
        Enumeration urls = classLoader.getResources("META-INF/spring.factories");

        while(urls.hasMoreElements()) {
                URL url = (URL)urls.nextElement();
                UrlResource resource = new UrlResource(url);
                //將掃描到的 META-INF/spring.factories 文件中內(nèi)容包裝成 properties 對象
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                Iterator var6 = properties.entrySet().iterator();

                while(var6.hasNext()) {
                    Map.Entry<?, ?> entry = (Map.Entry)var6.next();
                    //提取 properties 對象中的 key 值
                    String factoryTypeName = ((String)entry.getKey()).trim();
                    //提取 proper 對象中的 value 值(多個類的完全限定名使用逗號連接的字符串)
                    // 使用逗號為分隔符轉(zhuǎn)換為數(shù)組,數(shù)組內(nèi)每個元素都是配置類的完全限定名
                    String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                    String[] var10 = factoryImplementationNames;
                    int var11 = factoryImplementationNames.length;
                    //遍歷配置類數(shù)組,并將數(shù)組轉(zhuǎn)換為 list 集合
                    for(int var12 = 0; var12 < var11; ++var12) {
                        String factoryImplementationName = var10[var12];
                        ((List)result.computeIfAbsent(factoryTypeName, (key) -> {
                            return new ArrayList();
                        })).add(factoryImplementationName.trim());
                    }
                }
            }
            //將 propertise 對象的 key 與由配置類組成的 List 集合一一對應存入名為 result 的 Map 中
            result.replaceAll((factoryType, implementations) -> {
                return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
            });
            cache.put(classLoader, result);
            //返回 result
            return result;
        } catch (IOException var14) {
            throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
        }
     }
}

2.3 應用啟動時ConfigurationClassPostProcessor的注冊

Springboot 應用在啟動時,會創(chuàng)建一個ApplicationContext上下文實例。如果是Web應用,該上下文實例會使用類 AnnotationConfigEmbeddedWebApplicationContext來創(chuàng)建;否則使用類AnnotationConfigApplicationContext來創(chuàng)建。

不管是以上兩種情況中的哪一種,相應的上下文類實例在創(chuàng)建時都會創(chuàng)建一個AnnotatedBeanDefinitionReader實例用于讀取注解方式的Bean定義,而在創(chuàng)建這個AnnotatedBeanDefinitionReader實例的過程中,它注冊了一個 ConfigurationClassPostProcessor Bean定義到容器,這是一個BeanFactoryPostProcessor,更明確地講,它是一個BeanDefinitionRegistryPostProcessor。

ConfigurationClassPostProcessor被設計用來發(fā)現(xiàn)所有的配置類和相關(guān)的Bean定義并注冊到容器,它在所有BeanFactoryPostProcessor中具備最高執(zhí)行優(yōu)先級,因為其他BeanFactoryPostProcessor需要基于注冊了Bean定義工作

在springboot啟動過程中,具體的來講,是在以下位置被完成自動配置

SpringApplication.run()
=> refreshContext()
    => AbstractApplicationContext.refresh()
    => invokeBeanFactoryPostProcessors()
        => ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry()
        => processConfigBeanDefinitions()
            => ConfigurationClassParser.parse()
            => processDeferredImportSelectors()
                => AutoConfigurationImportSelector.selectImports()

在啟動時把實現(xiàn)類org.springframework.boot.autoconfigure.EnableAutoConfiguration定義了哪些類是自動配置類,使用屬性org.springframework.boot.autoconfigure.AutoConfigurationImportFilter定義了哪些自動配置導入過濾器會被應用,缺省情況下,只有一個過濾器會被應用,那就是OnClassCondition

AutoConfigurationImportSelector.selectImports()在執(zhí)行時 :

  1. 從spring-autoconfigure-metadata.properties屬性文件中提取自動配置元數(shù)據(jù)AutoConfigurationMetadata;

    spring-boot-autoconfigure jar包的文件結(jié)構(gòu)
    ├─META-INF
    │      spring-autoconfigure-metadata.properties
    │      spring.factories
    │
    └─org
        └─springframework
            └─boot
                └─autoconfigure
    
  1. 獲取spring.factories屬性文件中屬性org.springframework.boot.autoconfigure.EnableAutoConfiguration定義的所有配置類作為候選配置類;

  2. 然后,獲取spring.factories屬性文件中屬性org.springframework.boot.autoconfigure.AutoConfigurationImportFilter定義的過濾器,缺省是一個 : org.springframework.boot.autoconfigure.condition.OnClassCondition

  3. 對所有的候選配置類在自動配置元數(shù)據(jù)上 應用 所發(fā)現(xiàn)的過濾器,其實缺省情況下就一個,就是 OnClassCondition,也就是利用每個候選配置類上的注解 @ConditionalOnClass , 來看這個候選配置類是否需要被應用。

    selectImports()會返回所有需要被應用的配置類(注解@ConditionalOnClass條件被滿足),ConfigurationClassPostProcessor.postProcessBeanFactory()會將識別這些配置類中定義的bean并將它們注冊到容器

整個流程如下圖所示

image.png

2.4 自動配置的生效和修改

spring.factories 文件中的所有自動配置類(xxxAutoConfiguration),都是必須在一定的條件下才會作為組件添加到容器中,配置的內(nèi)容才會生效。這些限制條件在 Spring Boot 中以 @Conditional 派生注解的形式體現(xiàn),如下表。

注解 生效條件
@ConditionalOnJava 應用使用指定的 Java 版本時生效
@ConditionalOnBean 容器中存在指定的 Bean 時生效
@ConditionalOnMissingBean 容器中不存在指定的 Bean 時生效
@ConditionalOnExpression 滿足指定的 SpEL 表達式時生效
@ConditionalOnClass 存在指定的類時生效
@ConditionalOnMissingClass 不存在指定的類時生效
@ConditionalOnSingleCandidate 容器中只存在一個指定的 Bean 或這個 Bean 為首選 Bean 時生效
@ConditionalOnProperty 系統(tǒng)中指定屬性存在指定的值時生效
@ConditionalOnResource 類路徑下存在指定的資源文件時生效
@ConditionalOnWebApplication 當前應用是 web 應用時生效
@ConditionalOnNotWebApplication 當前應用不是 web 應用生效

下面我們以
ServletWebServerFactoryAutoConfiguration 為例,介紹 Spring Boot 自動配置是如何生效的。

ServletWebServerFactoryAutoConfiguration 代碼如下。

@Configuration(   //表示這是一個配置類,與 xml 配置文件等價,也可以給容器中添加組件
        proxyBeanMethods = false
)
@AutoConfigureOrder(-2147483648)
@ConditionalOnClass({ServletRequest.class})//判斷當前項目有沒有 ServletRequest 這個類
@ConditionalOnWebApplication(// 判斷當前應用是否是 web 應用,如果是,當前配置類生效 
type = Type.SERVLET
)
@EnableConfigurationProperties({ServerProperties.class})
//啟動指定類的屬性配置(ConfigurationProperties)功能;將配置文件中對應的值和 ServerProperties 綁定起來;并把 ServerProperties 加入到ioc容器中
@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, EmbeddedTomcat.class, EmbeddedJetty.class, EmbeddedUndertow.class})
public class ServletWebServerFactoryAutoConfiguration {
    public ServletWebServerFactoryAutoConfiguration() {
    }

    @Bean //給容器中添加一個組件,這個組件的某些值需要從properties中獲取
    public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties, ObjectProvider<WebListenerRegistrar> webListenerRegistrars) {
        return new ServletWebServerFactoryCustomizer(serverProperties, (List) webListenerRegistrars.orderedStream().collect(Collectors.toList()));
    }

    @Bean
    @ConditionalOnClass(
            name = {"org.apache.catalina.startup.Tomcat"}
    )
    public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(ServerProperties serverProperties) {
        return new TomcatServletWebServerFactoryCustomizer(serverProperties);
    }

    @Bean
    @ConditionalOnMissingFilterBean({ForwardedHeaderFilter.class})
    @ConditionalOnProperty(
            value = {"server.forward-headers-strategy"},
            havingValue = "framework"
    )
    public FilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter() {
        ForwardedHeaderFilter filter = new ForwardedHeaderFilter();
        FilterRegistrationBean<ForwardedHeaderFilter> registration = new FilterRegistrationBean(filter, new ServletRegistrationBean[0]);
        registration.setDispatcherTypes(DispatcherType.REQUEST, new DispatcherType[]{DispatcherType.ASYNC, DispatcherType.ERROR});
        registration.setOrder(-2147483648);
        return registration;
    }

    public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
        private ConfigurableListableBeanFactory beanFactory;

        public BeanPostProcessorsRegistrar() {
        }

        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            if (beanFactory instanceof ConfigurableListableBeanFactory) {
                this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
            }

        }

        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            if (this.beanFactory != null) {
                this.registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor", WebServerFactoryCustomizerBeanPostProcessor.class, WebServerFactoryCustomizerBeanPostProcessor::new);
                this.registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor", ErrorPageRegistrarBeanPostProcessor.class, ErrorPageRegistrarBeanPostProcessor::new);
            }
        }

        private <T> void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name, Class<T> beanClass, Supplier<T> instanceSupplier) {
            if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
                RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass, instanceSupplier);
                beanDefinition.setSynthetic(true);
                registry.registerBeanDefinition(name, beanDefinition);
            }

        }
    }
}

該類使用了以下注解:

  • @Configuration:用于定義一個配置類,可用于替換 Spring 中的 xml 配置文件;
  • @Bean:被 @Configuration 注解的類內(nèi)部,可以包含有一個或多個被 @Bean 注解的方法,用于構(gòu)建一個 Bean,并添加到 Spring 容器中;該注解與 spring 配置文件中 等價,方法名與 的 id 或 name 屬性等價,方法返回值與 class 屬性等價;
  • @EnableConfigurationProperties(ServerProperties.class).他的意思是啟動指定類的
    ConfigurationProperties功能;將配置文件中對應的值和 ServerProperties 綁定起來;并把
    ServerProperties 加入到 IOC 容器中。

除了 @Configuration 和 @Bean 注解外,該類還使用 5 個 @Conditional 衍生注解:

  • @ConditionalOnClass({ServletRequest.class}):判斷當前項目是否存在 ServletRequest 這個類,若存在,則該配置類生效。
  • @ConditionalOnWebApplication(type = Type.SERVLET):判斷當前應用是否是 Web 應用,如果是的話,當前配置類生效。
  • @ConditionalOnClass(name = {"org.apache.catalina.startup.Tomcat"}):判斷是否存在 Tomcat 類,若存在則該方法生效。
  • @ConditionalOnMissingFilterBean({ForwardedHeaderFilter.class}):判斷容器中是否有 ForwardedHeaderFilter 這個過濾器,若不存在則該方法生效。
  • @ConditionalOnProperty(value = {"server.forward-headers-strategy"},havingValue = "framework"):判斷配置文件中是否存在 server.forward-headers-strategy = framework,若不存在則該方法生效。

ServerProperties

ServletWebServerFactoryAutoConfiguration 類還使用了一個 @EnableConfigurationProperties 注解,通過該注解導入了一個 ServerProperties 類,其部分源碼如下。

@ConfigurationProperties(
    prefix = "server",
    ignoreUnknownFields = true
)
public class ServerProperties {
    private Integer port;
    private InetAddress address;
    @NestedConfigurationProperty
    private final ErrorProperties error = new ErrorProperties();
    private ServerProperties.ForwardHeadersStrategy forwardHeadersStrategy;
    private String serverHeader;
    private DataSize maxHttpHeaderSize = DataSize.ofKilobytes(8L);
    private Shutdown shutdown;
    @NestedConfigurationProperty
    private Ssl ssl;
    @NestedConfigurationProperty
    private final Compression compression;
    @NestedConfigurationProperty
    private final Http2 http2;
    private final ServerProperties.Servlet servlet;
    private final ServerProperties.Tomcat tomcat;
    private final ServerProperties.Jetty jetty;
    private final ServerProperties.Netty netty;
    private final ServerProperties.Undertow undertow;

    public ServerProperties() {
        this.shutdown = Shutdown.IMMEDIATE;
        this.compression = new Compression();
        this.http2 = new Http2();
        this.servlet = new ServerProperties.Servlet();
        this.tomcat = new ServerProperties.Tomcat();
        this.jetty = new ServerProperties.Jetty();
        this.netty = new ServerProperties.Netty();
        this.undertow = new ServerProperties.Undertow();
    }
    ....
}

我們看到,
ServletWebServerFactoryAutoConfiguration 使用了一個 @EnableConfigurationProperties 注解,而 ServerProperties 類上則使用了一個 @ConfigurationProperties 注解。這其實是 Spring Boot 自動配置機制中的通用用法。 Spring Boot 中為我們提供了大量的自動配置類 XxxAutoConfiguration 以及 XxxProperties,每個自動配置類 XxxAutoConfiguration 都使用了 @EnableConfigurationProperties 注解,而每個 XxxProperties 上都使用 @ConfigurationProperties 注解。

@ConfigurationProperties 注解的作用,是將這個類的所有屬性與配置文件中相關(guān)的配置進行綁定,以便于獲取或修改配置,但是 @ConfigurationProperties 功能是由容器提供的,被它注解的類必須是容器中的一個組件,否則該功能就無法使用。而 @EnableConfigurationProperties 注解的作用正是將指定的類以組件的形式注入到 IOC 容器中,并開啟其 @ConfigurationProperties 功能。因此,@ConfigurationProperties + @EnableConfigurationProperties 組合使用,便可以為 XxxProperties 類實現(xiàn)配置綁定功能。

自動配置類 XxxAutoConfiguration 負責使用 XxxProperties 中屬性進行自動配置,而 XxxProperties 則負責將自動配置屬性與配置文件的相關(guān)配置進行綁定,以便于用戶通過配置文件修改默認的自動配置。也就是說,真正“限制”我們可以在配置文件中配置哪些屬性的類就是這些 XxxxProperties 類,它與配置文件中定義的 prefix 關(guān)鍵字開頭的一組屬性是唯一對應的。

注意:XxxAutoConfiguration 與 XxxProperties 并不是一一對應的,大多數(shù)情況都是多對多的關(guān)系,即一個 XxxAutoConfiguration 可以同時使用多個 XxxProperties 中的屬性,一個 XxxProperties 類中屬性也可以被多個 XxxAutoConfiguration 使用。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容