我只能以使用Guice的经验为例说一下我的看法。简单来说,基于注解的配置极大地减少了连接应用程序所需编写的代码量,并使更改依赖关系变得更加容易...通常甚至不需要触及配置文件本身。它通过使最常见的情况非常简单来实现这一点,但以稍微较困难的方式处理某些相对罕见的情况为代价。
我认为过于教条地要求类“不了解注入”是一个问题。在类的代码中不应该有任何对注入容器的引用,我完全同意这一点。然而,我们必须明确一个观点:注解本身不是代码。它们本身并不会改变类的行为……即使有注解存在,您仍然可以像没有注解一样创建类的实例。因此,您可以完全停止使用DI容器并保留注解,没有任何问题。
当您选择不在类中提供关于注入的元数据提示(例如注解)时,您将放弃有关该类所需依赖项的有价值的信息源。您被迫在其他地方重复该信息(例如在XML中)或依靠不可靠的自动装配魔法,这可能导致意外问题。
针对您的一些具体问题:
它有助于摆脱XML配置
关于XML配置有很多问题。
- 它非常冗长。
- 没有特殊工具支持,它就不是类型安全的。
- 它强制使用字符串标识符。同样,在没有特殊工具支持的情况下并不安全。
- 它不利用语言的特性,需要使用各种丑陋的结构来完成可以用代码中的简单方法完成的事情。
尽管如此,我知道许多人已经使用XML已经很久了,他们认为它完全没问题,我也不指望改变他们的想法。
在许多情况下,每个接口只有一个实现
对于一个应用程序的单个配置(例如生产环境),通常每个接口只有一种实现方式。重点是,当启动您的应用程序时,通常只需要将一个接口绑定到一个实现即可,在许多其他组件中使用它。使用XML配置,您必须告诉使用该接口的每个组件使用该接口的这一个特定绑定(或“bean”如果您愿意)。使用基于注释的配置,您只需声明绑定一次,其他所有事情都会自动处理。这非常重要,并且大大减少了您需要编写的配置量。这也意味着,当您向组件添加新的依赖项时,通常根本不需要更改配置!
您拥有某些接口的模拟实现无关紧要。在单元测试中,您通常只需创建模拟对象并自行传递...与配置无关。如果您为集成测试设置了完整的系统,并使用某些接口而不是实际的实现...那也没有什么变化。对于系统的集成测试运行,您仍然只使用1种实现方式,并且只需要配置一次。
XML:您可以在任何地方注入任何内容
在Guice中,您可以轻松地做到这一点,我想您也可以在CDI中做到这一点。因此,使用基于注释的配置系统不一定会完全阻止您这样做。话虽如此,我敢说,在大多数应用程序中,大多数注入的类都是您自己可以添加@Inject
的类(如果尚未存在)。轻量级标准Java库JSR-330的存在使得将来更多的库和框架为组件提供带有@Inject
注释的构造函数变得更加容易。
一个接口的多个实现方式
一种解决方案是使用限定符,对于大多数情况这应该就足够了。但在某些情况下,您可能想要做一些特殊操作,而仅在特定注入类的参数上使用限定符并不起作用...很可能是因为您想要有多个该类的实例,每个实例都使用不同的接口实现或实例。Guice使用称为PrivateModule
的东西来解决这个问题。我不知道CDI在这方面提供了什么。但再次强调,这只是少数情况,只要您能处理,就没有必要让其它配置受影响。