Spring Boot-请求处理-【源码分析】-Rest映射及源码解析

917 阅读9分钟

请求处理-【源码分析】-Rest映射及源码解析

1、restful风格回顾

之前使用不同的url表示不同的操作,这些操作都只能使用get 或者 post两种请求方式,并且这些区分不同操作的取决于url,而不是请求方式!例如:/getuser获取用户、/deluser删除用户、/moduser修改用户、/saveuser保存用户;

但是如果使用rest风格可以将同一个url用不同的请求方式区分,然后根据不同的请求方式执行不同的操作;也就是说针对同一个url,不同的请求方式做不同的处理。例如:地址发起/user请求,由于请求方式的不同,GET-获取用户、 DELETE-删除用户 、PUT-修改用户、 POST-保存用户。

因此Rest风格支持(使用HTTP请求方式动词来表示对资源的操作)

备注:Rest风格的核心Filter过滤器:HiddenHttpMethodFilter

2、SpringBoot的rest风格用法

用表单进行rest风格各种请求的演示

2.1、页面index.html【首页】参数

1、开启页面表单的Rest功能,放置在静态资源static目录下或者template模板下

image.png

页面中需要使用表单提交,且提交非get/post请求时需要提交一个隐藏域_method=put、delete等(如果直接get或post,无需隐藏域),method属性为method=post,value属性为请求方式,这样提交非get/post请求后会解析隐藏域,将post请求转化为对应的请求value值。

put/delete请求为什么必须有一个隐藏域_method

答:springmvc源码自动配置类代码的HiddenHttpMethodFilter类中,就是说明与该与redtful风格有关,里面有属性_method的介绍

image.png

2.2、编写控制器

image.png

说明:

  • 控制层所有的path路径全都是一样的,但是需要给@RequestMapping注解设置属性处理的方式。
  • @ResponseBody表示直接响应到index页面中不经过视图处理器。

2.3、application.yaml配置

需要在配置文件开启rest风格的允许,否则依旧是不生效的。这几个步骤缺少其中的任何一个,rest风格都不会生效

image.png

为什么要进行该配置?

答:springmvc源码配置类中第一个类HiddenHttpMethodFilter,就是说明与该与redtful风格有关,如下,SpringBoot底层【版本2.5.4】默认是未开启restful风格的功能的。

image.png

2.4、测试

浏览器输入loclhost:8080,就会默认跳转到首页【这部分功能以前讲过】,然后进行相应命令的测试并返回相应的数据到页面即可成功

2.5、Restful风格的RequestMapping注解简化

请求映射格式:@xxxMapping;

  • @GetMapping
  • @PostMapping
  • @PutMapping
  • @DeleteMapping

因此在restful风格加持下,可以将该注解简化写成下面格式

image.png

3、Rest映射原理(基于表单提交要使用REST的时候)

rest映射主要是通过post请求提交并且携带隐藏域(_method),然后由HiddenHttpMethodFilter类将其转换。

SpringBoot启动就自动装配WebMvcAutoConfiguration类,然后由该类进行条件装配OrderedHiddenHttpMethodFilter类。

3.1、OrderedHiddenHttpMethodFilter解析

该类继承自HiddenHttpMethodFilter类,主要是绑定spring.mvc.hiddenmethod.filter配置参数 而这里有个参数name = enabled,并且默认是false的也就是说默认并没有开启rest映射。

image.png

3.2、HiddenHttpMethodFilter解析

HiddenHttpMethodFilter类首先是以过滤器的形式对请求进行过滤拦截,当请求过来被HiddenHttpMethodFilter拦截。

image.png

核心方法doFilterInternal解析如下:

  1.    首先这里必须是post请求,并且请求是合法的!
    
  2.    然后获取到methodParam隐藏域的值,判断合法性
    
  3.    不缺分请求方式值的大小写,并且全部转为大写。
    
  4.    判断提交的请求是否是delete、put、PATCH请求。
    
  5.    最后由HttpMethodRequestWrapper包装器解析并且返回,返回的值就是真正的请求方式!
    

3.3、修改默认的_method参数【了解】

由于无法直接修改绑定的配置参数,进而无法将methodParam = _method改成指定的名称。但是可以根据SpringBoot的条件装配进行装配。

image.png

可以看到这个类的装配只有IOC中没有HiddenHttpMethodFilter类才会装配,那么我们可以通过set方法手动向容器中注入这个Bean,并且顺手指定methodParam值。

image.png

由于手动向IOC中丢入HiddenHttpMethodFilter类,那么条件装配将不会生效,因此这样就使用我们自定义的了,所以前面我们也说过凡是有@conditionalOnMissingBean这类的注解,自定义配置就是大于默认的。 注意:这里前端传入的隐藏参数名称也要改为我们指定的query进行转换才能使得rest生效。

image.png

3.4、总结

综述:Rest映射原理如下:

  1.    表单提交会带上/_method=PUT请求
    
  2.    请求过来被HiddenHttpMethodFilter拦截
    
  3.    查看请求是否正常,并且原生请求方式是POST
    
  4.    获取到_method的值:比如DELETE,兼容以下请求;PUT.DELETE.PATCH【内部定义的允许的请求方式】
    
  5.    原生request方式(post),包装模式requestWrapper重写了getMethod方法,返回的是传入的method值。
    
  6.   过滤器链doFilter方法放行的时候用wrapper,以后的方法调用getMethod是调用requestWrapper的。
    

3.5、Rest使用客户端工具

如PostMan可直接发送put、delete等方式请求,不需要进行上述的配置,无需过滤器,所以上面的所有的演示是选择性的对表单进行的开启配置。

4、SpringMVC请求处理-【源码分析】-请求映射原理

image.png

4.1、涉及的接口与类

每次发请求,Springboot是怎么找到哪个方法来处理我们的请求的呢,SpringBoot底层依然用了SpringMVC,因此我们先回顾一下SpringMVC当中,请求的原理涉及到的一些类和方法,如下:

image.png

  • Servlet接口是最传统的接口,内部就四个方法,这里只列举一个核心方法service()。
  • GenericServlet是一个抽象类,内部也没有给出具体的service()实现。
  • HttpServlet这是有处理能力的Servlet实现类,实现了service()方法,并且针对不同的请求提供了不同的处理方法,这些请求统一交给了service调度。
  • HttpServletBean主要是对HttpServlet进行了简单的扩展,其余的主要是后面两个子类进行强大的扩充。
  • FrameworkServlet,首先重写了HttpServlet中的所有do请求,这些每一个do请求都会统一调度processRequest()方法;processRequest()方法再调度doService()方法进行处理;可惜的是doService并没有给出具体的实现。
  • DispatcherServlet前端控制器,这里主要是实现doService方法,调度doDispath()方法进行分发给处理器映射器,并且串联上所有的拦截器组成一个执行链。最后再交给适配器…

SpringMVC的完整请求响应的流程图如下【参考SpringMVC】

image.png

因此SpringMVC功能分析从 org.springframework.web.servlet.DispatcherServlet -> doDispatch()开始【参考SrpingMVC流程】-->最终每个请求都会调用 doDispatch()方法

4.2、映射原理

虽然涉及的类、方法较多看起来十分的复杂,但是核心全都在FrameworkServlet和DispatcherServlet中,下面以一个基本的doGet请求进行debug分析。

4.2.1、FrameworkServlet解析

image.png

  • FrameworkServlet收到请求后找到对应的执行doGet方法,之后doGet方法调processRequest进行一些参数和基本处理,然后交给抽象方法doService。
  • 但是这里的doService没有办法完成处理,只能交给DispatcherServlet中的具体实现方法完成。

4.2.2、DispatcherServlet(前端控制器)

image.png image.png

大致步骤如下:

  1. 首先是由FrameworkServlet执行doService方法进行处理的时候找到DispatcherServlet的具体实现方法doService进行完成。
  2. doService处理了一堆东西之后,找到doDispatch进行分发;此时的分发包括静态页面的请求与controller的请求,只要是请求就会走doDispatch方法。
  3. doDispatch进行工作,上来一顿处理操作然后判断是否是一个文件上传等一些判断之类的,开始进入正题,如何通过映射路径进行找到执行器…

4.2.3、映射处理器HandlerMapping

大家都叫它次级控制器,因为负责uri到controller的映射,通过映射处理器找到请求对应的控制器,在上述源码中体现为getHandler()方法,如下:

image.png

默认情况下什么都没有配置,但是自动装配了五个处理器映射器,如下,this.handlerMappings在Debug模式下展现的内容:

image.png

  1. 所有的HanlderMapping处理器映射器都保存了它应该管理的uri的映射!即保存了所有@RequestMapping 和handler的映射规则,

  2. 其中最熟悉的应该是RequestMappingHandlerMapping,保存了所有@RequestMapping注解的URI

image.png

WelcomePageHandlerMapping是处理欢迎页的处理器映射器。所以这样每个请求就找到了对应的控制器方法来进行请求的处理

4.2.4、HandlerAdapter适配器

适配器的功能很单纯,即针对不同的handler找到适配的处理器

image.png

根据这两步可以发现,基本的映射如何找到、如何适配的问题已经解决;最后会根据SpringMVC的流程图就是去执行对应的controller。

4.3、总结

大致步骤如下:

  • 所有的请求映射规则都在HandlerMapping中:
  • SpringBoot自动配置欢迎页的 WelcomePageHandlerMapping 。访问 /能访问到index.html;
  • SpringBoot自动配置了默认的 RequestMappingHandlerMapping,请求进来,挨个尝试所有的HandlerMapping看是否有请求信息。
  • 如果有就找到这个请求对应的handler,如上面流程进行接信息
  • 如果没有就是找下一个 HandlerMapping,找完了依然没有找到对应的Handler那么就会出现异常,这些异常会使用异常处理机制(aop)集中处理,看到的就是一些错误页面…
  • 因此如果我们需要一些自定义的映射处理,我们也可以自己给容器中放HandlerMapping。自定义 HandlerMapping

补充:IDEA快捷键:

  • Ctrl + Alt + U : 以UML的类图展现类有哪些继承类,派生类以及实现哪些接口。
  • Crtl + Alt + Shift + U : 同上,区别在于上条快捷键结果在新页展现,而本条快捷键结果在弹窗展现。
  • Ctrl + H : 以树形方式展现类层次结构图。