Spring源碼:ApplicationContext之提前實例化單例Bean

一、介紹

  標題中強調指出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實例的初始化方法等等,這些不是本章討論的重點。

三、收穫

其實到這兒就結束了,從中我們可以瞭解到以下幾點

  1. 實例化所有非懶加載Bean的入口在:AbstractApplicationContext類的finishBeanFactoryInitialization方法,也就是容器初始化的第11步(倒數第2步)。
    如果你遇到有些你配置的Bean沒有在容器中,可以從這個入口進行排查~
  2. 所有需要加載的beanName都保存在:DefaultListableBeanFactory類的beanDefinitionNames屬性中
/** List of bean definition names, in registration order */
private volatile List<String> beanDefinitionNames = new ArrayList<>(256);
  1. 如果想要等所有Bean都初始化完成之後執行一些操作,可以新建一個類實現SmartInitializingSingleton接口的afterSingletonsInstantiated方法,把這些步驟寫在這個方法中。並把這個類配置成一個Bean
    因爲我們從源碼中也可以看到,所有Bean循環實例化之後,又循環所有Bean實例挑出SmartInitializingSingleton類型的Bean回調這些Bean實例的afterSingletonsInstantiated方法
@Component
public class MySmartInitializingSingleton implements SmartInitializingSingleton {
    @Override
    public void afterSingletonsInstantiated() {
        System.out.println("所有的bean都初始化完成了...");
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章