Spring In Action 4(Spring實戰第四版)翻譯與理解 第一章 付諸行動

        這幾天筆者閱讀了一下Spring In Action 4,感受仍是有必定的收穫的。在以前的項目上,只會簡單地使用Spring MVC,對於不少概念並不理解。如今看看Spring的一些概念,如DI和AOP,以及Spring的各個模塊,對於Spring這一總體框架能夠有較深的瞭解。java

        這篇文章將主要以翻譯的形式來說解Spring,中間夾雜對於原文的理解,以及代碼實現,相信對於讀者會帶來必定的幫助,也是對本身閱讀的一個總結。若是讀者有不一樣的看法,還望能夠共同探討一下。順帶推薦一下Manning的In Action系列,筆者此前讀了《機器學習實戰》,外加上如今看的《Spring實戰》,感受真心不錯,國外的許多書籍,相比國內簡直是良心之做。可是,中文譯本的更新永遠沒法遇上英文本來的進度,就拿Spring來講,第三版的中文譯本還停留在Spring 3,然而Spring已經升級到了4版本,而英文本來緊跟技術進度,相比之下,第四版的中文譯本,還不知要到何年何月。固然,文章的翻譯除了要花費長時間的努力,還須要譯者對技術有較爲深刻的瞭解,在此,仍是要感謝那些辛勤的貢獻者。筆者翻譯此書的目的,是爲了在讀書的同時,可以總結到一些有用的知識,並指望能給讀者帶來必定的收穫。web

        (題外話:因爲開通了郵箱通知功能,近期Windows10任務欄右邊老是會顯示郵件提示。有一些問題,因爲筆者也沒有去深度地探究Spring MVC這個框架,也沒法作出具體的回答,但相信前面幾篇文章對於入門開發仍是存在必定的幫助的。真正的項目開發確定會比教程中更加複雜,由於除了業務邏輯,還有許多重要的問題,如高併發、多線程,以及相當重要的安全問題,是須要考慮進來的。)spring

        接下來,進入正題。。。(轉載請說明出處:Gaussic,AI研究生數據庫


Part 1 Spring 核心(Core Spring 

        Spring能作不少事情。可是對於Spring的全部的奇妙功能,它們的主要特徵是依賴注入(dependency indection, DI)和麪向方面編程(aspect-oriented programming, AOP,也能夠譯爲面向切面編程)。
express

        第一章,付諸行動(Springing into action),我將帶給你一個Spring 框架的快速概覽,包括Spring中的DI和AOP的快速概覽,並展現它們是如何幫助下降應用組件(components)的耦合度的。
編程

        第二章,裝配Bean(Wiring Beans),咱們將更爲深刻地探討如何將應用的各個組件拼湊在一塊兒。咱們將着眼於自動配置(automatic configuration)、基於Java的配置,和XML配置,這三種Spring提供的配置方法。
設計模式

        第三章,高級裝配(Advanced Wiring),將展現一些小把戲和手段,它們將幫助你深度挖掘Spring的強大能力,包括有條件的配置(conditional configuration),用以處理自動裝配(autowiring)、範圍控制(scoping)以及Spring表達式語言(Spring Expression Language, Spring EL)。
緩存

        第四章,面向方面的Spring(Aspect-oriented Spring),探究如何使用Spring的AOP特徵來解除系統級的服務(例如安全和審計(auditing))以及它們所服務的對象之間的耦合度。這一章爲以後的章節搭建了一個起始平臺,例如第九、13和14章,它們講解了如何在聲明式安全(declarative security)和緩存。
安全


第一章  付諸行動(Springing into action)

        對於Java開發者來講,如今是一個很好的時期。
服務器

        在20年的歷史中,Java經歷過一些好的時期以及很差的時期。儘管存在着一些粗糙點(rough spots),例如applets, Enterprise JavaBeans(EJB), Java Data Objects(JDO),以及數不盡的日誌框架,Java仍然擁有着一個豐富的多樣化的歷史,做爲許多企業級軟件的平臺而言。並且,Spring在這一段歷史上佔有很重要的一筆。

        在早期,Spring做爲更加劇量級的Java技術(特別是EJB)的替代品被創造出來。相比EJB,Spring提供了一個更輕量更簡潔的的編程模型。它給予了普通Java對象(POJO)更增強大的能力,而這些能力是EJB和一些其餘Java規格才擁有的。

        隨着時間的過去,EJB和J2EE獲得了發展。EJB開始自身提供簡單的面向POJO的變成模型。如今EJB利用了諸如依賴注入和麪向方面變成的思想,能夠說其靈感來自於Spring的成功。

        雖然J2EE(如今被稱爲JEE)能夠遇上Spring,可是Spring從未中止前進的腳步。Spring持續的發展各個領域。而這些領域,即便是如今,JEE纔剛剛開始開始探索,甚至還未引入。移動開發,社交API集成,NoSQL數據庫,雲計算,以及大數據只是Spring引入的一小部分領域。並且,將來的Spring依然一片光明。

        正如我所說,對於開發者來講,如今是一個很好的時期。

        這本書是對Spring的一個探索。在這一章節,咱們將在高層次測試Spring,讓你品嚐一下Spring究竟是什麼。這一章將讓你明白Spring到底解決了各類什麼樣的問題,而且未這本書的其他部分作好準備。


1.1 簡化Java開發

        Spring是由Rod Johnson創造的一個開源框架。Spring被創造出來以解決企業級應用開發的複雜問題,而且讓普通的JavaBeans(plain-vanilla JavaBeans)可以完成此前只有EJB纔可能完成的任務。可是Spring的用處並不只僅侷限於服務器端開發。並且Java應用能夠從Spring中獲益,例如間接性、可測試性以及鬆耦合。

        另外一種意義的bean...雖然在表示應用組件時,Spring大方地使用了bean和JavaBean這兩個詞,但這並不表示一個Spring組件必須嚴格地聽從JavaBean的規格。一個Spring組件能夠是任何形式的POJO。在本書中,我假設JavaBean是一個寬鬆的定義,與POJO同義。

        經過本書你將瞭解到,Spring作了許多事情。可是Spring提供的幾乎每個功能的根基,是一些很是基礎的想法,所有專一於Spring的基本任務:Spring簡化Java開發

        這裏是粗體!大量的框架說要簡化某一些事物。可是Spring旨在簡化Java開發這一普遍主題。這還須要更多的解釋。Spring是如何簡化Java開發的呢?

        爲了支持對Java複雜度的攻擊,Spring利用了4個關鍵策略

  • 利用POJO的輕量級與最小侵入式開發(Lightweight and minimally invasive development with POJOs)

  • 經過DI和麪向接口實現鬆耦合(Loose coupling through DI and interface orientation)

  • 經過方面和普通慣例實現聲明式編程(Declarative programming through aspects and common conventions)

  • 利用方面和模板消除陳詞濫調的代碼(Eliminating boilerplate code with aspects and templates)

        基本上Spring作的每一件事均可以追溯到這四條策略中的一條或幾條上。在這一章節的其餘部分,我將擴展到這些想法的每個中去,展現具體的例子以解釋Spring是如何完成其簡化Java開發的目標的。讓咱們從Spring如何經過鼓勵面向POJO的開發來保持最小化侵入式的(minimally invasive,這點很差翻譯,大體意思是不怎麼須要修改POJO的代碼)。


1.1.1 釋放POJOs的力量

        若是你作過很長時間的Java開發工做,你可能會發現某些框架會緊緊地困住你,它們強制你去繼承某一個類或實現某一個接口。侵略式編程模型一個簡單目標例子(easy-target example)是EJB 2-era stateless session beans(不詳,在此不作翻譯)。可是即便早期的EJBs是這樣一個簡單目標,侵入式編程編程仍然能在早期版本的Struts, WebWork, Tapestry,以及無數其餘的Java規格與框架中找到。

        Spring(儘量地)避免其API污染你的應用代碼。Spring幾乎歷來不強迫你去實現一個Spring接口,或集成一個Spring類。反而,一個基於Spring的應用中的類一般並無指示它們爲Spring所用。最壞狀況,一個類可能被Spring的一個註解標註,可是它另外一方面是一個POJO。

        爲了說明,考慮HelloWorldBean類。

        如你所見,這很簡單,就是一個普通的Java類,一個POJO。它並無什麼特殊的東西以代表它是一個Spring組件。Spring的非侵入式編程模型表示,這個類在Spring中一樣能夠很好的工做,就像它能在其餘非Spring應用中同樣。

        儘管樣式很是簡單,POJOs依然能夠很強大。Spring使得POJOs更強大的一個方法是利用DI裝配(assembling)它們。讓咱們看看DI是如何幫助解除應用對象之間的耦合度的。


1.1.2 注入依賴

        依賴注入這個詞聽起來可能有點高大上,讓人聯想出一些複雜的編程技術或設計模式的概念。可是結果證實,DI並無聽起來那麼複雜。經過在你的項目中利用DI,你將發現你的代碼將獲得極大地簡化,更容易理解,更容易測試。

DI是如何工做的

        任何非平凡的應用(比Hello World要複雜的多)都是有相互協做的多個類組成的,以執行一些業務邏輯。傳統地,每個對象負責獲取對它本身的依賴(與他協做的對象)的引用。這將會帶來高度耦合和難以測試的代碼。

        例如,考慮下面的Knight類。

package com.gaussic.knights;

// 一個專門拯救少女的騎士
public class DamselRescuingKnight implements Knight {

    private RescueDamselQuest quest;    // 拯救少女任務

    // 這裏出現了與RescueDamselQuest的緊密耦合
    public DamselRescuingKnight() {
        this.quest = new RescueDamselQuest();
    }

    // 騎士出征
    public void embarkOnQuest() {
        quest.embark();
    }
}

        相信你們都知道騎士與龍的故事,一個勇敢的騎士,除了可以戰勝巨龍以外,還須要拯救被囚禁的少女。可是在上面的DamselRescuingKnight中,咱們發現他在構造器中建立了他本身的任務,一個RescueDamselQuest(也就是說,這個騎士構造出來時內心就只有一個任務,就是拯救少女,注意這個任務是發自心裏的)。這致使了DamselRescuingKnight與RescueDamselQuest的高度耦合,這嚴重限制了騎士的行動。若是有一位少女須要拯救,那麼這個騎士將會出現。可是若是須要屠龍呢,或者須要開個圓桌會議呢,那這個騎士將被一腳踹開。

        此外,給DamselRescuingKnight寫一個單元測試是很是困難的。在這個測試中,你須要可以保證,在騎士的embarkOnQuest()方法被調用時,quest的embark()方法也被調用。可是在這裏並無明確的方法來完成它。不幸的是,DamselRescuingKnight將保持未測試狀態。

        耦合是一個雙頭猛獸。一方面,緊密耦合的代碼難以測試,難以重用,且難以理解,而且常常表現出「打地鼠」的bug行爲(修復了一個bug,又產生了一個或多個新的bug)。另外一方面,必定數量的耦合仍是有必要的,徹底不耦合的代碼作不了任何事情。爲了作一些有用的事情,類之間應該互相具備必定的認識。耦合是必需的,可是須要仔細管理。

        利用DI,對象在構建時被給予它們的依賴,這經過一些定位系統中的每個對象的三方來實現。對象不須要建立或得到它們的依賴。如圖所示,依賴被注入到須要它們的對象中。

        爲了說明這一點,讓我麼看看BraveKnight類:一個騎士不只僅要勇敢,還應該能夠完成任何到來的任務。

package com.gaussic.knights;

// 一個勇敢的騎士,應該能完成任何到來的任務
public class BraveKnight implements Knight {

    private Quest quest;    // 任務,這是一個接口

    // 構造器,注入Quest
    public BraveKnight(Quest quest) {
        this.quest = quest;
    }

    // 騎士出征
    public void embarkOnQuest() {
        quest.embark();
    }

    private BraveKnight() {}
}

        如你所見,BraveKnight不像DamselRescuingKnight同樣建立本身的任務,而是在構造的時候被傳入了一個任務做爲構造參數(也就是說,這個騎士構造出來時,有人給了他一個任務,他的目標是完成這個任務,想僱傭兵同樣)。這種類型的注入被稱爲構造注入(constructor injection)。

        此外,勇敢的騎士所賦予的任務是一個Quest類型,它是一個接口,全部的具體quest都要實現這個接口。於是BraveKnight能夠挑戰RescueDamselQuest,SlayDragonQuest,MakeRoundTableRounderQuest,或者任何其餘的Quest。

        這裏的關鍵點是,BraveKnight並不與任何一個特定的Quest的實現耦合。對於給予了什麼任務,他並不介意,只要它實現了Quest接口。這就是DI的一個關鍵益處-鬆耦合。若是一個對象僅僅經過依賴的接口(而不是它們的實現或者實例化)來了解這些依賴,那麼這個依賴就能夠自由地更換爲不一樣的實現,而一方(在此爲BraveKnight)不須要知道有任何的不一樣。

        換出依賴的一個最廣泛的方法是在測試時利用一個mock實現(one of the most common ways a dependency is swapped out is with a mock implementation during test)。因爲緊耦合,你沒法適合地測試DamselRescuingKnight,可是你能夠輕鬆地測試BraveKnight,經過給它一個Quest的mock實現(其實就是構建了一個虛擬的Quest),以下所示:

注意:運行如下測試須要引入junit包和mockito-core包)

package com.gaussic.knights.test;

import com.gaussic.knights.BraveKnight;
import com.gaussic.knights.Quest;
import org.junit.Test;
import org.mockito.Mockito;

// 一個勇敢的騎士,須要經歷mock測試的考驗
public class BraveKnightTest {

    @Test
    public void knightShouldEmbarkOnQuest() {
        Quest mockQuest = Mockito.mock(Quest.class);   // 構建 mock Quest,其實就是虛擬的Quest
        BraveKnight knight = new BraveKnight(mockQuest);  // 注入mockQuest
        knight.embarkOnQuest();
        Mockito.verify(mockQuest, Mockito.times(1)).embark();
    }
}

        在此使用了一個mock對象框架(Mockito)來建立一個Quest接口的虛擬實現。利用虛擬對象,咱們能夠建立一個新的BraveKnight實例,並經過構造器注入這一虛擬Quest。在調用embarkOnQuest()方法後,利用Mockito來驗證虛擬Quest的embark()方法僅僅被調用一次。

將一個Quest注入到一個Knight中

        如今,BraveKnight被寫成了這樣的形式,你能夠將任何一個任務交給騎士,那麼你應該如何指定給他什麼Quest呢?假設,例如,你想要BraveKnight去完成一項屠龍的任務。多是SlayDragonQuest,以下所示。

package com.gaussic.knights;

import java.io.PrintStream;

// 任務目標:屠殺巨龍
public class SlayDragonQuest  implements Quest {

    private PrintStream stream;    // 容許自定義打印流


    public SlayDragonQuest(PrintStream stream) {
        this.stream = stream;
    }

    public SlayDragonQuest() {}

    // 任務說明
    public void embark() {
        stream.println("Embark on quest to slay the dragon!");
    }
}

        如你所見,SlayDragonQuest實現了Quest接口,讓其能很好的適應BraveKnight。你也可能注意到,SlayDragonQuest在構造時請求了一個普通的PrintStream,而不像其餘的簡單Java示例同樣依靠System.out.println()。這裏有一個大問題,怎樣才能把SlayDragonQuest交給BraveKnight呢?還有怎樣才能把PrintStream交給SlayDragonQuest呢?

        在應用組件之間創建聯繫的行爲,一般被稱之爲裝配(wiring)。在Spring中,有不少將組件裝配在一塊兒的方法,可是一般的方法老是經過XML的。下面展現了一個簡單的Spring配置文件,knight.xml,它將BraveKnight, SlayDragonQuest和PrintStream裝配在了一塊兒。

<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- BraveKnight bean,id爲knight -->
    <bean id="knight" class="com.gaussic.knights.BraveKnight">
        <!-- 構造參數,引用 id爲quest的bean -->
        <constructor-arg ref="quest"/>
    </bean>

    <!-- SlayDragonQuest bean,id爲quest -->
    <bean id="quest" class="com.gaussic.knights.SlayDragonQuest">
        <!-- 構造參數,值爲 #{T(System).out} -->
        <constructor-arg value="#{T(System).out}"/>
    </bean>

</beans>

        在此,BraveKnight和SlayDragonQuest在Spring中都被定義爲bean。在BraveKnight bean中,它在構造是傳入了一個SlayDragonQuest的引用,做爲構造參數。同時,SlayDragonQuest bean中使用了Spring表達式語言來傳遞 System.out(這是一個PrintStream)給SlayDragonQuest的構造器。

        若是說XML配置不符合你的口味,Spring還提供了基於Java的配置方法。以下所示。

package com.gaussic.knights;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

// @Configuration註解,聲明這是一個配置類
@Configuration
public class KnightConfig {

    // 與XML中的bean目的相同,聲明一個SlayDragonQuest bean,傳入System.out
    @Bean
    public Quest quest() {
        return new SlayDragonQuest(System.out);
    }

    // 與XML中的bean目的相同,聲明一個BraveKnight bean,傳入quest
    @Bean
    public Knight knight() {
        return new BraveKnight(quest());
    }
}

        不管是使用基於XML仍是基於Java的配置,DI的益處都是相同的。雖然BraveKnight依賴於Quest,但它並不知道將被給予什麼樣的Quest,或者這個Quest是從哪裏來的。一樣的,SlayDragonQuest依賴於PrintStream,可是它並不須要瞭解那個PrintStream是什麼。只有Spring,經過它的配置,知道全部的組件是如何組織到一塊兒的。這樣,咱們就能夠在不修改當前類的狀況下,去修改它的依賴。

        這個例子展現了Spring中裝配bean的一個簡單方法。先不要在乎太多的細節,咱們將會在第二章深刻探討Spring配置。咱們也將探討Spring中裝配bean的一些其餘辦法,包括一種讓Spring自動發現bean和建立它們之間關係的方法。

        如今你已經生命了BraveKnight和Quest之間的關係,你須要載入XML配置文件而且打開應用。

看它工做

        在Spring應用中,一個應用上下文(application context)載入bean的定義並將它們裝配在一塊兒。Spring應用上下文徹底負責建立並裝配全部構成應用的對象。Spring提供了多種應用上下文的實現,每個主要的不一樣在於它們如何載入配置。

        當bean被聲明在xml文件中時,一個合適的方法是ClassPathXmlApplicationContext。這個Spring上下文實現從一個或多個(在應用的classpath目錄的)XML文件載入Spring上下文。下面的main()方法使用了ClassPathXmlApplicationContext來載入knight.xml,而且得到對Knight對象的一個引用。

package com.gaussic.knights;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class KnightMain {

    public static void main(String[] args) throws Exception {

        // 從應用的classpath目錄載入Spring配置文件
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("META-INF/spring/knight.xml");

        // 獲取bean,並執行
        Knight knight = context.getBean(Knight.class);
        knight.embarkOnQuest();
        
        context.close();
    }
}

        此處,main()方法基於knight.xml文件建立了Spring上下文。而後它利用這個上下文做爲一個工廠來檢索ID爲knight的bean。利用對Knight對象的一個引用,它調用了embarkOnQuest()方法來使knight執行給予他的任務。注意這個類並不知道騎士被賦予了什麼樣的任務。這樣,它也不知道這個任務是交給BraveKnight作的。只有knight.xml知道具體的實現是什麼。

        運行KnightMain的main()方法,將獲得下列結果:

        利用這個例子,你應該對依賴注入有了必定的瞭解。在這本書中你將看到更多的DI。如今讓咱們來看看Spring的另外一個Java簡化策略:基於方面(aspects)的聲明式編程。


1.1.3 方面(Aspects)的運用

        雖然DI能夠鬆散的綁定軟件的各個組件,面向方面編程(AOP)還可讓你更夠捕捉那些在整個應用中可重用的組件。

        AOP常常被定義爲一項提高軟件系統分離性(separation of concerns)的技術。系統每每由多個組件構成,每個組件負責一個特定的功能。可是一般這些組件還須要承擔其核心功能之外的附加功能。一些系統服務,例如日誌、事務管理和安全,每每會進入一些其餘的組件中,而這些組件每每負責其餘的核心功能。這些系統服務一般被稱爲交叉相關(cross-cutting concerns),由於它們在系統中每每交叉在多個組件內。

        因爲將這些相關性傳播到了多個組件中,你向你的代碼引入了兩種層次的複雜度:

  • 實現系統級相關性的代碼在多個組件中存在重複。這代表若是你須要改變這些相關性的工做方式,你須要訪問多個組件。即便你將這個相關性抽象爲一個獨立的模塊,這樣其餘的組件能夠經過模塊調用來使用它,這個方法調用依然在多個組件裏面重複。

  • 你的組件被與核心功能無關的代碼污染了。一個向地址簿添加一個地址的方法,應該僅僅關心如何去添加這個地址,而不是還得去關心它的安全性和事務性。

        下圖展現了這一複雜度。左邊的業務對象太過親密地與右邊的系統服務相聯繫。每一個對象不只僅知道本身被日誌記錄、安全檢查,以及涉及到事務中,並且還須要負責本身去執行這些服務。

        AOP能夠模塊化這些服務,而後聲明式地將它們運用到須要它們的組件中。這樣可使得其餘組件更加緊密地專一於負責它們本身的功能,徹底無視任何系統服務的存在。簡單的說,方面(aspects)使得POJOs保持普通(plain)。

        把方面想象成一個覆蓋多個組件和應用的毯子能夠幫助理解,以下圖所示。在其核心部分,一個包含多個模塊的應用實現了業務功能。利用AOP,你能夠利用一層層的其餘功能將你的核心應用覆蓋。這些層能夠聲明式地運用到你的應用中,以一種更爲靈活的方式,而不須要讓你的核心應用知道他們的存在。這是一個很強大的概念,由於它保證安全、事務管理和日誌不去污染你的應用的核心業務邏輯。

        爲了說明方面在Spring中是如何運用的,讓咱們再來看看騎士的例子,向其中添加一個基本的Spring方面。

AOP實戰

        勇敢的騎士在屠殺巨龍和拯救少女歸來以後,咱們這些百姓要如何才能知道他的豐功偉績呢?吟遊詩人(minstrel)會將勇士的故事編成歌謠在民間傳頌(有網絡紅人就必然要有網絡推手啊)。咱們假設你須要經過吟遊詩人的服務來記錄騎士出征和歸來的整個過程。下面展現了這個Minstrel類:

package com.gaussic.knights;

import java.io.PrintStream;

// 吟遊詩人
public class Minstrel {

    private PrintStream stream;

    public Minstrel(PrintStream stream) {
        this.stream = stream;
    }

    // 騎士出發前,頌唱
    public void singBeforeQuest() {
        stream.println("Fa la la, the knight is so brave!");
    }

    // 騎士歸來,頌場
    public void singAfterQuest() {
        stream.println("Tee hee hee, the brave knight did embark on a quest!");
    }
}

        如你所見,Minstrel是一個有兩個方法的簡單類。singBeforeQuest()旨在騎士出發前調用,singAfrterQuest()旨在騎士完成任務歸來調用。在這兩種狀況下,Minstrel都經過注入其構造器的PrintStrem來頌唱。

        把這個Minstrel運用到你的代碼中其實很是簡單----你能夠把它直接注入到BraveKnight中,對嗎?讓咱們對BraveKnight作一點適當的調整,以使用Minstrel。下面的代碼展現了將BraveKnight和Minstrel運用到一塊兒的第一次嘗試:

package com.gaussic.knights;

public class BraveKnight2 {

    private Quest quest;
    private Minstrel minstrel;

    public BraveKnight2(Quest quest, Minstrel minstrel) {
        this.quest = quest;
        this.minstrel = minstrel;
    }

    // 一個騎士應該管理本身的吟遊詩人嗎???
    public void embarkOnQuest() throws Exception {
        minstrel.singBeforeQuest();
        quest.embark();
        minstrel.singAfterQuest();
    }
}

        上面的代碼能夠完成任務。如今你只須要回到Spring配置文件中去定義Minstrel bean,並將其注入到BraveKnight bean的構造器中。可是等等。。。。

        好像有什麼不對勁。。。一個騎士難道應該去管理他本身的吟遊詩人嗎?一個偉大的騎士是不會本身去傳揚自身的功績的,有節操的吟遊詩人應該主動的去傳頌偉大騎士的功績,而不接受他人的收買。那麼,爲何這裏的騎士他時時地去提醒吟遊詩人呢?

        此外,因爲這個騎士須要知道這個吟遊詩人,你被強制將Minstrel注入到BraveKnight中。這不只僅複雜化了BraveKnight的代碼,還讓我想到,若是你須要一個沒有吟遊詩人的騎士要怎麼辦?若是Minstrel是null那怎麼辦(騎士所處的地區,壓根沒有吟遊詩人的存在)?那你是否是還要引入一些非空檢查邏輯到你的代碼裏面呢?

        你的簡單的BraveKnight類開始變得愈來愈複雜(而騎士只想作一個純粹的騎士)。可是,使用AOP,你能夠聲明,吟遊詩人應該主動地去頌唱騎士的功績,而騎士只須要作好本職工做。

        爲了把Minstrel轉變爲一個方面,你要作的僅僅是在Spring配置文件中聲明它。這是一個更新版的knight.xml,加入了Minstrel方面的聲明:

<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- BraveKnight bean,id爲knight -->
    <bean id="knight" class="com.gaussic.knights.BraveKnight">
        <!-- 構造參數,引用 id爲quest的bean -->
        <constructor-arg ref="quest"/>
    </bean>

    <!-- SlayDragonQuest bean,id爲quest -->
    <bean id="quest" class="com.gaussic.knights.SlayDragonQuest">
        <!-- 構造參數,值爲 #{T(System).out} -->
        <constructor-arg value="#{T(System).out}"/>
    </bean>

    <!-- Minstrel bean, id爲minstrel -->
    <bean id="minstrel" class="com.gaussic.knights.Minstrel">
        <!-- 構造參數,值爲 #{T(System).out} -->
        <constructor-arg value="#{T(System).out}" />
    </bean>

    <!-- 方面配置 -->
    <aop:config>
        <aop:aspect ref="minstrel">
            <!-- 在執行任何類的 embarkOnQuest()方法時調用 -->
            <aop:pointcut id="embark" expression="execution(* *.embarkOnQuest(..))"/>
            <!-- 在embark以前,調用 singBeforeQuest -->
            <aop:before pointcut-ref="embark" method="singBeforeQuest"/>
            <!-- 在embark以後,調用 singAfterQuest -->
            <aop:after pointcut-ref="embark" method="singAfterQuest"/>
        </aop:aspect>
    </aop:config>

</beans>

        這裏你使用了Spring的 aop configuration 命名空間來聲明Minstrel bean是一個方面。首先你聲明Minstrel是一個bean。而後在<aop:aspect>元素引入那個bean。更進一步的定義這個aspect,你聲明(使用 <aop:before>)了singBeforeQuest()方法在embarkOnQuest()方法前調用,稱之爲before advice。以及singAfterQuest()方法在embarkOnQuest()後調用,稱之爲 after advice。

        在以上兩種狀況中,pointcut-ref屬性都引用了一個名爲embark的pointcut(切入點)。這個pointcut定義在了以前的<pointcut>元素中,使用了表達式屬性級來選擇這個advice應該做用在那個位置。表達式語法是AspectJ的pointcut表達式語言。

        若是你不瞭解AspectJ,或者AspectJ pointcut表達式是如何編寫的,不用擔憂。咱們將在第4章對Spring AOP作更多的講解。如今,知道你要請求Spring在BraveKnight在出徵先後,調用Minstrel的singBeforeQuest()和singAfterQuest()方法足矣。

        如今,不改動KnightMain的任何地方,運行main()方法,結果以下:

        這就是它的所有了!只須要一點點的XML,你就將Minstrel轉換爲了一個Spring方面。若是它如今還沒徹底說明白,不用擔憂,你將在第4章中看到更多的Spring AOP示例,它們將給你更明確的概念。如今,這個例子有兩個重點須要強調。

        第一,Minstrel仍然是一個POJO,沒有任何代碼代表它將被做爲一個方面。Minstrel只在你在Spring context中聲明時,纔是一個方面。

        第二,也是最重要的,Minstrel能夠運用到BraveKnight中,而BraveKnight不須要明確地調用它。實際上,BraveKnight甚至一直不知道還有Minstrel的存在

        我還應該指出,雖然你使用了一些Spring的魔法來將Minstrel轉化爲一個方面,你仍是須要將它先聲明爲一個bean。重點是,你能夠用Spring aspects作任何的事情,像其餘的Spring beans同樣,例如將依賴注入給它們。

        使用aspects來頌唱騎士的功績是很好玩的。可是Spring的AOP能夠用在更多實用的地方。在後面你將看到,Spring AOP能夠用來提供服務,例如聲明式事務、安全,將在第9章和14章介紹。

        在這以前,咱們來看看Spring簡化Java開發的另外一個方法。


1.1.4 利用模板消除重複代碼

        你是否寫過一些看似以前寫過的代碼?這不是「似曾類似「,朋友。那是重複代碼(boilerplate code)---爲了執行常見或簡單的任務須要一遍一遍的寫類似的代碼。

        不幸的是,Java API的許多地方都含有重複的代碼。使用JDBC作數據庫查詢就是一個明顯的例子。若是你在使用JDBC,你可能須要寫一些以下所示的代碼:

public Employee getEmployeeById(long id) {
    Connection conn = null; 
        PreparedStatement stmt = null;
    ResultSet rs = null;
    try {
        conn = dataSource.getConnection();   // 創建鏈接
        // 選擇 employee
        stmt = conn.prepareStatement("select id, firstname, lastname, salary from employee where id=?");
        stmt.setLong(1, id);
        rs = stmt.executeQuery();
        Employee employee = null;
        if (rs.next()) {
            employee = new Employee();   // 建立對象
            employee.setId(rs.getLong("id"));
            employee.setFirstName(rs.getString("firstname"));
            employee.setLastName(rs.getString("lastname"));
            employee.setSalary(rs.getBigDecimal("salary"));
        }
        return employee;
    } catch (SQLException e) { // 錯誤處理
    } finally {
        if (rs != null) {  // 收拾殘局
            try {
                rs.close();
            } catch (SQLException e) {
            }
        }
        if (stmt != null) {
            try {
                stmt.close();
            } catch (SQLException e) {
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
            }
        }
    }
    return null;
}

        如你所見,這個JDBC代碼的目的是查詢一個員工的名字和工資狀況。因爲這項特定的查詢任務被埋藏在一堆的JDBC儀式中,你首先必須建立一個鏈接,而後建立一個陳述(statement),最後在查詢結果。並且,爲了安撫JDBC的脾氣,你必須catch SQLException,其實就算它拋出了錯誤,你也沒什麼可以作的。

        最後,在全部的事情都作完了,你還得收拾殘局,關閉鏈接(connection)、陳述(statement)、結果集(result set)。這仍是有可能出發JDBC的脾氣,於是你還得catch SQLException。

        上面代碼最顯著的問題是,若是你要作更多的JDBC查詢,你將會寫大量重複的代碼。可是隻有不多一部分是真正跟查詢相關的,而JDBC重複代碼要多得多。

        在重複代碼業務中,JDBC並不孤獨。許多的工做都包含類似的重複代碼。JMS、JNDI和大量的REST服務一般涉及到大量徹底重複的代碼。

        Spring經過將重複代碼封裝在模板中來消除它們。Spring的JdbcTemplate能夠在不須要全部傳統JDBC儀式的狀況下執行數據庫操做。

        例如,使用Spring的SimpleJdbcTemplate(JdbcTemplate的一個特定形式,利用了Java 5的優勢),getEmployeeById()方法能夠重寫,使得它僅關注於檢索員工數據的任務,而不須要考慮JDBC API的需求。下面的代碼展現了更新後的getEmployeeById()的樣子:

public Employee getEmployeeById(long id) {
    return jdbcTemplate.queryForObject(
            "select id, firstname, lastname, salary from employee where id=?", // SQL查詢
            new RowMapper<Employee>() {
                // 映射結果到對象
                public Employee mapRow(ResultSet rs, int rowNum) throws SQLException { 
                    Employee employee = new Employee();
                    employee.setId(rs.getLong("id"));
                    employee.setFirstName(rs.getString("firstname"));
                    employee.setLastName(rs.getString("lastname"));
                    employee.setSalary(rs.getBigDecimal("salary"));
                    return employee;
                }
            }, id);
}

        如你所見,新版本的getEmployeeById()更加簡潔且專一於從數據庫中查詢員工。模板的queryForObject()方法被給予一個SQL查詢,一個RowMapper(爲了將結果集數據映射到領域對象),以及0個或多個查詢參數。你沒法在getEmployeeById()中找到任何的JDBC重複代碼。這一切都交給了模板來處理。

        我已經想你展現了Spring是如何使用面向POJO的開發來下降Java開發複雜性的,DI, aspects 和 templates。同時,還展現瞭如何在XML配置文件中配置bean和aspect。可是如何載入這些文件呢?讓咱們看看Spring容器(container),存放應用的bean的地方。


1.2 盛裝你的bean

        在Spring應用中,你的應用對象住在Spring容器中。如圖1.4所示,容器建立對象,而後將它們裝配在一塊兒,配置它們,而後管理它們的生命週期,從襁褓到墳墓。

        在下一章節,你將瞭解如何配置Spring,使得它知道它須要建立、配置和裝配什麼對象。首先,知道對象何時hang out(閒逛?很差翻譯)是很重要的。瞭解容器將幫助你瞭解對象是如何被管理的。

        

        容器是Spring框架的核心。Spring的容器使用DI來管理組成一個應用的組件。這包括創建協調組件之間的聯繫。自己,這些組件更簡潔切更易理解,它們支持重用,且易於單元測試。

        沒有單獨的Spring容器。Spring包括多種的容器實現方法,能夠分爲兩種獨立的類型。Bean工廠(bean factory,由org.springframework.beans.factory.BeanFactory接口定義)是最簡單的容器,提供DI的基本支持。應用上下文(application context,由org.springframework.context.ApplicationContext接口定義)創建與bean工廠的概念之上,提供應用框架服務,例如從配置文件中讀取文本消息的能力和向感興趣的事件監聽器發送應用時間的能力。

        雖然使用bean factory和application均可以,bean factory對於大部分應用來講仍是過低級了。所以,相比bean factory,application context更受青睞。咱們將專一於使用application context,而不花更多的時間在bean factory上。


1.2.1 使用應用上下文

        Spring包含多種口味的應用上下文。如下是一部分你最有可能趕上的:

  • AnnotationConfigApplicationContext --- 從一個Java配置類中載入Spring應用上下文

  • AnnotationConfigWebApplicationContext --- 從一個Java配置類中載入Spring web應用上下文

  • ClassPathXmlApplicationContext --- 從一個或多個在classpath下的XML文件中載入Spring上下文,將上下文定義文件做爲一個classpath資源文件

  • FileSystemXmlApplicationContext --- 從文件系統中的XML文件中載入上下文定義

  • XmlWebApplicationContext --- 從包含在一個web應用中的一個或多的XML文件中載入上下文定義

        咱們將在第八章講解基於web的Spring應用時更加詳細的說明AnnotationConfigWebApplicationContext和XmlWebApplicationContext。如今,讓咱們用FileSystemXmlApplicationContext從文件系統載入Spring上下文,或用ClassPathXmlApplicationContext從classpath載入Spring上下文。

        從文件系統或classpath載入應用上下文與從bean factory中載入bean相相似。例如,下面是如何載入FileSystemXmlApplicationContext的:

ApplicationContext context = new FileSystemXmlApplicationContext("c:/knight.xml");

        相似地,可使用ClassPathXmlApplicationContext從應用的classpath載入應用上下文:

ApplicationContext context = new ClassPathXmlApplicationContext("knight.xml");

        FileSystemXmlApplicationContext和ClassPathXmlApplicationContext的不一樣之處在於,前者在文件系統的特定的位置尋找knight.xml,然後者在應用的classpath中尋找knight.xml。

        另外,若是你更偏向從Java配置中載入你的應用上下文的話,可使用AnnotationConfigApplicationContext:

ApplicationContext context = new AnnotationConfigApplicationContext(KnightConfig.class);

        取代指定一個載入Spring應用上下文的XML文件外,AnnotationConfigApplicationContext被給以一個配置類來載入bean。

        如今手頭上有了一個應用上下文,你能夠利用上下文的getBean()方法,從Spring容器中檢索bean了。

        如今你知道了如何建立Spring容器的基本方法,讓咱們更進一步的看看在Bean容器中一個bean的生命週期。


1.2.2 一個bean的一輩子

        在傳統的Java應用中,bean的生命週期很是簡單。Java的關鍵詞被用來實例化bean,而後這個bean就可使用了。一旦這個bean再也不使用,將會被垃圾收集器處理。

        相比之下,在Spring容器中的bean的生命週期更加複雜。理解Spring bean的生命週期是很是重要的,由於你可能須要利用Spring提供的一些有點來定製一個bean何時被建立。圖1.5展現了一個典型的bean在載入到應用上下文時的生命週期。

        如你所見,一個bean工廠在bean能夠被使用前,執行了一系列的設置操做。讓咱們來探討一些細節:

  1. Spring實例化bean。

  2. Spring將值和bean引用注入到bean的屬性中。

  3. 若是一個bean實現了BeanNameAware,Spring將這個bean的id傳遞給setBeanName()方法。

  4. 若是一個bean實習了BeanFactoryAware,Spring調用setBeanFactory()方法,將一個引用傳入一個附入(enclosing)的應用上下文。

  5. 若是一個bean實現了BeanPostProcessor接口,Spring調用其postProcessBeforeInitialization()方法。

  6. 若是一個bean實現了InitializingBean接口,Spring調用其afterPropertiesSet()方法。相似的,若是這個bean使用了init-method進行聲明,那麼特定的初始化方法將被調用。

  7. 若是一個bean實現了BeanPostProcessor,Spring調用其postProcessAfterInitialization()方法。

  8. 在這點,bean就能夠被應用使用了,而且一直保持在應用上下文中,直到應用上下文被銷燬。

  9. 若是一個bean實現了DisposableBean接口,Spring調用其destroy()方法。一樣,若是這個bean被聲明瞭destroy-method,將會調用特定的方法。

        如今你已經知道了如何建立並載入一個Spring容器。可是一個空的容器自己並無什麼好處,它不含任何東西,除非你將什麼放進去。爲了實現Spring DI的好處,你必須將Spring容器中的應用對象裝配起來。咱們將在第二章節更加詳細的講解bean的裝配。

        首先,咱們來調查一下現代Spring的藍圖(landscape),來看看Spring框架是由什麼構成的,以及後面版本的Spring提供了什麼新的特性。


1.3 Spring版圖概覽

        如你所見,Spring框架專一於經過DI、AOP和減小重複來簡化企業Java開發。即便那是Spring的所有,那也是值得一用的。可是對於Spring來講,還有不少令你難以置信的東西。

        在Spring框架中,你將發現許多Spring簡化Java開發的方法。可是,在Spring框架之上,是一個更大的創建在覈心框架之上的項目生態系統,將Spring擴展到更多的領域,例如web services, REST, mobile和NoSQL。

        讓咱們先來分解一下核心Spring框架,來看看它給咱們帶來了什麼。而後咱們再擴展咱們的視野來看看Spring包的更多成員。

Spring模塊

        當你下載了Spring distriution以後,查看它的libs目錄,你將發現多個JAR文件。在Spring 4.0中,有20個獨立的模塊,每一個模塊有3個JAR文件(binary class, source, JavaDoc)。完整的library JAR文件如圖1.6所示。

        這些模塊能夠被排成6個功能的類,如圖1.7所示。

        總的來講,這些模塊給了你任何你須要的東西,來開發企業級應用。可是你不須要讓你的應用徹底基於Spring框架。你能夠自由地選擇適合你的應用的模塊,而且在Spring沒法知足你的需求時,選擇其餘的模塊。Spring甚至提供了與許多其餘框架和庫的集成點,於是你不用去本身寫它們。

        讓咱們一個一個的來看看Spring的每個模塊,來了解一下每個模塊是如何拼湊整個Spring的版圖的。

核心Spring容器

        Spring框架的中心是一個容器,它負責管理Spring應用中的bean是如何建立、配置與管理的。這個模塊的內部是Spring bean工廠,是Spring提供DI的一部分。創建在bean工廠之上,你將發現Spring應用上下文的多個實現,每個都提供了配置Spring的不一樣方式。

        除了bean工廠和應用上下文以外,這個模塊還提供了許多企業級服務,例如郵件、JNDI訪問、EJB集成,和調度。

        Spring的全部模塊都都是創建在覈心容器之上的。你將在配置你的應用是隱式的使用這些類。咱們將通篇討論核心模塊,從第二章開始,咱們將更深刻的挖掘Spring DI。

Spring AOP 模塊

        Spring在AOP模塊中提供了面向方面編程的豐富支持。這個模塊的做用服務於------爲你本身的Spring應用開發你本身的aspects。與DI相似,AOP支持應用對象的鬆散耦合。可是利用AOP,應用級的相關性(例如事務和安全)解除了它們與其餘對象的耦合。

        咱們將在第4章更加深刻的講解Spring AOP支持。

數據訪問和集成

        使用JDBC時老是會形成大量的重複代碼(獲取鏈接,建立statement,處理結果集合,而後關閉鏈接)。Spring的JDBC和數據訪問對象(DAO)模塊將這些重複代碼抽象化,於是你能夠保持你的數據庫代碼乾淨和簡單,而且阻止了由數據庫資源訪問失敗致使的錯誤。這個模塊也在多種數據庫服務器返回的錯誤消息之上構建了一層很是有意義的exception。你並不須要去破解神祕而專用的SQL錯誤消息。

        對於那些更喜歡使用對象關係映射(object-relational mapping, ORM)工具的人來講,Spring提供了ORM模塊。Spring的ORM支持創建在DAO支持之上,以多種ORM方法提供了創建DAO的方便方法。Spring並無試圖去實現它本身的ORM方法,可是它提供了鏈接多個流行ORM框架的鉤子(hook),包括Hibernate,Java Persistemce APUI,Java Data Objects,以及iBATIS SQL Maps。Spring的事務管理像支持JDBC同樣支持這些ORM框架。

        在第10章節,咱們將講解Spring數據訪問,你將看到Spring的基於模板的JDBC抽象是如何可以大大地簡化JDBC代碼的。

        這個模塊還包括了一個對於Java Message Service(JMS)的Spring抽象,以經過消息機制進行與其餘應用的異步集成。此外,對於Spring 3.0,這個模塊包含了object-to-XML映射特性,它們是Spring Web Services項目的根本部分。

        此外,這個模塊使用了Spring的AOP模塊來提供Spring應用中對象的事務管理服務。

Web和遠程

        模型-視圖-控制器(MVC)樣式已經很是普遍地被接受,以用來構建web應用,使得用戶接口與應用邏輯向分離。Java在MVC框架中並沒有缺陷,Apache Struts, JSF, WebWork和Tapestry已經成爲了很是流行的MVC選擇。

        即便Spring能夠集成多種流行MVC框架,它的web和遠程模塊來自於一個可靠的MVC模塊,它在應用web層充分運用了Spring的鬆散耦合技術。咱們將在第5-7章講解Spring mvc框架。

        除了面向用戶的web應用之外,爲了創建能夠與其餘應用交互的應用,這個模塊還提供了多個遠程選項。Spring的遠程能力包括遠程方法調用(Remote Method Invocation, RMI), Hessian, Burlap, JAX-WS,以及Spring本身的HTTP invoker。Spring還提供了使用REST API的一級支持。

        在地第15章,我將講解Spring遠程。而且你將在第16章學習到如何建立與使用REST API。

儀器(INSTRUMENTATION)

        Spring的儀器(instrumentation,是這麼翻嗎)模塊包括了向JVM添加助理(agent)的支持。特別地,它向Tomcat提供了一個迂迴助理以轉換類文件,就像它們是被類加載器加載同樣。

        若是這聽起來難以理解,不要太擔憂。這個模塊提供的儀器用途很是窄,咱們將不會在這本書中講解。

測試

        意識到了開發者寫的測試的重要性,Spring提供了一個模塊以測試Spring應用。

        在這個模塊中,你將發現許多的mock對象實現,爲了撰寫做用於JNDI,servlet,和portlet的單元測試代碼。對於集成級的測試,這個模塊提供了載入Spring上下文的bean和操做bean的支持。

        在本書中,大多數例子將用測試來表示,利用Spring提供的測試方法。


1.3.2 Spring組合(Spring portfolio)

        Spring總比你能看到的要多得多。事實上,Spring的模塊比下載下來的要多得多。若是你僅僅停留在覈心的Spring框架,你將錯過Spring組合帶來的一筆巨大從潛力。整個Spring組合包包括了多個創建在覈心Spring框架上的框架和庫。 整體上,整個Spring組合包帶來了基本覆蓋Java開發每一面的Spring編程模型。

        要覆蓋Spring組合包提供的所有內容,須要大量的篇幅,並且不少都超出了本書的範圍。可是咱們將瞭解一下Spring組合包的一些元素;這裏是基於核心Spring框架的一個品嚐。

Spring Web Flow 網絡流

        Spring Web Flow創建在覈心Spring框架之上,提供了將Spring bean發佈爲web服務的聲明式方法,這些服務基於一個可論證的架構上下級的協議最後(contract-last)模型。服務的協議是由Bean的接口決定的。Spring Web Services提供了協優先(contract-first) web服務模型,其中服務實現是爲知足服務協議而編寫的。

Spring Security 安全

        安全是許多應用的關鍵方面。使用Spring AOP實現,Spring安全爲Spring應用提供了聲明式的安全機制。你將在第9章瞭解如何向web層添加Spring安全的。咱們將在第14章從新回到Spring安全來檢驗如何保證方法調用安全性的。

Spring Integration 集成

        許多的企業級應用必須與其餘的企業級應用進行交互。Spring集成提供了多種經常使用交互模式的實現,以Spring聲明式的形式。本書將不作講解。

Spring Batch 批處理

        當須要在數據上執行大量的操做時,沒有什麼能比得上批處理。若是你將去開發一個批處理應用,你能夠利用Spring Batch, 來使用Spring的魯棒、面向POJO的開發方法完成它。Spring Batch超出了本書的範圍。

Spring Data 數據

        Spring Data使得操做各類數據庫變得很是簡單。雖然關係型數據庫多年來一直用於企業應用中,現代的應用愈來愈意識到不是全部的數據均可以用表中的行和列來表示的。一種新產生的數據庫,一般被稱之爲NoSQL數據庫,提供了新的方法來操做新型數據庫,而非傳統關係型數據庫。

        無論你是在使用文檔數據庫如MongoDB,圖形數據庫如Neo4j,或者甚至傳統關係型數據庫,Spring Data爲persistence提供了簡化的編程模型。這包括,爲多種數據類型,一個自動倉庫(repository)機制來爲你建立倉庫實現。

        咱們將在第11章中講解如何用Spring Data來簡化Java Persistence API(JPA)開發,而後在第12章時擴展性地討論一些NoSQL數據庫。

Spring Social 社交

        社交網絡在互聯網上升趨勢,愈來愈多的應用開始集成社交網站的接口,例如Facebook和Twitter。若是你對這方面感興趣,你能夠了解一下Spring Social,Spring的一個社交網絡擴展。

        可是Spring Social並不只僅是微博和朋友。儘管這樣命名,Spring Social相比社交(social)這一詞更加偏向於鏈接(connect)。它幫助你利用REST API鏈接你的Spring應用,其中還包括不少沒有任何社交目的的應用。

        因爲空間的限制,咱們將不在本書中講解Spring Social。

Spring Mobile 移動

        移動應用是軟件開發另外一個熱門點。智能手機以及平板已經逐漸成爲備受用戶青睞的客戶端。Spring Mobile是Spring MVC的一個新的擴展,以支持移動web應用的開發。

Spring For Android 安卓

        與Spring Mobile相關的是Spring Android項目。這個項目旨在利用Spring框架爲一些安卓設備上的本地應用的開發帶來必定的便利。初始地,這個項目提供了一個版本的Spring RestTemplate,它能夠運用在Android應用中。它還能夠操做Spring Social來使得本地安卓app能夠與REST API相鏈接。本書將不討論Spring for Andoid。

Spring BOOT 快速啓動

        Spring大大簡化了許多編程任務,減小甚至消除了大量你可能一般很是須要的重複代碼。Spring Boot是一個很是激動人心的新項目,它的目的是在Spring開發時簡化Spring自己。

        Spring Boot大量地利用了自動配置技術,以消除大量的(在多種狀況下,能夠說是全部的)Spring配置。它還提供了許多的啓動器(starter)項目來幫助來減小你的Spring項目構建文件,在你使用Maven或Gradle時。咱們將在第21章講解Spring Boot。


1.4 Spring的新功能

        當這本書的第三版出版時,最晚的Spring版本是3.0.5。這大概是3年前,而且Spring自那起發生了許多的改變。Spring框架迎來了3個重大的發佈---3.1,3.2,以及如今的4.0---每一次發佈都帶來了許多新的特性與改善,以減輕應用開發。而且多個模塊成員經歷了巨大的改變。

        這一版本的Spring in Action已經進行了更新,覆蓋了大多數的這些版本的特性。可是如今,咱們簡單地來看看Spring更新了什麼。


1.4.1 Spring 3.1更新了什麼? 

        Spring 3.1有許多有用的新特性與改進,大多都專一於簡化和改進配置方法。此外,Spring 3.1提供了聲明式的緩存支持,以及對於Spring MVC的許多改進。這裏是Spring 3.1的一些亮點:

爲了解決從多種環境(例如開發,測試,與發佈)選擇獨立的配置的問題,Spring 3.1引入了環境資料(profile)。資料使得一些事情成爲可能,例如,依賴於應用的環境選擇不一樣的數據源bean。

創建在Spring 3.0的基於Java的配置的基礎上,Spring 3.1添加了多個enable註解以容許Spring用一個註解來進行特定特性的切換。

聲明式緩存支持被引入了Spring,使得咱們能夠利用簡單的註解來聲明緩存邊界與規則,這與咱們如何聲明事務邊界相相似。

一個新的c命名空間帶來了構造器注入,與Spring 2.0的p命名空間相似(用於屬性注入),簡化了配置方法。

Spring開始支持Servlet 3.0,包括利用基於Java的配置方法聲明servlet和filter,來取代原來的web.xml。

Spring JPA的改進使得咱們能夠徹底的配置JPA,而不須要persistence.xml。

        Spring 3.1還包括了多個對Spring MVC的加強:

path variables到model attributes的自動綁定

@RequestMappingproduces和consumes屬性,爲了匹配請求的Accept和Content-Type頭

@RequestPart註解,容許綁定部分的multipart請求到handler 方法參數

flush屬性支持(重定向的屬性),和一個RedirectAttributes類型來在請求見傳遞flash屬性

        與Spring 3.1更新了什麼同等重要的是,Spring 3.1中哪些已經再也不使用了。特別地,Spring的JpaTemplate和JpaDapSupport類以不被建議使用,取而代之的是EntityManager。即便它們已經不建議使用,它們依然在Spring 3.2中。可是你不該該使用它們,由於它們沒有升級到支持JPA 2.0,且在Spring 4中已經被移除。

        如今咱們來看看Spring 3.2更新了什麼。


1.4.2 Spring 3.2更新了什麼?

        Spring 3.1專一於利用一些其餘的改進來改善配置方法,包括Spring MVC的改進。Spring 3.2主要是一個專一於Spring MVC的版本。Spring 3.2有以下的改進:

  • Spring 3.2 controllers能夠充分利用Servlet 3的異步請求,將請求的處理傳給多個分離的線程,釋放servlet線程以處理更多的請求。

  • 雖然Spring MVC controllers自Spring 2.5起就能夠簡單地做爲POJO進行測試,Spring 3.2包含了一個Spring MVC測試框架,以針對controllers編寫更加豐富的測試,做爲controller來預警它們的行爲,而不須要servler container。

  • 除了改進controller測試,Spring 3.2包含了對測試基於RestTemplate的客戶端的支持,而不須要發送請求到真實的REST終端。

  • 一個@ControllerAdvice註解容許普通的@ExceptionHandler,@InitBinder,@ModelAttributes方法能夠出如今單一的類中,而且運用到全部的controllers。

  • 在Spring 3.2以前,全內容協議(full content negotiation)僅僅能夠經過ContentNegotiationViewResolver來支持。可是在Spring 3.2中,全內容協議在整個Spring MVC中都是可用的,甚至在一個依賴於消息轉換的controller方法。

  • Spring MVC 3.2包含了一個新的@MatrixVariable註解,已綁定一個請求的矩陣變量來處理方法參數。

  • 抽象基類AbstractDispatcherServletInitializer能夠用來方便的配置DispatcherServlet,而不使用web.xml。一樣,它的子類AbstractAnnotationConfigDispatcherServletInitializer能夠在配置基於Java的Spring配置方法時使用。

  • 添加了ResponseEntityExceptionHandler類做爲DefaultHandlerExceptionResolver的備用而使用。ResponseEntityExceptionHandler方法返回ResponseEntity<Object>而不是ModelAndView。

  • RestTemplate和@RequestBody參數支持通用類型。

  • RestTemplate和@RequestMapping方法支持HTTP PATCH方法。

  • 映射攔截器支持URL模式,以排除在攔截處理以外。

        雖然Spring MVC是Spring 3.2的主要部分,它還加入了一些非MVC的改進。這裏是一些最有趣的新特性:

  • @Autowired,@Value和@Bean註解能夠做爲meta-annotations使用,以建立定製化注入和bean聲明註解。

  • @DateTimeFormat註解再也不依賴於JodaTime。若是出現了JodaTime,它將被使用。不然,使用SimpleDateFormat。

  • Spring的聲明式緩存支持開始初始化支持JCache 0.5。

  • 你能夠定義全局formats以轉換和處理日期和時間。

  • 集成測試能夠配置和載入WebApplicationContext。

  • 集成測試能夠測試request和session範圍的beans。

        你將看到大量的Sping 3.2特性,在本書的多個章節中,特別是在web和REST章節。


1.4.3 Spring 4.0更新了什麼?        

        Spring 4.0是目前最新的Spring版本。在其中有許多的新特性,包含以下:

  • Spring如今包含了對WebSocket編程的支持,包括JSR-356: Java API for WebSocket。

  • 意識到WebSocket提供了一個低級的API,特別須要高級的抽象。Spring 4.0包含了一個高級的面向消息的編程模型,在WebSocket之上,基於SockJS,而且包含了STOMP子協議支持。

  • 一個來自Spring集成項目的帶有多種類型的消息模塊。這個消息模塊支持Sprig的SockJS/STOMP支持。它還包含發佈消息的基於模板的支持。

  • Spring 4.0是最先支持Java 8特性(包含 lambdas)的框架之一。這使得利用回調接口(例如使用JdbcTemplate的RowMapper)更加的乾淨易讀。

  • 爲基於Groovy開發的應用帶來了更加平滑的編程體驗,最重要的是讓Spring應用能夠徹底用Groovy來輕鬆開發。利用來自Grails的BeanBuilder,使Spring應用可以使用Groovy來配置。

  • 爲有條件的bean的建立提供了更加通用的支持,其中,bean只能在條件容許的狀況下被建立。

  • Spring 4.0還包含了Spring的RestTemplate的異步實現,直接返回,可是在操做完成以後回調。

  • 添加了許多JEE規範的支持,包括JMS 2.0,JTA 1.2,JPA 2.1和Bean Validation 1.1。

        如你所見,許多新的成員都在最新的Spring框架中出現。經過本書,咱們將瞭解大多數的新特性,以及長期支持的特性。


1.5 總結

        如今你應該知道Spring到底帶來了什麼吧。Spring旨在使得Java開發更加簡單化,且提倡鬆散耦合的代碼。這其中最重要的是依賴注入和麪向方面編程。

        在本章,你稍微品嚐了一下Spring的DI。DI是一種關聯應用對象的方法,使得對象不須要知道它們的依賴來自哪裏,或者它們是如何實現的。相比它們本身索取依賴,須要依賴的對象被它人給予了其所依賴的對象。由於依賴對象常常知道他們的注入對象(經過接口),使得耦合度很低。

        除了DI,你稍微瞭解了一點Spring的AOP支持。AOP使你可以專一於一個地方----一個方面----邏輯能夠從應用中分散開來。當Spring將你的bean裝配在一塊兒時,這些方面能夠在一個運行時中運行,高效的給予bean以新的行爲。

        DI和AOP是Spring的一切的中心。於是你必須理解如何使用這些重要功能,以可以使用框架的其餘部分。在本章,咱們僅僅看到了Spring的DI和AOP的表面。在接下來的幾章,咱們將深刻探討。

        沒有其餘閒事,讓咱們移動到第二章,來學習如何利用Spring DI將對象裝配在一塊兒。       

相關文章
相關標籤/搜索