Spring IOC循环依赖
什么是循环依赖
ClassA中依赖ClassB
ClassB中依赖ClassA
依赖主要分为两种:引用、成员变量。
依赖注入(依赖)分为两种:构造方法、setter方法。
//订单
public class OrderService {
@Autowired
private UserService userService;
public void saveOrder() {
//插入订 单表(需要用户名称,而页面只传递一个用户ID)
//调用UserService去查询用户信息
}
}
//用户
public class UserService {
@Autowired
private OrderService orderService;
public void queryOrders() {
//调用OrderService的服务
}
}
Spring中Bean实例的创建流程
- 实例化(new):此处通过反射调用构造器new对象,引处可能会发生循环依赖【构造器循环依赖】。无法通过自动解决,只能修改依赖关系或者改为setter方法依赖。
- 填充属性:此处通过反射调用成员变量setter方法进行依赖注入,此处可能会发生循环依赖【setter方法循环依赖】。可能通过缓存解决。
- 初始化:Bean初始化。
Spring三级缓存
源码 | 级别 | 描述 |
---|---|---|
singletonObjects | 一级缓存 | 用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用 |
earlySingletonObjects | 二级缓存 | 存放原始的 bean 对象(尚未填充属性),用于解决循环依赖 |
singletonFactories | 三级缓存 | 存放 bean 工厂对象,用于解决循环依赖 |
从缓存中获得bean顺序:
- 先从一级缓存获得,没有则从二级缓存获得,没有则从三级缓存获得。
ClassA创建流程
-
实例化
如果是单例bean,那么会创建ClassA对象并提前被暴露给三级缓存保存。
实例化过程中只会bean创建出来,成员变量都是空值。 -
填充属性
因为ClassA中有变量ClassB所以需要给ClassB变量赋值,此处从缓存中获得引用对象(现在缓存只有ClassA对象所以找不到)。
则实例化ClassB对象并把ClassB添加到三级缓存。
给ClassB填充ClassA对象,此时缓存中有ClassA对象,ClassB进行初始化。 -
初始化
ClassA进行初始化。
注:每次添加缓存操作:添加二级缓存时先删除三缓存,添加一级缓存时会先删除二、三级缓存。
添加二级缓存代码:
添加一级缓存代码:
此时你会感觉二级缓存作用不大,如果把二级缓存删除仿佛也能正常注入,主要有两方面原因:
- 三级缓存是bean的工厂,如果没有二级缓存那么每次从三级缓存获得出来的bean是不一样的。
- 二级缓存存放的bean是未初始化完成状态,属于提前暴露bean。
- 三级缓存也有提前暴露bean,还要对bean做beanPostProcessor后置处理。
思考
-
如果一个目标对象被aop动态加上事务增强功能(代理对象)的话,那么spring容器中存储的是目标对象,还是增强之后的对象,还是都存储?
答案是spring只会存储一个对象,如果目标对象被AOP产生了代理对象,那么存储的就是代理对象。 -
aop针对目标对象产生代理对象,是发生在bean创建的哪个流程呢?
是发生在Bean初始化的过程中,具体说,是发生在Bean调用初始化方法之后,去进行AOP流程。
版权声明:本文为h273979586原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。