HandlerMethodArgumentResolver是什么?它是springmvc提供的入参解析器,像平常应用的注解@RequestParam @PathVariable @ModelAttribute ...等等修饰在@RequestMapping下的参数上都可以用HandlerMethodArgumentResolver来解析。
这里看一下HandlerMethodArgumentResolver的源码:
这里以@RequestParam注解为例,作为源码解析:
@Override
public boolean supportsParameter(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 if (this.useDefaultResolution) {
return BeanUtils.isSimpleProperty(parameter.getNestedParameterType());
}
else {
return false;
}
}
}
@Override
@Nullable
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
//拿到请求体 是否是文件流
if (servletRequest != null) {
Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);
if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {
return mpArg;
}
}
Object arg = null;
MultipartHttpServletRequest multipartRequest = request.getNativeRequest(MultipartHttpServletRequest.class);
//也是判断是否携带文件的请求
if (multipartRequest != null) {
List<MultipartFile> files = multipartRequest.getFiles(name);
if (!files.isEmpty()) {
arg = (files.size() == 1 ? files.get(0) : files);
}
}
//如果不是文件,拿到参数值,然后返回
if (arg == null) {
String[] paramValues = request.getParameterValues(name);
if (paramValues != null) {
arg = (paramValues.length == 1 ? paramValues[0] : paramValues);
}
}
return arg;
}
在这里,我模拟一个场景,即用户对传入的参数进行非空和去trim判断,可以使用这个优雅的进行拦截和判空
一般情况下,我们对请求的参数判空都是用 if(xxx!=null) { ... },这样的作法特别不优雅,因此,我们可以利用HandlerMethodArgumentResolver对参数进行拦截,判空去trim
首先,自定义注解 Validator ,接下来编写一个class 实现HandlerMethodArgumentResolver,然后需要在WebMvcConfigurationSupport下添加参数解析器的类
import java.lang.annotation.*;
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Validator {
}
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.choviwu.movie.annotation.Validator;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* @author ChoviWu
*/
@Slf4j
public class ValidatorMethodArgumentResolver implements HandlerMethodArgumentResolver {
/**
* 参数有没有这个注解 有的话才能执行下一个方法
*
* @param methodParameter
* @return
*/
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
return methodParameter.hasParameterAnnotation(Validator.class);
}
@Override
public Object resolveArgument(MethodParameter methodParameter,
ModelAndViewContainer modelAndViewContainer,
NativeWebRequest nativeWebRequest,
WebDataBinderFactory webDataBinderFactory) throws Exception {
final StringBuilder value = new StringBuilder();
String methodParameterName = methodParameter.getParameter().getName();
AtomicBoolean flag = new AtomicBoolean(false);
Set<Map.Entry<String,String[]>> set = nativeWebRequest.getParameterMap().entrySet();
//传参
set.forEach(paramter -> {
log.info(">>>>>>>>> Parameter >>>>>>> {}, >>>>>>>>> Value : {}",paramter.getKey(),paramter.getValue()[0]);
if (methodParameterName.equals(paramter.getKey())) {
//cas
flag.compareAndSet(false,true);
if (StringUtils.isBlank(paramter.getValue()[0])) {
throw new IllegalArgumentException(paramter.getKey() + " is Not Null");
}
value.append(paramter.getValue()[0]);
}
});
//if foreach not scan vararible
if(!flag.get()){
throw new IllegalArgumentException(methodParameterName + " is Not Null");
}
return value.toString();
}
}
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.List;
/**
* @author Administrator
*/
@Configuration
public class WebConfig extends WebMvcConfigurationSupport{
@Override
protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new ValidatorMethodArgumentResolver());
super.addArgumentResolvers(argumentResolvers);
}
}
最后编写一个测试类,作为参数校验的测试
import org.choviwu.movie.annotation.Response;
import org.choviwu.movie.annotation.Validator;
import org.choviwu.movie.annotation.ValidatorBody;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class IndexController {
@Response
@ResponseBody
@RequestMapping(value = "index")
public Object index( @Validator String name, @Validator String halo){
return name + halo;
}
}
然后跑一下代码。
但是如果你的参数上有@RequestParam 等注解,@RequestParam解析器会拦截掉你的自定义拦截器(也就是RequestParamMethodArgumentResolver先执行)。所以,需要配置如下参数:
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Lists;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.method.annotation.RequestParamMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.List;
/**
* @author Administrator
*/
@Configuration
public class WebConfig extends WebMvcConfigurationSupport implements InitializingBean{
@Autowired
RequestMappingHandlerAdapter requestMappingHandlerAdapter;
@Override
public void afterPropertiesSet() throws Exception {
List<HandlerMethodArgumentResolver> unmodifiableList = requestMappingHandlerAdapter.getArgumentResolvers();
List<HandlerMethodArgumentResolver> list = Lists.newArrayList();
for (HandlerMethodArgumentResolver methodArgumentResolver : unmodifiableList) {
//需要在requestParam之前执行自定义逻辑,然后再执行下一个逻辑(责任链模式)
if (methodArgumentResolver instanceof RequestParamMethodArgumentResolver) {
list.add(new ValidatorMethodArgumentResolver(methodArgumentResolver));
}
list.add(methodArgumentResolver);
}
requestMappingHandlerAdapter.setArgumentResolvers(list);
}
新的执行逻辑应该这么写:
package org.choviwu.movie.config;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.choviwu.movie.annotation.Validator;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* @author ChoviWu
*/
@Slf4j
public class ValidatorMethodArgumentResolver implements HandlerMethodArgumentResolver {
private final HandlerMethodArgumentResolver methodArgumentResolver;
public ValidatorMethodArgumentResolver(HandlerMethodArgumentResolver methodArgumentResolver){
this.methodArgumentResolver = methodArgumentResolver;
}
/**
* 参数有没有这个注解 有的话才能执行下一个方法
*
* @param methodParameter
* @return
*/
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
return methodParameter.hasParameterAnnotation(Validator.class);
}
@Override
public Object resolveArgument(MethodParameter methodParameter,
ModelAndViewContainer modelAndViewContainer,
NativeWebRequest nativeWebRequest,
WebDataBinderFactory webDataBinderFactory) throws Exception {
final StringBuilder value = new StringBuilder();
String methodParameterName = methodParameter.getParameter().getName();
AtomicBoolean flag = new AtomicBoolean(false);
Set<Map.Entry<String,String[]>> set = nativeWebRequest.getParameterMap().entrySet();
//传参
set.forEach(paramter -> {
log.info(">>>>>>>>> Parameter >>>>>>> {}, >>>>>>>>> Value : {}",paramter.getKey(),paramter.getValue()[0]);
if (methodParameterName.equals(paramter.getKey())) {
//cas
flag.compareAndSet(false,true);
if (StringUtils.isBlank(paramter.getValue()[0])) {
throw new IllegalArgumentException(paramter.getKey() + " is Not Null");
}
value.append(paramter.getValue()[0]);
}
});
//if foreach not scan vararible
if(!flag.get()){
throw new IllegalArgumentException(methodParameterName + " is Not Null");
}
//执行@RequestParam 的参数解析逻辑
return methodArgumentResolver.resolveArgument(methodParameter,modelAndViewContainer,nativeWebRequest,webDataBinderFactory);
}
}
至此,编写完毕!