减轻多余负担,同时保持企业架构依然健壮 ![]() | ![]() |
![]() |
级别: 初级 Bruce Tate (bruce.tate@j2life.com), 总裁 2005 年 7 月 14 日 业务需要重量级架构(比如 Enterprise Java™Beans (EJB)技术)提供的企业服务,但这种架构对于解决日常问题可能过于复杂。本文介绍轻量级容器,并解释它们如何提供满足您业务需要的服务,而无需将您束缚在一个指定的编程模型之上。 我是一名狂热的山地车手。许多山地车手非常偏爱核心设备 —— 巨兽(behemoth),这是一种全悬挂系统(拥有前后两个巨大的减震器)—— 简直到了无以复加的地步。我在德克萨斯州奥斯汀附近的丘陵地区骑车,那里的环境要求我必须有一辆悬挂式山地车,但巨兽有些地方并不适合。我不可能带着多余 的负重攀岩。我必须有一辆轻便的山地车。 Java 行业经历了类似的情况。EJB 技术提供了核心的企业服务。如果您曾对一个复杂的、基于组件的模型编程,您会将业务组件放入一个容器中,该容器提供诸如事务、远程控制、安全和持久性之类的服务。 然而,这里存在一些开销。重量级架构对于解决许多问题都过于复杂。例如,实体 bean 会让您为每个实体编写 7 个文件。因此 EJB 技术就不值得用来解决日常问题。如今,许多业务仍然需要企业服务,但它们正在寻找达到该目标的新方向。它们使用轻量级容器。实际上,最新的 EJB V3.0 标准就使用了轻量级容器模型。 大多数容器 API(如 EJB API)强迫您编写一些接口或一个组件模型。将您的组件放入该容器后,容器会为您处理一些事情。EJB 容器提供企业服务。Servlet 容器(例如 Apache Jakarta Tomcat)实现了 Servlet API,使您可以将动态内容建立到服务器页面中,该页面随后会被发送到 Web 浏览器。 传统容器强迫使用指定的编程模型,轻量级容器则不是。它们使用普通 Java 对象(plain old Java object,POJO)。容器然后将 POJO 绑在一起,并将服务与它们相关联。轻量级容器的共同特征包括:
轻量级容器有许多胜于其他容器架构的优点。例如,您可以使用一个更加简单、基于 POJO 的编程模型。使用 POJO 编程,应用程序会更加易于测试。您的对象也可以在容器外运行 —— 例如,在一个测试用例中。通过依赖注入,轻量级容器减少了组件间的依赖性。它们也保护了您在代码上花费的心血,因为您可以在容器间移动应用程序的大部分。
革新浪潮引发了轻量级容器运动。依赖性管理在这场运动中吹响了第一声号角。早期轻量级容器,比如 Apache 的 Avalon,使用服务定位策略来管理依赖性。现代大多数容器使用依赖注入管理依赖性。 然而,解析依赖性还只是问题的一部分。您还需要可以将服务与容器中的 POJO 相关联。EJB 容器使用代码生成处理该问题。现代容器使用面向方面编程(AOP)和拦截。 在 Java 技术中,依赖注入正迅速地改变我们构建应用程序的方式。这种概念相对简单:一个消费者(类似下面的 清单 1. 依赖注入的示例
请注意 清单 2. Container 类
让我们稍微对该段代码进行重构。构建一个称为 Speaker 的接口和两个不同的实现:一个 Canadian speaker 和一个 Californian speaker。因此,您现在拥有了 Speaker 接口:
CanadianSpeaker 和 CalifornianSpeaker:
还有容器中的单行更改:
注意,您现在可以在 speaker 的两个实现之间变化,而惟一需要改变的代码就是容器。更关键的是,您可以轻松地注入模拟对象来替代真正的 Speaker 实现,并且无需影响其他的代码基就可以进行测试。 当然,最终目标是用专门定制的容器来替代这个手写容器。例如,使用 Spring 容器。在本例中,替换的是您的 清单 3. 用于 Spring 容器的 XML 文件
现在,您可以下载上下文,获取 Consumer bean,然后像下面这样运行它:
Spring 容器与您的容器完成相同的事情。它实例化 beans,并通过设置属性而将它们绑在一起。注意,两部分代码是完全去耦的;接口和容器确保了这一点。您可以使用依赖注入来满足进行企业级开发(例如,数据源或事务管理器)所遇到的许多依赖性。 您也会看到,应用程序的固有层次之间自然地相互依赖。您可能拥有一个由控制器调用的 Web 用户界面(UI)视图,它调用外观层,外观层调用数据访问对象,数据访问对象调用对象关系映射,对象关系映射调用数据库。这些关系就是依赖性。如果能将它 们解耦,会更加易于编码、测试和维护。
依赖注入让您将应用程序的主要层次编织到一起,从而,这使您产生一个具有视图、模型和控制器层的松散耦合应用程序。但是,轻量级容器解决了另一个问 题。您经常具有一些会影响到应用程序许多部分的关注点,比如日志记录、远程控制或安全性。EJB 通过使用代码生成和容器/组件接口解决了该问题。我们有能力做到更好。 您可以在合适的位置编写自己的横切关注点(crosscutting concern),然后使用称为拦截(interceptor)的技术将关注点绑定到需要它的方法上。比如说,一个调用者想要调用称为 图 1. 轻量级容器的主要拦截策略 ![]() 通过使用拦截,您可以更加高效地添加类似安全、声明性事务和远程控制这些自定义服务到 POJO 方法。需要附加说明的是:调用代码和被调用方法都不需要变更。而且,类似 Spring 的容器都预装了拦截来执行这些任务和其他任务。
AOP 更进了一步。使用 AOP,您能够立即快速指定所有需要给定服务的方法,通常是使用正则表达式来做到这一点。AOP 程序员称这一套方法为切点(point cut)。例如,您可能想将将声明性事务与外观中的所有方法相关联。对于以 在正确的位置得到正确的事务行为,无需修改调用者或目标方法中的任何代码。清单 4 展示了部分 Spring 上下文,它为这样的应用程序指定了切点。 清单 4. Spring 容器中指定切点的代码
您可以想像得到,许多人都在努力构建轻量级容器。轻量级容器空间围绕一些参与者,比如 Spring、Pico、HiveMind 和 EJB 技术,正迅速发展壮大。 处于领先地位的是 Spring Framework。Spring 使用 XML 配置并且依赖于 setter 进行依赖注入。它也使用构造函数和工厂方法,但您会看到的大多数示例和 Spring 代码基本身还是使用 setter。 Spring 通过使用胶水代码添加大量的 bean,从而超越了轻量级容器。使用 bean 和 代码,您可以插入数百种让您使用 J2EE API 的不同组件,从 JDO 到 Hibernate 的持久性引擎,工作流引擎,视图技术,等等。Spring 正在快速成熟,并将在可预见的未来成为一个参与者。 Pico 容器是最小的可用容器。它与 Spring 的主要区别之处是在风格上面。Pico 程序员首先依靠构造函数进行注入。Pico 不使用 XML,而是使用 Java 代码来注册容器中的 bean。类似 Spring,Pico 也支持 setter 注入,但主要使用构造函数。Pico 也远不及 Spring 所支持的服务数量。它主要是一个依赖注入容器。不过,当您不需要 Spring 提供的所有企业服务时,Pico 绝对是您的首选。 HiveMind 是最新的开放源码轻量级容器。它比 Pico 拥有更多的支持模块,但还是少于 Spring。尽管如此,HiveMind 使您可以使用 Spring 的一些 bean 和 服务。它能通过 setter 和构造函数管理依赖性。HiveMind 的主要贡献是以下两个重要思想:
现在说 HiveMind 是否会产生大的影响还为时过早。 考虑您现在使用 EJB 所做的事情,例如声明性事务、远程控制和安全性。如果您可以使用轻量级容器代替在 EJB 环境中所做的大多数事情,那为什么不使用轻量级容器呢?该问题迫使 EJB V3.0 专家组重新设计核心 EJB 架构。EJB V3.0 将更有效地实现轻量级容器策略。当前 EJB 架构和最新的 EJB V3.0 间的主要区别是:容器将提供您使用的主要服务,并且您可以使用 XML 来配置容器,但 EJB 技术也将严重地依赖配置的注释。 一些优秀的顾问已经对 EJB 技术中注释的过度使用引起了关注。我也保留我的意见,但 EJB 技术确实有了大麻烦。EJB 小组必须尽快发布补丁,否则轻量级容器可能会放弃使用 EJB 技术。客户已经可以使用 Spring 满足绝大多数的需要。在 EJB 技术大量使用之时,时机可能过晚了。
现在,您已经大概了解了轻量级容器。您了解了以下的基本设计理论:
在接下来的文章中,我将详尽地为您介绍主要的轻量级容器并说明何时使用它们。然后,我将比较完整地介绍 Spring(最流行的容器)。在本文开始的部分,我告诉您我需要在不牺牲自行车避震系统的前提下,减轻自行车的重量。现在您知道了,如何在不牺牲企业服 务的前提下减轻您的容器。 学习愉快!
|