---
title: Making JSpecify/NullAway Easier for Maven Users with the Nullability Maven Plugin
summary: This article introduces how to introduce JSpecify and NullAway in Maven and simplify the configuration using the Nullability Maven Plugin.
tags: ["NullAway", "JSpecify", "Maven", "Java"]
categories: ["Programming", "Java", "Maven", "NullAway"]
date: 2026-02-24T06:13:16.498Z
updated: 2026-05-08T01:49:42Z
---

### What is JSpecify?

[JSpecify](https://jspecify.dev/) is an open-source project aimed at standardizing nullability annotations in Java.

In the Java ecosystem, annotations such as `@Nullable` and `@NonNull` have traditionally been defined separately by multiple libraries like JetBrains, FindBugs, and Checker Framework, leading to compatibility issues. JSpecify aims to unify these and provide a specification that can be used consistently across tools.  
There was a JCP standardization specification for Java software defect detection annotations proposed around 2006, known as JSR‑305 (effectively deprecated), but JSpecify covers areas that were insufficient in JSR‑305, such as the handling of nullability for type parameters and generics.

It defines `@Nullable`, `@NonNull`, `@NullMarked`, etc., under the `org.jspecify.annotations` package. Static analysis tools and IDEs can recognize these, enabling more accurate null checks.

Spring Framework 7 supports JSpecify, requiring explicit `@Nullable` annotations for methods that may return null or arguments that may accept null. Otherwise, it assumes methods do not return null and do not accept null assignments.  
For more details, please refer to [this blog post](https://spring.io/blog/2025/11/12/null-safe-applications-with-spring-boot-4).

By using JSpecify annotations, IDEs can display warnings for code that lacks proper null checks.  
However, this alone does not cause compilation errors.

### What is NullAway?

NullAway is a tool that statically analyzes Java code to detect NullPointerExceptions. It operates as an [Error Prone](https://errorprone.info/) plugin, reporting null safety violations as warnings or errors during the build process.  
It recognizes JSpecify and JSR‑305 annotations and adopts an opt-out approach where references without `@Nullable` are treated as non-null. This design facilitates gradual adoption in existing codebases.

By combining NullAway with JSpecify, compilation errors can be generated for code that lacks proper null checks.

For example, compiling the following code with NullAway enabled:

```java
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);
	}

}
```

Results in the following compilation error:

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

This is because the `body` method of `RestClient` is annotated with `@Nullable` as shown below, indicating it may return null, yet the code returns it without any annotation or check:

```java
		/**
		 * 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);
```

You can remove the compilation error by handling the null case as shown below, or by adding `@Nullable` to the method to propagate the possibility of returning null:

```java
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");
	}

}
```

Note that to treat unannotated elements as non-null, you must add the `@NullMarked` annotation to the class or package.  
In the example above, a `package-info.java` like the following is prepared:

```java
@NullMarked
package com.example.foo;

import org.jspecify.annotations.NullMarked;
```

### Using JSpecify/NullAway in Maven

We have seen that using JSpecify/NullAway allows for mandatory null checks. So, how do we integrate this into Maven?

The following `maven-compiler-plugin` configuration is required (Requires: JDK 22+, or JDK 21.0.8+ / 17.0.19+ (OpenJDK-based) with the `-XDaddTypeAnnotationsToSymbol=true` flag).

```xml
      <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.49.0</version>
            </path>
            <path>
              <groupId>com.uber.nullaway</groupId>
              <artifactId>nullaway</artifactId>
              <version>0.13.4</version>
            </path>
          </annotationProcessorPaths>
          <fork>true</fork>
          <compilerArgs>
            <arg>-XDcompilePolicy=simple</arg>
            <arg>--should-stop=ifError=FLOW</arg>
            <!-- Required for JSpecify Mode on JDK 21.0.8+ / 17.0.19+ (OpenJDK). No-op on JDK 22+. -->
            <arg>-XDaddTypeAnnotationsToSymbol=true</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>
```

While you can simply copy and paste this, managing it becomes difficult when dealing with a large number of Maven projects...

### Introducing the Nullability Maven Plugin

The Nullability Maven Plugin removes this boilerplate from `pom.xml`.

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

The configuration mentioned above can be simplified using the Nullability Maven Plugin as follows:

```xml
<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.4.0</version>
    <extensions>true</extensions>
    <executions>
        <execution>
            <goals>
                <goal>configure</goal>
            </goals>
        </execution>
    </executions>
</plugin>
```

If there are existing settings in `maven-compiler-plugin`, they are automatically merged.

> [!TIP]  
> You can check the merged content using `mvn help:effective-pom`.

Although this is not a constraint of the plugin itself, to use NullAway's JSpecify Mode, you must compile with JDK 22 or later, or JDK 21.0.8+ / 17.0.19+ of an OpenJDK-based distribution (such as Liberica, Temurin, or Zulu) (the target version can be 17). In the latter case, the `-XDaddTypeAnnotationsToSymbol=true` javac flag is required, but the Nullability Maven Plugin automatically adds this when JSpecify Mode is enabled, so users do not need to worry about it. For details on running on JDKs that do not support this flag, such as Oracle JDK, or other version requirements, please refer to [here](https://github.com/making/nullability-maven-plugin?tab=readme-ov-file#requirements).

### Automatic Generation of package-info.java

One of the tedious aspects of using JSpecify is creating `package-info.java`. `package-info.java` files with `@NullMarked` need to be created in each subpackage, and it is easy to forget to create them.

By default, the Nullability Maven Plugin triggers an error if there are classes/packages without `@NullMarked`.  
Additionally, you can automatically generate `package-info.java` at build time with the following configuration:

```xml
<plugin>
    <groupId>am.ik.maven</groupId>
    <artifactId>nullability-maven-plugin</artifactId>
    <version>0.4.0</version>
    <extensions>true</extensions>
    <executions>
        <execution>
            <goals>
                <goal>configure</goal>
                <goal>generate-package-info</goal><!-- Added -->
            </goals>
        </execution>
    </executions>
</plugin>
```

For settings on the directory where files are generated, please refer to [here](https://github.com/making/nullability-maven-plugin?tab=readme-ov-file#generating-package-infojava-automatically).

---

I have introduced how Maven users can easily introduce JSpecify / NullAway using the Nullability Maven Plugin. Please give it a try.
