首页 > 解决方案 > 如何在 Spring 中实现基于注解的集合合并?

问题描述

我正在尝试使用一组特定类型的所有 bean 初始化一个 Spring 组件(实际上,任何我可以迭代的东西)。

Spring 核心文档讨论了集合合并,但仅在基于注释的配置的上下文中。

假设我有以下配置

@Configuration
public class MyConfig {
    @Bean
    public SomeInterface single() {
        return new SomeInterface() {};
    }

    @Bean
    public Set<SomeInterface> multi() {
        return Collections.singleton(
            new SomeInterface() {}
        );
    }
}

接口定义为

public interface SomeInterface {}

我希望这个组件能够获得两个 bean 的聚合——一些包含两个匿名类的集合。

@Component
public class MyComponent {
    public MyComponent(Set<SomeInterface> allInterfaces) {
        System.out.println(allInterfaces.size()); // expecting 2, prints 1
    }
}

我明白为什么 Spring 会达到它的结果;它看到这个方法需要一个Set<SomeInterface>并且MyConfig::multi是一个类型的bean Set<SomeInterface>,所以它会自动装配它。

如果我将签名更改为Collection<SomeInterface>,它会自动与MyConfig::single. 再次,我明白了原因:没有什么完全匹配的,但是有类型的 bean SomeInterface(在这种情况下,只有一个),所以它构造了它们的临时集合并自动装配。很好,但不是我想要的。

我希望解决方案是可扩展的,这样如果添加了另一个 bean,则依赖组件不需要更改。我尝试过使用两个参数,每个参数都有一个@Qualifier, 并且有效但不可扩展。

我怎样才能让它工作?

标签: javaspringdependency-injectionspring-bean

解决方案


正如您已经提到的,MyConfig::multi是 type 的 bean Set<SomeInterface>,因此自动装配Collection<Set<SomeInterface>>将为您提供所有这些集合。以下应该工作

public MyComponent(Collection<SomeInterface> beans,
                   Collection<Set<SomeInterface>> beanSets) {
    // merge both params here
}

如果您需要在多个位置进行所有实现,则定义另一个包含合并集合的 bean 并自动装配该 bean 可能是有意义的:

static class SomeInterfaceCollection {
    final Set<SomeInterface> implementations;

    SomeInterfaceCollection(Set<SomeInterface> implementations) {
        this.implementations = implementations;
    }
}

@Bean
public SomeInterfaceCollection collect(Collection<SomeInterface> beans,
        Collection<Collection<SomeInterface>> beanCollections) {
    final HashSet<SomeInterface> merged = ...
    return new SomeInterfaceCollection(merged);
}

推荐阅读