Java自定义注解入门

2,137 阅读2分钟

1. Constraint注解

/**
 * Link between a constraint annotation and its constraint validation implementations.
 * <p/>
 * A given constraint annotation should be annotated by a <code>@Constraint</code>
 * annotation which refers to its list of constraint validation implementations.
 *
 * @author Emmanuel Bernard
 * @author Gavin King
 * @author Hardy Ferentschik
 */
@Documented
@Target({ ANNOTATION_TYPE })
@Retention(RUNTIME)
public @interface Constraint {
    /**
     * <code>ConstraintValidator</code> classes must reference distinct target types.
     * If two <code>ConstraintValidator</code> refer to the same type,
     * an exception will occur.
     *
     * @return array of ConstraintValidator classes implementing the constraint
     */
    public Class<? extends ConstraintValidator<?, ?>>[] validatedBy();
}
  • validatedBy中的类需要实现ConstraintValidator接口
  • Constraint只能用在注解上

2. 自定义注解满足条件

  • @Retention需要包含RUNTIME

     @Retention(RUNTIME)
    
  • 自定义注解需要定义 javax.validation.Constraint注解

     @Constraint(validatedBy = {IsMobileValidator.class})
    

3. 自定义注解需要定义的属性

  • message

    • message属性值是用来创建错误信息

      String message() default "{com.acme.constraint.MyConstraint.message}";
      
    • 每一个自定义注解需要包含message属性,类型为String

  • groups

    • 每个约束注释都必须定义一个groups元素,该元素指定与约束声明关联的处理组。

      Class<?>[] groups() default {};
      
    • 默认值必须是一个空数组

    • 如果未指定该参数,那么校验都属于javax.validation.groups.Default分组。

    • 组通常用于控制计算约束的顺序,或执行javabean部分状态的验证。

  • payload - 校验错误级别

    • 约束注释必须定义一个payload元素,该元素指定约束声明与之关联的有效负载。

      Class<? extends Payload>[] payload() default {};
      
    • 默认值必须是一个空数组

    • 每个可附加的有效载荷扩展Payload

      /**
       * Payload type that can be attached to a given
       * constraint declaration.
       * Payloads are typically used to carry on metadata information
       * consumed by a validation client.
       *
       * Use of payloads is not considered portable.
       *
       * @author Emmanuel Bernard
       * @author Gerhard Petracek
       */
      public interface Payload {
      }
      
    • payload通常由验证客户端用于将某些元数据信息与给定的约束声明关联起来。

    • payload不可移植

      package com.acme.severity;
      public class Severity {
          public static class Info implements Payload {};
          public static class Error implements Payload {};
      }
      public class Address {
          @NotNull(message="would be nice if we had one", payload=Severity.Info.class)
          public String getZipCode() {...}
          @NotNull(message="the city is mandatory", payload=Severity.Error.class)
          String getCity() {...}
      }
      

4. 自定义注解示例

// 自定义注解
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = {IsMobileValidator.class})
public @interface IsMobile {
    //允许为空的属性
    boolean required() default true;

    //如果校验不通过返回的提示信息
    String message() default "手机号码格式错误";

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

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

// 约束实现类
public class IsMobileValidator implements ConstraintValidator<IsMobile, String> {

    // 默认值_false,用于接收注解上自定义的 required
    private boolean required = false;

    public void init(IsMobile annotation) {
        required = annotation.required();
    }

    @Override
    public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {

        if (required) {
            return s != null && s.length() == 11;
        } else {
            if (StringUtils.isEmpty(s)) {
                return true;
            } else {
                return s != null && s.length() == 11;
            }
        }
    }
}

// 实体类
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@Builder
@Validated
@Data
public class People {

  @NotNull(message = "name不能为null", payload = Severity.Info.class)
  @NotEmpty(message = "name不能为空", payload = Severity.Info.class)
  private String name;
  private Integer age;

  @Email(message = "email必须是一个电子信箱地址")
  @NotNull(message = "email不能为空", payload = Severity.Error.class)
  private String email;

  @NotNull(message = "mobile不能为null", payload = Severity.Info.class)
  @NotEmpty(message = "mobile不能为空", payload = Severity.Info.class)
  @IsMobile
  private String mobile;

}

// 测试类
public class PeopleTest {

    @Test
    public void Test(){

       People data = new People()
                .setAge(1)
                .setName("")
                .setMobile("1")
                .setEmail("");

        Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
        Set<ConstraintViolation<People>> validates = validator.validate(data);

        System.out.println("=========== error info ===============");
        for (ConstraintViolation<People> violation : validates) {
            System.out.println(violation.getPropertyPath()+": "+ violation.getMessage());
        }
        System.out.println("=========== error info ===============");

        System.out.println("data: " + data.toString());

    }
}