Java注解Annotation

從JDK5開始朗若,Java增加了Annotation(注解),Annotation是代碼里的特殊標記昌罩,這些標記可以在編譯哭懈、類加載、運行時被讀取茎用,并執(zhí)行相應的處理遣总。通過使用Annotation,開發(fā)人員可以在不改變原有邏輯的情況下轨功,在源文件中嵌入一些補充的信息旭斥。代碼分析工具、開發(fā)工具和部署工具可以通過這些補充信息進行驗證古涧、處理或者進行部署垂券。

Annotation提供了一種為程序元素(包、類羡滑、構造器菇爪、方法、成員變量柒昏、參數(shù)凳宙、局域變量)設置元數(shù)據(jù)的方法。Annotation不能運行职祷,它只有成員變量氏涩,沒有方法。Annotation跟public有梆、final等修飾符的地位一樣是尖,都是程序元素的一部分,Annotation不能作為一個程序元素使用泥耀。

1 定義Annotation

定義新的Annotation類型使用@interface關鍵字(在原有interface關鍵字前增加@符號)饺汹。定義一個新的Annotation類型與定義一個接口很像,例如:

public @interface Test{
}

定義完該Annotation后爆袍,就可以在程序中使用該Annotation首繁。使用Annotation,非常類似于public陨囊、final這樣的修飾符弦疮,通常,會把Annotation另放一行蜘醋,并且放在所有修飾符之前胁塞。例如:

@Test
public class MyClass{
....
}

1.1 成員變量
Annotation只有成員變量,沒有方法压语。Annotation的成員變量在Annotation定義中以“無形參的方法”形式來聲明啸罢,其方法名定義了該成員變量的名字,其返回值定義了該成員變量的類型胎食。例如:

public @interface MyTag{
    string name();
    int age();
}

示例中定義了2個成員變量扰才,這2個成員變量以方法的形式來定義。
一旦在Annotation里定義了成員變量后厕怜,使用該Annotation時就應該為該Annotation的成員變量指定值衩匣。例如:

public class Test{
    @MyTag(name="紅薯",age=30)
    public void info(){
    ......
    }
}

也可以在定義Annotation的成員變量時粥航,為其指定默認值琅捏,指定成員變量默認值使用default關鍵字。示例:

public @interface MyTag{
    string name() default "我蘭";
    int age() default 18;
}

如果Annotation的成員變量已經(jīng)指定了默認值递雀,使用該Annotation時可以不為這些成員變量指定值柄延,而是直接使用默認值。例如:

public class Test{
    @MyTag
    public void info(){
    ......
    }
}

根據(jù)Annotation是否包含成員變量缀程,可以把Annotation分為如下兩類:

  • 標記Annotation:沒有成員變量的Annotation被稱為標記搜吧。這種Annotation僅用自身的存在與否來為我們提供信息,例如@override等杠输。

  • 元數(shù)據(jù)Annotation:包含成員變量的Annotation赎败。因為它們可以接受更多的元數(shù)據(jù),因此被稱為元數(shù)據(jù)Annotation蠢甲。

1.2 元注解

在定義Annotation時僵刮,也可以使用JDK提供的元注解來修飾Annotation定義。JDK提供了如下4個元注解(注解的注解鹦牛,不是上述的”元數(shù)據(jù)Annotation“):

  • @Retention

  • @Target

  • @Documented

  • @Inherited
    1.2.1 @Retention

@Retention用于指定Annotation可以保留多長時間搞糕。

@Retention包含一個名為“value”的成員變量,該value成員變量是RetentionPolicy枚舉類型曼追。使用@Retention時窍仰,必須為其value指定值。value成員變量的值只能是如下3個:

  • RetentionPolicy.SOURCE:Annotation只保留在源代碼中礼殊,編譯器編譯時驹吮,直接丟棄這種Annotation针史。

  • RetentionPolicy.CLASS:編譯器把Annotation記錄在class文件中。當運行Java程序時碟狞,JVM中不再保留該Annotation啄枕。

  • RetentionPolicy.RUNTIME:編譯器把Annotation記錄在class文件中。當運行Java程序時族沃,JVM會保留該Annotation频祝,程序可以通過反射獲取該Annotation的信息。

示例:

package com.demo1;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

//name=value形式
//@Retention(value=RetentionPolicy.RUNTIME)

//直接指定
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTag{
    String name() default "我蘭";
}

如果Annotation里有一個名為“value“的成員變量脆淹,使用該Annotation時常空,可以直接使用XXX(val)形式為value成員變量賦值,無須使用name=val形式盖溺。
1.2.2 @Target

@Target指定Annotation用于修飾哪些程序元素漓糙。@Target也包含一個名為”value“的成員變量,該value成員變量類型為ElementType[ ]烘嘱,ElementType為枚舉類型兼蜈,值有如下幾個:

ElementType.TYPE:能修飾類、接口或枚舉類型

ElementType.FIELD:能修飾成員變量

ElementType.METHOD:能修飾方法

ElementType.PARAMETER:能修飾參數(shù)

ElementType.CONSTRUCTOR:能修飾構造器

ElementType.LOCAL_VARIABLE:能修飾局部變量

ElementType.ANNOTATION_TYPE:能修飾注解

ElementType.PACKAGE:能修飾包

示例1(單個ElementType):

package com.demo1;

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
public @interface AnnTest {
    String name() default "sunchp";
}

示例2(多個ElementType):

package com.demo1;

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target({ ElementType.FIELD, ElementType.METHOD })
public @interface AnnTest {
    String name() default "sunchp";
}

1.2.3 @Documented

如果定義注解A時拙友,使用了@Documented修飾定義为狸,則在用javadoc命令生成API文檔后,所有使用注解A修飾的程序元素遗契,將會包含注解A的說明辐棒。

示例:

@Documented
public @interface Testable {
}
public class Test {
    @Testable
    public void info() {
    }
}

1.2.4 @Inherited

@Inherited指定Annotation具有繼承性。

示例:

package com.demo2;

import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface MyTag{

}
package com.demo2;

@MyTag
public class Base {

}
package com.demo2;

//SubClass只是繼承了Base類
//并未直接使用@MyTag注解修飾
public class SubClass extends Base {
    public static void main(String[] args) {
        System.out.println(SubClass.class.isAnnotationPresent(MyTag.class));
    }
}

示例中Base使用@MyTag修飾牍蜂,SubClass繼承Base漾根,而且沒有直接使用@MyTag修飾,但是因為MyTag定義時鲫竞,使用了@Inherited修飾辐怕,具有了繼承性,所以運行結果為true从绘。

如果MyTag注解沒有被@Inherited修飾寄疏,則運行結果為:false。
1.3 基本Annotation

JDK默認提供了如下幾個基本Annotation:

  • **@Override **

限定重寫父類方法僵井。對于子類中被@Override 修飾的方法陕截,如果存在對應的被重寫的父類方法,則正確批什;如果不存在农曲,則報錯。@Override 只能作用于方法驻债,不能作用于其他程序元素乳规。

  • @Deprecated

用于表示某個程序元素(類形葬、方法等)已過時。如果使用被@Deprecated修飾的類或方法等暮的,編譯器會發(fā)出警告荷并。

  • @SuppressWarning

抑制編譯器警告。指示被@SuppressWarning修飾的程序元素(以及該程序元素中的所有子元素青扔,例如類以及該類中的方法.....)取消顯示指定的編譯器警告。例如翩伪,常見的@SuppressWarning(value="unchecked")

  • @SafeVarargs

@SafeVarargs是JDK 7 專門為抑制“堆污染”警告提供的微猖。

2 提取Annotation信息(反射)

當開發(fā)者使用了Annotation修飾了類、方法缘屹、Field等成員之后凛剥,這些Annotation不會自己生效,必須由開發(fā)者提供相應的代碼來提取并處理Annotation信息轻姿。這些處理提取和處理Annotation的代碼統(tǒng)稱為APT(Annotation Processing Tool)犁珠。

JDK主要提供了兩個類,來完成Annotation的提然チ痢:

  • java.lang.annotation.Annotation接口:這個接口是所有Annotation類型的父接口(后面會分析Annotation的本質(zhì)犁享,Annotation本質(zhì)是接口,而java.lang.annotation.Annotation接口是這些接口的父接口)豹休。

  • java.lang.reflect.AnnotatedElement接口:該接口代表程序中可以被注解的程序元素炊昆。

2.1 java.lang.annotation.Annotation

java.lang.annotation.Annotation接口源碼:

<pre class="brush:java;toolbar: true; auto-links: false;" style="box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; display: block; padding: 6px; margin: 0px 0px 10px; line-height: 1.45; color: rgb(51, 51, 51); word-break: break-all; word-wrap: break-word; background-color: rgb(246, 246, 246); border: none; border-radius: 3px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">package java.lang.annotation;

public interface Annotation {

boolean equals(Object obj);

int hashCode();

String toString();

Class<? extends Annotation> annotationType();

}</pre>

java.lang.annotation.Annotation接口的主要方法是annotationType( ),用于返回該注解的java.lang.Class威根。

2.2 java.lang.reflect.AnnotatedElement

java.lang.reflect.AnnotatedElement接口源碼:

<pre class="brush:java;toolbar: true; auto-links: false;" style="box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; display: block; padding: 6px; margin: 0px 0px 10px; line-height: 1.45; color: rgb(51, 51, 51); word-break: break-all; word-wrap: break-word; background-color: rgb(246, 246, 246); border: none; border-radius: 3px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">package java.lang.reflect;

import java.lang.annotation.Annotation;

public interface AnnotatedElement {

 boolean isAnnotationPresent(Class<? extends Annotation> annotationClass);

<T extends Annotation> T getAnnotation(Class<T> annotationClass);

Annotation[] getAnnotations();

Annotation[] getDeclaredAnnotations();

}</pre>

主要方法有:

  • isAnnotationPresent(Class<? extends Annotation> annotationClass):判斷該程序元素上是否存在指定類型的注解凤巨,如果存在則返回true,否則返回false洛搀。

  • getAnnotation(Class<T> annotationClass):返回該程序元素上存在的指定類型的注解敢茁,如果該類型的注解不存在,則返回null

  • Annotation[] getAnnotations():返回該程序元素上存在的所有注解留美。

java.lang.reflect.AnnotatedElement接口是所有程序元素(例如java.lang.Class彰檬、java.lang.reflect.Method、java.lang.reflect.Constructor等)的父接口谎砾。類圖結構如下:

[圖片上傳失敗...(image-fd9a37-1525656253127)]

所以程序通過反射獲取了某個類的AnnotatedElement對象(例如僧叉,A類method1()方法的java.lang.reflect.Method對象)后,就可以調(diào)用該對象的isAnnotationPresent( )棺榔、getAnnotation( )等方法來訪問注解信息瓶堕。

為了獲取注解信息,必須使用反射知識症歇。

PS:如果想要在運行時提取注解信息郎笆,在定義注解時谭梗,該注解必須使用@Retention(RetentionPolicy.RUNTIME)修飾。

2.3 示例

2.3.1 標記Annotation

給定一個類的全額限定名宛蚓,加載類激捏,并列出該類中被注解@MyTag修飾的方法和沒被修飾的方法。

注解定義:

<pre class="brush:java;toolbar: true; auto-links: false;" style="box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; display: block; padding: 6px; margin: 0px 0px 10px; line-height: 1.45; color: rgb(51, 51, 51); word-break: break-all; word-wrap: break-word; background-color: rgb(246, 246, 246); border: none; border-radius: 3px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">package com.demo1;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyTag {

}</pre>

注解處理:

<pre class="brush:java;toolbar: true; auto-links: false;" style="box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; display: block; padding: 6px; margin: 0px 0px 10px; line-height: 1.45; color: rgb(51, 51, 51); word-break: break-all; word-wrap: break-word; background-color: rgb(246, 246, 246); border: none; border-radius: 3px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">package com.demo1;

import java.lang.reflect.Method;

public class ProcessTool {
public static void process(String clazz) {
Class targetClass = null;

    try {
        targetClass = Class.forName(clazz);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }

    for (Method m : targetClass.getMethods()) {
        if (m.isAnnotationPresent(MyTag.class)) {
            System.out.println("被MyTag注解修飾的方法名:" + m.getName());
        } else {
            System.out.println("沒被MyTag注解修飾的方法名:" + m.getName());
        }
    }
}

}</pre>

測試類:

<pre class="brush:java;toolbar: true; auto-links: false;" style="box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; display: block; padding: 6px; margin: 0px 0px 10px; line-height: 1.45; color: rgb(51, 51, 51); word-break: break-all; word-wrap: break-word; background-color: rgb(246, 246, 246); border: none; border-radius: 3px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">package com.demo1;

public class Demo {
public static void m1() {

}

@MyTag
public static void m2() {

}

}</pre>

<pre class="brush:java;toolbar: true; auto-links: false;" style="box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; display: block; padding: 6px; margin: 0px 0px 10px; line-height: 1.45; color: rgb(51, 51, 51); word-break: break-all; word-wrap: break-word; background-color: rgb(246, 246, 246); border: none; border-radius: 3px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">package com.demo1;

public class Test {

public static void main(String[] args) {
    ProcessTool.process("com.demo1.Demo");
}

}</pre>

運行結果:

<pre class="brush:java;toolbar: true; auto-links: false;" style="box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; display: block; padding: 6px; margin: 0px 0px 10px; line-height: 1.45; color: rgb(51, 51, 51); word-break: break-all; word-wrap: break-word; background-color: rgb(246, 246, 246); border: none; border-radius: 3px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">沒被MyTag注解修飾的方法名:m1
被MyTag注解修飾的方法名:m2
沒被MyTag注解修飾的方法名:wait
沒被MyTag注解修飾的方法名:wait
沒被MyTag注解修飾的方法名:wait
沒被MyTag注解修飾的方法名:equals
沒被MyTag注解修飾的方法名:toString
沒被MyTag注解修飾的方法名:hashCode
沒被MyTag注解修飾的方法名:getClass
沒被MyTag注解修飾的方法名:notify
沒被MyTag注解修飾的方法名:notifyAll</pre>

2.3.2 元數(shù)據(jù)Annotation

給定一個類的全額限定名凄吏,加載類远舅,找出被注解MyTag修飾的方法,并輸出每個方法的MyTag注解的屬性痕钢。

注解定義:

<pre class="brush:java;toolbar: true; auto-links: false;" style="box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; display: block; padding: 6px; margin: 0px 0px 10px; line-height: 1.45; color: rgb(51, 51, 51); word-break: break-all; word-wrap: break-word; background-color: rgb(246, 246, 246); border: none; border-radius: 3px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">package com.demo1;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyTag {
String name() default "我蘭";

int age() default 18;

}</pre>

注解處理:

<pre class="brush:java;toolbar: true; auto-links: false;" style="box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; display: block; padding: 6px; margin: 0px 0px 10px; line-height: 1.45; color: rgb(51, 51, 51); word-break: break-all; word-wrap: break-word; background-color: rgb(246, 246, 246); border: none; border-radius: 3px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">package com.demo1;

import java.lang.reflect.Method;

public class ProcessTool {
public static void process(String clazz) {
Class targetClass = null;

    try {
        targetClass = Class.forName(clazz);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }

    for (Method m : targetClass.getMethods()) {
        if (m.isAnnotationPresent(MyTag.class)) {
            MyTag tag = m.getAnnotation(MyTag.class);
            System.out.println("方法" + m.getName() + "的MyTag注解內(nèi)容為:" + tag.name() + "图柏," + tag.age());
        }
    }
}

}</pre>

測試類:

<pre class="brush:java;toolbar: true; auto-links: false;" style="box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; display: block; padding: 6px; margin: 0px 0px 10px; line-height: 1.45; color: rgb(51, 51, 51); word-break: break-all; word-wrap: break-word; background-color: rgb(246, 246, 246); border: none; border-radius: 3px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">package com.demo1;

public class Demo {
public static void m1() {

}

@MyTag
public static void m2() {

}

@MyTag(name = "紅薯")
public static void m3() {

}

@MyTag(name = "紅薯", age = 30)
public static void m4() {

}

}</pre>

<pre class="brush:java;toolbar: true; auto-links: false;" style="box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; display: block; padding: 6px; margin: 0px 0px 10px; line-height: 1.45; color: rgb(51, 51, 51); word-break: break-all; word-wrap: break-word; background-color: rgb(246, 246, 246); border: none; border-radius: 3px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">package com.demo1;

public class Test {

public static void main(String[] args) {
    ProcessTool.process("com.demo1.Demo");
}

}</pre>

運行結果:

<pre class="brush:java;toolbar: true; auto-links: false;" style="box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; display: block; padding: 6px; margin: 0px 0px 10px; line-height: 1.45; color: rgb(51, 51, 51); word-break: break-all; word-wrap: break-word; background-color: rgb(246, 246, 246); border: none; border-radius: 3px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">方法m2的MyTag注解內(nèi)容為:我蘭,18
方法m3的MyTag注解內(nèi)容為:紅薯任连,18
方法m4的MyTag注解內(nèi)容為:紅薯蚤吹,30</pre>

若要獲取注解中的成員變量值,直接調(diào)用注解對象的"成員變量民( )"形式的方法就行随抠,例如示例中的tag.name()等裁着。

PS:在編譯器編譯注解定義時,自動在class文件中拱她,添加與成員變量同名的抽象方法二驰,用于反射時獲取成員變量的值。

通過上面的示例可以看出秉沼,其實Annotation十分簡單诸蚕,它是對源代碼增加的一些特殊標記,這些特殊標記可通過反射獲取氧猬,當程序獲取這些特殊標記后背犯,程序可以做出相應的處理(當然也可以完全忽略這些Annotation)。

3 注解本質(zhì)

對于示例”2.3.2 元數(shù)據(jù)Annotation“中的MyTag注解盅抚,在編譯后漠魏,生成一個MyTag.class文件。反編譯該class文件:

<pre class="brush:java;toolbar: true; auto-links: false;" style="box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; display: block; padding: 6px; margin: 0px 0px 10px; line-height: 1.45; color: rgb(51, 51, 51); word-break: break-all; word-wrap: break-word; background-color: rgb(246, 246, 246); border: none; border-radius: 3px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">javap -verbose -c MyTag.class > m.txt</pre>

MyTag注解的字節(jié)碼為:

[圖片上傳失敗...(image-45afbe-1525656253126)]

通過分析字節(jié)碼可知:

  • 注解實質(zhì)上會被編譯器編譯為接口妄均,并且繼承java.lang.annotation.Annotation接口柱锹。

  • 注解的成員變量會被編譯器編譯為同名的抽象方法。

  • 根據(jù)Java的class文件規(guī)范丰包,class文件中會在程序元素的屬性位置記錄注解信息禁熏。例如,RuntimeVisibleAnnotations屬性位置邑彪,記錄修飾該類的注解有哪些瞧毙;flags屬性位置,記錄該類是不是注解;在方法的AnnotationDefault屬性位置宙彪,記錄注解的成員變量默認值是多少矩动。

我們再反編譯下示例”2.3.2 元數(shù)據(jù)Annotation“中的Demo測試類,查看下”被注解修飾的方法是怎樣記錄自己被注解修飾的“:

<pre class="brush:java;toolbar: true; auto-links: false;" style="box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; display: block; padding: 6px; margin: 0px 0px 10px; line-height: 1.45; color: rgb(51, 51, 51); word-break: break-all; word-wrap: break-word; background-color: rgb(246, 246, 246); border: none; border-radius: 3px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">javap -verbose -c Demo.class > d.txt</pre>

反編譯結果如下:

[圖片上傳失敗...(image-5a9872-1525656253126)]

通過字節(jié)碼可知:

在字節(jié)碼文件中释漆,每個方法都有RuntimeVisibleAnnotations屬性位置悲没,用來放置注解和注解的成員變量賦值。JVM在解析class文件時男图,會解析RuntimeVisibleAnnotations屬性示姿,并新建相應類型的注解對象,并將成員變量賦值逊笆。

如果要明白JVM對注解的運行機制栈戳,需要對class文件的格式規(guī)范有一定了解。(資料

4 注解的意義

為編譯器提供輔助信息 — Annotations可以為編譯器提供而外信息览露,以便于檢測錯誤,抑制警告等.

編譯源代碼時進行而外操作 — 軟件工具可以通過處理Annotation信息來生成原代碼譬胎,xml文件等等.

運行時處理 — 有一些annotation甚至可以在程序運行時被檢測差牛,使用.

總之,注解是一種元數(shù)據(jù)堰乔,起到了”描述偏化,配置“的作用。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末镐侯,一起剝皮案震驚了整個濱河市侦讨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌苟翻,老刑警劉巖韵卤,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異崇猫,居然都是意外死亡沈条,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進店門诅炉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蜡歹,“玉大人,你說我怎么就攤上這事涕烧≡露” “怎么了?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵议纯,是天一觀的道長父款。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么铛漓? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任溯香,我火速辦了婚禮,結果婚禮上浓恶,老公的妹妹穿的比我還像新娘玫坛。我一直安慰自己,他們只是感情好包晰,可當我...
    茶點故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布湿镀。 她就那樣靜靜地躺著,像睡著了一般伐憾。 火紅的嫁衣襯著肌膚如雪勉痴。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天树肃,我揣著相機與錄音蒸矛,去河邊找鬼。 笑死胸嘴,一個胖子當著我的面吹牛雏掠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播劣像,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼乡话,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了耳奕?” 一聲冷哼從身側響起绑青,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎屋群,沒想到半個月后闸婴,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡芍躏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年掠拳,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片纸肉。...
    茶點故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡溺欧,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出柏肪,到底是詐尸還是另有隱情姐刁,我是刑警寧澤,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布烦味,位于F島的核電站聂使,受9級特大地震影響壁拉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜柏靶,卻給世界環(huán)境...
    茶點故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一弃理、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧屎蜓,春花似錦痘昌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至扼劈,卻和暖如春驻啤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背荐吵。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工骑冗, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人先煎。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓贼涩,卻偏偏與公主長得像,于是被迫代替她去往敵國和親榨婆。 傳聞我的和親對象是個殘疾皇子磁携,可洞房花燭夜當晚...
    茶點故事閱讀 45,500評論 2 359

推薦閱讀更多精彩內(nèi)容