java 为什么 Spring 不支持直接字段依赖注入(自动装配除外)?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5433143/
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-10-30 11:06:33  来源:igfitidea点击:

Why doesn't Spring support direct field dependency injection (except for autowired)?

javaspringdependency-injection

提问by Nakedible

I am interested in direct field dependency injection. Traditionally, Spring supports both constructor injection (supplying arguments to constructors) and setter-based injection (calling setters on a call).

我对直接字段依赖注入感兴趣。传统上,Spring 支持构造函数注入(向构造函数提供参数)和基于 setter 的注入(在调用时调用 setter)。

However, Spring is also capable of direct field injection (setting member fields of an object without a setter method), as evidenced by annotating fields with @Autowired. Autowiring is limited to just "beans", so primitive values cannot be injected (although this can somewhat be circumvented by creating beans of class "java.lang.String" - this works, but has the normal caveats of autowiring.) In addition to this, Spring supports @Valueto directly set values to member fields from properties etc.

但是,Spring 也能够直接字段注入(在没有 setter 方法的情况下设置对象的成员字段),如使用@Autowired. 自动装配仅限于“beans”,因此不能注入原始值(尽管这可以通过创建类“java.lang.String”的 bean 来规避 - 这可以工作,但具有自动装配的正常警告。)此外这一点,Spring 支持@Value从属性等直接为成员字段设置值。

Yet, Spring does not allow properties to be directly set to member fields (without autowiring).

然而,Spring 不允许将属性直接设置为成员字段(无需自动装配)。

My question is: why?

我的问题是:为什么?

It is obviously capable of doing so, so why doesn't it? Are there any big negative side-effects that prevent this? Or is the capability somehow limited so that only autowiring makes sense? Does it need some bigger hacks than calling setters?

它显然有能力这样做,那为什么不呢?是否有任何大的负面影响可以防止这种情况发生?或者功能是否受到某种限制,因此只有自动装配才有意义?它是否需要一些比调用 setter 更大的技巧?

Note that I do not wish to discuss the relative merits of having setters and getters in general, just the reasons why Spring has made this choice.

请注意,我不想讨论总体上拥有 setter 和 getter 的相对优点,只是讨论 Spring 做出这种选择的原因。

采纳答案by Daff

The @Autowired annotation uses reflection to make private fields accessible (see this related question). I can see three things why it isn't used in Spring configuration files.

@Autowired 注释使用反射使私有字段可访问(请参阅此相关问题)。我可以看到为什么它不在 Spring 配置文件中使用的三件事。

  1. Since configuration happens by bean properties (getters and setters) you can't really tell - in the likely case that both exist - if you want to e.g. call setValue or set the member value.
  2. It breaks encapsulation. Your Spring configuration has no reason to know about private member variables. With an annotation that's ok since it is already right there in the source code.
  3. Security concerns. A more restrictive security manager might not allow making private fields accessible via reflection.
  1. 由于配置是由 bean 属性(getter 和 setter)发生的,因此您无法真正判断 - 在可能的情况下两者都存在 - 如果您想调用 setValue 或设置成员值。
  2. 它打破了封装。您的 Spring 配置没有理由知道私有成员变量。有了注释就可以了,因为它已经在源代码中了。
  3. 安全问题。更严格的安全管理器可能不允许通过反射访问私有字段。

回答by Nakedible

I think I found the answer myself. I went over to the Spring source code and saw how the features were actually implemented. This is what I found:

我想我自己找到了答案。我查看了 Spring 源代码并查看了这些功能是如何实际实现的。这是我发现的:

Setting properties via XML is probably the oldest part of Spring, and it relies very heavily on "java.beans" classes for introspection, property enumeration, etc. And quite obviously, those do not support field introspection at all. On top of this is the type conversion machinery which determines how the property value can be converted to a suitable value for the property in question. There are no neatly separable pieces here.

通过 XML 设置属性可能是 Spring 最古老的部分,它非常依赖“java.beans”类进行自省、属性枚举等。很明显,这些类根本不支持字段自省。最重要的是类型转换机制,它确定如何将属性值转换为相关属性的合适值。这里没有整齐分开的部分。

All the @Autowired etc. stuff is implemented in a BeanPostProcessor, which has it's own type matching mechanic, which has nothing to do with the type conversion. That is also why it only injects beans. Same thing pretty much for @Value, it is just something that is resolved on the spot there and has nothing to do with properties.

所有@Autowired 等东西都在 BeanPostProcessor 中实现,它有自己的类型匹配机制,与类型转换无关。这也是它只注入 bean 的原因。对于@Value 来说,几乎相同的事情,它只是在那里当场解决的事情,与属性无关。

So, adding field injection support, for properties in particular, is not a trivial engineering effort as the parts of the code that do one or the other are pretty much completely separate.

因此,添加字段注入支持,尤其是对属性的支持,并不是一项微不足道的工程工作,因为执行一个或另一个的代码部分几乎完全独立。

This doesn't exactly answer "Why?", but I think this is a more compelling explanation as to why Spring hasn't added direct field dependency injection than the other explanations I've heard. Unless they have something fundamental against it (which I doubt, considering that they want to allow configuration of existing third party classes, not just JavaBeans), then it's just a matter of engineering effort to get the functionality in.

这并不能完全回答“为什么?”,但我认为这比我听到的其他解释更能解释为什么 Spring 没有添加直接字段依赖注入。除非他们有一些基本的反对它(我对此表示怀疑,考虑到他们希望允许配置现有的第三方类,而不仅仅是 JavaBeans),那么这只是获得功能的工程工作问题。

回答by steve_ash

It doessupport this through JSR-250 @Resource annotation (in Spring 3.0+)

确实通过 JSR-250 @Resource 注释支持这一点(在 Spring 3.0+ 中)

I personally prefer this to setter injection, and have mixed feelings with this when considering constructor injection. Here are the considerations that I have thought about FWIW:

我个人更喜欢这个而不是 setter 注入,并且在考虑构造函数注入时对此有复杂的感觉。以下是我对 FWIW 的考虑:

1) Constructor injection is a nice way to self-document your bean dependencies (pro) butcreates a lot of DRY violations: (a) private field, (b) constructor argument, (c) constructor code to set the field from the parameter, (d) the additional code in the bean configuration (either in @Configuration classes or in xml). That's a LOT of DRY violations just for the sake of some encapsulation purity, which I don't even care about. This is barely a violation of encapsulation -- but it does create a large dependency on someinjection container that respects JSR-250 annotations (see next)

1) 构造函数注入是一种自我记录 bean 依赖项的好方法 (pro),会产生很多 DRY 违规:(a) 私有字段,(b) 构造函数参数,(c) 构造函数代码以从参数设置字段, (d) bean 配置中的附加代码(在@Configuration 类中或在 xml 中)。这是很多 DRY 违规行为,只是为了某种封装纯度,我什至不在乎。这几乎没有违反封装——但它确实对某些尊重 JSR-250 注释的注入容器产生了很大的依赖(见下文)

2) Creates a dependency on JSR-250 compliant container. I have mixed feelings about this. When I first heard about @Resource I wrote it off saying it will make my system harder to test. However, I ended up using spring in my tests anyways. I can still use mock beans like I would anyways so it was never really a problem. And the question is outside of testing, when would you actually want to reuse this. In my mind if you're designing the system to take advantage of a container, then embrace it and use it. Are the dry violations actually worth the flexibility of not running inside of a container? At least with JSR-250 annotations you could run in any JEE6 environment and get injection like you want it.

2) 创建对 JSR-250 兼容容器的依赖。对此我五味杂陈。当我第一次听说@Resource 时,我把它写下来,说它会使我的系统更难测试。但是,我最终还是在测试中使用了 spring。我仍然可以像我一样使用模拟 bean,所以这从来都不是问题。问题是在测试之外,你什么时候真正想要重用它。在我看来,如果您正在设计系统以利用容器,那么请拥抱它并使用它。干式违规真的值得在容器内不运行的灵活性吗?至少使用 JSR-250 注释,您可以在任何 JEE6 环境中运行并按照您的需要进行注入。

3) Can create some not so great debugging scenarios if you instantiate one outside of the container: i.e. you will get null pointer exceptions instead of something nice. This is a trade-off. I personally think its not horrible -- I mean if you get a NPE on a line with a @Resource then you quickly realize it wasn't injected.

3) 如果您在容器外实例化一个,可以创建一些不太好的调试场景:即,您将获得空指针异常而不是一些好的东西。这是一种权衡。我个人认为它并不可怕——我的意思是如果你在一条线上得到一个带有@Resource 的 NPE,那么你很快就会意识到它没有被注入。