SpringBoot的嵌入式servlet容器

  • 1、SpringBoot默认的servlet容器
  • 2、嵌入式servlet配置修改
    • 2.1、通过全局配置文件修改
    • 2.2、通过WebServerFactoryCustomizer接口的Bean修改
  • 3、注册servlet三大组件
    • 3.1、servlet3.0规范提供的注解方式注册
    • 3.2、SpringBoot提供的servlet三大组件注册
  • 4、切换其它嵌入式servlet容器
  • 5、嵌入式servlet容器自动配置原理
    • 5.1、为什么可以根据配置的依赖自动使用对应的servlet容器?
    • 5.2、怎么根据配置文件中server.xxx设置servlet容器的一些参数?
    • 5.3、怎么让所有的WebServerFactoryCustomizer Bean一一配合使用?
    • 5.4、嵌入式的tomcat容器是如何启动的?

1、SpringBoot默认的servlet容器

SpringBoot默认的servlet容器是tomcat,如下图所示:

2、嵌入式servlet配置修改

2.1、通过全局配置文件修改

1、可以通过 server.xxx 来进行 web 服务配置,没有带服务器名称的则是通用配置,如下所示:
2、通过带了具体的服务器名称则是单独对该服务器进行设置,比如 server.tomcat.xxx 就是专门针对 tomcat 的配置。

server.port=8081
server.servlet.context-path=/tomcat

2.2、通过WebServerFactoryCustomizer接口的Bean修改

我们创建一个类实现于WebServerFactoryCustomizer接口,在里面的配置会优先于全局配置的配置,且会互补,代码如下所示:

package cool.ale.component;import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.stereotype.Component;@Component
public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {@Overridepublic void customize(ConfigurableServletWebServerFactory server) {server.setPort(8090);}
}

3、注册servlet三大组件

首先我们需要知道servlet的三大组件都是什么
servlet的三大组件是:servlet、listener、filter
我们有两种方式进行注册

3.1、servlet3.0规范提供的注解方式注册

我们需要先创建一个类,导入这几个注解其中之一即可。

@WebServlet
@WebListener
@WebFilter

然后在启动类中加入可以扫描以上注解的注解@ServletComponentScan,如下所示:

package cool.ale;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;@SpringBootApplication
@ServletComponentScan
public class SpringbootServletApplication {public static void main(String[] args) {SpringApplication.run(SpringbootServletApplication.class, args);}}

然后再完善一下刚才创建的类,如下所示,启动SpringBoot后,即可访问。

package cool.ale.servlet;import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;@WebServlet(name = "HelloServlet",urlPatterns = "/HelloServlet")
public class HelloServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {PrintWriter writer = resp.getWriter();writer.println("hello servlet!");}
}

3.2、SpringBoot提供的servlet三大组件注册

可以将 Servlet、Filter、Listener注册为相应的Spring Bean
可以使用以下三个Bean注册:ServletRegistrationBean、FilterRegistrationBean、ServletListenerRegistrationBean,将这三个类其中一个注册到配置文件中并返回即可。

先创建一个servlet类,如下所示:

package cool.ale.servlet;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;public class BeanServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {PrintWriter writer = resp.getWriter();writer.println("hello beanServlet!");}
}

然后创建一个配置类,返回上面ServletRegistrationBean类,并将我们刚才创建的servlet类配置进去,如下所示:

package cool.ale.config;import cool.ale.servlet.BeanServlet;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class WebMvcConfiguration {@Beanpublic ServletRegistrationBean myServlet(){// 声明一个 servlet 注册器 BeanServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean();// 设置相应的 servletservletRegistrationBean.setServlet(new BeanServlet());// 设置名称servletRegistrationBean.setName("BeanServlet");// 添加映射规则servletRegistrationBean.addUrlMappings("/BeanServlet");return servletRegistrationBean;}}

直接启动访问即可。

4、切换其它嵌入式servlet容器

SpringBoot包含对嵌入式Tomcat、Jetty和Undertow服务器的支持
tomcat(默认的嵌入式servlet容器)
Jetty(一般用于长链接,比如socket等)
Undertow(一般用于无阻塞式、响应式等)

一般我们需要切换成其它两个servlet容器时,需要操作以下两个步骤
1、排除掉tomcat服务器,在下面图中选中

然后代码中会对应如下所示:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><exclusions><exclusion><artifactId>spring-boot-starter-tomcat</artifactId><groupId>org.springframework.boot</groupId></exclusion></exclusions>
</dependency>

2、在pom文件中加上jetty的依赖或者undertow的依赖,如下所示:

<!-- jetty的依赖 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jetty</artifactId>
</dependency><!-- undertow的依赖 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-undertow</artifactId>
</dependency>

启动即可。

5、嵌入式servlet容器自动配置原理

我们在进行源码跟踪之前,先要提出几个问题,以方便我们寻求我们去跟踪源码的目的。
1、为什么可以根据配置的依赖自动使用对应的servlet容器。
2、怎么根据配置文件中server.xxx 以及 继承WebServerFactoryCustomizer接口时可以去设置servlet容器的一些参数。
3、怎么让所有的WebServerFactoryCustomizer Bean一一配合使用?
4、嵌入式的tomcat容器是如何启动的。
下来我们就根据这几个问题一起来进入servlet自动配置类来看看原理。
ServletWebServerFactoryAutoConfiguration。

先分析一下该类的注解

@Configuration(proxyBeanMethods = false
)
@AutoConfigureOrder(-2147483648)
// 只要依赖了任意一个servlet容器,都会依赖该类
@ConditionalOnClass({ServletRequest.class})
@ConditionalOnWebApplication(type = Type.SERVLET
)
// 启用了 server.xxx 的配置绑定到了当前类上,具体可分析ServerProperties.class
@EnableConfigurationProperties({ServerProperties.class})
@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, EmbeddedTomcat.class, EmbeddedJetty.class, EmbeddedUndertow.class})
public class ServletWebServerFactoryAutoConfiguration {
}

5.1、为什么可以根据配置的依赖自动使用对应的servlet容器?

由于 ServletWebServerFactoryAutoConfiguration 类导入了三个类,如下所示:

@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, EmbeddedTomcat.class, EmbeddedJetty.class, EmbeddedUndertow.class})
public class ServletWebServerFactoryAutoConfiguration {
}

在上面导入的这几个类中,我们可以随意的点击一个进去,就比如,我点击EmbeddedTomcat.class进到实现,这块注解写着当有Tomcat.class类存在于IOC容器中,tomcat的配置将生效。

@Configuration(proxyBeanMethods = false)// 依赖于Servlet.class, Tomcat.class, UpgradeProtocol.class三个类@ConditionalOnClass({Servlet.class, Tomcat.class, UpgradeProtocol.class})@ConditionalOnMissingBean(value = {ServletWebServerFactory.class},search = SearchStrategy.CURRENT)static class EmbeddedTomcat {EmbeddedTomcat() {}
}

5.2、怎么根据配置文件中server.xxx设置servlet容器的一些参数?

在ServletWebServerFactoryAutoConfiguration类中我们看到了如下方法,ServletWebServerFactoryCustomizer 类也是实现了WebServerFactoryCustomizer类,用于servlet容器的自动化配置,代码如下所示:

@Bean
// 通过WebServerFactoryCustomizer接口的Bean修改servlet配置
public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties, ObjectProvider<WebListenerRegistrar> webListenerRegistrars) {return new ServletWebServerFactoryCustomizer(serverProperties, (List)webListenerRegistrars.orderedStream().collect(Collectors.toList()));
}

当我们点击 ServletWebServerFactoryCustomizer 类看到它里面的customize方法,是使用了jdk8的新特性的方法,从配置文件里面拿出相应的配置参数来修改默认值:

public void customize(ConfigurableServletWebServerFactory factory) {PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();ServerProperties var10001 = this.serverProperties;var10001.getClass();map.from(var10001::getPort).to(factory::setPort);var10001 = this.serverProperties;var10001.getClass();map.from(var10001::getAddress).to(factory::setAddress);Servlet var5 = this.serverProperties.getServlet();var5.getClass();map.from(var5::getContextPath).to(factory::setContextPath);var5 = this.serverProperties.getServlet();var5.getClass();map.from(var5::getApplicationDisplayName).to(factory::setDisplayName);var5 = this.serverProperties.getServlet();var5.getClass();map.from(var5::isRegisterDefaultServlet).to(factory::setRegisterDefaultServlet);var5 = this.serverProperties.getServlet();var5.getClass();map.from(var5::getSession).to(factory::setSession);var10001 = this.serverProperties;var10001.getClass();map.from(var10001::getSsl).to(factory::setSsl);var5 = this.serverProperties.getServlet();var5.getClass();map.from(var5::getJsp).to(factory::setJsp);var10001 = this.serverProperties;var10001.getClass();map.from(var10001::getCompression).to(factory::setCompression);var10001 = this.serverProperties;var10001.getClass();map.from(var10001::getHttp2).to(factory::setHttp2);var10001 = this.serverProperties;var10001.getClass();map.from(var10001::getServerHeader).to(factory::setServerHeader);var5 = this.serverProperties.getServlet();var5.getClass();map.from(var5::getContextParameters).to(factory::setInitParameters);map.from(this.serverProperties.getShutdown()).to(factory::setShutdown);Iterator var3 = this.webListenerRegistrars.iterator();while(var3.hasNext()) {WebListenerRegistrar registrar = (WebListenerRegistrar)var3.next();registrar.register(factory);}}

当然,类似于下面这个方法,和上面的结构一样,是用来配置tomcat的参数,代码如下,也可以点击进去看相应的 customize 方法

@Bean
@ConditionalOnClass(name = {"org.apache.catalina.startup.Tomcat"}
)
public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(ServerProperties serverProperties) {return new TomcatServletWebServerFactoryCustomizer(serverProperties);
}

5.3、怎么让所有的WebServerFactoryCustomizer Bean一一配合使用?

我们都知道,当我们创建了一个类实现了 WebServerFactoryCustomizer 接口之后,在这个类里面就可以去设置相应的servlet容器的参数配置,但是这些参数和全局配置是互补的,所以下面需要研究一下为什么是互补的,其实重点就在BeanPostProcessorsRegistrar类上,这个类就是我们的自动配置文件导入进来的第一个类,代码如下所示:

@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, EmbeddedTomcat.class, EmbeddedJetty.class, EmbeddedUndertow.class})
public class ServletWebServerFactoryAutoConfiguration {
}

当我们点击进去之后,我们发现 BeanPostProcessorsRegistrar.class 这个类实现了 ImportBeanDefinitionRegistrar 接口,这个接口会提供一个方法registerBeanDefinitions,并提供Bean注册器使我们去注册Bean,代码如下:

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {if (this.beanFactory != null) {this.registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor", WebServerFactoryCustomizerBeanPostProcessor.class, WebServerFactoryCustomizerBeanPostProcessor::new);this.registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor", ErrorPageRegistrarBeanPostProcessor.class, ErrorPageRegistrarBeanPostProcessor::new);}
}

依照上面代码我们发现,注册了 WebServerFactoryCustomizerBeanPostProcessor.class 类,WebServerFactoryCustomizerBeanPostProcessor.class 类又实现了BeanPostProcessor,这个类在Bean初始化的时候提供了两个方法postProcessBeforeInitialization、postProcessAfterInitialization,我们看一下下面两个方法,就可以看出互补的地方:

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {// 每一个实现了 WebServerFactoryCustomizer 接口的类都会走到这里,所以是互补的if (bean instanceof WebServerFactory) {// 当是 WebServerFactory 的子类的时候,调用 postProcessBeforeInitialization 方法this.postProcessBeforeInitialization((WebServerFactory)bean);}return bean;
}public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {return bean;
}

进到 postProcessBeforeInitialization 方法去看一下,回调相应的customize方法,代码如下:

private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {// 调用 getCustomizers() 方法,获取到所有实现 WebServerFactoryCustomizer 的Bean((Callbacks)LambdaSafe.callbacks(WebServerFactoryCustomizer.class, this.getCustomizers(), webServerFactory, new Object[0]).withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)).// 这里的invoke() 方法就会拿到刚才获取到的所有实现 WebServerFactoryCustomizer 的Bean的数组去循环调用其对应的customize方法invoke((customizer) -> {customizer.customize(webServerFactory);});
}private Collection<WebServerFactoryCustomizer<?>> getCustomizers() {if (this.customizers == null) {this.customizers = new ArrayList(this.getWebServerFactoryCustomizerBeans());this.customizers.sort(AnnotationAwareOrderComparator.INSTANCE);this.customizers = Collections.unmodifiableList(this.customizers);}return this.customizers;
}

5.4、嵌入式的tomcat容器是如何启动的?

在我们程序.run的时候,会去加载Spring Ioc,加载Ioc的时候就会去调用下面的方法去启动tomcat。

// TomcatServletWebServerFactory
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);
}

SpringBoot的嵌入式servlet容器相关推荐

  1. SpringBoot配置嵌入式Servlet容器

    SpringBoot默认使用的是Tomcat作为嵌入式的Servlet容器,那么肯定会和外置的Tomcat有区别,那么就这些区别来谈一谈SpringBoot中对于容器的一些配置操作 如何定制和修改Se ...

  2. SpringBoot 配置嵌入式Servlet容器(tomcat,jetty,undertow)

    SpringBoot 默认打包方式为jar包,且可以自启动,就是因为它内嵌了Servlet容器. SpringBoot 默认使用嵌入式Servlet容器,SpringBoot 2.2.5 默认是 To ...

  3. idea servlet自动配置web.xml_Spring Boot学习04_嵌入式Servlet容器自动配置原理

    在Spring Boot的自动配置包下,找到web模块中的servlet文件夹下的ServletWebServerFactoryConfiguration类 一.嵌入式Servlet容器默认的配置原理 ...

  4. SpringBoot之配置嵌入式Servlet容器

    1.概述 文章目录 1.概述 2.如何修改SpringBoot的默认配置 3.定制和修改Servlet容器的相关配置 4.注册Servlet三大组件 5.替换为其他嵌入式Servlet容器 6.嵌入式 ...

  5. SpringBoot 嵌入式Servlet容器

    一.嵌入式Servlet容器 切换嵌入式Servlet容器 默认支持的webServer :Tomcat, Jetty, or Undertow ServletWebServerApplication ...

  6. Spring boot嵌入式Servlet容器定制器

    修改Servlet配置 可以通过配置文件修改 也可以编写一个嵌入式Servlet容器定制器 EmbeddedServletContainerCustomizer 在自定义配置中,添加组件 Embedd ...

  7. 嵌入式Servlet容器

    配置嵌入式Servlet容器 ##Spring Boot里面内置了嵌入式的Servlet容器(tomcat) 点击pom.xml->右键->Diagrams->show Depend ...

  8. servlet如何使用session把用户的手机号修改_SpringBoot源码学习系列之嵌入式Servlet容器...

    1.前言简单介绍 SpringBoot的自动配置就是SpringBoot的精髓所在:对于SpringBoot项目是不需要配置Tomcat.jetty等等Servlet容器,直接启动applicatio ...

  9. springboot(七) 配置嵌入式Servlet容器

    github代码地址:https://github.com/showkawa/springBoot_2017/tree/master/spb-demo/spb-brian-query-service ...

  10. Spring Boot配置嵌入式Servlet容器的两种方法

    一.前言 SpringBoot默认使用Tomcat作为嵌入式的Servlet容器 二.如何定制和修改Servlet容器的相关配置: 1.修改和server有关的配置(ServerProperties[ ...

最新文章

  1. 45种Javascript技巧大全
  2. 鼠标一点打开几个网页
  3. Uniform String
  4. java循环一年月份天数和_javawhile循环编写输入某年某月某日,判断这一天是这一年的第几…...
  5. c语言借阅管理题目内容描述,C语言 图书借阅管理统 第四组.doc
  6. 05-类--+-号使用
  7. Opencv打印显示Mat方法
  8. 【codevs1227】方格取数2,费用流
  9. Windows -- Qt不能进行调试 -- Unknown debugger type No Engine
  10. 谈了千百遍的缓存数据一致性问题
  11. webpack4配置(1)-打包一个js文件
  12. USACO / Mother's Milk (DFS)
  13. 软件工程第一次作业(第一次思考这些问题)
  14. 为什么快手不能左右滑了_快手上滑切换下一个视频怎么设置
  15. --go_out: protoc-gen-go: plugins are not supported问题处理
  16. DROP和DELETE的区别
  17. A Game of Thrones(24)
  18. ncs java 成都 面试_成都java工程师面试一般都是哪些问题,基础难不难!
  19. 2021年5月软件设计师考前总结笔记
  20. 汽车电子业务升级方式说明

热门文章

  1. 微信模拟地理位置_微信伪装地理位置是什么个原理
  2. 爱心打印函数(基于EasyX图形库)
  3. 广义线性模型之泊松回归
  4. 筑牢梅雨季用电“安全网”
  5. 浏览器内核信息和功能查看
  6. Java文件上传接口
  7. web前端vue融云即时通讯上手
  8. java向led屏下发字符串乱码_几种误解,以及乱码产生的原因和解决办法
  9. mysql +cobar_Cobar源码解析(一)
  10. qq服务器维护到什么时候,qq扩列功能怎么找不到了2021