実行時に注釈パラメータを変更する

実行時の注釈パラメーターの変更

1. 概要

Annotations、Javaコードに追加できるメタデータの形式。 これらのannotations は、コンパイル時に処理されてクラスファイルに埋め込まれるか、Reflectionを使用して実行時に保持およびアクセスできます。

この記事では、Reflectionを使用して実行時にannotation値を変更する方法について説明します。 この例では、クラスレベルの注釈を使用します。

2. アノテーション

Javaでは、既存のものを使用して新しいannotationsを作成できます。 最も単純な形式では、注釈は@記号とそれに続く注釈名として表されます。

@Override

独自の注釈Greeterを作成しましょう:

@Retention(RetentionPolicy.RUNTIME)
public @interface Greeter {
    public String greet() default "";
}

ここで、クラスレベルのannotation:を使用するJavaクラスGreetingsを作成します。

@Greeter(greet="Good morning")
public class Greetings {}

次に、リフレクションを使用して注釈値にアクセスします。 JavaクラスClassは、クラスのアノテーションにアクセスするためのメソッドgetAnnotationを提供します。

Greeter greetings = Greetings.class.getAnnotation(Greeter.class);
System.out.println("Hello there, " + greetings.greet() + " !!");

3. 注釈の変更

JavaクラスClass は、注釈を管理するためのマップを維持します–Annotationクラスをキーとして、Annotationオブジェクトを値として:

Map, Annotation> map;

このマップを更新して、実行時に注釈を変更します。 このマップにアクセスする方法は、さまざまなJDK実装によって異なります。 JDK7およびJDK8について説明します。

3.1. JDK7の実装

JavaクラスClass hasフィールドannotations。 これはプライベートフィールドであるため、アクセスするには、フィールドのアクセシビリティをtrueに設定する必要があります。 Javaは、名前で任意のフィールドにアクセスするためのメソッドgetDeclaredField を提供します。

Field annotations = Class.class.getDeclaredField(ANNOTATIONS);
annotations.setAccessible(true);

それでは、クラスGreeterの注釈マップにアクセスしましょう。

 Map, Annotation> map = annotations.get(targetClass);

これは、すべての注釈とその値オブジェクトに関する情報を含むマップです。 Greeterクラスの注釈オブジェクトを更新することで達成できるGreeter注釈値を変更したいと思います。

map.put(targetAnnotation, targetValue);

3.2. JDK8の実装

Java 8実装は、annotations情報をクラスAnnotationData内に格納します。 annotationDataメソッドを使用してこのオブジェクトにアクセスできます。 annotationDataメソッドはプライベートメソッドであるため、アクセシビリティをtrueに設定します。

Method method = Class.class.getDeclaredMethod(ANNOTATION_METHOD, null);
method.setAccessible(true);

これで、annotationsフィールドにアクセスできます。 このフィールドもプライベートフィールドであるため、アクセシビリティをtrueに設定します。

Field annotations = annotationData.getClass().getDeclaredField(ANNOTATIONS);
annotations.setAccessible(true);

このフィールドには、注釈クラスと値オブジェクトを格納する注釈キャッシュマップがあります。 それを変えましょう:

Map, Annotation> map = annotations.get(annotationData);
map.put(targetAnnotation, targetValue);

4. 応用

この例を見てみましょう:

Greeter greetings = Greetings.class.getAnnotation(Greeter.class);
System.err.println("Hello there, " + greetings.greet() + " !!");

これが注釈に提供した値であるため、これは「おはよう」の挨拶となります。 ここで、値が「こんばんは」のGreeterタイプのオブジェクトをもう1つ作成します。

Greeter targetValue = new DynamicGreeter("Good evening");

注釈マップを新しい値で更新しましょう:

alterAnnotationValueJDK8(Greetings.class, Greeter.class, targetValue);

挨拶の値をもう一度確認しましょう。

greetings = Greetings.class.getAnnotation(Greeter.class);
System.err.println("Hello there, " + greetings.greet() + " !!");

「こんばんは」と挨拶します。

5. 結論

Java実装では、注釈データを格納するために2つのデータフィールド(annotationsdeclaredAnnotations)を使用します。 これら2つの違い:最初に親クラスからの注釈も保存し、後で現在のクラスに対してのみ保存します。

getAnnotationの実装はJDK7とJDK8で異なるため、ここでは簡単にするためにannotations fieldマップを使用します。

そして、いつものように、実装のソースコードは利用可能なover on Githubです。