spring的自動註入

来源:https://www.cnblogs.com/sunankang/archive/2022/05/05/16226549.html
-Advertisement-
Play Games

Spring自動註入 spring的ioc 在剛開始學習spring的時候肯定都知道spring的兩個特點:ioc,aop,控制反轉和切麵編程,這篇就只說說ioc ioc是什麼:在我們原來的代碼中,如果A依賴了B,那麼我們會自己在A類中來new B,創建B的實例來使用,是程式主動的去創建依賴,但是我 ...


Spring自動註入

spring的ioc

在剛開始學習spring的時候肯定都知道spring的兩個特點:ioc,aop,控制反轉和切麵編程,這篇就只說說ioc

ioc是什麼:在我們原來的代碼中,如果A依賴了B,那麼我們會自己在A類中來new B,創建B的實例來使用,是程式主動的去創建依賴,但是我們在使用spring的了之後還會在A中主動的去創建B嗎?基本不會,因為創建對象的這個操作從原來的我們來控制變成了spring來管理,這個過程就稱為控制反轉,ioc並不是一種技術,而是一種編程思想,代碼的設計思路

那麼這樣做的好處是什麼?

  • 解耦,將對象之間的依賴關係交給spring來處理,避免硬編碼導致高度耦合

  • 資源的集中易於管理和配置

而spring實現ioc使用的方法是通過DI(依賴註入),當我們大部分的對象都被spring管理後那麼spring也需要將我們A中所依賴的B,C,D...都給填充到A中,這個過程是由spring來管理的,我們只需要按照spring的規則聲明或指定,那麼spring也會幫我們完成依賴的註入

自動註入

瞭解完spring的基本思想後,來回想一下當時學習spring的入門,有沒有說過spring的一個特點就是可以自動註入?

@Component
public class A   {
	@Autowired
	private B b;
}
@Component
public class B {
}

簡單的一段代碼,將A,B交給spring管理,在A中屬性b上添加一個@Autowired,當我們從spring的容器中取出A後發現屬性b居然有值,這個不就是自動註入嗎?

我是認為添加@Autowired註解是不算自動註入的,原因如下

1.名詞解釋

首先,什麼叫自動(給翻譯翻譯什麼叫tm的自動),生活中肯定都見過自動門,通過過自動門,當我們靠近的時候門會自動打開,通過後門自動關閉,自動就是指我們不需要手動的去開門/關門,那麼這裡的自動註入也是,我們不需要去手動的在需要註入的屬性上添加一個@Autowired註解

2.官方文檔

https://docs.spring.io/spring-framework/docs/current/reference/html/

在官方文檔的註入方式中,能看到

Dependency injection (DI) is a process whereby objects define their dependencies (that is, the other objects with which they work) only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method. The container then injects those dependencies when it creates the bean. This process is fundamentally the inverse (hence the name, Inversion of Control) of the bean itself controlling the instantiation or location of its dependencies on its own by using direct construction of classes or the Service Locator pattern.

Code is cleaner with the DI principle, and decoupling is more effective when objects are provided with their dependencies. The object does not look up its dependencies and does not know the location or class of the dependencies. As a result, your classes become easier to test, particularly when the dependencies are on interfaces or abstract base classes, which allow for stub or mock implementations to be used in unit tests.

DI exists in two major variants: Constructor-based dependency injection and Setter-based dependency injection.

翻譯就是

依賴項註入(DI)是一個過程,對象僅通過構造函數參數、工廠方法的參數或對象實例構造或從工廠方法返回後設置的屬性來定義其依賴項(即與它們一起工作的其他對象)。然後,容器在創建bean時註入這些依賴項。這個過程基本上是bean本身的逆過程(因此稱為控制反轉),通過使用類的直接構造或服務定位器模式來控制其依賴項的實例化或位置。

使用DI原則,代碼更乾凈,當對象具有依賴關係時,解耦更有效。對象不查找其依賴項,也不知道依賴項的位置或類別。因此,您的類變得更容易測試,尤其是當依賴項位於介面或抽象基類上時,這允許在單元測試中使用存根或模擬實現。

DI有兩種主要變體:基於構造函數的依賴項註入和基於Setter的依賴項註入。

註意最後一段話:有兩種主要的變體:基於構造和setter方法的依賴註入,也就是說還有其他的註入方式,下麵再細說

那麼你可能會問?我需要怎麼指定或聲明讓他去使用構造或setter方法進行註入而不是我手動指定@Autowired呢?

自動註入模式

還是spring的官網,網頁翻譯的

可以看到spring的自動裝配模式有下麵的4種,而預設的是no,也就是不自動註入

現在來說一下setter註入/構造註入和自動註入模式的關係

假設我們需要在大學中找一個人,我們可以問老師/同學,或者去查看學生名單

那麼找誰呢?我們可以去問他的名字,手機號,或者根據事件去問

比如我問老師有沒有一個叫張三的學生,可以得到結果,或者我根據手機號去查看學生的名單,也可以得到結果

我也可以去問同學,昨天下午逃課出去上網吧的人是誰?這就是根據事件去問,但是我能根據這個事件去查學生名單找到具體那個人嗎?不能

和spring的一樣,我可以根據類型去註入,通過的是setter方法,根據名稱去註入,也是通過的setter方法

但是當我指定使用構造註入的時候,那麼就是通過構造方法進行註入

那麼會發現自動註入以及DI的主要實現這裡面並沒有所說的 "@Autowired"

那麼我們怎麼使用自動註入呢?

在xml配置中,只需要將頭定義中加上一句

default-autowire="byType"

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd"
       default-autowire="byType">

而在javaconfig中要麻煩一點,我們需要自定義一個配置類,實現BeanFactoryPostProcessorBeanDefinitionRegisterPostProcessor來修改beanDefinition的註入模型

@Component
public class BeanFactoryPostProcessor implements BeanFactoryPostProcessor {

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		ScannedGenericBeanDefinition a =(ScannedGenericBeanDefinition) beanFactory.getBeanDefinition("a");
		a.setAutowireMode(2);
	}
}

這個2是啥意思呢,在介面AutowireCapableBeanFactory中,spring定義了每個註入模型的值

public interface AutowireCapableBeanFactory extends BeanFactory {
	int AUTOWIRE_NO = 0;

	int AUTOWIRE_BY_NAME = 1;

	int AUTOWIRE_BY_TYPE = 2;

	int AUTOWIRE_CONSTRUCTOR = 3;
	@Deprecated
	int AUTOWIRE_AUTODETECT = 4;
    ....
}

那麼當我們設置了byName或byType後,只需要提供一個setter方法即可,那麼只要這個屬性名稱的bean存在於spring容器中就會被註入

public interface Test {
}
@Component
public class TestA implements Test {
}
@Component
public class A {

	@Autowired
	private Test testA;

	public void setTestA(Test testA) {
		System.out.println("走set方法啦");
		this.testA = testA;
	}

	public A() {
		System.out.println("走無參構造方法啦");
	}

	public A(Test testA) {
		System.out.println("走有參構造方法啦");
		this.testA = testA;
	}

	@Override
	public String toString() {
		return "A{" +
				"testA=" + testA +
				'}';
	}
}

結果:

走無參構造方法啦
走set方法啦
A{testA=com.jame.pojo.test.TestA@721e0f4f}

當我設置為byType後結果一樣,就不再粘貼代碼了

而當我設置為根據構造註入後,將註入的模型改成3

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        ScannedGenericBeanDefinition a =(ScannedGenericBeanDefinition) beanFactory.getBeanDefinition("a");
        a.setAutowireMode(3);
    }
}

結果

走有參構造方法啦
A{testA=com.jame.pojo.test.TestA@28864e92}

那麼現在你會明白了所謂的@Autowired並不是自動註入,只要指定了這個bean的註入模型為byType/byName/構造後才是自動註入

@Autowired

@Autowired是使用setter註入是構造註入呢?是使用byName還是byType呢?

回答第一個問題:@Autowired是使用setter註入是構造註入呢?

將我的配置類BeanFactoryPostProcessor上的@Component註解去掉,然後在Test testA添加@Autowired

@Component
public class A {


	@Autowired
	private Test testA;

    //下麵的代碼和上面粘貼出的的代碼一樣

}

結果

走無參構造方法啦
A{testA=com.jame.pojo.test.TestA@546a03af}

??什麼情況,setter和構造都沒走,因為@Autowired底層使用的反射filed.set()來填充的屬性

DI有兩種主要變體:基於構造函數的依賴項註入和基於Setter的依賴項註入

主要的是使用構造和setter,而其他的說的就是這種@Autowired的註入方式

第二個問題:是使用byName還是byType呢?

答案是兩個都是,或者兩個都不是,來看例子

public interface Test {
}
@Component
public class TestA implements Test {
}
@Component
public class A {
	@Autowired
	private Test test;//註意這裡的屬性為test
    
    ....省略
}

這樣寫然後從spring容器中獲取肯定能獲取到結果,A中的testA屬性肯定有值的,就不演示了

但是,我給Test介面添加一個實現類TestB

@Component
public class TestB implements Test{

}

繼續執行代碼,發現報錯了

Error creating bean with name 'a': Unsatisfied dependency expressed through field 'test'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.jame.pojo.test.Test' available: expected single matching bean but found 2: testA,testB
創建名稱為“a”的 bean 時出錯:通過欄位“test”表示的依賴關係不滿足;嵌套異常是 org.springframework.beans.factory.NoUniqueBeanDefinitionException:沒有“com.jame.pojo.test.Test”類型的合格 bean 可用:預期單個匹配 bean,但找到 2:testA,testB

到這裡是不是就可以說明@Autowired是byType呢?,那怎麼證明它也是byName呢?

修改A中的Test test屬性為testA時

@Component
public class A {


	@Autowired
	private Test testA;//註意這裡的屬性為testA

	.....省略
}

繼續執行代碼發現又可以了

走無參構造方法啦
A{testA=com.jame.pojo.test.TestA@28864e92}

我們把屬性改為Test testB後繼續測試

走無參構造方法啦
A{testB=com.jame.pojo.test.TestB@28864e92}

發現也是可以的,那麼結論就是:@Autowired既是byType也是byName

當Spring容器中只有一個匹配的類時,會根據Type直接註入,而存在多個匹配的時候(接收的屬性定義為介面,有多個實現類),會直接拋出異常

這時候我們可以使用@Qualifier("testA")來指定具體哪一個類來註入

或者修改屬性的名字為需要註入類的名稱(首字母小寫)

測試了一下@Qualifier()是高於byName的,也就是說即使存在兩個匹配的類,即使屬性名叫testB,我只要使用@Qualifier("testA")來指定bean,那麼註入的就是testA

蕪湖沒了,拜拜


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

-Advertisement-
Play Games
更多相關文章
  • 隨著市場愈發成熟,開發者從平衡收益和風險的角度開始逐步探索混合變現的優勢,內購+廣告就是目前市場上混合變現的主要方式之一。 對於混合變現模式,您是否有這樣的困惑: 如何判斷哪些用戶更願意看廣告、哪些用戶付費意願更高,更好地平衡內購和廣告? 在提升整體收入的基礎上,怎樣為用戶提供更好的產品體驗? HM ...
  • 問題描述: 工作中碰到這樣一種場景, WebApp 已經實現了IM即時通訊及基於WebRTC實現的音視頻會議,音視頻聊天。 也是半路接手的項目,項目整體是使用WKWebView套殼載入h5 頁面實現(後期過審還有很多路要走) 。 h5與原生交互的方案使用的javascriptCore(具體如何使用, ...
  • 今天的內容vue腳手架,越來越有內味了,也慢慢地開始有點難度了哈哈,但是沒有關係,慢慢學慢慢琢磨,我倒是感覺有點越來越像node了,不知道怎麼回事,這是要向後端發展的節奏啊 一.初始化Vue腳手架 1.說明 一般腳手架選擇最新版本 2.具體步驟 全局安裝vue/cli腳手架 切換到項目目錄,運行 v ...
  • 常用函數封裝 獲取某日期若幹個工作日後的日期 * 參數: * time: [String] 給定日期 yyyy-MM-dd * itervalByDay: [Number] 相隔工作日 * separator: [String] 年月日分隔符 * 返回: * rq:[String] 匹配的日期yyy ...
  • 某日,群里有這樣一個問題,如何實現這樣的表盤刻度: 這其實是個挺有意思的問題,方法也有很多。 單標簽,使用 conic-gradient 實現表盤刻度 最簡單便捷的方式,就是利用角向漸變的方式 conic-gradient,代碼也非常簡單,首先,我們實現一個重覆角向漸變: <div></div> d ...
  • 今天是對vue組件化的一個理解,最主要的單文件組件,然後就可以腳手架的學習了,本來昨晚就該上傳的,但是用的那個上傳博客園的Python腳本不行了,換了一個新的。 組件化讓我越來越感覺到框架的力量了 一.模塊與組件,模塊化與組件化 1.對組件的理解 如果以我們原來編寫一個網頁的方式 依賴關係混亂我就不 ...
  • 1.基本介紹 1.1.概念 高層模塊不能依賴於一個“具體化、細節化”的低層模塊,而是通過一個抽象的“規範/標準”建立兩者之間的依賴關係,簡言之就是:不依賴於實現,而是依賴於抽象。這裡“實現”一詞有的地方也稱為“細節”,在編碼中主要體現的是我們根據業務模型具體自定義的普通類,比如:員工類、商品類等。而 ...
  • 分享一下 Idea 的 scope 功能 事情的起因是我在使用 idea 的call hierarchy功能時,覺得它沒有像find usage那樣有排除功能,並且如果點擊了展開全部,當代碼中使用了某些框架導致調用層級非常深時,idea 會占用非常高的 CPU。 於是我去 jetbrains 的缺陷 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...