单例Bean中依赖注入Scope类型的Bean ,为什么默认会失效?
单例Bean中依赖注入Scope类型的Bean,默认会失效
我演示一下
//启动类
@ComponentScan("com.itheima.a09")
public class A09 {
private static final Logger log = LoggerFactory.getLogger(A09.class);
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A09.class);
E bean = context.getBean(E.class);
log.debug("{}",bean.getF1());
log.debug("{}",bean.getF1());
log.debug("{}",bean.getF1());
log.debug("{}",bean.getF1());
context.close();
}
}
//输出结果:
com.itheima.a09.F1@3835c46
com.itheima.a09.F1@3835c46
com.itheima.a09.F1@3835c46
com.itheima.a09.F1@3835c46
发现get返回的F1都一样
@Component
public class E {
@Autowired
F1 f1;
public F1 getF1(){
return f1;
}
}
--------
@Scope("prototype")
@Component
public class F1 {
}
虽然F1配置成了多例的Bean,但是对于E来说,因为E是单例,只会被创建一下,对应的f1,依赖注入f1的时候只会注入一次f1,所以每次返回的时候仅仅返回我们刚开始第一次依赖注入时的F1
解决方案:
@Lazy
在被注入的类的成员变量上 加一个@Lazy
ProxyMode
在被注入的目标的Bean类中 将ProxyMode设置为ScopedProxyMode.TARGET_CLASS
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Lazy,ProxyMode 这两种方式本期其实是通过代理在我们要使用被依赖注入的bean的时候每次都会给我们代理创建新的Bean,代理在获取的时候调用toString方法的时候就会创建新的被注入的多例对象
ObjectFactory
@Component
public class E {
@Autowired
F1 f1;
public F1 getF1(){
return f1;
}
@Autowired
private ObjectFactory<F2> objectFactory;
public F2 getF2(){
return objectFactory.getObject();
}
}
//F2还是一个多例Bean
@Scope("prototype")
@Component
public class F2 {
}
//验证
System.out.println("--------------------------");
log.debug("{}",bean.getF2());
log.debug("{}",bean.getF2());
log.debug("{}",bean.getF2());
log.debug("{}",bean.getF2());
//此时的返回结果就都是多例Bean的结果了
这种方式是通过工厂的方式,在我们要使用被依赖注入的bean的时候工厂每次都会给我们创建新的Bean
依赖注入 Spring容器
//E中添加的代码
@Autowired
private AnnotationConfigApplicationContext context;
public F3 getF3(){
return context.getBean(F3.class);
}
//F3
@Scope("prototype")
@Component
public class F3 {
}
//测试
System.out.println("--------------------------");
log.debug("{}",bean.getF3());
log.debug("{}",bean.getF3());
log.debug("{}",bean.getF3());
log.debug("{}",bean.getF3());
/*
com.itheima.a09.F3@6dc17b83
com.itheima.a09.F3@5e0826e7
com.itheima.a09.F3@32eff876
com.itheima.a09.F3@8dbdac1
*/
通过注入容器的方式每次当我们使用F3的时候,直接通过容器.getBean的方式就一定会得到F3
Scope所定义的类型
总结
单例中注入其它类型的Scope解决方案:
@Lazy
ProxyMode设置为ScopedProxyMode.TARGET_CLASS
依赖注入 objectFactory<>
依赖注入 AP容器
前两种实质都是通过代理的方式来实现创建的
不推荐使用代理的方式,原因;
- 一方面有一定的性能损耗
- 用容器或工厂去拿显得更为简洁
此处我演示的是 单例中注入多例的解决方案,但是对其它的三种Scope类型同样试用
以上四种解决方案有一个共同点:
推迟其它ScopeBean的获取
在单例bean创建的时候并没有也创建被注入的依赖bean,而是通过中间间接创建并加入所依赖的bean对象,再到运行时获取这个多例