【问题标题】:DI injecting a String bean as single element of a List<String> instead of the proper List<String> beanDI 将 String bean 作为 List<String> 的单个元素注入,而不是正确的 List<String> bean
【发布时间】:2019-09-18 09:17:33
【问题描述】:

我是第一次学习 Spring Framework 和 DI,并尝试了一个带有 Spring-boot 1.2.0 的小测试应用程序(由于 Spring Framework 版本要求为 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,并且“做了次好的事情”,返回一个注入了它知道的唯一 String bean 的 List:title。

反过来,这让我相信我可能以错误的方式做整个 Autowired 事情,并且我可能无法按照要求按类型/名称排列自动装配。据我了解,默认自动装配是按类型,构造函数应该尝试查找两种不同类型的 bean:String 和 List,但我尝试使用 @Bean(name = "title"/"authors" 注释 bean ) 没有成功。如果我还尝试使用 @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”和 authors:[“A sample book”] 而作者的实际值应该是:[“John”,“Adam”,“哈利”]

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

【问题讨论】:

  • 问题是你可以从同一个对象创建多个bean,所以你可以使用@Autowire List&lt;MyObject&gt; objs来获取这个类型的所有bean作为列表。对于每个 @Bean 一个带有方法名称的 bean(如果您没有自己指定),因此如果您有多个相同类型的 bean,则如果您想访问单个 bean,则必须使用该名称。
  • @SamuelPhilipp 确实,我尝试添加第二个 String bean,并且 DI 同时返回“A sample title”和数组上的新 bean,我不知道默认情况下列表是如何工作的。我读到使用限定符应该可以工作,但到目前为止我一直在逃避。

标签: java spring spring-boot spring-4


【解决方案1】:

当类型化集合或数组上存在@Bean 注解时,该 bean 将自动填充应用程序上下文注册的所有该类型的 bean(请参阅reference documentation)。在您的情况下,这是 title 一直 (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;
}

【讨论】:

  • 在构造函数上使用 '@Qualifier 会导致启动时出错。也许 Spring 4.1.x 上没有按限定符自动装配的集合?我刚刚能够使它工作,使用作者bean的'@Resource注释而不是'@Autowired,但遗憾的是我不得不与构造函数注入分道扬镳,因为'@Resource仅在方法级别的字段中可用.
  • 您收到的错误是什么?对我来说,构造函数工作正常。
  • 我在帖子中详细介绍过的一个;太长无法将其粘贴到此处 :( 通过构造函数参数表示的不满足的依赖关系,索引为 1 类型 [java.util.List]: : 没有为依赖项 [java.lang 的集合找到类型 [java.lang.String] 的限定 bean。 String]:预计至少有 1 个 bean 有资格作为此依赖项的自动装配候选者。依赖项注释:{@org.springframework.beans.factory.annotation.Qualifier(value=authors)};对于构造函数:@Autowired public BookBean(String title, @Qualifier("authors") List&lt;String&gt; authors) {...}
  • 也许这与作者将 List 定义为 java @Bean 而不是 XML 有关?
  • 我编辑了我的答案。显然,文档建议在这种特定情况下使用@Resource。虽然我觉得 @Qualifier 为我工作而不是为你工作很奇怪。
【解决方案2】:

默认情况下@Autowired 将尝试按类型查找 bean。在您的 BookBean 类中,您正在注入 String titleList&lt;String&gt; authors

Spring 在幕后做了什么?

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

列出作者 - 它会尝试找到 all String 类型的 Bean,在您的场景中,您只有一个:title()

总而言之,除非您使用 @Resource(id ="authors") 注入,否则您的 List&lt;String&gt; authors() bean 将无法访问。

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

【讨论】:

  • 确实,@Resource 的作用就像一个魅力。唯一需要注意的是,您不能将它与构造函数注入一起使用,但除此之外,它可以达到其目的。原始 DI 是一个很好的提示,它可能会更快地解决这个问题,但我陷入了一个小角落。
猜你喜欢
  • 2020-10-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-17
  • 1970-01-01
  • 2019-09-10
  • 2021-01-13
相关资源
最近更新 更多