目录标题
-
[Java SE 演示](#Java SE 演示)
-
[Spring 容器演示](#Spring 容器演示)
-
循环依赖是什么?
-
循环依赖的场景有哪一些?
-
三级缓存分别是什么?三个Map有什么异同?
-
三级缓存是如何解决循环依赖的?
-
为什么要使用三级缓存?为什么不可以用二级缓存?
-
为什么构造器循环依赖、原型Bean循环依赖无法用三级缓存解决?
-
看过 Spring源码吗?一般我们说的 Spring容器是什么?
-
如何检测是否存在循环依赖?实际开发中见过循环依赖的异常吗?
-
如果循环依赖的时候,所有类又都需要 Spring AOP自动代理,那Spring如何提前曝光?曝光的是 原始bean 还是 代理后的bean ?
什么是循环依赖
多个Bean互相引用,形成环路
循环依赖场景
- 原型Bean的循环依赖
- 单例bean之构造注入的循环依赖
- 单例bean之setter注入的循环依赖
前两者无法解决,最后一种可以通过Spring提供的三级缓存来进行实现。
Java SE 演示
java
@Component
public class ServiceA {
private ServiceB serviceB;
public void setServiceB(ServiceB serviceB) {
this.serviceB = serviceB;
System.out.println("A 里面设置了B");
}
// public ServiceA(ServiceB serviceB) {
// this.serviceB = serviceB;
// }
}
java
@Component
public class ServiceB {
private ServiceA serviceA;
public void setServiceA(ServiceA serviceA) {
this.serviceA = serviceA;
System.out.println("B 里面设置了A");
}
// public ServiceB(ServiceA serviceA) {
// this.serviceA = serviceA;
// }
}
java
public class ClientDemo {
public static void main(String[] args) {
clientSet();
// clientConstruct();
}
/**
* setter注入
*/
private static void clientSet() {
//创建serviceA
ServiceA serviceA = new ServiceA();
//创建serviceB
ServiceB serviceB = new ServiceB();
//将serviceA注入到serviceB中
serviceB.setServiceA(serviceA);
//将serviceB注入到serviceA中
serviceA.setServiceB(serviceB);
}
/**
* 构造注入
*/
private static void clientConstruct(){
// new ServiceA(new ServiceB(new ServiceA(new ServiceB())));
}
}
Spring 容器演示
xml
<!-- <bean id="a" class="com.example.demo.circulardependency.spring.A" scope="singleton">-->
<bean id="a" class="com.example.demo.circulardependency.spring.A" scope="prototype">
<property name="b" ref="b"/>
</bean>
<!-- <bean id="b" class="com.example.demo.circulardependency.spring.B" scope="singleton">-->
<bean id="b" class="com.example.demo.circulardependency.spring.B" scope="prototype">
<property name="a" ref="a"/>
</bean>
java
public class ClientSpringContainer {
public static void main(String[] args) {
sampleDemo();
}
/**
* spring
*
* 2024/2/2 11:40
*/
private static void sampleDemo() {
/**
* setter注入
*
* 11:39:14.055 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'a'
* ---A created success
* 11:39:14.064 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'b'
* ---B created success
*/
/**
* 构造注入
*
* Exception in thread "main" org.springframework.beans.factory.BeanCreationException:
*
*/
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
A a = context.getBean("a", A.class);
B b = context.getBean("b", B.class);
}
}
三级缓存
核心知识
三级缓存
一级缓存 :Map<String, Object> singletonObjects,我愿称之为成品单例池,常说的 Spring 容器就是指它,我们获取单例 bean 就是在这里面获取的,存放已经经历了完整生命周期的Bean对象
二级缓存 :Map<String, Object> earlySingletonObjects,存放早期暴露出来的Bean对象,Bean的生命周期未结束(属性还未填充完整,可以认为是 半成品的 bean, 实例化但未初始化的Bean对象 )
三级缓存 :Map<String, ObiectFactory<?>> singletonFactories,存放可以生成Bean的工厂(FactoryBean),用于生产(创建)对象
java
/** Cache of singleton objects: bean name to bean instance. */
// 一级缓存:singleton对象的缓存:bean名称 - bean实例。
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of singleton factories: bean name to ObjectFactory. */
// 三级缓存:单例工厂的缓存:bean名称 - ObjectFactory
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name to bean instance. */
// 二级缓存:早期singleton对象的缓存:bean名称 - bean实例。
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
四大方法
- getSingleton():从容器里面获得单例的bean,没有的话则会创建 bean
- doCreateBean():执行创建 bean 的操作(在 Spring 中以 do 开头的方法都是干实事的方法)
- populateBean():创建完 bean 之后,对 bean 的属性进行填充
- addSingleton():bean 初始化完成之后,添加到单例容器池中,下次执行 getSingleton() 方法时就能获取到
三级缓存中的迁移
- A创建过程中需要B,于是A将自己放到三级缓存里面,去实例化B
- B实例化的时候发现需要A,于是B先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,找到了A,然后把三级缓存里面的这个A放到二级缓存里面,并删除三级缓存里面的A
- B顺利初始化完毕,将自己放到一级缓存里面(此时B里面的A依然是创建中状态),然后回来接着创建A,此时B已经创建结束,直接从一级缓存里面拿到B,然后完成创建,并将A自己放到一级缓存里面。
三级缓存源码分析
源码思维导图
源码图例
课前问题
还剩下三个:
- 为什么不可以用二级缓存?这部分我在网上搜寻了一下,跟AOP的代理有关(由于目前我对AOP不熟,怕误导了大家,就先欠着)
- 开发中解决循环依赖?欠着
- 循环依赖遇上AOP?欠着