Spring 通過(guò)一個(gè)配置文件來(lái)描述 Bean 及 Bean 之間的依賴關(guān)系粱侣,利用 Java 的反射功能實(shí)例化 Bean 并建立 Bean 之間的依賴關(guān)系 。Sprig 的 IoC 容器在完成這些底層工作的基礎(chǔ)上,還提供了 Bean 實(shí)例緩存 、 生命周期管理 、Bean 實(shí)例代理 着裹、 事件發(fā)布 、 資源裝載等高級(jí)服務(wù) 米同。
Bean 工廠( com.springframework.beans.factory.BeanFactory )是 Spring 框架中最核心的接口骇扇,它提供了高級(jí) IoC 的配置機(jī)制 。BeanFactory 使管理不同類型的 Java 對(duì)象成為可能面粮。而
應(yīng)用上下文( com.springframework.context.ApplicationContext )建立在 BeanFactory 基礎(chǔ)之上少孝,提供了更多面向應(yīng)用的功能,它提供了國(guó)際化支持和框架事件體系熬苍,更易于創(chuàng)建實(shí)際應(yīng)用 稍走。
我們一般稱 BeanFactory 為 IoC 容器,而稱 ApplicationContext 為應(yīng)用上下文或 Spring 容器 柴底。
BeanFactory 是 Spring 框架的基礎(chǔ)設(shè)施婿脸,面向 Spring 本身; ApplicationContext 面向使用 Spring 框架的開(kāi)發(fā)者柄驻,幾乎所有的應(yīng)用場(chǎng)合都可以直接使用 ApplicationContext狐树。
Spring 框架是生成類對(duì)象的工廠,而被創(chuàng)建的類對(duì)象本身也可能是一個(gè)工廠鸿脓,這就形成了 “創(chuàng)建工廠的工廠”抑钟。
1 BeanFactory
BeanFactory 是類的通用工廠,它可以創(chuàng)建并管理各種類的對(duì)象答憔,這些類就是 POJO味赃,Spring 稱這些被創(chuàng)建并管理的類對(duì)象為 Bean掀抹。
1.1 類體系結(jié)構(gòu)
BeanFactory 有眾多的實(shí)現(xiàn)虐拓,在 Spring 3.2 之前的版本中,最常用的是 XmlBeanFactory傲武,現(xiàn)已被廢棄蓉驹。建議使用 XmlBeanDefinitionReader 與 DefaultListableBeanFactory。
BeanFactory 接口位于類結(jié)構(gòu)樹(shù)的頂端揪利,它最主要的方法就是 getBean(String beanName) 态兴,該方法從容器中返回特定名稱的 Bean , BeanFactory 的功能通過(guò)其他接口而得到不斷擴(kuò)展疟位。
接口 | 說(shuō)明 |
---|---|
ListableBeanFactory | 該接口定義了訪問(wèn)容器中 Bean 基本信息的若干方法瞻润,如:查看 Bean 的個(gè)數(shù), 獲取某一類型 Bean 的配置名,或查看容器中是否包含某一 Bean绍撞。 |
HierarhicalBeanFactory | 父子級(jí)聯(lián) IoC 容器的接口正勒,子容器可以通過(guò)接口方法訪問(wèn)父容器。 |
ConfigurableBeanFactory | 該接口增強(qiáng)了IoC容器的可定制性傻铣,它定義了設(shè)置類裝載器章贞、屬性 編輯器、容器初始化后置處理器等方法非洲。 |
AutowireCapableBeanFactory | 定義了將容器中的 Bean 按某種規(guī)則(如:按名稱匹配 鸭限、 按類型匹配) 進(jìn)行自動(dòng)裝配的方法。 |
SingletonBeanFactory | 定義了允許在運(yùn)行期間向容器注冊(cè)單實(shí)例 Bean 的方法两踏。 |
BeanDefinitionRegistry | Spring 配置文件中每一個(gè) Bean 節(jié)點(diǎn)元素在 Spring 容器里都通過(guò)一個(gè) BeanDefinition 對(duì)象表示败京,它描述了 Bean 的配置信息。而 BeanDefinitionResgistry 接口提供了向容器手工注冊(cè) BeanDefinition 對(duì)象的方法梦染。 |
1.2 初始化
下面我們?cè)?Spring 配置文件中配置 Bean People喧枷,然后通過(guò) BeanFactory 加載配置文件,啟動(dòng) Ioc 容器弓坞。
Spring 配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="people" class="net.deniro.springBoot.spring4.IoC.People"
p:name="deniro"
p:age="25"
/>
</beans>
然后使用 DefaultListableBeanFactory 與 XmlBeanDefinitionReader 來(lái)啟動(dòng) Ioc 容器:
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource resource = resolver.getResource("classpath:beans.xml");
System.out.println("getURL:" + resource.getURL());
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(resource);
System.out.println("Bean 工廠已完成初始化");
People people = factory.getBean("people", People.class);
System.out.println("People 已被創(chuàng)建");
System.out.println(people.toString());
XmlBeanDefinitionReader 通過(guò) Resource 來(lái)裝載 Spring 的配置信息并啟動(dòng) Ioc 容器隧甚,然后就可以通過(guò) BeanFactory#getBean(name) 獲取 Bean 咯。Bean 初始化操作發(fā)生在第一次調(diào)用時(shí)渡冻。對(duì)于單實(shí)例的 Bean 來(lái)說(shuō)戚扳, BeanFactory 會(huì)緩存 Bean 實(shí)例, 所以第二次使用 getBean() 方法時(shí)就會(huì)直接從 IoC 容器的緩存中獲取 Bean 的實(shí)例族吻。
注意:初始化 BeanFactory 時(shí)必須為其提供一種日志框架帽借, 一般是使用 Log4J ,即在類路徑下提供 Log4J 的配置文件超歌,這樣才能正常啟動(dòng) Spring 容器砍艾。
2 ApplicationContext
ApplicationContext 由 BeanFactory 派生而來(lái),提供了很多實(shí)際應(yīng)用的功能 巍举。 在 BeanFactory 中脆荷,很多功能需要以編程的方式實(shí)現(xiàn),而在 ApplicationContext 中則可以通過(guò)配置的方式來(lái)實(shí)現(xiàn)懊悯。
2.1 ApplicationContext 類體系結(jié)構(gòu)
接口 | 說(shuō)明 |
---|---|
ApplicationEventPublisher | 讓容器擁有了發(fā)布應(yīng)用上下文事件的功能蜓谋,包括容器啟動(dòng)事件 、關(guān)閉事件等炭分。實(shí)現(xiàn)了 ApplicationListener 事件監(jiān)聽(tīng)接口的 Bean 可以接收到容器事件桃焕, 并對(duì)容器事件進(jìn)行響應(yīng)處理。在 ApplicationContext 抽象實(shí)現(xiàn)類 AbstractApplicationContext 中存在一個(gè) ApplicationEventMulticaster捧毛,它負(fù)責(zé)保存所有的監(jiān)聽(tīng)器观堂,以便在容器產(chǎn)生上下文事件時(shí)通知這些事件監(jiān)聽(tīng)者让网。 |
MessageSource | 為容器提供了 i18n 國(guó)際化信息訪問(wèn)的功能。 |
ResourcePatternResolver | 所有 ApplicationContext 實(shí)現(xiàn)類都實(shí)現(xiàn)了類似于 PathMatchingResourcePatternResolver 的功能师痕, 可以通過(guò)帶前綴 Ant 風(fēng)格的資源類文件路徑來(lái)裝載 Spring 的配置文件寂祥。 |
LifeCycle | 它提供了 start() 和 stop() 兩個(gè)方法, 主要用于控制異步處理過(guò)程七兜。在具體使用時(shí)丸凭,該接口同時(shí)被 ApplicationContext 實(shí)現(xiàn)及具體 Bean 實(shí)現(xiàn), ApplicationContext 會(huì)將 start/stop 的信息傳遞給容器中所有實(shí)現(xiàn)了該接口的 Bean 腕铸,以達(dá)到管理和控制 JMX惜犀、 任務(wù)調(diào)度等目的。 |
ConfigurableApplicationContext | 它擴(kuò)展了 ApplicationContext狠裹,讓 ApplicationContext 具有啟動(dòng)虽界、刷新和關(guān)閉應(yīng)用上下文的能力。上下文關(guān)閉時(shí)涛菠,調(diào)用 refresh() 即可啟動(dòng)上下文莉御;如果已經(jīng)啟動(dòng),則調(diào)用 refresh() 可清除緩存并重新加載配置信息俗冻;調(diào)用 close() 可關(guān)閉應(yīng)用上下文礁叔。 |
2.1.1 XML 配置
初始化 ApplicationContext 時(shí),根據(jù)配置文件的所在路徑迄薄,來(lái)選擇 ApplicationContext 的實(shí)現(xiàn)類琅关。
ApplicationContext 的主要實(shí)現(xiàn)類是:
- ClassPathXmlApplicationContext - 從類路徑加載配置文件。
- FileSystemXmlApplicationContext - 從文件系統(tǒng)加載配置文件讥蔽。
//從類路徑加載配置文件
ApplicationContext context1 = new ClassPathXmlApplicationContext("beans.xml");
//從文件系統(tǒng)加載配置文件
ApplicationContext context2 = new FileSystemXmlApplicationContext("d:/beans.xml");
還可以指定一組的配置文件涣易,Spring 會(huì)自動(dòng)將多個(gè)配置文件中的內(nèi)容整合起來(lái):
//指定一組的配置文件
ApplicationContext context3 = new ClassPathXmlApplicationContext(new
String[]{"beans.xml", "beans2.xml"});
注意:ClassPathXmlApplicationContext 與 FileSystemXmlApplicationContext 也可以顯式指定帶資源類型前綴的路徑。
ApplicationContext 與 BeanFactory 初始化之間的區(qū)別:
- ApplicationContext - 在初始化應(yīng)用上下文時(shí)冶伞,會(huì)實(shí)例化所有單實(shí)例的 Bean新症,所以相對(duì)來(lái)說(shuō),初始化時(shí)間會(huì)比 BeanFactory 稍長(zhǎng)响禽,不過(guò)稍后的調(diào)用沒(méi)有 “第一次懲罰” 的問(wèn)題徒爹。
- BeanFactory - 在初始化容器時(shí),并未實(shí)例化 Bean金抡。直到 Bean 被訪問(wèn)時(shí)瀑焦,才會(huì)被實(shí)例化。
2.1.2 類注解配置
Spring 支持基于類注解的配置方式梗肝,主要功能來(lái)自于 Spring 的一個(gè)名為 JavaConfig 子項(xiàng)目,目前 JavaConfig 已經(jīng)升級(jí)為 Spring 核心框架的一部分 铺董。 一個(gè)標(biāo)注 @Configuration 注解的 POJO 即可提供 Spring 所需的 Bean 配置信息 巫击。
@Configuration//表示這個(gè)類包含配置信息
public class Beans {
//定義 Bean
@Bean(name = "people")
public People build() {
People people = new People();
people.setAge(25);
people.setName("deniro");
return people;
}
}
與 XML 的配置方式相比禀晓,基于類注解的配置方式可以讓開(kāi)發(fā)者控制 Bean 的初始化過(guò)程,所以更加靈活坝锰。
基于類注解的 Bean 配置粹懒,需要使用 AnnotationConfigApplicationContext 來(lái)啟動(dòng) Spring 容器:
ApplicationContext context = new AnnotationConfigApplicationContext(Beans.class);
People people = context.getBean("people", People.class);
Assert.assertNotNull(people);
2.1.3 Groovy DSL 配置
Spring 4.x 支持使用 Groovy DSL 對(duì) Bean 進(jìn)行配置,通過(guò)它可以實(shí)現(xiàn)復(fù)雜顷级、靈活的配置邏輯凫乖。
首先在 pom.xml 中引入 Groovy:
<!-- groovy -->
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>${groovy.version}</version>
</dependency>
然后新增配置文件:
package net.deniro.springBoot.spring4.factory
import net.deniro.springBoot.spring4.IoC.People
beans {
people(People) {//格式:名字(類型)
name = "deniro"http://注入屬性
age = 25
}
}
最后使用 GenericGroovyApplicationContext 來(lái)啟動(dòng)容器:
ApplicationContext context = new GenericGroovyApplicationContext("classpath:groovy-beans.groovy");
People people = (People) context.getBean("people");
Assert.assertNotNull(people);
Assert.assertEquals(people.getName(), "deniro");
2.2 WebApplicationContext 類體系結(jié)構(gòu)
WebApplicationContext 專用于 web 應(yīng)用 , 它允許從相對(duì)于 web 根目錄的路徑中裝載配置文件完成初始化工作,從 WebApplicationContext 中可以獲得 ServletContext 的引用弓颈,整個(gè) Web 應(yīng)用上下文對(duì)象將作為屬性放置在 ServletContext 中帽芽,以便 web 應(yīng)用可以訪問(wèn) spring 上下文 ,spring 中提供 WebApplicationContextUtils 的 getWebApplicationContext(ServletContext src) 方法從 ServletContext 中獲取 WebApplicationContext 實(shí)例。
非 Web 應(yīng)用環(huán)境中翔冀,Bean 只有 singleton 與 prototype 兩種作用域导街。而在 WebApplicationContext 中,它為 Bean 添加了另外三種作用域:request纤子、session 與 global session搬瑰。
WebApplicationContext 擴(kuò)展了 ApplicationContext。WebApplicationContext 定義了一個(gè)常量 ROOT_WEB_APPLICATION_ CONTEXT_ATTRIBUTE 控硼,在上下文啟動(dòng)時(shí)泽论, WebApplicationContext 實(shí)例以這個(gè)常量為鍵,放置在 ServletContext 的屬性列表中卡乾,因此我們可以直接通過(guò)以下語(yǔ)句從 Web 容器中獲取 WebApplicationContext :
//從 servletContext 中獲取 WebApplicationContext
WebApplicationContext wac= (WebApplicationContext) servletContext.getAttribute(WebApplicationContext
.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
//從 WebApplicationContext 中獲取 servletContext
ServletContext sc=wac.getServletContext();
ConfigurableWebApplicationContext 擴(kuò)展了 WebApplicationContext 佩厚,它允許通過(guò)配置的方式實(shí)例化 WebApplicationContext ,它定義了兩個(gè)重要的方法:
方法名 | 說(shuō)明 |
---|---|
setServletContext(ServletContext servletContext) | 為 Spring 設(shè)置 Web 應(yīng)用上下文说订,以便兩者整合 |
setConfigLocations(String[] configLocations) | 設(shè)置 Spring 配置文件地址抄瓦,一般情況下,配置文件地址是相對(duì)于 Web 根目錄的地址陶冷,形如 /WEB-INF/config.xml 钙姊。 但用戶也可以使用帶資源類型前綴的地址,形如 classpath:net/deniro/beans.xml埂伦。 |
2.3 初始化 WebApplicationContext
WebApplicationContext 需要 ServletContext 實(shí)例煞额,它必須在擁有 Web 容器的前提下才能完成啟動(dòng)工作 。 我們可以在 web.xml 中配置自啟動(dòng)的 Servlet 或定義 Web 容器監(jiān)聽(tīng)器( ServletContextListener )沾谜,借助這兩者中之一膊毁,就可以啟動(dòng) Spring Web 應(yīng)用上下文 。
注意:所有版本的 Web 容器都支持自啟動(dòng)的 Servlet 基跑,但只有 Servlet 2.3 及以上版本的 Web 容器才支持 Web 容器監(jiān)聽(tīng)器 婚温。 但也有些 Web 容器是例外,比如 Weblogic 8.1媳否、WebSphere 5.x栅螟、Oracle OC4J 9.0 等荆秦。
Spring 提供了用于啟動(dòng) WebApplicationContext 的 Servlet 和 Web 容器監(jiān)聽(tīng)器:
- org.springframework.web.context.ContextLoaderServlet。
- org.springframework.web.context.ContextLoaderListener力图。
2.3.1 XML 方式
下面使用 ContextLoaderListener 啟動(dòng) WebApplicationContext:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<!-- 指定 spring 配置文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring-*.xml</param-value>
</context-param>
<!-- web 容器監(jiān)聽(tīng)器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
ContextLoaderListener 通過(guò) Web 容器上下文參數(shù) contextConfigLocation 獲取 Spring 配置文件的所在位置 步绸。 可以指定多個(gè)配置文件,任意選用逗號(hào) 吃媒、空格或冒號(hào)來(lái)分隔它們瓤介。 對(duì)于未帶資源類型前綴的文件路徑, WebApplicationContext 會(huì)默認(rèn)這些路徑相對(duì)于 Web 的部署根路徑 赘那。 當(dāng)然刑桑,也可以采用帶資源類型前綴的路徑配置,比如這里的 classpath*:spring-*.xml
漓概。
如果在不支持容器監(jiān)聽(tīng)器的低版本 Web 容器中漾月,我們可采用 ContextLoaderServlet 完成啟動(dòng)工作,因?yàn)?Spring4 已經(jīng)不再支持這個(gè)類胃珍,所以我們也就不再累述咯梁肿。
因?yàn)?WebApplicationContext 需要使用到日志,所以我們把 Log4J 的配置文件放置到類路徑 WEB-INF/classes 下觅彰,這樣 Log4J 引擎即可順利啟動(dòng) 吩蔑。 如果 Log4J 配置文件放置在其他位置,就必須在 web.xml 指定 Log4J 配置文件位置 填抬。Spring 為啟用 Log4J 引擎提供了兩個(gè)類似于啟動(dòng) WebApplicationContext 的實(shí)現(xiàn)類: Log4jConfigServlet(Spring4 中不再支持) 和 Log4jConfigListener烛芬,不管采用哪種方式都必須保證能夠在裝載 Spring 配置文件前先裝載 Log4J 配置信息 。
<!-- 指定 log4j 配置文件-->
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/log4j.properties</param-value>
</context-param>
<!-- Log4j 監(jiān)聽(tīng)器-->
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
注意: Log4jConfigListener 必須放置在 ContextLoaderListener 前面飒责,保證前者先啟動(dòng)赘娄,完成裝載 Log4J 配置文件并初始化 Log4J 引擎的工作,緊接著后者再啟動(dòng) 宏蛉。
2.3.2 注解方式
也可以使用帶注解 @Configuration 的 Java 類來(lái)提供配置信息:
<!-- 使用帶注解 @Configuration 的 Java 類來(lái)提供配置信息-->
<!-- 指定 AnnotationConfigWebApplicationContext 來(lái)啟動(dòng)容器-->
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>net.deniro.config1,net.deniro.config2</param-value>
</context-param>
這時(shí)的 ContextLoaderListener 如果發(fā)現(xiàn)了 contextClass 上下文參數(shù)遣臼,就會(huì)使用參數(shù)所指定的 AnnotationConfigWebApplicationContext 來(lái)初始化容器,該實(shí)現(xiàn)類會(huì)根據(jù) contextConfigLocation 上下文參數(shù)指定的 @Configuration 的配置類所提供的配置信息來(lái)初始化容器 拾并。
2.3.3 Groovy DSL 方式
也可以使用 Groovy DSL 方式揍堰,原理與注解方式類似。
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.GroovyWebApplicationContext</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring-*.grovvy</param-value>
</context-param>
2.4 父子容器
通過(guò) HierarchicalBeanFactory 接口嗅义, Spring 的 IoC 容器可以建立父子層級(jí)關(guān)聯(lián)的容器體系屏歹,子容器可以訪問(wèn)父容器中的 Bean ,但父容器不能訪問(wèn)子容器的 Bean之碗。 在容器內(nèi)蝙眶, Bean 的 id 必須是唯一的,但子容器可以擁有一個(gè)和父容器 id 相同的 Bean继控。 父子容器層級(jí)體系增強(qiáng)了 Spring 容器架構(gòu)的擴(kuò)展性和靈活性械馆,因此第三方可以通過(guò)編程的方式胖眷,為一個(gè)已經(jīng)存在的容器添加一個(gè)或多個(gè)特殊用途的子容器武通,以提供一些額外的功能 霹崎。
Spring 使用父子容器的特性實(shí)現(xiàn)了很多能力,比如在 Spring MVC 中冶忱,展現(xiàn)層 Bean 位于一個(gè)子容器中尾菇,而業(yè)務(wù)層和持久層的 Bean 位于父容器中 。 這樣囚枪,展現(xiàn)層 Bean 就可以引用業(yè)務(wù)層和持久層的 Bean 派诬,而業(yè)務(wù)層和持久層的 Bean 則看不到展現(xiàn)層的 Bean。