Spring Boot依赖注入方式比较

主要区别

写法1:字段注入(Field Injection)

java

@Autowired
private PrintTemplateService templateService;
  • 特点:Spring 自动注入依赖到字段

  • 问题

    • 违反了单一职责原则(类可以有太多依赖)

    • 不便于单元测试(必须通过反射设置私有字段)

    • 可能导致循环依赖

    • 无法声明字段为 final

写法2:构造器注入(Constructor Injection)

java

@RequiredArgsConstructor
public class PrintTemplateController {
    private final PrintTemplateService templateService;
}
  • 特点:使用 Lombok 生成包含所有 final 字段的构造器

  • Spring 会自动通过构造器注入依赖

  • 优势

    • 依赖不可变(final)

    • 便于单元测试(可以直接传参)

    • 保证依赖完整性(对象创建时所有依赖就位)

    • 避免循环依赖问题

    • 符合不可变对象设计原则

推荐写法

强烈推荐写法2(构造器注入)

理由:

  1. 不可变性:final 字段确保依赖不会在运行时被修改

  2. 可测试性:可以直接在测试中 new 出对象

  3. 代码更安全:编译时就能发现依赖问题

  4. Spring 官方推荐:Spring 官方自 4.3 版本起推荐构造器注入

最佳实践

java

@RestController
@RequestMapping("/print/template")
@Slf4j
@RequiredArgsConstructor
public class PrintTemplateController {
    // 使用 final 确保不可变性
    private final PrintTemplateService templateService;
    private final TemplateRenderService renderService;
    
    // 如果类比较简单,也可以显式写构造器(代替 @RequiredArgsConstructor)
    // public PrintTemplateController(PrintTemplateService templateService,
    //                                TemplateRenderService renderService) {
    //     this.templateService = templateService;
    //     this.renderService = renderService;
    // }
}

特殊情况

如果必须使用字段注入(比如在某些框架的基类中),可以使用:

java

@Autowired(required = false)  // 可选依赖
private SomeService someService;

但一般来说,构造器注入是首选方案,它能让代码更健壮、更易测试、更符合现代 Java 编程规范。

那为什么搞了@Autowired这个出来 ?

这是个很好的问题!@Autowired 的出现有其历史和技术背景:

历史演进

1. 早期 Spring(Spring 1.x-2.x)

主要使用 XML 配置

xml

<bean id="userService" class="com.example.UserService">
    <property name="userDao" ref="userDao"/>
</bean>

这种方式繁琐且容易出错。

2. Spring 2.5(2007年)引入了 @Autowired

初衷是为了简化配置

java

@Component
public class UserService {
    @Autowired  // 这当时是个巨大的进步!
    private UserDao userDao;
}

在当时看来,这简直是"魔法":

  • 告别冗长的 XML

  • 代码更简洁

  • 开发效率大幅提升

为什么还需要 @Autowired?

即使现在推荐构造器注入,@Autowired 仍有其价值:

1. 可选依赖

java

@Autowired(required = false)
private OptionalDependency optionalDep;

2. Setter 注入

某些场景需要重新设置依赖:

java

private DataSource dataSource;

@Autowired
public void setDataSource(DataSource dataSource) {
    this.dataSource = dataSource;
}

3. 复杂场景

  • 多实现选择

java

@Autowired
@Qualifier("primaryDataSource")
private DataSource dataSource;
  • 集合注入

java

@Autowired
private List<Validator> validators;  // 注入所有 Validator 实现

4. 第三方集成

有些老框架或第三方库必须使用字段注入:

java

// 某些 JPA 实现或旧框架
@PersistenceContext
private EntityManager entityManager;

现代 Spring 的智能改进

Spring 4.3+(2016年)的改进

java

// 只有一个构造器时,可以省略 @Autowired
@Component
public class UserService {
    private final UserDao userDao;
    
    // 不需要 @Autowired!
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }
}

Spring Boot 的进一步简化

配合 Lombok 更加简洁:

java

@Service
@RequiredArgsConstructor  // Lombok 生成构造器
public class UserService {
    private final UserDao userDao;  // Spring 自动注入
}

技术对比表格

特性@Autowired 字段注入构造器注入
出现时间2007年(Spring 2.5)一直存在,2016年后推荐
当时定位革命性简化方案传统但可靠
当前定位特定场景使用默认推荐
不可变性❌ 不支持 final✅ 支持 final
可测试性较差(需反射)优秀
循环依赖可能隐藏问题立即暴露问题
代码简洁度非常简洁较简洁(配合 Lombok)

历史视角看这个问题

可以把依赖注入的发展看作一个螺旋上升的过程:

  1. XML 配置时代(繁琐但明确)

  2. @Autowired 时代(简化但引入新问题)

  3. 构造器注入复兴(取两者优点:简洁 + 健壮)

结论

@Autowired 在当时是革命性的进步,极大地提升了开发体验。但随着最佳实践的演进,我们发现构造器注入在大多数场景下是更好的选择

现代 Spring 开发的建议

  • ✅ 首选构造器注入(配合 Lombok @RequiredArgsConstructor

  • ⚠️ 特定场景用 @Autowired(可选依赖、Setter 注入等)

  • ❌ 避免随意使用字段注入

这就好比:电锯(@Autowired)让伐木更高效,但熟练后你会发现斧头(构造器注入)在某些场景更安全可控。工具本身没有绝对的好坏,关键看如何正确使用。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值