HandlerMethodArgumentResolver是什么?它是springmvc提供的入参解析器,像平常应用的注解@RequestParam @PathVariable @ModelAttribute ...等等修饰在@RequestMapping下的参数上都可以用HandlerMethodArgumentResolver来解析。

这里看一下HandlerMethodArgumentResolver的源码: file

file

这里以@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;
    } 
}

然后跑一下代码。 file file file 但是如果你的参数上有@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);
    }
}

至此,编写完毕!