让我们通过一段儿代码来展开说明吧,如下:
@Configuration
public class DemoConfiguration {
/**
* DemoA 被 DemoB、DemoC、DemoD 所依赖
* @return
*/
@Bean
public DemoA demoA() {
DemoA da = new DemoA();
System.out.println("demoA..........." + da);
return da;
}
/**
* 通过调用 demoA() 注入依赖 DemoA
* @return
*/
@Bean
public DemoB demoB() {
DemoA da = demoA();
System.out.println("demoB..........." + da);
return new DemoB(da);
}
/**
* 通过入参注入依赖 DemoA
* @return
*/
@Bean
public DemoC demoC(DemoA demoA) {
System.out.println("demoC..........." + demoA);
return new DemoC(demoA);
}
/**
* 通过调用 demoA() 注入依赖 DemoA
* @return
*/
@Bean
public DemoD demoD() {
DemoA da = demoA();
System.out.println("demoD..........." + da);
return new DemoD(da);
}
public static class DemoA {
}
public static class DemoB {
private DemoA demoA;
public DemoB(DemoA demoA) {
this.demoA = demoA;
}
}
public static class DemoC {
private DemoA demoA;
public DemoC(DemoA demoA) {
this.demoA = demoA;
}
}
public static class DemoD {
private DemoA demoA;
public DemoD(DemoA demoA) {
this.demoA = demoA;
}
}
}
以上代码很显然是采用了JavaConfig 形式实现的配置类,那么第一个问题就来了。
demoA()、demoB()、demoC(DemoA demoA)、demoD() 初始化以后 IOC 环境中会存在几个 DemoA 实例对象呢?
从代码逻辑直观的判断是:
- demoA() 会初始化一个 DemoA 实例对象
- demoB() 因为调用了 demoA() 一次,那么会初始化一个 DemoA 实例对象
- demoC(DemoA demoA) 采用的是参数注入依赖(从 IOC 环境中获取已存在的 DemoA 实例对象),所以并没有再初始化一个 DemoA 实例对象
- demoD() 和 demoB() 一样,也是调用了 demoA() 一次,那么就又会初始化一个 DemoA 实例对象
这样看来,IoC 环境里应该会存在 3 个同样类型(DemoA)类型的实例对象,那么第二个问题就来了。
结果真的是这样吗( 3 个同样类型(DemoA)类型的实例对象)?
让我们来运行一下上边的代码,看看控制台的输出结果吧,如下:
demoA...........com.annoroad.openapi.config.DemoConfiguration$DemoA@54567b05
demoB...........com.annoroad.openapi.config.DemoConfiguration$DemoA@54567b05
demoC...........com.annoroad.openapi.config.DemoConfiguration$DemoA@54567b05
demoD...........com.annoroad.openapi.config.DemoConfiguration$DemoA@54567b05
惊喜不!?最终的结果是,无论是通过参数注入依赖(例如:demoC(DemoA demoA)),还是通过调用方法注入依赖(例如:demoB() 、demoD() ),最终打印出的实例对象都是同一个,第三个问题来了。
为什么会这样呢!?
在 Spring 的参考文档中找到了答案,原来 Spring 是通过拦截「配置类的方法调用」来避免多次初始化同一类型实例对象的,拦截逻辑中如果发现当前方法没有对应的类型实例会回去初始化对象实例,否则直接返回之前的对象实例。
写在后边的话
至此该问题算是搞明白了!不过,就我个人来说,还是比较习惯通过参数进行注入依赖(至少不会有多余的歧义,哈哈哈)。