前回はアノテーションの定義について解説しました。今回はアノテーションを処理する方法について解説していきます。

単純なアノテーション処理を行う

アノテーションを処理するには,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であれば,他のアノテーションプロセッサにアノテーション処理を依頼します。