SpringBoot 嵌入式容器原理与配置说明

Spring Boot 2.121 版本嵌入式Servlet容器自动配置及启动原理分析WebServerFactoryCustomizerBeanPostProcessor_mb645b608370ff5的技术博客_51CTO博客icon-default.png?t=N6B9https://blog.51cto.com/nizuimeiabc1/6266427SpringBoot之嵌入式Servlet容器_webserverfactorycustomizericon-default.png?t=N6B9https://blog.csdn.net/qq_22610595/article/details/119478184夜间沐水人的博客-csdn博客<>SpringBoot+内置Tomcat配置,参数调优,最大并发量,最大连接数_springboot tomcat性能调优_蚂蚁舞的博客-CSDN博客icon-default.png?t=N6B9https://blog.csdn.net/myyhtw/article/details/129069586Spring Boot-内置的Tomcat服务器配置详解_springboot tomcat配置_张某某啊哈的博客-CSDN博客icon-default.png?t=N6B9https://blog.csdn.net/qq_36908872/article/details/127300713

SpringBoot : server.tomcat.connection-timeout配置解析 - FynnWang - 博客园 (cnblogs.com)icon-default.png?t=N6B9https://www.cnblogs.com/wftop1/p/15788665.html

注:connection-timeout 属性在不同的springboot版本中 yml配置不同

比如:

2.0.7 版本:server.connection-timeout Appendix A. Common application properties (spring.io)

2.2.2 版本:server.tomcat.connection-timeout Common Application properties (spring.io)

1.前言

在 springboot1.x 版本中,通過EmbeddedServletContainerAutoConfiguration來定製嵌入式的servlet容器,如下所示。

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication
@Import(BeanPostProcessorsRegistrar.class)
//匯入BeanPostProcessorsRegistrar:給容器中匯入一些元件
//匯入了EmbeddedServletContainerCustomizerBeanPostProcessor
//後置處理器:bean初始化前後(建立完物件,還沒賦值賦值)執行初始化工作
public class EmbeddedServletContainerAutoConfiguration {

	@Configuration
	@ConditionalOnClass({ Servlet.class, Tomcat.class })//判斷當前是否引入了Tomcat依賴;
	@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
	//判斷當前容器沒有使用者自己定義EmbeddedServletContainerFactory:嵌入式的 Servlet容器工廠;作用:建立嵌入式的Servlet容器
	public static class EmbeddedTomcat {
		@Bean
		public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
			return new TomcatEmbeddedServletContainerFactory();
		}
	}
	... // undertow 和 jetty 的配置
}

在 springboot2.x 版本中,通過EmbeddedWebServerFactoryCustomizerAutoConfiguration自動建立對應的WebServerFactoryCustomizer來定製servlet容器,在此記錄一下在這個版本中的嵌入式Servlet容器自動配置原理以及啟動原理。

2.自動配置流程

2.1.分析部分原始碼
這裡記錄在 springboot2.x 版本中,嵌入式Servlet容器自動配置原理(以Tomcat為例)。

1.首先我們進入EmbeddedWebServerFactoryCustomizerAutoConfiguration的原始碼,發現 springboot2.4.0 支援四種servlet容器,從上到下分別為tomcat、jetty、undertow、netty。

@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication
@EnableConfigurationProperties(ServerProperties.class)
public class EmbeddedWebServerFactoryCustomizerAutoConfiguration {

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass({ Tomcat.class, UpgradeProtocol.class })
	public static class TomcatWebServerFactoryCustomizerConfiguration {
		@Bean
		public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(Environment environment,
				ServerProperties serverProperties) {
			return new TomcatWebServerFactoryCustomizer(environment, serverProperties);
		}
	}

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass({ Server.class, Loader.class, WebAppContext.class })
	public static class JettyWebServerFactoryCustomizerConfiguration {
		@Bean
		public JettyWebServerFactoryCustomizer jettyWebServerFactoryCustomizer(Environment environment,
				ServerProperties serverProperties) {
			return new JettyWebServerFactoryCustomizer(environment, serverProperties);
		}

	}


	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass({ Undertow.class, SslClientAuthMode.class })
	public static class UndertowWebServerFactoryCustomizerConfiguration {
		@Bean
		public UndertowWebServerFactoryCustomizer undertowWebServerFactoryCustomizer(Environment environment,
				ServerProperties serverProperties) {
			return new UndertowWebServerFactoryCustomizer(environment, serverProperties);
		}

	}

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(HttpServer.class)
	public static class NettyWebServerFactoryCustomizerConfiguration {
		@Bean
		public NettyWebServerFactoryCustomizer nettyWebServerFactoryCustomizer(Environment environment,
				ServerProperties serverProperties) {
			return new NettyWebServerFactoryCustomizer(environment, serverProperties);
		}

	}

}

2.本篇以tomcat為例,所以我們點進TomcatWebServerFactoryCustomizer(TomcatWeb伺服器工廠定製器)進行檢視。發現它應該是呼叫customize()定製介面,定製Servlet容器配置。

public class TomcatWebServerFactoryCustomizer implements WebServerFactoryCustomizer<ConfigurableTomcatWebServerFactory>, Ordered {
		
	private final ServerProperties serverProperties;

	@Override
	public void customize(ConfigurableTomcatWebServerFactory factory) {
		ServerProperties properties = this.serverProperties;
		ServerProperties.Tomcat tomcatProperties = properties.getTomcat();
		PropertyMapper propertyMapper = PropertyMapper.get();
		propertyMapper.from(tomcatProperties::getBasedir).whenNonNull().to(factory::setBaseDirectory);
		propertyMapper.from(tomcatProperties::getBackgroundProcessorDelay).whenNonNull().as(Duration::getSeconds)
				.as(Long::intValue).to(factory::setBackgroundProcessorDelay);
		customizeRemoteIpValve(factory);
		ServerProperties.Tomcat.Threads threadProperties = tomcatProperties.getThreads();
		propertyMapper.from(threadProperties::getMax).when(this::isPositive).to((maxThreads) -> customizeMaxThreads(factory, threadProperties.getMax()));
		...
		customizeStaticResources(factory);
		customizeErrorReportValve(properties.getError(), factory);
	}
}

此時我們大致瞭解,那我們就從入口開始分析。

2.2.執行程式時的流程

1.首先執行程式的main()方法,然後會呼叫run()方法。

在這裡插入圖片描述

2.執行run方法時,裡面會建立並初始化我們的IOC容器物件
 

在這裡插入圖片描述


3.在建立ApplicationContext的IOC容器物件時,在ServletWebServerApplicationContext有一個onRefresh()的方法,通過呼叫createWebServer()方法,建立WebServer。
 

在這裡插入圖片描述


 

在這裡插入圖片描述


 

在這裡插入圖片描述


4.來到createWebServer()方法,該方法通過裡面的getWebServerFactory()方法,通過名稱匹配最終能夠獲取到一個與當前應用(根據我們匯入的依賴servlet容器來獲取)所匯入的Servlet型別相匹配的web服務工廠,通過工廠就可以獲取到相應的 WebServerFactoryCustomizer (Web服務工廠定製器)

注:createWebServer()執行後,我們就到了 EmbeddedWebServerFactoryCustomizerAutoConfiguration,然後根據條件(配置的依賴)配置哪一個Web伺服器
 

在這裡插入圖片描述


 

在這裡插入圖片描述


5.我們點進factory.getWebServer()方法,找到了ServletWebServerFactory介面,因為我們前面繫結了tomcat,所以這裡會建立它的實現類TomcatServletWebServerFactory
 

在這裡插入圖片描述


到這裡位置,TomcatWebServerFactoryCustomizer元件建立完成,對應的服務配置類也已新增到IOC容器。
 

在這裡插入圖片描述


6.因為容器中某個元件要建立物件就會驚動後置處理器 然後就到WebServerFactoryCustomizerBeanPostProcessor(web服務工廠定製器元件的後置處理器),該類負責在bean元件初始化之前執行初始化工作。它先從IOC容器中獲取所有型別為WebServerFactoryCustomizerBeans(web服務工廠定製器的元件)

在這裡插入圖片描述


再通過後置處理器獲取到的TomcatWebServerFactoryCustomizer呼叫**customize()**定製方法,獲取到Servlet容器相關配置類ServerProperties,進行自動配置

	@Override
	public void customize(ConfigurableTomcatWebServerFactory factory) {
		ServerProperties properties = this.serverProperties;
		ServerProperties.Tomcat tomcatProperties = properties.getTomcat();
		PropertyMapper propertyMapper = PropertyMapper.get();
		propertyMapper.from(tomcatProperties::getBasedir).whenNonNull().to(factory::setBaseDirectory);
		propertyMapper.from(tomcatProperties::getBackgroundProcessorDelay).whenNonNull().as(Duration::getSeconds)
				.as(Long::intValue).to(factory::setBackgroundProcessorDelay);
		customizeRemoteIpValve(factory);
		ServerProperties.Tomcat.Threads threadProperties = tomcatProperties.getThreads();
		propertyMapper.from(threadProperties::getMax).when(this::isPositive)
				.to((maxThreads) -> customizeMaxThreads(factory, threadProperties.getMax()));
		propertyMapper.from(threadProperties::getMinSpare).when(this::isPositive)
				.to((minSpareThreads) -> customizeMinThreads(factory, minSpareThreads));
		propertyMapper.from(this.serverProperties.getMaxHttpHeaderSize()).whenNonNull().asInt(DataSize::toBytes)
				.when(this::isPositive)
				.to((maxHttpHeaderSize) -> customizeMaxHttpHeaderSize(factory, maxHttpHeaderSize));
		propertyMapper.from(tomcatProperties::getMaxSwallowSize).whenNonNull().asInt(DataSize::toBytes)
				.to((maxSwallowSize) -> customizeMaxSwallowSize(factory, maxSwallowSize));
		propertyMapper.from(tomcatProperties::getMaxHttpFormPostSize).asInt(DataSize::toBytes)
				.when((maxHttpFormPostSize) -> maxHttpFormPostSize != 0)
				.to((maxHttpFormPostSize) -> customizeMaxHttpFormPostSize(factory, maxHttpFormPostSize));
		propertyMapper.from(tomcatProperties::getAccesslog).when(ServerProperties.Tomcat.Accesslog::isEnabled)
				.to((enabled) -> customizeAccessLog(factory));
		propertyMapper.from(tomcatProperties::getUriEncoding).whenNonNull().to(factory::setUriEncoding);
		propertyMapper.from(tomcatProperties::getConnectionTimeout).whenNonNull()
				.to((connectionTimeout) -> customizeConnectionTimeout(factory, connectionTimeout));
		propertyMapper.from(tomcatProperties::getMaxConnections).when(this::isPositive)
				.to((maxConnections) -> customizeMaxConnections(factory, maxConnections));
		propertyMapper.from(tomcatProperties::getAcceptCount).when(this::isPositive)
				.to((acceptCount) -> customizeAcceptCount(factory, acceptCount));
		propertyMapper.from(tomcatProperties::getProcessorCache)
				.to((processorCache) -> customizeProcessorCache(factory, processorCache));
		propertyMapper.from(tomcatProperties::getRelaxedPathChars).as(this::joinCharacters).whenHasText()
				.to((relaxedChars) -> customizeRelaxedPathChars(factory, relaxedChars));
		propertyMapper.from(tomcatProperties::getRelaxedQueryChars).as(this::joinCharacters).whenHasText()
				.to((relaxedChars) -> customizeRelaxedQueryChars(factory, relaxedChars));
		customizeStaticResources(factory);
		customizeErrorReportValve(properties.getError(), factory);
	}

到這裡為止,嵌入式Servlet容器的自動配置完成。

綜上所述,我們有兩種解決方案用於配置嵌入式Servlet容器。
1.在全域性配置檔案application.properties中修改與server有關的配置

server.port=8090
server.tomcat.uri-encoding=UTF-8
server.tomcat.xxx…

2、實現WebServerFactoryCustomizer介面,重寫它的customize()方法,對容器進行定製配置:

@FunctionalInterface
public interface WebServerFactoryCustomizer<T extends WebServerFactory> {
    void customize(T factory);
}

3.嵌入式servlet容器啟動原理

1、應用啟動後,根據匯入的依賴資訊,建立了相應的Servlet容器工廠,建立了TomcatServletWebServerFactory,呼叫重寫的getWebServer()方法建立Tomcat容器。

public class TomcatServletWebServerFactory extends AbstractServletWebServerFactory implements ConfigurableTomcatWebServerFactory, ResourceLoaderAware {

	...

	public WebServer getWebServer(ServletContextInitializer... initializers) {
        if (this.disableMBeanRegistry) {
            Registry.disableRegistry();
        }

        Tomcat tomcat = new Tomcat();
        File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
        tomcat.setBaseDir(baseDir.getAbsolutePath());
        Connector connector = new Connector(this.protocol);
        connector.setThrowOnFailure(true);
        tomcat.getService().addConnector(connector);
        this.customizeConnector(connector);
        tomcat.setConnector(connector);
        tomcat.getHost().setAutoDeploy(false);
        this.configureEngine(tomcat.getEngine());
        Iterator var5 = this.additionalTomcatConnectors.iterator();

        while(var5.hasNext()) {
            Connector additionalConnector = (Connector)var5.next();
            tomcat.getService().addConnector(additionalConnector);
        }

        this.prepareContext(tomcat.getHost(), initializers);
        return this.getTomcatWebServer(tomcat);
    }
    
...

}

2.找到這個方法的返回值中this.getTomcatWebServer(tomcat),繼續向下尋找該方法
 

在這裡插入圖片描述


3.進入TomcatWebServer檢視它的有參構造器,發現其中執行了一個initialize() 初始化方法,跟進檢視。
 

在這裡插入圖片描述


4.通過 this.tomcat.start(); tomcat啟動了。

在這裡插入圖片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值