加入收藏 | 设为首页 | 会员中心 | 我要投稿 安卓应用网 (https://www.0791zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

Spring循环依赖正确性及Bean注入的顺序关系详解

发布时间:2020-05-23 19:17:29 所属栏目:Java 来源:互联网
导读:一、前言我们知道Spring可以是懒加载的,就是当真正使用到Bean的时候才实例化Bean。当然也不全是这样,例如配置Bean的lazy-init属性,可以控制Spring的加载时机。现在机器的性能、内存等都比较高,基本上也不使用懒

一、前言

我们知道 Spring 可以是懒加载的,就是当真正使用到 Bean 的时候才实例化 Bean。当然也不全是这样,例如配置 Bean 的 lazy-init 属性,可以控制 Spring 的加载时机。现在机器的性能、内存等都比较高,基本上也不使用懒加载,在容器启动时候来加载bean,启动时间稍微长一点儿,这样在实际获取 bean 供业务使用时,就可以减轻不少负担,这个后面再做分析。 我们使用到 Bean 的时候,最直接的方式就是从 Factroy 中获取,这个就是加载 Bean 实例的源头。

最近在做项目时候遇到一个奇葩问题,就是bean依赖注入的正确性与bean直接注入的顺序有关系,但是正常情况下明明是和顺序没关系的啊,究竟啥情况那,不急,让我一一道来。

二、普通Bean循环依赖-与注入顺序无关

2.1 循环依赖例子与原理

public class BeanA {
private BeanB beanB;
public BeanB getBeanB() {
 return beanB;
}
public void setBeanB(BeanB beanB) {
 this.beanB = beanB;
}
}
public class BeanB {
private BeanA beanA;
public BeanA getBeanA() {
 return beanA;
}
public void setBeanA(BeanA beanA) {
 this.beanA = beanA;
}
}
<bean id="beanA" class="com.alibaba.test.circle.BeanA">
<property name="beanB">
 <ref bean="beanB" />
</property>
</bean>
<bean id="beanB" class="com.alibaba.test.circle.BeanB">
<property name="beanA">
 <ref bean="beanA" />
</property>
</bean>

上述循环依赖注入能够正常工作,这是因为Spring提供了EarlyBeanReference功能,首先Spring里面有个名字为singletonObjects的并发map用来存放所有实例化并且初始化好的bean,singletonFactories则用来存放需要解决循环依赖的bean信息(beanName,和一个回调工厂)。当实例化beanA时候会触发getBean(“beanA”);首先看singletonObjects中是否有beanA有则返回:

(1)

Object sharedInstance = getSingleton(beanName);//getSingleton(beanName,true);
if (sharedInstance != null && args == null) {
if (logger.isDebugEnabled()) {
 if (isSingletonCurrentlyInCreation(beanName)) {
 logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
 "' that is not fully initialized yet - a consequence of a circular reference");
 }
 else {
 logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
 }
}
 // 如果是普通bean直接返回,工厂bean则返回sharedInstance.getObject();
bean = getObjectForBeanInstance(sharedInstance,name,beanName,null);
}
protected Object getSingleton(String beanName,boolean allowEarlyReference) {
 Object singletonObject = this.singletonObjects.get(beanName);
 if (singletonObject == null) {
 synchronized (this.singletonObjects) {
 singletonObject = this.earlySingletonObjects.get(beanName);
 if (singletonObject == null && allowEarlyReference) {
 ObjectFactory singletonFactory = (ObjectFactory) this.singletonFactories.get(beanName);
 if (singletonFactory != null) {
  singletonObject = singletonFactory.getObject();
  this.earlySingletonObjects.put(beanName,singletonObject);
  this.singletonFactories.remove(beanName);
 }
 }
 }
 }
 return (singletonObject != NULL_OBJECT ? singletonObject : null);
}

一开始肯定没有所以会实例化beanA,如果设置了allowCircularReferences=true(默认为true)并且当前bean为单件并且该bean目前在创建中,则初始化属性前把该bean信息放入singletonFactories单件map里面:

(2)

boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
 logger.debug("Eagerly caching bean '" + beanName +
 "' to allow for resolving potential circular references");
}
addSingletonFactory(beanName,new ObjectFactory() {
 public Object getObject() throws BeansException {
 return getEarlyBeanReference(beanName,mbd,bean);
 }
});
}
protected void addSingletonFactory(String beanName,ObjectFactory singletonFactory) {
Assert.notNull(singletonFactory,"Singleton factory must not be null");
synchronized (this.singletonObjects) {
 if (!this.singletonObjects.containsKey(beanName)) {
 this.singletonFactories.put(beanName,singletonFactory);
 this.earlySingletonObjects.remove(beanName);
 this.registeredSingletons.add(beanName);
 }
}
}

然后对该实例进行属性注入beanB,属性注入时候会getBean(“beanB”) ,发现beanB 不在singletonObjects中,就会实例化beanB,然后放入singletonFactories,然后进行属性注入beanA,然后触发getBean(“beanA”);这时候会到(1)getSingleton返回实例化的beanA。到此beanB初始化完毕添加beanB 到singletonObjects然后返回,然后beanA 初始化完毕,添加beanA到singletonObjects然后返回

2.2 允许循环依赖的开关

public class TestCircle2 {
private final static ClassPathXmlApplicationContext moduleContext;
private static Test test;
static {
 moduleContext = new ClassPathXmlApplicationContext(new String[]{"beans-circile.xml"});
 moduleContext.setAllowCircularReferences(false);
 test = (Test) moduleContext.getBean("test");
}
public static void main(String[] args) {

 System.out.println(test.name);
}
}

ClassPathXmlApplicationContext类中有个属性allowCircularReferences用来控制是否允许循环依赖默认为true,这里设置为false后发现循环依赖还是可以正常运行,翻看源码:

public ClassPathXmlApplicationContext(String[] configLocations) throws BeansException {
this(configLocations,true,null);
}
public ClassPathXmlApplicationContext(String[] configLocations,boolean refresh,ApplicationContext parent)
 throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
 refresh();
}
}
public ClassPathXmlApplicationContext(String[] configLocations,ApplicationContext parent)
 throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
 refresh();
}
}

知道默认构造ClassPathXmlApplicationContext时候会刷新容器。

refresh方法会调用refreshBeanFactory:

protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
 destroyBeans();
 closeBeanFactory();
}
try {
 // 创建bean工厂
 DefaultListableBeanFactory beanFactory = createBeanFactory();
 //定制bean工厂属性
 customizeBeanFactory(beanFactory);
 loadBeanDefinitions(beanFactory);
 synchronized (this.beanFactoryMonitor) {
 this.beanFactory = beanFactory;
 }
}
catch (IOException ex) {
 throw new ApplicationContextException(
 "I/O error parsing XML document for application context [" + getDisplayName() + "]",ex);
}
}
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
if (this.allowBeanDefinitionOverriding != null) {
 beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding.booleanValue());
}
if (this.allowCircularReferences != null) {
 beanFactory.setAllowCircularReferences(this.allowCircularReferences.booleanValue());
}
}

到这里就知道了,我们在调用 moduleContext.setAllowCircularReferences(false)前,spring留出的设置bean工厂的回调customizeBeanFactory已经执行过了,最终原因是,调用设置前,bean工厂已经refresh了,所以测试代码改为:

public class TestCircle {
private final static ClassPathXmlApplicationContext moduleContext;
private static Test test;
static {
 //初始化容器上下文,但是不刷新容器
 moduleContext = new ClassPathXmlApplicationContext(new String[]{"beans-circile.xml"},false);
 moduleContext.setAllowCircularReferences(false);
 //刷新容器
 moduleContext.refresh();
 test = (Test) moduleContext.getBean("test");
}
public static void main(String[] args) {
 System.out.println(test.name);
}
}

现在测试就会抛出异常:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'beanA' defined in class path resource [beans-circile.xml]: Cannot resolve reference to bean 'beanB' while setting bean property 'beanB'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'beanB' defined in class path resource [beans-circile.xml]: Cannot resolve reference to bean 'beanA' while setting bean property 'beanA'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'beanA': Requested bean is currently in creation: Is there an unresolvable circular reference?

三、工厂Bean与普通Bean循环依赖-与注入顺序有关

3.1 测试代码

(编辑:安卓应用网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读