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. 推荐构造器注入的理由

相比字段注入,构造器注入有以下几个优点:

  1. 支持不可变性
  2. 依赖明确
  3. 单元测试友好
  4. 循环依赖检测前置,提前暴露问题

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注解用于在编译时自动生成包含特定字段的构造方法。

字段筛选逻辑如下所示:

  1. final修饰的未显式初始化的非静态字段
  2. @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官方推荐构造器注入,是因为相比字段注入,构造器注入有以下几个优点:

  1. 支持不可变性
  2. 依赖明确
  3. 单元测试友好
  4. 循环依赖检测前置,提前暴露问题

使用构造器注入时,为了避免样板化代码或者为了简化代码,可以使用@RequiredArgsConstructor注解来代替显式的构造方法,

因为@RequiredArgsConstructor注解可以在编译时自动生成包含特定字段的构造方法。

至于项目中要不要使用构造器注入,使用显式的构造方法还是使用@RequiredArgsConstructor注解来简化代码,可以根据个人喜好及

团队规范自行决定。

文章持续更新,欢迎关注微信公众号「申城异乡人」第一时间阅读!

聊聊@Autowired注解的Field injection is not recommended提示问题的更多相关文章

  1. @Autowired注解警告Field injection is not recommended

    在使用spring框架中的依赖注入注解@Autowired时,idea报了一个警告 大部分被警告的代码都是不严谨的地方,所以我深入了解了一下. 被警告的代码如下: @Autowired UserDao ...

  2. 【已解决】Field injection is not recommended和Could not autowired. No beans of 'xxx' type found.

    目录 问题 解决办法 备注 问题 在项目中,我们使用Spring的@Autowired注解去引入其他类时有时候阿里的编码规约插件就会提示:"Field injection is not re ...

  3. @Autowired 警告 Field injection is not recommended Spring @Autowired注入

    问题: 一. 在IDEA升级2017版后,发现以前使用的 @Autowired 出现了个警告 Field injection is not recommended. @Autowired的三种使用方式 ...

  4. IntelliJ IDEA:Field injection is not recommended

    使用IntelliJ IDEA进行开发的时候,code analyze的时候会出现提示“Field injection is not recommended”. stackoverflow上有篇回答: ...

  5. Spring5:@Autowired注解、@Resource注解和@Service注解

    什么是注解 传统的Spring做法是使用.xml文件来对bean进行注入或者是配置aop.事物,这么做有两个缺点: 1.如果所有的内容都配置在.xml文件中,那么.xml文件将会十分庞大:如果按需求分 ...

  6. @Autowired注解到底是byType还是byName?

    2016-08-05 14:29:32 杨家昌 阅读数 13400更多 分类专栏: spring   版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明 ...

  7. @Autowired 注解详解

    前言 我们平时使用 Spring 时,想要 依赖注入 时使用最多的是 @Autowired 注解了,本文主要讲解 Spring 是如何处理该注解并实现 依赖注入 的功能的. 正文 首先我们看一个测试用 ...

  8. Spring IoC @Autowired 注解详解

    前言 本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本.因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析. 我们平时使用 Spring 时,想要 依赖 ...

  9. Spring @Autowired 注解自动注入流程是怎么样?

    面试中碰到面试官问:"Spring 注解是如果工作的?",当前我一惊,完了这不触及到我的知识误区了吗?,还好我机智,灵机一动回了句:Spring 注解的工作流程倒还没有看到,但是我 ...

  10. 如何实现一个简易版的 Spring - 如何实现 @Autowired 注解

    前言 本文是 如何实现一个简易版的 Spring 系列第四篇,在 上篇 介绍了 @Component 注解的实现,这篇再来看看在使用 Spring 框架开发中常用的 @Autowired 注入要如何实 ...

随机推荐

  1. 【Python脚本】路径管理之pathlib

    在Python的pathlib模块中,Path类和PurePath类是用于处理文件和目录路径的两个主要类.它们具有不同的目的和功能,以下是它们的主要异同点: 类的继承关系: Path类继承自PureP ...

  2. Linux下使用sz/rz命令从服务器下载或上传文件

    简介 Linux中rz命令和sz命令都可用于文件传输,而rz命令主要用于文件的上传,sz命令用于从Linux服务器下载文件到本地. 安装 yum安装 yum -y install lrzsz 源码安装 ...

  3. PostgreSQL的可变字符串

    在Oralce中,通常都使用varchar2作为字符串,它能自动删除前后空格.因业务需要用到Postgre,使用了character类型,用起来是没有什么问题.后来发现在smartBI引用这里面的数据 ...

  4. IvorySQL 4.4 发布

    IvorySQL 4.4 已于 2025 年 3 月 10 日正式发布.新版本全面支持 PostgreSQL 17.4,新增多项新功能,并修复了已知问题. 增强功能 PostgreSQL 17.3 增 ...

  5. Portainer安装配置

    什么是portainer 官网:https://www.portainer.io/ Portainer(基于 Go) 是一个轻量级的Web管理界面,可让您轻松管理 Docker 主机 或 Swarm ...

  6. DeepSeek 会话补全 API

    DeepSeek 会话补全 API 是一个超强大的 AI 对话接口 ,可以让你: 打造自己的 智能聊天机器人 让 AI 帮你 写文章.改代码.编故事 甚至模拟 各种角色(比如猫娘.霸道总裁.科幻作家- ...

  7. ShardingJdbc学习笔记

    Mysql主从复制遇到问题 安装mysql Install/Remove of the Service Denied!错误的解决办法

  8. python爬虫爬取B站视频字幕,简单的数据处理(pandas将字幕写入到CSV文件中)

    上文,我们爬取到B站视频的字幕:https://www.cnblogs.com/becks/p/14540355.html 这篇,讲讲怎么把爬到的字幕写到CSV文件中,以便用于后面的分析 本文主要用到 ...

  9. kettle安装文件下载(含多版本)

    kettle是一款基于java开发的洗数工具,可以通过图像化的操作界面,拖拉拽的操作方式,实现数据导入导出清洗等功能,还支持编写脚本进行数据处理,功能十分强大. 本文主要记录一下kettle各版本下载 ...

  10. kette介绍-Step之Table output

    表输入(Table output)介绍: Table output步骤常被用于将转换中的行集从内存持久化到数据库,对转换而言是 行集被拿出去的感觉,故名为输出.可以限制提交记录数量和指定插入的目标表 ...