spring的property对象循环引用是否会报错

关于spring的property对象循环引用是否会报错

结论

  1. 会,但是报错的原因原因是Error creating bean with name 'student': Requested bean is currently in creation: Is there an unresolvable circular reference?,并非是StackOverFlow
  2. 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对象其实是正在创建,这样就能够发现当前创建的实例之间存在循环依赖。

此处附上beforePrototypeCreationafterPrototypeCreationisPrototypeCurrentlyInCreation的代码;其中prototypesCurrentlyInCreationThreadLocal对象。

	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))));
	}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值