SpringBoot2.4.0中嵌入式servlet容器的自动配置以及启动原理(自我理解)

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启动了。
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

舍其小伙伴

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值