深入解读springmvc--1,摸清DispatcherServlet

2023-10-18 14:49

本文主要是介绍深入解读springmvc--1,摸清DispatcherServlet,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一,前言

很惭愧最近没更新博客,闲的慌废寝忘食地看了几本小说,还好一周时间就看吐了,上周空闲之余粗略看了下Spring MVC的源码,先把上周的一些思路整理一下,走一下springmvc的流程。

打算写一个springmvc系列的博客,从源码解读,到自己实现一个处理请求,生成视图和模型并且可以进行视图的解析的A货MVC框架。DispatcherServlet是整个SpringMvc框架的核心,是整个web应用程序的入口点,捕获了所有的请求,进行分析找到相应的控制器和方法来处理这个请求,然后生成模型和视图,最后由视图来进行渲染自身。

整篇博客分为三个部分:

  • 了解Java Servlet
  • DispatcherServlet的继承关系
  • DispatcherServlet初始化做的每一步的工作

下一篇博客会继续分析DispatcherServlet:

  • mvc处理Http请求的具体流程
  • 自己实现一个A货DispatcherServlet

二,了解Java Servlet

什么是Servlet呢,servlet是一个java类,是服务器端运行的一个小程序,并且看可以通过请求-响应编程模型来访问的服务器内存中的程序。

java实现servlet的三种方式:

1,java定义好了servlet的接口,我们直接实现其即可:

enter description here

五个方法:

  • init: servlet被Servlet容器实例化的时候就调用,如果init方法抛出ServletException或者是没有在规定时间内return那么容器就会直接忽视这个Servlet
  • getServletConfig: 获得Servlet初始化的配置
  • service: 被Servlet容器调用进行请求的处理
  • getServletInfo: 获取Servlet的信息
  • destroy: 当Servlet服务完成之后由容器调用,进行资源的释放等操作

实现了Servlet接口,就可以放在Servlet容器中,在web.xml配置中配置Servlet和其对应的响应路径即可。给出一个简单的例子:

项目结构:

enter description here

web.xml配置:

1
2
3
4
5
6
7
8
9
<servlet><servlet-name>xihao</servlet-name><servlet-class>com.test.xihao.XihaoWeb</servlet-class></servlet><servlet-mapping><servlet-name>xihao</servlet-name><url-pattern>/xihao</url-pattern></servlet-mapping>

XihaoWeb就是一个简单实现了Servlet接口的类,然后把整个项目部署到tomcat中即可。

2,继承GenericServlet

GenericServlet是一个通用的,不依赖具体协议的Servlet,实现了Servlet,ServletConfig接口,还实现了log方法,只需要继承GenericServlet,实现一个抽象方法service即可。

然后在web.xml中声明其映射。

3,继承HttpServlet

具体的Http协议的实现,继承了GenericServlet,最通用的一种实现servlet的方式就是直接继承HttpServlet然后重写其中的:

  • doGet
  • doPost
  • doPut
  • doDelete
  • init
  • getServletInfo
    等方法即可。

三,DispatcherServlet的继承关系

springmvc源码错综复杂,就是一个庞大的蜘蛛网,我们的思路一定要清晰,先从DispatcherServlet的继承关系下手,看下其继承关系:

enter description here

HttpServletBean

HttpServletBean 实现了EnvironmentCapable,EnvironmentAware接口,可以自定义web运行的环境,继承HttpServlet,主要需要注意的是初始化的时候完成了web容器的依赖注入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public final void init() throws ServletException {if (logger.isDebugEnabled()) {logger.debug("Initializing servlet '" + getServletName() + "'");}// Set bean properties from init parameters.PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);if (!pvs.isEmpty()) {try {BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);//从ServletContext中获取Bean属性参数,然后进行web容器的依赖注入ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));initBeanWrapper(bw);bw.setPropertyValues(pvs, true);}catch (BeansException ex) {if (logger.isErrorEnabled()) {logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);}throw ex;}}//子类自己实现ServletBean具体的初始化initServletBean();if (logger.isDebugEnabled()) {logger.debug("Servlet '" + getServletName() + "' configured successfully");}}

FrameworkServlet

SpringMvc框架的基层Servlet,提供了在bean层面上与Spring应用上下文的集成,主要实现了三个功能:

  • 基于每个Servlet的命名空间中的beans,为每个Servlet实例化一个WebApplicationContext,并对其进行管理
  • 在处理请求上发布事件,不管请求是否成功
  • 完成了Servlet上下文的建立之后,子类实现onRefresh方法,完成IOC容器的初始化

子类必须覆盖其doService方法实现具体的请求处理逻辑。

DispatcherServlet

作为一个前端控制器,所有的web请求都需要通过它进行处理,进行转发,匹配,数据处理后,并由页面进行处理。是SpringMvc的核心。

主要实现的功能可以分为两个部分:

  • 初始化部分,主要在initWebApplicationContext中,对web模块需要的bean进行了初始化
  • 对Http请求进行响应,主要在doDispatch中实现

四,DispatcherServlet的执行链

DispatcherServle怎么样处理Http请求的呢,还有初始化的时候具体做了些什么呢?

看下两条主线:

DispatcherServlet自身IOC容器的初始化:

enter description here

处理Http请求:

enter description here

1,DISPATCHERSERVLET的初始化

DispatcherServlet自己持有一个IOC容器,整个web应用有一个根上下文,在ContextLoaderListener初始化的时候被建立了,并且被设置到WebApplicationContextUtils中去。

然后可以理解为每个Servlet持有一个子上下文,DispatcherServlet的持有的WebApplicationContext以web应用的根上下文作为自己的parent(这个过程在FrameworkServlet中实现)。需要知道的是,在向IOC容器getBean的时候,首先会到双亲上下文中寻找,也就是说,整个web项目的根上下文定义的bean是可以被所有的子上下文共享的。

具体看下FrameworkServlet中的initServletBean方法,以及一些注释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
protected WebApplicationContext initWebApplicationContext() {//获取根上下文,使用这个作为当前mvc上下文的双亲上下文WebApplicationContext rootContext =WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null;if (this.webApplicationContext != null) {//构造函数时候已经注入了web应用上下文wac = this.webApplicationContext;if (wac instanceof ConfigurableWebApplicationContext) {ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;if (!cwac.isActive()) {// The context has not yet been refreshed -> provide services such as// setting the parent context, setting the application context id, etcif (cwac.getParent() == null) {//没有双亲上下文的话,设置上下文,可能为nullcwac.setParent(rootContext);}//对子上下文进行基本配置和最终调用IOC容器的refresh方法configureAndRefreshWebApplicationContext(cwac);}}}if (wac == null) {// 没有的话 就去ServletContext中找,找到的话 默认按照根上下文已经被设置并且用户已经执行了初始化来处理wac = findWebApplicationContext();}if (wac == null) {// 最后还是没有找到的话,创建一个wac = createWebApplicationContext(rootContext);}if (!this.refreshEventReceived) {// 还没有进行刷新事件的话// 如果创建的上下文不支持刷新或者是构造器注入的上下文已经被调用过refresh方法,在这里手动刷新onRefresh(wac);}if (this.publishContext) {//把生成的子上下文设置到SevletContext中去String attrName = getServletContextAttributeName();getServletContext().setAttribute(attrName, wac);if (this.logger.isDebugEnabled()) {this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +"' as ServletContext attribute with name [" + attrName + "]");}}return wac;}

FrameworkServlet中的onRefresh方法是空的,是一个模板留给子类实现。然后DispatcherServlet具体实现了onRefresh方法,调用自身的initStrategies方法,看下其具体实现:

enter description here

initStrategies(ApplicationContext context)就是mvc框架的各种实现元素的初始化,包括国际化的LocaleResolver,配置请求和controller映射的HandlerMappings等。

上面的流程还有最后的两步没有打通,哪两步呢:

  • configureAndRefreshWebApplicationContext方法和createWebApplicationContext的具体实现
  • 最后的以initHandlerMapping方法为例,最后的mvc组件的具体实例化又是怎么样的呢?

两个方法解析:

首先看下createWebApplicationContext:

enter description here

设置一些基本属性之后还是调用的configureAndRefreshWebApplicationContext方法,看下configureAndRefreshWebApplicationContext:

enter description here

其实就是设置一些基本的参数之后,然后进行容器的所有bean的实例化。

initHandlerMapping具体解析:

enter description here

看下默认的配置,DispatcherServlet.properties:

enter description here

可以看到默认的HandlerMappings的具体实现是BeanNameUrlHandlerMapping和RequestMappingHandlerMapping。

  • BeanNameUrlHandlerMapping 基于bean名字来匹配映射,举个例子就是有个/userName请求,会查找controller的name属性为”/userName”的controller,支持模糊匹配,”/user*”也可以匹配到”/userName”的controller。
  • RequestMappingHandlerMapping 应该是最常用的一个解析器了吧,请求直接映射到@RequestMapping注解的类和方法上面。

五,结尾

写到这里已经是深夜的00:30了,这种源码阅读的操作简直就是在啃书一样,太过于晦涩了,错综复杂的调用关系以及层层继承下来,需要时刻保持头脑的高度清晰和集中。

但是现在,整个DispatcherServlet初始化的步骤已经很明朗,流程贯通的话,还是有一点点的成就感的,清楚了各个步骤之后,我们就可以自己定制一个DispatcherServlet了,也可以按照自己的需求进行框架的深度定制,这些操作留到下一篇博客,给自己找一些成就感哈哈。

这篇关于深入解读springmvc--1,摸清DispatcherServlet的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/233252

相关文章

Spring Boot中WebSocket常用使用方法详解

《SpringBoot中WebSocket常用使用方法详解》本文从WebSocket的基础概念出发,详细介绍了SpringBoot集成WebSocket的步骤,并重点讲解了常用的使用方法,包括简单消... 目录一、WebSocket基础概念1.1 什么是WebSocket1.2 WebSocket与HTTP

SpringBoot+Docker+Graylog 如何让错误自动报警

《SpringBoot+Docker+Graylog如何让错误自动报警》SpringBoot默认使用SLF4J与Logback,支持多日志级别和配置方式,可输出到控制台、文件及远程服务器,集成ELK... 目录01 Spring Boot 默认日志框架解析02 Spring Boot 日志级别详解03 Sp

java中反射Reflection的4个作用详解

《java中反射Reflection的4个作用详解》反射Reflection是Java等编程语言中的一个重要特性,它允许程序在运行时进行自我检查和对内部成员(如字段、方法、类等)的操作,本文将详细介绍... 目录作用1、在运行时判断任意一个对象所属的类作用2、在运行时构造任意一个类的对象作用3、在运行时判断

java如何解压zip压缩包

《java如何解压zip压缩包》:本文主要介绍java如何解压zip压缩包问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Java解压zip压缩包实例代码结果如下总结java解压zip压缩包坐在旁边的小伙伴问我怎么用 java 将服务器上的压缩文件解压出来,

SpringBoot中SM2公钥加密、私钥解密的实现示例详解

《SpringBoot中SM2公钥加密、私钥解密的实现示例详解》本文介绍了如何在SpringBoot项目中实现SM2公钥加密和私钥解密的功能,通过使用Hutool库和BouncyCastle依赖,简化... 目录一、前言1、加密信息(示例)2、加密结果(示例)二、实现代码1、yml文件配置2、创建SM2工具

Spring WebFlux 与 WebClient 使用指南及最佳实践

《SpringWebFlux与WebClient使用指南及最佳实践》WebClient是SpringWebFlux模块提供的非阻塞、响应式HTTP客户端,基于ProjectReactor实现,... 目录Spring WebFlux 与 WebClient 使用指南1. WebClient 概述2. 核心依

Spring Boot @RestControllerAdvice全局异常处理最佳实践

《SpringBoot@RestControllerAdvice全局异常处理最佳实践》本文详解SpringBoot中通过@RestControllerAdvice实现全局异常处理,强调代码复用、统... 目录前言一、为什么要使用全局异常处理?二、核心注解解析1. @RestControllerAdvice2

Spring IoC 容器的使用详解(最新整理)

《SpringIoC容器的使用详解(最新整理)》文章介绍了Spring框架中的应用分层思想与IoC容器原理,通过分层解耦业务逻辑、数据访问等模块,IoC容器利用@Component注解管理Bean... 目录1. 应用分层2. IoC 的介绍3. IoC 容器的使用3.1. bean 的存储3.2. 方法注

Spring事务传播机制最佳实践

《Spring事务传播机制最佳实践》Spring的事务传播机制为我们提供了优雅的解决方案,本文将带您深入理解这一机制,掌握不同场景下的最佳实践,感兴趣的朋友一起看看吧... 目录1. 什么是事务传播行为2. Spring支持的七种事务传播行为2.1 REQUIRED(默认)2.2 SUPPORTS2

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.