2018-03-30 裝配bean之自動化裝配
最可怕的敵人,就是沒有堅強的信念。
——羅曼·羅蘭
任何一個成功的應用都是由多個為了實現一個業務目標而相互協作的組件構成的。這些組件必須相互了解,並且相互協作來完成工作。創建應用對象之間關聯關系的傳統方法通常會導致結構復雜的代碼,這些代碼很難被復用也很難進行單元測試。
在Spring中對象無需自己查找或創建與其關聯的其他對象(即所依賴的對象),而是通過容器負責將相互協作的對象引用賦予給各個對象。創建應用之間相互協作關系的行為通常稱為裝配,這也是DI的本質。
在應用程序中,Spring怎麽知道你需要創建的bean並將其裝配在一起呢? Spring提供了三種主要的裝配機制:
隱式的bean發現機制和自動裝配
Spring從兩個角度實現自動化裝配:
1)組件掃描(component scanning):Spring會自動發現應用上下文中所創建的bean。
2)自動裝配(autowaring):Spring自動滿足bean之間的依賴。
1.創建可被發現的bean
1)定義一個CD接口,它可將CD播放器的任意實現與CD本身的耦合降低到最小的程度。
package chapter2.practice1; public interface CompactDisc { void play(); }
2)創建CD的實現,需要註意的是@component這個簡單的註解表明該類會作為組件類,並告知Spring要為這個類創建Bean。
package chapter2.practice1; import org.springframework.stereotype.Component; @Component public class SgtPeppers implements CompactDisc { private String title = "死了都要愛"; private String artist = "阿信"; public void play() { System.out.println("Playing " + title + " by " + artist); } }
3)組件掃描默認是不啟用的,我們需要顯示配置一下Spring,從而命令它去尋找帶有@Component註解的類,並為其創建bean。
package chapter2.practice1; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan public class CDPlayerConfig { /** * @ComponentScan 在Sring中啟用組件掃描。默認會掃描與CDPlayerConfig類相同的包,即Spring將會掃描chapter2包及這個 * 包下的所有子包,查找帶有@Component註解的類。 */ }
4)為了測試組件掃描的功能,我們創建一個簡單的JUnit測試,它會創建Spring上下文,並判斷bean是否被真的創建出來了。
package chapter2.practice1; 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 play() { cd.play(); } }
2.為組建掃描的bean命名
Spring應用上下文中所有的bean都會給定一個ID,具體來講,這個bean的ID就是將雷鳴的第一個字母變成小寫。我們也可以通過下面的方式為這個bean指定期望的ID:
1)將期望的ID作為值傳遞給@Component註解,即@Component("期望的ID值")
2)使用Java依賴註入規範(Java Dependency Injection)中所提供的@Named註解來為bean設置ID。
3.設置組件掃描的基礎包
按照默認規則,@ComponentScan會以配置類所在的包作為基礎包來掃描組件。有一個原因會促使我們明確地設置基礎包,那就是我們想要將配置類放在單獨的包中,使其與其它應用代碼區分開來。如果是這樣的話,默認的基礎包就不能滿足要求了。那麽我們怎麽指定一個或多個基礎掃描包呢?
1)在@ComponentScan的value屬性中指定包的名稱,但是該方法只能指定一個基礎包;
2)在@ComponentScan的basePackages屬性中指定一個或多個包的名稱;
3)在@ComponentScan的basePackageClasses屬性中指定類,這些類所在的包將會作為組件掃描的基礎包。
1) 2) 所設置的基礎包是以String類型表示的,是類型不安全的,如果重構代碼的話,那麽所制定的基礎包可能會出現錯誤。3)中設置的是組建類,但是可以考慮在包中創建一個用來進行掃描的空標記接口,這樣可以避免代碼重構可能出現的錯誤。
4.通過為bean添加註解實現自動裝配
在一個應用程序中,很多對象會依賴其他對象相互協作完成任務,我們需要一種方法能夠將組建掃描得到的bean和它們依賴的對象裝配在一起,自動裝配就是Spring自動滿足bean依賴的一種方法,在滿足依賴的過程中,會在Spring應用上下文中尋找匹配某個bean需求的其他bean。
package chapter2.practice1; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class CDPlayer implements MediaPlayer { private CompactDisc cd; @Autowired public void setCd(CompactDisc cd) { this.cd = cd; } @Autowired public CDPlayer(CompactDisc cd) { this.cd = cd; } public void play() { cd.play(); } }
4.1 @Autowired註解能夠用在構造器上,還能用在屬性的Set方法上,在Spring初始化bean後,它會盡可能的滿足bean的依賴。實際上,@Autowired註解可以用在類的任何方法上,Spring都會嘗試滿足方法參數上所聲明的依賴。
1)假如有且只有一個bean匹配依賴需求的話,那麽這個bean就會被裝配進來;
2)如果沒有匹配的bean,那麽應用上下文創建的時候,Spring會拋出一個異常。為了避免異常的出現,可以將@Autowired的requested屬性設置為false,將其設置為false時,Spring會嘗試執行自動裝配,如果沒有匹配的bean,Spring會讓這個bean處於未裝配的狀態;如果在你的代碼中沒有進行null檢查,那麽有可能會出現空指針異常。
3)如果有多個bean能夠滿足依賴關系,那麽Spring會拋出一個異常,表明沒有明確指定要選擇哪個bean進行自動裝配。
4.2 @Autowired是Spring特有的註解,你還可以用@Inject替換它。@Inject註解來源於Java依賴註入規範。
2018-03-30 裝配bean之自動化裝配