今天和同事讨论到Spring自动注入时,发现有这么一段代码特别地困惑,当然大致的原理还是可以理解的,只不过以前从来没有这么用过。想到未来可能会用到,或者未来看别人写的代码时不至于花时间解决同样的困惑,所以小编还是觉得有必要研究记录一下。
首先让我们先看下这段代码是什么?
@Autowired private XiaoMing xiaoming; @Autowired private XiaoMing wanger; 复制代码
XiaoMing.java
package com.example.demo.beans.impl; import org.springframework.stereotype.Service; /** * * The class XiaoMing. * * Description:小明 * * @author: huangjiawei * @since: 2018年7月23日 * @version: $Revision$ $Date$ $LastChangedBy$ * */ @Service public class XiaoMing { public void printName() { System.err.println("小明"); } } 复制代码
我们都知道 @Autowired
可以根据类型( Type
)进行自动注入,并且默认注入的bean为单例( SingleTon
)的,那么我们可能会问,上面注入两次不会重复吗?答案是肯定的。而且每次注入的实例都是同一个实例。下面我们简单验证下:
@RestController public class MyController { @Autowired private XiaoMing xiaoming; @Autowired private XiaoMing wanger; @RequestMapping(value = "/test.json", method = RequestMethod.GET) public String test() { System.err.println(xiaoming); System.err.println(wanger); return "hello"; } } 复制代码
调用上面的接口之后,将输出下面内容,可以看出两者为同一实例。
com.example.demo.beans.impl.XiaoMing@6afd4ce9 com.example.demo.beans.impl.XiaoMing@6afd4ce9 复制代码
如果我们要注入的类型声明为一个接口类型,而且该接口有1个以上的实现类,那么下面这段代码还能够正常运行吗?我们假设 Student
为接口, WangEr
和 XiaoMing
为两个实现类。
@Autowired private Student stu1; @Autowired private Student stu2; 复制代码
@Service public class XiaoMing implements Student { 复制代码
@Service public class WangEr implements Student { 复制代码
答案是上面的代码不能正常运行,而且Spring 还启动报错了,原因是Spring想为 Student
注入一个单例的实例,但在注入的过程中意外地发现两个,所以报错,具体错误信息如下:
Field stu1 in com.example.demo.controller.MyController required a single bean, but 2 were found: - wangEr: defined in file [C:/Users/huangjiawei/Desktop/demo/target/classes/com/example/demo/beans/impl/WangEr.class] - xiaoMing: defined in file [C:/Users/huangjiawei/Desktop/demo/target/classes/com/example/demo/beans/impl/XiaoMing.class] 复制代码
那该怎么弄才行呢?一般思路我们会想到为每个实现类分配一个id值,结果就有了下面的代码:
@Autowired private Student stu1; @Autowired private Student stu2; 复制代码
@Service("stu1") public class XiaoMing implements Student { 复制代码
@Service("stu2") public class WangEr implements Student { 复制代码
做完上面的配置之后,Spring就会根据字段名称默认去bean工厂找相应的bean进行注入,注意名称不能够随便取的,要和注入的属性名一致。