APT、APTってよく言うけど、いまだにAPTって言葉使ってるの日本人だけっぽいよ。
ということで Annotation Processing 、あるいは JSR269 と呼ぼう。。
- ソースのコンパイル前に何かを処理する機構
- 実行時の動的処理から、実行前の静的処理に
//下記クラスの name フィールドを取得する場合、
public class Hoge extends Entity {
private String name;
}
//リフレクションでは、フィールド名を文字列で指定
Field field = Hoge.class.getField("name")
//APでは、専用メソッドを持つクラスを用意できる(HogeMetaクラスをAPで生成)
Field field = HogeMeta.getNameField();
- EntityをJson化する際のDTOクラスの自動生成
Hoge hoge = new Hoge();
//エンティティをDTO変換するクラスを生成(HogeDto)
HogeDto dto = HogeDto.create(hoge);
- Application.confのアクセサクラスとか
String value = Play.configuration.getProperty("adminUserName");
//ではなく、以下のように取得できるクラスを生成(PlayConf)
String value = PlayConf.getAdminUserName();
- Maven インストール
$ brew install homebrew/versions/maven32
- Java8 maven archetype インストール
$ git clone https://github.com/asufana/my-maven-archetype
$ cd my-maven-archetype
$ mvn install
- プロジェクト雛形作成
$ mvn archetype:generate -DinteractiveMode=false -DarchetypeGroupId=com.github.asufana -DarchetypeArtifactId=my-maven-archetype -DgroupId=com.github.YOURNAME -DartifactId=my-annotation-processor
- Eclipse 設定ファイル生成
$ cd my-annotation-processor
$ mvn eclipse:eclipse
Eclipse 上で Import > Maven - Existing Maven Projects にて、プロジェクトインポート
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.github.YOURNAME</groupId>
<artifactId>my-annotation-processor</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<!-- disable annotation processing -->
<compilerArgument>-proc:none</compilerArgument>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
<dependency>
<groupId>com.squareup</groupId>
<artifactId>javapoet</artifactId>
<version>1.5.1</version>
</dependency>
</dependencies>
</project>
mvn eclipse:eclipse
にて依存ライブラリの取得と Eclipse 定義の更新をしておく。
src/main/java に com.github.YOURNAME.MyAnnotaionProcessor.java を追加
package com.github.YOURNAME;
import java.io.*;
import java.util.*;
import javax.annotation.processing.*;
import javax.lang.model.*;
import javax.lang.model.element.*;
import javax.tools.Diagnostic.Kind;
import javax.tools.*;
import com.squareup.javapoet.*;
@SupportedAnnotationTypes("*")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyAnnotationProcessor extends AbstractProcessor {
private void log(final String msg) {
final Messager messager = processingEnv.getMessager();
messager.printMessage(Kind.NOTE, msg);
}
@Override
public boolean process(final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
if (annotations.size() == 0) {
return true;
}
log("PlayConfAnnotationProcessor");
/** クラス定義 */
final TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.build();
final JavaFile javaFile = JavaFile.builder("generated", helloWorld).build();
/** クラスファイル生成 */
writeClass("generated/HelloWorld", javaFile);
return true;
}
private void writeClass(final String fqcn, final JavaFile javaFile) {
try {
final JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile(fqcn);
try (final Writer writer = sourceFile.openWriter()) {
javaFile.writeTo(writer);
}
}
catch (final Exception e) {
log(e.getMessage());
throw new RuntimeException(e);
}
}
}
src/main/resources
ソースフォルダを作成- resources 配下に
META-INF/services
フォルダを追加 - services 配下に
javax.annotation.processing.Processor
ファイル作成 - 上記ファイルに
com.github.YOURNAME.MyAnnotationProcessor
を記述
$ mvn clean package
target
フォルダに my-annotation-processor-1.0-SNAPSHOT-jar-with-dependencies.jar
が生成されていることを確認
$ cd my-annotation-processor
$ mkdir sample
$ cd sample
$ play new my-annotation-processor-test
$ cd my-annotation-processor-test
$ cp ../../target/my-annotation-processor-1.0-SNAPSHOT-jar-with-dependencies.jar my-annotation-processor-test/lib
$ cd my-annotation-processor-test
$ play ec
上で作成したプロジェクトを Eclipse にインポートし、プロジェクト設定を行う
- Java Compiler > Annotation Processing から Enable Annotatoin Processing を有効化
- Generated Source directory を app に変更
- Java Compiler > Annotation Processing > Factory Path から Jar ファイルを追加
- app/generated に HelloWorld クラスが生成されることを確認
- http://qiita.com/opengl-8080/items/beda51fe4f23750c33e9
- http://blog.64p.org/entry/2015/03/26/055347
- http://www.slideshare.net/vvakame/apt-7568357
- APライブラリに必要なライブラリを含める、jarファイル名にjar-with-dependenciesを付加しない
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<appendAssemblyId>false</appendAssemblyId>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
- Playで推移的にライブラリを取得しないようにする
- com.github.asufana -> play1-conf-accessor [1.0,):
transitive: false
- AnnotationProcessing自体のコードで、lombokほかAnnotationProcessingを利用するライブラリを使わない方がよい、ServiceLoaderが上書きされて動作しない場合がある