Spring 依赖注入的三种方式优缺点

前言

在前面的文章中介绍了基于注解的方式将Bean存储到Spring中, 接下来介绍如何基于注解的方式从Spring中取对象, 也就是实现DI依赖注入. 本篇文章将会介绍三种依赖注入的方式, 分别是属性注入, Setter注入构造方法注入. 并介绍其各自优缺点.

属性注入

属性注入要用到注解@Autowired

@Controller
public class UseController {
    @Autowired
    private UseService useService;
    public void print(){
        System.out.println("do_useController");
        useService.print();
    }
}

使用@Autowired自动注入, 就是去Spring容器中找到UseService类的Bean对象. 如果Bean对象只有一个, 则直接将对象注入给useService引用. 如果Bean对象有多个, 则根据引用的名字来进行匹配.

在这里插入图片描述

1. 属性注入的优点

属性注入最大的优点就是使用简单, 只需要添加一个@Autowited注解即可.

2. 属性注入的缺点

  1. 属性注入不能注入给一个final修饰的引用.
    在这里插入图片描述原因是final修饰的变量必须要在定义时初始化, 或者在构造方法中对其进行初始化.
  2. 属性注入的通用性差, 只能用于IoC容器中.
  3. 属性注入违背单一设计原则的可能性大
    所谓单一设计原则, 就是一个类只用来完成一个功能.
    属性注入的方式使用起来非常简单, 因此程序员很可能在一个类中注入多个对象. 从而使得这个类的单一设计原则被破坏. 因此说属性输入会使程序违背单一设计原则的概率加大.

Setter注入

@Controller
public class UseController {
    private UseService useService;
    @Autowired
    public void setUseService(UseService useService){
        this.useService = useService;
    }
    public void print(){
        System.out.println("do_useController");
        useService.print();
    }
}

使用setter注入, 通过@Autowired从Spring中取出UseService Bean, 作为实参传给setUseService, 然后在setUseService中将其赋值给引用.

Setter注入的优点

在Setter注入中, 每个Setter只针对一个Bean, 所以他符合单一设计原则.

Setter注入的缺点

  1. 不能注入给一个final修饰的引用
    在这里插入图片描述

final修饰的变量必须要在定义时初始化, 或者在构造方法中对其进行初始化.

  1. 注入的对象可被修改.
    在程序执行时, 通过调用setUseService方法, 可以对注入的对象进行修改.

构造方法注入

@Controller
public class UseController {
    private UseService useService;
    @Autowired
    public UseController(UseService useService) {
        this.useService = useService;
    }
    public void print(){
        System.out.println("do_useController");
        useService.print();
    }

}

如果类中只有一个构造方法时, @Autowired可以省略. 这是因为Spring官方推荐构造方法的使用, 所以在底层会自动实现@Autowired

1. 构造方法的优点

  1. 可以注入给final修饰的引用
    在这里插入图片描述

  2. 符合单一设计原则
    在一次注入中, 只针对一个Bean.

  3. 注入对象不会被改变
    由于构造方法只会执行一次, 所以注入的对象不会被改变

  4. 注入对象会被完全初始化
    因为依赖对象的传递是在构造方法中执行的, 而构造方法是在对象创建之初执行的, 所以构造方法注入确保在对象被创建的时候,所有必要的依赖关系都被传递进来并初始化。

举例来说,如果有一个类 A, 它的构造方法接受一个类 B 的对象作为参数, 那么在创建 A 的对象时, 类 B的对象就是被传递的依赖关系, 并且在 A 的构造方法中对 B 的对象进行了初始化. 这确保了在使用 A 的对象时, A 依赖的对象 B是已经准备好并初始化的。

  1. 通用性更好
    构造方法注入可适用非 IoC 框架. 对于IoC框架和非IoC框架. 构造方法注入的代码都是通用的, 所以它的通用性更好.



总结

综上, 依赖注入的实现方式有以上三种. 分别是属性注入, Setter注入和构造方法注入.

属性注入的写法最简单, 使用频率最高. 但缺点也很明确.
Setter注入适用于注入可变对象的场景.
构造方法注入是Spring官方最推荐的注入方法. 明显优势是可以注入对象给final修饰的引用. 通用性更好.

补充

在进行类注入时, 除了使用@Autowired注解之外, 还可以使用@Resource进行注入.

@Aurowired注解和@Resource注解的区别

  1. 来源不同
    @Autowired注解是Spring框架提供的. 而Resource是JDK提供的, 可以在非Spring的环境下使用
  2. 参数不同
    相比于@Autowired来说, @Resource支持更多的参数配置
    如Resource可以通过name参数来指定具体的Bean名称.
    但是@Autowired可以使用另一个注解@Qualifier注解来指定具体的Bean名称

比如现在有Dog和Cat两个类都实现了Animal接口. 当要依赖注入一个Animal Bean时, 可以通过@Qualifier注解来显式指定要注入的是哪个Bean(注意: @Qualifier不适合于构造方法注入)

@Component
public class Zoo {
    @Autowired
    @Qualifier("dog")
    private Animal animal;

}
  1. 查找Bean方式不同
    @Autowired查找Bean会先根据类型(类名)进行查找, 如果一个类型中有多个Bean, 则会根据对象名来进行匹配.
    @Resource查找Bean则是先根据对象名来进行查找, 然后再根据类型来进行查找
    也可以参考上面参数不同里面的方式进行查找设置.
  2. 使用范围不同
    @Autowired可以用于以上三种注入方式
    但@Resource不适用于构造方法注入.(@Resource@Qualifier都不适合于构造注入)
  3. 可选性不同
    @Autowired 可以设置非非必需, 即如果找不到匹配的Bean, 属性可以为null. 但可以使用@Autowire(required = true)来设置为必需的(默认为必须).
    @Resource 默认是必需的, 不支持可选性. 如果找不到匹配的Bean, 会抛出异常.
    在这里插入图片描述

总而言之, @Autowired 更为灵活, 而 @Resource 更加标准化, 可以跨平台使用.

<think>好的,我现在需要帮助用户了解Spring框架中的依赖注入方法及其优缺点。首先,我得回忆一下用户提供的引用内容,确保信息准确。根据引用[1]、[2]、[3]、[4],Spring主要有三种依赖注入方式:属性注入、Setter注入和构造方法注入。 首先,属性注入。引用[1]提到属性注入使用@Autowired注解直接标注在字段上,代码简洁,但可能引发空指针异常,因为依赖未完全初始化就被使用。同时,引用[4]指出构造方法注入Spring 4.x之后推荐的方式,而属性注入在开发中仍然常用。这里需要比较不同注入方式优缺点。 接下来是Setter注入。引用[3]说明Setter方法允许可选依赖,适合需要改变依赖的情况。但用户需要确保调用setter方法,可能存在未初始化的风险。引用[2]中的例子展示了TestController依赖TestService,但没有具体说明注入方式,可能需结合其他引用分析。 然后是构造方法注入。引用[4]中的例子使用构造函数注入,确保依赖不可变,符合不可变原则,并且避免循环依赖问题。同时,引用[3]提到构造函数注入能保证对象在构造时就完全初始化,提高安全性。 用户可能想知道每种方法的具体应用场景和选择依据。需要总结各方法的优缺点,比如属性注入的简便性vs构造方法注入的安全性。还要注意复杂类型的注入,如集合,引用[3]提到可以通过配置实现。 需要检查是否有遗漏的注入方式,比如接口注入,但根据引用,Spring主要支持这三种。确保回答结构清晰,分点说明每种方法,再总结比较。最后生成相关问题,可能涉及实际应用、最佳实践或与其他框架对比。 现在组织内容:先概述三种方法,再分别详细说明优缺点,引用对应的引用来源,末尾添加相关问题。注意使用中文,符合格式要求,避免Markdown,使用$$公式的地方如果有的话,但问题中可能不需要,所以可能不涉及数学表达式。检查引用标注是否正确,如[^1]、[^2]等。</think>Spring框架中依赖注入(DI)主要通过三种方式实现:**属性注入**、**Setter注入**和**构造方法注入**。以下是它们的定义、优缺点及适用场景: --- ### 1. 属性注入(Field Injection) **定义**:通过`@Autowired`注解直接注入到类的属性字段。 **示例**: ```java @Controller public class UserController { @Autowired private UserService userService; } ``` **优点**: - **简洁性**:代码量少,适合快速开发。 - **灵活性**:无需显式编写构造方法或Setter方法。 **缺点**: - **不可变性问题**:字段被`final`修饰时无法使用(依赖必须在对象创建时注入)[^4]。 - **隐藏依赖关系**:类的外部无法直接感知依赖,可能引发空指针异常(依赖未正确注入时)。 - **测试困难**:需依赖Spring容器或反射工具注入依赖[^3]。 **适用场景**:小型项目或临时测试代码。 --- ### 2. Setter注入(Setter Injection) **定义**:通过Setter方法注入依赖,通常结合`@Autowired`注解。 **示例**: ```java @Controller public class OrderController { private OrderService orderService; @Autowired public void setOrderService(OrderService orderService) { this.orderService = orderService; } } ``` **优点**: - **可选依赖支持**:适合需要动态更换依赖的场景(如配置热更新)[^3]。 - **兼容性**:允许部分依赖初始化,避免完全耦合。 **缺点**: - **不一致状态风险**:对象可能在未完全初始化时被使用。 - **代码冗余**:需为每个依赖编写Setter方法。 **适用场景**:依赖可能变化的模块(如插件化系统)。 --- ### 3. 构造方法注入(Constructor Injection) **定义**:通过类的构造函数注入依赖,Spring 4.x后推荐的方式。 **示例**: ```java @Controller public class ProductController { private final ProductService productService; @Autowired public ProductController(ProductService productService) { this.productService = productService; } } ``` **优点**: - **不可变性**:配合`final`关键字确保依赖不可变,线程安全[^4]。 - **强契约性**:对象创建时即完成依赖注入,避免空指针异常[^3]。 - **显式依赖**:通过构造函数明确类的依赖关系,便于测试。 **缺点**: - **代码冗余**:需为每个依赖编写构造函数参数。 - **循环依赖问题**:若类A依赖类B,类B又依赖类A,可能导致启动失败。 **适用场景**:大型项目或需要高安全性的核心模块。 --- ### 总结对比 | 注入方式 | 代码简洁性 | 安全性 | 可测试性 | 适用场景 | |----------------|------------|--------|----------|------------------| | **属性注入** | 高 | 低 | 低 | 小型项目、快速开发 | | **Setter注入** | 中 | 中 | 中 | 动态依赖场景 | | **构造方法注入**| 低 | 高 | 高 | 大型项目、核心模块 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

With Order @!147

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值