已經想不起來上一次買技術相關的書是什么時候了,一直以來都習慣性的下載一份電子檔看看。顯然,如果不是基於強烈的需求或強大的動力鞭策下,大部分的書籍也都只是蜻蜓點水,浮光掠影。
就像有位同事說的一樣,有些書沒有真真切切的經歷過,你去看,看了就是看了,只是沒有留下多少印象。我回頭仔細想了想,大概就是這樣,好比你去看設計模式相關的書籍,了解到了適配器模式,但是還是不夠深刻。比如說某天你去面試的時候別人會問你,"你了解過適配器模式么,你有過這個模式的開發經歷么,你能否畫出你使用適配器模式的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. 微信