《Spring實戰(zhàn)》學習筆記-第一章:Spring之旅

簡潔的Spring

為了降低Java開發(fā)的復(fù)雜性,Spring采取了以下4種關(guān)鍵策略:

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

激發(fā)POJO的潛能

相對于EJB的臃腫,Spring盡量避免因自身的api而弄亂用戶的應(yīng)用代碼,Spring不會強迫用戶實現(xiàn)Spring規(guī)范的接口或繼承Spring規(guī)范的類,相反,在基于Spring構(gòu)建的應(yīng)用中,它的類通常沒有任何痕跡表明你使用了Spring。最壞的場景是,一個類或許會使用Spring注解,但它依舊是POJO。

Spring賦予POJO魔力的方式之一就是通過依賴注入來裝載它們。

依賴注入

任何一個有意義的應(yīng)用一般都需要多個組件,這些組件之間必定需要進行相互協(xié)作才能完成特定的業(yè)務(wù),從而導致組件之間的緊耦合,牽一發(fā)而動全身
代碼示例:

package com.springinaction.knights;

public class DamselRescuingKnight implements Knight {

    private RescueDamselQuest quest;

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

    @Override
    public void embarhOnQuest() throws QuestException {
        quest.embark();
    }

}

正如你所見,DamselRescuingKnight 在它的構(gòu)造函數(shù)中自行創(chuàng)建了RescueDamselQuest,這使得DamselRescuingKnight和RescueDamselQuest緊密地耦合到了一起,因此極大地限制了這個騎士的執(zhí)行能力。如果一個少女需要救援,這個騎士能夠召之即來。但是如果一條惡龍需要殺掉,那么這個騎士只能愛莫能助了。

另一方面,可以通過依賴注入的方式來完成對象之間的依賴關(guān)系,對象不再需要自行管理它們的依賴關(guān)系,而是通過依賴注入自動地注入到對象中去。

代碼示例:

package com.springinaction.knights;

public class BraveKnight implements Knight {

    private Quest quest;

    public BraveKnight(Quest quest) {
        this.quest = quest;// quest被注入到對象中
    }

    @Override
    public void embarhOnQuest() throws QuestException {
        quest.embark();
    }

}

不同于之前的DamselRescuingKnight,BraveKnight沒有自行創(chuàng)建探險任務(wù),而是在構(gòu)造器中把探險任務(wù)作為參數(shù)注入,這也是依賴注入的一種方式,即構(gòu)造器注入

更為重要的是,BraveKnight中注入的探險類型是Quest,Quest只是一個探險任務(wù)所必須實現(xiàn)的接口。因此,BraveKnight能夠響RescueDamselQuest、SlayDraonQuest等任意一種Quest實現(xiàn),這正是多態(tài)的體現(xiàn)。

這里的要點是BraveKnight沒有與任何特定的Quest實現(xiàn)發(fā)生耦合。對它來說,被要求挑戰(zhàn)的探險任務(wù)只要實現(xiàn)了Quest接口,那么具體是哪一類型的探險就無關(guān)緊要了。這就是依賴注入最大的好處--松耦合。如果一個對象只通過接口(而不是具體實現(xiàn)或初始化的過程)來表明依賴關(guān)系,那么這種依賴就能夠在對象本身毫不知情的情況下,用不同的具體實現(xiàn)進行替換。

注入一個Quest到Knight

創(chuàng)建應(yīng)用組件之間協(xié)作關(guān)系的行為稱為裝配,Spring有多種裝配Bean的方式,其中最常用的就是通過XML配置文件的方式裝配。
示例代碼:使用Spring將SlayDragonQuest注入到BraveKnight中。

<?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.xsd">

    <bean id="knight" class="com.springinaction.knights.BraveKnight">
        <constructor-arg ref="quest"></constructor-arg>
    </bean>

    <bean id="quest" class="com.springinaction.knights.SlayDragonQuest"></bean>

</beans>

Spring是如何注入的?

Spring通過應(yīng)用上下文(ApplicationContext)來裝載Bean,ApplicationContext全權(quán)負責對象的創(chuàng)建和組裝。

Spring自帶了多種ApplicationContext來加載配置,比如,Spring可以使用ClassPathXmlApplicationContext來裝載XML文件中的Bean對象。

package com.springinaction.knights;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class KnightMain {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("knights.xml");// 加載Spring上下文
        Knight knight = (Knight) context.getBean("knight");// 獲取knight Bean
        knight.embarhOnQuest();// 使用knight
    }
}

這個示例代碼中,Spring上下文加載了knights.xml文件,隨后獲取了一個ID為knight的Bean的實例,得到該對象實例后,就可以進行正常的使用了。需要注意的是,這個類中完全不知道是由哪個Knight來執(zhí)行何種Quest任務(wù),只有knights.xml文件知道。

應(yīng)用切面

通常情況下,系統(tǒng)由許多不同組件組成,其中的每一個組件分別負責一塊特定功能。除了實現(xiàn)自身核心的功能之外,這些組件還經(jīng)常承擔著額外的職責,諸如日志、事務(wù)管理和安全等,此類的系統(tǒng)服務(wù)經(jīng)常融入到有自身核心業(yè)務(wù)邏輯的組件中去,這些系統(tǒng)服務(wù)通常被稱為橫切關(guān)注點,因為它們總是跨越系統(tǒng)的多個組件,如下圖所示。

對遍布系統(tǒng)的橫切關(guān)注點的調(diào)用散布在各個組件里,而這些關(guān)注點并不是組件的核心業(yè)務(wù)
對遍布系統(tǒng)的橫切關(guān)注點的調(diào)用散布在各個組件里,而這些關(guān)注點并不是組件的核心業(yè)務(wù)

AOP可以使得這些服務(wù)模塊化,并以聲明的方式將它們應(yīng)用到相應(yīng)的組件中去,這樣,這些組件就具有更高內(nèi)聚性以及更加關(guān)注自身業(yè)務(wù),完全不需要了解可能涉及的系統(tǒng)服務(wù)的復(fù)雜性。總之,AOP確保POJO保持簡單。

利用AOP,可以將橫切關(guān)注點覆蓋在所需的組件之上,而這些組件不再需要額外的關(guān)注這些非核心業(yè)務(wù)。
利用AOP,可以將橫切關(guān)注點覆蓋在所需的組件之上,而這些組件不再需要額外的關(guān)注這些非核心業(yè)務(wù)。

如圖所示,我們可以把切面想象為覆蓋在很多組件之上的一個外殼。利用AOP,你可以使用各種功能層去包裹核心業(yè)務(wù)層。這些層以聲明的方式靈活應(yīng)用到你的系統(tǒng)中,甚至你的核心應(yīng)用根本不知道它們的存在。

AOP應(yīng)用

接上面騎士的故事,現(xiàn)在需要一個詩人來歌頌騎士的勇敢事跡,代碼如下「Minstrel是中世紀的音樂記錄器」:

package com.springinaction.knights;

public class Minstrel {
    public void singBeforeQuest() { // 探險之前調(diào)用
        System.out.println("Fa la la; The knight is so brave!");
    }

    public void singAfterQuest() { // 探險之后調(diào)用
        System.out.println("Tee hee he; The brave knight did embark on a quest!");
    }
}

如代碼中所示,詩人會在騎士每次執(zhí)行探險前和結(jié)束時被調(diào)用,完成騎士事跡的歌頌。騎士必須調(diào)用詩人的方法完成歌頌:

package com.springinaction.knights;

public class BraveKnight implements Knight {

    private Quest quest;
    private Minstrel minstrel;

    public BraveKnight(Quest quest) {
        this.quest = quest;// quest被注入到對象中
    }
    
    public BraveKnight(Quest quest, Minstrel minstrel) {
        this.quest = quest;// quest被注入到對象中
        this.minstrel = minstrel;
    }

    @Override
    public void embarhOnQuest() throws QuestException {
        minstrel.singAfterQuest();
        quest.embark();
        minstrel.singAfterQuest();
    }

}

但是,感覺是騎士在路邊抓了一個詩人為自己「歌功頌德」,而不是詩人主動地為其傳揚事跡。簡單的BraveKnight類開始變得復(fù)雜,如果騎士不需要詩人,那么代碼將會更加復(fù)雜。

但是有了AOP,騎士就不再需要自己調(diào)用詩人的方法為自己服務(wù)了,這就需要把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">

    <bean id="knight" class="com.springinaction.knights.BraveKnight">
        <constructor-arg ref="quest"></constructor-arg>
    </bean>

    <bean id="quest" class="com.springinaction.knights.SlayDragonQuest"></bean>

    <!-- 聲明詩人Minstrel,待切入的對象(刀) -->
    <bean id="minstrel" class="com.springinaction.knights.Minstrel"></bean>

    <aop:config>
        <aop:aspect ref="minstrel">
            <!-- 定義切面,即定義從哪里切入 -->
            <aop:pointcut expression="execution(* *.embarkOnQuest(..))"
                id="embark" />
            <!-- 聲明前置通知,在切入點之前執(zhí)行的方法 -->
            <aop:before method="singBeforeQuest" pointcut-ref="embark" />

            <!-- 聲明后置通知,在切入點之后執(zhí)行的方法  -->
            <aop:after method="singAfterQuest" pointcut-ref="embark" />
        </aop:aspect>
    </aop:config>

</beans>

通過運行結(jié)果可以發(fā)現(xiàn),在沒有改動BraveKnight的代碼的情況下,就完成了Minstrel對其的歌頌,而且BraveKnight并不知道Minstrel的存在。

使用Spring模版

使用Spring模版可以消除很多樣板式代碼,比如JDBC、JMS、JNDI、REST等。

容納Bean

在Spring中,應(yīng)用對象生存于Spring容器中,如圖所示,Spring容器可以創(chuàng)建、裝載、配置這些Bean,并且可以管理它們的生命周期。

在Spring中,對象由Spring容器創(chuàng)建、裝配、管理
在Spring中,對象由Spring容器創(chuàng)建、裝配、管理

Spring的容器實現(xiàn)

  • Bean工廠(org.springframework.beans.factory.BeanFactory):最簡單的容器,提供基本的DI支持;
  • 應(yīng)用上下文(org.springframework.context.ApplicationContext):基于BeanFactory之上構(gòu)建,提供面向應(yīng)用的服務(wù)。

常用的幾種應(yīng)用上下文

  • ClassPathXmlApplicationContext:從類路徑中的XML配置文件加載上下文,會在所有的類路徑(包括jar文件)下查找;
  • FileSystemXmlApplicationContext:從文件系統(tǒng)中讀取XML配置文件并加載上下文,在指定的文件系統(tǒng)路徑下查找;
  • XmlWebApplicationContext:讀取Web應(yīng)用下的XML配置文件并加載上下文;

Bean的生命周期

Spring中Bean的生命周期
Spring中Bean的生命周期
  1. Spring對Bean進行實例化;
  2. Spring將值和Bean的引用注入進Bean對應(yīng)的屬性中;
  3. 如果Bean實現(xiàn)了BeanNameAware接口,Spring將Bean的ID傳遞給setBeanName()接口方法;
  4. 如果Bean實現(xiàn)了BeanFactoryAware接口,Spring將調(diào)setBeanFactory()接口方法,將BeanFactory容器實例傳入;
  5. 如果Bean實現(xiàn)了ApplicationContextAware接口,Spring將調(diào)用setApplicationContext()接口方法,將應(yīng)用上下文的引用傳入;
  6. 如果Bean實現(xiàn)了BeanPostProcessor接口,Spring將調(diào)用postProcessBeforeInitialization()接口方法;
  7. 如果Bean實現(xiàn)了InitializationBean接口,Spring將調(diào)用afterPropertiesSet()方法。類似的如果Bean使用了init-method聲明了初始化方法,該方法也會被調(diào)用;
  8. 如果Bean實現(xiàn)了BeanPostProcessor接口,Spring將調(diào)用ProcessAfterInitialization()方法;
  9. 此時此刻,Bean已經(jīng)準備就緒,可以被應(yīng)用程序使用了,它們將一直駐留在應(yīng)用上下文中,直到該應(yīng)用上下文被銷毀;
  10. 如果Bean實現(xiàn)了DisposableBean接口,Spring將調(diào)用destory()方法,同樣的,如果Bean中使用了destroy-method聲明了銷毀方法,也會調(diào)用該方法;

縱觀Spring

Spring模塊

Spring中的6個重要模塊
Spring中的6個重要模塊

核心Spring容器

容器是Spring框架最核心的部分,它負責Spring應(yīng)用中Bean的創(chuàng)建、配置和管理。Spring模塊都構(gòu)建與核心容器之上,當配置應(yīng)用時,其實都隱式地使用了相關(guān)的核心容器類。另外,該模塊還提供了許多企業(yè)級服務(wù),如郵件、JNDI訪問、EJB集成和調(diào)度等。

AOP

AOP是Spring應(yīng)用系統(tǒng)開發(fā)切面的基礎(chǔ),與依賴注入一樣,可以幫助應(yīng)用對象解耦。借助于AOP,可以將遍布于應(yīng)用的關(guān)注點(如事務(wù)和安全等)從所應(yīng)用的對象中解耦出來。

數(shù)據(jù)訪問與集成

Spring的JDBC和DAO模塊封裝了大量的樣板代碼,這樣可以使得在數(shù)據(jù)庫代碼變得簡潔,也可以更專注于我們的業(yè)務(wù),還可以避免數(shù)據(jù)庫資源釋放失敗而引發(fā)的問題。另外,Spring AOP為數(shù)據(jù)訪問提供了事務(wù)管理服務(wù)。同時,Spring還與流程的ORM(Object-Relational Mapping)進行了集成,如Hibernate、MyBatis等。

Web和遠程調(diào)用

Spring提供了兩種Web層框架:面向傳統(tǒng)Web應(yīng)用的基于Servlet的框架和面向使用Java Portlet API的基于Portlet的應(yīng)用。Spring遠程調(diào)用服務(wù)集成了RMI、Hessian、Burlap、JAX-WS等。

測試

Spring提供了測試模塊來測試Spring應(yīng)用。


如果覺得有用,歡迎關(guān)注我的微信,有問題可以直接交流:

你的關(guān)注是對我最大的鼓勵!
你的關(guān)注是對我最大的鼓勵!
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,117評論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,860評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,128評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,291評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,025評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,421評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,477評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,642評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,177評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,970評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,157評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,717評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,410評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,821評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,053評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,896評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,157評論 2 375

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,806評論 18 139
  • 依賴注入 按照傳統(tǒng)的做法,每個對象負責管理與自己相互協(xié)作的對象的引用,這將會導致高度耦合和難以測試的代碼。例如: ...
    謝隨安閱讀 544評論 0 0
  • 縱覽Spring , 讀者會發(fā)現(xiàn)Spring 可以做非常多的事情。 但歸根結(jié)底, 支撐Spring的僅僅是少許的基...
    六尺帳篷閱讀 1,653評論 0 12
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,900評論 6 342
  • 父親的財富 微風如裙紗一般 溫柔拂過青澀的苗圃 一雙可人的 興艷的眸子 笑意里是黃土地 給予父親的希望 童年的時候...
    零溫度閱讀 258評論 0 3