前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringMvc源码之解析参数HandlerMethodArgumentResolver

SpringMvc源码之解析参数HandlerMethodArgumentResolver

作者头像
简单的程序员
发布2020-04-18 00:30:23
8770
发布2020-04-18 00:30:23
举报
文章被收录于专栏:奕仁专栏奕仁专栏

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

这里看一下HandlerMethodArgumentResolver的源码:

file
file
file
file

这里以@RequestParam注解为例,作为源码解析:

代码语言:javascript
复制
@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;
			}
		}
	}
代码语言:javascript
复制
@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下添加参数解析器的类

编写注解

代码语言:javascript
复制
import java.lang.annotation.*;

@Target({ElementType.TYPE,ElementType.FIELD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Validator {
}
代码语言:javascript
复制
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();
    }
}
代码语言:javascript
复制
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);
    }
}

最后编写一个测试类,作为参数校验的测试

代码语言:javascript
复制
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
file
file
file

但是如果你的参数上有@RequestParam 等注解,@RequestParam解析器会拦截掉你的自定义拦截器(也就是RequestParamMethodArgumentResolver先执行)。所以,需要配置如下参数:

代码语言:javascript
复制
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);
    }

新的执行逻辑应该这么写:

代码语言:javascript
复制
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);
    }
}

至此,编写完毕!

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-04-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 编写注解
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档