Spring MVC源码解析:各种类型Handler的执行

HandlerAdapter执行handler

在上一节文章我们提到,HandlerMapping和HandlerAdapter并不是一一对应的,所有当HandlerMapping找出handler时,我们要遍历所有的HandlerAdapter,看看当前的HandlerAdapter能执行handler不?能就执行,否则就尝试下一个

所以HandlerAdapter接口定义如下

public interface HandlerAdapter {

  // 该适配器是否能支持指定处理器
  boolean supports(Object handler);

  // 执行处理逻辑,返回 ModelAndView
  @Nullable
  ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

  // 请求的目标资源最近一次的修改时间
  long getLastModified(HttpServletRequest request, Object handler);

}


DispatcherServlet有一个成员变量

private List<HandlerAdapter> handlerAdapters;

当通过HandlerMapping找到Handler后,会依次调用handlerAdapters的supports方法,找到第一个返回true的HandlerAdapter,然后调用HandlerAdapter的handle方法,完成执行。

常用的HandlerAdapter如下

类名 作用
HttpRequestHandlerAdapter 执行实现了HttpRequestHandler接口的Handler
SimpleControllerHandlerAdapter 执行实现了Controller接口的Handler
RequestMappingHandlerAdapter 执行Handler类型是HandlerMethod及其子类的Handler,RequestMappingHandlerMapping返回的Handler是HandlerMethod类型

接着我们来看一下每种HandlerAdapter是如何执行handler的

HttpRequestHandlerAdapter


强转为HttpRequestHandler然后调用handleRequest方法,最后返回null,当ModelAndView为null的时候,ViewResolver查找View,并且View进行渲染的过程会被省略

 SimpleControllerHandlerAdapter


直接强转然后调用handleRequest方法

RequestMappingHandlerAdapter

可以看到HttpRequestHandlerAdapter和SimpleControllerHandlerAdapter的执行策略还是非常简单的,直接强转为对应的接口,然后调用接口的方法。

而RequestMappingHandlerAdapter的执行策略还是比较复杂的。RequestMappingHandlerAdapter自成体系,包含了大量组件对请求进行处理。

一些常用的组件如下

组件名 作用
HandlerMethodArgumentResolver 参数解析器 ,解析@RequestParam,@RequestBody对应的参数
HandlerMethodReturnValueHandler 返回值处理器,处理handler的返回值
HttpMessageConverter 消息转换器,用来进行报文和对象的转换
HandlerExceptionResolver 异常解析器,统一处理handler执行时发生的异常
ViewResolver 根据视图名字返回对应的视图对象

 RequestMappingHandlerAdapter的继承关系如下

RequestMappingHandlerAdapter的handler函数在父类AbstractHandlerMethodAdapter中,定义如下AbstractHandlerMethodAdapter#supports

其中RequestMappingHandlerAdapter重写了supportsInternal方法,永远返回true,即RequestMappingHandlerAdapter支持Handler类型是HandlerMethod的Handler

而RequestMappingHandlerMapping返回的Handler类型就是HandlerMethod,因此可以知道@RequestMapping对应的HandlerMapping是RequestMappingHandlerMapping,对应的HandlerAdapter是RequestMappingHandlerAdapter

HandlerMethod的定义也很简单,封装了要执行方法所对应的类,方法,参数。这样直接就能通过反射来执行。

public class HandlerMethod {

  // 封装的handler的类
  private final Object bean;

  // 封装的handler的方法
  private final Method bridgedMethod;

  // 封装方法的参数
  private final MethodParameter[] parameters;
  
}
method.invoke(obj, args);

所以我们就重点看一下RequestMappingHandlerAdapter的执行过程

初始化组件
构造函数初始化了HttpMessageConverter,HttpMessageConverter主要用来支持@RequestBody和@ResponseBody中Java对象的序列化和反序列化

RequestMappingHandlerAdapter实现了InitializingBean接口,并重写了afterPropertiesSet方法。即在Bean生命周期的初始化阶段,又设置了HandlerMethodArgumentResolver等

默认情况下argumentResolvers等都为空RequestMappingHandlerAdapter#afterPropertiesSet

其中HandlerMethodArgumentResolverComposite和HandlerMethodReturnValueHandlerComposite都是典型的组合模式的实现

添加默认实现的时候还会把用户自定义的实现也添加进去。我们可以通过实现WebMvcConfigurer#addArgumentResolvers方法,来添加用户自定义的实现。其他组件也都是类似的套路

initBinderArgumentResolvers是用来设置@InitBinder方法的参数,这个注解基本上不用,就不分析了

因为handler可以返回多种类型的返回值,例如加了@ResponseBody,ModelAndView,String等。针对不同类型的返回值需要调用不同的HandlerMethodReturnValueHandler

执行Handler并返回ModelAndView
AbstractHandlerMethodAdapter#handle

AbstractHandlerMethodAdapter#handle

RequestMappingHandlerAdapter#handleInternal

RequestMappingHandlerAdapter#invokeHandlerMethodRequestMappingHandlerAdapter#invokeHandlerMethod(删除部分方法)

 

RequestMappingHandlerAdapter#getModelAndView

当处理完返回值的时候,会调用RequestMappingHandlerAdapter#getModelAndView获取ModelAndView,如果mavContainer.isRequestHandled()=true,表明请求已经被处理好了,返回null,后续就不会进行视图渲染,当方法上加了@ResponseBody,mavContainer.isRequestHandled()=true

异常处理和视图解析
当handler执行发生异常时,需要交给HandlerExceptionResolver来处理,具体的处理策略我们单开一章来分享把。

DispatcherServlet#processDispatchResult

当返回的ModelAndView为null时,就不会进行视图解析的部分,因为要返回的报文在之前已经处理好了,比如当方法上加了@ResponseBody时,返回的ModelAndView就为null。

当返回的ModelAndView不为null时,如果视图名不为空,则先利用ViewResolver将视图名转为View对象,执行View#render给视图添加数据后,返回给用户页面

当返回的ModelAndView为null时,就不会进行视图解析的部分,因为要返回的报文在之前已经处理好了,比如当方法上加了@ResponseBody时,返回的ModelAndView就为null。
当返回的ModelAndView不为null时,如果视图名不为空,则先利用ViewResolver将视图名转为View对象,执行View#render给视图添加数据后,返回给用户页面

因为在微服务项目中,早就实现了前后端分离,方法上都加了@ResponseBody注解,所有ViewResolver基本上不会用到,就不多做解析了
 

猜你喜欢

转载自blog.csdn.net/chuixue24/article/details/130707208