框架进行时——SpringMVC流程简析(一)

基于 SpringWeb(5.3.23)的接口请求分析

前情提要

假定当前 Web 项目中有如下实体类和接口:

 package com.example.entity;

public class WebUser {
private String name;
private Integer age;
private LocalDate birthday;
private Boolean gender;

// getter、setter、toString ...
}
 package com.example.controller;

@RestController @RequestMapping("/test")
public class WebController {

@RequestMapping("/1")
public String test1(HttpServletRequest request, @RequestParam Map<String, Object> params, WebUser webUser) {
System.out.println("request.getClass() = " + request.getClass());
System.out.println("params = " + params);
System.out.println("webUser = " + webUser);
return UUID.randomUUID().toString();
}
}

使用 Postman 发送请求测试,结果符合预期:

postman截图
服务端日志

下面就一些关键点进行探究分析。

由谁来处理本次请求?——请求处理器

SpringMVC 中定义了多种“处理器映射”

HandlerMapping
,用来根据特定的请求返回对应的处理器(handler)。处理器映射的接口声明如下:

 package org.springframework.web.servlet;

public interface HandlerMapping {

// 根据具体的请求返回一个处理器执行器链
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

处理器执行器链

HandlerExecutionChain
由处理器和围绕该处理器的所有处理器拦截器
HandlerInterceptor
组成,是对处理器的一层封装(下文中“处理器”一词也代指“处理器执行器链”,如无特殊说明不再区分)。

SpringMVC 中默认的处理器映射有以下5个:

0 = { RequestMappingHandlerMapping @7031} (order=0)
1 = { BeanNameUrlHandlerMapping @7032} (order=2)
2 = { RouterFunctionMapping @7033} (order=3)
3 = { SimpleUrlHandlerMapping @7034} (order=2147483646)
4 = { WelcomePageHandlerMapping @7035} (order=2147483647)

当 DispatcherServlet 被初始化时,如果处理器映射有多个,DispatcherServlet 会调用

 // We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);

对它们进行排序。其中的排序规则如下:

  1. 实现了
    PriorityOrdered
    接口的优先。
  2. 实现了
    Ordered
    接口的优先,然后按照 getOrder() 的值从小到大排序。
  3. 如以上条件均不满足,则排到最后。
 // 摘自 org.springframework.core.OrderComparator
private int compare(Object o1, Object o2) {
boolean p1 = o1 instanceof PriorityOrdered;
boolean p2 = o2 instanceof PriorityOrdered;
if (p1 && !p2) {
return -1;
} else if (p2 && !p1) {
return 1;
} else {
int i1 = this.getOrder(o1);
int i2 = this.getOrder(o2);
return Integer.compare(i1, i2);
}
}

private int getOrder(Object obj) {
if (obj != null && obj instanceof Ordered) {
return ((Ordered)obj).getOrder();
}
return Ordered.LOWEST_PRECEDENCE;
}

由此可见,一个处理器映射可以通过实现

Ordered
接口后重写 getOrder() 方法以指定自身的次序。
此外,
AbstractHandlerMapping
类中定义了一个
order
属性,继承了该抽象类的子类也可调用 setOrder 方法间接地指定自身的次序。

处理器需要由处理器适配器

HandlerAdapter
来执行。处理器适配器的接口声明如下:

 package org.springframework.web.servlet;

public interface HandlerAdapter {
// 判断是否支持给定的处理器
boolean supports(Object handler);

// 使用给定的处理器对本次请求进行处理
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}

SpringMVC 中默认的处理器适配器有如下4个(同处理器映射一样,它们也会被排序):

0 = { RequestMappingHandlerAdapter @6792}
1 = { HandlerFunctionAdapter @6793}
2 = { HttpRequestHandlerAdapter @6794}
3 = { SimpleControllerHandlerAdapter @6795}

常用的适配器就是第一个

RequestMappingHandlerAdapter
,它的支持规则很简单——处理器是
HandlerMethod
类型的即可。而
RequestMappingHandlerMapping
返回的处理器正是这一类型。

注意到 org.springframework.web.servlet.DispatcherServlet#doDispatch 方法中有如下代码片段:

 // Determine handler for the current request.
HandlerExecutionChain mappedHandler = getHandler(processedRequest);

// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

// Actually invoke the handler.
ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

即分三步:获取处理器、获取适配器、使用适配器执行处理器。

获取处理器,就是按照次序遍历所有的处理器映射,找到第一个非空的处理器执:

 protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings == null) {
return null;
}
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}

获取适配器的,就是遍历所有的适配器,找到第一个支持当前处理器的适配器:

 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");
}

谁来为接口的请求参数赋值?——参数解析器

在案例中,接口可直接使用定义的三个参数 request、params 和 webUser,但并没有显示地为它们赋值——这是由 SpringMVC 的参数解析器自动完成的。

SpringMVC 中定义了多种参数解析器

HandlerMethodArgumentResolver
,特定的解析器支持在特定的条件下为参数赋值。参数解析器的接口声明如下:

 package org.springframework.web.method.support;

public interface HandlerMethodArgumentResolver {
// 是否支持这样的参数,即当参数满足什么样的条件时可以为其赋值
boolean supportsParameter(MethodParameter parameter);

// 如何为参数赋值
Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception;
}

参数解析器有一个重要的组合器实现

HandlerMethodArgumentResolverComposite
,用于管理其他的参数解析器,其核心代码如下:

 package org.springframework.web.method.support;

public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
// 管理所有的参数解析器
private final List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<>();
// 缓存后不必每次都遍历所有
private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache = new ConcurrentHashMap<>(256);

// 检查所管理的参数解析器,看其中是否有支持的
@Override
public boolean supportsParameter(MethodParameter parameter) {
return getArgumentResolver(parameter) != null;
}

// 用第一个支持此参数的解析器来处理
@Override @Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
if (resolver == null) {
throw new IllegalArgumentException("Unsupported parameter type [" + parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
}
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}

// 遍历所有已注册的参数解析器,找到第一个支持这种参数的。如果未找到则返回 null
@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
if (result != null) {
return result;
}
for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
if (resolver.supportsParameter(parameter)) {
this.argumentResolverCache.put(parameter, resolver);
return resolver;
}
}
// 通常来说不可能走到这里,因为最后一个解析器 ServletModelAttributeMethodProcessor 是几乎“万能”的
}
}

默认的参数解析器(此处的 this.argumentResolvers)有27个,它们各自对 supportsParameter 方法的实现如下(按顺序排列):

0 = { RequestParamMethodArgumentResolver @6892}

 /*
* 满足以下三类条件中的任意一类:
* 1. 参数标有 @RequestParam 注解、且类型不是 Map 或其子类
* 2. 参数标有 @RequestParam 注解、且类型是 Map 或其子类、且 @RequestParam 注解的"name"或"value"属性不为空
* 3. 参数类型是 MultipartFile 或 Part(包括它们的数组或集合)、且没有标 @RequestPart 注解
*/
public boolean supportsParameter0(MethodParameter parameter) {
if (parameter.hasParameterAnnotation(RequestParam.class)) {
if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
return (requestParam != null && StringUtils.hasText(requestParam.name()));
} else {
return true;
}
} else {
if (parameter.hasParameterAnnotation(RequestPart.class)) {
return false;
}
parameter = parameter.nestedIfOptional();
return MultipartResolutionDelegate.isMultipartArgument(parameter);
}
}

1 = { RequestParamMapMethodArgumentResolver @6893}

 /*
* 同时满足以下三个条件:
* 1. 参数类型是 Map 或其子类
* 2. 参数标有 @RequestParam 注解
* 3. @RequestParam 注解未设置"name"或"value"属性
*/
public boolean supportsParameter(MethodParameter parameter) {
RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
return requestParam != null && Map.class.isAssignableFrom(parameter.getParameterType()) && !StringUtils.hasText(requestParam.name());
}

2 = { PathVariableMethodArgumentResolver @6894}

 /*
* 满足以下两类条件中的任意一类:
* 1. 参数标有 @PathVariable 注解、且类型不是 Map 或其子类
* 2. 参数标有 @PathVariable 注解、且类型是 Map 或其子类、且 @PathVariable 注解的"name"或"value"属性不为空
*/
public boolean supportsParameter(MethodParameter parameter) {
if (!parameter.hasParameterAnnotation(PathVariable.class)) {
return false;
}
if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
PathVariable pathVariable = parameter.getParameterAnnotation(PathVariable.class);
return (pathVariable != null && StringUtils.hasText(pathVariable.value()));
}
return true;
}

3 = { PathVariableMapMethodArgumentResolver @6895}

 /*
* 同时满足以下三个条件:
* 1. 参数类型是 Map 或其子类
* 2. 参数标有 @PathVariable 注解
* 3. @PathVariable 注解未设置"name"或"value"属性
*/
public boolean supportsParameter(MethodParameter parameter) {
PathVariable pathVariable = parameter.getParameterAnnotation(PathVariable.class);
return (pathVariable != null && Map.class.isAssignableFrom(parameter.getParameterType()) && !StringUtils.hasText(pathVariable.value()));
}

4 = { MatrixVariableMethodArgumentResolver @6896}

 /*
* 满足以下两类条件中的任意一类:
* 1. 参数标有 @MatrixVariable 注解、且类型不是 Map 或其子类
* 2. 参数标有 @MatrixVariable 注解、且类型是 Map 或其子类、且 @MatrixVariable 注解的"name"或"value"属性不为空
*/
public boolean supportsParameter(MethodParameter parameter) {
if (!parameter.hasParameterAnnotation(MatrixVariable.class)) {
return false;
}
if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
MatrixVariable matrixVariable = parameter.getParameterAnnotation(MatrixVariable.class);
return (matrixVariable != null && StringUtils.hasText(matrixVariable.name()));
}
return true;
}

5 = { MatrixVariableMapMethodArgumentResolver @6897}

 /*
* 同时满足以下三个条件:
* 1. 参数类型是 Map 或其子类
* 2. 参数标有 @MatrixVariable 注解
* 3. @MatrixVariable 注解未设置"name"或"value"属性
*/
public boolean supportsParameter(MethodParameter parameter) {
MatrixVariable matrixVariable = parameter.getParameterAnnotation(MatrixVariable.class);
return (matrixVariable != null && Map.class.isAssignableFrom(parameter.getParameterType()) && !StringUtils.hasText(matrixVariable.name()));
}

6 = { ServletModelAttributeMethodProcessor @6898}
ModelAttributeMethodProcessor

 // 参数标有 @ModelAttribute 注解
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(ModelAttribute.class);
}

7 = { RequestResponseBodyMethodProcessor @6899}

 // 参数标有 @RequestBody 注解
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestBody.class);
}

8 = { RequestPartMethodArgumentResolver @6900}

 /*
* 满足以下两类条件中的任意一类:
* 1. 参数标有 @RequestPart 注解
* 2. 参数类型是 MultipartFile 或 Part(包括它们的数组或集合)、且没有标 @RequestParam 注解
*/
public boolean supportsParameter(MethodParameter parameter) {
if (parameter.hasParameterAnnotation(RequestPart.class)) {
return true;
}
if (parameter.hasParameterAnnotation(RequestParam.class)) {
return false;
}
return MultipartResolutionDelegate.isMultipartArgument(parameter.nestedIfOptional());
}

9 = { RequestHeaderMethodArgumentResolver @6901}

 /*
* 同时满足以下两个条件:
* 1. 参数标有 @RequestHeader 注解
* 2. 参数类型不是 Map 或其子类
*/
public boolean supportsParameter(MethodParameter parameter) {
return (parameter.hasParameterAnnotation(RequestHeader.class) && !Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType()));
}

10 = { RequestHeaderMapMethodArgumentResolver @6902}

 /*
* 同时满足以下两个条件:
* 1. 参数标有 @RequestHeader 注解
* 2. 参数类型是 Map 或其子类
*/
public boolean supportsParameter(MethodParameter parameter) {
return (parameter.hasParameterAnnotation(RequestHeader.class) && Map.class.isAssignableFrom(parameter.getParameterType()));
}

11 = { ServletCookieValueMethodArgumentResolver @6903}
AbstractCookieValueMethodArgumentResolver

 // 参数标有 @CookieValue 注解
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(CookieValue.class);
}

12 = { ExpressionValueMethodArgumentResolver @6904}

 // 参数标有 @Value 注解
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(Value.class);
}

13 = { SessionAttributeMethodArgumentResolver @6905}

 // 参数标有 @SessionAttribute 注解
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(SessionAttribute.class);
}

14 = { RequestAttributeMethodArgumentResolver @6906}

 // 参数标有 @RequestAttribute 注解
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestAttribute.class);
}

15 = { ServletRequestMethodArgumentResolver @6907}

 /*
* 满足以下三类条件中的任意一类:
* 1. 参数类型 Principal 或其子类,且没有标任何注解
* 2. 参数类型是 WebRequest、ServletRequest、MultipartRequest、HttpSession、PushBuilder、InputStream 或 Reader 或它们的子类
* 2. 参数类型是 HttpMethod、Locale、TimeZone 或 ZoneId
*/
public boolean supportsParameter(MethodParameter parameter) {
Class<?> paramType = parameter.getParameterType();
return (Principal.class.isAssignableFrom(paramType) && !parameter.hasParameterAnnotations())
|| (WebRequest||ServletRequest||MultipartRequest||HttpSession||PushBuilder||InputStream||Reader).class.isAssignableFrom(paramType)
|| (HttpMethod||Locale||TimeZone||ZoneId).class == paramType;
}

16 = { ServletResponseMethodArgumentResolver @6908}

 // 参数类型是 ServletResponse、OutputStream 或 Writer 或它们的子类
public boolean supportsParameter(MethodParameter parameter) {
return (ServletResponse || OutputStream || Writer).class.isAssignableFrom(parameter.getParameterType());
}

17 = { HttpEntityMethodProcessor @6909}

 // 参数类型是 HttpEntity 或 RequestEntity
public boolean supportsParameter(MethodParameter parameter) {
Class<?> parameterType = parameter.getParameterType();
return (parameterType == HttpEntity.class) || (parameterType == RequestEntity.class);
}

18 = { RedirectAttributesMethodArgumentResolver @6910}

 // 参数类型是 RedirectAttributes 或其子类
public boolean supportsParameter(MethodParameter parameter) {
return RedirectAttributes.class.isAssignableFrom(parameter.getParameterType());
}

19 = { ModelMethodProcessor @6911}

 // 参数类型是 Model 或其子类
public boolean supportsParameter(MethodParameter parameter) {
return Model.class.isAssignableFrom(parameter.getParameterType());
}

20 = { MapMethodProcessor @6912}

 /*
* 同时满足以下两个条件:
* 1. 参数类型是 Map 或其子类
* 2. 参数没有标任何注解
*/
public boolean supportsParameter(MethodParameter parameter) {
return (Map.class.isAssignableFrom(parameter.getParameterType()) && parameter.getParameterAnnotations().length == 0);
}

21 = { ErrorsMethodArgumentResolver @6913}

 // 参数类型是 Errors 或其子类
public boolean supportsParameter(MethodParameter parameter) {
return Errors.class.isAssignableFrom(parameter.getParameterType());
}

22 = { SessionStatusMethodArgumentResolver @6914}

 // 参数类型是 SessionStatus 或其子类
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType() == SessionStatus.class;
}

23 = { UriComponentsBuilderMethodArgumentResolver @6915}

 // 参数类型是 UriComponentsBuilder 或 ServletUriComponentsBuilder
public boolean supportsParameter(MethodParameter parameter) {
Class<?> parameterType = parameter.getParameterType();
return (parameterType == UriComponentsBuilder.class) || (parameterType == ServletUriComponentsBuilder.class);
}

24 = { PrincipalMethodArgumentResolver @6916}

 // 参数类型是 Principal 或其子类
public boolean supportsParameter(MethodParameter parameter) {
return Principal.class.isAssignableFrom(parameter.getParameterType());
}

25 = { RequestParamMethodArgumentResolver @6917}

 /*
* 满足以下四类条件中的任意一类:
* 1. 参数标有 @RequestParam 注解、且类型不是 Map 或其子类
* 2. 参数标有 @RequestParam 注解、且类型是 Map 或其子类、且 @RequestParam 注解的"name"或"value"属性不为空
* 3. 参数类型是 MultipartFile 或 Part(包括它们的数组或集合)、且没有标 @RequestPart 注解
* 4. 参数类型不简单、且没有标 @RequestPart 注解
*/
public boolean supportsParameter25(MethodParameter parameter) {
if (parameter.hasParameterAnnotation(RequestParam.class)) {
if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
return (requestParam != null && StringUtils.hasText(requestParam.name()));
} else {
return true;
}
} else {
if (parameter.hasParameterAnnotation(RequestPart.class)) {
return false;
}
parameter = parameter.nestedIfOptional();
if (MultipartResolutionDelegate.isMultipartArgument(parameter)) {
return true;
} else {
return BeanUtils.isSimpleProperty(parameter.getNestedParameterType());
}
}
}

26 = { ServletModelAttributeMethodProcessor @6918}

 /*
* 满足以下两个条件中的任意一个:
* 1. 参数标有 @ModelAttribute 注解
* 2. 参数类型不简单
*/
public boolean supportsParameter(MethodParameter parameter) {
return (parameter.hasParameterAnnotation(ModelAttribute.class)) || (!BeanUtils.isSimpleProperty(parameter.getParameterType()));
}

逐个分析可知,案例中接口的参数:

  • request
    :其类型是 HttpServletRequest,即 Servlet 的子类,因此会被15号解析器
    ServletRequestMethodArgumentResolver
    处理。
  • params
    :其类型是 Map,且标有未设置属性的 @RequestParam 注解,因此会被1号解析器
    RequestParamMapMethodArgumentResolver
    处理。
  • webUser
    :该参数不是简单类型,且没有标任何注解,只能被最后一个解析器
    ServletModelAttributeMethodProcessor
    处理。
    接下来调用各自的 resolveArgument 方法即获得参数值
    ... ...

标签: Java

添加新评论