Spring核心系列之ApplicationContext

我又不是架構師發表於2018-01-04

Spring核心系列之ApplicationContext

Hello,大家好,今天開始,小弟準備推出Spring系列的部落格,希望大家喜歡。關於Spring其實我就不用再多介紹了,做過Web開發的,基本都使用Spring,包括現在比較時尚的Spring cloud微服務架構,其實也是基於Spring boot ,Spring boot 說到底其實還是傳統的Spring三件套,只是避免了使用者自己配置Bean,而是採用了自動配置,Spring boot後期有時間也專門給大家出一系列部落格。這一期作為Spring的第一篇,先來點基礎的,OK,來套路,文章結構:

  1. Spring中的Resource介面.
  2. BeanFactory和ApplicationContext
  3. WebApplicationContext

1. Spring中的Resource介面.

其實對於很多業務開發程式設計師來講,這個介面是比較陌生的,大家知道的是在初始化容器的時候直接指定檔案路徑,然後容器就初始化好了,這個Resource介面是Spring提供的,它為應用提供了更強的底層資源訪問能力。比JDK自帶的File和URL類不知道強了多少。列下類結構:

Spring核心系列之ApplicationContext
說幾個重點的: ClassPathResource: 以類路徑為相對路勁下的資源 。 FileSystemResource : 以檔案系統路徑查詢的資源。 ServletContextResource : 以相對於Web應用跟目錄的方式訪問。 ByteArrayResource: 二進位制陣列表示的資源。 比如現在Web的路徑下又一個Spring的配置檔案,我們可以這樣載入:

Resource res =new ServletContectResource(/WEB-INF/classes/spring/application.xml);
複製程式碼

看到程式碼,相信比較敏感的小夥伴應該感覺到了,這種載入方式顯然是不合適的,因為在使用不同的資源型別時,必須使用相應的Resource資源類,這是比較麻煩的,是否可以在不現實使用Resource實現類的情況下,僅通過資源地址的特殊標識就可以訪問相應的資源呢?Spring這麼偉大的框架,當然是可以的。Spring不僅能夠通過"classpath:","file"等資源地址字首識別不同資源型別,還支援Ant風格帶萬用字元的資源地址。Spring內部會通過資源載入器來根據不同的資源路徑選擇相應的Resource實現類。避免的使用者自己選擇實現類。

1.1 資源地址表示式

先看下Spring內建的一些資源字首表達的含義:

Spring核心系列之ApplicationContext
然後Ant風格:

  • ?:匹配檔名中的一個字元.
  • *:匹配檔案命中的任意字元.
  • ** :匹配多層路徑.

具體就不舉例子了,Ant風格的萬用字元太常用了。

1.2 資源載入器

Spring核心系列之ApplicationContext
這些介面我就不多講了,沒什麼實際意義,只說一個,PathMatchingResourcePatternResolver,這個是Spring提供的標準資源載入器,能夠支援Ant風格的資源路徑,並根據不同字首型別返回相應的Resource.舉個例子:

public class GaoshiApplication {

	public static void main(String[] args) throws IOException {

		PathMatchingResourcePatternResolver resolver=new PathMatchingResourcePatternResolver();
		Resource[] res =resolver.getResources("file:/Users/zdy/Desktop/sql.txt");
		for (Resource  resource : res){
			System.out.println(resource.getDescription()+"----"+resource.getFilename());
		}
		SpringApplication.run(GaoshiApplication.class, args);
	}
}
複製程式碼
輸出結果:
URL [file:/Users/zdy/Desktop/sql.txt]----sql.txt
複製程式碼

好了,其實這一小節為什麼要給大家講Resource和PathMatchingResourcePatternResolver資源載入器呢,很多人可能感覺沒什麼用,反正Spring內部自己使用就完了,跟我沒關係。其實不是的,老鐵們,Spring內部自己使用不假,首先,知道點底層原理不好嗎?其次,大家如果自己有配置檔案在classpath下,然後想通過程式碼去載入(雖然這種需求非常少),那麼大家就可以自己使用Spring提供的資源載入器了。這個載入器還是很方便的,直接根據字首載入資源,而且還支援Ant萬用字元。你說厲害不厲害。

2. BeanFactory和ApplicationContext

2.1 BeanFactory

BeanFactory,其實很多人聽過,只是沒有用過,大家啟動容器的時候可能都是用的ApplicationContext的某個實現類,所以這個BeanFactory慢慢的都淡了,更別說它的實現類了。說下特性:

  1. BeanFactory是延遲載入,如果Bean的某一個屬性沒有注入,BeanFacotry載入後,直至第一次使用呼叫getBean方法才會丟擲異常;而ApplicationContext則在初始化自身是檢驗,這樣有利於檢查所依賴屬性是否注入;所以通常情況下我們選擇使用ApplicationContext。
  2. ApplicationContext繼承自BeanFactory,提供的功能更加強大,一般情況下都是使用ApplicationContext

然後舉個初始化BeanFactory的例子吧:

//呼叫資源載入器載入Spring配置檔案
		PathMatchingResourcePatternResolver resolver=new PathMatchingResourcePatternResolver();
		Resource res =resolver.getResource("classpath:/application.xml");
		
		//建立預設Spring提供的BeanFactory實現類
		DefaultListableBeanFactory bf =new DefaultListableBeanFactory();
		
		//BeanDefinition 讀取器,專門讀取資源到容器
		XmlBeanDefinitionReader reader =new XmlBeanDefinitionReader(bf);
		
		//讀取資源進到容器 
		reader.loadBeanDefinitions(res);
		
		//此時容器初始化完畢後可以取裡面的bean了.
		bf.getBean("...");
複製程式碼

可以看到,程式碼比較繁瑣,而且很囉嗦。BeanFactory的功能還比較弱,所以大家就當瞭解吧。

2.2 ApplicationContext

這個ApplicationContext其實就是老鐵們經常說的Spring容器了。因為相對要重要一些,所以列了大致的類繼承圖:

Spring核心系列之ApplicationContext
說下圖中幾個重要的類或介面:

  • ApplicationEventPublisher: 讓容器擁有釋出應用程式上下文事件的功能.
  • ResourcePatternResolver:實現了類似於資源載入器的功能,能夠識別特定字首加Ant風格的路徑並載入到容器中.
  • LifeCycle:管理容器中bean的生命週期。
  • ConfigurableApplicationContext:主要新增了refresh()和close()方法,讓ApplicationContext有用了啟動,關係和重新整理上下文的功能。
  • ClassPathXmlApplicationContext與FileSystemXmlApplicationContext:Spring提供的兩個常用的ApplicationContext實現類,前者預設從類路徑載入配置檔案,後者從檔案系統載入。 然後初始化一個ApplicationContext看看:
ApplicationContext ac =new ClassPathXmlApplicationContext("application.xml");
ApplicationContext ac =new FileSystemXmlApplicationContext(/User/Desktop/application.xml);
複製程式碼

ClassPathXmlApplicationContext如果沒有字首預設就是classpath: FileSystemXmlApplicationContext如果沒有字首預設就是 file:

然後再說一個,Spring容器不僅可以通過xml檔案來初始化,@Configuration註解大家應該知道,也是註冊Bean用的,當然了,Spring也提供了相應的AnnotationConfigApplicationContext.

ApplicationContext context =new AnnotationConfigApplicationContext(Config.class);
複製程式碼

Config類就是我們加了@Configuration的類了。

3. WebApplicationContext

其實WebApplicationContext也屬於ApplicationContext,為什麼單獨拿出來講呢,因為比較重要。WebApplicationContext是Spring專門為Web應用準備的。它允許從相對於Web根目錄的路徑中裝載配置檔案完成初始化工作。WebApplicationContext與ServletContext可以相互獲得.在非Web應用的環境下,Bean只有singleton和prototype兩種作用域,而WebApplicationContext為Bean新增了三個新的作用域:request,session,global session。來看下類繼承圖:

Spring核心系列之ApplicationContext
最最核心的XmlWebApplicationContext和AnnotationConfigWebApplicationContext,大家猜都應該猜到了,一個是XML配置的,一個是@Configuration配置的。

然後說下WebApplicationContext和ServletContext是如何相互獲取的,其實很簡單,在WebApplicationContext裡有ServletContext成員變數,直接get就完了。在ServletContext裡有一個寫死的attrbute,也是直接get..而且Spring提供了一個WebApplicationContextUtils來封裝了這個寫死的attribute.

其實就是直接呼叫了ServletContext.getAttribute(ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
    public static WebApplicationContext getWebApplicationContext(ServletContext sc) {
        return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
    }
複製程式碼

Spring核心系列之ApplicationContext

然後說下WebApplication的初始化,做過Web的都知道,其實要麼在Web.xml裡面配置一個ContextLoaderListener,要麼配置一個自啟動的LoaderServlet.其實比較簡單了,我展示下ContextLoaderListener.

  <!-- 載入spring容器 -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring/applicationContext-*.xml</param-value>
  </context-param>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
複製程式碼

如果不是根據xml檔案啟動Spring容器而是根據@Configuration類啟動,其實也很簡單.

  <context-param>
    <param-name>contextClass</param-name>
    <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
  </context-param>
  <!-- 載入spring容器 -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>com.zdy.Configuration</param-value>
  </context-param>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
複製程式碼

配置一個contextClass引數ApplicationContext改為AnnotationConfigWebApplicationContext,然後contextConfigLocation不是指定配置檔案xml位置了,改為指定Configuration類的位置。就OK了。

最後,需要強調的是,由於Spring容器自帶的Log4j功能,所以使用者可以直接將Log4j的配置檔案放置在類路徑下,直接就生效了,但是,如果不在類路徑下,需要在Web.xml中手動指定檔案的位置和啟動log4j的listener,這裡我就不演示了,需要注意的是,這個log4j的listener或者servlet必須在spring容器的listenier或servlet前面。不要問為什麼。就是這麼規定的。不過這點大家不常用,所以我只是提一提。

結語

好了,其實大家也看出來了,講Spring的時候我還是偏向於使用的,Spring的底層深層次的原理其實設計到的不多。Spring這個至尊框架,我覺得先要巨集觀使用上先整明白。後期有機會給大家多講點底層實現。喜歡大家多關注接下來的Spring系列文章。Over,Have a good day .

相關文章