1。いくつかの基本概念
開始する前に、重要なことを宣言する必要があります。実行時に反射メカニズムを介して処理される注釈について議論するのではなく、コンパイル時に処理される注釈について説明します。
コンパイル時間の注釈とランタイムアノテーションの違いは何ですか?実際、それは主にパフォーマンスの問題のために大きくありません。ランタイムアノテーションは主に反射に完全に依存しており、反射の効率はネイティブのものよりも遅いため、メモリが少なく、CPUが低いマシンにいくつかの遅れがあります。ただし、編集プロセス(java-> class)では、いくつかのクラスまたはファイルを動的に生成するためにいくつかの注釈マークが使用されるため、APK操作とは何の関係もないため、注釈はまったくこの問題を抱えていません。したがって、より有名なオープンソースプロジェクトが注釈機能を使用する場合、通常、コンパイル時の注釈を使用します。
注釈プロセッサは、編集期間中に注釈情報をスキャンおよび処理するために使用されるJavacに付属するツールです。特定の注釈のために独自の注釈プロセッサを登録できます。ここでは、注釈が何であり、それらをカスタマイズする方法をすでに理解していると思います。まだ注釈を理解していない場合は、公式のドキュメントを確認できます。注釈プロセッサはすでにJava 5に存在していましたが、Java 6(2006年12月にリリース)までAPIはありませんでした。 Javaユーザーが注釈プロセッサのパワーに気付くまでにはしばらく時間がかかりました。したがって、近年人気が高まっています。
特定の注釈を備えたプロセッサは、Javaソースコード(またはコンパイルされたBytecode)を入力として取得し、いくつかのファイル(通常.javaファイル)を出力として生成します。それはどういう意味ですか? Javaコードを生成できます!これらのJavaコードは、生成された.javaファイルにあります。したがって、メソッドの追加など、既存のJavaクラスを変更することはできません。これらの生成されたJavaファイルは、他の手動で書かれたJavaソースコードのようにJavacによってコンパイルされます。
注釈処理は、コンピレーション段階で実行されます。その原則は、Javaソースコードを読み、注釈を解析し、新しいJavaコードを生成することです。新しく生成されたJavaコードは、最終的にJava Bytecodeにコンパイルされます。注釈プロセッサは、Javaメソッドの追加または削除など、読み取りJavaクラスを変更することはできません。
2。AbstractProcessor
プロセッサのAPIを見てみましょう。以下に示すように、すべてのプロセッサが抽象プロセッサを継承します。
パッケージcom.example; import java.util.linkedhashset; import java.util.set; import javax.annotation.processing.abstractprocessor; Import javax.annotation.processing.processingenvironment; Import javax.annotation.Rondenvironment; Import; javax.annotation.processing.supportedAnnotationTypes;インポートjavax.annotation.annotation.phportedsourceversion; Import javax.lang.model.sourceversion; Import javax.lang.model.element.typeelement; Public Class Public Procuss TypeLement> Ancountements、Rundenvironment env){return false; } @Override public set <string> getSupportedAnnotationTypes(){set <string> annotataions = new linkedhashset <string>(); annotataions.add( "com.example.myannotation"); Annotataionsを返します。 } @Override public sourceVersion getSupportedSourceversion(){return sourceversion.latestSupported(); } @Override public同期void init(processEnvironment processenv){super.init(processenv); }}init(processenvironment processenv):すべての注釈プロセッサクラスには、パラメーターのないコンストラクターが必要です。ただし、特別なメソッドinit()があり、これは注釈処理ツールによって呼び出され、ProcessEnvironmentをパラメーターとして使用します。 ProcessEnvironmentは、いくつかの実用的なツールクラスの要素、タイプ、およびファイラーを提供します。後で使用します。
process(Set<? extends TypeElement> annoations, RoundEnvironment env) :これは、各プロセッサのメイン()メソッドに似ています。この方法で、スキャン、注釈の処理、Javaファイルの生成をエンコードおよび実装できます。 roundenvironmentパラメーターを使用して、特定の注釈で注釈された要素をクエリすることができます(元のテキスト:特定の注釈で注釈された要素をクエリすることができます)。後で詳細を確認します。
getSupportedAnnotationTypes():この方法では、注釈プロセッサがどのアノテーションを登録するかを指定する必要があります。その返品値は、注釈プロセッサが処理したいアノテーションタイプのフルネームを含む文字列コレクションであることに注意してください。言い換えれば、ここでは、注釈プロセッサが処理する注釈を定義します。
getSupportedSourceVersion() :使用するJavaバージョンを指定するために使用されます。通常、 SourceVersion.latestSupported()を返す必要があります。ただし、Java 6に固執する十分な理由がある場合は、 SourceVersion.RELEASE_6を返すこともできます。 SourceVersion.latestSupported()を使用することをお勧めします。 Java 7では、注釈を使用して、以下に示すように、 getSupportedAnnotationTypes()およびgetSupportedSourceVersion()を書き換えることもできます。
@supportedsourceversion(value = sourceversion.release_7)@supportedannotationtypes({//完全なqulifiedアノテーションタイプ名「com.example.myannotation "、" com.example.anotherannotation "})Public Class MyProcess rundenvironment env){falseを返します。 } @Override public同期void init(processEnvironment processenv){super.init(processenv); }}特にAndroidの互換性の問題により、 @SupportedAnnotationTypesと@SupportedSourceVersionを使用する代わりに、 getSupportedAnnotationTypes()およびgetSupportedSourceVersion()を書き換えることをお勧めします。
次に知っておくべきことは、注釈プロセッサが独自のJVMで実行されることです。はい、あなたはそれを正しく読みます。 Javacは、Annotationプロセッサを実行するために完全なJava仮想マシンを起動します。それはどういう意味ですか?通常のJavaプログラムで使用するものは何でも使用できます。グアバを使って! Daggerや、使用する他のクラスライブラリなどの依存関係噴射ツールを使用できます。ただし、たとえそれが単なるプロセッサであっても、他のJavaプログラムを開発するのと同じように、効率的なアルゴリズムと設計パターンの使用に注意する必要があることを忘れないでください。
3.プロセッサを登録します
「私の注釈プロセッサをJavacに登録する方法」を尋ねるかもしれません。 .jarファイルを提供する必要があります。他の.jarファイルと同じように、すでにコンパイルされている注釈プロセッサをこのファイルにパッケージ化します。また、.jarファイルでは、特別なファイルjavax.annotation.processing.processorをMeta-inf/servicesディレクトリにパッケージ化する必要があります。したがって、あなたの.jarファイルディレクトリ構造は次のようになります:
myProcess.jar -com -example -myProcess.class -meta -inf -services -javax.annotation.processing.processor
javax.annotation.processing.processorファイルの内容はリストであり、各行は注釈プロセッサのフルネームです。例えば:
com.example.myprocess
com.example.anotherProcess
4。例:工場モデル
私たちが解決したい問題は、ピザストアを実装したいということです。ピザストアは、2つのピザ(MargheritaとCalzone)とデザートティラミス(ティラミス)を顧客に提供します。
パブリックインターフェイスミール{public float getPrice();} public class margheritapizzaは食事を実装します{@override public float getPrice(){return 6.0f; }} public class calzonepizzaは食事を実装します{@override public float getPrice(){return 8.5f; }} public class tiramisuは食事を実装します{@override public float getPrice(){return 4.5f; }} public class pizzastore {public Meal Order(String Mealname){if(null == Mealname){Throw New IllegalargumentException( "Meal of Meal is null!"); } if( "margherita" .equals(mealname)){return new margheritapizza(); } if( "calzone" .equals(mealname)){return new calzonepizza(); } if( "tiramisu" .equals(mealname)){return new Tiramisu(); }新しいIllegalargumentException(「不明な食事」 " + Mealname +" '")を投げます。 } private static string readconsole(){scanner scanner = new Scanner(system.in); string meal = scanner.nextline(); scanner.close();食事を返します。 } public static void main(string [] args){system.out.println( "Pizza Storeへようこそ"); Pizzastore Pizzastore = new Pizzastore();食事の食事= pizzastore.order(readconsole()); System.out.println( "bill:$" + meal.getPrice()); }}ご覧のとおり、Order()メソッドでは、条件判断ステートメントの場合は多くあります。そして、新しいピザを追加する場合、条件付きの新しい判断を追加する必要があります。ただし、アノテーションプロセッサとファクトリーモードを使用して、注釈プロセッサにこれらを生成してもらうことができます。このようにして、私たちが望むコードは次のようになります:
Public Class Pizzastore {Private MealFactory Factory = new MealFactory();パブリックミールオーダー(String Mealname){return Factory.create(Mealname); } private static string readconsole(){scanner scanner = new Scanner(system.in); string meal = scanner.nextline(); scanner.close();食事を返します。 } public static void main(string [] args){system.out.println( "Pizza Storeへようこそ"); Pizzastore Pizzastore = new Pizzastore();食事の食事= pizzastore.order(readconsole()); System.out.println( "bill:$" + meal.getPrice()); }} public class mealfactory {public Meal create(string id){if(id == null){throw new IllegalargumentException( "id is null!"); } if( "calzone" .equals(id)){return new calzonepizza(); } if( "tiramisu" .equals(id)){return new Tiramisu(); } if( "margherita" .equals(id)){return new margheritapizza(); }新しいIllegalargumentException( "不明id =" + id); }}5。@Factory Annotation
アノテーションプロセッサを使用してMealeFactoryクラスを生成する予定であると推測できますか。より一般的には、工場のクラスを生成するための注釈とプロセッサを提供したいと考えています。
@Factoryアノテーションを見てみましょう:
@target(elementtype.type)@retention(retentionPolicy.class)public @interface Factory { / ** * Factory * / class <?> type(); / ** *どのアイテムをインスタンス化するかを決定するための識別子 */ string id();}アイデアは次のとおりです。これらの食品クラスに注釈を付け、Type()を使用して、このクラスがどの工場に属しているかを示し、ID()を使用してこのクラスの特定のタイプを示します。これらのクラスに@Factoryアノテーションを適用しましょう。
@factory(type = margheritapizza.class、id = "margherita")パブリッククラスmargheritapizzaは食事を実装します{@override public float getprice(){return 6.0f; }} @factory(type = calzonepizza.class、id = "calzone")public class calzonepizzaは食事を実装します{@override public float getprice(){return 8.5f; }} @factory(type = tiramisu.class、id = "tiramisu")Public class Tiramisuは食事を実装します{@Override public float getPrice(){return 4.5f; }}あなたが尋ねるかもしれません、私たちは食事インターフェイスに@Factoryアノテーションを適用するだけですか?答えはいいえです。注釈は継承できないからです。つまり、クラスXに注釈がある場合、クラスYはXを拡張し、クラスYはクラスXで注釈を継承しません。プロセッサを書く前に、いくつかのルールを明確にする必要があります。
注釈プロセッサ:
Public Class FactoryProcessorは、AbstractProcessor {private Types typeutils;プライベートエレメントElementutils;プライベートファイラーファイラー。プライベートメッサーメッサー。プライベートマップ<string、factorygroupedclasses> factoryclasses = new linkedhashmap <string、factorygroupedclasses>(); @Override public同期void init(processenvironment processenv){super.init(processenv); typeutils = processenv.getTypeutils(); elementutils = processenv.getElementutils(); filer = processenv.getFiler(); messager = processenv.getMessager(); } @Override public booleanプロセス(set <?extends typeLement> arg0、roundenvironment arg1){... return false; } @Override public set <string> getSupportedAnnotationTypes(){set <string> annotataions = new linkedhashset <string>(); annotataions.add(factory.class.getCanonicalName()); Annotataionsを返します。 } @Override public sourceVersion getSupportedSourceversion(){return sourceversion.latestSupported(); }} getSupportedAnnotationTypes()メソッドでは、@Factoryアノテーションがこのプロセッサによって処理されることを指定します。
要約します
上記は、この記事のコンテンツ全体です。この記事の内容には、すべての人の研究や仕事に特定の参照値があることを願っています。ご質問がある場合は、メッセージを残してコミュニケーションをとることができます。 wulin.comへのご支援ありがとうございます。