盡管Spring的配置風(fēng)格是可以互相搭配的,但是應(yīng)該盡可能的使用自動(dòng)配置的機(jī)制,顯式配置越少越好
Spring從兩個(gè)角度來實(shí)現(xiàn)自動(dòng)化裝配:
- 組件掃描:Spring會(huì)自動(dòng)發(fā)現(xiàn)應(yīng)用上下文中所創(chuàng)建的bean
- 自動(dòng)裝配:Spring會(huì)自動(dòng)滿足bean之間的依賴
組件掃描和自動(dòng)裝配組合在一起就可以將所需要的顯式配置降低到最少。
首先定義一個(gè)代表了CD概念的接口CompactDisc:
public interface CompactDisc {
void play();
}
作為一個(gè)接口,它將CD播放器和CD之間的耦合降低到了最小的程度。
下面創(chuàng)建一個(gè)CD接口的實(shí)現(xiàn)SgtPeppers類:
import org.springframework.stereotype.Component;
@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.print("Playing " + title+" by " + artist);
}
}
注意在這里使用了@Component注解,這個(gè)注解表明該類會(huì)作為組件類,并告知Spring為該類創(chuàng)建bean。只需要使用了這個(gè)注解,就沒有必要顯式配置SgtPeppers bean,Spring會(huì)把所有任務(wù)處理妥當(dāng)。
我們可以在@Component注解中為bean命名,如果采取默認(rèn)ID的話,Spring會(huì)根據(jù)類名指定一個(gè)ID,具體規(guī)則就是將類名的第一個(gè)字母變成小寫。如果需要自定義ID的話,可以采取下面這樣的方式配置注解:
import org.springframework.stereotype.Component;
@Component("happyBean")
public class SgtPeppers implements CompactDisc {
}
不過需要注意的是:組件掃描默認(rèn)是不啟用的。我們需要顯式配置一下Spring,從而命令它去尋找所有帶有@Component注解的類,并為它創(chuàng)建bean。
下面這個(gè)CDPlayerConfig類展現(xiàn)了如何實(shí)現(xiàn)顯式配置Spring的組件掃描:
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan
public class CDPlayerConfig {
}
在CDPlayerConfig類中沒有顯式聲明任何bean,但是它通過使用@ComponentScan注解,讓Spring啟用了組件掃描。如果沒有其他配置的話,Spring會(huì)默認(rèn)掃描與配置類相同的包。
如果希望Spring不僅僅掃描默認(rèn)的基礎(chǔ)包,那么我們可以采用如下的方式進(jìn)行配置:
@Configuration
@ComponentScan(basePackages = {"soundsystem","video"})
public class CDPlayerConfig {
}
但是采用這樣的方法是類型不安全的,如果需要重構(gòu)代碼的話,那么所指定的基礎(chǔ)包就可能出現(xiàn)錯(cuò)誤。除了將包設(shè)置為簡單的String之外,@Component還提供了另一種方法,那就是將其指定為包中所包含的類或者接口:
@Configuration
@ComponentScan(basePackageClasses = {CDPlayer.class, Video.class})
public class CDPlayerConfig {
}
雖然在上面的示例中使用的是組件類,但是我們應(yīng)該考慮在包中創(chuàng)建一個(gè)用來進(jìn)行掃描的空標(biāo)記接口。通過標(biāo)記接口的方式,依然能夠保持對重構(gòu)友好的接口引用,同時(shí)能夠避免引用任何實(shí)際的應(yīng)用程序代碼。
下面我們使用測試類來測試CompactDisc是不是真的創(chuàng)建出來了:
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 cd;
@Test
public void cdShouldNotBeNull() {
assertNotNull(cd);
}
}
這個(gè)測試類自動(dòng)創(chuàng)建了Spring應(yīng)用上下文,注解@ContextConfiguration告訴其需要在CDPlayerConfig中加載配置。因?yàn)镃DPlayerConfig類中包含了@ComponentScan,因此最終的應(yīng)用上下文應(yīng)該包含了CompactDisc bean。
這個(gè)測試類應(yīng)該可以通過測試,并以測試成功的顏色顯示。
組件掃描之后就應(yīng)該是自動(dòng)裝配了。簡單來說,自動(dòng)裝配就是讓Spring自動(dòng)滿足bean依賴的一種方法,在滿足依賴的過程中,會(huì)在Spring應(yīng)用上下文中尋找匹配某個(gè)bean需求的其他bean。
為了實(shí)現(xiàn)自動(dòng)裝配,我們可以借助Spring的@Autowired注解。
考慮下面這個(gè)示例,通過自動(dòng)裝配,將一個(gè)CompactDisc注入到CDPlayer中:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class CDPlayer implements MediaPlayer {
private CompactDisc cd;
@Autowired
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}
public void play() {
cd.play();
}
}
它實(shí)現(xiàn)的MediaPlayer接口:
public interface MediaPlayer {
void play();
}
@Autowired注解不僅可以用在構(gòu)造器上,在其他方法上,Spring都會(huì)嘗試滿足方法參數(shù)上聲明的依賴。
如果沒有匹配的bean,那么在創(chuàng)建應(yīng)用上下文時(shí),Spring會(huì)拋出一個(gè)異常。
下面使用一個(gè)測試類來測試自動(dòng)裝配是否成功:
import static org.junit.Assert.*;
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;
@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",
log.getLog());
}
}
在最后的斷言中,paly()測試方法調(diào)用了CDPlayer的paly()方法,并且斷言它的行為和預(yù)期的一致。
????文章原文在這里