SpringBoot自動(dòng)裝配

1.SpringBoot自動(dòng)裝配原理

1.1 核心是這個(gè)@SpringBootApplication注解
里面主要包含三個(gè)注解:@SpringBootConfiguration、@EnableAutoConfiguration斜做、@ComponentScan贴汪;
1.2 @EnableAutoConfiguration:表示開啟自動(dòng)裝配慧脱;@ComponentScan:表示掃描當(dāng)前配置類所在路徑的全部配置類凤价。
1.3其中@EnableAutoConfiguration這個(gè)注解里面引入了一個(gè)@Import(AutoConfigurationImportSelector.class),而這個(gè)類實(shí)現(xiàn)了DeferredImportSelector接口乎莉。這個(gè)接口的selectImports()將在spring解析完我們自定義的配置類之后抄瓦,才會(huì)解析自動(dòng)配置類截型。
1)在selectImports()方法中的getAutoConfigurationEntry()將會(huì)從spring.factories文件讀取key=org.springframework.boot.autoconfigure.EnableAutoConfiguration對(duì)應(yīng)的自動(dòng)配置類:


spring.factories

2)在getAutoConfigurationEntry()中讼载,getConfigurationClassFilter()方法會(huì)讀取spring.factories 文件中key=AutoConfigurationImportFilter的值轿秧,同時(shí)會(huì)得到三個(gè)條件過濾器,OnBeanCondition咨堤、OnClassCondition菇篡、OnWebApplicationCondition使用這三個(gè)注解進(jìn)一步過濾掉不符合的自動(dòng)配置類:


spring.factories

3)在過濾器比較篩選自動(dòng)配置類時(shí),會(huì)讀取"META-INF/spring-autoconfigure-metadata.properties"文件中的內(nèi)容一喘,進(jìn)行加快篩選:
spring-autoconfigure-metadata.properties

2.AutoConfigurationImportSelector的源碼解析

public String[] selectImports(AnnotationMetadata annotationMetadata) {
    // 會(huì)在所有@Configuration都解析完了之后才執(zhí)行

    if (!isEnabled(annotationMetadata)) {
    return NO_IMPORTS;
}

// 獲取自動(dòng)配置類(spring.factories中所導(dǎo)入的)
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        }
        // 獲取@EnableAutoConfiguration的屬性 比如exclude excludeName
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        // 獲取spring.factories中@EnableAutoConfiguration對(duì)應(yīng)的所有的AutoConfiguration(自動(dòng)配置類)
        List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
        // 去重(也就是按類名去重)
        configurations = removeDuplicates(configurations);
        // 獲取需要排除的AutoConfiguration驱还,可以通過@EnableAutoConfiguration注解的exclude屬性铝侵,或者spring.autoconfigure.exclude來配置
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);

        // 排除部分自動(dòng)裝配類
        checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);

        // 獲取spring.factories中的AutoConfigurationImportFilter對(duì)AutoConfiguration進(jìn)行過濾
        // 默認(rèn)會(huì)拿到OnBeanCondition、OnClassCondition咪鲜、OnWebApplicationCondition
        // 這三個(gè)會(huì)去判斷上面的AutoConfiguration是否符合它們自身所要求的條件撞鹉,不符合的會(huì)過濾掉,表示不會(huì)進(jìn)行解析了
        // 會(huì)利用spring-autoconfigure-metadata.properties中的配置來進(jìn)行過濾
        configurations = getConfigurationClassFilter().filter(configurations);
        // configurations表示合格的享郊,exclusions表示被排除的,把它們記錄在條件報(bào)告中
        fireAutoConfigurationImportEvents(configurations, exclusions);

        // 最后返回的都是符合條件的自動(dòng)裝配類
        return new AutoConfigurationEntry(configurations, exclusions);
    }
private ConfigurationClassFilter getConfigurationClassFilter() {
    if (this.configurationClassFilter == null) {
        // 讀取spring.factories中key = AutoConfigurationImportFilter的value
        List<AutoConfigurationImportFilter> filters = getAutoConfigurationImportFilters();
        for (AutoConfigurationImportFilter filter : filters) {
            invokeAwareMethods(filter);
        }
        // 給filter集合賦值
        this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters);
    }
    return this.configurationClassFilter;
}

ConfigurationClassFilter#filter():

List<String> filter(List<String> configurations) {
    long startTime = System.nanoTime();
    // configurations就是讀取到的自動(dòng)配置類
    String[] candidates = StringUtils.toStringArray(configurations);
    boolean skipped = false;

    // 逐個(gè)利用AutoConfigurationImportFilter來判斷所有的自動(dòng)配置類的條件是否匹配炊琉,匹配結(jié)果存在match數(shù)組中
    // 這里過濾器的順序是spring.factories文件中配置的順序
    for (AutoConfigurationImportFilter filter : this.filters) {
        // 調(diào)用具體過濾器實(shí)現(xiàn)的match方法
        boolean[] match = filter.match(candidates, this.autoConfigurationMetadata);

        for (int i = 0; i < match.length; i++) {
            if (!match[i]) {
                candidates[i] = null;
                skipped = true;
            }
        }
    }

    // 全部都匹配
    if (!skipped) {
        return configurations;
    }

    // 把匹配的記錄在result集合中又活,最后返回
    List<String> result = new ArrayList<>(candidates.length);
    for (String candidate : candidates) {
        if (candidate != null) {
            result.add(candidate);
        }
    }
    if (logger.isTraceEnabled()) {
        int numberFiltered = configurations.size() - result.size();
        logger.trace("Filtered " + numberFiltered + " auto configuration class in "
                + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
    }
    return result;
}

這里的過濾器只有三個(gè)并不是全的苔咪,來源就是spring.factories中定義的:


三種過濾器

3.@AutoConfigurationPackage自動(dòng)配置包路徑

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {}

主要執(zhí)行的方法是registerBeanDefinitions():

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

   @Override
   public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
      // 將MyApplication.class所在的包路徑注冊(cè)成BeanDefinition,
      // 方便第三方減少配置,比如mybatis不用配置掃描路徑柳骄,直接獲取這個(gè)BeanDefinition
      register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
   }
}
PackageImports(AnnotationMetadata metadata) {
    // 獲取注解的屬性
    AnnotationAttributes attributes = AnnotationAttributes
            .fromMap(metadata.getAnnotationAttributes(AutoConfigurationPackage.class.getName(), false));
    // 注解屬性的value
    List<String> packageNames = new ArrayList<>(Arrays.asList(attributes.getStringArray("basePackages")));
    for (Class<?> basePackageClass : attributes.getClassArray("basePackageClasses")) {
        packageNames.add(basePackageClass.getPackage().getName());
    }
    // 當(dāng)前配置類所在的包路徑
    if (packageNames.isEmpty()) {
        // metadata.getClassName() :獲取當(dāng)前配置類的類名
        // ClassUtils.getPackageName:獲取配置類所在的包的路徑
        packageNames.add(ClassUtils.getPackageName(metadata.getClassName()));
    }
    this.packageNames = Collections.unmodifiableList(packageNames);
}

該方法register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]))团赏,最終會(huì)將收到的包路徑注冊(cè)到Spring容器當(dāng)中,方便第三方對(duì)接時(shí)不用填寫掃描路徑也仍然可以獲取到掃描路徑耐薯。

4.@ComponentScan中的兩個(gè)excluseFilter

1.TypeExcludeFilter.class

public class TypeExcludeFilter implements TypeFilter, BeanFactoryAware {

    private BeanFactory beanFactory;
    // 所有這個(gè)TypeExcludeFilter類型的Bean集合
    private Collection<TypeExcludeFilter> delegates;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
    throws IOException {
        if (this.beanFactory instanceof ListableBeanFactory && getClass() == TypeExcludeFilter.class) {

            // 從Spring容器中獲取TypeExcludeFilter舔清,然后進(jìn)行匹配丝里,匹配的則不解析
            for (TypeExcludeFilter delegate : getDelegates()) {
                // 調(diào)用自定義的TypeExcludeFilter的match方法,匹配就排除這個(gè)類
                if (delegate.match(metadataReader, metadataReaderFactory)) {
                    return true;
                }
            }
        }
        return false;
    }

    private Collection<TypeExcludeFilter> getDelegates() {
        Collection<TypeExcludeFilter> delegates = this.delegates;
        if (delegates == null) {
            // 從Spring容器獲取這個(gè)類型的Bean
            delegates = ((ListableBeanFactory) this.beanFactory).getBeansOfType(TypeExcludeFilter.class).values();
            this.delegates = delegates;
        }
        return delegates;
    }

}

2.AutoConfigurationExcludeFilter.class

public class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoaderAware {

    private ClassLoader beanClassLoader;

    private volatile List<String> autoConfigurations;

    @Override
    public void setBeanClassLoader(ClassLoader beanClassLoader) {
        this.beanClassLoader = beanClassLoader;
    }

    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
    throws IOException {
        // 這個(gè)方法就是驗(yàn)證當(dāng)前類是否是配置類并且是自動(dòng)配置類
        // 如果符合体谒,就排除這個(gè)類杯聚,不去解析,讓@EnableAutoConfiguration那邊去處理
        return isConfiguration(metadataReader) && isAutoConfiguration(metadataReader);
    }

    // 當(dāng)前類是否有@Configuration注解
    private boolean isConfiguration(MetadataReader metadataReader) {
        return metadataReader.getAnnotationMetadata().isAnnotated(Configuration.class.getName());
    }

    // 當(dāng)前類是否是自動(dòng)配置類
    private boolean isAutoConfiguration(MetadataReader metadataReader) {
        return getAutoConfigurations().contains(metadataReader.getClassMetadata().getClassName());
    }

    protected List<String> getAutoConfigurations() {
        if (this.autoConfigurations == null) {
            this.autoConfigurations = SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,
                                                                             this.beanClassLoader);
        }
        return this.autoConfigurations;
    }

}

這兩個(gè)過濾器都是在掃描階段過濾掉部分類营密,而AutoConfigurationExcludeFilter則是過濾自動(dòng)配置類械媒;TypeExcludeFilter根據(jù)match方法匹配,符合就過濾掉评汰。
1.為什么要在掃描階段過濾自動(dòng)配置類呢纷捞?
因?yàn)锧EnableAutoConfiguration實(shí)現(xiàn)了DeferredImportSelector接口,而這個(gè)接口會(huì)在所有配置類解析完之后被去,才會(huì)調(diào)用的主儡,在掃描階段過濾自動(dòng)配置類可以提高性能,也保證了配置類的解析順序惨缆。

2.我們?cè)诶^承TypeExcludeFilter重寫match方法時(shí)糜值,如果是用@Bean注入Spring容器中,將不會(huì)生效坯墨。
和執(zhí)行順序有關(guān)寂汇,@Component骄瓣、@ComponentScan會(huì)先執(zhí)行榕栏,@Bean在后面執(zhí)行扒磁。當(dāng)掃描解析配置類時(shí)妨托,@Bean對(duì)應(yīng)的Bean對(duì)象還沒生成添加到Spring容器中。

對(duì)于這個(gè)問題的解決辦法脆贵,可以在掃描前卖氨,就將我們重寫的TypeExcludeFilter添加到容器中筒捺。
1)比如可以實(shí)現(xiàn)ApplicationContextInitializer初始化器系吭,然后再spring.factories中配置我們實(shí)現(xiàn)的類肯尺;
2)然后再initialize方法中將我們的實(shí)現(xiàn)類注冊(cè)到spring容器當(dāng)中则吟。

5.Starter機(jī)制

Springboot中的Starter機(jī)制氓仲,就是一個(gè)Maven依賴敬扛,當(dāng)我們?cè)陧?xiàng)目中引入spring-boot-starter-web依賴時(shí)啥箭,實(shí)際上引入了spring-boot-starter捉蚤、spring-boot-starter-json缆巧、spring-boot-starter-tomcat等和Web開發(fā)相關(guān)的依賴包陕悬。
然后再通過條件注解用來判斷當(dāng)前應(yīng)用的依賴中是否存在某個(gè)類或某些類捉超,這樣就可以動(dòng)態(tài)的注入某些類枝誊。

對(duì)于@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class }) 這個(gè)注解叶撒,如果Servlet.class類不存在,但是運(yùn)行時(shí)不報(bào)錯(cuò)压汪,SpringBoot是怎么處理的止剖?
1.我們引入某個(gè)依賴時(shí)穿香,依賴中的文件都是已經(jīng)編譯好的扔水,因此在我們項(xiàng)目編譯期間魔市,是不會(huì)再次編譯的待德。
2.運(yùn)行時(shí)不報(bào)錯(cuò)将宪,是因?yàn)镾pringboot使用了ASM技術(shù)较坛,直接處理字節(jié)碼文件丑勤,而不是使用反射生成實(shí)例法竞,通過獲取某個(gè)類的類名岔霸,再使用類加載器去驗(yàn)證是否存在呆细。

6.自動(dòng)裝配Tomcat

1.創(chuàng)建WebServer

ServletWebServerApplicationContext#onRefresh():

protected void onRefresh() {
    super.onRefresh();
    try {
        // 啟動(dòng)Tomcat
        createWebServer();
    }
    catch (Throwable ex) {
        throw new ApplicationContextException("Unable to start web server", ex);
    }
}

private void createWebServer() {
    WebServer webServer = this.webServer;
    ServletContext servletContext = getServletContext();
    if (webServer == null && servletContext == null) {
        // 獲取具體的WebServerFactory,比如TomcatServletWebServerFactory
        ServletWebServerFactory factory = getWebServerFactory();
        this.webServer = factory.getWebServer(getSelfInitializer()); // 這個(gè)方法啟動(dòng)tomcat

        getBeanFactory().registerSingleton("webServerGracefulShutdown",
                new WebServerGracefulShutdownLifecycle(this.webServer));
        getBeanFactory().registerSingleton("webServerStartStop",
                new WebServerStartStopLifecycle(this, this.webServer));
    }
    else if (servletContext != null) {
        try {
            getSelfInitializer().onStartup(servletContext);
        }
        catch (ServletException ex) {
            throw new ApplicationContextException("Cannot initialize servlet context", ex);
        }
    }
    initPropertySources();
}

getWebServerFactory()獲取ServletWebServerFactory實(shí)例:

protected ServletWebServerFactory getWebServerFactory() {
    // Use bean names so that we don't consider the hierarchy
    //通過類型獲取beanName
    String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class); 
    // 期間發(fā)現(xiàn)多個(gè)或沒有都會(huì)報(bào)錯(cuò)
    if (beanNames.length == 0) {
        throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing "
                                              + "ServletWebServerFactory bean.");
    }
    if (beanNames.length > 1) {
        throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "
                                              + "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
    }
    // 這里才會(huì)返回對(duì)應(yīng)的Bean實(shí)例
    return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}

2.ServletWebServerFactoryAutoConfiguration自動(dòng)配置類:

這個(gè)類Import了EmbeddedTomcat.class,EmbeddedJetty.class,EmbeddedUndertow.class 三個(gè)類,就是為了生成對(duì)應(yīng)的WebServer實(shí)例略水。

@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class) //ServerProperties用于讀取properties配置文件渊涝,里面以server開頭的內(nèi)容
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
        ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
        ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
        ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {

    @Bean
    public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties,
            ObjectProvider<WebListenerRegistrar> webListenerRegistrars,
            ObjectProvider<CookieSameSiteSupplier> cookieSameSiteSuppliers) {
        return new ServletWebServerFactoryCustomizer(serverProperties,
                webListenerRegistrars.orderedStream().collect(Collectors.toList()),
                cookieSameSiteSuppliers.orderedStream().collect(Collectors.toList()));
    }

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

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnProperty(value = "server.forward-headers-strategy", havingValue = "framework")
    @ConditionalOnMissingFilterBean(ForwardedHeaderFilter.class)
    static class ForwardedHeaderFilterConfiguration {

        @Bean
        @ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
        ForwardedHeaderFilterCustomizer tomcatForwardedHeaderFilterCustomizer(ServerProperties serverProperties) {
            return (filter) -> filter.setRelativeRedirects(serverProperties.getTomcat().isUseRelativeRedirects());
        }

        @Bean
        FilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter(
                ObjectProvider<ForwardedHeaderFilterCustomizer> customizerProvider) {
            ForwardedHeaderFilter filter = new ForwardedHeaderFilter();
            customizerProvider.ifAvailable((customizer) -> customizer.customize(filter));
            FilterRegistrationBean<ForwardedHeaderFilter> registration = new FilterRegistrationBean<>(filter);
            registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.ERROR);
            registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
            return registration;
        }

    }

    interface ForwardedHeaderFilterCustomizer {

        void customize(ForwardedHeaderFilter filter);

    }

    /**
     * Registers a {@link WebServerFactoryCustomizerBeanPostProcessor}. Registered via
     * {@link ImportBeanDefinitionRegistrar} for early registration.
     */
    public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {

        private ConfigurableListableBeanFactory beanFactory;

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

        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
                BeanDefinitionRegistry registry) {
            if (this.beanFactory == null) {
                return;
            }
            // 往Spring容器中注冊(cè)一個(gè)WebServerFactoryCustomizerBeanPostProcessor
            registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
                    WebServerFactoryCustomizerBeanPostProcessor.class,
                    WebServerFactoryCustomizerBeanPostProcessor::new);
            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);
            }
        }

    }

}

我們以EmbeddedTomcat這個(gè)類為例:

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedTomcat {

    @Bean
    TomcatServletWebServerFactory tomcatServletWebServerFactory(
            ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
            ObjectProvider<TomcatContextCustomizer> contextCustomizers,
            ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
        TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); //這里new出來使用的是默認(rèn)的端口8080

        // orderedStream()調(diào)用時(shí)會(huì)去Spring容器中找到TomcatConnectorCustomizer類型的Bean,默認(rèn)是沒有的缆娃,程序員可以自己定義
        factory.getTomcatConnectorCustomizers()
                .addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
        factory.getTomcatContextCustomizers()
                .addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
        factory.getTomcatProtocolHandlerCustomizers()
                .addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
        return factory;
    }

}

只有當(dāng)spring容器中沒有ServletWebServerFactory贯要,并且所依賴的三個(gè)類也都存在時(shí)崇渗,才會(huì)創(chuàng)建TomcatServletWebServerFactory宅广。

3.關(guān)于application.properties屬性是如何替換默認(rèn)值

BeanPostProcessorsRegistrar.class當(dāng)中跟狱,會(huì)調(diào)用registerBeanDefinitions()向spring容器中注冊(cè)WebServerFactoryCustomizerBeanPostProcessor兽肤。

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) {
    if (this.beanFactory == null) {
        return;
    }
    // 往Spring容器中注冊(cè)一個(gè)WebServerFactoryCustomizerBeanPostProcessor
    registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
            WebServerFactoryCustomizerBeanPostProcessor.class,
            WebServerFactoryCustomizerBeanPostProcessor::new);
    registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",
            ErrorPageRegistrarBeanPostProcessor.class, ErrorPageRegistrarBeanPostProcessor::new);
}

然后會(huì)接著調(diào)用WebServerFactoryCustomizerBeanPostProcessor#postProcessBeforeInitialization()方法

private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
    // 獲取WebServerFactoryCustomizer對(duì)WebServerFactory進(jìn)行自定義资铡,默認(rèn)會(huì)拿到5個(gè)笤休,其中就包括ServletWebServerFactoryCustomizer
    LambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory)
            .withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
            .invoke((customizer) -> customizer.customize(webServerFactory));
}

最終會(huì)執(zhí)行對(duì)應(yīng)的customize()店雅,讀取properties文件中配置的信息闹啦,替換默認(rèn)值:

@Override
public void customize(ConfigurableServletWebServerFactory factory) {
    PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
    map.from(this.serverProperties::getPort).to(factory::setPort);
    map.from(this.serverProperties::getAddress).to(factory::setAddress);
    map.from(this.serverProperties.getServlet()::getContextPath).to(factory::setContextPath);
    map.from(this.serverProperties.getServlet()::getApplicationDisplayName).to(factory::setDisplayName);
    map.from(this.serverProperties.getServlet()::isRegisterDefaultServlet).to(factory::setRegisterDefaultServlet);
    map.from(this.serverProperties.getServlet()::getSession).to(factory::setSession);
    map.from(this.serverProperties::getSsl).to(factory::setSsl);
    map.from(this.serverProperties.getServlet()::getJsp).to(factory::setJsp);
    map.from(this.serverProperties::getCompression).to(factory::setCompression);
    map.from(this.serverProperties::getHttp2).to(factory::setHttp2);
    map.from(this.serverProperties::getServerHeader).to(factory::setServerHeader);
    map.from(this.serverProperties.getServlet()::getContextParameters).to(factory::setInitParameters);
    map.from(this.serverProperties.getShutdown()).to(factory::setShutdown);
    for (WebListenerRegistrar registrar : this.webListenerRegistrars) {
        registrar.register(factory);
    }
    if (!CollectionUtils.isEmpty(this.cookieSameSiteSuppliers)) {
        factory.setCookieSameSiteSuppliers(this.cookieSameSiteSuppliers);
    }
}

4.通過自定義TomcatConnectorCustomizer更改tomcat信息

在TomcatServletWebServerFactory#getWebServer荐健,其中customizeConnector就會(huì)調(diào)用之前設(shè)置到factory里面的自定義的TomcatConnectorCustomizer江场,然后對(duì)connector進(jìn)行配置:

public WebServer getWebServer(ServletContextInitializer... initializers) {
    if (this.disableMBeanRegistry) {
        Registry.disableRegistry();
    }
    Tomcat tomcat = new Tomcat();
    File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
    tomcat.setBaseDir(baseDir.getAbsolutePath());
    for (LifecycleListener listener : this.serverLifecycleListeners) {
        tomcat.getServer().addLifecycleListener(listener);
    }
    Connector connector = new Connector(this.protocol);
    connector.setThrowOnFailure(true);
    tomcat.getService().addConnector(connector);
    // 配置tomcat端口號(hào)
    customizeConnector(connector);
    tomcat.setConnector(connector);
    tomcat.getHost().setAutoDeploy(false);
    configureEngine(tomcat.getEngine());
    for (Connector additionalConnector : this.additionalTomcatConnectors) {
        tomcat.getService().addConnector(additionalConnector);
    }
    prepareContext(tomcat.getHost(), initializers);
    return getTomcatWebServer(tomcat);// 這里啟動(dòng)tomcat
}

protected void customizeConnector(Connector connector) {
    int port = Math.max(getPort(), 0);
    connector.setPort(port);
    if (StringUtils.hasText(getServerHeader())) {
        connector.setProperty("server", getServerHeader());
    }
    if (connector.getProtocolHandler() instanceof AbstractProtocol) {
        customizeProtocol((AbstractProtocol<?>) connector.getProtocolHandler());
    }
    invokeProtocolHandlerCustomizers(connector.getProtocolHandler());
    if (getUriEncoding() != null) {
        connector.setURIEncoding(getUriEncoding().name());
    }
    // Don't bind to the socket prematurely if ApplicationContext is slow to start
    connector.setProperty("bindOnInit", "false");
    if (getHttp2() != null && getHttp2().isEnabled()) {
        connector.addUpgradeProtocol(new Http2Protocol());
    }
    if (getSsl() != null && getSsl().isEnabled()) {
        customizeSsl(connector);
    }
    TomcatConnectorCustomizer compression = new CompressionConnectorCustomizer(getCompression());
    compression.customize(connector);
    // 這個(gè)customizer就是程序員自己定義的bean,然后覆蓋默認(rèn)的值
    for (TomcatConnectorCustomizer customizer : this.tomcatConnectorCustomizers) {
        customizer.customize(connector);
    }
}


// 示例
// 通過自定義TomcatConnectorCustomizer佑附,實(shí)現(xiàn)更改tomcat的信息
@Bean
public TomcatConnectorCustomizer tomcatConnectorCustomizer(){
    return new TomcatConnectorCustomizer() {
        @Override
        public void customize(Connector connector) {
            connector.setPort(8082);
        }
    };
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末帮匾,一起剝皮案震驚了整個(gè)濱河市瘟斜,隨后出現(xiàn)的幾起案子螺句,更是在濱河造成了極大的恐慌蛇尚,老刑警劉巖取劫,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谱邪,死亡現(xiàn)場(chǎng)離奇詭異惦银,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)书蚪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門殊校,熙熙樓的掌柜王于貴愁眉苦臉地迎上來箩艺,“玉大人,你說我怎么就攤上這事拜英±糯撸” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵侠碧,是天一觀的道長(zhǎng)弄兜。 經(jīng)常有香客問我替饿,道長(zhǎng)贸典,這世上最難降的妖魔是什么廊驼? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任妒挎,我火速辦了婚禮饥漫,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘闯割。我一直安慰自己竿拆,他們只是感情好丙笋,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布锥忿。 她就那樣靜靜地躺著怠肋,像睡著了一般笙各。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上数尿,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天右蹦,我揣著相機(jī)與錄音嫩实,去河邊找鬼甲献。 笑死晃洒,一個(gè)胖子當(dāng)著我的面吹牛朦乏,可吹牛的內(nèi)容都是我干的呻疹。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼朦佩,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼语稠!你這毒婦竟也來了仙畦?” 一聲冷哼從身側(cè)響起慨畸,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤先口,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后厢汹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體烫葬,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡搭综,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年兑巾,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片帅掘。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡修档,死狀恐怖吱窝,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情兴使,我是刑警寧澤撕予,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布欠母,位于F島的核電站吆寨,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏六水。R本人自食惡果不足惜掷贾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一想帅、第九天 我趴在偏房一處隱蔽的房頂上張望啡莉。 院中可真熱鬧,春花似錦浅缸、人聲如沸衩椒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)囤躁。三九已至荔睹,卻和暖如春僻他,著一層夾襖步出監(jiān)牢的瞬間吨拗,已是汗流浹背劝篷。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工民宿, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留活鹰,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓着绷,卻偏偏與公主長(zhǎng)得像荠医,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子豫喧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容