Setting a Spring Bean to Null – 将 Spring Bean 设置为空

最后修改: 2023年 11月 23日

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

1. Overview

1.概述

In this tutorial, we’ll learn how to set beans in the Spring context to nulls. This might be useful in some cases, such as testing when we don’t want to provide mocks. Also, while using some optional features, we might want to avoid creating implementation and pass null instead.

在本教程中,我们将学习如何将 Spring 上下文中的 Bean 设置为 nulls。这在某些情况下可能很有用,例如在测试时我们不想提供模拟。此外,在使用某些可选功能时,我们可能希望避免创建实现,而是传递null

Additionally, this way, we can create placeholders if we want to defer the decision of picking needed implementation outside of the beans’ lifecycle. Lastly, this technique might be the first step during the deprecation process, which involves removing specific beans from the context.

此外,如果我们想推迟决定是否在 Bean 的生命周期之外选择所需的实现,也可以通过这种方式创建占位符。最后,这种技术可能是废弃过程中的第一步,即从上下文中移除特定的 Bean。

2. Components Setup

2.组件设置

A couple of ways exist to set a bean to null, depending on how the context is configured. We’ll consider XML, annotation, and Java configurations. We’ll be using a simple setup with two classes:

将 bean 设置为 null 有几种方法,这取决于上下文是如何配置的。我们将考虑 XML注释和 Java 配置

@Component
public class MainComponent {
    private SubComponent subComponent;
    public MainComponent(final SubComponent subComponent) {
        this.subComponent = subComponent;
    }
    public SubComponent getSubComponent() {
        return subComponent;
    }
    public void setSubComponent(final SubComponent subComponent) {
        this.subComponent = subComponent;
    }
}

We’ll show how to set SubComponent to null in the Spring context:

我们将展示如何在 Spring 上下文中将 SubComponent 设置为 null

@Component
public class SubComponent {}

3. XML Configuration Using Placeholder

3.使用占位符进行 XML 配置

In XML configuration, we can use a special placeholder to identify null values:

在 XML 配置中,我们可以使用一个特殊的占位符来标识 null 值:

<beans>
    <bean class="com.baeldung.nullablebean.MainComponent" name="mainComponent">
        <constructor-arg>
            <null/>
        </constructor-arg>
    </bean>
</beans>

This configuration would provide the following result:

这种配置的结果如下

@Test
void givenNullableXMLContextWhenCreatingMainComponentThenSubComponentIsNull() {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
      "nullable-application-context.xml");
    MainComponent bean = context.getBean(MainComponent.class);
    assertNull(bean.getSubComponent());
}

4. XML Configuration Using SpEL

4.使用 SpEL 进行 XML 配置

We can achieve similar results using SpEL in XML. There will be a couple of differences from the previous configuration:

我们可以使用 XML 中的 SpEL 实现类似的结果。与之前的配置相比,会有一些不同之处:

<beans>
    <bean class="com.baeldung.nullablebean.MainComponent" name="mainComponent">
        <constructor-arg value="#{null}"/>
    </bean>
</beans>

Similarly to the last test, we can identify that the SubComponent is null:

与上次测试类似,我们可以确定 SubComponentnull

@Test
void givenNullableSpELXMLContextWhenCreatingMainComponentThenSubComponentIsNull() {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
      "nullable-spel-application-context.xml");
    MainComponent bean = context.getBean(MainComponent.class);
    assertNull(bean.getSubComponent());
}

5. XML Configuration Using SpEL With Properties

5.使用带有属性的 SpEL 进行 XML 配置

One of the ways to improve the previous solution is to store the bean name in a property file. This way, we can pass a null value whenever needed without changing the configuration:

改进前一种解决方案的方法之一是在属性文件中存储 Bean 名称。这样,我们就可以在需要时传递一个 null 值,而无需更改配置:

nullableBean = null

The XML configuration would use PropertyPlaceholderConfigurer to read the properties:

XML 配置将使用 PropertyPlaceholderConfigurer 来读取属性:

<beans>
    <bean id="propertyConfigurer"
      class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="classpath:nullable.properties"/>
    </bean>
    <bean class="com.baeldung.nullablebean.MainComponent" name="mainComponent">
        <constructor-arg value="#{ ${nullableBean} }"/>
    </bean>
    <bean class="com.baeldung.nullablebean.SubComponent" name="subComponent"/>
</beans>

However, we should use the property placeholder inside the SpEL expression so that the values would be read correctly. As a result, we’ll initialize the SubComponent to null:

但是,我们应该在 SpEL 表达式中使用属性占位符,以便正确读取值。因此,我们将SubComponent初始化为null

@Test
void givenNullableSpELXMLContextWithNullablePropertiesWhenCreatingMainComponentThenSubComponentIsNull() {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
      "nullable-configurable-spel-application-context.xml");
    MainComponent bean = context.getBean(MainComponent.class);
    assertNull(bean.getSubComponent());
}

To provide an implementation, we’ll have to change only the properties:

要提供实现方法,我们只需更改属性:

nullableBean = subComponent

6. Null Supplier in Java Configuration

6.Java 配置中的 Null 供应商

It’s impossible to return null directly from a method annotated with @Bean. That’s why we need to wrap it in some way. We can use Supplier to do so:

使用 @Bean 标记的方法不可能直接返回 null 。因此,我们需要以某种方式对其进行封装。我们可以使用 Supplier 来实现这一目的:

@Bean
public Supplier<SubComponent> subComponentSupplier() {
    return () -> null;
}

Technically, we can use any class to wrap a null value, but using Supplier is more idiomatic. In the case of null, we don’t care that the Supplier might be called several times. However, if we want to implement a similar solution for the usual beans, we must ensure that the Supplier provides the same instance if a singleton is required.

从技术上讲,我们可以使用任何类来封装 null 值,但使用 Supplier 更符合习惯。在 null 的情况下,我们并不关心 Supplier 是否会被多次调用。但是,如果我们要为常见的Bean实现类似的解决方案,就必须确保 Supplier 在需要单例时提供相同的实例。

This solution would also provide us with the correct behavior:

这一解决方案也将为我们提供正确的行为:

@Test
void givenNullableSupplierContextWhenCreatingMainComponentThenSubComponentIsNull() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
      NullableSupplierConfiguration.class);
    MainComponent bean = context.getBean(MainComponent.class);
    assertNull(bean.getSubComponent());
}

Note that simply returning null from @Bean might create problems:

请注意,简单地从 @Bean 返回 null 可能会产生问题:

@Bean
public SubComponent subComponent() {
    return null;
}

In this case, the context would fail with UnsatisfiedDependencyException:

在这种情况下,上下文将以 UnsatisfiedDependencyException 失败:

@Test
void givenNullableContextWhenCreatingMainComponentThenSubComponentIsNull() {
    assertThrows(UnsatisfiedDependencyException.class, () ->  new AnnotationConfigApplicationContext(
      NullableConfiguration.class));
}

7. Using Optional

7.使用选项</em

When using Optional, Spring automatically identifies that the bean can be absent from the context and passes null without any additional configuration:

当使用 Optional 时,Spring 会自动识别上下文中可能不存在的 Bean,并传递 null 而无需任何额外配置:

@Bean
public MainComponent mainComponent(Optional<SubComponent> optionalSubComponent) {
    return new MainComponent(optionalSubComponent.orElse(null));
}

If Spring cannot find SubComponent in the context, it will pass an empty Optional:

如果 Spring 无法在上下文中找到 子组件,它将传递一个空的 选项:</em

@Test
void givenOptionableContextWhenCreatingMainComponentThenSubComponentIsNull() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
      OptionableConfiguration.class);
    MainComponent bean = context.getBean(MainComponent.class);
    assertNull(bean.getSubComponent());
}

8. Non-Required Autowiring

8.非必要自动接线

Another way to use null as a value for a bean is to declare it non-required. However, this method would work only with non-constructor injections:

null 用作 Bean 值的另一种方法是声明它为非必填值。不过,这种方法只适用于非构造函数注入:

@Component
public class NonRequiredMainComponent {
    @Autowired(required = false)
    private NonRequiredSubComponent subComponent;
    public NonRequiredSubComponent getSubComponent() {
        return subComponent;
    }
    public void setSubComponent(final NonRequiredSubComponent subComponent) {
        this.subComponent = subComponent;
    }
}

This dependency is not required for the proper functioning of the component:

组件的正常运行并不需要这种依赖性:

@Test
void givenNonRequiredContextWhenCreatingMainComponentThenSubComponentIsNull() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
      NonRequiredConfiguration.class);
    NonRequiredMainComponent bean = context.getBean(NonRequiredMainComponent.class);
    assertNull(bean.getSubComponent());
}

9. Using @Nullable

9.使用 @Nullable

Additionally, we can use @Nullable annotation to identify that we expect that the bean might be null. Both Spring and Jakarta annotations would work for this:

此外,我们还可以使用 @Nullable 注解来标识我们预期 Bean 可能是 null 的。SpringJakarta 注解均可用于此目的:

@Component
public class NullableMainComponent {
    private NullableSubComponent subComponent;
    public NullableMainComponent(final @Nullable NullableSubComponent subComponent) {
        this.subComponent = subComponent;
    }
    public NullableSubComponent getSubComponent() {
        return subComponent;
    }
    public void setSubComponent(final NullableSubComponent subComponent) {
        this.subComponent = subComponent;
    }
}

We don’t need to identify NullableSubComponent as a Spring component:

我们不需要将 NullableSubComponent 标识为 Spring 组件:

public class NullableSubComponent {}

Spring context will set it to null based on the @Nullable annotation:

Spring 上下文将根据 @Nullable 注解将其设置为 null

@Test
void givenContextWhenCreatingNullableMainComponentThenSubComponentIsNull() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
      NullableJavaConfiguration.class);
    NullableMainComponent bean = context.getBean(NullableMainComponent.class);
    assertNull(bean.getSubComponent());
}

10. Conclusion

10.结论

Using nulls in a Spring context isn’t the most common practice, but it might be reasonable sometimes. However, the process of setting a bean to null might not be very intuitive.

在 Spring 上下文中使用 nulls 并不是最常见的做法,但有时可能是合理的。不过,将 Bean 设置为 null 的过程可能不是很直观。

In this article, we’ve learned how to address this issue in multiple ways.

在本文中,我们了解了如何通过多种方式解决这一问题。

As always, the code from the article is available over on GitHub.

与往常一样,本文的代码可在 GitHub 上获取