天天看點

Spring源碼學習10

1.ApplicationContext ac = new ClassPathXmlApplicationContext("anComplexBeanTest.xml")

/**
	 * 使用給定父級建立新的ClassPathXmlApplicationContext,
	 * 從給定的XML檔案加載定義
	 * @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);
		//設定帶讀取xml配置檔案的位置
		setConfigLocations(configLocations);
		if (refresh) {
			refresh();
		}
	}
           

2.refresh()方法實作在AbstractApplicationContext類,我們先從字面意思了解各個方法,接下來再去深入分析每個方法做了什麼事情

@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			//準備此上下文以進行重新整理,其實無非就是設定啟動時間,是否活動狀态
			prepareRefresh();

			// 告訴子類重新整理内部bean工廠。
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// 準備bean工廠以在此上下文中使用
			prepareBeanFactory(beanFactory);

			try {
				//TODO 擴充點 允許在上下文子類中對bean工廠進行後處理。
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				// 調用注冊的BeanFactoryPostProcessor的BeanFactoryPostProcessor,
				// 比如設定propertyEditor到BeanFactory中
				invokeBeanFactoryPostProcessors(beanFactory);

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

				// Initialize message source for this context.
				// 為上下文初始化messagesource
				initMessageSource();

				// Initialize event multicaster for this context.
				// 初始化應用消息廣播
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				// TODO 留給子類初始化其他bean
				onRefresh();

				// Check for listener beans and register them.
				// 檢查并注冊listener beans
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				// 初始化剩下的(非懶惰的)singletons
				finishBeanFactoryInitialization(beanFactory);

				// 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.
				// 銷毀已建立的單例bean避免占用資源
				destroyBeans();

				// Reset 'active' flag.
				// 取消重新整理,重置active 标志
				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...
				//從我們開始,重置Spring核心中的常見内省緩存
				//可能再也不需要單例bean的中繼資料......
				resetCommonCaches();
			}
		}
	}
           

3.首先是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() {
		// Switch to active.
		//設定啟動日期
		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.
		//TODO 擴充點 初始化在上下文中的任何占位符資源
		initPropertySources();

		// Validate that all properties marked as required are resolvable:
		// see ConfigurablePropertyResolver#setRequiredProperties
		//驗證标記為必需的所有屬性是否可解析
		//請參閱ConfigurablePropertyResolver#setRequiredProperties
		//TODO 擴充點留給我們去實作,比如驗證我們所需要的某一個環境是否存在
		getEnvironment().validateRequiredProperties();

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

沒有太多的邏輯,主要是設定啟動日期,colsed,active狀态,并且預置了兩個擴充點,用于加載PropertyResources和校驗資源

4.obtainFreshBeanFactory()方法,擷取beanFactory工廠

/**
	 * 告訴子類重新整理内部bean工廠
	 * @return the fresh BeanFactory instance
	 * @see #refreshBeanFactory()
	 * @see #getBeanFactory()
	 */
	protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
		//初始化beanFactory,對XML檔案讀取,并beanFactory指派給beanFactory
		refreshBeanFactory();
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
		if (logger.isDebugEnabled()) {
			logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
		}
		return beanFactory;
	}
           

我們來看下refreshBeanFactory()在AbstractApplicationContext子類

/**
	 *此實作執行此上下文的基礎bean工廠的實際重新整理,
	 * 關閉先前的bean工廠(如果有)并初始化上一個生命周期的下一階段的新bean工廠
	 */
	@Override
	protected final void refreshBeanFactory() throws BeansException {
		//如果beanFactory不為null
		if (hasBeanFactory()) {
			//銷毀beanFactory中的singeton
			destroyBeans();
			//設定beanFactory為null
			closeBeanFactory();
		}
		try {
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			//指定可序列化的id,可以從該id反序列化出來beanFactory對象
			beanFactory.setSerializationId(getId());
			//TODO 擴充點重名bean定義配置和,循環引用配置,可以根據需求自定義beanFactory設定
			customizeBeanFactory(beanFactory);
			//讀取bean定義到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);
		}
	}
           
  • a.判斷工廠是否為null,不為null銷毀beans(),關閉工廠
  • b.使用過構造方法建立一個工廠,new DefaultListableBeanFactory(getInternalParentBeanFactory());并且會擷取AbstractApplicationContext的内部beanFactory作為new出來工廠的父beanFactory
  • c.然後Spring給我們預留了一個個性化beanFactory的擴充點customizeBeanFactory()
  • d.然後加載beanDefinitions交給子類AbstractXmlApplicationContext.loadBeanDefinitions
/**
	 * 通過XmlBeanDefinitionReader加載bean定義。
	 * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
	 * @see #initBeanDefinitionReader
	 * @see #loadBeanDefinitions
	 */
	@Override
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		// 為給定的BeanFactory建立一個新的XmlBeanDefinitionReader。
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

		// Configure the bean definition reader with this context's
		// resource loading environment.
		// 對beanDefinitonReader設定環境變量
		beanDefinitionReader.setEnvironment(this.getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
		// EntityResolver:用于解決從網絡上下載下傳xml驗證慢導緻啟動慢,使Spring支援從本地的查找本地的xx.dtd
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

		// Allow a subclass to provide custom initialization of the reader,
		// then proceed with actually loading the bean definitions.
		// TODO 允許子類提供讀者的自定義初始化,
        //允許子類提供讀者的自定義初始化,
		然後繼續實際加載bean定義。
		initBeanDefinitionReader(beanDefinitionReader);
		//然後繼續實際加載bean定義
		loadBeanDefinitions(beanDefinitionReader);
	}
           
  • e.對于loadBeanDefinitions方法中Spring也給我們預留了相應的擴充點initBeanDefinitionReader(beanDefinitionReader);我們可以實作該類
  • 定制化字迹的beanDefinitionReader
  • f.最後是loadBeanDefinitions

obtainFactory方法,幫我們建立了beanFactory,并且使用XmlBeanDefinitionReader幫我們加載讀取了beanDefintion到beanDefinitionRegistry中。

5.beanFactory已經建立了,為了實作上下文的功能,我們需要對工廠prepareBeanFactory(beanFactory);我們着重關注裡面的某些方法

beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));用于解析SPEL表達式#{}

//設定beanFactory的表達式語言處理,Spring3增加了表達式 語言支援預設可以使用#{bean.xxx}
//來調用相關屬性值
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
           

由于SPEL表達式是為了解析某個表達式并給bean屬性指派,不難想象到StandardBeanExpressionResolver在給bean屬性指派applyPropertyValues()的時候會用到

//解析value,比如value可能為map、list、runtimeBean,SPEL字元串類型
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
           
Spring源碼學習10

繼續深入最終會看到 使用beanExpressionResolver解析SPEL表達式

/**
	 * Evaluate the given String as contained in a bean definition,
	 * potentially resolving it as an expression.
	 *
	 * 評估bean定義中包含的給定String,可能将其解析為表達式
	 * @param value the value to check
	 * @param beanDefinition the bean definition that the value comes from
	 * @return the resolved value
	 * @see #setBeanExpressionResolver
	 */
	protected Object evaluateBeanDefinitionString(String value, BeanDefinition beanDefinition) {
		if (this.beanExpressionResolver == null) {
			return value;
		}
		Scope scope = (beanDefinition != null ? getRegisteredScope(beanDefinition.getScope()) : null);
		return this.beanExpressionResolver.evaluate(value, new BeanExpressionContext(this, scope));
	}
           

beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));用于為Spring增加預設的自定義屬性編輯器

/**
 * PropertyEditorRegistrar implementation that populates a given
 * {@link org.springframework.beans.PropertyEditorRegistry}
 * (typically a {@link org.springframework.beans.BeanWrapper} used for bean
 * creation within an {@link org.springframework.context.ApplicationContext})
 * with resource editors. Used by
 * {@link org.springframework.context.support.AbstractApplicationContext}.
 *
 * @author Juergen Hoeller
 * @author Chris Beams
 * @since 2.0
 */
public class ResourceEditorRegistrar implements PropertyEditorRegistrar {

	private static Class<?> pathClass;

	static {
		try {
			pathClass = ClassUtils.forName("java.nio.file.Path", ResourceEditorRegistrar.class.getClassLoader());
		}
		catch (ClassNotFoundException ex) {
			// Java 7 Path class not available
			pathClass = null;
		}
	}


	private final PropertyResolver propertyResolver;

	private final ResourceLoader resourceLoader;


	/**
	 * Create a new ResourceEditorRegistrar for the given {@link ResourceLoader}
	 * and {@link PropertyResolver}.
	 * @param resourceLoader the ResourceLoader (or ResourcePatternResolver)
	 * to create editors for (usually an ApplicationContext)
	 * @param propertyResolver the PropertyResolver (usually an Environment)
	 * @see org.springframework.core.env.Environment
	 * @see org.springframework.core.io.support.ResourcePatternResolver
	 * @see org.springframework.context.ApplicationContext
	 */
	public ResourceEditorRegistrar(ResourceLoader resourceLoader, PropertyResolver propertyResolver) {
		this.resourceLoader = resourceLoader;
		this.propertyResolver = propertyResolver;
	}


	/**
	 * Populate the given {@code registry} with the following resource editors:
	 * ResourceEditor, InputStreamEditor, InputSourceEditor, FileEditor, URLEditor,
	 * URIEditor, ClassEditor, ClassArrayEditor.
	 * <p>If this registrar has been configured with a {@link ResourcePatternResolver},
	 * a ResourceArrayPropertyEditor will be registered as well.
	 * @see org.springframework.core.io.ResourceEditor
	 * @see org.springframework.beans.propertyeditors.InputStreamEditor
	 * @see org.springframework.beans.propertyeditors.InputSourceEditor
	 * @see org.springframework.beans.propertyeditors.FileEditor
	 * @see org.springframework.beans.propertyeditors.URLEditor
	 * @see org.springframework.beans.propertyeditors.URIEditor
	 * @see org.springframework.beans.propertyeditors.ClassEditor
	 * @see org.springframework.beans.propertyeditors.ClassArrayEditor
	 * @see org.springframework.core.io.support.ResourceArrayPropertyEditor
	 */
	@Override
	public void registerCustomEditors(PropertyEditorRegistry registry) {
		ResourceEditor baseEditor = new ResourceEditor(this.resourceLoader, this.propertyResolver);
		doRegisterEditor(registry, Resource.class, baseEditor);
		doRegisterEditor(registry, ContextResource.class, baseEditor);
		doRegisterEditor(registry, InputStream.class, new InputStreamEditor(baseEditor));
		doRegisterEditor(registry, InputSource.class, new InputSourceEditor(baseEditor));
		doRegisterEditor(registry, File.class, new FileEditor(baseEditor));
		if (pathClass != null) {
			doRegisterEditor(registry, pathClass, new PathEditor(baseEditor));
		}
		doRegisterEditor(registry, Reader.class, new ReaderEditor(baseEditor));
		doRegisterEditor(registry, URL.class, new URLEditor(baseEditor));

		ClassLoader classLoader = this.resourceLoader.getClassLoader();
		doRegisterEditor(registry, URI.class, new URIEditor(classLoader));
		doRegisterEditor(registry, Class.class, new ClassEditor(classLoader));
		doRegisterEditor(registry, Class[].class, new ClassArrayEditor(classLoader));

		if (this.resourceLoader instanceof ResourcePatternResolver) {
			doRegisterEditor(registry, Resource[].class,
					new ResourceArrayPropertyEditor((ResourcePatternResolver) this.resourceLoader, this.propertyResolver));
		}
	}

	/**
	 * Override default editor, if possible (since that's what we really mean to do here);
	 * otherwise register as a custom editor.
	 */
	private void doRegisterEditor(PropertyEditorRegistry registry, Class<?> requiredType, PropertyEditor editor) {
		if (registry instanceof PropertyEditorRegistrySupport) {
			((PropertyEditorRegistrySupport) registry).overrideDefaultEditor(requiredType, editor);
		}
		else {
			registry.registerCustomEditor(requiredType, editor);
		}
	}

}
           

我們可以在Spring的AbstractBeanFactory.registerCustomEditors方法中可以找到調用

Spring源碼學習10

 new ApplicationContextAwareProcessor(this);對于我們實作EnvironmentAware/ApplicationContextAware/ResourceLoaderAware/MessageSourceAware接口的bean,ApplicationContextAwareProcessor幫轉我們将相應的Environment/ApplicationContext/ResourceLoader/MessageSource類型set注入進去,

既然可以通過實作xxxAware接口注入那麼,這些類型的接口依賴肯定不需要Spring注入了,是以有以下方法

Spring源碼學習10

接下裡就是支援aop實作的關鍵new LoadTimeWeaverAwareProcessor(beanFactory),這個PostProcessor實作的功能是将那些實作loadTimeWeaver的bean注入loadTimeWeaver,據說這是AOP實作的關鍵,後面在探讨

Spring源碼學習10

最後将系統預設的bean注冊到beanFactory

Spring源碼學習10

6. 擴充點:在标準初始化之後修改應用程式上下文的内部bean工廠。 将加載所有bean定義,但尚未執行個體化任何bean。這允許在某些ApplicationContext實作中注冊特殊的BeanPostProcessors等

//TODO 擴充點 允許在上下文子類中對bean工廠進行後處理。
//我們拿到了beanFactory想做什麼就做什麼了,但是不要
//先把bean建立出來
postProcessBeanFactory(beanFactory);
           

繼續閱讀