构造器注入、setter方法注入、属性自动注入

3 27~35 min

在Spring框架中,构造器注入、setter方法注入和属性自动注入是三种主要的依赖注入方式,它们的区别主要体现在以下方面:


1. 注入方式与时机

  • 构造器注入

    • 方式:通过类的构造函数注入依赖,依赖项作为构造函数的参数传递。

    • 时机:在对象创建时完成注入,依赖项必须在实例化时提供。

    • 代码示例

      public class UserService {
          private final UserRepository userRepo;
          
          // 构造器注入
          public UserService(UserRepository userRepo) {
              this.userRepo = userRepo;
          }
      }
  • Setter方法注入

    • 方式:通过类的Setter方法注入依赖。

    • 时机:对象创建,通过调用Setter方法设置依赖。

    • 代码示例

      public class UserService {
          private UserRepository userRepo;
          
          // Setter注入
          public void setUserRepo(UserRepository userRepo) {
              this.userRepo = userRepo;
          }
      }
  • 属性自动注入

    • 方式:由Spring容器自动装配依赖(如通过@Autowired注解、@Resource或XML配置的autowire)。

    • 时机:在Bean初始化阶段,容器自动解析依赖并注入。

    • 代码示例

      public class UserService {
          @Autowired
          private UserRepository userRepo;
      }

2. 依赖的强制性与灵活性

  • 构造器注入

    • 强制性:适合必需依赖,确保对象创建时所有依赖已就绪,避免不完整状态。

    • 不可变性:依赖可声明为final,实现不可变对象,提升线程安全性。

  • Setter注入

    • 灵活性:适合可选依赖或需要动态修改的依赖(如配置热更新)。

    • 风险:可能遗漏必需依赖的调用,导致运行时异常(如NPE)。

  • 自动注入

    • 隐式性:依赖关系由容器管理,减少显式配置,但可能隐藏依赖细节。

    • 歧义性:需处理多个同类型Bean的冲突(如结合@Qualifier@Primary)。


3. 代码与配置

  • 构造器注入

    • 配置:XML中使用<constructor-arg>,Java配置中直接传递参数。

    • 优点:明确依赖关系,适合依赖较多的场景(结合Builder模式优化)。

  • Setter注入

    • 配置:XML中使用<property>标签,或通过Setter方法调用。

    • 优点:更符合JavaBean规范,便于与其他框架(如JPA)整合。

  • 自动注入

    • 配置:使用注解(@Autowired@Resource)或XML的autowire属性。

    • 优点:简化配置,适合快速开发,但需谨慎处理循环依赖。


4. 循环依赖处理

  • 构造器注入:无法解决循环依赖(如A依赖B,B也通过构造器依赖A),会抛出BeanCurrentlyInCreationException

  • Setter/自动注入:允许通过“三级缓存”机制解决循环依赖(先创建对象,再注入依赖)。


5. Spring官方推荐

  • 构造器注入是首选方式(尤其对必需依赖),原因包括:

    • 明确依赖关系,避免NullPointerException

    • 支持不可变对象,提升线程安全性和代码健壮性。

    • 便于单元测试(直接通过构造函数传入Mock对象)。

  • Setter/自动注入适用于可选依赖或需要灵活修改的场景。


总结对比表

特性

构造器注入

Setter注入

自动注入

依赖强制性

强制(必需依赖)

可选

可配置(required属性)

不可变性

支持(final字段)

不支持

不支持

循环依赖处理

不支持

支持

支持

代码显式性

高(显式声明依赖)

中(需调用Setter)

低(容器隐式处理)

配置复杂度

高(依赖较多时)

适用场景

必需依赖、不可变对象

可选依赖、动态配置

快速开发、减少样板代码


选择建议

  • 优先使用构造器注入:确保依赖完整性和不可变性。

  • Setter注入用于可选依赖:如配置参数或需要动态更新的场景。

  • 谨慎使用自动注入:在简单场景中减少配置,但需避免歧义依赖。