Java
注解的解析有两种方式:反射和注释处理器(APT/JSR 269
)。
本文主要介绍通过反射来解析注解,它是在运行时解析的,所以反射要求注解必须是 @Retention(RetentionPolicy.RUNTIME)
类型的。在 .class
文件和 JVM
中都会保留注解信息,运行时才能通过反射来获取并解析注解。但是反射比较慢,所以需要考虑效率问题。
注解体系
所有的注解都是 Annotation
的子接口,类中所有的元素都是 AnnotatedElement
的实现。
源码分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public interface Annotation { boolean equals(Object obj); int hashCode(); String toString();
Class<? extends Annotation> annotationType(); }
public interface AnnotatedElement { default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {...} <T extends Annotation> T getAnnotation(Class<T> annotationClass); Annotation[] getAnnotations(); default <T extends Annotation> T[] getAnnotationsByType (Class<T> annotationClass) {...} default <T extends Annotation> T getDeclaredAnnotation (Class<T> annotationClass) {...} default <T extends Annotation> T[] getDeclaredAnnotationsByType (Class<T> annotationClass) {...} Annotation[] getDeclaredAnnotations(); }
|
类图结构
注解的本质
注解的位置只能放到类型前面,不能放到变量名,方法名之前。
注解是接口
注解使用关键字 @interface
来表示,实际上它也的确是接口。
1 2 3 4 5
| @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface BindView { int value(); }
|
BindView
用来注解字段,并且在运行时也保留了注解信息。反编译结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| xmt@server005:~/annotation$ javap -v -c BindView.class ... // 反编译发现注解实际就是一个接口文件,并继承了 Annotation public interface BindView extends java.lang.annotation.Annotation SourceFile: "BindView.java" RuntimeVisibleAnnotations: 0: #9(#4=e#10.#11) 1: #12(#4=[e#13.#14]) minor version: 0 major version: 51 flags: ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION Constant pool: ... { public abstract int value(); flags: ACC_PUBLIC, ACC_ABSTRACT }
|
反编译后发现所有的注解实际是接口,并且继承了 Annotation
。所以使用反射解析注解时,可以使用父接口 Annotation
来表示任意注解类型。
注解在 class
文件中
自定义注解如果标注为 CLASS, RUNTIME
,表示会在 class
文件中保留注解信息。
1 2 3 4
| public class TestAnnotation{ @BindView(2) public int i; }
|
测试类中使用 @BindView
注解整型变量 i
,反编译结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| xmt@server005:~/annotation$ javap -v -c TestAnnotation ... public class TestAnnotation SourceFile: "TestAnnotation.java" minor version: 0 major version: 51 flags: ACC_PUBLIC, ACC_SUPER Constant pool: ... #7 = Utf8 LBindView; #8 = Utf8 value #9 = Integer 2 ... { public int i; flags: ACC_PUBLIC RuntimeVisibleAnnotations: 0: #7(#8=I#9) ... }
|
反编译后发现,变量 i
中保留了一个 RuntimeVisibleAnnotations
标记。后续反射解析时,就是通过这个标记来获取相关注解的。
AnnotatedType
体系介绍
AnnotatedType
体系是 Java 8
新加的,是泛型 Type
体系对应的被注解类型。
AnnotatedType
既能获取被注解元素的类型,也能获取该元素上的注解数组。示例:@CustomAnnotation("value") CustomClass
AnnotatedType.getType
被注解元素的类型,即 CustomClass
。
AnnotatedElement.getAnnotations
该元素上的注解数组,即 {CustomAnnotation}
。
AnnotatedType
类型并不是一定会有注解,只是代表拥有被注解的能力。比如 int i
,AnnotatedType
仅仅代表整型 int
,而它并没有被注解。
源码分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| public interface AnnotatedType extends AnnotatedElement { public Type getType(); }
public interface AnnotatedTypeVariable extends AnnotatedType { AnnotatedType[] getAnnotatedBounds(); }
public interface AnnotatedArrayType extends AnnotatedType { AnnotatedType getAnnotatedGenericComponentType(); }
public interface AnnotatedWildcardType extends AnnotatedType { AnnotatedType[] getAnnotatedLowerBounds(); AnnotatedType[] getAnnotatedUpperBounds(); }
public interface AnnotatedParameterizedType extends AnnotatedType { AnnotatedType[] getAnnotatedActualTypeArguments(); }
|
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
|
public class TestGenericAnnotation< @AnyAnnotation("classTypeParameter") S extends Number & @AnyAnnotation("classTypeParameterBounds") Comparable, T extends Cloneable, U> extends @AnyAnnotation("extends") Thread implements @AnyAnnotation("implements1") Runnable, Cloneable, @AnyAnnotation("implements2") Serializable {
@AnyAnnotation("field") public boolean typeAnnotatedBoolean; public @AnyAnnotation("array4") boolean @AnyAnnotation("array1") [] @AnyAnnotation("array2") [] @AnyAnnotation("array3") [] typeAnnotatedArray;
public @AnyAnnotation("typeVariable") S s; public @AnyAnnotation("beforeType") int array[]; public @AnyAnnotation("map1") Map< @AnyAnnotation("map2") ? extends @AnyAnnotation("map3") String, @AnyAnnotation("map4") List<@AnyAnnotation("map5") Object>> typeAnnotatedMap; @AnyAnnotation("return") public <@AnyAnnotation("methodTypeParameter") U, V> Class<?> typeAnnotatedMethod( @AnyAnnotation("formalParameter") TestGenericAnnotation arg) throws @AnyAnnotation("throwException") ClassNotFoundException {
@AnyAnnotation("local_variable_type") int foo = 0; throw new ClassNotFoundException(); } }
|
反射解析注解
动态代理
反射解析注解都使用的是动态代理技术访问注解的键值对,解析类源码文件:AnnotationParser.java, AnnotationInvocationHandler.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class AnnotationParser {
// 解析注解,并将注解作为键值对返回 public static Map<Class<? extends Annotation>, Annotation> parseAnnotations(byte[] rawAnnotations, ConstantPool constPool , Class<?> container) {...}
// 最终都是通过 annotationForMap 来生成注解 // 使用了动态代理来访问原始注解 public static Annotation annotationForMap( final Class<? extends Annotation> type, final Map<String, Object> memberValues){ return AccessController.doPrivileged(new PrivilegedAction<Annotation>(){ public Annotation run() { return (Annotation) Proxy.newProxyInstance( type.getClassLoader(), new Class<?>[] { type }, new AnnotationInvocationHandler(type, memberValues)); }}); } ... }
|
测试代码打印反射获取的 Annotation
类类型:
1 2 3 4 5 6 7 8 9
| Annotation[] annotations = TestAnnotation.class.getDeclaredAnnotations(); for (Annotation annotation : annotations){ System.out.println(annotation.getClass()); }
class com.sun.proxy.$Proxy1 class com.sun.proxy.$Proxy3
|
从输出结果可以看出,反射获取的 Annotation
类类型都是动态代理生成新类的类名。
常规解析
被注解的的类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| @MetaAnnotation( author = "xmt", date = "2018.4.27", currentVersion = 2, reviewer = {"a", "b", "c"} ) @Roles(value = {@Role(name = "doctor"), @Role(name = "police")}) public class TestAnnotation {
@BindView(2) public String view; private int count;
@Creatable public TestAnnotation(){
}
public TestAnnotation(int i){ count = i; }
@Testable public void testJavaDoc(){ System.out.println("Annotation viewValue = " + view); }
private void testAnnotationMethod(){
} }
|
注解反射解析:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
| public class TestAnnotationParsingReflect { private Class<?> clazz = TestAnnotation.class; private Object testAnnotation = null; private void parsingClass(){ Annotation[] annotations = clazz.getDeclaredAnnotations(); for (Annotation annotation : annotations){ if (annotation instanceof MetaAnnotation){ MetaAnnotation metaAnnotation = (MetaAnnotation) annotation; System.out.println("author: " + metaAnnotation.author()); System.out.println("date: " + metaAnnotation.date()); System.out.println("currentVersion: " + metaAnnotation.currentVersion()); StringBuilder builder = new StringBuilder(); for (String s : metaAnnotation.reviewer()){ builder.append(s); builder.append(" "); } System.out.println("reviewer: " + builder.toString()); }else if (annotation instanceof Roles){ Roles roles = (Roles) annotation; for ( Role role : roles.value()){ System.out.println("name: " + role.name()); } } } }
private void parsingConstructor(){ Constructor[] constructors = clazz.getDeclaredConstructors(); for (Constructor constructor : constructors){ Annotation[] annotations = constructor.getDeclaredAnnotations(); for (Annotation annotation : annotations){ if (annotation instanceof Creatable){ try { testAnnotation = constructor.newInstance(null); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } } } }
private void parsingField(){ Field[] fields = clazz.getDeclaredFields(); for (Field field : fields){ Annotation[] annotations = field.getDeclaredAnnotations(); for (Annotation annotation : annotations){ if (annotation instanceof BindView){ if (testAnnotation != null){ int value = ((BindView) annotation).value(); try { field.set(testAnnotation, String.valueOf(value)); System.out.println("set testAnnotation." + field.getName() + " = " + value); } catch (IllegalAccessException e) { e.printStackTrace(); } } } } } } private void parsingMethod(){ Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods){ Annotation[] annotations = method.getDeclaredAnnotations(); for (Annotation annotation : annotations){ if (annotation instanceof Testable){ if (testAnnotation != null){ try { method.invoke(testAnnotation, null); System.out.println("invoke testAnnotation." + method.getName()); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } } } } }
public static void main(String[] args) { TestAnnotationParsingReflect reflect = new TestAnnotationParsingReflect(); reflect.parsingClass(); reflect.parsingConstructor(); reflect.parsingField(); reflect.parsingMethod(); } }
|
AnnotatedType
体系解析
AnnotatedType
是 Java 8
新增加强型注解,可以注解到任意代码位置,如下示例为解析 TestGenericAnnotation
类中的所有注解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
| public class TestGenericAnnotationParsingReflect { private Class<?> clazz = TestGenericAnnotation.class; private void parsingClass(){ TypeVariable<?>[] typeVariables = clazz.getTypeParameters(); if (typeVariables != null && typeVariables.length > 0) { for (int i = 0; i < typeVariables.length; i++) { Annotation[] annotations = typeVariables[i].getAnnotations(); for (Annotation annotation : annotations) { System.out.println("Annotation: " + annotation); }
AnnotatedType[] annotatedBounds = typeVariables[i].getAnnotatedBounds(); if (annotatedBounds != null && annotatedBounds.length > 0 ){ for (int j = 0; j < annotatedBounds.length; j++){ System.out.println("TypeVariable bounds: " + annotatedBounds[j].getType()); String boundName = annotatedBounds[j].getType().getTypeName(); if (!boundName.equals(Object.class.getTypeName())){ Annotation[] annotations1 = annotatedBounds[j].getAnnotations(); for (Annotation annotation : annotations1) { System.out.println("Annotation: " + annotation); } } } } } }
Class<?> superClass = clazz.getSuperclass(); if (superClass != null && superClass != Object.class){ System.out.println("Super Class: " + superClass.getSimpleName());
AnnotatedType annotatedSuperclass = clazz.getAnnotatedSuperclass(); if (annotatedSuperclass != null) { Annotation[] annotations = annotatedSuperclass.getAnnotations(); for (Annotation annotation : annotations) { System.out.println("Annotation: " + annotation); } } }
AnnotatedType[] annotatedTypes = clazz.getAnnotatedInterfaces(); if (annotatedTypes != null && annotatedTypes.length > 0) { for (int i = 0; i < annotatedTypes.length; i++) { String interfaceName = annotatedTypes[i].getType().getTypeName(); System.out.println("Implements interface: " + interfaceName); Annotation[] annotations =annotatedTypes[i].getAnnotations(); for (Annotation annotation : annotations){ System.out.println("Annotation: " + annotation); appendAnnotation(builder, annotation); } } } } private void recurseParsingAnnotatedType(AnnotatedType annotatedType){ System.out.println("recurseParsingAnnotatedType: " + annotatedType.getType()); Annotation[] annotations = annotatedType.getAnnotations(); for (Annotation annotation : annotations) { System.out.println("Annotation: " + annotation); }
if (annotatedType instanceof AnnotatedArrayType){ AnnotatedArrayType arrayType = (AnnotatedArrayType)annotatedType; AnnotatedType genericComponentType = arrayType.getAnnotatedGenericComponentType(); recurseParsingAnnotatedType(genericComponentType); } else if (annotatedType instanceof AnnotatedParameterizedType){ AnnotatedParameterizedType parameterizedType = (AnnotatedParameterizedType) annotatedType; AnnotatedType[] annotatedTypes = parameterizedType.getAnnotatedActualTypeArguments(); for (AnnotatedType type : annotatedTypes){ recurseParsingAnnotatedType(type); } } else if (annotatedType instanceof AnnotatedTypeVariable) { AnnotatedTypeVariable typeVariable = (AnnotatedTypeVariable) annotatedType; AnnotatedType[] typeBounds = typeVariable.getAnnotatedBounds(); for (AnnotatedType type : typeBounds){ recurseParsingAnnotatedType(type); } } else if (annotatedType instanceof AnnotatedWildcardType) { AnnotatedWildcardType wildcardType = (AnnotatedWildcardType) annotatedType; AnnotatedType[] lowerBounds = wildcardType.getAnnotatedLowerBounds(); for (AnnotatedType type : lowerBounds){ recurseParsingAnnotatedType(type); } AnnotatedType[] upperBounds = wildcardType.getAnnotatedUpperBounds(); for (AnnotatedType type : upperBounds){ recurseParsingAnnotatedType(type); } } } private void parsingFields(){ Field[] fields = clazz.getDeclaredFields(); for (Field field : fields){ System.out.println("Field: " + field.toGenericString()); AnnotatedType annotatedType = field.getAnnotatedType(); recurseParsingAnnotatedType(annotatedType); } } private void parsingMethods(){ Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods){ System.out.println("Method: " + method.toGenericString()); AnnotatedType returnType = method.getAnnotatedReturnType(); recurseParsingAnnotatedType(returnType);
AnnotatedType[] parameterTypes = method.getAnnotatedParameterTypes(); for (AnnotatedType parameterType : parameterTypes) { recurseParsingAnnotatedType(parameterType); }
AnnotatedType receiverType = method.getAnnotatedReceiverType(); recurseParsingAnnotatedType(receiverType);
AnnotatedType[] exceptionTypes = method.getAnnotatedExceptionTypes(); for (AnnotatedType exceptionType : exceptionTypes){ recurseParsingAnnotatedType(exceptionType); } } } public static void main(String[] args) { TestGenericAnnotationParsingReflect parsingReflect = new TestGenericAnnotationParsingReflect(); parsingReflect.parsingClass(); parsingReflect.parsingFields(); parsingReflect.parsingMethods(); } }
|
参考文档