@Autowired 注解在构造器上的使用规则(字段注入也挺好的)

news2025/3/16 18:36:31

背景

  • 在看Spring Framework官方文档时,看到这样一段描述:

As of Spring Framework 4.3, an @Autowired​ annotation on such a constructor is no longer necessary if the target bean defines only one constructor to begin with. However, if several constructors are available and there is no primary/default constructor, at least one of the constructors must be annotated with @Autowired​ in order to instruct the container which one to use. See the discussion on constructor resolution for details.

  • 为了更好地理解这段话,做了一些实践,写成文章分享给大家。

实践

第一句

“As of Spring Framework 4.3, an @Autowired annotation on such a constructor is no longer necessary if the target bean defines only one constructor to begin with.”

  • 含义:从 Spring Framework 4.3 版本开始,如果一个目标 Bean 只定义了一个构造器,那么在该构造器上标注 @Autowired​ 注解就不再是必须的。
@RestController
public class HelloController {

    private HelloService helloService;

    // 只有一个构造器,可以不写@Autowired
    public HelloController(HelloService helloService) {
        this.helloService = helloService;
    }

    @GetMapping("/hello")
    public String hello() {
        return helloService.sayHello();
    }
}

第二句

“However, if several constructors are available and there is no primary/default constructor, at least one of the constructors must be annotated with @Autowired in order to instruct the container which one to use.”

  • 含义:然而,如果一个类有多个构造器,并且没有一个被标记为“主要构造器”或默认构造器,那么至少需要在一个构造器上标注 @Autowired​,以便告诉 Spring 容器应该使用哪一个构造器。
@RestController
public class HelloController {

    private HelloService helloService;

//    @Autowired 不写@Autowired,helloSService会为null
    public HelloController(HelloService helloService) {
        this.helloService = helloService;
    }

    public HelloController() {

    }

    @GetMapping("/hello")
    public String hello() {
        return helloService.sayHello();
    }
}


Cannot invoke "com.forrest.learn.spring.autowired.example1.HelloService.sayHello()" because "this.helloService" is null
@RestController
public class HelloController {

    private HelloService helloService;

    @Autowired
    public HelloController(HelloService helloService) {
        this.helloService = helloService;
    }

    public HelloController() {

    }

    @GetMapping("/hello")
    public String hello() {
        return helloService.sayHello();
    }
}

想法

  • 在实际开发中,很少会采用构造器注入,因为写起来太麻烦了…

    image

    image

那我们需要搞清楚,为什么不推荐使用注入?

  • Why field injection is not recommended?

    • (1)不方便写单测(不赞同,挺方便的)
    • (2)不能被final修饰(确实有风险,但完全属于人为造成的风险…)
    • (3)字段注入隐藏了类的依赖项(不赞同,根据字段注入,也可以知道当前类依赖了哪些类)
    • (4)字段注入依赖于 Spring 框架在构建对象后填充依赖项。(下文解释)
    • (5)字段注入使类依赖于用于注入依赖项的框架。(不赞同,@Autowired是Spring的注解,确实依赖了框架,但我们可以用Java的@Resource进行依赖注入)

不能被final修饰(确实有风险,但helloService被恶意修改了,在运行时可以被测出来,因为运行结果不符合预期了)

@RestController
public class HelloController {

    @Autowired
    private HelloService helloService;

    @GetMapping("/hello")
    public String hello() {
        return helloService.sayHello();
    }
}
  • 由于helloService不是final的,因此有被修改的风险:
@RestController
public class HelloController {

    @Autowired
    private HelloService helloService;

    @Autowired
    private ApplicationContext applicationContext;

    @GetMapping("/hello")
    public String hello() {
        HelloController helloController = applicationContext.getBean(HelloController.class);
        Field field = null;
        try {
            field = HelloController.class.getDeclaredField("helloService");
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        }
        field.setAccessible(true);

        try {
            field.set(helloController, null);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }

        return helloService.sayHello();
    }
}

image

helloService注入时不为null,但被修改成了null,导致了npe…

不方便写单测(挺方便的)

public class HelloControllerTest {

    private MockMvc mockMvc;

    @Mock
    private HelloService helloService;

    @InjectMocks
    private HelloController helloController;

    @BeforeEach
    public void setup() {
        MockitoAnnotations.initMocks(this);
        mockMvc = MockMvcBuilders.standaloneSetup(helloController).build();
    }

    @Test
    public void hello_WhenCalled_ReturnsHelloMessage() throws Exception {
        String expectedMessage = "Hello, World!";
        when(helloService.sayHello()).thenReturn(expectedMessage);

        mockMvc.perform(get("/hello"))
                .andExpect(status().isOk())
                .andExpect(content().string(expectedMessage));
    }
}

字段注入依赖于 Spring 框架在构建对象后填充依赖项。

image

@Service
public class HelloServiceImpl implements HelloService {

    private final HelloRepository helloRepository;
//
//    public HelloServiceImpl() {
//
//    }

    @Autowired
    public HelloServiceImpl(HelloRepository helloRepository) {
        this.helloRepository = helloRepository;
        helloRepository.initialize();
    }

    @Override
    public String sayHello() {
        return "Hello World";
    }
}
  • 但完全可以先字段注入,然后通过@PostContruct再调用依赖的方法:

    image

总结

  • 通过@Autowired字段注入,并未有不可接受的副作用,而且代码简洁。

  • IDEA的警告挺烦的,怎么关了?

    image

  • 或者采用@Resouce

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

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

相关文章

深度学习视觉2D检测算法综述

目录 一、两阶段目标检测算法 1.1 R-CNN(Region-based CNN,2014) 1.2 Fast R-CNN(Fast Region-based CNN,2015) 1.3 Faster R-CNN(Faster Region-based CNN,2016) 1…

复试不难,西电马克思主义学院—考研录取情况

01、马克思主义学院各个方向 02、24马克思主义学院近三年复试分数线对比 PS:马院24年院线相对于23年院线增加15分,反映了大家对于马克思主义理论学习与研究的热情高涨,也彰显了学院在人才培养、学科建设及学术研究等方面的不断进步与成就。 6…

【A2DP】深入解读A2DP中通用访问配置文件(GAP)的互操作性要求

目录 一、模式支持要求 1.1 发现模式 1.2 连接模式 1.3 绑定模式 1.4 模式间依赖关系总结 1.5 注意事项 1.6 协议设计深层逻辑 二、安全机制(Security Aspects) 三、空闲模式操作(Idle Mode Procedures) 3.1 支持要求 …

分享一个免费的CKA认证学习资料

关于CKA考试 CKA(Certified Kubernetes Administrator)是CNCF基金会(Cloud Native Computing Foundation)官方推出的Kubernetes管理员认证计划,用于证明持有人有履行Kubernetes管理的知识,技能等相关的能力…

观成科技:​加密C2框架Platypus流量分析

一、工具介绍 Platypus 是一款支持多会话的交互式反向 Shell 管理器。在实际的渗透测试中,为了解决 Netcat/Socat 等工具在文件传输、多会话管理方面的不足,该工具在多会话管理的基础上增加了在渗透测试中能更好发挥作用的功能(如:交互式 Sh…

Jetson Nano NX 重装系统

本篇记录了自己刚拿到Jetson板子后,刻意去学习给板子重刷系统的过程,学会重装系统是玩嵌入式开发板的基操。 注意:我使用的是 Nvidia 官方 SDK Manager 给 Jetson 刷系统的,需要额外准备一台 linux 电脑(双系统或者虚拟…

Java数据结构第二十三期:Map与Set的高效应用之道(二)

专栏:Java数据结构秘籍 个人主页:手握风云 目录 一、哈希表 1.1. 概念 1.2. 冲突 1.3. 避免冲突 1.4. 解决冲突 1.5. 实现 二、OJ练习 2.1. 只出现一次的数字 2.2. 随机链表的复制 2.3. 宝石与石头 一、哈希表 1.1. 概念 顺序结构以及平衡树中…

linux系统命令——权限

一、有哪些权限 读(r)——对应数字4 写(w)——对应数字2 执行(x)——对应数字1 二、权限及数字的对应 4对应r-- 2对应-w- 1对应--x 5对应r-x 6对应rw- 7对应rwx 三、文件的基本属性 如图&#…

PentestGPT 下载

PentestGPT 下载 PentestGPT 介绍 PentestGPT(Penetration Testing GPT)是一个基于大语言模型(LLM)的智能渗透测试助手。它结合了 ChatGPT(或其他 GPT 模型)与渗透测试工具,帮助安全研究人员自…

JVM 2015/3/15

定义:Java Virtual Machine -java程序的运行环境(java二进制字节码的运行环境) 好处: 一次编写,到处运行 自动内存管理,垃圾回收 数组下标越界检测 多态 比较:jvm/jre/jdk 常见的JVM&…

sql靶场-时间盲注(第九、十关)保姆级教程

目录 时间盲注(第九、十关) 1.判断 2.确认时间盲注 2.手工尝试时间盲注 数据库名长度 数据库名字符 表数 表名长度 表名字符 字段数 字段名长度 字段名字符 4.脚本时间盲注注入 5.第十关 时间盲注(第九、十关) 1.判…

51c自动驾驶~合集54

我自己的原文哦~ https://blog.51cto.com/whaosoft/13517811 #Chameleon 快慢双系统!清华&博世最新:无需训练即可解决复杂道路拓扑 在自动驾驶技术中,车道拓扑提取是实现无地图导航的核心任务之一。它要求系统不仅能检测出车道和交…

大模型推理:LM Studio在Mac上部署Deepseek-R1模型

LM Studio LM Studio是一款支持离线大模型部署的推理服务框架,提供了易用的大模型部署web框架,支持Linux、Mac、Windows等平台,并提供了OpenAI兼容的SDK接口,主要使用LLama.cpp和MLX推理后端,在Mac上部署时选择MLX推理…

扩散模型:AIGC领域的核心引擎,解锁图像生成新维度

一、扩散模型技术原理 扩散模型是一类生成模型,它运用了物理热力学中的扩散思想, 主要包括前向扩散和反向扩散两个过程。 1.1、生成模型 在深度学习中,生成模型的目标是根据给定的样本(训练数据) 生成新样本。首先给…

Java多线程与高并发专题——原子类和 volatile、synchronized 有什么异同?

原子类和 volatile异同 首先,通过我们对原子类和的了解,原子类和volatile 都能保证多线程环境下的数据可见性。在多线程程序中,每个线程都有自己的工作内存,当多个线程访问共享变量时,可能会出现一个线程修改了共享变…

【数据结构】数据结构,算法 概念

0.本篇问题: 数据、数据元素、数据对象、数据项之间的基本关系?ADT是什么?数据结构的三要素?数据的逻辑结构有哪些?数据的存储结构有哪些?算法的五个特征?O(1) O(logn) O(n^n) O(n) O(n^2…

总结 HTTP 协议的基本格式, 相关知识以及抓包工具fiddler的使用

目录 1 HTTP是什么 2 HTTP协议格式 3 HTTP请求(Request) 3.1 认识URL 3.2 方法 3.3 认识请求"报头"(header) 4 HTTP响应详解 4.1 认识"状态码"(statuscode) 4.2 认识响应"报头"(header) 4.3 认识响应"正⽂"(body) 5 通过f…

探索Maas平台与阿里 QWQ 技术:AI调参的魔法世界

摘要:本文介绍了蓝耘 Maas 平台在人工智能领域的表现及其核心优势,包括强大的模型支持、高效的资源调度和友好的操作界面。文章还探讨了蓝耘 Maas 平台与阿里 QWQ 技术的融合亮点及应用拓展实例,并提供了调参实战指南,最后对蓝耘 …

Linux第三次练习

1、创建根目录结构中的所有的普通文件 首先在根目录下面新创建一个test目录,然后将查找到的普通文件新建到test目录下 2、列出所有账号的账号名 3、将/etc/passwd中内容按照冒号隔开的第三个字符从大到小排序后输出所有内容 4、列出/etc/passwd中的第20行-25行内容…

软件测试知识总结

1、黑盒测试、白盒测试、灰盒测试 1.1 黑盒测试 黑盒测试又叫功能测试、数据驱动测试 或 基于需求规格说明书的功能测试。该类测试注重于测试软件的功能性需求。 采用这种测试方法,测试工程师把测试对象看作一个黑盒子,完全不考虑程序内部的逻辑结构和…