1、概览 {#1概览}
本文将带你了解 Spring 中 Fallback Bean 的概念。
Fallback Bean 是在 Spring Framework 6.2.0-M1 中引入的,当另一个相同类型的 Bean 不可用或无法初始化时,它提供了一种替代实现。
2、Primary Bean 和 Fallback Bean {#2primary-bean-和-fallback-bean}
在 Spring 中,我们可以定义多个相同类型的 Bean。默认情况下,Spring 使用 Bean 名称和类型来标识 Bean。当有多个名称和类型相同的 Bean 时,可以使用 @Primary
注解将其中一个标记为 Primary(主要)Bean,使其优先于其他 Bean。如果在 Application Context 初始化时创建了多个相同类型的 Bean,而我们又想指定默认使用哪个 Bean,那么这就非常有用了。
同样,我们可以定义一个 Fallback Bean,以便在没有其他合格 Bean 时提供替代实现。我们可以使用 @Fallback
注解将一个 Bean 标记为 Fallback(后备) Bean。只有当没有其他同名的 Bean 可用时,才会将后备 Bean 注入到 Application Context。
3、示例代码 {#3示例代码}
来看一个示例,演示如何在 Spring 应用中使用 Primary Bean 和 Fallback Bean。
我们要创建一个使用不同 MQ 服务发送消息的小应用。假设我们在生产环境和非生产环境中拥有多个 MQ 服务,并且需要在它们之间切换以优化性能和成本。
3.1、Messaging 接口 {#31messaging-接口}
首先,为服务定义一个接口:
public interface MessagingService {
void sendMessage(String text);
}
该接口有一个发送文本消息的方法。
3.2、Primary Bean {#32primary-bean}
接下来,实现 MessagingService
的 Primary Bean 实现:
@Service
@Profile("production")
@Primary
public class ProductionMessagingService implements MessagingService {
@Override
public void sendMessage(String text) {
// 生产环境中的实现
}
}
如上,我们使用 @Profile
注解来指定此 Bean 仅在 production profile 激活时可用。我们还使用 @Primary
注解将其标记为 Primary Bean。
3.3、非 Primary Bean {#33非-primary-bean}
定义另一个 MessagingService
接口的实现,非 Primary Bean:
@Service
@Profile("!test")
public class DevelopmentMessagingService implements MessagingService {
@Override
public void sendMessage(String text) {
// 开发环境下的实现
}
}
在此实现中,我们使用 @Profile
注解来指定该 Bean 在 test Profile 未激活时可用。这意味着除了 test Profile 外,它在所有 Profile 中都是可用的。
3.4、Fallback Bean {#34fallback-bean}
最后,为 MessagingService
定义一个 Fallback(备选) Bean:
@Service
@Fallback
public class FallbackMessagingService implements MessagingService {
@Override
public void sendMessage(String text) {
// fallback 实现
}
}
在此实现中,我们使用 @Fallback
注解将此 Bean 标记为 Fallback Bean。只有当没有其他相同类型的 Bean 可用时,才会注入此 Bean。
4、测试 {#4测试}
现在,让我们通过自动装配 MessagingService
并根据 Profile 检查使用的实现来测试我们的应用。
4.1、未激活 Profile {#41未激活-profile}
第一个测试,不激活任何 Profile。由于 production Profile 未激活,ProductionMessagingService
不可用,而其他两个 Bean 可用。
此时的 MessagingService
实现应该是 DevelopmentMessagingService
,因为它优先于 Fallback 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、激活 production Profile {#42激活-production-profile}
接下来,激活 production Profile。现在,ProductionMessagingService
和另外两个 Bean 都可用。
在此测试中 MessagingService
的实现应该是 ProductionMessagingService
,因为它被标记为 Primary 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 Profile {#43激活-test-profile}
最后,激活 test Profile。这将从 Application Context 中移除 DevelopmentMessagingService
Bean。由于未激活 production profile,ProductionMessagingService
也不可用。
此时 MessagingService
的实现应该是 FallbackMessagingService
,因为它是唯一可用的 Bean:
@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、总结 {#5总结}
本文介绍了 Spring 中的 Fallback Bean 概念,通过示例介绍了如何定义 Primary Bean 和 Fallback Bean 以及如何在 Spring 应用中使用它们。
当任何其他符合条件的 Bean 不可用时,Fallback Bean 提供了一种替代实现。这在根据激活的 profile 或其他条件在不同实现之间切换时非常有用。
Ref:https://www.baeldung.com/spring-fallback-beans