[03] 处理注解:反射
1、AnnotatedElement接口
如果没有用来读取注解的方法和工作,那么注解也就不会比注释更有用处了。使用注解的过程中,很重要的一部分就是创建于使用注解处理器。Java SE5扩展了反射机制的API,以帮助程序员快速的构造自定义注解处理器。
Java用Annotation接口来代表程序元素前面的注解,该接口是所有Annotation类型的父接口。如在java.lang.reflect包中有一个接口AnnotatedElement,其中定义了一些注解相关的方法,如判断某元素是否标注了注解的方法:
- boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
- 这里的传参实际上就是传入 ”某个注解.class“
另外,Java在java.lang.reflect 包下新增了 AnnotatedElement 接口:
/**
* Represents an annotated element of the program currently running in this
* VM. This interface allows annotations to be read reflectively. All
* annotations returned by methods in this interface are immutable and
* serializable. It is permissible for the caller to modify the
* arrays returned by accessors for array-valued enum members; it will
* have no affect on the arrays returned to other callers.
*
* <p>If an annotation returned by a method in this interface contains
* (directly or indirectly) a {@link Class}-valued member referring to
* a class that is not accessible in this VM, attempting to read the class
* by calling the relevant Class-returning method on the returned annotation
* will result in a {@link TypeNotPresentException}.
*
* <p>Similarly, attempting to read an enum-valued member will result in
* a {@link EnumConstantNotPresentException} if the enum constant in the
* annotation is no longer present in the enum type.
*
* <p>Finally, Attempting to read a member whose definition has evolved
* incompatibly will result in a {@link
* java.lang.annotation.AnnotationTypeMismatchException} or an
* {@link java.lang.annotation.IncompleteAnnotationException}.
*
* @since 1.5
* @author Josh Bloch
*/
26
1
/**
2
* Represents an annotated element of the program currently running in this
3
* VM. This interface allows annotations to be read reflectively. All
4
* annotations returned by methods in this interface are immutable and
5
* serializable. It is permissible for the caller to modify the
6
* arrays returned by accessors for array-valued enum members; it will
7
* have no affect on the arrays returned to other callers.
8
*
9
* <p>If an annotation returned by a method in this interface contains
10
* (directly or indirectly) a {@link Class}-valued member referring to
11
* a class that is not accessible in this VM, attempting to read the class
12
* by calling the relevant Class-returning method on the returned annotation
13
* will result in a {@link TypeNotPresentException}.
14
*
15
* <p>Similarly, attempting to read an enum-valued member will result in
16
* a {@link EnumConstantNotPresentException} if the enum constant in the
17
* annotation is no longer present in the enum type.
18
*
19
* <p>Finally, Attempting to read a member whose definition has evolved
20
* incompatibly will result in a {@link
21
* java.lang.annotation.AnnotationTypeMismatchException} or an
22
* {@link java.lang.annotation.IncompleteAnnotationException}.
23
*
24
* @since 1.5
25
* @author Josh Bloch
26
*/
该接口代表程序中可以接受注解的程序元素,这句话怎么理解?我们从另一头倒着来理:
(1)Field、Method、Constructor
在反射中我们知道的像属性、方法、构造函数等封装的类,都继承了一个类,叫AccessibleObject;
(2)AccessibleObject (参考链接)
java.lang.reflect包中有类AccessibleObject,从类的注释可以看出,The AccessibleObject class is the base class for Field, Method and Constructor objects. It provides the ability to flag a reflected object as suppressing default Java language access control checks when it is used. 显然,它可以让一个反射对象去禁止Java语言的访问控制检测,从而能够调用对象的私有属性和方法。
这个类实现了一个接口,就是我们提到的AnnotatedElement了,实现该接口的类(Represents an annotated element of the program currently running in this VM. This interface allows annotations to be read reflectively.)即代表那些可注解的元素,同时可以利用反射机制读取注解。所以,它的几个主要实现类有:
- Class 类
- Constructor 构造器
- Field 类的成员变量
- Method 类的方法
- Package 类的包
注意:如Class、Package未继承AccessibleObject,毕竟这两个类不需要所谓的访问权限设置,但是它们是实现了AnnotatedElement接口的。
所以当我们通过反射获取某个类的AnnotatedElement对象之后,就可以调用AnnotatedElement接口定义好的几个方法:
- //判断该程序元素上是否包含指定类型的注解,存在返回true,否则返回false
- boolean isAnnotationPresent(Class<? extends Annotation> annotationClass);
- //返回该程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null
- <T extends Annotation> T getAnnotation(Class<T> annotationClass);
- //返回该程序元素上存在的所有注解
- Annotation[] getAnnotations();
- //返回直接存在于该元素上的所有注解(与该接口中其他方法不同,该方法将忽略继承的注解)
- //如果没有注解直接存在于此元素上,则返回长度为零的一个数组
- //该方法调用者可以随意修改返回的数组,不会对其他调用者返回的数组产生影响
- Annotation[] getDeclaredAnnotations();
2、AnnotatedElement使用示例
2.1 自定义注解
/**
* 水果名称注解
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitName {
String value() default "";
}
/**
* 水果颜色注解
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitColor {
/**
* 颜色枚举
*/
public enum Color {BULE, RED, GREEN}
/**
* 颜色属性
*/
public Color fruitColor() default Color.GREEN;
}
/**
* 水果供应商注解
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitProvider {
/**
* 供应商编号
*/
public int id() default -1;
/**
* 供应商名称
*/
public String name() default "";
/**
* 供应商地址
*/
public String address() default "";
}
54
1
/**
2
* 水果名称注解
3
*/
4
ElementType.FIELD) (
5
RetentionPolicy.RUNTIME) (
6
7
public @interface FruitName {
8
String value() default "";
9
}
10
11
12
/**
13
* 水果颜色注解
14
*/
15
ElementType.FIELD) (
16
RetentionPolicy.RUNTIME) (
17
18
public @interface FruitColor {
19
/**
20
* 颜色枚举
21
*/
22
public enum Color {BULE, RED, GREEN}
23
24
/**
25
* 颜色属性
26
*/
27
public Color fruitColor() default Color.GREEN;
28
}
29
30
31
/**
32
* 水果供应商注解
33
*/
34
ElementType.FIELD) (
35
RetentionPolicy.RUNTIME) (
36
37
public @interface FruitProvider {
38
39
/**
40
* 供应商编号
41
*/
42
public int id() default -1;
43
44
/**
45
* 供应商名称
46
*/
47
public String name() default "";
48
49
/**
50
* 供应商地址
51
*/
52
public String address() default "";
53
54
}
注意:当一个Annotation类型被定义为运行时(RUNTIME)的Annotation时,该注解才能是运行时可见,当class文件被装载时被保存在class文件中的Annotation才会被虚拟机读取。
2.2 使用自定义注解的类
/**
* 苹果类
*/
public class Apple {
/**
* 苹果名称
*/
@FruitName("红富士")
private String appleName;
/**
* 苹果颜色
*/
@FruitColor(fruitColor = FruitColor.Color.RED)
private String appleColor;
/**
* 苹果供应商
*/
@FruitProvider(id = 1, name = "陕西红富士集团", address = "陕西省西安市延安路89号红富士大厦")
private String appleProvider;
public String getAppleName() {
return appleName;
}
public void setAppleName(String appleName) {
this.appleName = appleName;
}
public String getAppleColor() {
return appleColor;
}
public void setAppleColor(String appleColor) {
this.appleColor = appleColor;
}
public String getAppleProvider() {
return appleProvider;
}
public void setAppleProvider(String appleProvider) {
this.appleProvider = appleProvider;
}
}
47
1
/**
2
* 苹果类
3
*/
4
public class Apple {
5
6
/**
7
* 苹果名称
8
*/
9
"红富士") (
10
private String appleName;
11
12
/**
13
* 苹果颜色
14
*/
15
fruitColor = FruitColor.Color.RED) (
16
private String appleColor;
17
18
/**
19
* 苹果供应商
20
*/
21
id = 1, name = "陕西红富士集团", address = "陕西省西安市延安路89号红富士大厦") (
22
private String appleProvider;
23
24
public String getAppleName() {
25
return appleName;
26
}
27
28
public void setAppleName(String appleName) {
29
this.appleName = appleName;
30
}
31
32
public String getAppleColor() {
33
return appleColor;
34
}
35
36
public void setAppleColor(String appleColor) {
37
this.appleColor = appleColor;
38
}
39
40
public String getAppleProvider() {
41
return appleProvider;
42
}
43
44
public void setAppleProvider(String appleProvider) {
45
this.appleProvider = appleProvider;
46
}
47
}
2.3 注解处理
public class FruitInfoUtil {
/**
* 获取水果信息
* @param clazz 类对象
*/
public static void getFruitInfo(Class<?> clazz) {
String strFruitName;
String strFruitColor;
String strFruitProvider;
//获取该类对象包含的所有属性对象
Field[] fields = clazz.getDeclaredFields();
//遍历属性对象
for (Field field : fields) {
//如果属性包含的注解是FruitName
if (field.isAnnotationPresent(FruitName.class)) {
FruitName fruitName = field.getAnnotation(FruitName.class);
strFruitName = "水果名称:" + fruitName.value();
System.out.println(strFruitName);
}
//如果属性包含的注解是FruitColor
else if (field.isAnnotationPresent(FruitColor.class)) {
FruitColor fruitColor = field.getAnnotation(FruitColor.class);
strFruitColor = "水果颜色:" + fruitColor.fruitColor().toString();
System.out.println(strFruitColor);
}
//如果属性包含的注解是FruitProvider
else if (field.isAnnotationPresent(FruitProvider.class)) {
FruitProvider fruitProvider = field.getAnnotation(FruitProvider.class);
strFruitProvider = "供应商编号:" + fruitProvider.id()
+ ";供应商名称:" + fruitProvider.name()
+ ";供应商地址:" + fruitProvider.address();
System.out.println(strFruitProvider);
}
}
}
}
41
1
public class FruitInfoUtil {
2
3
/**
4
* 获取水果信息
5
* @param clazz 类对象
6
*/
7
public static void getFruitInfo(Class<?> clazz) {
8
9
String strFruitName;
10
String strFruitColor;
11
String strFruitProvider;
12
13
//获取该类对象包含的所有属性对象
14
Field[] fields = clazz.getDeclaredFields();
15
16
//遍历属性对象
17
for (Field field : fields) {
18
//如果属性包含的注解是FruitName
19
if (field.isAnnotationPresent(FruitName.class)) {
20
FruitName fruitName = field.getAnnotation(FruitName.class);
21
strFruitName = "水果名称:" + fruitName.value();
22
System.out.println(strFruitName);
23
}
24
//如果属性包含的注解是FruitColor
25
else if (field.isAnnotationPresent(FruitColor.class)) {
26
FruitColor fruitColor = field.getAnnotation(FruitColor.class);
27
strFruitColor = "水果颜色:" + fruitColor.fruitColor().toString();
28
System.out.println(strFruitColor);
29
}
30
//如果属性包含的注解是FruitProvider
31
else if (field.isAnnotationPresent(FruitProvider.class)) {
32
FruitProvider fruitProvider = field.getAnnotation(FruitProvider.class);
33
strFruitProvider = "供应商编号:" + fruitProvider.id()
34
+ ";供应商名称:" + fruitProvider.name()
35
+ ";供应商地址:" + fruitProvider.address();
36
System.out.println(strFruitProvider);
37
}
38
}
39
}
40
41
}
2.4 测试和结果输出
public class TestAnnotation {
public static void main(String[] args) {
FruitInfoUtil.getFruitInfo(Apple.class);
}
}
//结果输出
水果名称:红富士
水果颜色:RED
供应商编号:1;供应商名称:陕西红富士集团;供应商地址:陕西省西安市延安路89号红富士大厦
x
1
public class TestAnnotation {
2
public static void main(String[] args) {
3
FruitInfoUtil.getFruitInfo(Apple.class);
4
}
5
}
6
7
//结果输出
8
水果名称:红富士
9
水果颜色:RED
10
供应商编号:1;供应商名称:陕西红富士集团;供应商地址:陕西省西安市延安路89号红富士大厦