为什么在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字段注入,不用急着一次性改完。新代码统一用构造器注入,老代码改动时顺手切过来。

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

相关推荐
折哥的程序人生 · 物流技术专研5 小时前
第4篇:Lambda 简化策略模式(Java 8+)
java·设计模式·策略模式·函数式编程·lambda·代码简化·扩充系列
researcher-Jiang5 小时前
高性能计算之OpenMP——超算习堂学习1
android·java·学习
烽火聊员7 小时前
查看Android Studio错误日志
android·ide·android studio
西门吹-禅7 小时前
java springboot N+1问题
java·开发语言·spring boot
DLYSB_7 小时前
生命通道:如何用 HIS 医疗系统直连网络声光终端,打造“零延误”的危急值响应网关?
java·网络·数据库·报警灯
weixin_BYSJ19878 小时前
SpringBoot + MySQL 乒乓球运动员信息管理系统项目实战--附源码04954
java·javascript·spring boot·python·django·flask·php
AI小码8 小时前
LLM 应用的缓存工程:当每次 API 调用都在燃烧成本
java·人工智能·spring·计算机·llm·编程·api
Hui Baby8 小时前
Spring Security
java·后端·spring
IT笔记9 小时前
【Rust】Rust Match 模式匹配详解
java·开发语言·rust
我才是银古10 小时前
构建 Java GIS 工具库:从碎片化 API 到统一抽象的设计实践
java·gis·geotools·gdal·esri