web-dev-qa-db-ja.com

Java 6注釈処理-注釈からクラスを取得する

私は@Pojoと呼ばれるカスタムアノテーションを持っています。これはwikiドキュメントの自動生成に使用しています。

package com.example.annotations;

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

@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.METHOD)
public @interface Pojo {
    Class<?> value();
}

私はそれを次のように使用します:

@Pojo(com.example.restserver.model.appointment.Appointment.class)

リソースメソッドに注釈を付けると、注釈プロセッサは、期待するリソースとタイプを説明するWikiページを自動的に生成できます。

注釈プロセッサのvalueフィールドの値を読み取る必要がありますが、ランタイムエラーが発生します。

私のプロセッサのソースコードには、次の行があります。

final Pojo pojo = element.getAnnotation(Pojo.class);
// ...
final Class<?> pojoJavaClass = pojo.value();

しかし、実際のクラスはプロセッサで利用できません。実際のクラスのサロゲートとして代わりにjavax.lang.model.type.TypeMirrorが必要だと思います。入手方法はわかりません。

私が得ているエラーは:

javax.lang.model.type.MirroredTypeException: Attempt to access Class object for TypeMirror com.example.restserver.model.appointment.Appointment

Appointmentは、私の@Pojoアノテーションの1つで言及されているクラスです。

残念ながら、Javaアノテーション処理に関するドキュメントやチュートリアルは不足しているようです。グーグルを試しました。

45
Ralph

この記事を読みましたか http://blog.retep.org/2009/02/13/getting-class-values-from-annotations-in-an-annotationprocessor/

トリックは、実際にgetAnnotation()を使用してMirroredTypeExceptionをキャッチすることです。驚くべきことに、例外は必要なクラスのTypeMirrorを提供します。

これが良い解決策かどうかはわかりませんが、それは解決策です。私の個人的な意見では、MirroredTypeの背後にある型を取得しようとしますが、これが可能かどうかはわかりません。

26
Ralph

私はまったく同じ質問をするためにここに来ました。 ...そして、Ralphによって投稿された同じ ブログリンク を見つけました。

長い記事ですが、非常に豊富です。ストーリーの概要-簡単な方法と「より適切な」方法の2つの方法があります。

これは簡単な方法です:

private static TypeMirror getMyValue1(MyAnnotation annotation) {
    try
    {
        annotation.myValue(); // this should throw
    }
    catch( MirroredTypeException mte )
    {
        return mte.getTypeMirror();
    }
    return null; // can this ever happen ??
}

その他の面倒な方法(例外なし):

private static AnnotationMirror getAnnotationMirror(TypeElement typeElement, Class<?> clazz) {
    String clazzName = clazz.getName();
    for(AnnotationMirror m : typeElement.getAnnotationMirrors()) {
        if(m.getAnnotationType().toString().equals(clazzName)) {
            return m;
        }
    }
    return null;
}

private static AnnotationValue getAnnotationValue(AnnotationMirror annotationMirror, String key) {
    for(Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : annotationMirror.getElementValues().entrySet() ) {
        if(entry.getKey().getSimpleName().toString().equals(key)) {
            return entry.getValue();
        }
    }
    return null;
}


public TypeMirror getMyValue2(TypeElement foo) {
    AnnotationMirror am = getAnnotationMirror(foo, MyAnnotation.class);
    if(am == null) {
        return null;
    }
    AnnotationValue av = getAnnotationValue(am, "myValue");
    if(av == null) {
        return null;
    } else {
        return (TypeMirror)av.getValue();
    }
}

もちろん、一度TypeMirrorを取得すると、(少なくとも私の経験では)代わりにTypeElementが必要になります。

private TypeElement asTypeElement(TypeMirror typeMirror) {
    Types TypeUtils = this.processingEnv.getTypeUtils();
    return (TypeElement)TypeUtils.asElement(typeMirror);
}

...最後の自明ではないビットは、最初にそれを分類する前に、髪を引っ張るのに1時間かかりました。これらの注釈プロセッサは実際にはまったく書くのが難しくありません。APIは最初は非常に混乱し、驚くほど冗長です。私はすべての基本的な操作を明らかにするヘルパークラスを出したくなります...しかし、それは別の日の話です(必要に応じてメッセージを送ってください)。

44
Dave Dopson

これは私のトリックです:

public class APUtils {

  @FunctionalInterface
  public interface GetClassValue {
    void execute() throws MirroredTypeException, MirroredTypesException;
  }

  public static List<? extends TypeMirror> getTypeMirrorFromAnnotationValue(GetClassValue c) {
    try {
      c.execute();
    }
    catch(MirroredTypesException ex) {
      return ex.getTypeMirrors();
    }
    return null;
  }

}

次に、任意の注釈値で使用できます。

public @interface MyAnnotationType {
  public Class<?>[] value() default {};
}

@MyAnnotationType(String.class)
public class Test { ... }

注釈プロセッサでの使用

MyAnnotationType a = element.getAnnotation(MyAnnotationType.class);
List<? extends TypeMirror> types = APUtils.getTypeMirrorFromAnnotationValue(() -> a.value());

JDK 1.8.0_192でテスト済み

1
mnesarco