• 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个,其中标记红色的那个是我自定义的。

    image-20221204200429632

    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;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

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

    image-20221204200645733

    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;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    实现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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

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

    @Configuration
    public class StaticConfig implements WebMvcConfigurer {
        @Override
        public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
            resolvers.add(new GetQueryJsonParamResolver());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    4测试

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

    image-20221204201452924

        @GetMapping("/requestParam")
        public void requestParam(@JsonParam UserInfo userInfo, HttpServletResponse response){
            AjaxResult<UserInfo> ajaxResult = new AjaxResult<>(200,"操作成功",0,"请求成功",userInfo);
            ResponseUtil.response(response,ajaxResult);
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

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

    }
    
    • 1
    
    
    
    
    • 1
    • 2
    • 3
  • 相关阅读:
    BTC相关收入下降34% 数字支付巨头Block整体收入下滑
    基于微信小程序的医院门诊体检预约管理系统设计与实现(源码+lw+部署文档+讲解等)
    Mybatis-plus(学习笔记)
    寻找小红书达人技巧有哪些,小红书行业黑话汇总!
    Python随手记
    理解RNN以及模型搭建代码
    变压器的应用
    Rainiverse VoxEdit 大赛
    FFplay文档解读-39-视频过滤器十四
    Python——案例
  • 原文地址:https://blog.csdn.net/m0_46188681/article/details/128176764