传统开发过程
写一个web应用,将web打成war包,需要在web配好Tomcat环境,Tomcat就是一个Servlet容器,把我们的war包放在Tomcat上,即可启动。
嵌入式Servlet容器
Spring Boot默认使用的是嵌入式的Servlet容器(比如Tomcat);
spring-boot-starter-web --> spring-boot-starter-tomcat --> 下图中Tomcat版本为9.0.36
通过上图依赖树中可以看出,Spring Boot默认使用嵌入式的Servlet容器。
问题?
-
如何定制和修改Servlet容器的相关配置;
-
改ServerProperties类中的设置(在application.yml配置文件中写)1. 通用的容器Servlet容器设置
server.xxx server.port=8081 //修改和server有关的配置
2. 跟Tomcat有关的设置
server.tomcat.uri-encoding = UTF-8
- 编写一个WebServerFactoryCustomizer(嵌入式的Servlet容器的定制器) (写一个配置类)
扩展:在Spring Boot中会有很多的xxxCustomizer帮助我们进行定制配置。
-
-
Spring Boot能否支持其他Servlet容器;
注册Servlet, Filter, Listner
创建的Spring Boot应用,默认是以Jar包的方式启用嵌入式的Tomcat,而不是Web应用的目录结构,因为没有web-app/WEB-INF/web.xml(注册web三大组件的配置文件)。
所以在Spring Boot中是如何注册三大组件的呢?
Spring Boot提供了ServletRegistrationBean, FilterRegistrationBean, 和ServletListenerRegistrationBean.
下图为如何定制Servlet及如何注册到容器中:
注册自定制Filter和Listner如下图:
Filter是什么可以参考博文:Servlet之Filter详细讲解,这篇博文讲了如何利用filter做“禁用所有动态页面的缓存过滤器” 及 “分IP统计网站的访问次数过滤器”
拦截路径:
SpringBoot帮我们自动配置Spring MVC的时候,帮我们自动注册了SpringMVC的前端控制器:DipatcherServlet;
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet, WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet, webMvcProperties.getServlet().getPath());
registration.setName("dispatcherServlet");
registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
multipartConfig.ifAvailable(registration::setMultipartConfig);
return registration;
}
有注册dispatcherServlet的源代码可以看到,默认拦截的请求路径为webMvcProperties.getServlet().getPath()
所以我们可以通过server.servletPath来修改SpringMVC前端控制器默认拦截的请求路径
使用其他嵌入式Servlet容器
Spring Boot不只支持Tomcat作为嵌入式Servlet容器,还支持以下两种,他们各自的特性如下
- Jetty(长连接)
- Undertow(不支持JSP)
可以自动切换:
1. 排除原有依赖中对spring-boot-starter-tomcat的依赖
2. 添加对spring-boot-starter-jetty的依赖
如果不做修改,就是默认使用Tomcat。
当然,如果以上三种不满足要求时,也可以使用外置的Servlet,以下是使用外置Tomcat的步骤:
1. 必须创建一个war项目
2. 将嵌入式的Tomca指定为provided
3. 必须编写一个SpringBootServletInitialzer的子类,并调用configure方法
4. 启动服务器
这个时候在main函数直接启动程序,是不会成功的。
原理:
jar包:执行Spring Boot主类的main方法,启动IOC容器,创建嵌入式的Servlet容器;
war包:启动服务器,服务器启动Spring Boot容器[SpringBootServletInitialzer], 启动IOC容器
嵌入式Servlet配置原理
根据@ConditainalOnClass里的判断条件,判定该创建那个类型的Servlet工厂,所以验证刚才前面所说的可以通过改依赖的方式切换Servlet类型。
这之后的源码非常难懂,但是一般项目不会改动到Servlet类型,也不会触及怎样修改Tomcat本身的属性,之后再来看。
嵌入式Servlet容器启动原理
获取嵌入式的Servlet容器工厂:
1. Spring Boot应用启动运行run方法
2. run方法中有一步是refreshContext(context); Spring Boot刷新IOC容器【创建IOC容器对象,并初始化容器,创建容器中的每一个组件(bean)】
刷新容器之前,执行过一步创建容器createApplicationContext,创建时会做判断,如果是web应用,则创建AnnotationConfigServletWebServerApplicationContext👇
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
Web类型的IOC容器,和普通IOC容器:
DEFAULT_SERVLET_WEB_CONTEXT_CLASS:AnnotationConfigServletWebServerApplicationContext
DEFAULT_CONTEXT_CLASS:AnnotationConfigApplicationContext
3. refresh(context);刷新刚才创建好的IOC容器
4. 在上一步的刷新中,会执行onRefresh()方法,web的ioc容器重写了onRefresh方法
5. web IOC容器会创建servlet方法 createEmbeddedServletContainer()
6. 获取嵌入式的Servlet容器工厂
7. 使用容器工厂获取嵌入式的servlet容器
8. 嵌入式的Servlet容器创建对象并启动Servlet容器
先启动嵌入式的Servlet容器,再将ioc容器中剩下没有创建出的对象获取出来;