??Spring MVC中使用HandlerMethodArgumentResolver策略介面來定義處理器方法引數決議器,@MatrixVariable使用的是MatrixVariableMapMethodArgumentResolver和MatrixVariableMethodArgumentResolver,接下來一起來深入了解一下其原始碼實作,


??類決議
??HandlerMethodArgumentResolver和AbstractNamedValueMethodArgumentResolver是決議策略的上層定義和抽象,關于這兩個類可以參照《Spring 注解面面通 之 @CookieValue引數系結原始碼決議》中的決議,
??MatrixVariableMapMethodArgumentResolver和MatrixVariableMethodArgumentResolver則是用來針對不用型別的方法引數的決議,
??1) MatrixVariableMapMethodArgumentResolver實作了HandlerMethodArgumentResolver的supportsParameter(...)和resolveArgument(...)方法,
??MatrixVariableMapMethodArgumentResolver在某些條件成立的情況下才會使用此類進行決議:
??① 方法引數由@MatrixVariable注解注釋,
??② 方法引數型別必須是Map型別,
??③ 注釋方法引數的@MatrixVariable的name屬性不能有值,
??resolveArgument(...)決議引數從NativeWebRequest.getAttribute( HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST)獲得,
package org.springframework.web.servlet.mvc.method.annotation;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.MatrixVariable;
import org.springframework.web.bind.annotation.ValueConstants;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.HandlerMapping;
/**
* 決議帶@MatrixVariable注解的Map型別的引數,其中注解未指定名稱.
* 換句話說,這個決議器的目的是提供對多個矩陣變數的訪問,這些變數可以是全部的,也可以是與特定路徑變數相關聯的.
*
* 指定名稱時,型別為Map的引數被視為具有映射值的單個屬性,并由MatrixVariableMethodArgumentResolver決議.
*/
public class MatrixVariableMapMethodArgumentResolver implements HandlerMethodArgumentResolver {
/**
* 方法引數檢查.
* 方法引數需包含@MatrixVariable注解、為Map型別引數,且@MatrixVariable的name屬性不可有值.
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
MatrixVariable matrixVariable = parameter.getParameterAnnotation(MatrixVariable.class);
return (matrixVariable != null && Map.class.isAssignableFrom(parameter.getParameterType()) &&
!StringUtils.hasText(matrixVariable.name()));
}
/**
* 決議方法引數值.
*/
@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest request, @Nullable WebDataBinderFactory binderFactory) throws Exception {
// 獲取NativeWebRequest中存盤的HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE的值.
@SuppressWarnings("unchecked")
Map<String, MultiValueMap<String, String>> matrixVariables =
(Map<String, MultiValueMap<String, String>>) request.getAttribute(
HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
// 矩陣變數為空.
if (CollectionUtils.isEmpty(matrixVariables)) {
return Collections.emptyMap();
}
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
MatrixVariable ann = parameter.getParameterAnnotation(MatrixVariable.class);
Assert.state(ann != null, "No MatrixVariable annotation");
// 獲取@MatrixVariable指定的矩陣變數的路徑值.
String pathVariable = ann.pathVar();
// @MatrixVariable指定的矩陣變數的路徑值不為空.
if (!pathVariable.equals(ValueConstants.DEFAULT_NONE)) {
// 根據路徑獲取矩陣變數.
MultiValueMap<String, String> mapForPathVariable = matrixVariables.get(pathVariable);
if (mapForPathVariable == null) {
return Collections.emptyMap();
}
map.putAll(mapForPathVariable);
}
// @MatrixVariable指定的矩陣變數的路徑值為空.
else {
// 設定全部矩陣變數.
for (MultiValueMap<String, String> vars : matrixVariables.values()) {
vars.forEach((name, values) -> {
for (String value : values) {
map.add(name, value);
}
});
}
}
// MultiValueMap轉換為Map.
return (isSingleValueMap(parameter) ? map.toSingleValueMap() : map);
}
/**
* 方法引數型別是否是MultiValueMap型別.
* @param parameter 方法引數.
* @return 是否是MultiValueMap型別.
*/
private boolean isSingleValueMap(MethodParameter parameter) {
if (!MultiValueMap.class.isAssignableFrom(parameter.getParameterType())) {
ResolvableType[] genericTypes = ResolvableType.forMethodParameter(parameter).getGenerics();
if (genericTypes.length == 2) {
Class<?> declaredClass = genericTypes[1].getRawClass();
return (declaredClass == null || !List.class.isAssignableFrom(declaredClass));
}
}
return false;
}
}
??2) MatrixVariableMethodArgumentResolver繼承自抽象AbstractNamedValueMethodArgumentResolver(可以參照《Spring 注解面面通 之 @CookieValue引數系結原始碼決議》),
??MatrixVariableMethodArgumentResolver相對MatrixVariableMapMethodArgumentResolver來說,實作基本相同,其在某些條件成立的情況下才會使用此類進行決議:
??① 方法引數由@MatrixVariable注解注釋,
??② 當方法引數型別是Map時,要求其必須由@MatrixVariable注解注釋,同時@MatrixVariable的name屬性必須有值,
??resolveArgument(...)決議引數從NativeWebRequest.getAttribute( HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST)獲得,
package org.springframework.web.servlet.mvc.method.annotation;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.springframework.core.MethodParameter;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.MatrixVariable;
import org.springframework.web.bind.annotation.ValueConstants;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver;
import org.springframework.web.servlet.HandlerMapping;
/**
* 決議用@MatrixVariable注釋的引數.
*
* 如果方法引數是Map型別,它將由MatrixVariableMapMethodArgumentResolver決議,除非注釋指定了一個名稱,
* 在這種情況下,它被視為Map型別的單個屬性(而不是在映射中收集的多個屬性).
*/
public class MatrixVariableMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
/**
* Constructor.
*/
public MatrixVariableMethodArgumentResolver() {
super(null);
}
/**
* 方法引數檢查.
* 方法引數需包含@MatrixVariable注解.
* 若方法引數為Map型別引數,且@MatrixVariable的name屬性必須有值.
*/
@Override
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;
}
/**
* 創建NamedValueInfo.
*/
@Override
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
MatrixVariable ann = parameter.getParameterAnnotation(MatrixVariable.class);
Assert.state(ann != null, "No MatrixVariable annotation");
return new MatrixVariableNamedValueInfo(ann);
}
/**
* 決議方法引數值.
*/
@Override
@SuppressWarnings("unchecked")
@Nullable
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
// 獲取NativeWebRequest中存盤的HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE的值.
Map<String, MultiValueMap<String, String>> pathParameters = (Map<String, MultiValueMap<String, String>>)
request.getAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
// 矩陣變數為空.
if (CollectionUtils.isEmpty(pathParameters)) {
return null;
}
MatrixVariable ann = parameter.getParameterAnnotation(MatrixVariable.class);
Assert.state(ann != null, "No MatrixVariable annotation");
// 獲取@MatrixVariable指定的矩陣變數的路徑值.
String pathVar = ann.pathVar();
List<String> paramValues = null;
// @MatrixVariable指定的矩陣變數的路徑值不為空.
if (!pathVar.equals(ValueConstants.DEFAULT_NONE)) {
if (pathParameters.containsKey(pathVar)) {
paramValues = pathParameters.get(pathVar).get(name);
}
}
// @MatrixVariable指定的矩陣變數的路徑值為空.
else {
boolean found = false;
paramValues = new ArrayList<>();
// 設定全部矩陣變數.
for (MultiValueMap<String, String> params : pathParameters.values()) {
if (params.containsKey(name)) {
if (found) {
String paramType = parameter.getNestedParameterType().getName();
throw new ServletRequestBindingException(
"Found more than one match for URI path parameter '" + name +
"' for parameter type [" + paramType + "]. Use 'pathVar' attribute to disambiguate.");
}
paramValues.addAll(params.get(name));
found = true;
}
}
}
if (CollectionUtils.isEmpty(paramValues)) {
return null;
}
else if (paramValues.size() == 1) {
return paramValues.get(0);
}
else {
return paramValues;
}
}
/**
* 處理引數值缺失例外.
*/
@Override
protected void handleMissingValue(String name, MethodParameter parameter) throws ServletRequestBindingException {
throw new ServletRequestBindingException("Missing matrix variable '" + name +
"' for method parameter of type " + parameter.getNestedParameterType().getSimpleName());
}
/**
* MatrixVariableNamedValueInfo.
*/
private static class MatrixVariableNamedValueInfo extends NamedValueInfo {
private MatrixVariableNamedValueInfo(MatrixVariable annotation) {
super(annotation.name(), annotation.required(), annotation.defaultValue());
}
}
}
??3) NativeWebRequest的
HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE屬性
??分析過MatrixVariableMapMethodArgumentResolver和MatrixVariableMethodArgumentResolver原始碼后,發現矩陣變數都是從NativeWebRequest屬性中獲得,那么是在哪里進行的矩陣模板變數決議呢?
??① 涉及矩陣決議的呼叫程序,
DispatcherServlet.doDispatch(...) --呼叫-->
DispatcherServlet.getHandler(...) --呼叫-->
AbstractHandlerMapping.getHandler(...) --呼叫-->
AbstractHandlerMethodMapping.getHandlerInternal(...) --呼叫-->
AbstractHandlerMethodMapping.lookupHandlerMethod(...) --呼叫-->
RequestMappingInfoHandlerMapping.handleMatch(...) --呼叫-->
RequestMappingInfoHandlerMapping.extractMatrixVariables(...) --呼叫-->
根據已決議的URI模板變數,來決議矩陣變數
??② 查看RequestMappingInfoHandlerMapping.handleMatch(...)方法,可以發現,在這里對矩陣變數進行了決議,并設定到NativeWebRequest屬性中,
/**
* 暴露URI模板變數、矩陣變數和可生成的媒體型別.
*/
@Override
protected void handleMatch(RequestMappingInfo info, String lookupPath, HttpServletRequest request) {
super.handleMatch(info, lookupPath, request);
String bestPattern;
Map<String, String> uriVariables;
Map<String, String> decodedUriVariables;
Set<String> patterns = info.getPatternsCondition().getPatterns();
// 若不存在匹配模式,則均設定為空.
if (patterns.isEmpty()) {
bestPattern = lookupPath;
uriVariables = Collections.emptyMap();
decodedUriVariables = Collections.emptyMap();
}
else {
bestPattern = patterns.iterator().next();
// 決議URI模板變數.
uriVariables = getPathMatcher().extractUriTemplateVariables(bestPattern, lookupPath);
// 對URI模板變數進行解碼.
decodedUriVariables = getUrlPathHelper().decodePathVariables(request, uriVariables);
}
// 設定匹配模式到HttpServletRequest的BEST_MATCHING_PATTERN_ATTRIBUTE.
request.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, bestPattern);
// 設定URI模板變數到HttpServletRequest的HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE.
request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, decodedUriVariables);
// 處理矩陣變數.
if (isMatrixVariableContentAvailable()) {
// 提取矩陣變數.
Map<String, MultiValueMap<String, String>> matrixVars = extractMatrixVariables(request, uriVariables);
// 設定矩陣變數到HttpServletRequest的HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE.
request.setAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, matrixVars);
}
// 處理可生成的媒體型別.
if (!info.getProducesCondition().getProducibleMediaTypes().isEmpty()) {
// 獲取可生成的媒體型別.
Set<MediaType> mediaTypes = info.getProducesCondition().getProducibleMediaTypes();
// 設定可生成媒體型別到HttpServletRequest的PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE.
request.setAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes);
}
}
??③ RequestMappingInfoHandlerMapping.extractMatrixVariables(...)負責矩陣變數的具體決議,
/**
* 提取矩陣變數.
* @param request 當前請求實體.
* @param uriVariables URI模板變數.
* @return 矩陣變數.
*/
private Map<String, MultiValueMap<String, String>> extractMatrixVariables(
HttpServletRequest request, Map<String, String> uriVariables) {
Map<String, MultiValueMap<String, String>> result = new LinkedHashMap<>();
// 遍歷矩陣變數.
uriVariables.forEach((uriVarKey, uriVarValue) -> {
// URI模板變數值中存在=.
int equalsIndex = uriVarValue.indexOf('=');
if (equalsIndex == -1) {
return;
}
String matrixVariables;
// URI模板變數值中存在;,若不存在;或;在首位,或=在;前,URI模板變數的值即是矩陣變數.
int semicolonIndex = uriVarValue.indexOf(';');
if ((semicolonIndex == -1) || (semicolonIndex == 0) || (equalsIndex < semicolonIndex)) {
matrixVariables = uriVarValue;
}
else {
matrixVariables = uriVarValue.substring(semicolonIndex + 1);
uriVariables.put(uriVarKey, uriVarValue.substring(0, semicolonIndex));
}
// 決議矩陣變數到Map.
MultiValueMap<String, String> vars = WebUtils.parseMatrixVariables(matrixVariables);
result.put(uriVarKey, getUrlPathHelper().decodeMatrixVariables(request, vars));
});
return result;
}
??總結
??以上便是@MatrixVariable對方法引數決議的全程序,對于理解@MatrixVariable原理來說還是十分有用處的,更有利于在實際開發中解決那些疑難雜癥,
??若文中存在錯誤和不足,歡迎指正
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/230716.html
標籤:java
