DispatcherServlet概述

在Spring Boot框架未出现之前,要开发一个基于Spring MVC框架的项目,通常需要在Java web项目的描述符文件web.xml中添加如下配置:

<!-- 初始化Spring IoC容器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener> <!-- 手动配置DispatcherServlet -->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:/spring*.xml</param-value>
</init-param>
</servlet> <!-- 配置DispatcherServlet拦截路径,让所有Web请求都经过DispatcherServlet -->
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

添加如上配置之后,就能实现将Spring MVC框架整合到Web项目中了,那这是怎么做到的呢?下面一一解答下。

首先,org.springframework.web.context.ContextLoaderListener实现了接口javax.servlet.ServletContextListener,这样就能确保Web应用在启动后回调该接口。

public interface ServletContextListener extends EventListener {
// Web应用在启动之后会调用该方法,通过传递的事件参数获取到ServletContext上下文环境
public void contextInitialized(ServletContextEvent sce); // Web应用在停止后会回调该方法
public void contextDestroyed(ServletContextEvent sce);
}

其次,在org.springframework.web.context.ContextLoaderListener中实现了contextInitialized()方法,并且在方法实现中触发了对Spring IoC容器的初始化,并且将ServletContext上下文保存到了IoC容器中。

// ServletContextListener
@Override
public void contextInitialized(ServletContextEvent event) {
// 触发对Spring IoC容器的初始化
initWebApplicationContext(event.getServletContext());
} // AbstractRefreshableWebApplicationContext
@Override
public void setServletContext(@Nullable ServletContext servletContext) {
// 在IoC容器中保存了ServletContext上下文
this.servletContext = servletContext;
}

既然Spring IoC容器已经和ServletContext建立了联系,那当Web请求被DispatcherServlet拦截之后就可以基于Spring IoC容器环境进行处理了,实际上这样就建立了一个从Serlvet到Spring MVC框架的桥梁。

至此,关于Spring MVC框架如何与Web应用集成的问题就算得到了解答,很显然在传统的Spring MVC项目中,这是通过手动配置实现的。而在使用Spring Boot框架时就没有再看到这些配置了, Spring Boot的强大之处在于自动装配机制,虽然我们没有手动去配置,实际上是Spring Boot框架帮我们自动实现了。

那么,Spring Boot是如何实现的呢?

Spring Boot自动初始化DispatcherServlet

如下解读基于Spring Boot 2.7.14版本进行。

经过对Spring Boot的源码解读和梳理后知道,在Spring Boot框架中DispatcherServlet的自动装配是通过注解@DispatcherServletAutoConfiguration实现的。

具体流程如下:

首先,在Spring Boot的核心注解@EnableAutoConfiguration中引入了一个类AutoConfigurationImportSelector,Spring Boot在启动时会触发该类中如下方法的调用。

// AutoConfigurationImportSelector.getCandidateConfigurations()
// 该方法是在Spring Boot启动时调用的,具体来说是在刷新Spring IoC容器的时候触发的
// 在该方法中实现加载自动配置类,具体来说,是加载2个配置文件中的自动配置类
// 其一,加载各种starter组件jar中"META-INF/spring.factories"文件指定的自动配置类
// 其二,加载Sring Boot自己的自动配置类,这些类在文件"META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports"中指定
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// 首先加载各类starter组件jar包中"META-INF/spring.factories"文件指定的自动配置类
List<String> configurations = new ArrayList<>(
SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));
// 再加载文件"META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports"中指定的自动配置类
ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);
return configurations;
}

其次,在加载Spring Boot的“META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports”自动配置类的时候,就会加载到org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration

DispatcherServletAutoConfiguration的源码中可以很清晰地看到正是在该配置类中实现了对dispatcherServlet的注入。

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@AutoConfiguration(after = ServletWebServerFactoryAutoConfiguration.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
public class DispatcherServletAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@Conditional(DefaultDispatcherServletCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
protected static class DispatcherServletConfiguration { // 实际上是在DispatcherServletAutoConfiguration的内部静态类DispatcherServletConfiguration中完成了对"dispatcherServlet"的注入
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
return dispatcherServlet;
} // 省略了其他代码
// ...
}
}

虽然解答了DispatcherServlet的自动注入问题,但是还没有解答Spring IoC容器是如何与ServletContext上下文建立联系的。

经过对Spring Boot启动流程的源码解读知道,其实建立Spring IoC容器与ServletContext的关系是在ServletWebServerApplicationContext.onRefresh()方法中实现的。

// 在刷新Spring IoC容器的过程中会调用该方法
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
} private void createWebServer() {
// 在这里会完成对Servlet容器的创建,并且与ServletContext上下文进行关联
}

所以,Spring Boot通过自动装配机制完成了对Spring MVC的DispatcherServlet注入,并且还创建了嵌入式的Servlet容器,并以Deamon线程方式运行在后台。

细说Spring Boot初始化DispatcherServlet的更多相关文章

  1. 1.spring boot初始化项目

    初始化spring boot项目的方式非常多,如使用Spring Tool Suite.使用IntelliJ IDEA.使用NetBeans.在start.spring.io网站中.curl命令.sp ...

  2. Spring Boot 初始化运行特定方法

    Spring Boot提供了两种 “开机自启动” 的方式,ApplicationRunner和CommandLineRunner 这两种方式的目的是为了满足,在容器启动时like执行某些方法.我们可以 ...

  3. Spring Boot整合Dubbo使用及开发笔记

    一.概述: Spring Dubbo是我开发的一个基于spring-boot和dubbo,目的是使用Spring boot的风格来使用dubbo.(即可以了解Spring boot的启动过程又可以学习 ...

  4. Spring Boot 中的静态资源到底要放在哪里?

    当我们使用 SpringMVC 框架时,静态资源会被拦截,需要添加额外配置,之前老有小伙伴在微信上问松哥Spring Boot 中的静态资源加载问题:"松哥,我的HTML页面好像没有样式?& ...

  5. Spring Boot 2.0 配置图文教程

    摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 本章内容 自定义属性快速入门 外化配置 自动配置 自定义创建 ...

  6. spring boot到底帮我们做了那些事?

    一.前言     上一篇介绍了注解,也是为这一篇做铺垫,传统的都是通过配置文件来启动spring,那spring boot到底是做了什么能让我们快速开发昵? 二.启动原理     看下程序启动的入口, ...

  7. Spring boot+ logback环境下,日志存放路径未定义的问题

    日志路径未定义 环境:Spring boot + logback 配置文件: <configuration> <springProfile name="dev"& ...

  8. idea中使用tomcat 方式启动spring boot项目

    Spring boot 的main 入口启动方式相信都会用,直接运行main直接就启动了,但是往往这种方式并不是最佳的启动方式,比如运维的层面更希望调整tomcat的调优参数,而只使用嵌入启动方式很难 ...

  9. Spring Boot 构建一个 RESTful Web Service

    1  项目目标: 构建一个 web service,接收get 请求 http://localhost:8080/greeting 响应一个json 结果: {"id":1,&qu ...

  10. 创建Spring Boot项目的几种方式总结

    一.我们可以使用Spring Initializr来创建SpringBoot项目. Spring Initializr从本质上来说就是一个Web应用程序,它能为你生成Spring Boot项目结构.虽 ...

随机推荐

  1. [转帖]ls 只显示目录

    https://www.cnblogs.com/lavin/p/5912369.html 只显示目录: ls -d */ 在实际应用中,我们有时需要仅列出目录,下面是 4 种不同的方法. 1. 利用 ...

  2. Navicat For Redis 的学习与使用

    Navicat For Redis 的学习与使用 背景 周末在家看了几个公众号: 说到Navicat 16.2已经有了 Redis的客户端. 想着前段时间一直在学习Redis, 但是没有GUI的工具, ...

  3. [转帖]新一代垃圾回收器ZGC的探索与实践

    1. 引入 1.1 GC之痛 很多低延迟高可用Java服务的系统可用性经常受GC停顿的困扰. GC停顿指垃圾回收期间STW(Stop The World),当STW时,所有应用线程停止活动,等待GC停 ...

  4. 高性能Redis服务器注意事项

    摘要 昨天简单理了理安装与配置相关的 但是很多比较重要的核心性能参数并没有进行学习与探讨 就基于昨天理解不深入的地方进行进一步的学习与了解 希望能够提高Redis-Server的性能. 第一部分: 规 ...

  5. 一条sql了解MYSQL的架构设计

    1 前言 对于一个服务端开发来说 MYSQL 可能是他使用最熟悉的数据库工具,然而,大部分的Java工程师对MySQL的了解和掌握程度,大致就停留在这么一个阶段:它可以建库.建表.建索引,然后就是对里 ...

  6. vue中jsx

    //item.vue 文件如下 <template> <div> <h1 v-if="id===1"> <slot></slo ...

  7. echarts的初始化和销毁dispose

    容器节点被销毁以及被重建时 假设页面中存在多个标签页, 每个标签页都包含一些图表. 当选中一个标签页的时候,其他标签页的内容在 DOM 中被移除了. 这样,当用户再选中这些标签页的时候,就会发现图表& ...

  8. 深入理解TF-IDF、BM25算法与BM25变种:揭秘信息检索的核心原理与应用

    深入理解TF-IDF.BM25算法与BM25变种:揭秘信息检索的核心原理与应用 1.文本特征表示方法: TF-IDF 在信息检索, 文本挖掘和自然语言处理领域, IF-IDF 这个名字, 从它在 20 ...

  9. 深度学习应用篇-计算机视觉-OCR光学字符识别[7]:OCR综述、常用CRNN识别方法、DBNet、CTPN检测方法等、评估指标、应用场景

    深度学习应用篇-计算机视觉-OCR光学字符识别[7]:OCR综述.常用CRNN识别方法.DBNet.CTPN检测方法等.评估指标.应用场景 1.OCR综述 OCR(Optical Character ...

  10. 强化学习技巧三:Python多进程

    1.Python多进程模块 Python中的多进程是通过multiprocessing包来实现的,和多线程的threading.Thread差不多,它可以利用multiprocessing.Proce ...