SpringBoot嵌入式Servlet容器与定制化组件超详细讲解
作者:Decade0712 发布时间:2023-03-31 09:07:05
嵌入式Servlet容器
在Spring Boot中,默认支持的web容器有 Tomcat, Jetty, 和 Undertow
1、原理分析
那么这些web容器是怎么注入的呢?我们一起来分析一下
当SpringBoot应用启动发现当前是Web应用,它会创建一个web版的ioc容器ServletWebServerApplicationContext
这个类下面有一个createWebServer()
方法,当执行关键代码ServletWebServerFactory factory = this.getWebServerFactory();
时,它会在系统启动的时候寻找 ServletWebServerFactory
(Servlet 的web服务器工厂—> 用于生产Servlet 的web服务器)
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = this.getServletContext();
if (webServer == null && servletContext == null) {
StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
// 获取ServletWebFactory
ServletWebServerFactory factory = this.getWebServerFactory();
createWebServer.tag("factory", factory.getClass().toString());
// 这里会去调用系统中获取到的web容器工厂类的getWebServer()方法
this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
createWebServer.end();
this.getBeanFactory().registerSingleton("webServerGracefulShutdown", new WebServerGracefulShutdownLifecycle(this.webServer));
this.getBeanFactory().registerSingleton("webServerStartStop", new WebServerStartStopLifecycle(this, this.webServer));
} else if (servletContext != null) {
try {
this.getSelfInitializer().onStartup(servletContext);
} catch (ServletException var5) {
throw new ApplicationContextException("Cannot initialize servlet context", var5);
}
}
this.initPropertySources();
}
获取ServletWebFactory
protected ServletWebServerFactory getWebServerFactory() {
String[] beanNames = this.getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
if (beanNames.length == 0) {
throw new MissingWebServerFactoryBeanException(this.getClass(), ServletWebServerFactory.class, WebApplicationType.SERVLET);
} else if (beanNames.length > 1) {
throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
} else {
return (ServletWebServerFactory)this.getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}
}
SpringBoot底层默认有很多的WebServer工厂:TomcatServletWebServerFactory
,,JettyServletWebServerFactory
和 UndertowServletWebServerFactory
那么究竟返回哪一个工厂呢?
我们需要分析一下底层的自动配置类,ServletWebServerFactoryAutoConfiguration
@AutoConfiguration
@AutoConfigureOrder(-2147483648)
@ConditionalOnClass({ServletRequest.class})
@ConditionalOnWebApplication(
type = Type.SERVLET
)
@EnableConfigurationProperties({ServerProperties.class})
@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, EmbeddedTomcat.class, EmbeddedJetty.class, EmbeddedUndertow.class})
public class ServletWebServerFactoryAutoConfiguration {
public ServletWebServerFactoryAutoConfiguration() {
}
...
它引入了一个配置类ServletWebServerFactoryConfiguration
,这个类里面会根据动态判断系统中到底导入了那个Web服务器的包,然后去创建对应的web容器工厂,spring-boot-starter-web
这个依赖默认导入tomcat,所以我们系统会创建TomcatServletWebServerFactory
,由这个工厂创建tomcat容器并启动
一旦我们获取到web Server的工厂类,createWebServer()
方法就会去调用this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
根据断点一直深入,我们可以发现,Tomcat, Jetty, 和 Undertow的工厂类最后都会去调用getWebServer()
方法,设置了链接参数,例如TomcatServletWebServerFactory
的getWebServer()
方法
在方法的最后,它会执行return this.getTomcatWebServer(tomcat);
,跟着断点深入,我们发现它会去调用对应web容器类的构造方法,如TomcatWebServer
的构造方法,启动tomcat容器
public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
this.monitor = new Object();
this.serviceConnectors = new HashMap();
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---会调用this.tomcat.start();启动容器
this.initialize();
}
2、Servlet容器切换
Spring Boot默认使用的是tomcat容器,那如果我们想要使用Undertow应该如何切换呢
只需要修改pom文件即可,排除web启动器中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>
3、定制Servlet容器配置
如果想要自己定义一个Servlet容器,可以通过哪些途径呢?
通过分析
ServletWebServerFactoryAutoConfiguration
绑定了ServerProperties
配置类可知,我们想要修改容器的配置,只需要修改配置文件中对应的server.xxx
配置项即可创建一个配置类,通过@Configuration+@Bean的方式,向容器中注入一个
ConfigurableServletWebServerFactory
类的实现类,ConfigurableServletWebServerFactory
是ServletWebServerFactory
类的子类,提供了很多方法供我们使用
代码样例如下
package com.decade.config;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyConfig {
@Bean
public ConfigurableServletWebServerFactory defineWebServletFactory() {
final TomcatServletWebServerFactory tomcatServletWebServerFactory = new TomcatServletWebServerFactory();
tomcatServletWebServerFactory.setPort(8081);
return tomcatServletWebServerFactory;
}
}
自定义一个ServletWebServerFactoryCustomizer
类,它的下面有一个customize()
方法,能把配置文件的值和ServletWebServerFactory 进行绑定
Spring官网提供的样例如下
Spring中有很多xxxxxCustomizer,它的作用是定制化器,可以改变xxxx的默认规则
定制化组件
结合之前的原理分析过程可知,我们分析一个组件的过程可以概括为:
导入对应启动器xxx-starter---->分析xxxAutoConfiguration---->导入xxx组件---->绑定xxxProperties配置类----->绑定配置项
那么如果我们要定制化组件,例如自定义参数解析器或者应用启动端口等,可以怎么做呢?
修改配置文件 server.xxx
参考上面编写一个xxxxxCustomizer类
编写自定义的配置类xxxConfiguration:使用@Configuration + @Bean替换、增加容器中默认组件
如果是Web应用,编写一个配置类实现
WebMvcConfigurer
接口,重写对应方法即可定制化web功能,或者使用@Bean给容器中再扩展一些组件(这条是最重要的)
注意:@EnableWebMvc + 实现WebMvcConfigurer
接口:配置类中定义的@Bean可以全面接管SpringMVC,所有规则全部自己重新配置
原理:
WebMvcAutoConfiguration
类是SpringMVC的自动配置功能类。配置了静态资源、欢迎页…一旦使用
@EnableWebMvc
会,@Import(DelegatingWebMvcConfiguration.class)
DelegatingWebMvcConfiguration
类的作用是:只保证SpringMVC最基本的使用
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport
表明它是WebMvcConfigurationSupport
的子类它会把所有系统中的 WebMvcConfigurer的实现类拿过来,所有功能的定制都是这些WebMvcConfigurer的实现类合起来一起生效
WebMvcConfigurationSupport
自动配置了一些非常底层的组件,例如RequestMappingHandlerMapping,这些组件依赖的其他组件都是从容器中获取的,例如ContentNegotiationManager等
由代码可知,WebMvcAutoConfiguration
里面的配置要能生效必须系统中不存在WebMvcConfigurationSupport
类,所以,一旦配置类上加了@EnableWebMvc
,就会导致WebMvcAutoConfiguration
没有生效
来源:https://blog.csdn.net/Decade0712/article/details/127025304


猜你喜欢
- 本文实例为大家分享了Android实现拼图小游戏的具体代码,供大家参考,具体内容如下目标效果: 1.activity_main.x
- 1、多态性多态性是面向对象的最后一个特征,它本身主要分为两个方面: 方法的多态性:重载与覆写1、重载:同一个方法名称,根据参数类型以及个数完
- 概述从今天开始, 小白我将带大家开启 Java 数据结构 & 算法的新篇章.冒泡排序冒泡排序 (Bubble Sort) 是一种简单
- 今天来学习总结一下,Android 后添加的一些新的组件和UI效果,Material Dialog,SwipeRefreshLayout,L
- 1. 前言对于写Crud的老司机来说时间非常宝贵,一些样板代码写不但费时费力,而且枯燥无味。经常有小伙伴问我,胖哥你怎么天天那么有时间去搞新
- c++优先队列(priority_queue)用法详解普通的队列是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除。在优先队列中,元
- Servlet3.0的出现是servlet史上最大的变革,其中的许多新特性大大的简化了web应用的开发,为广大劳苦的程序员减轻了压力,提高了
- 最近由于项目需求,需要做一个listview中的item策划删除的效果,与是查找资料和参考了一些相关的博客,终于完美实现了策划删除的效果。先
- 本文实例为大家分享了C#添加读取Word脚注尾注的具体代码,供大家参考,具体内容如下脚注和尾注是对文本的补充说明。脚注一般位于页面的底部,可
- 一、创建web项目1、打开idea软件,点击界面上的Create New Project2、进入如下界面。选中 java Enterpris
- 我的安卓开发经历始于一个原生安卓项目开发。后来由于公司有个项目与几家医疗设备公司合作,需要我写安卓端的桥接代码给 react native
- 1.为什么要使用synchronized在并发编程中存在线程安全问题,主要原因有:1.存在共享数据 2.多线程共同操作共享数据。关键字syn
- 一、创建一个cs文件,定义Time 对象 public class WebTimer_AutoRepayment{ &n
- Pattern类定义 public final class Pattern
- 目录一、数组、集合和 LINQ1、数组二、字符串内插三、模式匹配四、委托和 Lambda 表达式五、async/await六、属性一、数组、
- 在 C# 中,程序中在运行时出现的错误,会不断在程序中进行传播,这种机制称为“异常”。异常通常由错误的代码引发,并由能够更正错误的代码进行
- springBoot是java开发中会经常用到的框架,那么在实际项目中项目配置了springBoot框架,应该如何在项目中读取配置文件中的参
- 本文实例讲述了C#通过属性名字符串获取、设置对象属性值操作.分享给大家供大家参考,具体如下:#通过反射获取对象属性值并设置属性值0、定义一个
- 一、前言对于任何框架而言,在使用前都要进行一系列的初始化,MyBatis也不例外。二、MyBatis的初始化做了什么2.1 Mybatis的
- 项目里一直用的是 spring-security ,不得不说,spring-security 真是东西太多了,学习难度太大(可能我比较菜),