BeanFactory 和ApplicationContext(Bean工廠和應用上下文)
一、BeanFactory 和ApplicationContext
Bean 工廠(com.springframework.beans.factory.BeanFactory)是Spring 框架最核心的介面,它提供了高階IoC 的配置機制。
應用上下文(com.springframework.context.ApplicationContext)建立在BeanFactory 基礎之上。
幾乎所有的應用場合我們都直接使用ApplicationContext 而非底層的BeanFactory。
1.1 BeanFactory 的類體系結構
BeanFactory 介面位於類結構樹的頂端, 它最主要的方法就是getBean(StringbeanName),該方法從容器中返回特定名稱的Bean,BeanFactory 的功能通過其他的介面得到不斷擴充套件。
ListableBeanFactory:該介面定義了訪問容器中Bean 基本資訊的若干方法,如檢視Bean 的個數、獲取某一型別Bean 的配置名、檢視容器中是否包括某一Bean 等方法;
HierarchicalBeanFactory:父子級聯IoC 容器的介面,子容器可以通過介面方法訪問父容器;
ConfigurableBeanFactory:是一個重要的介面,增強了IoC 容器的可定製性,它定義了設定類裝載器、屬性編輯器、容器初始化後置處理器等方法;
AutowireCapableBeanFactory:定義了將容器中的Bean 按某種規則(如按名字匹配、按型別匹配等)進行自動裝配的方法;
SingletonBeanRegistry:定義了允許在執行期間向容器註冊單例項Bean 的方法;
BeanDefinitionRegistry:Spring 配置檔案中每一個<bean>節點元素在Spring 容器裡都通過一個BeanDefinition 物件表示,它描述了Bean 的配置資訊。而BeanDefinitionRegistry 介面提供了向容器手工註冊BeanDefinition 物件的方法。
1.2 ApplicationContext 的類體系結構
ApplicationContext由BeanFactory 派生而來,提供了更多面向實際應用的功能。在BeanFactory 中,很多功能需要以程式設計的方式實現,而在ApplicationContext 中則可以通過配置的方式實現。
ApplicationContext 的主要實現類是ClassPathXmlApplicationContext 和FileSystemXmlApplicationContext,前者預設從類路徑載入配置檔案,後者預設從檔案系統中裝載配置檔案。
核心介面包括:
ApplicationEventPublisher:讓容器擁有釋出應用上下文事件的功能,包括容器啟動事件、關閉事件等。實現了ApplicationListener 事件監聽介面的Bean 可以接收到容器事件, 並對事件進行響應處理。在ApplicationContext 抽象實現類AbstractApplicationContext 中,我們可以發現存在一個ApplicationEventMulticaster,它負責儲存所有監聽器,以便在容器產生上下文事件時通知這些事件監聽者。
MessageSource:為應用提供i18n 國際化訊息訪問的功能;
ResourcePatternResolver : 所有ApplicationContext 實現類都實現了類似於
PathMatchingResourcePatternResolver 的功能,可以通過帶字首的Ant 風格的資源文
件路徑裝載Spring 的配置檔案。
LifeCycle:該介面是Spring 2.0 加入的,該介面提供了start()和stop()兩個方法,主要用於控制非同步處理過程。在具體使用時,該介面同時被ApplicationContext 實現及具體Bean 實現,ApplicationContext 會將start/stop 的資訊傳遞給容器中所有實現了該介面的Bean,以達到管理和控制JMX、任務排程等目的。
ConfigurableApplicationContext 擴充套件於ApplicationContext,它新增加了兩個主要的方法:refresh()和close(),讓ApplicationContext 具有啟動、重新整理和關閉應用上下文的能力。在應用上下文關閉的情況下呼叫refresh()即可啟動應用上下文,在已經啟動的狀態下,呼叫refresh()則清除快取並重新裝載配置資訊,而呼叫close()則可關閉應用上下文。這些介面方法為容器的控制管理帶來了便利
程式碼示例:
ApplicationContext ctx =new ClassPathXmlApplicationContext("com/baobaotao/context/beans.xml");
ApplicationContext ctx =new FileSystemXmlApplicationContext("com/baobaotao/context/beans.xml");
ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"conf/beans1.xml","conf/beans2.xml"});
ApplicationContext 的初始化和BeanFactory 有一個重大的區別:BeanFactory在初始化容器時,並未例項化Bean,直到第一次訪問某個Bean 時才例項目標Bean;而ApplicationContext 則在初始化應用上下文時就例項化所有單例項的Bean 。
二、WebApplicationContext 類體系結構
WebApplicationContext 是專門為Web 應用準備的,它允許從相對於Web 根目錄的路徑中裝載配置檔案完成初始化工作。從WebApplicationContext 中可以獲得ServletContext 的引用,整個Web 應用上下文物件將作為屬性放置到ServletContext 中,以便Web 應用環境可以訪問Spring 應用上下文(ApplicationContext)。Spring 專門為此提供一個工具類WebApplicationContextUtils,通過該類的getWebApplicationContext(ServletContext
sc)方法,即可以從ServletContext 中獲取WebApplicationContext 例項。
Spring 2.0 在WebApplicationContext 中還為Bean 添加了三個新的作用域:request 作用域、session 作用域和global session 作用域。而在非Web 應用的環境下,Bean 只有singleton和prototype 兩種作用域。
由於 Web 應用比一般的應用擁有更多的特性,因此WebApplicationContext 擴充套件了ApplicationContext。WebApplicationContext 定義了一個常量ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,在上下文啟動時,WebApplicationContext 例項即以此為鍵放置在ServletContext 的屬性列表中,因此我們可以直接通過以下語句從Web 容器中獲取
WebApplicationContext:
WebApplicationContext wac = (WebApplicationContext)servletContext.getAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
2.1 WebApplicationContext 的初啟動方式和BeanFactory、ApplicationContext 有所區別。
因為WebApplicationContext 需要ServletContext 例項,也就是說它必須在擁有Web 容器的前提下才能完成啟動的工作。有過Web 開發經驗的讀者都知道可以在web.xml 中配置自啟
動的Servlet 或定義Web 容器監聽器(ServletContextListener),藉助這兩者中的任何一個,我們就可以完成啟動Spring Web 應用上下文的工作。
Spring 分別提供了用於啟動WebApplicationContext 的Servlet 和Web 容器監聽器:
org.springframework.web.context.ContextLoaderServlet;
org.springframework.web.context.ContextLoaderListener。
兩者的內部都實現了啟動WebApplicationContext 例項的邏輯,我們只要根據Web 容器的具體情況選擇兩者之一,並在web.xml 中完成配置就可以了。
2.2 下面是使用ContextLoaderListener 啟動WebApplicationContext 的具體配置:
Xml程式碼
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/baobaotao-dao.xml, /WEB-INF/baobaotao-service.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener </listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/baobaotao-dao.xml, /WEB-INF/baobaotao-service.xml </param-value>
</context-param>
2.3 如果在不支援容器監聽器的低版本Web 容器中,我們可採用ContextLoaderServlet 完成相同的工作:
Xml程式碼
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/baobaotao-dao.xml, /WEB-INF/baobaotao-service.xml </param-value>
</context-param>
<servlet>
<servlet-name>springContextLoaderServlet</servlet-name>
<servlet-class>org.springframework.web.context.ContextLoaderServlet </servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
三、WebApplicationContext 需要使用日誌功能
使用者可以將Log4J 的配置檔案放置到類路徑WEB-INF/classes 下,這時Log4J 引擎即可順利啟動。
如果Log4J 配置檔案放置在其他位置,使用者還必須在 web.xml 指定Log4J 配置檔案位置。
Spring 為啟用Log4J 引擎提供了兩個類似於啟動WebApplicationContext 的實現類:Log4jConfigServlet 和Log4jConfigListener,
不管採用哪種方式都必須保證能夠在裝載Spring 配置檔案前先裝載Log4J 配置資訊。
3.1 Log4jConfigServlet啟動日誌:
Xml程式碼<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/baobaotao-dao.xml,/WEB-INF/baobaotao-service.xml
</param-value>
</context-param>
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/log4j.properties</param-value>
</context-param>
<servlet>
<servlet-name>log4jConfigServlet</servlet-name>
<servlet-class>org.springframework.web.util.Log4jConfigServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name> springContextLoaderServlet</servlet-name>
<servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
注意上面我們將log4jConfigServlet 的啟動順序號設定為1,而springContextLoaderServlet的順序號設定為2。這樣,前者將先啟動,完成裝載Log4J 配置檔案初始化Log4J 引擎的工作,緊接著後者再啟動。如果使用Web 監聽器,則必須將Log4jConfigListener 放置在ContextLoaderListener 的前面。
3.2 Log4jConfigListener啟動日誌:
Xml程式碼 <!-- Log4J 配置 -->
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/conf/log4j.properties</param-value>
</context-param>
<!-- Spring上下文配置 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/conf/spring/applicationContext.xml</param-value>
</context-param>
<!-- Log4J監聽器 -->
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<!-- Spring監聽器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
四、父子容器
通過 HierarchicalBeanFactory 介面,Spring 的IoC 容器可以建立父子層級關聯的容器
體系,子容器可以訪問父容器中的Bean,但父容器不能訪問子容器的Bean。在容器內,Bean 的id 必須是唯一的,但子容器可以擁有一個和父容器id 相同的Bean。父子容器層級體系增強了Spring 容器架構的擴充套件性和靈活性,因為第三方可以通過程式設計的方式,為一個已經存在的容器新增一個或多個特殊用途的子容器,以提供一些額外的功能。
Spring 使用父子容器實現了很多功能,比如在Spring MVC 中,展現層Bean 位於一個子容器中,而業務層和持久層的Bean 位於父容器中。這樣,展現層Bean 就可以引用業務層和持久層的Bean,而業務層和持久層的Bean 則看不到展現層的Bean。