Spring從兩個角度來實現自動化裝配:java
爲了更形象的解釋組件掃描與自動裝配,咱們舉一個音響系統的例子,主要包含如下內容:git
關於CD和CD播放器關係的解釋:github
若是你不將CD插入(注入)到CD播放器中,那麼CD播放器實際上是沒有太大用處的。因此,能夠這樣說,spring
CD播放器依賴於CD才能完成它的使命。安全
先建立CD接口CompactDisc:框架
package soundsystem; public interface CompactDisc { void play(); }
而後建立CD接口的一個實現類SgtPeppers:ide
package soundsystem; import org.springframework.stereotype.Component; @Component public class SgtPeppers implements CompactDisc { @Override public void play() { String title = "Sgt.Pepper's Lonely Hearts Club Band"; String artists = "The Beatles"; System.out.println("Playing " + title + " By " + artists); } }
SgtPeppers類與以往類的區別在於使用了@Component註解。這個註解代表該類會做爲組件類,並告知Spring要爲這個類建立bean。測試
建立了bean,那麼如何讓Spring發現它呢?這時就須要用到組件掃描,不過,在Spring中,組件掃描默認是不啓用的。所以咱們須要顯式配置一下Spring,從而命令它去尋找帶有@Component註解的類,併爲其建立bean。ui
建立CDPlayerConfig類:this
package soundsystem; import org.springframework.context.annotation.ComponentScan; @ComponentScan public class CDPlayerConfig { }
這個類與以往類的區別是使用了@ComponentScan註解,這個註解可以Spring中啓用組件掃描。
@ComponentScan默認會掃描與配置類相同的包以及這個包下的全部子包,查找帶有@Component註解的類。
爲了驗證建立的bean可否被Spring發現,咱們建立一個簡單的JUnit測試,完成此測試須要導入如下兩個jar包:
導入jar包的方式以下:
導入完成後的項目結構圖以下所示:
package soundsystem; import static org.junit.Assert.*; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = CDPlayerConfig.class) public class CDPlayerTest { @Autowired private CompactDisc compactDisc; @Test public void cdShouldNotBeNull() { assertNotNull(compactDisc); compactDisc.play(); } }
代碼簡單講解:
@RunWith(SpringJUnit4ClassRunner.class),會在測試開始的時候自動建立Spring的應用上下文。
@ContextConfiguration(classes = CDPlayerConfig.class)會告訴Spring須要在CDPlayerConfig中加載配置。
字段compactDisc上的@Autowired註解,會將CompactDisc bean(實際上是SgtPeppers)注入到測試代碼之中。
運行測試方法cdShouldNotBeNull,會發現測試經過,compactDisc不爲null:
Spring應用上下文中全部的bean都會給定一個ID,默認狀況下,Spring會將類名的第一個字母變爲小寫,做爲該bean的ID。
如上面代碼中SgtPeppers bean的ID爲sgtPeppers。
有如下兩種方式來設置bean ID:
@Component("lonelyHeartsClub") public class SgtPeppers implements CompactDisc { ... }
@Named註解不是Spring框架的註解,而是Java 依賴注入規範(Java Dependency Injection)中的註解,所以須要導入jar包:javax.inject-1.jar。
package soundsystem; import javax.inject.Named; @Named("lonelyHeartsClub") public class SgtPeppers implements CompactDisc { ... }
在Spring項目中建議使用@Component註解。
按照默認規則 ,@ComponentScan註解會以配置類所在的包做爲基礎包(base package)來掃描組件。
但有時候,咱們會將配置類放在單獨的包中,使其與其餘的應用代碼區分開來。
這種場景下,默認的基礎包就知足不了需求。
@ComponentScan註解支持傳入指定的基礎包,有如下幾種場景:
@ComponentScan("soundsystem") public class CDPlayerConfig { }
或者:
@ComponentScan(basePackages = "soundsystem") public class CDPlayerConfig { }
@ComponentScan(basePackages = {"soundsystem", "video"}) public class CDPlayerConfig { }
@ComponentScan(basePackageClasses = {CDPlayer.class}) public class CDPlayerConfig { }
如上所示,basePackageClasses也支持指定多個類,指定類所在的包將會做爲組件掃描的基礎包。
建議使用這種類型安全方式來指定掃描的基礎包。
自動裝配是讓Spring自動知足bean 依賴的一種方法,在知足依賴的過程當中,會在Spring應用上下文中尋找匹配某個bean須要的其餘bean。
實現自動裝配,須要使用Spring的@Autowired註解。
@Autowired通常狀況下,有如下3種使用方式:
package soundsystem; public interface MediaPlayer { void play(); }
package soundsystem; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class CDPlayer implements MediaPlayer { private CompactDisc compactDisc; @Autowired public CDPlayer(CompactDisc compactDisc) { this.compactDisc = compactDisc; } @Override public void play() { compactDisc.play(); } }
package soundsystem; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class CDPlayer implements MediaPlayer { private CompactDisc compactDisc; @Autowired public void setCompactDisc(CompactDisc compactDisc) { this.compactDisc = compactDisc; } @Override public void play() { compactDisc.play(); } }
package soundsystem; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class CDPlayer implements MediaPlayer { private CompactDisc compactDisc; @Autowired public void insertDisc(CompactDisc compactDisc) { this.compactDisc = compactDisc; } @Override public void play() { compactDisc.play(); } }
無論是構造器、Setter方法仍是其餘的方法,Spring都會嘗試知足方法參數上所聲明的依賴。
假若有且只有一個bean匹配依賴需求的話,那麼這個bean將會被裝配進來。
若是沒有匹配的bean,那麼在應用上下文建立的時候,Spring會拋出一個異常。
能夠經過設置require屬性爲false避免該異常出現:
@Autowired(required = false) public CDPlayer(CompactDisc compactDisc) { this.compactDisc = compactDisc; }
不過建議謹慎使用,避免未找到bean進行匹配,並且代碼沒有進行null檢查而出現NullPointerException。
若是有多個bean都能知足依賴關係的話,Spring將會拋出一個異常,代表沒有明確指定要選擇哪一個bean進行自動裝配。
@Autowired註解也能夠替換成@Inject註解(來源於Java依賴注入規範),一樣能夠實現自動裝配:
package soundsystem; import org.springframework.stereotype.Component; import javax.inject.Inject; @Component public class CDPlayer implements MediaPlayer { private CompactDisc compactDisc; @Inject public CDPlayer(CompactDisc compactDisc) { this.compactDisc = compactDisc; } ... }
在Spring項目中建議使用@Autowired註解。
修改CDPlayerTest類代碼測試自動裝配
package soundsystem; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.StandardOutputStreamLog; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = CDPlayerConfig.class) public class CDPlayerTest { @Rule public final StandardOutputStreamLog log = new StandardOutputStreamLog(); @Autowired private MediaPlayer player; @Autowired private CompactDisc compactDisc; @Test public void cdShouldNotBeNull() { assertNotNull(compactDisc); compactDisc.play(); } @Test public void play() { player.play(); assertEquals("Playing Sgt.Pepper's Lonely Hearts Club Band By The Beatles\r\n", log.getLog()); } }
由於代碼中使用了StandardOutputStreamLog類,所以須要導入jar包:system-rules-1.16.0.jar
運行測試方法play(),輸出內容和預期一致,說明字段player已經被MediaPlayer的實現類CDPlayer bean裝配,測試經過,以下所示:
源碼地址:https://github.com/zwwhnly/spring-action.git,歡迎下載。