SpringBoot2 | 第二十六篇(四):组合注解

在项目中,有一些属性上的部分注解是一样的,所以就想到了组合,为了减少代码的重复。于是就是看了其它的校验注解以及部分源码,做了对比,瞎忙活了大半天,把组合给搞定了,下面是对组合注解的使用做了笔记。

[TOC]

环境/版本一览:

  • 开发工具:Intellij IDEA 2018.2.2
  • springboot: 2.1.0.RELEASE
  • jdk:1.8.0_171
  • maven:3.3.9

1、pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

2、annotation

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
package com.fatal.annotation;

import javax.validation.Constraint;
import javax.validation.Payload;
import javax.validation.ReportAsSingleViolation;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
* 适用于修饰数量(比如:金额,单位为分)
* @desc 值必须大于或等于 0
* @author Fatal
* @date 2019/8/12 0012 11:01
*/
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Documented
@NotNull
@Min(0)
@Constraint(validatedBy = { })
@ReportAsSingleViolation
public @interface Amount {

String message() default "不能为空且不能小于0";

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

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

}

属性:message、groups、payload这三个是属于校验注解的基础属性,必不可少

注解:

  • @Constraint(validatedBy = { })

    指定 ConstraintValidator 集合作为校验实现,这个在源码中,可以参考 org.hibernate.validator.internal.metadata.core.ConstraintHelper.getAllValidatorDescriptors(Class<A> annotationType)方法,该方法用于获取指定校验注解的所有 ConstraintValidator

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    /**
    * Returns the constraint validator classes for the given constraint
    * annotation type, as retrieved from
    *
    * <ul>
    * <li>{@link Constraint#validatedBy()},
    * <li>internally registered validators for built-in constraints</li>
    * <li>XML configuration and</li>
    * <li>programmatically registered validators (see
    * {@link org.hibernate.validator.cfg.ConstraintMapping#constraintDefinition(Class)}).</li>
    * </ul>
    *
    * The result is cached internally.
    *
    * @param annotationType The constraint annotation type.
    * @param <A> the type of the annotation
    *
    * @return The validator classes for the given type.
    */
    public <A extends Annotation> List<ConstraintValidatorDescriptor<A>> getAllValidatorDescriptors(Class<A> annotationType) {
    Contracts.assertNotNull( annotationType, MESSAGES.classCannotBeNull() );
    return validatorDescriptors.computeIfAbsent( annotationType, a -> getDefaultValidatorDescriptors( a ) );
    }
  • @ReportAsSingleViolation

    如果任何组合的注解失败(这里指@NotNull、@Min(0)成功拦截),那么承载组合的注解(这里指 @Amount)将返回错误信息提示,忽略任意组合的注解的错误提示(这里指忽略@NotNull、@Min(0)它们的message)。如果还是不理解,那么你把这个注解注释之后测下即可

3、entity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.fatal.entity;

import com.fatal.annotation.Amount;
import lombok.Data;

/**
* @author Fatal
* @date 2019/8/12 0012 10:52
*/
@Data
public class Book {

private Long id;

/**
* 价格(单位:分)
*/
@Amount(message = "price 不能为null且不能小于0")
private Integer price;

}

4、controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.fatal.controller;

import com.fatal.entity.Book;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* @author Fatal
* @date 2019/8/12 0012 10:50
*/
@RestController
public class FatalController {

@PostMapping
public String combination(@Validated Book book, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return bindingResult.getFieldError().getDefaultMessage();
}
return "success";
}

}

5、测试

启动项目

访问 http://localhost:8080 带参数 price: -1

1565600721855

参考资料

校验注解源码 @Range

总结

SpringBoot的知识已经有前辈在我们之前探索了。比较喜欢的博主有:唐亚峰 | Battcn方志朋的专栏程序猿DD纯洁的微笑。对这门技术感兴趣的可以去他们的博客逛逛。谢谢他们的分享~~

以上文章是我用来学习的Demo,都是基于 SpringBoot2.x 版本。

源码地址: https://github.com/ynfatal/springboot2-learning/tree/master/chapter26_4