我是第一次学习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的东西,并且我可能无法按照要求按类型/名称排列自动装配。我的理解是默认的autowire是Type,并且构造函数应该尝试查找两种不同类型的bean:String和List,但我尝试使用@Bean注释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的标题是:“样本书”和作者:[“样本书”]当作者的实际值应为:[“John”,“Adam”,“Harry”]
使用@Qualifier注释,它无法启动。
默认情况下,@Autowired
会尝试按类型查找bean。在你的BookBean
课程中,你注射了String title
和List<String> authors
。
Spring在引擎盖下做了什么?
字符串标题 - 它会找到一个String类型的Bean(如果它找到多个,它需要一个@Qualifier来确定要注入哪一个),在你的情况下@Bean String title()
列表作者 - 它将尝试查找所有类型为String的Bean,在您的场景中,您只有一个:title()
总而言之,除非你使用List<String> authors()
注入它,否则你的@Resource(id ="authors")
bean将无法访问。
退后几步,你不应该依赖原语进行依赖注入,因为它们可能会误导你。我建议将它们包装在某些类中,如果你真的需要使用依赖注入,a.k.a定义一个Title
类和一个Authors
类。
当类型化集合或数组上存在@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;
}