嵌入式Servlet容器原理
Spring Boot 默认支持的三种Servlet容器 Tomcat(默认) Jetty Undertow Netty
切换Servlet容器
将tomcat 改成 undertow
- 排除默认的tomcat
- 添加undertow依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
Servlet 容器的自动装配原理 以Tomcat为例
-
以WebMvcAutoConfiguration 为入口 找到DispatcherServletAutoConfiguration 找到ServletWebServerFactoryAutoConfiguration 此类是容器自动装配核心
-
该类@Import 导入了 各种Servlet容器 以Tomcat为例 首先判断一下环境中是否有Tomcat.class 保证是tomcat的环境 因为web-starter默认带的是tomcat依赖,此时这段配置生效
-
分析配置 往IOC容器中添加一个TomcatServletWebServerFactory
@Bean TomcatServletWebServerFactory tomcatServletWebServerFactory( ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers, ObjectProvider<TomcatContextCustomizer> contextCustomizers, ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) { // 核心就是 new 了一个TomcatServlet web服务器工厂 添加了各种自定义配置Customizers TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); 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; }
第二步
-
web应用会创建一个web的IOC容器ServletWebServerApplicationContext
-
在该类中有一个onRefresh方法 调用了createWebServer方法创建容器
@Override protected void onRefresh() { super.onRefresh(); try { // 核心方法 createWebServer(); } catch (Throwable ex) { throw new ApplicationContextException("Unable to start web server", ex); } }
-
createWebServer方法分析 从IOC容器中拿到ServletWebServerFactory 调用工厂的getWebServer方法得到一个WebServer
private void createWebServer() { WebServer webServer = this.webServer; ServletContext servletContext = getServletContext(); if (webServer == null && servletContext == null) { ServletWebServerFactory factory = getWebServerFactory(); this.webServer = factory.getWebServer(getSelfInitializer()); 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 定义了容器里面只能有一个 Web容器 如果出现多个容器会抛出异常,调用方法之后此时就会拿到之前装配进去的TomcatServletWebServerFactory然后调用他的getWebServer方法
protected ServletWebServerFactory getWebServerFactory() { // Use bean names so that we don't consider the hierarchy String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class); 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)); } return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class); }
-
getWebServer方法 new Tomcat() 设置一些配置参数 相当于把之前启动tomcat的逻辑用代码实现了
@Override public WebServer getWebServer(ServletContextInitializer... initializers) { if (this.disableMBeanRegistry) { Registry.disableRegistry(); } // 创建Tomcat实例 Tomcat tomcat = new Tomcat(); // 设置各种Tomcat 参数 File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat"); tomcat.setBaseDir(baseDir.getAbsolutePath()); Connector connector = new Connector(this.protocol); connector.setThrowOnFailure(true); tomcat.getService().addConnector(connector); 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); // 获取Tomcat WebServer return getTomcatWebServer(tomcat); }
-
getTomcatWebServer方法底层其实new TomcatWebServer
// 构造器 public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) { Assert.notNull(tomcat, "Tomcat Server must not be null"); this.tomcat = tomcat; this.autoStart = autoStart; this.gracefulShutdown = (shutdown == Shutdown.GRACEFUL) ? new GracefulShutdown(tomcat) : null; // 初始化方法 initialize(); } private void initialize() throws WebServerException { logger.info("Tomcat initialized with port(s): " + getPortsDescription(false)); synchronized (this.monitor) { try { // 各种添加监听器 省略 // tomcat启动 这一行执行 Tomcat Server才真正的启动完成 this.tomcat.start(); // 其他非核心逻辑省略 } catch (Exception ex) { stopSilently(); destroySilently(); throw new WebServerException("Unable to start embedded Tomcat", ex); } } }
自定义Servlet容器的三种方式
- 实现WebServerFactoryCustomizer可以修改一些底层的默认配置
- 将配置文件与ServletWebServerFacotry绑定
- 修改配置文件server.xxx属性 (推荐)
- 直接自定义ConfigurableServletWebServerFactory
@Component
public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
@Override
public void customize(ConfigurableServletWebServerFactory server) {
server.setPort(9000);
}
}