DispatcherServlet 源码分析
一.DispatcherServlet 源码分析
本文仅了解源码内容即可。
1.1初始化(了解)
web.xml是web项⽬的配置⽂件,⼀般的web⼯程都会⽤到web.xml来Listener,Filter,Servlet等, Spring框架从3.1版本开始⽀持Servlet3.0, DispatcherServlet, 实现不再使⽤web.xml
init() 具体代码如下:
@Overridepublic final void init() throws ServletException {try {// ServletConfigPropertyValues 是静态内部类,使⽤ ServletConfig 获取web.xml 中配置的参数PropertyValues pvs = newServletConfigPropertyValues(getServletConfig(), this.requiredProperties);// 使⽤ BeanWrapper 来构造 DispatcherServletBeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);ResourceLoader resourceLoader = newServletContextResourceLoader(getServletContext());bw.registerCustomEditor(Resource.class, newResourceEditor(resourceLoader, getEnvironment()));initBeanWrapper(bw);bw.setPropertyValues(pvs, true);} catch (BeansException ex) {}// 让⼦类实现的⽅法,这种在⽗类定义在⼦类实现的⽅式叫做模版⽅法模式initServletBean();}
/*** Overridden method of {@link HttpServletBean}, invoked after any bean properties* have been set. Creates this servlet's WebApplicationContext.*/@Overrideprotected final void initServletBean() throws ServletException {getServletContext().log("Initializing Spring " + getClass().getSimpleName()+ " '" + getServletName() + "'");if (logger.isInfoEnabled()) {logger.info("Initializing Servlet '" + getServletName() + "'");}long startTime = System.currentTimeMillis();try {//创建ApplicationContext容器this.webApplicationContext = initWebApplicationContext();initFrameworkServlet();}catch (ServletException | RuntimeException ex) {logger.error("Context initialization failed", ex);throw ex;}if (logger.isDebugEnabled()) {String value = this.enableLoggingRequestDetails ?"shown which may lead to unsafe logging of potentially sensitive data" :"masked to prevent unsafe logging of potentially sensitive data";logger.debug("enableLoggingRequestDetails='" +this.enableLoggingRequestDetails +"': request parameters and headers will be " + value);}if (logger.isInfoEnabled()) {logger.info("Completed initialization in " + (System.currentTimeMillis()- startTime) + " ms");}}
源码跟踪技巧:
在阅读框架源码的时候, ⼀定要抓住关键点, 找到核⼼流程.切忌从头到尾⼀⾏⼀⾏代码去看, ⼀个⽅法的去研究, ⼀定要找到关键流程, 抓住关键点, 先在宏观上对整个流程或者整个原理有⼀个认识, 有精⼒再去研究其中的细节
初始化web容器的过程中, 会通过onRefresh 来初始化SpringMVC的容器
protected WebApplicationContext initWebApplicationContext() {//...if (!this.refreshEventReceived) {//初始化Spring MVCsynchronized (this.onRefreshMonitor) {onRefresh(wac);}}return wac;}@Overrideprotected void onRefresh(ApplicationContext context) {initStrategies(context);}/*** Initialize the strategy objects that this servlet uses.* <p>May be overridden in subclasses in order to initialize further strategy objects.*/protected void initStrategies(ApplicationContext context) {initMultipartResolver(context);initLocaleResolver(context);initThemeResolver(context);initHandlerMappings(context);initHandlerAdapters(context);initHandlerExceptionResolvers(context)initRequestToViewNameTranslator(context);initViewResolvers(context);initFlashMapManager(context);
}
1.初始化⽂件上传解析器MultipartResolver: 从应⽤上下⽂中获取名称为multipartResolver的Bean,如果没有名为multipartResolver的Bean,则没有提供上传⽂件的解析器2.初始化区域解析器LocaleResolver: 从应⽤上下⽂中获取名称为localeResolver的Bean,如果没有这个Bean,则默认使⽤AcceptHeaderLocaleResolver作为区域解析器3.初始化主题解析器ThemeResolver: 从应⽤上下⽂中获取名称为themeResolver的Bean,如果没有这个Bean,则默认使⽤FixedThemeResolver作为主题解析器4.初始化处理器映射器HandlerMappings: 处理器映射器作⽤,1)通过处理器映射器找到对应的处理器适配器,将请求交给适配器处理;2)缓存每个请求地址URL对应的位置(Controller.xxx⽅法);如果在ApplicationContext发现有HandlerMappings,则从ApplicationContext中获取到所有的HandlerMappings,并进⾏排序;如果在ApplicationContext中没有发现有处理器映射器,则默认BeanNameUrlHandlerMapping作为处理器映射器5.初始化处理器适配器HandlerAdapter: 作⽤是通过调⽤具体的⽅法来处理具体的请求;如果在 ApplicationContext发现有handlerAdapter,则从ApplicationContext中获取到所有的HandlerAdapter,并进⾏排序;如果在ApplicationContext中没有发现处理器适配器,则默认 SimpleControllerHandlerAdapter作为处理器适配器6.初始化异常处理器解析器HandlerExceptionResolver: 如果在ApplicationContext发现有handlerExceptionResolver,则从ApplicationContext中获取到所有的HandlerExceptionResolver,并进⾏排序;如果在ApplicationContext中没有发现异常处理器解析器,则不设置异常处理器7.初始化RequestToViewNameTranslator: 其作⽤是从Request中获取viewName,从ApplicationContext发现有viewNameTranslator的Bean,如果没有,则默认使⽤DefaultRequestToViewNameTranslator8.初始化视图解析器ViewResolvers: 先从ApplicationContext中获取名为viewResolver的Bean如果没有,则默认InternalResourceViewResolver作为视图解析器9.初始化FlashMapManager: 其作⽤是⽤于检索和保存FlashMap(保存从⼀个URL重定向到另⼀个URL时的参数信息),从ApplicationContext发现有flashMapManager的Bean,如果没有,则默认使⽤DefaultFlashMapManager
1.2 处理请求(核心)
protected void doDispatch(HttpServletRequest request, HttpServletResponseresponse) throws Exception {HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {try {ModelAndView mv = null;Exception dispatchException = null;try {processedRequest = this.checkMultipart(request);multipartRequestParsed = processedRequest != request;//1. 获取执⾏链//遍历所有的 HandlerMapping 找到与请求对应的HandlermappedHandler = this.getHandler(processedRequest);if (mappedHandler == null) {this.noHandlerFound(processedRequest, response);return;}//2. 获取适配器//遍历所有的 HandlerAdapter,找到可以处理该 Handler 的HandlerAdapterHandlerAdapter ha =this.getHandlerAdapter(mappedHandler.getHandler());String method = request.getMethod();boolean isGet = HttpMethod.GET.matches(method);if (isGet || HttpMethod.HEAD.matches(method)) {long lastModified = ha.getLastModified(request,mappedHandler.getHandler());if ((new ServletWebRequest(request,response)).checkNotModified(lastModified) && isGet) {return;}}//3. 执⾏拦截器preHandle⽅法if (!mappedHandler.applyPreHandle(processedRequest, response)){return;}//4. 执⾏⽬标⽅法mv = ha.handle(processedRequest, response,mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}this.applyDefaultViewName(processedRequest, mv);//5. 执⾏拦截器postHandle⽅法mappedHandler.applyPostHandle(processedRequest, response, mv);} catch (Exception var20) {dispatchException = var20;} catch (Throwable var21) {dispatchException = new NestedServletException("Handlerdispatch failed", var21);}//6. 处理视图, 处理之后执⾏拦截器afterCompletion⽅法this.processDispatchResult(processedRequest, response,mappedHandler, mv, (Exception)dispatchException);} catch (Exception var22) {//7. 执⾏拦截器afterCompletion⽅法this.triggerAfterCompletion(processedRequest, response,mappedHandler, var22);} catch (Throwable var23) {this.triggerAfterCompletion(processedRequest, response,mappedHandler, new NestedServletException("Handler processing failed", var23));}} finally {if (asyncManager.isConcurrentHandlingStarted()) {if (mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}} else if (multipartRequestParsed) {this.cleanupMultipart(processedRequest);}}} HandlerAdapter 主要⽤于⽀持不同类型的处理器(如 Controller、HttpRequestHandler 或者Servlet 等),让它们能够适配统⼀的请求处理流程。这样,Spring MVC 可以通过⼀个统⼀的接⼝来处理来⾃各种处理器的请求。
boolean applyPreHandle(HttpServletRequest request, HttpServletResponseresponse) throws Exception {for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex =i++) {// 获取项⽬中使⽤的拦截器 HandlerInterceptorHandlerInterceptor interceptor =(HandlerInterceptor)this.interceptorList.get(i);if (!interceptor.preHandle(request, response, this.handler)) {this.triggerAfterCompletion(request, response, (Exception)null);return false;}}return true;}
相关文章:
DispatcherServlet 源码分析
一.DispatcherServlet 源码分析 本文仅了解源码内容即可。 1.观察我们的服务启动⽇志: 当Tomcat启动之后, 有⼀个核⼼的类DispatcherServlet, 它来控制程序的执⾏顺序.所有请求都会先进到DispatcherServlet,执⾏doDispatch 调度⽅法. 如果有拦截器, 会先执⾏拦截器…...
代码随想录算法训练营第十八天| 530.二叉搜索树的最小绝对差 ● 501.二叉搜索树中的众数 ● 236. 二叉树的最近公共祖先
题目: 530. 二叉搜索树的最小绝对差 给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值 。 差值是一个正数,其数值等于两值之差的绝对值。 示例 1: 输入:root [4,2,6,1,3] 输出:…...
会议室占用的时间(75%用例)D卷(JavaPythonC++Node.jsC语言)
现有若干个会议,所有会议共享--个会议室,用数组表示各个会议的开始时间和结束时间,格式为: 会议1开始时间,会议1结束时间 会议2开始时间,会议2结束时间 请计算会议室占用时间段。 输入描述: 第一行输入一个整数 n,表示会议数量 之后输入n行,每行两个整数,以空格分隔,…...
C++初阶_1:namespace
本章详细解说:namespace 。 namespace: namespace,意为:命名空间,c的关键字(关键字,就是提示:取变量名,函数名时不能与之撞名)。 namespace的价值: 为了解…...
低代码开发平台:效率革命还是质量隐忧?
如何看待“低代码”开发平台的兴起? 近年来,“低代码”开发平台如雨后春笋般涌现,承诺让非专业人士也能快速构建应用程序。这种新兴技术正在挑战传统软件开发模式,引发了IT行业的广泛讨论。低代码平台是提高效率的利器࿰…...
在 Django 表单中传递自定义表单值到视图
在Django中,我们可以通过表单的初始化参数initial来传递自定义的初始值给表单字段。如果我们想要在视图中设置表单的初始值,可以在视图中创建表单的实例时,传递一个字典给initial参数。 1、问题背景 我们遇到了这样一个问题:在使…...
Android之复制文本(TextView)剪贴板
效果图: 功能简单就是点击“复制”,将邀请码复制到 剪贴板中 布局 <androidx.constraintlayout.widget.ConstraintLayoutandroid:id"id/clCode"android:layout_width"dimen/dp_0"android:layout_height"dimen/dp_49"…...
Ubuntu24.04设置国内镜像软件源
参考文章: Ubuntu24.04更换源地址(新版源更换方式) - 陌路寒暄 一、禁用原来的软件源 Ubuntu24.04 的源地址配置文件发生改变,不再使用以前的 sources.list 文件,升级 24.04 之后,该文件内容变成了一行注…...
分布式与微服务详解
1. 单机架构 只有一台机器,这个机器负责所有的工作 (这里假定一个电商网站) 现在大部分公司的产品都是单机架构 。 2. 分布式架构 一台机器的硬件资源是有限的,服务器处理请求是需要占用硬件资源的,如果业务增长&a…...
Vue设置滚动条自动保持到最底端
需求描述:在开发中我们常常会遇到需要让滚动条保持到最底端的需求,比如在开发一个聊天框时,请求接口拿到消息列表数据,展示到前端页面时,需要让滚动条自动滚到最底端,以此来展示最后的聊天记录。同时&#…...
uniapp创建一个新项目并导入uview-plus框架
近年来,随着技术的发展,人们越来越意识到跨平台和统一的重要性。对于同一款应用来说,一般都会有移动端、PC端、甚至小程序端。这是由于设备的不同,我们必须要做很多的客户端来满足不同的用户需求。但是由于硬件设施的不同…...
LabVIEW光电在线测振系统
开发了一种基于LabVIEW软件和光电技术的在线测振系统。该系统利用激光作为调制光源,并通过位置敏感型光电传感器(PSD)进行轴振动的实时检测。其主要特点包括非接触式测量、广泛的测量范围、高灵敏度和快速响应时间,且具备优良的抗…...
分布式光伏电站 转化能源 丰富用电结构
分布式光伏系统是一种利用分散式的可再生能源,在靠近用户端的地方安装光伏发电设施,通过光伏效应将太阳能转化为直流电能,并通过逆变器将其转换为交流电,以供用户使用的系统。以下是对分布式光伏系统的详细阐述: 一、…...
环境配置:如何在IntelliJ IDEA中安装和修改JDK版本配置(以Windows为例)
环境配置:如何在IntelliJ IDEA中安装和修改JDK版本配置(以Windows为例) 为了在Java开发中使用最新的功能和优化,升级和配置JDK版本是必不可少的。本文将详细介绍如何下载、安装、配置最新的JDK版本,并在IntelliJ IDEA…...
Spring AOP 原理——代理模式
目录 一、代理模式 1.1 静态代理 1.2 动态代理 1.2.1 JDK动态代理 1.2.2 CGLIB动态代理 Spring AOP 是基于动态代理来实现AOP的。 一、代理模式 代理模式, 也叫委托模式。该模式是为其他对象提供⼀种代理以控制对这个对象的访问。它的作用就是通过提供一个代理类&#…...
leetcode 234.回文链表
思路:其实就是判断反转链表是不是和原链表一样的问题。 我们可以借助反转链表的思路,首先我们先把链表的全部元素正向存储,然后再把链表进行反转。 之后我们再遍历反转之后的链表结点元素是不是和刚刚存储数组里面的元素一致就可以了。一旦…...
AD中Split Planes 的作用和功能
在 Altium Designer (AD) 中,Split Planes 功能允许你在一个平面层(例如电源层或地层)上分割出多个不同的区域,每个区域可以分配给不同的网络(net)。这对于设计中需要管理多种电源或接地类型的情况下非常有…...
[linux][命令]linux文件操作命令大全
Linux操作系统提供了丰富的文件操作命令,以下是一些常用的文件操作命令列表: 查看文件内容 cat:查看文件内容。less:分页显示文件内容。more:分页显示文件内容,一次显示一屏。head:查看文件的前…...
大语言模型 (LLM) 窥探未来
初始的探索 在NLP领域,早期的模型如 LSTM 和 GRU 在处理序列数据时取得了一定的成功。但随着数据量和复杂性的增加,这些模型开始显得力不从心。 Transformer的诞生 Transformer 模型的提出,它通过自注意力(Self-Attention&…...
WPF DataGrid调试错误总结
最近WPF中使用了DataGrid做了表格,框架版本为472,遇到了不少的问题,因为软件添加了一个退出进程的全局错误捕获,因此不得不解决所有问题,这边总结一下DataGrid的问题 EditItem is not allowed for this view 按字面意…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...
【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...
React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...
什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...
解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错
出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上,所以报错,到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本,cu、torch、cp 的版本一定要对…...
Mac下Android Studio扫描根目录卡死问题记录
环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…...
Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...
【Linux系统】Linux环境变量:系统配置的隐形指挥官
。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量:setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...
【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error
在前端开发中,JavaScript 异常是不可避免的。随着现代前端应用越来越多地使用异步操作(如 Promise、async/await 等),开发者常常会遇到 Uncaught (in promise) error 错误。这个错误是由于未正确处理 Promise 的拒绝(r…...
pycharm 设置环境出错
pycharm 设置环境出错 pycharm 新建项目,设置虚拟环境,出错 pycharm 出错 Cannot open Local Failed to start [powershell.exe, -NoExit, -ExecutionPolicy, Bypass, -File, C:\Program Files\JetBrains\PyCharm 2024.1.3\plugins\terminal\shell-int…...
