LoginSignup
1
0

More than 3 years have passed since last update.

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

Last updated at Posted at 2019-08-17

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

サンプルコード

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

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

参考資料

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0