获取单例

Object sharedInstance = getSingleton(beanName);
notion image
 
@Override @Nullable public Object getSingleton(String beanName) { //获取BeanName对应的单例对象,默认是允许提前引用的 return getSingleton(beanName, true); }
 
notion image

三级缓存

方法getSingleton中,总共也就用到了三个缓存,分别是singletonObjectsearlySingletonObjectssingletonFactories
 
  1. singletonObjects:用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用
  1. earlySingletonObjects:提前曝光的单例对象的cache,存放原始的 bean 对象(尚未填充属性),用于解决循环依赖
  1. singletonFactories:单例对象工厂的cache,存放 bean 工厂对象,用于解决循环依赖
 
 

循环依赖

 

setter注入的循环依赖

建了两个类Student1和Student2,然后在这个两个类中分别都创建了一个成员变量,Student1的成员变量student2依赖Student2,而Student2中的成员变量student1依赖Student1。
public class Student1 { private Student2 student2; public Student2 getStudent2() { return student2; } public void setStudent2(Student2 student2) { this.student2 = student2; } @Override public String toString() { return "Student1{" + "student2=" + student2 + '}'; } }
 
public class Student2 { private Student1 student1; public Student1 getStudent1() { return student1; } public void setStudent1(Student1 student1) { this.student1 = student1; } @Override public String toString() { return "Student2{" + "student1=" + student1 + '}'; } }
 
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="student1" class="com.ruyuan.container.cycle.setter.Student1"> <property name="student2" ref="student2"/> </bean> <bean id="student2" class="com.ruyuan.container.cycle.setter.Student2"> <property name="student1" ref="student1"/> </bean> </beans>
 
public class ApplicationContextDemo { public static void main(String[] args) throws Exception { ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); Student1 student1 = (Student1) ctx.getBean("student1"); System.out.println(student1); } }
notion image
因为循环依赖的问题,会导致student1在实例化时就陷入了一个死循环中了,最终耗尽栈内存的空间而抛出栈内存溢出的异常。

构造方法循环依赖

我们依然创建了两个类,这两个类中的成员变量和刚才都是一样的,唯一的区别是,我们还添加了构造方法用于注入属性的值
public class Student1 { private Student2 student2; public Student1(Student2 student2) { this.student2 = student2; } public Student2 getStudent2() { return student2; } public void setStudent2(Student2 student2) { this.student2 = student2; } @Override public String toString() { return "Student1{" + "student2=" + student2 + '}'; }
 
public class Student2 { private Student1 student1; public Student2(Student1 student1) { this.student1 = student1; } public Student1 getStudent1() { return student1; } public void setStudent1(Student1 student1) { this.student1 = student1; } @Override public String toString() { return "Student2{" + "student1=" + student1 + '}'; } }
可以看到在配置文件中,唯一和刚才不同的是注入属性的方式,从property标签改成了constructor-arg标签,也就是通过构造方法的方式设置属性的值了。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="student1" class="com.ruyuan.container.cycle.constructor.Student1"> <constructor-arg name="student2" ref="student2"/> </bean> <bean id="student2" class="com.ruyuan.container.cycle.constructor.Student2"> <constructor-arg name="student1" ref="student1"/> </bean> </beans>
 
public class ApplicationContextDemo { public static void main(String[] args) throws Exception { ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); Student1 student1 = (Student1) ctx.getBean("student1"); System.out.println(student1); } }
notion image
 

三级缓存解决setter循环依赖

普通Bean实例化过程分为三个步骤
  1. 通过反射创建一个实例bean
  1. 为bean填充属性
  1. bean的一些初始化操作
 
解决setter注入的循环依赖的详细过程
notion image
Student2会把刚刚通过反射初步实例化好的早期单例bean,如图先封装到对象工厂ObjectFactory中,井添加到单例工厂缓存singletonFactories里,这样的话,就相当于Spring把Student2初步实例化的单例bean暴露给外界了。
student1此时如果要为属性赋值的话,就可以从工厂缓存中获取到Student2的早期单例bean,完成属性赋值,进而完成Student1 bean的实例化,而Student2的bean继续实例化时,发现自己为属性赋值时也需要依赖Student1的bean,这个时候可能Student1的bean已经实例化好了,所以Student2的实例化也可以完成了。