Using @NotNull on a Method Parameter – 在方法参数上使用@NotNull

最后修改: 2022年 7月 18日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

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

NullPointerExceptions是一个常见的问题。我们可以保护我们的代码的一个方法是在我们的方法参数上添加注释,如@NotNull

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.

通过使用@NotNull,我们表明,如果我们想避免出现异常,我们决不能用null调用我们的方法。然而,就其本身而言,这是不够的。让我们了解一下原因。

2. @NotNull Annotation on a Method Parameter

2.方法参数上的@NotNull注解

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

首先,让我们创建一个类,它有一个简单返回String长度的方法。

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

让我们也给我们的参数添加一个@NotNull注解。

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.

当我们导入NotNull时,我们应该注意到@NotNull注解有几种实现因此,我们需要确保它来自正确的包。

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

我们将使用javax.validation.constraints包。

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

现在,让我们创建一个NotNullMethodParameter,用一个null参数调用我们的方法。

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

Despite our NotNull annotation, we get a NullPointerException:

尽管我们有NotNull注解,但我们得到一个NullPointerException

java.lang.NullPointerException

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

我们的注释没有效果,因为没有验证器来执行它。

3. Adding a Validator

3.添加一个验证器

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

所以,让我们添加Hibernate Validator,javax.validation参考实现,来识别我们的@NotNull

Besides our validator, we also need to add a dependency for the expression language (EL) that it uses for rendering messages:

除了我们的验证器,我们还需要为表达式语言(EL)添加一个依赖关系,它用于渲染消息。

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

<dependency>
    <groupId>org.glassfish</groupId>
    <artifactId>javax.el</artifactId>
    <version>3.0.0</version>
</dependency>

When we don’t include the EL dependency, we get a ValidationException to remind us:

当我们不包括EL依赖时,我们会得到一个ValidationException来提醒我们。

javax.validation.ValidationException: HV000183: Unable to initialize 'javax.el.ExpressionFactory'. Check that you have the EL dependencies on the classpath, or use ParameterMessageInterpolator instead

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

有了我们的依赖关系,我们可以强制执行我们的@NotNull注解。

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

因此,让我们使用默认的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:

现在,当我们用一个空参数调用我们的方法时,我们的@NotNull被强制执行。

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

4.Spring启动

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

幸运的是,有一种更简单的方法,我们可以在我们的Spring Boot应用程序中使用。

4.1. Spring Boot Validation

4.1.Spring Boot验证

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

首先,让我们添加Maven依赖性,以便用Spring Boot进行验证。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
    <version>2.7.1</version>
</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 and EL dependencies to keep our pom.xml clean.

我们的spring-boot-starter-validation依赖性带来了我们所需要的Spring Boot和验证。这意味着我们可以删除之前的Hibernate和EL依赖,以保持我们的pom.xml干净。

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:

现在,让我们创建一个Spring管理的Component确保我们添加了@Validated 注释。让我们用一个validateNotNull方法来创建它,该方法接收一个String参数并返回数据的长度,并用@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,将我们的ValidatingComponent自动连接进去。让我们也添加一个以null作为方法参数的测试。

@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:

我们得到的ConstraintViolationException有我们的参数名称和一个 “不得为空 “的消息

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

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

我们可以在method constraints文章中了解更多关于注释我们的方法的信息。

4.2. A Cautionary Word

4.2.警示语

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方法有效,但让我们看看当我们添加另一个没有注释但调用我们原来的注释方法的方法时会发生什么。

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.

我们的NullPointerException返回。当我们从驻扎在同一个类中的另一个方法中调用注解的方法时,Spring并没有强制执行NotNull约束。

4.3. Jakarta and Spring Boot 3.0

4.3.雅加达和Spring Boot 3.0

With Jakarta, the validation package names recently changed from javax.validation to jakarta.validation. Spring Boot 3.0 is based on Jakarta and so uses the newer jakarta.validation packages. This is also the case for versions of hibernate-validator from 7.0.* and onwards. This means that when we upgrade, we’ll need to change the package names we use in our validation annotations.

通过Jakarta,验证包的名称最近从javax.validation变为jakarta.validationSpring Boot 3.0 基于 Jakarta,因此使用较新的jakarta.validation包。7.0.*及以上版本的hibernate-validator也是如此。这意味着,当我们升级时,我们需要改变我们在验证注释中使用的包名。

5. Conclusion

5.总结

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. Finally, we noted that we should expect to change our javax packages to jakarta when we update our Spring Boot projects to 3.0.

在这篇文章中,我们学习了如何在标准Java应用程序中的方法参数上使用@NotNull注解。我们还学习了如何使用Spring Boot的@Validated注解来简化我们的Spring Bean方法参数验证,同时也注意到它的局限性。最后,我们注意到,当我们将Spring Boot项目更新到3.0时,我们应该将javax包改为jakarta

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

像往常一样,本文中显示的所有代码样本都可以在GitHub上找到