最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring構(gòu)造器注入及@Autowired、lombok的@RequiredArgsConstructor的異同點說明

 更新時間:2026年03月01日 10:36:49   作者:花花魚  
本文介紹了Spring框架中構(gòu)造器注入的實現(xiàn)原理,包括Spring如何找到參數(shù)、構(gòu)造器注入與Autowired的區(qū)別,以及為什么Spring官方推薦構(gòu)造器注入,文章還強調(diào)了構(gòu)造器注入中使用final關(guān)鍵字的好處,并提供了代碼示例,幫助讀者更好地理解和應用Spring的最佳實踐

一、構(gòu)造器注入:Spring 是如何 “找到參數(shù)” 的?

構(gòu)造器SysUserController:

public SysUserController(SysUserService sysUserService, 
                         ApplicationContext applicationContext, 
                         UserAuthService userAuthService) {
    this.sysUserService = sysUserService;
    this.applicationContext = applicationContext;
    this.userAuthService = userAuthService;
}

Spring 創(chuàng)建 SysUserController Bean 時,找構(gòu)造器參數(shù)的過程分 3 步,核心是「按類型匹配 + 按名稱兜底」:

步驟 1:掃描并注冊所有 Bean

Spring 啟動時,會掃描標注了 @Service/@Component/@Repository 等注解的類,把它們實例化成 Bean,并存入「Spring 容器」(可以理解為一個 “Bean 字典”):

  • SysUserService:因為加了 @Service,會被注冊為「類型 = SysUserService,名稱 = sysUserService」的 Bean;
  • ApplicationContext:Spring 內(nèi)置的核心 Bean,啟動時自動注冊,類型就是 ApplicationContext;
  • UserAuthService:它的實現(xiàn)類(比如 UserAuthServiceImpl)加了 @Service,會被注冊為「類型 = UserAuthService(接口),名稱 = userAuthServiceImpl」的 Bean。

步驟 2:解析構(gòu)造器參數(shù),按「類型」匹配 Bean

Spring 發(fā)現(xiàn) SysUserController 有且只有一個有參構(gòu)造器,會:

遍歷構(gòu)造器的每個參數(shù),先按「參數(shù)類型」去容器里找匹配的 Bean:

  • 第一個參數(shù) SysUserService:容器里有且只有一個 SysUserService 類型的 Bean → 直接匹配;
  • 第二個參數(shù) ApplicationContext:容器里只有一個 Spring 內(nèi)置的 ApplicationContext Bean → 直接匹配;
  • 第三個參數(shù) UserAuthService:容器里有 UserAuthService 類型的實現(xiàn)類 Bean → 直接匹配。

如果一個類型對應多個 Bean(比如有兩個 UserAuthService 實現(xiàn)類),Spring 會再按「參數(shù)名稱」匹配(比如參數(shù)名是 userAuthService,就找名稱為 userAuthService 的 Bean);

把匹配到的 Bean 作為參數(shù),調(diào)用構(gòu)造器,創(chuàng)建 SysUserController 實例。

步驟 3:特殊情況處理(兜底邏輯)

  • 如果按類型 / 名稱都找不到參數(shù)對應的 Bean → 拋出 NoSuchBeanDefinitionException(Bean 不存在);
  • 如果一個類型有多個 Bean,且參數(shù)名稱也匹配不上 → 拋出 NoUniqueBeanDefinitionException(Bean 不唯一);

解決方法:用 @Qualifier("bean名稱") 標注參數(shù),指定具體要哪個 Bean,比如:

運行

public SysUserController(SysUserService sysUserService, 
                         ApplicationContext applicationContext, 
                         @Qualifier("userAuthServiceImpl") UserAuthService userAuthService) {
    // ...
}

二、構(gòu)造器注入 vs @Autowired:核心本質(zhì)差異

先給結(jié)論:兩者最終都是從 Spring 容器找 Bean,但注入時機、強制約束、安全性完全不同,用表格對比核心差異:

表格

維度構(gòu)造器注入@Autowired 字段注入
注入時機Bean 實例化時(構(gòu)造階段)Bean 實例化后(初始化階段)
依賴強制性必須找到所有參數(shù) Bean,否則實例化失敗可選(默認 required=true,可設(shè) false)
字段不可變性可加 final,實例化后字段不可修改不能加 final,字段可被篡改
空指針風險無(實例化時就注入,字段必不為 null)有(初始化失敗時字段為 null)
依賴透明度所有依賴都在構(gòu)造器參數(shù)里,一目了然依賴藏在字段里,需看注解才知道
循環(huán)依賴處理無法解決循環(huán)依賴(Spring 不支持)可以解決循環(huán)依賴(Spring 緩存半成品)
單元測試友好性直接傳參創(chuàng)建實例,無需 Spring 容器需 Mock 容器或反射注入,繁瑣

關(guān)鍵差異拆解(通俗版)

1. 注入時機:“先有雞還是先有蛋”

  • 構(gòu)造器注入:創(chuàng)建 SysUserController 實例時,必須先拿到所有參數(shù) Bean → 實例化完成后,所有依賴都已就緒;
  • @Autowired 字段注入:先調(diào)用無參構(gòu)造器創(chuàng)建 SysUserController 空實例 → 再通過反射給字段賦值 → 如果賦值失敗,實例已經(jīng)創(chuàng)建但字段是 null。

舉個例子:如果 SysUserService Bean 不存在:

  • 構(gòu)造器注入:Spring 連 SysUserController 實例都創(chuàng)建不出來,啟動直接報錯,提前暴露問題;
  • @Autowired 注入:SysUserController 實例能創(chuàng)建,但 sysUserService 字段是 null → 運行時調(diào)用 sysUserService 才會報空指針,問題暴露晚。

2. 不可變性:“常量 vs 變量”

  • 構(gòu)造器注入的字段可以加 final,一旦賦值就不能改 → 線程安全(多線程調(diào)用 Controller 時,字段不會被篡改);
  • @Autowired 字段不能加 final(加了會編譯報錯)→ 字段可能被反射 / 代碼篡改,有線程安全風險。

3. 循環(huán)依賴:“死循環(huán) vs 緩兵之計”

  • 構(gòu)造器注入:如果 A 的構(gòu)造器依賴 B,B 的構(gòu)造器又依賴 A → Spring 實例化時會陷入死循環(huán),直接報錯(強制你解耦,是好事);
  • @Autowired 字段注入:Spring 會創(chuàng)建 “半成品 Bean”(實例化但未初始化),暫時緩存 → 先給 A 注入 B 的半成品,再給 B 注入 A 的半成品 → 表面解決循環(huán)依賴,但會埋下運行時隱患。

三、為什么 Spring 官方推薦構(gòu)造器注入?

結(jié)合你的代碼場景,推薦構(gòu)造器注入的核心原因:

  1. 提前暴露問題:如果 SysUserService/UserAuthService 沒被注冊為 Bean,啟動時就報錯,而不是運行時才發(fā)現(xiàn);
  2. 依賴透明:看構(gòu)造器就知道 SysUserController 依賴哪些 Bean,不用翻字段注解;
  3. 線程安全final 字段不可變,Controller 是單例 Bean,多線程調(diào)用時不會出問題;
  4. 測試方便:單元測試時,不用啟動 Spring 容器,直接 new SysUserController(mockSysUserService, mockContext, mockUserAuthService) 就能創(chuàng)建實例,無需 Mock 注解。

總結(jié):

構(gòu)造器注入找參數(shù)的邏輯:Spring 先掃描注冊所有 Bean → 按參數(shù)「類型」匹配 Bean → 類型不唯一時按「名稱」匹配 → 匹配失敗則啟動報錯;

和 @Autowired 的本質(zhì)差異:核心是「注入時機」和「依賴強制性」—— 構(gòu)造器注入是 “實例化時必須有依賴”,@Autowired 是 “實例化后可選注入”;

實戰(zhàn)建議

  • 必需依賴(比如你的 SysUserService/UserAuthService)→ 用構(gòu)造器注入 + final;
  • 可選依賴(非必須的 Bean)→ 用 @Autowired(required = false);
  • 杜絕 “構(gòu)造器 + @Autowired” 混用,避免依賴不透明和初始化風險。

你的這段構(gòu)造器注入代碼是完全符合 Spring 最佳實踐的,這也是為什么我們之前要把 SysUserService 加到構(gòu)造器里,而不是保留 @Autowired 的原因。

Spring 注入規(guī)范的核心細節(jié),我會結(jié)合你的代碼場景,用 “原理 + 實戰(zhàn)” 的方式講清楚,讓你不僅知其然,還知其所以然。

四、final在構(gòu)造器注入中「不是必須加,但強烈推薦加」

1.final的核心作用(不是為了 Spring,是為了你的代碼)

final 修飾字段的含義是:字段一旦賦值,就不能被修改。在構(gòu)造器注入中加 final 的核心價值和 Spring 注入邏輯無關(guān),而是為了代碼的「安全性 + 可讀性」:

  • 線程安全:Controller 是 Spring 單例 Bean(整個應用只有一個實例),多線程調(diào)用時,final 字段不會被篡改(比如不會被反射 / 其他代碼意外賦值為 null);
  • 依賴不可變:明確表示這些依賴是 Controller 運行的 “必需品”,實例化后絕不更改;
  • 編譯期校驗:如果構(gòu)造器沒有給 final 字段賦值,編譯器會直接報錯(比如漏寫 this.sysUserService = sysUserService),提前規(guī)避低級錯誤。

2. 不加final會怎么樣?

如果去掉 final

private SysUserService sysUserService; // 去掉final
private ApplicationContext applicationContext;
private UserAuthService userAuthService;

public SysUserController(SysUserService sysUserService, ApplicationContext applicationContext, UserAuthService userAuthService) {
    this.sysUserService = sysUserService;
    this.applicationContext = applicationContext;
    this.userAuthService = userAuthService;
}
  • Spring 依然能正常注入(構(gòu)造器注入的核心是 “構(gòu)造器傳參”,和 final 無關(guān));
  • 但字段變成 “可變的”,存在被篡改的風險(比如在某個方法里寫 this.sysUserService = null);
  • 代碼可讀性下降:無法一眼看出這些依賴是 “必需且不可變” 的。

3. 總結(jié):final的使用原則

表格

場景是否加 final原因
構(gòu)造器注入的「必需依賴」必須加線程安全 + 依賴不可變 + 編譯校驗
構(gòu)造器注入的「可選依賴」不加(實際很少見,可選依賴建議用 @Autowired (required=false))

你的代碼中 SysUserService/ApplicationContext/UserAuthService 都是 Controller 的必需依賴,加 final 是完全符合最佳實踐的。

五、為什么你的代碼不用@RequiredArgsConstructor?

1. 先搞懂@RequiredArgsConstructor是什么

@RequiredArgsConstructor 是 Lombok 提供的注解,作用是:自動為類中所有 final 字段生成對應的構(gòu)造器

@RequiredArgsConstructor // 加這個注解
@RestController
@RequestMapping("/user")
public class SysUserController {
    private final SysUserService sysUserService;
    private final ApplicationContext applicationContext;
    private final UserAuthService userAuthService;

    // 不用手動寫構(gòu)造器,Lombok 會自動生成下面的代碼:
    // public SysUserController(SysUserService sysUserService, ApplicationContext applicationContext, UserAuthService userAuthService) {
    //     this.sysUserService = sysUserService;
    //     this.applicationContext = applicationContext;
    //     this.userAuthService = userAuthService;
    // }
}

2. 你的代碼不用@RequiredArgsConstructor的核心原因

不是 “不能用”,而是 “手動寫構(gòu)造器更清晰”,具體分兩種情況:

  • 情況 1:你想「顯式控制構(gòu)造器」手動寫構(gòu)造器可以清晰看到所有依賴,尤其是團隊協(xié)作時,新人不用去查 Lombok 注解的作用,直接看構(gòu)造器就知道依賴了哪些 Bean;
  • 情況 2:不需要 Lombok 簡化(字段少)你的構(gòu)造器只有 3 個參數(shù),手動寫的代碼量很少,沒必要用 Lombok 注解;如果構(gòu)造器參數(shù)有 5+ 個,用 @RequiredArgsConstructor 能減少重復代碼。

3. 手動構(gòu)造器 vs@RequiredArgsConstructor的對比

維度手動寫構(gòu)造器@RequiredArgsConstructor
代碼可讀性高(直接看構(gòu)造器)中(需知道注解作用)
代碼量稍多(3 行賦值)極少(1 行注解)
編譯期依賴無(純 Java 語法)需 Lombok 插件 / 依賴
調(diào)試友好性高(斷點可直接打到構(gòu)造器)中(需 Lombok 反編譯看生成的代碼)

4. 補充:如果要用@RequiredArgsConstructor,代碼怎么寫?

你的代碼可以改成這樣(完全等價,且更簡潔):

@Slf4j
@RequiredArgsConstructor // 替代手動構(gòu)造器
@RestController
@RequestMapping("/user")
public class SysUserController {
    private final SysUserService sysUserService;
    private final ApplicationContext applicationContext;
    private final UserAuthService userAuthService;

    // 無需手動寫構(gòu)造器,Lombok 自動生成
    // ... login 方法等邏輯不變
}

總結(jié)

關(guān)于 final

  • 構(gòu)造器注入中 final 不是 Spring 強制要求的,但「必需依賴」強烈推薦加;
  • 核心價值:線程安全、依賴不可變、編譯期校驗,和構(gòu)造器注入的 “找參數(shù)邏輯” 無關(guān)。

關(guān)于 @RequiredArgsConstructor

  • 你的代碼不用這個注解,是因為「手動寫構(gòu)造器更清晰」,而非技術(shù)上不可用;
  • 如果想簡化代碼,加 @RequiredArgsConstructor 完全可行,和手動構(gòu)造器效果一致。

你的這段代碼寫法(final 字段 + 手動構(gòu)造器注入)是 Spring 官方最推薦的 “標準寫法”,既保證了代碼的安全性,又兼顧了可讀性,完全無需調(diào)整。

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java利用多線程和分塊實現(xiàn)快速讀取文件

    Java利用多線程和分塊實現(xiàn)快速讀取文件

    在工作中經(jīng)常會有接收文件并且讀取落庫的需求,讀取方式都是串行讀取,所以本文主要為大家介紹一下如何利用多線程和分塊實現(xiàn)快速讀取文件,希望對大家有所幫助
    2023-09-09
  • Java變量與運算符用法詳解

    Java變量與運算符用法詳解

    這篇文章介紹了Java中的關(guān)鍵字、標識符、變量、基本數(shù)據(jù)類型、變量作用域、基本數(shù)據(jù)類型間的運算、字符串、基本數(shù)據(jù)類型與字符串的運算、計算機底層數(shù)據(jù)存儲、運算符的使用、賦值運算符、比較運算符、邏輯運算符、位運算符和條件運算符用法,感興趣的朋友跟隨小編看看吧
    2026-01-01
  • 淺析Spring和MyBatis整合及逆向工程

    淺析Spring和MyBatis整合及逆向工程

    這篇文章主要介紹了Spring和MyBatis整合及逆向工程的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2016-06-06
  • SpringMVC轉(zhuǎn)發(fā)與重定向參數(shù)傳遞的實現(xiàn)詳解

    SpringMVC轉(zhuǎn)發(fā)與重定向參數(shù)傳遞的實現(xiàn)詳解

    這篇文章主要介紹了SpringMVC轉(zhuǎn)發(fā)與重定向參數(shù)傳遞,對于重定向,可以通過FlashMap或RedirectAttributes來在請求間傳遞數(shù)據(jù),因為重定向涉及兩個獨立的HTTP請求,而轉(zhuǎn)發(fā)則在同一請求內(nèi)進行,數(shù)據(jù)可以直接通過HttpServletRequest共享,需要的朋友可以參考下
    2022-07-07
  • springboot?整合hbase的示例代碼

    springboot?整合hbase的示例代碼

    這篇文章主要介紹了springboot?整合hbase的示例代碼,本篇詳細總結(jié)了hbase的Java客戶端的使用,在實際開發(fā)過程中,還需要結(jié)合自身的情況做更加細致的整合與優(yōu)化,需要的朋友可以參考下
    2022-04-04
  • SpringBoot3配置文件的使用技巧分享

    SpringBoot3配置文件的使用技巧分享

    SpringBoot 的核心優(yōu)勢之一便是約定大于配置,無需繁瑣的 XML 配置,僅通過簡單的配置文件就能完成項目的個性化定制,本文將從配置文件的核心類型、基礎(chǔ)使用、高級特性、最佳實踐四個維度,全方位拆解 SpringBoot3 配置文件的使用技巧,需要的朋友可以參考下
    2026-02-02
  • java數(shù)組輸出的實例代碼

    java數(shù)組輸出的實例代碼

    這篇文章主要介紹了java數(shù)組輸出的實例代碼,有需要的朋友可以參考一下
    2013-12-12
  • SpringBoot實現(xiàn)企業(yè)級敏感詞攔截檢查系統(tǒng)的設(shè)計方案

    SpringBoot實現(xiàn)企業(yè)級敏感詞攔截檢查系統(tǒng)的設(shè)計方案

    本文介紹了基于SpringBoot和DFA算法的敏感詞檢測系統(tǒng)設(shè)計,涵蓋業(yè)務背景、技術(shù)架構(gòu)、核心模塊、數(shù)據(jù)庫設(shè)計、使用示例、監(jiān)控與告警、安全設(shè)計、部署方案等多個方面,旨在實現(xiàn)高效的實時敏感詞檢測,保證內(nèi)容合規(guī)性,提升平臺穩(wěn)定性,需要的朋友可以參考下
    2026-01-01
  • java計算π的多種方法

    java計算π的多種方法

    這篇文章主要介紹了使用java計算π的多種方法,代碼詳細,邏輯清晰,對于算法思路可能有所幫助,需要的朋友可以參考下
    2021-04-04
  • Java源碼解析之HashMap的put、resize方法詳解

    Java源碼解析之HashMap的put、resize方法詳解

    這篇文章主要介紹了Java源碼解析之HashMap的put、resize方法詳解,文中有非常詳細的代碼示例,對正在學習java的小伙伴們有很大的幫助,需要的朋友可以參考下
    2021-04-04

最新評論

东平县| 葵青区| 安乡县| 黄梅县| 长兴县| 青神县| 睢宁县| 庐江县| 南宁市| 湾仔区| 化州市| 海兴县| 石景山区| 普格县| 刚察县| 武川县| 清原| 灌南县| 东丽区| 武鸣县| 兖州市| 达拉特旗| 化州市| 固阳县| 尉犁县| 慈利县| 工布江达县| 大化| 曲周县| 漳州市| 达日县| 阜新市| 惠安县| 平阴县| 察雅县| 凤翔县| 嘉定区| 富蕴县| 海城市| 鸡泽县| 丘北县|