springBoot使用自定义注解实现复杂校验

目录

    • 为什么使用?
    • 前置条件
    • 前置知识
      • 注解的保留策略
      • 注解的作用目标
      • 其他
    • 使用自定义注解
    • 我的实体类定义
      • 我的注解定义
      • 我的注解校验
      • 测试注解
    • 易错、注意点

为什么使用?

在业务开发中,比如开发一个用户系统,使用NotBliank,NotNull等原生注解只能校验某个参数是不是为空。在实际的service中我们可能要写很多冗余的代码量,你要写很多if else,比如邀请码是不是为空,是不是符合六位,用户昵称规范(只由数字或者只有字母组成)。这种写法当然也可以实现,但是代码量会很复杂,而且冗余。当然这些代码你可以放到utils封装成工具类方法,但是没有自定义注解来的优雅与简洁。

前置条件

springboot 2.6.x
pom依赖

        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-validationartifactId>
        dependency>

前置知识

注解的保留策略

  1. @Retention(RetentionPolicy.SOURCE)
    只存在源码中,即运行以后这个注解就没了。
    注:一开始我也觉得这个注解没用,既然运行就没了,那我写这个有什么用。其实我们lombok的@Data注解就是这个类型的,在运行以后,Data注解生成了get、set方法以后就功成身退没有了。springBoot使用自定义注解实现复杂校验_第1张图片

  2. @Retention(RetentionPolicy.CLASS)
    在字节码中存在这个注解,运行期间没有。
    应用场景:
    依靠这种类型的注解,在运行的时候生成一些代码,类似于Data注解,不过是更上一层的使用。

  3. @Retention(RetentionPolicy.RUNTIME)、
    运行期间仍然存在,运行的时候可以通过反射去获取

注解的作用目标

格式:@Target(xxxx.xxx)
ElementType.TYPE
接口,类,枚举注解
ElementType.FIELD
字段、枚举的常量
ElementType.METHOD
方法
还有很多,我主要就用到这些

其他

@Documented注解,我理解的就是生成帮助文档javadoc
@Inherited注解,自动继承注解

使用自定义注解

我的实体类定义

package com.example.demo.dao;

import com.example.demo.common.annotation.ArticleCheck;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import org.springframework.data.elasticsearch.annotations.Document;

import javax.persistence.Column;
import javax.persistence.Id;
import java.time.LocalDateTime;

/**
 * @descriptions: 文章实体
 * @author: zhangpeng
 * @date: 2022/10/20 14:35
 * @version: 1.0
 */
@Data
@Document(indexName = "article")
@ArticleCheck
public class Article {
    @Id
    private String id;

    @Column(columnDefinition="varchar(32) COMMENT '连接主表的article_id'")
    private String articleId;

    @Column(columnDefinition="varchar(32) COMMENT '作者用户open id'")
    private String openId;


    @Column(columnDefinition="varchar(191) COMMENT '文章标题'")
    private String title;


    @Column(columnDefinition="varchar(33) COMMENT '文章分类id'")
    private String categoryId;


    @Column(columnDefinition="int(2) COMMENT '匿名性,匿名1,公开0'")
    private int anonymity;

    @Column(columnDefinition="int(2) COMMENT '帖子状态(删除0,草稿1,发布2,隐藏3)默认草稿'")
    private int state;

    @Column(columnDefinition="longtext COMMENT '帖子内容'")
    private String content;

    @CreationTimestamp
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createdTime;

    @UpdateTimestamp
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime updatedTime;


    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime deletedTime;
}

我的注解定义

package com.example.demo.common.annotation;

import com.example.demo.common.annotation.validator.ArticleCheckValidator;
import com.example.demo.dao.Article;

import javax.validation.Constraint;
import java.lang.annotation.*;

/**
 * @descriptions: 文章类校验
 * @author: zhangpeng
 * @date: 2022/10/20 17:05
 * @version: 1.0
 */
@Target({ ElementType.TYPE, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy= ArticleCheckValidator.class)
public @interface ArticleCheck {
    // 注解未通过,打印message信息
    String message() default "article invalid";

    Class<?>[] groups() default { };

    Class<? extends Article>[] payload() default { };
}

我的注解校验

package com.example.demo.common.annotation.validator;

import com.example.demo.common.annotation.ArticleCheck;
import com.example.demo.dao.Article;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.lang.annotation.Annotation;

/**
 * @descriptions: 校验方法
 * @author: zhangpeng
 * @date: 2022/10/21 9:30
 * @version: 1.0
 */
public class ArticleCheckValidator implements ConstraintValidator<ArticleCheck, Article> {
    /**
     * 获取自定义中value属性的值
     * @param constraintAnnotation 注解传入值,这个不用管默认就行
     */
    @Override
    public void initialize(ArticleCheck constraintAnnotation) {
        ConstraintValidator.super.initialize(constraintAnnotation);
    }
    //

    /**
     * 自定义注解处理程序, 返回true则通过自定义注解的校验,
     * 返回false则是没有通过自定义注解的校验,并返回自定义注解中message的内容
     * @param article 注解中传入的article实体类对象
     * @param constraintValidatorContext 上下文,这个不用管
     * @return
     */
    @Override
    public boolean isValid(Article article, ConstraintValidatorContext constraintValidatorContext) {
        if (article.getArticleId()=="123"){
            return true;
        }
        return false;
    }
}

测试注解

这是controller层的代码

    @PostMapping("/test")
    public Result testArticle(@RequestBody @Validated Article article){
        System.out.println(article);
        return Result.ok(article.toString());
    }

易错、注意点

  1. 在controller层里面要是怕post方法,要加上@RequestBody。
  2. 你要想让你的类执行校验还有加上@Validated,只有有这个注解,类中的自定义注解才可以生效。

你可能感兴趣的:(spring,spring,boot,java,spring)