最近一直陷入一個誤區,老是找一些網上關於SSM速成等視頻學習,然後盲目的跟著'複製'代碼,當時跟著視頻敲完代碼,實現了某些功能後,感覺自己對Spring等一些框架已經有了足夠的瞭解(其實只是知其然,不知其所以然。) 過了一段時間,工作中用不到Spring,等到某天需要使用的時候,突然發現連手動搭建一 ...
最近一直陷入一個誤區,老是找一些網上關於SSM速成等視頻學習,然後盲目的跟著'複製'代碼,當時跟著視頻敲完代碼,實現了某些功能後,感覺自己對Spring等一些框架已經有了足夠的瞭解(其實只是知其然,不知其所以然。) 過了一段時間,工作中用不到Spring,等到某天需要使用的時候,突然發現連手動搭建一個基本的Spring項目環境都不會。。。幡然領悟~~跟著視頻敲的代碼果然都是別人的@_@。故此,買了<Spring Action>這本書,開始系統性的瞭解Spring(@_@)。讀完第一章,總結了其中幾個知識點:
Spring是一個分層的JavaSE/EE full-stack(一站式) 輕量級開源框架。其中一站式是指使用Spring框架就可以構建一個WEB應用,DAO層Spring JDBC Template,WEB層SpringMVC,SERVICE層Spring的IoC。Spring最根本的使命:簡化Java開發。那麼Spring是怎麼做到簡化Java開發的?
圍繞這個使命,Spring採取以下4中關鍵策略來減低Java開發的複雜性:
1.基於POJO的輕量級和最小侵入性編程
POJO(Plain Ordinary Java Object):簡單的Java對象,實際就是普通JavaBeans。
侵入式編程方式:框架通過強迫應用繼承它們的類或實現它們的介面,將應用與框架綁死,緊耦合。
而Spring不會強迫你實現Spring規範的介面或繼承Spring規範的類,在基於Spring構建的應用中,它的類沒有任何痕跡表明你使用了Spring,就算你在類中使用Spring註解,但它仍然是POJO,這樣應用對象彼此之間保持鬆散耦合。那麼Spring是怎麼做到這一點的呢? 方式之一就是通過DI(依賴註入)來裝配它們。
2.通過依賴註入和麵向介面實現松耦合
依賴註入(DI):對象的依賴關係將由系統中負責協調個對象的第三方組件在創建對象的時候進行設定,對象無需自行創建或管理他們的依賴關係。
任何一個有實際意義的應用都會有兩個或更多的類組成,這些類之間進行協作來完成特定的業務邏輯。在傳統的做法中,每個對象負責管理與自己相互協作的對象(即它所依賴的對象)的引用,這將會導致高度耦合和難以測試的代碼。如下:
package chapter1; import infs.Knight; public class DamselRescuingKnight implements Knight { //DamselRescuingKnight類所依賴對象的引用 private RescueDamselQuest quest; /** * 在構造函數中創建所依賴對象 */ public DamselRescuingKnight() { this.quest = new RescueDamselQuest(); //與RescueDamselQuest緊耦合 } public void embarkOnQuest() { //該騎士只能執行RescueDamselQuest探險任務 quest.embark(); } }
在該代碼中,DamselRescuingKnight與RescueDamselQuest緊密地耦合到一起,極大地限制了這個騎士執行探險的能力(即無法擴展該騎士的能力,若需要騎士去救援,則必須另外在寫一個類),更糟糕的是無法進行單元測試。
耦合具有兩面性,一方面,緊密耦合的代碼難以測試、難以復用、難以理解;另一方面,一定的耦合是必須的,不同的類必須以適當的方式進行交互。
如下,Spring通過DI,達到了松耦合的目的。
圖1.1 依賴註入會將所依賴的關係自動交給目標對象,而不是讓對象自己去獲取依賴
package chapter1; import infs.Knight; import infs.Quest; public class BraveKnight implements Knight { //使用Quest介面 private Quest quest; /** * 通過構造函數的方式註入Quest * 只要是實現了Quest介面的類對象都可以註入 * @param quest */ public BraveKnight(Quest quest) { this.quest = quest; } public void embarkOnQuest() { //完全不知道是哪種類型的Quest quest.embark(); } }
以上代碼最重要的點是BraveKnight沒有與特定的Quest實現發生耦合,對於它來說,只要求探險任務實現了Quest介面,具體是哪種類型的探險無關緊要。這就是DI所帶來的最大收益--松耦合。
對依賴進行替換的一個最常用的方法就是在測試的時候使用mock實現。
package chapter1; import static org.mockito.Mockito.*; import org.junit.Test; import infs.Quest; public class BraveKnightTest { @Test public void knightShouldEmbarkOnQuest() { //使用mock(class)創建Quest對象 Quest mockQuest = mock(Quest.class); //註入Quest BraveKnight knight = new BraveKnight(mockQuest); knight.embarkOnQuest(); //當調用embarkOnQuest()方法時,使用verify要求Mockito框架驗證Quest的mock實現的embark()方法僅調用了一次 verify(mockQuest, times(1)).embark(); } }
現在BraveKnight類可以接受任意一種Quest探險任務,如果我現在需要這位勇敢的騎士去消滅一條惡龍,那麼怎麼將消滅惡龍這個特定的Quest任務傳給它?
package chapter1; import java.io.PrintStream; import infs.Quest; public class DestoryDragonQuest implements Quest { private PrintStream stream; public DestoryDragonQuest(PrintStream stream) { this.stream = stream; } public void embark() { stream.println("去消滅惡龍吧!"); } }