前回はアノテーションの定義について解説しました。今回はアノテーションを処理する方法について解説していきます。
単純なアノテーション処理を行う
アノテーションを処理するには,javax.annotation.processing.Processorインタフェースを実装したクラスを作成する必要があります。通常はProcessorインタフェースを実装したjavax.annotation.processing.AbstractProcessorクラスを使用します。ここではProcessorインタフェースを実装したクラスをアノテーションプロセッサと呼びます。
AbstractProcessorクラスのJavadocを見てみると,abstractで定義してあるメソッドはprocessメソッドだけです。つまり,アノテーションを処理するにはprocessメソッドをオーバーロードすればいいことが分ります。
では,ここではクラスの中に使用されているアノテーションの一覧を出力するアノテーションプロセッサを作ってみましょう。
AnnotationListingProcessor.java
以下にAnnotationListingProcessorクラスを示します。
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
@SupportedSourceVersion(SourceVersion.RELEASE_6)
@SupportedAnnotationTypes({"*"})
public class AnnotationListingProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnvironment) {
for(TypeElement annotation : annotations) {
System.out.println(annotation);
}
return true;
}
}
AbstractProcessorクラスのサブクラスを作成するには青字で示した2つのアノテーションを付加する必要があります。
javax.annotation.processing.SupportedSourceVersionアノテーションは,アノテーションプロセッサがサポートするソースのバージョンを示します。SupportedSourceVersionアノテーションのvalue要素は値にjavax.lang.model.SourceVersion列挙型で定義された定数を使用します。
Java SE 6まで対応するのであれば,SourceVersion.RELEASE_6,J2SE 5.0まで対応するのであればSourceVersion.RELEASE_5と記述します。
javax.annotation.processing.SupportedAnnotationTypeアノテーションは,アノテーションプロセッサが処理を行うことのできるアノテーションを記述します。value要素は文字列の配列です。ここに,対応できるアノテーションをパッケージも含めた文字列として列挙します。
すべてのアノテーションに対応する場合,アスタリスクを使用して"*"と記述します。 実際にアノテーションを処理するのがprocessメソッドです。
processメソッドの第1引数が処理するアノテーションの情報を保持するjavax.lang.model.element.TypeElementインタフェースのセットとなります。このTypeElementインタフェースについては後述します。
第2引数の型はjavax.annotation.processing.RoundEnvironmentインタフェースです。アノテーション処理に関する情報の問い合わせをRoundEnvironmentインタフェースに対して行うことができます。
これ以外にも,Environmentという名前のつくProcessingEnvironmentインタフェースが提供されています。これら2つのインタフェースの違いは何なのでしょう。
両者の違いは,アノテーションの処理が1度で終わらせられないことにあります。
アノテーション処理を行うことで新たにソースファイルを作成することがよくあります。たとえば,JAX-WSは,様々なアノテーションからサービス用のクラスやファクトリクラスなどを生成していました。このように生成したソースコードの中にも再びアノテーションが含まれているかもしれません。そのため,アノテーションプロセッサは,生成したソースコードに対しても再びアノテーション処理を行います。
このように,アノテーション処理のはじめの処理は1ラウンド,次の生成されたアノテーションに対する処理は2ラウンドと,複数回のラウンドで構成されます。ラウンドの都度,processメソッドがコールされます。そして,各ラウンドの情報を保持するのがRoundEnvironmentインタフェースなのです。
一方のProcessingEnvironmentインタフェースは一連のラウンドを含めた全体のアノテーション処理の情報を保持しています。
processメソッドの中身は,赤字で示したように引数のannotationsを出力しているだけです。これで,使用されているアノテーションが分ります。
processメソッドの戻り値はアノテーションプロセッサが与えられたアノテーションを処理するかどうかを示します。trueを戻せば,このアノテーションプロセッサがannotations引数で提示されたアノテーションを処理することを意味します。
アノテーションプロセッサは複数使用することができます。processメソッドの戻り値がtrueであれば,同じアノテーションを処理できる他のアノテーションプロセッサがあったとしても,処理を依頼することはありません。
逆に戻り値がfalseであれば,他のアノテーションプロセッサにアノテーション処理を依頼します。