Java

注釈処理ツール (apt) 入門

apt 目次

apt とは何か

apt はコマンド行ユーティリティで、注釈処理ツールです。検証が行われている一連の指定されたソースファイルに存在する注釈に基づいて、「注釈プロセッサ」 を検出し実行します。注釈プロセッサは一連の関連する API とサポートするインフラストラクチャにより、注釈 (JSR 175) プログラムを処理します。aptに関連する API により、プログラム構造を、ビルド時に、ソースベースまた読み取り専用で見ることが可能になります。これらの関連する API は、汎用 (JSR 14) プログラムの追加後に、JavaTM プログラム言語の型システムを正常にモデル化するために設計されました。apt は、最初に新しいソースコードと他のファイルを作成する注釈プロセッサを実行します。次に、元のファイルと作成されたソースファイルのコンパイルを行います。これにより開発サイクルは簡素化されます。

apt を使用する理由

注釈の使用が必要となるのは、新しい派生ファイル (ソースファイル、クラスファイル、配備記述子など) の作成に使用される情報を保持するベースファイルに注釈があり、ベースファイルとその注釈に論理的に一貫性がある場合です。つまり、一連のファイルすべての一貫性を手動で維持するのではなく、派生ファイルはベースファイルから作成されるので、ベースファイルだけを維持する必要があるということです。apt ツールは派生ファイルを作成することを目的として設計されました。

注釈に基づいて派生ファイルを生成する場合、ドックレットと比較すると apt には次の利点があります。

注釈処理の目的以外に、apt は他の関連するプログラミングタスクに対しても使用できます。

apt の使用方法

概要

まず、apt は、操作されているソースコードに存在する注釈を決定します。次に、記述した「注釈プロセッサファクトリ」を検索します。ファクトリで処理されている注釈が確認されます。次に、ファクトリが操作されているソースファイルに存在する注釈を処理する場合、「注釈プロセッサ」を渡すようにファクトリに求めます。次に、注釈プロセッサが実行されます。プロセッサに生成された新しいソースファイルがある場合、apt は新しいソースファイルが生成されないようになるまで、この処理を繰り返します。

注釈プロセッサの開発

注釈プロセッサの書き込みは、次の 4 つのパッケージに依存します。 各プロセッサは、com.sun.mirror.apt パッケージの AnnotationProcessor インタフェースを実装します。このインタフェースにはプロセッサを呼び出すために、apt ツールにより使用される、1 つのメソッド process があります。プロセッサはひとつまたは複数の注釈型を処理します。

プロセッサのインスタンスが、関連するファクトリ AnnotationProcessorFactory により返されます。apt ツールはファクトリの getProcessorFor メソッドを呼び出しプロセッサを保持します。この呼び出しの間に、ツールは AnnotationProcessorEnvironment を提供します。この環境では、プロセッサは起動するために必要なすべての情報を検出します。この情報には、操作しているプログラム構造の参照、新しいファイルの作成および警告とエラーメッセージを渡すことにより、apt ツールと通信および連携する方法が含まれます。

ファクトリを見るには 2 つの方法があります。使用するファクトリは「-factory」コマンド行オプションを使用して指定できます。またはファクトリは、apt discovery プロシージャ中に格納することが可能です。「-factory」オプションを使用すると、既知の単一のファクトリをもっとも簡単に実行できます。このオプションはファクトリが実行する方法をより制御する必要がある場合にも使用されます。特定のパスにおいてファクトリを検出するには、jar ファイル META-INF/services 情報により取得される検出プロシージャは、次に示すフォーマットに従います。

-factory」オプションを使用する注釈プロセッサを作成し使用するには、次の手順で行います。

  1. AnnotationProcessorFactory を記述します。これにより、対象の注釈タイプに対する AnnotationProcessor を作成できます。
  2. クラスパス tools.jar とともに javac を使用するプロセッサおよびファクトリをコンパイルします。tools.jar には、com.sun.mirror.* インタフェースが含まれます。
  3. コンパイルされたクラスファイル、またはクラスファイルを含む jar ファイルを、apt を呼び出す場合に適切なパスに置きます。
デフォルトの検出プロシージャとともに注釈プロセッサを作成し使用するには、最初の 2 つのステップを使用します。
  1. META-INF/services で、com.sun.mirror.apt.AnnotationProcessorFactory という名前の UTF-8 で符号化されたテキストファイルを作成します。その内容は、各行が固定ファクトリクラスの完全指定名のリストです (sun.misc.Service により使用されるリストと同じ形式です)。
  2. ファクトリ、プロセッサ、および META-INF/services 情報を jar ファイルにパッケージします。
  3. jar ファイルを apt を呼び出す場合に適切な場所に置きます。適切なパスについては、「検出」 で説明します。

注釈プロセッサの簡単な例

import com.sun.mirror.apt.*;
import com.sun.mirror.declaration.*;
import com.sun.mirror.type.*;
import com.sun.mirror.util.*;

import java.util.Collection;
import java.util.Set;
import java.util.Arrays;

import static java.util.Collections.*;
import static com.sun.mirror.util.DeclarationVisitors.*;

/*
 * This class is used to run an annotation processor that lists class
 * names.  The functionality of the processor is analogous to the
 * ListClass doclet in the Doclet Overview.
 */
public class ListClassApf implements AnnotationProcessorFactory {
    // Process any set of annotations
    private static final Collection<String> supportedAnnotations
        = unmodifiableCollection(Arrays.asList("*"));

    // No supported options
    private static final Collection<String> supportedOptions = emptySet();

    public Collection<String> supportedAnnotationTypes() {
        return supportedAnnotations;
    }

    public Collection<String> supportedOptions() {
        return supportedOptions;
    }

    public AnnotationProcessor getProcessorFor(
            Set<AnnotationTypeDeclaration> atds,
            AnnotationProcessorEnvironment env) {
        return new ListClassAp(env);
    }

    private static class ListClassAp implements AnnotationProcessor {
        private final AnnotationProcessorEnvironment env;
        ListClassAp(AnnotationProcessorEnvironment env) {
            this.env = env;
        }

        public void process() {
	    for (TypeDeclaration typeDecl : env.getSpecifiedTypeDeclarations())
		typeDecl.accept(getDeclarationScanner(new ListClassVisitor(),
						      NO_OP));
        }

	private static class ListClassVisitor extends SimpleDeclarationVisitor {
	    public void visitClassDeclaration(ClassDeclaration d) {
		System.out.println(d.getQualifiedName());
	    }
	}
    }
}

このプロセッサの例では、数多くの新しい言語とライブラリの特徴が使用されています。まず、static インポートが使用され、さまざまなユーティリティメソッドで単純名が使用できます。たとえば
unmodifiableCollection

Collections.unmodifiableCollection」の代わりに使用されています。
次に、一般的なコレクションが全体で使用されています。Arrays.asList メソッドは、var-args メソッドであるために、コンマ区切りの文字列のリストを受け取り、目的の要素を使用するリストを作成できます。Collections.emptySet メソッドは一般的なメソッドであり、型保証された空のセットを作成するために使用できます。プロセスメソッドの for ループは、コレクションに対する繰り返し処理を行う、拡張された for ループです。

注釈をプロセスに指定する

処理している注釈をツールに指示するために、この例に示されるように、ファクトリは import 型の文字列のコレクションを返します。特定の文字列のエントリには次の 3 つのうち 1 つの形式があります。
*
「すべて」の注釈を処理する。これは注釈のリストが空の場合にも処理する。つまり、* を処理するファクトリは、注釈が存在しない場合でも重要なプロセッサを要求される可能性がある。この機能により com.sun.mirror API は、一般的なソースコード処理ツールを記述するために使用できる
foo.bar.Baz
標準的な名前が「foo.bar.Baz」という注釈を処理する
foo.bar.*
標準的な名前が「foo.bar.」で始まる注釈を処理する

apt ツールは処理するファクトリに対して一連の注釈を示します。一連の注釈と注釈プロセッサ環境に基づいて、ファクトリは単一の注釈プロセッサを返します。ファクトリが複数の注釈プロセッサを返すように指定する場合はどうなるのでしょうか。ファクトリは com.sun.mirror.apt.AnnotationProcessors.getCompositeAnnotationProcessor を使用して、複数の注釈プロセッサの操作を組み合わせて配列できます。

認識するコマンド行オプションを指定する

supportedOptions メソッドにより、ファクトリは認識するコマンド行オプションの apt と通信します。「-A」で始まるコマンド行オプションは、注釈プロセッサと通信するために予約されています。たとえば、このファクトリが -Adebug および -Aloglevel=3 などのオプションを認識する場合、「-Adebug」および「-Aloglevel」の文字列を返します。後に、ファクトリが認識しない -A オプションが指定された場合、apt はその通知を行います。

apt コマンド行

独自のオプションに加えて、apt ツールは javac が受け入れるすべてのコマンド行オプションを受け入れます。javac オプションが存在する場合は、最後の javac の呼び出しに渡されます。

apt に固有のオプションを次に示します。

-s dir
プロセッサが生成したソースファイルが配置されるディレクトリのルートを指定する。ファイルはパッケージ名前空間に基づいてサブディレクトリに配置される
-nocompile
ソースファイルをクラスファイルに対してコンパイルしない
-print
指定した型のテキスト表現を出力し、注釈プロセスまたはコンパイルを行わない
-A[key[=val]]
注釈プロセッサに渡されるオプション。これらは直接 apt により解釈されないが、個々のプロセッサにより使用可能にされる
-factorypath path
注釈プロセッサファクトリの検索先を指定する。このオプションが使用されると、クラスパスはファクトリを検索「しない」
-factory classname
使用する AnnotationProcessorFactory 名。デフォルトの検出プロセスをバイパスする
apt が、javac のオプションのいくつかを共有する方法を次に示します。
-d dir
プロセッサと javac が生成するクラスファイルを配置する場所を指定する
-cp path or -classpath path
ユーザクラスファイルおよび注釈プロセッサファクトリを検出する場所を指定する。-factorypath が与えられた場合、クラスパスはファクトリを検索しない
デバッグに有用な、apt の隠しオプションを次に示します。
-XListAnnotationTypes
注釈の型に検出されるリスト
-XListDeclarations
指定および宣言がインクルードされるリスト
-XPrintAptRounds
初期および再帰的な apt ラウンドに関する情報を出力する
-XPrintFactoryInfo
処理を要求するファクトリの注釈に関する情報を出力する

apt ツールの動作方法

検出

コマンド行に存在する注釈を決定するために、ソースファイルをスキャンした後に、デフォルトでは、apt ツールは適切なパスに存在する注釈プロセッサファクトリを検索します。-factorypath オプションが使用された場合、そのパスがファクトリを検索するための適切なパスになります。それ以外では、クラスパスが適切なパスになります。ファクトリが処理する注釈を決定するために照会されます。ファクトリが存在する注釈の 1 つに処理する場合、その注釈が要求済みであるとみなされます。すべての注釈が要求されると、ツールは追加のファクトリを検索しません。注釈がすべて要求された後、またはさらなるファクトリが検索できない場合、apt はファクトリの getProcessorFor メソッドを呼び出し、そのファクトリが要求した一連の注釈を渡します。各ファクトリは単一のプロセッサを返し、該当する一連の注釈に対する適切な処理を行います。すべてのプロセッサが返された後、apt は順番に各プロセッサを呼び出します。プロセッサが新しいソースファイルを生成した場合、apt の再帰的なラウンドが発生します。再帰的な apt ラウンドでは、ファクトリが現在の注釈を処理していなくても、検出プロシージャは以前のラウンドでプロセッサを供給したファクトリで getProcessorFor を呼び出します。これにより、ファクトリが後続の apt ラウンドでリスナーを登録できるようになります。ただしほとんどのファクトリは、この場合単純に AnnotationProcessors.NO_OP を返します。新しいソースファイルが生成されなかったラウンドの後に、apt は元のおよび生成されたソースファイル上で、javac を呼び出します。プロセッサが検出されない、またはプロセッサが既存の注釈を処理できない場合、apt を呼び出すことは、ソースファイル上で直接 javac を呼び出すことと本質的には同じです。

ファクトリクラスが注釈処理の複数のラウンドで使用される場合、ファクトリクラスは 1 回ロードされ、ファクトリの getProcessorFor メソッドがラウンドごとに 1 回呼び出されます。これによりファクトリはラウンドを越えて静的状態を格納できます。

-factory オプションが使用されている場合、指定されたファクトリだけが照会されます。

apt 処理のラウンド

apt の最初のラウンドは、入力ソースファイルを分析し、検出プロシージャを実行し、出力される注釈プロセッサを呼び出します。apt の 2 番目のラウンドでは、最初のラウンド (存在する場合) で生成された新しいソースファイルを分析し、それらの新しいファイルの検出プロシージャを実行し、出力される注釈プロセッサを呼び出します。同様に、2 番目のラウンドが新しいソースファイルを生成した場合、3 番目のラウンドは新しいソースを分析し、検出プロシージャを実行します。apt ラウンドは新しいソースファイルが生成されなくなるまで続きます。デフォルトでは、最後のラウンドの後に、apt ツールが元のソースファイルおよび生成されたソースファイル上で javac を実行します。

リスナー

注釈プロセッサまたはファクトリは、その環境において addListener メソッドを使用して、ラウンドの最後にリスナーを登録できます。ツールは登録されたリスナーを、そのラウンドに対するすべての注釈プロセッサが完了した時点で呼び出します。リスナーはラウンドのステータスについての情報を渡します。その情報には、新しいソースファイルが記述された場合、エラーが発生した場合、完成したラウンドが最後のラウンドである場合などがあります。リスナーはすべての注釈プロセスが完了した場合に、最後のファイルの末尾を書き出すために使用されることがあります。同じクラスは AnnotationProcessor および RoundCompleteListener の両インタフェースに実装できるために、同じオブジェクトが両コンテキスト内で機能します。

リターンコード

最後の apt ラウンドの後に javac が呼び出された場合、apt のリターンコードは、それらのファイルをコンパイルする javac のリターンコードになります。javac が呼び出されなかった場合、apt は、ツール自体またはプロセッサによりエラーが報告されなかった場合には 0 の終了状態になります。不正なソースファイルまたは不完全なソースファイル自体の操作では 0 以外の終了状態にはなりません。

宣言と型

API のミラーは、主に Declaration インタフェースと、com.sun.mirror.declaration パッケージのサブインタフェースの階層を介して、ソースコードを構築することを表しています。Declaration は、パッケージ、クラス、メソッドなどのプログラムの要素を表し、通常ソースコードの特定の部分に、1 対 1 で対応します。Declarations は注釈される構造です。

型は TypeMirror インタフェース、および com.sun.mirror.type パッケージ内のサブインタフェースの階層により表されます。型にはプリミティブ型、クラスおよびインタフェース型、配列型、型変数、およびワイルドカード型が含まれます。

API は宣言と型を慎重に区別します。これは 1 つの宣言がすべてのファミリの型を定義する、汎用データ型においてもっとも重要です。たとえば、java.util.Set クラスの宣言は以下に対応します。

宣言には、ドキュメントコメント、ソースの位置、修飾子、および注釈が含まれます。宣言には異なる種類の名前 (単純、指定) が含まれる可能性があります。より特定した宣言サブクラスにより、その構造に適切な追加の情報が与えられます。たとえば、クラス宣言により、コンストラクタおよびスーパークラスへのアクセスが可能になります。enum に対する宣言には、enum 定数に提供するメソッドが含まれています。

TypeMirror はソースコードの戻り値の型、パラメータ型などをモデル化するために使用されます。参照型に対する TypeMirror は、型から対応する宣言へのマッピングを行います。たとえば、java.util.Set<String> に対するミラー型から java.util.Set に対する宣言へのマッピングを行います。


FAQ


関連項目


Copyright © 2004 Sun Microsystems, Inc.All Rights Reserved. Sun