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

SpringBoot中依賴注入的常用方式對比詳解

 更新時間:2026年07月26日 10:00:48   作者:dkbnull  
依賴注入(Dependency Injection,DI)是 Spring 框架的核心特性之一,本文將系統(tǒng)講解構(gòu)造器注入、字段注入、Setter 注入、方法注入的用法與區(qū)別,并涵蓋 @Autowired 與 @Resource 的差異,感興趣的小伙伴可以了解下

依賴注入(Dependency Injection,DI)是 Spring 框架的核心特性之一,也是實現(xiàn) IoC(控制反轉(zhuǎn))的具體方式。Spring 提供了多種依賴注入方式,每種方式各有優(yōu)劣。本文系統(tǒng)講解構(gòu)造器注入、字段注入、Setter 注入、方法注入的用法與區(qū)別,并涵蓋 @Autowired@Resource 的差異、循環(huán)依賴問題、最佳實踐等內(nèi)容。

一、依賴注入的基本概念

1.1 什么是依賴注入

依賴注入是指對象不再自行創(chuàng)建其依賴項,而是由容器(Spring IoC 容器)在運行時將依賴項注入到對象中。

傳統(tǒng)方式(手動創(chuàng)建依賴):Controller ──new──→ Service ──new──→ Repository,對象自己控制依賴的創(chuàng)建,耦合度高

依賴注入(容器管理依賴):

  • Container ──注入──→ Controller
  • Container ──注入──→ Service
  • Container ──注入──→ Repository

容器統(tǒng)一管理對象的創(chuàng)建和裝配,對象只聲明需要的依賴

1.2 依賴注入的三個角色

角色說明示例
依賴者需要其他組件的對象UserController
依賴項被需要的組件UserService
注入者負責(zé)將依賴項注入依賴者Spring IoC 容器

1.3 Spring 依賴注入方式總覽

注入方式注解 / 配置特點
構(gòu)造器注入構(gòu)造方法 + @Autowired(可省略)官方推薦,不可變,可保證完全初始化
字段注入@Autowired 標注在字段上簡潔,但不推薦
Setter 注入@Autowired 標注在 Setter 方法上可選依賴,可重新注入
方法注入@Autowired 標注在普通方法上靈活,可注入多個依賴
Lookup 方法注入@Lookup解決單例注入原型的問題

二、構(gòu)造器注入

2.1 基本用法

通過構(gòu)造方法將依賴傳入類中。Spring 4.3+ 如果類只有一個構(gòu)造方法,@Autowired 可以省略。

import org.springframework.stereotype.Service;

/**
 * 訂單服務(wù)
 */
@Service
public class OrderService {

    private final UserService userService;
    private final OrderRepository orderRepository;

    /**
     * 構(gòu)造器注入(Spring 4.3+ 單構(gòu)造方法可省略 @Autowired)
     */
    public OrderService(UserService userService, OrderRepository orderRepository) {
        this.userService = userService;
        this.orderRepository = orderRepository;
    }

    public void createOrder(Long userId) {
        // 使用注入的依賴
        userService.getUser(userId);
        orderRepository.save(null);
    }
}

2.2 結(jié)合 Lombok 簡化

實際項目中最常見的寫法,使用 @RequiredArgsConstructor 自動生成構(gòu)造方法:

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 * 訂單服務(wù)
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class OrderService {

    private final UserService userService;           // final 字段
    private final OrderRepository orderRepository;   // final 字段

    public void createOrder(Long userId) {
        userService.getUser(userId);
        orderRepository.save(null);
    }
}

關(guān)鍵@RequiredArgsConstructor 只為 final 字段和 @NonNull 標注的字段生成構(gòu)造方法。非 final 字段不會包含在構(gòu)造方法中。

2.3 多構(gòu)造方法場景

當類有多個構(gòu)造方法時,必須用 @Autowired 指定主構(gòu)造方法:

@Service
public class OrderService {

    private final UserService userService;
    private final OrderRepository orderRepository;
    private final LogService logService;  // 可選依賴

    /**
     * 主構(gòu)造方法(Spring 使用此構(gòu)造方法實例化)
     */
    @Autowired
    public OrderService(UserService userService, OrderRepository orderRepository) {
        this.userService = userService;
        this.orderRepository = orderRepository;
        this.logService = null;
    }

    /**
     * 輔助構(gòu)造方法(不參與 Spring 注入)
     */
    public OrderService(UserService userService, OrderRepository orderRepository,
                        LogService logService) {
        this.userService = userService;
        this.orderRepository = orderRepository;
        this.logService = logService;
    }
}

2.4 優(yōu)缺點

優(yōu)點缺點
依賴不可變(final 字段),線程安全依賴較多時構(gòu)造方法參數(shù)過多
對象創(chuàng)建即完全初始化,不會出現(xiàn) NPE無法注入循環(huán)依賴(但這本身是好事)
易于單元測試(可直接 new 傳入 mock)需要配合 Lombok 減少樣板代碼
明確聲明所有必需依賴
Spring 官方推薦

三、字段注入

3.1 基本用法

直接在字段上標注 @Autowired,Spring 通過反射注入依賴:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * 訂單服務(wù)(字段注入 - 不推薦)
 */
@Service
public class OrderService {

    @Autowired
    private UserService userService;

    @Autowired
    private OrderRepository orderRepository;

    public void createOrder(Long userId) {
        userService.getUser(userId);
        orderRepository.save(null);
    }
}

3.2 優(yōu)缺點

優(yōu)點缺點
代碼簡潔,最少樣板代碼無法使用 final,依賴可變,不安全
不需要構(gòu)造方法或 Setter單元測試困難(無法通過構(gòu)造方法傳入 mock)
隱藏了依賴關(guān)系(類表面看不到依賴)
違反單一職責(zé)(加依賴太容易,容易過度膨脹)
依賴注入發(fā)生在對象創(chuàng)建之后,可能存在半初始化狀態(tài)
Spring 官方不推薦

Spring 官方建議:從 Spring 4.0 開始,官方文檔明確推薦構(gòu)造器注入,不推薦字段注入。

3.3 為什么字段注入仍被廣泛使用

盡管不推薦,字段注入在實際項目中仍很常見,原因:

  • 代碼量少,開發(fā)效率高
  • 早期 Spring 教程普遍使用字段注入
  • 依賴較少時,缺點不明顯

但這是技術(shù)債務(wù),建議在新項目中避免使用。

四、Setter 注入

4.1 基本用法

通過 Setter 方法注入依賴:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * 訂單服務(wù)(Setter 注入)
 */
@Service
public class OrderService {

    private UserService userService;
    private OrderRepository orderRepository;

    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    @Autowired
    public void setOrderRepository(OrderRepository orderRepository) {
        this.orderRepository = orderRepository;
    }

    public void createOrder(Long userId) {
        userService.getUser(userId);
        orderRepository.save(null);
    }
}

4.2 可選依賴

通過 required = false 標記可選依賴,當容器中找不到對應(yīng) Bean 時不報錯:

@Autowired(required = false)
public void setLogService(LogService logService) {
    this.logService = logService;
}

注意:構(gòu)造器注入不支持 required = false。如果需要可選依賴,必須使用 Setter 注入。

4.3 優(yōu)缺點

優(yōu)點缺點
支持可選依賴(required = false依賴可變(非 final),不保證不變性
可以在對象創(chuàng)建后重新注入(熱替換)對象可能處于未完全初始化狀態(tài)
單元測試方便(可調(diào)用 Setter 傳入 mock)代碼量比字段注入多
解決循環(huán)依賴(與構(gòu)造器注入配合)

五、方法注入

5.1 基本用法

在任意方法上標注 @Autowired,可以一次注入多個依賴:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * 訂單服務(wù)(方法注入)
 */
@Service
public class OrderService {

    private UserService userService;
    private OrderRepository orderRepository;

    @Autowired
    public void configureDependencies(UserService userService, OrderRepository orderRepository) {
        this.userService = userService;
        this.orderRepository = orderRepository;
    }

    public void createOrder(Long userId) {
        userService.getUser(userId);
        orderRepository.save(null);
    }
}

5.2 優(yōu)缺點

優(yōu)點缺點
靈活,可一次注入多個依賴不如構(gòu)造器注入清晰
方法名可自定義,語義更靈活使用場景少
可以在方法中添加初始化邏輯

使用場景:較少使用,通常構(gòu)造器注入或 Setter 注入已足夠。適合需要同時設(shè)置多個相關(guān)依賴并執(zhí)行初始化邏輯的場景。

六、@Autowired 與 @Resource 的區(qū)別

@Autowired@Resource 都用于依賴注入,但來源和行為不同。

6.1 基本對比

特性@Autowired@Resource
來源Spring(org.springframework.beans.factory.annotationJSR-250 標準(jakarta.annotation
默認匹配方式按類型(byType)按名稱(byName)
找不到時的行為required = false 可設(shè)為可選無法設(shè)為可選,找不到直接報錯
配合注解@Qualifier 指定 Bean 名稱name 屬性直接指定
適用位置構(gòu)造器、字段、Setter、方法字段、Setter(不支持構(gòu)造器)
框架依賴僅 Spring標準 JSR-250,其他框架也支持

6.2 @Autowired 匹配流程

@Autowired
private UserService userService;

匹配流程:

1. 按類型查找 UserService 的 Bean

2. 找到唯一 → 注入 ?

3. 找到多個 → 按字段名 "userService" 匹配 Bean 名稱

4. 仍不唯一 → 報錯 NoUniqueBeanDefinitionException

5. 配合 @Qualifier("beanName") → 按指定名稱注入

示例:

// 多個實現(xiàn)類場景
public interface PaymentService {
    void pay(String orderId);
}

@Service("alipayService")
public class AlipayServiceImpl implements PaymentService {
    @Override
    public void pay(String orderId) {
        System.out.println("支付寶支付: " + orderId);
    }
}

@Service("wechatPayService")
public class WechatPayServiceImpl implements PaymentService {
    @Override
    public void pay(String orderId) {
        System.out.println("微信支付: " + orderId);
    }
}

多實現(xiàn)場景的注入方式:

// 方式1:@Autowired + @Qualifier(按名稱指定)
@Autowired
@Qualifier("alipayService")
private PaymentService paymentService;

// 方式2:字段名與 Bean 名稱一致(Spring 自動匹配)
@Autowired
private PaymentService alipayService;  // 字段名 "alipayService" 匹配 Bean 名稱

// 方式3:@Primary 標注默認實現(xiàn)
@Service
@Primary  // 多個實現(xiàn)時,@Autowired 優(yōu)先注入標注了 @Primary 的
public class AlipayServiceImpl implements PaymentService { }

6.3 @Resource 匹配流程

@Resource
private UserService userService;

匹配流程:

1. 如果指定了 name → 只按名稱查找

2. 如果未指定 name → 先按字段名 "userService" 查找

3. 找不到 → 再按類型查找

4. 仍找不到 → 報錯 NoSuchBeanDefinitionException

示例:

// 方式1:默認按字段名匹配
@Resource
private PaymentService alipayService;  // 按名稱 "alipayService" 查找

// 方式2:指定 name
@Resource(name = "wechatPayService")
private PaymentService paymentService;

// 方式3:指定 type
@Resource(type = AlipayServiceImpl.class)
private PaymentService paymentService;

6.4 選型建議

場景推薦
Spring 項目,構(gòu)造器注入@Autowired@Resource 不支持構(gòu)造器)
需要按名稱注入@Autowired + @Qualifier
跨框架兼容@Resource(JSR-250 標準)
簡單項目均可,團隊統(tǒng)一即可

Spring 官方推薦:優(yōu)先使用 @Autowired,構(gòu)造器注入時 @Autowired 可省略。

七、@Autowired 的 required 屬性

7.1 必需依賴 vs 可選依賴

// 必需依賴(默認 required = true)
// 容器中找不到對應(yīng) Bean 會啟動失敗
@Autowired
private UserService userService;

// 可選依賴(required = false)
// 容器中找不到對應(yīng) Bean 不報錯,注入 null
@Autowired(required = false)
private LogService logService;

7.2 各注入方式對 required 的支持

注入方式支持 required = false說明
構(gòu)造器注入不支持構(gòu)造方法參數(shù)始終是必需的
字段注入支持@Autowired(required = false)
Setter 注入支持@Autowired(required = false)
方法注入支持@Autowired(required = false)

7.3 Optional 替代方案

Java 8+ 推薦用 Optional 聲明可選依賴,語義更清晰:

import java.util.Optional;

@Service
public class OrderService {

    private final UserService userService;
    private final Optional<LogService> logService;

    public OrderService(UserService userService, Optional<LogService> logService) {
        this.userService = userService;
        this.logService = logService;
    }

    public void createOrder(Long userId) {
        userService.getUser(userId);
        // 使用前判斷是否存在
        logService.ifPresent(service -> service.log("創(chuàng)建訂單: " + userId));
    }
}

注意Optional 只能用于構(gòu)造器注入和 Setter 注入,不能用于字段注入。

八、循環(huán)依賴問題

8.1 什么是循環(huán)依賴

兩個或多個 Bean 互相依賴,形成閉環(huán):

A 依賴 B,B 依賴 A → 循環(huán)依賴

@Service
class A {
    @Autowired 
    B b;  // A 需要 B
}

@Service
class B {
    @Autowired 
    A a;  // B 需要 A
}

8.2 Spring 如何解決循環(huán)依賴

Spring 通過三級緩存機制解決 Setter/字段注入的循環(huán)依賴:

緩存級別名稱存儲內(nèi)容
一級緩存singletonObjects完全初始化好的 Bean(成品)
二級緩存earlySingletonObjects提前暴露的半成品 Bean(已實例化,未注入依賴)
三級緩存singletonFactoriesBean 工廠(ObjectFactory,用于生成半成品)

解決循環(huán)依賴的流程(A 依賴 B,B 依賴 A):

1. 創(chuàng)建 A:實例化 A → 將 A 的工廠放入三級緩存
2. 注入 A 的依賴 B → 創(chuàng)建 B
3. 創(chuàng)建 B:實例化 B → 將 B 的工廠放入三級緩存
4. 注入 B 的依賴 A → 查找 A
   → 一級緩存沒有
   → 二級緩存沒有
   → 三級緩存有 A 的工廠 → 生成 A 的半成品 → 放入二級緩存 → 刪除三級緩存
5. B 拿到 A 的半成品 → B 注入完成 → B 放入一級緩存
6. A 拿到 B → A 注入完成 → A 放入一級緩存

8.3 構(gòu)造器注入的循環(huán)依賴

構(gòu)造器注入的循環(huán)依賴無法解決!

  • A 的構(gòu)造方法需要 B → 創(chuàng)建 B
  • B 的構(gòu)造方法需要 A → 創(chuàng)建 A
  • A 的構(gòu)造方法需要 B → ... 死循環(huán)

Spring 會拋出:BeanCurrentlyInCreationException

8.4 各注入方式的循環(huán)依賴支持

注入方式是否支持循環(huán)依賴說明
構(gòu)造器注入? 不支持無法實例化任何一方,直接報錯
字段注入? 支持通過三級緩存解決
Setter 注入? 支持通過三級緩存解決
方法注入? 支持通過三級緩存解決

關(guān)鍵理解:構(gòu)造器注入不支持循環(huán)依賴,這其實是優(yōu)點而非缺點。循環(huán)依賴通常意味著設(shè)計有問題,構(gòu)造器注入強制你面對并解決它。

8.5 循環(huán)依賴的解決方案

方案說明適用場景
重構(gòu)消除循環(huán)提取公共邏輯到第三個類首選方案
改為 Setter 注入將其中一方改為 Setter 注入臨時方案,不推薦長期保留
使用 @Lazy延遲注入代理對象臨時方案
事件機制解耦用事件代替直接調(diào)用適合通知類場景

@Lazy 方案:

@Service
public class OrderService {

    private final UserService userService;

    // @Lazy 注入代理對象,首次使用時才真正初始化
    public OrderService(@Lazy UserService userService) {
        this.userService = userService;
    }
}

九、Lookup 方法注入

9.1 問題場景

單例 Bean 注入原型 Bean 時,原型 Bean 只在注入時創(chuàng)建一次,之后不再創(chuàng)建新的:

@Service
public class OrderService {

    // 希望每次使用都是新的實例,但實際只創(chuàng)建了一次
    @Autowired
    private PrototypeBean prototypeBean;  // 始終是同一個對象 ?
}

@Scope("prototype")
@Component
public class PrototypeBean {
    // 原型作用域,應(yīng)該每次獲取都創(chuàng)建新實例
}

9.2 解決方案:@Lookup

import org.springframework.beans.factory.annotation.Lookup;
import org.springframework.stereotype.Service;

/**
 * 訂單服務(wù)
 */
@Service
public abstract class OrderService {

    /**
     * Lookup 方法注入:每次調(diào)用都返回新的 PrototypeBean 實例
     * Spring 通過 CGLIB 動態(tài)子類化重寫此方法
     */
    @Lookup
    protected abstract PrototypeBean getPrototypeBean();

    public void processOrder(String orderId) {
        // 每次調(diào)用 getPrototypeBean() 都返回新實例
        PrototypeBean bean = getPrototypeBean();
        bean.doSomething(orderId);
    }
}

注意@Lookup 標注的方法必須是抽象方法(或返回值與 Bean 類型匹配的具體方法),Spring 通過 CGLIB 生成子類重寫該方法。因此類不能是 final,方法也不能是 final/private

9.3 替代方案:ObjectFactory

如果不想用抽象類,可以用 ObjectFactory

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.stereotype.Service;

@Service
public class OrderService {

    private final ObjectProvider<PrototypeBean> prototypeBeanProvider;

    public OrderService(ObjectProvider<PrototypeBean> prototypeBeanProvider) {
        this.prototypeBeanProvider = prototypeBeanProvider;
    }

    public void processOrder(String orderId) {
        // 每次調(diào)用 getObject() 都返回新實例
        PrototypeBean bean = prototypeBeanProvider.getObject();
        bean.doSomething(orderId);
    }
}

優(yōu)勢ObjectProvider 是構(gòu)造器注入友好的方案,不需要抽象類,推薦使用。

十、泛型注入

Spring 支持泛型類型的依賴注入,通過類型參數(shù)自動匹配對應(yīng)實現(xiàn)。

10.1 泛型基類

/**
 * 通用 Repository 基類
 * @param <T> 實體類型
 */
public abstract class BaseRepository<T> {
    public abstract T findById(Long id);
    public abstract void save(T entity);
}

10.2 具體實現(xiàn)

import org.springframework.stereotype.Repository;

@Repository
public class UserRepository extends BaseRepository<User> {
    @Override
    public User findById(Long id) {
        return new User(id, "張三");
    }

    @Override
    public void save(User entity) {
        System.out.println("保存用戶: " + entity);
    }
}

@Repository
public class OrderRepository extends BaseRepository<Order> {
    @Override
    public Order findById(Long id) {
        return new Order(id, 100.0);
    }

    @Override
    public void save(Order entity) {
        System.out.println("保存訂單: " + entity);
    }
}

10.3 泛型 Service 注入

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * 通用 Service 基類
 * Spring 根據(jù)泛型參數(shù)自動匹配對應(yīng)的 Repository
 */
public abstract class BaseService<T> {

    protected BaseRepository<T> repository;

    @Autowired
    public void setRepository(BaseRepository<T> repository) {
        this.repository = repository;
    }

    public T getById(Long id) {
        return repository.findById(id);
    }

    public void save(T entity) {
        repository.save(entity);
    }
}
@Service
public class UserService extends BaseService<User> {
    // Spring 自動注入 UserRepository(因為 BaseRepository<User> 匹配 UserRepository)
}

@Service
public class OrderService extends BaseService<Order> {
    // Spring 自動注入 OrderRepository(因為 BaseRepository<Order> 匹配 OrderRepository)
}

使用:

@Autowired
private UserService userService;

userService.getById(1L);     // 調(diào)用 UserRepository.findById()
userService.save(new User()); // 調(diào)用 UserRepository.save()

優(yōu)勢:泛型注入實現(xiàn)了代碼復(fù)用,避免了為每個實體重復(fù)編寫 CRUD 方法。

十一、Bean 生命周期回調(diào)與注入

11.1 注入時機

Bean 生命周期中的注入時機:

1. 實例化(Instantiation)       → 構(gòu)造方法調(diào)用
2. 屬性填充(Population)        → 依賴注入發(fā)生在此階段
3. 初始化(Initialization)
   ├── @PostConstruct           → 注入完成后執(zhí)行
   ├── InitializingBean.afterPropertiesSet()
   └── @Bean(initMethod=...)    
4. 使用(In Use)
5. 銷毀(Destruction)
   ├── @PreDestroy
   ├── DisposableBean.destroy()
   └── @Bean(destroyMethod=...)

11.2 @PostConstruct 中使用注入的依賴

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import org.springframework.stereotype.Service;

/**
 * 訂單服務(wù)
 */
@Service
public class OrderService {

    private final UserService userService;

    public OrderService(UserService userService) {
        this.userService = userService;
        // ?? 此時依賴已注入,但如果通過字段注入則可能還未注入
        System.out.println("1. 構(gòu)造方法");
    }

    @PostConstruct
    public void init() {
        // ? 所有依賴已注入完畢,可以安全使用
        System.out.println("2. @PostConstruct: 依賴已就緒");
        userService.warmUpCache();
    }

    @PreDestroy
    public void cleanup() {
        System.out.println("3. @PreDestroy: 清理資源");
    }
}

關(guān)鍵@PostConstruct 在所有依賴注入完成后執(zhí)行,是執(zhí)行初始化邏輯的安全位置。不要在構(gòu)造方法中調(diào)用被注入的依賴的方法(字段注入場景下依賴可能還未注入)。

十二、四種注入方式橫向?qū)Ρ?/h2>

12.1 全面對比

維度構(gòu)造器注入字段注入Setter 注入方法注入
不可變性? final???
完全初始化保證????
單元測試便利性? 可直接 new? 需反射? 可 Setter? 可調(diào)用方法
可選依賴????
循環(huán)依賴? 不支持? 支持? 支持? 支持
代碼簡潔度???(Lombok)????????
依賴可見性? 構(gòu)造方法明確? 隱藏? Setter 明確? 方法明確
Spring 官方推薦? 強烈推薦? 不推薦? 可選依賴時使用? 特殊場景
違反單一職責(zé)感知? 參數(shù)過多有警示? 加字段太容易? 中等? 中等

12.2 適用場景

場景推薦方式理由
必需依賴構(gòu)造器注入保證不可變和完全初始化
可選依賴Setter 注入required = false
簡單工具類字段注入(可接受)代碼簡潔,但僅限非核心邏輯
需要多個相關(guān)依賴同時設(shè)置方法注入可在方法中添加初始化邏輯
單例注入原型ObjectProvider / @Lookup每次獲取新實例
通用泛型基類泛型注入代碼復(fù)用
配置類 @Bean 方法方法參數(shù)注入Spring 自動從容器中查找

12.3 @Bean 方法中的注入

@Configuration 類中,@Bean 方法的參數(shù)會自動從容器中注入:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

    /**
     * 參數(shù) userService 自動從容器中注入
     * 無需標注 @Autowired(Spring 4.3+ 自動注入)
     */
    @Bean
    public OrderService orderService(UserService userService, OrderRepository orderRepository) {
        return new OrderService(userService, orderRepository);
    }
}

十三、最佳實踐

13.1 項目中的注入規(guī)范

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 * 訂單服務(wù) - 推薦寫法示范
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class OrderService {

    // ? 必需依賴:final + 構(gòu)造器注入(Lombok 生成構(gòu)造方法)
    private final UserService userService;
    private final OrderRepository orderRepository;

    // ? 可選依賴:ObjectProvider(構(gòu)造器友好)
    private final ObjectProvider<LogService> logServiceProvider;

    /**
     * 創(chuàng)建訂單
     */
    public void createOrder(Long userId, OrderCreateRequest request) {
        // 使用必需依賴
        User user = userService.getById(userId);

        // 使用可選依賴
        logServiceProvider.ifAvailable(service -> 
            service.log("創(chuàng)建訂單: userId=" + userId)
        );

        // 業(yè)務(wù)邏輯
        Order order = new Order();
        order.setUserId(userId);
        order.setAmount(request.getAmount());
        orderRepository.save(order);
    }
}

13.2 最佳實踐清單

編號實踐說明
1構(gòu)造器注入優(yōu)先所有必需依賴用構(gòu)造器注入,配合 @RequiredArgsConstructor
2字段加 final保證依賴不可變,線程安全
3避免字段注入除非是遺留代碼或快速原型
4可選依賴用 ObjectProvider@Autowired(required = false) 語義更清晰
5多實現(xiàn)用 @Qualifier明確指定 Bean 名稱,避免歧義
6接口編程注入接口類型,而非實現(xiàn)類
7避免循環(huán)依賴構(gòu)造器注入天然避免,如遇循環(huán)則重構(gòu)
8不在構(gòu)造方法中調(diào)用依賴方法字段注入場景下依賴可能未就緒
9初始化邏輯放 @PostConstruct此時所有依賴已注入
10團隊統(tǒng)一風(fēng)格全部用構(gòu)造器注入 + Lombok,保持一致性

13.3 單元測試對比

構(gòu)造器注入的測試便利性:

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import static org.mockito.Mockito.*;

/**
 * 構(gòu)造器注入 → 單元測試極簡
 */
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {

    @Mock
    private UserService userService;

    @Mock
    private OrderRepository orderRepository;

    @Test
    void testCreateOrder() {
        // 直接 new 傳入 mock,無需 Spring 容器
        OrderService orderService = new OrderService(userService, orderRepository);

        when(userService.getById(1L)).thenReturn(new User(1L, "張三"));

        orderService.createOrder(1L, new OrderCreateRequest());

        verify(orderRepository, times(1)).save(any());
    }
}

字段注入的測試困難:

/**
 * 字段注入 → 單元測試困難
 * 需要反射或 Spring TestContext 才能注入 mock
 */
class OrderServiceFieldInjectionTest {

    @Test
    void testCreateOrder() throws Exception {
        OrderService service = new OrderService();

        // 需要通過反射注入私有字段(非常 hacky)
        Field field = OrderService.class.getDeclaredField("userService");
        field.setAccessible(true);
        field.set(service, mock(UserService.class));

        // ... 測試邏輯
    }
}

十四、總結(jié)

14.1 核心要點速查

  1. 構(gòu)造器注入是官方推薦:不可變、完全初始化、測試友好
  2. 字段注入不推薦:隱藏依賴、不可變、測試困難
  3. Setter 注入用于可選依賴required = falseObjectProvider
  4. @Autowired 按類型匹配,配合 @Qualifier 按名稱指定
  5. @Resource 按名稱匹配,JSR-250 標準,不支持構(gòu)造器
  6. 多實現(xiàn)場景@Primary 指定默認 + @Qualifier 精確指定
  7. 構(gòu)造器注入不支持循環(huán)依賴:這是優(yōu)點,強制解決設(shè)計問題
  8. Spring 三級緩存解決 Setter/字段注入的循環(huán)依賴
  9. 單例注入原型用 ObjectProvider@Lookup
  10. 泛型注入實現(xiàn)代碼復(fù)用BaseRepository<T> 自動匹配實現(xiàn)
  11. @PostConstruct 是初始化邏輯的安全位置
  12. Lombok @RequiredArgsConstructor 簡化構(gòu)造器注入

14.2 決策樹

依賴是否必需?
  ├── 必需 → 構(gòu)造器注入(final + @RequiredArgsConstructor)
  └── 可選 → ObjectProvider(構(gòu)造器友好)
            或 Setter 注入(@Autowired(required = false))
是否有多個實現(xiàn)?
  ├── 是 → @Primary 指定默認 + @Qualifier 精確指定
  └── 否 → 直接注入
是否是單例注入原型?
  ├── 是 → ObjectProvider 或 @Lookup
  └── 否 → 正常注入
是否遇到循環(huán)依賴?
  ├── 是 → 重構(gòu)消除循環(huán)(首選)
  │        或 @Lazy 延遲注入(臨時)
  └── 否 → 正常注入

以上就是SpringBoot中依賴注入的常用方式對比詳解的詳細內(nèi)容,更多關(guān)于SpringBoot依賴注入的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java后端SSM框架圖片上傳功能實現(xiàn)方法解析

    Java后端SSM框架圖片上傳功能實現(xiàn)方法解析

    這篇文章主要介紹了Java后端SSM框架圖片上傳功能實現(xiàn)方法解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-06-06
  • Spring中@DependsOn注解的使用代碼實例

    Spring中@DependsOn注解的使用代碼實例

    這篇文章主要介紹了Spring中@DependsOn注解的使用代碼實例,Spring中@DependsOn,主要是使用在類和方法上, 作用是當前對象要依賴另外一些對象,被依賴的對象會先注冊到Spring的IOC容器中,需要的朋友可以參考下
    2024-01-01
  • Spring?多數(shù)據(jù)源方法級別注解實現(xiàn)過程

    Spring?多數(shù)據(jù)源方法級別注解實現(xiàn)過程

    多數(shù)據(jù)源管理是Spring框架中非常重要的一部分,它可以提高應(yīng)用程序的靈活性和可靠性,從而更好地滿足業(yè)務(wù)需求,這篇文章主要介紹了Spring?多數(shù)據(jù)源方法級別注解實現(xiàn),需要的朋友可以參考下
    2023-07-07
  • SpringBoot集成ECDH密鑰交換的方法

    SpringBoot集成ECDH密鑰交換的方法

    ECDH密鑰交換算法通過橢圓曲線和Diffie-Hellman方法生成共享密鑰,用于前端和后端之間的AES加密通信,前端使用elliptic.js生成密鑰對,后端使用crypto-js.min.js進行AES加密,本文給大家介紹SpringBoot集成ECDH密鑰交換的相關(guān)知識,感興趣的朋友一起看看吧
    2025-01-01
  • EditPlus運行java時從鍵盤輸入數(shù)據(jù)的操作方法

    EditPlus運行java時從鍵盤輸入數(shù)據(jù)的操作方法

    這篇文章主要介紹了EditPlus運行java時從鍵盤輸入數(shù)據(jù)的操作方法,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-03-03
  • Java語言實現(xiàn)簡單FTP軟件 FTP遠程文件管理模塊實現(xiàn)(10)

    Java語言實現(xiàn)簡單FTP軟件 FTP遠程文件管理模塊實現(xiàn)(10)

    這篇文章主要為大家詳細介紹了Java語言實現(xiàn)簡單FTP軟件,F(xiàn)TP遠程文件管理模塊的實現(xiàn)方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-04-04
  • 多數(shù)據(jù)源@DS和@Transactional實戰(zhàn)

    多數(shù)據(jù)源@DS和@Transactional實戰(zhàn)

    這篇文章主要介紹了多數(shù)據(jù)源@DS和@Transactional實戰(zhàn),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • SpringBoot Maven打包插件spring-boot-maven-plugin無法解析原因

    SpringBoot Maven打包插件spring-boot-maven-plugin無法解析原因

    spring-boot-maven-plugin是spring boot提供的maven打包插件,本文主要介紹了SpringBoot Maven打包插件spring-boot-maven-plugin無法解析原因,具有一定的參考價值,感興趣的可以了解一下
    2024-03-03
  • 對handlerexecutionchain類的深入理解

    對handlerexecutionchain類的深入理解

    下面小編就為大家?guī)硪黄獙andlerexecutionchain類的深入理解。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-07-07
  • java.io.File的renameTo方法移動文件失敗的解決方案

    java.io.File的renameTo方法移動文件失敗的解決方案

    這篇文章主要介紹了java.io.File的renameTo方法移動文件失敗的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07

最新評論

土默特右旗| 伊春市| 广河县| 宜丰县| 高青县| 晴隆县| 南皮县| 甘孜县| 准格尔旗| 慈利县| 库伦旗| 新和县| 信丰县| 搜索| 开阳县| 象山县| 灌云县| 巴彦淖尔市| 芜湖县| 金平| 鞍山市| 扶风县| 鹤岗市| 定结县| 金秀| 平凉市| 河曲县| 阳西县| 雷山县| 翁牛特旗| 灌阳县| 易门县| 青岛市| 息烽县| 水富县| 桃源县| 和田市| 平乡县| 东丽区| 温州市| 保亭|