为什么Spring和IDEA都不推荐使用 @Autowired 注解
作者:时间静止不是简史 发布时间:2023-12-08 17:54:44
前言
请看下面几个问题
Spring为什么不推荐使用
@Autowired
注解?为什么推荐使用
@Resource
代替@Autowired
注解?如何快速使用构造注入代替
@Autowired
?@Autowired, @Qualifier, @Resource,
三者有何区别?
下面, 我们带着以上问题去梳理和学习, 体会知识之间的关联性
Spring为什么不推荐使用@Autowired 注解
背景
做开发的同学可能都会发现, idea 在我们经常使用的@Autowired
注解上添加了警告
警告内容是: Field injection is not recommended
, 译为: 不推荐使用属性注入
我们点击右侧三个小点查看描述, 可以看到信息如下图
原因详情描述: Inspection info: Spring Team recommends: "Always use constructor based dependency injection in your beans. Always use assertions for mandatory dependencies".
译为: Spring 团队建议: 始终在您的 bean 中使用基于构造函数的依赖注入。始终对强制依赖项使用断言
原因
为什么 Spring 建议我们在Bean中使用构造注入呢?
想要回答这个问题, 我们需要了解 Spring的依赖注入(DI)方式
Spring常用的注入方式有: 简单类型注入, 集合类型注入, 域属性自动注入, 自动注入的类别, 空值注入, 构造注入
可以简化为: 属性注入, 构造方法注入, set 方法注入
下面, 来用代码展示下三种方式注入
属性注入
可以看到, 我们开发最常用的就是属性注入
@RestController
public class AppointmentNumberConfigurationController {
@Autowired
private AppointmentNumberConfigurationService numberConfigurationService;
}
set 方法注入
set 方法注入也会用到@Autowired
注解,但使用方式与属性注入有所不同,
属性注入是用在成员变量上,而set 方法的时候,是用在成员变量的Setter函数上。
@RestController
public class AppointmentNumberConfigurationController {
private AppointmentNumberConfigurationService numberConfigurationService;
@Autowired
public void setNumberConfigurationService(AppointmentNumberConfigurationService numberConfigurationService) {
this.numberConfigurationService = numberConfigurationService;
}
构造方法注入
Constructor Injection是构造器注入,是我们最为推荐的一种使用方式。
但是, 每次注入都按照这样的流程去构造注入的话, 会显得比较麻烦.
至于如何去简化这一步骤, 我们可以继续往下看.
@RestController
public class AppointmentNumberConfigurationController {
final AppointmentNumberConfigurationService numberConfigurationService;
public AppointmentNumberConfigurationController(AppointmentNumberConfigurationService numberConfigurationService) {
this.numberConfigurationService = numberConfigurationService;
}
}
三种方式对比如下
使用属性注入可能会出现的问题
基于属性注入的方式, 违背单一职责原则
因为现在的业务一般都会使用很多依赖, 但拥有太多的依赖通常意味着承担更多的责任,而这显然违背了单一职责原则.
并且类和依赖容器强耦合,不能在容器外使用。
基于属性注入的方式, 容易导致Spring 初始化失败
因为现在在Spring特别是Spring Boot使用中, 经常会因为初始化的时候, 由于属性在被注入前就引用而导致npe(空指针错误),
进而导致容器初始化失败(类似下面代码块). Java 在初始化一个类时,
是按照 静态变量或静态语句块 –> 实例变量或初始化语句块 –> 构造方法 -> @Autowired 的顺序。
所以在执行这个类的构造方法时,person 对象尚未被注入,它的值还是 null。
通过@Autowired 注入, 又因为是 ByType 注入, 因此有可能会出现两个相同的类型bean
如下代码快, 就会产生两个相同的Bean, 进而导致Spring 装配失败
//2. 基于属性注入的方式, 容易导致Spring 初始化失败
@Autowired
private Person person;
private String company;
public UserServiceImpl(){
this.company = person.getCompany();
}
//3. 通过@Autowired 注入, 又因为是 ByType 注入, 因此有可能会出现两个相同的类型bean
public interface IUser {
void say();
}
@Service
public class User1 implements IUser{
@Override
public void say() {
}
}
@Service
public class User2 implements IUser{
@Override
public void say() {
}
}
@Service
public class UserService {
@Autowired
private IUser user;
}
解决
如果一定要使用属性注入, 可以使用 @Resource
代替 @Autowired
注解@Resource
的作用相当于@Autowired
,只不过@Autowired
按照byType
自动注入。
如果我们想使用按照名称byName
来装配,可以结合@Qualifier
注解一起使用。
如果可能的话, 尽量使用构造注入
Lombok提供了一个注解@RequiredArgsConstructor
, 可以方便我们快速进行构造注入, 例如:
@RestController
@RequiredArgsConstructor
public class AppointmentNumberConfigurationController {
final AppointmentNumberConfigurationService numberConfigurationService;
}
同时需要注意:
使用@RequiredArgsConstructor
注解需要导入Lombok 包 或者安装lombok 插件
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
必须声明的变量为final
根据构造器注入的,相当于当容器调用带有一组参数的类构造函数时,基于构造函数的 DI 就完成了,
其中每个参数代表一个对其他类的依赖。基于构造方法为属性赋值,容器通过调用类的构造方法将其进行依赖注入
思考
为什么推荐使用@Resource,不推荐使用@Autowired
通过对问题1 的梳理, 我们可以知道.
因为@Autowired
注解在Bean 注入的时候是基于ByType
, 因此会由于注入两个相同类型的Bean导致装配失败
@Resource
的作用相当于@Autowired
,只不过@Autowired
按照byType
自动注入。
如果我们想使用按照名称byName
来装配,可以结合@Qualifier
注解一起使用。
@Resource
装配顺序:
①如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。
②如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。
③如果指定了type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。
④如果既没有指定name,又没有指定type,则自动按照byName
方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配。
因此, 如果一定要使用属性注入, 可以使用 @Resource 代替 @Autowired 注解
@Autowired, @Qualifier, @Resource, 三者有何区别
@Autowired
: 通过byType
方式进行装配, 找不到或是找到多个,都会抛出异常。@Qualifier
: 如果想让@Autowired
注入的Bean进行byName
装配, 可以使用@Qualifier
进行指定@Resource
:作用相当于@Autowired
,只不过@Resource
默认按照byName
方式装配, 如果没有匹配, 则退回到byType
方式进行装配
参考文档
为什么IDEA不推荐你使用@Autowired?
@Autowired和@Resource的区别是什么?
来源:https://timepause.blog.csdn.net/article/details/123529701
猜你喜欢
- 前言春节要到了,看惯了前端各种小游戏,确实做得很好,很精致。但是我也要为后端程序员稍微做一点贡献,做一款java版本的【年兽大作战】。这个游
- 本文实例讲述了java内部类原理与用法。分享给大家供大家参考,具体如下:概念内部类:可以包含在另外一个类中的类外部类:包含内部类的类每个内部
- 前言之前写了一个博客是关于使用SpringBoot使用validation-api实现参数校验,当时使用的注解都是validation-ap
- Java基于对象流实现银行系统的具体代码,供大家参考,具体内容如下系统特点:数据持久化到文件中,系统启动后,加载文件中数据到集合中,相当于做
- 一个线程如何知道另一线程已经结束?Thread类提供了回答此问题的方法。有两种方法可以判定一个线程是否结束。第一,可以在线程中调用isAli
- 前言如今发短信功能已经成为互联网公司的标配,本篇文章将一步步实现java发送短信考察了许多提供短信服务的三方,几乎所有都需要企业认证才可以使
- 栈和队列的本质是相同的,都只能在线性表的一端进行插入和删除。因此,栈和队列可以相互转换。用栈实现队列—力扣232题题目要求:仅使用两个栈实现
- JDK * :利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。CGlib * :利用AS
- 异常与错误:异常: 在Java中程序的错误主要是语法错误和语义错误,一个程序在编译和运行时出现的错误我们统一称之为异常,它是VM(虚拟机)通
- 本文实例讲述了Java枚举类用法。分享给大家供大家参考。具体如下:package com.school.stereotype; /** *
- 对于分布式应用来说,最开始遇到的问题就是 session 的存储了,解决方案大致有如下几种使用 spring-session 它可以把 se
- 一、定时任务的使用场景和常见的定时任务某个时间定时处理某个任务、发邮件、短信、消息提醒、订单通知、统计报表等定时任务划分单机定时任务:单机的
- 本文实例讲述了java生成XML的方法。分享给大家供大家参考,具体如下:下拉框的生成,我是通过javascript读取xml文件生成的。Xm
- 简介Spring Security,这是一种基于 Spring AOP 和 Servlet 过滤器的安全框架。它提供全面的安全性解决方案,同
- mybatis初始化SqlSessionFactory失败总结原因有几点1.resources中的xml配置文件放错位置或者是放的太深加载不
- 一、SpringMVC使用1.工程创建创建maven工程。添加java、resources目录。引入Spring-webmvc 依赖。<
- Java线程分为两类分别为daemon线程(守护线程)和User线程(用户线程),在JVM启动时候会调用main函数,main函数所在的线程
- java解析json数组最简单的json数组[ { &quo
- 输入方法第一种输入方法:scannerimport java.util.Scanner; // 导入java.util.Scannerpub
- 半藏商城中会有一些用户提交了订单但是一直没有支付的情况,之前我是通过quartz定时任务每天的5点扫描未支付订单然后读取用户的邮箱地址发送邮