文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

请不要再使用@Autowired/@Resource注解进行字段注入

2024-11-29 19:56

关注

我们可以通过三种方式注入依赖对象:

而这里的字段注入是使用 @Autowired 注解将依赖关系直接注入类中。虽然这可能是最简单的方法,但我们必须明白它可能会导致一些潜在的问题。而且,甚至Spring的官方文档也不再将字段注入列为依赖注入的选项之一。如下官方文档:

图片

2. 引发的问题

2.1 空安全(Null-Safety)

如果未正确初始化依赖关系,字段注入会产生 NullPointerException 风险。如下示例:

@Repository
public class PersonDAO {
}
@Service
public class PersonService {
  // @Autowired
  @Resource
  private PersonDAO dao ;
  public String toString() {
    return "PersonService [dao=" + dao + "]";
  }
}

在上面的示例中,只有容器中存在 PersonDAO 依赖项,PersonService才能正常工作。然而,在使用字段注入时,我们没有提供直接实例化 PersonService 所需的依赖关系的方法。

使用@Autowired时,启动容器会报错,这也还算好,如果我们如下使用:

@Autowired(required = false)

容器启动将正常,但是如果当调用方法时将出现NPE。这里如果你使用@Resource也会出现NPE问题。

此外,我们还可以使用默认构造函数创建 PersonService 实例:

PersonService ps = new PersonService() ;
ps.xxx() ;

这都将导致NPE问题。

对于这NPE问题,我们可以通过构造函数注入来降低NPE问题。

private final PersonDAO dao ;
public PersonService(PersonDAO dao) {
  this.dao = dao ;
}

通过这种方法,我们公开了所需的依赖关系。此外,我们现在要求客户提供必须的依赖项。换句话说,如果不提供 PersonDAO 实例,就无法创建新的 PersonService 实例。

2.2 不变性(Immutability)

使用字段注入,我们无法创建不可变类。

我们需要在声明或通过构造函数实例化最终字段。此外,一旦构造函数被调用,Spring 就会执行自动装配。因此,不可能使用字段注入方式来装配最终字段。

由于依赖关系是可变的,我们无法确保它们在初始化后保持不变。此外,重新分配非最终字段可能会在运行应用程序时产生意想不到的副作用。或者,我们可以对强制依赖关系使用构造器注入,对可选依赖关系使用setter注入。这样,我们就能确保所需的依赖关系保持不变。

2.3 设计问题

违反单一职责

作为SOLID原则的一部分,单一职责原则规定每个类应该只有一项职责。换句话说,一个类应该只负责一个操作,因此只有一个改变的理由。

当我们使用字段注入时,最终可能会违反单一责任原则。我们很容易添加超出必要的依赖关系,并创建一个身兼多职的类。

另一方面,如果我们使用构造函数注入,我们就会发现,如果一个构造函数有超过几个依赖关系,我们可能会遇到设计问题。此外,如果构造函数中的参数超过 7 个,甚至集成开发环境也会发出警告。

循环依赖问题

简单地说,当两个或多个类相互依赖时,就会出现循环依赖。由于存在这些依赖关系,因此无法构造对象,执行过程中可能会出现运行时错误或无限循环。

使用字段注入可能会导致循环依赖被忽视,如下示例:

@Component
public class A {
  @Autowired
  private B b ;
}
@Component
public class B {
  @Autowired
  private A a ;
}

这里两个类通过字段注入,形成循环依赖。这样运行不会有问题也就是并不会抛出BeanCurrentlyInCreationException 异常;简单说,对于这种循环依赖其实是一种设计的缺陷我们应该避免,但是这里并没有表现出来。

注意:在Spring环境下默认是允许循环依赖的。 

但是在Spring Boot环境并且从2.6版本开始循环依赖默认是不允许的,也就是上面的字段注入将抛出异常。

乘热打铁,接下来说说循环依赖的解决。

如果你将上面的依赖注入方式改成了构造函数,如下:

@Component
public class A {
  private B b ;
  public A(B b) {
    this.b = b ;
  }
}
@Component
public class B {
  private A a ;
  public B(A a) {
    this.a = a ;
  }
}

容器启动将抛出BeanCurrentlyInCreationException 异常;对于这种构造函数的循环依赖是可以通过@Lazy解决。如下示例:

// 构造函数上添加注解或者是在参数加都可以
// @Lazy
public A(@Lazy B b) {
  this.b = b ;
}

只要在任意一端添加了@Lazy都可解决。

最后,我们可以使用构造器注入来代替字段注入,构造器注入是必需的,而setter注入则是可选的。

来源:Spring全家桶实战案例源码内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯