天天看點

Spring源碼解析之零 ------ 容器初始化過程(refresh()方法)概要

Spring的核心功能就是IOC和AOP。

IOC分兩個過程:bean的解析注冊 和 bean的執行個體化。

AOP是面向切面程式設計,但是它也離不開bean的解析注冊。

本篇主要講解,容器初始化時候的refresh()方法裡的,幾個重要方法的基本作用。

首先來看一下refresh()方法。spring容器的啟動,建立bean,bean的初始化等一系列過程都在這個refresh方法裡面,進行調用。接下來,對每個方法的作用做一個簡要的說明。

public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();//bean的解析注冊

			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				initMessageSource();

				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);//初始化非懶加載的bean

				// Last step: publish corresponding event.
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				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();
			}
		}
	}
           

1.prepareRefresh()方法

設定spring上下文的重新整理時間,并将active設為true,初始化一些容器啟動必要的資源。我們可以看到,這個方法裡面是打了日志的,在我debug的時候,我的日志列印如下:

[INFO][main][2017-07-03 11:56:40][org.springframework.context.support.AbstractApplicationContext] - Refreshing org[email protected]7fac631b: startup date [Mon Jul 03 11:56:40 CST 2017]; root of context hierarchy

protected void prepareRefresh() {
		this.startupDate = System.currentTimeMillis();
		this.closed.set(false);
		this.active.set(true);

		if (logger.isInfoEnabled()) {
			logger.info("Refreshing " + this);
		}

		// Initialize any placeholder property sources in the context environment
		initPropertySources();

		// Validate that all properties marked as required are resolvable
		// see ConfigurablePropertyResolver#setRequiredProperties
		getEnvironment().validateRequiredProperties();

		// Allow for the collection of early ApplicationEvents,
		// to be published once the multicaster is available...
		this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
	}
           

2.invokeBeanFactoryPostProcessors()

beanFactoryPostprocessor的作用是在beanFactory初始化之後提供一個修改的機會。spring已經提供了不少實作,我們自己也可以寫一些實作配置在xml中 或者手動調用。

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
		PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
	}
           

對于它的講解,另外的兩個部落格,我覺得很好

http://www.jianshu.com/p/0e7f65afa156

http://blog.csdn.net/lsm135/article/details/53300912

3.obtainFreshBeanFactory()方法

obtainFreshBeanFactory()方法,是進行bean的解析注冊的地方。所謂bean的解析注冊就是指 将xml中配置的bean,轉化為Java對象的BeanDefinition,并将它們儲存在容器的map裡。傳回的beanFactory裡面,就攜帶着存放beanName和BeanDefinition的map。

在這個方法執行完畢後,BeanDefinitionMap裡面就存放着beanName和beanDefinition的對應資訊。而這個時候,存放beanName和執行個體化bean對象的singletonObjections仍然為空。

Spring源碼解析之零 ------ 容器初始化過程(refresh()方法)概要

這個方法對應着1,2

4.finishBeanInitialization(beanFactory)

這個方法裡面,用來初始化非懶加載的bean。并非所有bean都在容器啟動的時候執行個體化。在xml中配置bean的時候,有個lazy-ini屬性,預設為false。是以預設情況下,單例的非懶加載的bean在容器啟動的時候會執行個體化。如果是懶加載的,那麼在getBean的時候,再執行個體化。

具體來說:

Spring什麼時候執行個體化bean,首先要分2種情況 

  第一:如果你使用BeanFactory作為Spring Bean的工廠類,則所有的bean都是在第一次使用該Bean的時候執行個體化 

  第二:如果你使用ApplicationContext作為Spring Bean的工廠類,則又分為以下幾種情況: 

       (1):如果bean的scope是singleton的,并且lazy-init為false(預設是false,是以可以不用設定),則ApplicationContext啟動的時候就執行個體化該Bean,并且将執行個體化的Bean放在一個map結構的緩存中,下次再使用該Bean的時候,直接從這個緩存中取 

       (2):如果bean的scope是singleton的,并且lazy-init為true,則該Bean的執行個體化是在第一次使用該Bean的時候進行執行個體化 

       (3):如果bean的scope是prototype的,則該Bean的執行個體化是在第一次使用該Bean的時候進行執行個體化 

在我工程的Demo裡,是通過ClassPathXmlApplicationContext擷取xml的。是以屬于通過ApplicationContext擷取bean,它的預設BeanFactory是DefaultListableBeanFactory。

最後通過這個圖,可以大概了解整個流程:

Spring源碼解析之零 ------ 容器初始化過程(refresh()方法)概要