28

いくつかのメソッドによって生成されたいくつかの値を注釈に提供したいと考えています。

私はこれまでにこれを試しました:

public @interface MyInterface {
    String aString();
}

@MyInterface(aString = MyClass.GENERIC_GENERATED_NAME)
public class MyClass {

    static final String GENERIC_GENERATED_NAME = MyClass.generateName(MyClass.class);

    public static final String generateName(final Class<?> c) {
        return c.getClass().getName();
    }
}

考えGENERIC_GENERATED_NAMEstatic final、それは不平を言う

注釈属性の値MyInterface.aStringは定数式でなければなりません

では、これを達成する方法は?

4

3 に答える 3

28

注釈で使用される文字列を動的に生成する方法はありません。RetentionPolicy.RUNTIMEコンパイラは、コンパイル時に注釈の注釈メタデータを評価しますが、GENERIC_GENERATED_NAME実行時まではわかりません。RetentionPolicy.SOURCEまた、生成された値はコンパイル後に破棄されるため、注釈に使用することはできません。そのため、生成された値は決してわかりません。

于 2012-05-17T13:06:49.460 に答える
9

解決策は、代わりに注釈付きのメソッドを使用することです。そのメソッドを (リフレクションを使用して) 呼び出して、動的な値を取得します。

ユーザーの観点からは、次のようになります。

@MyInterface
public class MyClass {
    @MyName
    public String generateName() {
        return MyClass.class.getName();
    }
}

注釈自体は次のように定義されます。

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

これらの両方の注釈のルックアップを実装するのはかなり簡単です。

// as looked up by @MyInterface
Class<?> clazz;

Method[] methods = clazz.getDeclaredMethods();
if (methods.length != 1) {
    // error
}
Method method = methods[0];
if (!method.isAnnotationPresent(MyName.class)) {
    // error as well
}
// This works if the class has a public empty constructor
// (otherwise, get constructor & use setAccessible(true))
Object instance = clazz.newInstance();
// the dynamic value is here:
String name = (String) method.invoke(instance);
于 2014-08-12T00:20:31.053 に答える
5

他の人が言ったように、注釈のプロパティを動的に変更する方法はありません。それでもそれを達成したい場合、これを行うには2つの方法があります。

  1. 注釈のプロパティに式を割り当て、注釈を取得するたびにその式を処理します。あなたの場合、あなたの注釈は

    @MyInterface(aString = "objectA.doSomething(args1, args2)")

それを読むと、文字列を処理してメソッド呼び出しを行い、値を取得できます。Spring は SPEL (Spring 式言語) によってそれを行います。これはリソースを大量に消費し、式を処理するたびに CPU サイクルが浪費されます。スプリングを使用している場合は、beanPostProcessor をフックして式を 1 回処理し、結果をどこかに保存できます。(グローバル プロパティ オブジェクト、またはどこからでも取得できるマップ内のいずれか)。

  1. これは、私たちがやりたいことを行うハックな方法です。Java は、クラス/フィールド/メソッドの注釈のマップを維持するプライベート変数を格納します。リフレクションを使用して、そのマップを取得できます。そのため、アノテーションを初めて処理するときに、式を解決して実際の値を見つけます。次に、必要なタイプの注釈オブジェクトを作成します。新しく作成されたアノテーションを実際の値 (定数) でアノテーションのプロパティに配置し、取得したマップで実際のアノテーションをオーバーライドできます。

jdk がアノテーション マップを格納する方法は、Java のバージョンに依存し、使用のために公開されていない (非公開) ため、信頼性がありません。

ここで参照実装を見つけることができます。

https://rationalemotions.wordpress.com/2016/05/27/ching-annotation-values-at-runtime/

PS: 2 番目の方法は試していません。

于 2017-01-21T15:21:28.660 に答える