IDE で注釈処理を動かすときに、 @Retention(RetentionPolicy.SOURCE)
で定義しているアノテーションの情報が、特定の条件下で取得できないことがある。
環境
OS
Windows 10
Java
openjdk 10.0.2
Eclipse
Photon (4.8.0)
※Pleiades
IntelliJ
IntelliJ IDEA 2018.2.3 (Community Edition)
Gradle
Gradle 4.10
症状
ソースコード
プロジェクト構成
|-foo/
| `-src/main/java/
| `-sample/foo/
| |-Fizz.java
| `-Buzz.java
|
|-bar/
| |-src/main/java/
| | `-sample/bar/
| | |-MyRuntimeRetention.java
| | |-MyClassRetention.java
| | |-MySourceRetention.java
| | `-MyProcessor.java
| |
| `-src/main/resources/
| `-META-INF/services
| `-javax.annotation.processing.Processor
|
|-build.gradle
`-settings.gradle
-
foo
,bar
のマルチプロジェクト -
bar
の方が注釈処理を実装したプロジェクト -
foo
はbar
の注釈処理を利用しているプロジェクト -
MyRuntimeRetention.java
,MyClassRetention.java
,MySourceRetention.java
は、それぞれ自作のアノテーションでRetention
に名前に対応する値を設定している
MyRuntimeRetention.java
package sample.bar;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRuntimeRetention {
}
MyClassRetention.java
package sample.bar;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface MyClassRetention {
}
MySourceRetention.java
package sample.bar;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface MySourceRetention {
}
-
MyProcessor.java
が注釈処理を実装したクラス
MyProcessor.java
package sample.bar;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.tools.Diagnostic.Kind;
public class MyProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
Elements elements = this.processingEnv.getElementUtils();
if (!annotations.isEmpty()) {
this.printAnnotations(elements.getTypeElement("sample.foo.Fizz"));
this.printAnnotations(elements.getTypeElement("sample.foo.Buzz"));
}
return true;
}
private void printAnnotations(TypeElement element) {
element.getAnnotationMirrors().forEach(annotation -> {
this.processingEnv.getMessager().printMessage(Kind.NOTE, element.getSimpleName() + " : " + annotation);
});
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.RELEASE_10;
}
@Override
public Set<String> getSupportedAnnotationTypes() {
return Set.of(MySourceRetention.class.getCanonicalName());
}
}
-
Fizz
,Buzz
クラスのTypeElement
を取得して、クラスに設定されているアノテーションを一覧で出力している
Fizz.java
package sample.foo;
import sample.bar.MyClassRetention;
import sample.bar.MyRuntimeRetention;
import sample.bar.MySourceRetention;
@MyRuntimeRetention
@MyClassRetention
@MySourceRetention
public class Fizz {
}
Buzz.java
package sample.foo;
import sample.bar.MyClassRetention;
import sample.bar.MyRuntimeRetention;
import sample.bar.MySourceRetention;
@MyRuntimeRetention
@MyClassRetention
@MySourceRetention
public class Buzz {
}
-
foo
プロジェクトのFizz
,Buzz
は、クラスに3つの自作アノテーションを設定している
実施する処理
この状態で Fizz
クラスだけを修正してコンパイルを実行する。
すると、 Gradle で実行したときと IDE (Eclipse, IntelliJ) で実行したときで異なる結果になる。
Gradle で動かしたとき
> gradle :foo:compileJava
注意:Fizz : @sample.bar.MyRuntimeRetention
注意:Fizz : @sample.bar.MyClassRetention
注意:Fizz : @sample.bar.MySourceRetention
注意:Buzz : @sample.bar.MyRuntimeRetention
注意:Buzz : @sample.bar.MyClassRetention
注意:Buzz : @sample.bar.MySourceRetention
- 普通にすべてのアノテーションの情報が取得できている
Eclipse で動かしたとき
-
Fizz
クラスのアノテーションはすべて取得できている - しかし、
Buzz
クラスのアノテーションは@MySourceRetention
が取得できていない
IntelliJ で動かしたとき
- こちらも Eclipse と同じで、
Buzz
クラスのアノテーション情報から@MySourceRetention
が取得できていない
Buzz も修正すると IDE でも取得できる
-
Fizz
クラスだけでなくBuzz
クラスも修正してからビルドを実行して注釈処理を動かす - すると、次のような結果になる
Eclipse
-
Buzz
に設定された@MySourceRetention
も取得できている
IntelliJ
- こちらも同様で、
@MySourceRetention
を取得できている
何が起こったか推測
-
Retention
をSOURCE
にした場合、アノテーションの情報はクラスファイルには残らない - 修正していない場合に
SOURCE
指定したアノテーションの情報だけが取得できていなかったことを鑑みるに、おそらく IDE (Eclipse, IntelliJ) で注釈処理が動いた場合は、修正が入っていないクラスの情報は以前のコンパイル結果(class ファイル)が利用されている気がする - ちなみに、 NetBeans は試していない
結論(推測)
- IDE (Eclipse, IntelliJ) で注釈処理が動くとき、修正が入っていないクラスに設定されている
Retention
がSOURCE
のアノテーションの情報は取得できないっぽい - 理由はおそらく、 IDE は修正が入っていないクラスの情報を以前のコンパイル結果(class ファイル)から取得しているっぽいから
- 自作で注釈処理を書くときは、
Retention
がSOURCE
のアノテーションの扱いには注意が必要 - アノテーションを自作しているのであれば、
Retention
をCLASS
以上にしておくのが良さげ