將 Spring Bean 設定為 Null
1. 概述
在本教程中,我們將學習如何將 Spring 上下文中的 beans 設定為nulls
。這在某些情況下可能很有用,例如當我們不想提供模擬時進行測試。另外,在使用一些可選功能時,我們可能希望避免建立實作並傳遞null
。
此外,透過這種方式,如果我們想推遲在 bean 生命週期之外選擇所需實現的決定,我們可以建立佔位符。最後,此技術可能是棄用過程中的第一步,其中涉及從上下文中刪除特定的 bean。
2. 組件設定
有幾種方法可以將 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;
}
}
我們將展示如何在 Spring 上下文中將SubComponent
設定為null
:
@Component
public class SubComponent {}
3. 使用佔位符的 XML 配置
在XML配置中,我們可以使用特殊的佔位符來識別null
值:
<beans>
<bean class="com.baeldung.nullablebean.MainComponent" name="mainComponent">
<constructor-arg>
<null/>
</constructor-arg>
</bean>
</beans>
此配置將提供以下結果:
@Test
void givenNullableXMLContextWhenCreatingMainComponentThenSubComponentIsNull() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"nullable-application-context.xml");
MainComponent bean = context.getBean(MainComponent.class);
assertNull(bean.getSubComponent());
}
4. 使用 SpEL 進行 XML 配置
我們可以使用 XML 中的 SpEL 來獲得類似的結果。與先前的配置相比,會有一些差異:
<beans>
<bean class="com.baeldung.nullablebean.MainComponent" name="mainComponent">
<constructor-arg value="#{null}"/>
</bean>
</beans>
與上一個測試類似,我們可以辨識出SubComponent
為null
:
@Test
void givenNullableSpELXMLContextWhenCreatingMainComponentThenSubComponentIsNull() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"nullable-spel-application-context.xml");
MainComponent bean = context.getBean(MainComponent.class);
assertNull(bean.getSubComponent());
}
5. 使用 SpEL 和屬性進行 XML 配置
改進先前解決方案的方法之一是將 bean 名稱儲存在屬性檔案中。這樣,我們就可以在需要時傳遞null
值,而無需更改配置:
nullableBean = null
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>
但是,我們應該在 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());
}
要提供實現,我們只需更改屬性:
nullableBean = subComponent
6. Java配置中的Null
供應商
使用@Bean
註解的方法不可能直接傳回null
。這就是為什麼我們需要以某種方式包裝它。我們可以使用Supplier
來做到這一點:
@Bean
public Supplier<SubComponent> subComponentSupplier() {
return () -> null;
}
從技術上講,我們可以使用任何類別來包裝null
值,但使用Supplier
更為慣用。在null
的情況下,我們不關心Supplier
可能會被多次呼叫。然而,如果我們想為普通的bean實現類似的解決方案,我們必須確保Supplier
在需要單例的情況下提供相同的實例。
該解決方案還將為我們提供正確的行為:
@Test
void givenNullableSupplierContextWhenCreatingMainComponentThenSubComponentIsNull() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
NullableSupplierConfiguration.class);
MainComponent bean = context.getBean(MainComponent.class);
assertNull(bean.getSubComponent());
}
請注意,簡單地從@Bean
返回null
可能會產生問題:
@Bean
public SubComponent subComponent() {
return null;
}
在這種情況下,上下文將會失敗並出現UnsatisfiedDependencyException
:
@Test
void givenNullableContextWhenCreatingMainComponentThenSubComponentIsNull() {
assertThrows(UnsatisfiedDependencyException.class, () -> new AnnotationConfigApplicationContext(
NullableConfiguration.class));
}
7. 使用Optional
當使用Optional
時,Spring會自動識別該bean可以不在上下文中並傳遞null
,而無需任何額外的配置:
@Bean
public MainComponent mainComponent(Optional<SubComponent> optionalSubComponent) {
return new MainComponent(optionalSubComponent.orElse(null));
}
如果Spring在上下文中找不到SubComponent
,它將傳遞一個空的Optional:
@Test
void givenOptionableContextWhenCreatingMainComponentThenSubComponentIsNull() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
OptionableConfiguration.class);
MainComponent bean = context.getBean(MainComponent.class);
assertNull(bean.getSubComponent());
}
8. 非必要的自動裝配
使用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;
}
}
組件的正常運作不需要這種依賴關係:
@Test
void givenNonRequiredContextWhenCreatingMainComponentThenSubComponentIsNull() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
NonRequiredConfiguration.class);
NonRequiredMainComponent bean = context.getBean(NonRequiredMainComponent.class);
assertNull(bean.getSubComponent());
}
9.使用@Nullable
此外,我們可以使用@Nullable
註解來識別我們期望 bean 可能為null.
Spring 和 Jakarta 註解都適用於此:
@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;
}
}
我們不需要將NullableSubComponent
識別為 Spring 元件:
public class NullableSubComponent {}
Spring上下文將根據@Nullable
註解將其設定為null
:
@Test
void givenContextWhenCreatingNullableMainComponentThenSubComponentIsNull() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
NullableJavaConfiguration.class);
NullableMainComponent bean = context.getBean(NullableMainComponent.class);
assertNull(bean.getSubComponent());
}
10. 結論
在 Spring 上下文中使用nulls
並不是最常見的做法,但有時可能是合理的。然而,將 bean 設為null
的過程可能不是很直觀。
在本文中,我們學習如何以多種方式解決此問題。
與往常一樣,本文中的程式碼可以在 GitHub 上找到。