52

我知道有很多文章解释了如何在 Java EE 中使用 CDI,但我无法弄清楚这实际上带来了什么优势。例如,假设我有一个当前使用 Foo 实例的类。我可能会做

Foo myFoo = new Foo();

或者

// Better, FooFactory might return a mock object for testing    
Foo myFoo = FooFactory.getFoo();

我一直在阅读使用 CDI 可以做到的内容:

@Inject
Foo myFoo;

但为什么这比以前基于工厂的方法更好?我假设还有其他一些我不知道的用例,但我无法确定这一点。

如果我理解了下面的回答,那么这个概念就是 DI 框架充当集中配置的主对象工厂。这是一个合理的解释吗?

更新

从那以后我开始学习 Spring,现在这变得更有意义了。下面的段落取自Spring in Practice,以一个类为例,AccountService该类又使用AccountDao. 我为冗长的引用道歉,但我认为这确实是为什么注入的资源提供了超出标准初始化的东西的核心。

您可以使用 new 关键字构造 AccountService,但服务层对象的创建很少如此简单。它们通常依赖于 DAO、邮件发送者、SOAP 代理等等。您可以在 AccountService 构造函数中以编程方式(或通过静态初始化)实例化这些依赖项中的每一个,但这会导致硬依赖项和级联更改,因为它们被换出。

此外,您可以在外部创建依赖项,并通过 setter 方法或构造函数参数在 AccountService 上设置它们。这样做会消除硬的内部依赖关系(只要它们是通过接口在 AccountService 中声明的),但是您会到处重复初始化代码。以下是创建 DAO 并以 Spring 方式将其连接到 AccountService 的方法:

<bean id="accountDao" class="com.springinpractice.ch01.dao.jdbc.JdbcAccountDao"/>

<bean id="accountService"
    class="com.springinpractice.ch01.service.AccountService">
    <property name="accountDao" ref="accountDao"/>
</bean>

如上所述配置了 beans,您的程序现在可以AccountService从 Spring ApplicationContext 请求一个实例,Spring DI 框架将处理所有需要实例化的实例。

4

4 回答 4

49

编写 CDI 的人给了你一个大对象工厂;他们为您完成了工作,比您做得更好。它是 XML 配置或注释驱动的,因此您不必将所有内容都嵌入代码中。

依赖注入引擎,比如 Spring,比你的工厂做得更多。复制他们提供的所有东西需要不止一个工厂类和一行代码。

当然,您不必使用它。你总是可以自由地发明自己的轮子。你应该——如果你的目的是学习如何制造轮子或消除依赖。

但是,如果您只想开发应用程序,最好使用其他人提供的工具,当他们为您提供优势时。

关于依赖注入的开创性文章由 Martin Fowler 撰写。我建议阅读它;八年后,它仍然很棒。

“还不清楚更多是什么”

这里有几个优点:

  1. 松耦合
  2. 更容易测试
  3. 更好的层次感
  4. 基于接口的设计
  5. 动态代理(转至 AOP)。
于 2012-10-24T11:01:35.757 回答
19

使用依赖注入的目的是使使用注入的东西的代码不依赖于工厂。对于您的工厂代码示例,您的代码中嵌入了一个静态方法调用,而 DI 方法不需要该方法调用。

被注入的东西myFoo不应该知道工厂。工厂对您的 DI 所没有的测试选项进行了限制。具体来说,如果你有一个 Bar 对象有一个 Foo,那么对该 Bar 对象的测试可以直接创建一个 Foo 或模拟 Foo 并将其放在 Bar 上,而无需涉及 FooFactory,并且你不必编写一个 FooFactory,这是样板,不做任何事情来实现应用程序业务逻辑。

于 2012-10-24T11:49:29.053 回答
5

这是关于企业编程是什么的一个重要而微妙的问题。

名称选择得很好:上下文和依赖项。

CDI 与更好或更简洁的代码无关,它旨在确保大型组织能够构建复杂的分布式软件系统并共享数据。这是为了 100% 确保政府或其他官僚机构可以不加选择地为他们控制的每一个软件分发独立的、记录良好的软件包。请记住,现在几乎可以注入任何 POJO。

假设您正在构建某种客户端应用程序,并且您希望它在角落打印用户的名字。

  • 这家大公司的企业架构师希望你拥有这种能力,但作为一名初级软件工程师,你不可能获得数据库的密钥。

  • 他们还希望通过网络保护数据,但该公司不会支付任何工程师在每次需要共享数据片段时重新设计身份验证客户端。

  • 他们希望您能够查询和更新此信息,但希望在比任何应用程序更高的级别上处理交易。

  • 他们希望您能够在设置块中使用简单的模拟来测试您的课程。

  • 他们希望类之间的耦合涉及最少的静态方法。

  • 不断地不断地......

大多数 JSR 可能有一个“EA 希望能够……”隐藏在某个地方。

CDI 是首选,因为它允许大型(任意?)水平和垂直规模的应用程序共享上下文、依赖关系,从而共享数据。

用福勒的话来说:

“问题是我怎样才能建立这个链接,以便我的列表器类不知道实现类,但仍然可以与实例对话以完成其工作。”

“但是如果我们希望以不同的方式部署这个系统,我们需要使用插件来处理与这些服务的交互,以便我们可以在不同的部署中使用不同的实现。”

“这些容器使用的方法是确保插件的任何用户都遵循一些约定,允许单独的汇编模块将实现注入到列表器中。”

简而言之,它们允许对复杂的企业应用程序进行集中的“命令和控制”。 Java EE是一个系统化、可靠的抽象过程,而 CDI 是它的化身,运行良好,几乎使其不可见。它使复杂应用程序的拼接变得微不足道。

还有两件事:

  1. 请注意,CDI 与“服务定位器模式”和平共处,在 Java EE 中称为 JNDI,如果客户端开发人员需要在许多相同类型的替代方案中进行选择,则这种模式更为可取。

  2. 在许多情况下,尤其是非企业(字面意思)情况下,CDI 的火力超出了所需。

于 2015-01-05T00:08:21.783 回答
4

在高层次上,与 CompSci 上的大多数东西一样,它提供了一定程度的间接(或抽象),否则将在您的应用程序中硬编码为Foo myFoo = new Foo();. 这种间接性带来了松散耦合的代码,这使得事情变得模块化,这使得以更简单的方式替换、服务、测试等类或子系统变得容易。

请注意,间接/抽象有许多设计和模式——依赖注入只是其中之一。

您问题的另一个方面是“为什么选择 CDI?” - 好吧,因为有人已经为你完成了这项工作。您总是可以构建自己的东西,但是当目标是构建一个必须在预算内按时执行的真实世界系统时,这通常是浪费时间。当有一位米其林星级厨师愿意为您做这些工作时,为什么还要为杂货和烹饪烦恼呢?

于 2013-12-21T18:47:33.377 回答