第六章 请求源码

DispatcherServlet结构分析 == Servlet

接收所有请求
图片说明
在FrameworkServlet中复写 doGet 和doPost -> abstract doService
图片说明

DispatcherServlet 源码运行分析 -> controller -> 页面

什么时候运行了目标方法 利用反射运行
什么时候运行了页面
doDispatch方法详解
图片说明

  • 第一步 判断区别是不是多文件上传请求
    图片说明

  • 第二步 找到处理器 -> 处理器映射RequestMapping
    图片说明

    图片说明

  • 第三步 拿到适配器来处理 适配器封装了一个专门请求注解方法执行的工具
    图片说明

    图片说明

  • 第四步 控制台输出了 :运行了目标方法 ; 通过上面获取的适配器ha来执行 不管是否为String 还是Model 最终返回了一个modelAndView
    下面是看看是不是异步处理
    没有视图名 默认给一个视图名 请求的地址是什么 作为默认
    图片说明

  • 第五步 程序运行了页面 根据方法最终执行完成封装的ModelAndView 转发到对应页面 而且ModelAndView中的数据可以从请求域中获取
    图片说明

getHandler详细处理过程:怎么根据当前请求就能找到哪个类能来处理

  • 返回的是目标Handler执行链 而不是method.invoke();
  • : 在目标方法执行的时候 用*拦截一下
    图片说明
    processedRequest:请求地址
    图片说明
  • 怎么通过请求地址找到目标类 或者目标方法
    handlerMappings:处理器映射:里面保存了每一个处理器能处理哪些请求(都标志了注解了)的映射信息 ,有两个:基于注解/写配置Bean
    图片说明
    全部都保持在handlerMap 【注解下面的handlerMap不为空,而配置下的handlerMap下面为{}空】 "/hello"【helloController】 "/requ"【MyController】 所有的控制器都会被扫描 并保存到里面
    图片说明
    handlerMap:ioc容器自动创建Controller对象的时候扫描每一个处理器都能处理什么请求,保存在handlerMapping的hadllerMap属性中;下一次请求过来,就来看那那个HandlerMapping中由这个请求映射信息就行了

如何找目标处理类的适配器,要拿适配器才能去执行目标方法
图片说明

  • 通过for循环找到合适的适配器
    图片说明
    一共有三个适配器
    图片说明
    第一个适配需要实现HttpRequestHandler接口 (我们处理器不是这种类型 没有继承任何接口)
    第二个需要继承Controller接口 之前的方法 老版本的 我们也不是
    第三个注解版的适配器 处理器中只要有标了注解的方法就能用
    拿到适配器了 执行目标方法

SpringMVC 九大组件 关键位置都是由这些组件完成的

九大组件初始化的地方
图片说明
也就是ioc一启动 上面的方法就会被运行 初始化策略方法()
图片说明
初始化
图片说明
如果需要DispatcherServlet修改某些属性变量的默认配置 可以用web.xml配置
图片说明
一些配置信息都写在Properties
图片说明


  1. MultipartResolver(多部件解析器 文件处理器),对应的初始化方法是initMultipartResolver(context),用于处理上传请求。处理方法是将普通的request包装成MultipartHttpServletRequest,后者可以直接调用getFile方法获取File。

  2. LocaleResolver(区域信息解析器+跟国际化 当前环境处理器),对应的初始化方法是initLocaleResolver(context),这就相当于配置数据库的方言一样,有了这个就可以对不同区域的用户显示不同的结果。SpringMVC主要有两个地方用到了Locale:一是ViewResolver视图解析的时候;二是用到国际化资源或者主题的时候。

  3. ThemeResolver(主题处理器:主题效果更换),对应的初始化方法是initThemeResolver(context),用于解析主题。SpringMVC中一个主题对应一个properties文件,里面存放着跟当前主题相关的所有资源,如图片、css样式等。SpringMVC的主题也支持国际化。

  4. HandlerMappings(处理器映射器),对应的初始化方法是initHandlerMappings(context),这就是根据用户请求的资源uri来查找Handler的。在SpringMVC中会有很多请求,每个请求都需要一个Handler处理,具体接收到一个请求之后使用哪个Handler进行处理呢
    图片说明

  1. HandlerAdapters(处理器适配器),对应的初始化方法是initHandlerAdapters(context),从名字上看,它就是一个适配器。Servlet需要的处理方法的结构却是固定的,都是以request和response为参数的方法。如何让固定的Servlet处理方法调用灵活的Handler来进行处理呢?这就是HandlerAdapters要做的事情。

  2. HandlerExceptionResolvers(异常处理器:springmvc强大的异常解析功能),对应的初始化方法是initHandlerExceptionResolvers(context),其它组件都是用来干活的。在干活的过程中难免会出现问题,出问题后怎么办呢?这就要有一个专门的角色对异常情况进行处理,在SpringMVC中就是HandlerExceptionResolver。

  3. RequestToViewNameTranslator(视图名称翻译器),对应的初始化方法是initRequestToViewNameTranslator(context),有的Handler处理完后并没有设置View也没有设置ViewName,这时就需要从request获取ViewName了,如何从request中获取ViewName就是RequestToViewNameTranslator要做的事情了。

  4. ViewResolvers(页面渲染处理器 ),对应的初始化方法是initViewResolvers(context),ViewResolver用来将String类型的视图名和Locale解析为View类型的视图。View是用来渲染页面的,也就是将程序返回的参数填入模板里,生成html(也可能是其它类型)文件。

  5. FlashMapManager(参数传递管理器:springmvc中运行重定向携带数据定 功能),对应的初始化方法是initFlashMapManager(context),用来管理FlashMap的,FlashMap主要用在redirect重定向中传递参数。

九大组件都是接口:规范程序 提供了非常强大的扩展性
组件的初始化:

  • 有些组件在容器中是使用类型找的,有些组件是使用id找的
  • 去容器中找到这个组件,如果没有找到就用默认的配置

锁定目标方法的执行

@Controller
public class MyController {
    @RequestMapping("requ")
//多参数来解决
    public String getBook(@RequestParam("bookValue")Double bookValue,@ModelAttribute("book") Book book,Model model) {
        return "hello";
    }
    @ModelAttribute
    public void prePro(Map map) {
        Book book = new Book();
        book.setBookName("西游记");
        System.out.println("ModelAttribute.....");
    }
}
  • 第一步 看看目标类有没有SessionAttribute 注解
    图片说明
  • 第二步 进入
    图片说明
  • 第三步 获取到方法解析器 通过方法解析器得到用哪个方法 【根据当前请求地址找到真正的目标方法】 还得到方法执行器 重新包赚request和response 还new了一个BindingAwareModelMap();【隐含模型】 所有的数据都在请求域中
    图片说明
  • 第四步 这个方***运行ModelAttribute方法 和 处理器方法
    目标方法利用反射执行期间确定参数值,提前执行modelattribute等所有的操作都在方法中
    图片说明

modelAttribute

围绕这个方法进行实验
图片说明

  • 第一步 方法执行的细节
    图片说明
    所有参数
    图片说明

  • 第二步看看参数有没有Attribute 然后放在隐含模型中
    图片说明

  • 第三步 看看ModelAttributeMethod方法有哪些 找到所有ModelAttribute注解标注的方法
    attributeMehodToInvoke 就是要执行的方法 例如hahaModelAttribute
    图片说明
    确定值
    下面的代码是resolveHandlerArguments()
    图片说明
    确定方法运行时使用的每一个参数的值:上面args里面的方法 其中paramtypes参数类型是一个Map类 而args是一个和参数个数一样多的数组,会保存每个参数的值 下面是处理args赋值的方法
    图片说明 图片说明
    其中上面的Annotation[] ParamAnns = methodParam.getParamterAnnotations();如果参数中没有任何的注解 则为空,如果有注解 对各种注解进行分类 进行判断时利用request等方法获取对应的值
    图片说明
    前一张图对方法里面参数进行遍历 每一个参数编写了注解只能用一个 超过1就报错
    图片说明
    解析普通参数
    图片说明
    进入resolveCommonArgument 因为没有自定义 所以这里为空 到下一个图
    图片说明
    解析标准参数:
    图片说明
    解析标准参数:判断是不是ServletRequest、ServletResponse...原生API 因为我们传入的是Map,什么都是不是就随便new一个Object
    图片说明
    如果没有注解(resolveCommonArgument--resolveStandardArgument),先看是否是普通参数,就是确定当前的参数是否是原生API 【request】等等

    在注解为Annotation=0的情况下,看看参数是否是Map类型 如果是将之前的隐含模型传给Map/Model旗下
    图片说明
    下一步是看看modelAttribute是否有value,如@ModelAttribute(value="haha") 我们没有为"",attrName=value 也是为""且为这个变量new一个变量名,返回值类型首字母小写void/book
    因为方法是void 所以attrValue是null
    图片说明
    向隐含模型里面放值
    图片说明
    总体上 @modelAttribute的作用就是运行 放数据
    图片说明
    执行目标方法前的准备
    图片说明

  • 对ModelAttribute目标方法参数进行赋值【标了注解 没标注解】
    目标方法的参数
    图片说明

    图片说明
    第一个参数 从request里获取【多值或者单值】
    图片说明
    第二个参数 Map 没有注解 隐含模型赋值过去 至始至终都是用隐含模型赋值
    图片说明
    第三个参数 request对象 解析普通参数里面 确定API
    图片说明
    第四个参数自定义 book注入【有注解】 attrName如果是空串 赋值为参数类型首字母小写 作为值 如book 不看名字book123 然后去隐含模型里面找
    图片说明

    总结
    图片说明
    图片说明
    book注入【有无注解的步骤】
    图片说明
    重要的两件事
    图片说明

modelAttribute知识点补充

@SessionAttributes(value="msg")
@Controller
public class MyController {
    @RequestMapping("requ")
    public String getBook(@RequestParam("bookValue")Double bookValue,@ModelAttribute("book") Book book,Model model) {
        System.out.println(book);
        return "hello";
    }
    @ModelAttribute
    public void prePro(Map map) {
        Book book = new Book();
        book.setBookName("西游记");
        map.put("book", book);// Book book 没有注解的话,参数类型首字母小写找
        map.put("haha", book);// @ModelAttribute("book") Book book
        //ModelAttribute 找不到 去@SessionAttribute找 找不到就报异常 
//如果没有SessionAttribute 利用反射来获取book初始化对象
        System.out.println("ModelAttribute.....");
    }
}
  • 利用名字来赋值
    图片说明
  • 通过类名去找
    图片说明
  • 利用sessionAttribute去找
    图片说明
  • 利用返回值找
    图片说明

@ModelAttribute标注的方***提前运行并把方法的运行结果放在隐含模型中
放的时候使用一个key:

  • @如果 @ModelAttribute("book")指定了,就用指定的book
  • 如果没指定就用返回值类型的首字母小写作为key

@SessionAttributes 最好不使用 不然会报错

  • 为了避免可能引发的异常 保证有两点
    • 要么隐含模型中有@SessionAttribute标注的属性
    • 要么Session中有
全部评论

相关推荐

点赞 评论 收藏
分享
10-09 00:50
已编辑
长江大学 算法工程师
不期而遇的夏天:1.同学你面试评价不错,概率很大,请耐心等待;2.你的排名比较靠前,不要担心,耐心等待;3.问题不大,正在审批,不要着急签其他公司,等等我们!4.预计9月中下旬,安心过节;5.下周会有结果,请耐心等待下;6.可能国庆节前后,一有结果我马上通知你;7.预计10月中旬,再坚持一下;8.正在走流程,就这两天了;9.同学,结果我也不知道,你如果查到了也告诉我一声;10.同学你出线不明朗,建议签其他公司保底!11.同学你找了哪些公司,我也在找工作。
点赞 评论 收藏
分享
Noob1024:一笔传三代,人走笔还在
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务