概述
注解想必大家在项目中经常使用,比如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
指定注解的使用范围,如类、方法、属性、局部属性、参数等, 可以多选。
@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(); }
具体可选的枚举如下:
public 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 @Inherited public @interface DataAuthorize { /** * 资产ID * @return */ String assetId(); /** * 资产类型 * @return */ String assetType(); /** * 权限代码 * @return */ String authCode() default ""; /** * 使用的类型 * @return */ Class[] useType(); }
使用反射操作注解
大部分情况下,我们的项目或者开源框架中都定义了大量的注解,而且都是@Retention(RetentionPolicy.RUNTIME)
运行时阶段,我们可以通过反射获取注解中的信息,所以整体遵循下面的一个范式。
- 自定义注解
- 扫描注解
- 通过反射获取注解的信息,执行相应的逻辑。
下面我们重点使用下如何用反射来获取注解的信息。
- 定义target是注解的注解
@Inherited @Retention( value = RetentionPolicy.RUNTIME) @Target(value = {ElementType.ANNOTATION_TYPE}) public @interface AnnoTest { String value() default "anno"; }
- 定义一个几乎全量信息的注解
@AnnoTest("alvinAnno") @Inherited @Retention( value = RetentionPolicy.RUNTIME) @Target(value = {ElementType.TYPE_USE,ElementType.PACKAGE,ElementType.FIELD, ElementType.TYPE_PARAMETER,ElementType.CONSTRUCTOR,ElementType.LOCAL_VARIABLE}) @Documented public @interface FullAnnoTest { String value() default "FullAnnoTest"; }
- 定义测试类和反射代码
@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"); } }
- 查看对应的执行结果
修饰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: package hello
注解的本质和底层实现
大家有没有想过注解的本质是什么?
我们先通过反编译查看注解生成的字节码,可以通过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方法中打个断点,就明白了~~