概述
注解想必大家在项目中经常使用,比如Spring框架中常用的一些注解:@Controller、@Service、@RequestMapping等等,它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
注解的分类
根据注解的使用场景,主要分为三类,元注解、内置注解和自定义注解。
元注解
用于定义注解的注解,通常用于注解的定义上,标明该注解的使用范围、生效范围等。简而言之,元注解是用来修饰注解的。
@Retention
指定注解信息保留到哪个阶段,分别为源代码阶段、编译Class阶段、运行阶段。
@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.ANNOTATION_TYPE)public @interface Retention {/*** Returns the retention policy.* @return the retention policy*/RetentionPolicy value();}
- SOURCE: 保留在源代码java中,被编译器丢弃,也就是说在class文件中不包含注解信息,通常用来标记源码,引起大家的注意,比如自定义一个注解例如@ThreadSafe,用来标识一个类时线程安全的。
- CLASS:编译后的class文件中包含注解信息,但是会被jvm丢弃
- RUNTIME: 注解信息在运行期(JVM)保留(.class也有),可以通过反射机制读取注解的信息
@Target
指定注解的使用范围,如类、方法、属性、局部属性、参数等, 可以多选。 ```java @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target { /**- Returns an array of the kinds of elements an annotation type
- can be applied to.
- @return an array of the kinds of elements an annotation type
- can be applied to */ ElementType[] value(); }
具体可选的枚举如下:```javapublic enum ElementType {/** 适用范围:类、接口、注解类型,枚举类型enum */TYPE,/** 作用于类属性 (includes enum constants) */FIELD,/** 作用于方法 */METHOD,/** 作用于参数声明 */PARAMETER,/** 作用于构造函数声明 */CONSTRUCTOR,/** 作用于局部变量声明 */LOCAL_VARIABLE,/** 作用于注解声明 */ANNOTATION_TYPE,/** 作用于包声明 */PACKAGE,/** 作用于类型参数(泛型参数)声明 */TYPE_PARAMETER,/** 作用于使用类型的任意语句(不包括class) */TYPE_USE}
@Inherited
加上该注解的注解,表示可以被标注的类子类继承,比如A上标记了带有@Inherited的注解,那么类B继承了A, 那么B也会有这个注解,默认情况下注解是不支持继承的。
@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.ANNOTATION_TYPE)public @interface Inherited {}
@Document
将此注解包含在 javadoc 中 ,它代表着此注解会被javadoc工具提取成文档。
@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.ANNOTATION_TYPE)public @interface Documented {}
@Repeatable
1.8中加入的元注解,用来标记是否可以重复标记。
@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.ANNOTATION_TYPE)public @interface Repeatable {/*** Indicates the <em>containing annotation type</em> for the* repeatable annotation type.* @return the containing annotation type*/Class<? extends Annotation> value();}
内置注解
java提供了一些内置注解,可以配合编译器来检查代码的正确性, 我们可以关注他们的元注解。
@Override
标记当前方法是覆写父类的方法。
@Target(ElementType.METHOD)@Retention(RetentionPolicy.SOURCE)public @interface Override {}
@Deprecated
标记一个元素为已过期,不要在使用了
@Documented@Retention(RetentionPolicy.RUNTIME)@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})public @interface Deprecated {}
@SuppressWarnings
用来关闭编译器输出的警告信息
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})@Retention(RetentionPolicy.SOURCE)public @interface SuppressWarnings {String[] value();}
@FunctionalInterface
java8中引入,标记是一个函数式接口,也就是说有且只有一个抽象方法的接口
@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface FunctionalInterface {}
自定义注解
注解遵循的格式一般如下:
//元注解部分 xxxx@Retention(xxxx)@Target(xxxx)public @interface 注解名 {返回值 属性名() 默认值;返回值 属性名() 默认值;}
- 返回值支持的类型如下:java的8种基础类型(不支持包装类型)、String、Class、Enum、Annotation、以及上面类型的数组。
- 默认值可选,非必有。
举个项目中自定义的栗子:
@Target({ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documented@Inheritedpublic @interface DataAuthorize {/*** 资产ID* @return*/String assetId();/*** 资产类型* @return*/String assetType();/*** 权限代码* @return*/String authCode() default "";/*** 使用的类型* @return*/Class[] useType();}
使用反射操作注解
大部分情况下,我们的项目或者开源框架中都定义了大量的注解,而且都是@Retention(RetentionPolicy.RUNTIME)运行时阶段,我们可以通过反射获取注解中的信息,所以整体遵循下面的一个范式。
- 自定义注解
- 扫描注解
- 通过反射获取注解的信息,执行相应的逻辑。
下面我们重点使用下如何用反射来获取注解的信息。
定义target是注解的注解 ```java @Inherited @Retention( value = RetentionPolicy.RUNTIME) @Target(value = {ElementType.ANNOTATION_TYPE}) public @interface AnnoTest {
String value() default “anno”;
}
2. 定义一个几乎全量信息的注解```java@AnnoTest("alvinAnno")@Inherited@Retention( value = RetentionPolicy.RUNTIME)@Target(value = {ElementType.TYPE_USE,ElementType.PACKAGE,ElementType.FIELD,ElementType.TYPE_PARAMETER,ElementType.CONSTRUCTOR,ElementType.LOCAL_VARIABLE})@Documentedpublic @interface FullAnnoTest {String value() default "FullAnnoTest";}
- 定义测试类和反射代码 ```java @FullAnnoTest(“package”) package com.alvin.java.core.anno;
public class ParentObj { }
@FullAnnoTest(“testAnnoReflect”) public class TestAnnoReflect<@FullAnnoTest(“parameter”) T > extends @FullAnnoTest(“parent”)ParentObj {
@FullAnnoTest("constructor")TestAnnoReflect() {}//注解字段域private @FullAnnoTest("name") String name;//注解泛型字段域private @FullAnnoTest("value") T value;//注解通配符private @FullAnnoTest("list") List<@FullAnnoTest("generic") ?> list;//注解方法@FullAnnoTest("method") //注解方法参数public String hello(@FullAnnoTest("methodParameter") String name)throws @FullAnnoTest("Exception") Exception { // 注解抛出异常//注解局部变量,现在运行时暂时无法获取(忽略)@FullAnnoTest("result") String result;result = "siting";System.out.println(name);return result;}public static void main(String[] args) throws Exception {TestAnnoReflect<String> TestAnnoReflect = new TestAnnoReflect<> ();Class<TestAnnoReflect<Object>> clazz = (Class<TestAnnoReflect<Object>>) TestAnnoReflect.getClass();//class的注解Annotation[] annotations = clazz.getAnnotations();FullAnnoTest testTmp = (FullAnnoTest) annotations[0];System.out.println("修饰TestAnnoReflect.class注解value: "+testTmp.value());//构造器的注解Constructor<TestAnnoReflect<Object>> constructor = (Constructor<TestAnnoReflect<Object>>) clazz.getDeclaredConstructors()[0];testTmp = constructor.getAnnotation(FullAnnoTest.class);System.out.println("修饰构造器的注解value: "+testTmp.value());//继承父类的注解AnnotatedType annotatedType = clazz.getAnnotatedSuperclass();testTmp = annotatedType.getAnnotation(FullAnnoTest.class);System.out.println("修饰继承父类的注解value: "+testTmp.value());//注解的注解AnnoTest AnnoTest = testTmp.annotationType().getAnnotation(AnnoTest.class);System.out.println("修饰注解的注解AnnoTest-value: "+AnnoTest.value());//泛型参数 T 的注解TypeVariable<Class<TestAnnoReflect<Object>>> variable = clazz.getTypeParameters()[0];testTmp = variable.getAnnotation(FullAnnoTest.class);System.out.println("修饰泛型参数T注解value: "+testTmp.value());//普通字段域 的注解Field[] fields = clazz.getDeclaredFields();Field nameField = fields[0];testTmp = nameField.getAnnotation(FullAnnoTest.class);System.out.println("修饰普通字段域name注解value: "+testTmp.value());//泛型字段域 的注解Field valueField = fields[1];testTmp = valueField.getAnnotation(FullAnnoTest.class);System.out.println("修饰泛型字段T注解value: "+testTmp.value());//通配符字段域 的注解Field listField = fields[2];AnnotatedParameterizedType annotatedPType = (AnnotatedParameterizedType)listField.getAnnotatedType();testTmp = annotatedPType.getAnnotation(FullAnnoTest.class);System.out.println("修饰泛型注解value: "+testTmp.value());//通配符注解 的注解AnnotatedType[] annotatedTypes = annotatedPType.getAnnotatedActualTypeArguments();AnnotatedWildcardType annotatedWildcardType = (AnnotatedWildcardType) annotatedTypes[0];testTmp = annotatedWildcardType.getAnnotation(FullAnnoTest.class);System.out.println("修饰通配符注解value: "+testTmp.value());//方法的注解Method method = clazz.getDeclaredMethod("hello", String.class);annotatedType = method.getAnnotatedReturnType();testTmp = annotatedType.getAnnotation(FullAnnoTest.class);System.out.println("修饰方法的注解value: "+testTmp.value());//异常的注解annotatedTypes = method.getAnnotatedExceptionTypes();testTmp = annotatedTypes[0].getAnnotation(FullAnnoTest.class);System.out.println("修饰方法抛出错误的注解value: "+testTmp.value());//方法参数的注解annotatedTypes = method.getAnnotatedParameterTypes();testTmp = annotatedTypes[0].getAnnotation(FullAnnoTest.class);System.out.println("修饰方法参数注解value: "+testTmp.value());//包的注解Package p = Package.getPackage("com.alvin.java.core.anno");testTmp = p.getAnnotation(FullAnnoTest.class);System.out.println("修饰package注解value: "+testTmp.value());TestAnnoReflect.hello("hello");}
}
4. 查看对应的执行结果```java修饰TestAnnoReflect.class注解value: testAnnoReflect修饰构造器的注解value: constructor修饰继承父类的注解value: parent修饰注解的注解AnnoTest-value: alvinAnno修饰泛型参数T注解value: parameter修饰普通字段域name注解value: name修饰泛型字段T注解value: value修饰泛型注解value: list修饰通配符注解value: generic修饰方法的注解value: method修饰方法抛出错误的注解value: Exception修饰方法参数注解value: methodParameter修饰package注解value: packagehello
注解的本质和底层实现
大家有没有想过注解的本质是什么?
我们先通过反编译查看注解生成的字节码,可以通过javap -v FullAnnoTest.class查看如下:
可以看到,我们的注解是继承自Annotation接口。
public interface Annotation {boolean equals(Object obj);int hashCode();String toString();/*** Returns the annotation type of this annotation.* @return the annotation type of this annotation*/Class<? extends Annotation> annotationType();}
所以注解相当于一个语法糖一样,可以方便我们使用,本质上它是继承自Annotation的一个接口。
那大家有没有想过它的实现类在哪里?比如下面的代码,获取到注解,按照上面的解释,它是一个接口,那调用value()方法时,它具体调用的哪个实现类呢?我们并没有写实现类啊…..
答案当然就是动态代理生成的实现类。
AnnoTest annoTest = testTmp.annotationType().getAnnotation(AnnoTest.class);System.out.println("修饰注解的注解AnnoTest-value: "+annoTest.value());
我们可以在启动参数添加如下命令可以查看生成的代理类:-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
执行后,生成代理类如下,
代理大致的代码如下:
public final class $Proxy2 extends Proxy implements FullAnnoTest {private static Method m1;private static Method m2;private static Method m4;private static Method m0;private static Method m3;public $Proxy2(InvocationHandler var1) throws {super(var1);}public final boolean equals(Object var1) throws {try {return (Boolean)super.h.invoke(this, m1, new Object[]{var1});} catch (RuntimeException | Error var3) {throw var3;} catch (Throwable var4) {throw new UndeclaredThrowableException(var4);}}public final Class annotationType() throws {try {return (Class)super.h.invoke(this, m4, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}public final String value() throws {try {return (String)super.h.invoke(this, m3, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}static {try {m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));m2 = Class.forName("java.lang.Object").getMethod("toString");m4 = Class.forName("com.alvin.java.core.anno.FullAnnoTest").getMethod("annotationType");m0 = Class.forName("java.lang.Object").getMethod("hashCode");m3 = Class.forName("com.alvin.java.core.anno.FullAnnoTest").getMethod("value");} catch (NoSuchMethodException var2) {throw new NoSuchMethodError(var2.getMessage());} catch (ClassNotFoundException var3) {throw new NoClassDefFoundError(var3.getMessage());}}}
我们看value()方法,这里调用了super.h对象,也就是InvocationHandler对象,而我们注解用的是AnnotationInvocationHandler这个子类,我们在invoke方法中打个断点,就明白了~~
参考
https://www.cnblogs.com/ziph/p/13056092.html
https://blog.csdn.net/KingBoyWorld/article/details/105337011
https://segmentfault.com/a/1190000027073489
