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

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

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

一、構(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 時(shí),找構(gòu)造器參數(shù)的過(guò)程分 3 步,核心是「按類型匹配 + 按名稱兜底」:

步驟 1:掃描并注冊(cè)所有 Bean

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

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

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

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

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

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

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

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

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

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

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

運(yùn)行

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

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

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

表格

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

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

1. 注入時(shí)機(jī):“先有雞還是先有蛋”

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

舉個(gè)例子:如果 SysUserService Bean 不存在:

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

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

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

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

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

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

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

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

總結(jié):

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

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

實(shí)戰(zhàn)建議

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

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

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

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

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

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

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

2. 不加final會(huì)怎么樣?

如果去掉 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 無(wú)關(guān));
  • 但字段變成 “可變的”,存在被篡改的風(fēng)險(xiǎn)(比如在某個(gè)方法里寫 this.sysUserService = null);
  • 代碼可讀性下降:無(wú)法一眼看出這些依賴是 “必需且不可變” 的。

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

表格

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

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

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

1. 先搞懂@RequiredArgsConstructor是什么

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

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

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

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

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

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

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

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

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

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

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

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

總結(jié)

關(guān)于 final

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

關(guān)于 @RequiredArgsConstructor

  • 你的代碼不用這個(gè)注解,是因?yàn)椤甘謩?dòng)寫構(gòu)造器更清晰」,而非技術(shù)上不可用;
  • 如果想簡(jiǎn)化代碼,加 @RequiredArgsConstructor 完全可行,和手動(dòng)構(gòu)造器效果一致。

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

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

相關(guān)文章

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

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

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

    Java變量與運(yùn)算符用法詳解

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

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

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

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

    這篇文章主要介紹了SpringMVC轉(zhuǎn)發(fā)與重定向參數(shù)傳遞,對(duì)于重定向,可以通過(guò)FlashMap或RedirectAttributes來(lái)在請(qǐng)求間傳遞數(shù)據(jù),因?yàn)橹囟ㄏ蛏婕皟蓚€(gè)獨(dú)立的HTTP請(qǐng)求,而轉(zhuǎn)發(fā)則在同一請(qǐng)求內(nèi)進(jìn)行,數(shù)據(jù)可以直接通過(guò)HttpServletRequest共享,需要的朋友可以參考下
    2022-07-07
  • springboot?整合hbase的示例代碼

    springboot?整合hbase的示例代碼

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

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

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

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

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

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

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

    java計(jì)算π的多種方法

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

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

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

最新評(píng)論

潼关县| 射阳县| 前郭尔| 扎鲁特旗| 临西县| 绩溪县| 台安县| 偏关县| 临泽县| 习水县| 德兴市| 漠河县| 永嘉县| 商河县| 崇州市| 萍乡市| 东方市| 桓台县| 汉沽区| 华蓥市| 新疆| 新宁县| 通海县| 栖霞市| 阿瓦提县| 西昌市| 张家口市| 邮箱| 云和县| 吴江市| 手游| 青阳县| 冕宁县| 宜宾县| 沅江市| 东乡族自治县| 安徽省| 石狮市| 类乌齐县| 乐至县| 中超|