在Spring框架中我们是这样获取Bean实例的,本文就这段代码进行一个详细的解析,了解在Spring框架中BeanDefinition是如何被加载为一个可使用的bean实例的。
TestBean bean = (TestBean)beanFactory.getBean("testBean");
1.代码位置
在经过一系列的getBean->doGetBean之后,代码的具体位置如下:org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean,在这个方法中,经过一系列的解析,成功获取到了bean对象,由于代码太长,这里只在必要时贴出关键代码。
2.从缓存中获取
方法的开始部分,通过传入参数name从缓存的bean名称或别名中获取到beanName,然后会先调用getSingleton方法在缓存中寻找bean是否已经创建好,如果创建好的话直接将初始的bean实例进行包装,即调用getObjectForBeanInstance方法,这个方法比较重要,后面会单独讲,另外从缓存中获取bean也是一个比较复杂的过程,涉及到循环依赖的问题,之后会单独开一个文章进行讲解,这里仅简单提一嘴,正常情况下第一次进入时缓存中是没有的,我们已缓存中没有的这种情况为基准进行解析。
3.bean加载的整体过程
如果缓存中不存在已经加载的单例,就需要从头开始bean的加载过程,如下图所示,一共分为以下三步,获取RootBeanDefinition对象,单例的实例化bean以及从bean的实例中获取对象。接下来将详细对这三个步骤进行解析。
4.获取RootBeanDefinition对象
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
因为从XML配置文件中读取到的bean信息是存储在GernericBeanDefinition中的,但它不适合进行后续的处理,RootBeanDefinition中用于比它更强大的功能例如缓存、工厂方法、泛型等等,因此需要将GernericBeanDefinition转换为RootBeanDefinition。那为什么不在一开始XML解析时就使用RootBeanDefinition呢?其实GernericBeanDefinition也有它的优点,它自2.5版本之后加入作为通用的bean实现,是ChildBeanDefinition和RootBeanDefinition的替代者,它实现了parentName属性的get和set方法,而RootBeanDefinition却不提供继承相关的操作,因此bean的一般形式是以GenericBeanDefinition存在的,在特定的时机会将它转成RootBeanDefinition。那么既然RootBeanDefinition无法指定父类,那某个bean的父类如何体现呢,这就体现了Spring的思想,它会首先初始化父类的RootBeanDefinition,然后根据子类的GernericBeanDefinition覆盖父类中的相应属性,最终获取子类RootBeanDefinition。
解释完了为什么要转换,我们来看一下实际的代码是如何转换的,其实就是根据beanName从缓存中获取到GenericBeanDefinition,然后在AbstractBeanDefinition有一个构造方法可以将任何类型的BeanDefinition进行转化,而RootBeanDefinition继承了这个构造方法,在转化完成之后还会对其进行一个校验,看是否转化成功。
5.bean的初始化
//1.8新版
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
//老版
sharedInstance = getSingleton(beanName, new ObjectFactory<>(Object){
public Object getObject()throws BeansException{
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
}
});
在看源码时可能大家会有疑惑,getSingleton方法中的这个singletonFactory这个变量是从哪里来的,其实这里是用了一个1.8的新特性,上面我为大家展示了新老版本的区别,看老版可能更加明白。这里有两个方法getSingleton和createBean,其中createBean方法是真正的创建的过程,但由于太过于复杂,将其放在下一篇文章中详细介绍,我们这里仅介绍bean的初始化过程。
在getSingleton方法中主要做了以下几件事:
- 检查缓存是否已经加载过
- 若没加载则记录beanName的正在加载状态
- 加载单例前记录加载状态
- 通过入参singletonFactory的getObject方法实例化bean
- 加载单例后的处理方法调用
- 将结果记录至缓存并删除加载bean过程中记录的各种辅助状态
- 返回处理结果
前三步主要涉及到一个缓存的操作,和加载状态的记录,这样做是为了防止出现循环依赖问题,记录的加载状态会在加载完成之后进行删除,即第六步的操作。从上述步骤可以看出重点在第四步,这里就涉及到singletonFactory这个入参了。可以看到它的类型是ObjectFactory,这个接口里面只有一个方法getObject(),在调用getSingleton方法时通过一个匿名内部类将其创建出来,这里采用的是工厂模式。和他有同样作用的还有FactoryBean接口,同样也是通过工厂模式来获取bean,两者的不同在于ObjectFactory生成实例对象可部分或完全自主不受容器管控,而FactoryBean完全自主容器会提供信息。
6.从Bean的实例中获取对象
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
这是bean加载的最后一步处理,同时这个方法在getBean中也高频的出现,无论是从缓存中获取还是新创建的bean都需要经过这个方法,因为此时的bean还是最原始的状态,并不一定是我们最终想要的,它可能是一个factoryBean,因此需要通过这个方法就是最后的解析。这个方法主要做了以下几件事:
- 对FactoryBean正确性的验证
- 对非FactoryBean不做任何处理
- 对bean进行转换
- 将从Factory中解析bean的工作委托给getObjectFromFactoryBean
可以看到最终要的工作交给了getObjectFromFactoryBean方法,而这个方法主要保证了单例bean的全局唯一,并且调用factoryBean的getObject方法获取到bean对象,在进行后置处理之后返回。
总结
在bean的加载前后做了很多的事情,如果缓存中已经加载过则直接返回,如果没有则需要经过一系列的步骤来获取到bean实例,再经过初始化后并且通过验证才能真正的使用。当然最重要的bean的加载过程还没有开始,下一篇文章中将详细介绍bean是如何加载的。