看源码的时候,看到注解,还是有些不理解的地方。于是打算从头学习注解
首先要回答的的第一个问题是注解的定位是什么?带着这个问题我去翻了官方写的指导书《The Java™ Tutorials》,在注释这一节的开始,有如下文字:
Annotations, a form of metadata, provide data about a program that is not part of the program itself. Annotations have no direct effect on the operation of the code they annotate. ---《The Java™ Tutorials》
注解是一种元数据,提供关于程序的信息,但并不是程序的有机组成,注解并不对被它修饰的代码直接起作用。
《The Java™ Tutorials》:
Information for the compiler — Annotations can be used by the compiler to detect errors or suppress warnings.
Compile-time and deployment-time processing — Software tools can process annotation information to generate code, XML files, and so forth.
Runtime processing — Some annotations are available to be examined at runtime.
@Controller是SpringMVC提供的注解,一个类打上@Controller注解后,成为一个控制器。
如下所示我定义了一个注解:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Inherited public @interface SimpleAnnotation { String name() default ""; }
从@interface来看,注解更像是一个接口(事实上它就是一个接口,后文会这一点进行详细阐释,现在我们先关注怎么用),@Rentention、@Target、@Document、@Inherited是Java提供的元注解,怎么理解为元注解呢?就像是建筑师的建房子用的砖头一样,这些元注解就是Java提供给我们的砖头。
我们将注解中的name理解为属性,String限制了赋值的类型,default意味你没给注解中的属性赋值的时候,此时该属性为默认值。
@Rentention定义注解在哪里可用,这个属性在赋值是必须为RetentionPolicy类型,RetentionPolicy是一个枚举类型,有三个值
@Target定义注解可以加在哪里(例如方法上、字段上),这个属性所赋的值必须为ElementType,ElementType也是一个枚举类型。
ANNOTATION_TYPE
当注解未指定Target值时,此注解可以使用任何元素之上。
该注解将会,Javadoc工具会将此注解标记元素的注解信息包含在javadoc中。默认,注解信息不会包含在Javadoc中。
@Repeatable(SimpleAnnotation.class) public @interface SimpleAnnotationDemo01 { String value(); }
这个SimpleAnnotation也是一个注解,其中的属性是一个SimpleAnnotationDemo01 的数组,相当于SimpleAnnotationDemo01 存储了SimpleAnnotation在一个地方多次使用的值。
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Inherited public @interface SimpleAnnotation { SimpleAnnotationDemo01[] value() ; }
public class TestAnnotation { private int name; public <@SimpleAnnotation(name="T type") T> T getName(T t) { return t; } }
带着这个疑问,我将注解打在类上,并执行编译后,通过java p -c(不知道这个命令,请去看我以前的文章: 两个java命令和一道看起来比较简单的面试题 ) 来看其反汇编之后的字节码,
示例代码:
@SimpleAnnotationDemo01(value = "01") @SimpleAnnotationDemo01(value = "01") public class TestAnnotation { public static void main(String[] args) { System.out.println("hello world"); } }
反汇编后的字节码:
public interface com.cxk.annotation.SimpleAnnotationDemo01 extends java.lang.annotation.Annotation { public abstract java.lang.String value(); }
所以这个注解本质上还是接口喽。那这个方法怎么理解啊? 我传进去值相当于调方法喽,可这是一个接口啊!
那你为啥不弄成属性啊,费这个劲,弄成方法,有些理解不了啊。
所以是动态代理喽,本篇的主题注解,有关动态代理,会专门有一篇文章来进行介绍。注解本质上是一种接口,在运行时,加入你的注解是能够保存到运行时,那么JVM就会帮你创建你的注解的实例。
那么你有什么证据呢?
首先设置一个JVM的虚拟机参数中可以帮助我们捕捉JDK动态代理类: -Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
代码如下:
@SimpleAnnotationDemo01(value = "01") @SimpleAnnotationDemo01(value = "01") public class TestAnnotation { public static void main(String[] args) throws NoSuchMethodException { Class<TestAnnotation> clzz = TestAnnotation.class; System.out.println(clzz.getAnnotations()); } }
InvocationHandler是一个接口,那么它的实例是哪一个呢?我们打断点来看下.
代码如下:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Documented @Inherited public @interface SimpleDemo01 { String name() default ""; }
原来是AnnotationInvocationHandler的实例啊.
参考书籍和博客如下: