1. 概述

本文将演示如何在 Spring 框架中注入 Java 集合类型,主要包括 ListMapSet 三大常用接口。

核心思路是:通过 Spring 的依赖注入机制,将集合实例或集合中的元素(包括普通值或 Bean 引用)自动装配到目标 Bean 中。这在处理多实现策略、插件化设计或配置集合化参数时非常实用。

✅ 掌握集合注入,能帮你更灵活地组织 Bean 之间的关系,避免硬编码。


2. 使用 @Autowired 注入 List

我们先定义一个包含 List<String> 字段的 Bean:

public class CollectionsBean {

    @Autowired
    private List<String> nameList;

    public void printNameList() {
        System.out.println(nameList);
    }
}

这里使用了字段注入(field injection),通过 @Autowired 让 Spring 自动装配一个字符串列表。

接着在配置类中注册 CollectionsBean,并提供一个 List<String> 类型的 Bean:

@Configuration
public class CollectionConfig {

    @Bean
    public CollectionsBean getCollectionsBean() {
        return new CollectionsBean();
    }

    @Bean
    public List<String> nameList() {
        return Arrays.asList("John", "Adam", "Harry");
    }
}

⚠️ 注意:Spring 会自动将所有类型匹配的 Bean 收集起来,注入到 List 中。这里的 nameList() 方法返回的 Bean 正好匹配 List<String> 类型。

测试代码:

ApplicationContext context = new AnnotationConfigApplicationContext(CollectionConfig.class);
CollectionsBean collectionsBean = context.getBean(CollectionsBean.class);
collectionsBean.printNameList();

输出结果:

[John, Adam, Harry]

3. 使用构造器注入 Set

现在我们改用 Set<String> 并采用构造器注入方式:

public class CollectionsBean {

    private Set<String> nameSet;

    public CollectionsBean(Set<String> strings) {
        this.nameSet = strings;
    }

    public void printNameSet() {
        System.out.println(nameSet);
    }
}

构造器注入更利于不可变性和单元测试,属于推荐做法。

配置类中需要手动创建 Set 实例并传入构造器:

@Bean
public CollectionsBean getCollectionsBean() {
    return new CollectionsBean(new HashSet<>(Arrays.asList("John", "Adam", "Harry")));
}

✅ 构造器注入时,Spring 不会自动聚合多个 Bean,需要开发者显式构造集合。


4. 使用 Setter 注入 Map

接下来演示 Map 类型的注入,使用 setter 方式:

public class CollectionsBean {

    private Map<Integer, String> nameMap;

    @Autowired
    public void setNameMap(Map<Integer, String> nameMap) {
        this.nameMap = nameMap;
    }

    public void printNameMap() {
        System.out.println(nameMap);
    }
}

在配置类中定义一个 Map<Integer, String> 类型的 Bean:

@Bean
public Map<Integer, String> nameMap(){
    Map<Integer, String> nameMap = new HashMap<>();
    nameMap.put(1, "John");
    nameMap.put(2, "Adam");
    nameMap.put(3, "Harry");
    return nameMap;
}

调用 printNameMap() 后输出:

{1=John, 2=Adam, 3=Harry}

⚠️ 注意:Map 注入时,key 和 value 都由提供方决定,接收方只需类型匹配即可。


5. 注入 Bean 引用集合

更常见的场景是注入一组相同类型的 Bean 实例,比如多个策略实现类。

先定义一个简单 Bean:

public class BaeldungBean {

    private String name;

    public BaeldungBean(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return name;
    }
}

然后在 CollectionsBean 中注入 List<BaeldungBean>

public class CollectionsBean {

    @Autowired(required = false)
    private List<BaeldungBean> beanList;

    public void printBeanList() {
        System.out.println(beanList);
    }
}

在配置类中注册多个 BaeldungBean 类型的 Bean:

@Configuration
public class CollectionConfig {

    @Bean
    public BaeldungBean getElement() {
        return new BaeldungBean("John");
    }

    @Bean
    public BaeldungBean getAnotherElement() {
        return new BaeldungBean("Adam");
    }

    @Bean
    public BaeldungBean getOneMoreElement() {
        return new BaeldungBean("Harry");
    }
}

Spring 会自动将所有 BaeldungBean 类型的 Bean 聚合成一个 List 并注入。

输出结果:

[John, Adam, Harry]

5.1 使用 @Order 控制 Bean 注入顺序

默认情况下,Bean 的注入顺序不确定。若需排序,可使用 @Order 注解:

@Configuration
public class CollectionConfig {

    @Bean
    @Order(2)
    public BaeldungBean getElement() {
        return new BaeldungBean("John");
    }

    @Bean
    @Order(3)
    public BaeldungBean getAnotherElement() {
        return new BaeldungBean("Adam");
    }

    @Bean
    @Order(1)
    public BaeldungBean getOneMoreElement() {
        return new BaeldungBean("Harry");
    }
}

Spring 会按 @Order 值从小到大排序注入:

[Harry, John, Adam]

@Order 在 AOP、监听器、集合注入等场景中都非常有用。


5.2 使用 @Qualifier 筛选特定 Bean

有时我们不想要所有 BaeldungBean,只想注入其中一部分。这时可以用 @Qualifier 标记目标 Bean。

修改注入点:

@Autowired
@Qualifier("CollectionsBean")
private List<BaeldungBean> beanList;

并在配置类中标记目标 Bean:

@Configuration
public class CollectionConfig {

    @Bean
    @Qualifier("CollectionsBean")
    public BaeldungBean getElement() {
        return new BaeldungBean("John");
    }

    @Bean
    public BaeldungBean getAnotherElement() {
        return new BaeldungBean("Adam");
    }

    @Bean
    public BaeldungBean getOneMoreElement() {
        return new BaeldungBean("Harry");
    }
}

此时只有被 @Qualifier("CollectionsBean") 标记的 getElement() 返回的 Bean 被注入。

测试输出:

[John]

@Qualifier 是实现“按需注入”的利器,尤其适合多实现类共存的场景。


6. 设置空集合为默认值

有时候我们希望即使没有配置,也能拿到一个空集合而不是 null,避免 NPE。

可以使用 @Value 结合 SpEL 设置默认值:

public class CollectionsBean {

    @Value("${names.list:}#{T(java.util.Collections).emptyList()}")
    private List<String> nameListWithDefaultValue;
    
    public void printNameListWithDefaults() {
        System.out.println(nameListWithDefaultValue);
    }
}
  • ${names.list:} 表示从配置文件读取 names.list,冒号后是默认值(空)。
  • #{T(java.util.Collections).emptyList()} 是 SpEL 表达式,调用静态方法返回不可变空列表。

如果未在 application.properties 中设置 names.list,输出为:

[]

⚠️ 注意:Collections.emptyList() 返回的是不可变集合。如果后续需要修改,建议用 new ArrayList<>() 包装。


7. 总结

本文系统演示了 Spring 中集合注入的多种方式:

  • ListSetMap 均可通过 @Autowired 注入
  • ✅ 支持字段、构造器、setter 三种注入方式,推荐构造器
  • ✅ 可注入值类型集合,也可注入 Bean 引用集合
  • ✅ 使用 @Order 控制注入顺序
  • ✅ 使用 @Qualifier 实现精准筛选
  • ✅ 通过 SpEL 设置默认空集合,避免 null

这些技巧在实际项目中非常实用,比如:

  • 多个 Validator 实现注入 List<Validator>
  • 多个 Handler 按顺序执行
  • 动态配置白名单、黑名单等

完整代码示例可参考:GitHub 项目地址


原始标题:Spring - Injecting Collections | Baeldung