51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

Spring 中的 Fallback Bean

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

赞(3)
未经允许不得转载:工具盒子 » Spring 中的 Fallback Bean