@Autowired 注入三种方式

时间:2025-03-25 19:56:45

问题

在使用 IDEA 开发 SpringBoot 项目时,在Controller类中使用注解 @Autowired 注入一个依赖出现了警告提示。这是怎么回事?
当我们鼠标停留在警告线处会出现提示:Field injection is not recommended(不建议Field注入)

Spring Bean 的注入方式

1.变量(Field

@RestController
@RequestMapping(value = "/test")
public class ParkMapController{
    @Autowired
    private ParkMapService parkMapService;
}

优点:
代码少,简洁明了
新增依赖十分方便,不需要修改原有代码
注入简单,只需要使用 @Autowired 注解或者 @Resource 注解
缺点:
容易出现空指针异常,Field 注入允许构建对象实例的时候依赖的示例对象为空,这就导致了空指针异常无法尽早的暴露出来,因为你不调用将一直无法发现NullPointException的存在
对于IOC容器以外的环境,除了使用反射来提供它需要的依赖之外,无法复用该实现类。对单元测试不友好,如果使用 Field 注入,那么进行单元测试就需要初始化整个Spring 环境,将所有 Bean 实例化
使用field注入会出现循环依赖的隐患
容易破坏单一职责原则

2.构造器注入

@RestController
@RequestMapping(value = "/valet/parking/")
public class CommonController
{
    private final CommonService commonService;
 
    @Autowired
    public CommonController(CommonService commonService)
    {
         = commonService;
    }
}

优点:

保证依赖不可变(final关键字)。

保证依赖不为空,强依赖处理,在编译阶段就能暴露出问题(省去了我们对其检查)。

保证返回客户端(调用)的代码的时候是完全初始化的状态,方便单元测试。

避免了循环依赖。

提升了代码的可复用性。

可以明确成员变量的注入顺序。

缺点:当注入参数较多时,代码臃肿,不够友好。

注入

@RestController
@RequestMapping(value = "/valet/parking/task/")
public class ParkTaskController
{
    private ParkTaskService parkTaskService;
 
    @Autowired
    public void setParkTaskService(ParkTaskService parkTaskService) {
         = parkTaskService;
    }
}

优点:

  1. 相比构造器注入,set注入类似于选择性注入。
  2. 允许在类构造完成后重新注入。

缺点:暂无

构造函数注入 vs Setter注入

构造函数注入:
构造函数注入对于强制依赖项是有好处的。对象正常运行所需的依赖项通过在构造函数中提供这些参数,可以确保在构造对象时就可以使用它。构造函数中分配的字段也可以是final,允许对象是完全不可变的,或者至少保护其必需的字段。
使用构造函数提供依赖关系的一个后果是,以这种方式构造的两个对象之间不再可能存在循环依赖关系(与setter注入不同)。这实际上是一件好事,而不是一种限制,因为应该避免循环依赖,这通常是糟糕设计的标志。这样就可以避免这种做法。
另一个优点是,如果使用spring 4.3+,可以完全将类与DI框架解耦。原因是Spring现在支持一个构造函数场景的隐式构造函数注入。这意味着不再需要在类中使用DI注解。当然,也可以通过在spring配置中为给定的类显式配置DI来实现相同的目的,这只是使整个过程变得更加简单。

官方建议从开始,Spring文档鼓励使用构造函数注入也没有否认setter注入的好处:

Setters:
使用setter注入依赖项。类应该能够在不提供它们的情况下工作。在对象实例化之后,可以随时更改依赖项。根据具体情况,这可能不是一个优势。有时,拥有一个不可变的对象是可取的。有时在运行时更改对象的协作者是很好的——例如JMX托管的mbean。

官方建议从开始。文档鼓励使用setter而不是构造函数:

结论

应尽量避免Field注入。推荐使用构造函数或方法来注入依赖项。两者各有利弊,其用法取决于具体情况。但是,由于这些方法可以混合使用,所以这不是非必须选择一种,可以将setter和构造函数注入合并到一个类中。构造函数更适合于强制依赖项和以不变性为目标的情况。对于可选的依赖项,setter更好。