基于 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 發送請求測驗,結果符合預期:


下面就一些關鍵點進行探究分析,
由誰來處理本次請求?——請求處理器
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);
對它們進行排序,其中的排序規則如下:
- 實作了
PriorityOrdered介面的優先, - 實作了
Ordered介面的優先,然后按照 getOrder() 的值從小到大排序, - 如以上條件均不滿足,則排到最后,
// 摘自 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 方法即獲得引數值
... ...
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/536084.html
標籤:其他
上一篇:創建第一個springmvc程式
下一篇:練習中的反思(順序結構程式設計)
