关于spring的property对象循环引用是否会报错
结论
- 会,但是报错的原因原因是
Error creating bean with name 'student': Requested bean is currently in creation: Is there an unresolvable circular reference?
,并非是StackOverFlow
。 - 在
BeanFacotry
创建的时候不会有任何的问题,仅当第一次向IOC容器获取循环依赖的bean时(创建)才会抛出异常。(注意:lazy-init
是不起作用的)
测试代码
这里只给出xml配置的方法,对应的entity自己创建即可。
<bean id="student" class="org.example.StudentPOJO" scope="prototype">
<property name="name" value="测试" />
<property name="teacher" ref="teacher" />
</bean>
<bean id="teacher" class="org.example.TeacherPOJO" scope="prototype">
<property name="name" value="教师" />
<property name="student" ref="student" />
</bean>
下面是Main方法的调用代码,报错的位置是在getBean
的时候。
public class PropertyCycleRefTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("propertyCycleRefTest.xml");
StudentPOJO student = (StudentPOJO)context.getBean("student"); //报错是在这里抛出的
System.out.println(student);
System.out.println(student.getTeacher());
TeacherPOJO teacher = (TeacherPOJO)context.getBean("teacher");
System.out.println(teacher);
System.out.println(teacher.getStudent());
}
}
原因
本次创建的两个类都是propterty
类型的,所以在创建的new ClassPathXmlApplicationContext("propertyCycleRefTest.xml")
的时候是不会对对StudentPOJO
进行实例化,只是解析xml中的信息获取BeanDefinition
的数据结构,并把BeanDefinition
注册到BeanFactory
里面。
在我们首次调用context.getBean("student")
的时候,才会对类进行创建,然后进行实例化。该方法实际调用的是AbstractBeanFactory.doGetBean()
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
// 省略部分代码
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
if (isPrototypeCurrentlyInCreation(beanName)) { // 校验当前beanName对应的实例是不是正在创建,如果是,就存在循环引用
throw new BeanCurrentlyInCreationException(beanName);
}
// 省略部分代码
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName); // 开始创建实例前,把当前beanName设置为正在创建
prototypeInstance = createBean(beanName, mbd, args); // createBean包含了依赖注入的过程
}
finally {
afterPrototypeCreation(beanName); // 创建结束,把当前beanName从正在创建的set中移除
}
beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
// 省略部分代码
}
createBean
包含了依赖注入的过程,如果student
依赖的是teacher
对象,这个时候就会调用beanFactory.getBean("teacher")
方法获取teacher
对象,从而触发teacher
对象创建实例的过程。同理,teacher
创建的时候,会调用beanFactory.getBean("student")
触发student
再次实例化,但是isPrototypeCurrentlyInCreation
就会发现student
对象其实是正在创建,这样就能够发现当前创建的实例之间存在循环依赖。
此处附上beforePrototypeCreation
、afterPrototypeCreation
、isPrototypeCurrentlyInCreation
的代码;其中prototypesCurrentlyInCreation
是ThreadLocal
对象。
protected void beforePrototypeCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get();
if (curVal == null) {
this.prototypesCurrentlyInCreation.set(beanName);
}
else if (curVal instanceof String) {
Set<String> beanNameSet = new HashSet<>(2);
beanNameSet.add((String) curVal);
beanNameSet.add(beanName);
this.prototypesCurrentlyInCreation.set(beanNameSet);
}
else {
Set<String> beanNameSet = (Set<String>) curVal;
beanNameSet.add(beanName);
}
}
protected void afterPrototypeCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get();
if (curVal instanceof String) {
this.prototypesCurrentlyInCreation.remove();
}
else if (curVal instanceof Set) {
Set<String> beanNameSet = (Set<String>) curVal;
beanNameSet.remove(beanName);
if (beanNameSet.isEmpty()) {
this.prototypesCurrentlyInCreation.remove();
}
}
}
protected boolean isPrototypeCurrentlyInCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get();
return (curVal != null &&
(curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
}