How to dynamically Autowire a Bean in Spring – 如何在Spring中动态地自动连接一个Bean

最后修改: 2020年 5月 9日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

In this short tutorial, we’ll show how to dynamically autowire a bean in Spring.

在这个简短的教程中,我们将展示如何在Spring中动态地自动连接一个bean

We’ll start by presenting a real-world use case where dynamic autowiring might be helpful. In addition to this, we’ll show how to solve it in Spring in two different ways.

我们将首先介绍一个真实世界的用例,在这个用例中,动态自动布线可能是有帮助的。除此之外,我们将展示如何用两种不同的方式在Spring中解决这个问题。

2. Dynamic Autowiring Use Cases

2.动态自动布线用例

Dynamic autowiring is helpful in places where we need to dynamically change the Spring’s bean execution logic. It’s practical especially in places where what code to execute is chosen based on some runtime variables.

动态自动布线在我们需要动态改变Spring的Bean执行逻辑的地方很有帮助。特别是在根据一些运行时变量来选择执行什么代码的地方,它很实用。

To demonstrate a real-world use case, let’s create an application that controls servers in different regions of the world. For this reason, we’ve created an interface with two simple methods:

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

public interface RegionService {
    boolean isServerActive(int serverId);

    String getISOCountryCode();
}

and two implementations:

和两个实施方案。

@Service("GBregionService")
public class GBRegionService implements RegionService {
    @Override
    public boolean isServerActive(int serverId) {
        return false;
    }

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

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

Let’s say we have a website where a user has an option to check whether the server is active in the selected region. Consequently, we’d like to have a service class that dynamically changes the RegionService interface implementation given the input of the user. Undoubtedly, this is the use case where dynamic bean autowiring comes into play.

假设我们有一个网站,用户可以选择检查服务器是否在选定的区域内活动。因此,我们希望有一个服务类能够根据用户的输入动态地改变RegionService接口的实现。毋庸置疑,这就是动态Bean自动布线发挥作用的用例。

3. Using BeanFactory

3.使用BeanFactory

BeanFactory is a root interface for accessing a Spring bean container. In particular, it contains useful methods to obtain specific beans. Since BeanFactory is also a Spring bean, we can autowire and use it directly in our class:

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;
    }
}

We’ve used an overloaded version of the getBean() method to get the bean with the given name and desired type.

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

And while this works, we’d really rather rely on something more idiomatic; that is, something that uses dependency injection.

虽然这样做行得通,但我们确实更愿意依赖一些更成文的东西,也就是使用依赖注入的东西。

4. Using Interfaces

4.使用接口

To solve this with dependency injection, we’ll rely on one of Spring’s lesser-known features.

为了用依赖注入解决这个问题,我们将依靠Spring的一个不太为人所知的特性。

Besides standard single-field autowiring, Spring gives us an ability to collect all beans that are implementations of the specific interface into a Map:

除了标准的单字段自动布线,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);
    }
}

We’ve created a map in a constructor that holds implementations by their country code. Furthermore, we can use it later in a method to get a particular implementation to check whether a given server is active in a specific region.

我们在构造函数中创建了一个地图,按国家代码保存实现。此外,我们可以在以后的方法中使用它来获得一个特定的实现,以检查一个给定的服务器是否在一个特定的地区活动。

5. Conclusion

5.总结

In this quick tutorial, we’ve seen how to dynamically autowire a bean in Spring using two different approaches.

在这个快速教程中,我们看到了如何使用两种不同的方法在Spring中动态地自动连接一个bean。

As always, the code shown in this article is available over on GitHub.

一如既往,本文中显示的代码可在GitHub上获得