完整代碼請見:https://github.com/codercuixi...
建立應用對象之間協做關係的行爲一般稱爲裝配(wiring),這也是依賴注入(DI)的本質。java
在xml文件中顯式配置(基本上公司中都不用了)
在Java中進行顯式配置
隱式的bean發現機制和自動裝配
建議:儘量使用自動配置的機制,顯式配置越少越好。
當你必需要顯式配置bean的時候(好比源碼不是由你來維護的,而你須要這些代碼配置bean的時候),使用類型安全而且比xml更爲強大的JavaConfig。
也就是本身寫的儘可能使用自動配置,他人的(第三方)使用JavaConfiggit
主要經過兩個角度來實現自動化配置
組件掃描(component scanning):Spring會自動發現應用上下文所建立的Bean
自動裝配(autowiring):Spring會自動知足bean之間的依賴github
總共分爲三步:
第一步,將要使用的Bean添加上@Component註解spring
package stereo_autoconfig.soundsystem; import org.springframework.stereotype.Component; /** * Create by cuixin on 2018/8/26 **/ @Component public class SgtPeppers implements CompactDisc { private String title = "Sgt.Peppers's Lonely Hearts Club Band"; private String artist = "The Beatles"; @Override public void play() { System.out.println("Play "+title+" by "+ artist); } }
第二步,經過@ComponentScan註解來掃描指定包及其子包中帶有@Component註解的類數組
package stereo_autoconfig.soundsystem; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; /** * Create by cuixin on 2018/8/26 * 經過@ComponentScan註解啓動組件掃描, * 默認掃描所在包及其子包中帶有@Component註解的類 **/ @Configuration @ComponentScan public class CDPlayerConfig { }
第三步,經過@ContextConfiguration配置上下文信息,也就是聲明第二步中使用配置類安全
package stereo_autoconfig.soundsystem; 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; import static org.junit.Assert.assertNotNull; /** * Create by cuixin on 2018/8/26 **/ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = CDPlayerConfig.class) public class CDPlayerTest { @Autowired private CompactDisc cd; @Test public void cdShouldNotBeNull() { assertNotNull(cd); } }
默認bean的id是將類名的第一個字母小寫,好比上面的bean的ID就是sgtPeppers.
若是想要指定不一樣的ID,只需將值傳給@Component註解。session
@Component("lonelyHeartClub") public class SgtPeppers implements CompactDisc { ... }
Spring實戰中說這種方式是類型不安全的,我使用IDEA重命名包時,只有在Refactor時選擇Rename Package纔會更改@ComponentScan中的String值,因此才說這是不安全的。
@ComponentScan默認的掃描的是所在包及其子包,若是你掃描其餘包,或者想掃描多個包,應該怎麼設置呢?指定不一樣的基礎包ide
@Configuration //方式一,指定一個,經過設置value屬性 @ComponentScan("stereo_autoconfig.soundsystem") public class CDPlayerConfig { } @Configuration //方式二,指定一個,經過設置basePackages屬性 @ComponentScan(basePackages = "stereo_autoconfig.soundsystem") public class CDPlayerConfig { } @Configuration//方式三,指定多個,經過設置basePackages爲數組 @ComponentScan(basePackages = {"stereo_autoconfig.soundsystem", "stereo_autoconfig.video"}) public class CDPlayerConfig { }
@Configuration @ComponentScan(basePackageClasses = {CompactDisc.class, Video.class}) public class CDPlayerConfig { }
固然爲了重構時刪除接口及相關類,你能夠在包下面建立一個空標記接口(Marker Interface),專門用來指定基礎包。ui
public Interface SoundSystemMarkerInface{ } @Configuration @ComponentScan(basePackageClasses = {SoundSystemMarkerInface.class, Video.class}) public class CDPlayerConfig { }
經過@ComponentScan,咱們已經能夠掃描獲得Bean,可是如何裝配這個Bean依賴的另外一個bean呢?這就是自動裝配解決的問題了。
簡單來講,自動裝配就是讓Spring自動知足bean依賴的一種方法。this
@Component public class CDPlayer implements MediaPlayer { private CompactDisc cd; @Autowired public CDPlayer(CompactDisc cd){ this.cd = cd; System.out.println("構造器 自動裝配"); } @Override public void play() { cd.play(); } @Autowired public void setCompactDisc(CompactDisc cd){ this.cd = cd; System.out.println("Setter方法 自動裝配"); } @Autowired(required = false) public void insertDisc(CompactDisc cd){ this.cd = cd; System.out.println("其餘方法 自動裝配"); } }
package stereo_autoconfig.soundsystem; 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; import static org.junit.Assert.assertNotNull; /** * Create by cuixin on 2018/8/26 **/ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = CDPlayerConfig.class) public class CDPlayerTest { @Autowired private CompactDisc cd; @Autowired private MediaPlayer player; @Test public void cdShouldNotBeNull() { assertNotNull(cd); } @Test public void play(){ player.play(); } }
不論是構造器,Setter方法仍是其餘的方法,Spring都會去嘗試知足方法參數上所聲明的依賴。上面依次會先執行構造器方法,
而後按照出現代碼順序執行其餘自動注入方法。因此上面的輸出是
構造器 自動裝配
Setter方法 自動裝配
其餘方法 自動裝配
第一點,假若有且只有一個bean匹配依賴需求的話,那麼這個bean就會被裝配進來。
第二點,若是沒有匹配的bean,那麼在應用上下文建立的時候spring會拋出一個異常。
爲了不異常的出現,能夠將@AutoWired的required屬性設置爲false。但這可能致使這個bean處於未轉配狀態,使用的使用須要判斷是否爲空。
第三點,若是出現多個bean知足依賴關係的話,spring的@AutoWired自動裝配也會拋出異常,由於他們不知道選哪個
當使用第三方庫中的組件裝配到你的應用中,在這種狀況下,是沒有辦法在它的類上添加@ComponentScan和@AutoWired註解的,所以就不能使用自動化裝配方案了。
JavaConfig比xml的優點:更爲強大,類型安全,而且對重構友好。
儘管不是必須的,但一般會將JavaConfig放到單獨的包中,使他與其餘的應用程序邏輯分離開來。
步驟:使用@Configuration建立配置類,並使用帶有@Bean的方法聲明bean,最後經過@ContextConfiguration來建立上下文
package stereo_javaconfig.soundsystem; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * Create by cuixin on 2018/8/26 **/ @Configuration //建立配置類 public class CDPlayerConfig { @Bean //經過bean註解來聲明簡單的bean public CompactDisc compactDisc(){ return new SgtPeppers(); } @Bean(name = "cdPlayer") //bean id默認使用的方法名,使用方法參數實現注入,最優方法,能夠將CompactDisc放在其餘配置類中 public CDPlayer cdPlayer(CompactDisc compactDisc){ return new CDPlayer(compactDisc); } // @Bean(name = "cdPlayer2") // //bean id默認使用的方法名,引用建立bean的方法實現注入,spring會攔截全部compactDisc()調用, // // 確保按照規定(single,request,session)使用正確的bean,而不是簡單地每次調用都生成一個 // public CDPlayer cdPlayer2(){ // return new CDPlayer(compactDisc()); // } }
package stereo_javaconfig.soundsystem; 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; import stereo_javaconfig.soundsystem.*; import static org.junit.Assert.*; /** * Create by cuixin on 2018/8/26 **/ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = CDPlayerConfig.class) public class CDPlayeTest { @Autowired private MediaPlayer player; @Test public void player(){ player.play(); } }
只是維護早先代碼的時候你纔會用到這個。