前言
在上一篇文章提到在AbstractApplicationContext类的refresh方法中,可以通过invokeBeanFactoryPostProcessors方法,修改BeanDefinition对象。
其实在refresh方法中,还有:registerBeanPostProcessors、initMessageSource、initApplicationEventMulticaster、onRefresh、registerListeners、finishBeanFactoryInitialization和finishRefresh方法没有讲。
registerBeanPostProcessors方法主要是注册BeanPostProcessor对象到Spring容器,后面在Bean初始化前后会用到。这个方法的逻辑非常直观,跟invokeBeanFactoryPostProcessors方法差不多,在这里就不多介绍了。
initMessageSource方法:主要初始化支持国际化用的功能,内容也很简单。initApplicationEventMulticaster方法:主要是初始化广播事件,内容也很简单。onRefresh方法:是一个空方法,给子类重写的。registerListeners方法:主要是注册事件监听器,内容也很简单。
接下来,把我们的目光一起聚集到finishBeanFactoryInitialization方法上来,这个方法初始化单例bean,是我们今天的重头戏。
1 finishBeanFactoryInitialization
通过前面的操作,Spring已经从XML文件中,把bean的相关内容解析到了BeanDefinition对象中,接下来,需要把这些对象真正的实例化。
Spring为了性能考虑,把单例的并且非懒加载的bean,在Spring容器启动的时候,先实例化并且初始化好,保存到缓存中。
这样真正的用户请求来了,Spring可以直接从缓存中获取bean实例,提升整个框架的性能。
我们一起看看finishBeanFactoryInitialization方法是怎么初始化bean的。
protectedvoidfinishBeanFactoryInitialization(ConfigurableListableBeanFactorybeanFactory){
//1初始化类型转换器
if(beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME)&&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME,ConversionService.class)){
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME,ConversionService.class));
}
// 2 注册内容解析器,解析内容中的比如:$等
if(!beanFactory.hasEmbeddedValueResolver()){
beanFactory.addEmbeddedValueResolver(strVal->getEnvironment().resolvePlaceholders(strVal));
}
//3实例化并初始化@EnableLoadTimeWeavering或<context:load-time-weaver>标记的类
String[]weaverAwareNames=beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class,false,false);
for(StringweaverAwareName:weaverAwareNames){
getBean(weaverAwareName);
}
//将临时的classLoader设置成null
beanFactory.setTempClassLoader(null);
//马上要进行初始化了,冻结所有的bean
beanFactory.freezeConfiguration();
//初始化单例非懒加载的bean
beanFactory.preInstantiateSingletons();
}其中preInstantiateSingletons方法,才是真正 初始化单例非懒加载的bean的入口:
该方法会遍历所有的beanName,获取到对应的BeanDefinition对象,然后判断,如果该BeanDefinition不是抽象的,是单例的,并且不是延迟加载的,则进行下一步处理。
再判断如果该beanName对于的类是FactoryBean类型的,则使用&beanName作为key,调用getBean方法获取对应bean。
如果beanName不是FactoryBean类型的,则直接使用beanName作为key,调用getBean方法获取对应bean。
其实真正实例化并且初始化bean的地方在getBean方法中,我们下一篇重点介绍。
此外,还有两个比较有意思的方法:getMergedLocalBeanDefinition和isFactoryBean。
2 getMergedLocalBeanDefinition
首先getMergedLocalBeanDefinition这个方法的名字,就比较有意思。
为什么不直接取名getBeanDefinition,而取名getMergedLocalBeanDefinition呢?
很显然这个方法,不是简单的通过beanName获取BeanDefinition对象。
先从缓存mergedBeanDefinitions中根据beanName获取BeanDefinition对象,如果能够获取到,则直接返回。
如果缓存中为空,则调用重载的getMergedBeanDefinition方法,将通过beanName获取到的BeanDefinition和beanName合并。
听上去是不是有点懵?
假如有这样的场景:
@Data
publicclassPeople{
privateintage=25;
privateStringname="苏三";
}
@Data
publicclassStudentextendsPeople{
privateintscore=90;
}这里有People和Student两个类,其中Student类是People类的子类,这样Student就可以将People的所有属性,这里的age和name,继承过来了。
对应到BeanDefinition,需要把父类中的属性合并过来。
getMergedBeanDefinition调用了重载getMergedBeanDefinition方法:
这个方法一上来,就是用synchronized关键字,锁定了mergedBeanDefinitions对象,说白了是为了防止并发的场景下,有多个线程同时执行下面的代码块,导致merge的属性有问题。
由于containingBd是null,所以会先从缓存mergedBeanDefinitions中获取一下,如果缓存中获取到了,则直接返回。
如果缓存中获取不到对象,则判断一下parentName是否为空,如果为空,则判断对象是否为RootBeanDefinition类型,如果是则拷贝BeanDefinition,如果对象不是RootBeanDefinition类型,则创建一个RootBeanDefinition类型的对象。
如果parentName不为空,则先获取parentBeanName,判断parentBeanName跟当前beanName是否相等,如果不相等,则获取parentBeanName合并后的BeanDefinition。
如果name相等,则获取parentBeanFactory对象,如果该对象是ConfigurableBeanFactory,则获取parentBeanFactory对象的parentBeanName合并后的BeanDefinition。
3 isFactoryBean
再一起看看isFactoryBean方法,这个方法也挺有意思的,它主要是用来判断指定的name,是不是FactoryBean。
先调用transformedBeanName方法,将name转换成beanName,然后调用getSingleton方法,从三级缓存中获取bean实例。
如果从缓存中可以获取到bean实例beanInstance对象,直接判断一下beanInstance对象的类型,是不是FactoryBean,然后直接返回。
如果beanInstance对象为空,则判断如果parentBeanFactory是ConfigurableBeanFactory类型,则调用parentBeanFactory的isFactoryBean方法判断是不是FactoryBean。
如果beanInstance对象为空,并且parentBeanFactory不是ConfigurableBeanFactory类型,则获取合并之后的RootBeanDefinition,调用重载的isFactoryBean方法。
这个方法中,先判断mbd.isFactoryBean是空,则获取该beanName对应的Class对象,最后判断Class对象的类型是不是FactoryBean类型。
为什么Spring这么纠结,要花这么大的力气,去判断当前beanName对象是不是FactoryBean呢?
后面会有一篇专题文章。
4 afterSingletonsInstantiated
最后,回到preInstantiateSingletons方法中,其中有这样一段代码:
说白了,就是遍历所有的beanName,如果bean是单例的,并且实现了SmartInitializingSingleton接口,则会调用它的preInstantiateSingletons方法。
在这个方法中,可以做一些初始化的工作。
我们所知道的在bean中初始化的方法有:
- 在方法上使用@PostConstruct注解。
- 实现InitializingBean接口,重写afterPropertiesSet方法。
这里告诉大家一种新的方式,实现SmartInitializingSingleton接口,重写preInstantiateSingletons方法。
前面两种方式,Spring是在bean实例化完成,bean的初始化过程当中调用的。
而SmartInitializingSingleton接口这种方式,是在bean初始化完成之后,任然给大家提供了一个初始化的机会。
有些业务场景,通过前面方式初始化数据是有问题的。
关于bean的初始化,后面会有一篇专栏。
好了,今天的内容分享到这里,我们下期再见。