文档章节

Spring 循环引用 ——理解singleton与prototype初始化的区别

非沧海一粟不随波逐流
 非沧海一粟不随波逐流
发布于 2016/12/19 16:37
字数 5696
阅读 89
收藏 1

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

所谓的循环引用,就是A依赖B,B又依赖A,A与B两个对象相互持有。像下面这种情况:

class A
{
	 B b;
	public A(B b) {
		this.b=b;
	}
}

class B
{
	A a;
	public B(A a )
	{
		this.a=a;
	}
	
}
我们知道spring在获取对象或者在加载的时候,触发依赖注入。例如触发A对象的依赖注入,发现需要B对象,而此时B还没有初始化,就去实例化B对象,而又需要A对象,这样就进入了一种死循环状态,有点像操作系统里面的死锁。似乎这种情况发生了,Spring就陷入僵局了。显然Spring有方法去解决这个问题。对于依赖注入的情况,大致分为构造注入和设值注入两种方式,而实际上因为Spring注入的触发机制不一样,这个问题又被分为singleton对象和prototype(其他三个作用域大致相同)对象的区别。我们可以把问题大致分为三类。

1 singleton对象循环引用

1.1 构造器循环依赖

这种依赖Spring是无法给你解决的,将会抛出BeanCurrentlyInCreationException异常。来看一下测试代码

(1) 定义两个类 A,B 构成构造成循环依赖的情况

class A
{
	 B b;
	public A(B b) {
		this.b=b;
	}
}

class B
{
	A a;
	public B(A a )
	{
		this.a=a;
	}
	
}

(2) 配置文件中配置两个类,在不指明作用域的时候,默认为singleton对象

<bean id="xiaoA" class="com.songxu.learn.A">
		<constructor-arg name="b" ref="xiaoB" />
	</bean>

	<bean id="xiaoB" class="com.songxu.learn.B">
		<constructor-arg name="a" ref="xiaoA" />
	</bean>

(3)加载配置文件,两个类默认被加载以及注入

ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
<span style="font-size:18px;">		</span>
(4) 结果抛出了异常,来看一下日志输出

2015-08-01 02:39:15,869>>>>[org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] >>>>Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@2ef5e5e3: defining beans [xiaoA,xiaoB]; root of factory hierarchy
  2015-08-01 02:39:15,869>>>>[org.springframework.beans.factory.support.DefaultSingletonBeanRegistry]-[DEBUG] >>>>Creating shared instance of singleton bean 'xiaoA'
  2015-08-01 02:39:15,869>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Creating instance of bean 'xiaoA'
  2015-08-01 02:39:15,889>>>>[org.springframework.beans.factory.support.DefaultSingletonBeanRegistry]-[DEBUG] >>>>Creating shared instance of singleton bean 'xiaoB'
  2015-08-01 02:39:15,889>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Creating instance of bean 'xiaoB'
  2015-08-01 02:39:15,889>>>>[org.springframework.beans.factory.support.DefaultSingletonBeanRegistry]-[DEBUG] >>>>Creating shared instance of singleton bean 'xiaoA'
  2015-08-01 02:39:15,889>>>>[org.springframework.context.support.AbstractApplicationContext]-[WARN] >>>>Exception encountered during context initialization - cancelling refresh attempt
  org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'xiaoA' defined in class path resource [applicationContext.xml]: Cannot resolve reference to bean 'xiaoB' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'xiaoB' defined in class path resource [applicationContext.xml]: Cannot resolve reference to bean 'xiaoA' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'xiaoA': Requested bean is currently in creation: Is there an unresolvable circular reference?

这个过程是这样的,容器构造Class A的对象,发现构造参数需要一个ClassB的对象,而此时容器中没有ClassB的实例,因此转到ClassB的实例化过程,ClassB构造又需要ClassA,这样进入了循环状态。双方都无法完成构造的过程,因此抛出了异常。

1.2 设值注入循环依赖

对于设值注入的情况,循环依赖是可以完成的。来看一下测试代码。

(1) 构造两个类 A,B  构成循环依赖,并且使用设值注入的方式。

class CircleA
{
	private CircleB b;
	public CircleA(CircleB b)
	{
		this.b=b;
	}
	public CircleB getB() {
		return b;
	}
	public void setB(CircleB b) {
		this.b = b;
	}
	public CircleA()
	{
		
	}

}

class CircleB
{
	private CircleA a;
	
	public CircleA getA() {
		return a;
	}

	public void setA(CircleA a) {
		this.a = a;
	}

	public CircleB(CircleA a)
	{
		this.a=a;
	}
	public CircleB()
	{
		
	}
}

(2) 配置文件 采用默认的方式


<bean id="circleA" class="com.songxu.learn.CircleA" >
		<property name="b" ref="circleB"></property>
	</bean>
	<bean id="circleB" class="com.songxu.learn.CircleB" >
		<property name="a" ref="circleA"></property>
	</bean>

(3)加载配置文件,两个类默认被加载以及注入

ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");

(4)结果运行通过,看一下日志输出

2015-08-01 03:41:32,302>>>>[org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] >>>>Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@100fc185: defining beans [circleA,circleB]; root of factory hierarchy
  2015-08-01 03:41:32,302>>>>[org.springframework.beans.factory.support.DefaultSingletonBeanRegistry]-[DEBUG] >>>>Creating shared instance of singleton bean 'circleA'
  2015-08-01 03:41:32,302>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Creating instance of bean 'circleA'
  2015-08-01 03:41:32,312>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Eagerly caching bean 'circleA' to allow for resolving potential circular references
  2015-08-01 03:41:32,312>>>>[org.springframework.beans.factory.support.DefaultSingletonBeanRegistry]-[DEBUG] >>>>Creating shared instance of singleton bean 'circleB'
  2015-08-01 03:41:32,312>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Creating instance of bean 'circleB'
  2015-08-01 03:41:32,312>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Eagerly caching bean 'circleB' to allow for resolving potential circular references
  2015-08-01 03:41:32,312>>>>[org.springframework.beans.factory.support.AbstractBeanFactory]-[DEBUG] >>>>Returning eagerly cached instance of singleton bean 'circleA' that is not fully initialized yet - a consequence of a circular reference
  2015-08-01 03:41:32,322>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Finished creating instance of bean 'circleB'
  2015-08-01 03:41:32,322>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Finished creating instance of bean 'circleA'
  2015-08-01 03:41:32,322>>>>[org.springframework.beans.factory.support.AbstractBeanFactory]-[DEBUG] >>>>Returning cached instance of singleton bean 'circleB'
这个过程是这样的, A构造空对象放入缓存池中(暂且称为A空),触发了B对象注入,然后去构造B对象,又触发了对A对象的注入,这个时候缓存中已经有一个空的A对象,就把A空对象注入给B, 然后B构造完成,返回给A,这时A对象已经完成了注入。而同时B中持有的A对象也不再是A空了, 因为A是单例的,且A完成了注入,此时A=A空。也就完成了整个注入过程。

1.3设值与构造混合的循环依赖

依然设置A,B两个类,构成循环依赖,A采用设值注入B,B采用构造器注入A。对于这种情况,Spring是可以完成注入的。来查看示例代码

(1) 构造两个类 A,B 构成循环依赖关系,A采用设值注入B,B采用构造器注入A

class A
{
	 B b;
	
	public B getB()
	{
		return b;
	}
	public void setB(B b)
	{
		this.b = b;
	}
}

class B
{
	A a;
	public B(A a )
	{
		this.a=a;
	}
	
}

(2) 配置文件中配置两个类,在不指明作用域的时候,默认为singleton对象
<bean id="xiaoA" class="com.songxu.learn.A">
		<constructor-arg name="b" ref="xiaoB" />
	</bean>

	<bean id="xiaoB" class="com.songxu.learn.B">
		<constructor-arg name="a" ref="xiaoA" />
	</bean>

(3)加载配置文件,两个类默认被加载以及注入
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
<span style="font-size:18px;">		</span>
(4) 结果正常,A,B注入完成,查看一下日志输出

2015-08-01 04:00:32,884>>>>[org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] >>>>Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@6d00a15d: defining beans [xiaoA,xiaoB]; root of factory hierarchy
  2015-08-01 04:00:32,884>>>>[org.springframework.beans.factory.support.DefaultSingletonBeanRegistry]-[DEBUG] >>>>Creating shared instance of singleton bean 'xiaoA'
  2015-08-01 04:00:32,884>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Creating instance of bean 'xiaoA'
  2015-08-01 04:00:32,894>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Eagerly caching bean 'xiaoA' to allow for resolving potential circular references
  2015-08-01 04:00:32,904>>>>[org.springframework.beans.factory.support.DefaultSingletonBeanRegistry]-[DEBUG] >>>>Creating shared instance of singleton bean 'xiaoB'
  2015-08-01 04:00:32,904>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Creating instance of bean 'xiaoB'
  2015-08-01 04:00:32,904>>>>[org.springframework.beans.factory.support.AbstractBeanFactory]-[DEBUG] >>>>Returning eagerly cached instance of singleton bean 'xiaoA' that is not fully initialized yet - a consequence of a circular reference
  2015-08-01 04:00:32,914>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Eagerly caching bean 'xiaoB' to allow for resolving potential circular references
  2015-08-01 04:00:32,914>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Finished creating instance of bean 'xiaoB'
  2015-08-01 04:00:32,924>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Finished creating instance of bean 'xiaoA'
  2015-08-01 04:00:32,924>>>>[org.springframework.beans.factory.support.AbstractBeanFactory]-[DEBUG] >>>>Returning cached instance of singleton bean 'xiaoB'

1.4 Singleton的循环依赖注入总结

两个类构成循环依赖并且需要完成Spring注入的唯一条件是,可以构造一个空的对象放入缓存,否则将会失败。而这种空对象的构造必须采用设值注入的方式。在后面将给出深度解析。

2 Prototype的循环依赖

依然采用上面的三种情况去分析

2.1 构造器依赖注入

(1)类与上面的一样,只是配置文件稍作修改

<bean id="xiaoA" class="com.songxu.learn.A" scope="prototype">
		<constructor-arg name="b" ref="xiaoB" />
		<!-- <property name="b"  ref="xiaoB"></property> -->
	</bean>

	<bean id="xiaoB" class="com.songxu.learn.B" scope="prototype">
		<constructor-arg name="a" ref="xiaoA" />
	</bean>

(2)测试代码需要调用getBean方法,否则无法触发依赖注入

ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
		A xiaoA=(A)applicationContext.getBean("xiaoA");

(3)结果抛出异常BeanCurrentlyInCreationException。下面看日志输出

2015-08-01 04:09:31,425>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Creating instance of bean 'xiaoA'
  2015-08-01 04:09:31,435>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Creating instance of bean 'xiaoB'
  Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'xiaoA' defined in class path resource [applicationContext.xml]: Cannot resolve reference to bean 'xiaoB' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'xiaoB' defined in class path resource [applicationContext.xml]: Cannot resolve reference to bean 'xiaoA' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'xiaoA': Requested bean is currently in creation: Is there an unresolvable circular reference?

2.2 设值注入

(1)类与上面的一样,只是配置文件稍作修改

<span style="font-size:18px;"><bean id="circleA" class="com.songxu.learn.CircleA"  scope="prototype">
        <property name="b" ref="circleB"></property>
    </bean>
    <bean id="circleB" class="com.songxu.learn.CircleB" scope="prototype" >
        <property name="a" ref="circleA"></property>
    </bean>  

</span>

(2)测试代码需要调用getBean方法,否则无法触发依赖注入
<span style="font-size:18px;">ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
        CircleB b=(CircleB)applicationContext.getBean("circleB");
        
</span>

(3)结果抛出异常BeanCurrentlyInCreationException。下面看日志输出

2015-08-01 04:13:29,505>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Creating instance of bean 'circleB'
  2015-08-01 04:13:29,515>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Creating instance of bean 'circleA'
  Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'circleB' defined in class path resource [applicationContext.xml]: Cannot resolve reference to bean 'circleA' while setting bean property 'a'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'circleA' defined in class path resource [applicationContext.xml]: Cannot resolve reference to bean 'circleB' while setting bean property 'b'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'circleB': Requested bean is currently in creation: Is there an unresolvable circular reference?

2.3 混合模式

测试代码与上面类似,结果同样是无法完成注入,抛出BeanCurrentlyInCreationException异常。

2.4 小结

对于prototype来说,只有存在循环引用的情况,是无法完成注入的。


3 prototype与singleton的混合模式循环引用

这种混合模式在某些情况下是成立的。这里只给出一种成功的案例,其他模式下无法完成。这一成功的类型是AB两个类都采用设值注入的方式。

(1) 构造A ,B两个类,构成循环依赖关系

class CircleA
{
	private CircleB b;
	
	public CircleB getB() {
		return b;
	}
	public void setB(CircleB b) {
		this.b = b;
	}
	public CircleA()
	{
		
	}

}

class CircleB
{
	private CircleA a;
	public CircleB()
	{
		
	}
	public CircleA getA() {
		return a;
	}

	public void setA(CircleA a) {
		this.a = a;
	}

	}

(2) 配置文件 配置A为prototype B为默认  AB均采用设值注入的方式

<bean id="circleA" class="com.songxu.learn.CircleA" scope="prototype" >
		<property name="b" ref="circleB"></property>  
		
	</bean>
	<bean id="circleB" class="com.songxu.learn.CircleB" >
		<property name="a" ref="circleA"></property>
		
	</bean>

(3) 测试结果 通过,而且B中持有的A对象 与getBean得到的A对象不是一个对象。

ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
		CircleA A=(CircleA)applicationContext.getBean("circleA");
		CircleB B=(CircleB)applicationContext.getBean("circleB");
		System.out.println(A==B.getA());

(4)查看一下日志输出

2015-08-01 04:45:08,952>>>>[org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] >>>>Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@100fc185: defining beans [circleA,circleB]; root of factory hierarchy
  2015-08-01 04:45:08,952>>>>[org.springframework.beans.factory.support.DefaultSingletonBeanRegistry]-[DEBUG] >>>>Creating shared instance of singleton bean 'circleB'
  2015-08-01 04:45:08,952>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Creating instance of bean 'circleB'
  2015-08-01 04:45:08,962>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Eagerly caching bean 'circleB' to allow for resolving potential circular references
  2015-08-01 04:45:08,962>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Creating instance of bean 'circleA'
  2015-08-01 04:45:08,962>>>>[org.springframework.beans.factory.support.AbstractBeanFactory]-[DEBUG] >>>>Returning eagerly cached instance of singleton bean 'circleB' that is not fully initialized yet - a consequence of a circular reference
  2015-08-01 04:45:08,972>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Finished creating instance of bean 'circleA'
  2015-08-01 04:45:08,972>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Finished creating instance of bean 'circleB'
  2015-08-01 04:45:08,972>>>>[org.springframework.context.support.AbstractApplicationContext]-[DEBUG] >>>>Unable to locate LifecycleProcessor with name 'lifecycleProcessor': using default [org.springframework.context.support.DefaultLifecycleProcessor@1dfe2924]
  2015-08-01 04:45:08,972>>>>[org.springframework.beans.factory.support.AbstractBeanFactory]-[DEBUG] >>>>Returning cached instance of singleton bean 'lifecycleProcessor'
  2015-08-01 04:45:08,982>>>>[org.springframework.core.env.PropertySourcesPropertyResolver]-[DEBUG] >>>>Searching for key 'spring.liveBeansView.mbeanDomain' in [systemProperties]
  2015-08-01 04:45:08,982>>>>[org.springframework.core.env.PropertySourcesPropertyResolver]-[DEBUG] >>>>Searching for key 'spring.liveBeansView.mbeanDomain' in [systemEnvironment]
  2015-08-01 04:45:08,982>>>>[org.springframework.core.env.PropertySourcesPropertyResolver]-[DEBUG] >>>>Could not find key 'spring.liveBeansView.mbeanDomain' in any property source. Returning [null]
  init finished============================================
2015-08-01 04:45:08,982>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Creating instance of bean 'circleA'
  2015-08-01 04:45:08,982>>>>[org.springframework.beans.factory.support.AbstractBeanFactory]-[DEBUG] >>>>Returning cached instance of singleton bean 'circleB'
  2015-08-01 04:45:08,982>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Finished creating instance of bean 'circleA'
  2015-08-01 04:45:08,982>>>>[org.springframework.beans.factory.support.AbstractBeanFactory]-[DEBUG] >>>>Returning cached instance of singleton bean 'circleB'
  false


4 singleton与prototype的初始化的区别

4.1 singleton初始化过程

下面的示意图大致显示了这个过程的关键步骤,关于细节的代码,可以查看另一篇博客。

这个图中的数字标识了各个过程,红色填图的区域表示关键步骤,黄色填图的区域为logger日志输出内容,虚线边框代表一个方法的内部。


下面是一段 singleon对象循环引用的日志输出。用来配合理解这个过程

2015-08-01 09:51:34,966>>>>[org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] >>>>Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@100fc185: defining beans [circleA,circleB]; root of factory hierarchy
  2015-08-01 09:51:34,966>>>>[org.springframework.beans.factory.support.DefaultSingletonBeanRegistry]-[DEBUG] >>>>Creating shared instance of singleton bean 'circleA'
  2015-08-01 09:51:34,966>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Creating instance of bean 'circleA'
  A开始初始化
2015-08-01 09:51:34,976>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Eagerly caching bean 'circleA' to allow for resolving potential circular references
  2015-08-01 09:51:34,976>>>>[org.springframework.beans.factory.support.DefaultSingletonBeanRegistry]-[DEBUG] >>>>Creating shared instance of singleton bean 'circleB'
  2015-08-01 09:51:34,976>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Creating instance of bean 'circleB'
  B开始初始化
2015-08-01 09:51:34,976>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Eagerly caching bean 'circleB' to allow for resolving potential circular references
  2015-08-01 09:51:34,976>>>>[org.springframework.beans.factory.support.AbstractBeanFactory]-[DEBUG] >>>>Returning eagerly cached instance of singleton bean 'circleA' that is not fully initialized yet - a consequence of a circular reference
  B开始注入
B完成注入
2015-08-01 09:51:34,986>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Finished creating instance of bean 'circleB'
  A开始注入
A完成注入
2015-08-01 09:51:34,986>>>>[org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory]-[DEBUG] >>>>Finished creating instance of bean 'circleA'

4.1.1 辅助关键属性

在DefaultSingletonBeanRegistry类中,定义了一些属性,它决定着整个依赖注入过程的关键步骤跳转,同时也伴随着整个bean的关键周期.

/** 保存所有的singletonBean的实例 */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);

	/** singletonBean的生产工厂*/
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

	/** 保存所有早期创建的Bean对象,这个Bean还没有完成依赖注入 */
	private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

	/** 保存所有已经完成初始化的Bean的名字(name) */
	private final Set<String> registeredSingletons = new LinkedHashSet<String>(64);

	/** 标识指定name的Bean对象是否处于创建状态  这个状态非常重要 */
	private final Set<String> singletonsCurrentlyInCreation =
			Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(16));

4.1.2 singleton的创建流程

singleton的创建开始于getBean方法,关于这个流程,可以查看另外一篇博客。这个方法调用了doGetBean方法,也是示意图中最顶端的方法,这是创建singleton的开始。

  1. 进入1号代码区域,这是一个关键的代码,getSingleton方法去查找缓存,如果存在未创建完成的Bean(earlyBean),就返回这个结果,作为doGetBean的返回值,并输出一段日志内容,流程结束:
  2. 否则进入2号代码区,创建一个ObjectFactory,作为创建singleton的工厂,注册给getSingleton方法,进入红色方法区;
  3. 输出3号日志内容;
  4. 进入4号方法,这是一个关键节点,在这里把当前正常初始化的BeanName放入singletonsCurrentlyInCreation集合中,此时与BeanName绑定的属性为true,表示这个beanName正在创建;
  5. 进入5号方法,也就是绿色代码区,这是调用前面注册的工厂的创建方法,也是Spring中所有的Bean的初始化的入口;
  6. 输出6号日志内容;
  7. 调用doGetBean方法,进入蓝色代码区,这是创建Bean的核心代码区;
  8. 进入7号关键代码区,这个地方完成了构造方法的匹配工作,构造注入与设值注入的区别点就在这;采用构造注入的话,需要在这里面获得所有构造方法参数的属性值,如果属性值为另一个Bean的话,就会调用getBean方法,递归地回到整个流程的入口处;注意,在这个时候4.1.1中的关键属性的状态为 singletonsCurrentlyInCreation.get(Beanname)=true; singgletonFactories.get(name)=null;singletonObjects.get(beanName)=null;
  9. 如果采用设值注入的话,此处仅仅会调用默认的空的构造方法,此时已经创建了一个空的Bean对象(earlyBean)然后向下进入8号区域;
  10. 进入8号区域输出日志内容;
  11. 进入9号方法,这是一个singleton创建后的一个后处理方法,在这里又一次注册了一个工厂,并且注册的工厂方法为getEarlyBeanReference,并把这个方法加入到singletonFactories中,此时singletonFactories.get(name)!=null;
  12. 进入10号代码区,这里完成了依赖注入的过程,如果需要注入Bean的话,就会调用getBean的方法;
  13. 进入11号区域,整个singletonBean创建完成,输出日志内容,然后进行一些后处理工作,流程退出

4.1.3源码

整个过程的源码很多,这里仅截取与singleton创建相关的部分

(1)图中1号代码getSingleton

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		//在已经完成创建的singleton集合中查找
		Object singletonObject = this.singletonObjects.get(beanName);
		//如果没有找到 并且还处于当前创建状态,就应该可以找到earlyReference
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
				singletonObject = this.earlySingletonObjects.get(beanName);//在earlyReference查找
				if (singletonObject == null && allowEarlyReference) {
					//如果还没有找到,就去找对应的工厂方法,这里与9号代码回应。
					//如果创建了空的Bean的话,在此处一定能拿到对应的工厂
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
						singletonObject = singletonFactory.getObject();//在这获得对应的earlyBean
						this.earlySingletonObjects.put(beanName, singletonObject);//放入earlyBeanObjects集合中
						this.singletonFactories.remove(beanName);//移除工厂方法
					}
				}
			}
		}
		return (singletonObject != NULL_OBJECT ? singletonObject : null);
	}

(2)第一个日志输出的地方,这里的getSingleton就是上面(1)里面的方法

// Eagerly check singleton cache for manually registered singletons.
		Object sharedInstance = getSingleton(beanName);
		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 = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}

(3) 如果getSingleton返回null,就进入创建阶段

if (mbd.isSingleton()) {
					//注册工厂  调用另一个getSingleton
					sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
						@Override
						public Object getObject() throws BeansException {
							try {
								//创建Bean的实际方法
								return createBean(beanName, mbd, args);
							}
							catch (BeansException ex) {
								// Explicitly remove instance from singleton cache: It might have been put there
								// eagerly by the creation process, to allow for circular reference resolution.
								// Also remove any beans that received a temporary reference to the bean.
								destroySingleton(beanName);
								throw ex;
							}
						}
					});
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

(4) 红色代码框的getSingleton的方法

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
			//非空校验
		Assert.notNull(beanName, "'beanName' must not be null");
		synchronized (this.singletonObjects) {
			Object singletonObject = this.singletonObjects.get(beanName);
			if (singletonObject == null) {
				if (this.singletonsCurrentlyInDestruction) {
					throw new BeanCreationNotAllowedException(beanName,
							"Singleton bean creation not allowed while the singletons of this factory are in destruction " +
							"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
				}
				//前置处理
				beforeSingletonCreation(beanName);
				boolean newSingleton = false;
				boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
				if (recordSuppressedExceptions) {
					this.suppressedExceptions = new LinkedHashSet<Exception>();
				}
				try {
					//调用工程方法
					singletonObject = singletonFactory.getObject();
					newSingleton = true;
				}
				catch (IllegalStateException ex) {
					// Has the singleton object implicitly appeared in the meantime ->
					// if yes, proceed with it since the exception indicates that state.
					singletonObject = this.singletonObjects.get(beanName);
					if (singletonObject == null) {
						throw ex;
					}
				}
				catch (BeanCreationException ex) {
					if (recordSuppressedExceptions) {
						for (Exception suppressedException : this.suppressedExceptions) {
							ex.addRelatedCause(suppressedException);
						}
					}
					throw ex;
				}
				finally {
					if (recordSuppressedExceptions) {
						this.suppressedExceptions = null;
					}
					//后置处理
					afterSingletonCreation(beanName);
				}
				if (newSingleton) {
					addSingleton(beanName, singletonObject);
				}
			}
			return (singletonObject != NULL_OBJECT ? singletonObject : null);
		}
	}

(5) beforeSingletonCreation 前置处理, 在这可能抛出重复创建的异常

protected void beforeSingletonCreation(String beanName) {
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
			throw new BeanCurrentlyInCreationException(beanName);
		}
	}

(6)doCreateBean方法 已略去无关代码

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
		//...
		if (instanceWrapper == null) {
			//创建Bean的实际方法
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		//...

		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");
			}
			//注册getEaryBean的工厂方法
			addSingletonFactory(beanName, new ObjectFactory<Object>() {
				@Override
				public Object getObject() throws BeansException {
					return getEarlyBeanReference(beanName, mbd, bean);
				}
			});
		}

		// Initialize the bean instance.
		Object exposedObject = bean;
		try {
			//开始依赖注入  触发getBean方法
			populateBean(beanName, mbd, instanceWrapper);
			if (exposedObject != null) {
				exposedObject = initializeBean(beanName, exposedObject, mbd);
			}
		}
		catch (Throwable ex) {
			if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
				throw (BeanCreationException) ex;
			}
			else {
				throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
			}
		}

		//...

		return exposedObject;
	}

(7 )createBeanInstance方法,实际创建Bean的方法

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
		//...

		// 在这里去匹配合适的构造方法,如果存在构造注入,需要找到所有的注入的值,触发getBean
		Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
		if (ctors != null ||
				mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
				mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args))  {
			return autowireConstructor(beanName, mbd, ctors, args);
		}

		// No special handling: simply use no-arg constructor.
		return instantiateBean(beanName, mbd);
	}

4.1.4不能采用构造注入的原因

通过上面的分析,已经基本可以看出设值注入与构造注入的区别在哪里,就是触发getBean递归的时机不一致,导致1号的getSingleton的结果不一样

对于设值注入来说,首先进入getA的getBean,此时getSingleton返回null,进入Bean的创建阶段,此时采用设值注入,构造默认的空的Bean,并注册一个工厂方法,然后依赖注入,getBean的时候触发了对B的构造过程,与A一样,构造默认的空的Bean,并注册一个工厂方法,然后依赖注入,这时需要A,第二次进入A的getBean流程,而此时因为存在工厂方法,所以1号的getSingleton不再返回空,这样就返回了一个Earlybean对象注入到B中;然后B完成注入,接着A完成注入。

对于构造注入来说,首先进入getA的getBean,此时getSingleton返回null,进入Bean的创建阶段,此时采用构造注入,在查找注入参数的时候,就触发了B的getBean方法,注意此时并没有注册工厂方法,B的创建流程开始,采用构造注入,查找注入参数,第二次进入A的getBean方法,因为没有提供可以拿到earlyBean的工厂方法,1号getSingleton将返回空,此时将再次进入红色方法区,再次调用4号方法时,就会抛出重复创建的异常。

对于混合方式来说,因为其中之一采用设值注入,再第二个进入这个对象的getBean方法的时候,总会得到earlyBean,因此不会抛出异常,也就可以完成创建。

在此可以得出结论,Spring对于循环引用的解决方案就是暴露一个ObjectFactroy,对于未创建完成的Bean,可以通过这个工厂得到一个earlyBean保证“锁”被释放,完成流程。而可以暴露这个ObjectFactory的唯一条件就是必须采用设值注入,以保证可以提前构造一个空的earlyBean。

4.2 prototype的初始化流程

4.2.1 prototype的初始化流程

相对于singleton来说,prototype就简单的多,由于方法名都类似,在这直接给出整个流程的代码。

(1) 关键属性  只有一个,保存着正在创建的prototype的beanName

/** Names of beans that are currently in creation */
	private final ThreadLocal<Object> prototypesCurrentlyInCreation =
			new NamedThreadLocal<Object>("Prototype beans currently in creation");

(2) 流程代码 ,这里没有像前面给出那么多代码的原因,是因为后面的代码都一样,并没有添加与prototype相关的,也就是说prototype的创建流程也米有对外暴露什么工厂之类的辅助。

//流程1  检验当前的bean是否处于
	// Fail if we're already creating this bean instance:
	// We're assumably within a circular reference.
	if (isPrototypeCurrentlyInCreation(beanName)) {
		throw new BeanCurrentlyInCreationException(beanName);
	}
	//流程2  创建Bean
	else if (mbd.isPrototype()) {
	// It's a prototype -> create a new instance.
	Object prototypeInstance = null;
		try {
			    //前置处理
				beforePrototypeCreation(beanName);
				//创建流程的开始
				prototypeInstance = createBean(beanName, mbd, args);
			}
		finally {
				afterPrototypeCreation(beanName);
			}
		bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
	}

(3) 前置处理、检验部分代码

前置处理看着挺复杂,其实就是把每个正在创建的prototype的BeanName放入一个set中

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<String>(2);
			beanNameSet.add((String) curVal);
			beanNameSet.add(beanName);
			this.prototypesCurrentlyInCreation.set(beanNameSet);
		}
		else {
			Set<String> beanNameSet = (Set<String>) curVal;
			beanNameSet.add(beanName);
		}
	}

检验就是检查当前的beanName是否处于创建状态,如果是就抛出异常

protected boolean isPrototypeCurrentlyInCreation(String beanName) {
		Object curVal = this.prototypesCurrentlyInCreation.get();
		return (curVal != null &&
				(curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
	}


4.2.2 prototype不能实现循环引用的原因

从流程上就可以查看,无论是构造注入还是设值注入,第二次进入同一个Bean的getBean方法是,一定会在校验部分抛出异常,因此不能完成注入,也就不能实现循环引用。
































本文转载自:http://blog.csdn.net/u010723709/article/details/47185959

非沧海一粟不随波逐流
粉丝 4
博文 87
码字总数 3797
作品 0
无锡
程序员
私信 提问
通过循环依赖问题彻底理解SpringIOC的精华

前言 你可能会有如下问题: 1、想看Spring源码,但是不知道应当如何入手去看,对整个Bean的流程没有概念,碰到相关问题也没有头绪如何下手 2、看过几遍源码,没办法彻底理解,没什么感觉,没...

Java填坑之路
2018/11/19
0
0
Spring中bean的scope用法

Spring中bean的scope有六种:singleton,prototype,request,session,global session,application。此外,用户还可以自定义scope。还有一种scope是thread类型的,但是默认没有启用。 sing...

davelet
2016/04/25
0
0
Spring Bean Scope

Spring Bean Scope 简单的解释一下bean 的scope singleton 当一个bean的作用域设置为singleton, 那么Spring IOC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义...

秋风醉了
2014/11/26
39
0
SpringFramework核心技术一(IOC:Bean的分析)

Bean的概述 Spring IoC容器管理一个或多个bean。这些bean是使用您提供给容器的配置元数据创建的,例如,以XML 定义的形式 。 在容器本身中,这些bean定义被表示为BeanDefinition 对象,其中包...

王木东
2018/05/13
0
0
解决Spring中singleton的Bean依赖于prototype的Bean的问题

当Spring容器中作用域不同的Bean相互依赖时,可能出现一些问题,例如:一个作用域为Singleton的Bean(设为A)依赖于一个作用域为prototype的Bean(设为B)。由于A是单例的,只有一次初始化的...

摆渡者
2014/03/06
3.6K
0

没有更多内容

加载失败,请刷新页面

加载更多

计算机电子书 2018 BiliDrive 备份

下载方式 根据你的操作系统下载不同的 BiliDrive 二进制。 执行: bilidrive download <link> 链接 <!--more--> 文档 链接 Webpack 中文指南.epub (409.01 KB) bdrive://ce58b7b58292296a61......

ApacheCN_飞龙
45分钟前
4
0
js转义字符串

js转义字符串 转义 字符串转义,简单粗暴的方法encodeURIComponent; encodeURIComponent 转义除了字母、数字、(、)、.、!、~、*、'、-和_之外的所有字符。 解码方法 decodeURIComponent(enc...

DrChenXX
48分钟前
4
0
在CentOS7搭建Redis哨兵服务

记一次搭建Redis哨兵服务(一主两从三哨兵) 1、下载Redis安装包http://www.redis.cn/download.html下载最新版,本次文档使用的是redis-5.0.6 2、准备三台虚拟机并且依次创建redis用户。设置...

jxgshxs
57分钟前
3
0
如何在单引号引起来的字符串中转义单引号

假设您有一个Bash alias例如: alias rxvt='urxvt' 效果很好。 然而: alias rxvt='urxvt -fg '#111111' -bg '#111111'' 将不起作用,也不会: alias rxvt='urxvt -fg \'#111111\' -bg \'......

技术盛宴
今天
5
0
网络时间服务和 chrony

网络时间服务和 chrony ntp 服务 chrony 服务 公共 NTP 服务 时间工具 ntp 服务 ntp 既可作客户端也可做服务器,需要时时开启与服务器同步,也需要时时等待客户端的连接,因此不同与c/s 结构...

hardstudy-win
今天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部