如何正确地进行依赖注入(在Spring中)?

Joa*_*sta 26 java spring dependency-injection

我怀疑使用Spring将对象注入到类中.我在我的项目中使用了这种代码:

@Resource // or @Autowired even @Inject
private PersonRepository personRepository;
Run Code Online (Sandbox Code Playgroud)

然后在方法上正常使用它:

personRepository.save(p);
Run Code Online (Sandbox Code Playgroud)

否则我在Spring示例中找到了注入构造函数:

private final PersonRepository personRepository;

@Autowired
public PersonController(PersonRepository personRepository) {
  this.personRepository = personRepository;
}
Run Code Online (Sandbox Code Playgroud)

那两个都是正确的?或者每个都有它的属性和用法?

Oli*_*ohm 72

tl; dr - 构造函数注入是进行DI的最佳方式

后者是正确的,这不是因为Spring或任何依赖注入容器,而是面向对象的类设计原则.

细节

应该设计一个类型,以便您只能从中创建处于有效状态的实例.为实现此目的,该类型的所有必需依赖项都需要是构造函数参数.这意味着,可以null检查这些依赖项,将其分配给最终字段以提升不变性.除此之外,在处理代码时,对于该实例的调用者(或创建者)来说,它必须立即显示它必须提供哪些依赖关系(通过浏览API文档或使用IDE中的代码完成).

所有这些都不可能通过现场注入.你没有从外面看到依赖关系,你需要一些黑魔法来注入依赖关系,你永远不能确定它们不是null你盲目地信任容器.

实际上,最后但并非最不重要的方面是,通过字段注入,向类中添加大量依赖项不那么痛苦,这本身就是一个设计问题.随着构造函数变得更加痛苦,这是一件好事,因为它告诉你关于你的类设计的一些事情:这个类有太多的责任.首先,无需计算其上的指标,当您尝试扩展它时,您会感觉到它.

集装箱

人们经常争辩说这只是学术上的废话,因为你无论如何都可以依赖于容器.这是我对此的看法:

  • 仅仅因为存在容器,并不意味着你必须抛弃所有基本的面向对象设计原则,是吗?即使存在止汗剂,你仍然会洗澡,对吧?

  • 甚至设计用于容器的类型也将手动使用:在单元测试中.如果你不编写单元测试......那么那是另一个主题.

  • 所谓的额外构造函数的冗长("我可以通过单行注入!!来实现同样的目标!" - "不,你不能.你实际上可以通过编写一行代码来获得更多东西.")可以减轻像龙目岛这样的东西.使用Spring和Lombok注入构造函数的组件如下所示:

    @Component
    @RequiredArgsConstructor
    class MyComponent implements MyComponentInterface {
    
      private final @NonNull MyDependency dependency;
    
      …
    }
    
    Run Code Online (Sandbox Code Playgroud)

Lombok将负责生成一个构造函数,为每个final字段获取一个参数,并null在分配之前检查给定的参数.因此,您可以有效地获得现场注入的简洁性和构造注入的设计优势.

结语

我最近与一些非Java人员进行了讨论,我对构造函数DI使用术语"注入"感到非常困惑.实际上,他们认为 - 并且有很多事实 - 通过构造函数传递依赖关系根本不是注入,因为它是将对象交给其他人的最自然的方式(与任何种类的注入形成鲜明对比).

也许我们应该为这种风格注入不同的术语?依赖性喂养,也许?

资源