1. 概述
本教程將討論 Spring 框架中備用 Bean 的概念。備用 Bean 自 Spring Framework 6.2.0-M1 版本開始引入。它們在另一個同類型的 Bean 不可用或初始化失敗時,提供備選實現。
這在我們需要優雅地處理故障並提供備用機制,以確保應用程序繼續運行的場景中非常有用。
2. 主 Bean 和備用 Bean
在 Spring 應用中,我們可以定義同類型的多個 Bean。默認情況下,Spring 使用 Bean 名稱和類型來標識 Bean。當存在同名的同類型多個 Bean 時,我們可以使用 <em>@Primary</em> 註解標記其中一個 Bean 為主 Bean,使其優先於其他 Bean。這在應用上下文初始化時,多個同類型 Bean 被創建,並且我們希望指定哪個 Bean 作為默認 Bean 使用時非常有用。
同樣,我們可以定義備用 Bean 以在沒有其他符合條件的 Bean 可用時提供備選實現。我們可以使用 <em>@Fallback</em> 註解標記 Bean 為備用 Bean。只有當沒有同名的其他 Bean 可用時,備用 Bean 才會注入到應用上下文中。
3. 代碼示例
讓我們來看一個示例,以演示在 Spring 應用中使用主 bean 和備用 bean 的用法。我們將創建一個小應用程序,該應用程序使用不同的消息傳遞服務發送消息。 假設我們有多個消息傳遞服務,分別在生產和非生產環境中,需要在它們之間切換,以優化性能和成本。
3.1. 消息接口
首先,讓我們定義一個用於我們服務的接口:
public interface MessagingService {
void sendMessage(String text);
}
接口提供了一個方法,用於將提供的文本作為消息發送。
3.2. 主 Bean
接下來,讓我們定義消息服務作為主 Bean 的實現:
@Service
@Profile("production")
@Primary
public class ProductionMessagingService implements MessagingService {
@Override
public void sendMessage(String text) {
// implementation in production environment
}
}
在當前實現中,我們使用@Profile註解來指定該 Bean 僅在production 模式處於活動狀態時可用。我們還使用@Primary註解將其標記為主 Bean。
3.3. 非主Bean
以下定義另一個消息服務實現作為非主Bean:
@Service
@Profile("!test")
public class DevelopmentMessagingService implements MessagingService {
@Override
public void sendMessage(String text) {
// implementation in development environment
}
}
在本次實現中,我們使用@Profile註解來指定該 Bean 在test 激活時不可用。 這意味着該 Bean 將在所有 Profile 中可用,除非test Profile 被激活。
3.4. 備用 Bean
最後,讓我們為消息服務定義一個備用 Bean:
@Service
@Fallback
public class FallbackMessagingService implements MessagingService {
@Override
public void sendMessage(String text) {
// fallback implementation
}
}
在本實現中,我們使用@Fallback註解來標記此 Bean 為備用 Bean。該 Bean 僅在同類型的其他 Bean 不可用時才會被注入。
4. 測試
現在,讓我們通過自動注入消息服務並檢查根據活動配置文件使用的實現來測試我們的應用程序。
4.1. 無配置文件
在第一次測試中,我們不激活任何配置文件。由於生產配置文件未激活,因此ProductionMessagingService 不可用,而其他兩個 Bean 則可用。
當測試消息服務時,它應使用DevelopmentMessagingService,因為它優先於備用 Bean:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {FallbackMessagingService.class, DevelopmentMessagingService.class, ProductionMessagingService.class})
public class DevelopmentMessagingServiceUnitTest {
@Autowired
private MessagingService messagingService;
@Test
public void givenNoProfile_whenSendMessage_thenDevelopmentMessagingService() {
assertEquals(messagingService.getClass(), DevelopmentMessagingService.class);
}
}
4.2. 生產環境配置
接下來,我們激活生產環境配置。現在,ProductionMessagingService應該可用,並且其他兩個 Bean 也是如此。
當我們測試消息服務時,它應該使用ProductionMessagingService,因為它被標記為主要 Bean:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {FallbackMessagingService.class, DevelopmentMessagingService.class, ProductionMessagingService.class})
@ActiveProfiles("production")
public class ProductionMessagingServiceUnitTest {
@Autowired
private MessagingService messagingService;
@Test
public void givenProductionProfile_whenSendMessage_thenProductionMessagingService() {
assertEquals(messagingService.getClass(), ProductionMessagingService.class);
}
}
4.3. 測試配置文件
最後,讓我們激活測試配置文件。這會從上下文中移除 test 豆。由於我們已經移除了生產配置文件,ProductionMessagingService 也不再可用。
在這種情況下,消息服務應使用 FallbackMessagingService,因為它是一個可用的唯一豆:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {FallbackMessagingService.class, DevelopmentMessagingService.class, ProductionMessagingService.class})
@ActiveProfiles("test")
public class FallbackMessagingServiceUnitTest {
@Autowired
private MessagingService messagingService;
@Test
public void givenTestProfile_whenSendMessage_thenFallbackMessagingService() {
assertEquals(messagingService.getClass(), FallbackMessagingService.class);
}
}5. 結論
在本教程中,我們討論了 Spring 框架中備用 Bean 的概念。我們看到了如何定義主 Bean 和備用 Bean,以及如何在 Spring 應用程序中使用它們。備用 Bean 在任何其他符合條件的 Bean 不可用時,提供替代實現。這在根據活動配置文件或其他條件之間切換不同實現時非常有用。