Spring框架的核心功能:
- Spring 容器作為超級大工廠,負(fù)責(zé)創(chuàng)建、管理所有的Java對象,這些Java對象被稱為 Bean。
- Spring 容器管理容器中 Bean 之間的依賴關(guān)系,Spring 使用一種被稱為“依賴注入”的方式來管理 Bean 之間的依賴關(guān)系。
當(dāng)某個Java對象(調(diào)用者)需要調(diào)用另一個Java對象(被調(diào)用者)的方法時,傳統(tǒng)方式:
- 原始做法:調(diào)用者主動創(chuàng)建被依賴的對象,然后再調(diào)用被依賴對象的方法
- 簡單工廠模式:調(diào)用者先找到被依賴的工廠,然后主動通過工廠去獲取被依賴對象,最后再調(diào)用被依賴對象的方法。
使用依賴注入,不僅可以為 Bean 注入普通的屬性值,還可以注入其它 Bean 的引用。通過這種依賴注入,Java EE 應(yīng)用中的各個組件不需要以硬編碼的方式耦合在一起,甚至無需使用工廠模式。調(diào)用者獲取被依賴對象的方式由原來的主動獲取,變成被動接受,即控制反轉(zhuǎn)。控制反轉(zhuǎn)和依賴注入其實是同一種行為的兩種表達(dá),只是描述的角度不同而已。
依賴注入的兩種方式:
設(shè)值注入
IoC 容器使用成員變量的setter方法來注入被依賴對象。
<property name="axe" ref="steelAxe"></property>
構(gòu)造注入
IoC容器使用構(gòu)造器來注入被以來對象:直接調(diào)用有參數(shù)的構(gòu)造器,當(dāng)Bean實例創(chuàng)建完成之后,已經(jīng)完成了依賴關(guān)系的注入
<constructor-arg ref ="steelAxe"></constructor-arg>
兩種方式對比
這兩種注入方式?jīng)]有絕對的好壞,只是適應(yīng)的場景有所不同
設(shè)值注入的優(yōu)點:
- 與傳統(tǒng)的 JavaBean的寫法相比,程序開發(fā)人員更容易理解、接受。通過setter方法設(shè)定依賴關(guān)系顯得更加直觀自然。
- 對于復(fù)雜的依賴關(guān)系,采用構(gòu)造注入會導(dǎo)致構(gòu)造器過于臃腫,難以閱讀。
- 尤其在某些成員變量可選的情況下,多參數(shù)的構(gòu)造器更加笨重。
構(gòu)造注入的優(yōu)點:
- 構(gòu)造注入可以在構(gòu)造器中決定依賴關(guān)系的注入順序,優(yōu)先依賴的優(yōu)先注入。
- 對于依賴關(guān)系無需變化的Bean,構(gòu)造注入更有用處。因為沒有setter方法,所有的依賴關(guān)系全部在構(gòu)造器內(nèi)設(shè)定。因此無需擔(dān)心后續(xù)代碼對依賴關(guān)系產(chǎn)生破壞。
- 依賴關(guān)系只能在構(gòu)造器中設(shè)定,只有組件的創(chuàng)建者才能改變組件的依賴關(guān)系。對于組件的調(diào)用者而言,組件內(nèi)部的依賴關(guān)系完全透明,更符合高內(nèi)聚的原則。
建議采用以設(shè)值注入為主,構(gòu)造注入為輔的注入策略。對于依賴關(guān)系無變化的注入,盡量采用構(gòu)造注入;而其它依賴關(guān)系的注入,則考慮采用設(shè)值注入。
Spring容器
Spring容器最基本的接口是BeanFactory,它負(fù)責(zé)配置、創(chuàng)建、管理Bean,它有一個子接口:ApplicationContext,因此也被成為Spring上下文。Spring容器還負(fù)責(zé)管理Bean與Bean之間的依賴關(guān)系。
BeanFactory接口包含以下幾個基本方法:
boolean containsBean(String name);
<T> T getBean(Class<T> requiredType);
Object getBean(String name);
<T> T getBean(String name, Class RequiredType);
Class<?> getType(String name);
ApplicationContext是BeanFactory的子接口,對于大部分的Java EE 應(yīng)用而言,使用它作為Spring容器更為方便,其常用實現(xiàn)類:FileSystemXmlApplicationContext、ClassPathXmlApplicationContext和AnnotationConfigApplicationContext。如果在Web應(yīng)用中使用Spring容器,則通常有XmlWebApplicationContext、AnnotationCongifWebApplicationContext兩個實現(xiàn)類。
ApplicationContext允許以聲明式方式操作容器,無需手動創(chuàng)建。除了提供BeanFactory所支持的全部功能之外,還有如下額外的功能:
- 默認(rèn)初始化所有的
singleton Bean(系統(tǒng)前期創(chuàng)建ApplicationContext將會有較大的系統(tǒng)開銷,一旦初始化完成,程序后面獲取singleton Bean實例將會擁有較好的性能),也可以通過配置取消預(yù)初始化(lazy-init="true")。 - 繼承
MessageSource接口,因此提供國家化支持。 - 資源訪問。
- 事件機(jī)制。
- 同時加載多個配置文件。
- 以聲明式方式啟動并創(chuàng)建Spring容器。
創(chuàng)建Bean的三種方式
- 使用構(gòu)造器創(chuàng)建Bean實例
- 使用靜態(tài)工廠方法創(chuàng)建Bean實例
<Bean.../>元素需要制定兩個屬性:
-
class:該屬性值設(shè)置為靜態(tài)工廠類的類名 -
factory-method:該屬性指定靜態(tài)工廠方法來生產(chǎn)Bean實例
若靜態(tài)工廠方法需要參數(shù),則使用<constructor-arg.../>元素傳入
- 使用實例工廠方法創(chuàng)建Bean實例
<Bean.../>元素需要制定兩個屬性:
-
factory-bean:該屬性值設(shè)置為工廠Bean的id -
factory-method:該屬性指定實例工廠方法來生產(chǎn)Bean實例
使用這種方法時,必須將實例工廠配置成Bean的還是理;而配置靜態(tài)工廠方法創(chuàng)建Bean時無需配置工廠Bean。
深入理解容器中的Bean
Spring框架絕大部分工作都集中在對容器中Bean的管理上,包括管理容器中Bean的生命周期,使用Bean繼承等。
抽象Bean和子Bean
將多個<bean.../>配種中相同的信息提取出來,集中成配置模板——這個配置模板不是真正的Bean,Spring容器不應(yīng)該創(chuàng)建該配置模板,于是需要為該<bean.../>增加abstract="true",即抽象Bean。抽象Bean不能實例化。
將大部分相同信息配置成抽象Bean之后,將實際的Bean實例配置成該抽象Bean的子Bean即可。子Bean定義可以從父Bean繼承實現(xiàn)類,構(gòu)造參數(shù)、屬性值等配置信息。除此之外,子Bean配置可以增加新的配置信息,并可指定新的配置信息覆蓋父Bean的定義。
Bean繼承與Java繼承的區(qū)別
- 子Bean和父Bean可以是不同的類型
- Bean的繼承是實例之間的關(guān)系,主要表現(xiàn)為參數(shù)值的延續(xù)
- 子Bean不可以作為父Bean使用,不具有多態(tài)性
獲取Bean本身的id
可借助Spring提供的 BeanNameAware接口,實現(xiàn)該接口,并實現(xiàn)該接口提供的setBeanName()方法。這個方法會在容器創(chuàng)建完該Bean之后,由Spring容器自動調(diào)用。
強(qiáng)制初始化Bean
為了顯示指定被依賴Bean在目標(biāo)Bean之前初始化,可以使用depends-on屬性,該屬性可以在初始化主調(diào)Bean之前,強(qiáng)制初始化一個或多個Bean。
容器中Bean的生命周期
Spring可以管理singleton 作用于的Bean的生命周期,可以精確地知道該Bean何時被創(chuàng)建、何時被初始化完成、容器何時準(zhǔn)備銷毀該Bean實例。
對于prototype作用域的Bean,Spring僅僅負(fù)責(zé)創(chuàng)建,創(chuàng)建完成之后,Bean實例完全交給客戶端代碼管理,容器不再跟蹤其生命周期。
兩個時機(jī):
- 注入依賴關(guān)系之后
- 即將銷毀Bean之前
依賴關(guān)系注入之后的行為
Spring提供兩種方式在Bean全部屬性設(shè)置成功后執(zhí)行特定行為
使用init-method屬性,只是增加一個普通的方法,沒有代碼污染。
實現(xiàn)InitializingBean接口,實現(xiàn)其 void afterPropertiesSet() Throws Exception方法,污染代碼。
若同時使用兩種方式,則先執(zhí)行接口中的方法,再執(zhí)行屬性指定的方法,下同。
Bean銷毀之前的行為
Spring也提供了兩種方式定制Bean實例銷毀之前的特定行為
使用destroy-method屬性
實現(xiàn)DisposableBean接口,實現(xiàn)void destroy() Throws Exception方法
如何讓Spring容器優(yōu)雅地關(guān)閉呢?在JVM里注冊一個關(guān)閉鉤子(shutdown hook)
AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
// 相應(yīng)的業(yè)務(wù)代碼
// 為Spring容器注冊關(guān)閉鉤子,程序?qū)谕顺鯦VM之前關(guān)閉Spring容器
// 并保證關(guān)閉Spring之前調(diào)用singleton Dean實例的析構(gòu)回調(diào)方法。
ctx.registerShutdownHook();
協(xié)調(diào)作用于不同步的Bean
當(dāng)singleton作用域的Bean依賴prototype作用域的Bean時,Spring容器會在初始化singleton作用域的Bean之前,先創(chuàng)建prototype作用域的Bean,然后才初始化singleton Bean,并將prototype Bean注入到singleton Bean。在這種情況下,singleton Bean訪問prototype Bean時得到的永遠(yuǎn)是最初的那個prototype Bean,會產(chǎn)生不同步的情況。解決方法:
- 放棄依賴注入,代碼主動向容器請求Bean實例。
- 利用方法注入:lookup方法