当前位置: 首页 > news >正文

Spring Boot2.x教程:(十)从Field injection is not recommended谈谈依赖注入

从Field injection is not recommended谈谈依赖注入

  • 1、问题引入
  • 2、依赖注入的三种方式
    • 2.1、字段注入(Field Injection)
    • 2.2、构造器注入(Constructor Injection)
    • 2.3、setter注入(Setter Injection)
  • 3、为什么不推荐字段注入
    • 3.1、违反单一职责原则
    • 3.2、无法创建不可变对象
    • 3.3、可测试性差
  • 4、推荐使用构造器注入的原因
    • 4.2、明确的依赖关系
    • 4.2、不可变性保证
    • 4.3、单元测试友好
    • 4.4、容器无关性
  • 5、最佳实践示例
  • 6、Lombok的@RequiredArgsConstructor
  • 7、总结

大家好,我是欧阳方超,可以扫描下方二维码关注我的公众号“欧阳方超”,后续内容将在公众号首发。
在这里插入图片描述

在这里插入图片描述

1、问题引入

在使用Spring框架时,我们经常会看到IDE给出这样的警告:

Field injection is not recommended

这通常出现在我们使用@Autowired注解直接注入字段时:

@Service
public class UserService {@Autowiredprivate UserRepository userRepository;  // Field injection
}

2、依赖注入的三种方式

在Spring中,依赖注入(DI)主要有三种方式,我们先来了解一下。

2.1、字段注入(Field Injection)

字段注入是一种依赖注入的方式,通过直接将依赖项(如服务或组件)注入到类的字段中。通常,这种方式通过使用注解(如 @Autowired)来实现。例如:

@Service
public class UserService {@Autowiredprivate UserRepository userRepository;
}

2.2、构造器注入(Constructor Injection)

通过构造器传递依赖项是最推荐的方式。这种方式可以确保所有必需的依赖项在对象创建时就被提供,并且可以轻松进行单元测试。

@Service
public class UserService {private final UserRepository userRepository;@Autowired  // 在Spring 4.3+,当类只有一个构造器时,@Autowired可以省略public UserService(UserRepository userRepository) {this.userRepository = userRepository;}
}

2.3、setter注入(Setter Injection)

Setter 注入是一种依赖注入的方式,通过公开的 setter 方法将依赖项注入到对象中。这种方式允许在对象创建后动态地设置依赖项,提供了比字段注入更好的灵活性和可测试性。Setter 注入通常与 Spring 框架一起使用,依赖项通过 @Autowired 注解标记的 setter 方法进行注入。

@Service
public class UserService {private UserRepository userRepository;@Autowiredpublic void setUserRepository(UserRepository userRepository) {this.userRepository = userRepository;}
}

3、为什么不推荐字段注入

3.1、违反单一职责原则

字段注入(Field Injection)在依赖注入的实现中常常被认为违反了单一职责原则(Single Responsibility Principle, SRP)。单一职责原则是面向对象设计中的一个重要原则,旨在确保一个类应该只有一个原因去改变,即一个类应该仅承担一个责任。
使用字段注入时,类的职责往往会变得模糊。具体来说,类不仅需要处理其核心业务逻辑,还需要负责管理其依赖关系的生命周期和注入。这种责任的混淆使得类变得更加复杂,难以理解和维护。

public class UserService {@Autowiredprivate UserRepository userRepository; // 依赖关系的管理public void createUser(User user) {// 业务逻辑}
}

在这个例子中,UserService 类不仅负责用户相关的业务逻辑,还需要处理 UserRepository 的依赖关系。这使得 UserService 的职责超出了其核心功能。

3.2、无法创建不可变对象

使用字段注入时:

@Service
public class UserService {@Autowiredprivate UserRepository userRepository;
}

这种方式下,userRepository无法声明为final,意味着字段可能在运行时被修改、无法保证线程安全、对象状态可能发生变化。

3.3、可测试性差

在单元测试中,我们通常希望能够控制被测试对象的所有依赖项,以便验证其行为。使用字段注入时,无法通过构造函数或方法直接传递模拟对象,这会导致下面的问题。
无法直接提供模拟对象:
由于依赖项是通过字段自动注入的,无法在创建被测试对象时直接提供一个模拟(mock)对象。这意味着必须依赖 Spring 的上下文来创建对象,而这通常会引入不必要的复杂性。
需要 Spring 上下文:
由于依赖项是通过 Spring 容器管理的,需要启动 Spring 上下文才能进行测试。这会增加测试的开销,并可能导致测试运行速度变慢。
难以隔离测试:
字段注入使得类与 Spring 框架紧密耦合,这使得单元测试难以隔离被测类和其依赖项,从而降低了测试的独立性。

4、推荐使用构造器注入的原因

4.2、明确的依赖关系

@Service
public class UserService {private final UserRepository userRepository;private final EmailService emailService;public UserService(UserRepository userRepository, EmailService emailService) {this.userRepository = userRepository;this.emailService = emailService;}
}

通过构造器参数,清晰地表明类的依赖,使得代码更易于维护和理解。

4.2、不可变性保证

依赖可以声明为final,确保运行时不会被修改,保证了线程安全。

4.3、单元测试友好

@Test
public void testUserService() {// 方便进行mock测试UserRepository mockRepo = mock(UserRepository.class);EmailService mockEmail = mock(EmailService.class);UserService service = new UserService(mockRepo, mockEmail);// 进行测试...
}

4.4、容器无关性

类可以在Spring容器之外实例化,提高了代码的可复用性。

5、最佳实践示例

@Service
public class UserService {private final UserRepository userRepository;private final EmailService emailService;private final SecurityService securityService;public UserService(UserRepository userRepository,EmailService emailService,SecurityService securityService) {this.userRepository = Objects.requireNonNull(userRepository, "UserRepository must not be null");this.emailService = Objects.requireNonNull(emailService, "EmailService must not be null");this.securityService = Objects.requireNonNull(securityService, "SecurityService must not be null");}public User createUser(UserDTO userDTO) {// 使用注入的依赖User user = userRepository.save(userDTO.toUser());emailService.sendWelcomeEmail(user);securityService.grantDefaultPermissions(user);return user;}
}

6、Lombok的@RequiredArgsConstructor

如果依赖较多,构造器代码会比较冗长。可以使用Lombok的@RequiredArgsConstructor注解简化:

@Service
@RequiredArgsConstructor
public class UserService {private final UserRepository userRepository;private final EmailService emailService;private final SecurityService securityService;// Lombok会自动生成包含所有final字段的构造器
}

7、总结

尽管字段注入在某些情况下看起来更简单,但它带来了许多潜在的问题,特别是在可测试性和可维护性方面。因此,我们建议使用构造函数或方法参数进行依赖注入,以提高代码质量和可读性。通过采取这些最佳实践,将能够编写出更加健壮、易于维护和高效的 Java 应用程序。
我是欧阳方超,把事情做好了自然就有兴趣了,如果你喜欢我的文章,欢迎点赞、转发、评论加关注。我们下次见。

相关文章:

Spring Boot2.x教程:(十)从Field injection is not recommended谈谈依赖注入

从Field injection is not recommended谈谈依赖注入 1、问题引入2、依赖注入的三种方式2.1、字段注入(Field Injection)2.2、构造器注入(Constructor Injection)2.3、setter注入(Setter Injection) 3、为什…...

在 Android Studio 上运行 Java 的 main 函数

直接写了个main函数运行的时候提示 这时我们需要在idea目录下找到gradle.xml文件 添加 <option name"delegatedBuild" value"false" />搞定...

【Nas】X-DOC:Mac mini 安装 ZeroTier 并替换 planet 实现内网穿透

【Nas】X-DOC&#xff1a;Mac mini 安装 ZeroTier 并替换 planet 实现内网穿透 1、下载客户端 ZeroTier One2、安装过程3、更换planet备份原planet4、重启服务5、加入网络6、NAT内网穿透 1、下载客户端 ZeroTier One https://www.zerotier.com/download/ 选择 MacOS 适用版本&…...

Spring Boot 集成 RabbitMQ

在现代分布式系统中&#xff0c;消息队列扮演着至关重要的角色。它能够实现系统间的异步通信、解耦组件以及提高系统的可扩展性和可靠性。RabbitMQ 作为一款广泛使用的开源消息中间件&#xff0c;具有强大的功能和灵活的配置。而 Spring Boot 则是一种流行的 Java 开发框架&…...

存在sql注入的公网站点

此数据为博主在新手阶段练习sql注入时发现的站点&#xff0c;漏洞可能修复&#xff0c;备注可能错误 url: https://www.uni-1.com.hk/about_en.php?id2 注入点类型&#xff1a;数值 sql报错回显&#xff1a;无 sql语句执行&#xff1a;[order by] [union] 字段数&#xff1a;1…...

linux之网络子系统- 内核发送数据包流程以及相关实际问题

一、相关实际问题 查看内核发送数据消耗的CPU时应该看sy还是si在服务器上查看/proc/softirqs&#xff0c;为什么NET_RX要比NET_TX大得多发送网络数据的时候都涉及那些内存拷贝操作零拷贝到底是怎么回事为什么Kafka的网络性能很突出 二、网络包发送过程总览 调用系统调用send发…...

UDP 实现的 Echo Server 和 Echo Client 回显程序

欢迎浏览高耳机的博客 希望我们彼此都有更好的收获 感谢三连支持! 在网络编程中&#xff0c;Echo Server 和 Echo Client 回显是一种经典的示例&#xff0c;用于演示基本的网络通信。Echo Server 接收来自客户端的数据&#xff0c;并将相同的数据发送回客户端。这种模式在测试…...

AUTOSAR CP MCAL微控制器抽象层介绍

AUTOSAR&#xff08;Automotive Open System Architecture&#xff09;即汽车开放系统架构&#xff0c;它将汽车电子控制单元&#xff08;ECU&#xff09;的软件底层做了一个标准的封装&#xff0c;使得开发者能够共用一套底层软件&#xff0c;并通过修改参数来匹配不同的硬件和…...

SpringBoot应用部署到Docker中MySQL8时间戳相差8小时问题及处理方式

文章目录 SpringBoot应用部署到Docker中MySQL8时间戳相差8小时问题及处理方式1. 检查MySQL服务器的时间区设置2. 在Spring Boot应用程序中设置时间区3. Docker容器中通过Dockerfile设置时区4. 在运行Docker容器时通过命令行传递环境变量5. 启动SpringBoot应用时设置JVM参数来指…...

飞桨首创 FlashMask :加速大模型灵活注意力掩码计算,长序列训练的利器

在 Transformer 类大模型训练任务中&#xff0c;注意力掩码&#xff08;Attention Mask&#xff09;一方面带来了大量的冗余计算&#xff0c;另一方面因其 O ( N 2 ) O(N^2) O(N2)巨大的存储占用导致难以实现长序列场景的高效训练&#xff08;其中 N N N为序列长度&#xff09;…...

【含文档+源码】基于SpringBoot+Vue的新型吃住玩一体化旅游管理系统的设计与实现

开题报告 本文旨在探讨新型吃住玩一体化旅游管理系统的设计与实现。该系统融合了用户注册与登录、旅游景点管理、旅游攻略发帖、特色旅游路线推荐、附近美食推荐以及酒店客房推荐与预定等多项功能&#xff0c;旨在为游客提供全方位、一体化的旅游服务体验。在系统设计中&#…...

【网络安全】揭示 Web 缓存污染与欺骗漏洞

未经许可,不得转载。 文章目录 前言污染与欺骗Web 缓存污染 DoS1、HTTP 头部超大 (HHO)2、HTTP 元字符 (HMC)3、HTTP 方法覆盖攻击 (HMO)4、未键入端口5、重定向 DoS6、未键入头部7、Host 头部大小写规范化8、路径规范化9、无效头部 CP-DoS10、HTTP 请求拆分Web 缓存污染与有害…...

PHP如何防止防止源代码的暴露

在PHP开发中&#xff0c;防止源代码暴露是确保应用程序安全性的重要一环。源代码暴露可能会让攻击者发现敏感信息&#xff0c;如数据库凭据、业务逻辑漏洞等&#xff0c;从而进行恶意攻击。以下是一些防止PHP源代码暴露的方法&#xff1a; 禁用PHP短标签&#xff1a; 在php.in…...

C++智能指针的实现

本篇文章详细探讨下如何使用裸指针实现智能指针。 补充内容 由于本篇文章主要是探讨怎么实现三种智能指针,但是在编码过程中,博主可能会使用些有些同学不了解的特性,为了保证大家思绪不被打断,博主先把这些小特性介绍出来,大家选择性参考。 1、什么是RAII? RAII(Reso…...

硅谷(12)菜单管理

菜单管理模块 11.1 模块初始界面 11.1.1 API&&type API: import request from /utils/request import type { PermisstionResponseData, MenuParams } from ./type //枚举地址 enum API {//获取全部菜单与按钮的标识数据ALLPERMISSTION_URL /admin/acl/permission…...

定子调压调速系统

定子调压调速系统是一种用于控制三相交流绕线电机的调速系统&#xff0c;它通过改变电动机定子电压和转子电阻来实现对电机转速的控制。以下是关于定子调压调速系统的详细介绍&#xff1a; 工作原理 定子电压调控&#xff1a;在1&#xff5e;3档时&#xff0c;系统通过控制定子…...

从APP小游戏到Web漏洞的发现

一、前因&#xff1a; 在对一次公司的一个麻将游戏APP进行渗透测试的时候发现&#xff0c;抓到HTTP请求的接口&#xff0c;但是反编译APK后发现没有在本身发现任何一个关于接口或者域名相关的关键字&#xff0c;对此感到了好奇。 于是直接解压后everything搜索了一下&#xff…...

设计模式07-结构型模式(装饰模式/外观模式/代理模式/Java)

4.4 装饰模式 4.4.1 装饰模式的定义 1.动机&#xff1a;在不改变一个对象本身功能的基础上给对象增加额外的新行为 2.定义&#xff1a;动态地给一个对象增加一些额外的职责&#xff0c;就增加对象功能来说&#xff0c;装饰模式比生成子类实现更为灵活 4.4.2 装饰模式的结构…...

C# 广播技术——发现局域网设备技术——

一广播技术应用 客户端发现与管理&#xff1a;软件可以通过广播消息来发现网络中的客户端&#xff0c;从而方便对客户端进行集中管理和监控。服务通知&#xff1a;向所有客户端广播重要的通知、更新或警告信息&#xff0c;确保客户端及时了解相关情况。资源共享与分配&#xf…...

【QA】windows和linux陷入系统调用后有什么区别?

最近被某面试官的这个问题拷打&#xff0c;当场脸烧起来… 首先讲讲系统调用&#xff1a; 系统调用是操作系统为调用者提供服务的接口&#xff0c;以便程序员聚焦于业务问题。分为文件操作&#xff0c;内存分配&#xff0c;进程管理等等。用户使用系统调用后会触发软中断&…...

OmenSuperHub:惠普游戏本性能优化的终极免费解决方案

OmenSuperHub&#xff1a;惠普游戏本性能优化的终极免费解决方案 【免费下载链接】OmenSuperHub 使用 WMI BIOS控制性能和风扇速度&#xff0c;自动解除DB功耗限制。 项目地址: https://gitcode.com/gh_mirrors/om/OmenSuperHub OmenSuperHub是一款专为惠普OMEN游戏本设…...

如何实现快速排名?冷门制造业网站的3天起步法

小型机械厂、精细化工厂、模具厂拥有小众的工业产品。工业产品在网络上的搜索量极低。一款直径 50 毫米的硬质合金钻头&#xff0c;全球每月搜索量仅有 120 次。高空作业平台零部件的搜索量低至 50 次。极低的搜索热度带来一个现象&#xff1a;大型网络平台不参与这类词汇的竞争…...

视启未来[特殊字符]百度智能云:给大模型一双手,让AI真正触碰物理世界

如果说过去两年&#xff0c;大模型在数字世界里掀起了一场海啸&#xff1b;那么2026年&#xff0c;这场海啸正在以“具身智能”的形态&#xff0c;猛烈地拍击物理世界的海岸线。但这里却有一个“骨感”的现实&#xff1a;AI能写出拿普利策奖的文章&#xff0c;能画出媲美梵高的…...

协议转换网关与数据采集网关的区别与差异

摘要在工业自动化、物联网、智能建筑等领域中&#xff0c;“协议转换”和“数据采集网关”是两个常被提及但容易混淆的概念。它们虽有关联&#xff0c;却扮演着不同的角色。理解其核心差异对于构建高效、可靠的数据通信系统至关重要。1.核心定义&#xff1a;本质差异1.1协议转换…...

拟态设计革命来了,你还在用老版MJ?2024Q2官方未披露的3类新拟态纹理权重算法首度解密

更多请点击&#xff1a; https://kaifayun.com 第一章&#xff1a;拟态设计革命的底层逻辑与时代必然性 拟态设计并非视觉层面的风格迁移&#xff0c;而是一场由安全范式迁移、计算环境异构化与攻击面指数级扩张共同驱动的系统性重构。其底层逻辑根植于“动态异构冗余”&…...

AIDD入门第七课:大语言模型是如何读懂文字,又如何走进药物发现的?

前几篇文章中&#xff0c;我们已经介绍了机器学习、深度学习、神经网络&#xff0c;以及CNN、RNN、Transformer等经典结构。今天这篇文章&#xff0c;进入一个更靠近当下AI浪潮的主题&#xff1a;大语言模型与自然语言处理。自然语言处理&#xff08;Natural Language Processi…...

【RT-DETR实战】061、端到端速度优化:从数据加载到后处理

昨天深夜调模型的时候又遇到性能瓶颈——明明GPU利用率只有60%,帧率死活上不去。 盯着nvidia-smi的输出发呆半小时,突然意识到问题不在前向推理那几百毫秒,而在数据加载和后处理这些“边角料”环节。今天咱们就聊聊RT-DETR端到端流水线里那些容易被忽略的速度陷阱。 数据加…...

集装箱箱号与ISO代码区域检测数据集VOC+YOLO格式887张2类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件)图片数量(jpg文件个数)&#xff1a;887标注数量(xml文件个数)&#xff1a;887标注数量(txt文件个数)&#xff1a;887标注类别数&…...

告别C盘爆满!VSCode插件和用户数据迁移到D盘的保姆级教程(附注册表修改)

告别C盘爆满&#xff01;VSCode插件和用户数据迁移到D盘的保姆级教程 每次打开VSCode都看到C盘空间告急的红色警告&#xff1f;作为开发者&#xff0c;我们往往会在不知不觉中安装几十个甚至上百个插件&#xff0c;这些插件和用户数据默认都存储在C盘&#xff0c;日积月累就会…...

实战解析:如何用Qualcomm AI Engine Direct的OpPackage机制为你的AI模型添加自定义算子

深度实战&#xff1a;利用Qualcomm AI Engine Direct的OpPackage机制实现自定义算子全流程开发 在移动端AI模型部署的实践中&#xff0c;我们常常会遇到一个关键挑战&#xff1a;当模型包含特殊算子或自研算法时&#xff0c;如何在不修改底层框架的前提下实现高效执行&#xff…...