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源码分析、底层原理相关推荐

  1. SpringMVC源码分析_1 SpringMVC容器启动和加载原理

                                                                    SpringMVC源码分析_1 SpringMVC启动和加载原理     ...

  2. SpringMVC源码分析(4)剖析DispatcherServlet重要组件

    简单介绍了一个请求的处理过程, 简略描述了调用过程,并没有涉及过多细节,如url匹配,报文解析转换等. <SpringMVC源码分析(2)DispatcherServlet的初始化>:介绍 ...

  3. MyBatis 源码分析 - 缓存原理

    1.简介 在 Web 应用中,缓存是必不可少的组件.通常我们都会用 Redis 或 memcached 等缓存中间件,拦截大量奔向数据库的请求,减轻数据库压力.作为一个重要的组件,MyBatis 自然 ...

  4. springMVC源码分析--访问请求执行ServletInvocableHandlerMethod和InvocableHandlerMethod

    在之前一篇博客中 springMVC源码分析--RequestMappingHandlerAdapter(五)我们已经简单的介绍到具体请求访问的执行某个Controller中的方法是在RequestM ...

  5. Tomcat7.0源码分析——请求原理分析(上)

    前言 谈起Tomcat的诞生,最早可以追溯到1995年.近20年来,Tomcat始终是使用最广泛的Web服务器,由于其使用Java语言开发,所以广为Java程序员所熟悉.很多早期的J2EE项目,由程序 ...

  6. 简单直接让你也读懂springmvc源码分析(3.1)-- HandlerMethodReturnValueHandler

    该源码分析系列文章分如下章节: springmvc源码分析(1)-- DispatcherServlet springmvc源码分析(2)-- HandlerMapping springmvc源码分析 ...

  7. SpringMVC源码分析_框架原理图

                                                                                 SpringMVC源码分析_框架原理图     ...

  8. laravel $request 多维数组取值_Laravel 运行原理分析与源码分析,底层看这篇足矣

    精选文章内容 一.运行原理概述 laravel的入口文件 index.php 1.引入自动加载 autoload.php 2.创建应用实例,并同时完成了: 基本绑定($this.容器类Containe ...

  9. 04特性源码分析-ReentrantReadWriteLock原理-AQS-并发编程(Java)

    文章目录 1 锁重入 2 锁重入计数 2.1 读锁加锁计数 2.2 读锁解锁计数 3 公平与非公平锁 3.1 非公平锁 3.2 公平锁 4 锁降级与锁升级 4.1 锁升级 4.2 锁降级 5 full ...

最新文章

  1. VIM中空格和TAB的替换 [转]
  2. SQLserver2008数据表的修改操作
  3. Java7 ConcurrentHashMap源码浅析
  4. python中用函数设计栈的括号匹配问题_数据结构和算法(Python版):利用栈(Stack)实现括号的匹配问题...
  5. spark基础之Spark SQL和Hive的集成以及ThriftServer配置
  6. php -- 取日期
  7. 聊一聊字节跳动的面试
  8. C语言PAT乙级试题答案1016
  9. 数据库试题及答案 两套
  10. 【已解决】 “discovered_interpreter_python“: “/usr/bin/python“
  11. 为什么16位int的取值范围是-32768~32767?
  12. Laravel填充数据Seeder出现 Target class [xxx] does not exist 错误
  13. flac格式转mp3
  14. gazebo设置_gazebo教程(六)插件配置
  15. [生存志] 第55节 吴公子札巡访中原
  16. 【Bugzilla】我按照bugzilla的官方指导进行的安装。(一)
  17. python生成12点的时钟_Python上24小时时间转换为12小时时钟(ProblemSetQuestion)
  18. C# 程序间通信的各种途径及解析
  19. Windows Robo 3T 安装及配置
  20. 购物商城订单分库分表应该如何设计

热门文章

  1. HTTP协议及URL统一资源定位符详解
  2. 树的常见概念,二叉树的性质
  3. 在 Chrome 控制台可以玩的两个骚操作,你知道吗?
  4. 定个小目标,炒股咯....
  5. 在windows下安装配置Ulipad
  6. POJ - 2182 Lost Cows【线段树】
  7. 如何保护 .NET 应用的安全?
  8. 基于9款CSS3鼠标悬停相册预览特效
  9. 6、Actor,Stage的学习
  10. 如何将sinaweibo demo project 加入到 Three20UI中