深度剖析:为什么 Spring 和 IDEA 都不推荐使用 @Autowired 注解

目录

  1. 依赖注入简介

  2. @Autowired 注解的优缺点

  3. Spring 和 IDEA 不推荐使用 @Autowired 的原因

    • 构造器注入的优势

    • @Autowired 注解的局限性

    • 可读性和可测试性的问题

  4. 推荐的替代方案

    • 构造器注入

    • Setter 注入

    • Java Config @Bean 注解

  5. 项目示例:@Autowired vs 构造器注入

    • 示例代码

    • 可读性和可测试性的对比

  6. 总结

1. 依赖注入简介

依赖注入是一种设计模式,用于实现对象之间的解耦。在传统的编程方式中,一个类通常会直接创建它所依赖的对象,这导致了高度的耦合性和难以进行单元测试。而依赖注入则是通过外部化组件的创建和管理来实现对象间的松耦合。

Spring 框架使用了依赖注入这一设计模式,使得对象的创建和管理更加灵活。例如,下面是一个简单的依赖关系示例:


public class Service {private Repository repository;// 传统的依赖方式public Service() {this.repository = new Repository();}}

在上述代码中,Service 类直接创建了一个 Repository 对象,导致两者之间存在紧密的耦合。而使用依赖注入后,可以改写为:


public class Service {private Repository repository;// 依赖注入public Service(Repository repository) {this.repository = repository;}}

这样,Service 类的依赖通过构造器注入的方式被外部传入,从而实现了解耦。

2. @Autowired 注解的优缺点

在 Spring 框架中,@Autowired 注解用于自动注释框架中所需的依赖。许多开发者在使用 Spring 时,会利用 @Autowired 注解,将其直接应用于类的字段、构造器或 Setter 方法中,以实现自动注入。


@Componentpublic class Service {@Autowiredprivate Repository repository;}

优点

  1. 易于使用:通过简单地添加 @Autowired 注解,即可实现依赖注入,减少了手工编写代码的复杂性。

  2. 减少样板代码:@Autowired 注解减少了代码中显示注入所需要的样板代码。

  3. 自动扫描和装配:Spring 会自动扫描应用程序上下文中的所有 Bean,并进行自动装配,提供了快速开发的便利性。

缺点

  1. 难以测试:字段注入不便于进行单元测试,特别是在没有 DI 容器(如 Spring Context)的情况下。

  2. 隐式依赖:@Autowired 注解的使用有时会造成依赖关系的不明显,使代码的读者难以理解对象的依赖结构。

  3. 不推荐的实践:Spring 官方以及主流开发工具(例如 IntelliJ IDEA)已经不推荐直接使用字段注入。

3. Spring 和 IDEA 不推荐使用 @Autowired 的原因

构造器注入的优势

  1. 强制依赖变量初始化:通过构造函数注入,类在实例化时必须注入所有依赖,确保了依赖变量在类实例化时就能够被正确初始化。

  2. 不变性:构造函数注入提倡依赖变量的不变性(final),这样可以确保引用一旦被注入,便不会被更改,从而提高了代码的安全性和可维护性。

  3. 简化测试:通过构造函数注入,可以方便地进行单元测试而无需启动整个 Spring 容器,只需传递模拟对象(Mock objects)即可。

@Autowired 注解的局限性

  1. 反射开销:使用 @Autowired 注解时,Spring 容器在运行时需要使用反射机制来注入依赖,这带来了额外的性能开销。

  2. 隐式依赖关系:当使用字段注入时,依赖关系变得隐式,这会导致代码的可读性下降,需要更多文档和注释解释类的依赖关系。

  3. 代码可维护性差:当类的依赖越来越多时,使用 @Autowired 注解会使代码变得复杂,增加了维护的难度。

可读性和可测试性的问题

  1. 代码可读性差:@Autowired 注解使外部依赖的绑定显得不够直观,开发者在阅读代码时需要额外的心智负担来追踪依赖关系。

  2. 增加测试复杂性:使用字段注入会使单元测试变得困难,因为需要在测试中启动 Spring 容器或使用反射来初始化依赖对象。

  3. 脆弱性:如果依赖对象未正确注入,@Autowired 注解的字段会处于不确定状态,这可能会在运行时导致 NullPointerException。

4. 推荐的替代方案

虽然 @Autowired 注解提供了快速实现依赖注入的方式,但从长期的维护性和可测试性考虑,有更为推荐的替代方案:

构造器注入

构造器注入是一种显式的依赖注入方式,可以确保依赖对象在类实例化时被正确注入。这是一种更安全、可测试性更高的注入方式。


@Componentpublic class Service {private final Repository repository;@Autowiredpublic Service(Repository repository) {this.repository = repository;}}

Setter 注入

Setter 注入是一种通过 Setter 方法设置依赖的方式。虽然使用频率相对较低,但在某些需要可选依赖的情况下,Setter 注入是一种灵活的选择。


@Componentpublic class Service {private Repository repository;@Autowiredpublic void setRepository(Repository repository) {this.repository = repository;}}

Java Config @Bean 注解

使用 Java 配置类和 @Bean 注解,可以显式地定义和注入 Bean,从而实现更灵活的依赖注入。


@Configurationpublic class AppConfig {@Beanpublic Repository repository() {return new Repository();}@Beanpublic Service service() {return new Service(repository());}}

5. 项目示例:@Autowired vs 构造器注入

通过一个简单的项目示例,我们可以更直观地理解 @Autowired 和构造器注入之间的区别。

示例代码

使用 @Autowired 注解

@Componentpublic class Service {@Autowiredprivate Repository repository;public void performService() {repository.doSomething();}}@Componentpublic class Repository {public void doSomething() {// 执行数据库操作}}
使用构造器注入

@Componentpublic class Service {private final Repository repository;@Autowiredpublic Service(Repository repository) {this.repository = repository;}public void performService() {repository.doSomething();}}@Componentpublic class Repository {public void doSomething() {// 执行数据库操作}}

可读性和可测试性的对比

可读性

使用构造器注入时,依赖关系显而易见,读者一眼就可以看到该类需要的所有依赖。这使得代码的理解和管理变得更加容易,而不需要额外的注释来解释注入的依赖。

可测试性

使用构造器注入时,可以轻松地进行单元测试,无需启动整个 Spring 容器,只需传递模拟对象即可:


public class ServiceTest {@Testpublic void testPerformService() {Repository mockRepository = mock(Repository.class);Service service = new Service(mockRepository);// 测试业务逻辑service.performService();// 验证方法调用verify(mockRepository).doSomething();}}

而使用 @Autowired 注解时,进行单元测试则变得更为复杂,需要额外的代码启动 Spring 容器或使用反射机制:


@RunWith(SpringRunner.class)@SpringBootTestpublic class ServiceTest {@Autowiredprivate Service service;@MockBeanprivate Repository repository;@Testpublic void testPerformService() {// 测试业务逻辑service.performService();// 验证方法调用verify(repository).doSomething();}}

6. 总结

通过本文的详细分析,我们可以看出为什么 Spring 和 IntelliJ IDEA 都不推荐使用 @Autowired 注解。主要原因包括:

  1. 构造器注入的优势明显,不仅可以强制依赖变量初始化,还能提高代码的安全性和可维护性。

  2. @Autowired 注解的使用会导致代码的隐式依赖关系,降低代码的可读性和可维护性。

  3. 使用构造器注入或者其它显式的依赖注入方式,可以使代码更易于测试,避免了启动整个 Spring 容器的麻烦。

为了提高代码质量,增强代码的可读性和可测试性,开发者应尽量避免使用 @Autowired 注解,而优先选择构造器注入、Setter 注入或者 Java Config @Bean 注解等显式的依赖注入方式。通过这些替代方案,不仅可以提升代码的整体质量,还能在不同的开发阶段(如维护和测试)中,减少不必要的复杂性和潜在问题。希望本文的分析和建议能为广大开发者提供有价值的参考。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/18173.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android 项目Gradle文件讲解(Groovy和Kotlin)

Android 项目Gradle文件讲解(Groovy和Kotlin) 前言正文一、Gradle的作用二、Gradle的种类① 工程build.gradle② 项目build.gradle③ settings.gradle④ gradle.properties⑤ gradle-wrapper.properties⑥ local.properties 三、Groovy和Kotlin的语言对比…

装饰模式:鸡腿堡

文章目录 UML类图目录结构Humburger.javaChickenBurger.javaCondiment.javaChuilli.javaLettuce.javaTest.java深度理解test怎么写 UML类图 目录结构 我们从指向最多的开始写 Humburger.java package zsms;public abstract class Humburger {protected String name;public S…

【接口自动化_05课_Pytest接口自动化简单封装与Logging应用】

一、关键字驱动--设计框架的常用的思路 封装的作用:在编程中,封装一个方法(函数)主要有以下几个作用:1. **代码重用**:通过封装重复使用的代码到一个方法中,你可以在多个地方调用这个方法而不是…

C++贪心算法(3)

整数区间 #include<bits/stdc.h> using namespace std; int main() {int n;cin>>n;int a[110][10]{0};for(int i0;i<n;i){cin>>a[i][1]>>a[i][2];}int cnt[110]{0};int mi99999;int mii-1;bool f[110]{false,false,false,false,false,false,false,…

debian让dotnet 程序以守护进程方式运行,如果意外退出主动开启

创建服务文件: 打开一个新的.service文件在/etc/systemd/system/目录下&#xff0c;例如myapp.service sudo nano /etc/systemd/system/myapp.service编辑服务文件: 添加以下内容到myapp.service文件&#xff0c;确保修改ExecStart以指向你的.NET Core应用程序的可执行文件&am…

JVM-调优之-高内存占用问题排查

排查思路 1&#xff09;检查jvm内存的分配情况 2&#xff09;检查jvm的gc情况 3&#xff09; 找出占用量比较大的对象 第一步&#xff1a;jmap -heap PID 查看jvm内存使用情况 jmap -heap 2525 可以看到老年代年轻代等其他内存区域内存使用率百分比 第二步&#xff1a;jsta…

Golang:使用roylee0704/gron实现定时任务

可以使用roylee0704/gron实现定时任务 文档 https://github.com/roylee0704/gronhttps://pkg.go.dev/github.com/roylee0704/gron 安装 go get github.com/roylee0704/gron代码示例 package mainimport ("fmt""time""github.com/roylee0704/gron…

如何用分立器件设计一个过流保护电路

过流保护电路是指当后级负载电流过大或者发生短路时&#xff0c;自动切断电源与后级负载&#xff0c;从而防止大电流损害电源电路&#xff0c;过流保护又叫OCP。 常见的过流保护电路有保险丝过流保护&#xff0c;集成的过流保护芯片&#xff0c;还有这种分立器件搭建的过流保护…

Browserify:将 Node.js 模块转换为浏览器可用的 JavaScript

什么是 Browserify&#xff1f; Browserify 是一个 JavaScript 工具&#xff0c;用于将 Node.js 模块转换为在浏览器环境中可用的单个 JavaScript 文件。通过 Browserify&#xff0c;您可以使用 require() 函数引入其他模块&#xff0c;就像在 Node.js 中一样&#xff0c;而不…

详解 Scala 的隐式转换

当编译器第一次编译失败的时候&#xff0c;会在当前的环境中查找能让代码编译通过的方法&#xff0c;用于将某个类型进行转换&#xff0c;实现二次编译通过 一、隐式函数 隐式函数可以在不修改任何代码的情况下&#xff0c;扩展某个类的功能 /**声明语法&#xff1a;implicit d…

Ubuntu上安装Maven

在Ubuntu上安装Maven的步骤如下&#xff1a; 更新包索引&#xff1a; sudo apt update 安装Maven&#xff1a; sudo apt install maven 验证安装是否成功&#xff1a; mvn -version 以上步骤将会安装Maven并添加到系统路径中&#xff0c;你可以通过运行mvn -version来验…

TIM输出比较

一、OC&#xff08;Output Compare&#xff09;输出比较 1、输出比较可以通过比较CNT&#xff08;计数器&#xff09;与CCR&#xff08;捕获/比较寄存器&#xff09;寄存器值的关系&#xff0c;来对输出电平进行置1、置0或翻转的操作&#xff0c;用于输出一定频率和占空比的PW…

微信小程序连接阿里云快速入门【物联网】

一、前言 1.1 项目背景 随着5G的逐渐普及&#xff0c;万物互联的浪潮已经席卷而来。在万物互联的场景下&#xff0c;如何实现设备之间的互联互通&#xff0c;成为了一个亟待解决的问题。 微信小程序作为一款轻量级的小程序开发框架&#xff0c;以其简洁的语法和丰富的组件库…

08、SpringBoot 源码分析 - 自动配置深度分析一

SpringBoot 源码分析 - 自动配置深度分析一 refresh和自动配置大致流程如何自动配置SpringBootApplication注解EnableAutoConfiguration注解AutoConfigurationImportSelector自动配置导入选择器DeferredImportSelectorHandler的handleDeferredImportSelectorGroupingHandler的r…

【学习笔记】C++每日一记[20240520]

简述几种内存泄漏的预防机制 用智能指针代替普通指针&#xff0c;由于智能指针自带引用计数功能&#xff0c;能够记录动态分配空间的引用数量&#xff0c;在引用计数为零时&#xff0c;自动调用析构函数释放空间。 借助一些内存泄漏检测工具&#xff0c;例如Valgrind、Memche…

DNS 解析过程

文章目录 简介特点查询方式⚡️1. 浏览器缓存2. 系统缓存&#xff08;hosts文件&#xff09;3. 路由器缓存4. 本地域名服务器5. 根域名服务器6. 顶级域名服务器7. 权限域名服务器8. 本地域名服务器缓存并返回9. 操作系统缓存并返回10. 浏览器缓存并访问流程图 总结 简介 DNS&a…

Enable Full Line suggestions 启用全行建议

开启后效果如下&#xff1a; 直接提示可能要输入的参数

基于 Solana 的 Drift Protocol 快速崛起,重新定义去中心化衍生品市场

随着区块链技术的快速发展&#xff0c;加密市场的格局正在悄然改变。投资者对透明度、效率和去中心化的需求愈发强烈&#xff0c;之前完全由中心化交易主导的加密货币交易&#xff0c;开始向链上转移。 根据 Coingecko 的最新数据&#xff0c;CEX 和 DEX 的现货交易量已经达到了…

WWW24因果论文(3/8) |通过因果干预实现图分布外泛化

【摘要】由于图神经网络 (GNN) 通常会随着分布变化而出现性能下降&#xff0c;因此分布外 (OOD) 泛化在图学习中引起了越来越多的关注。挑战在于&#xff0c;图上的分布变化涉及节点之间错综复杂的互连&#xff0c;并且数据中通常不存在环境标签。在本文中&#xff0c;我们采用…

系统开发与运行知识

系统开发与运行知识 导航 文章目录 系统开发与运行知识导航一、软件工程二、软件生命周期三、开发模型四、开发方法五、需求分析结构化分析 六、数据流图分层数据流图的画法设计注意事项 七、数据字典数据字典的内容 八、系统设计九、结构化设计常用工具十、面向对象十一、UML…