上文我們介紹了IOC和DI,IOC是一種設計模式,DI是它的具體實現,有很多的框架都有這樣的實現,本文主要以spring框架的實現,來看具體的註入實現邏輯。 spring是如何將對象加入容器的 spring將對象加入容器的方式有很多種,最主要的是xml和註解的形式,而當下註解的形式應用更加的廣泛,所 ...
上文我們介紹了IOC和DI,IOC是一種設計模式,DI是它的具體實現,有很多的框架都有這樣的實現,本文主要以spring框架的實現,來看具體的註入實現邏輯。
spring是如何將對象加入容器的
spring將對象加入容器的方式有很多種,最主要的是xml和註解的形式,而當下註解的形式應用更加的廣泛,所以這裡我們也主要介紹註解註入模式下的相關知識點。
spring下的註解也是有很多種的,其中應用最為廣泛的就是模式註解。
模式註解 stereotype annotations
咋一看,這是啥,說實話我也不太清楚,官方說的就叫這哈,其實他說的就是平時最常用的那些 @Component @Service @Controller @Repository @Configuration
註解。
如果一個類被打上 @Component
註解,那他就會被容器掃描到並加入到容器里去。那這裡我們用spring的方式再來實現一下上篇文章的獲取英雄的需求。
先給 Diana
這個英雄打上一個 @Component
註解:
@Component
public class Diana{
private String skillName = "Diana R";
public Diana() {
}
public void q(){
System.out.println("Diana Q");
}
public void w(){
System.out.println("Diana W");
}
public void e(){
System.out.println("Diana E");
}
public void r(){
System.out.println(this.skillName);
}
}
假設現在有一個Controller需要用到 Diana
@RestController
public class BannerController {
@Autowired
private Diana diana;
@RequestMapping(value = "/v2/banner", method = {RequestMethod.GET})
public String test() {
diana.r();
return "Hello SpringBoot";
}
}
@Autowired
的意思就是說在這裡註入一個對象,然後我們啟動應用,
通過瀏覽器訪問 http://localhost:8081/v2/banner
,會發現在控制台會輸出 Diana
的r方法的列印內容,說明這裡確實拿到了 Diana
這個對象。
對象的註入時機
如果我們把 Diana
的 @Component
註解去掉,其他地方不變,再運行程式會發生什麼,試試看。
重新啟動的時候會提示我們下麵的錯誤:
看錯誤提示,說程式沒有找到對應的bean,就是說我這裡要註入bean了,但是在容器里沒有找到。
這裡就會得出一個結論:容器在初始化後就會給相應的代碼片段進行對象的註入。
從提示信息中可以看到Autowired(required=true),這裡設置的預設是true,表示註入的時候,該對象必須存在,否則就會註入失敗。當然這裡可設置不報錯,Autowired(required=false),這個時候再啟動就不會報錯了,但是在訪問的時候會報空指針異常。
那這裡spring的容器究竟是在什麼時候實例化的對象呢,是在訪問controller的時候呢,還是在容器啟動的時候呢?我們可以通過下麵的手段檢測一下,在 Diana
的無參構造方法中放一個列印語句,然後再啟動程式:
public Diana() {
System.out.println("I am Diana");
}
程式啟動以後,會看到在控制台會顯示出構造方法中列印的語句
這就說明在容器啟動後把類載入到容器以後,就會實例化一個對象出來。這就又得出一個結論:容器初始化以後就會對bean進行實例化
對象的延遲註入
上面提到容器在確定後就會把對象(也就是bean)實例化,那有沒有辦法讓他不馬上實例化?
spring提供了@Lazy這個註解,用以表明某個bean可以延遲載入,用之前的 Diana
延時一下,給他加上@Lazy這個註解:
@Component
@Lazy
public class Diana{
private String skillName = "Diana R";
public Diana() {
System.out.println("I am Diana");
}
public void q(){
System.out.println("Diana Q");
}
public void w(){
System.out.println("Diana W");
}
public void e(){
System.out.println("Diana E");
}
public void r(){
System.out.println(this.skillName);
}
}
再次啟動程式,會發現在控制台還是會執行構造方法里的列印語句,這是什麼情況,不是明明已經加了延遲載入了麽?
這裡會設計到spring的一個機制,就是說如果某個bean沒有設置延遲載入,這個bean會在容器啟動就實例化,並且這個bean裡面所依賴的其他bean都會進行過實例化,即使設置了懶載入。
我們在BannerController里用到了 Diana
這個bean,並把它設置了延遲載入,但是並沒有把BannerController也設置成延遲載入,所以容器再實例化BannerController的時候同樣會 Diana
這個bean進行實例化。如果要真正做到延遲載入,需要讓BannerController也要延遲載入。給它加上@Lazy以後,再啟動看一下:
這個時候控制就沒有那條列印語句的輸出內容了,此時我們通過瀏覽器來訪問BannerController的路由,此時就會在控制台輸出構造方法里列印語句的輸出內容了。
對象的註入邏輯
上文 Diana
的就是一個單獨的類,沒有實現任何介面,這種實現方式他是有問題的,很難進行擴展,這裡不應該一類具體的實現類,而是要應該依賴抽象,這樣才能滿足開閉原則,讓程式具有良好的擴展性。
這裡讓 Diana
實現了一 Skill
介面,
public interface Skill {
void q();
void w();
void e();
void r();
}
在BannerController註入這裡也要改一下:
@Autowired
private Skill diana;
運行程式,發先能夠運行成功,如果新添加一個英雄的實現類:
@Component
public class Irelia implements Skill {
public Irelia() {
System.out.println("Hello, Irelia");
}
public void q(){
System.out.println("Irelia Q");
}
public void w(){
System.out.println("Irelia W");
}
public void e(){
System.out.println("Irelia E");
}
public void r(){
System.out.println("Irelia R");
}
}
我們再去運行程式,發現還是能運行成功,並且發現註入的是 Diana
而不是 Irelia
,那這裡我們可能會問為什麼呀,前面好像也沒指定註入哪個實現類。
難道是因為 private Skill diana;
這裡寫了 diana
麽?你還別說,還真是這個原因,這裡涉及到spring的註入機制了。
當spring的IOC容器註入bean的時候,如果發現有多個相同類型的bean時,就會去看它們的名稱(name),如果名稱符合要求,就會註入這個名稱的bean。每個被容器掃描到的bean都會有一個預設的name,就是它的類名,首字母會小寫,比如我們實例中的:BannerController的name就是bannerController,Irelia就是irelia。
可以試著把 private Skill diana
改成 private Skill skill
,再次運行,就會發生報錯:
提示你找不到響應的bean。這裡要明確一點哈,雖然報錯了,但是這種寫法是標準的,上面那個直接寫實現類的名字不是標準的寫法。
那怎麼解決這個找不到的問題呢?
spring提供了一個@Qualifier的註解,給他傳入要註入bean的名字,就可以指定註入的bean,@Qualifier("irelia")。
總結
Autowired的註入方式有兩種,一種是按照類型註入,一種是按照名稱註入,預設是按照類型註入,如果只有一個實現類 Diana
,這裡寫成 private Skill skill
,運行程式,你會發現是能夠註入成功的。因為Skill類型下只有一個實現類,自然就是註入它了。如果有多個實現類,那就會先按照這個類型查找,這個類型下所有到的實現類再按照名稱查找,直至找到符合要求的,找不到就報錯。
更多關於spring IOC內容,請查看(博客網站)[https://www.immortalp.com]
歡迎大家去 我的博客 瞅瞅,裡面有更多關於測試實戰的內容哦!!