Javaアノテーション原理

 

   以前は、「XML」は緩く、事実上すべての構成のためのフレームワークを完了するための方法を結合された主要なフレームワークによって支持されていますが、より多くの大規模なプロジェクトで、「XML」の内容は、ますます複雑になってきている、メンテナンスコスト高いです。

だから、何人かの人々は、高結合型の構成、のマークアップ前方に置く「のメモを。」この方法は、注釈を付けることができるクラスが注釈を付けることができ、ほとんどとにかくあなたは注釈を付けることができます設定する必要がある場合は、フィールド属性も、注釈を付けることができます。

長年にわたって議論、「ノート」と二つの異なるコンフィギュレーションモードで、「XML」、について、それぞれ独自の長所と短所があり、注釈は利便性、メンテナンス変更の容易さが、高結合、およびXMLに関してを提供することができますコメントが逆転しています。

我々は低結合効率の追求を放棄する必要があり、効率の追求は、カップリングに遭遇することがバインドされています。この記事は、もはや両方に優れている人を区別するために、ほとんどの簡単な言葉で、基本的なコンテンツに関連するノートを紹介するものではありません。

自然の注釈

「Java.lang.annotation.Annotation」インターフェース記述するので、多くの言葉がある「ノート」。

すべての注釈型によって拡張される共通のインタフェース

すべての注釈型が共通のインタフェースから継承されている(注釈)

少し抽象この文は、しかし、彼らは、注釈の性質を言います。私たちは、JDKビルトイン定義された注釈を見て:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {

}

ノートは、実際には、それが本質的である@Overrideこれは定義です:

public interface Override extends Annotation{ }

はい、性質が継承された注釈インターフェイス注釈インタフェースです。この点で、あなたは結果を得るだろう、アノテーションクラスのいずれかをコンパイルするために行くことができます。

それはそれもコメントしないかもしれないようなコードが解決しない場合は、正確な意味でのノートでは、それは、特別なコメントです。

クラスまたはアノテーションを解析するための方法は、多くの場合、二つの形式、スキャンの直接編集、および反射の実行を持っています。我々はパーサジェネレータをスキャンすると、クラスを参照したりする方法は、いくつかの注意事項が検出されたバイトコードにコンパイルされたJavaコードを処理するように変更されているが、反映していると言うだろうどのようなこと、それはこれらのアノテーションになりますいくつかの処理。

典型的なコメント@Overrideは、コンパイラが方法が@Overrideアノテーションで変更された検出すると、コンパイラがチェックする現在の方法は、比較的親クラスである本当の親クラスのメソッドシグネチャを、オーバーライドするかどうか、です同様の方法で署名するかどうか。

この場合は、このような組み込みのJDKいくつかの注意事項として、唯一のクラスコンパイラがすでに精通しているそれらの注釈に適用され、カスタムコンパイラに注釈を付けますが、もちろん、この注釈役割を認識していない、に対処する方法を知りませんでしたが、多くの場合、バイトコードファイルにコンパイルするかどうかを選択して、注釈に従い、何よりもスコープます。

元のメモ

「メタ注釈は」など、通常のアノテーションの定義に、注釈付きのノートを修正するために使用されます。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {

}

これは@Overrideアノテーションの私達の定義である、あなたは、リテンション2つのノート@、ターゲット@それらを見ることができ、私たちは「メタアノテーション」と呼ぶもので、「メタアノテーションは、」一般的注釈情報のライフサイクルと目標の役割を指定するために使用されます。

JAVAはいくつかの「メタアノテーションを」持っています:

  • @Target:ターゲット注釈の役割
  • @Retention:ライフサイクルの注意事項
  • @Documented:ノートはJavadocドキュメントに含まれるべきかどうか
  • @Inherited:サブクラスはコメントを継承できるようにするかどうか

ターゲットアノテーションは、彼が誰であるか、それは最終的にあなたのコメントに指定されていることができる究極の目標の修正役割を示すために使用される@どの、方法を変更するために使用されていますか?変更されたクラス?またはフィールドのプロパティを変更するために使用。

次のように定義さ@Target:

画像

私たちは、途中、この値によって値を渡すことができます。

@Target(value = {ElementType.FIELD})

この注釈付き修正@Targetコメントは、メソッドやクラスを変更するために使用することはできません、メンバーフィールドに作用します。これは、のElementTypeが列挙型で、次の値のいくつかがあります。

  • クラス、インターフェイス、および列挙で修正役割に関するコメントを許可:ElementType.TYPEを
  • ElementType.FIELD:属性フィールドに作用させ
  • ElementType.METHOD:の方法に作用させ
  • ElementType.PARAMETER:メソッドのパラメータに作用させ
  • ElementType.CONSTRUCTOR:コンストラクタに作用させ
  • ElementType.LOCAL_VARIABLE:ローカルローカル変数に作用させ
  • ElementType.ANNOTATION_TYPE:ノートに作用させ
  • ElementType.PACKAGE:クラッドに作用させ

@Retentionアノテーションは以下のように、現在のライフサイクルは、その基本的な定義があることを示しています。

画像

同様に、それはまた、value属性があります。

@Retention(value = RetentionPolicy.RUNTIME

ここRetentionPolicyは、以下の望ましい列挙値を持って、まだ列挙型です。

  • RetentionPolicy.SOURCE:見て、現在の注釈のコンパイル、クラスファイルを作成していません
  • RetentionPolicy.CLASS:クラスローディング相を廃棄、クラスファイルを書き込みます。
  • RetentionPolicy.RUNTIME:永久保存、反射を得ることができます

@Retentionアノテーションは、注釈付きに変更ライフサイクルは、一つだけ、それはコンパイラにクラスにコンパイルされたファイルをコンパイルした後に廃棄されるクラスやメソッド、および偶数フィールドのいずれかコンパイル時に表示されていることを指定します彼らはすべてのプロパティシートで、JAVA仮想マシンは、いくつかのアノテーションは、アノテーション情報を格納するテーブルの属性を定義し、これは可視域の方法をもたらしたことができない、クラスがロードされたときに破棄されます、最後のものがあります視認性の永続的。

、比較的単純な、小さな、ここでは詳述しないが、私たちの日常使用の注釈の残りの二つのタイプが導入された、あなただけのそれぞれの役割をすることができ知っている必要があります。@Documentedコメントはパッケージ化するときに破棄されます逆に、我々はJavadocドキュメントを実行すると、パッケージがドキュメントのドキュメントに保存され、コメントを修正しました。@Inherited注釈ノート修正継承され、また、我々は、クラスを変更することを指摘し、そのクラスのサブクラスは、自動的に親クラスのノートを継承します。

JAVA建て3つのノート

上記4元のノートに加えて、JDKもあり、当社の他の3つの定義済みの注釈、用:

  • @オーバーライド
  • @Deprecated
  • @SuppressWarnings

@Overrideコメントは、おそらく誰もが精通しており、以下のように定義されています。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

これは、任意のプロパティを持っていないので、その他の情報を格納しないでください。それだけで、それがコンパイルの終了後に破棄され、上記の方法に基づいて行動することができます。

だから、それは「タグ型ノート」典型的であるだけでコンパイラを示し、方法の検出時に、バイトコードにコンパイルされたJavaファイルの過程で、コンパイラは、コメントの変更であることがわかりない場合は、馬が親クラスが機能同じメソッドシグネチャを持っているかどうかに行くだろう、コンパイルができませんでした。

次のように実質的に定義@deprecated

画像

それでも「マーカー注釈型」永久ではありません、あなたはすべてのタイプを変更することができるなど、現在のクラスやメソッド、またはフィールドをマークすることで、もはや使用することは推奨され、JDKの次のバージョンが削除されますことがあります。

もちろん、コンパイラちょうどあなたのJDKを教えて、あなたが何を強制しませんが、もはや現在のメソッドやクラスを使用して推奨されていない、私はあなたが交換を使用することをお勧め。

主に次のように実質的に定義されている警告を抑制するために使用するJava @SuppressWarnings。

画像

それはあなたが値を渡すためのイニシアチブを取る必要があるvalue属性は、この値は、それが何を意味して表し、この値が警告の種類を表してい抑制する必要があります。例えば:

public static void main(String[] args) {
    Date date = new Date(2018, 7, 11); }

プログラムが起動するときにコードの一部は、コンパイラは警告を報告します。

警告:(8、21)のjava:java.util.Date日付(int型、int型、int型)廃止

我々は、プログラムが起動しない場合、コンパイラは、コード時代遅れの方法をチェックして、あなたは@SuppressWarningsコメントを使用し、それをコンパイラをチェック抑制するために渡された値属性パラメータ値を与えることができます。

@SuppressWarning(value = "deprecated")
public static void main(String[] args) {
    Date date = new Date(2018, 7, 11); }

だから、時代遅れの方法をチェックしませんコンパイラは次の主な方法は、このような警告を、コンパイラのチェックを抑制します呼び出すことがわかります。

もちろん、JAVAは、アラートの多くの種類があり、彼らは、検査の種類に警告、このタイプのを抑制するためにvalue属性の値を設定することで、文字列に対応します。

ノートの内容の定義は、それらを繰り返すことはしませんので、それはあなたが次のような構文によってコメントをカスタマイズすることができ、比較的簡単です。

public @interface InnotationName{
    
}

もちろん、メタ注釈の選択も変更されたときにカスタム注釈を使用することができますので、あなたの注釈のライフサイクル、スコープやその他の情報を指定して、より具体的にすることができます。

Notesと反射

注釈の詳細に関する私たちの上に、また簡単に、言及することができます今、私たちは、仮想マシンレベルで見ている「コメントの本質は注釈インタフェースコネクタを継承している」、ノートの性質が最後にものですはい。

まず第一に、我々は、カスタム注釈タイプがあります。

画像

こんにちはここでは、この注釈が唯一のフィールドやメソッドを変更指定し、我々は反射を得ることができるようにノートには、永久に存続します。

私たちは、仮想マシンの仕様は、注釈と関連付けられているプロパティシートのセットを定義する前に変更が注釈されている場合、それは、それはフィールド、メソッドやクラス自体であるかどうか、であると述べている、バイトコードファイルに書き込むことができます。プロパティシートは、次のとおりです。

  • RuntimeVisibleAnnotations:运行时可见的注解
  • RuntimeInVisibleAnnotations:运行时不可见的注解
  • RuntimeVisibleParameterAnnotations:运行时可见的方法参数注解
  • RuntimeInVisibleParameterAnnotations:运行时不可见的方法参数注解
  • AnnotationDefault:注解类元素的默认值

给大家看虚拟机的这几个注解相关的属性表的目的在于,让大家从整体上构建一个基本的印象,注解在字节码文件中是如何存储的。

所以,对于一个类或者接口来说,Class 类中提供了以下一些方法用于反射注解。

  • getAnnotation:返回指定的注解
  • isAnnotationPresent:判定当前元素是否被指定注解修饰
  • getAnnotations:返回所有的注解
  • getDeclaredAnnotation:返回本元素的指定注解
  • getDeclaredAnnotations:返回本元素的所有注解,不包含父类继承而来的

方法、字段中相关反射注解的方法基本是类似的,这里不再赘述,我们下面看一个完整的例子。

首先,设置一个虚拟机启动参数,用于捕获 JDK 动态代理类。

-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true

然后 main 函数。

画像

我们说过,注解本质上是继承了 Annotation 接口的接口,而当你通过反射,也就是我们这里的 getAnnotation 方法去获取一个注解类实例的时候,其实 JDK 是通过动态代理机制生成一个实现我们注解(接口)的代理类。

我们运行程序后,会看到输出目录里有这么一个代理类,反编译之后是这样的:

画像

画像

代理类实现接口 Hello 并重写其所有方法,包括 value 方法以及接口 Hello 从 Annotation 接口继承而来的方法。

而这个关键的 InvocationHandler 实例是谁?

AnnotationInvocationHandler 是 JAVA 中专门用于处理注解的 Handler, 这个类的设计也非常有意思。

画像

这里有一个 memberValues,它是一个 Map 键值对,键是我们注解属性名称,值就是该属性当初被赋上的值。

画像

画像

而这个 invoke 方法就很有意思了,大家注意看,我们的代理类代理了 Hello 接口中所有的方法,所以对于代理类中任何方法的调用都会被转到这里来。

var2 指向被调用的方法实例,而这里首先用变量 var4 获取该方法的简明名称,接着 switch 结构判断当前的调用方法是谁,如果是 Annotation 中的四大方法,将 var7 赋上特定的值。

如果当前调用的方法是 toString,equals,hashCode,annotationType 的话,AnnotationInvocationHandler 实例中已经预定义好了这些方法的实现,直接调用即可。

那么假如 var7 没有匹配上这四种方法,说明当前的方法调用的是自定义注解字节声明的方法,例如我们 Hello 注解的 value 方法。这种情况下,将从我们的注解 map 中获取这个注解属性对应的值。

其实,JAVA 中的注解设计个人觉得有点反人类,明明是属性的操作,非要用方法来实现。当然,如果你有不同的见解,欢迎留言探讨。

最后我们再总结一下整个反射注解的工作原理:

首先,我们通过键值对的形式可以为注解属性赋值,像这样:@Hello(value = "hello")。

接着,你用注解修饰某个元素,编译器将在编译期扫描每个类或者方法上的注解,会做一个基本的检查,你的这个注解是否允许作用在当前位置,最后会将注解信息写入元素的属性表。

然后,当你进行反射的时候,虚拟机将所有生命周期在 RUNTIME 的注解取出来放到一个 map 中,并创建一个 AnnotationInvocationHandler 实例,把这个 map 传递给它。

最后,虚拟机将采用 JDK 动态代理机制生成一个目标注解的代理类,并初始化好处理器。

那么这样,一个注解的实例就创建出来了,它本质上就是一个代理类,你应当去理解好 AnnotationInvocationHandler 中 invoke 方法的实现逻辑,这是核心。一句话概括就是,通过方法名返回注解属性值

于是就有人提出来一种标记式高耦合的配置方式,『注解』。方法上可以进行注解,类上也可以注解,字段属性上也可以注解,反正几乎需要配置的地方都可以进行注解。

关于『注解』和『XML』两种不同的配置模式,争论了好多年了,各有各的优劣,注解可以提供更大的便捷性,易于维护修改,但耦合度高,而 XML 相对于注解则是相反的。

追求低耦合就要抛弃高效率,追求效率必然会遇到耦合。本文意不再辨析两者谁优谁劣,而在于以最简单的语言介绍注解相关的基本内容。

注解的本质

「java.lang.annotation.Annotation」接口中有这么一句话,用来描述『注解』。

The common interface extended by all annotation types

所有的注解类型都继承自这个普通的接口(Annotation)

这句话有点抽象,但却说出了注解的本质。我们看一个 JDK 内置注解的定义:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {

}

这是注解 @Override 的定义,其实它本质上就是:

public interface Override extends Annotation{ }

没错,注解的本质就是一个继承了 Annotation 接口的接口。有关这一点,你可以去反编译任意一个注解类,你会得到结果的。

一个注解准确意义上来说,只不过是一种特殊的注释而已,如果没有解析它的代码,它可能连注释都不如。

而解析一个类或者方法的注解往往有两种形式,一种是编译期直接的扫描,一种是运行期反射。反射的事情我们待会说,而编译器的扫描指的是编译器在对 java 代码编译字节码的过程中会检测到某个类或者方法被一些注解修饰,这时它就会对于这些注解进行某些处理。

典型的就是注解 @Override,一旦编译器检测到某个方法被修饰了 @Override 注解,编译器就会检查当前方法的方法签名是否真正重写了父类的某个方法,也就是比较父类中是否具有一个同样的方法签名。

这一种情况只适用于那些编译器已经熟知的注解类,比如 JDK 内置的几个注解,而你自定义的注解,编译器是不知道你这个注解的作用的,当然也不知道该如何处理,往往只是会根据该注解的作用范围来选择是否编译进字节码文件,仅此而已。

元注解

『元注解』是用于修饰注解的注解,通常用在注解的定义上,例如:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {

}

这是我们 @Override 注解的定义,你可以看到其中的 @Target,@Retention 两个注解就是我们所谓的『元注解』,『元注解』一般用于指定某个注解生命周期以及作用目标等信息。

JAVA 中有以下几个『元注解』:

  • @Target:注解的作用目标
  • @Retention:注解的生命周期
  • @Documented:注解是否应当被包含在 JavaDoc 文档中
  • @Inherited:是否允许子类继承该注解

其中,@Target 用于指明被修饰的注解最终可以作用的目标是谁,也就是指明,你的注解到底是用来修饰方法的?修饰类的?还是用来修饰字段属性的。

@Target 的定义如下:

画像

我们可以通过以下的方式来为这个 value 传值:

@Target(value = {ElementType.FIELD})

被这个 @Target 注解修饰的注解将只能作用在成员字段上,不能用于修饰方法或者类。其中,ElementType 是一个枚举类型,有以下一些值:

  • ElementType.TYPE:允许被修饰的注解作用在类、接口和枚举上
  • ElementType.FIELD:允许作用在属性字段上
  • ElementType.METHOD:允许作用在方法上
  • ElementType.PARAMETER:允许作用在方法参数上
  • ElementType.CONSTRUCTOR:允许作用在构造器上
  • ElementType.LOCAL_VARIABLE:允许作用在本地局部变量上
  • ElementType.ANNOTATION_TYPE:允许作用在注解上
  • ElementType.PACKAGE:允许作用在包上

@Retention 用于指明当前注解的生命周期,它的基本定义如下:

画像

同样的,它也有一个 value 属性:

@Retention(value = RetentionPolicy.RUNTIME

这里的 RetentionPolicy 依然是一个枚举类型,它有以下几个枚举值可取:

  • RetentionPolicy.SOURCE:当前注解编译期可见,不会写入 class 文件
  • RetentionPolicy.CLASS:类加载阶段丢弃,会写入 class 文件
  • RetentionPolicy.RUNTIME:永久保存,可以反射获取

@Retention 注解指定了被修饰的注解的生命周期,一种是只能在编译期可见,编译后会被丢弃,一种会被编译器编译进 class 文件中,无论是类或是方法,乃至字段,他们都是有属性表的,而 JAVA 虚拟机也定义了几种注解属性表用于存储注解信息,但是这种可见性不能带到方法区,类加载时会予以丢弃,最后一种则是永久存在的可见性。

剩下两种类型的注解我们日常用的不多,也比较简单,这里不再详细的进行介绍了,你只需要知道他们各自的作用即可。@Documented 注解修饰的注解,当我们执行 JavaDoc 文档打包时会被保存进 doc 文档,反之将在打包时丢弃。@Inherited 注解修饰的注解是具有可继承性的,也就说我们的注解修饰了一个类,而该类的子类将自动继承父类的该注解。

JAVA 的内置三大注解

除了上述四种元注解外,JDK 还为我们预定义了另外三种注解,它们是:

  • @Override
  • @Deprecated
  • @SuppressWarnings

@Override 注解想必是大家很熟悉的了,它的定义如下:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

它没有任何的属性,所以并不能存储任何其他信息。它只能作用于方法之上,编译结束后将被丢弃。

所以你看,它就是一种典型的『标记式注解』,仅被编译器可知,编译器在对 java 文件进行编译成字节码的过程中,一旦检测到某个方法上被修饰了该注解,就会去匹对父类中是否具有一个同样方法签名的函数,如果不是,自然不能通过编译。

@Deprecated 的基本定义如下:

画像

依然是一种『标记式注解』,永久存在,可以修饰所有的类型,作用是,标记当前的类或者方法或者字段等已经不再被推荐使用了,可能下一次的 JDK 版本就会删除。

当然,编译器并不会强制要求你做什么,只是告诉你 JDK 已经不再推荐使用当前的方法或者类了,建议你使用某个替代者。

@SuppressWarnings 主要用来压制 java 的警告,它的基本定义如下:

画像

它有一个 value 属性需要你主动的传值,这个 value 代表一个什么意思呢,这个 value 代表的就是需要被压制的警告类型。例如:

public static void main(String[] args) {
    Date date = new Date(2018, 7, 11); }

这么一段代码,程序启动时编译器会报一个警告。

Warning:(8, 21) java: java.util.Date 中的 Date(int,int,int) 已过时

而如果我们不希望程序启动时,编译器检查代码中过时的方法,就可以使用 @SuppressWarnings 注解并给它的 value 属性传入一个参数值来压制编译器的检查。

@SuppressWarning(value = "deprecated")
public static void main(String[] args) {
    Date date = new Date(2018, 7, 11); }

这样你就会发现,编译器不再检查 main 方法下是否有过时的方法调用,也就压制了编译器对于这种警告的检查。

当然,JAVA 中还有很多的警告类型,他们都会对应一个字符串,通过设置 value 属性的值即可压制对于这一类警告类型的检查。

自定义注解的相关内容就不再赘述了,比较简单,通过类似以下的语法即可自定义一个注解。

public @interface InnotationName{
    
}

当然,自定义注解的时候也可以选择性的使用元注解进行修饰,这样你可以更加具体的指定你的注解的生命周期、作用范围等信息。

注解与反射

上述内容我们介绍了注解使用上的细节,也简单提到,「注解的本质就是一个继承了 Annotation 接口的接口」,现在我们就来从虚拟机的层面看看,注解的本质到底是什么。

首先,我们自定义一个注解类型:

画像

这里我们指定了 Hello 这个注解只能修饰字段和方法,并且该注解永久存活,以便我们反射获取。

之前我们说过,虚拟机规范定义了一系列和注解相关的属性表,也就是说,无论是字段、方法或是类本身,如果被注解修饰了,就可以被写进字节码文件。属性表有以下几种:

  • RuntimeVisibleAnnotations:运行时可见的注解
  • RuntimeInVisibleAnnotations:运行时不可见的注解
  • RuntimeVisibleParameterAnnotations:运行时可见的方法参数注解
  • RuntimeInVisibleParameterAnnotations:运行时不可见的方法参数注解
  • AnnotationDefault:注解类元素的默认值

给大家看虚拟机的这几个注解相关的属性表的目的在于,让大家从整体上构建一个基本的印象,注解在字节码文件中是如何存储的。

所以,对于一个类或者接口来说,Class 类中提供了以下一些方法用于反射注解。

  • getAnnotation:返回指定的注解
  • isAnnotationPresent:判定当前元素是否被指定注解修饰
  • getAnnotations:返回所有的注解
  • getDeclaredAnnotation:返回本元素的指定注解
  • getDeclaredAnnotations:返回本元素的所有注解,不包含父类继承而来的

方法、字段中相关反射注解的方法基本是类似的,这里不再赘述,我们下面看一个完整的例子。

首先,设置一个虚拟机启动参数,用于捕获 JDK 动态代理类。

-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true

然后 main 函数。

画像

我们说过,注解本质上是继承了 Annotation 接口的接口,而当你通过反射,也就是我们这里的 getAnnotation 方法去获取一个注解类实例的时候,其实 JDK 是通过动态代理机制生成一个实现我们注解(接口)的代理类。

我们运行程序后,会看到输出目录里有这么一个代理类,反编译之后是这样的:

画像

画像

代理类实现接口 Hello 并重写其所有方法,包括 value 方法以及接口 Hello 从 Annotation 接口继承而来的方法。

而这个关键的 InvocationHandler 实例是谁?

AnnotationInvocationHandler 是 JAVA 中专门用于处理注解的 Handler, 这个类的设计也非常有意思。

画像

这里有一个 memberValues,它是一个 Map 键值对,键是我们注解属性名称,值就是该属性当初被赋上的值。

画像

画像

而这个 invoke 方法就很有意思了,大家注意看,我们的代理类代理了 Hello 接口中所有的方法,所以对于代理类中任何方法的调用都会被转到这里来。

var2 指向被调用的方法实例,而这里首先用变量 var4 获取该方法的简明名称,接着 switch 结构判断当前的调用方法是谁,如果是 Annotation 中的四大方法,将 var7 赋上特定的值。

如果当前调用的方法是 toString,equals,hashCode,annotationType 的话,AnnotationInvocationHandler 实例中已经预定义好了这些方法的实现,直接调用即可。

那么假如 var7 没有匹配上这四种方法,说明当前的方法调用的是自定义注解字节声明的方法,例如我们 Hello 注解的 value 方法。这种情况下,将从我们的注解 map 中获取这个注解属性对应的值。

其实,JAVA 中的注解设计个人觉得有点反人类,明明是属性的操作,非要用方法来实现。当然,如果你有不同的见解,欢迎留言探讨。

最后我们再总结一下整个反射注解的工作原理:

首先,我们通过键值对的形式可以为注解属性赋值,像这样:@Hello(value = "hello")。

接着,你用注解修饰某个元素,编译器将在编译期扫描每个类或者方法上的注解,会做一个基本的检查,你的这个注解是否允许作用在当前位置,最后会将注解信息写入元素的属性表。

あなたが反映する場合、仮想マシンがマップに出てノートのすべてのライフサイクルRUNTIMEに取るとAnnotationInvocationHandlerインスタンスを作成します。その後、このマップは、それに渡されます。

最後に、仮想マシンがターゲット注釈プロキシクラスを生成するためのJDKダイナミックプロキシ機構を使用し、優れたプロセッサを初期化します。

そのような注釈インスタンスが出て作成された後、それはあなたがコアで良いAnnotationInvocationHandlerのinvokeメソッドでそのロジックを理解するべきで、基本的にプロキシクラスです。一つの文章にまとめ、メソッド名による注釈のプロパティ値を返します

おすすめ

転載: www.cnblogs.com/cgfpx/p/11564254.html