35

注释变得流行。Spring-3 支持它们。CDI 严重依赖它们(我不能在没有注释的情况下使用 CDI,对吗?)

我的问题是为什么

我听到了几个问题:

  1. “它有助于摆脱 XML”。但是xml有什么不好呢?依赖关系本质上是声明性的,而 XML 非常适合声明(而对于命令式编程则非常糟糕)。使用好的 IDE(比如想法)很容易编辑和验证 xml,不是吗?

  2. “在许多情况下,每个接口只有一个实现”。那不是真的!我系统中的几乎所有接口都有用于测试的模拟实现。

还有其他问题吗?

现在我对 XML 的优点:

  1. 您可以在任何地方注入任何东西(不仅是具有注释的代码)

  2. 如果一个接口有多个实现,我该怎么办?使用限定词?但它迫使我的班级知道它需要什么样的注射。这不利于设计。

基于 XML 的 DI 使我的代码清晰:每个类都不知道注入,因此我可以对其进行配置并以任何方式对其进行单元测试。

你怎么看?

4

10 回答 10

25

我只能根据 Guice 的经验说话,但这是我的看法。简而言之,基于注释的配置极大地减少了将应用程序连接在一起所需的编写量,并且可以更轻松地更改取决于什么的内容……通常甚至无需接触配置文件本身。它通过使最常见的情况绝对微不足道来做到这一点,但代价是使某些相对罕见的情况稍微更难处理。

我认为对于让课程“不知道注入”过于教条是一个问题。类的代码中不应有对注入容器的引用。我完全同意这一点。但是,我们必须明确一点:注解不是代码。它们本身并没有改变类的行为方式……您仍然可以创建带有注释的类的实例,就好像它们根本不存在一样。因此,您可以完全停止使用 DI 容器并将注释保留在那里,不会有任何问题。

当您选择不在类中提供有关注入的元数据提示(即注释)时,您将丢弃有关该类需要哪些依赖项的宝贵信息来源。您被迫要么在其他地方重复该信息(例如在 XML 中),要么依赖不可靠的魔法,如自动装配,这可能会导致意想不到的问题。

要解决您的一些具体问题:

它有助于摆脱 XML

XML 配置有很多不好的地方。

  • 它非常冗长。
  • 如果没有特殊工具,它就不是类型安全的。
  • 它要求使用字符串标识符。同样,没有特殊工具支持也不安全。
  • 没有利用语言的任何特性,需要各种丑陋的结构来完成可以用代码中的简单方法完成的事情。

就是说,我知道很多人使用 XML 的时间已经够长了,以至于他们确信这很好,而且我真的不希望改变他们的想法。

在许多情况下,每个接口只有一个实现

对于应用程序的单个配置(例如生产),每个接口通常只有一个实现。关键是在启动应用程序时,您通常只需要将接口绑定到单个实现。然后可以在许多其他组件中使用它。使用 XML 配置,您必须告诉使用该接口的每个组件使用该接口的这个特定绑定(或“bean”,如果您愿意)。使用基于注解的配置,您只需声明一次绑定其他一切都会自动处理。这非常重要,并且大大减少了您必须编写的配置量。这也意味着当您向组件添加新的依赖项时,您通常根本不需要更改任何配置!

你有一些接口的模拟实现是无关紧要的。在单元测试中,您通常只需创建模拟并将其传递给自己......它与配置无关。如果您设置一个完整的系统来使用某些接口进行集成测试,而不是使用模拟......这不会改变任何事情。对于系统的集成测试运行,您仍然只使用 1 个实现,您只需配置一次。

XML:你可以在任何地方注入任何东西

你可以在 Guice 中轻松地做到这一点,我想你也可以在 CDI 中做到这一点。因此,使用基于注释的配置系统并不会完全阻止您这样做。也就是说,我敢说大多数应用程序中的大多数注入类都是你可以添加@Inject到自己的类,如果它还没有的话。用于注解的轻量级标准 Java 库 (JSR-330) 的存在使得未来更多的库和框架也更容易为组件提供带@Inject注解的构造函数。

一个接口的多个实现

限定符是解决此问题的一种方法,在大多数情况下应该没问题。但是,在某些情况下,您确实希望在特定注入类中对参数使用限定符不起作用...通常是因为您希望拥有该类的多个实例每个实例使用不同的接口实现或实例。PrivateModuleGuice 用一个叫做s 的东西解决了这个问题。我不知道 CDI 在这方面提供了什么。但同样,这种情况是少数,只要你能处理它,就不值得让你的其余配置受到影响。

于 2011-02-14T18:03:57.707 回答
10

我有以下原则:配置相关的bean是用XML定义的。其他一切 - 带有注释。

为什么?因为您不想更改类中的配置。另一方面,在要启用的类中编写@Serviceand要简单得多。@Inject

这不会以任何方式干扰测试 - 注释只是由容器解析的元数据。如果你喜欢,你可以设置不同的依赖。

至于 CDI - 它具有 XML 配置的扩展,但你是对的,它主要使用注释。不过,那是我不特别喜欢的东西。

于 2011-02-14T17:39:11.877 回答
4

正如您所指出的,我喜欢保持我的代码清晰。至少对我而言,XML 在 IOC 原则方面做得更好。

用于配置的依赖注入的基本原则是应用程序对象不应该负责查找它们所依赖的资源或协作者。相反,IoC 容器应该配置对象,将资源查找从应用程序代码外部化到容器中。(没有 EJB 的 J2EE 开发 - Rod Johnson - 第 131 页)

同样,这只是我的观点,没有原教旨主义:)

编辑:一些有用的讨论:

于 2011-02-14T17:32:55.240 回答
4

在我看来,这更多的是一个品味问题。

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

2)使用Spring,如果存在多个,您可以使用@Qualifier 匹配接口的某个实现。是的,这意味着您必须命名实际的实现,但我不介意。

在我们的例子中,使用 XML 来处理所有的 DI 会使 XML 配置文件膨胀很多,尽管它可以在单独的 xml 文件(或文件)中完成,所以它不是那么有效的点;)。正如我所说,这是一个品味问题,我只是认为通过注释处理注入更容易和更干净(你可以通过查看类而不是通过 XML 文件来查看服务/存储库/任何使用的东西寻找 bean 声明)。

编辑:这是我完全同意的关于@Autowired 与 XML 的观点:Spring @Autowired 用法

于 2011-02-14T17:38:04.017 回答
3

“但是 xml 有什么不好呢?” 这是另一个要管理的文件,也是另一个必须去寻找错误的地方。如果您的注释就在您的代码旁边,则管理和调试会容易得多。

于 2011-02-14T18:42:13.163 回答
2

像所有事情一样,应该适度使用依赖注入。此外,注入的所有陷阱都应与应用程序代码分离,并归入与 main 关联的代码。

一般来说,应用程序应该有一个边界,将抽象应用程序代码与具体实现细节分开。所有跨越该边界的源代码依赖项都应指向应用程序。我称该边界的具体一侧为主要分区,因为那是“主要”(或等效的)应该存在的地方。

主分区由工厂实现、策略实现等组成。依赖注入框架应该在边界的这一侧完成它的工作。然后这些注入的依赖项可以通过正常方式越过边界传递到应用程序中。(例如作为参数)。

注入依赖的数量应该比较少。十几个或更少。在这种情况下,XML 或注释之间的决定是没有实际意义的。

于 2012-06-14T15:31:26.967 回答
1

就我而言,编写应用程序的开发人员与配置应用程序的开发人员不同(不同的部门、不同的技术/语言),最后一组甚至无法访问源代码(在许多企业设置中都是这种情况)。这使得 Guice 无法使用,因为我必须公开源代码,而不是使用实现应用程序的开发人员配置的 xml。

总的来说,我认为重要的是要认识到提供组件和组装/配置应用程序是两个不同的练习,如果需要,可以提供这种关注点分离。

于 2011-02-23T04:03:07.440 回答
1

也不要忘记 Spring JavaConfig

于 2011-02-14T17:40:02.177 回答
0

我只是有几件事要添加到已经在这里的内容中。

  • 对我来说,DI 配置就是代码。我想这样对待它,但是 XML 的本质在没有额外工具的情况下阻止了这种情况。

  • Spring JavaConfig 在这方面向前迈出了一大步,但它仍然存在复杂性。组件扫描、接口实现的自动魔术选择以及围绕 CGLIB 拦截 @Configuration 注释类的语义使得它比它需要的更复杂。但它仍然比 XML 更进了一步。

  • 将 IoC 元数据与应用程序对象分离的好处被夸大了,尤其是在 Spring 中。也许如果您只将自己限制在 Spring IoC 容器中,这将是正确的。但是 Spring 提供了基于 IoC 容器(安全性、Web MVC 等)构建的广泛应用程序堆栈。一旦你利用其中任何一个,无论如何你都会被绑定到容器上。

于 2012-04-01T22:37:16.610 回答
0

XML具有声明式风格的唯一好处,该风格的定义与应用程序代码本身明确分离。这与 DI 问题无关。缺点是冗长、重构健壮性差和一般的运行时故障行为。与 IDE 对例如 Java 的支持相比,只有一个通用 (XML) 工具支持几乎没有什么好处。此外,这种 XML 会带来性能开销,因此它通常比代码解决方案慢。

在重构应用程序代码时,通常说注解更加直观和健壮他们还受益于 guice 提供的更好的 IDE 指导。但是他们将应用程序代码与 DI 问题混合在一起。应用程序依赖于框架。清晰的分离几乎是不可能的。当根据其他情况(例如机器人腿问题)描述同一位置(构造函数、字段)的不同注入行为时,注释也受到限制。此外,它们不允许将外部类(库代码)视为您自己的源代码。因此,它们被认为比 XML 运行得更快。

这两种技术都有严重的缺点。因此我推荐使用Silk DI。它是在代码中声明性定义的(出色的 IDE 支持),但 100% 与您的应用程序代码分离(无框架依赖)。无论是来自您的源代码还是来自外部库,它都允许对所有代码进行相同的处理。像机器人腿问题这样的问题很容易通过通常的绑定来解决。此外,它有很好的支持来适应您的需求。

于 2012-12-09T14:55:17.933 回答