Spring實戰——無需一行xml配置實現自動化注入


  已經想不起來上一次買技術相關的書是什么時候了,一直以來都習慣性的下載一份電子檔看看。顯然,如果不是基於強烈的需求或強大的動力鞭策下,大部分的書籍也都只是蜻蜓點水,浮光掠影。

  就像有位同事說的一樣,有些書沒有真真切切的經歷過,你去看,看了就是看了,只是沒有留下多少印象。我回頭仔細想了想,大概就是這樣,好比你去看設計模式相關的書籍,了解到了適配器模式,但是還是不夠深刻。比如說某天你去面試的時候別人會問你,"你了解過適配器模式么,你有過這個模式的開發經歷么,你能否畫出你使用適配器模式的UML圖..." 如果當時看書的時候沒有動手,你可能會先“讓懵逼飛一會兒”, 然后一直懵逼-_-! 設想面試之后,你便迫不及待的回去重新翻了一遍什么是設配器模式,有哪幾種適配器模式,連抄代寫的畫出了自己的UML圖。這時候書上的適配器模式就成了你自己的適配器模式。

  上面大概說了下電子檔書籍可能會讓你帶來看書上面的懈怠,畢竟大部分電子檔書籍都是唾手可得的,近乎免費(偶爾有些網站需要積分來兌現),而人卻又這樣的一個認知慣性,就是這樣越容易得到的往往不太會珍惜,所以讀書往往不能物盡其用。當然了,電子檔數據有其先天的優勢,比如便宜,比如方便易攜帶,比如你猛地一天想起一個知識點,就可以全局搜索一把來精准定位等等。說到這些優點也真是紙質書籍相形見絀的,一本800頁的大部頭書籍讓你裝在包里用來乘坐地鐵時打發時間,我想能堅持下來的體力應該都還不錯。對於動輒就是好幾十甚至好幾百的書籍,再查看下這個月的消費賬單,想想還是先填飽自己的肚子吧。或者哪天你突然想起來需要找到一句在書中出現的至理名言,好吧,准備瓶眼葯水可勁的找吧,但願你當時做了筆記或者折了書角。但是紙質書籍也有他的好,首先不上眼,其次可以做筆記,當你真真切切的翻過某一頁的時候,你可能會謹慎的問自己,過去的一夜我真的懂了么。

  說了這么多閑話,只是為了引出——我買了一本《Spring 實戰(第四版)》

  理由很簡單:一是學習需要;二是支持下知識產權。

 

  看了第一章和第二章,發現其實在Spring 4版本中有很多的新特性了,但是網上流傳的還是傳統的用法,所以決定有必要梳理分享下。

環境介紹

  Intellij Idea:14.0.2

  Gradle:2.7

  JDK:1.8.0_60

  Spring-framework: 4.0.7.RELEASE

 

  這篇開始主要講的是依賴注入,值得一提的是,從作者的文字看來是極力推崇自動化裝配方案的,而不是稍顯臃腫的基於xml配置。

環境搭建

  Gradle 

  Gradle是一個基於Apache Ant和Apache Maven概念的項目自動化建構工具。它使用一種基於Groovy的特定領域語言(DSL)來聲明項目設置,拋棄了基於XML的各種繁瑣配置。

  這個東東以前從來沒有接觸過,但是因為Spring提供的代碼是基於這個構建工具來解決依賴管理的,我暫且認為它和maven的作用等同,某種程度上來說,比maven來的要簡潔(源於maven是基於xml方式配置依賴的),具體看后面就知道是不是名副其實的簡潔style了。真的看下來,其實也沒有什么特別的地方,起碼示例項目跑起來並不那么復雜。

  首先來一睹Spring實戰的某個項目中用於自動化構建的文件

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'

jar {
    baseName = 'stereo-autoconfig'
    version =  '0.0.1-SNAPSHOT'
}

repositories {
    mavenLocal()
    mavenCentral()
}

dependencies {
	compile "org.springframework:spring-context:${springVersion}"

	testCompile "org.springframework:spring-test:${springVersion}"
	testCompile "com.github.stefanbirkner:system-rules:${systemRulesVersion}"
}

task wrapper(type: Wrapper) {
	gradleVersion = '1.11'
}

  這里的repositories實際上除了添加maven倉庫以外,還支持Ivy和Ant倉庫

  dependencies中就是你們熟悉的maven配置,乍一看,着實簡潔了太多,不用在寫GroupId,ArtifactId,Version等等。這個簡潔范兒,我喜歡。

  還有就是gradleVersion,就是Gradle版本。

 

  備注:本來准備自己機子上裝個Gradle,但是發現機子上不知什么時候已經裝好了,好吧,就不再介紹安裝了,網上一查資料很多跟配置Maven差不多。

 

  項目導入

  1. 打開Intellij Idea,選擇File->Import Project

 

  2.選擇導入的項目

 

  3. 選擇Gradle項目類型

 

  4. 進入下一步選擇Gradle點擊finish

 

  5. 完成build compile等步驟后,完成項目導入

 

  6. 項目目錄結構

 

  備注:在構建項目的過程中遇到類似這樣的錯誤

19:25:19: Executing external task 'build'...
:compileJava

FAILURE: Build failed with an exception.

* What went wrong:
Could not resolve all dependencies for configuration ':compile'.
> Could not resolve org.springframework:spring-context:4.0.7.RELEASE.
  Required by:
      :stereo-mixedconfig:unspecified
   > Could not HEAD 'http://maven.oschina.net/content/groups/public/org/springframework/spring-context/4.0.7.RELEASE/spring-context-4.0.7.RELEASE.pom'.
      > Connection to http://maven.oschina.net refused

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

BUILD FAILED

Total time: 24.733 secs
Connection timed out: connect
19:25:45: External task execution finished 'build'.

 

  主要就是中央倉庫慢或者ping不通,無法下載依賴的jar包,最后通過更改中央倉庫的地址為

repositories {
    mavenLocal()
    maven { url "http://maven.aliyun.com/nexus/content/groups/public" }
}

 

Spring裝配Bean的方式有三種:

  在XML中進行顯示配置;

  在Java中進行顯示配置;

  隱式的bean發現機制和自動裝配。

 

  書中作者推薦的順序依次是隱式的bean發現機制和自動裝配->在Java中進行顯示配置->在XML中進行顯示配置,理由也很簡單,盡可能的減少顯示配置,好比在XML文件中聲明這種。這里首先來介紹這種自動化裝配方式(基於XML的方式已經在《學習Spring——依賴注入》中介紹過了)。

 

Spring實現自動化化裝配需要從以下兩個方面:

  組件掃描(component scanning): Spring會自動發現應用上下文中所創建的bean。

  自動裝配(autowiring):Spring自動滿足bean之間的依賴。

 

代碼

  這里的應用場景是CD機播放光盤,所以需要准備幾個bean。類的關系圖如下

  首先需要CompactDisc類,再次一個CDPlayer類,我們需要Spring將CompactDisc bean注入CDPlayer來實現真正的播放音樂。

public interface CompactDisc {
  void play();
}

  這是一個接口,其定義了CD播放機對於光盤的操作。

 

  這時候我們還有一個實現CompactDisc接口的實現類SgtPeppers(《Sgt. Pepper's Lonely Hearts Club Band》 是英國搖滾樂隊The Beatles發行於1967年6月1日的第8張錄音室專輯)。

@Component
public class SgtPeppers implements CompactDisc {

  private String title = "Sgt. Pepper's Lonely Hearts Club Band";  
  private String artist = "The Beatles";
  
  public void play() {
    System.out.println("Playing " + title + " by " + artist);
  }
  
}

  這個@Component注解很關鍵,有了這個注解,好比該類自動告訴Spring,SgtPeppers就是一個組件類,Spring會自動為其創建bean。

  使用@Component注解,Spring會默認為其創建名為sgtPeppers的bean(第一個字母小寫),你也可以自定義bean的名字比如通過配置@Component("lonelyHeartsClub“”)就能創建名為lonelyHeartsClub的bean。

 

  關於Spring會自動為其創建bean,並不完全正確,因為我還需要多做一步才能真正達到這樣的效果。我們需要讓Spring的組件掃描配置並開啟,使得Spring能夠掃描這些帶有@Component或者其他注解的類。

@Configuration
@ComponentScan
public class CDPlayerConfig { 
}

  @Configuration表明了它是一個配置類;

  @ComponentScan表明該類啟用Spring的組件掃描機制,或許你看到最多或者用的最多的應該是這種<context:component-scan base-package="soundsystem">,有了@ComponentScan,我們就不需要這種方式了,是不是簡潔了很多。

  另外,@ComponentScan默認是掃描與配置類在相同包下面的類,當然,你也可以自定義掃描一個或多個包路徑。比如配置如下

@Configuration
@ComponentScan(basePackages={"soundsystem", "video"})
public class CDPlayerConfig { 
}

  

  也可以指定掃描一個或多個類和接口比如

@Configuration
@ComponentScan(basePackageClasses={CDPlayer.class, DVDPlayer.class})
public class CDPlayerConfig { 
}

  

  下面來看看Spring是如何將CompactDisc注入到CDPlayer中的

@Component
public class CDPlayer implements MediaPlayer {
  private CompactDisc cd;

  @Autowired
  public CDPlayer(CompactDisc cd) {
    this.cd = cd;
  }

  public void play() {
    cd.play();
  }

}

  首先必不可少需要一個@Component注解以示該類收Spring管轄。

  再者,我們看到在CDPlayer的構造函數上,我們使用了@Autowired注解,該注解是用來在Spring為CDPlayer創建bean的時候,通過這個構造器傳入一個可設置給CompactDisc的bean,從而解決CDPlayer類對於CompactDisc的依賴問題。

  除了通過構造函數注入bean還有通過setter方法注入,比如

@Autowired
publicvoidsetCompactDisc(CompactDisc cd) {
      this.cd = cd;
}

  甚至還可以通過將@Autowired注解用在任何類的方法上來實現依賴注入問題。

 

測試驗證

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=CDPlayerConfig.class)
public class CDPlayerTest {

  @Rule
  public final StandardOutputStreamLog log = new StandardOutputStreamLog();

  @Autowired
  private MediaPlayer player;
  
  @Autowired
  private CompactDisc cd;
  
  @Test
  public void cdShouldNotBeNull() {
    assertNotNull(cd);
  }

  @Test
  public void play() {
    player.play();
    assertEquals(
        "Playing Sgt. Pepper's Lonely Hearts Club Band by The Beatles\r\n",
        log.getLog());
  }

}

  這一個測試類基本就能驗證上面的知識點。

  首先運行cdShouldNotBeNull()方法得到結果如下

 

  該測試方法,表明通過自動化裝配的方法,注入到CDPlayerTest類中的CompactDisc確實是經過Spring實例化后的bean,而不是空對象。

 

  運行play()方法

  該測試方法表明,注入到測試方法類中的MediaPlayer也是被Spring實例化過后的bean,而且可以調用該類中的play方法。

 

  以上測試說明,Spring的自動化裝配就是這么簡潔高效。

  另外我覺得將CDPlayer類改寫如下

@Component
public class CDPlayer implements MediaPlayer {

  @Autowired
  private CompactDisc cd;

  public CDPlayer() {

  }

  public void play() {
    cd.play();
  }

}

  也是可行的,並且也通過了測試,這種方式是工作中做常見的場景之一。

 

如果您覺得閱讀本文對您有幫助,請點一下“推薦”按鈕,您的“推薦”將是我最大的寫作動力!如果您想持續關注我的文章,請掃描二維碼,關注JackieZheng的微信公眾號,我會將我的文章推送給您,並和您一起分享我日常閱讀過的優質文章。


如果你覺得博主的文章對你那么一點小幫助,恰巧你又有想打賞博主的小沖動,那么事不宜遲,趕緊掃一掃,小額地贊助下,攢個奶粉錢,也是讓博主有動力繼續努力,寫出更好的文章^^。

  1. 支付寶                             2. 微信

                      

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM