WebMvcConfigurer addArgumentResolvers() 添加参数解析器

本文将介绍怎样简单的实现 WebMvcConfigurer 接口的 addArgumentResolvers() 方法,添加自定义的参数解析器,利用解析器去解析我们自定义的注解等。

需求

我们在日常开发中,编写 HTTP 接口时需要对接口接受的参数进行有效性、非空验证。通常做法是通过大量的 if 语句进行判断,但是这样做太过麻烦。我们是不是可以通过类似 @RequestParam 注解的方式对每个参数进行有效性、非空验证呢?这样我们就可以避免编写大量的 if 语句。

思路

在 Spring 框架中,提供了 WebMvcConfigurer 接口。该接口其实是 Spring 内部的一种配置方式,采用 JavaBean 的形式来代替传统的 xml 配置文件形式进行针对框架个性化定制,可以自定义一些 Handler,Interceptor,ViewResolver,MessageConverter。基于 java-based 方式的 Spring MVC 配置,需要创建一个配置类并实现 WebMvcConfigurer 接口。

在 Spring Boot 1.5 版本中,你可以重写 WebMvcConfigurerAdapter 的方法来添加自定义拦截器、消息转换器、参数解析器等。Spring Boot 2.0 后,该类被标记为 @Deprecated(被弃用)。官方推荐直接实现 WebMvcConfigurer 接口(因为 Java8 支持接口提供默认方法,这也就替代了 WebMvcConfigurerAdapter 迭代器的作用)或者直接继承 WebMvcConfigurationSupport 类。

笔者推荐去实现 WebMvcConfigurer 接口。

实现

示例1

自定义一个注解,使用自定义的注解修饰接口的某个参数,然后自定义参数解析器。验证是否触发我们的解析器,后续在进一步深入。代码如下:

(1)自定义注解 Demo1Annotation,允许该注解用来修饰方法参数。如下:

import java.lang.annotation.*;

// 允许将注解包含在 JavaDoc 文档中
@Documented
// 指定注解使用范围为参数
@Target(ElementType.PARAMETER)
// 指定注解会在 class 字节码中存在,在运行时可以通过反射获取
@Retention(RetentionPolicy.RUNTIME)
public @interface Demo1Annotation {

}

(2)创建自定义的 @Configuration 类,继承 WebMvcConfigurerAdapter 适配器。重写 addArgumentResolvers() 方法,在该方法中创建一个匿名 HandlerMethodArgumentResolver 类,实现该类的 supportsParameter() 和 resolveArgument() 方法。如下:

import org.springframework.context.annotation.Configuration;
import org.springframework.core.MethodParameter;
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 org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import java.util.List;

/**
 * 配置参数解析处理器
 * @author Administrator 2021/4/16 12:40
 * @version 1.0
 */
@Configuration
public class Demo1Handel extends WebMvcConfigurerAdapter {

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(new HandlerMethodArgumentResolver(){
            @Override
            public boolean supportsParameter(MethodParameter methodParameter) {
                if(methodParameter.hasParameterAnnotation(Demo1Annotation.class)) {
                    return true;
                }
                return false;
            }

            @Override
            public Object resolveArgument(MethodParameter methodParameter,
                    ModelAndViewContainer modelAndViewContainer,
                    NativeWebRequest nativeWebRequest,
                    WebDataBinderFactory webDataBinderFactory) throws Exception {
                if(methodParameter.hasParameterAnnotation(Demo1Annotation.class)) {
                    System.out.println("Demo1Annotation");
                }

                // 获取修饰的参数名称
                String paramName = methodParameter.getParameterName();

                // 获取被修饰参数接收的值
                String paramValue = nativeWebRequest.getParameter(paramName);
                System.out.println(paramName + " ==> " + paramValue);

                return paramValue;
            }
        });
    }
}

其中:

  • supportsParameter():方法用于判断给定的参数是否是我们想要的参数。可以通过 MethodParameter 的 hasParameterAnnotation() 方法判断给定的参数是否被自定义的注解 Demo1Annotation 修饰。如果被 Demo1Annotation 修饰,则返回 true;否则,返回 false;

  • resolveArgument():方法中依然可以使用 hasParameterAnnotation() 方法判断参数是否被自定义的注解 Demo1Annotation 修饰。如果是,我们就可以做一些业务处理(上面方法仅仅执行 System.out.println("Demo1Annotation") 语句)。

(3)定义一个控制器,在控制器的 demo() 方法中使用 @Demo1Annotation 注解修饰 msg 参数。如下:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 使用自定义的注解修饰接口参数
 * @author Administrator 2021/4/16 12:47
 * @version 1.0
 */
@RestController
@RequestMapping("/demo1")
public class Demo1Controller {

    @GetMapping("/demo")
    public String demo(@Demo1Annotation String msg, String version){
        return "From Server ==> msg=" + msg + ", version=" + version;
    }

}

示例2

自定义一个注解,注解提供 notNull(判空,如果为true,表示参数不能为空;如果为false,则表示允许参数为空)和 msg(当参数没有通过验证是抛出的错误信息)成员,然后使用该注解去验证接口参数是否为空。如下:

(1)自定义 Demo2Annotation 注解,如下:

import java.lang.annotation.*;

/**
 * 自定义注解,验证接口参数
 * @author Administrator 2021/4/16 12:37
 * @version 1.0
 */
// 允许将注解包含在 JavaDoc 文档中
@Documented
// 指定注解使用范围为参数
@Target(ElementType.PARAMETER)
// 指定注解会在 class 字节码中存在,在运行时可以通过反射获取
@Retention(RetentionPolicy.RUNTIME)
public @interface Demo2Annotation {

    /**
     * 验证参数不为空,默认为false
     * @return
     */
    boolean notNull() default false;

    /**
     * 指定错误提示信息
     * @return
     */
    String msg() default "";

}

(2)创建自定义的 @Configuration 类,继承 WebMvcConfigurerAdapter 适配器。重写 addArgumentResolvers() 方法,在该方法中创建一个匿名 HandlerMethodArgumentResolver 类,实现该类的 supportsParameter() 和 resolveArgument() 方法。如下:

import org.springframework.context.annotation.Configuration;
import org.springframework.core.MethodParameter;
import org.springframework.util.StringUtils;
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 org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import java.lang.reflect.AnnotatedElement;
import java.util.List;

/**
 * 配置参数解析处理器
 * @author Administrator 2021/4/16 12:40
 * @version 1.0
 */
@Configuration
public class Demo2Handel extends WebMvcConfigurerAdapter {

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(new HandlerMethodArgumentResolver(){
            @Override
            public boolean supportsParameter(MethodParameter methodParameter) {
                if(methodParameter.hasParameterAnnotation(Demo2Annotation.class)) {
                    return true;
                }
                return false;
            }

            @Override
            public Object resolveArgument(MethodParameter methodParameter,
                    ModelAndViewContainer modelAndViewContainer,
                    NativeWebRequest nativeWebRequest,
                    WebDataBinderFactory webDataBinderFactory) throws Exception {

                // 获取修饰的参数名称
                String paramName = methodParameter.getParameterName();

                // 获取被修饰参数接收的值
                String paramValue = nativeWebRequest.getParameter(paramName);
                System.out.println(paramName + " ==> " + paramValue);

                if(methodParameter.hasParameterAnnotation(Demo2Annotation.class)) {
                    System.out.println("Demo2Annotation");
                    System.out.println("开始校验参数:" + paramName);
                    Demo2Annotation annotation =
                            methodParameter.getParameterAnnotation(Demo2Annotation.class);
                    System.out.println("annotation=" + annotation);

                    boolean notNull = annotation.notNull();
                    if(notNull && StringUtils.isEmpty(paramValue)) {
                        String errorMsg = annotation.msg();
                        throw new IllegalArgumentException(errorMsg);
                    }
                }

                return paramValue;
            }
        });
    }
}

(3)定义控制器 Demo2Controller,在 demo() 方法中使用 @Demo2Annotation 注解来验证 msg 参数。如下:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 使用自定义的注解验证接口参数
 * @author Administrator 2021/4/16 12:47
 * @version 1.0
 */
@RestController
@RequestMapping("/demo2")
public class Demo2Controller {

    @GetMapping("/demo")
    public String demo(@Demo2Annotation(notNull = true, msg = "msg 不能为空") String msg,
            String version){
        return "From Server ==> msg=" + msg + ", version=" + version;
    }

}

注意:我们可以通过 NativeWebRequest 类的 getHeader() 方法获取 HTTP 请求头中的字段,例如:

// 权限校验
String token = nativeWebRequest.getHeader("token");
System.out.println("token=" + token);

上面代码获取 HTTP 头中的 token 字段,可以通过该种方式实现权限验证。

睡眠和休息丧失了时间,却取得了明天工作的精力。 —— 毛泽东
0 不喜欢
说说我的看法 -
全部评论(
没有评论
关于
本网站属于个人的非赢利性网站,转载的文章遵循原作者的版权声明,如果原文没有版权声明,请来信告知:hxstrive@outlook.com
公众号