为什么在Java EE中使用CDI?
我知道有很多文章解释了如何在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框架作为一个集中configuration的主对象工厂。 这是一个合理的解释?
更新
自从我开始学习Spring之后,现在变得更有意义了。 下面的段落取自实践中的Spring,以AccountService
类为例,该类又使用了AccountDao
一个实例。 我为长篇大论表示抱歉,但是我认为它真正成为注入资源为什么会提供标准初始化的核心。
您可以使用new关键字构造AccountService,但是创build服务层对象很less如此简单。 他们通常依赖于DAO,邮件发送者,SOAP代理以及其他东西。 您可以在AccountService构造函数中(或通过静态初始化)以编程方式实例化这些依赖关系中的每一个,但这会导致在被交换出来时出现严重的依赖关系和级联更改。
此外,您可以创build外部依赖关系,并通过setter方法或构造函数参数将它们设置在AccountService上。 这样做可以消除内部依赖关系(只要它们是通过接口在AccountService中声明的),但是到处都有重复的初始化代码。 以下是你如何创build一个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>
像上面那样configuration好了bean之后,你的程序现在可以从Spring ApplicationContext中请求一个AccountService
的实例,而Spring DI框架将会在实例化所有需要实例化的东西之后。
写CDI的人给了你一个大对象工厂, 他们为你做了工作,比你想的要好。 这是XMLconfiguration或注释驱动的,所以你不必在代码中embedded一切。
像Spring这样的dependency injection引擎比你的工厂做得更多。 这将需要多个工厂类和一行代码来复制它们提供的所有内容。
当然,你不必使用它。 你总是可以自由发明自己的车轮。 你应该 – 如果你的目的是学习如何制造轮子或消除依赖。
但是,如果您只想开发应用程序,最好使用其他人提供给您的优势的工具。
关于dependency injection的开创性文章是由Martin Fowler撰写的。 我build议读一下; 八年后还是很棒的。
“还是不清楚的是什么”
这里有几个优点:
- 宽松耦合
- 更简单的testing
- 更好的分层
- 基于接口的devise
- dynamic代理(延续到AOP)。
使用dependency injection的目的是使得使用注入的东西的代码不依赖于工厂。 使用工厂代码示例,您的代码中embedded了一个静态方法调用,而DI方法并不需要该调用。
正在注入myFoo
的东西不应该知道工厂。
与CompSci上的大部分内容一样,它提供了一个间接 (或抽象) 的级别,否则将在您的应用程序中硬编码为Foo myFoo = new Foo();
。 这种间接导致了松耦合的代码,这使得事物变得模块化,这使得以更简单的方式更换,服务,testing等类或子系统变得容易。
请注意,有许多devise和模式间接/抽象 – dependency injection只是一个。
你的问题的另一个方面是“为什么CDI?” – 好吧,因为有人已经为你做了工作。 你可以随时build立你自己的东西,但是当目标是build立一个真正的世界系统,而且这个系统必须在预算之内,按时完成时,通常是浪费时间。 为什么在米其林星级厨师愿意为你做这个工作的时候,为什么要打杂货和做饭呢?
这是关于什么是企业编程的重要和微妙的问题。
这个名字是很好的select:上下文和依赖关系。
CDI与更好或更简洁的代码无关,而是确保大型组织可以构build复杂的分布式软件系统和共享数据。 这是为了100%确定政府或其他官僚机构可以不分青红皂白地为他们控制的每一个软件分发自包含的,有详细logging的软件包。 请记住,现在几乎所有的POJO都可以被注入。
假设您正在构build某种客户端应用程序,并且希望它在angular落中显示用户的名字。
-
这个大公司的企业架构师会希望你有这个能力,但是作为一个初级软件工程师,你没有机会把数据库交给你。
-
他们还希望通过networking保护数据,但是公司每次需要共享数据时都不会付费给任何工程师重新devise身份validation客户端。
-
他们希望你能够查询和更新这个信息,但希望事务处理的级别高于任何一个应用程序。
-
他们希望你能够在设置块中用简单的模拟来testing你的课程。
-
他们希望类之间的耦合涉及最less的静态方法。
-
而且,在…上…
大多数JSR可能有一个“EA想要能够……”埋在某个地方。
CDI是首选,因为它允许大(任意的)水平和垂直比例的应用程序共享上下文,依赖关系和数据。
用福勒的话说:
“问题是我怎样才能build立这样的联系,以便我的class级对实施class级一无所知,但仍然可以和一个实例交stream来完成工作。”
“但是如果我们希望以不同的方式部署这个系统,我们需要使用插件来处理与这些服务的交互,所以我们可以在不同的部署中使用不同的实现。”
“这些容器使用的方法是确保插件的任何用户遵循一些允许单独的汇编程序模块将实现注入列表程序的约定。
简而言之,它们允许对复杂的企业应用程序进行集中的“命令和控制”。 Java EE是一个系统化,可靠的抽象过程,CDI是一个非常好的化身,几乎使它看不见。 它使复杂的应用程序拼接在一起几乎微不足道。
还有两件事:
-
请注意,CDI与“服务定位符模式”(在Java EE中称为JNDI)一起和平地存在,如果客户端开发人员需要在许多相同types的替代方法中进行select,则CDI更可取。
-
CDI比许多情况下所需的火力更大,特别是非企业(字面上的)情况。