背景

项目中使用了undertow作为web容器,以获得更好的性能,这里先不谈undertow相比其他容器(tomcat,jetty等)的优缺点,只是单纯分析下Spring Boot 是如何集成web容器的

项目中使用undertow

Spring Boot默认使用tomcat作为内嵌web容器,可以通过修改pom更改为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><!-- 使用undertow 作为web服务器 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-undertow</artifactId></dependency>

原理分析

通过上面简单的修改就可以使用undertow, 那么Spring Boot内部是如何实现的呢? 带着这个疑问,走了下源码(本人使用Spring Boot版本为2.0.6)。

@SpringBootApplication
public class UsBridgeApp {public static void main( String[] args ) {SpringApplication.run(UsBridgeApp.class, args);}
}

上面是一个简单的入口程序,主要是使用了SpringBootApplication注解和main方法中运行SpringApplication 的静态run方法,进入SpringApplication不难发现,静态run方法最终也是调用SpringApplication类实例run方法

/*** Run the Spring application, creating and refreshing a new* {@link ApplicationContext}.* @param args the application arguments (usually passed from a Java main method)* @return a running {@link ApplicationContext}*/public ConfigurableApplicationContext run(String... args) {StopWatch stopWatch = new StopWatch();stopWatch.start();ConfigurableApplicationContext context = null;Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();configureHeadlessProperty();SpringApplicationRunListeners listeners = getRunListeners(args);listeners.starting();try {ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);configureIgnoreBeanInfo(environment);Banner printedBanner = printBanner(environment);context = createApplicationContext();exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context);prepareContext(context, environment, listeners, applicationArguments,printedBanner);refreshContext(context);afterRefresh(context, applicationArguments);stopWatch.stop();if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}listeners.started(context);callRunners(context, applicationArguments);}catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, listeners);throw new IllegalStateException(ex);}try {listeners.running(context);}catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, null);throw new IllegalStateException(ex);}return context;}

代码先是通过createApplicationContext()创建了ApplicationContext,之后调refreshContext方法,它们做了什么呢?

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);}}catch (ClassNotFoundException ex) {throw new IllegalStateException("Unable create a default ApplicationContext, "+ "please specify an ApplicationContextClass",ex);}}return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);}

通过判断webApplicationType值来选择加载哪个类, WebApplicationType 通过静态方法获得

static WebApplicationType deduceFromClasspath() {if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)&& !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {return WebApplicationType.REACTIVE;}for (String className : SERVLET_INDICATOR_CLASSES) {if (!ClassUtils.isPresent(className, null)) {return WebApplicationType.NONE;}}return WebApplicationType.SERVLET;}

可以看出默认使用WebApplicationType.SERVLET, 所以默认加载的spring context 是 DEFAULT_SERVLET_WEB_CONTEXT_CLASS,

/*** The class name of application context that will be used by default for web* environments.*/public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";

回到refreshContext,实际是调了AbstractApplicationContext 的refresh方法

@Overridepublic void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// Prepare this context for refreshing.prepareRefresh();// Tell the subclass to refresh the internal bean factory.ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// Prepare the bean factory for use in this context.prepareBeanFactory(beanFactory);try {// Allows post-processing of the bean factory in context subclasses.postProcessBeanFactory(beanFactory);// Invoke factory processors registered as beans in the context.invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.registerBeanPostProcessors(beanFactory);// Initialize message source for this context.initMessageSource();// Initialize event multicaster for this context.initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses.onRefresh();// Check for listener beans and register them.registerListeners();// Instantiate all remaining (non-lazy-init) singletons.finishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event.finishRefresh();}catch (BeansException ex) {if (logger.isWarnEnabled()) {logger.warn("Exception encountered during context initialization - " +"cancelling refresh attempt: " + ex);}// Destroy already created singletons to avoid dangling resources.destroyBeans();// Reset 'active' flag.cancelRefresh(ex);// Propagate exception to caller.throw ex;}finally {// Reset common introspection caches in Spring's core, since we// might not ever need metadata for singleton beans anymore...resetCommonCaches();}}}

这个方法做了很多事情,其中调用的onRefresh 就是调用了AnnotationConfigServletWebServerApplicationContext的父类ServletWebServerApplicationContext方法

@Overrideprotected void onRefresh() {super.onRefresh();try {createWebServer();}catch (Throwable ex) {throw new ApplicationContextException("Unable to start web server", ex);}}

终于看到了想看到的,createWebServer()似乎就是我要找的了,继续...

private void createWebServer() {WebServer webServer = this.webServer;ServletContext servletContext = getServletContext();if (webServer == null && servletContext == null) {ServletWebServerFactory factory = getWebServerFactory();this.webServer = factory.getWebServer(getSelfInitializer());}else if (servletContext != null) {try {getSelfInitializer().onStartup(servletContext);}catch (ServletException ex) {throw new ApplicationContextException("Cannot initialize servlet context",ex);}}initPropertySources();}

ServletWebServerFactory 创建了webServer , ServletWebServerFactory 通过从spring容器中获得

protected ServletWebServerFactory getWebServerFactory() {// Use bean names so that we don't consider the hierarchyString[] 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);}

现在只要弄清楚Spring Boot 此时到底使用的哪个factory 便可,Spring Boot 基本配置都在spring-boot-autoconfigure 这个jar中,首先看org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration

@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,ServletWebServerFactoryConfiguration.EmbeddedJetty.class,ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {@Beanpublic ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {return new ServletWebServerFactoryCustomizer(serverProperties);}@Bean@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(ServerProperties serverProperties) {return new TomcatServletWebServerFactoryCustomizer(serverProperties);}...
}    

通过Import注解加入了 Tomcat, Jetty,Undertow三种支持,

/** Copyright 2012-2018 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package org.springframework.boot.autoconfigure.web.servlet;import javax.servlet.Servlet;import io.undertow.Undertow;
import org.apache.catalina.startup.Tomcat;
import org.apache.coyote.UpgradeProtocol;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.webapp.WebAppContext;
import org.xnio.SslClientAuthMode;import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** Configuration classes for servlet web servers* <p>* Those should be {@code @Import} in a regular auto-configuration class to guarantee* their order of execution.** @author Phillip Webb* @author Dave Syer* @author Ivan Sopov* @author Brian Clozel* @author Stephane Nicoll*/
@Configuration
class ServletWebServerFactoryConfiguration {@Configuration@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)public static class EmbeddedTomcat {@Beanpublic TomcatServletWebServerFactory tomcatServletWebServerFactory() {return new TomcatServletWebServerFactory();}}/*** Nested configuration if Jetty is being used.*/@Configuration@ConditionalOnClass({ Servlet.class, Server.class, Loader.class,WebAppContext.class })@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)public static class EmbeddedJetty {@Beanpublic JettyServletWebServerFactory JettyServletWebServerFactory() {return new JettyServletWebServerFactory();}}/*** Nested configuration if Undertow is being used.*/@Configuration@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)public static class EmbeddedUndertow {@Beanpublic UndertowServletWebServerFactory undertowServletWebServerFactory() {return new UndertowServletWebServerFactory();}}}

最开始的pom配置因为排除了Tomcat.class 类的依赖而引入了Undertow.class类,所有项目实际加载的ServletWebServerFactory 为UndertowServletWebServerFactory;

转载于:https://www.cnblogs.com/ljgeng/p/11079091.html

Spring Boot 集成undertow作为web容器分析相关推荐

  1. 三、spring boot 1.5.4 web容器定制(端口号等修改)

    spring boot 默认采用tomcat作为嵌入的web容器 定制方式有三种 1. 2.如下 @Componentpublic class CustomizationBean implements ...

  2. Tomcat8源码分析系列-spring boot集成tomcat

    前言 本文基于 spring boot 1.5.9 spring boot 支持目前主流的 servlet 容器,包括 tomcat.jetty.undertow,可以在我们的项目中方便地集成这些 s ...

  3. Spring Boot 集成 WebFlux 开发 Reactive Web 应用

    Spring Boot 集成 WebFlux 开发 Reactive Web 应用 <Spring Boot 实战开发>-- 基于 Gradle + Kotlin的企业级应用开发最佳实践 ...

  4. Spring Boot 集成SnakerFlow流程引擎,简介、功能列表、详细解读、扩展点分析

    文章目录 简介 功能列表 流程定义 任务参与者 参与者设置 动态添加.删除参与者 组支持 详细解读 Spring Boot集成 表定义 表详细说明: 字段详细说明: 常见操作 常规API 综合查询 模 ...

  5. springboot使用undertow作为web容器而引发的中文乱码问题

    先说一下出现这种情况的场景.该场景在使用jetty,tomcat作为容器时是可以正常运行的.首先是表单提交. <form id="submitForm" action=&qu ...

  6. spring boot 集成sleuth

    spring boot 集成sleuth 1. 理论 1.1 sleuth是什么 1.2 sleuth有哪些 1.3 链路追踪的一些基本概念 1.4 zipkin的组成 2. zipkin 实例 2. ...

  7. Spring Boot集成Hazelcast实现集群与分布式内存缓存

    2019独角兽企业重金招聘Python工程师标准>>> Hazelcast是Hazelcast公司开源的一款分布式内存数据库产品,提供弹性可扩展.高性能的分布式内存计算.并通过提供诸 ...

  8. Spring Boot(3) Web开发(1)静态资源处理

    Spring Boot(3) Web开发(1)静态资源处理 基于spring boot 2.4.3版本 1.静态资源访问 1.1 静态资源目录 把静态资源放在类路径下的以下目录:/static; /p ...

  9. spring boot 集成 websocket 实现消息主动推送

    前言 http协议是无状态协议,每次请求都不知道前面发生了什么,而且只可以由浏览器端请求服务器端,而不能由服务器去主动通知浏览器端,是单向的,在很多场景就不适合,比如实时的推送,消息通知或者股票等信息 ...

  10. 从零搭建开发脚手架 Spring Boot集成Mybatis-plus之一

    文章目录 简介 特性 框架结构 依赖集成 依赖 配置 编码 开始使用 核心功能 代码生成器 添加依赖 编码 编写配置 自定义模板引擎 自定义代码模板 自定义属性注入 字段其他信息查询注入 实战总结 常 ...

最新文章

  1. iOS10 UI教程视图的绘制与视图控制器和视图
  2. DropDownList控件
  3. 入侵检测系统基础知识
  4. matlab找数据的转账点,nodejs开发EOS转账服务的两种方案
  5. 鸡啄米vc++2010系列40(文档、视图和框架:分割窗口)
  6. linux驱动编写(虚拟字符设备编写)
  7. GaiGai----1
  8. SPSS和excel数据分析之平均值和标准误差对比图
  9. js页面跳转,参数传递
  10. 施耐德 m340 编程手册_施耐德推出开放自动化平台,开启“软件驱动自动化”时代...
  11. 使用计算机组成原理全加器设计,杭电计算机组成原理全加器设计实验1
  12. Rhino 6 Essential Training Rhino6基本教程 Lynda课程中文字幕
  13. 国内10大著名珠宝品牌
  14. 阿里云后台部署全过程-2-Linux环境配置
  15. 虚拟机由于找不到msvcr120dll_计算机提示丢失msvcr120.dll文件怎么办?
  16. <Python的输入、输出、注释>——《Python》
  17. ESP32 单片机学习笔记 - 06 - (以太网)Ethernet转Wifi
  18. 你嗑的瓜子要涨价啦?域名你“嗑”懂了吗?
  19. ARM linux系统调用的实现原理
  20. 在LINUX中用cal命令解了一段人类文明历史 1752年 - 九月

热门文章

  1. Hash 函数的现状,2012
  2. 【资源】机器学习资源积累(积累中...)
  3. 和 对比_Yeezy350V2新灰橙真假对比
  4. 操作系统导论 书中代码下载_经典教材统计学习导论终于有Python版了(附下载)...
  5. bootstrap 4 自动水平居中
  6. 并行 并发 多线程 区别
  7. linux 查看http连接等
  8. java ssh2连接_通过JCraft的jsch包基于SSH2协议实现连接linux服务器提供终端操作的java工具实现类SftpUtil...
  9. 邮件服务器搬家,企业邮箱怎么“搬家”
  10. spring事务的传播属性和事务隔离级别及配置事务(注解方式)