简体   繁体   English

Java依赖注入:XML或注释

[英]Java Dependency injection: XML or annotations

Annotations becoming popular. 注释变得流行。 Spring-3 supports them. Spring-3支持它们。 CDI depends on them heavily (I can not use CDI with out of annotations, right?) CDI严重依赖它们(我不能在没有注释的情况下使用CDI,对吧?)

My question is why ? 我的问题是为什么

I heard several issues: 我听到几个问题:

  1. "It helps get rid of XML". “它有助于摆脱XML”。 But what is bad about xml? 但是xml有什么不好的? Dependencies are declarative by nature, and XML is very good for declarations (and very bad for imperative programming). 依赖性本质上是声明性的,XML对于声明非常有用(对于命令式编程来说非常糟糕)。 With good IDE (like idea) it is very easy to edit and validate xml, is not it? 有了很好的IDE(就像想法一样),编辑和验证xml非常容易,不是吗?

  2. "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: 现在我的XML优势:

  1. You can inject anything anywhere (not only code that has annotations) 你可以在任何地方注入任何东西(不仅是带有注释的代码)

  2. 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. 基于XML的DI使我的代码清晰:每个类都不知道注入,所以我可以配置它并以任何方式对它进行单元测试。

What do you think? 你怎么看?

I can only speak from experience with Guice, but here's my take. 我只能用Guice的经验说话,但这是我的看法。 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. 所以你可以完全停止使用DI容器并在那里留下注释,这样就不会有任何问题。

When you choose not to provide metadata hints about injection within a class (ie 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. 您不得不在其他地方重复这些信息(例如XML),或依赖不可靠的魔法,如自动装配,这可能会导致意外问题。

To address some of your specific questions: 要解决您的一些具体问题:

It helps get rid of XML 它有助于摆脱XML

Many things are bad about XML configuration. XML配置很多都很糟糕。

  • It's terribly verbose. 这非常冗长。
  • It isn't type-safe without special tools. 没有特殊工具,它不是类型安全的。
  • It mandates the use of string identifiers. 它要求使用字符串标识符。 Again, not safe without special tool support. 同样,没有特殊工具支持也不安全。
  • Doesn't take any advantage of the features of the language, requiring all kinds of ugly constructs to do what could be done with a simple method in code. 不利用语言的功能,需要各种丑陋的构造来完成可以通过代码中的简单方法完成的任务。

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. 也就是说,我知道很多人已经使用XML足够长的时间,以至于他们确信它很好,我真的不希望改变他们的想法。

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 (eg 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). 使用XML配置,您必须告诉使用该接口的每个组件使用该接口的特定绑定(如果您愿意,还可以使用“bean”)。 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. 对于系统的集成测试运行,您仍然只使用1个实现,而您只需配置一次。

XML: You can inject anything anywhere XML:你可以随处注入任何东西

You can do this easily in Guice and I imagine you can in CDI too. 您可以在Guice中轻松完成此操作,我想您也可以在CDI中完成此操作。 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. 也就是说,我冒昧地说,大多数应用程序中的大多数注入类都是可以为自己添加@Inject类,如果它还没有。 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. 用于注释的轻量级标准Java库(JSR-330)的存在使得更多的库和框架在将来更容易为组件提供带有@Inject注释的构造函数。

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. Guice用名为PrivateModule的东西来解决这个问题。 I don't know what CDI offers in this regard. 我不知道CDI在这方面提供了什么。 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. 但同样,这是一个少数情况下的例子,只要你可以处理它就不值得让你的其余配置受到影响。

I have the following principle: configuration-related beans are defined with XML. 我有以下原则:配置相关的bean是用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. 另一方面,在要启用的类中编写@Service@Inject要简单得多。

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. 至于CDI - 它有XML配置的扩展,但你是对的,它主要使用注释。 That's something I don't particularly like in it though. 这是我不喜欢的东西。

I like to keep my code clear, as you pointed. 正如你所指出的,我喜欢保持我的代码清晰。 XML feets better, at least for me, in the IOC principle. 在国际奥委会的原则中,至少对我来说,XML更好。

The fundamental principle of Dependency Injection for configuration is that application objects should not be responsible for looking up the resources or collaborators they depend on. 配置依赖注入的基本原则是应用程序对象不应负责查找它们所依赖的资源或协作者。 Instead, an IoC container should configure the objects, externalizing resource lookup from application code into the container. 相反,IoC容器应配置对象,将应用程序代码中的资源查找外部化到容器中。 (J2EE Development without EJB - Rod Johnson - page 131) (没有EJB的J2EE开发 - Rod Johnson - 第131页)

Again, it just my point of view, no fundamentalism in there :) 再次,这只是我的观点,那里没有原教旨主义:)

EDIT: Some useful discussions out there: 编辑:一些有用的讨论:

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. 1)在我们的项目中(使用Spring 3),我们希望XML配置文件就是:配置。 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. 如果不需要配置(从最终用户的角度来看)或某些其他问题不强制它在xml中完成,请不要将bean-definitions / wirings放入XML配置中,使用@Autowired等等。

2) With Spring, you can use @Qualifier to match a certain implementation of the interface, if multiple exist. 2)使用Spring,您可以使用@Qualifier来匹配接口的某个实现(如果存在多个)。 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 ;). 在我们的例子中,使用XML来处理所有DI会使XML配置文件膨胀很多,尽管它可以在一个单独的xml文件(或文件)中完成,所以它不是那个有效点;)。 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). 正如我所说,这是一个品味问题我只是认为通过注释处理注入更容易,更干净(你可以通过查看类而不是通过XML文件来查看什么服务/存储库/任何东西使用寻找豆子宣言)。

Edit: Here's an opinion about @Autowired vs. XML that I completely agree with: Spring @Autowired usage 编辑:这是关于@Autowired与XML的观点,我完全赞同: Spring @Autowired用法

"But what is bad about xml?" “但是xml的坏处是什么?” It's yet another file to manage and yet another place to have to go look for a bug. 这是另一个要管理的文件,还有另一个必须去寻找bug的地方。 If your annotations are right next to your code it's much easier to mange and debug. 如果您的注释正好在您的代码旁边,那么管理和调试就会容易得多。

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. 此外,注入的所有陷阱应与应用程序代码分开,并降级为与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. 我称那个边界的具体方面是主分区,因为那就是'main'(或者它的等价物)应该存在的地方。

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. 然后,这些注入的依赖项可以通过常规方式跨越边界传递到应用程序中。 (eg 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. 在这种情况下,XML或注释之间的决定没有实际意义。

另外不要忘记Spring JavaConfig

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. 这使得Guice无法使用,因为我必须公开源代码而不是使用由实现app的开发人员配置的xml。

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. 总的来说,我认为重要的是要认识到提供组件和组装/配置应用程序是两个不同的练习,并在需要时提供这种关注点分离。

XML has the only benefit of a declarative style that is defined clearly separated from the application code itself. XML具有声明式样式的唯一优点,声明式样式与应用程序代码本身明确分开。 That stays independent from DI concerns. 这与DI问题无关。 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 eg Java. 与IDE支持例如Java相比,只有一般的(XML)工具支持几乎没有什么好处。 Besides this XML comes with a performance overhead so it usually is slower than code solutions. 除此之外,XML还带来了性能开销,因此它通常比代码解决方案慢。

Annoations often said to be more intuitive and robust when re-factoring application code. 在重新分解应用程序代码时, Annoations通常被认为更直观,更健壮。 Also they benefit from a better IDE guidance like guice provides. 他们也受益于像guice提供的更好的IDE指导。 But they mix application code with DI concerns. 但他们将应用程序代码与DI问题混在一起 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 (eg 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. 因此,它们被认为比XML运行得更快。

Both techniques have serious downsides. 这两种技术都有严重的缺点。 Therefore I recommend to use Silk DI . 因此我建议使用Silk DI It is declarative defined in code (great IDE support) but 100% separated from your application code (no framework dependency). 它是在代码中声明性定义的(很棒的IDE支持),但是100%与应用程序代码分开(没有框架依赖性)。 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. 此外,它有很好的支持,以适应您的需求。

I just have a couple of things to add to what's already here. 我刚刚在这里添加了一些东西。

  • To me, DI configuration is code. 对我来说,DI配置是代码。 I would like to treat it as such, but the very nature of XML prevents this without extra tooling. 我想这样对待它,但XML的本质在没有额外工具的情况下阻止了这种情况。

  • Spring JavaConfig is a major step forward in this regard, but it still has complications. Spring JavaConfig是这方面的重大进步,但它仍然存在复杂性。 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. 组件扫描,接口实现的自动魔术选择以及围绕@Configuration注释类的CGLIB拦截的语义使得它比它需要的更复杂。 But it's still a step forward from XML. 但它仍然是XML的一大进步。

  • The benefit of separating IoC metadata from application objects is overstated, especially with Spring. 将IoC元数据与应用程序对象分离的好处被夸大了,特别是对于Spring。 Perhaps if you confined yourself to the Spring IoC container only, this would be true. 也许如果你只限于Spring IoC容器,那就是这样。 But Spring offers a wide application stack built on the IoC container (Security, Web MVC, etc). 但Spring提供了基于IoC容器(安全性,Web MVC等)构建的广泛应用程序堆栈。 As soon as you leverage any of that, you're tied to the container anyway. 一旦你利用其中的任何一个,你就会被绑在容器上。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM