Spring框架中的 Fallback Bean 指南

在本教程中,我们讨论了 Spring 框架中的后备 bean(fallback bean) 概念。我们了解了如何定义主 bean 和后备 bean,以及如何在 Spring 应用程序中使用它们。当任何其他合格 bean 不可用时,后备 bean 提供了替代实现。当根据活动配置文件或其他条件在不同的实现之间切换时,这很有用。

Fallback bean 是在 Spring Framework 版本 6.2.0-M1 中引入的。当另一个相同类型的 bean 不可用或无法初始化时,它们提供替代实现。

这在我们希望妥善处理故障并提供回退机制以确保应用程序继续运行的情况下非常有用。

主 Bean 和后备 Bean
在 Spring 应用程序中,我们可以定义多个相同类型的 bean。默认情况下,Spring 使用 bean 名称和类型来标识 bean。当我们有多个相同名称和类型的 bean 时,我们可以使用@Primary 注释将其中一个标记为主 bean,以优先于其他 bean。如果在初始化应用程序上下文时创建了多个相同类型的 bean,并且我们想要指定默认情况下应使用哪个 bean,则这很有用。

类似地,我们可以定义一个 fallback bean,当没有其他符合条件的 bean 可用时,提供备用实现。我们可以使用注解@Fallback 将 bean 标记为 fallback bean。只有当没有其他同名 bean 可用时,fallback bean 才会被注入到应用程序上下文中。

代码示例
让我们看一个例子来演示在 Spring 应用程序中如何使用主 bean 和后备 bean。我们将创建一个使用不同消息服务发送消息的小型应用程序。假设我们在生产和非生产环境中有多个消息服务,需要在它们之间切换以优化性能和成本。

消息传递接口
首先,让我们为我们的服务定义一个接口:

public interface MessagingService {
    void sendMessage(String text);
}

该接口有一种方法可以将提供的文本作为消息发送。

主Bean
接下来,让我们定义一个消息服务的实现作为主 bean:

@Service
@Profile("production")
@Primary
public class ProductionMessagingService implements MessagingService {
    @Override
    public void sendMessage(String text) {
       
// implementation in production environment
    }
}

在此实现中,我们使用@Profile 注释来指定此 bean 仅在生产配置 文件处于活动状态 时可用 。我们还使用@Primary注释将其标记为主 bean   。

非主Bean
我们将消息服务的另一种实现定义为非主 bean:

@Service
@Profile("!test")
public class DevelopmentMessagingService implements MessagingService {
    @Override
    public void sendMessage(String text) {
       
// implementation in development environment
    }
}

在此实现中,我们使用 @Profile注释来指定此 bean 在测试配置文件未激活 时可用 。这意味着它将在除 测试 配置文件之外的所有配置文件中可用。

后备 Bean
最后,让我们为消息服务定义一个后备 bean:

@Service
@Fallback
public class FallbackMessagingService implements MessagingService {
    @Override
    public void sendMessage(String text) {
        // fallback implementation
    }
}

在这个实现中,我们使用 @Fallback 注解来标记这个 bean 为 fallback bean, 只有当没有其他同类型的 bean 可用时,才会注入这个 bean。

测试
现在,让我们通过自动装配消息服务并检查根据活动配置文件使用了哪种实现来测试我们的应用程序。

没有profile
在第一个测试中,我们没有激活任何配置文件。由于 生产配置文件未激活,因此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);
    }
}

生产环境
接下来,让我们激活 生产配置文件。现在 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);
    }
}

测试配置文件
最后,让我们激活 测试 配置文件。这会从上下文中删除DevelopmentMessagingService bean。由于我们已删除生产 配置文件, 因此ProductionMessagingService 也不可用。

在这种情况下,消息服务应该使用 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);
    }
}