示例
@Constraint(validatedBy = {AgeValidator.class})
@Documented
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Age {
String message() default "年龄是非法的,最大不能超过{max}岁,最小不能小于{min}岁";
int max() default 120;
int min() default 1;
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
注解相关说明
注解的实现部分
在自定义注解中,其实现部分只能定义一个东西:注解类型元素(annotation type element)
定义注解类型元素时需要注意如下几点:
访问修饰符必须为public,不写默认为public;
该元素的类型只能是基本数据类型、String、Class、枚举类型、注解类型(体现了注解的嵌套效果)以及上述类型的一位数组;
该元素的名称一般定义为名词,如果注解中只有一个元素,请把名字起为value(后面使用会带来便利操作);
()不是定义方法参数的地方,也不能在括号中定义任何参数,仅仅只是一个特殊的语法;
default代表默认值,值必须和第2点定义的类型一致;
如果没有默认值,代表后续使用注解时必须给该类型元素赋值。
常用的元注解
一个最最基本的注解定义就只包括了上面的两部分内容:1、注解的名字;2、注解包含的类型元素。但是,我们在使用JDK自带注解的时候发现,有些注解只能写在方法上面(比如@Override);有些却可以写在类的上面(比如@Deprecated)。当然除此以外还有很多细节性的定义,那么这些定义该如何做呢?接下来就该元注解出场了!
元注解:专门修饰注解的注解。它们都是为了更好的设计自定义注解的细节而专门设计的。我们为大家一个个来做介绍。
@Retention :保留、保持
用来说明该注解类的生命周期。它有以下三个参数:
RetentionPolicy.SOURCE : 注解只保留在源文件中
RetentionPolicy.CLASS : 注解保留在class文件中,在加载到JVM虚拟机时丢弃
RetentionPolicy.RUNTIME : 注解保留在程序运行期间,此时可以通过反射获得定义在某个类上的所有注解。
@Target : 目标、靶子
用来说明该注解可以被声明在那些元素之前。
ElementType.TYPE:说明该注解只能被声明在一个类前。
ElementType.FIELD:说明该注解只能被声明在一个类的字段前。
ElementType.METHOD:说明该注解只能被声明在一个类的方法前。
ElementType.PARAMETER:说明该注解只能被声明在一个方法参数前。
ElementType.CONSTRUCTOR:说明该注解只能声明在一个类的构造方法前。
ElementType.LOCAL_VARIABLE:说明该注解只能声明在一个局部变量前。
ElementType.ANNOTATION_TYPE:说明该注解只能声明在一个注解类型前。
ElementType.PACKAGE:说明该注解只能声明在一个包名前。
@ Documented
这个注解只是用来标注生成javadoc的时候是否会被记录。
@Constraint : 约束
来限定、校验自定义注解的类
public class AgeValidator implements ConstraintValidator<Age, Integer> {
private Integer max;
private Integer min;
public void initialize(Age age) {
this.max = age.max();
this.min = age.min();
}
public boolean isValid(Integer value, ConstraintValidatorContext context) {
return value < max && value > min;
}
}
反射操作获取注解
public class TestAnnotation {
public static void main(String[] args){
try {
//获取Student的Class对象
Class stuClass = Class.forName("pojos.Student");
//说明一下,这里形参不能写成Integer.class,应写为int.class
Method stuMethod = stuClass.getMethod("study",int.class);
if(stuMethod.isAnnotationPresent(CherryAnnotation.class)){
System.out.println("Student类上配置了CherryAnnotation注解!");
//获取该元素上指定类型的注解
CherryAnnotation cherryAnnotation = stuMethod.getAnnotation(CherryAnnotation.class);
System.out.println("name: " + cherryAnnotation.name() + ", age: " + cherryAnnotation.age()
+ ", score: " + cherryAnnotation.score()[0]);
}else{
System.out.println("Student类上没有配置CherryAnnotation注解!");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
//解释一下:
// 如果我们要获得的注解是配置在方法上的,那么我们要从Method对象上获取;
// 如果是配置在属性上,就需要从该属性对应的Field对象上去获取,
// 如果是配置在类型上,需要从Class对象上去获取。总之在谁身上,就从谁身上去获取!
// isAnnotationPresent(Class<? extends Annotation> annotationClass)方法是专门判断该元素上是否配置有某个指定的注解;
// getAnnotation(Class<A> annotationClass)方法是获取该元素上指定的注解。之后再调用该注解的注解类型元素方法就可以获得配置时的值数据;
// 反射对象上还有一个方法getAnnotations(),该方法可以获得该对象身上配置的所有的注解。它会返回给我们一个注解数组,需要注意的是该数组的类型是Annotation类型,这个Annotation是一个来自于java.lang.annotation包的接口。