一.HandlerAdapter设计原理
SpringMVC的第一步,通过HandleMapping找到了某个请求对应的Handler,现在Handler有了,但是不同的handler的执行逻辑完全不一样,实现了Controller接口的handler,直接调用handler的handleRequest(……)
方法就行,使用RequestMapping注解的handler,需要找到具体的方法再执行。所以为了统一Handler的处理逻辑,最合适的方法是采用适配器模式。
那么一个Handler适配器需要具备哪些功能呢?
- 统一的执行方法,方法的入参很简单,HttpServletRequest、HttpServletResponse和具体的handler,出参需要定义一个统一的模型,该模型尤为重要,一方面需要包括所有的内容请求处理的数据,另一方面需要指出这些数据的处理方式
- 第一步,我们有了一个Handler,那么第二步需要做的是根据handler找到handler对应的HandlerAdapter
public interface HandlerAdapter {
// 判断该HandlerAdapter是否支持指定的handler
boolean supports(Object handler);
// 执行handler
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
long getLastModified(HttpServletRequest request, Object handler);
}
public class ModelAndView {
private Object view;
private ModelMap model;
private HttpStatus status;
private boolean cleared = false;
}
HandlerAdapter继承关系:
- SimpleControllerHandlerAdapter:适配实现了Controller接口的handler
- RequestMappingHandlerAdapter:适配通过@RequestMapping注解的handler,@RequestMapping注解的方法,会封装为HandleMethod对象,HandleMethod对象也就是真正的handler
- HttpRequestHandler:适配实现了HttpRequestHandler接口的handler
- SimpleServletHandlerAdapter:适配实现了Servlet接口的handler
除了RequestMappingHandlerAdapter外,其他的HandlerAdapter其实都比较简单,下面我们详细的分析一下RequestMappingHandlerAdapter。
二.RequestMappingHandlerAdapter
处理一个请求大致上都是下面3个步骤:
- 入参解析
- 执行方法
- 出参转换
一个http请求的入参,基于不同的content-type,会有不一样的取值逻辑,取到具体的值之后,根据方法参数上配置的不同,转换逻辑又不一样。所以整个入参解析就尤为复杂。不过,http请求的入参虽然千差万别,但是方法的入参是固定的,所以根据方法需要的入参,再到请求入参中取查找,就变得容易多了。
基于此,就有了HandlerMethodArgumentResolver接口。
public interface HandlerMethodArgumentResolver {
boolean supportsParameter(MethodParameter parameter);
Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception;
}
再来看看HandlerMethodArgumentResolver接口的继承关系。
下面,我们来看看几个比较常见的HandlerMethodArgumentResolver的使用方式及解析过程
- ServletRequestMethodArgumentResolver
// 支持的参数类型,其中最常见的有ServletRequest、MultipartRequest
public boolean supportsParameter(MethodParameter parameter) {
Class<?> paramType = parameter.getParameterType();
return (WebRequest.class.isAssignableFrom(paramType) ||
ServletRequest.class.isAssignableFrom(paramType) ||
MultipartRequest.class.isAssignableFrom(paramType) ||
HttpSession.class.isAssignableFrom(paramType) ||
(pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) ||
Principal.class.isAssignableFrom(paramType) ||
InputStream.class.isAssignableFrom(paramType) ||
Reader.class.isAssignableFrom(paramType) ||
HttpMethod.class == paramType ||
Locale.class == paramType ||
TimeZone.class == paramType ||
ZoneId.class == paramType);
}
- ServletResponseMethodArgumentResolver
// 支持的参数类型,其中最常见的有ServletResponse
public boolean supportsParameter(MethodParameter parameter) {
Class<?> paramType = parameter.getParameterType();
return (ServletResponse.class.isAssignableFrom(paramType) ||
OutputStream.class.isAssignableFrom(paramType) ||
Writer.class.isAssignableFrom(paramType));
}
- RequestParamMapMethodArgumentResolver:解析@RequestParam注解
public boolean supportsParameter(MethodParameter parameter) {
RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
return (requestParam != null && Map.class.isAssignableFrom(parameter.getParameterType()) &&
!StringUtils.hasText(requestParam.name()));
}