理解循环依赖问题,首先明白 spring 有四种注入方式。第一种,SET 注入 a 类中持有 b 类的引用,并且 a 类有 b 的 set 方法。在 bean 中添加 <property> 标签即可注入。实质上是将 b 实例化,然后调用 set 方法注入。
<bean id=”a” class=”com.qunar.pojo.StudentA” scope=”singleton”>
<property name=”studentB” ref=”b”></property>
</bean>
第二种,构造器注入 a 类中持有 b 类的引用,并且 a 的构造函数参数中有 b。实质上就是通过构造函数注入,创建 a 对象时要把 b 对象传进去。
<bean id=”a” class=”com.qunar.pojo.StudentA”>
<constructor-arg index=”0″ ref=”b”></constructor-arg>
</bean>
第三种,静态工厂如果有需要静态工厂实例化的类,不能通过静态工厂. 方法实现。在 bean 属性中对应类指向静态工厂,对应方法指向返回实例的方法
图片描述
第四种,实例工厂如果工厂不是静态,需要实例化,就实例化对应工厂,设定 factory-bean 和 factory-method 进行方法调用。
图片描述
设定三个实体类,StudentA,B,C 代码如下,A 持有 B,B 持有 C,C 持有 A
public class StudentA {
private StudentB studentB ;
public void setStudentB(StudentB studentB) {
this.studentB = studentB;
}
public StudentA() {
}
public StudentA(StudentB studentB) {
this.studentB = studentB;
}
}
当我通过构造器注入时,会产生 BeanCurrentlyInCreationException 异常。为什么会出现这种异常,spring 如何加载实体?
图片描述
Spring 容器会将每一个正在创建的 Bean 标识符放在一个“当前创建 Bean 池”中,Bean 标识符在创建过程中将一直保持在这个池中,因此如果在创建 Bean 过程中发现自己已经在“当前创建 Bean 池”里时将抛出 BeanCurrentlyInCreationException 异常表示循环依赖;而对于创建完毕的 Bean 将从“当前创建 Bean 池”中清除掉。
Spring 容器先创建单例 StudentA,StudentA 依赖 StudentB,然后将 A 放在“当前创建 Bean 池”中,此时创建 StudentB,StudentB 依赖 StudentC , 然后将 B 放在“当前创建 Bean 池”中, 此时创建 StudentC,StudentC 又依赖 StudentA,但是,此时 Student 已经在池中,所以会报错,因为在池中的 Bean 都是未初始化完的,所以会依赖错误. 解决这个问题,可以用 setter 注入的方式。
图片描述
Spring 是先将 Bean 对象实例化之后,再设置对象属性。所以会先调用他的无参构造函数实例化。每个对象存在一个 map 中。当遇到依赖,就去 map 中调用对应的单例对象。
图片描述
一部分源码
另外:对于“prototype”作用域 Bean,Spring 容器无法完成依赖注入,因为“prototype”作用域的 Bean,Spring 容器不进行缓存,因此无法提前暴露一个创建中的 Bean。
Spring 装配 Bean 的过程
实例化; 设置属性值; 如果实现了 BeanNameAware 接口, 调用 setBeanName 设置 Bean 的 ID 或者 Name; 如果实现 BeanFactoryAware 接口, 调用 setBeanFactory 设置 BeanFactory; 如果实现 ApplicationContextAware, 调用 setApplicationContext 设置 ApplicationContext 调用 BeanPostProcessor 的预先初始化方法; 调用 InitializingBean 的 afterPropertiesSet()方法; 调用定制 init-method 方法;调用 BeanPostProcessor 的后初始化方法;
Spring 容器关闭过程
调用 DisposableBean 的 destroy();
调用定制的 destroy-method 方法;
图片描述
了解了 bean 默认是单例模式,不由想 spring 的单例和设计模式单例同一种吗?其实不一样。单例模式是指在一个 JVM 进程中仅有一个实例,而 Spring 单例是指一个 Spring Bean 容器 (ApplicationContext) 中仅有一个实例。如果有多个 Spring 容器,可能有多个 Bean 对象。spring 单例是一种类似注册表实现的方式。利用 hashmap,向 map 中注册和取值,思路类似下面代码
public class Singleton {
private static Map<String,Singleton> map = new HashMap<String,Singleton>();
static{
Singleton single = new Singleton();
map.put(single.getClass().getName(), single);
}
// 保护的默认构造子
protected Singleton(){}
// 静态工厂方法, 返还此类惟一的实例
public static Singleton getInstance(String name) {
if(name == null) {
name = Singleton.class.getName();
}
if(map.get(name) == null) {
try {
map.put(name, (Singleton) Class.forName(name).newInstance());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return map.get(name);
}
}