1. 程式人生 > >容器初始化之FileSystemXmlApplicationContext建構函式

容器初始化之FileSystemXmlApplicationContext建構函式

宅男Coder,沒有其他愛好,閒暇之餘抱著瞻仰的心態去閱讀一下Spring的原始碼,期許能收穫一支半解。要學習Spring的原始碼,第一步自然是下載和編譯Spring的原始碼,這個我在之前的博文中已經發表過了。具體可參考:《SpringFramework原始碼下載和編譯教程》

面對茫茫多的Spring的工程和程式碼,很多人可能會無從下手。其實想想,Spring也是有入口的,那就是配置檔案的載入。Spring容器的構建完全是基於配置檔案的配置的。不論是Web工程,還是普通的Java應用,載入Spring配置檔案都是首要的工作。所以,我就從配置檔案的載入學起。

要載入配置檔案,首先當然是要找到該檔案。大多數人通常都是在Web應用中使用Spring。網上搜搜配置,配置檔案的名字就叫約定的:applicationContext.xml,然後往編譯路徑下一扔,Spring自然就好用了,就沒過多的關注過其他容器初始化的問題。其實,一個自然應該想到的問題就是:一個普通的J2SE應用該如何使用Spring呢?答案很簡單:new 出一個ApplicationContext

的例項就好了。例如:

	private static final String SPRINT_FILEPATH_CONTEXT = "D:\\workspace-home\\OpenSourceStudy\\src\\main\\resources\\spring\\app-context.xml";          
	ApplicationContext  appContext = new FileSystemXmlApplicationContext(
				SPRINT_FILEPATH_CONTEXT);

只需上述一行程式碼,一個基於指定的配置檔案的Spring容器就初始化完成了。

下面,我們來仔細看看FileSystemXmlApplicationContext這個類:

	public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {
	/**
	 * Create a new FileSystemXmlApplicationContext for bean-style configuration.
	 * @see #setConfigLocation
	 * @see #setConfigLocations
	 * @see #afterPropertiesSet()
	 */
public FileSystemXmlApplicationContext() { } /** * Create a new FileSystemXmlApplicationContext for bean-style configuration. * @param parent the parent context * @see #setConfigLocation * @see #setConfigLocations * @see #afterPropertiesSet() */ public FileSystemXmlApplicationContext(ApplicationContext parent) { super(parent); } /** * Create a new FileSystemXmlApplicationContext, loading the definitions * from the given XML file and automatically refreshing the context. * @param configLocation file path * @throws BeansException if context creation failed */ public FileSystemXmlApplicationContext(String configLocation) throws BeansException { this(new String[] {configLocation}, true, null); } /** * Create a new FileSystemXmlApplicationContext, loading the definitions * from the given XML files and automatically refreshing the context. * @param configLocations array of file paths * @throws BeansException if context creation failed */ public FileSystemXmlApplicationContext(String... configLocations) throws BeansException { this(configLocations, true, null); } /** * Create a new FileSystemXmlApplicationContext with the given parent, * loading the definitions from the given XML files and automatically * refreshing the context. * @param configLocations array of file paths * @param parent the parent context * @throws BeansException if context creation failed */ public FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException { this(configLocations, true, parent); } /** * Create a new FileSystemXmlApplicationContext, loading the definitions * from the given XML files. * @param configLocations array of file paths * @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. * @throws BeansException if context creation failed * @see #refresh() */ public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException { this(configLocations, refresh, null); } /** * Create a new FileSystemXmlApplicationContext with the given parent, * loading the definitions from the given XML files. * @param configLocations array of file paths * @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 FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { super(parent); setConfigLocations(configLocations); if (refresh) { refresh(); } } /** * Resolve resource paths as file system paths. * <p>Note: Even if a given path starts with a slash, it will get * interpreted as relative to the current VM working directory. * This is consistent with the semantics in a Servlet container. * @param path path to the resource * @return Resource handle * @see org.springframework.web.context.support.XmlWebApplicationContext#getResourceByPath */ @Override protected Resource getResourceByPath(String path) { if (path != null &amp;&amp; path.startsWith("/")) { path = path.substring(1); } return new FileSystemResource(path); } }

一共七個建構函式和一個複寫的方法。我們現在重點關注建構函式,除前兩個之外,其他的建構函式都最終指向建構函式

	public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
		super(parent);
		setConfigLocations(configLocations);
		if (refresh) {
			refresh();
		}

只是對沒有傳入的引數,給了一個預設值。

該建構函式有三個引數:

  • String[] configLocations - 配置檔案的路徑陣列。是陣列也就是說,支援同時傳入多個配置檔案路徑。
  • boolean refresh - 是否重新整理,如果是true,則會開始初始化Spring容器, false則暫時不初始化Spring容器。
  • ApplicationContext parent - Spring容器上下文。即可以傳入已經初始化過的Spring容器,新初始化的容器會包含parent上下文中的內容,例如:父容器中定義的bean等。

refresh()方法可謂Spring的核心的入口函式,Spring容器的初始化正是由此開始。一些Spring學習的書中,也向學習Spring的讀者推薦,如果感到無所適從,可從該方法入手,研究Spring的整個生命週期。後續我們也會從該方法入手重點研究。現在只需理解,其為Spring容器初始化的一個“開關”即可。

前兩個建構函式與該建構函式最大的區別就是,沒有呼叫refresh函式。也就是說,Spring容器,此時並未初始化。此時如果用getBean方法去獲取Bean的例項,會報容器並未初始化的異常。

下面給出一些關於建構函式的測試用例,能更直觀、具體的說明問題

	private String filePath = "D:\\workspace-home\\spring-custom\\src\\main\\resources\\spring\\app-context.xml";
	private String parentFilePath = "D:\\workspace-home\\spring-custom\\src\\main\\resources\\spring\\parent-context.xml";

	/**
	 * 直接指定一個單一的配置檔案初始化Spring容器。容器中包含該指定配置檔案中定義的Bean{@code VeryCommonBean}。
	 * 
	 * @author lihzh
	 * @date 2012-4-29 下午9:39:27
	 */
	@Test
	public void testFileContextWithPath() {
		ApplicationContext appContext = new FileSystemXmlApplicationContext(
				filePath);
		assertNotNull("The app context should not be null.", appContext);
		assertNotNull(appContext.getBean(VeryCommonBean.class));
	}

	/**
	 * 將已經初始化好的Spring容器上下文傳遞給新的Spring容器。需手動呼叫容器的重新整理
	 * {@code ConfigurableApplicationContext#refresh()}(初始化)。
	 * 
	 * @author lihzh
	 * @date 2012-5-4 下午9:28:40
	 */
	@Test
	public void testFileContextWithParentOnly() {
		ApplicationContext parentContext = new FileSystemXmlApplicationContext(
				parentFilePath);
		ConfigurableApplicationContext appContext = new FileSystemXmlApplicationContext(
				parentContext);
		assertNotNull("The parent context should not be null.", parentContext);
		assertNotNull(appContext);
		// 需要手動重新整理(初始化)容器
		appContext.refresh();
		assertNotNull(parentContext.getBean(ParentBean.class));
		assertNotNull(appContext.getBean(ParentBean.class));
	}

	/**
	 * 根據指定的配置檔案和已經初始化好的Parent容器上下文,例項化一個新的Spring容器,該容器中包含這兩部分的資訊。
	 * 
	 * @author lihzh
	 * @date 2012-5-4 下午10:07:20
	 */
	@Test
	public void testFileContextWithParentAndItself() {
		ApplicationContext parentContext = new FileSystemXmlApplicationContext(
				parentFilePath);
		ApplicationContext appContext = new FileSystemXmlApplicationContext(
				new String[] { filePath }, parentContext);
		assertNotNull("The parent context should not be null", parentContext);
		assertNotNull(appContext);
		assertTrue("Should not have bean:[VeryCommonBean] in parent context.",
				!parentContext.containsBean("VeryCommonBean"));
		assertNotNull(parentContext.getBean(ParentBean.class));
		assertNotNull(appContext.getBean(ParentBean.class));
		assertNotNull("Should have bean:[VeryCommonBean].",
				appContext.getBean("VeryCommonBean"));
	}

	/**
	 * 同時指定多個配置檔案來初始化Spring容器
	 * 
	 * @author lihzh
	 * @date 2012-5-4 下午10:09:02
	 */
	@Test
	public void testFileContextWithMultiPath() {
		ApplicationContext appContext = new FileSystemXmlApplicationContext(
				parentFilePath, filePath);
		assertNotNull(appContext);
		assertNotNull(appContext.getBean(ParentBean.class));
		assertNotNull("Should have bean:[VeryCommonBean].",
				appContext.getBean(VeryCommonBean.class));
	}

瞭解這些有什麼用?

  • 學會在一個普通的J2SE應用中使用Spring,初始化Spring容器。
  • 瞭解初始化Spring容器的幾種方式和傳入的引數,可配合不同的場景使用。例如:在程式執行期動態初始化一個新的Spring容器,包含已經初始化的容器,即可用與parent相關的建構函式。再例如:在可能的特定的場景下,Spring容器需要在特定的實際初始化,可先呼叫空建構函式,再傳入location,再手動refresh。或者直接給refresh傳入false,再手動refresh。再比如:配合Spring的foo包,可監控Spring配置檔案的變化,利用refresh函式,實時更新Spring容器等等。