Tagbangers Blog

Spring Java Format を使って Springスタイルでコードフォーマット+チェックする

Springのコードを書き、PRを出すタイミングで不要なスペースや改行してないなどといったコードフォーマットのチェックを行いたい時に、
Spring Java formatを利用する機会があったので紹介します。

Spring Java Formatとは

Spring本家から出ている、一貫したSpringスタイルを提供するために任意のJavaプロジェクトに適用できるプラグイン

詳細はGithubにて

できること:

  • 折り返しと空白の規約を適用するソースフォーマッタ
  • コードベース全体の一貫性を強化する(チェックスタイル)

シンプルで、準備もpomに書いて実行するだけなのでとても簡単ですが、
自社のスタイルをこのプラグインに合わせたい、という内容のカスタマイズはできません。
コードをSpring スタイルに合わせるためのものです。コードの一貫性を求める場合に使えます。

ソースフォーマット

mavenの場合は以下をpom.xmlに追加

<build>
    <plugins>
        <plugin>
            <groupId>io.spring.javaformat</groupId>
            <artifactId>spring-javaformat-maven-plugin</artifactId>
            <version>0.0.6</version>
            <executions>
                <execution>
                    <phase>validate</phase>
                    <inherited>true</inherited>
                    <goals>
                        <goal>validate</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

でもってこちらを実行すると、コードをきれいにしてくれます

$ ./mvnw spring-javaformat:apply

フォーマットされたコードの例

class Name {

    private final String first;

    private final String last;

    public Name(String first, String last) {
        this.first = first;
        this.last = last;
    }

}

checkstyle

こちらは静的解析した上でビルドする時にエラーを吐くようにさせる(executionのとこ)

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-checkstyle-plugin</artifactId>
            <version>3.0.0</version>
            <dependencies>
                <dependency>
                    <groupId>com.puppycrawl.tools</groupId>
                    <artifactId>checkstyle</artifactId>
                    <version>8.11</version>
                </dependency>
                <dependency>
                    <groupId>io.spring.javaformat</groupId>
                    <artifactId>spring-javaformat-checkstyle</artifactId>
                    <version>0.0.6</version>
                </dependency>
            </dependencies>
            <executions>
                <execution>
                    <id>checkstyle-validation</id>
                    <phase>validate</phase>
                    <inherited>true</inherited>
                    <configuration>
                        <configLocation>io/spring/javaformat/checkstyle/checkstyle.xml</configLocation>
                        <includeTestSourceDirectory>true</includeTestSourceDirectory>
                    </configuration>
                    <goals>
                        <goal>check</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

サンプルプロジェクトで./mvnw clean package してみると以下のようにcheckstyleに大量に怒られました。

[ERROR] src/main/java/jp/co/tagbangers/sandbox/Application.java:[1] (extension) SpringHeader: header.missing
[ERROR] src/main/java/jp/co/tagbangers/sandbox/Application.java:[6] (javadoc) JavadocType: Missing a Javadoc comment.
[ERROR] src/main/java/jp/co/tagbangers/sandbox/Application.java:[6,1] (design) HideUtilityClassConstructor: Utility classes should not have a public or default constructor.
[ERROR] src/main/java/jp/co/tagbangers/sandbox/entity/Person.java:[1] (extension) SpringHeader: header.mismatch
[ERROR] src/main/java/jp/co/tagbangers/sandbox/entity/Person.java:[6] (imports) ImportOrder: Wrong order for 'java.util.Date' import.
[ERROR] src/main/java/jp/co/tagbangers/sandbox/entity/Person.java:[8] (javadoc) JavadocType: Missing a Javadoc comment.
[ERROR] src/main/java/jp/co/tagbangers/sandbox/entity/Person.java:[12] (javadoc) JavadocType: Missing a Javadoc comment.
[ERROR] src/main/java/jp/co/tagbangers/sandbox/entity/Person.java:[14,17] (javadoc) JavadocVariable: Missing a Javadoc comment.
[ERROR] src/main/java/jp/co/tagbangers/sandbox/entity/Person.java:[14,22] (javadoc) JavadocVariable: Missing a Javadoc comment.
[ERROR] src/main/java/jp/co/tagbangers/sandbox/entity/Person.java:[14,29] (javadoc) JavadocVariable: Missing a Javadoc comment.
[ERROR] src/main/java/jp/co/tagbangers/sandbox/controller/HelloController.java:[1] (extension) SpringHeader: header.mismatch
[ERROR] src/main/java/jp/co/tagbangers/sandbox/controller/HelloController.java:[4] (imports) ImportOrder: 'org.springframework.beans.factory.annotation.Autowired' should be separated from previous imports.
[ERROR] src/main/java/jp/co/tagbangers/sandbox/controller/HelloController.java:[10] (imports) ImportOrder: Wrong order for 'java.util.List' import.
[ERROR] src/main/java/jp/co/tagbangers/sandbox/controller/HelloController.java:[12] (javadoc) JavadocType: Missing a Javadoc comment.
[ERROR] src/main/java/jp/co/tagbangers/sandbox/controller/HelloController.java:[21,32] (coding) RequireThis: Reference to instance variable 'familyService' needs "this.".
[ERROR] src/main/java/jp/co/tagbangers/sandbox/service/FamilyService.java:[1] (extension) SpringHeader: header.mismatch
[ERROR] src/main/java/jp/co/tagbangers/sandbox/service/FamilyService.java:[4] (imports) ImportOrder: 'org.springframework.stereotype.Service' should be separated from previous imports.
...

上記では configLocationが spring io の checkstyle.xml を参照にしていたのですが、それだと
Javadocコメントがない、ヘッダーがSpringスタイルに合ってない、・・・と細かめに指摘が入るので
怒られる量を削減したい場合は以下のようにcheckstyle.xmlをオーバーライドすることもできます。

checkstyleをオーバーライドしてみる

pom.xml
<executions>
    <execution>
        <id>checkstyle-validation</id>
        <phase>validate</phase>
        <inherited>true</inherited>
        <configuration>
            <!-- locationの参照箇所変更 + suppressoionsLocation追加-->
            <configLocation>src/checkstyle/checkstyle.xml</configLocation>
            <suppressionsLocation>src/checkstyle/checkstyle-suppressions.xml</suppressionsLocation>
            <!-- -->
            <includeTestSourceDirectory>true</includeTestSourceDirectory>
        </configuration>
        <goals>
            <goal>check</goal>
        </goals>
    </execution>
</executions>

オーバーライド用のファイルを配置
src/checkstyle/checkstyle.xml

<?xml version="1.0"?>
<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.2//EN" "http://www.puppycrawl.com/dtds/configuration_1_2.dtd">
<module name="com.puppycrawl.tools.checkstyle.Checker">
    <module name="io.spring.javaformat.checkstyle.SpringChecks" />
</module>


以下のファイルでどのファイルに対してどのチェックを行わないかを指定。
checkstyleの元ネタはこちらを参考

src/checkstyle/checkstyle-suppressions.xml
<?xml version="1.0"?>
<!DOCTYPE suppressions PUBLIC "-//Puppy Crawl//DTD Suppressions 1.1//EN" "http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
<suppressions>
    <suppress files="\.java" checks="SpringHeaderCheck" />
    <suppress files="\.java" checks="ImportOrderCheck" />
    <suppress files="\.java" checks="AvoidStarImportCheck" />
    <suppress files="\.java" checks="AvoidStaticImportCheck" />
    <suppress files="\.java" checks="JavadocStyleCheck" />
    <suppress files=".+Application\.java" checks="HideUtilityClassConstructor" />
</suppressions>