Spring解析之IoC:bean的加载(一)
前言
看过Spring解析前两篇文章的读者知道,之前分析的思路一直都源自ClassPathXmlApplicationContext
的初始化,但该类的初始化还未分析完毕为什么突然切成bean
的加载呢?实际上在ClassPathXmlApplicationContext
初始化剩下的部分中多次进行了bean
的加载和获取,当然该流程是可以放在初始化过程中一并分析的,但bean
加载的流程比较复杂,如果将该流程和初始化过程放在一起无疑加重了原本就难于理解的逻辑,此外,从容器中获取对象是Spring最常用的功能,单独的剥离分析更加有利于整体脉络的构建,因此另开一文单独讲解。bean
的加载由两到三篇文章构成,第一篇也就是本文,以显式调用getBean
为切入点
在首篇开始的例子中可知,通常我们手动获取bean
使用getBean
方法,该方法有几种重载形式,最普遍的为Obejct getBean(String)
,根据beanName
得到实际类型为beanName
对应bean
实例的Object
对象;另一种是我喜欢用的T getBean(Class<T>)
,这种方式省去了强转过程。不管任何一种获取bean
的方式都殊途同归,我们先以Obejct getBean(String)
为模板进行分析
ClassPathXmlApplicationContext
获取bean
实际调用了其父类AbstractApplicationContext
中对应方法。之前说过BeanFactory
的核心类为DefaultListableBeanFactory
,所以这里的getBeanFactory()
返回的就是该类对象。除了T getBean(Class<T>)
这种方式是在DefaultListableBeanFactory
中提供的,其他获取bean
的方法都封装在另一个父类AbstractBeanFactory
中图2. AbstractBeanFactory中获取bean的一系列方法
四种方式又同时抽成了
T doGetBean(String, Class<T>, Object[], boolean)
,该方法较长,见代码清单1
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
// (1)
final String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
// (2)
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 + "'");
}
}
// (3)
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// Check if bean definition exists in this factory.
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
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);
}
}
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
try {
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dependsOnBean : dependsOn) {
getBean(dependsOnBean);
registerDependentBean(dependsOnBean, beanName);
}
}
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
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;
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
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);
}
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>() {
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;
}
}
// Check if required type matches the type of the actual bean instance.
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;
}
从整体看上面的代码可根据最顶层的if/else
分为上下两部分,上半部分在调用getBean(String)
时进入,下半部分在new ClassPathXmlApplicationContext(String[])
时就会隐式执行,下半部分进行首次初始化,上半部分从缓存中获取,通常情况隐式执行早于显示调用,虽然真实情况下存在谁先谁后等“时序”因素,但为了分析方便,清楚表述,我在文章中都尽可能不考虑”时序“的影响,只关注逻辑流程,由于执行逻辑比较复杂,我们一点点分析。标注1将name
进行了规范化处理,这里的name
存在多种可能:1.普通的id/name
;2.bean
的昵称;3.FactoryBean
对应的id/name
,三种情况对应三种不同的规范化方式,第一种不做处理直接返回;第二种根据昵称从SimpleAliasRegistry
中的成员变量Map<String, String> aliasMap
中获得昵称对应的真实id/name
返回,至于昵称是什么时候放入aliasMap
的,在Spring解析之IoC:<bean>解析及Bean的注册的最后进行了bean
的注册,该方法的是由BeanDefinitionReaderUtils
调用的
下划线处就是bean
注册的流程,而红框内就是保存所有bean
对应昵称的过程,根据我们之前给过的DefaultListableBeanFactory
类图可知,这里的registry
就是DefaultListableBeanFactory
,但registerAlias(String, String)
定义在父类SimpleAliasRegistry
中,所有的alias
就放在了该类的成员变量aliasMap
中。至于第三种情况我们需要先知道FactoryBean
的用法和用途,FactoryBean
可以看成一个实现了FactoryBean<T>
接口的特殊bean
,它既可以是一个普通的bean
,也可以成为生产另一个bean
的工厂,当创建一个FactoryBean
后,我们可以通过getBean("FactoryBean的id/name")
得到该FactoryBean
生产的普通bean
对象,如果就想得到该FactoryBean
实例行不行呢?答案当然是可以的,我们需要这么写getBean("&FactoryBean的id/name")
,下面我们举个例子说明,首先写一个实现FactoryBean<T>
的工厂Bean
接下来在配置文件上加上对该类的配置,注意我没有对Student
类进行XML配置
最后我们获得Student
实例以及对应的生产工厂实例
代码清单1 标注2根据beanName
获得单例对象,但这里的单例对象不一定就是我们在配置文件中定义的bean
的实例,为什么这么说我们进入方法看看
图中标记出了三个不同的“缓存”,所谓的缓存就是定义在DefaultSingletonBeanRegistry
的三个成员变量,该类和DefaultListableBeanFactory
是父子关系,借用其他文章对这三个对象的称谓,singletonObjects
为一级缓存,保存beanName
和bean
实例的对应关系;earlySingletonObjects
是二级缓存,也保存了beanName
和bean
的对应关系。我们都知道假设A对象在初始化时依赖B对象,Spring会在A对象初始化前先将B对象初始化,本文将这种实例化完成并形成依赖关系的整个过程称为完全初始化;将没有建立依赖关系,仅仅实例化完成的过程称为不完全初始化,那这时解释singletonObjects
和earlySingletonObjects
的区别就很简单了,前者保存了完全初始化的实例映射,后者保存未完全初始化的实例映射;最后一个变量singletonFactories
是三级缓存,保存了beanName
和接口ObjectFactory
实现类的映射,现在问题又来了ObjectFactory
是什么东东?我们可以将其理解为生产对象的工厂,和上面刚说到的FactoryBean
有几分相似,之后我们会看到该类的具体实现和具体用法
和平时的认知一样,三级缓存的调用依次深入,且相互排斥,即一个beanName
只会存在于一个缓存中,设置如此复杂的逻辑目的是尽可能的解决循环依赖问题。解释完后再看代码就很清楚了,先根据beanName
从一级缓存中获取bean
,如果没有取到并且该对象正在创建isSingletonCurrentlyInCreation(String)
,就去二级缓存中查找。什么叫该对象正在创建呢?该对象正在创建又和循环依赖什么关系呢?举个例子:A对象依赖B对象,B对象依赖A对象,在创建A对象时要先创建B对象,在创建B对象时依赖A对象,A创建了一半等待B对象,此时A对象的状态就是正在创建,对于B对象也是一样的。如果二级缓存中依然没有,且允许早期引用标识allowEarlyReference = true
,就会从三级缓存中得到ObjectFactory
进而创建出最终的对象,创建完成后会将该对象存放在二级缓存中
代码清单1 标注3才是获得真正bean
的方法
注意方法上注解对于传参的解释,
name
是没有进过规范化处理的名称,那么其中可能包含&
,可能是昵称,也可能就是普通的beanName
。第一处判断name
是否包含工厂bean前缀&
且beanInstance
不属于FactoryBean
,这种情况是不可能存在的,所以抛出异常;第二处判断过滤掉两种情况:1.普通的bean
,前半部分为true,直接返回;2.只想获得FactoryBean
,后半部分为true,直接返回。只有想通过FactoryBean
获得普通bean
时才程序才会继续往下执行另外我们需要说一下
RootBeanDefinition
,之前在分析配置加载解析的过程中bean
对应的实体一直为GenericBeanDefinition
,而这里又出来个RootBeanDefinition
,该类与前者不同之处在于,前者可以称为一般意义上的bean
,后者主要用于封装存在继承关系的bean
对象,比如<bean>
中存在parent
属性就意味着存在root
和child
的关系,在解析的时候Spring会将所有的父子关系merge
到一个BeanDefinition
中,而RootBeanDefinition
就能很好的表示bean
直接的父子关系。当然现在Spring已经推荐使用GenericBeanDefinition
,该类同样可以封装父子关系,至于为什么RootBeanDefinition
依然在使用,我觉得可能是为了兼容老版本的原因吧上面说过,既然流程走到标注3处说明此时我们使用
FactoryBean
的id/name
要获得生产的bean
,此时首先从Map<String, Object> factoryBeanObjectCache
缓存中获取,其中key
为FactoryBean name
,value
为根据FactoryBean
生产的对应bean
,缓存中已经存在直接返回,没有继续往下图9. AbstractBeanFactory的getMergedLocalBeanDefinition(String)
繁琐的Spring又出现一个缓存mergedBeanDefinition
,key
为标签id/name
,value
保存对应RootBeanDefinition
,存在RootBeanDefinition
直接返回,但要注意点,经过上两篇文章的分析,此时在DefaultListableBeanFactory
中的缓存beanDefinitionMap
中已经存在beanName
和BeanDefinition
的映射了,所以图中的getBeanDefinition(String)
实际上会返回beanName
对应的GenericBeanDefinition
对象,最后调用RootBeanDefinition getMergedBeanDefinition(String, BeanDefinition, BeanDefinition)
,上面说过<bean>
可能存在父子关系,在该方法中如果当前BeanDefinition
为子对象,会根据parentName
得到对应的父BeanDefinition
,并通过递归调用的方式将父BeanDefinition
融合进子BeanDefinition
,最后包装成RootBeanDefinition
返回,该方法中又是一坨恶心的逻辑,这里就不挨个细聊,有兴趣的读者可以继续深入。图8标注5是根据FactoryBean
名称获取bean
的入口
首先根据
beanName
判断该FactoryBean
(由前面的分析可知此时beanName
只可能是FactoryBean
自定义实现类的id/name
)是否单例,是否存在于缓存singletonObjects
中,不满足直接调用核心方法Object doGetObjectFromFactoryBean(FactoryBean<?>, String, boolean)
生成对应bean
,否则再尝试从factoryBeanObjectCache
中获取bean
,上面说过该缓存内是FactoryBean name
和生成bean
的映射,如果映射不存在依然调用Object doGetObjectFromFactoryBean(FactoryBean<?>, String, boolean)
,并更新factoryBeanObjectCache
,因此最后核心创建bean
的职责就落在了红框内图11. FactoryBeanRegistrySupport的Object doGetObjectFromFactoryBean(FactoryBean<?>, String, boolean)
忽略其他不相干的部分,只关注所画代码逻辑还是非常清楚的,直接
factory.getObject()
多态执行我们自定义FactoryBean
的getObject()
,如果存在自定义的BeanPostProcessor
会进入标注2处代码,我们还是以例子作为切入点讲解。写一个自定义后处理器首先需要实现BeanPostProcessor
接口图12. 自定义BeanPostProcessorImpl
接口有两个待实现方法,好理解的是该处理器肯定是在
bean
实例创建完成之后调用的,但是这里的before/after Initialization
指的是是什么呢?我们的bean
依然是Student
,但是该类实现了InitializingBean
的afterPropertiesSet()
方法,该方法可供创建对象时进行一些初始化的行为,我们分别在Student
的构造器和afterPropertiesSet()
内打印一句话图13. Student对象
这里扩充点知识,Spring提供了三种方式让程序员在对象创建和销毁时进行一些自定义的处理:1.
@PostConstruct
和@PreDestory
;2.标签中定义init-method
和destory-method
;3.实现InitializingBean
和DisposableBean
,即便都是初始化/销毁方式内部执行的顺序也是有不同的,具体的使用方式和差异请读者自行研究。但是上面所说的before/after Initialization
就是基于最后一种方式来说的。我们在XML上配置好自定义bean
后处理器和Student
运行一下,结果如下图14. bean后处理例子顺序问题
结果很明显最先执行的是
Student
的构造器,而后为自定义bean
后处理器的postProcessBeforeInitialization()
,实现的afterPropertiesSet()
第三,最后是postProcessAfterInitialization()
。这里例子中Spring创建了普通的bean
,如果使用FactoryBean
来生成Student
对象又会发生什么呢?答案在BeanPostProcessor
的注解中写的很明白图14. BeanPostProcessor接口注解
对于使用自定义
FactoryBean
创建bean
的流程来说postProcessAfterInitialization()
方法会调用两次,一次是实例化自定义FactoryBean
时,第二次是FactoryBean.getObject()
创建bean
时。解释完bean
后处理器的使用和触发顺序我们再来看Spring是如何调用我们自定的后处理器,图12标注2就是其中一个调用点,此时this
为DefaultListableBeanFactory
的实例,获取所有自定后处理器并调用的方法封装在DefaultListableBeanFactory
的父类AbstractAutowireCapableBean
中图15. AbstractAutowireCapableBean的Object applyBeanPostProcessorsAfterInitialization(Object, String)
从
List<BeanPostProcessor> beanPostProcessors
中遍历每一个自定义后处理器,依次调用postProcessAfterInitialization(Object, String)
,大家可能会感到奇怪,为什么只调用postProcessAfterInitialization
而没有调用postProcessBeforeInitialization
呢,其实呢上面我们说过对于使用自定义FactoryBean
创建bean
流程来说postProcessAfterInitialization
会被调用两次,这里实际上就是第二次对于生产出来的bean
做的后处理,而第一次调用在哪?postProcessBeforeInitialization
的调用在哪?答案是在new ClassPathXmlApplicationContext(String[])
中隐式调用过了,也就是文章开始说过的“下半部分”处进行了调用,如果大家还懵逼的话,下一篇分析”下半部分“就清楚了