springmvc是什么_当一个http请求来临时,SpringMVC究竟偷偷帮你做了什么?

202705f9272a4d6d4445fa0a3bba66da.png

前文

Spring MVC是一个基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架,通过把Model,View,Controller分离,将web层进行职责解耦,把复杂的web应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合。

Springmvc的优点:

  1. 可以支持各种视图技术,而不仅仅局限于JSP;
  2. 与Spring框架集成(如IoC容器、AOP等);
  3. 清晰的角色分配:前端控制器(dispatcherServlet) , 请求到处理器映射(handlerMapping), 处理器适配器(HandlerAdapter), 视图解析器(ViewResolver)。
  4. 支持各种请求资源的映射策略。

请求映射器源码解析

这些优秀的特性使得它在企业级开发中使用率超过98%,如此优秀的框架,你是否疑惑过,在一个请求到达后,是如何被SpringMvc拦截到并处理的?

89660e92f220e59c9c490000e59302ae.png

相信大家对上面的流程图都很熟悉,或多或少无论是在准备面试的时候,还是自己学习的时候,都会接触到这个流程图,我见过很多的人,对着这个图死记硬背!我也面试过一些技术人员,问到这块知识,仰着头闭着眼(夸张一下)把这块知识说出来,再往深了问一点就懵逼,归根到底就是对框架理解不够深刻。

SpringMVC是如何感知到每个方法对应的url路径的?

org.springframework.web.servlet.handler.AbstractHandlerMethodMapping 实现 org.springframework.beans.factory.InitializingBean 覆盖 afterPropertiesSet方法,这个方法会在Spring容器初始化的时候回调该方法

34af1a8df0edd958ac738948e3ed1ceb.png
该方法类定义为
@Override
public void afterPropertiesSet() {initHandlerMethods();
}
复制代码
调用initHandlerMethods方法,那么initHandlerMethods里面干了什么事情呢?对该方法逐步分析!
/*** Scan beans in the ApplicationContext, detect and register handler methods.* @see #getCandidateBeanNames()* @see #processCandidateBean* @see #handlerMethodsInitialized*/
protected void initHandlerMethods() {for (String beanName : getCandidateBeanNames()) {if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {processCandidateBean(beanName);}}handlerMethodsInitialized(getHandlerMethods());
}
首先 getCandidateBeanNames() 方法,我们看它的定义
/*** Determine the names of candidate beans in the application context.* @since 5.1* @see #setDetectHandlerMethodsInAncestorContexts* @see BeanFactoryUtils#beanNamesForTypeIncludingAncestors*/
protected String[] getCandidateBeanNames() {return (this.detectHandlerMethodsInAncestorContexts ?BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :obtainApplicationContext().getBeanNamesForType(Object.class));
}
  • 这个方法本质的目的就是为了从bean容器中,获取所有的bean,为什么是获取全部的 因为它是基于Object.class类型来获取的类,故而是全部的类,但是这个方法其实深究起来,知识点很多,因为它涉及到Spring父子容器的知识点,所以我决定,后面花一篇文档单独去讲,这里你只需要知道,这个方法可以获取Spring容器里面所有的bean然后返回!
initHandlerMethods() 获取到所有的bean之后然后循环遍历,我们将目光聚集在循环体内部的processCandidateBean方法
protected void processCandidateBean(String beanName) {Class<?> beanType = null;try {beanType = obtainApplicationContext().getType(beanName);}catch (Throwable ex) {// An unresolvable bean type, probably from a lazy bean - let's ignore it.if (logger.isTraceEnabled()) {logger.trace("Could not resolve type for bean '" + beanName + "'", ex);}}if (beanType != null && isHandler(beanType)) {detectHandlerMethods(beanName);}
}
  • beanType = obtainApplicationContext().getType(beanName); 这个方法是基于bean名称获取该类的Class对象
  • isHandler(beanType) 这个方法是判断该类是是加注了Controller注解或者RequestMapping
@Override
protected boolean isHandler(Class<?> beanType) {return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
  • detectHandlerMethods(Object handler)
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,(MethodIntrospector.MetadataLookup<T>) method -> {try {return getMappingForMethod(method, userType);}catch (Throwable ex) {throw new IllegalStateException("Invalid mapping on handler class [" +userType.getName() + "]: " + method, ex);}});

内部该段逻辑可以遍历某个类下所有的方法

  • getMappingForMethod(method, userType); 这个方法的内部做了什么呢? 该i方内部读取所有的映射方法的所有定义,具体的逻辑如下

8a0f366bbae9cd724bb58223e409f3a2.png
设置了该方法 的映射路径,方法对象,方法参数,设置的方法请求头,消费类型,可接受类型,映射名称等信息封装成RequestMappingInfo对象返回!
  • getPathPrefix(handlerType); 该方法是处理方法前缀,如果存在和前者方法级别的合并
  • 最终返回一个方法与方法描述信息的map映射集合(Map<Method, RequestMappingInfo>),循环遍历该集合! Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);找到该方法的代理方法! registerHandlerMethod(handler, invocableMethod, mapping);注册该方法! 我们深入该方法摒弃其他与本文无关的代码,会发现这么一段代码!

f8242b8aac2c026dc9ad1adf3b2ee347.png

会发现,我们方法上标注的 url会和前面读取的该方法的定义绑定在一个叫做 urlLookup的方法里面,请大家记住这个方法,这个方法对我们理解SpringMvc的处理逻辑有大用处!

3.请求获取逻辑源码解析

现在,整个工程所有对应的@requestMapping的方法已经被缓存,以该方法为例子!
@RestController
public class TestController {@RequestMapping("test")public String test(){return "success";}
}

现在在urlLookup属性里面就有一个 key为test,value为test()方法详细定义的 k:v键值对:v:

我们看下下面这个类图,DispatcherServlet这个关键的中央类,实际上是Servlet的子类,熟悉Servlet的同学都知道,之前在做Servlet开发的时候,所有的请求经过配置后都会被内部的doget和dopost方法拦截,至此SpringMvc为什么能够拦截URL也就不难分析了,拦截到url后,进入如下的流程调用链!

7bd0e94372dff07b4c944f9d538f2696.png
请求经由 org.springframework.web.servlet.FrameworkServlet#doGet捕获,委托给org.springframework.web.servlet.FrameworkServlet#processRequest方法,最后在调用org.springframework.web.servlet.DispatcherServlet#doService来处理真正的逻辑!

我们看一下这个方法里面的一些主要逻辑吧!

org.springframework.web.servlet.DispatcherServlet#doDispatch调用org.springframework.web.servlet.DispatcherServlet#getHandler方法,再次调用org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler经由org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal方法的org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#lookupHandlerMethod的org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#getMappingsByUrl

讲过这么长的调用链是不是懵了,此时我们终于看到了正主!

/*** Return matches for the given URL path. Not thread-safe.* @see #acquireReadLock()*/
@Nullable
public List<T> getMappingsByUrl(String urlPath) {return this.urlLookup.get(urlPath);
}

这段代码是不是熟悉?这就是我们Spring容器在初始化的时候将url和方法定义放置的那个属性,现在Spring容器经由DispatcherServlet拦截请求后又重新找到该方法,并且返回!此时就完成了MVC流程图里面的HandlerMapping处理映射器的部分!

本章关于请求映射器的源码分析到这也就结束了,后续作者会将处理适配器,处理器,视图解析器一一讲明白,其实后续的逻辑也就很简单了,简单来说,拿到方法后反射执行该方法(不一定,一般场景是这样),然后拿到返回值,判断是否有@responseBody注解,判断是否需要转换成json,再通过write写回到页面!大致流程就是这样,详细过程作者后续会写!

经过今天的流程分析,你能否基于Servlet写一个属于自己的SpringMvc呢?

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

怎么用python画饼状图_Python入门进阶:Python绘制饼图到Microsoft Excel

原标题&#xff1a;Python入门进阶&#xff1a;Python绘制饼图到Microsoft Excel 来自&#xff1a;Linux迷https://www.linuxmi.com/python-pie-chart-microsoft-excel.html 在本文中&#xff0c;我想向您展示使用Python向Microsoft Excel绘制饼图&#xff0c;为此我们将使用Xl…

Linux系统Shutdown命令定时关机详解

Linux系统下的shutdown命令用于安全的关闭/重启计算机&#xff0c;它不仅可以方便的实现定时关机&#xff0c;还可以由用户决定关机时的相关参数。在执行shutdown命令时&#xff0c;系统会给每个终端&#xff08;用户&#xff09;发送一条屏显&#xff0c;提示关机操作。定时关…

_ASSERTE(_CrtIsValidHeapPointer(pUserData))错误详解

可能原因&#xff1a;DLL和EXE主程序使用的不是同一个堆造成。 解决办法&#xff1a; 1. 采用谁分配谁释放的原则&#xff1b; 2. 绕过 new 和 delete&#xff0c;使用 GlovalAlloc 和 GlobalFree&#xff1b; 3. 更改工程选项&#xff0c; release 版本肯定不会出现这个失败&a…

【机器学习】【线性回归】梯度下降

文章目录 [toc]数据集实际值估计值估计误差代价函数学习率参数更新Python实现导包数据预处理迭代过程数据可视化完整代码 线性拟合结果代价结果 个人主页&#xff1a;丷从心 系列专栏&#xff1a;机器学习 数据集 ( x ( i ) , y ( i ) ) , i 1 , 2 , ⋯ , m \left(x^{(i)} , …

python api是什么_Python/C API

Python/C API Python/C API可能是被最广泛使用的方法。它不仅简单&#xff0c;而且可以在C代码中操作你的Python对象。 这种方法需要以特定的方式来编写C代码以供Python去调用它。所有的Python对象都被表示为一种叫做PyObject的结构体&#xff0c;并且Python.h头文件中提供了各…

Sublime Text插件列表

本文由伯乐在线 - 艾凌风 翻译&#xff0c;黄利民 校稿。英文出处&#xff1a;ipestov.com。欢迎加入翻译组。本文收录了作者辛苦收集的Sublime Text最佳插件&#xff0c;很全。 最佳的Sublime Text 插件 朋友们你们好&#xff01;我尝试着收集了最佳的ST插件&#xff0c;这些插…

C语言sendto()函数:经socket传送数据

相关函数&#xff1a;send, sendmsg, recv, recvfrom, socket头文件&#xff1a;#include <sys/types.h> #include <sys/socket.h>定义函数&#xff1a;int sendto(int s, const void * msg, int len, unsigned int flags, const struct sockaddr * to, int tole…

javaone_JavaOne 2012:向上,向上和向外:使用Akka扩展软件

javaone在晚些时候的社区主题演讲之后&#xff0c;我前往希尔顿金门大桥3/4/5观看了维克多巴生 &#xff08; Viktor Klang &#xff09;的&#xff08; Typesafe &#xff09;“向上&#xff0c;向上和向外&#xff1a;Akka”演讲。 巴生&#xff08;Klang&#xff09;是Akka的…

Windows平台下动态链接库的总结

1、 动态链接库与静态连接库 静态连接库与动态链接库都是经过编译器编译之后的&#xff0c;在计算机上可以直接运行的二进制目标文件&#xff0c;就像exe文件一样&#xff0c;但不同于exe文件的是静态链接库和动态链接库不可以独立运行&#xff0c;一般而言&#xff0c;动态链接…

python建模分析实操_城市公交站点设置优化模型-基于Python

城市公交站点设置的优化分析 一、模型应用 进入21世纪以来&#xff0c;我国城市公共交通飞速发展&#xff0c;然而随着经济社会发展&#xff0c;城市不断升级以及人民生活品质越来越好&#xff0c;城市交通拥堵、出行不便等问题日益突出&#xff0c;严重损坏了市民日常的生活体…

【递归:把目录下所有文件的绝对路径给输出在控制台】

package com.bornsoft.test.capitalpool.tyc;import java.io.File;/*** author shusheng* description* Email shushengyiji.com* date 2018/10/16 10:26*/ public class DiGuiDemo2 {/***需求&#xff1a;请大家把目录下所有文件的绝对路径给输出在控制台*分析&#xff1a;*A:…

UDP sendto和recvfrom使用详解

在网络编程中&#xff0c;UDP运用非常广泛。很多网络协议是基于UDP来实现的&#xff0c;如SNMP等。大家常常用到的局域网文件传输软件飞鸽传书也是基于UDP实现的。 本篇文章跟大家分享linux下UDP的使用和实现&#xff0c;主要介绍下sendto()和recvfrom()两个函数的使用&#xf…

SpringOne Platform 2016回顾

我最近结束了在拉斯维加斯参加SpringOne Platform会议的总结。 这是我第一次参加SpringOne。 这是聆听演讲并与软件开发领域的一些顶级专家进行对话的一种体验。 如果您没有参加SpringOne&#xff0c;那么您肯定会想要阅读这篇文章。 我们将介绍这四个主题&#xff0c;以及如何…

欧姆龙cp1hum读保护解密步骤_欧姆龙PLC的NJ系列NJ产品功能介绍

欧姆龙PLC的NJ 系列NJ运动、逻辑和视觉集于一体欧姆龙PLC的NJ 系列NJ特点One Machine Control运动、逻辑和视觉集于一体将组成机械所需的各种控制设备汇集于一体&#xff0c;使用一个软件即可进行控制。 这就是Sysmac自动化平台的努力目标。 我们的新型机器自动化控制器NJ系列通…

关于CUDA和CuDNN配置的小问题

为了方便组员操作&#xff0c;简单写一下CUDA的配置啦。 首先你需要一台电脑&#xff0c;有NVDIA显卡的那种&#xff08;就那个煤气灶&#xff0c;你懂我意思吧&#xff09;&#xff0c;然后就继续往下走吧&#xff0c;如果没有的话可以找一下右上角的红叉了&#xff0c;这篇文…

PyMongo--非关系型数据库mongodb入门(一步一步 版)

PyMongo--非关系型数据库mongodb入门&#xff08;一步一步 版&#xff09; 本文主要内容&#xff1a; 1.简要介绍mongodb 2.Pymongo 3.mongo shell 4.我的mongodb入门之旅 1.简要介绍mongodb MongoDB是一个基于分布式文件存储的数据库。由C语言编写。旨在为WEB应用提供可扩展的…

python画图颜色种类_Python可视化|matplotlib07-python colormap(颜色映射)(三)

本篇详细介绍matplotlib内置的颜色条Colormap使用。 本文将学到什么&#xff1f; 1、colormap名称 2、colormap可视化 3、colormap使用方法 4、参考资料 更好的阅读体验请戳&#xff1a; 1、colormap名称 colormap颜色通过matplotlib的cm模块调用&#xff0c;print(dir(cm))即可…

element table 组件内容换行方案

element table 组件内容换行方案 white-space的值&#xff1a; normal 默认。空白会被浏览器忽略。pre 空白会被浏览器保留。其行为方式类似 HTML 中的<pre> 标签。nowrap 文本不会换行&#xff0c;文本会在在同一行上继续&#xff0c;直到遇到 <br>标签为止。pre-…

UDP Socket编程 C/C++实现 (Windows Platform SDK)

挺无聊一小程序&#xff0c;俩SB一人说一句&#xff0c;据说聊天程序最好用UDP写。 Server&#xff1a; [csharp] view plaincopy #pragma comment (lib,"ws2_32.lib") #include <Winsock2.h> #include <stdio.h> void main() {…

关于Mongodb的全面总结,学习mongodb的人,可以从这里开始!

MongoDB的内部构造《MongoDB The Definitive Guide》 MongoDB的官方文档基本是how to do的介绍&#xff0c;而关于how it worked却少之又少&#xff0c;本人也刚买了《MongoDB TheDefinitive Guide》的影印版&#xff0c;还没来得及看&#xff0c;本文原作者将其书中一些关于M…