DI 将 String bean 作为 List<String> 的单个元素而不是正确的 List<String> bean 注入

DI injecting a String bean as single element of a List<String> instead of the proper List<String> bean

我是第一次学习 Spring 框架和 DI,并尝试使用 Spring-boot 1.2.0 的小测试应用程序(由于 Spring 框架版本要求的项目限制be 4.1.x), 我制作了一个 BookBean,它有两个属性:一个字符串标题和一个作者列表。显然 DI 将标题作为列表的唯一成员注入,我完全不知道为什么。

我在标记为@Bean 的方法上添加了一些日志记录,以查看何时调用它们以及 BookBean 构造函数,我注意到在调用构造函数之后调用了 String 方法:

[2019-04-29 14:46:05.631] boot - 3888  INFO [main] --- CollectionConfig: returning title: [A sample book]
[2019-04-29 14:46:05.637] boot - 3888  INFO [main] --- BookBean: construction called
[2019-04-29 14:46:05.649] boot - 3888  INFO [main] --- CollectionConfig: returning authors: [[John, Adam, Harry]]

这让我相信 DI 在尝试构造 BookBean 时没有可用的 List bean,并且 "does the next best thing" 返回一个注入了它知道的唯一 String bean 的 List:title。

反过来,这让我相信我可能以错误的方式进行了整个自动装配,并且我可能无法按要求按 type/name 排列自动装配。据我了解,默认的自动装配是按类型进行的,构造函数应该尝试寻找两种不同类型的 bean:String 和 List,但我尝试用 @Bean(name = "title"/"authors") 没有成功。如果我还尝试使用 @Qualifier("title"/"authors") 注释构造函数参数,我会收到以下错误:

[2019-04-29 14:54:25.847] boot - 20824  INFO [main] --- CollectionConfig: returning title: [A sample book]
[2019-04-29 14:54:25.853] boot - 20824  WARN [main] --- AnnotationConfigEmbeddedWebApplicationContext: Exception encountered during context initialization - cancelling refresh attempt
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'bookBean' defined in URL [jar:file:/C:/dev/Repos/branches/branch_rest_remake/branch_2019_04_11/webapp/target/webapp-0.0.1-SNAPSHOT.jar!/com/company/spring/webapp/domain/BookBean.class]: Unsatisfied dependency expressed through constructor argument with index 1 of type [java.util.List]: : No qualifying bean of type [java.lang.String] found for dependency [collection of java.lang.String]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Qualifier(value=authors)}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [java.lang.String] found for dependency [collection of java.lang.String]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Qualifier(value=authors)}

这些是我的 类:(编辑 - 从示例中删除了 Logger 实例化,但它们在代码中)

@RestController
@RequestMapping("/")
public class DummyController {
    @Autowired
    private BookBean bookBean;

    @RequestMapping("/di")
    String dummyDependencyInjection() {
        logger.info("called /di");
        return bookBean.printAuthors();
    }
}
@Configuration
public class CollectionConfig {
    @Bean
    public String title() {
        String title = "A sample book";
        logger.info("returning title: ["+title+"]");
        return title;
    }

    @Bean
    public List<String> authors() {
        List<String> authors = Arrays.asList("John", "Adam", "Harry");
        logger.info("returning authors: ["+authors+"]");
        return authors;
    }
}
@Component
public class BookBean {
    private String title;
    private List<String> authors;

    @Autowired
    public BookBean(String title, List<String> authors) {
        logger.info("construction called");
        this.title = title;
        this.authors = authors;
    }

    public String printAuthors() {
        logger.info("printAuthors called");
        return "Authors in "+ title + "\n"+authors;
    }
}

如果没有 @Qualifier 注释,bean 将使用标题:"A sample book" 和作者:["A sample book"] 构建,而作者的实际值应为:["John","Adam", "Harry"]

使用@Qualifier 注释,它只是无法启动。

当类型化集合或数组上出现 @Bean 批注时,该 bean 将自动填充应用程序上下文注册的该类型的所有 bean(请参阅 reference documentation)。在您的情况下,这是 title been (A sample book),因此 authors 列表仅包含该条目。

如果您想自动装配特定的 bean,您可以使用 @Qualifier 注释(参见 reference documentation)来引用该名称。

在你的情况下,构造函数可以重写为:

@Autowired
public BookBean(String title, @Qualifier("authors") List<String> authors) {
    this.title = title;
    this.authors = authors;
}

使用类型化集合时 reference documentation actually suggests(在灰色部分下方稍微向下滚动)。您要使用@Resource 注释:

private String title;

@Resource
private List<String> authors;

public BookBean(String title) {
    this.title = title;
}

默认情况下 @Autowired 将尝试按类型查找 beans。在您的 BookBean class 中,您正在注入一个 String title 和一个 List<String> authors.

Spring 是做什么的?

String title - 它会找到 a String 类型的 Bean(如果找到多个,它需要一个 @Qualifier 来确定注入哪一个),在你的情况下 @Bean String title()

List authors - 它将尝试查找 all String 类型的 Beans,在您的场景中您只有一个:title()

总而言之,您的 List<String> authors() bean 将无法访问,除非您使用 @Resource(id ="authors") 注入它。

退后几步,您永远不应该依赖原语进行依赖注入,因为它们可能会误导您。我建议将它们包装在一些 classes 中,a.k.a 定义一个 Title class 和一个 Authors class if 你真的需要使用依赖注入。