実行時に自前のアノテーションを処理する
サンプルコード
自前のアノテーションクラス。
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 のコンポーネントスキャンのように)。
参考資料
- The Java® Language Specification - Chapter 9. Interfaces 9.6. Annotation Types
- ClassLoader (Java SE 11 & JDK 11 )
- Java 独自のアノテーションを作成して値を取得するサンプル | ITSakura
- Javaでアノテーションを作ってみた。 - Qiita
- Javaで超簡易Webフレームワークを作ってみよう - Qiita
コンパイル時に自前のアノテーションを処理する
Lombok の例
Lombok は javac などでコンパイルする際に annotation processor を実行し、クラスにメソッドなどを追加している。
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
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