Help us understand the problem. What is going on with this article?

Java で自前のアノテーションを処理する

More than 1 year has passed since last update.

実行時に自前のアノテーションを処理する

サンプルコード

自前のアノテーションクラス。

package com.example;

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

// 実行時にアノテーションを使う
@Retention(RetentionPolicy.RUNTIME)
// アノテーションを型(クラスなど)、フィールド、メソッドに適用する
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
// 自前のアノテーションを定義する
public @interface MyAnnotation {
  String value() default "";
  String myvalue() default "";
}

アノテーションを使うクラス。

package com.example;

@MyAnnotation("サンプルクラス")
public class Sample {

  @MyAnnotation("サンプルフィールド")
  public String sampleField = "サンプルフィールドの値";

  @MyAnnotation(value = "サンプルメソッド", myvalue = "サンプルメソッドのmyvalue")
  public void sampleMethod() {
    System.out.println("サンプルメソッドを実行");
  }
}

メインクラス。

package com.example;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class Main {

  public static void main(String[] args) {

    // サンプルクラスを普通に使う
    Sample sample = new Sample();
    System.out.println(sample.sampleField);
    sample.sampleMethod();

    // サンプルクラスのアノテーション情報を出力する
    print(Sample.class);
  }

  private static void print(Class cls) {
    print(cls.getAnnotations());
    Field[] fields = cls.getFields();
    for (Field f : fields) {
      print(f.getAnnotations());
    }
    Method[] methods = cls.getMethods();
    for (Method m : methods) {
      print(m.getAnnotations());
    }
  }

  private static void print(Annotation[] annotations) {
    for (Annotation a : annotations) {
      // 今回取得したいアノテーションは com.example パッケージに属するものだけ
      if (a.annotationType().getPackageName().equals("com.example")) {
        System.out.println(a);
      }
    }
  }
}

実行結果。

サンプルフィールドの値
サンプルメソッドを実行
@com.example.MyAnnotation(value="サンプルクラス", myvalue="")
@com.example.MyAnnotation(value="サンプルフィールド", myvalue="")
@com.example.MyAnnotation(value="サンプルメソッド", myvalue="サンプルメソッドのmyvalue")

今回は Sample.class というクラスを指定してアノテーションを取得したが、 ClassLoader 等を利用して .class や .jar ファイルの場所を取得しすべてのクラスを探索することも可能 (Spring Framework のコンポーネントスキャンのように)。

参考資料

コンパイル時に自前のアノテーションを処理する

Lombok の例

Lombok は javac などでコンパイルする際に annotation processor を実行し、クラスにメソッドなどを追加している。

Lombok Execution Path

Lombok is on the classpath, and javac will load every META-INF/services/javax.annotation.processing.Processor file on the classpath it can find, reading each line and loading that class, then executing it as an annotation processor. lombok.jar has this file, it lists lombok.launch.AnnotationProcessorHider$AnnotationProcessor as entry.

Project Lombok: Creating Custom Transformations

Project Lombok runs as an annotation processor. The annotation processor acts as a dispatcher that delegates to Lombok annotation handlers (this is what we're going to create).

誰も教えてくれなかった Lombok のこと (ja) - notepad

コンパイラがソースファイルを読み込んでAST(Abstract Sintax Tree; 抽象構文木)という内部形式に変換して、本来ならそのままコンパイルを行うべきところを、LombokがASTを横取りして定型コードを付け加えた上で、変更後のASTでコンパイルを行います。

java - How does lombok work? - Stack Overflow

Note that lombok.jar contains a file named /META-INF/services/javax.annotation.processing.Processor. When javac sees this file in a compilation classpath, it runs annotation processors defined there during compilation.

Lombok の Ant 用ビルド設定ファイルを見ると META-INF/services/javax.annotation.processing.Processor を出力しているのがわかる。

lombok/build.xml at master · rzwitserloot/lombok · GitHub

<echo file="build/lombok/META-INF/services/javax.annotation.processing.Processor">lombok.launch.AnnotationProcessorHider$AnnotationProcessor
lombok.launch.AnnotationProcessorHider$ClaimingProcessor</echo>

Lombok には AnnotationProcessor クラスが用意されている。

lombok/AnnotationProcessor.java at master · rzwitserloot/lombok · GitHub

Project Lombok によるカスタム AST 変換

Lombok は、よく使用されている 2 つの Java コンパイラー、javac と ECJ (Eclipse Compiler for Java) をサポートします。この 2 つのコンパイラーは同じような出力を生成しますが、それぞれの実装はまったく異なります。そのため、Lombok にはアノテーション・ハンドラー (Lombok に組み込まれる、コード生成ロジックが含まれるコード) のセットが 2 つ付属しています (それぞれのコンパイラー用に 1 つのセット)。

Lombok の java 用のハンドラーパッケージは lombok.javac.handlers となっている。

lombok/src/core/lombok/javac/handlers at master · rzwitserloot/lombok · GitHub

参考資料

niwasawa
迷子になりがちな地図・位置情報系プログラマ。
http://niwasawa.hatenablog.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした