美文网首页
Java注解之实战

Java注解之实战

作者: Good龙辉 | 来源:发表于2020-12-07 00:47 被阅读0次

    一、什么是注解

    • Java 注解是在 JDK5 时引入的新特性,注解(也被称为元数据)为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个阶段方便的使用这些数据。

    二、JDK常用注解、元注解

    常用注解如下

    • @Override: 表示注解修饰的方法必须满足重写的规则
    • @Deprecated: 表示成员过时,编译器可以在程序运行的时候获取到该注解
    • @SupressWarnings: 表示忽略编译器的警告
      常见的元注解:
    • @Target 可声明在哪些目标元素之前
     ElementType.PARAMETER               声明在参数上
     ElementType.METHOD                  声明在方法上
     ElementType.FIELD                   声明在字段上
     ElementType.TYPE                    声明在类,接口,枚举
     ElementType.CONSTRUCTOR             声明在构造函数
     ElementType.LOCAL_VARIABLE          局部变量
     ElementType.ANNOTATION_TYPE         注释类型声明
     ElementType.PACKAGE                 包声明
    
    • @Retention 注解类的生命周期,及作用在哪个阶段
    RetentionPolicy.RUNTIME   程序运行时期起作用
    RetentionPolicy.SOURCE    在原文件中有效,被编译器丢弃
    RetentionPolicy.CLASS     程序编译时期起作用
    
    • @Documented 生成文档的时候,会被写入到文档中

    三、如何使用注解

    那么注解是怎么在某个阶段被方便的使用呢,比如我们自定义一个注解,程序是如何使用的呢?其实注解不能自己起作用,需要程序员自身去针对注解写专门的逻辑,比如:日志切面针对日志注解进行扫描处理;登录注解使用拦截器去判断是否包含再处理登录逻辑。

    四、注解实战

    接下来介绍下注解在实际中的使用,本次介绍的demo是一个利用注解来实现动态参数校验功能。
    1、首先新建一个验证注解,用于切入点进行参数校验。

    @Target({ElementType.PARAMETER})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface ValidParam {
    }
    

    2、其次编写实现此注解的扫描,对含有此注解的方法参数进行校验。这里我们用到了SpringMVC中的参数解析器,RequestBody注解的操作类继承了AbstractMessageConverterMethodArgumentResolver,因此我们也使用此类实现RequestBody相同参数解析功能。

    @Slf4j
    public class ValidMethodArgumentResolver extends AbstractMessageConverterMethodArgumentResolver {
        private static Validator validator;
    
        static {    
            validator = Validation.buildDefaultValidatorFactory().getValidator();
        }
    
        public ValidMethodArgumentResolver(List<HttpMessageConverter<?>> converters, List<Object> requestResponseBodyAdvice){
            super(converters, requestResponseBodyAdvice);
        }
    
    
        public ValidMethodArgumentResolver(List<HttpMessageConverter<?>> converters) {
            super(converters);
        }
    
        @Override
        public boolean supportsParameter(MethodParameter methodParameter) {
            return methodParameter.hasParameterAnnotation(ValidParam.class);
        }
    
        @Override
        public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
            final Object object = readWithMessageConverters(nativeWebRequest, methodParameter, methodParameter.getParameterType());
            validateParam(object);
            return object;
        }
    
        /**
         * 参数校验
         * @param object
         */
        private void validateParam(Object object) {
            Set<ConstraintViolation<Object>> constraintViolations;
            if(object instanceof List){
                List list = (List)object;
                list.forEach(p -> validateParam(p));
            }else {
                constraintViolations = validator.validate(object);
                if (!constraintViolations.isEmpty()) {
                    ConstraintViolation<Object> constraint = (ConstraintViolation<Object>)constraintViolations.iterator().next();
                    throw new ParamException(GloabEnums.PARAM_ERROR.getCode(), constraint.getMessage());
                }
            }
        }
    }
    
    

    使用之后我们需要将此类配置到参数解析器中,及实现WebMvcConfigurer类中,具体代码如下:

     @Override
        public void addArgumentResolvers(List<HandlerMethodArgumentResolver> list) {
            List<HttpMessageConverter<?>> converters = new ArrayList<>();
            converters.add(new MappingJackson2HttpMessageConverter());
            list.add(new ValidMethodArgumentResolver(converters));
        }
    

    首先是实现HandlerMethodArgumentResolver方法的supportsParameter方法,用于判断是否包含自定义验证注解,其次包含则调用resolveArgument方法对参数进行处理验证;这里我们使用了validator验证工具包进行校验。
    3、接下来我们对入参实体进行校验验证,这里我们使用了NotB

    @Data
    public class UserDto {
        @NotBlank(message = "名字不能为空")
        private String name;
        @NotBlank(message = "电话号码为空")
        @Pattern(regexp = "^1(?:3\\d|4[4-9]|5[0-35-9]|6[67]|7[013-8]|8\\d|9\\d)\\d{8}$", message = "电话号码有误")
        private String phone;
        private int age;
    }
    @Slf4j
    @RestController
    @RequestMapping("valid")
    public class ValidController {
    
        @PostMapping("user")
        public R insertAddress(@ValidParam UserDto dto){
            log.info("参数验证成功,执行正常逻辑:{}", dto);
            return R.ok();
        }
    }
    

    调用该接口:


    image.png

    有人会问,当我们使用增、删、改需要使用同一个类,如用如上参数注解校验,则会引起新增的方法判断了修改方法的校验,我们不同的方法是需要对此类不同的字段进行校验,这不是要写多个类才能进行校验吗?
    既然有不同的需求,我们就有不同的方案来进行处理,这里我们引入group组这个概念,将字段划分组进行区分校验。

    public interface QueryGroup {}
    
    public @interface ValidParam {
        Class value() default QueryGroup.class;
    }
    
    @NotBlank(message = "名字不能为空", groups = QueryGroup.class)
        private String name;
    
    @PostMapping("user")
        public R insertAddress(@ValidParam(value = QueryGroup.class) UserDto dto){
            log.info("参数验证成功,执行正常逻辑:{}", dto);
            return R.ok();
        }
    
    /**
         * 参数校验
         * @param object
         */
        private void validateParam(Object object, Class<?>... groups) {
            Set<ConstraintViolation<Object>> constraintViolations;
            if(object instanceof List){
                List list = (List)object;
                list.forEach(p -> validateParam(p, groups));
            }else {
                constraintViolations = validator.validate(object, groups);
                if (!constraintViolations.isEmpty()) {
                    ConstraintViolation<Object> constraint = (ConstraintViolation<Object>)constraintViolations.iterator().next();
                    throw new ParamException(GloabEnums.PARAM_ERROR.getCode(), constraint.getMessage());
                }
            }
        }
    
    

    四、总结

    1、注解就是对代码的解释,可以把它类同于标签
    2、注解的定义只是interface前加了个@
    3、注解不会自己起作用,需要程序开发人员去开发对应的功能

    相关文章

      网友评论

          本文标题:Java注解之实战

          本文链接:https://www.haomeiwen.com/subject/fpuawktx.html