Spring源码分析——Bean创建和循环依赖

1:Spring创建Bean的主要代码区域

启动IOC容器时会去调用refresh()方法,其中内部总共调用了15个方法,
finishBeanFactoryInitialization(beanFactory);此方法就是创建我们所声明的所有非懒加载的Bean,
该方法内部主要核心是beanFactory.preInstantiateSingletons();
在DefaultListableBeanFactory类中的preInstantiateSingletons方法中下列代码块会将声明的Bean添加到singletonObjects单例缓存池中并且注册到registeredSingletons容器中

for (String beanName : beanNames) {
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
				if (isFactoryBean(beanName)) {
					final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
					boolean isEagerInit;
					if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
						isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
							@Override
							public Boolean run() {
								return ((SmartFactoryBean<?>) factory).isEagerInit();
							}
						}, getAccessControlContext());
					}
					else {
						isEagerInit = (factory instanceof SmartFactoryBean &&
								((SmartFactoryBean<?>) factory).isEagerInit());
					}
					if (isEagerInit) {
						getBean(beanName);
					}
				}
				else {
                          //获取bean
					getBean(beanName);
				}
			}
		}
2:Spring涉及的容器
 //存储单例缓存对象,bean name --> bean instance ,此Map就是IOC容器
     //pring中Bean描述的就是此容器元素
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
//用于保存BeanName和创建bean的工厂之间的关系, bean name --> ObjectFactory
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
//保存BeanName和创建bean实例之间的关系,与singletonObjects 不同之处在于,当一个
     //单例bean被放在这里后,那么当bean还在创建过程中,就可以通过getBean方法获取到了
    //此容器存在的目的就是用来检测循环引用
	private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
     //用来保存当前所有已注册的bean,当bean完成属性注入后,缓存到单例池中,该bean同时会被缓存到这里
	private final Set<String> registeredSingletons = new LinkedHashSet<String>(256);
     //用来保存当前正在创建的bean,存储beanName,标识指定name的Bean对象是否处于创建状态
	private final Set<String> singletonsCurrentlyInCreation =
			Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(16));
三级缓存

singletonObjects 一级缓存
earlySingletonObjects 二级缓存
singletonFactories 三级缓存

二级缓存存在的意义

上面的对象为什么要把对象从三级缓存singletonFactories 中添加到二级缓存earlySingletonObjects 中,之后又要把三级缓存singletonFactories 中对象删掉,这么多余做为什么?
分析:提高性能,earlySingletonObjects 单纯一个集合,而每次从singletonFactories 中获取需要调用工厂方法,如果多个对象持有相同引用属性,那么就需要重复调用,这个工厂方法或许又会非常复杂又要去调用一系列的后置处理器,这种出现重复创建重复调用的问题,会影响性能,因为调用工厂方法产生的或者说包装后的都是一样的,徒增时间消耗. 避免重复创建,提高效率
earlySingletonObjects 存在的必要性:减少循环引用是创建工厂对象的资源消耗时间消耗,提高程序性能,避免重复创建.

3:getBean()分析

IOC容器启动时会去调用getBean方法,现在就来看下这个方法内部都做了那些事,该方法主要功能实现在于doGetBean中

protected <T> T doGetBean(
			final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
			throws BeansException {

        //转换BeanName,提取最终BeanName  
		final String beanName = transformedBeanName(name);
		Object bean;

		//尝试从单例池或者singletonFactory中获取Bean对象
		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实例 
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}

		else {
			//只有单例情况才会尝试解决循环依赖,原型对象无法解决循环依赖,抛异常
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}
            //获取父类的BeanFactory,如果子类BeanDefinitionMap中没有匹配BeanName,则去父类中查找
 			BeanFactory parentBeanFactory = getParentBeanFactory();
			if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
                //如果父类的 BeanFactory不为空,则去父类中查找Bean
				String nameToLookup = originalBeanName(name);
				if (args != null) {
					// Delegation to parent with explicit args.
					return (T) parentBeanFactory.getBean(nameToLookup, args);
				}
				else {
					// No args -> delegate to standard getBean method.
					return parentBeanFactory.getBean(nameToLookup, requiredType);
				}
			}
            //如果不是仅仅做类型检查则是创建Bean,这里进行标记存入集合alreadyCreated中
			if (!typeCheckOnly) {
				markBeanAsCreated(beanName);
			}

			try {
				//1:将GernericBeanDefinition转换为RootBeanDefinition
				//2: 合并子类和父类的相关属性
				final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				checkMergedBeanDefinition(mbd, beanName, args);

			    //针对配置了dependsOn属性进行递归实例化依赖
				String[] dependsOn = mbd.getDependsOn();
				if (dependsOn != null) {
					for (String dependsOnBean : dependsOn) {
						if (isDependent(beanName, dependsOnBean)) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"Circular depends-on relationship between '" + beanName + "' and '" + dependsOnBean + "'");
						}
						registerDependentBean(dependsOnBean, beanName);
						getBean(dependsOnBean);
					}
				}
                //实例化依赖的bean后便可以实例化RootBeanDefinition
				if (mbd.isSingleton()) {
					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);
				}

				else if (mbd.isPrototype()) {
					//如果scope是原型的话,创建new
					Object prototypeInstance = null;
					try {
						beforePrototypeCreation(beanName);
						prototypeInstance = createBean(beanName, mbd, args);
					}
					finally {
						afterPrototypeCreation(beanName);
					}
					bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
				}

				else {
					String scopeName = mbd.getScope();
					final Scope scope = this.scopes.get(scopeName);
					if (scope == null) {
						throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
					}
					try {
						Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
							@Override
							public Object getObject() throws BeansException {
								beforePrototypeCreation(beanName);
								try {
									return createBean(beanName, mbd, args);
								}
								finally {
									afterPrototypeCreation(beanName);
								}
							}
						});
						bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
					}
					catch (IllegalStateException ex) {
						throw new BeanCreationException(beanName,
								"Scope '" + scopeName + "' is not active for the current thread; " +
								"consider defining a scoped proxy for this bean if you intend to refer to it from a singleton",
								ex);
					}
				}
			}
			catch (BeansException ex) {
				cleanupAfterBeanCreationFailure(beanName);
				throw ex;
			}
		}

        //检查需要的类型是否符合bean的实际类型 requiredType
  		if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
			try {
				return getTypeConverter().convertIfNecessary(bean, requiredType);
			}
			catch (TypeMismatchException ex) {
				if (logger.isDebugEnabled()) {
					logger.debug("Failed to convert bean '" + name + "' to required type [" +
							ClassUtils.getQualifiedName(requiredType) + "]", ex);
				}
				throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
			}
		}
		return (T) bean;
	}

上述代码描述了bean的创建加载过程,里面涉及的功能点非常多且比较复杂,下面针对重要的流程做系列分析

(1):转换beanName

transformedBeanName(name);
这里涉及到俩方面考虑,一方面针对于FactoryBean的处理,如果是实现FactoryBean的子类,它可以创建俩种不同的对象,如果beanName=”&A”,那么此时表示的bean就是实现FactoryBean接口的子类,如果是beanName=”A”,则此时表示的是FactoryBean类中getObject返回的对象bean;另一方面是针对别名的处理,取alias所表示的最终的beanName

(2):尝试从缓存中加载单例对象

Object sharedInstance = getSingleton(beanName);
单例在Spring的同一个容器内只会被创建一次,后续再获取bean就直接从单例缓存中获取了。当然这里也只是尝试加载,首先尝试从缓存中加载,如果加载不成功则再次尝试从singletonFactories中加载。因为在创建单例bean的时候会存在依赖注入的情况,而在创建依赖的时候为了避免循环依赖,在Spring中创建bean的原则是不等bean创建完成就会将创建bean的ObjectFactory提早曝光加入到缓存中,一旦下个bean创建时候需要依赖上一个bean则直接使用ObjectFactory

(3):bean的实例化

bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
如果从缓存中得到了 bean的原始状态,则需要对bean迸行实例化。这里有必要强调一下,缓存中记录的只是最原始的bean状态,并不一定是我们最终想要的 。举个例子,假如我们需要对工厂bean进行处理,那么这里得到的其实是工厂bean的初始状态,但是我们真正需要的是工厂bean中定义的factory-method方法中返回的bean,而getObjectForBeanlnstance就是完成这个工作的

(4)原型模式的依赖检査

if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
只有在单例情况下才会尝试解决循环依赖,如果存在A中有B的属性. B中有A的属性,那么当依赖注入的时候,就会产生当A还未创建完的时候因为对于B的创建再次返回创建A.造成循环依赖,也就条件:isPrototypeCurrentlyInCreation(beanName)判断 true。
此时抛出异常throw new BeanCurrentlyInCreationException(beanName);

(5 )检测 parentBeanFactory
	BeanFactory parentBeanFactory = getParentBeanFactory();
			if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
                //如果父类的 BeanFactory不为空,则去父类中查找Bean
				String nameToLookup = originalBeanName(name);
				if (args != null) {
					// Delegation to parent with explicit args.
					return (T) parentBeanFactory.getBean(nameToLookup, args);
				}
				else {
					// No args -> delegate to standard getBean method.
					return parentBeanFactory.getBean(nameToLookup, requiredType);
				}
			}

如果缓存没有数据的话直接转到父类工厂上去加载了,这是为什么呢?
这是因为检测如果当前加载的XML配置文件中不包含beanName所对应的配置,就只能到parentBeanFactory去尝试下了,然后再去递归的调用getBean方法

(6 )将存储 XML 配置文件的 GemericBeanDefinition 转换为 RootBeanDefinition
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);

因为从XML配置文件中读取到的Bean信息是存储在GemericBeanDefinition中的,但是所有的Bean后续处理都是针对于RootBeanDefinition的,所以这里需要进行一个转换,转换的同时如果父类bean不为空的话,则会一并合并父类的属性

(7)寻找依赖
String[] dependsOn = mbd.getDependsOn();

因为bean的初始化过程中很可能会用到某些属性,而某些属性很可能是动态配置的,并且配置成依赖于其他的bean,那么这个时候就有必要先加载依赖的bean,所以,在Spring的加载顺序中,在初始化某一个bean的时候首先会初始化这个bean所对应的依赖

(8)针对不同的scope进行bean的创建
if (mbd.isSingleton()) {
					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);
				}

				else if (mbd.isPrototype()) {
					//如果scope是原型的话,创建new
					Object prototypeInstance = null;
					try {
						beforePrototypeCreation(beanName);
						prototypeInstance = createBean(beanName, mbd, args);
					}
					finally {
						afterPrototypeCreation(beanName);
					}
					bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
				}

				else {
					String scopeName = mbd.getScope();
					final Scope scope = this.scopes.get(scopeName);
					if (scope == null) {
						throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
					}
					try {
						Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
							@Override
							public Object getObject() throws BeansException {
								beforePrototypeCreation(beanName);
								try {
									return createBean(beanName, mbd, args);
								}
								finally {
									afterPrototypeCreation(beanName);
								}
							}
						});
						bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
					}
					catch (IllegalStateException ex) {
						throw new BeanCreationException(beanName,
								"Scope '" + scopeName + "' is not active for the current thread; " +
								"consider defining a scoped proxy for this bean if you intend to refer to it from a singleton",
								ex);
					}
				}
			}
			catch (BeansException ex) {
				cleanupAfterBeanCreationFailure(beanName);
				throw ex;
			}
		}

在Spring中存在着不同的scope,其中默认的是singleton,但是还有些其他
的配置诸如prototype、request、session之类的。在这个步骤中,Spring会根据不同的配置进行不同的初始化策略

(9)类型转换
if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
			try {
				return getTypeConverter().convertIfNecessary(bean, requiredType);
			}
			catch (TypeMismatchException ex) {
				if (logger.isDebugEnabled()) {
					logger.debug("Failed to convert bean '" + name + "' to required type [" +
							ClassUtils.getQualifiedName(requiredType) + "]", ex);
				}
				throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
			}
		}

程序到这里返回bean后已经基本结束了,通常对该方法的调用参数requiredType是为空的,但是可能会存在这样的情况,返回的bean其实是个String,但是requiredType却传入Integer类型,那么这时候本步骤就会起作用了,它的功能是将返回的bean转换为requiredType所指定的类型。

4:缓存中获取Bean

单例在Spring的同一个容器内只会被创建一次,后续再获取bean直接从单例缓存中获取,当然这里也只是尝试加载,首先尝试从缓存中加栽,然后再次尝试尝试从singletonFactories中加载因为在创建单例bean的时候会存在依赖注入的情况,而在创建依赖的时候为了避免循环依赖Spring创建bean的原则是不等bean创建完成就会将创建bean的ObjectFactory提早曝光加入到缓存中,一旦下一 bean创建时需要依赖上个bean,则宜接使用ObjectFactory
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		//首先尝试从单例池中加载singletonObjects
		Object singletonObject = this.singletonObjects.get(beanName);
		//如果不存在且存在于singletonsCurrentlyInCreation容器内即状态为正在创建中
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			//锁住单例池singletonObjects,避免此时有线程添加bean
			synchronized (this.singletonObjects) {
				//尝试从earlySingletonObjects获取bean,即用于解决循环依赖的提前暴露对象bean
				singletonObject = this.earlySingletonObjects.get(beanName);
					//如果不存在尝试从earlySingletonObjects获取bean且允许提早引用,allowEarlyReference默认为true
				if (singletonObject == null && allowEarlyReference) {
				     //尝试获取对象工厂singletonFactories
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					//如果存在对象工厂
					if (singletonFactory != null) {
						//获取对象工厂getObject方法返回的对象bean
						singletonObject = singletonFactory.getObject();
						//添加到earlySingletonObjects容器中
						this.earlySingletonObjects.put(beanName, singletonObject);
						//移除singletonFactories容器
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		//返回满足条件的对象
		return (singletonObject != NULL_OBJECT ? singletonObject : null);
	}

这个方法因为涉及循环依赖的检测,以及涉及很多变量的记录存取。这个方法首先尝试从singletonObjects里面获取实例,如果获取不到再从earlySingletonObjects里面获取,如果还获取不到,再尝试从singletonFactories里面获取beanName对应的objectFactory,然后调用这个 objectFactory的 getObject 来创建 bean,并放到 earlySingletonObjects里面去,并且从singletonFacotories里面remove掉这个ObjedFactory,而对于后续的所有内存操作都只为了循环依赖检测时候使用,也就是在allowEarlyReference为true的情况下才会使用

5:循环依赖

循环依赖就是循环引用,就是两个或多个bean相互之间的持有对方,比如A引用B, B引用A,则它们最终反映为一个环。

注意:循环引用不是循环调用,循环调用是方法的循环调用,就是死循环,二者不同
在这里插入图片描述

构造器循环引用
public class A {
    private B b;
    public A(B b) {
        this.b = b;
    }
}
public class B {
    private  A a;
    public B(A a) {
        this.a = a;
    }
}

配置文件

 <!--构造器循环依赖-->
     <bean id="a" class="bean.A">
         <constructor-arg index="0" ref="b" />
     </bean>
    <bean id="b" class="bean.B">
        <constructor-arg index="0" ref="a" />
    </bean>

测试

public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
    }

在这里插入图片描述

通过构造器注入的循环依赖是无法解决的,只能抛出BeanCurrentlyInCreationException异常

如在创建A类时,构造器需要B类,那将去创建B,在创建TEB类时又发现需要A,从而形成一个环,没办法创建。Spring容器将每一个正在创建的bean标识符放在一个“当前创建bean池“中,bean标识符在创建过程中将一直保持在这个池中,因此如果在创建bean过程中发现自己已经在“当前创建bean池”里时,将抛出BeanCurrentlyInCreationException异常表示循环依赖;而对于创建完毕的bean将从“当前创建bean池”中清除掉

prototype依赖处理

对于“prototype”作用域bean. Spring容器无法完成依赖注人 因为Spring容器不进行缓存-prototype"作用域的bean,因此无法提前暴露创建中的bean。

public class A {

    private B b;

    public B getB() {
        return b;
    }

    public void setB(B b) {
        this.b = b;
    }
}
public class B {
    private  A a;

    public A getA() {
        return a;
    }

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

配置文件

 <bean id="a" class="bean.A" scope="prototype">
       <property name="b" ref="b"/>
     </bean>
    <bean id="b" class="bean.B" scope="prototype">
        <property name="a" ref="a"/>
    </bean>

public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        Object a = context.getBean("a");
    }

使用prototype配置的scope在IOC容器启动时不会报错,但是在获取bean时会抛出异常

在这里插入图片描述

setter循环依赖

表示通过setter注入方式构成的循环依赖。对于setter注入造成的依賴是通过Spring容器提前暴露刚完成构造器注入但未完成其他步骤(如setter注入)的bean来完成的,而且只能解决单例作用域的bean循环依赖。通过提前暴露单例工厂方法,从而使其他bean能引用到该bean

public class A {

    private B b;

    public B getB() {
        return b;
    }

    public void setB(B b) {
        this.b = b;
    }
}
public class B {
    private  A a;

    public A getA() {
        return a;
    }

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

配置文件

 <bean id="a" class="bean.A">
       <property name="b" ref="b"/>
     </bean>
    <bean id="b" class="bean.B">
        <property name="a" ref="a"/>
    </bean>
 public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        System.out.println(context.getBean("a"));
    }

在这里插入图片描述
内部流程:
1: Spring容器创建单例bean A,首先根据无参构造器创建bean,并暴露一个ObjectFactory用于返回一个提前暴露一个创建中的bean,并将“A”标识符放到“当前创建bean池,然后进行setter注入“testB”。
(2) Spring容器创建单例B,首先根据无参构造器创建bean,并暴露一个ObjectFactory用于返回一个提前暴露一个创建中的bean,并将“B”标识符放到“当前创建bean池”,然后进行setter注入“A”。
(3)进行注入A时由于提前暴露了 ObjectFactory工厂,从而使用它返回提前暴露一个创建中的bean
(4) B注入属性A完成后(循环依赖中是递归式的注入,即环尾先完整注入),方法栈退出,最后A注入属性B 完成setter注入

addSingletonFactory(beanName, new ObjectFactory<Object>() {
				@Override
				public Object getObject() throws BeansException {
					return getEarlyBeanReference(beanName, mbd, bean);
				}
			});

备注:
对于 singleton作用域 bean,可以通过 setAllowCircularReference(false); 来禁用循环
引用

原创文章 105 获赞 33 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Octopus21/article/details/105776063