springboot自定义参数解析器

Posted 野生java研究僧

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了springboot自定义参数解析器相关的知识,希望对你有一定的参考价值。

springboot自定义参数解析器

1.前言

1.springMVC是如何把参数解析完毕后注入到controller方法参数上的呢?在javaweb阶段,我们都学过使用HttpServletRequest这个对象获取参数,比如 request.getParameter(parameterName);那么springMVC其实也是用于这个来进行获取原始的参数的。

比如:@RequestBody,@RequestParam注解等

2.springMVC参数解析器

在请求经过原生的servlet过后,会将请求通过DispatchServlet将请求分发下去,然后执行到InvocableHandlerMethod.invokeForRequest()方法的时候,其中 Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); 这一行就是获取请求参数的。最终会来到getMethodArgumentValues()这个方法,这个方法,最关键的其中一行就是 this.resolvers.supportsParameter(parameter) ,这个会找所有实现了 HandlerMethodArgumentResolver 接口的bean,挨个循环调用supportsParameter()这个方法,看看那个解析器能解析这个参数。如果能解析,然后执行 this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory); 解析完获取到指定的参数然后返回。最后会根据请求路径找到对应的controller的method对象,然后通过反射的方式调用,并且把解析完毕的参数传递进去,这样走到我们controller的时候,controller参数就被绑定上了。

通过debug可以看到 最开始的参数解析器有31个,其中标记红色的那个是我自定义的。

HandlerMethodArgumentResolver接口就是参数解析器,supportsParameter表示是否支持解析,resolveArgument()是执行具体的解析逻辑。


public interface HandlerMethodArgumentResolver 

	/**
	 * Whether the given @linkplain MethodParameter method parameter is
	 * supported by this resolver.
	 * @param parameter the method parameter to check
	 * @return @code true if this resolver supports the supplied parameter;
	 * @code false otherwise
	 */
	boolean supportsParameter(MethodParameter parameter);

	/**
	 * Resolves a method parameter into an argument value from a given request.
	 * A @link ModelAndViewContainer provides access to the model for the
	 * request. A @link WebDataBinderFactory provides a way to create
	 * a @link WebDataBinder instance when needed for data binding and
	 * type conversion purposes.
	 * @param parameter the method parameter to resolve. This parameter must
	 * have previously been passed to @link #supportsParameter which must
	 * have returned @code true.
	 * @param mavContainer the ModelAndViewContainer for the current request
	 * @param webRequest the current request
	 * @param binderFactory a factory for creating @link WebDataBinder instances
	 * @return the resolved argument value, or @code null if not resolvable
	 * @throws Exception in case of errors with the preparation of argument values
	 */
	@Nullable
	Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;


可以看到这个接口有很多的实现类,比如::@RequestBody,@RequestParam,@CookieValue等注解,都是有对应实现的

3.如何自定义参数解析器

因为我们知道POST请求使用@RequestBody是可以接收json格式的数据直接绑定到对应的参数名对象上,而GET请求是不可以的,那接下来我们就实现一个@JsonParam注解,然后让GET请求也支持json格式的参数传递。

@JsonParam:

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface JsonParam 
    String value() default "";
    String name() default "";
    boolean required() default true;
    String defaultValue() default ValueConstants.DEFAULT_NONE;


实现HandlerMethodArgumentResolver接口,指定遇到什么参数的时候进行解析,每解析controller方法参数的每一项时都会调用HandlerMethodArgumentResolver的supportsParameter和resolveArgument方法进行解析

public class GetQueryJsonParamResolver implements HandlerMethodArgumentResolver 
    @Override
    public boolean supportsParameter(MethodParameter parameter) 
        Annotation[] annotations = parameter.getParameterAnnotations();
        for (int i = 0; i < annotations.length; i++) 
            Annotation item = annotations[i];
            if (item.annotationType().equals(JsonParam.class))
                return true;
            
        
        return false;


    

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception 
        HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
        String parameterName = parameter.getParameterName();
        Annotation[] annotations = parameter.getParameterAnnotations();
        String params = request.getParameter(parameterName);
        if (StringUtils.isBlank(params))
            for (Annotation item : annotations) 
                if (item instanceof JsonParam) 
                    JsonParam param = (JsonParam) item;
                    params = request.getParameter(param.value());
                    if (StringUtils.isBlank(params)) 
                        params = param.defaultValue();
                    
                    if (StringUtils.isBlank(params) && param.required() || params.equals(ValueConstants.DEFAULT_NONE)) 
                        throw new RuntimeException(parameterName + ":不能为空");
                    
                
            
        
        Class<?> parameterType = parameter.getParameterType();
        Object bean = JSONUtil.toBean(params, parameterType);
        return bean;
    

最后我们在配置类中添加参数解析器 添加完毕后,参数解析器从原来的31个就会变成32个,到解析参数的时候就会走到我们自己写的参数解析器哪里,解析完毕后把对应的参数返回去。

@Configuration
public class StaticConfig implements WebMvcConfigurer 
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) 
        resolvers.add(new GetQueryJsonParamResolver());
    

4测试

最后我们在controller上写一个方法进行测试即可,可以看到我们传递的参数被成功的解析到

    @GetMapping("/requestParam")
    public void requestParam(@JsonParam UserInfo userInfo, HttpServletResponse response)
        AjaxResult<UserInfo> ajaxResult = new AjaxResult<>(200,"操作成功",0,"请求成功",userInfo);
        ResponseUtil.response(response,ajaxResult);

    

esult ajaxResult = new AjaxResult<>(200,“操作成功”,0,“请求成功”,userInfo);
ResponseUtil.response(response,ajaxResult);





以上是关于springboot自定义参数解析器的主要内容,如果未能解决你的问题,请参考以下文章

springboot源码解析-自定义系统初始化器

springboot源码解析-自定义系统初始化器

请求参数的自定义 Spring 注释 - 从未调用自定义解析器

第269天学习打卡(知识点回顾 springboot视图解析原理流程)

HandlerMethodArgumentResolver用于统一获取当前登录用户 — 转

如何在 argparse 中嵌套自定义参数解析器实例