Spring快速入門基礎

1、Spring簡介

       Spring框架是由於軟件開發的複雜性而創建的。Spring使用的是基本的JavaBean(POJO,Plain Ordinary Java Object,簡單的Java對象)來完成以前只可能由EJB完成的事情。Spring是一個輕量級控制反轉(IoC)和麪向切面(AOP)的容器框架。Spring可以做很多事情,它爲企業級開發提供給了豐富的功能,但是這些功能的底層都依賴於它的兩個核心特性,也就是依賴注入(dependency injection, DI) 和麪向切面編程(aspect-oriented programming, AOP)。

注:

       JavaBean:一種JAVA語言寫成的可重用組件,它可以被Applet、Servlet、SP等Java應用程序調用.也可以可視化地被Java開發工具使用。它包含屬性(Properties)、方法(Methods)、事件(Events)等特性。

       EJ8(Enterprise Java Beans):企業Java Beans。基於分佈式事務處理的企業級應用程序的組件。EJB是用於開發和部署多層結構的、分佈式的、面向對象的Java應用系統的跨平臺的構件體系結構。

 

2、特點

       輕量:從大小與開銷兩方面而言Spring都是輕量的。完整的Spring框架可以在一個大小隻有1MB多的JAR文件裏發佈。並且Spring所需的處理開銷也是微不足道的。此外,Spring是非侵入式的:典型地,Spring應用中的對象不依賴於Spring的特定類。

        控制反轉:Spring通過一種稱作控制反轉(IoC)的技術促進了松耦合。當應用了IoC,一個對象依賴的其它對象會通過被動的方式傳遞進來,而不是這個對象自己創建或者查找依賴對象。你可以認爲IoC與JNDI相反——不是對象從容器中查找依賴,而是容器在對象初始化時不等對象請求就主動將依賴傳遞給它。

       面向切面:Spring提供了面向切面編程的豐富支持,允許通過分離應用的業務邏輯與系統級服務(例如審計(auditing)和事務(transaction)管理)進行內聚性的開發。應用對象只實現它們應該做的——完成業務邏輯——僅此而已。它們並不負責(甚至是意識)其它的系統級關注點,例如日誌或事務支持。

       容器:Spring包含並管理應用對象的配置和生命週期,在這個意義上它是一種容器,你可以配置你的每個bean如何被創建——基於一個可配置原型(prototype),你的bean可以創建一個單獨的實例或者每次需要時都生成一個新的實例——以及它們是如何相互關聯的。然而,Spring不應該被混同於傳統的重量級的EJB容器,它們經常是龐大與笨重的,難以使用。

       框架:Spring可以將簡單的組件配置、組合成爲複雜的應用。在Spring中,應用對象被聲明式地組合,典型地是在一個XML文件裏。Spring也提供了很多基礎功能(事務管理、持久化框架集成等等),將應用邏輯的開發留給了你。

 

3、爲了降低Java開發的複雜性,Spring採取了以下4種關鍵策略:

  • 基於POJO的輕量級和最小侵入性編程;
  • 通過依賴注入和麪向接口實現松耦合;
  • 基於切面和慣例進行聲明式編程;
  • 通過切面和模板減少樣板式代碼。

注:

      最小侵入性編程:侵入性編程是指,框架通過強迫應用繼承它們的類或實現它們的接口從而導致應用與框架綁死。Spring竭力避免因自身的AP而弄亂你的應用代碼。Spring不會強迫你實現Spring規範的接口或繼承Spring規範的類,相反,在基於Spring構建的應用中,它的類通常沒有任何痕跡表明你使用了Spring,即最小侵入性編程。最壞的場景是,一個類或許會使用Spring註解,但它依舊是POJO。

       依賴注入(DI):通過DI,對象的依賴關係將由系統中負責協調各對象的第三方組件在創建對象的時候進行設定。對象無需自行創建或管理它們的依賴關係,依賴關係將被自動注入到需要它們的對象當中去。

       裝配(wiring):創建應用組件之間協作的行爲通常稱爲裝配(wiring) 。Spring有多種裝配bean的方式,採用XML是很常見的一種裝配方式。Spring還支持使用Java來描述配置。

 

4、依賴注入和控制反轉

(1)依賴注入和控制反轉

       Rod Johnson是第一個高度重視以配置文件來管理Java實例的協作關係的人,他給這種方式起了一個名字:控制反轉(Inverse of Control,IoC)。後來Martine Fowler爲這種方式起了另一個名稱:依賴注入(Dependency Injection),因此不管是依賴注入,還是控制反轉,其含義完全相同。當某個Java對象(調用者)需要調用另一個Java對象(被依賴對象)的方法時,在傳統模式下通常有兩種做法:

  • 原始做法: 調用者主動創建被依賴對象,然後再調用被依賴對象的方法。
  • 簡單工廠模式: 調用者先找到被依賴對象的工廠,然後主動通過工廠去獲取被依賴對象,最後再調用被依賴對象的方法。

       注意上面的主動二字,這必然會導致調用者與被依賴對象實現類的硬編碼耦合,非常不利於項目升級的維護。使用Spring框架之後,調用者無需主動獲取被依賴對象,調用者只要被動接受Spring容器爲調用者的成員變量賦值即可,由此可見,使用Spring後,調用者獲取被依賴對象的方式由原來的主動獲取,變成了被動接受——所以Rod Johnson稱之爲控制反轉

       另外從Spring容器的角度來看,Spring容器負責將被依賴對象賦值給調用者的成員變量——相當於爲調用者注入它依賴的實例,因此Martine Fowler稱之爲依賴注入

(2)兩種注入方式

設值注入

       設值注入是指IoC容器通過成員變量的setter方法來注入被依賴對象。這種注入方式簡單、直觀,因而在Spring的依賴注入裏大量使用。

構造注入

       利用構造器來設置依賴關係的方式,被稱爲構造注入。通俗來說,就是驅動Spring在底層以反射方式執行帶指定參數的構造器,當執行帶參數的構造器時,就可利用構造器參數對成員變量執行初始化——這就是構造注入的本質。

兩種注入方式的對比

設值注入有如下優點:

  • 與傳統的JavaBean的寫法更相似,程序開發人員更容易理解、接受。通過setter方法設定依賴關係顯得更加直觀、自然。
  • 對於複雜的依賴關係,如果採用構造注入,會導致構造器過於臃腫,難以閱讀。Spring在創建Bean實例時,需要同時實例化其依賴的全部實例,因而導致性能下降。而使用設值注入,則能避免這些問題。
  • 尤其在某些成員變量可選的情況下,多參數的構造器更加笨重。

構造注入優勢如下:

  • 構造注入可以在構造器中決定依賴關係的注入順序,優先依賴的優先注入。
  • 對於依賴關係無需變化的Bean,構造注入更有用處。因爲沒有setter方法,所有的依賴關係全部在構造器內設定,無須擔心後續的代碼對依賴關係產生破壞。
  • 依賴關係只能在構造器中設定,則只有組件的創建者才能改變組件的依賴關係,對組件的調用者而言,組件內部的依賴關係完全透明,更符合高內聚的原則。

注意:

       建議採用設值注入爲主,構造注入爲輔的注入策略。對於依賴關係無須變化的注入,儘量採用構造注入;而其他依賴關係的注入,則考慮採用設值注入。

 

5、Spring的核心機制——管理Bean

       程序主要是通過Spring容器來訪問容器中的Bean,ApplicationContext是Spring容器最常用的接口,該接口有如下兩個實現類:

  • ClassPathXmlApplicationContext: 從類加載路徑下搜索配置文件,並根據配置文件來創建Spring容器。
  • FileSystemXmlApplicationContext: 從文件系統的相對路徑或絕對路徑下去搜索配置文件,並根據配置文件來創建Spring容器。

 

6、Spring框架的組成結構圖

(1)Spring核心容器

        容器是Spring框架最核心的部分,它管理着Spring應用中bean的創建、配置和管理。在該模塊中,包括了Spring beanI廠,它爲Spring提供了DI的功能。基於bean工廠,我們還會發現有多種Spring應用上下文的實現,每一種都提供了配置Spring的不同方式。

       除了bean工廠和應用上下文,該模塊也提供了許多企業服務,例如E-mail、JNDI訪問、EJB集成和調度。所有的Spring模塊都構建於核心容器之上。當你配置應用時,其實你隱式地使用了這些類。

(2)Spring的AOP模塊

       在AOP模塊中,Spring對面向切面編程提供了豐富的支持。這個模塊是Spring應用系統中開發切面的基礎。與DI樣,AOP可以幫助應用對象解耦。藉助於AOP,可以將遍佈系統的關注點(例如事務和安全)從它們所應用的對象中解耦出來。

(3)數據訪問與集成

       使用JDBC編寫代碼通常會導致大量的樣板式代碼,例如獲得數據庫連接、創建語句、處理結果集到最後關閉數據庫連接。Spring的JDBC和DAO (Data Access Object)模塊抽象了這些樣板式代碼,使我們的數據庫代碼變得簡單明瞭,還可以避免因爲關閉數據庫資源失敗而引發的問題。該模塊在多種數據庫服務的錯誤信息之上構建了一個語義豐富的異常層,以後我們再也不需要解釋那些隱晦專有的SQL錯誤信息了!

       對於那些更喜歡ORM (Object-Relational Mapping)工具而不願意直接使用JDBC的開發者,Spring提 供了ORM模塊。Spring的ORM模塊建立在對DAO的支持之上,併爲多個ORM框架提供了一種 構建DAO的簡便方式。Spring沒有 嘗試去創建自己的ORM解決方案,而是對許多流行的ORM框架進行了集成,包括Hibernate、 Java Persistermce API、Java Data Object和iBATIS SQL Maps。Spring的事 務管理支持所有的ORM框架以及JDBC.

      本模塊同樣包含了在JMS (Java Message Service)之上構建的Spring抽象層,它會使用消息以異步的方式與其他應用集成。從Spring 3.0開始,本模塊還包含對象到XML映射的特性,它最初是Spring Web Service項目的一部分。

       除此之外,本模塊會使用SpringAOP模塊爲Spring應用中的對象提供事務管理服務。

(4)Web與遠程調用

       MVC (Model-View-Controller) 模式是一種普遍被接受的構建Web應用的方法,它可以幫助用戶將界面邏輯與應用邏輯分離。Java從來不缺少MVC框架,Apache 的Struts、JSF 、WebWork和Tapestry都是 可選的最流行的MVC框架。

       雖然Spring能夠與多種流行的MVC框架進行集成,但它的Web和遠程調用模塊自帶了一個強大的MVC框架,有助於在Web層提升應用的松耦合水平。

        除了面向用戶的Web應用,該模塊還提供了多種構建與其他應用交互的遠程調用方案。Spring遠程調用功能集成了RMI (Remote MethodInvocation)、Hessian、 Burlap、 JAX-WS,同時Spring還自帶了一個遠程調用框架: HTTP invoker。Spring還提供了暴露和使用REST API的良好支持。

(5)Instrumentation

       Spring的Instrumentation模塊提供了爲JVM添加代理(agent) 的功能。具體來講,它爲Tomcat提供了一個織入代理,能夠爲Tomcat傳遞類文件,就像這些文件是被類加載器加載的一-樣。

(6)測試

       鑑於開發者自測的重要性,Spring提 供了測試模塊以致力於Spring應用的測試。通過該模塊,你會發現Spring爲使用JNDI、SerMet和Portlet編 寫單元測試提供了一系列的mock對象實現。對於集成測試,該模塊爲加載Spring應用上下文中的bean集合以及與Spring上下文中的bean進行交互提供了支持。

 

7、Spring Portfolio

       當談論Spring時,其實它遠遠超出我們的想象。事實上,Spring遠不 是Spring框架所下載的那些。如果僅僅停留在覈心的Spring框架層面,我們將錯過Spring Portfolio所提供的鉅額財富。整個Spring Portolio包括多個構建於核心Spring框架之上的框架和類庫。概括地講,整個SpringPortfolio幾乎爲每一個領域的Java開 發都提供了Spring編程模型。

  • Spring Web Flow建立於Spring MVC框架之上,它爲基於流程的會話式Web應用(可以想一下購物車或者嚮導功能)提供了支持。
  • Spring Web Service:雖然核心的Spring框架提供了將Spring bean以聲明的方式發佈爲Web Service的功能,但是這些服務是基於一個具有爭議性的架構(拙劣的契約後置模型)之上而構建的。這些服務的契約由bean的接口來決定。Spring Web Service提供了契約優先的Web Service模型,服務的實現都是爲了滿足服務的契約而編寫的。
  • Spring Security安全對於許多應用都是一個非常關鍵的切面。利用Spring AOP,Spring Security爲Spring應用提供了聲明式的安全機制。
  • Spring Integration許多企業級應用都需要與其他應用進行交互。SpringIntegration提供了多種通用應用集成模式的Spring聲明式風格實現。
  • Spring Batch當我們需要對數據進行大量操作時,沒有任何技術可以比批處理更勝任這種場景。如果需要開發一個批處理應用,你可以通過Spring Batch,使用Spring強大的面向POJO的編程模型。
  • Spring DataSpringData使得在Spring中使用任何數據庫都變得非常容易。儘管關係型數據庫統治企業級應用多年,但是現代化的應用正在認識到並不是所有的數據都適合放在一張表中 的行和列中。-一種新的數據庫種類,通常被稱之爲NoSQL數據庫2),提供了使用數據的新方法,這些方法會比傳統的關係型數據庫更爲合適。不管你使用文檔數據庫,如MongoDB, 圖數據庫,如Neo4j, 還是傳統的關係型數據庫,Spring Data都爲持久化提供了- -種簡單的編程模型。這包括爲多種數據庫類型提供了一種自動化的Repository機制,它負貴爲你創建Repository的實現。
  • Spring Social社交網絡是互聯網領域中新興的- -種 潮流,越來越多的應用正在融入社交網絡網站,例如Facebook或者Twitter.如果對此感興趣,你可以瞭解一下Spring Social,這是Spring的-一個社交網絡擴展模塊。不過,Spring Social並不僅僅是tweet和好友。儘管名字是這樣,但Spring Socia更多的是關注連接(comect),而不是社交(social) 。它能夠幫助你通過REST API連接Spring應用,其中有些Spring應用可能原本並沒有任何社交方面的功能目標。
  • Spring Mobile移動應用是另一個引人矚目的軟件開發領域。智能手機和平板設備已成爲許多用戶首選的客戶端。Spring Mobile是Spring MVC新的擴展模塊,用於支持移動Web應用開發。
  • Spring for Android與Spring Mobile相關的是Spring Android項目。這個新項目,旨在通過Spring框架爲開發基於Android設備的本地應用提供某些簡單的支持。最初,這個項目提供了Spring RestTemplate的一個可以用於Android應用之中的版本。它還能與Spring Social協作,使得原生應用可以通過REST API進行社交網絡的連接。
  • Spring BootSpring極大地簡化了衆多的編程任務,減少甚至消除了很多樣板式代碼,如果沒有Spring的話, 在日常工作中你不得不編寫這樣的樣板代碼。Spring Boot是一個嶄新的令人興奮的項目,它以Spring的視角, 致力於簡化Spring本身。Spring Boot大量依賴於自動配置技術,它能夠消除大部分(在很多場景中,甚至是全部) Spring配置。 它還提供了多個Starter項目,不管你使用Maven還是Gradle,這都能減少Spring工程構建文件的大小。

 

8、實例

例1:普通依賴關係導致緊耦合

public interface Knight {
    void embarkOnQuest();
}
public class DamselRescuingKnight implements Knight {
    private RescueDamselQuest quest;

    public DamselRescuingKnight() {
        this.quest = new RescueDamselQuest();   // 與RescueDamselQuest緊耦合
    }

    public void embarkOnQuest() {
        quest.embark();
    }
}

       可以看到,DamselRescuingKnight 在它的構造函數中自行創建了Rescue DamselQuest. 這使得DamselRescuingKnight 緊密地和RescueDamselQuest耦合到了一起。

       通過DI,對象的依賴關係將由系統中負責協調各對象的第三方組件在創建對象的時候進行設定。對象無需自行創建或管理它們的依賴關係。

例2:依賴注入

public class BraveKnight implements Knight {
    private Quest quest;

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

    public void embarkOnQuest() {
        quest.embark();
    }
}

       這裏的要點是BraveKnight沒有與任何特定的Quest實現發生耦合。對它來說,被要求挑戰的探險任務只要實現了Quest接口,那麼具體是哪種類型的探險就無關緊要了。這就是DI所帶來的最大收益——松耦合。如果一個對象只通過接口(而不是具體實現或初始化過程)來表明依賴關係,那麼這種依賴就能夠在對象本身毫不知情的情況下,用不同的具體實現進行替換。

例3:SlayDragonQuest堤要注入到BraveKnight中的Quest實現

public class SlayDragonQuest implements Quest {
    private PrintStream stream;

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

    public SlayDragonQuest(String s) {

    }

    public void embark() {
        stream.println("Embarking on quest to slay the dragon!");
    }
}

       我們可以看到,SlayDragonQuest實現了 Quest接口,這樣它就適合注入到BraveKnight中去了。這裏最大的問題在於,我們該如何將SlayDragonQuest交給BraveKnight呢?又如何將PrintStream交給SlayDragonQuest呢?

例4:使用Spring將SlayDragonQuest注入到BraveKnight中

<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">

<!--注入Quest-->
<bean id="knight" class="main.java.sia.knights.BraveKnight">
    <constructor-arg ref="quest" />
</bean>

<bean id="quest" class="main.java.sia.knights.SlayDragonQuest">
    <constructor-arg value="#{T(System).out}" />
</bean>

</beans>

knights.xml,該配置文件將BraveKnight、SlayDragonQues t和PrintStream裝配到了一起。

例5:Spring提供了基於Java的配置,可作爲XML的替代方案

package main.java.sia.knights.config;

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

import main.java.sia.knights.BraveKnight;
import main.java.sia.knights.Knight;
import main.java.sia.knights.Quest;
import main.java.sia.knights.SlayDragonQuest;

@Configuration
public class KnightConfig {
    @Bean
    public Knight knight() {
        return new BraveKnight(quest());
    }

    @Bean
    public Quest quest() {
        return new SlayDragonQuest(System.out);
    }
}

       不管你使用的是基於XML的配置還是基於Java的配置,DI所帶來的收益都是相同的。儘管BraveKnight依賴於Quest,但是它並不知道傳遞給它的是什麼類型的Quest,也不知道這個Quest來自哪裏。與之類似,SlayDragonQuest依賴於PrintStream, 但是在編碼時它並不需要知道這個PrintStream是什麼樣子的。只有Spring通過它的配置,能夠了解這些組成部分是如何裝配起來的。這樣的話,就可以在不改變所依賴的類的情況下,修改依賴關係。

如何工作:

       Spring通過應用上下文(Application Contex)裝載bean的定義並把它們組裝起來。Spring應用 上下文全權負責對象的創建和組裝。Spring自帶了多種應用上下文的實現,它們之間主要的區別僅僅在於如何加載配置。

       因爲knights. xml中的bean是使用XML文件進行配置的,所以選擇ClassPathXmlApp1 icationContext作爲應用上下文相對是比較合適的。該類加載位於應用程序類路徑下的一個或多個XML配置文件。

例:

package main.java.sia.knights;

import org.springframework.context.support.
        ClassPathXmlApplicationContext;

public class KnightMain {
    public static void main(String[] args) throws Exception {
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("main/resources/META-INF/spring/knight.xml");
        Knight knight = context.getBean(Knight.class);
        knight.embarkOnQuest();
        context.close();
    }
}

輸出:

       這裏的main()方法基於knights.xml文件創建了Spring應用上下文。隨後它調用該應用上下文獲取一個ID 爲knight的bean。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章