我们先看一个现象:
我在test方法中打印这两个对象A和B
ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("test.xml");
A a=applicationContext.getBean("a", A.class);
System.out.println(a.getB());
B b=applicationContext.getBean("b",B.class);
System.out.println(b.getA());
其中a和b这两个bean对象我在xml文件中进行了配置,并且这两个bena对象相互依赖:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="a" class="com.hosp.pojo.A" init-method="initMethod">
<property name="b" ref="b" />
</bean>
<bean id="b" class="com.hosp.pojo.B" >
<property name="a" ref="a"/>
</bean>
</beans>
可以看到,很明显,A类中有一个属性指向了b的引用,同时B类中又有一个属性指向了a的引用。所以我们思考一个问题:
问题1:
spring容器在加载bena对象的时候,很明显这两个bean之间相互依赖了,如果这两个bean没有交给spring容器管理,我们自己去创建出这两个bean对象,我们自己怎么做到这种相互依赖的问题?
所以这要看我们是如何创建并管理benau对象的,并且如何做到依赖注入的。所以这里依赖注入的时候,绝对不能使用构造方法的方式,那样我们会不停的创建出一堆而且一摸一样的对象,我们只能用set属性的方式!我们需要一个容器去装载创建好的bean对象,去解决重复创建对象的问题,同时因为我们有了这个容器,我们只要创建出两个完整的A类和B类对象,a的属性b指向b,b的属性a指向a,就可以了。所以,spring容器中是如何管理创建出来的bean?如何识别出bean对象的所处的状态?如何进行依赖注入?接下来从这三个目标着手来研究源码。
1.spring容器中是如何管理创建出来的bean?
要知道如何管理,先要知道怎么创建出来的,所以这里不得不说初始化容器不得不看的一个函数refresh(),在这个方法里有一个finishBeanFactoryInitialization方法,在这一步
// Instantiate all remaining (non-lazy-init) singletons. //实例化所有剩余的单例bean对象,所以这里也包括我们自定义的两个bean:a和b finishBeanFactoryInitialization(beanFactory);
进去之后还有一个方法:preInstantiateSingletons,初始化,再进,最终在DefaultListableBeanFactory的preInstantiateSingletons方法中,看到有这样一个方法:
public void preInstantiateSingletons() throws BeansException {
//.....
for (String beanName : this.beanDefinitionNames) {
//...
//初始化单例对象的时候,其实调用的是getBean方法,参数是beanname,也就是说传给他一个bean的名字,在这个方法内部,会创建出一个对象并添加到对象池中
getBean(beanName);
//...
}
}
点进去,一路点getBean()->doGetBean(),
之后到了doGetBean()方法里,我们看到首先做的一部操作:检查缓存
Object sharedInstance = getSingleton(beanName);//检查单例缓存
在getSingleton这个方法中,我们看到了BeanFactory中维护的三块缓存:
//后面会多次用到,getSingleton方法中,就是依次去这三块缓存中找有没有对应的bean //而现在我们还没有对a、b这两个bean做任何的初始化,所以结果必然是null singletonObjects、earlySingletonObjects、singletonFactories
接下来源码非常多,我们不必在意,也没有必要全部每行代码都看懂,我们的目的是研究三块缓存怎么解决的循环依赖的问题,所以bean对象的创建、修改、以及三块缓存区内容的修改才是我们的关注点。。
后面有一大堆准备工作,无伤大雅,再后来有一个这样的方法:
sharedInstance = getSingleton(beanName, new ObjectFactory() {
public Object getObject() throws BeansException {
try {
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;
}
}
});
这里通过getSingleton这个方法去根据beanname创建出了一个bean对象并且赋值给了sharedInstance。(注意是一个匿名内部类,或者说是一种回调函数)关键是genSingleton这个方法可以理解为创建出了一个完整的bean对象。
的确我们在createBean函数中看到了,当中步骤有点多,省略没用的,
createBean->doCreateBean->createBeanInstance->instantiateBean->getInstantiationStrategy().instantiate->instantiateClass,最终通过反射创建了一个空对象,注意这个时候beanA的内容,完全是一个空对象,属性全都是默认值:
然后继续回到doCreateBean里面,现在创建了一个空对象之后,有一个判断:
earlySingletonExposure,当为true的时候,做了一件事:addSingletonFactory
if (earlySingletonExposure) { //.... addSingletonFactory(beanName, new ObjectFactory() { public Object getObject() throws BeansException { return getEarlyBeanReference(beanName, mbd, bean); } }); }
在这里我们看到了之前用到的三级缓存,他把singletonFactory放到了第三季缓存里面,而这个singletonFactory是啥,又是一个回调函数,或者说一个匿名内部类的对象。同时registeredSingletons也把beanname加进去了。
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); } } }
所以说为什么我们在getSingleton的方法上面看到了这样一段话:
Returns: the registered singleton object
在接下来,回到doCreateBean,看到这样一个方法:为a这个bean填充属性,而现在要填充的是b这个bean,所以这里做一个猜测:一定会再去创建b这个bean,也就是说,会重新执行到之前的getSingleton那个方法里面,接下来我们跟一遍看看是不是这样。。
populateBean(为beanA填充属性)->applyPropertyValues(为属性赋值)->resolveValueIfNecessary(解析value的值)
我们注意resolveValueIfNecessary这个方法,很有可能这里就要去创建beanB了,接着走,
resolveValueIfNecessary->resolveReference(解析引用)
这时看到了熟悉的方法:
//从beanfactory中获取bean对象,换句话说,这里终于又要创建beanB了,从而也就证实了之前的猜测
Object bean = this.beanFactory.getBean(refName);
所以到了这一步,我们又回到了之前的beanA创建的时候走的流程:
getSingleton判断三级缓存中有没有beanB->getSingleton->beanFactory匿名内部类的回调函数createBean->doCreateBean->createBeanInstance->instantiateBean(无参构造)
这里创建出了一个空的beanB,然后和beanA一样,把一个匿名内部类放到了singletonFactories缓存里面。
后面又是b的populateBean。。。
b的populateBean(给beanB填充属性)->applyPropertyValues(为beanB的属性赋值)->valueResolver.resolveValueIfNecessary(解析beanB的属性a的值)->resolveReference-> this.beanFactory.getBean(refName)(从容器中获取beanA)
注意,此时容器中已经有了beanA了,在三级缓存里面,所以调用singletonFactory.getObject方法可以获取到beanA。
现在获取到了beanA是为了继续给beanB的属性a填充值的,最终
//反射给beanB的a属性赋值了,到这一步,beanB的属性赋值工作已经完成了,也就是说这个时候beanB已经是一个完整的bean了 writeMethod.invoke(this.object, value);
这个时候三级缓存中的内容是:singletonFactory有一个beanB,earlySingletonObjects有一个beanA
现在beanB已经是一个完整的bean了,所以下一步addSingleton把beanB放到singletonObjects也就是一级缓存里面。
现在一级缓存里面有一个beanB,而beanA还在二级缓存里面。
接下来,就是给beanA填充属性了,和beanB一样,最终beanA从二级缓存被移到了一级缓存了。结束。。。
5.如果采用不同的方式配置aop,aop的执行顺序会不会不一样?答:会
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
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);
}
}
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}