⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 juejin.cn/post/7463352165487935542 「四七伵」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

前言

在使用 Spring 框架时,依赖注入(DI)是一个非常重要的概念。通过注解,我们可以方便地将类的实例注入到其他类中,提升开发效率。

@Autowired又是被大家最为熟知的方式,但很多开发者在使用 IntelliJ IDEA 时,常常看到 IDEA 提示不推荐使用@Autowired,这是为什么呢?

今天讲一下我对于@Autowired字段注入的理解。

1. 为什么不推荐使用@Autowired?

1.1. 隐式依赖

@Autowired会根据类型自动注入依赖,可能导致依赖不明确。特别是在存在多个符合类型的候选对象时,可能会导致注入失败或错误地注入实例。

1.2. 循环依赖

@Autowired可能导致循环依赖,尤其在单例 Bean 中,虽然 Spring 有解决方案,但仍需额外处理。

1.3. 生命周期不透明

通过@Autowired注入的 Bean 由 Spring 管理,类无法显式了解其生命周期。相比之下,构造器注入可以显式管理依赖的生命周期。

1.4. 不符合不可变性原则

字段注入使得依赖关系在对象生命周期内可能发生变化,而构造器注入确保依赖在对象创建时设定,符合不可变性原则。

经常看到的争议点:不推荐使用@Autowired,所以就使用@Resource(使用@Resource时,IDEA不会有波浪线提示)。

2. @Autowired和@Resource的基本用法

2.1. @Autowired

@Autowired是 Spring 提供的注解,用于自动注入依赖项。Spring 会根据类型(默认情况下)或者名称(通过 @Qualifier)来自动注入所需的 Bean。

@Autowired
private UserService userService;

在这个例子中,Spring 会自动将 UserService 类型的 Bean 注入到 userService 属性中。

2.2. @Resource

@Resource 是 Java 规范的一部分,来自 javax/jakarta.annotation 包,它更注重按照名称来注入 Bean。默认情况下,它会尝试按名称匹配,但如果没有找到匹配的名称,再通过类型注入。

@Resource
private UserService userService;

与@Autowired不同@Resource会首先查找名为userService的 Bean,如果找不到,再通过类型注入。

可以看出其实两者本质上是没有区别的,只是匹配顺序不一样。

3. @Autowired和@Resource实测对比

假如有一个接口类,如下:

@Service
public interface UserService {
// 用户服务...
}

两个实现类,如下:

@Component
public class UserServiceImpl implements UserService {
// 实现细节
}

@Component
public class AnotherUserServiceImpl implements UserService {
// 另一个实现
}

3.1. 使用@Autowired做测试

在测试接口里面加入@Autowired,如下:

/**
* 测试接口
*/
@RestController("test")
public class TestController {
@Autowired
private UserService userService;
}

启动项目,控制台输出错误,如下:

错误提示:需要一个bean,但是找到了两个bean。

修改之后指定具体的bean名,如下:

@RestController("test")
public class TestController {
@Autowired
// @Qualifier("userServiceImpl") // 或者使用Qualifier来指定bean名
private UserService userServiceImpl;
}

启动项目,项目正常启动,断点测试如下:

正常获取到需要的bean实例。

3.2. 使用@Resource做测试

在测试接口里面加入@Resource,如下:

/**
* 测试接口
*/
@RestController("test")
public class TestController {
@Resource
private UserService userService;
}

启动项目,控制台输出错误,如下:

错误提示:需要一个bean,但是找到了两个bean。

修改之后指定具体的bean名,如下:

@RestController("test")
public class TestController {
@Resource
// @Resource(name = "userServiceImpl") // 或者使用name来指定bean名
private UserService userServiceImpl;
}

启动项目,项目正常启动,断点测试如下:

正常获取到需要的bean实例。

可以看出实际上@Autowired和@Resource的使用效果是一样的。(@Resource并不优于@Autowired,两者都是基于字段注入。甚至于在Spring框架中,@Autowired性能更好,例如:自动装配的细粒度控制 。)

4. 推荐注入的方式

构造器注入是推荐的首选方式,尤其在处理复杂依赖关系、不可变对象、单元测试等场景时,构造器注入非常有用。

4.1. 推荐原因

  • 显式依赖关系: 构造器注入使依赖关系在对象创建时就显式地列出,代码更清晰。
  • 不可变性: 依赖可以声明为 final,避免修改。
  • 避免循环依赖: 能及时发现并避免循环依赖问题。
  • 明确依赖关系: 构造器清晰暴露依赖,增强代码可读性。

4.2. 示例

@RestController("test")
publicclass TestController {
privatefinal UserService userService;

public TestController(UserService userServiceImpl) {
this.userService = userServiceImpl;
}
}

// 或者使用lombok简化代码。
@RestController("test")
@RequiredArgsConstructor
publicclass TestController {
privatefinal UserService userServiceImpl;
}

伪逻辑说明:

  • 显式依赖关系: 应该关注的是,整个项目的代码规范,而不是针对具体的某一个类去优化,使用构造器注入。
  • 不可变性: 应该关注的是,合理的设计,项目中bean在初始时,如果使用@Autowired注入某个类,就应该不在初始化期间去修改它。
  • 避免循环依赖: 也是和项目的架构相关,如果无法中设计上避免,也可以通过代理和懒加载去解决。
  • 明确依赖关系: 如果项目类名命名规范,不使用相同的类名,或者使用一些前缀后缀去区分,也能够不影响依赖关系。

5. 构造器一定优于@Autowired吗?

@Autowired在一些方面要优于构造器注入?

  • 简化代码: 自动注入依赖,无需手动编写构造器或 setter 方法,代码更简洁。
  • 快速开发: 灵活且快速,不需要修改构造函数,可以直接通过字段注入依赖。
  • 自动依赖管理: Spring 自动管理依赖,简化了依赖的选择和注入过程。
  • 避免构造函数冗长: 对于依赖较多的类,字段注入避免了构造函数过长的问题。

结语

虽然 IDEA 提示不推荐使用 @Autowired,但这并不意味着它是一个糟糕的选择。不同的注入方式有其各自的优缺点,选择依赖注入方式时,应该根据项目规模、复杂性以及团队编码习惯来决定,而不是盲目跟随趋势。

文章目录
  1. 1. 前言
  2. 2. 1. 为什么不推荐使用@Autowired?
    1. 2.1. 1.1. 隐式依赖
    2. 2.2. 1.2. 循环依赖
    3. 2.3. 1.3. 生命周期不透明
    4. 2.4. 1.4. 不符合不可变性原则
  3. 3. 2. @Autowired和@Resource的基本用法
    1. 3.1. 2.1. @Autowired
    2. 3.2. 2.2. @Resource
  4. 4. 3. @Autowired和@Resource实测对比
    1. 4.1. 3.1. 使用@Autowired做测试
    2. 4.2. 3.2. 使用@Resource做测试
  5. 5. 4. 推荐注入的方式
    1. 5.1. 4.1. 推荐原因
    2. 5.2. 4.2. 示例
  6. 6. 5. 构造器一定优于@Autowired吗?
  7. 7. 结语