AProxy对象—>A代理对象—>A代理对象的target = A普通对象
A—>推断构造方法—>普通对象—>依赖注入—>初始化前—>初始化—>初始化后(AOP)—>代理对象—>放入单例池(三级缓冲的第一级缓冲)—>bean对象
@Component
public class UserService {
@Autowired
private OrderService orderService;
public void test() {
System.out.println(orderService);
}
}
class AProxy extends A {
A target;
public void test() {
//切面逻辑
//xxx
target.test();// A普通对象.test()打印orderService,可以正常打印出来orderService
}
}
public static void main() {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
//这里拿到的是userService的代理对象,cglb代理是继承实现的,所以代理对象可以强转为UserService
UserService userService = (UserService)ac.getBean(UserService.class);
userService.test();
}
注意:
1、普通对象是经过了依赖注入的,所以target.test()方法,执行正常
2、但是代理对象并没有进行依赖注入,没有必要
先执行”切面逻辑“,可以看到实际是代理对象的切面逻辑
普通对象的test()在打印orderService
A—>无参构造方法—>普通对象—>依赖注入—>初始化前—>初始化—>初始化后(AOP)—>放入单例池(三级缓冲的第一级缓冲)—>bean对象
第三级:singletonFactories Map
第二级:earlySingletonObjects Map
第一级:singletonObjects Map
earlyProxyReferences:判断是否进行了AOP,避免第4步重复AOP
creatingSet:保存正在创建过程中的bean,判断是否出现循环依赖
A的bean生命周期:
1、实例化—>A的普通对象
2、填充B—>单例池—>创建B
3、填充其他属性
4、其他步骤(包括AOP)
5、加入单例池中
在创建B时,发现缺少A,发生了循环依赖,要解决就必须打破循环,要想打破循环,就必须在创建B时,A属性可以在一个地方被找到就打破了,所以要有一个地方要能找到A的对象,在创建B时能用。所以在创建A普通对象时,引入ZhouyuMap
A的bean生命周期:
1、实例化—>A的普通对象—>放入ZhouyuMap
2、填充B—>单例池—>创建B
3、填充其他属性
4、其他步骤(包括AOP)
5、加入单例池中
(此时ZhouyuMap,到底算是第几级缓存,还不清楚)
上面虽然打破了循环,但是当A需要AOP时,就需要进行代理,产生代理对象,我们知道在spring中,经过AOP的bean,在单例池中是
A的bean生命周期:
1、实例化—>A的普通对象—>放入ZhouyuMap
2、填充B—>单例池—>创建B
3、填充其他属性
4、其他步骤(包括AOP)—>A代理对象
5、加入单例池中
要想解决这个问题,也好办,那就是在2.2步从ZhouyuMap
A的bean生命周期:
1、实例化—>A的普通对象—>AOP—>A代理对象—>放入ZhouyuMap
2、填充B—>单例池—>创建B
3、填充其他属性
4、其他步骤(包括AOP)—>A代理对象
5、加入单例池中
上步中“提前AOP“是在解决出现了循环依赖,还又要进行AOP想到的办法,但是不是每一个bean都要“提前进行AOP”,也就是说出现了循环依赖,才“提前进行AOP”
A的bean生命周期:
1、实例化—>A的普通对象—>循环依赖—>AOP—>A代理对象—>放入ZhouyuMap
2、填充B—>单例池—>创建B
3、填充其他属性
4、其他步骤(包括AOP)—>A代理对象
5、加入单例池中
现在问题就是,怎么判断出现了循环依赖?这里很明显是在2.2步发现了出现了循环依赖,这里就是”正在创建的bean,被别人依赖了“,A在“创建过程中”被B依赖了,就说明发生了循环依赖呗!所以我们要记录”正在创建的bean“,引入creatingSet记录正在创建的bean,在2.2步中,从单例池中没有获取到A,而且还在creatingSet中得知A正在创建,那肯定就发生了循环依赖。
A的bean生命周期:
0、creatingSet[A]
1、实例化—>A的普通对象
2、填充B—>单例池—>创建B
3、填充其他属性
4、其他步骤(包括AOP)—>A代理对象
5、加入单例池中
但是问题来了,假如此时A还有C依赖A呢?
A的bean生命周期:
0、creatingSet[A]
1、实例化—>A的普通对象
2、填充B—>单例池—>创建B
3、填充其他属性
4、其他步骤(包括AOP)—>A代理对象
5、加入单例池中
发现B、C分别创建了不同的A代理对象,这是有问题的。此时考虑是否可以将2.2中A的代理对象放入单例池中呢,创建C时从单例池中去除A的代理对象赋值给C,保证一个A代理对象,肯定是不能的,因为A的创建才走到第2步,而单例池中的对象是经过了完整的bean生命周期动作的对象,那又需要一个Map保证单例,那就新加一个Map呗,earlySingletonObjects(根据名称”提前单例对象“)。
A的bean生命周期:
0、creatingSet[A]
1、实例化—>A的普通对象
2、填充B—>单例池—>创建B
3、填充其他属性
4、其他步骤(包括AOP)—>A代理对象
5、加入单例池中
我们发现B、C的A属性赋的值,都是从earlySingletonObjectsMap中拿的,earlySingletonObjectsMap中拿的对象是自己还没创建完,已经赋值给其他对象用了,所以叫”提前暴露“,earlySingletonObjectsMap就是为了”保存出现了循环依赖,提前给其他bean去用的代理bean“
但是还有个小问题,其实在2.2中A的AOP生成A的代理对象时,是需要A的普通对象的,否则代理是完不成的,因为代理对象的target指向普通对象,这么说循环还是没有解决,没有打破,所以还得有一个缓存去缓存A的普通对象去彻底打破循环,第三级缓冲singletonFactories,先简单理解为singletonFactoriesMap
A的bean生命周期:
0、creatingSet[A]
1、实例化—>A的普通对象—>singletonFactoriesMap
2、填充B—>单例池—>创建B
3、填充其他属性
4、其他步骤(包括AOP)—>A代理对象
5、加入单例池中
但是实际第三级缓存存储的不是A的普通对象,而是一个ObjectFactory,这是一个函数式接口lamda表达式
其实这里就能想通了,上面我们也说了,并不是每一个bean都需要AOP的,那么这个lamba表达式就是用来判断是否需要AOP的,在第1步放进去时,就只是singletonFactoriesMap.put进去一个表达式,并不会立即执行,在2.2步中从第三级缓冲中取出对应A的lamda表达式判断是否需要AOP。可以搜搜wraIfNecessary、getarlyBeanReference方法再详细看看。
A的bean生命周期:
0、creatingSet[A]
1、实例化—>A的普通对象—>singletonFactoriesMap
2、填充B—>单例池—>创建B
3、填充其他属性
4、其他步骤(包括AOP,注意执行完lamda表达式后singletonFactories.remove了,就不会重复AOP了)
5、earlySingletonObjects.get(A),加入单例池中
通过源码,发现singletonFactory.getObject()执行完lamda后,singletonObject对象可能是代理对象也可能是普通对象,会在singletonFactories.remove掉这个lamda表达式,也就是这个lamda表达式是一次性的,执行一次就够了,避免在第4步AOP重复。
AOP其实是一个插件,用户可以根据需求选择是否使用AOP,需要用时就在配置类中加上@EnableAspectJAutoProxy,然后在需要AOP增强的地方配置切点相关的即可使用了。所以第4步判断是否需要AOP,其实不是Spring做的事情,而是这个”插件AOP“去做的事情,
A的bean生命周期:
0、creatingSet[A]
1、实例化—>A的普通对象—>singletonFactoriesMap
2、填充B—>单例池—>创建B
3、填充其他属性
4、其他步骤(包括AOP)—> earlyProxyReferences
5、earlySingletonObjects.get(A),加入单例池中
问题
@Component
public class A {
private B b;
public A(B b) {
this.b = b;
}
}
@Component
public class B {
private A a;
public B(A a) {
this.a = a;
}
}
像上面这种情况,属性上没有@Autowired注解的,并且提供了有参构造(覆盖无参构造),此时发生循环依赖,A的普通对象都创建不出来的。
解决方式:
1、因为有参构造覆盖无参构造,所以另外提供一个无参构造方法即可
2、在这个有参构造方法上面加上@Lazy注解,大致解决思路就是:给B生成一个代理对象(注意这个代理和AOP代理没有任何关系),这个代理对象也是CGLB继承式的继承了B,然后把这个代理对象B赋值给A的B属性,从而A对象创建完成,放入单例池,然后创建B,从单例池取出A赋值给B的A属性。直接没有发生循环依赖;你可能会说此时beanB和B的代理对象不是一个对象啊?是的,不是一个对象,但是实际上,不影响真实调用的,例如执行beanA的方法用到了b属性的方法,此时b代理对象中执行时是先从容器中找到beanB对象,再去执行beanB的方法。
class BProxy extends B {
//这个代理和AOP代理不同,没有target
public void test() {
//从容器中找beanB
//执行beanB的test()
beanB.test();
}
}
第三级:singletonFactories Map
第二级:earlySingletonObjects Map
第一级:singletonObjects Map
earlyProxyReferences:判断是否进行了AOP,避免第4步重复AOP
creatingSet:保存正在创建过程中的bean,判断是否出现循环依赖