Spring Boot——@Autowired属性注入问题

news2025/2/26 15:06:44

🎈 @Autowired问题

当我们在使用@Autowired属性注入时,会发现idea提示Field injection is not recommended ,译为:不推荐使用属性注入
在这里插入图片描述   要想了解Spring和idea之所以不推荐使用@Autowired属性注入,首先就要先了解Spring常用的注入方式:简单类型注入、集合类型注入, 域属性自动注入, 自动注入的类别, 空值注入, 构造注入。

在 Spring 中,常见的依赖注入方式有以下三种:

  1. Constructor Injection(构造函数注入):通过类的构造函数来注入依赖项。

  2. Setter Injection(setter 方法注入):通过类中的 setter 方法来注入依赖项。

  3. Field Injection(属性注入):通过直接在类的属性上使用注解来注入依赖项。

这三种方式各有优缺点,例如:

  • Constructor Injection 在单元测试中更易于模拟依赖项,可以提高代码的可测试性和可维护性。但是,当类的依赖项较多时,构造函数可能会变得很长,使得类的代码难以阅读和理解。

  • Setter Injection 使得依赖项可以在运行时动态修改,使用起来比较灵活方便。但是,使用 Setter Injection 的类可能会存在不完整的状态,如果必需的依赖项没有被设置,可能会导致运行时错误。

  • Field Injection 可以减少类的代码,使其更加简洁易懂。但是,当依赖项过多时,类的属性列表可能会变得很长,降低代码的可读性和可维护性。此外,由于依赖关系是通过类的属性直接注入的,容易出现一些难以发现的错误。

综上所述,建议在实际应用中优先使用 Constructor Injection 或 Setter Injection 来注入依赖项,以提高代码的可测试性和可维护性。Field Injection 应该谨慎使用,只有当属性较少且必需的依赖项很明显时才使用。

🎈 Spring中的三种依赖注入方式

💧 Field Injection

Field Injection(属性注入)是 Spring 中一种依赖注入方式,它使用 @Autowired@Inject 注解直接在属性上进行注入。例如:

@Service
public class UserServiceImpl implements UserService {
 
    @Autowired
    private UserRepository userRepository;
 
    // ...
}

在该例子中,UserServiceImpl 类中的 userRepository 属性通过 @Autowired 注入了 UserRepository 类型的 bean。

虽然 Field Injection 是一种简单方便的依赖注入方式,但也存在一些问题:

  1. 不利于单元测试:Field Injection 的方式使得测试时修改或替换依赖对象可能更加困难。因为需要通过反射机制才能访问和修改被注入的属性。

  2. 缺乏明确的依赖顺序:在类中使用 Field Injection 时,无法明确知道哪些依赖必须先注入,在什么顺序下注入。这可能导致一些潜在的问题。

  3. 难以发现错误:由于依赖关系是通过属性来注入的,容易出现拼写错误、类型错误等问题。这会导致运行时错误非常难以发现和定位。

因此,建议在应用程序中使用 Constructor Injection 或 Setter Injection 来代替 Field Injection,以达到更好的可测试性、可维护性和代码清晰度。

💧 Constructor Injection

Constructor Injection是构造器注入,它通过类的构造函数来注入依赖。例如:

@Service
public class UserServiceImpl implements UserService {
 
    private final UserRepository userRepository;
 
    @Autowired
    public UserServiceImpl(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
 
    // ...
}

在上面的示例中,UserServiceImpl 类中的依赖 userRepository 通过构造函数注入进来。

相比于 Field Injection,Constructor Injection 有以下优点:

  1. 明确的依赖关系:使用构造函数注入时,我们可以明确知道哪些依赖项是必须的,以及它们的顺序。这有助于消除代码中可能存在的歧义。

  2. 更好的可测试性:由于依赖被注入到类的构造函数中,因此在单元测试中可以更容易地模拟依赖项。

  3. 对不变性的支持:当类的依赖被注入后,它们通常是不变的。这使得类可以更好地支持不变性和线程安全性。

  4. 更好的代码清晰度:使用构造函数注入时,我们可以更清晰地看到类与其他组件之间的关系。这使得代码更易于阅读和理解。

在实践中,建议使用 Constructor Injection 作为首选方法来注入依赖项,以提高代码的可测试性和可维护性。

💧 Setter Injection

Setter Injection也会用到@Autowired注解,但使用方式与Field Injection有所不同,Field Injection是用在成员变量上,而Setter Injection的时候,是用在成员变量的Setter函数上。例如:

@Service
public class UserServiceImpl implements UserService {
 
    private UserRepository userRepository;
 
    @Autowired
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
 
    // ...
}

在上面的示例中,UserServiceImpl 类中的依赖 userRepository 通过 setter 方法注入进来。

相比于 Field Injection,Setter Injection 的主要优势是可以避免直接访问类的属性,而是通过 setter 方法来设置依赖项,从而实现更好的封装性。同时,使用 Setter Injection 的类可以动态地修改依赖项,这使得它们更加灵活。

但是,Setter Injection 也存在一些缺点。例如,使用 Setter Injection 的类可能会存在不完整的状态,如果必需的依赖项没有被设置,可能会导致运行时错误。此外,Setter Injection 使得代码更加分散,使用起来可能比 Constructor Injection 要更加繁琐。

因此,在使用 Setter Injection 时需要注意以下几点:

  1. 避免在类的构造函数中直接使用依赖项,而应该通过 setter 方法来设置依赖项。

  2. 设计 setter 方法时要确保参数非空,以避免在运行时出现 NullPointerException 错误。

  3. 在使用 Setter Injection 的类中,必需的依赖项应该在对象创建后尽早设置好,以避免出现不完整的状态。

总之,Setter Injection 是 Spring 中一种重要的依赖注入方式,使用它可以让代码更加灵活、封装性更好。但是,在使用 Setter Injection 时需要注意避免一些常见的陷阱,以确保代码的可测试性和可维护性。

🎈 三种依赖注入的对比

这三种注入方式各有优劣,下面我将对它们进行一些简单的对比。

  1. Constructor Injection(构造函数注入):

Constructor Injection 是一种通过类的构造函数来注入依赖项的方式。通过构造函数注入时,可以保证必需的依赖项在对象创建后立即可用,并且在创建对象的过程中就可以将依赖项完全实例化。

优点:使用构造函数注入可以非常明确地表达一个类的依赖关系,使得代码依赖性更加明显和易于维护。此外,由于所有必须的依赖项都必须在构造函数中声明,因此这种方式可以提高代码的可测试性。

缺点:当类的依赖项较多时,构造函数可能会变得很长,使得类的代码难以阅读和理解。此外,使用构造函数注入时必须额外定义构造函数,造成代码冗长。

  1. Setter Injection(setter 方法注入):

Setter Injection 是一种通过类中的 setter 方法来注入依赖的方式。这种注入方式比 Constructor Injection 更加灵活,因为 Setter Injection 允许在运行时动态更改依赖项。

优点:使用 Setter Injection 的类可以动态地修改依赖项,从而实现更好的灵活性。此外,Setter Injection 可以避免直接访问类的属性,从而实现更好的封装性。

缺点:使用 Setter Injection 的类可能会存在不完整的状态,如果必需的依赖项没有被设置,可能会导致运行时错误。此外,Setter Injection 使得代码更加分散,使用起来可能比 Constructor Injection 更加繁琐。

  1. Field Injection(属性注入):

Field Injection 是一种通过直接在类的属性上使用注解来注入依赖项的方式。这种注入方式可以使类的代码更加简洁,但也可能引发一些问题。

优点:Field Injection 能够减少类的代码,使其更加简洁易懂。此外,使用 Field Injection 的类可以直接访问依赖项,从而使代码更加紧凑。

缺点:Field Injection 的主要问题是它容易出现一些难以发现的错误。由于依赖关系是通过类的属性直接注入的,使用 Field Injection 可能会导致一些不必要的依赖项。此外,通过属性直接注入依赖项可能会违反单一责任原则。

综合比较:

注入方式可靠性可维护性可测试性灵活性循环关系的检测性能影响
Field Injection不可靠很灵活不检测启动快
Constructor Injection可靠不灵活自动检测启动慢
Setter Injection不可靠很灵活不检测启动快

总之,在使用三种依赖注入方式时,需要结合具体情况进行选择。在大多数情况下,Constructor Injection 和 Setter Injection 是比较常用和推荐的方式。同时,需要注意避免一些常见的陷阱,以确保代码的可测试性和可维护性。

🎈 属性注入解决方法

属性注入的问题:

  1. 基于属性注入的方式,违反单一职责原则​ 因为现在的业务一般都会使用很多依赖, 但拥有太多的依赖通常意味着承担更多的责任,而这显然违背了单一职责原则.并且类和依 赖容器强耦合,不能在容器外使用。
  2. 基于属性注入的方式,容易导致Spring 初始化失败​ 初始化Spring 容器是,由于属性在被注入前就引用而导致npe(空指针),进而导致容器初始化失败。​ java 在初始化一个类的顺序为,静态变量和静态代码块 -> 实例变量或者初始化语句 -> 构造函数 然后才会执行Spring 注解 @Autowired 自动装配依赖,所以在执行这个类的构造方法时,依赖属性还未被注入。
  3. @Autowired 是ByType注入,当存在两个类型相同的对象时就会注入失败

💧 @Resource

@Resource 是 Java EE 提供的一种注入方式,可以实现对其他组件(如 Bean、EJB 等)进行自动注入。在 Spring 中,@Resource 注解可以用来注入依赖项。

@Resource 注解默认按名称进行装配,可以指定其 name 属性显式指定要装配的 Bean 的名称。如果没有指定 name 属性,则会使用属性名作为 Bean 名称进行装配。如果找不到与 name 或属性名相匹配的 Bean,则会抛出 NoSuchBeanDefinitionException 异常。

下面是一个使用 @Resource 注解进行注入的示例:

@Service
public class UserService {
    public void addUser() {
        // ...
    }
}

@RestController
public class UserController {
    @Resource
    private UserService userService;

    @GetMapping("/addUser")
    public String addUser() {
        userService.addUser();
        return "Success";
    }
}

在上述示例中,@Resource 注解会自动注入 UserService 类型的 Bean。

需要注意的是,@Resource 注解和 @Autowired 注解类似,但存在一些细微的区别。例如,@Autowired 注解默认按类型进行装配,而 @Resource 注解默认按名称进行装配。此外,@Autowired 注解可以通过 @Qualifier 注解指定要装配的 Bean 的名称,而 @Resource 注解则直接使用 name 属性指定要装配的 Bean 的名称。因此,在使用 @Resource 注解时需要格外注意其装配方式和使用方法。

总之,在 Spring 中可以使用 @Resource 注解进行依赖注入,它可以根据名称或类型进行装配,提高了代码的灵活性和可维护性。

💧 @RequiredArgsConstructor

在 Spring 中,可以使用 @RequiredArgsConstructor 进行属性注入。这种方式可以帮助我们避免手动编写构造函数或繁琐的 setter 方法,同时还可以增加代码的可读性和可维护性。

在使用 @RequiredArgsConstructor 进行属性注入时,需要满足以下条件:

  1. 类中必须有 final 修饰的属性;
  2. 需要使用 lombok 插件。

下面是一个使用 @RequiredArgsConstructor 进行属性注入的示例:

@Service
@RequiredArgsConstructor
public class UserService {
    private final UserRepository userRepository;

    public User getUserById(Long id) {
        return userRepository.findById(id).orElse(null);
    }
}

在上述示例中,@RequiredArgsConstructor 注解会为 UserService 自动生成一个有参构造函数,并自动注入 final 修饰的 userRepository 属性。

需要注意的是,在使用 @RequiredArgsConstructor 进行属性注入时,我们应该尽可能地将依赖项声明为 final 属性。这样可以确保依赖项一旦被初始化就无法修改,增加代码的安全性和稳定性。

总之,@RequiredArgsConstructor 能够帮助我们简化代码,提高代码的可读性和可维护性。在实际开发中,可以根据具体情况合理使用 @RequiredArgsConstructor 进行属性注入。

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

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

相关文章

基于Redis的分布式限流详解

前言 Redis除了能用作缓存外,还有很多其他用途,比如分布式锁,分布式限流,分布式唯一主键等,本文将和大家分享下基于Redis分布式限流的各种实现方案。 一、为什么需要限流 用最简单的话来说:外部请求是不可…

ArcGIS Pro拓扑

地理数据库拓扑帮助确保数据完整性。拓扑的使用提供了一种对数据执行完整性检查的机制,帮助地理数据库中验证和保持更好的要素表示。 拓扑是点、线和多边形要素共享几何的方式的排列布置。拓扑的用途包括以下几个方面: (1)限制要…

模型服务,支持渲染多张输出图片|ModelWhale 版本更新

清明时节雨纷纷。晚春的雨季中,ModelWhale 迎来了新一轮的版本更新。 本次更新中,ModelWhale 主要进行了以下功能迭代: • 新增 模型服务多图输出渲染(专业版✓ 团队版✓ ) • 优化 门户成果交流展示(团队…

Java异常机制

异常概念 异常是程序在运行期发生的不正常的事件,它会打断指令的正常执行流程。 设计良好的程序应该在异常发生时提供处理这些不正常事件的方法,使程序不会因为异常的发生而阻断或产生不可预见的结果。 Java语言使用异常处理机制为程序提供了异常处理的能…

卡尔曼滤波简介 —— 一维卡尔曼滤波

原文:The alpha - beta - gamma filter (kalmanfilter.net) 一维卡尔曼滤波 在本章中,我们将在一个维度上推导出卡尔曼滤波。本章的主要目标是简单直观地解释卡尔曼滤波的概念,而不使用可能看起来复杂和令人困惑的数学工具。 我们将逐步推进…

oracle connect by 学习

【Connect by 层次查询】 https://www.bilibili.com/video/BV1jV411t7CB/?share_sourcecopy_web&vd_sourced88a617727cccf1c106d623afec0c6b6 简单来说这个connect by 就是为了查父子节点的。 CREATE TABLE test.emp (id varchar(10),name varchar(10),manager_id varch…

Java的位运算

目录 1 Java中支持的位运算 2 位运算规则 3 逻辑运算 3.1 与运算&#xff08;&&#xff09; 3.2 或运算&#xff08;|&#xff09; 3.3 异或运算&#xff08;^&#xff09; 3.3 取反运算&#xff08;~&#xff09; 4 位移操作 4.1 左移&#xff08;<<&#…

Steam-V Rising 私人服务器架设教程

一、安装前的准备 一台服务器 拥有公网IP并且做好了端口映射 二、使用SteamCMD安装服务器 1.下载SteamCMD SteamCMD是Steam专用的命令行式客户端程序&#xff0c;所有的安装方式可以参照&#xff1a;https://developer.valvesoftware.com/wiki/SteamCMD 或者在其他站点自行…

yum、yumdownloader学习

yum命令 https://blog.csdn.net/Netfilter007/article/details/103873293 yum命令是在Fedora和RedHat以及SUSE中基于rpm的软件包管理器。 常用命令 显示&#xff1a;yum list xxx 安装&#xff1a;yum install xxx 升级&#xff1a;yum update xxx 删除&#xff1a;yum remov…

JAVA开发与运维(web生产环境部署)

web生产环境部署&#xff0c;往往是分布式&#xff0c;和开发环境或者测试环境我们一般使用单机不同。 一、部署内容 1、后端服务 2、后台管理系统vue 3、小程序 二、所需要服务器 5台前端服务器 8台后端服务 三、所需要的第三方组件 redismysqlclbOSSCDNWAFRocketMQ…

mac 安装mongodb 无法打开 mongod 无法验证开发者

https://www.mongodb.com/try/download/community 一、下载解压 下载的tgz包&#xff0c;解压到本地 /usr/local 目录下 二、编辑.bash_profile 文件 终端输入命令打开并编辑 .bash_profile 文件 open .bash_profile .bash_profile文件最下面新增一行mongodb相关配置&#x…

leetcode142_环形链表 II

文章目录 题目详情分析Java完整代码 题目详情 给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给…

【5天打卡】学习Lodash的第四天——安全漏洞学习

安全漏洞的问题一直是大家关心的问题&#xff0c;仿佛是巧合&#xff0c;在云视频会议服务提供商 Zoom 刚刚被爆出存在”零日漏洞“威胁 Mac 用户隐私和信息安全的同时&#xff0c;开发者熟知的 npm 库 Lodash 也被爆出存在高严重性安全漏洞——”原型污染“漏洞&#xff0c;该…

全网最新版ChatGLM-6B开源模型环境详细部署及安装——如何在低显存单显卡上面安装私有ChatGPT GPT-4大语言模型

目录 前言前期准备电脑要求安装anaconda安装相应版本的CUDA配置ChatGLM-6B Conda环境安装pytorch ChatGLM-6B最新版模型环境部署及安装源码下载模型下载相关库安装运行web演示作为API部署 参考资料其它资料下载 前言 ChatGPT的爆火让许多公司和个人都想要开发自己的大型语言模…

多模态之clip

论文&#xff1a;Learning Transferable Visual Models From Natural Language Supervision Github&#xff1a;https://github.com/OpenAI/CLIP OpenAI出品 论文通过网络爬取4亿(image, text)对&#xff0c;使用对比学习的方法训练得到clip&#xff08;Contrastive Languag…

golang 输出固定位数的整数

需求&#xff1a;将0输出为000,99输出为099 c# Console.WriteLine(0.ToString("000")); Console.WriteLine(99.ToString("000"));go fmt.Printf("%03d", 0) fmt.Printf("%03d", 99)运行效果&#xff1a;

深入浅出Java中参数传递的原理

前言 今天&#xff0c;想和大家聊聊关于java中的参数传递的原理&#xff0c;参数的传递有两种&#xff0c;值传递和引用传递。 值传递&#xff1a;是指在调用函数时将实际参数复制一份传递到函数中&#xff0c;这样在函数中如果对参数进行修改&#xff0c;将不会影响到实际参…

windows系统 vmware17安装ubuntu server 22.04.2

vmware17 创建虚拟机 新建虚拟机 选择镜像文件&#xff1a;我这个镜像文件是需要联网才能安装的 设置虚拟机名称&#xff0c;设置存储路径 根据用户实际使用情况设置合适的大小&#xff1a;比如我是用来安装wvp平台的&#xff0c;20g太小&#xff0c;导致安装出现问题。 最…

scratch猫猫的儿童节 中国电子学会图形化编程 少儿编程 scratch编程等级考试三级真题和答案解析2023年3月

目录 scratch猫猫的儿童节 一、题目要求 1、准备工作 2、功能实现 二、案例分析

如何使用java编写差分进化算法

差分进化算法属于一种进化算法,以全局最优性、收敛速度快等特点,得到很多学者的关注,并将其扩展到参数优化、数值优化、工程优化、路径优化、机器学习等一系列研究中。 而差分进化算法的原理即过程又是什么呢? 一、什么是差分进化算法 差分进化算法的原理属于内部寻优机…