Spring IOC的核心機制:實例化與注入

上文咱們介紹了IOC和DI,IOC是一種設計模式,DI是它的具體實現,有不少的框架都有這樣的實現,本文主要以spring框架的實現,來看具體的注入實現邏輯。java

spring是如何將對象加入容器的

spring將對象加入容器的方式有不少種,最主要的是xml和註解的形式,而當下註解的形式應用更加的普遍,因此這裏咱們也主要介紹註解注入模式下的相關知識點。spring

spring下的註解也是有不少種的,其中應用最爲普遍的就是模式註解。設計模式

模式註解 stereotype annotations

咋一看,這是啥,說實話我也不太清楚,官方說的就叫這哈,其實他說的就是平時最經常使用的那些 @Component @Service @Controller @Repository @Configuration 註解。瀏覽器

若是一個類被打上 @Component註解,那他就會被容器掃描到並加入到容器裏去。那這裏咱們用spring的方式再來實現一下上篇文章的獲取英雄的需求。app

先給 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的意思就是說在這裏注入一個對象,而後咱們啓動應用,ui

經過瀏覽器訪問 http://localhost:8081/v2/banner,會發如今控制檯會輸出 Diana的r方法的打印內容,說明這裏確實拿到了 Diana這個對象。this

對象的注入時機

若是咱們把 Diana@Component註解去掉,其餘地方不變,再運行程序會發生什麼,試試看。spa

從新啓動的時候會提示咱們下面的錯誤:

file

看錯誤提示,說程序沒有找到對應的bean,就是說我這裏要注入bean了,可是在容器裏沒有找到。

這裏就會得出一個結論:容器在初始化後就會給相應的代碼片斷進行對象的注入

從提示信息中能夠看到Autowired(required=true),這裏設置的默認是true,表示注入的時候,該對象必須存在,不然就會注入失敗。固然這裏可設置不報錯,Autowired(required=false),這個時候再啓動就不會報錯了,可是在訪問的時候會報空指針異常。

那這裏spring的容器到底是在何時實例化的對象呢,是在訪問controller的時候呢,仍是在容器啓動的時候呢?咱們能夠經過下面的手段檢測一下,在 Diana的無參構造方法中放一個打印語句,而後再啓動程序:

public Diana() {
    System.out.println("I am Diana");
}複製代碼

程序啓動之後,會看到在控制檯會顯示出構造方法中打印的語句

file

這就說明在容器啓動後把類加載到容器之後,就會實例化一個對象出來。這就又得出一個結論:容器初始化之後就會對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之後,再啓動看一下: file

這個時候控制就沒有那條打印語句的輸出內容了,此時咱們經過瀏覽器來訪問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,再次運行,就會發生報錯:

file

提示你找不到響應的bean。這裏要明確一點哈,雖然報錯了,可是這種寫法是標準的,上面那個直接寫實現類的名字不是標準的寫法。

那怎麼解決這個找不到的問題呢?

spring提供了一個@Qualifier的註解,給他傳入要注入bean的名字,就能夠指定注入的bean,@Qualifier("irelia")。

總結

Autowired的注入方式有兩種,一種是按照類型注入,一種是按照名稱注入,默認是按照類型注入,若是隻有一個實現類 Diana,這裏寫成 private Skill skill,運行程序,你會發現是可以注入成功的。由於Skill類型下只有一個實現類,天然就是注入它了。若是有多個實現類,那就會先按照這個類型查找,這個類型下全部到的實現類再按照名稱查找,直至找到符合要求的,找不到就報錯。

更多關於spring IOC內容,請查看(博客網站)[www.immortalp.com]

相關文章
相關標籤/搜索