API开发中经常会遇到一些对请求数据进行验证的情况,这时候如果使用注解就有两个好处,一是验证逻辑和业务逻辑分离,代码清晰,二是验证逻辑可以轻松复用,只需要在要验证的地方加上注解就可以。

Java提供了一些基本的验证注解,比如@NotNull@Size,但是更多情况下需要自定义验证逻辑,这时候就可以自己实现一个验证注解,方法很简单,仅需要两个东西:

  • 一个自定义的注解,并且指定验证器
  • 一个验证器的实现

自定义验证注解

考虑有一个API,接收一个Student对象,并希望对象里的age域的值是奇数,这时候就可以创建以下注解:

  1. @Target(ElementType.FIELD)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Constraint(validatedBy = AgeValidator.class)
  4. public @interface Odd {
  5. String message() default "Age Must Be Odd";
  6. Class<?>[] groups() default {};
  7. Class<? extends Payload>[] payload() default {};
  8. }

其中:

  • @Target指明这个注解要作用在什么地方,可以是对象、域、构造器等,因为要作用在age域上,因此这里选择FIELD
  • @Retention指明了注解的生命周期,可以有SOURCE(仅保存在源码中,会被编译器丢弃),CLASS(在class文件中可用,会被VM丢弃)以及RUNTIME(在运行期也被保留),这里选择了生命周期最长的RUNTIME
  • @Constraint是最关键的,它表示这个注解是一个验证注解,并且指定了一个实现验证逻辑的验证器
  • message()指明了验证失败后返回的消息,此方法为@Constraint要求
  • groups()payload()也为@Constraint要求,可默认为空,详细用途可以查看@Constraint文档

创建验证器

有了注解之后,就需要一个验证器来实现验证逻辑:

  1. public class AgeValidator implements ConstraintValidator<Odd,Integer> {
  2. @Override
  3. public void initialize(Odd constraintAnnotation) {
  4. }
  5. @Override
  6. public boolean isValid(Integer age, ConstraintValidatorContext constraintValidatorContext) {
  7. return age % 2 != 0;
  8. }
  9. }

其中:

  • 验证器有两个类型参数,第一个是所属的注解,第二个是注解作用地方的类型,这里因为作用在age上,因此这里用了Integer
  • initialize()可以在验证开始前调用注解里的方法,从而获取到一些注解里的参数,这里用不到
  • isValid()就是判断是否合法的地方

应用注解

注解和验证器创建好之后,就可以使用注解了:

  1. public class Student {
  2. @Odd
  3. private int age;
  4. private String name;
  5. public String getName() {
  6. return name;
  7. }
  8. public void setName(String name) {
  9. this.name = name;
  10. }
  11. public int getAge() {
  12. return age;
  13. }
  14. public void setAge(int age) {
  15. this.age = age;
  16. }
  17. }
  1. @RestController
  2. public class StudentResource {
  3. @PostMapping("/student")
  4. public String addStudent(@Valid @RequestBody Student student) {
  5. return "Student Created";
  6. }
  7. }

在需要启用验证的地方加上@Valid注解,这时候如果请求里的Student年龄不是奇数,就会得到一个400响应:

  1. {
  2. "timestamp": "2018-08-15T17:01:44.598+0000",
  3. "status": 400,
  4. "error": "Bad Request",
  5. "errors": [
  6. {
  7. "codes": [
  8. "Odd.student.age",
  9. "Odd.age",
  10. "Odd.int",
  11. "Odd"
  12. ],
  13. "arguments": [
  14. {
  15. "codes": [
  16. "student.age",
  17. "age"
  18. ],
  19. "arguments": null,
  20. "defaultMessage": "age",
  21. "code": "age"
  22. }
  23. ],
  24. "defaultMessage": "Age Must Be Odd",
  25. "objectName": "student",
  26. "field": "age",
  27. "rejectedValue": 12,
  28. "bindingFailure": false,
  29. "code": "Odd"
  30. }
  31. ],
  32. "message": "Validation failed for object='student'. Error count: 1",
  33. "path": "/student"
  34. }

也可以手动来处理错误,加上一个BindingResult来接收验证结果即可:

  1. @RestController
  2. public class StudentResource {
  3. @PostMapping("/student")
  4. public String addStudent(@Valid @RequestBody Student student, BindingResult validateResult) {
  5. if (validateResult.hasErrors()) {
  6. return validateResult.getAllErrors().get(0).getDefaultMessage();
  7. }
  8. return "Student Created";
  9. }
  10. }

这时候如果验证出错,便只会返回一个状态为200,内容为Age Must Be Odd的响应。

JAVA里自定义注解来进行数据验证的更多相关文章

  1. Java实现自定义注解开发

    Java实现自定义注解开发 一直都对注解开发挺好奇的,最近终于有时间自己实践了一把,记录一下 万一后期会用到呢 哈哈哈 首先我们了解一下自定义注解的标准示例,注解类使用 @interface 关键字修 ...

  2. Java Annotation自定义注解详解

    在开发过程中总能用到注解,但是从来没有自己定义过注解.最近赋闲在家,研究整理了一番,力求知其然知其所以然. 本文会尝试描述什么是注解,以及通过一个Demo来说明如何在程序中自定义注解.Demo没有实际 ...

  3. 反射+自定义注解---实现Excel数据列属性和JavaBean属性的自动映射

    简单粗暴,直奔主题.   需求:通过自定义注解和反射技术,将Excel文件中的数据自动映射到pojo类中,最终返回一个List<pojo>集合? 今天我只是通过一位使用者的身份来给各位分享 ...

  4. Java利用自定义注解、反射实现简单BaseDao

    在常见的ORM框架中,大都提供了使用注解方式来实现entity与数据库的映射,这里简单地使用自定义注解与反射来生成可执行的sql语句. 这是整体的目录结构,本来是为复习注解建立的项目^.^ 好的,首先 ...

  5. Java如何自定义注解

    本文主要是记录所学,以供后续参考.注解是Java 1.5引入的,Java自定义注解是通过运行时靠反射获取注解,注解相当于是一种嵌入在程序中的元数据,可以使用注解解析工具或编译器对其进行解析,也可以指定 ...

  6. Springboot--元注解及自定义注解(表单验证)

    本文简单说明一下元注解,然后对元注解中的@Retention做深入的讨论,在文章最后使用元注解写一个自定义注解来结尾. 一.结论: @Target:注解的作用目标 @Target(ElementTyp ...

  7. Java基于自定义注解的面向切面的实现

    目的:实现在任何想要切的地方添加一个注解就能实现面向切面编程 自定义注解类 @Target({ElementType.PARAMETER, ElementType.METHOD}) @Retentio ...

  8. java/springboot自定义注解实现AOP

    java注解 即是注释了,百度解释:也叫元数据.一种代码级别的说明. 个人理解:就是内容可以被代码理解的注释,一般是一个类. 元数据 也叫元注解,是放在被定义的一个注解类的前面 ,是对注解一种限制. ...

  9. [转] Java @interface 自定义注解

    [From] http://blog.csdn.net/afterlife_qiye/article/details/53748973 1. 注解的好处 注解可以替代配置文件完成对某些功能的描述,减少 ...

随机推荐

  1. tab 切换实现方法

    ul li 实现方法(只适用于,一个页面只有一组ul>li) <!--menu--> <div class="nav"> <ul class=& ...

  2. Appium+java 获取元素状态

    元素的属性我们经常会用到,当定位到某个元素后,有时会需要用到这个元素的text值.className.resource-id.checked等.  一般标准的属性我们都可以通过get_attribut ...

  3. spark查看DF的partition数目及每个partition中的数据量【集群模式】

    println("--------------------"+data.rdd.getNumPartitions) // 获取DF中partition的数目 val partiti ...

  4. linux下ssh通过公钥登录服务器

    经常会通过ssh登录远程服务器,一种是通过密码方式登录,一种是通过公钥登录. 如何设置通过公钥登录服务器 1. 首先生成自己的公钥和私钥 ssh-keygen 命令用来生成公钥和私钥 -t 用来指定密 ...

  5. 【Apache运维基础(6)】Apache的日志管理与分析

    简述 Apache 访问日志在实际工作中非常有用,比较典型的例子是进行网站流量统计,查看用户访问时间.地理位置分布.页面点击率等.Apache 的访问日志具有如下4个方面的作用: 记录访问服务器的远程 ...

  6. django 下载文件

    方法一. from django.http import StreamingHttpResponse def big_file_download(request): # do something... ...

  7. [转载]Windows 2003 R2 SP2 VOL 企业版(简体中文)

    Windows 2003 R2 SP2 VOL 企业版(简体中文) 要是这个的话,分享个电驴的下载连接吧(可以复制后用快车和迅雷直接下)32位版CD1:SHA1值:d0dd2782e9387328eb ...

  8. C# 枚举转列表

    using System; using System.Collections.Generic; using System.ComponentModel; namespace Common.Utils ...

  9. AI学习---基于TensorFlow的案例[实现线性回归的训练]

    线性回归原理复习 1)构建模型               |_> y = w1x1 + w2x2 + -- + wnxn + b        2)构造损失函数               | ...

  10. 修改Windows默认远程端口号

    1.定位注册表,[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\Wds\rdpwd\Tds\tcp],右侧修改 ...