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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24337486/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 11:37:10  来源:igfitidea点击:

How to properly do dependency injection (in Spring)?

javaspringdependency-injection

提问by Joao Evangelista

I have a doubt about injecting objects to a class using Spring. I used in my projects this kind of code:

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

@Resource // or @Autowired even @Inject
private PersonRepository personRepository;

then used it normally on methods as :

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

personRepository.save(p);

Otherwise I found on Spring examples, injecting the constructor:

否则我在 Spring 示例中发现,注入构造函数:

private final PersonRepository personRepository;

@Autowired
public PersonController(PersonRepository personRepository) {
  this.personRepository = personRepository;
}

So both are correct? Or each on has its properties and usages?

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

采纳答案by Oliver Drotbohm

tl;dr - Constructor injection is the best way of doing DI

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

The latter is correct and this is not so much because of Spring or any dependency injection container, but object-oriented class design principles.

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

Details

细节

A type should be designed, so that you can only create instances from it, that are in a valid state. To achieve this, all mandatory dependencies of that type needto be constructor arguments. This means, these dependencies can be null-checked, assigned to final fields to promote immutability. Beyond that, when working with the code, for the caller (or creator) of that instance it's immediately obvious which dependencies it has to provide (through either skimming through the API docs or using code completion in the IDE).

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

All of this is impossible with field injection. You don't see the dependencies from the outside, you requiresome black magic to inject the dependencies and you can never be sure they're not nullexcept you blindly trust the container.

所有这一切对于场注入都是不可能的。您不会从外部看到依赖项,您需要一些黑魔法来注入依赖项,null除非您盲目信任容器,否则您永远无法确定它们不是。

The last but not least important aspect actually is, that with field injections, it's less painful to add a lot of dependencies to your class, which is a design problem in itself. With constructors that becomes much more painful much earlier which is a good thingas it tells you something about your class design: the class has too many responsibilities. No need to calculate metrics on it in the first place, you feel it when you try to extend it.

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

Containers

容器

People often argue that that's just academic bullshit as you can rely on the container anyway. Here's my take on this:

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

  • Just because a container exists, it doesn't mean you have to throw all basic object-oriented design principles over board, does it? You still take a shower, even if antiperspirants exist, right?

  • Even types designed for usage with a container, will beused manually: in unit tests. If you don't write unit tests… well that's another topic then.

  • The alleged verbosity of an additional constructor ("I can achieve the same thing with a single line of field injection!!" - "No you can't. You actually getstuff from writing a line of code more.") can be mitigated by things like Lombok. A constructor injected component with Spring and Lombok looks like this:

    @Component
    @RequiredArgsConstructor
    class MyComponent implements MyComponentInterface {
    
      private final @NonNull MyDependency dependency;
    
      …
    }
    
  • 仅仅因为存在一个容器,并不意味着您必须抛弃所有基本的面向对象设计原则,不是吗?即使存在止汗剂,您仍然会洗澡,对吗?

  • 即使是设计用于容器的类型,也会手动使用:在单元测试中。如果您不编写单元测试……那么那就是另一个话题了。

  • 所谓的额外构造函数的冗长(“我可以通过单行字段注入实现同样的事情!!” - “不,你不能。你实际上从编写一行代码中得到了更多的东西。”)可以得到缓解。像龙目岛这样的东西。带有 Spring 和 Lombok 的构造函数注入组件如下所示:

    @Component
    @RequiredArgsConstructor
    class MyComponent implements MyComponentInterface {
    
      private final @NonNull MyDependency dependency;
    
      …
    }
    

Lombok will take care of generating a constructor taking a parameter for each final field and check the given parameter for nullbefore assigning it. So you effectively get the brevity of field injection and the design advantages of constructor injection.

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

Epilogue

结语

I been part of a discussion recently with some non-Java folks that I terribly puzzled by using the term "injection" for constructor DI. Effectively, they argued - and there's a lot of truth to that - that passing dependencies through a constructor is not injection at all, as it's the most natural way to hand objects into others (in stark contrast to injections of any sorts).

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

Maybe we should coin a different term for that kind of style? Dependency feeding, maybe?

也许我们应该为这种风格创造一个不同的术语?依赖喂养,也许?

Resources

资源

回答by Ryan McGuinness

Academically speaking I agree that constructors are simply a better way to create objects. However the java beans specification is built on the premis of mutators to ease reflection. Too many tools and frameworks have been built around that ease of access paradigm. For services, DAOs and other singleton scenarios I believe only constructor injection should be used as mutators break the age old rule "only friends can see your private parts".

从学术上讲,我同意构造函数只是创建对象的更好方法。然而,java bean 规范建立在 mutator 的前提上以简化反射。围绕这种易于访问的范例构建了太多工具和框架。对于服务、DAO 和其他单例场景,我认为应该只使用构造函数注入作为增变器,这打破了“只有朋友才能看到您的私人部分”的古老规则。