嵌入式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,,JettyServletWebServerFactoryUndertowServletWebServerFactory

那么究竟返回哪一个工厂呢?

我们需要分析一下底层的自动配置类,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()方法,设置了链接参数,例如TomcatServletWebServerFactorygetWebServer()方法

在方法的最后,它会执行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类的实现类,ConfigurableServletWebServerFactoryServletWebServerFactory类的子类,提供了很多方法供我们使用

代码样例如下

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没有生效

到此这篇关于SpringBoot嵌入式Servlet容器与定制化组件超详细讲解的文章就介绍到这了,更多相关SpringBoot Servlet容器内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

SpringBoot嵌入式Servlet容器与定制化组件超详细讲解的更多相关文章

  1. HTML5 WebSocket实现点对点聊天的示例代码

    这篇文章主要介绍了HTML5 WebSocket实现点对点聊天的示例代码的相关资料,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. HTML5页面无缝闪开的问题及解决方案

    这篇文章主要介绍了HTML5页面无缝闪开方案,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  3. ios – 伞框架

    错误.应用程序,通常位于…错误仍然存在你也可以在这里添加(子)框架的路径.

  4. ios – 在设备上构建和运行时,仅将嵌入式框架与其他动态框架链接失败

    TL;博士将您的嵌入式框架与其他框架链接,并且不将其他框架与您的应用程序链接,导致Build&在设备上运行.描述:建立:我的设置非常简单(Swift2.3&XcodeXcode8.0;Build版本8S162m):>使用Carthage(0.17.2)我用xcodebuild8.0和TOOLCHAINS=com.apple.dt.toolchain.Swift_2_3carthagebui

  5. iOS 8嵌入式框架中的头文件

    我正在尝试创建一个用于iOS8的嵌入式框架.在创建一个名为SampleKit(BTW;这里有任何约定,我应该使用前缀吗?)之后,它包含一个令我困惑的头文件:我知道FOUNDATION_EXPORT是extern或extern“C”的宏,但我不确定这两个常量.我应该在哪里为他们设定价值?解决方法项目>构建设置>版本控制>当前项目版本:

  6. ios – 将容器带到视图前方

    我怎样才能解决这个问题?

  7. ios – 如何使用XCode 6.4下载和替换AppGroup容器

    我知道如何使用XCode6的Devices窗口下载和替换特定iOS应用程序的文件系统容器.但是对于我正在开发的应用程序,我需要能够下载和替换共享的AppGroup容器以进行调试.这将使我能够模拟AppGroup文件夹内容中的情况以进行测试.任何人都可以告诉我如何做到这一点?

  8. ios – 在UITableView上移动UIView – 触摸顶部UIView仍然选择表行

    =======用一些代码编辑:这是我在容器B中所做的代码.这是B帧的一个非常直接的动画.self.view是ContainerB的UIView.所有视图都在屏幕上通过故事板.其他容器是B的子视图.请让我知道你想看到的其他代码.解决方法嗯……不确定这是否适用于您的情况,但尝试在容纳所有其他容器的更大容器中管理您的动画.我的意思是,创建一个包含A,B,O及其子视图的ContainerZ,并尝试从Z中设置B的位置动画,检查B是否在A前面.

  9. ios – 与容器视图通信的最佳实践是什么?

    我最近经常使用容器VC,我一直想知道主Vc和容器VC之间的最佳通信方式是什么.现在我正在使用通知,但我宁愿使用更好的东西.如何获取指向容器VC的指针,以便至少可以使用委托?

  10. ios – 编程嵌入UIViewController?

    我有一个带有一个容器视图的UIViewController的Storyboard设置,以便我可以在其中嵌入另一个UIViewController.在某种情况下,我需要更改嵌入式视图控制器.在我的故事板中,我的容器视图不能有两个区段.这导致我以编程方式进行.我的容器视图在我的故事板,没有连接的嵌入.现在从这一点开始,我如何以编程方式嵌入我所选择的UIViewController对象?

随机推荐

  1. 基于EJB技术的商务预订系统的开发

    用EJB结构开发的应用程序是可伸缩的、事务型的、多用户安全的。总的来说,EJB是一个组件事务监控的标准服务器端的组件模型。基于EJB技术的系统结构模型EJB结构是一个服务端组件结构,是一个层次性结构,其结构模型如图1所示。图2:商务预订系统的构架EntityBean是为了现实世界的对象建造的模型,这些对象通常是数据库的一些持久记录。

  2. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  4. (jsp/html)网页上嵌入播放器(常用播放器代码整理)

    网页上嵌入播放器,只要在HTML上添加以上代码就OK了,下面整理了一些常用的播放器代码,总有一款适合你,感兴趣的朋友可以参考下哈,希望对你有所帮助

  5. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  6. Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  7. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  8. 面试突击之跨域问题的解决方案详解

    跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。那怎么解决这个问题呢?接下来我们一起来看

  9. Mybatis-Plus接口BaseMapper与Services使用详解

    这篇文章主要为大家介绍了Mybatis-Plus接口BaseMapper与Services使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  10. mybatis-plus雪花算法增强idworker的实现

    今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,进一步增强实现生成分布式唯一ID,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部