Springmvc源码分析、底层原理
1.Springmvc是如何找到Controller的?
首先在请求过来时,会先进入DispatcherServlet进行请求分发,执行DispatcherServlet类中的doDispatch()方法。
-------------- spring MVC找Controller流程 ----------------:
1.扫描整个项目(spring已经做了)定义一个map集合
2.拿到所有加了@Controller注解的类
3.遍历类中所有的方法对象(把方法作为对象)
4.判断方法是否加了@RequestMapping注解
5.把@RequestMapping注解的value(/getEmp/byId),作为map的key给put进去,把方法对象作为value放进map的value中
6.根据用户发送的请求,拿到uri
url:http://localhost:8081/index/do
uri:/index/do
7.使用请求的uri作为map的key,去map里边,查看是否由对应的返回值
2.---------------源码分析---------------
2.1请求过来时,会先到达DispatcherServlet类中,进行请求分发,看一下DispatcherServlet源码
主要功能:检查请求是否含有文件,调用getHandler()方法,使用handlerMappings映射器来处理业务,并没有直接去找controller,扩展性很强
public class DispatcherServlet extends FrameworkServlet {
.......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);// Determine handler for the current request.//*******调用gethandler方法,框架就是框架,getHandler并没由直接去找controller,//*******而使用handlerMappings映射器来处理业务,可扩展性很强!mappedHandler = getHandler(processedRequest);if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}........}
2.2 进入getHandler()源码
@Nullableprotected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {//******springmvc框架中新建了一个类 HandlerMapping ,以后去找controller的任务就交给他,//******有什么需要修改的地方只需要动HandlerMapping 就可以了,代码解耦if (this.handlerMappings != null) {for (HandlerMapping mapping : this.handlerMappings) {//******请求mapping.getHandler方法,在方法里拿到uriHandlerExecutionChain handler = mapping.getHandler(request);if (handler != null) {return handler;}}}return null;}
如上图:spring mvc 提供了两个HandlerMapping 映射器去找Controller,为什么定义两个映射器呢?
首先要知道Controller的定义方式 有 两种类型和三种实现
两种类型: BeanName类型 和 @Controller类型
三种实现: 实现HttpRequestHandler、实现Controller、加@Controller注解
实例:
1.@Controller类型
@Controller
public class MenuController {}
2.BeanName类型
实现controller接口:
//------实现controller接口,并加@Component注解
@Component(/getEmp/byId) //括号内为拦截路径
public class MenuController implements Controller{}
实现HttpRequestHandler接口:
//------实现HttpRequestHandler接口,并加@Component注解
@Component(/getEmp/byId) //括号内为拦截路径
public class MenuController implements HttpRequestHandler{}
以上两种方式都可以定义controller,HandlerMapping 映射器去找controller时也要根据不同情况去找!
1.RequestMappingHandlerMapping:找所有以@Controller注解标注的对象
2.BeanNameUrlHandlerMapping:找所有实现Controller、HttpRequestHandler接口的对象,称为BeanName类型
如果项目中控制层都是用@Controller标注的,则BeanNameUrlHandlerMapping映射器为空!!
进入mapping.getHandler(request)源码
2.3 进入getHandlerInternal源码
根据用户发送的请求,拿到uri
@Overrideprotected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {//*********getUrlPathHelper()获取uri路径 uri为/loginString lookupPath = getUrlPathHelper().getLookupPathForRequest(request);request.setAttribute(LOOKUP_PATH, lookupPath);this.mappingRegistry.acquireReadLock();try {//******接下来执行lookupHandlerMethod()方法HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);}finally {this.mappingRegistry.releaseReadLock();}}
根据用户发送的请求,拿到uri ,uri为/login,接下来执行lookupHandlerMethod()方法
2.4 进入lookupHandlerMethod()方法源码,
getMappingsByUrl()方法,传入了uri:/login
2.5 进入getMappingsByUrl()方法源码,
urlLookup:为最上边提到的spring扫描的map对象
urlPath:请求的uri
意义:根据uri去找对应的方法
@Nullablepublic List<T> getMappingsByUrl(String urlPath) {//return this.urlLookup.get(urlPath);}
这样就找到了Controller
3.HandlerAdapter
上边通过DispatchServlet中的doDispatch()方法中的getHandler()方法找到了Controller,接下来要调用getHandlerAdapter方法获取对应的HandlerAdapter
进入getHandlerAdapter()源码
适配器找到之后,使用适配器调用对应的方法
通过debug,可知进入的第一个handle方法
再进入invokeHandlerMethod中:
进入invokeForRequest中
调用完成结束!
注意:
在获取具体参数中的getMethodArgumentValues方法中,需要判断参数类型,(比如是不是string、integer之类的),spring mvc没有用普通的if判断,而是把封装了许多类,每一个类对应一个参数类型,针对不同的参数类型去不同的类中去处理,与业务代码解耦
3.参数处理器引发的思考
在上边获取具体参数时,其实经过了spring mvc的参数处理器处理后才返回的
我们在实际项目可以使用参数处理器吗?当然可以
自定义参数处理器使用场景
1.原始版:当前端传来一个参数username,在Controller层需要获取这个username的详细信息,需要从redis或者mysql中根据username查询到这个人的详情。如果很多Controller层都需要获取详细信息的话,每一个Controller中都需要写一段查询代码。
2.使用mvc参数处理器:
2.1:自定义一个注解@UserParam,加在需要获取详细信息的Controller的参数之前。代表这个参数需要使用参数处理器处理
2.2:自定义类ArgumentResovel实现HandlerMethodArgumentResolver参数处理器类
2.3:判断你的参数是否需要当前参数处理器来处理(依据:是否含有之定义注解@UserParam)
2.3:如果supportsParameter返回true, resolveArgument执行处理逻辑
2.4:把自定义参数处理器配置进去
Springmvc源码分析、底层原理相关推荐
- SpringMVC源码分析_1 SpringMVC容器启动和加载原理
SpringMVC源码分析_1 SpringMVC启动和加载原理 ...
- SpringMVC源码分析(4)剖析DispatcherServlet重要组件
简单介绍了一个请求的处理过程, 简略描述了调用过程,并没有涉及过多细节,如url匹配,报文解析转换等. <SpringMVC源码分析(2)DispatcherServlet的初始化>:介绍 ...
- MyBatis 源码分析 - 缓存原理
1.简介 在 Web 应用中,缓存是必不可少的组件.通常我们都会用 Redis 或 memcached 等缓存中间件,拦截大量奔向数据库的请求,减轻数据库压力.作为一个重要的组件,MyBatis 自然 ...
- springMVC源码分析--访问请求执行ServletInvocableHandlerMethod和InvocableHandlerMethod
在之前一篇博客中 springMVC源码分析--RequestMappingHandlerAdapter(五)我们已经简单的介绍到具体请求访问的执行某个Controller中的方法是在RequestM ...
- Tomcat7.0源码分析——请求原理分析(上)
前言 谈起Tomcat的诞生,最早可以追溯到1995年.近20年来,Tomcat始终是使用最广泛的Web服务器,由于其使用Java语言开发,所以广为Java程序员所熟悉.很多早期的J2EE项目,由程序 ...
- 简单直接让你也读懂springmvc源码分析(3.1)-- HandlerMethodReturnValueHandler
该源码分析系列文章分如下章节: springmvc源码分析(1)-- DispatcherServlet springmvc源码分析(2)-- HandlerMapping springmvc源码分析 ...
- SpringMVC源码分析_框架原理图
SpringMVC源码分析_框架原理图 ...
- laravel $request 多维数组取值_Laravel 运行原理分析与源码分析,底层看这篇足矣
精选文章内容 一.运行原理概述 laravel的入口文件 index.php 1.引入自动加载 autoload.php 2.创建应用实例,并同时完成了: 基本绑定($this.容器类Containe ...
- 04特性源码分析-ReentrantReadWriteLock原理-AQS-并发编程(Java)
文章目录 1 锁重入 2 锁重入计数 2.1 读锁加锁计数 2.2 读锁解锁计数 3 公平与非公平锁 3.1 非公平锁 3.2 公平锁 4 锁降级与锁升级 4.1 锁升级 4.2 锁降级 5 full ...
最新文章
- VIM中空格和TAB的替换 [转]
- SQLserver2008数据表的修改操作
- Java7 ConcurrentHashMap源码浅析
- python中用函数设计栈的括号匹配问题_数据结构和算法(Python版):利用栈(Stack)实现括号的匹配问题...
- spark基础之Spark SQL和Hive的集成以及ThriftServer配置
- php -- 取日期
- 聊一聊字节跳动的面试
- C语言PAT乙级试题答案1016
- 数据库试题及答案 两套
- 【已解决】 “discovered_interpreter_python“: “/usr/bin/python“
- 为什么16位int的取值范围是-32768~32767?
- Laravel填充数据Seeder出现 Target class [xxx] does not exist 错误
- flac格式转mp3
- gazebo设置_gazebo教程(六)插件配置
- [生存志] 第55节 吴公子札巡访中原
- 【Bugzilla】我按照bugzilla的官方指导进行的安装。(一)
- python生成12点的时钟_Python上24小时时间转换为12小时时钟(ProblemSetQuestion)
- C# 程序间通信的各种途径及解析
- Windows Robo 3T 安装及配置
- 购物商城订单分库分表应该如何设计