SpringMVC源码分析

文章目录

    • 概要
    • 启动阶段
    • 请求阶段

概要

以下是调试mvc源码过程中用到的demo以及配置文件
webapp/WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

	<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*:springmvc.xml</param-value>
		</init-param>
		<!-- load-on-startup元素标记容器是否在启动的时候就加载这个servlet(实例化并调用其init()方法) -->
		<load-on-startup>1</load-on-startup>
	</servlet>
	
	<servlet-mapping>
		<servlet-name>springmvc</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
</web-app>

springmvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xmlns:context="http://www.springframework.org/schema/context"
	   xmlns:mvc="http://www.springframework.org/schema/mvc"
	   xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd
">


	<!--开启controller扫描-->
	<context:component-scan base-package="com.ocean.base.controller"/>
	<!--配置springmvc的视图解析器-->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/jsp/"/>
		<property name="suffix" value=".jsp"/>
	</bean>
	<!--
		自动注册最合适的处理器映射器,处理器适配器(调用handler方法)
	-->
	<!--<mvc:annotation-driven/>-->

</beans>

Controller

@Controller
@RequestMapping("/demo")
public class DemoController {


	@RequestMapping("/handle01")
	public String handle01(String name, Map<String, Object> model) {
		System.out.println("++++++++handler业务逻辑处理中....");
		Date date = new Date();
		model.put("date", date);
		return "success";
	}

	@RequestMapping("/index")
	public ModelAndView getModeAndView() {
		//创建一个模型视图对象
		ModelAndView mav = new ModelAndView("index");
		return mav;
	}
}

mvc调用的主流程
在这里插入图片描述

  1. DispatcherServlet(前端控制器) 是个servlet,负责接收Request 并将Request 转发给对应的处理组件。
  2. HanlerMapping (处理器映射器)是SpringMVC 中完成url 到Controller 映射的组件。DispatcherServlet HandlerMapping 查找处理RequestController
  3. HanlerMapping 返回一个执行器链(url 到Controller 映射的组件)给DispatcherServlet
  4. DispatcherServlet请求处理器适配器HandlerAdapter
  5. 处理器适配器HandlerAdapter去访问我们的handler(controller)
  6. handler(controller)返回ModelAndView给处理器适配器HandlerAdapter
  7. 处理器适配器HandlerAdapter返回ModelAndViewDispatcherServlet
  8. DispatcherServlet请求ViewResolver视图解析器
  9. ViewResolver视图解析器返回view给DispatcherServlet
  10. DispatcherServlet请求view做页面解析和渲染
  11. view将渲染好的数据返回给DispatcherServletDispatcherServlet将渲染好的字符流给client,看到了页面!

启动阶段

先来看总体的类关系图
在这里插入图片描述
项目启动后,会先执行javax.servlet.Servlet#init方法,从而进行初始化,而·org.springframework.web.servlet.HttpServletBean#init实现了该接口,进而在这个实现方法里面有调到了org.springframework.web.servlet.FrameworkServlet#initServletBean ,整个web容器便是在该方法中初始化完成
在这里插入图片描述
在web容器创建后,会调用到ioc容器的初始化方法
在这里插入图片描述
在ioc容器初始阶段的最后,会发布一个事件
在这里插入图片描述
org.springframework.web.servlet.FrameworkServlet.ContextRefreshListener会监听到该事件

@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
	FrameworkServlet.this.onApplicationEvent(event);
}

public void onApplicationEvent(ContextRefreshedEvent event) {
	this.refreshEventReceived = true;
	synchronized (this.onRefreshMonitor) {
		onRefresh(event.getApplicationContext());
	}
}

监听到事件后,就开始初始化mvc的9大组件

	protected void initStrategies(ApplicationContext context) {
		// 多文件上传
		initMultipartResolver(context);
		// 初始化本地语言环境
		initLocaleResolver(context);
		// 初始化模板处理器
		initThemeResolver(context);
		// 初始化HandlerMapping
		initHandlerMappings(context);
		// 初始化HandlerAdapter
		initHandlerAdapters(context);
		// 初始化异常处理拦截器
		initHandlerExceptionResolvers(context);
		// 初始化视图预处理器
		initRequestToViewNameTranslator(context);
		// 初始化视图转换器
		initViewResolvers(context);
		// 初始化FlashMap管理器
		initFlashMapManager(context);
	}

请求阶段

项目启动后,在浏览器中访问:http://localhost:8080/springmvc/index是,首先会经过`javax.servlet.http.HttpServlet#service(javax.servlet.ServletRequest, javax.servlet.ServletResponse),在该方法中会调用。

javax.servlet.http.HttpServlet#service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse),该方法被子类org.springframework.web.servlet.FrameworkServlet#service所覆盖,这时会跑到子类中进行执行

@Override
	protected void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
		if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
			processRequest(request, response);
		}
		else {
		// 子类中又调了父类的另外一个service方法
			super.service(request, response);
		}
	}

protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        String method = req.getMethod();

        if (method.equals(METHOD_GET)) {
            long lastModified = getLastModified(req);
            if (lastModified == -1) {
                // servlet doesn't support if-modified-since, no reason
                // to go through further expensive logic
                doGet(req, resp);
            } else {
                long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                if (ifModifiedSince < lastModified) {
                    // If the servlet mod time is later, call doGet()
                    // Round down to the nearest second for a proper compare
                    // A ifModifiedSince of -1 will always be less
                    maybeSetLastModified(resp, lastModified);
                    doGet(req, resp);
                } else {
                    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                }
            }

        } else if (method.equals(METHOD_HEAD)) {
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doHead(req, resp);

        } else if (method.equals(METHOD_POST)) {
            doPost(req, resp);
            
        } else if (method.equals(METHOD_PUT)) {
            doPut(req, resp);
            
        } else if (method.equals(METHOD_DELETE)) {
            doDelete(req, resp);
            
        } else if (method.equals(METHOD_OPTIONS)) {
            doOptions(req,resp);
            
        } else if (method.equals(METHOD_TRACE)) {
            doTrace(req,resp);
            
        } else {
            //
            // Note that this means NO servlet supports whatever
            // method was requested, anywhere on this server.
            //

            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[1];
            errArgs[0] = method;
            errMsg = MessageFormat.format(errMsg, errArgs);
            
            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
        }
    }

org.springframework.web.servlet.FrameworkServlet#doGet

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {

	long startTime = System.currentTimeMillis();
	Throwable failureCause = null;

	LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
	LocaleContext localeContext = buildLocaleContext(request);

	RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
	ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

	WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
	asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

	initContextHolders(request, localeContext, requestAttributes);

	try {
		//重点查看,跳到DispatcherServlet 类中(子类重写)
		doService(request, response);
	}
...
}

org.springframework.web.servlet.DispatcherServlet#doService

@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
	logRequest(request);

	....

	RequestPath requestPath = null;
	if (this.parseRequestPath && !ServletRequestPathUtils.hasParsedRequestPath(request)) {
		requestPath = ServletRequestPathUtils.parseAndCache(request);
	}

	try {
	//重点关注
		doDispatch(request, response);
	} finally {
		...
	}
}
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
	HttpServletRequest processedRequest = request;
	HandlerExecutionChain mappedHandler = null;
	boolean multipartRequestParsed = false;

	WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

	try {
		//创建视图对象
		ModelAndView mv = null;
		Exception dispatchException = null;

		try {
			// 检查是否是文件上传的请求
			processedRequest = checkMultipart(request);
			multipartRequestParsed = (processedRequest != request);

			// 根据当前的请求去拿一个Handler.这里包括拦截器
			mappedHandler = getHandler(processedRequest);
			if (mappedHandler == null) {
				noHandlerFound(processedRequest, response);
				return;
			}

			// 处理器适配器
			HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

			// Process last-modified header, if supported by the handler.
			String method = request.getMethod();
			boolean isGet = "GET".equals(method);
			if (isGet || "HEAD".equals(method)) {
				long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
				if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
					return;
				}
			}

			if (!mappedHandler.applyPreHandle(processedRequest, response)) {
				return;
			}

			// // 执行我们的业务控制器方法
			mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

			if (asyncManager.isConcurrentHandlingStarted()) {
				return;
			}
			//视图解析器
			applyDefaultViewName(processedRequest, mv);
			mappedHandler.applyPostHandle(processedRequest, response, mv);
		} catch (Exception ex) {
			dispatchException = ex;
		} catch (Throwable err) {
			// As of 4.3, we're processing Errors thrown from handler methods as well,
			// making them available for @ExceptionHandler methods and other scenarios.
			dispatchException = new NestedServletException("Handler dispatch failed", err);
		}
		//视图渲染
		processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
	} catch (Exception ex) {
		triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
	} catch (Throwable err) {
		triggerAfterCompletion(processedRequest, response, mappedHandler,
				new NestedServletException("Handler processing failed", err));
	} finally {
		if (asyncManager.isConcurrentHandlingStarted()) {
			// Instead of postHandle and afterCompletion
			if (mappedHandler != null) {
				mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
			}
		} else {
			// Clean up any resources used by a multipart request.
			if (multipartRequestParsed) {
				cleanupMultipart(processedRequest);
			}
		}
	}
}

以上就是大体的执行请求流程

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

熊海CMS漏洞练习平台的一次xss、sql注入、越权黑盒思路分析

简介 熊海CMS是由熊海开发的一款功能丰富的网站综合管理系统&#xff0c;广泛应用于个人博客、个人网站以及企业网站&#xff0c;本文章用于黑盒测试&#xff0c;如果需要「源码审计」后台回复【「CMS」】获取即可&#xff0c;精心准备了40多个cms源码漏洞平台&#xff0c;供宝…

记一次docker容器安装MySQL,navicat无法连接报错(10060错误)

今天在云服务器上使用docker部署mysql 8.0.11时&#xff0c;遇到了一个诡异的问题&#xff0c;在云服务器的docker容器内可以连接上mysql&#xff0c;然而在自己电脑上连接mysql时报错&#xff1a;Can‘t connect to MySQL server on localhost (10060) 下面是网上搜寻的几种可…

《SpringBoot 整合 Prometheus 采集自定义指标》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; 近期刚转战 CSDN&#xff0c;会严格把控文章质量&#xff0c;绝不滥竽充数&#xff0c;如需交流&#xff…

C语言 ——— const关键字

目录 const修饰变量 const修饰指针变量 const放在指针类型之前 const放在指针类型之后 小结 const修饰变量 当 const 修饰 int类型 的 变量a 后&#xff0c;此时的 变量a 就具有长属性&#xff0c;就不能被赋值为其他的值 将 变量a的地址 存储到 指针变量pa 中&#xff…

计算机网络——常见问题汇总

1. introduction 1.1 Explain what a communication protocol is and why its important. A communication protocol is a set of rules and conventions(公约) that govern(统治) how data is transmitted and received between devices(设备), systems, or entities in a ne…

1、BOREDHACKERBLOG:社交网络

靶机&#xff1a;https://www.vulnhub.com/entry/boredhackerblog-social-network,454/ 参考&#xff1a;Vulnhub靶机&#xff1a;BOREDHACKERBLOG: SOCIAL NETWORK_boredhackerblog系列-CSDN博客 需要使用virtualbox。 先去官网下载了最新版的vietualbox&#xff0c;以及把这…

使用 Unstructured.io 和 Elasticsearch 向量数据库搜索复杂文档

作者&#xff1a;来自 Elastic Amy Ghate, Rishikesh Radhakrishnan, Hemant Malik 使用非结构化和 Elasticsearch 向量数据库为 RAG 应用程序提取和搜索复杂的专有文档 在使信息可搜索之前解析文档是构建实际 RAG 应用程序的重要步骤。Unstructured.io 和 Elasticsearch 在此…

Admin.NET源码学习(2:安装并运行前端)

根据Admin.NET的GitHub主页介绍&#xff0c;前端运行步骤需要运行pnpm命令。百度pnpm的话&#xff0c;需要支持npm相关的命令支持。   根据参考文献4&#xff0c;安装Node.js后会提供npm命令支持&#xff08;npm是Node.js的软件包管理器&#xff0c;用于安装、发布和共享Jav…

FreeRTOS 入门 知识

什么是FreeRTOS FreeRTOS 是一个轻量级的实时操作系统&#xff08;RTOS&#xff09;&#xff0c;由 Richard Barry 在 2003 年开发&#xff0c;并且由亚马逊的 FreeRTOS 项目&#xff08;一个由 Amazon Web Services (AWS) 支持的开源项目&#xff09;进一步推动和发展。FreeR…

顺序表算法 - 合并两个有序数组

88. 合并两个有序数组 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/merge-sorted-array/description/思路: void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n) {int l1,l2,l3;l1 m-1;l2 n-1;l3 mn-1;//l1和l2大于…

DispatcherServlet懒加载带来的问题和思考

问题 DispatcherServlet的懒加载会导致在用户在进行第一次请求的时候会比正常慢很多&#xff0c;如果这个时候大量请求同时过来&#xff0c;那么阻塞和cpu的暴增就会显而易见。 背景 在回顾SpringMvc对servlet的增强的过程中&#xff0c;突然发现DispatcherServlet是懒加载的…

7.2 AQS原理

AQS 原理 概述 全称是 AbstractQueuedSynchronizer&#xff0c;是阻塞式锁和相关的同步器工具的框架。 特点&#xff1a; 用 state 属性来表示资源的状态&#xff08;分独占模式和共享模式&#xff09;&#xff0c;子类需要定义如何维护这个状态&#xff0c;控制如何获取锁和…

JAVA自定义注释

interface 声明 package test; public interface InProgress { } InProgress public void calculateInterest(float amount, float rate) { } 带成员 public interface TODO {String value(); } InProgress //只有成员变量名有value时&#xff0c;值有给value赋值时可以这…

redis源码分析之底层数据结构(一)-动态字符串sds

1.绪论 我们知道redis是由c语言实现的&#xff0c;c语言中是自带字符串的&#xff0c;但是为什么redis还要再实现自己的动态字符串呢&#xff0c;这种动态字符串的底层数据结构是怎样的呢?接下来我们带着这些问题来看一看redis中的动态字符串sds。 2.sds的组成 struct __at…

MySQL覆盖索引和索引跳跃扫描

最近在深入学习MySQL&#xff0c;在学习最左匹配原则的时候&#xff0c;遇到了一个有意思的事情。请听我细细道来。 我的MySQL版本为8.0.32 可以通过 show variables like version; 查看使用的版本。 准备工作&#xff1a; 先建表&#xff0c;SQL语句如下&#xff1a; c…

ARM学习(29)NXP 双coreMCU IMX1160学习----NorFlash 启动引脚选择

ARM学习&#xff08;28&#xff09;NXP 双coreMCU IMX1160学习----NorFlash 启动引脚选择 1、多种启动方式介绍 IMX1166 支持多组flexSPI 引脚启动&#xff0c;FlexSPI1以及FlexSPI2&#xff0c;通过boot cfg可以切换FlexSPI得实例。 每个实例又支持多组引脚&#xff0c;总共…

Linux系统的用户组管理和权限以及创建用户

1.Linux是多用户的操作系统&#xff0c;正如在Windows系统中可以进行用户账号的切换&#xff0c;Linux同样允许多用户操作。在Linux服务器环境中&#xff0c;通常由多名运维人员共同管理&#xff0c;而这些运维人员各自拥有不同的权限和级别。因此&#xff0c;我们可以根据每个…

LeetCode 3011.判断一个数组是否可以变为有序

注&#xff1a;这个题目有序的意思是“升序” 解法一&#xff1a;bubblesort O(nlogn) 核心思想&#xff1a;冒泡每次会将一个数归位到最后的位置上&#xff0c;所以我们如果碰到无法向右交换的数字&#xff0c;即可return false class Solution { public:// 返回一个十进制…

《昇思25天学习打卡营第2天|02快速入门》

课程目标 这节课准备再学习下训练模型的基本流程&#xff0c;因此还是选择快速入门课程。 整体流程 整体介绍下流程&#xff1a; 数据处理构建网络模型训练模型保存模型加载模型 思路是比较清晰的&#xff0c;看来文档写的是比较连贯合理的。 数据处理 看数据也是手写体数…