当前位置: 首页 > news >正文

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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvchttps://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);// 初始化HandlerMappinginitHandlerMappings(context);// 初始化HandlerAdapterinitHandlerAdapters(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所覆盖,这时会跑到子类中进行执行

@Overrideprotected 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 logicdoGet(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 lessmaybeSetLastModified(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 afterCompletionif (mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}} else {// Clean up any resources used by a multipart request.if (multipartRequestParsed) {cleanupMultipart(processedRequest);}}}
}

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

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • SpringBoot+Vue实现简单的文件上传(Excel篇)
  • 【机器翻译】基于术语词典干预的机器翻译挑战赛
  • Jenkins 离线升级
  • 【排序算法】—— 归并排序
  • 海事无人机解决方案
  • 前端开发(基础)
  • 对B-树的理解
  • bug定位策略
  • python连接kafka生产者发送消息
  • Memcached vs Redis——Java项目缓存选择
  • 数据结构-C语言-排序(2)
  • excel系列(二) - 利用 easypoi 快速实现 excel 文件导入导出
  • QQ频道导航退出
  • CV09_深度学习模块之间的缝合教学(4)--调参
  • 自定义 Java ClassLoader:深入探索
  • 【前端学习】-粗谈选择器
  • CentOS 7 防火墙操作
  • classpath对获取配置文件的影响
  • Java读取Properties文件的六种方法
  • NLPIR语义挖掘平台推动行业大数据应用服务
  • Node 版本管理
  • php面试题 汇集2
  • Python学习之路16-使用API
  • vue和cordova项目整合打包,并实现vue调用android的相机的demo
  • 关于Flux,Vuex,Redux的思考
  • 缓存与缓冲
  • 三分钟教你同步 Visual Studio Code 设置
  • 适配mpvue平台的的微信小程序日历组件mpvue-calendar
  • 详解移动APP与web APP的区别
  • 项目实战-Api的解决方案
  • 终端用户监控:真实用户监控还是模拟监控?
  • Unity3D - 异步加载游戏场景与异步加载游戏资源进度条 ...
  • ​力扣解法汇总946-验证栈序列
  • # Pytorch 中可以直接调用的Loss Functions总结:
  • $refs 、$nextTic、动态组件、name的使用
  • (C#)一个最简单的链表类
  • (LeetCode C++)盛最多水的容器
  • (大众金融)SQL server面试题(1)-总销售量最少的3个型号的车及其总销售量
  • (官网安装) 基于CentOS 7安装MangoDB和MangoDB Shell
  • (机器学习-深度学习快速入门)第三章机器学习-第二节:机器学习模型之线性回归
  • (接口自动化)Python3操作MySQL数据库
  • .net FrameWork简介,数组,枚举
  • .NET 程序如何获取图片的宽高(框架自带多种方法的不同性能)
  • .NET/C# 使用 #if 和 Conditional 特性来按条件编译代码的不同原理和适用场景
  • .net反混淆脱壳工具de4dot的使用
  • .NET开源快速、强大、免费的电子表格组件
  • .NET企业级应用架构设计系列之结尾篇
  • .so文件(linux系统)
  • 。。。。。
  • @Data注解的作用
  • @基于大模型的旅游路线推荐方案
  • []T 还是 []*T, 这是一个问题
  • [AIGC] Nacos:一个简单 yet powerful 的配置中心和服务注册中心
  • [AutoSar]BSW_OS 01 priority ceiling protocol(PCP)
  • [C# WPF] DataGrid选中行或选中单元格的背景和字体颜色修改