1. 程式人生 > >Spring原始碼解析之四(容器的功能擴充套件)

Spring原始碼解析之四(容器的功能擴充套件)

容器的功能擴充套件

1、概述

之前的文章中BeanFactory介面以及它的預設實現類XmlBeanFactory為例進行分析,但是Spring中還提供了另一個介面ApplicationContext,用於擴充套件BeanFactory中現有的功能。 
ApplicationContext和BeanFactory兩者都是用於載入Bean的,但是相比之下,ApplicationContext提供了更多的擴充套件功能,簡而言之:ApplicationContext包含BeanFactory的所有功能。通常建議比優先使用ApplicationContext
我們就以ClassPathXmlApplicationContext作為切入點,開始對整體功能進行分析,程式碼如下:

/**
	 * Create a new ClassPathXmlApplicationContext with the given parent,
	 * loading the definitions from the given XML files.
	 * @param configLocations array of resource locations
	 * @param refresh whether to automatically refresh the context,
	 * loading all bean definitions and creating all singletons.
	 * Alternatively, call refresh manually after further configuring the context.
	 * @param parent the parent context
	 * @throws BeansException if context creation failed
	 * @see #refresh()
	 */
	public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
			throws BeansException {

		super(parent);
		setConfigLocations(configLocations);
		if (refresh) {
			refresh();
		}
	}

2、設定路徑( setConfigLocations(configLocations);)

/**
	 * Set the config locations for this application context.
	 * <p>If not set, the implementation may use a default as appropriate.
	 */
	public void setConfigLocations(String... locations) {
		if (locations != null) {
			Assert.noNullElements(locations, "Config locations must not be null");
			this.configLocations = new String[locations.length];
			for (int i = 0; i < locations.length; i++) {
				this.configLocations[i] = resolvePath(locations[i]).trim();
			}
		}
		else {
			this.configLocations = null;
		}
	}

3、擴充套件功能refresh();

refresh函式中包含了幾乎ApplicationContext中提供的全部功能.

@Override
	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();

			// 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);

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

			catch (BeansException ex) {
				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;
			}
		}
	}

我們簡單的分析下程式碼的步驟: 
(1)初始化前的準備工作,例如對系統屬性或者環境變數進行準備及驗證。 
在某種情況下專案的使用需要讀取某些系統變數,而這個變數的設定很可能會影響著系統的正確性,那麼ClassPathXmlApplicationContext為我們提供的這個準備函式就顯得非常必要,他可以在spring啟動的時候提前對必須的環境變數進行存在性驗證。 
(2)初始化BeanFactory,並進行XML檔案讀取。 
之前提到ClassPathXmlApplicationContext包含著對BeanFactory所提供的一切特徵,那麼這一步中將會複用BeanFactory中的配置檔案讀取解析其他功能,這一步之後ClassPathXmlApplicationContext實際上就已經包含了BeanFactory所提供的功能,也就是可以進行Bean的提取等基本操作了。 
(3)對BeanFactory進行各種功能填充 
@Qualifier和@Autowired應該是大家非常熟悉的註解了,那麼這兩個註解正是在這一步驟中增加支援的。 
(4)子類覆蓋方法做額外處理。 
(5)啟用各種BeanFactory處理器 
(6)註冊攔截bean建立的bean處理器,這裡只是註冊,真正的呼叫是在getBean時候 
(7)為上下文初始化Message源 
(8)初始化應用訊息廣播器,並放入“applicationEventMulticaster”bean中 
(9)留給子類來初始化其他的bean 
(10)在所有註冊的bean中查詢listener bean,註冊到訊息廣播器中 
(11)初始化剩下的單例項(非惰性的) 
(12)完成重新整理過程,通知生命週期處理器lifecycleProcessor重新整理過程,同時發出ContextRefreshEvent通知別人。 
接下來我們就詳細的講解每一個過程:

4、環境準備prepareRefresh();

/**
	 * Prepare this context for refreshing, setting its startup date and
	 * active flag as well as performing any initialization of property sources.
	 */
	protected void prepareRefresh() {
		this.startupDate = System.currentTimeMillis();
		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();
	}

   (1) initPropertySources,這正符合Spring的開放式結構設計,給使用者最大擴充套件Spring的能力。使用者可以根據自身的需要重寫initPropertySourece方法,並在方法中進行個性化的屬性處理及設定。 
(2)validateRequiredProperties則是對屬性進行驗證
5、載入BeanFactory()(obtainFreshBeanFactory()方法)

/**
	 * Tell the subclass to refresh the internal bean factory.
	 * @return the fresh BeanFactory instance
	 * @see #refreshBeanFactory()
	 * @see #getBeanFactory()
	 */
	protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
		refreshBeanFactory();
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
		if (logger.isDebugEnabled()) {
			logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
		}
		return beanFactory;
	}

核心在refreshBeanFactory方法中,方法的實現是在AbstractRefreshableApplicationContext中:


	/**
	 * This implementation performs an actual refresh of this context's underlying
	 * bean factory, shutting down the previous bean factory (if any) and
	 * initializing a fresh bean factory for the next phase of the context's lifecycle.
	 */
	@Override
	protected final void refreshBeanFactory() throws BeansException {
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());
			customizeBeanFactory(beanFactory);
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}

我們詳細分析下上面程式碼的步驟: 
(1)建立DefaultListableBeanFactory 
以前我們分析BeanFactory的時候,不知道是否還有印象,宣告方式為:BeanFactory bf = new XmlBeanFactory(“beanFactoryTest.xml”),其中的XmlBeanFactory繼承自DefaulltListableBeanFactory;,並提供了XmlBeanDefinitionReader型別的reader屬性,也就是說DefaultListableBeanFactory是容器的基礎。必須首先要例項化,這裡就是建立DefaultListableBeanFactory的步驟。 
(2)指定序列號ID 
(3)定製BeanFactory 
(4)載入BeanDefinition 
(5)使用全域性變數儲存BeanFactory 
因為DefaultListableBeanFactory型別的變數beanFactory是函式內部的區域性變數, 所以要使用全域性變數記錄解析結果

5.1 定製BeanFactory(通過customizeBeanFactory(beanFactory);)

/**
	 * Customize the internal bean factory used by this context.
	 * Called for each {@link #refresh()} attempt.
	 * <p>The default implementation applies this context's
	 * {@linkplain #setAllowBeanDefinitionOverriding "allowBeanDefinitionOverriding"}
	 * and {@linkplain #setAllowCircularReferences "allowCircularReferences"} settings,
	 * if specified. Can be overridden in subclasses to customize any of
	 * {@link DefaultListableBeanFactory}'s settings.
	 * @param beanFactory the newly created bean factory for this context
	 * @see DefaultListableBeanFactory#setAllowBeanDefinitionOverriding
	 * @see DefaultListableBeanFactory#setAllowCircularReferences
	 * @see DefaultListableBeanFactory#setAllowRawInjectionDespiteWrapping
	 * @see DefaultListableBeanFactory#setAllowEagerClassLoading
	 */
	protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
		if (this.allowBeanDefinitionOverriding != null) {
			beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
		if (this.allowCircularReferences != null) {
			beanFactory.setAllowCircularReferences(this.allowCircularReferences);
		}
	}

5.2 載入BeanFactory(通過loadBeanDefinitions(beanFactory);實現)

/**
	 * Loads the bean definitions via an XmlBeanDefinitionReader.
	 * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
	 * @see #initBeanDefinitionReader
	 * @see #loadBeanDefinitions
	 */
	@Override
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		// Create a new XmlBeanDefinitionReader for the given BeanFactory.
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

		// Configure the bean definition reader with this context's
		// resource loading environment.
		beanDefinitionReader.setEnvironment(getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

		// Allow a subclass to provide custom initialization of the reader,
		// then proceed with actually loading the bean definitions.
		initBeanDefinitionReader(beanDefinitionReader);
		loadBeanDefinitions(beanDefinitionReader);
	}

除了初始化DefaultListableBeanFactory外,還需要XmlBeanDefinitionReader來讀取XML,那麼在loadBeanDefinitions方法中首先要做的就是初始化XmlBeanDefinitonReader。在初始化了DefaultListableBeanFactory和XmlBeanDefinitionReader後,就可以進行配置檔案的讀取了。繼續進入到loadBeanDefinitions(beanDefinitionReader)方法體中,程式碼如下:

/**
	 * Load the bean definitions with the given XmlBeanDefinitionReader.
	 * <p>The lifecycle of the bean factory is handled by the refreshBeanFactory method;
	 * therefore this method is just supposed to load and/or register bean definitions.
	 * <p>Delegates to a ResourcePatternResolver for resolving location patterns
	 * into Resource instances.
	 * @throws IOException if the required XML document isn't found
	 * @see #refreshBeanFactory
	 * @see #getConfigLocations
	 * @see #getResources
	 * @see #getResourcePatternResolver
	 */
	protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
		String[] configLocations = getConfigLocations();
		if (configLocations != null) {
			for (String configLocation : configLocations) {
				reader.loadBeanDefinitions(configLocation);
			}
		}
	}

因為在XmlBeanDefinitionReader中已經將之前初始化的DefaultListableBeanFactory註冊進去了,所以XmlBeanDefinitionReader所讀取的BeanDefinitionHolder都會註冊到DefinitionListableBeanFactory中,也就是經過這個步驟,DefaultListableBeanFactory的變數beanFactory已經包含了所有解析好的配置。

6、功能擴充套件

// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);
/**
	 * Configure the factory's standard context characteristics,
	 * such as the context's ClassLoader and post-processors.
	 * @param beanFactory the BeanFactory to configure
	 */
	protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		// Tell the internal bean factory to use the context's class loader etc.
		beanFactory.setBeanClassLoader(getClassLoader());
		beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
		beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

		// Configure the bean factory with context callbacks.
		beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
		beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
		beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
		beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
		beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
		beanFactory.ignoreDependencyInterface(EnvironmentAware.class);

		// BeanFactory interface not registered as resolvable type in a plain factory.
		// MessageSource registered (and found for autowiring) as a bean.
		beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
		beanFactory.registerResolvableDependency(ResourceLoader.class, this);
		beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
		beanFactory.registerResolvableDependency(ApplicationContext.class, this);

		// Detect a LoadTimeWeaver and prepare for weaving, if found.
		if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
			beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
			// Set a temporary ClassLoader for type matching.
			beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
		}

		// Register default environment beans.
		if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
			beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
		}
		if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
			beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
		}
		if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
			beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
		}
	}

詳細分析下程式碼發現上面函式主要是在以下方法進行了擴充套件: 
(1)對SPEL語言的支援 
(2)增加對屬性編輯器的支援 
(3)增加對一些內建類的支援,如EnvironmentAware、MessageSourceAware的注入 
(4)設定了依賴功能可忽略的介面 
(5)註冊一些固定依賴的屬性 
(6)增加了AspectJ的支援 
(7)將相關環境變數及屬性以單例模式註冊 
接下來分析下以上步驟。

6.1 增加對SPEL語言的支援

beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));

6.2 增加屬性編輯器

beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

7、設定忽略依賴

// Configure the bean factory with context callbacks.
		beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
		beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
		beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
		beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
		beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
		beanFactory.ignoreDependencyInterface(EnvironmentAware.class);

8、註冊依賴

// BeanFactory interface not registered as resolvable type in a plain factory.
		// MessageSource registered (and found for autowiring) as a bean.
		beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
		beanFactory.registerResolvableDependency(ResourceLoader.class, this);
		beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
		beanFactory.registerResolvableDependency(ApplicationContext.class, this);

9、BeanFactory的後處理