Java 依赖注入:XML 或注释
注释变得流行。 Spring-3 支持它们。 CDI 严重依赖它们(我不能在没有注释的情况下使用 CDI,对吗?)
我的问题是为什么?
我听到了几个问题:
“它有助于摆脱 XML”。但是 xml 有什么不好呢?依赖关系本质上是声明性的,而 XML 非常适合声明(但不适合命令式编程)。 有了好的 IDE(比如 idea),编辑和验证 xml 就很容易了,不是吗?
“在许多情况下,每个接口只有一个实现”。那不是真的! 我的系统中几乎所有接口都有用于测试的模拟实现。
还有其他问题吗?
现在我对 XML 的优点是:
您可以在任何地方注入任何内容(不仅是具有注释的代码)
如果我有一个接口的多个实现?使用限定符?但这迫使我的班级知道需要什么样的注射。 不利于设计。
基于 XML 的 DI 使我的代码变得清晰:每个类都不知道注入,因此我可以以任何方式配置它并对其进行单元测试。
你怎么认为?
Annotations becoming popular. Spring-3 supports them. CDI depends on them heavily (I can not use CDI with out of annotations, right?)
My question is why?
I heard several issues:
"It helps get rid of XML". But what is bad about xml? Dependencies are declarative by nature, and XML is very good for declarations (and very bad for imperative programming).
With good IDE (like idea) it is very easy to edit and validate xml, is not it?"In many cases there is only one implementation for each interface". That is not true!
Almost all interfaces in my system has mock implementation for tests.
Any other issues?
And now my pluses for XML:
You can inject anything anywhere (not only code that has annotations)
What should I do if I have several implementations of one interface? Use qualifiers? But it forces my class to know what kind of injection it needs.
It is not good for design.
XML based DI makes my code clear: each class has no idea about injection, so I can configure it and unit-test it in any way.
What do you think?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
我只能根据 Guice 的经验来谈谈,但这是我的看法。缺点是基于注释的配置大大减少了将应用程序连接在一起所需编写的代码量,并且可以更轻松地更改依赖于什么的内容......通常甚至无需触及配置文件本身。它通过使最常见的情况变得绝对微不足道来实现这一点,但代价是使某些相对罕见的情况稍微难以处理。
我认为过于教条地让课程“不知道注入”是一个问题。类的代码中不应引用注入容器。我绝对同意这一点。然而,我们必须明确一点:注释不是代码。它们本身并没有改变类的行为方式......您仍然可以使用注释创建类的实例,就好像它们根本不存在一样。所以你可以完全停止使用 DI 容器并将注释保留在那里,不会有任何问题。
当您选择不提供有关类内注入的元数据提示(即注释)时,您就放弃了有关该类需要哪些依赖项的宝贵信息源。您被迫要么在其他地方(例如,在 XML 中)重复该信息,要么依赖不可靠的魔法(例如自动装配),这可能会导致意外问题。
解决您的一些具体问题:
它有助于摆脱 XML
XML 配置有很多不好的地方。
也就是说,我知道很多人已经使用 XML 足够长的时间了,他们相信 XML 就很好,而且我真的不希望改变他们的想法。
在许多情况下,每个接口只有一种实现
对于应用程序(例如生产)的单个配置,每个接口通常只有一种实现。要点是,在启动应用程序时,通常只需将接口绑定到单个实现。然后它可以用于许多其他组件。使用 XML 配置,您必须告诉每个使用该接口的组件使用该接口的这一特定绑定(或“bean”,如果您愿意)。使用基于注释的配置,您只需声明一次绑定,其他所有事情都会自动处理。这非常重要,并且极大地减少了您必须编写的配置量。这也意味着当您向组件添加新的依赖项时,通常无需更改任何配置!
您拥有某些接口的模拟实现是无关紧要的。在单元测试中,您通常只需创建模拟并自己传递它......它与配置无关。如果您使用模拟来设置一个完整的系统来进行某些接口的集成测试......这不会改变任何东西。对于系统的集成测试运行,您仍然只使用 1 个实现,并且只需配置一次。
XML:您可以在任何地方注入任何内容
您可以在 Guice 中轻松完成此操作,我想您在 CDI 中也可以。因此,使用基于注释的配置系统并不是绝对可以阻止您执行此操作。也就是说,我敢说大多数应用程序中的大多数注入类都是您可以为自己添加
@Inject
的类(如果它尚不存在的话)。用于注释的轻量级标准 Java 库 (JSR-330) 的存在也使得未来更多的库和框架可以更轻松地为组件提供带有@Inject
带注释的构造函数。接口限定符的多个实现
是解决此问题的一种方法,并且在大多数情况下应该没问题。然而,在某些情况下,您确实想要做一些在特定注入类中的参数上使用限定符不起作用的事情……通常是因为您希望拥有该类的多个实例,每个实例都使用不同的接口实现或实例。 Guice 通过称为
PrivateModule
的东西解决了这个问题。我不知道CDI在这方面提供什么。但同样,这种情况只占少数,只要您能够处理它,就不值得让您的其他配置受到影响。I can only speak from experience with Guice, but here's my take. The short of it is that annotation-based configuration greatly reduces the amount you have to write to wire an application together and makes it easier to change what depends on what... often without even having to touch the configuration files themselves. It does this by making the most common cases absolutely trivial at the expense of making certain relatively rare cases slightly more difficult to handle.
I think it's a problem to be too dogmatic about having classes have "no idea about injection". There should be no reference to the injection container in the code of a class. I absolutely agree with that. However, we must be clear on one point: annotations are not code. By themselves, they change nothing about how a class behaves... you can still create an instance of a class with annotations as if they were not there at all. So you can stop using a DI container completely and leave the annotations there and there will be no problem whatsoever.
When you choose not to provide metadata hints about injection within a class (i.e. annotations), you are throwing away a valuable source of information on what dependencies that class requires. You are forced to either repeat that information elsewhere (in XML, say) or to rely on unreliable magic like autowiring which can lead to unexpected issues.
To address some of your specific questions:
It helps get rid of XML
Many things are bad about XML configuration.
That said, I know a lot of people have been using XML for long enough that they are convinced that it is just fine and I don't really expect to change their minds.
In many cases there is only one implementation for each interface
There is often only one implementation of each interface for a single configuration of an application (e.g. production). The point is that when starting up your application, you typically only need to bind an interface to a single implementation. It may then be used in many other components. With XML configuration, you have to tell every component that uses that interface to use this one particular binding of that interface (or "bean" if you like). With annotation-based configuration, you just declare the binding once and everything else is taken care of automatically. This is very significant, and dramatically reduces the amount of configuration you have to write. It also means that when you add a new dependency to a component, you often don't have to change anything about your configuration at all!
That you have mock implementations of some interface is irrelevant. In unit tests you typically just create the mock and pass it in yourself... it's unrelated to configuration. If you set up a full system for integration tests with certain interfaces using mocks instead... that doesn't change anything. For the integration test run of the system, you're still only using 1 implementation and you only have to configure that once.
XML: You can inject anything anywhere
You can do this easily in Guice and I imagine you can in CDI too. So it's not like you're absolutely prevented from doing this by using an annotation-based configuration system. That said, I'd venture to say that the majority of injected classes in the majority of applications are classes that you can add an
@Inject
to yourself if it isn't already there. The existence of a lightweight standard Java library for annotations (JSR-330) makes it even easier for more libraries and frameworks to provide components with an@Inject
annotated constructor in the future, too.More than one implementation of an interface
Qualifiers are one solution to this, and in most cases should be just fine. However, in some cases you do want to do something where using a qualifier on a parameter in a particular injected class would not work... often because you want to have multiple instances of that class, each using a different interface implementation or instance. Guice solves this with something called
PrivateModule
s. I don't know what CDI offers in this regard. But again, this is a case that is in the minority and it's not worth making the rest of your configuration suffer for it as long as you can handle it.我有以下原则:配置相关的bean是用XML定义的。其他一切 - 带注释。
为什么?因为您不想更改类中的配置。另一方面,在要启用的类中编写
@Service
和@Inject
则要简单得多。这不会以任何方式干扰测试 - 注释只是容器解析的元数据。如果您愿意,可以设置不同的依赖项。
至于 CDI - 它有 XML 配置的扩展,但你是对的,它主要使用注释。不过,这是我不太喜欢的一点。
I have the following principle: configuration-related beans are defined with XML. Everything else - with annotations.
Why? Because you don't want to change configuration in classes. On the other hand, it's much simpler to write
@Service
and@Inject
, in the class that you want to enable.This does not interfere with testing in any way - annotations are only metadata that is parsed by the container. If you like, you can set different dependencies.
As for CDI - it has an extension for XML configuration, but you are right it uses mainly annotations. That's something I don't particularly like in it though.
在我看来,这更多是一个品味问题。
1) 在我们的项目(使用 Spring 3)中,我们希望 XML 配置文件就是这样的:configuration。如果不需要配置(从最终用户的角度来看)或者某些其他问题不强制它在 xml 中完成,则不要将 bean 定义/接线放入 XML 配置中,请使用 @Autowired等等。
2)使用Spring,您可以使用@Qualifier来匹配接口的某个实现(如果存在多个)。是的,这意味着您必须命名实际的实现,但我不介意。
在我们的例子中,使用 XML 来处理所有 DI 会使 XML 配置文件膨胀很多,尽管它可以在单独的 xml 文件(或多个文件)中完成,所以这不是有效点;)。正如我所说,这是一个品味问题,我只是认为通过注释处理注入更容易、更干净(你可以通过查看类而不是通过 XML 文件来查看服务/存储库/任何东西使用的内容)寻找 bean 声明)。
编辑:这是我完全同意的关于 @Autowired 与 XML 的观点:Spring @Autowired 用法< /a>
In my opinion, this is more a matter of taste.
1) In our project (using Spring 3), we want the XML-configuration files to be just that: configuration. If it doesn't need to be configured (from end-user perspective) or some other issue doesn't force it to be done in xml, don't put the bean-definitions/wirings into the XML-configurations, use @Autowired and such.
2) With Spring, you can use @Qualifier to match a certain implementation of the interface, if multiple exist. Yes, this means you have to name the actual implementations, but I don't mind.
In our case, using XML for handling all the DI would bloat the XML-configuration files a lot, although it could be done in a separate xml-file (or files), so it's not that valid point ;). As I said, it's a matter of taste and I just think it's easier and more clean to handle the injections via annotations (you can see what services/repositories/whatever something uses just by looking at the class instead of going through the XML-file looking for the bean-declaration).
Edit: Here's an opinion about @Autowired vs. XML that I completely agree with: Spring @Autowired usage
正如您所指出的,我喜欢保持代码清晰。 XML 在 IOC 原则方面表现得更好,至少对我来说是这样。
同样,这只是我的观点,其中没有原教旨主义:)
编辑: 一些有用的讨论:
I like to keep my code clear, as you pointed. XML feets better, at least for me, in the IOC principle.
Again, it just my point of view, no fundamentalism in there :)
EDIT: Some useful discussions out there:
“但是 xml 有什么不好呢?”这是另一个需要管理的文件,也是另一个必须去寻找错误的地方。如果您的注释就在代码旁边,那么管理和调试就会容易得多。
"But what is bad about xml?" It's yet another file to manage and yet another place to have to go look for a bug. If your annotations are right next to your code it's much easier to mange and debug.
像所有事情一样,依赖注入应该适度使用。此外,所有注入陷阱都应与应用程序代码隔离,并归入与 main 相关的代码。
一般来说,应用程序应该有一个边界,将抽象应用程序代码与具体实现细节分开。所有跨越该边界的源代码依赖项都应指向应用程序。我将该边界的具体一侧称为主分区,因为那是“主”(或等效的)应该存在的地方。
主要分区由工厂实现、策略实现等组成。依赖注入框架应该在边界的这一侧完成它的工作。然后,这些注入的依赖项可以通过正常方式跨边界传递到应用程序中。 (例如作为参数)。
注入的依赖项数量应该相对较少。十几个或更少。在这种情况下,选择 XML 还是注释就没有意义了。
Like all things, dependency injection should be used in moderation. Moreover, all trappings of the injections should be segregated from the application code and relegated to the code associated with main.
In general applications should have a boundary that separates the abstract application code from the concrete implementation details. All the source code dependencies that cross that boundary should point towards the application. I call the concrete side of that boundary, the main partition, because that's where 'main' (or it's equivalent) should live.
The main partition consists of factory implementations, strategy implementations, etc. And it is on this side of the boundary that the dependency injection framework should do it's work. Then those injected dependencies can be passed across the boundary into the application by normal means. (e.g. as arguments).
The number of injected dependencies should be relatively small. A dozen or less. In which case, the decision between XML or annotations is moot.
另外不要忘记 Spring JavaConfig.
Also don't forget Spring JavaConfig.
就我而言,编写应用程序的开发人员与配置应用程序的开发人员不同(不同的部门、不同的技术/语言),最后一组甚至无法访问源代码(许多企业设置中都是这种情况)。这使得 Guice 无法使用,因为我必须公开源代码,而不是使用实现应用程序的开发人员配置的 xml。
总的来说,我认为重要的是要认识到提供组件和组装/配置应用程序是两个不同的练习,并在需要时提供这种关注点分离。
In my case the developers writing the application are different that the ones configuring it (different departments, different technologies/languages) and the last group doesn't even has access to the source code (which is the case in many enterprise setups). That makes Guice unusable since I would have to expose source code rather than consuming the xmls configured by the developers implementing the app.
Overall I think it is important to recognize that providing the components and assembling/configuring an application are two different exercises and provide if needed this separation of concerns.
我只是要在已有内容的基础上添加一些内容。
对我来说,DI 配置就是代码。我想这样对待它,但 XML 的本质阻止了这种情况,无需额外的工具。
Spring JavaConfig 在这方面向前迈出了一大步,但它仍然存在一些复杂性。组件扫描、接口实现的自动魔术选择以及 @Configuration 注解类的 CGLIB 拦截的语义使其比需要的更加复杂。但它仍然比 XML 向前迈出了一步。
将 IoC 元数据与应用程序对象分离的好处被夸大了,尤其是对于 Spring。如果您仅将自己限制在 Spring IoC 容器中,也许这就是事实。但 Spring 提供了构建在 IoC 容器(安全、Web MVC 等)上的广泛应用程序堆栈。一旦你利用其中任何一个,你就与容器绑定在一起了。
I just have a couple of things to add to what's already here.
To me, DI configuration is code. I would like to treat it as such, but the very nature of XML prevents this without extra tooling.
Spring JavaConfig is a major step forward in this regard, but it still has complications. Component scanning, auto-magic selection of interface implementations, and semantics around CGLIB interception of @Configuration annotated classes make it more complex than it needs to be. But it's still a step forward from XML.
The benefit of separating IoC metadata from application objects is overstated, especially with Spring. Perhaps if you confined yourself to the Spring IoC container only, this would be true. But Spring offers a wide application stack built on the IoC container (Security, Web MVC, etc). As soon as you leverage any of that, you're tied to the container anyway.
XML 的唯一好处是声明式样式,它的定义与应用程序代码本身明确分开。这与 DI 问题无关。缺点是冗长、重构稳健性差以及一般的运行时故障行为。与 IDE 对 Java 等的支持相比,只有通用(XML)工具支持,没有什么好处。此外,这种 XML 会带来性能开销,因此它通常比代码解决方案慢。
注释通常被认为在重构应用程序代码时更加直观和健壮。他们还受益于 guice 提供的更好的 IDE 指导。但他们将应用程序代码与 DI 问题混合在一起。应用程序依赖于框架。完全分离几乎是不可能的。当依赖于其他情况(例如机器人腿问题)描述同一位置(构造函数、字段)的不同注入行为时,注释也受到限制。此外,它们不允许将外部类(库代码)视为您自己的源代码。因此它们被认为比 XML 运行得更快。
这两种技术都有严重的缺点。因此我建议使用Silk DI。它是在代码中声明性定义的(强大的 IDE 支持),但与应用程序代码 100% 分离(无框架依赖性)。它允许对所有代码进行相同的处理,无论它是来自您的源代码还是外部库。像机器人腿问题这样的问题很容易用普通的绑定来解决。此外,它还具有良好的支持,可以使其适应您的需求。
XML has the only benefit of a declarative style that is defined clearly separated from the application code itself. That stays independent from DI concerns. The downsides are verbosity, poor re-factoring robustness and a general runtime failure behaviour. There is just a general (XML) tool support with little benefit compared to IDE support for e.g. Java. Besides this XML comes with a performance overhead so it usually is slower than code solutions.
Annoations often said to be more intuitive and robust when re-factoring application code. Also they benefit from a better IDE guidance like guice provides. But they mix application code with DI concerns. An application gets dependent on a framework. Clear separation is almost impossible. Annotations are also limited when describing different injection behaviour at the same place (constructor, field) dependent on other circumstances (e.g. robot legs problem). Moreover they don't allow to treat external classes (library code) like your own source. Therefore they are considered to run faster than XML.
Both techniques have serious downsides. Therefore I recommend to use Silk DI. It is declarative defined in code (great IDE support) but 100% separated from your application code (no framework dependency). It allows to treat all code the same no matter if it is from your source or a external library. Problems like the robot legs problem are easy to solve with usual bindings. Furthermore it has good support to adapt it to your needs.