为什么在IDEA使用@Autowired会报黄?

其实就两个:

  • 一个final关键字;
  • 一个@RequiredArgsConstructor注解

够了,就这两个,你看看下面的:

复制代码
@RequiredArgsConstructor
@Service
@Slf4j
public class OrderApplicationService {
    private final OrderDomainService orderDomainService;
    private final TransactionTemplate transactionTemplate;
}

具体的,我下面会说明。我们需要先看看为啥不推荐使用@Autowired了。

你的IDEA里给一个字段加上@Autowired,会看到一条黄色波浪线。鼠标悬停上去,提示信息是:Field injection is not recommended。

这不是IDEA的问题。IntelliJ这条检查规则,依据的是Spring官方说明。Spring团队在官方文档里明确写了,推荐使用构造器注入。

那@Autowired字段注入有啥问题?

先看一段典型的字段注入代码:

复制代码
@Service
public class OrderService {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private OrderRepository orderRepository;

}

这种写法在老项目里到处都是。每个依赖一行注解加一行声明,看着还行。但是问题在两个地方。

依赖没法用final修饰

字段注入的工作原理是:Spring先创建对象实例,再通过反射把依赖设置到字段上。赋值发生在对象构造之后,所以这些字段不能声明为final

不能用final,意味着依赖可以在运行时被修改:

复制代码
@Service
public class OrderService {

    @Autowired
    private UserRepository userRepository;

    public void someMethod() {
        // 编译不报错,运行时也不会立刻出问题
        this.userRepository = null;
    }
}

下次调用userRepository的方法时,直接空指针异常。

换成构造器注入,字段可以声明为final

复制代码
@Service
public class OrderService {

    private final UserRepository userRepository;

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

    public void someMethod() {
        // 编译直接报错:Cannot assign a value to final variable
        this.userRepository = null;
    }
}

依赖一旦注入就不可更改,编译器直接拦住。

脱离容器单元测试不好搞

字段注入的类,所有依赖都通过Spring容器的反射机制设置。如果不启动容器,直接new一个对象:

复制代码
// 所有依赖都是null
OrderService service = new OrderService();

想让测试跑起来,要么用@SpringBootTest启动整个容器,要么用反射工具手动设值。

构造器注入的类可以直接实例化:

复制代码
OrderService service = new OrderService(
    mockUserRepository,
    mockOrderRepository,
    mockNotificationService
);

不依赖框架,不启动容器,直接传Mock对象进去测业务逻辑。

我们来看Spring官方是怎么说的

Spring Framework官方文档里有一段专门讨论这个问题,原文如下:

The Spring team generally advocates constructor injection, as it lets you implement application components as immutable objects and ensures that required dependencies are not null. Furthermore, constructor-injected components are always returned to the client (calling) code in a fully initialized state.

这段话出自Spring Framework 6.0的官方文档,是框架团队的明确立场,翻译一下关键信息:

  • Spring团队主张使用构造器注入
  • 构造器注入让组件成为不可变对象
  • 确保必需的依赖不会为null
  • 组件返回给调用方时,一定处于完全初始化的状态

Spring从4.3版本开始还做了一个改动:如果一个类只有一个构造器,@Autowired注解可以省略,Spring会自动用这个构造器进行注入。这个设计本身就是在引导开发者往构造器注入迁移。

另外,Spring官方文档里也提到了这一点:大量的构造器参数是一个代码坏味道,说明这个类承担了太多职责,应该重构。

构造器注入的写法

手写构造器

最基础的方式,不依赖额外工具:

复制代码
@Service
public class OpenAppService {

    private final OpenAppMapper openAppMapper;

    public OpenAppService(OpenAppMapper openAppMapper) {
        this.openAppMapper = openAppMapper;
    }
}

类里只有一个构造器,Spring自动识别并注入,不需要加@Autowired。字段用final修饰,保证不可变。

这种写法在依赖少的时候足够用。一旦依赖超过三四个,手写构造器和赋值语句会显得比较啰嗦,「代码颜值也不高」。

该@RequiredArgsConstructor + final出场了

目前主流项目的标准做法:

复制代码
@RequiredArgsConstructor
@Service
@Slf4j
public class OrderApplicationService {
    private final OrderDomainService orderDomainService;
    private final TransactionTemplate transactionTemplate;
}

@RequiredArgsConstructorLombok提供的注解,编译期自动生成一个包含所有final字段的构造器。效果和手写完全一样,但省掉了模板代码。

新加一个依赖,只需要加一行private final字段声明。Lombok自动把它加到构造器参数里,不需要手动维护构造器。

整个项目统一这种写法,代码风格干净一致。

对比速查表

维度 @Autowired字段注入 构造器注入
不可变性 不支持final 支持final
单元测试 需要反射或启动容器 直接new,传Mock对象
依赖可见性 分散在各个字段上 集中在构造器参数
空指针风险 运行时可能为null 容器启动时校验
Spring官方态度 不推荐 明确推荐

小结

适当的约束比完全的自由更有价值。 final是约束,构造器参数太长时的不适感也是约束。这些约束会推着你在更早的阶段发现设计问题。

如果你在维护一个老项目,里面到处都是@Autowired字段注入,不用急着一次性改完。新代码统一用构造器注入,老代码改动时顺手切过来。

希望这篇内容可以帮到你。

相关推荐
Try,多训练12 分钟前
软件设计师备考第一性原理分析
java·经验分享·学习方法
Seven9724 分钟前
Tomcat Container容器之Engine:StandardEngine
java
jinanwuhuaguo27 分钟前
(第三十六篇)OpenClaw 去中心化的秩序——从“中心调度”到“网格自治”的治理革命
java·大数据·开发语言·网络·docker·去中心化·github
Thanks_ks31 分钟前
【第 002 讲】Python 标准开发环境搭建:运行环境 | 环境变量 | IDE 部署 | 配置优化
ide·python·pycharm·开发工具·环境配置·环境变量·编程基础
AI进化营-智能译站6 小时前
ROS2 C++开发系列17-多线程驱动多传感器|chrono高精度计时实现机器人同步控制
java·c++·ai·机器人
qq_589568109 小时前
springbootweb案例,出现访问 http://localhost:8080/list 一直处于浏览器运转阶段
java·网络协议·http·list·springboot
JAVA面经实录9179 小时前
计算机基础(完整版·超详细可背诵)
java·linux·数据结构·算法
白云LDC9 小时前
Android Studio新建Vecter asset一直显示Loading icons(转圈圈)的解决办法
android·ide·android studio
AC赳赳老秦9 小时前
知识产权辅助:用 OpenClaw 批量生成专利交底书 / 软著申请材料,自动校验格式与内容合规性
java·人工智能·python·算法·elasticsearch·deepseek·openclaw