いくつかのJavaアノテーションマジック。
だから...注釈付きのクラス、メソッド、フィールドがあります。リフレクションを使用してクラスでいくつかのチェックを実行し、クラスに値を注入するメソッドがあります。これはすべて正常に動作します。
しかし、私は注釈のインスタンス(いわば)が必要な場合に直面しています。そのため、アノテーションは通常のインターフェースとは異なり、クラスの匿名実装を行うことはできません。わかった。同様の問題に関するいくつかの投稿をここで見ましたが、探しているものへの答えを見つけることができないようです。
基本的に、アノテーションのインスタンスを取得して、リフレクションを使用してそのフィールドの一部を設定できるようにしたいと思います(そうですね)。これを行う方法はありますか?
まあ、どうやらそれほど複雑なことではないようです。 Really !
同僚が指摘したように、次のようにアノテーションの匿名インスタンスを作成できます(他のインターフェイスと同様)。
MyAnnotation:
public @interface MyAnnotation
{
String foo();
}
呼び出しコード:
class MyApp
{
MyAnnotation getInstanceOfAnnotation(final String foo)
{
MyAnnotation annotation = new MyAnnotation()
{
@Override
public String foo()
{
return foo;
}
@Override
public Class<? extends Annotation> annotationType()
{
return MyAnnotation.class;
}
};
return annotation;
}
}
Martin Grigorov へのクレジット。
Gunnar's answer で提案されているプロキシアプローチは、すでに GeAnTyRef で実装されています。
Map<String, Object> annotationParameters = new HashMap<>();
annotationParameters.put("name", "someName");
MyAnnotation myAnnotation = TypeFactory.annotation(MyAnnotation.class, annotationParameters);
これにより、取得するものと同等の注釈が生成されます。
@MyAnnotation(name = "someName")
この方法で生成された注釈インスタンスは、Java通常生成されるものと同じ動作をします。また、hashCode
とequals
は互換性のために適切に実装されているため、奇妙なことはありません受け入れられた回答のように、アノテーションを直接インスタンス化する場合のような注意点実際、JDKは内部的にこの同じアプローチを使用しています: Sun.reflect.annotation.AnnotationParser#annotationForMap 。
ライブラリ自体は小さく、依存関係はありません。
開示:私はGeAnTyRefの開発者です。
Sun.reflect.annotation.AnnotationParser.annotationForMap(Class, Map)
を使用できます:
_public @interface MyAnnotation {
String foo();
}
public class MyApp {
public MyAnnotation getInstanceOfAnnotation(final String foo) {
MyAnnotation annotation = AnnotationParser.annotationForMap(
MyAnnotation.class, Collections.singletonMap("foo", "myFooResult"));
}
}
_
欠点:_Sun.*
_のクラスは後のバージョンで変更される可能性があります(ただし、このメソッドはJava 5が同じ署名を持つため)が存在し、すべてのJava実装、 この説明 を参照してください。
それが問題の場合:独自のInvocationHandler
を使用して汎用プロキシを作成できます。これは、AnnotationParser
が内部的に行っていることです。または、定義済みのMyAnnotation
の独自の実装 here を使用します。どちらの場合も、annotationType()
、equals()
、およびhashCode()
を実装することを忘れないでください。結果は_Java.lang.Annotation
_専用にドキュメント化されています。
Apache Commons AnnotationUtils の助けを借りてプロキシアプローチを使用するむしろ粗雑な方法
public static <A extends Annotation> A mockAnnotation(Class<A> annotationClass, Map<String, Object> properties) {
return (A) Proxy.newProxyInstance(annotationClass.getClassLoader(), new Class<?>[] { annotationClass }, (proxy, method, args) -> {
Annotation annotation = (Annotation) proxy;
String methodName = method.getName();
switch (methodName) {
case "toString":
return AnnotationUtils.toString(annotation);
case "hashCode":
return AnnotationUtils.hashCode(annotation);
case "equals":
return AnnotationUtils.equals(annotation, (Annotation) args[0]);
case "annotationType":
return annotationClass;
default:
if (!properties.containsKey(methodName)) {
throw new NoSuchMethodException(String.format("Missing value for mocked annotation property '%s'. Pass the correct value in the 'properties' parameter", methodName));
}
return properties.get(methodName);
}
});
}
渡されたプロパティのタイプは、アノテーションインターフェイスで宣言された実際のタイプではチェックされず、不足している値は実行時にのみ検出されます。
kaqqao's answer (そしておそらく Gunnar's Answer -)))、内部Java APIを Tobias Liefkeの答えのように使用することのマイナス面なし)