为什么推荐使用构造函数注入而非@Autowired注解进行字段注入

news2024/12/27 5:20:03

在 Spring 框架中,推荐使用构造函数注入而非@Autowired注解进行字段注入,主要有以下几个原因:

1. 依赖不可变和空指针安全

  • 构造函数注入:使用构造函数注入时,依赖在对象创建时就必须提供,一旦对象创建完成,其依赖关系就固定下来,不会再改变。这有助于确保对象的状态在整个生命周期中都是一致和可预测的。而且,由于依赖是在构造函数中注入的,在使用对象之前,依赖已经被初始化,不会出现空指针异常。
    • 举例:假设有一个UserService类,它依赖于UserRepository。使用构造函数注入的方式如下:
public class UserService {
    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    // 业务方法
    public User findUserById(Long id) {
        return userRepository.findById(id);
    }
}

在这个例子中,UserService在创建时就必须传入UserRepository实例,之后在调用findUserById方法时,userRepository肯定已经被初始化,不会出现空指针问题。

  • 字段注入:使用@Autowired注解进行字段注入时,依赖是在对象创建之后通过反射机制注入的。这就意味着在对象创建和依赖注入之间存在一个短暂的时间窗口,在这个窗口内,依赖可能为空。如果在依赖注入完成之前就访问依赖,就会导致空指针异常。
    • 举例:同样是UserService类,使用字段注入的方式如下:
public class UserService {
    @Autowired
    private UserRepository userRepository;

    // 业务方法
    public User findUserById(Long id) {
        return userRepository.findById(id);
    }
}

在这个例子中,如果UserService在构造函数或者其他方法中过早地访问userRepository,而此时依赖注入还未完成,就可能会抛出空指针异常。

2. 便于单元测试

  • 构造函数注入:构造函数注入使得单元测试更加简单和直观。在进行单元测试时,只需要创建一个模拟的依赖对象,并通过构造函数传递给被测试对象即可。这使得测试代码与生产代码之间的依赖关系更加清晰,也更容易控制测试环境。
    • 举例:对于上述使用构造函数注入的UserService,单元测试可以这样写:
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

public class UserServiceTest {
    @Test
    public void testFindUserById() {
        // 创建模拟的UserRepository
        UserRepository mockRepository = mock(UserRepository.class);
        when(mockRepository.findById(1L)).thenReturn(new User());

        // 通过构造函数创建UserService实例
        UserService userService = new UserService(mockRepository);

        // 调用方法进行测试
        User user = userService.findUserById(1L);
        assertNotNull(user);
    }
}
  • 字段注入:使用@Autowired注解进行字段注入时,在单元测试中需要使用反射或者其他复杂的技术来模拟依赖注入。这使得测试代码变得更加复杂,也增加了测试的难度和维护成本。
    • 举例:对于使用字段注入的UserService,单元测试可能需要这样写:
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;

@SpringBootTest
@ContextConfiguration
public class UserServiceTest {
    @Autowired
    private ApplicationContext applicationContext;

    @Test
    public void testFindUserById() {
        // 创建模拟的UserRepository
        UserRepository mockRepository = mock(UserRepository.class);
        when(mockRepository.findById(1L)).thenReturn(new User());

        // 通过反射设置UserService的userRepository字段
        UserService userService = applicationContext.getBean(UserService.class);
        try {
            java.lang.reflect.Field field = UserService.class.getDeclaredField("userRepository");
            field.setAccessible(true);
            field.set(userService, mockRepository);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }

        // 调用方法进行测试
        User user = userService.findUserById(1L);
        assertNotNull(user);
    }
}

可以看到,字段注入的测试代码更加复杂,需要使用反射来设置依赖,增加了测试的难度和维护成本。

3. 更好的代码可读性和可维护性

  • 构造函数注入:构造函数注入将对象的依赖关系明确地展示在构造函数的参数列表中。这使得代码的阅读者能够一眼看出该对象依赖于哪些其他对象,以及这些依赖是如何被初始化的。这种清晰的依赖关系有助于提高代码的可读性和可维护性。
    • 举例:对于前面的UserService类,通过构造函数注入,我们可以清楚地看到UserService依赖于UserRepository,并且依赖是在构造函数中进行初始化的。
  • 字段注入:使用@Autowired注解进行字段注入时,依赖关系被隐藏在字段声明中,没有在构造函数或者其他明显的位置展示。这使得代码的阅读者需要花费更多的时间和精力来理解对象的依赖关系和初始化过程。
    • 举例:在使用字段注入的UserService类中,依赖关系通过@Autowired注解隐藏在字段声明中,不直观。

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

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

相关文章

01- 三自由度串联机械臂位置分析

三自由度串联机械臂如下图所示(d180mm,L1100mm,L280mm),利用改进DH法建模,坐标系如下所示: 利用改进DH法建模,该机器人的DH参数表如下所示: 对该机械臂进行位置分析&…

lxml 解析xml\html

from lxml import etree# XML文档示例 xml_doc """ <root><book><title>Python编程指南</title><author>张三</author></book><book><title>Python高级编程</title><author>李四</autho…

用Python写炸金花游戏

文章目录 **代码分解与讲解**1. **扑克牌的生成与洗牌**2. **给玩家发牌**3. **打印玩家的手牌**4. **定义牌的优先级**5. **判断牌型**6. **确定牌型优先级**7. **比较两手牌的大小**8. **计算每个玩家的牌型并找出赢家**9. **打印结果** 完整代码 以下游戏规则&#xff1a; 那…

基于 SpringBoot微信小程序的医院预约挂号系统

摘 要 时代在飞速进步&#xff0c;每个行业都在努力发展现在先进技术&#xff0c;通过这些先进的技术来提高自己的水平和优势&#xff0c;医院预约挂号系统当然不能排除在外。医院预约挂号系统是在实际应用和软件工程的开发原理之上&#xff0c;运用微信开发者、java语言以及…

高仿CSDN编辑器,前端博客模板

高仿CSDN编辑器纯前端模板&#xff0c;使用的js、html、vue、axios等技术&#xff0c;网络请求库已进行封装&#xff0c;可以按需调整界面,需要源码联系(4k左右)。 1.支持代码高亮 2.支持目录点击定位 3.支持文件上传、图片上传&#xff08;需要自己写后端接口&#xff09; 4.M…

【C++11】类型分类、引用折叠、完美转发

目录 一、类型分类 二、引用折叠 三、完美转发 一、类型分类 C11以后&#xff0c;进一步对类型进行了划分&#xff0c;右值被划分纯右值(pure value&#xff0c;简称prvalue)和将亡值 (expiring value&#xff0c;简称xvalue)。 纯右值是指那些字面值常量或求值结果相当于…

在线oj项目 Ubuntu安装vue/cil(vue脚手架)

参考:https://blog.csdn.net/weixin_66062303/article/details/129046198 笔记 参考:https://blog.csdn.net/m0_74352571/article/details/144076227 https://cli.vuejs.org/zh/guide/installation.html 确保nodejs已经安装 npm换源淘宝镜像&#xff08;可以不操作或者使用魔…

Python字符串及正则表达式(十一):正则表达式、使用re模块实现正则表达式操作

前言&#xff1a;在 Python 编程的广阔天地中&#xff0c;字符串处理无疑是一项基础而关键的技能。正则表达式&#xff0c;作为处理字符串的强大工具&#xff0c;以其灵活的模式匹配能力&#xff0c;在文本搜索、数据清洗、格式验证等领域发挥着不可替代的作用。本系列博客已经…

项目37:简易个人健身记录器 --- 《跟着小王学Python·新手》

项目37&#xff1a;简易个人健身记录器 — 《跟着小王学Python新手》 《跟着小王学Python》 是一套精心设计的Python学习教程&#xff0c;适合各个层次的学习者。本教程从基础语法入手&#xff0c;逐步深入到高级应用&#xff0c;以实例驱动的方式&#xff0c;帮助学习者逐步掌…

华为:数字化转型只有“起点”,没有“终点”

上个月&#xff0c;我收到了一位朋友的私信&#xff0c;他询问我是否有关于华为数字化转型的资料。幸运的是&#xff0c;我手头正好收藏了一些&#xff0c;于是我便分享给他。 然后在昨天&#xff0c;他又再次联系我&#xff0c;并感慨&#xff1a;“如果当初我在进行企业数字…

count(1)、count(_)与count(列名)的区别?

大家好&#xff0c;我是锋哥。今天分享关于【count(1)、count(_)与count(列名)的区别&#xff1f;】面试题。希望对大家有帮助&#xff1b; count(1)、count(_)与count(列名)的区别&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 在 SQL 中&#xff0c…

AAAI-2024 | 大语言模型赋能导航决策!NavGPT:基于大模型显式推理的视觉语言导航

作者&#xff1a;Gengze Zhou, Yicong Hong, Qi Wu 单位&#xff1a;阿德莱德大学&#xff0c;澳大利亚国立大学 论文链接&#xff1a; NavGPT: Explicit Reasoning in Vision-and-Language Navigation with Large Language Models &#xff08;https://ojs.aaai.org/index.p…

Linux高级--2.4.1 网络概念(分层、TCP)

关于网络分层理解的难点 对于一般人&#xff08;不参与设计和维护网络协议栈的人&#xff09;来讲&#xff0c;物理层和应用层很容易理解&#xff0c;也很好记住。首先&#xff0c;物理层是看的到的网线、基站的实体。再者&#xff0c;应用层是用户自己参与编写的程序。 而那…

使用VSCode Debugger 调试 React项目

一般我们调试代码时&#xff0c;用的最多的应该就是console.log方式了&#xff0c;还有的是使用Chrome DevTools 通过在对应的 sourcemap代码位置打断点进行调试&#xff0c;除了上面两种方式外还有一种更好用的调试方式&#xff1a; VSCode Debugger。 VSCode Debugger可以直…

Redis-十大数据类型

Reids数据类型指的是value的类型&#xff0c;key都是字符串 redis-server:启动redis服务 redis-cli:进入redis交互式终端 常用的key的操作 redis的命令和参数不区分大小写 &#xff0c;key和value区分 查看当前库所有的key keys * 判断某个key是否存在 exists key 查看key是什…

Git--tag标签远程管理

目录 一、git 标签 tag管理 1.创建一个轻量级标签 2.创建一个带有附注的标签 3.删除标签 二、标签推送 1.再创建两个分支 2.把多个标签推送到远程 三、标签拉取 四、删除远程标签 1.命令 2.查看远程仓库&#xff0c;标签被删除 3.远程标签删除后本地标签不会消失&a…

通过nginx设置一个图片服务器,并使用 Nginx 作为反向代理

通过nginx设置一个图片服务器&#xff0c;并使用 Nginx 作为反向代理 安装nginx 首先需要去官网下载一个nginx&#xff0c;我这里下载了最新的稳定版本&#xff1a;nginx-1.26.2&#xff0c;下载下来是一个压缩包&#xff0c;解压之后就可以直接用了。 修改nginx的配置文件 …

第十六届“蓝桥杯”全国软件和信息技术专业人才大赛简介及资料大全

蓝桥杯全国软件和信息技术专业人才大赛是由工业和信息化部人才交流中心主办的一项全国性竞赛&#xff0c;面向全国高校大学生&#xff0c;累计参赛院校超过1200余所&#xff0c;参赛人数达40万人&#xff0c;是我国极有影响力的高校IT类赛事。 “第十六届蓝桥杯全国软件和信息…

快速理解24种设计模式

简单工厂模式 建立产品接口类&#xff0c;规定好要实现方法。 建立工厂类&#xff0c;根据传入的参数&#xff0c;实例化所需的类&#xff0c;实例化的类必须实现指定的产品类接口 创建型 单例模式Singleton 保证一个类只有一个实例&#xff0c;并提供一个访问他它的全局…

【山西长治】《长治市市直部门政务信息化建设项目预算编制规范和预算编制标准》(长财行[2022]25号)-省市费用标准解读系列32

《长治市市直部门政务信息化建设项目预算编制规范和预算编制标准(试行)》&#xff08;长财行[2022]25号&#xff09;于2022年8月1日开始试行&#xff0c;此标准由长治市财政局、长治市行政审批管理局编制&#xff0c;是对信息化建设项目预算管理的基本要求&#xff0c;主要适用…