SpringBoot的嵌入式servlet容器
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容器相关推荐
- SpringBoot配置嵌入式Servlet容器
SpringBoot默认使用的是Tomcat作为嵌入式的Servlet容器,那么肯定会和外置的Tomcat有区别,那么就这些区别来谈一谈SpringBoot中对于容器的一些配置操作 如何定制和修改Se ...
- SpringBoot 配置嵌入式Servlet容器(tomcat,jetty,undertow)
SpringBoot 默认打包方式为jar包,且可以自启动,就是因为它内嵌了Servlet容器. SpringBoot 默认使用嵌入式Servlet容器,SpringBoot 2.2.5 默认是 To ...
- idea servlet自动配置web.xml_Spring Boot学习04_嵌入式Servlet容器自动配置原理
在Spring Boot的自动配置包下,找到web模块中的servlet文件夹下的ServletWebServerFactoryConfiguration类 一.嵌入式Servlet容器默认的配置原理 ...
- SpringBoot之配置嵌入式Servlet容器
1.概述 文章目录 1.概述 2.如何修改SpringBoot的默认配置 3.定制和修改Servlet容器的相关配置 4.注册Servlet三大组件 5.替换为其他嵌入式Servlet容器 6.嵌入式 ...
- SpringBoot 嵌入式Servlet容器
一.嵌入式Servlet容器 切换嵌入式Servlet容器 默认支持的webServer :Tomcat, Jetty, or Undertow ServletWebServerApplication ...
- Spring boot嵌入式Servlet容器定制器
修改Servlet配置 可以通过配置文件修改 也可以编写一个嵌入式Servlet容器定制器 EmbeddedServletContainerCustomizer 在自定义配置中,添加组件 Embedd ...
- 嵌入式Servlet容器
配置嵌入式Servlet容器 ##Spring Boot里面内置了嵌入式的Servlet容器(tomcat) 点击pom.xml->右键->Diagrams->show Depend ...
- servlet如何使用session把用户的手机号修改_SpringBoot源码学习系列之嵌入式Servlet容器...
1.前言简单介绍 SpringBoot的自动配置就是SpringBoot的精髓所在:对于SpringBoot项目是不需要配置Tomcat.jetty等等Servlet容器,直接启动applicatio ...
- springboot(七) 配置嵌入式Servlet容器
github代码地址:https://github.com/showkawa/springBoot_2017/tree/master/spb-demo/spb-brian-query-service ...
- Spring Boot配置嵌入式Servlet容器的两种方法
一.前言 SpringBoot默认使用Tomcat作为嵌入式的Servlet容器 二.如何定制和修改Servlet容器的相关配置: 1.修改和server有关的配置(ServerProperties[ ...
最新文章
- 45种Javascript技巧大全
- 鼠标一点打开几个网页
- Uniform String
- java循环一年月份天数和_javawhile循环编写输入某年某月某日,判断这一天是这一年的第几…...
- c语言借阅管理题目内容描述,C语言 图书借阅管理统 第四组.doc
- 05-类--+-号使用
- Opencv打印显示Mat方法
- 【codevs1227】方格取数2,费用流
- Windows -- Qt不能进行调试 -- Unknown debugger type No Engine
- 谈了千百遍的缓存数据一致性问题
- webpack4配置(1)-打包一个js文件
- USACO / Mother's Milk (DFS)
- 软件工程第一次作业(第一次思考这些问题)
- 为什么快手不能左右滑了_快手上滑切换下一个视频怎么设置
- --go_out: protoc-gen-go: plugins are not supported问题处理
- DROP和DELETE的区别
- A Game of Thrones(24)
- ncs java 成都 面试_成都java工程师面试一般都是哪些问题,基础难不难!
- 2021年5月软件设计师考前总结笔记
- 汽车电子业务升级方式说明