Spring 注解面面通之@MatrixVariable参数绑定源码解析
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://blog.csdn.net/securitit/article/details/110676001