聊聊@Autowired注解的Field injection is not recommended提示问题
1. 前言
在我接触过的大部分Java项目中,经常看到使用@Autowired
注解进行字段注入:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
@Autowired
private PaymentService paymentService;
@Autowired
private InventoryService inventoryService;
}
在IDEA中,以上代码@Autowired
注解下会显示波浪线,鼠标悬停后提示:Field injection is not recommended,
翻译过来就是不建议使用字段注入。
关于该提示问题,有直接修改IDEA设置关闭该提示的,有替换为使用@Resource
注解的,但这都不是该问题的本质。
该问题的本质是Spring官方推荐使用构造器注入,IDEA作为一款智能化的IDE,针对该项进行了检测并给以提示。
所以该提示背后的本质问题是:为什么Spring官方推荐构造器注入而不是字段注入?
2. 推荐构造器注入的理由
相比字段注入,构造器注入有以下几个优点:
- 支持不可变性
- 依赖明确
- 单元测试友好
- 循环依赖检测前置,提前暴露问题
2.1 支持不可变性
构造器注入允许将依赖字段声明为final
,确保对象一旦创建,其依赖关系不再被修改。
字段注入无法使用final,依赖可能在对象生命周期中被意外修改,破坏状态一致性。
构造器注入示例:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
private final PaymentService paymentService;
private final InventoryService inventoryService;
@Autowired
public OrderService(PaymentService paymentService, InventoryService inventoryService) {
this.paymentService = paymentService;
this.inventoryService = inventoryService;
}
}
说明:如果Spring版本是4.3或者更高版本且只有一个构造器,构造器上的
@Autowired
注解可以省略。
2.2 依赖明确
构造器注入通过在类的构造函数中显式声明依赖,并且强制要求在创建对象时必须提供所有必须的依赖项,
通过构造函数参数,使用者对该类的依赖一目了然。
字段注入通过在类的字段上直接使用@Autowired
注解注入依赖,依赖关系隐藏在类的内部,使用者无法直接看到该类的依赖。
2.3 单元测试友好
构造器注入允许直接通过new创建对象,无需依赖Spring容器或反射,降低了测试复杂度。
字段注入需要依赖Spring容器或反射,增加了测试复杂度。
2.4 循环依赖检测前置,提前暴露问题
构造器注入在应用启动时直接暴露循环依赖,强制开发者通过设计解决问题。
字段注入在应用启动时不会暴露循环依赖,直到实际调用时才可能暴露问题,增加调试难度。
示例:
假设项目中有以下两个Service存在循环依赖:
import org.springframework.stereotype.Service;
@Service
public class OrderService {
private final PaymentService paymentService;
public OrderService(PaymentService paymentService) {
this.paymentService = paymentService;
}
}
import org.springframework.stereotype.Service;
@Service
public class PaymentService {
private final OrderService orderService;
public PaymentService(OrderService orderService) {
this.orderService = orderService;
}
}
此时启动项目会报错,抛出org.springframework.beans.factory.BeanCurrentlyInCreationException
异常,
大致的异常信息如下所示:
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'orderService': Requested bean is currently in creation: Is there an unresolvable circular reference?
将以上两个Service修改为字段注入:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
@Autowired
private PaymentService paymentService;
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class PaymentService {
@Autowired
private OrderService orderService;
}
此时启动项目不会报错,可以启动成功。
3. @RequiredArgsConstructor注解的使用及原理
为了避免样板化代码或者为了简化代码,有的项目中可能会使用@RequiredArgsConstructor
注解来代替显式的构造方法:
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@RequiredArgsConstructor
@Service
public class OrderService {
private final PaymentService paymentService;
private final InventoryService inventoryService;
}
接下来简单讲解下@RequiredArgsConstructor
注解的原理。
@RequiredArgsConstructor
注解用于在编译时自动生成包含特定字段的构造方法。
字段筛选逻辑如下所示:
- 被
final
修饰的未显式初始化的非静态字段 - 被
@NonNull
注解标记的未显式初始化的非静态字段
示例:
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class User {
private final String name;
@NonNull
private Integer age;
private final String address = "";
private String email;
private static String city;
@NonNull
private String sex = "男";
}
以上代码在编译时自动生成的构造方法如下所示:
public User(String name, @NonNull Integer age) {
if (age == null) {
throw new NullPointerException("age is marked non-null but is null");
} else {
this.name = name;
this.age = age;
}
}
从生成的构造方法可以看出:
1)如果字段被lombok.NonNull
注解标记,在生成的构造方法内会做null值检查。
2)address字段虽然被final
修饰,但因为已初始化,所以未包含在构造方法中。
3)email字段既没被final
修饰,也没被lombok.NonNull
注解标记,所以未包含在构造方法中。
4)city字段是静态字段,所以未包含在构造方法中。
5)sex字段虽然被lombok.NonNull
注解标记,但因为已初始化,所以未包含在构造方法中。
4. 总结
@Autowired
注解在IDEA中提示:Field injection is not recommended,其背后的本质问题是:
Spring官方推荐构造器注入而不是字段注入。
而Spring官方推荐构造器注入,是因为相比字段注入,构造器注入有以下几个优点:
- 支持不可变性
- 依赖明确
- 单元测试友好
- 循环依赖检测前置,提前暴露问题
使用构造器注入时,为了避免样板化代码或者为了简化代码,可以使用@RequiredArgsConstructor
注解来代替显式的构造方法,
因为@RequiredArgsConstructor
注解可以在编译时自动生成包含特定字段的构造方法。
至于项目中要不要使用构造器注入,使用显式的构造方法还是使用@RequiredArgsConstructor
注解来简化代码,可以根据个人喜好及
团队规范自行决定。
文章持续更新,欢迎关注微信公众号「申城异乡人」第一时间阅读!
聊聊@Autowired注解的Field injection is not recommended提示问题的更多相关文章
- @Autowired注解警告Field injection is not recommended
在使用spring框架中的依赖注入注解@Autowired时,idea报了一个警告 大部分被警告的代码都是不严谨的地方,所以我深入了解了一下. 被警告的代码如下: @Autowired UserDao ...
- 【已解决】Field injection is not recommended和Could not autowired. No beans of 'xxx' type found.
目录 问题 解决办法 备注 问题 在项目中,我们使用Spring的@Autowired注解去引入其他类时有时候阿里的编码规约插件就会提示:"Field injection is not re ...
- @Autowired 警告 Field injection is not recommended Spring @Autowired注入
问题: 一. 在IDEA升级2017版后,发现以前使用的 @Autowired 出现了个警告 Field injection is not recommended. @Autowired的三种使用方式 ...
- IntelliJ IDEA:Field injection is not recommended
使用IntelliJ IDEA进行开发的时候,code analyze的时候会出现提示“Field injection is not recommended”. stackoverflow上有篇回答: ...
- Spring5:@Autowired注解、@Resource注解和@Service注解
什么是注解 传统的Spring做法是使用.xml文件来对bean进行注入或者是配置aop.事物,这么做有两个缺点: 1.如果所有的内容都配置在.xml文件中,那么.xml文件将会十分庞大:如果按需求分 ...
- @Autowired注解到底是byType还是byName?
2016-08-05 14:29:32 杨家昌 阅读数 13400更多 分类专栏: spring 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明 ...
- @Autowired 注解详解
前言 我们平时使用 Spring 时,想要 依赖注入 时使用最多的是 @Autowired 注解了,本文主要讲解 Spring 是如何处理该注解并实现 依赖注入 的功能的. 正文 首先我们看一个测试用 ...
- Spring IoC @Autowired 注解详解
前言 本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本.因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析. 我们平时使用 Spring 时,想要 依赖 ...
- Spring @Autowired 注解自动注入流程是怎么样?
面试中碰到面试官问:"Spring 注解是如果工作的?",当前我一惊,完了这不触及到我的知识误区了吗?,还好我机智,灵机一动回了句:Spring 注解的工作流程倒还没有看到,但是我 ...
- 如何实现一个简易版的 Spring - 如何实现 @Autowired 注解
前言 本文是 如何实现一个简易版的 Spring 系列第四篇,在 上篇 介绍了 @Component 注解的实现,这篇再来看看在使用 Spring 框架开发中常用的 @Autowired 注入要如何实 ...
随机推荐
- verilator书写C++版模块testbench
默认顶层模型名称为top,环境名称为contextp const std::unique_ptr<VerilatedContext> contextp{new VerilatedConte ...
- Web前端入门第 11 问:HTML 常用标签有多少?全量标签有多少?
HELLO,这里是大熊学习前端开发的入门笔记. 本系列笔记基于 windows 系统. 截止发文,MDN 收录的 HTML 全量标签有 126 个,有 18 个标记已弃用. 名词解释:MDN --- ...
- jQuery ajax 文件上传 Request Headers 缺少 boundary
原文地址: https://blog.jijian.link/2020-07-28/jquery-ajax-upload-file/ 一般上传方式 const file = document.getE ...
- 原生JS实现虚拟列表(不使用Vue,React等前端框架)
好家伙, 1. 什么是虚拟列表 虚拟列表(Virtual List)是一种优化长列表渲染性能的技术.当我们需要展示成千上万条数据时,如果一次性将所有数据渲染到DOM中,会导致页面卡顿甚至崩溃.虚拟 ...
- Centos 安装 nload (流量监控)
QQ交流群:646559931.系统安装基本工具[root@localhost ~]# yum install -y gcc gcc-c++ make flex byacc libpcap ncurs ...
- 【C语言】格式符
对于很多人来说,用格式符都是熟能生巧,而不清楚为什么是那样的格式符,所以我在这列了一个表,翻译了其对应的英文. 进制名称 英文 缩写 二进制 Binary B 八进制 Octal O 十进制 Deci ...
- 【uniapp】文本控件多余文字省略号代替
多余文字使用省略号效果 代码 .l-dd-content{ width: 100%; color: #8b8b8b; display: -webkit-box; /** 对象作为伸缩盒子模型显示 ** ...
- 探秘 MySQL 索引底层原理,解锁数据库优化的关键密码(下)
上两篇文章<探秘MySQL索引底层原理,解锁数据库优化的关键密码(上)>和<探秘 MySQL 索引底层原理,解锁数据库优化的关键密码(中)>主要讲了MySQL索引的底层原理,且 ...
- Assets, Resources and AssetBundles(五):AssetBundle usage patterns
这是系列文章中的第五章,内容涉及"Unity5"中的资产.资源和资源管理. 本系列的前一章介绍了AssetBundles的基本原理,其中包括各种加载API的低级行为.本章讨论了在实 ...
- MySQL 中的索引数量是否越多越好?为什么?
MySQL 中的索引数量是否越多越好?为什么? 虽然索引能够提高查询性能,但并不是索引越多越好.索引数量过多会带来一定的负面影响,尤其是在写操作频繁的场景下.需要根据实际的查询需求来合理设计索引,以平 ...