tomcat + spring mvc原理(十一):spring mvc对请求的适配处理和返回1

前言

    原理九原理十主要研究了HandlerMapping如何通过request找到request对应的拦截器Interceptor和处理方法HandlerMethod。但是即使找到了HandlerMethod,也还存在多个问题,比如:

@RestController
public class DemoController {
    @GetMapping("get")
    public String test(@RequestParam String s1){
        return s1;
    }

    @PostMapping("post")
    public String test2(@RequestBody RequestTest request ) {
        return request.s2 + " " + request.s3;
    }

}

class RequestTest {
    String s2;
    String s3;

    void setS2(String s2) { this.s2 = s2;}
    String getS2() { return s2; }
    void setS3(String s3) { this.s3 = s3; }
    String getS3() { return s3; }
}

对于不同类型的请求,Post和Get类型的参数解析的方式就不一样。即使是相同类型的请求,不同处理方法的参数也存在不同。所以这里就需要适配器将Http的Request或者其他类型Requet解析成处理方法能够接收的参数,这些工作就由HandlerAdapter承担。
    再来回顾一下DispatcherServlet处理请求的整个流程中关于这个HandlerAdapter的部分。

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
......
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

首先通过getHandlerAdapter获取HandlerMethod对应的HandlerAdapter,然后再调用对应HandlerAdapter的handle方法来处理请求,这个方法中传入了请求体requet和应答体response以及获取的HandlerMethod(Interceptor的调用 是在之前,这里跳过了不分析)。其中getHandlerAdapter方法是遍历了所有HandlerAdapter,每个实例都包含一个supports方法,能判断是否支持处理这个对应的HandlerMethod。

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
  if (this.handlerAdapters != null) {
    for (HandlerAdapter adapter : this.handlerAdapters) {
      if (adapter.supports(handler)) {
        return adapter;
      }
    }
  }
  throw new ServletException("No adapter for handler [" + handler +
      "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

HandlerAdapter的整体结构

    HandlerAdapter接口中只有三个方法。
tomcat + spring mvc原理(十一):spring mvc请求的适配处理和返回-HandlerAdapter.png

public interface HandlerAdapter {
  //判断是否支持处理传入的Handler
	boolean supports(Object handler);

  //处理请求
	@Nullable
	ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

  //获取请求资源的上次更改时间
	long getLastModified(HttpServletRequest request, Object handler);
}

实现HandlerAdapter接口的有四个分支,SimpleServletHandlerAdapter、HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter和RequestMappingHandlerAdapter。其中,RequestMappingHandlerAdapter比较复杂,继承了AbstractHandlerMethodAdapter抽象类,一般现在用来适配HandlerMethod(即Controller中由@RequestMapping、@GetMaping或者@PostMapping注解的方法)都会调用这个类的handle()方法。

处理请求的具体实现

//SimpleServletHandlerAdapter
public class SimpleServletHandlerAdapter implements HandlerAdapter {
  ......
	@Override
	@Nullable
	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		((Servlet) handler).service(request, response);
		return null;
	}
  ......
}
//HttpRequestHandlerAdapter
public class HttpRequestHandlerAdapter implements HandlerAdapter {
  ......
	@Override
	@Nullable
	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		((HttpRequestHandler) handler).handleRequest(request, response);
		return null;
	}
  ......
}
//SimpleControllerHandlerAdapter
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
  ......
	@Override
	@Nullable
	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		return ((Controller) handler).handleRequest(request, response);
	}
  ......

}

    上面列出的三种HandlerAdapter,处理的都是固定格式的参数的Handler,SimpleServletHandlerAdapter对应的是Servlet类型的Handler,HttpRequestHandlerAdapter和SimpleControllerHandlerAdapter分别对应HttpRequestHandler类型、Controller类型的Handler。这些类型的Handler处理请求的格式比较固定,可以直接调用handleRequest、传入request和response解决,所以对应的HandlerAdapter的内部逻辑并不复杂。

参数格式可变的HandlerMethod的处理

    目前业务中广发使用的@RequestMapping或者等效的@GetMapping和@PostMapping注解修饰的方法,都是使用HandlerMethodAdapter这一分支的HandlerAdapter适配的。spring mvc历史上的设计思想是集成前后端的MVC理论,尽管这种设计思想导致前后端的业务开发耦合较大而被生产淘汰(具体可以参考专门为此作解释的另一篇文章tomcat + spring mvc原理外传:spring mvc与前端的纠葛),但优化代码的设计依然是在原工程上打补丁。所以要处理这种复杂场景,HandlerMethodAdapter这一分支的代码逻辑比较复杂,我会尽量理清逻辑,给大家一个比较清晰的解释。

父类AbstractHandlerMethodAdapter
public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {
  ......
  //设置优先级
	private int order = Ordered.LOWEST_PRECEDENCE;
	public void setOrder(int order) {
		this.order = order;
	}
	@Override
	public int getOrder() {
		return this.order;
	}

  //suports方法
	@Override
	public final boolean supports(Object handler) {
		return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
	}
	protected abstract boolean supportsInternal(HandlerMethod handlerMethod);

  //handle方法
	@Override
	@Nullable
	public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		return handleInternal(request, response, (HandlerMethod) handler);
	}
	protected abstract ModelAndView handleInternal(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception;

  //getLastModified方法
	@Override
	public final long getLastModified(HttpServletRequest request, Object handler) {
		return getLastModifiedInternal(request, (HandlerMethod) handler);
	}
}

    AbstractHandlerMethodAdapter代码非常简单。实现了order接口,设置了优先级。supports方法只是验证Handler是不是HandlerMethod类型,然后就交由模板方法supportsInternal处理。handle方法直接交由handleInternal处理,getLastModified方法交由getLastModifiedInternal。supportsInternal、handleInternal和getLastModifiedInternal代码都是由子类RequestMappingHandlerAdapter实现。

RequestMappingHandlerAdapter
处理流程简述

    supportsInternal直接返回true,所以不会对HandlerMethod进行额外筛选。getLastModifiedInternal返回-1,所以不能使用http的LastModified属性。这样看来,重要的代码还是集中在handleInternal方法中。
tomcat + spring mvc原理(十一):spring mvc请求的适配处理和返回-brief.png
    即使不了解handleInternal的源码,也能够大致推测出如上图所示的整个流程。然而,要想对这个问题的细节有更进一步的深入了解,需要知道实际参数和返回结果存在不同的类别。传入的参数可以分为四种类型:

  1. 贯彻始终的request请求体中包含的参数。主要有请求的url、GET或者POST中传入的参数、cookie信息等。
  2. spring mvc设计理念中的数据模型model包含的参数。
  3. 会话session中缓存的参数。
  4. 重定向参数。

spring mvc中的model数据模型在目前流行的RESTful Web服务中比较少见,如果不太了解,可以参考tomcat + spring mvc原理外传:spring mvc与前端的纠葛。session是http协议用来存储状态的设计,和cookie存放在客户端的浏览器的理念不同,session是存放在服务的缓存数据,目前spring mvc中主要是作为跨请求的状态缓存,但是由于大型项目会使用同一服务的镜像部署,通过负载均衡来实现服务的高可用,所以session的本地缓存不能够及时同步到其他镜像服务中,目前生产中使用也比较少。在tomcat + spring mvc原理(八):spring mvc对请求的处理流程中简略介绍了FlashMap是用来存储重定向的相关参数,这里所说的重定向参数是重定向的model数据,与FlashMap中存储的数据并不相关。FlashMap作为重定向的组件会在后面详细介绍。后三类参数会被统一存放在ModelAndViewContainer数据体中,这一数据体会贯穿于整个HandlerAdapter处理请求的流程中,包括最后将处理得到的model和view赋值给ModelAndView类实例,然后传递出去。
    handleInternal方法中,会对是否使用session机制进行校验。由于session使用比较少,关于session部分,这里不会详细介绍。剩下比较重要的方法调用只有:

ModelAndView mav;
······
mav = invokeHandlerMethod(request, response, handlerMethod);

invokeHandlerMethod统领了后续的请求处理的整个流程,包括多个组件的使用,是需要关注的重点方法。这个方法中涉及过多具体处理的细节,需要对上面的流程图进行扩充,我另开一篇详细介绍。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值