0%

Java注解

序言

事不凝滞,理贵变通。

写框架离不开Java注解,今天来总结一下。

Java元注解

四个:@Retention @Target @Docuement @Inherited

元注解,用来标注注解的注解。

@Retention:注解的保留位置         

​ @Retention(RetentionPolicy.SOURCE) //注解仅存在于源码中,在编译期就被丢掉了,不会被class包含。

​ @Retention(RetentionPolicy.CLASS) // 不会加载进JVM,注解会在class字节码文件中存在,但运行时无法获得。

​ @Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到。

这里如果@Retention未标注,默认是CLASS阶段

通常我们自定义的都是RUNTIME阶段,一定要标注。

@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) ///包

@Document:说明该注解将被包含在javadoc中

@Inherited:说明子类可以继承父类中的该注解。

​ @Inherited仅针对@Target(ElementType.TYPE)类型的注解有效,并且仅针对类的继承,对接口的继承无效。

Java注解的使用

注解可以附加在程序元素( 包、类、构造器、方法、成员变量、参数、局域变量 )上面,为其添加额外的辅助信息,可以通过反射机制访问这些数据。

写个小demo:

1
2
3
4
5
6
7
8
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Report {
boolean id() default false;
int type() default 0;
String level() default "info";
String value() default "";
}

本注解仅针对method,作用在runtime阶段。

使用该注解的方式应为:

1
2
@Report(id=xxx,type=xxx,level=xxx,value=xxx)
public void testMethod(...)

default的意义在于如果使用注解的时候未给参数赋值,可以采用default预先写好的值。

如果注解想在多个target使用:

1
2
3
4
5
6
7
8
9
10
11
@Retention(RetentionPolicy.RUNTIME)
@Target({
ElementType.METHOD,
ElementType.TYPE
})
public @interface Report {
boolean id() default false;
int type() default 0;
String level() default "info";
String value() default "";
}

自定义注解

必须要写:

  • @Target
  • @Retention

可定义多个参数和默认值,核心参数使用value名称。

应当设置@Retention(RetentionPolicy.RUNTIME)便于运行期读取该Annotation。

Java提供的使用反射API读取Annotation的方法包括:

判断某个注解是否存在于ClassFieldMethodConstructor

  • Class.isAnnotationPresent(Class)
  • Field.isAnnotationPresent(Class)
  • Method.isAnnotationPresent(Class)
  • Constructor.isAnnotationPresent(Class)

例如:

1
2
// 判断@Report是否存在于Person类:
Person.class.isAnnotationPresent(Report.class);

使用反射API读取Annotation:

  • Class.getAnnotation(Class)
  • Field.getAnnotation(Class)
  • Method.getAnnotation(Class)
  • Constructor.getAnnotation(Class)

例如:

1
2
3
4
// 获取Person定义的@Report注解:
Report report = Person.class.getAnnotation(Report.class);
int type = report.type();
String level = report.level();

demo:

1
2
3
4
5
6
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Range {
int min() default 0;
int max() default 255;
}

某个JavaBean:

1
2
3
4
5
6
7
public class Person {
@Range(min=1, max=20)
public String name;

@Range(max=10)
public String city;
}

编写一个Person实例的检查方法,它可以检查Person实例的String字段长度是否满足@Range的定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void check(Person person) throws Exception{
// 遍历所有Field:
for (Field field : person.getClass().getFields()) {
// 获取Field定义的@Range:
Range range = field.getAnnotation(Range.class);
// 如果@Range存在:
if (range != null) {
// 获取Field的值:
Object value = field.get(person);
// 如果值是String:
if (value instanceof String) {
String s = (String) value;
// 判断值是否满足@Range的min/max:
if (s.length() < range.min() || s.length() > range.max()) {
throw new IllegalArgumentException("Invalid field: " + field.getName());
}
}
}
}
}

参考:

liaoxuefeng

竹子