一、准备工作
下面新建一个Maven工程的Web项目,其中有两个实体类分别如下:
package com.egov.pojo;
/**
* Created by wuguoping on 2017/9/9 Desc:
*/
public class ClassA {
private ClassB classB;
public void setClassB(ClassB classB) {
this.classB = classB;
}
public ClassB getClassB() {
return classB;
}
public ClassA(){
}
public ClassA(ClassB classB){
this.classB = classB;
}
}
package com.egov.pojo;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Created by wuguoping on 2017/9/9 Desc:
*/
public class ClassB {
private ClassA classA;
public void setClassA(ClassA classA) {
this.classA = classA;
}
public ClassA getClassA() {
return classA;
}
public ClassB(){
}
public ClassB(ClassA classA){
this.classA = classA;
}
}
Bean在Spring的配置文件applicationContext.xml中的配置在后续具体分析是给出。
测试类如下:
public class Main {
public static void main(String[] args){
ApplicationContext applicationContext = new
ClassPathXmlApplicationContext("classpath:config/applicationContext.xml");
}
}
二、先看现象
1、构造器注入,两者为单例模式---报错
其中在Spring 的配置文件applicationContext.xml对两个个类的定义如下:
<bean id="classA" class="com.egov.pojo.ClassA" scope="singleton">
<constructor-arg index="0" ref="classB" />
</bean>
<bean id="classB" class="com.egov.pojo.ClassB" scope="singleton">
<constructor-arg index="0" ref="classA" />
</bean>
执行结果:
警告: Exception encountered during context initialization - cancelling refresh attempt
org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'classA' defined in class path resource [config/applicationContext.xml]: Cannot resolve reference to bean 'classB' while setting constructor argument;
nested exception is org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'classB' defined in class path resource [config/applicationContext.xml]: Cannot resolve reference to bean 'classA' while setting constructor argument;
nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'classA': Requested bean is currently in creation: Is there an unresolvable circular reference?
2、构造器注入,两者为原型对象---报错
public class Main {
public static void main(String[] args){
ApplicationContext applicationContext = new
ClassPathXmlApplicationContext("classpath:config/applicationContext.xml");
//在此需要请求触发
System.out.print(applicationContext.getBean("classA", ClassA.class));
}
}
其中在Spring 的配置文件applicationContext.xml对两个个类的定义如下:
<bean id="classA" class="com.egov.pojo.ClassA" scope="prototype">
<constructor-arg index="0" ref="classB" />
</bean>
<bean id="classB" class="com.egov.pojo.ClassB" scope="prototype">
<constructor-arg index="0" ref="classA" />
</bean>
执行结果:
Exception in thread "main" org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'classA' defined in class path resource [config/applicationContext.xml]: Cannot resolve reference to bean 'classB' while setting constructor argument;
nested exception is org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'classB' defined in class path resource [config/applicationContext.xml]: Cannot resolve reference to bean 'classA' while setting constructor argument;
nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'classA': Requested bean is currently in creation:
Is there an unresolvable circular reference?
3、构造器注入,A为单例,B为原型--报错
4、构造器注入,A为原型,B为单例--报错
上述两种情况没必要再贴出来,后面会分析,这是必然情况。
5、属性注入(Setting),两者都为单例--成功
<bean id="classA" class="com.egov.pojo.ClassA" scope="singleton">
<property name="classB" ref="classB" />
</bean>
<bean id="classB" class="com.egov.pojo.ClassB" scope="singleton">
<property name="classA" ref="classA" />
</bean>
信息: Loading XML bean definitions from class path resource [config/applicationContext.xml]
com.egov.pojo.ClassA@dc24521
Process finished with exit code 0
6、属性注入,A为单例,B为原型--成功
7、属性注入,A为原型,B为单例--报错
8、属性注入,两者都为原型---报错
...
错误原因一样,就不贴了,偷偷懒
三、原因分析
现象背后必然有原因,接下来就用源码来论证上述结论。首先,不难看出,上面一共有3个变量,具体分类就是:注入方式(2种)、Bean类型(2种)、依赖顺序(2种)。在此,主要分析Spring针对不同生命周期类型的Bean,以不同方式实例化Bean有何不同,在此之后,自然就能明白为何依赖顺序会有关系。
首先对Spring Ioc 容器了解的童鞋一定知道,Ioc的初始化过程与Ioc对Bean依赖关系的注入是分开的(当然特殊情况除外,你懂的,lazy-init),依赖注入的过程是用户第一次向IoC容器索要Bean时触发的。好了,上源码。
再等等,我们先来看个图。下图是我们使用的具体容器实例,即ClassPathXmlApplicationContext的结构图。
我们知道在Spring IoC的设计中,有个类十分的重要,那就是DefaultListableBeanFactory,因为在设计它时,就已经把IoC重要的功能都纳入了,后面的子类实现大多都是基于它的扩展。ClassPathXmlApplicationContext的父类AbstractRefreshableApplicationContext同样如此:
//AbstractRefreshableApplicationContext
/**
* Create an internal bean factory for this context.
* Called for each {@link #refresh()} attempt.
* <p>The default implementation creates a
* {@link org.springframework.beans.factory.support.DefaultListableBeanFactory}
* with the {@linkplain #getInternalParentBeanFactory() internal bean factory} of this
* context's parent as parent bean factory. Can be overridden in subclasses,
* for example to customize DefaultListableBeanFactory's settings.
* @return the bean factory for this context
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowBeanDefinitionOverriding
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowEagerClassLoading
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowCircularReferences
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowRawInjectionDespiteWrapping
*/
protected DefaultListableBeanFactory createBeanFactory() {
return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}
好了,绕了一大圈,回到主题。为什么绕?因为我不绕,突然讲DefaultListableBeanFactory会不会有点突兀哦!下面是他的结构图:
其实,也还没到他,先来看看他的爷爷--AbstractBeanFactory。到这里,我们就可以正式回归主题了。我们知道,在IoC老祖BeanFactory中定义了getBean(),而依赖注入正是要从该方法说起。AbstractBeanFactory是如何实现该方法的呢?看代码:
//AbstractBeanFactory中getBean()的实现
public <T> T getBean(String name, Class<T> requiredType, Object... args) throws BeansException {
return this.doGetBean(name, requiredType, args, false);
}
靠,打游击,真正的实现是在doGetBean()方法中:
//妈蛋,这个方法好长。。。单独把有用的代码捞出来吧
//这里就是实际获取Bean的地方,也就是触发依赖注入发生的地方
protected <T> T doGetBean(String name, Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException {
**//这里是说先从缓存中去找Bean,若已经有了就不需要重新创建,只针对单例 Bean **
Object sharedInstance = this.getSingleton(beanName);
//当前bean正在创建池中,就不要建了,这个是针对原型的Bean
else {
if (this.isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
//这里会触发getBean()的递归调用
String[] dependsOn = mbd.getDependsOn();
//这里最终会调用ObjectFactory的createBean()
if (mbd.isSingleton()) {
------> //这里在DefaultSingletonBeanRegistry--》getSingleton<-------
sharedInstance = this.getSingleton(beanName, new ObjectFactory<Object>() {
public Object getObject() throws BeansException {
try {
return AbstractBeanFactory.this.createBean(beanName, mbd, args);
} catch (BeansException var2) {
AbstractBeanFactory.this.destroySingleton(beanName);
throw var2;
}
}
});
bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
} else if (mbd.isPrototype()) {//原型bean创建
prototypeInstance = this.createBean(beanName, mbd, args);
}
下面来看DefaultSingletonBeanRegistry中getSingleton方法:
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
//在创建前,做最终检查,若通过才能重新创建
this.beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = this.suppressedExceptions == null;
try {
singletonObject = singletonFactory.getObject();
newSingleton = true;
} catch (IllegalStateException var16) {
singletonObject = this.singletonObjects.get(beanName);
} catch (BeanCreationException var17) {
return singletonObject != NULL_OBJECT ? singletonObject : null;
}
}
DefaultSingletonBeanRegistry 中beforeSingletonCreation方法:
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
如果beforeSingletonCreation通过,即满足:1、创建池中没有这个bean;2、改bean不在创建中,那个接下来就是真正的创建bean了。
在AbstractAutowireCapableBeanFactory的doCreateBean方法
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, Object[] args) {
... 1
if (instanceWrapper == null) {
instanceWrapper = this.createBeanInstance(beanName, mbd, args);
}
...
... 2
boolean earlySingletonExposure = mbd.isSingleton() && this.allowCircularReferences && this.isSingletonCurrentlyInCreation(beanName);
if (earlySingletonExposure) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references");
}
this.addSingletonFactory(beanName, new ObjectFactory<Object>() {
public Object getObject() throws BeansException {
return AbstractAutowireCapableBeanFactory.this.getEarlyBeanReference(beanName, mbd, bean);
}
});
}
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
Class<?> beanClass = this.resolveBeanClass(mbd, beanName, new Class[0]);
if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
} else if (mbd.getFactoryMethodName() != null) {
return this.instantiateUsingFactoryMethod(beanName, mbd, args);
} else {
boolean resolved = false;
boolean autowireNecessary = false;
// 无参走这里
if (args == null) {
Object var7 = mbd.constructorArgumentLock;
synchronized(mbd.constructorArgumentLock) {
if (mbd.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
}
if (resolved) {
return autowireNecessary ? this.autowireConstructor(beanName, mbd, (Constructor[])null, (Object[])null) : this.instantiateBean(beanName, mbd);
} else {
Constructor<?>[] ctors = this.determineConstructorsFromBeanPostProcessors(beanClass, beanName);
return ctors == null && mbd.getResolvedAutowireMode() != 3 && !mbd.hasConstructorArgumentValues() && ObjectUtils.isEmpty(args) ? this.instantiateBean(beanName, mbd) : this.autowireConstructor(beanName, mbd, ctors, args);
}
}
}
在“1”createBeanInstance中可以看出,调用构造方法创建一个实例对象,如果这个构造方法有参数,而且就是循环依赖的参数,那么这个对象就无法创建了,因为到这里对象没有创建,也没有暴露当前对象,如果是无参的构造方法,那么就可以,先创建一个对象,尽管所有的属性都为空。
在“2”中,申明了必须满足三个条件才能暴露当前创建的对象:
1、该对象是单例;
2、该对象允许循环依赖(默认是true);
3、在当前创建池中有。
至此,我们可以来分析了:
属性注入默认是调用无参构造器创建一个实例,但是属性都为空,并把该对象的引用提前暴露出来,这样依赖于他的Bean就能获取到该Bean,从而能够完成自身的初始化。总结如下:
1、如果循环依赖的都是单例对象(都是通过setter方式注入属性的),那么这个肯定是可以的;
2、如果一个是单例,一个是原型,那么一定要保证单例对象能提前暴露出来,才可以正常注入属性。