@Retention
@Retention注解用来表示注解保留的阶段(源码、字节码、运行时)
@Retention(RetentionPolicy.SOURCE) 注解仅存在于源码中,在class字节码文件中不包含
@Retention(RetentionPolicy.CLASS),默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得
@Retention(RetentionPolicy.RUNTIME),注解会在class字节码文件中存在,在运行时可以通过反射获取到
自定义注解只能使用RetentionPolicy.RUNTIME
@Retention(RetentionPolicy.RUNTIME)
public @interface FirstAnnotation {
}
@Target
@Target用来限定注解可以使用的范围,可以是类、方法、参数等
@Target(ElementType.TYPE) 作用接口、类、枚举、注解
@Target(ElementType.FIELD) 作用属性字段、枚举的常量
@Target(ElementType.METHOD) 作用方法
@Target(ElementType.PARAMETER) 作用方法参数
@Target(ElementType.CONSTRUCTOR) 作用构造函数
@Target(ElementType.LOCAL_VARIABLE)作用局部变量
@Target(ElementType.ANNOTATION_TYPE)作用于注解
@Target(ElementType.PACKAGE) 作用于包
@Target(ElementType.TYPE_PARAMETER) 作用于类型泛型
@Target(ElementType.TYPE_USE) 类型使用.可以用于标注任意类型除了 class
一般比较常用的是ElementType.TYPE
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FirstAnnotation {
}
@Documented
@Documented的作用是能够将注解中的元素包含到 Javadoc 中去
@Inherited
当一个类被@Inherited标记时,如果子类没有被其他注解标记,那么子类会继承父类的注解
@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FirstAnnotation {
}
@Repeatable
@Repeatable标记的注解表示可以同时标记一个对象多次
@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Repeatable(FirstAnnotation.class)
public @interface FirstAnnotation {
RepeatAnnotionTest[] value();
}
@RepeatAnnotionTest(value = "Java")
@RepeatAnnotionTest(value = "C++")
@RepeatAnnotionTest(value = "Python")
public class TestRepeat {
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Repeatable(FirstAnnotation.class)
public @interface RepeatAnnotionTest {
String value() default "";
}
实例
假如我们现在要写一个校验的注解,用来校验年龄不能低于多少岁
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ValidateAge {
int minAge() default 1;
}
public class TestValidateAge {
public static void main(String[] args){
TestValidateAge testValidateAge = new TestValidateAge();
testValidateAge.validateAge(15);
}
@ValidateAge(minAge = 20)
public void validateAge(int age){
System.out.println(processAnnotion(age));
}
//解析注解
private String processAnnotion(int age){
try {
//通过反射拿到对应的方法
Method validateAge = TestValidateAge.class.getDeclaredMethod("validateAge",int.class);
//判断方法上是否存在指定的注解
boolean isPresent = validateAge.isAnnotationPresent(ValidateAge.class);
if(isPresent){
//获取方法上的注解
ValidateAge annotation = validateAge.getAnnotation(ValidateAge.class);
//获取注解中属性的值
int minAge = annotation.minAge();
if(age < minAge ){
return "年龄不能低于" + minAge + "岁,校验失败";
} else {
return "你的年龄是" + age + "岁,校验通过";
}
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return "校验失败";
}
}
@RestController
@RequestMapping("/user")
@Validated //controller开启校验
public class TestController {
@PostMapping("/test/{id}")
public String test(
@PathVariable @Max(value = 10,message = "不可以超过10") Integer id, //@Max hibernate自带的校验 高版本的springboot好像没有 要自行导包
@RequestBody @Valid TestDTO testDTO) { //表示要校验对象类型的 和@Validated一样的效果
System.out.println(testDTO.getName());
TestDTO testDTO1 = TestDTO.builder().name("李四").age(19).build(); //开启了loombook的build模式 这样设置值比较简单 但是好像有坑 比如不能像平时那样new对象了
return "hello world";
}
}
@ExceptionHandler(MethodArgumentNotValidException.class) //参数校验不通过的异常
@ResponseBody
@ResponseStatus(code = HttpStatus.BAD_REQUEST) // 状态码400
public UnifyResponse handleBeanValidation(HttpServletRequest req, MethodArgumentNotValidException e) {
String requestUrl = req.getRequestURI(); //请求地址
String method = req.getMethod(); //请求方法
List<ObjectError> errors = e.getBindingResult().getAllErrors(); //获取拿到所有报错信息
String message = this.formatAllErrorMessages(errors); //拼起来 一起返回
return new UnifyResponse(10001, message,method + " " + requestUrl);
}
private String formatAllErrorMessages(List<ObjectError> errors) {
StringBuffer errorMsg = new StringBuffer();
errors.forEach(error ->
errorMsg.append(error.getDefaultMessage()).append(';')
);
return errorMsg.toString();
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Constraint(validatedBy = PasswordValidator.class)
public @interface PasswordEqual {
int min() default 4; //定义变量
int max() default 8; //定义变量
String message() default "passwords are not equal"; //定义变量
Class<?>[] groups() default {}; //好像是固定的 暂时不知道怎么用
Class<? extends Payload>[] payload() default {}; //暂时不知道哪来干嘛
}
@Target 元注解 定义了注解要用在什么上面 @Retention 元注解 定义了注解的保留阶段 @Constraint(validatedBy = PasswordValidator.class) 指向处理逻辑的类
public class PasswordValidator implements ConstraintValidator<PasswordEqual, TestDTO> {
private int min;
private int max;
@Override
public void initialize(PasswordEqual constraintAnnotation) { //初始化参数
this.min = constraintAnnotation.min();
this.max = constraintAnnotation.max();
}
@Override
public boolean isValid(TestDTO testDTO, ConstraintValidatorContext constraintValidatorContext) {
String password1 = testDTO.getPassword1();
String password2 = testDTO.getPassword2();
boolean match = password1.equals(password2); //处理逻辑 当两个新旧密码不一样的时候 返回false 表示校验不通过
return match;
}
}
@Data
@Builder
@PasswordEqual(message = "两次密码不相同") //传参
public class TestDTO {
private String name;
private Integer age;
private String password1;
private String password2;
}