I think this kind of parser is a complement to the interface based on a key parameter analysis, which relies on the relevant notes based on a key interface parameters resolution. Once upon a time you thought about whether full-time by @RequestParam to package into a Map, and then again own analysis? A processor to present the same class @ RequestHeader, @ PathVariable, @ MatrixVariable have given this ability -
1, PathVariableMapMethodArgumentResolver
@Override
public boolean supportsParameter(MethodParameter parameter) {
PathVariable ann = parameter.getParameterAnnotation(PathVariable.class);
return (ann != null && Map.class.isAssignableFrom(parameter.getParameterType()) &&
!StringUtils.hasText(ann.value()));
}
/**
* Return a Map with all URI template variables or an empty map.
*/
@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
@SuppressWarnings("unchecked")
Map<String, String> uriTemplateVars =
(Map<String, String>) webRequest.getAttribute(
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
if (!CollectionUtils.isEmpty(uriTemplateVars)) {
return new LinkedHashMap<>(uriTemplateVars);
}
else {
return Collections.emptyMap();
}
}
1, note processor
- It must be marked with the
@PathVariable
annotation, and the parameter type is a map
2, the parameter parsing
- All of the parameters used paths filled return to Map
二、RequestParamMapMethodArgumentResolver
@Override
public boolean supportsParameter(MethodParameter parameter) {
RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
return (requestParam != null && Map.class.isAssignableFrom(parameter.getParameterType()) &&
!StringUtils.hasText(requestParam.name()));
}
@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
if (MultiValueMap.class.isAssignableFrom(parameter.getParameterType())) {
// MultiValueMap
Class<?> valueType = resolvableType.as(MultiValueMap.class).getGeneric(1).resolve();
if (valueType == MultipartFile.class) {
MultipartRequest multipartRequest = MultipartResolutionDelegate.resolveMultipartRequest(webRequest);
return (multipartRequest != null ? multipartRequest.getMultiFileMap() : new LinkedMultiValueMap<>(0));
}
else if (valueType == Part.class) {
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
if (servletRequest != null && MultipartResolutionDelegate.isMultipartRequest(servletRequest)) {
Collection<Part> parts = servletRequest.getParts();
LinkedMultiValueMap<String, Part> result = new LinkedMultiValueMap<>(parts.size());
for (Part part : parts) {
result.add(part.getName(), part);
}
return result;
}
return new LinkedMultiValueMap<>(0);
}
else {
Map<String, String[]> parameterMap = webRequest.getParameterMap();
MultiValueMap<String, String> result = new LinkedMultiValueMap<>(parameterMap.size());
parameterMap.forEach((key, values) -> {
for (String value : values) {
result.add(key, value);
}
});
return result;
}
}
else {
// Regular Map
Class<?> valueType = resolvableType.asMap().getGeneric(1).resolve();
if (valueType == MultipartFile.class) {
MultipartRequest multipartRequest = MultipartResolutionDelegate.resolveMultipartRequest(webRequest);
return (multipartRequest != null ? multipartRequest.getFileMap() : new LinkedHashMap<>(0));
}
else if (valueType == Part.class) {
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
if (servletRequest != null && MultipartResolutionDelegate.isMultipartRequest(servletRequest)) {
Collection<Part> parts = servletRequest.getParts();
LinkedHashMap<String, Part> result = new LinkedHashMap<>(parts.size());
for (Part part : parts) {
if (!result.containsKey(part.getName())) {
result.put(part.getName(), part);
}
}
return result;
}
return new LinkedHashMap<>(0);
}
else {
Map<String, String[]> parameterMap = webRequest.getParameterMap();
Map<String, String> result = new LinkedHashMap<>(parameterMap.size());
parameterMap.forEach((key, values) -> {
if (values.length > 0) {
result.put(key, values[0]);
}
});
return result;
}
}
}
1, note processor
- Use
@RequestParam
annotation, and the parameter type is a map
2, the parameter parsing
- It can not pass a multiple-level key if the same key occurs, the value of the key top prevail. Map instance is an
LinkedHashMap<String,String>
instance of
2, case
@ResponseBody
@GetMapping("/test")
public Object test(@RequestParam Map<String,Object> params) {
System.out.println(params);
return params;
}
Search Path: /test?name=fsx&age=18&age=28
console output is as follows:{name=fsx, age=18}
age can only take one of the values
3、RequestHeaderMapMethodArgumentResolver
The first-time request to get information: the type of expenditure data write MultiValueMap (LinkedMultiValueMap) / HttpHeaders / Map
@Override
public boolean supportsParameter(MethodParameter parameter) {
return (parameter.hasParameterAnnotation(RequestHeader.class) &&
Map.class.isAssignableFrom(parameter.getParameterType()));
}
@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
Class<?> paramType = parameter.getParameterType();
if (MultiValueMap.class.isAssignableFrom(paramType)) {
MultiValueMap<String, String> result;
if (HttpHeaders.class.isAssignableFrom(paramType)) {
result = new HttpHeaders();
}
else {
result = new LinkedMultiValueMap<>();
}
for (Iterator<String> iterator = webRequest.getHeaderNames(); iterator.hasNext();) {
String headerName = iterator.next();
String[] headerValues = webRequest.getHeaderValues(headerName);
if (headerValues != null) {
for (String headerValue : headerValues) {
result.add(headerName, headerValue);
}
}
}
return result;
}
else {
Map<String, String> result = new LinkedHashMap<>();
for (Iterator<String> iterator = webRequest.getHeaderNames(); iterator.hasNext();) {
String headerName = iterator.next();
String headerValue = webRequest.getHeader(headerName);
if (headerValue != null) {
result.put(headerName, headerValue);
}
}
return result;
}
}
1, note processor
- It must be marked
@RequestHeader
, and the parameter ismap
type
2, case
@ResponseBody
@GetMapping("/test")
public Object test(@RequestHeader Map<String, Object> headers) {
headers.forEach((k, v) -> System.out.println(k + "-->" + v));
return headers;
}
Console output:
host-->localhost:8080
connection-->keep-alive
cache-control-->max-age=0
upgrade-insecure-requests-->1
。。。。
But strongly recommended direct use Map, but the use of HttpHeaders type. So write @RequestHeader HttpHeaders headers, get a more convenient time.
4、MapMethodProcessor
It handles Map type, but does not mark any case annotations, after its execution order is very reliable, so a bit reveal all the details of the meaning.
The processor also explains: why write Map on your method to the Senate, HashMap, ModelMap and so on can be very convenient access to a cause value of the model ~
@Override
public boolean supportsParameter(MethodParameter parameter) {
return Map.class.isAssignableFrom(parameter.getParameterType());
}
@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
Assert.state(mavContainer != null, "ModelAndViewContainer is required for model exposure");
return mavContainer.getModel();
}
1, note processor
- The processor, the processing chain is on the spring of the parameter, the processing is marked no annotation
map
type
2, the parameter parsing
- The Model direct return
5、MatrixVariableMapMethodArgumentResolver
slightly.