Course – LS – All

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE

1. Overview

NullPointerExceptions are a common problem. One way we can protect our code is to add annotations such as @NotNull to our method parameters.

By using @NotNull, we indicate that we must never call our method with a null if we want to avoid an exception. However, by itself, that’s not enough. Let’s learn why.

2. @NotNull Annotation on a Method Parameter

First, let’s create a class with a method that simply returns the length of a String.

Let’s also add a @NotNull annotation to our parameter:

public class NotNullMethodParameter {
    public int validateNotNull(@NotNull String data) {
        return data.length();
    }
}

When we import NotNull, we should note that there are several implementations of a @NotNull annotation. So, we need to make sure that it’s from the right package.

We’ll use the jakarta.validation.constraints package.

Now, let’s create a NotNullMethodParameter and call our method with a null parameter:

NotNullMethodParameter notNullMethodParameter = new NotNullMethodParameter();
notNullMethodParameter.doesNotValidate(null);

Despite our NotNull annotation, we get a NullPointerException:

java.lang.NullPointerException

Our annotation has no effect because there’s no validator to enforce it.

3. Adding a Validator

So, let’s add Hibernate Validator, the jakarta.validation reference implementation, to recognize our @NotNull.

<dependency> 
  <groupId>org.hibernate.validator</groupId> 
  <artifactId>hibernate-validator</artifactId> 
  <version>8.0.0.Final</version>
</dependency>

With our dependencies in place, we can enforce our @NotNull annotation.

So, let’s create a validator using the default ValidatorFactory:

ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();

And then, let’s validate our argument as the first line of our annotated method:

validator.validate(myString);

Now, when we call our method with a null parameter, our @NotNull is enforced:

java.lang.IllegalArgumentException: HV000116: The object to be validated must not be null.

This is great, but having to add a call to our validator inside every annotated method results in a lot of boilerplate.

4. Spring Boot

Fortunately, there’s a much simpler approach that we can use in our Spring Boot applications.

4.1. Spring Boot Validation

First, let’s add the Maven dependency for validation with Spring Boot:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

Our spring-boot-starter-validation dependency brings in all we need for Spring Boot and validation. This means that we can remove our earlier Hibernate dependencies to keep our pom.xml clean.

Now, let’s create a Spring-managed Component, ensuring we add the @Validated annotation. Let’s create it with a validateNotNull method that takes a String parameter and returns the length of our data, and annotate our parameter with @NotNull:

@Component
@Validated
public class ValidatingComponent {
    public int validateNotNull(@NotNull String data) {
        return data.length();
    }
}

Finally, let’s create a SpringBootTest with our ValidatingComponent autowired in. Let’s also add a test with null as a method parameter:

@SpringBootTest
class ValidatingComponentTest {
    @Autowired ValidatingComponent component;

    @Test
    void givenNull_whenValidate_thenConstraintViolationException() {
        assertThrows(ConstraintViolationException.class, () -> component.validate(null));
    }
}

The ConstraintViolationException that we get has our parameter name and a ‘must not be null’ message:

javax.validation.ConstraintViolationException: validate.data: must not be null

We can learn more about annotating our methods in our method constraints article.

4.2. A Cautionary Word

Although this works for our public method, let’s see what happens when we add another method that isn’t annotated but that calls our original annotated method:

public String callAnnotatedMethod(String data) {
    return validateNotNull(data);
}

Our NullPointerException returns. Spring doesn’t enforce NotNull constraints when we invoke the annotated method from another method that resides in the same class.

 

4. Conclusion

In this article, we learned how to use the @NotNull annotation on a method parameter in a standard Java application. We also learned how to use Spring Boot’s @Validated annotation to simplify our Spring Bean method parameter validation while also noting its limitations.

As usual, all the code samples shown in this article are available over on GitHub.

Course – LS – All

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE
res – REST with Spring (eBook) (everywhere)
Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.