Feb 24, 2026
Feb 24, 2026
N/A Views
MD

JSpecify とは?

JSpecify とは、Java の nullability アノテーションを標準化するためのオープンソースプロジェクトです。

Java エコシステムでは従来、@Nullable@NonNull といったアノテーションが JetBrains、FindBugs、Checker Framework など複数のライブラリでバラバラに定義されており、互換性の問題がありました。JSpecify はこれを統一し、ツール間で共通して使える仕様を提供することを目的としています。
2006 年頃に提案された Java のソフトウェア欠陥検出用アノテーションの JCP 標準化仕様に JSR‑305(事実上廃止)がありましたが、JSpecify は型引数やジェネリクスに対する nullability の扱いなど、JSR‑305 では不十分だった領域をカバーしています。

org.jspecify.annotations パッケージ配下に @Nullable@NonNull@NullMarked などを定義しており、静的解析ツールや IDE がこれらを認識することで、より正確な null チェックが可能になります。

Spring Framework 7 は JSpecify に対応し、null を返すメソッドや null を渡してもよい引数には明示的に @Nullable をつけるようになりました。それ以外では null を返さず、null の設定も許容しません。
詳しくは こちらのブログ記事 を参照してください。

JSpecify のアノテーションを使用することで、適切な null チェックが行われていないコードに対して、IDE は警告を表示できます。
しかし、これ単体ではコンパイルエラーを発生させることはありません。

NullAway とは?

NullAway は、Java の NullPointerException を静的解析で検出するツールです。Error Prone プラグインとして動作し、ビルド時に null 安全性の違反を警告・エラーとして報告します。
JSpecify や JSR‑305 のアノテーションを認識し、「@Nullable が付いていない参照は non-null とみなす」というオプトアウト方式を採用しています。これにより既存コードベースへの段階的な導入がしやすい設計になっています。

NullAway と JSpecify を組み合わせることによって、適切な null チェックが行われていないコードに対して、コンパイルエラーを発生させることができるようになります。

例えば、次のコードを NullAway が有効な状態でコンパイルします。

package com.example.foo;

import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClient;

@Component
public class Foo {

    private final RestClient restClient;

    public Foo(RestClient.Builder restClientBuilder) {
        this.restClient = restClientBuilder.build();
    }

    public String getFoo() {
        return this.restClient.get().uri("http://example.com/foo").retrieve().body(String.class);
    }

}

すると、次のコンパイルエラーが発生します。

[ERROR] /Users/toshiaki/.../Foo.java:[16,2] error: [NullAway] returning @Nullable expression from method with @NonNull return type

RestClientbody メソッドには次のように @Nullable アノテーションがついており、null を返す可能性があるのに、何もアノテーションやチェックを行わずに return しているからです。

        /**
         * Extract the body as an object of the given type.
         * @param bodyType the type of return value
         * @param <T> the body type
         * @return the body, or {@code null} if no response body was available
         * @throws RestClientResponseException by default when receiving a
         * response with a status code of 4xx or 5xx. Use
         * {@link #onStatus(Predicate, ErrorHandler)} to customize error response
         * handling.
         */
        <T> @Nullable T body(Class<T> bodyType);

次のように null の場合に対処するコードを書く、あるいはメソッドに @Nullable をつけて、null を返す可能性を伝播させることでコンパイルエラーを取り除くことができます。

package com.example.foo;

import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClient;

import java.util.Objects;

@Component
public class Foo {

    private final RestClient restClient;

    public Foo(RestClient.Builder restClientBuilder) {
        this.restClient = restClientBuilder.build();
    }

    public String getFoo() {
        return Objects.requireNonNull(this.restClient.get().uri("http://example.com").retrieve().body(String.class),
                "Response body must not be null");
    }

}

なお、何もアノテーションを指定しない場合に non-null として扱うようにするには、クラスまたはパッケージに @NullMarked アノテーションをつける必要があります。
上記の例では次のような package-info.java を用意してあります。

@NullMarked
package com.example.foo;

import org.jspecify.annotations.NullMarked;

JSpecify/NullAway を Maven で使う

ここまでで JSpecify/NullAway を使うことで、強制的に null チェックができるようになることがわかりました。さて、これを Maven に組み込むにはどうすればよいでしょうか。

次の maven-compiler-plugin に次のような設定が必要です(要: JDK 22+)。

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.15.0</version>
        <configuration>
          <annotationProcessorPaths>
            <path>
              <groupId>com.google.errorprone</groupId>
              <artifactId>error_prone_core</artifactId>
              <version>2.47.0</version>
            </path>
            <path>
              <groupId>com.uber.nullaway</groupId>
              <artifactId>nullaway</artifactId>
              <version>0.13.1</version>
            </path>
          </annotationProcessorPaths>
          <fork>true</fork>
          <compilerArgs>
            <arg>-XDcompilePolicy=simple</arg>
            <arg>--should-stop=ifError=FLOW</arg>
            <!-- @formatter:off -->
            <arg>-Xplugin:ErrorProne -XepDisableAllChecks -XepOpt:NullAway:OnlyNullMarked=true -XepOpt:NullAway:JSpecifyMode=true -Xep:NullAway:ERROR -XepExcludedPaths:(.*/test/java/.*|.*/target/generated-sources/.*)</arg>
            <!-- @formatter:on -->
            <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</arg>
            <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED</arg>
            <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</arg>
            <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED</arg>
            <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED</arg>
            <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED</arg>
            <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</arg>
            <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
            <arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg>
            <arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED</arg>
          </compilerArgs>
        </configuration>
      </plugin>

コピー&ペーストすればよいのですが、Maven プロジェクトの量が多いと管理が大変になります…

Nullability Maven Plugin の導入

Nullability Maven Plugin はこの pom.xml のボイラープレートを取り除いてくれる Maven プラグインです。

https://github.com/making/nullability-maven-plugin

前述の設定は Nullability Maven Plugin を使うと、次のように簡略化されます。

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.15.0</version>
</plugin>
<plugin>
    <groupId>am.ik.maven</groupId>
    <artifactId>nullability-maven-plugin</artifactId>
    <version>0.3.0</version>
    <extensions>true</extensions>
    <executions>
        <execution>
            <goals>
                <goal>configure</goal>
            </goals>
        </execution>
    </executions>
</plugin>

maven-compiler-plugin に自身で設定した内容があれば自動でマージされます。

Tip

mvn help:effective-pom でマージされた内容を確認できます。

このプラグイン自体の制約ではありませんが、NullAway の JSpecify Mode を使用するには JDK 22 以上でコンパイルする必要がありますが、ターゲットは 17 でも構いません。どうしても古いバージョンの JDK(17 または 21)でコンパイルしたい場合は、こちら を参照してください。

package-info.java の自動生成

JSpecify を使う上で面倒なのが、package-info.java の作成です。@NullMarked のついた package-info.java は各サブパッケージに作成する必要があり、つい作成を忘れがちです。

Nullability Maven Plugin はデフォルトで、@NullMarked がついていないクラス/パッケージがあればエラーにします。
また、次の設定で package-info.java をビルド時に自動生成させることもできます。

<plugin>
    <groupId>am.ik.maven</groupId>
    <artifactId>nullability-maven-plugin</artifactId>
    <version>0.3.0</version>
    <extensions>true</extensions>
    <executions>
        <execution>
            <goals>
                <goal>configure</goal>
                <goal>generate-package-info</goal><!-- 追加 -->
            </goals>
        </execution>
    </executions>
</plugin>

生成させるディレクトリの設定は こちら を参照してください。


Nullability Maven Plugin で Maven ユーザーが簡単に JSpecify / NullAway を導入できる方法を紹介しました。ぜひ試してください。

Found a mistake? Update the entry.
Share this article: