跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava

Spring 依赖注入(DI)详解:原理、实现与最佳实践

综述由AI生成Spring 依赖注入(DI)是控制反转(IoC)的一种实现方式,用于降低代码耦合度。文章介绍了构造注入、设值注入和属性注入三种方式及其优缺点。重点讲解了@Autowired、@Qualifier、@Resource 和@Value 等核心注解的使用场景。此外还涵盖了条件注入、循环依赖处理及 Bean 作用域控制等高级特性,并提供了基于构造注入的最佳实践建议,帮助开发者构建可测试、易维护的 Spring 应用。

利刃发布于 2026/3/16更新于 2026/5/2626 浏览
Spring 依赖注入(DI)详解:原理、实现与最佳实践

Spring 依赖注入(DI)详解:原理、实现与最佳实践

引言

在现代软件开发中,松耦合设计是构建高质量系统的关键。Spring 框架通过依赖注入(Dependency Injection, DI)实现了这一目标。DI 不仅简化了代码的编写,还提高了系统的可测试性和可维护性。

一、什么是依赖注入(DI)?

依赖注入是一种设计模式,其核心思想是将对象的依赖关系由外部注入,而不是在类内部自行创建或查找。通过这种方式,代码的耦合度得以降低,系统更加灵活和易于维护。

DI 与 IoC 的关系

依赖注入是控制反转(IoC)的一种实现方式。IoC 的核心思想是'让框架控制程序的执行流程',而 DI 则是实现 IoC 的具体手段之一。

二、Spring DI 的实现方式

在 Spring 框架中,依赖注入主要通过以下三种方式实现:

1. 构造注入(Constructor Injection)

通过构造方法注入依赖。这种方式在类初始化时完成注入,适合处理不可变依赖。

@Controller
public class HelloController {
    private final Student student;

    @Autowired
    public HelloController(Student student) {
        this.student = student;
    }
}

补充代码进行测试:

@Data
public class Student {
    public String name;
    public int age;
}

优点

强制依赖满足:构造方法在对象创建时必须满足所有依赖,确保了对象在初始化时是完整的。 不可变性:通过 final 关键字,可以确保依赖在对象生命周期内不会改变。 提高测试性:在单元测试中,可以轻松地为构造方法注入不同的依赖实现。

缺点

配置复杂性:在需要注入多个依赖时,构造方法的参数列表可能变得冗长。

2. 设值注入(Setter Injection)

通过 setter 方法注入依赖。这种方式适合处理可变依赖,但在 Spring 推荐使用构造注入。

@Controller
public class HelloController {
    private Student student;

    @Autowired
    public void setStudent(Student student) {
        this.student = student;
    }
}

优点

可变依赖:适用于在运行时动态改变依赖的情况。 延迟初始化:依赖可以在对象创建后注入,适用于可选依赖。

缺点

初始化问题:对象可能在依赖注入前被使用,导致空指针异常。 难以控制顺序:多个依赖的注入顺序难以控制,可能导致不一致的状态。

3. 属性注入(Field Injection)

属性注入是直接在类的字段上使用@Autowired 注解,将依赖注入到字段中。

@Service
public class UserService {
    @Autowired
    private Student s3;

    public void print() {
        System.out.println("do Service");
        System.out.println(s3);
    }
}

判断属性注入是否成功,需补充测试代码:

@Data
public class Student {
    public String name;
    public int age;
}

@Component
public class StudentComponent {
    @Bean
    public Student s1() {
        Student student = new Student();
        student.setAge(18);
        student.setName("张三");
        return student;
    }

    @Primary
    @Bean
    public Student s2() {
        Student student = new Student();
        student.setAge(20);
        student.setName("李四");
        return student;
    }
}

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(DemoApplication.class, args);
        UserService userService = context.getBean(UserService.class);
        userService.print();
    }
}

优点

简单直观,代码简洁。 适用于不需要显式控制注入时机的场景。

缺点

破坏封装性:直接暴露了类的字段,违反了面向对象的封装原则。 延迟初始化问题:字段可能在类初始化时未被注入,导致空指针异常。 难以测试:由于字段直接依赖外部注入,单元测试时可能需要额外的配置。

三、Spring DI 的核心注解

在 Spring 中,依赖注入主要通过以下注解实现:

1. @Autowired

@Autowired是最常用的注解,用于自动注入依赖。

@Service
public class UserService {
    @Autowired
    private Student s3;

    public void print() {
        System.out.println("do Service");
        System.out.println(s3);
    }
}
2. @Qualifier

当存在多个相同类型的 Bean 时,可以通过@Qualifier指定具体的 Bean。

@Component
public class StudentComponent {
    @Bean(name = "s3")
    public Student s1() {
        Student student = new Student();
        student.setAge(18);
        student.setName("张三");
        return student;
    }

    @Primary
    @Bean(name = "s4")
    public Student s2() {
        Student student = new Student();
        student.setAge(20);
        student.setName("李四");
        return student;
    }
}

Spring 默认情况下会根据类型来注入 Bean,但如果存在多个相同类型的 Bean,Spring 会不知道该注入哪一个。此时可以使用@Primary注解来指定一个默认的 Bean,或使用@Qualifier明确指定。

@Service
public class UserService {
    @Autowired
    @Qualifier("s3")
    private Student s3;

    public void print() {
        System.out.println("do Service");
        System.out.println(s3);
    }
}
3. @Resource

@Resource是 JDK 提供的注解,也可用于依赖注入。

@Service
public class UserService {
    @Resource(name = "s3")
    private Student s3;

    public void print() {
        System.out.println("do Service");
        System.out.println(s3);
    }
}
4. @Value

@Value用于注入配置文件中的属性值。

@Service
public class UserService {
    @Value("${my.key}")
    private String myKey;

    public void print() {
        System.out.println("配置文件属性值" + myKey);
    }
}

四、Spring DI 的高级特性

1. 条件注入

通过@Conditional系列注解,可以根据条件动态注入 Bean。

@Configuration
public class AppConfig {
    @Bean
    @ConditionalOnProperty(name = "env", havingValue = "dev")
    public UserDAO devUserDAO() {
        return new DevUserDAO();
    }

    @Bean
    @ConditionalOnProperty(name = "env", havingValue = "prod")
    public UserDAO prodUserDAO() {
        return new ProdUserDAO();
    }
}
2. 循环依赖

在 Spring 中,循环依赖通常通过构造注入解决。通过@Autowired注解的required属性,可以控制依赖是否为必选。

@Service
public class ServiceA {
    @Autowired(required = false)
    private ServiceB serviceB;

    public void doSomething() {
        if (serviceB != null) {
            serviceB.doSomething();
        }
    }
}
3. 注入范围

通过@Scope注解,可以控制 Bean 的作用域(如singleton、prototype等)。

@Service
@Scope("prototype")
public class UserService {
    public void saveUser(User user) {
        System.out.println("Saving user in prototype scope");
    }
}

五、三种注入方式的比较

特性构造注入设值注入属性注入
依赖完整性强制满足可选/延迟可选/延迟
不可变性支持 (final)不支持不支持
测试难度低中高
封装性好好差

六、最佳实践

  1. 优先使用构造注入:构造注入在处理不可变依赖时更加安全和直观。
  2. 避免过度注入:只注入真正需要的依赖,避免'饥饿注入'。
  3. 合理使用@Qualifier:在存在多个相同类型 Bean 时,通过@Qualifier明确指定。
  4. 结合配置文件:通过@Value注解将配置文件中的属性注入到 Bean 中。
  5. 测试驱动开发:通过依赖注入,更容易编写单元测试。

七、总结

通过本文,你了解了 Spring 依赖注入(DI)的核心原理,掌握了如何用它实现代码的优雅解耦。Spring DI 帮助你实现模块化设计、提升可测试性和增强扩展性。用 Spring DI,让你的代码更干净、更易维护,也让开发过程更具乐趣。

目录

  1. Spring 依赖注入(DI)详解:原理、实现与最佳实践
  2. 引言
  3. 一、什么是依赖注入(DI)?
  4. DI 与 IoC 的关系
  5. 二、Spring DI 的实现方式
  6. 1. 构造注入(Constructor Injection)
  7. 2. 设值注入(Setter Injection)
  8. 3. 属性注入(Field Injection)
  9. 三、Spring DI 的核心注解
  10. 1. @Autowired
  11. 2. @Qualifier
  12. 3. @Resource
  13. 4. @Value
  14. 四、Spring DI 的高级特性
  15. 1. 条件注入
  16. 2. 循环依赖
  17. 3. 注入范围
  18. 五、三种注入方式的比较
  19. 六、最佳实践
  20. 七、总结
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Mamba2:基于 SSD 框架的 SSM 新架构,速度提升 8 倍
  • SpringAI 与 Deepseek 大模型应用开发实战笔记(上)
  • 前端 PWA:构建离线可用与可安装 Web 应用
  • 大模型高效微调:LoRA 技术原理与实战经验总结
  • 行星减速器原理、计算公式与 C++ 实战
  • Git 下载及安装教程
  • 大模型时代程序员如何提升效率:AIGC 辅助编程实践与展望
  • MixAIHub:主流 AI 模型聚合访问平台
  • GitHub Copilot Pro 学生身份认证与配置指南
  • LigerUI入门帮助(API里没写的入门帮助)【不断更新】
  • AI 时代的技术民主化与个人创作实践
  • 鸿蒙金融理财全栈项目:生态合作与用户运营优化
  • WebLogic 未授权 RCE(CVE-2020-14882 & CVE-2020-14883)复现
  • VS Code 禁用 Copilot 智能补全(算法练习场景)
  • AI 如何重构智能家居:从指令执行到主动理解
  • 基于 OpenAI Python SDK 的 API 调用示例脚本
  • DeepSeek 使用指南:提示词技巧与知识库搭建
  • 基于 brpc+MinIO 的分布式文件存储架构设计与实战
  • Java String 核心机制与常用 API 实战
  • 电科金仓发布融合数据库 KES V9,探索 AI 时代新形态

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online