注解原理与自定义注解
1、引入依赖:
<!-- https://mvnrepository.com/artifact/javax.validation/validation-api -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
2、定义注解@JachinLength
利用该注解验证某字符串属性长度必须为指定长度
package com.jachin.zhujie;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = LengthValidatorClass.class) // 指定验证类
public @interface JachinLength {
int length() default 11; // 允许字符串长度
String message() default "kkkkk"; // 自定义的错误提示信息
// 下面这两个必须有
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
下面做几点说明:
1、注解的定义有点像定义接口 interface
,但唯一不同的是前面需要加一个 @
符号
2、注解的成员变量只能使用基本类型、 String
或者 enum
枚举,比如 int
可以,但 Integer
这种包装类型就不行。
3、像上面 @Target
、 @Retention
这种加在注解定义上面的注解,称为 “元注解”,元注解就是专门用于给注解添加注解的注解
4、 @Target(xxx)
用来说明该自定义注解可以用在什么位置,比如:
ElementType.FIELD
:说明自定义的注解可以用于类的变量ElementType.METHOD
:说明自定义的注解可以用于类的方法ElementType.TYPE
:说明自定义的注解可以用于类本身、接口或enum
类型
5、 @Retention(xxx)
用来说明你自定义注解的生命周期,比如:
@Retention(RetentionPolicy.RUNTIME)
:表示注解可以一直保留到运行时,因此可以通过反射获取注解信息@Retention(RetentionPolicy.CLASS)
:表示注解被编译器编译进class
文件,但运行时会忽略@Retention(RetentionPolicy.SOURCE)
:表示注解仅在源文件中有效,编译时就会被忽略
所以声明周期从长到短分别为:RUNTIME > CLASS > SOURCE
6、@Constraint:指定验证属性的类。
-
message()指明了验证失败后返回的消息,此方法为@Constraint要求
-
groups()和payload()也为@Constraint要求,可默认为空,详细用途可以查看@Constraint文档
3、实现验证类LengthValidatorClass
package com.jachin.zhujie;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
/**
* @description:
* @Author: JachinDo
* @Date: 2020/03/09 11:55
*/
public class LengthValidatorClass implements ConstraintValidator<JachinLength, String> { // 第一个参数是注解类,第二个注解是作用的对象类型
private JachinLength length;
// 将注解提取出来,底层利用反射生成注解的代理类
@Override
public void initialize(JachinLength constraint) {
this.length = constraint;
}
// 验证逻辑
@Override
public boolean isValid(String obj, ConstraintValidatorContext context) {
return obj.length() != length.length() ? false : true;
}
}
4、实体类与使用controller处验证字段
实体类:
package com.jachin.zhujie;
import lombok.Data;
/**
* @description:
* @Author: JachinDo
* @Date: 2020/03/09 12:06
*/
@Data
public class Person {
// 之前定义时默认长度为11
@JachinLength(message = "长度错误!")
private String phone;
}
controller:
package com.jachin.zhujie;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
/**
* @description:
* @Author: JachinDo
* @Date: 2020/03/09 13:02
*/
@RestController
@RequestMapping("/anno")
public class TestZhujie {
@PostMapping("/phone")
public String print(@Valid Person person,
BindingResult bindingResult) throws Exception {
if (bindingResult.hasErrors()) {
System.out.println("错误 " + person);
throw new Exception(bindingResult.getFieldError().getDefaultMessage());
}
return person.toString();
}
}
结果:
当输入phone长度不为11时: