【Spring】依賴注入的方式:構造方法、setter注入、字段注入

在Spring框架中,除了構造器注入(Constructor Injection)Setter注入(Setter Injection),還有一種依賴注入方式:字段注入(Field Injection)。字段注入通過在Bean的字段上直接使用@Autowired(或@Resource@Inject)注解來注入依賴。這種方式在Spring中常用于單例Bean,但也有其局限性和爭議。

以下是對字段注入的詳細說明,包括代碼示例、優缺點、與構造器/Setter注入的對比,以及在單例Bean循環依賴中的表現。


1. 字段注入(Field Injection)

  • 定義:通過在Bean的私有字段上添加@Autowired注解,Spring直接通過反射將依賴注入到字段中,無需構造器或Setter方法。

  • 特點

    • 依賴注入由Spring容器在Bean創建后通過反射完成。
    • 字段通常是私有的,無需提供Getter/Setter,代碼簡潔。
    • 依賴注入的時機在Bean實例化后、初始化前(類似Setter注入)。
  • 代碼示例

    @Component
    public class MyService {public String process() {return "Processed by MyService";}
    }@Controller
    public class MyController {@Autowiredprivate MyService myService; // 字段注入@GetMapping("/test")public String test() {return myService.process();}
    }
    
    • Spring會通過反射將MyService的單例實例注入到MyControllermyService字段。
  • 配置方式

    • 僅需在字段上添加@Autowired(或@Resource@Inject)。
    • 不需要XML或Java配置顯式指定字段注入,Spring自動處理。
    • 如果字段是可選依賴,可設置@Autowired(required = false)
      @Autowired(required = false)
      private MyService myService;
      

2. 字段注入與循環依賴

  • 單例Bean中的循環依賴

    • 字段注入的注入時機與Setter注入類似,發生在Bean實例化后、初始化前。
    • Spring通過三級緩存singletonObjectsearlySingletonObjectssingletonFactories)解決單例Bean的循環依賴。
    • 字段注入支持循環依賴的解決,行為與Setter注入一致。例如:
      @Component
      public class BeanA {@Autowiredprivate BeanB beanB;
      }@Component
      public class BeanB {@Autowiredprivate BeanA beanA;
      }
      
      • 解決流程
        1. 創建BeanA,實例化后放入三級緩存(ObjectFactory)。
        2. BeanA注入beanB,觸發BeanB創建,BeanB放入三級緩存。
        3. BeanB需要BeanA,從三級緩存獲取BeanA的早期引用,注入到beanB字段。
        4. BeanB完成,放入一級緩存;BeanA繼續注入beanB,完成并放入一級緩存。
      • 結果:循環依賴通過三級緩存成功解決,BeanABeanB相互引用。
  • 非單例Bean(如prototype

    • 字段注入無法解決原型作用域的循環依賴,因為Spring不緩存原型Bean。
    • 會拋出BeanCurrentlyInCreationException,需使用@LazyObjectProvider解決。
  • 構造器注入對比

    • 字段注入與Setter注入類似,支持循環依賴的自動解決。
    • 構造器注入由于依賴在實例化時注入,無法利用三級緩存解決循環依賴,需@Lazy或改用字段/Setter注入。

3. 字段注入的優缺點

優點
  1. 代碼簡潔
    • 無需編寫構造器或Setter方法,減少樣板代碼。
    • 適合快速開發或小型項目。
  2. 直觀
    • 依賴直接在字段上聲明,易于查看Bean的依賴關系。
  3. 支持循環依賴
    • 與Setter注入類似,字段注入天然支持單例Bean的循環依賴解決。
  4. 靈活性
    • 支持可選依賴(@Autowired(required = false)),字段可以為空。
缺點
  1. 隱藏依賴關系
    • 依賴未通過構造器或Setter顯式聲明,難以通過代碼接口了解Bean的完整依賴。
    • 違反“顯式優于隱式”的原則。
  2. 測試困難
    • 字段注入依賴Spring的反射機制,單元測試無法通過構造器或Setter傳入Mock對象。
    • 需使用反射工具(如ReflectionTestUtils)或PowerMock修改私有字段,增加測試復雜性。
  3. 不可變性缺失
    • 字段注入的依賴無法使用final修飾,可能被運行時修改(例如通過反射或手動賦值),影響線程安全。
  4. 耦合Spring框架
    • 字段注入依賴@Autowired等Spring注解,Bean與Spring容器強耦合,難以脫離Spring使用。
  5. 潛在空指針風險
    • 如果忘記配置依賴或Spring未正確注入,可能導致運行時NullPointerException(尤其是required = false時)。
  6. 不推薦在現代Spring中
    • Spring官方和社區(如Spring Boot)更推薦構造器注入,字段注入被視為“過時”或“不優雅”的方式。

4. 字段注入 vs 構造器注入 vs Setter注入

特性字段注入構造器注入Setter注入
代碼簡潔性最簡潔,無需方法需要構造器,稍復雜需要Setter方法,中等復雜
依賴強制性可選(required = false強制,必須提供依賴可選,依賴可以為空
不可變性不支持(非final支持(final修飾)不支持,依賴可修改
循環依賴支持(三級緩存)不支持(需@Lazy支持(三級緩存)
線程安全較低(可修改字段)較高(不可變)較低(可修改)
測試友好困難(需反射)簡單(通過構造器Mock)中等(通過Setter Mock)
耦合Spring高(依賴注解)低(可無注解)中等(需注解或XML)
推薦度不推薦(僅簡單場景)推薦(現代Spring首選)次選(可選依賴或循環依賴)

5. 單例Bean中字段注入的行為

  • 單例Bean
    • 默認情況下,Spring容器為每個Bean定義創建單一實例,字段注入的依賴也是單例Bean的同一實例。
    • 多個請求訪問MyController,共享同一個MyController實例及其myService字段。
  • 線程安全
    • 如果myService字段僅用于讀取(無修改),字段注入在單例Bean中是線程安全的。
    • 如果運行時通過反射或其他方式修改myService字段,可能引發線程安全問題(類似Setter注入)。
  • 循環依賴
    • 字段注入與Setter注入一樣,利用Spring的三級緩存解決單例Bean的循環依賴。
    • 注入時機在Bean實例化后,允許Spring先創建Bean再注入早期引用。

6. 字段注入的替代方案

由于字段注入的缺點,推薦以下替代方案:

  1. 構造器注入(首選)

    @Controller
    public class MyController {private final MyService myService;@Autowiredpublic MyController(MyService myService) {this.myService = myService;}@GetMapping("/test")public String test() {return myService.process();}
    }
    
    • 不可變、測試友好、顯式依賴。
    • 使用Lombok的@RequiredArgsConstructor進一步簡化:
      @Controller
      @RequiredArgsConstructor
      public class MyController {private final MyService myService;@GetMapping("/test")public String test() {return myService.process();}
      }
      
  2. Setter注入(次選)

    @Controller
    public class MyController {private MyService myService;@Autowiredpublic void setMyService(MyService myService) {this.myService = myService;}@GetMapping("/test")public String test() {return myService.process();}
    }
    
    • 適合可選依賴或循環依賴場景。
  3. 解決循環依賴

    • 如果字段注入用于解決循環依賴,可改用Setter注入或構造器注入+@Lazy
      @Component
      public class BeanA {private final BeanB beanB;@Autowiredpublic BeanA(@Lazy BeanB beanB) {this.beanB = beanB;}
      }
      

7. 字段注入的使用場景

盡管不推薦,字段注入在以下場景可能仍被使用:

  • 快速原型開發:小型項目或PoC(概念驗證),追求開發速度。
  • 簡單Bean:依賴關系簡單、無需測試或修改的場景。
  • 遺留代碼:早期Spring項目中常見字段注入,維護時可能繼續使用。
  • 非核心代碼:如配置類、工具類,依賴固定且無復雜邏輯。

注意:即使在這些場景中,也應盡量遷移到構造器注入,以提高代碼質量和可維護性。


8. 如何避免字段注入的問題

  1. 強制構造器注入
    • 配置Spring Boot的spring.main.allow-bean-definition-overriding=false,強制顯式依賴。
    • 使用靜態分析工具(如SonarQube)檢測字段注入。
  2. 單元測試
    • 避免字段注入,確保通過構造器或Setter傳入Mock對象。
    • 示例(使用Mockito):
      @Test
      public void testController() {MyService mockService = mock(MyService.class);when(mockService.process()).thenReturn("Mocked");MyController controller = new MyController(mockService);assertEquals("Mocked", controller.test());
      }
      
  3. 代碼規范
    • 團隊約定優先使用構造器注入,禁用字段注入。
    • 使用Lombok或IDE模板減少構造器樣板代碼。

9. 總結

  • 字段注入
    • 通過@Autowired直接注入字段,代碼簡潔但隱藏依賴。
    • 支持單例Bean的循環依賴(通過三級緩存),與Setter注入類似。
  • 缺點
    • 測試困難、不可變性缺失、耦合Spring、潛在空指針風險。
    • 不推薦在現代Spring項目中使用。
  • 推薦
    • 優先使用構造器注入,確保不可變性和測試友好。
    • 次選Setter注入,用于可選依賴或循環依賴。
    • 字段注入僅限快速原型或遺留代碼,盡量遷移到構造器注入。
  • 循環依賴
    • 字段注入支持單例Bean循環依賴,但構造器注入需@Lazy或改用字段/Setter注入。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/77854.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/77854.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/77854.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【數學建模】隨機森林算法詳解:原理、優缺點及應用

隨機森林算法詳解:原理、優缺點及應用 文章目錄 隨機森林算法詳解:原理、優缺點及應用引言隨機森林的基本原理隨機森林算法步驟隨機森林的優點隨機森林的缺點隨機森林的應用場景Python實現示例超參數調優結論參考文獻 引言 隨機森林是機器學習領域中一種…

HttpSessionListener 的用法筆記250417

HttpSessionListener 的用法筆記250417 以下是關于 HttpSessionListener 的用法詳解,涵蓋核心方法、實現步驟、典型應用場景及注意事項,幫助您全面掌握會話(Session)生命周期的監聽與管理: 1. 核心功能 HttpSessionLi…

【Python爬蟲基礎篇】--2.模塊解析

目錄 1.urllib庫 1.1.request模塊 1.1.1、urllib.request.urlopen() 函數 1.1.2.urllib.request.urlretrieve() 函數 1.2. error模塊 1.3. parse 模塊 2. BeautifulSoup4庫 2.1.對象種類 2.2.對象屬性 2.2.1.子節點 2.2.2.父節點 2.2.3.兄弟節點 2.2.4.回退和前進 …

Ubuntu-Linux從桌面到顯示的全流程:技術分析總結

引言 Ubuntu作為主流的Linux發行版,其顯示系統經歷了從傳統X11到現代Wayland的演進。本文將詳細分析從應用程序到屏幕顯示的完整技術流程,包括桌面環境、顯示服務器、圖形棧和硬件交互等核心環節。 1. 系統架構概覽 Ubuntu的顯示系統架構可分為四個主要…

在PyCharm中部署AI模型的完整指南

引言 隨著人工智能技術的快速發展,越來越多的開發者開始將AI模型集成到他們的應用程序中。PyCharm作為一款強大的Python IDE,為AI開發提供了出色的支持。本文將詳細介紹如何在PyCharm中部署AI模型,從環境配置到最終部署的完整流程。 第一部分:準備工作 1. 安裝PyCharm …

WHAT - 靜態資源緩存穿透

文章目錄 1. 動態哈希命名的基本思路2. 具體實現2.1 Vite/Webpack 配置動態哈希2.2 HTML 文件中動態引用手動引用使用 index.html 模板動態插入 2.3 結合 Cache-Control 避免緩存穿透2.4 適用于多環境的動態策略 總結 在多環境部署中,靜態資源緩存穿透是一個常見問題…

PoCL環境搭建

PoCL環境搭建 **一.關鍵功能與優勢****二.設計目的****三.測試步驟**1.創建容器2.安裝依賴3.編譯安裝pocl4.運行OpenCL測試程序 Portable Computing Language (PoCL) 簡介 Portable Computing Language (PoCL) 是一個開源的、符合標準的異構計算框架,旨在為 OpenCL…

【區塊鏈技術解析】從原理到實踐的全鏈路指南

目錄 前言:技術背景與價值當前技術痛點解決方案概述目標讀者說明 一、技術原理剖析核心概念圖解核心作用講解關鍵技術模塊技術選型對比 二、實戰演示環境配置要求核心代碼實現(10個案例)案例1:創建簡單區塊鏈案例2:工作…

在Windows上安裝Git

一、安裝 Git 下載 Git地址:Git - Downloads (git-scm.com) 1、在頁面中找到適用于 Windows 系統的最新版本安裝包(通常為.exe 格式文件),點擊下載鏈接。 出于訪問Git官網需要科學上網,不會的可以私信我要軟件包&…

Golang interface總結(其一)

本篇是對golang 中的interface做一些淺層的、實用的總結 多態 常用場景 interface內僅包含函數類型,然后定義結構體去實現,如下 package mainimport "fmt"type Animal interface {Sound()Act() }type Cat struct{}func (c *Cat) Sound() {…

TVM計算圖分割--Collage

1 背景 為滿足高效部署的需要,整合大量優化的tensor代數庫和運行時做為后端成為必要之舉。現在的深度學習后端可以分為兩類:1)算子庫(operator kernel libraries),為每個DL算子單獨提供高效地低階kernel實現。這些庫一般也支持算…

Redis——內存策略

目錄 前言 1.過期策略 1.1過期策略——DB結構 1.2過期策略——惰性刪除 1.3過期策略——定期刪除 2.淘汰策略 2.1最少最近使用和使用頻率原理 2.2內存淘汰策略執行流程 總結: 前言 Redis之所以性能強,主要的原因就是基于內存存儲。然而單節點的R…

原型模式詳解及在自動駕駛場景代碼示例(c++代碼實現)

模式定義 原型模式(Prototype Pattern)是一種創建型設計模式,通過克隆已有對象來創建新對象,避免重復執行昂貴的初始化操作。該模式特別適用于需要高效創建相似對象的場景,是自動駕駛感知系統中處理大量重復數據結構的…

在kali中安裝AntSword(蟻劍)

步驟一、下載壓縮包 源碼:https://github.com/AntSwordProject/antSword,下載壓縮包。 加載器:https://github.com/AntSwordProject/AntSword-Loader,根據系統選擇壓縮包(kali選擇AntSword-Loader-v4.0.3-linux-x64&…

華為倉頡編程語言基礎概述

第一章:技術演進與誕生背景 1.1 萬物智聯時代的編程挑戰 在5G、物聯網、邊緣計算等技術推動下,全球智能設備數量呈指數級增長。據IDC預測,2025年全球IoT設備將突破550億臺,這對系統級編程語言提出新要求: 異構硬件兼…

【Linux篇】探索進程間通信:如何使用匿名管道構建高效的進程池

從零開始:通過匿名管道實現進程池的基本原理 一. 進程間通信1.1 基本概念1.2 通信目的1.3 通信種類1.3.1 同步通信1.3.2 異步通信 1.4 如何通信 二. 管道2.1 什么是管道2.2 匿名管道2.2.1 pipe()2.2.2 示例代碼:使用 pipe() 進行父子進程通信2.2.3 管道容…

【LeetCode】嚼爛熱題100【持續更新】

2、字母異位詞分組 方法一&#xff1a;排序哈希表 思路&#xff1a;對每個字符串排序&#xff0c;排序后的字符串作為鍵插入到哈希表中&#xff0c;值為List<String>形式存儲單詞原型&#xff0c;鍵為排序后的字符串。 Map<String, List<String>> m new Ha…

2025年最新版 Git和Github的綁定方法,以及通過Git提交文件至Github的具體流程(詳細版)

文章目錄 Git和Github的綁定方法與如何上傳至代碼倉庫一. 注冊 GitHub 賬號二.如何創建自己的代碼倉庫&#xff1a;1.登入Github賬號&#xff0c;完成登入后會進入如下界面&#xff1a;2.點擊下圖中紅色框選的按鈕中的下拉列表3.選擇New repostitory4.進入創建界面后&#xff0…

FPGA開發板這樣做?(一)-像 Arduino 一樣玩 FPGA

這也是一個系列文章&#xff0c;來源之前和粉絲們在評論區討論的國外對于FPGA的開發或者入門所做的努力。 基本一篇文章會介紹一個FPGA開發板&#xff0c;重點在于為開發板準備的開發方式&#xff08;和國內大不相同&#xff09;。 今天的主角-PulseRain M10&#xff1a;像 Ard…

【C++游戲引擎開發】第21篇:基于物理渲染(PBR)——統計學解構材質與光影

引言 宏觀現象:人眼觀察到的材質表面特性(如金屬的高光銳利、石膏的漫反射柔和),本質上是微觀結構對光線的統計平均結果。 微觀真相:任何看似平整的表面在放大后都呈現崎嶇的微觀幾何。每個微表面(Microfacet)均為完美鏡面,但大量微表面以不同朝向分布時,宏觀上會表…