如何在Spring中动态自动装配Bean

2023/05/13

1. 概述

在本文中,我们将演示如何在Spring中动态地自动注入bean

我们首先演示一个动态自动装配可能会有所帮助的真实用例。除此之外,我们将演示如何在Spring中以两种不同的方式解决它。

2. 动态自动装配用例

在需要动态更改Spring的bean执行逻辑的地方,动态自动装配很有用。它特别适用于根据一些运行时变量选择要执行的代码的地方。

为了演示一个真实的用例,让我们创建一个控制世界不同地区的服务器的应用程序。出于这个原因,我们创建一个带有两个简单方法的接口:

public interface RegionService {
    boolean isServerActive(int serverId);

    String getISOCountryCode();
}

和两个实现类:


@Service("CNregionService")
public class CNRegionService implements RegionService {

    @Override
    public boolean isServerActive(int serverId) {
        return false;
    }

    @Override
    public String getISOCountryCode() {
        return "CN";
    }
}

@Service("USregionService")
public class USRegionService implements RegionService {
    @Override
    public boolean isServerActive(int serverId) {
        return true;
    }

    @Override
    public String getISOCountryCode() {
        return "US";
    }
}

假设我们有一个网站,用户可以选择检查服务器在所选区域是否处于活动状态。 因此,我们希望有一个Service类,根据用户的输入动态更改RegionService接口实现。 毫无疑问,这是动态bean自动注入发挥作用的地方。

3. 使用BeanFactory

BeanFactory是用于访问Spring bean容器的顶层接口。特别是,它包含获取特定bean的有用方法。 由于BeanFactory也是一个Spring bean,我们可以自动装配并直接在我们的类中使用它:


@Service
public class BeanFactoryDynamicAutowireService {
    private static final String SERVICE_NAME_SUFFIX = "regionService";
    private final BeanFactory beanFactory;

    @Autowired
    public BeanFactoryDynamicAutowireService(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    public boolean isServerActive(String isoCountryCode, int serverId) {
        RegionService service = beanFactory.getBean(getRegionServiceBeanName(isoCountryCode), RegionService.class);
        return service.isServerActive(serverId);
    }

    private String getRegionServiceBeanName(String isoCountryCode) {
        return isoCountryCode + SERVICE_NAME_SUFFIX;
    }
}

我们使用了getBean()方法的重载版本来获取具有给定名称和所需类型的bean。

虽然这是可行的,但我们还是更倾向于依赖一些更地道的东西;也就是说,使用依赖注入

4. 使用接口

为了通过依赖注入解决这个问题,我们将依赖Spring一个鲜为人知的特性。

除了标准的单字段自动注入之外,Spring还允许我们能够将实现特定接口的所有bean收集到一个Map中


@Service
public class CustomMapFromListDynamicAutowireService {
    private final Map<String, RegionService> servicesByCountryCode;

    @Autowired
    public CustomMapFromListDynamicAutowireService(List<RegionService> regionServices) {
        servicesByCountryCode = regionServices.stream().collect(Collectors.toMap(RegionService::getISOCountryCode, Function.identity()));
    }

    public boolean isServerActive(String isoCountryCode, int serverId) {
        RegionService service = servicesByCountryCode.get(isoCountryCode);
        return service.isServerActive(serverId);
    }
}

我们在构造函数中创建了一个Map,该Map按国家代码保存实现类。 此外,我们可以稍后在方法中使用它来获取特定实现类,以检查给定服务器是否在特定区域中处于活动状态。

以下是测试类:


@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = DynamicAutowireConfig.class)
class DynamicAutowireIntegrationTest {

    @Autowired
    private BeanFactoryDynamicAutowireService beanFactoryDynamicAutowireService;

    @Autowired
    private CustomMapFromListDynamicAutowireService customMapFromListDynamicAutowireService;

    @Test
    void givenDynamicallyAutowiredBean_whenCheckingServerInGB_thenServerIsNotActive() {
        assertThat(beanFactoryDynamicAutowireService.isServerActive("CN", 101), is(false));
        assertThat(customMapFromListDynamicAutowireService.isServerActive("CN", 101), is(false));
    }

    @Test
    void givenDynamicallyAutowiredBean_whenCheckingServerInUS_thenServerIsActive() {
        assertThat(beanFactoryDynamicAutowireService.isServerActive("US", 101), is(true));
        assertThat(customMapFromListDynamicAutowireService.isServerActive("US", 101), is(true));
    }
}

5. 总结

在这个教程中,我们介绍了如何在Spring中使用两种不同的方法动态地自动装配bean。

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章