1. 程式人生 > >SpringFramework的核心:IOC容器的實現------ApplicationContext的特點以及Bean的Resource定位

SpringFramework的核心:IOC容器的實現------ApplicationContext的特點以及Bean的Resource定位

 

上次我解釋了關於BeanFactory的一些功能。那麼以BeanFactory的一個實現類為例,我們來看一下BeanFactory的工作原理。

private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
public XmlBeanFactory(Resource resource) throws BeansException {
		this(resource, null);
	}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
		super(parentBeanFactory);
		this.reader.loadBeanDefinitions(resource);
	}

這是關於XmlBeanFactory的構造方法,從這裡我們可以看到,我們需要自己定義Resource資源來建立這樣一個IOC容器。總體而言這樣的構造方法我們可以用程式設計的方式來宣告出來

                ClassPathResource res = new ClassPathResource("beam.xml");
		DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
		XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
		reader.loadBeanDefinitions(res);

在使用IOC容器之前我們需要以下幾個步驟

   1.建立IOC容器配置檔案的抽象資源,這個抽象資源包括了BeanDefinition的定義資訊

   2.建立一個BeanFactory,這裡使用的是DefaultListableBeanFactory

   3.建立一個BeanDefinition的讀取器,並設定入參factory(對應的DefaultListableBeanFactory)表示讀取到的bean都要最終註冊到這個factory中

   4..這個時候使用讀取器來讀取我們的resource資源並最終將resource註冊到factory

接下來來說一下關於ApplicationContext的特點

   

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
		MessageSource, ApplicationEventPublisher, ResourcePatternResolver

我們可以看到實際上ApplicationContext也是繼承了BeanFactory的一些實現類,因此我們可以認為ApplicationContext也是一個BeanFactory,但是它有一些自己的特點。

     1.支援不同的資訊源。

     2.訪問資源,體現在對ResourceLoader和Resource的支援上,這樣我們可以從不同地方法得到Bean資源。

     3.支援應用事件。繼承了介面ApplicationEventPublisher,這樣在上下文中引入了事件機制。

     4.在ApplicationContext中提供的附加服務。這些服務使得基本IOC容器的功能更豐富。

接下來我們來看一下關於IOC容器的初始化

   IOC容器的初始化包括BeanDefinition的Resource定位,載入和註冊三個基本過程。現在我主要來講一下關於BeanDefinition的Resource定位過程。

   我們以org.springframework.context.support.FileSystemXmlApplicationContext為例

首先來看這個類的繼承關係圖

首先我們來看這個類的構造方法

    

public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
			throws BeansException {

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

這個方法如果有父類容器,我們設定載入父類的容器,呼叫父類的有參構造方法。

之後setConfigLocations(configLocations);設定我們的xml檔案的地址,這個方法是在一個配置載入類中

org.springframework.context.support.AbstractRefreshableConfigApplicationContext.setConfigLocations(String...)。呼叫的是父類的方法。之後它要進行容器的更新呼叫的方法是

org.springframework.context.support.AbstractApplicationContext.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();

			// 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) {
				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();
			}
		}
	}

這個方法實現了BeanDefinition的Resource定位,載入和註冊。首先來看關於Resource的定位,在

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

程式碼中的obtainFreshFactory()方法中,我們可以看到refreshBeanFactory()方法,但是這個方法是一個抽象方法,需要它的子類來實現,這個方法實現是在

org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory()中進行實現

在這個方法中,有這樣的一行程式碼,將BeanDefinition載入到factory中

loadBeanDefinitions(beanFactory);

由於這個方法是一個抽象方法,我們來看這個AbstractRefreshableApplicationContext的子類

org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(DefaultListableBeanFactory)對方法的具體的實現

  

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(this.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);
	}

我們可以看到這個方法裡面有兩行程式碼和我們前面的程式設計式初始化IOC容器很相近

XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
loadBeanDefinitions(beanDefinitionReader);

這個地方是使用讀取器來讀取beanDefinition,但是BeanDefinition的讀取方式有兩種,一種是根據configLocations讀取,另一種是根據resources進行讀取,對於FileSystemXmlApplicationContext類來說,很顯然由於構造方法裡面的

setConfigLocations(configLocations);

這樣一條語句,所以是根據configLocations,也就是路徑獲取xml檔案來構造beanfinition。

 

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
		Resource[] configResources = getConfigResources();
		if (configResources != null) {
			reader.loadBeanDefinitions(configResources);
		}
		String[] configLocations = getConfigLocations();
		if (configLocations != null) {
			reader.loadBeanDefinitions(configLocations);
		}
	}

之後我們再回到最開始的配置載入類中查詢我們之前配置好的configLocations

org.springframework.context.support.AbstractRefreshableConfigApplicationContext.getConfigLocations()

protected String[] getConfigLocations() {
		return (this.configLocations != null ? this.configLocations : getDefaultConfigLocations());
	}

這就是關於BeanDefinition的Resource資源定位,關於載入和註冊我將會在之後的閱讀學習中繼續談到。