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

来源:https://www.cnblogs.com/zyjimmortalp/archive/2020/05/04/12828726.html
-Advertisement-
Play Games

上文我們介紹了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註解去掉,其他地方不變,再運行程式會發生什麼,試試看。

重新啟動的時候會提示我們下麵的錯誤:

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內容,請查看(博客網站)[https://www.immortalp.com]

歡迎大家去 我的博客 瞅瞅,裡面有更多關於測試實戰的內容哦!!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • JavaScript實現對象混合與對象淺度克隆和對象的深度克隆 1.實現對象混合: this.myPlugin = this.myPlugin || {}; /** * 將obj2混合到obj1產生新對象 * 實現方式1 */ this.myPlugin.mixObj = function (obj ...
  • 構造函數中的方法每當new一個對象的時候,就會創建一個構造函數里的方法,如果多個實例對象就會創建多個方法,占用記憶體,沒有提高代碼的復用性; 將方法定義到構造函數的原型對象里,創建多個實例對象而共用一個方法,方法創建了一次。 <script> function Persion(name, age) { ...
  • 初識前端 前端開發工作已經變的越來越複雜,僅僅是想羅列一份前端開發的學習列表就已經是一件艱巨的工作。曾經只要會編寫 HTML, CSS 和Javascript 就是能夠找到一份前端開發工作的全部要求。而現在,web 開發遠遠不止是簡單編碼。因為我們的互聯網上有了更多的內容,也因為有更多的人、更多設備 ...
  • 定義: 外觀模式(Facade):又叫門面模式,為子系統中的一組介面提供一個一致的界面,此模式定義了一個高層介面,這個介面使得這一子系統更加容易使用。 代碼實例: 假設實現一個功能需要用到子系統中的四個方法。 /** * Class SubSystemOne */ class SubSystemOn ...
  • 定義: 裝飾器模式(Decorator):動態的給一個對象添加一些額外的職責,就增加功能來說,裝飾器比生成子類更加靈活。 結構: Component:定義一個對象介面,可以給這些對象動態地添加職責。 ConcreteComponent:定義了一個具體的對象,也可以給這個對象添加一些職責。 Decor ...
  • 一、enum關鍵字 enum關鍵字是在Java1.5也就是Java SE5之後引入的一個新特性:它通過關鍵字enum來定義一個枚舉類,這個被定義的枚舉類繼承Enum類,這個枚舉類算是一種特殊類,它同樣能像其他普通類一樣擁有構造器、方法,也能夠實現介面,但是它不能再繼承其他別的類,因為它的直接父類是E ...
  • 1.編譯 如果你已經是一位程式員,並且對語言有了大概的概念,那麼不推薦你從這裡開始看,因為那樣可能會 浪費你的時間,從這裡開始是將是我幾乎為零的起點開始描述記錄的。對於以及有經驗的來說,並不重要 。如果你瞭解過編成,那麼也許會知道,幾乎所有編成語言大可分為兩類, 編譯型語言和解析性語言。 而C語言屬 ...
  • 1. jupyter提供導出的格式有.py、.html、.md、.pdf等。 2.需要先安裝MiKTeX,Windows版一路next安裝就行,安裝包有190MB,安裝過程還是耗費些時間的,下載路徑:https://miktex.org/download 3.ipynb文件編譯為tex,在命令行中定 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...