一、介紹
標題中強調指出ApplicationContext,是因爲使用Spring框架有兩種方式:BeanFactory和ApplicationContext,代碼如下:
// beanFactory方式
@Test
public void beanFactoryTest() {
Resource classPathResource = new ClassPathResource("applicationContext.xml");
BeanFactory xmlBeanFactory = new XmlBeanFactory(classPathResource);
}
// ApplicationContext方式
@Test
public void awareTest() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
"applicationContext.xml");
}
它們的區別是:BeanFactory在初始化的時候,不會加載任何bean實例;而ApplicationContext初始化的時候,會提前加載xml中配置的所有單例bean。
我們本篇的目的,是爲了探究ApplicationContext初始化的時候,在哪個步驟加載這些單例bean的。
二、源碼分析
1. ApplicationContext初始化入口
以下是ApplicationContext初始化的經典入口代碼:
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 1. 準備刷新的上下文環境
prepareRefresh();
// 2. 初始化BeanFactory,並進行XML文件的加載
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 3. 對BeanFactory進行各種功能填充
prepareBeanFactory(beanFactory);
try {
// 4. 子類覆蓋犯法做額外的處理
postProcessBeanFactory(beanFactory);
// 5. 調用BeanFactory後處理器
invokeBeanFactoryPostProcessors(beanFactory);
// 6. 註冊bean後處理器,在調用getBean的時候回調這些bean後處理器的方法
registerBeanPostProcessors(beanFactory);
// 7. 爲上下文初始化Message源
initMessageSource();
// 8. 初始化事件多播器
initApplicationEventMulticaster();
// 9. 留給子類初始化其他bean
onRefresh();
// 10. 註冊監聽器
registerListeners();
// 11. 初始化剩下的單例Bean(非惰性的)
finishBeanFactoryInitialization(beanFactory);
// 12. 最後一步,發佈通知事件
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// 銷燬已經創建的單例,以避免掛起資源。
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
與本文相關的代碼在第11步,加載剩下的單例Bean。我們由此入口來開始分析~
2. 完成BeanFactory實例化
進入到AbstractApplicationContext類的finishBeanFactoryInitialization方法
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// 1.初始化此上下文的ConversionService
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.初始化LoadTimeWeaverAware
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
// 4.停止使用臨時類加載器進行類型匹配
beanFactory.setTempClassLoader(null);
// 5.允許緩存所有bean定義元數據,而不期望有進一步的更改。
beanFactory.freezeConfiguration();
// 6.實例化所有剩餘的(非lazy-init)單例。
beanFactory.preInstantiateSingletons();
}
前面幾個步驟看不懂沒關係,我們重點在第6步,實例化所有非懶加載的單例Bean
懶加載的意思就是,容器啓動時要不要加載某個bean,可以通過bean標籤中的lazy-init屬性配置,默認值爲false,即非懶加載(容器啓動就加載該bean)
<!-- 設置爲懶加載,容器啓動時不會實例化這個Bean -->
<bean id="person" class="com.kaka.spring.beans.Person" lazy-init="true"/>
3. 加載所有非懶加載的單例Bean
public void preInstantiateSingletons() throws BeansException {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Pre-instantiating singletons in " + this);
}
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// 1. 實例化所有非懶加載的單例Bean
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
// 1.1 FactoryBean類型的bean
if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
final FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
((SmartFactoryBean<?>) factory)::isEagerInit,
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
}
else {
// 1.2 其他類型的bean
getBean(beanName);
}
}
}
// 2. 回調SmartInitializingSingleton類型Bean的afterSingletonsInstantiated方法
for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
smartSingleton.afterSingletonsInstantiated();
return null;
}, getAccessControlContext());
}
else {
smartSingleton.afterSingletonsInstantiated();
}
}
}
}
以上代碼來自於DefaultListableBeanFactory類的preInstantiateSingletons方法。看着代碼量挺多,其實就幹了兩件事兒:
- 實例化所有非懶加載的Bean
- 回調SmartInitializingSingleton類型Bean的afterSingletonsInstantiated方法
實例化bean的邏輯都在上面的getBean方法中,裏面無非是創建了一個Bean實例、給Bean實例填充屬性信息、調用bean實例的初始化方法等等,這些不是本章討論的重點。
三、收穫
其實到這兒就結束了,從中我們可以瞭解到以下幾點
- 實例化所有非懶加載Bean的入口在:AbstractApplicationContext類的finishBeanFactoryInitialization方法,也就是容器初始化的第11步(倒數第2步)。
如果你遇到有些你配置的Bean沒有在容器中,可以從這個入口進行排查~ - 所有需要加載的beanName都保存在:DefaultListableBeanFactory類的beanDefinitionNames屬性中
/** List of bean definition names, in registration order */
private volatile List<String> beanDefinitionNames = new ArrayList<>(256);
- 如果想要等所有Bean都初始化完成之後執行一些操作,可以新建一個類實現SmartInitializingSingleton接口的afterSingletonsInstantiated方法,把這些步驟寫在這個方法中。並把這個類配置成一個Bean
因爲我們從源碼中也可以看到,所有Bean循環實例化之後,又循環所有Bean實例挑出SmartInitializingSingleton類型的Bean回調這些Bean實例的afterSingletonsInstantiated方法。
@Component
public class MySmartInitializingSingleton implements SmartInitializingSingleton {
@Override
public void afterSingletonsInstantiated() {
System.out.println("所有的bean都初始化完成了...");
}
}