Spring致力于J2EE應用的各種解決方案顾腊,而不僅僅專注于某一層解決方案粤铭。可以說Spring是企業(yè)應用開發(fā)的“一站式”選擇杂靶, Spring貫穿于表現(xiàn)層梆惯、業(yè)務層、持久層吗垮,然而Spring并不想取代那些已經(jīng)有的框架垛吗,而是以高度的開放性,與這些已有的框架進行整合烁登。
Spring目標:
- 1.讓現(xiàn)有的技術(shù)更容易使用
Spring堅持一個原則:不重新造輪子怯屉。 - 2.促進良好的編程習慣
1.概述
- Spring 是最受歡迎的企業(yè)級 Java 應用程序開發(fā)框架,數(shù)以百萬的來自世界各地的開發(fā)人員使用 Spring 框架來創(chuàng)建性能好饵沧、易于測試锨络、可重用的代碼。
Spring 框架的核心特性是可以用于開發(fā)任何 Java 應用程序狼牺,但是在 Java EE 平臺上構(gòu)建 web 應用程序是需要擴展的羡儿。 Spring 框架的目標是使 J2EE 開發(fā)變得更容易使用,通過啟用基于 POJO 編程模型來促進良好的編程實踐锁右。 - 依賴注入
Spring 最認同的技術(shù)是控制反轉(zhuǎn)的依賴注入(DI)模式失受⊙忍控制反轉(zhuǎn)(IoC)是一個通用的概念咏瑟,它可以用許多不同的方式去表達拂到,依賴注入僅僅是控制反轉(zhuǎn)的一個具體的例子。
當編寫一個復雜的 Java 應用程序時码泞,應用程序類應該盡可能的獨立于其他的 Java 類來增加這些類可重用可能性兄旬,當進行單元測試時,可以使它們獨立于其他類進行測試余寥。依賴注入(或者有時被稱為配線)有助于將這些類粘合在一起领铐,并且在同一時間讓它們保持獨立。
到底什么是依賴注入宋舷?讓我們將這兩個詞分開來看一看绪撵。這里將依賴關(guān)系部分轉(zhuǎn)化為兩個類之間的關(guān)聯(lián)。例如祝蝠,類 A 依賴于類 B∫粽現(xiàn)在,讓我們看一看第二部分绎狭,注入细溅。所有這一切都意味著類 B 將通過 IoC 被注入到類 A 中。 - AOP
Spring 框架的一個關(guān)鍵組件是面向切面的程序設計(AOP)框架儡嘶。一個程序中跨越多個點的功能被稱為橫切關(guān)注點喇聊,這些橫切關(guān)注點在概念上獨立于應用程序的業(yè)務邏輯。有各種各樣常見的很好的關(guān)于方面的例子蹦狂,比如日志記錄誓篱、聲明性事務、安全性凯楔,和緩存等等燕鸽。
在 OOP 中模塊化的關(guān)鍵單元是類,而在 AOP 中模塊化的關(guān)鍵單元是方面啼辣。AOP 幫助你將橫切關(guān)注點從它們所影響的對象中分離出來啊研,然而依賴注入幫助你將你的應用程序?qū)ο髲谋舜酥蟹蛛x出來。
2鸥拧、關(guān)鍵概念
2.1 POJO編程模型
- 簡單的java對象(plain old java objects)党远。旨在簡化java應用程序(特別是企業(yè)級)的編碼、測試以及部署等階段富弦。
- EJB編程模型存在的問題
(1)它提供了一種與運行時平臺相結(jié)合的分布式業(yè)務組建模型沟娱,運行時該平臺提供了執(zhí)行EJB組件所需要的所有中間件服務。
(2)可測試性艱難腕柜。JNDI查找是依賴注入的早期形式济似,但由于其拉式性質(zhì)矫废,使得在單元測試期間使對組件隔離變得非常困難(因為對JNDI上下文的依賴)。
(3)部署耗時且易于出錯砰蠢。
(4)EJB編程模型趨向于將開發(fā)人員的風格轉(zhuǎn)向面向過程程序設計風格蓖扑。 - POJO編程模型的優(yōu)點
(1)編寫應用程序類非常快速和簡單台舱。因為編寫的類不需要依賴任何特定的API律杠,不需要視線任何特定的接口或擴展自某一特定的框架類。
(2)以面向?qū)ο蟮姆绞絹砭幊叹和铮毮P吞幚順I(yè)務邏輯柜去,可以讓行為處理更加豐富
2.2 輕量級容器和IOC
1.輕量級容器
(1)容器(container):在該環(huán)境中,所有組件都被創(chuàng)建和裝配拆宛,并提供了所需的中間件服務嗓奢。
(2)Java EE平臺提供了多個這樣的容器,每一個專門容器為應用程序的一個特定層提供服務浑厚。
(3)Spring container也是一個容器股耽,在這個里面,應用程序被裝配瞻颂,彼此創(chuàng)建豺谈,并以一種輕量級的方式提供中間件服務。
(4)容器可以向環(huán)境提供所被預期的服務:
①生命周期管理
②依賴解析
③組件查找
④應用程序配置
其余更優(yōu)預期的服務:事務管理贡这、安全性茬末、線程管理、對象和資源池盖矫、對組件的遠程訪問丽惭、通過JMX之類的API管理組件、容器的擴展和定制辈双。
(5)一個輕量級的容器(lightweight container)包含上述所有功能责掏,同時不依賴這些API編寫應用程序代碼。輕量級容器沒有入侵性湃望,啟動非郴怀模快,不需要將其部署到一個功能齊全的Java EE應用程序服務器上就可以使用這些服務证芭。
Spring application framework是最著名的輕量級容器之一瞳浦。2.控制反轉(zhuǎn)
(1)容器及其管理的組件所提供的最重要的好處是可插拔的體系結(jié)構(gòu)。
(2)組件實現(xiàn)接口废士,用來訪問其他組件提供的服務叫潦。
(3)容器創(chuàng)建組件及所依賴的服務,并將之裝配在一起官硝。
(4)組件類中矗蕊,在運行時由容器實例將依賴組件注入組件短蜕。
對依賴項的控制由組件轉(zhuǎn)到容器,這種模式就成為反轉(zhuǎn)控制inversion of controll傻咖,簡稱IOC朋魔。
IOC是容器的基本功能之一,主要有兩種形式:
(1)依賴查找(dependence lookup)
在依賴查找中没龙,容器向管理組件提供的回調(diào)方法铺厨,被組件用來與容器進行交互并顯式的獲取它們的依賴項缎玫。
這種情況下硬纤,通常使用一個查找上下文來訪問依賴及容器管理的資源。
J2EE環(huán)境中赃磨,通過查找上下文即JNDI上下文筝家,訪問EJB組件及其他資源,如JDBCDataSource和JMS ConnectionFactory邻辉。
(2)依賴注入(dependence injection)
在依賴注入中溪王,組件提供了合適構(gòu)造函數(shù)和setter方法,以便容器可以注入依賴組件值骇。
隨著Spring application framework 和其他輕量級IOC框架出現(xiàn)莹菱,容器根據(jù)自己的配置機制來確定組件實例化和需要的依賴組件。
J2EE向Java EE演化的過程中吱瘩,使用JDNI進行的顯式依賴查找被逐步轉(zhuǎn)變?yōu)殡[式依賴注入方法道伟。
2.3 依賴注入DI
1、基本原則:應用程序?qū)ο蟛粦撠撠煵檎宜鼈兯蕾嚨馁Y源或者協(xié)作者使碾,而是由IOC容器處理對象創(chuàng)建和依賴注入蜜徽,從而導致資源查找外部化,從應用程序代碼轉(zhuǎn)移到容器票摇。
2拘鞋、給系統(tǒng)帶來的好處:
(1)完全刪除應用程序中的查找邏輯代碼,依賴項以可插拔的方式注入到目標組件中矢门。
(2)組件不需要知道依賴項的位置或類盆色,很容易進行組件的單元測試。
(3)對容器API沒有任何的依賴祟剔。
(4)不需要實現(xiàn)任何特殊接口隔躲。3、兩種依賴注入方法
(1)構(gòu)造函數(shù)注入
(2)setter注入
一個好的容器可以同時支持這兩個種方法峡扩,并允許混合使用蹭越。4、setter注入
(1)當一個對象被實例化之后就會馬上調(diào)用setter方法教届。
①該注入在組件的創(chuàng)建和初始化階段發(fā)生响鹃,并且在處理業(yè)務方法調(diào)用之前完成驾霜。
②setter方法是javabean規(guī)范的一部分。
③使用javabean屬性使簡單屬性外部化买置。
(2)優(yōu)點:組件在創(chuàng)建之后可以進行重新配置粪糙。
①組件依賴可以在運行時更改。
②現(xiàn)有的類提供了getter和setter方法來訪問它們的屬性忿项。
(3)缺點:并不是所有的依賴項都可以在使用前被注入蓉冈,從而使組件處于一種部分配置狀態(tài)。5轩触、構(gòu)造函數(shù)注入
(1)bean可以利用構(gòu)造函數(shù)參數(shù)來表達依賴項寞酿,這樣就可以在組件創(chuàng)建期間注入依賴項。
(2)出于線程安全的考慮脱柱,也需要使用構(gòu)造函數(shù)注入伐弹。
(3)可以像注入構(gòu)造函數(shù)參數(shù)那樣注入簡單的屬性。
(4)優(yōu)點
①保證容器中每一個被管理的組件都處于一致的狀態(tài)榨为,并在創(chuàng)建之后可立刻使用惨好。
②編寫的代碼量比setter注入編寫的代碼量要少一些。
(5)缺點
①在組件創(chuàng)建完畢后就無法再對組件進行重新分配随闺。除非為相關(guān)屬性提供一個setter作為構(gòu)造函數(shù)參數(shù)坞生。
②具體繼承可能有問題计济。
3.Spring體系結(jié)構(gòu)
3.1 核心容器
核心容器由spring-core,spring-beans,spring-context逆皮,spring-context-support和spring-expression(SpEL西设,Spring表達式語言柳譬,Spring Expression Language)等模塊組成群扶,它們的細節(jié)如下:
spring-core模塊提供了框架的基本組成部分,包括 IoC 和依賴注入功能笨使。
spring-beans 模塊提供 BeanFactory卿樱,工廠模式的微妙實現(xiàn),它移除了編碼式單例的需要硫椰,并且可以把配置和依賴從實際編碼邏輯中解耦繁调。
context模塊建立在由core和 beans 模塊的基礎上建立起來的,它以一種類似于JNDI注冊的方式訪問對象靶草。Context模塊繼承自Bean模塊蹄胰,并且添加了國際化(比如,使用資源束)奕翔、事件傳播裕寨、資源加載和透明地創(chuàng)建上下文(比如,通過Servelet容器)等功能。Context模塊也支持Java EE的功能宾袜,比如EJB捻艳、JMX和遠程調(diào)用等。ApplicationContext接口是Context模塊的焦點庆猫。spring-context-support提供了對第三方庫集成到Spring上下文的支持认轨,比如緩存(EhCache, Guava, JCache)、郵件(JavaMail)月培、調(diào)度(CommonJ, Quartz)嘁字、模板引擎(FreeMarker, JasperReports, Velocity)等。
spring-expression模塊提供了強大的表達式語言杉畜,用于在運行時查詢和操作對象圖纪蜒。它是JSP2.1規(guī)范中定義的統(tǒng)一表達式語言的擴展,支持set和get屬性值寻行、屬性賦值霍掺、方法調(diào)用匾荆、訪問數(shù)組集合及索引的內(nèi)容拌蜘、邏輯算術(shù)運算、命名變量牙丽、通過名字從Spring IoC容器檢索對象简卧,還支持列表的投影、選擇以及聚合等烤芦。举娩。
3.2 數(shù)據(jù)訪問/集成
數(shù)據(jù)訪問/集成層包括 JDBC,ORM构罗,OXM铜涉,JMS 和事務處理模塊,它們的細節(jié)如下:
(注:JDBC=Java Data Base Connectivity遂唧,ORM=Object Relational Mapping芙代,OXM=Object XML Mapping,JMS=Java Message Service)
JDBC 模塊提供了JDBC抽象層盖彭,它消除了冗長的JDBC編碼和對數(shù)據(jù)庫供應商特定錯誤代碼的解析纹烹。
ORM 模塊提供了對流行的對象關(guān)系映射API的集成,包括JPA召边、JDO和Hibernate等铺呵。通過此模塊可以讓這些ORM框架和spring的其它功能整合,比如前面提及的事務管理隧熙。
OXM 模塊提供了對OXM實現(xiàn)的支持片挂,比如JAXB、Castor、XML Beans音念、JiBX滋将、XStream等。
JMS 模塊包含生產(chǎn)(produce)和消費(consume)消息的功能症昏。從Spring 4.1開始随闽,集成了spring-messaging模塊。肝谭。
事務模塊為實現(xiàn)特殊接口類及所有的 POJO 支持編程式和聲明式事務管理掘宪。(注:編程式事務需要自己寫beginTransaction()、commit()攘烛、rollback()等事務管理方法魏滚,聲明式事務是通過注解或配置由spring自動處理,編程式事務粒度更細)
3.3 Web
Web 層由 Web坟漱,Web-MVC鼠次,Web-Socket 和 Web-Portlet 組成,它們的細節(jié)如下:
Web 模塊提供面向web的基本功能和面向web的應用上下文芋齿,比如多部分(multipart)文件上傳功能腥寇、使用Servlet監(jiān)聽器初始化IoC容器等。它還包括HTTP客戶端以及Spring遠程調(diào)用中與web相關(guān)的部分觅捆。赦役。
Web-MVC 模塊為web應用提供了模型視圖控制(MVC)和REST Web服務的實現(xiàn)。Spring的MVC框架可以使領(lǐng)域模型代碼和web表單完全地分離栅炒,且可以與Spring框架的其它所有功能進行集成掂摔。
Web-Socket 模塊為 WebSocket-based 提供了支持,而且在 web 應用程序中提供了客戶端和服務器端之間通信的兩種方式赢赊。
Web-Portlet 模塊提供了用于Portlet環(huán)境的MVC實現(xiàn)乙漓,并反映了spring-webmvc模塊的功能。
3.4 其他
還有其他一些重要的模塊释移,像 AOP叭披,Aspects,Instrumentation秀鞭,Web 和測試模塊趋观,它們的細節(jié)如下:
AOP 模塊提供了面向方面的編程實現(xiàn),允許你定義方法攔截器和切入點對代碼進行干凈地解耦锋边,從而使實現(xiàn)功能的代碼徹底的解耦出來皱坛。使用源碼級的元數(shù)據(jù),可以用類似于.Net屬性的方式合并行為信息到代碼中豆巨。
Aspects 模塊提供了與 AspectJ 的集成剩辟,這是一個功能強大且成熟的面向切面編程(AOP)框架。
Instrumentation 模塊在一定的應用服務器中提供了類 instrumentation 的支持和類加載器的實現(xiàn)。
Messaging 模塊為 STOMP 提供了支持作為在應用程序中 WebSocket 子協(xié)議的使用贩猎。它也支持一個注解編程模型熊户,它是為了選路和處理來自 WebSocket 客戶端的 STOMP 信息。
測試模塊支持對具有 JUnit 或 TestNG 框架的 Spring 組件的測試吭服。
4.Spring IoC容器
-
Spring 容器是 Spring 框架的核心嚷堡。容器將創(chuàng)建對象,把它們連接在一起艇棕,配置它們蝌戒,并管理他們的整個生命周期從創(chuàng)建到銷毀。Spring 容器使用依賴注入(DI)來管理組成一個應用程序的組件沼琉。這些對象被稱為 Spring Beans北苟。
通過閱讀配置元數(shù)據(jù)提供的指令,容器知道對哪些對象進行實例化打瘪,配置和組裝友鼻。配置元數(shù)據(jù)可以通過 XML,Java 注釋或 Java 代碼來表示闺骚。下圖是 Spring 如何工作的高級視圖彩扔。 Spring IoC 容器利用 Java 的 POJO 類和配置元數(shù)據(jù)來生成完全配置和可執(zhí)行的系統(tǒng)或應用程序。
- Spring的兩種容器:
1)Spring BeanFactory 容器
它是最簡單的容器葛碧,給 DI 提供了基本的支持借杰,它用org.springframework.beans.factory.BeanFactory 接口來定義。BeanFactory 或者相關(guān)的接口进泼,如 BeanFactoryAware,InitializingBean纤虽,DisposableBean乳绕,在 Spring 中仍然存在具有大量的與 Spring 整合的第三方框架的反向兼容性的目的。
2)Spring ApplicationContext 容器
該容器添加了更多的企業(yè)特定的功能逼纸,例如從一個屬性文件中解析文本信息的能力洋措,發(fā)布應用程序事件給感興趣的事件監(jiān)聽器的能力。該容器是由 org.springframework.context.ApplicationContext 接口定義杰刽。
ApplicationContext 容器包括 BeanFactory 容器的所有功能菠发。
4.1 Spring BeanFactory容器
- 這是一個最簡單的容器,它主要的功能是為依賴注入 (DI) 提供支持贺嫂,這個容器接口在 org.springframework.beans.factory.BeanFactory中被定義滓鸠。 BeanFactory 和相關(guān)的接口,比如BeanFactoryAware第喳、 DisposableBean糜俗、InitializingBean,仍舊保留在 Spring 中,主要目的是向后兼容已經(jīng)存在的和那些 Spring 整合在一起的第三方框架悠抹。
在 Spring 中珠月,有大量對 BeanFactory 接口的實現(xiàn)。其中楔敌,最常被使用的是 XmlBeanFactory 類啤挎。這個容器從一個 XML 文件中讀取配置元數(shù)據(jù),由這些元數(shù)據(jù)來生成一個被配置化的系統(tǒng)或者應用卵凑。
在資源寶貴的移動設備或者基于 applet 的應用當中侵浸, BeanFactory 會被優(yōu)先選擇。否則氛谜,一般使用的是 ApplicationContext掏觉,除非你有更好的理由選擇 BeanFactory。
package com.tutorialspoint;
public class HelloWorld {
private String message;
public void setMessage(String message){
this.message = message;
}
public void getMessage(){
System.out.println("Your Message : " + message);
}
}
- 測試程序
package com.tutorialspoint;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class MainApp {
public static void main(String[] args) {
XmlBeanFactory factory = new XmlBeanFactory
(new ClassPathResource("Beans.xml"));
HelloWorld obj = (HelloWorld) factory.getBean("helloWorld");
obj.getMessage();
}
}
1)第一步利用框架提供的 XmlBeanFactory() API 去生成工廠 bean 以及利用 ClassPathResource() API 去加載在路徑 CLASSPATH 下可用的 bean 配置文件值漫。XmlBeanFactory() API 負責創(chuàng)建并初始化所有的對象澳腹,即在配置文件中提到的 bean。
2)第二步利用第一步生成的 bean 工廠對象的 getBean() 方法得到所需要的 bean杨何。 這個方法通過配置文件中的 bean ID 來返回一個真正的對象酱塔,該對象最后可以用于實際的對象。一旦得到這個對象危虱,就可以利用這個對象來調(diào)用任何方法羊娃。
- Beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="helloWorld" class="com.tutorialspoint.HelloWorld">
<property name="message" value="Hello World!"/>
</bean>
</beans>
4.2 Spring ApplicationContext容器
- Application Context 是 spring 中較高級的容器。和 BeanFactory 類似埃跷,它可以加載配置文件中定義的 bean蕊玷,將所有的 bean 集中在一起,當有請求的時候分配 bean弥雹。 另外垃帅,它增加了企業(yè)所需要的功能,比如剪勿,從屬性文件中解析文本信息和將事件傳遞給所指定的監(jiān)聽器贸诚。這個容器在 org.springframework.context.ApplicationContext interface 接口中定義。
ApplicationContext 包含 BeanFactory 所有的功能厕吉,一般情況下酱固,相對于 BeanFactory,ApplicationContext 會更加優(yōu)秀头朱。當然运悲,BeanFactory 仍可以在輕量級應用中使用,比如移動設備或者基于 applet 的應用程序髓窜。 - 最常被使用的 ApplicationContext 接口實現(xiàn):
1)FileSystemXmlApplicationContext:該容器從 XML 文件中加載已被定義的 bean扇苞。在這里欺殿,你需要提供給構(gòu)造器 XML 文件的完整路徑。
2)ClassPathXmlApplicationContext:該容器從 XML 文件中加載已被定義的 bean鳖敷。在這里脖苏,你不需要提供 XML 文件的完整路徑,只需正確配置 CLASSPATH 環(huán)境變量即可定踱,因為棍潘,容器會從 CLASSPATH 中搜索 bean 配置文件。
3)WebXmlApplicationContext:該容器會在一個 web 應用程序的范圍內(nèi)加載在 XML 文件中已被定義的 bean崖媚。
4.3 Spring Bean定義
-
被稱作 bean 的對象是構(gòu)成應用程序的支柱也是由 Spring IoC 容器管理的亦歉。bean 是一個被實例化,組裝畅哑,并通過 Spring IoC 容器所管理的對象肴楷。這些 bean 是由用容器提供的配置元數(shù)據(jù)創(chuàng)建的,例如荠呐,已經(jīng)在先前章節(jié)看到的赛蔫,在 XML 的表單中的 定義。
bean 定義包含稱為配置元數(shù)據(jù)的信息:
1)如何創(chuàng)建一個 bean
2)bean 的生命周期的詳細信息
3)bean 的依賴關(guān)系
Spring 配置元數(shù)據(jù)
Spring IoC 容器完全由實際編寫的配置元數(shù)據(jù)的格式解耦泥张。有下面三個重要的方法把配置元數(shù)據(jù)提供給 Spring 容器:
1)基于 XML 的配置文件呵恢。
2)基于注解的配置
3)基于 Java 的配置
4.4 Spring Bean作用域
-
當在 Spring 中定義一個 bean 時,你必須聲明該 bean 的作用域的選項媚创。例如渗钉,為了強制 Spring 在每次需要時都產(chǎn)生一個新的 bean 實例,你應該聲明 bean 的作用域的屬性為 prototype钞钙。同理鳄橘,如果你想讓 Spring 在每次需要時都返回同一個bean實例,你應該聲明 bean 的作用域的屬性為 singleton歇竟。
Spring 框架支持以下五個作用域挥唠,如果你使用 web-aware ApplicationContext 時,其中三個是可用的焕议。
- Prototype是原型類型,它在我們創(chuàng)建容器的時候并沒有實例化弧关,而是當我們獲取bean的時候才會去創(chuàng)建一個對象盅安,而且我們每次獲取到的對象都不是同一個對象。根據(jù)經(jīng)驗世囊,對有狀態(tài)的bean應該使用prototype作用域别瞭,而對無狀態(tài)的bean則應該使用singleton作用域。
4.5 Spring Bean生命周期
- 理解 Spring bean 的生命周期很容易株憾。當一個 bean 被實例化時蝙寨,它可能需要執(zhí)行一些初始化使它轉(zhuǎn)換成可用狀態(tài)晒衩。同樣,當 bean 不再需要墙歪,并且從容器中移除時听系,可能需要做一些清除工作。
為了定義安裝和拆卸一個 bean虹菲,我們只要聲明帶有 init-method 和/或 destroy-method 參數(shù)的 靠胜。init-method 屬性指定一個方法,在實例化 bean 時毕源,立即調(diào)用該方法浪漠。同樣,destroy-method 指定一個方法霎褐,只有從容器中移除 bean 之后址愿,才能調(diào)用該方法。 - 如果你在非 web 應用程序環(huán)境中使用 Spring 的 IoC 容器冻璃;例如在豐富的客戶端桌面環(huán)境中响谓;那么在 JVM 中你要注冊關(guān)閉 hook。這樣做可以確保正常關(guān)閉俱饿,為了讓所有的資源都被釋放歌粥,可以在單個 beans 上調(diào)用 destroy 方法。
<bean id="helloWorld"
class="com.tutorialspoint.HelloWorld"
init-method="init" destroy-method="destroy">
<property name="message" value="Hello World!"/>
</bean>
4.6 Spring Bean后置處理器
- BeanPostProcessor 接口定義回調(diào)方法拍埠,你可以實現(xiàn)該方法來提供自己的實例化邏輯失驶,依賴解析邏輯等。你也可以在 Spring 容器通過插入一個或多個 BeanPostProcessor 的實現(xiàn)來完成實例化枣购,配置和初始化一個bean之后實現(xiàn)一些自定義邏輯回調(diào)方法嬉探。
你可以配置多個 BeanPostProcessor接口,通過設置 BeanPostProcessor 實現(xiàn)的 Ordered 接口提供的 order 屬性來控制這些 BeanPostProcessor 接口的執(zhí)行順序棉圈。
BeanPostProcessor 可以對 bean(或?qū)ο螅嵗M行操作涩堤,這意味著 Spring IoC 容器實例化一個 bean 實例,然后 BeanPostProcessor 接口進行它們的工作分瘾。
ApplicationContext 會自動檢測由 BeanPostProcessor 接口的實現(xiàn)定義的 bean胎围,注冊這些 bean 為后置處理器,然后通過在容器中創(chuàng)建 bean德召,在適當?shù)臅r候調(diào)用它白魂。
package com.tutorialspoint;
public class HelloWorld {
private String message;
public void setMessage(String message){
this.message = message;
}
public void getMessage(){
System.out.println("Your Message : " + message);
}
public void init(){
System.out.println("Bean is going through init.");
}
public void destroy(){
System.out.println("Bean will destroy now.");
}
}
- 在任何 bean 的初始化的之前和之后輸入該 bean 的名稱。你可以在初始化 bean 的之前和之后實現(xiàn)更復雜的邏輯上岗,因為你有兩個訪問內(nèi)置 bean 對象的后置處理程序的方法福荸。 InitHelloWorld.java 如下:
package com.tutorialspoint;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.BeansException;
public class InitHelloWorld implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeforeInitialization : " + beanName);
return bean; // you can return any other object as well
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("AfterInitialization : " + beanName);
return bean; // you can return any other object as well
}
}
package com.tutorialspoint;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
AbstractApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
obj.getMessage();
context.registerShutdownHook();
}
}
<bean id="helloWorld" class="com.tutorialspoint.HelloWorld"
init-method="init" destroy-method="destroy">
<property name="message" value="Hello World!"/>
</bean>
<bean class="com.tutorialspoint.InitHelloWorld" />
- 輸出如下:
BeforeInitialization : helloWorld
Bean is going through init.
AfterInitialization : helloWorld
Your Message : Hello World!
Bean will destroy now.
4.7 Spring Bean定義繼承
<bean id="helloWorld" class="com.tutorialspoint.HelloWorld">
<property name="message1" value="Hello World!"/>
<property name="message2" value="Hello Second World!"/>
</bean>
<bean id="helloIndia" class="com.tutorialspoint.HelloIndia" parent="helloWorld">
<property name="message1" value="Hello India!"/>
<property name="message3" value="Namaste India!"/>
</bean>
- 定義模板
<bean id="beanTeamplate" abstract="true">
<property name="message1" value="Hello World!"/>
<property name="message2" value="Hello Second World!"/>
<property name="message3" value="Namaste India!"/>
</bean>
<bean id="helloIndia" class="com.tutorialspoint.HelloIndia" parent="beanTeamplate">
<property name="message1" value="Hello India!"/>
<property name="message3" value="Namaste India!"/>
</bean>
5.Spring DI
- 每個基于應用程序的 java 都有幾個對象,這些對象一起工作來呈現(xiàn)出終端用戶所看到的工作的應用程序肴掷。當編寫一個復雜的 Java 應用程序時敬锐,應用程序類應該盡可能獨立于其他 Java 類來增加這些類重用的可能性背传,并且在做單元測試時,測試獨立于其他類的獨立性台夺。依賴注入(或有時稱為布線)有助于把這些類粘合在一起径玖,同時保持他們獨立。
- 假設你有一個包含文本編輯器組件的應用程序谒养,并且你想要提供拼寫檢查挺狰。標準代碼看起來是這樣的:
public class TextEditor {
private SpellChecker spellChecker;
public TextEditor() {
spellChecker = new SpellChecker();
}
}
在這里我們所做的就是創(chuàng)建一個 TextEditor 和 SpellChecker 之間的依賴關(guān)系。在控制反轉(zhuǎn)的場景中买窟,我們反而會做這樣的事情:
public class TextEditor {
private SpellChecker spellChecker;
public TextEditor(SpellChecker spellChecker) {
this.spellChecker = spellChecker;
}
}
在這里丰泊,TextEditor 不應該擔心 SpellChecker 的實現(xiàn)。SpellChecker 將會獨立實現(xiàn)始绍,并且在 TextEditor 實例化的時候?qū)⑻峁┙o TextEditor瞳购,整個過程是由 Spring 框架的控制。
在這里亏推,我們已經(jīng)從 TextEditor 中刪除了全面控制学赛,并且把它保存到其他地方(即 XML 配置文件),且依賴關(guān)系(即 SpellChecker 類)通過類構(gòu)造函數(shù)被注入到 TextEditor 類中吞杭。因此盏浇,控制流通過依賴注入(DI)已經(jīng)“反轉(zhuǎn)”,因為你已經(jīng)有效地委托依賴關(guān)系到一些外部系統(tǒng)芽狗。
- 依賴注入的第二種方法是通過 TextEditor 類的 Setter 方法绢掰,我們將創(chuàng)建 SpellChecker 實例,該實例將被用于調(diào)用 setter 方法來初始化 TextEditor 的屬性童擎。
5.1 Constructor-based dependency injection
- 當容器調(diào)用帶有一組參數(shù)的類構(gòu)造函數(shù)時滴劲,基于構(gòu)造函數(shù)的 DI 就完成了,其中每個參數(shù)代表一個對其他類的依賴顾复。
package com.tutorialspoint;
public class TextEditor {
private SpellChecker spellChecker;
public TextEditor(SpellChecker spellChecker) {
System.out.println("Inside TextEditor constructor." );
this.spellChecker = spellChecker;
}
public void spellCheck() {
spellChecker.checkSpelling();
}
}
package com.tutorialspoint;
public class SpellChecker {
public SpellChecker(){
System.out.println("Inside SpellChecker constructor." );
}
public void checkSpelling() {
System.out.println("Inside checkSpelling." );
}
}
package com.tutorialspoint;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("Beans.xml");
TextEditor te = (TextEditor) context.getBean("textEditor");
te.spellCheck();
}
}
<!-- Definition for textEditor bean -->
<bean id="textEditor" class="com.tutorialspoint.TextEditor">
<constructor-arg ref="spellChecker"/>
</bean>
<!-- Definition for spellChecker bean -->
<bean id="spellChecker" class="com.tutorialspoint.SpellChecker">
</bean>
- 輸出
Inside SpellChecker constructor.
Inside TextEditor constructor.
Inside checkSpelling.
- 如果存在不止一個參數(shù)時班挖,當把參數(shù)傳遞給構(gòu)造函數(shù)時,可能會存在歧義芯砸。要解決這個問題萧芙,那么構(gòu)造函數(shù)的參數(shù)在 bean 定義中的順序就是把這些參數(shù)提供給適當?shù)臉?gòu)造函數(shù)的順序就可以了。
- 最好的傳遞構(gòu)造函數(shù)參數(shù)的方式假丧,使用 index 屬性來顯式的指定構(gòu)造函數(shù)參數(shù)的索引末购。
- 如果你想要向一個對象傳遞一個引用,你需要使用 標簽的 ref 屬性虎谢,如果你想要直接傳遞值,那么你應該使用如上所示的 value 屬性曹质。
5.2 Setter-based dependency injection
- 當容器調(diào)用一個無參的構(gòu)造函數(shù)或一個無參的靜態(tài) factory 方法來初始化你的 bean 后婴噩,通過容器在你的 bean 上調(diào)用設值函數(shù)擎场,基于設值函數(shù)的 DI 就完成了。
- 基于構(gòu)造函數(shù)注入和基于設值函數(shù)注入中的 Beans.xml 文件的區(qū)別几莽。唯一的區(qū)別就是在基于構(gòu)造函數(shù)注入中迅办,我們使用的是〈bean〉標簽中的〈constructor-arg〉元素,而在基于設值函數(shù)的注入中章蚣,我們使用的是〈bean〉標簽中的〈property〉元素站欺。
5.3 內(nèi)部bean
- Java 內(nèi)部類是在其他類的范圍內(nèi)被定義的,同理纤垂,inner beans 是在其他 bean 的范圍內(nèi)定義的 bean矾策。
<bean id="outerBean" class="...">
<property name="target">
<bean id="innerBean" class="..."/>
</property>
</bean>
5.4 注入集合
-
如果想傳遞多個值,如 Java Collection 類型 List峭沦、Set贾虽、Map 和 Properties,應該怎么做呢吼鱼。為了處理這種情況蓬豁,Spring 提供了四種類型的集合的配置元素:
- 傳遞集合值
<bean id="javaCollection" class="com.tutorialspoint.JavaCollection">
<!-- results in a setAddressList(java.util.List) call -->
<property name="addressList">
<list>
<value>INDIA</value>
<value>Pakistan</value>
<value>USA</value>
<value>USA</value>
</list>
</property>
<!-- results in a setAddressSet(java.util.Set) call -->
<property name="addressSet">
<set>
<value>INDIA</value>
<value>Pakistan</value>
<value>USA</value>
<value>USA</value>
</set>
</property>
<!-- results in a setAddressMap(java.util.Map) call -->
<property name="addressMap">
<map>
<entry key="1" value="INDIA"/>
<entry key="2" value="Pakistan"/>
<entry key="3" value="USA"/>
<entry key="4" value="USA"/>
</map>
</property>
<!-- results in a setAddressProp(java.util.Properties) call -->
<property name="addressProp">
<props>
<prop key="one">INDIA</prop>
<prop key="two">Pakistan</prop>
<prop key="three">USA</prop>
<prop key="four">USA</prop>
</props>
</property>
</bean>
- 注入 bean 的引用
<!-- Bean Definition to handle references and values -->
<bean id="..." class="...">
<!-- Passing bean reference for java.util.List -->
<property name="addressList">
<list>
<ref bean="address1"/>
<ref bean="address2"/>
<value>Pakistan</value>
</list>
</property>
<!-- Passing bean reference for java.util.Set -->
<property name="addressSet">
<set>
<ref bean="address1"/>
<ref bean="address2"/>
<value>Pakistan</value>
</set>
</property>
<!-- Passing bean reference for java.util.Map -->
<property name="addressMap">
<map>
<entry key="one" value="INDIA"/>
<entry key ="two" value-ref="address1"/>
<entry key ="three" value-ref="address2"/>
</map>
</property>
</bean>
- 注入 null 和空字符串的值
<bean id="..." class="exampleBean">
<property name="email" value=""/>
</bean>
<bean id="..." class="exampleBean">
<property name="email"><null/></property>
</bean>
6.Spring Beans自動裝配
- Spring 容器可以在不使用<constructor-arg>和<property> 元素的情況下自動裝配相互協(xié)作的 bean 之間的關(guān)系,這有助于減少編寫一個大的基于 Spring 的應用程序的 XML 配置的數(shù)量菇肃。
-
下列自動裝配模式地粪,它們可用于指示 Spring 容器為來使用自動裝配進行依賴注入。你可以使用<bean>元素的 autowire 屬性為一個 bean 定義指定自動裝配模式琐谤。
-
局限性
6.1 byName
- 這種模式由屬性名稱指定自動裝配蟆技。Spring 容器看作 beans,在 XML 配置文件中 beans 的 auto-wire 屬性設置為 byName笑跛。然后付魔,它嘗試將它的屬性與配置文件中定義為相同名稱的 beans 進行匹配和連接。如果找到匹配項飞蹂,它將注入這些 beans几苍,否則,它將拋出異常陈哑。
例如妻坝,在配置文件中,如果一個 bean 定義設置為自動裝配 byName惊窖,并且它包含 spellChecker 屬性(即刽宪,它有一個 setSpellChecker(...) 方法),那么 Spring 就會查找定義名為 spellChecker 的 bean界酒,并且用它來設置這個屬性圣拄。你仍然可以使用 <property> 標簽連接其余的屬性。
<!-- Definition for textEditor bean -->
<bean id="textEditor" class="com.tutorialspoint.TextEditor"
autowire="byName">
<property name="name" value="Generic Text Editor" />
</bean>
<!-- Definition for spellChecker bean -->
<bean id="spellChecker" class="com.tutorialspoint.SpellChecker">
</bean>
6.2 byType
- 這種模式由屬性類型指定自動裝配毁欣。Spring 容器看作 beans庇谆,在 XML 配置文件中 beans 的 autowire 屬性設置為 byType岳掐。然后,如果它的 type 恰好與配置文件中 beans 名稱中的一個相匹配饭耳,它將嘗試匹配和連接它的屬性串述。如果找到匹配項,它將注入這些 beans寞肖,否則纲酗,它將拋出異常。
例如新蟆,在配置文件中觅赊,如果一個 bean 定義設置為自動裝配 byType,并且它包含 SpellChecker 類型的 spellChecker 屬性栅葡,那么 Spring 就會查找定義名為 SpellChecker 的 bean茉兰,并且用它來設置這個屬性。你仍然可以使用 <property> 標簽連接其余屬性欣簇。
<!-- Definition for textEditor bean -->
<bean id="textEditor" class="com.tutorialspoint.TextEditor"
autowire="byType">
<property name="name" value="Generic Text Editor" />
</bean>
<!-- Definition for spellChecker bean -->
<bean id="SpellChecker" class="com.tutorialspoint.SpellChecker">
</bean>
6.3 由構(gòu)造函數(shù)自動裝配
- 這種模式與 byType 非常相似规脸,但它應用于構(gòu)造器參數(shù)。Spring 容器看作 beans熊咽,在 XML 配置文件中 beans 的 autowire 屬性設置為 constructor莫鸭。然后,它嘗試把它的構(gòu)造函數(shù)的參數(shù)與配置文件中 beans 名稱中的一個進行匹配和連線横殴。如果找到匹配項被因,它會注入這些 bean,否則衫仑,它會拋出異常梨与。
例如,在配置文件中文狱,如果一個 bean 定義設置為通過構(gòu)造函數(shù)自動裝配粥鞋,而且它有一個帶有 SpellChecker 類型的參數(shù)之一的構(gòu)造函數(shù),那么 Spring 就會查找定義名為 SpellChecker 的 bean瞄崇,并用它來設置構(gòu)造函數(shù)的參數(shù)呻粹。你仍然可以使用 <constructor-arg> 標簽連接其余屬性。
<!-- Definition for textEditor bean -->
<bean id="textEditor" class="com.tutorialspoint.TextEditor"
autowire="constructor">
<constructor-arg value="Generic Text Editor"/>
</bean>
<!-- Definition for spellChecker bean -->
<bean id="SpellChecker" class="com.tutorialspoint.SpellChecker">
</bean>
7.Spring 基于注解的配置
- 使用注解來配置依賴注入苏研。而不是采用 XML 來描述一個 bean 連線等浊,你可以使用相關(guān)類,方法或字段聲明的注解摹蘑,將 bean 配置移動到組件類本身筹燕。
7.1 @Required
- @Required 注釋應用于 bean 屬性的 setter 方法,它表明受影響的 bean 屬性在配置時必須放在 XML 配置文件中,否則容器就會拋出一個 BeanInitializationException 異常庄萎。下面顯示的是一個使用 @Required 注釋的示例踪少。
package com.tutorialspoint;
import org.springframework.beans.factory.annotation.Required;
public class Student {
private Integer age;
private String name;
@Required
public void setAge(Integer age) {
this.age = age;
}
public Integer getAge() {
return age;
}
@Required
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
<!-- Definition for student bean -->
<bean id="student" class="com.tutorialspoint.Student">
<property name="name" value="Zara" />
<property name="age" value="11"/>
</bean>
7.2 @Autowired
- @Autowired 注釋對在哪里和如何完成自動連接提供了更多的細微的控制朴乖。
@Autowired 注釋可以在 setter 方法中被用于自動連接 bean拴还,就像 @Autowired 注釋厉斟,容器,一個屬性或者任意命名的可能帶有多個參數(shù)的方法忍捡。 - Setter 方法中的 @Autowired
你可以在 XML 文件中的 setter 方法中使用 @Autowired 注釋來除去 元素。當 Spring遇到一個在 setter 方法中使用的 @Autowired 注釋切黔,它會在方法中視圖執(zhí)行 byType 自動連接砸脊。
package com.tutorialspoint;
import org.springframework.beans.factory.annotation.Autowired;
public class TextEditor {
private SpellChecker spellChecker;
@Autowired
public void setSpellChecker( SpellChecker spellChecker ){
this.spellChecker = spellChecker;
}
public SpellChecker getSpellChecker( ) {
return spellChecker;
}
public void spellCheck() {
spellChecker.checkSpelling();
}
}
<!-- Definition for textEditor bean without constructor-arg -->
<bean id="textEditor" class="com.tutorialspoint.TextEditor">
</bean>
<!-- Definition for spellChecker bean -->
<bean id="spellChecker" class="com.tutorialspoint.SpellChecker">
</bean>
- 屬性中的@Autowired
你可以在屬性中使用 @Autowired 注釋來除去 setter 方法。當時使用 為自動連接屬性傳遞的時候纬霞,Spring 會將這些傳遞過來的值或者引用自動分配給那些屬性凌埂。
package com.tutorialspoint;
import org.springframework.beans.factory.annotation.Autowired;
public class TextEditor {
@Autowired
private SpellChecker spellChecker;
public TextEditor() {
System.out.println("Inside TextEditor constructor." );
}
public SpellChecker getSpellChecker( ){
return spellChecker;
}
public void spellCheck(){
spellChecker.checkSpelling();
}
}
<!-- Definition for textEditor bean -->
<bean id="textEditor" class="com.tutorialspoint.TextEditor">
</bean>
<!-- Definition for spellChecker bean -->
<bean id="spellChecker" class="com.tutorialspoint.SpellChecker">
</bean>
- 構(gòu)造函數(shù)中的 @Autowired
你也可以在構(gòu)造函數(shù)中使用 @Autowired。一個構(gòu)造函數(shù) @Autowired 說明當創(chuàng)建 bean 時诗芜,即使在 XML 文件中沒有使用 元素配置 bean 瞳抓,構(gòu)造函數(shù)也會被自動連接。
package com.tutorialspoint;
import org.springframework.beans.factory.annotation.Autowired;
public class TextEditor {
private SpellChecker spellChecker;
@Autowired
public TextEditor(SpellChecker spellChecker){
System.out.println("Inside TextEditor constructor." );
this.spellChecker = spellChecker;
}
public void spellCheck(){
spellChecker.checkSpelling();
}
}
<!-- Definition for textEditor bean without constructor-arg -->
<bean id="textEditor" class="com.tutorialspoint.TextEditor">
</bean>
<!-- Definition for spellChecker bean -->
<bean id="spellChecker" class="com.tutorialspoint.SpellChecker">
</bean>
- @Autowired 的(required=false)選項
默認情況下伏恐,@Autowired 注釋意味著依賴是必須的孩哑,它類似于 @Required 注釋,然而翠桦,你可以使用 @Autowired 的 (required=false) 選項關(guān)閉默認行為横蜒。
7.3 @Qualifier 注釋
- 可能會有這樣一種情況,當你創(chuàng)建多個具有相同類型的 bean 時销凑,并且想要用一個屬性只為它們其中的一個進行裝配丛晌,在這種情況下,你可以使用 @Qualifier 注釋和 @Autowired 注釋通過指定哪一個真正的 bean 將會被裝配來消除混亂
package com.tutorialspoint;
public class Student {
private Integer age;
private String name;
public void setAge(Integer age) {
this.age = age;
}
public Integer getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
package com.tutorialspoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
public class Profile {
@Autowired
@Qualifier("student1")
private Student student;
public Profile(){
System.out.println("Inside Profile constructor." );
}
public void printAge() {
System.out.println("Age : " + student.getAge() );
}
public void printName() {
System.out.println("Name : " + student.getName() );
}
}
package com.tutorialspoint;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
Profile profile = (Profile) context.getBean("profile");
profile.printAge();
profile.printName();
}
}
<context:annotation-config/>
<!-- Definition for profile bean -->
<bean id="profile" class="com.tutorialspoint.Profile">
</bean>
<!-- Definition for student1 bean -->
<bean id="student1" class="com.tutorialspoint.Student">
<property name="name" value="Zara" />
<property name="age" value="11"/>
</bean>
<!-- Definition for student2 bean -->
<bean id="student2" class="com.tutorialspoint.Student">
<property name="name" value="Nuha" />
<property name="age" value="2"/>
</bean>
7.4 Spring JSR-250 注釋
- Spring還使用基于 JSR-250 注釋斗幼,它包括 @PostConstruct澎蛛, @PreDestroy 和 @Resource 注釋。
為了定義一個 bean 的安裝和卸載孟岛,我們使用 init-method 和/或 destroy-method 參數(shù)簡單的聲明一下 瓶竭。init-method 屬性指定了一個方法,該方法在 bean 的實例化階段會立即被調(diào)用渠羞。同樣地斤贰,destroy-method 指定了一個方法,該方法只在一個 bean 從容器中刪除之前被調(diào)用次询。
你可以使用 @PostConstruct 注釋作為初始化回調(diào)函數(shù)的一個替代荧恍,@PreDestroy 注釋作為銷毀回調(diào)函數(shù)的一個替代。
package com.tutorialspoint;
import javax.annotation.*;
public class HelloWorld {
private String message;
public void setMessage(String message){
this.message = message;
}
public String getMessage(){
System.out.println("Your Message : " + message);
return message;
}
@PostConstruct
public void init(){
System.out.println("Bean is going through init.");
}
@PreDestroy
public void destroy(){
System.out.println("Bean will destroy now.");
}
}
<context:annotation-config/>
<bean id="helloWorld"
class="com.tutorialspoint.HelloWorld"
init-method="init" destroy-method="destroy">
<property name="message" value="Hello World!"/>
</bean>
- 可以在字段中或者 setter 方法中使用 @Resource 注釋,它和在 Java EE 5 中的運作是一樣的送巡。@Resource 注釋使用一個 ‘name’ 屬性摹菠,該屬性以一個 bean 名稱的形式被注入。你可以說骗爆,它遵循 by-name 自動連接語義次氨。
package com.tutorialspoint;
import javax.annotation.Resource;
public class TextEditor {
private SpellChecker spellChecker;
@Resource(name= "spellChecker")
public void setSpellChecker( SpellChecker spellChecker ){
this.spellChecker = spellChecker;
}
public SpellChecker getSpellChecker(){
return spellChecker;
}
public void spellCheck(){
spellChecker.checkSpelling();
}
}
7.5 基于Java的配置
- 基于 Java 的配置選項,可以使你在不用配置 XML 的情況下編寫大多數(shù)的 Spring
- @Configuration 和 @Bean 注解
帶有 @Configuration 的注解類表示這個類可以使用 Spring IoC 容器作為 bean 定義的來源摘投。@Bean 注解告訴 Spring煮寡,一個帶有 @Bean 的注解方法將返回一個對象,該對象應該被注冊為在 Spring 應用程序上下文中的 bean犀呼。
package com.tutorialspoint;
import org.springframework.context.annotation.*;
@Configuration
public class HelloWorldConfig {
@Bean
public HelloWorld helloWorld(){
return new HelloWorld();
}
}
等價于:
<beans>
<bean id="helloWorld" class="com.tutorialspoint.HelloWorld" />
</beans>
public static void main(String[] args) {
ApplicationContext ctx =
new AnnotationConfigApplicationContext(HelloWorldConfig.class);
HelloWorld helloWorld = ctx.getBean(HelloWorld.class);
helloWorld.setMessage("Hello World!");
helloWorld.getMessage();
}
- 可以加載各種配置類
package com.tutorialspoint;
import org.springframework.context.annotation.*;
@Configuration
public class TextEditorConfig {
@Bean
public TextEditor textEditor(){
return new TextEditor( spellChecker() );
}
@Bean
public SpellChecker spellChecker(){
return new SpellChecker( );
}
}
package com.tutorialspoint;
public class TextEditor {
private SpellChecker spellChecker;
public TextEditor(SpellChecker spellChecker){
System.out.println("Inside TextEditor constructor." );
this.spellChecker = spellChecker;
}
public void spellCheck(){
spellChecker.checkSpelling();
}
}
package com.tutorialspoint;
public class SpellChecker {
public SpellChecker(){
System.out.println("Inside SpellChecker constructor." );
}
public void checkSpelling(){
System.out.println("Inside checkSpelling." );
}
}
package com.tutorialspoint;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.*;
public class MainApp {
public static void main(String[] args) {
ApplicationContext ctx =
new AnnotationConfigApplicationContext(TextEditorConfig.class);
TextEditor te = ctx.getBean(TextEditor.class);
te.spellCheck();
}
}
- @Import注解
允許從另一個配置類中加載 @Bean 定義幸撕。
@Configuration
public class ConfigA {
@Bean
public A a() {
return new A();
}
}
@Configuration
@Import(ConfigA.class)
public class ConfigB {
@Bean
public B a() {
return new A();
}
}
public static void main(String[] args) {
ApplicationContext ctx =
new AnnotationConfigApplicationContext(ConfigB.class); //只要指定B即可
// now both beans A and B will be available...
A a = ctx.getBean(A.class);
B b = ctx.getBean(B.class);
}
- 生命周期回調(diào)
@Bean 注解支持指定任意的初始化和銷毀的回調(diào)方法,就像在 bean 元素中 Spring 的 XML 的初始化方法和銷毀方法的屬性外臂。
public class Foo {
public void init() {
// initialization logic
}
public void cleanup() {
// destruction logic
}
}
@Configuration
public class AppConfig {
@Bean(initMethod = "init", destroyMethod = "cleanup" )
public Foo foo() {
return new Foo();
}
}
- 指定Bean的作用域
@Configuration
public class AppConfig {
@Bean
@Scope("prototype")
public Foo foo() {
return new Foo();
}
}
7.6 Spring 中的事件處理
- Spring 的核心是 ApplicationContext坐儿,它負責管理 beans 的完整生命周期。當加載 beans 時宋光,ApplicationContext 發(fā)布某些類型的事件貌矿。例如,當上下文啟動時跃须,ContextStartedEvent 發(fā)布站叼,當上下文停止時,ContextStoppedEvent 發(fā)布菇民。
通過 ApplicationEvent 類和 ApplicationListener 接口來提供在 ApplicationContext 中處理事件尽楔。如果一個 bean 實現(xiàn) ApplicationListener,那么每次 ApplicationEvent 被發(fā)布到 ApplicationContext 上第练,那個 bean 會被通知阔馋。 - 由于 Spring 的事件處理是單線程的,所以如果一個事件被發(fā)布娇掏,直至并且除非所有的接收者得到的該消息呕寝,該進程被阻塞并且流程將不會繼續(xù)。因此婴梧,如果事件處理被使用下梢,在設計應用程序時應注意。
- 標準事件
1)ContextRefreshedEvent
ApplicationContext 被初始化或刷新時塞蹭,該事件被發(fā)布孽江。這也可以在 ConfigurableApplicationContext 接口中使用 refresh() 方法來發(fā)生。
2)ContextStartedEvent
當使用 ConfigurableApplicationContext 接口中的 start() 方法啟動 ApplicationContext 時番电,該事件被發(fā)布岗屏。你可以調(diào)查你的數(shù)據(jù)庫辆琅,或者你可以在接受到這個事件后重啟任何停止的應用程序。
3)ContextStoppedEvent
當使用 ConfigurableApplicationContext 接口中的 stop() 方法停止 ApplicationContext 時这刷,發(fā)布這個事件婉烟。你可以在接受到這個事件后做必要的清理的工作。
4)ContextClosedEvent
當使用 ConfigurableApplicationContext 接口中的 close() 方法關(guān)閉 ApplicationContext 時暇屋,該事件被發(fā)布似袁。一個已關(guān)閉的上下文到達生命周期末端;它不能被刷新或重啟率碾。
5)RequestHandledEvent
這是一個 web-specific 事件叔营,告訴所有 bean HTTP 請求已經(jīng)被服務。