Spring學習指南 內容提要 Spring框架是以簡化J2EE應用程式開發為特定目標而創建的,是當前最流行的Java開發框架。 本書從介紹Spring框架入手,針對Spring4.3和Java8介紹bean的配置、依賴註入、定義bean、基於Java的容器、AOP、Spring Data、Spri ...
Spring學習指南
內容提要
Spring框架是以簡化J2EE應用程式開發為特定目標而創建的,是當前最流行的Java開發框架。
本書從介紹Spring框架入手,針對Spring4.3和Java8介紹bean的配置、依賴註入、定義bean、基於Java的容器、AOP、Spring Data、Spring MVC等知識,旨在幫助讀者更輕鬆的學習Spring框架的方法。
第一章 Spring框架簡介
簡介
1、創建結構良好、易於維護和易於測試的應用程式是開發者的職責
2、Spring框架是SpringSource出品的一個用於簡化Java企業級應用開發的開源應用程式框。他提供了開發一個結構良好的,可維護和易於測試的應用所需的基礎設施
3、Spring框架的核心是提供了依賴註入(Dependency Injection,DI)機制的控制反轉(Inversion of Control,IoC)容器。
註意:在本書中,我們將以一個名為MyBank的網上銀行應用為例,介紹Spring框架的功能
Spring框架的模塊
Spring框架有多個模塊組成,他們提供應用開發功能進行分組,下麵描述Spring框架中各個模塊組
模塊組 | 描述 |
---|---|
Core container | 包含構成Spring框架基礎的模塊,該組中的spring-core和spring-beans模塊提供了Spring的DI功能和IoC容器實現。spring-expressions模塊為在Spring應用中通過Spring表達式語言配置應用程式對象提供了支持 |
AOP and instrumentation | 包含支持AOP(面向切麵編程)和類工具模塊,The spring-aop模塊提供了Spring的AOP功能spring-instrument提供了對類工具的支持 |
Messaging | 包含簡化開發基於消息的應用的spring-messaging模塊 |
Data Access/Integration | 包含簡化與資料庫和消息提供者交互的模塊。spirng-jdbc模塊簡化了用JDBC於資料庫交互,spring-orm模塊提供了與ORM(對象映射關係)框架的集成,如JPA何Hibernate。spring-jms模塊簡化了與JMS提供者的交互。此模塊組還包含了spring-tx模塊,該模塊提供了編程式與聲明式事務管理 |
Web | 包含簡化開發web和portlet應用的模塊。spirng-web和spirng-webmvc模塊都是用於開發web應用和RESTful的web服務的。spring-websocket模塊支持使用WebSocket開發Web應用 |
Test | 包含spirng-test模塊,該模塊簡化了創建單元和集成測試 |
//讀者註:spring websocket用於實現前後端通信
==總結==
如圖所示,Spring涵蓋了企業應用程式開發的各個方面,可以使用Spring開發Web應用程式、訪問資料庫、管理事務、創建單元測試和集成測試等。在設計Spring框架模塊時,你只需要引入應用程式所需要的模塊。例如,在應用程式中使用Spring的DI功能,只需要引入Core container組中的模塊
Spring IoC容器
一個Java應用程式,由互相調用以提供應用程式行為的一組對象組成。某個對象調用其他對象稱為它的依賴項,例如,如果對象X調用了對象Y和Z,那麼Y和Z就是對象X的依賴項。DI是一種設計模式,其中對象的依賴項通常被指定為其構造函數和setter方法的參數。並且,這些依賴項將在這些對象創建時註入到該對象中。
在Spring應用程式中,Spring IoC容器負責創建應用程式對象並註入他們的依賴項。Spring容器創建和管理的應用對象稱為Bean。由於Spring容器負責將應用程式對象組合在一起,因此不需要實現諸如工廠或者服務定位器等設計模式來構成應用。因為創建和註入依賴項的不是應用程式的對象,而是Spring容器。所以DI也稱為控制反轉(IoC)。
假設MyBank應用程式包含FixedDepositController和FixedDepositService兩個對象。FixedDepositController依賴於FixedDepositService。
package sample.spring.chapter01.bankapp;
//因為FixedDepositController調用了FixedDepositService,所以FixedDepositService就是FixedDepositController的依賴項
public class FixedDepositController {
private FixedDepositService fixedDepositService;
//創建FixedDepositController的構造函數
//用於調用FixedDepositService的save方法
public FixedDepositController(){
fixedDepositService = new FixedDepositService();
}
//
public boolean submit(){
//保存定期存款明細
fixedDepositService.save();
return true;
}
}
如果將FixedDepositController配置為一個Spring Bean,首先要修改程式實例中的FixedDepositController類,讓他接收FixedDepositService依賴作為構造函數或者setter方法的參數,修改後的FixedDepositController類。
package sample.spring.chapter01.bankapp;
public class FixedDepositController {
private FixedDepositService fixedDepositService;
public FixedDepositController(FixedDepositService fixedDepositService){
this.fixedDepositService = fixedDepositService;
}
public boolean submit(){
fixedDepositService.save();
return true;
}
}
FixedDepositService示例現在已經作為構造函數參數傳遞到FixedDepositController實例中,現在的FixedDepositService類可以配置一個Spring bean。註意,FixedDepositController類並沒有實現或者繼承任何spring的介面或者類。
在基於Spring的應用程式中,有關程式對象及其依賴項的信息都是由配置元數據來指定的。Spring IoC容器讀取應用程式的配置元數據來實例化應用程式對象並註入它們的依賴項。
//展示MyController和MyService兩個類的應用配置元數據
<beans....>
<bean id="myController" class="sample.spring.controller.MyController">
<constructor-arg ref="myService"></constructor-arg>
</bean>
<bean id="myService" class="sample.spring.service.MyService"></bean>
</beans>
每個
//讀者認為上面這句話的意思就是這行代碼的意思
public FixedDepositController(FixedDepositService fixedDepositService){
this.fixedDepositService = fixedDepositService;
}
Spring 容器讀取應用程式的配置元數據後,創建由
使用 Spring 框架的好處
- Spring 負責應用程式對象的創建並註入他們的依賴項。簡化了Java應用程式的組成
- Spring 推動了POJO的形式來開發應用程式
讀者註://POJO:Plain Ordinary Java Object,簡單的Java對象,實際就是普通的JavaBeans
讓我們快速的通過幾個例子來更好的理解使用Spring開發應用程式有哪些好處
1. 管理本地和全局事務一致的方法
如果你正在使用Spring開發一個需要事務的應用程式,那麼可以使用Spring的聲明式事務管理來管理事務
MyBank應用程式中的FixedDepositService類,如下所示
public class FixedDepositService{
public FixedDepositDetails getFixedDepositDetails(....){....}
public boolean createFixedDeposit(FixedDepositDetails FixedDepositDetails){......}
}
FixedDepositService類是用來定義存款業務中創建和取回明細方法的POJO類,
一位客戶在上面的表單中輸入了定期存款金額、存期、電子郵箱信息、並單擊保存來創建一筆新的定期存款,此時會調用在FixedDepositService中的createFixedDeposit方法來創建存款,createFixedDeposit 方法該客戶銀行賬戶中扣除他輸入的金額並創建一筆等額的定期存款。
假定關於客戶銀行餘額的信息存在數據表BANK_ACCOUNT_DETAILS中,定期存款的明細存在數據表FIXED_DEPOSIT_DETAILS表中。如果客戶創建了一筆金額為x的定期存款,應該在BANK_ACCOUNT_DETAILS表中減去x併在FIXED_DEPOSIT_DETAILS表中插入一條記錄來反映這筆新加的定期存款。如果BANK_ACCOUNT_DETAILS表沒有更新或者新的記錄沒有插入FIXED_DEPOSIT_DETAILS表,這會讓系統處於不一致狀態。這意味著createFixedDeposit方法必須在一個事務中執行
public class FixedDepositService{
public FixedDepositDetaile getFixedDepositDetails(....){....}
public boolean createFixedDeposit(FixedDepositDetails fixedDepositDetails){
Connection con = ....;
try{
con.setAutoCommit(false);
//--執行修改資料庫表的sql語句
con.commit();
}catch(SQLException sqle){
if(con !=null){
con.rollback();
}
}
}
}
上面代碼展示瞭如何在createFixedDeposit方法中以編程方式使用JDBC連接對象管理資料庫事務。這種方式適合只涉及單個資料庫的應用場景。具體資源相關的事務,如與JDBC連接相關的事務,稱為本地事務。
當多個事務性資源都有涉及,使用JTA(Java事務API)來管理事務時,例如要在同一個事務中將JMS消息發送到消息中間件(一種事務資源)並更新資料庫(另一種事務資源),則必須使用一個JTA事務管理器管理事務。JTA事務也稱為全局(或分散式)事務。要使用JTA,需要先從JNDI獲取UserTransaction對象(這是JTA API的一部分),並編程開始和提交(回滾)事務。
如你所見,可以使用JDBC連接(本地事務)或userTransaction(對於全局事務)對象以編程方式管理事務。但是請註意,本地事務無法在全局事務中運行。這意味著如果要在createFixedDeposit資料庫更新方法,使之成為JTA事務的一部分,則需要修改createFixedDeposit方法,用UserTransaction對象進行事務管理。
Spring 通過提供一個抽象層來簡化事務管理,從而提供管理本地和全局事務的一致方法。這意味著如果用Spring的事務抽象寫createfixeddeposit方法,那麼從本地切換到全局事務管理時不需要修改方法,反之亦然。Spring的事務抽象將在第八章詳細說明。
//讀者註:JTA相關的博文https://blog.csdn.net/qingmuluoyang/article/details/82961801
2. 聲明式事務管理
Spring 提供了使用聲明式事務管理的選項,可以在一個方法上使用Spring 的@Transactional註解並讓Spring 來處理事務
public class FixfedDepositService{
public FixedDepositDetails getFixedDepositDetails(....)(.....)
@Transactional
public boolean createFixedDeposit(FixedDepositDetails fixedDepositDetails){....}
}
FixfedDepositService類沒有實現任何介面或繼承任何Spring 特定的類以得到Spring的事務管理能力。Spring框架透明的通過@Transactional 註解為createFixedDeposit方法提供事務管理功能。這說明Spring 是一個非侵入性的框架,因為他不需要應用對象依賴於Spirng 特定的類或介面,由於事務管理是由Spring接管的。因此不需要直接使用事務管理API來管理事務。
3. 安全
對於任何Java應用程式來說,安全都是一個重要的方面。Spring Security是一個SpringSoure置於Spring框架頂層的項目,它提供了身份驗證和授權功能,可以用來保護Java應用程式。下麵以3個在MyBank應用程式中認證過的用戶角色為例進行說明,即LOAN_CUSTOMER、SAVINGS_ACCOUNT_CUSTOMER和APPLICATION_ADMIN。調用FixedDepositService類中createFixedDeposit方法的客戶必須是相關的SAVINGS_ACCOUNT_CUSTOMER或者擁有APPLICATION_ADMIN角色。而使用Spring Security時,你可以通過在createFixedDeposit方法上添加Spring Security的@Secured註解來輕鬆地解決這個問題
//使用@Secured註解的createFixedDeposit方法
import org.springframework.transaction.annotation.Transactional;
import org.springframework.security.access.annotation.Secured;
public class FixedDepositService{
public FixedDepositDetails getFixedDepositDetails(....){....}
@Transactional
@Secured({"SAVINGS_ACCOUNT_CUSTOMER","APPLICATION_ADMIN"})
public boolean createFixedDeposit(FixedDepositDetails fixedDepositDetails){....}
}
如果用@Secured給一個方法加註解,安全特性將被Spring Security框架透明地應用到該方法上。上面代碼說明,為了實現方法級別的安全,你無須繼承或實現任何Spring特定類或介面,而且不需要再業務方法中寫安全相關的代碼。
我們將在第16章詳細討論Spring Security框架.
4. JMX(Java 管理擴展)
Spring對JMX的支持可以讓你非常簡單地將JMX技術融合到應用程式中。
假設Mybank應用程式的定期存款功能應該只在每天早上9點到下午6點的時間段提供給客戶。為了滿足這個要求,需要在FixedDepositService中增加一個變數,以此作為一個標誌標明定期存款服務是否活躍。
//使用活躍變數的FixedDepositService類
public class FixedDepositService{
private boolean active;
public FixedDepositDetails getFixedDepositDetails(...){
if(active){....}
}
public boolean createFixedDeposit(FixedDepositDetails fixedDepositDetails){
if(active){....}
}
public void activateService(){
active = true;
}
public void deactivateService(){
active = false;
}
}
上述代碼表明,FixedDepositService類中加了一個名為active的變數,如果active變數的值為true,getFixedDepositDetails 和 createFixedDeposit方法將按照預期工作。如果active變數的值為false,getFixedDepositDetails 和 createFixedDeposit方法將拋出一個異常。表明定期存款服務當前不可用。activateService 和 deactivateService 方法分別將 active 變數的值置為 true 和 false。
那麼,誰調用 activeService 和 deactivateService 方法呢? 假設有一個名為Bank App Scheduler 的調度應用程式,分別在上午9:00 和下午 6:00 執行 activateservice 和 deactivateservice 方法 。Bank App Scheduler 應用使用 JMX(Java 管理擴展) API 與 FixedDepositService 實例遠程交互。
Bank App Scheduler 使用 JMX改變 FixedDepositService 中 active 變數的值,你需要將 FixedDepositService實例在一個可被管理的 bean (或者成為MBean) 伺服器上註冊為一個 MBean ,並將 FixedDepositService 中的activateService 和 deactivateService方法暴露為 JMX 操作方法 。在Spring 中,你可以通過在一個類上添加 Spring 的 @ManagedResource 註釋來將一個類的實例註冊到MBean伺服器上。並且可以使用 Spring @ManagedOperation 註釋將該類的方法暴露為 JMX 操作方法。
下麵展示使用 @ManagedResource 和 @ManagedOperation 註釋將 FixedDepositService 類的實例註冊到MBean伺服器,並將activateService 和 deactivateService 方法暴露為 JMX 操作方法
//使用 Spring JMX 支持的FixedDepositService類
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource;
@ManagedResource(objectName = "fixed_deposit_service:name = FixedDepositService")
public class FixedDepositService{
private boolean active;
public FixedDepositDetails getFixedDepositDetails(...){
if(active){....}
}
public boolean createFixedDeposit(FixedDepositDetails fixedDepositDetails){
if(active){....}
}
@ManagedOperation
public void activateService(){
active = true;
}
@ManagedOperation
public void deactivateService(){
active = false;
}
}
上面代碼表明 FixedDepositService 類將他的實例註冊到MBean伺服器並暴露他的方法為JMX 操作方法時並沒有直接使用JMX API。
讀者總結:為了實現定期存款功能每天只在9點到6點之間開發,需要在FixedDepositService中添加一個boolean 的變數 active ,當 active 為 true 時就開啟功能,否則拋出服務不可用。要操作這個 active 的值,需要藉助到 JMX(Java 管理擴展)
5. JMS(Java 消息服務)
Spring 的 JMS 支持簡化了從 JMS 提供者發送和接收消息。
在 MyBank 應用程式中,當客戶通過電子郵件提交一個接收其定期存款明細的請求時,FixedDepositService 將請求的明細發送到 JMS 消息中間件(比如 ActiveMQ) ,而請求隨後由消息偵聽器處理。Spring 通過提供了一個抽象層來簡化與 JMS 提供者 的交互。下麵代碼展示了FixedDepositService類如何通過 Spring 的 JmsTemplate 將請求的明細發送到 JMS 提供者 。
//發送JMS 消息的FixedDepositService類
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
public class FixedDepositService{
@Autowired
private transient JmsTemplate jmsTemplate;
......
public boolean submitRequest(Request request){
jmsTemplate.converAndSend(request);
}
}
FixedDepositService定義了一個 JmsTemplate 類型的變數,這個變數使用了 Spring 的@Autowired 註釋,現在,你可以讓我 @Autowired 註釋 提供了一個 JmsTemplate實例。這個 JmsTemplate 實例知道 JMS 消息發送的目的地。如何配置這個 JmsTemplate 的細節會在第十章介紹。FixedDepositService類的 submitRequest 方法調用了 JmsTemplate 的 convertAndSend 方法,把請求的明細 (由 submitRequest 方法的 Request 參數表示) 作為一個 JMS 消息發送到 JMS 提供者。
這也再一次表明,如果使用 Spring 框架 向 JMS 提供者發送消息,並不需要直接處理 JMS API.
6. 緩存
Spring 的緩存抽象提供了在應用程式中使用緩存的一致方法。
使用緩存解決方案來提高應用程式的性能是很常見的。 MyBank 應用使用一個緩存產品以提高讀取定期存款明細操作的性能。Spring 框架通過抽象緩存相關的邏輯來簡化與不同緩存解決方案的交互。
下麵代碼展示了FixedDepositService 類的getFixedDepositDetails 方法使用 Spring的緩存抽象功能來緩存定期存款明細。
//將定期存款明細緩存的FixedDepositService類
import org.springframework.cache.annotation.Cacheable;
public class FixedDepositService{
@Cacheable("fixedDeposits")
public FixedDepositDetails getFixedDepositDetails(....){....}
public boolean createFixedDeposit(FixedDepositDetails fixedDepositDetails){....}
}
在上面代碼中,Spring的@Cacheable 註解表明有getFixedDepositDetails 方法返回的定期存款明細將被緩存起來,如果使用同樣的參數來調用 getFixedDepositDetails 方法,getFixedDepositDetails 方法並不會實際運行,而是直接返回緩存中的定期存款明細。這表明,如果使用Spring框架,則不需要再類中編寫與緩存相關的邏輯。Spring緩存抽象在第十章中詳細介紹。
在這一部分中,我們看到Spring框架通過透明的向POJO提供服務的方式簡化了企業應用開發,從而將開發者從底層API 細節中解放出來。Spring還提供了與各種標準框架,如Hibernate,Quartz,JSF,Struts和EJB等的簡單集成,使得Spring成為企業應用程式開發的理想選擇。
一個簡單的 Spring 應用程式
在這一部分,我們來關註一個使用 Spring 的 DI 功能的簡單的 Spring 應用程式。在一個應用程式中使用 Spring 的 DI 功能,需要遵循以下步驟。
- 確定應用程式對象 及其依賴關係。
- 根據步驟 1 中 確定的應用程式對象創建 POJO 類。
- 創建描述應用程式對象及其依賴項的配置元數據
- 創建一個 Spring IoC 容器的實例並將配置元數據傳遞給他
- 從 Spring IoC容器實例中訪問應用程式對象
現在讓我們來看看上訴步驟在 MyBank 應用程式中是如何體現的。
1.確定應用程式對象及其依賴關係
前面討論過,MyBank 應用程式中,FixedDepositController 調用 FixedDepositService(服務層對象)的方法createFixedDeposit方法。然後,FixedDepositService 調用 FixedDepositDao 對象(數據訪問對象)來把定期存款明細保存到應用程式的數據存儲區。因此,FixedDepositService 是 FixedDepositController 對象的依賴項。而 FixedDepositDao 是 FixedDepositService 對象的依賴項。
2.根據確定的應用程式對象創建 POJO 類
一旦已經確定了應用程式對象,下一步就是根據這些應用程式對象創建 POJO 類。
之前沒有討論過把依賴項作為構造函數參數或作為setter方法參數傳遞給應用程式對象。接下來展示了一個FixedDepositSerice 的實例(FixedDepositController的依賴項)是如何作為一個setter方法的參數傳遞給FixedDepositController類的
package sample.spring.chapter01.bankapp;
import org.apache.log4j.Logger;
public class FixedDepositController {
private static Logger logger = Logger.getLogger(FixedDepositController.class);
private FixedDepositService fixedDepositService;
public FixedDepositController() {
logger.info("initializing");
}
public void setFixedDepositService(FixedDepositService fixedDepositService) {
logger.info("Setting fixedDepositService property");
this.fixedDepositService = fixedDepositService;
}
public boolean submit() {
return fixedDepositService.createFixedDeposit(
new FixedDepositDetails(1, 10000, 365, "[email protected]"));
}
public FixedDepositDetails get() {
return fixedDepositService.getFixedDepositDetails(1L);
}
}
在上面代碼中,FixedDepositService 這個依賴項是通過 setFixedDepositService 方法被傳遞給 FixedDepositController 的。我們馬上就能看到 setFixedDepositService 的setter 方法被 Spring 調用
如果觀察FixedDepositController 、 FixedDepositService 和 FixedDepositDao 類,你會發現這幾個類都沒有實現任何 Spring 特定介面或繼承任何 Spring 指定的類
3.創建配置元數據
Spring 容器讀取指定了應用程式對象及其依賴項的配置元數據,將應用程式對象實例化並註入他們的依賴項。在本節中,我們將首先介紹配置元數據中包含的其他信息,然後深入研究如何用 XML 方式指定配置元數據。
配置元數據指定應用程式所需的企業服務(如事務管理、安全性、遠程訪問)的信息。例如,如果想讓 Spring 來管理事務,你需要在配置元數據中配置 對 Spring 的 PlatformTransactionManager 介面的一個實現。PlatformTransactionManager 實現複製管理事務。(更多關於 Spring 的事務管理功能詳見第八章)
你可以通過 XML 文件或者通過POJO類中的註解將配置元數據提供給 Spring 容器。從 Spring 3.0 版本開始。你也可以通過 Java 類 上添加 Spring 的 @Configuration 註解 來將配置元數據提供給 Spring 容器。在本節中,我們將介紹如何通過 XML 方式 指定配置元數據。
通過創建一個包含應用程式對象及其依賴項信息的應用程式上下文XML文件,可以按照 XML 格式將配置元數據提供給應用程式。下麵的 XML 展示了 MyBank 應用程式上下文 XML文件 有 FixedDepositController、FixedDepositService、以及FixedDepositDao等對象組成
<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.xsd">
<bean id="controller"
class="sample.spring.chapter01.bankapp.FixedDepositController">
<property name="fixedDepositService" ref="service" />
</bean>
<bean id="service"
class="sample.spring.chapter01.bankapp.FixedDepositService">
<property name="fixedDepositDao" ref="dao" />
</bean>
<bean id="dao" class="sample.spring.chapter01.bankapp.FixedDepositDao" />
</beans>
以下是關於應用程式上下文 XML 文件的要點。
- 在 spring-beans.xsd schema(也別稱為Spring 的 bean schema) 中定義的
元素是應用程式上下文的 XML 文件的跟元素。spring-beans.xsd schema 在 Spring 框架發佈的 spring-beans-4.3.0.RELEASE.jar JAR包中。 - 每個
元素配置一個由Spring 容器管理的應用程式對象。在 Spring 框架的術語中。一個 元素代表一個bean定義。Spring 容器創建的基於bean定義的對象稱為一個bean。id特性指定bean唯一名稱,class特性指定bean完全限定類名。還可以使用 元素的name特性指定bean的別名,在 MyBank應用程式中,FixedDepositController、FixedDepositService和FixedDepositDao 為應用程式對象,因此我們有三個 元素--------每個應用程式對象對應一個 元素。由於Spring容器管理者有 元素配置的應用程式對象,Spring 容器也就需要承擔創建並註入他們的依賴關係的責任。不需要直接創建由 元素定義的應用程式對象實例,而是應該從Spring 容器中獲取他們。在本節後面部分,我們將介紹如何獲取由Spring 容器管理的應用程式對象。 - 沒有和Mybank應用程式中的FixedDepositDetails域對象相對應的
元素。這是因為域對象通常不是由Spring 管理的,他們由應用程式所使用的ORM框架(如Hibernate)創建,或者通過使用new運算符方式創建他們 元素指定由 元素配置的bean依賴項(或者配置屬性)。 元素對應於bean 類中的 JavaBean 風格 的setter 方法,該方法由Spring 容器調用以設置bean 依賴關係(或配置屬性)。