Bruce Tate , 总裁, J2Life, LLC
轻量级容器可以动态地使系统主要组件之间的耦合变松散。不同的容器包含相同的设计模式,但却具有根本不同的哲学。本文帮助您在下列三种轻量级容器之间作出最佳选择:Spring Framework、HiveMind 和 PicoContainer。
2002 年在科罗拉多的一次旅行中,我完美地感受了阿肯色河。在三段不同的漂流中,这条河展示了令人惊异的多样性。柔美的布朗峡谷有着开阔的急流,翻滚着巨大的波浪。Royal Gorge 别具特色的悬崖峭壁引导着巨大而笔直的峡谷之下的水力,在这条直线上发生一点小闪失都会受到长途游泳的惩罚。Numbers 具有精密的落差,需要人们在范围狭窄的圆石花园里精确操纵。在一条河里,我有了三次极不相同的体验。
在我的上一篇文章“轻量级开发的成功秘诀,第 3 部分:Spring 露出水面”中,我们学习了轻量级容器的基本原理。本文将向您展示三种最流行的容器:
这三种容器都源于依赖注入,但每种容器都具有极不相同的特征。当我介绍每种容器的高级描述时,您将看到正在运行的每种框架,以及可以应用每种框架的环境。
核心哲学
这三种容器都接受 POJO (plain old Java object),都具有对象生命周期的钩子(所以它们可以在创建或销毁 bean 时调用您的代码),都执行依赖注入。您可能认为这些主旋律将导致相似的容器,但事实并非如此。尽管植入每种容器的代码可能相似,但容器本身反映了不同的能力、风格和整体哲学。总而言之,每种容器的作者都忠于他们的哲学。
Spring Framework
作为开放源码框架的 Geneva,Spring Framework 为数百个 Java 2 Platform, Enterprise Edition (J2EE) API 和开放源码框架提供了轻量级容器和胶水代码 (glue code)。Spring 有一个最重要的前景:让 J2EE 更易使用。读完一些示例和书籍之后,您将看到一些常见的主题:
- Spring 支持三种依赖注入——setter、构造函数 和 方法 注入——但总的来说,最流行的模型是 setter 注入。
- 在灵活性和简单性之间,Spring 的 XML 风格配置更重视灵活性。您可以做任何事情,但对于初学者来说,配置文件是晦涩难懂的。
- Spring 的创始人认为,容器只是整体框架的一小部分。Spring 的大部分价值来源于支持该框架的数千行胶水代码。它易于插入任何系统中。
- Spring 框架是三种容器实现中最完美的。一般来说,优秀的文档都是完美编写的。
- Spring 具有自动连线 (autowire) 方式,但大多数示例都没有使用它。我并不十分了解这个决策,但有时候,能够看到明确列出的依赖关系是不错的。
- Spring 提供了完整的 AOP 框架,使得更容易附加服务。您可以使用 Spring 自己的框架或依赖丰富的 AspectJ 集成(参阅 参考资料)。
如果要用一个短语来形容 Spring,我会说让企业更强。
HiveMind
Howard Lewis Ship 是 Jakarta Tapestry Web 框架的创建者,他还创建了 HiveMind。作为一个容器,HiveMind 是灵巧、干净且易于使用的。与其他许多较好的开放源码框架一样,Ship 创建 HiveMind 是为了让它帮助解决现实问题。但是,HiveMind 向传统的轻量级容器添加了两个创新:
- 最重要的 HiveMind 创新是模块。据 Ship 所说,Eclipse 插件激发了他的 HiveMind 模块的灵感。
- HiveMind 强制您编写接口。(与所有轻量级容器一样,它不提供接口,而由您自己提供接口。)
- HiveMind 是用户友好的,它提供称为 HiveDoc 的文档工具,友好简明的 XML 配置,以及行准确的错误报告。
- HiveMind 用户通常优先选择 setter 注入,但该容器还支持构造函数注入。
如果用一个短语来形容 HiveMind 的话,我会说它是概念正确 的。
PicoContainer
到目前为止,PicoContainer 最重要的特征是它的尺寸。它没有提供许多附加物,但它具有完整的依赖注入容器。PicoContainer 还具有一些惟一特性:
- PicoContainer 很小,所以它没有拦截器、AOP 或相似类型的服务,而选择了让其他框架创建这些服务。
- PicoContainer 支持 Java 配置技术,而不支持 XML 配置技术,这与其他容器一样。
- PicoContainer 流行的使用模型是构造函数注入,但它也支持 setter 注入。
- PicoContainer 没有提供许多文档,而且一些现有文档是不完整的,但您不会太需要。
- PicoContainer 具有一个自动连线方式,它很不错。
- PicoContainer 的发展似乎有点停滞。
如果用一个短语来形容 PicoContainer 的话,我会选择理论完美,但不如 Spring 或 HiveMind 实用。
编程模型
现在我将向您展示社区中流行的编程示例,以帮助您更好地理解容器的作者希望您如何使用它们。我使用 PicoContainer 中的 Kiss 示例来展示 autowiring 和 Java 技术风格的配置,使用 HiveMind 加法器示例来展示模块能力,使用 Spring PetClinic 应用程序来展示 Hibernate 集成。
Kiss 示例 (PicoContainer)
在这三个容器中,PicoContainer 具有最简单的编程模型。要查看 Kiss 示例,可从 PicoContainer.org 下载它。安装该示例,浏览到 docs\Two+minute+tutorial.htm,然后您会看到两个组件:
清单 1. 两个 Kiss 组件
public class Boy {
public void kiss(Object kisser) {
System.out.println("I was kissed by " + kisser);
}
}
public class Girl {
Boy boy;
public Girl(Boy boy) {
this.boy = boy;
}
public void kissSomeone() {
boy.kiss(this);
}
}
|
这两个类是自解释的。Girl 对 Boy 有依赖关系。该依赖关系将通过构造函数被注入。先实例化一个容器:
MutablePicoContainer pico = new DefaultPicoContainer();
|
然后注册两个组件:
pico.registerComponentImplementation(Boy.class);
pico.registerComponentImplementation(Girl.class);
|
稍后您可以向 PicoContainer 请求一个对象,然后操作它:
Girl girl = (Girl) pico.getComponentInstance(Girl.class);
girl.kissSomeone();
|
这样就差不多了。编程模型是优雅的,基于构造函数的风格意味着您无需包括无参构造函数。对本例中的 Girl 调用这种函数将会使该对象处于不一致的状态,因为 kiss
方法将抛出异常。
加法器示例 (HiveMind)
现在,让我们看一下 HiveMind 的编程示例。从 Apache Jakarta Project 下载 HiveMind,然后查看加法器示例。您会看到接口和实现。(记住:HiveMind 强制编写接口。)
清单 2. 加法器示例接口和实现
public interface Adder
{
public double add(double arg0, double arg1);
}
public class AdderImpl implements Adder
{
public double add(double arg0, double arg1)
{
return arg0 + arg1;
}
}
|
将该服务暴露在 XML 文件中,如下所示:
清单 3. 将该服务暴露在 XML 文件中
然后,其他应用程序就可以使用该服务了,如下所示:
清单 4. 其他应用程序可以使用该服务
Registry registry = RegistryBuilder.constructDefaultRegistry();
Adder adder = (Adder) registry.getService("examples.Adder",
Adder.class);
... adder.add(arg0, arg1)
|
注意,HiveMind 的模块让您可以将多个服务组合到一起。如果您需要向容器中的服务添加功能,可以使用拦截器:
清单 5. 使用拦截器添加功能
PetClinic 应用程序 (Spring)
Spring 处理事情的方法有些不同。因为 Spring 框架不带有简单的应用程序,我从我的书籍 Spring: A Developer's Notebook 中选择了一个。您可以从 O'Reilly Media 获取该示例代码。解压示例 4,它展示了一个用于 RentaBike 商店的带有属性的 CommandLineView
对象,该对象最终成为该应用程序的数据访问对象。
清单 6. CommandLineView 对象
public class CommandLineView {
private RentABike rentaBike;
public CommandLineView() {}
public void setRentABike(RentABike rentaBike) {this.rentaBike = rentaBike;}
public RentABike getRentaBike() { return this.rentaBike; }
...
}
|
RentaBike 是具有您希望在自行车商店对象中看到的各种方法的接口:
清单 7. 接口方法
public interface RentABike {
List getBikes();
Bike getBike(String serialNo);
void setStoreName(String name);
String getStoreName();
}
|
没有显示 ArrayListBikeStore
,它是 BikeStore 接口的存根实现。注意,Spring 允许编写接口,但不强制编写接口。下面是描述该应用程序中 bean 的 XML 配置文件:
清单 8. 描述应用程序 bean 的 XML 配置文件
该上下文中有两个 bean。commandLineView
bean 依赖于 rentaBike
bean。该应用程序通过为 rentaBike
属性指定 rentaBike
名称,显式解析该依赖关系。注意,PicoContainer 自动连接这种显式关系,Spring 也可以,但大多数用户不使用它的自动连线选项。Spring 还允许您通过拦截器或 AOP 向外观的任何方法添加服务。
比较
既然已经看到每种容器的哲学,下面是对每种环境的无形特性的详细比较,比如市场份额、整体质量(fit and finish)和整体特性列表。毕竟,即使编程模型是完美的,但如果没有文档,或者由于缺乏社区而您必须自己支持它,那么它也不会成为一个好容器。
活动社区
Spring 有一个充满活力的社区,和一个支持该框架的称为 Interface21 的职业服务公司。这很重要,因为您知道您可以获得良好的支持,公司才有动力来支持 Spring 框架。我在社区的经历简直太美好了。Spring 贡献者、创始人和用户都以杰出的内容填满了留言板。
HiveMind 框架是一个 Apache Jakarta 项目,所以有着扎实的基础。它有一个正在成长的萌芽社区。该框架的创始人 Howard Lewis Ship 是独立顾问、优秀导师和不屈不挠的提倡者。但是,要利用 HiveMind 的质量帮助或者查找其 Web 站点之外的内容仍然十分困难。尽管如此,它的在线帮助似乎不错,而且社区似乎正在成长。Hibernate 获得了有趣的胜利,它被选中——或者更应该说,Ship 被选中——组成 TheServerSide.com 的新基础设施,TheServerSide.com 是最重要的 Java 技术社区之一。
PicoContainer 也是一个 Apache Jakarta 项目,它似乎发展缓慢。截止本文撰稿,PicoContainer 的最后一次主要代码发行是在 2004 年 11 月。您看不到太多有关 PicoContainer 的新文章,这有点惭愧,因为我喜欢 PicoContainer 的一些哲学。事实上,我不太确定有没有三种开放源码轻量级容器的空间,尤其是最近第四种轻量级容器项目 Avalon 关闭之后。
就每个社区生成的活动而言,Spring 无疑是优胜者。Interface21 的支持、奇思妙想的论坛、活跃的邮件列表以及社区的跟踪记录都是无与伦比的。
整体质量
社区的大小和实力通常驱动开放源码项目的整体质量。充满活力的社区需要更好的文档和示例,而且它们会参与完成结尾的详细信息。
Spring 团队编写了可与我见过的一些比较好的商业产品相媲美的文档。如果这还不够的话,您还可以找到至少五本主要 Spring 书籍和其他许多包含 Spring 内容的出版物。(我自己曾撰写过两本有关 Spring 的书籍,其中一本书中包括 Jolt-winning Better, Faster, Lighter Java 一章,另一本是快速入门书籍 Spring: A Developer's Notebook)。错误消息是专业性和描述性的。与第三方框架和 API 的集成是所有 Java 技术框架中最好的。包装是经过深思熟虑的,不过略有多余。(它帮助我开始把一些比较小的项目划分成模块。)示例是优秀且有指导意义的。
与 Tapestry 一样,HiveMind 也具有好的整体质量。Ship 自己以那些让 HiveMind 变得简单易用的特性而自豪,比如行准确的错误报告;友好简明的 XML 语法;良好的文档工具 HiveDoc。与用于低级详细信息的 JavaDoc 文档结合使用,您可以更好地描述您的应用程序(HiveMind 模块)的高级特性,从而完善它们之间的依赖关系。
PicoContainer 编程模型感觉自然,但文档不完整(许多方法标记看起来过时好几个月了),而且没有许多使用该容器的真实世界示例。有时候,我会觉得自己在独自穿过鬼魂出没的破屋。
但使用 PicoContainer 确实有一个主要优点。因为您配置现实世界的对象时,会得到一些编译时错误检查。实际上,该容器太小太轻了,以至于除了基本配置之外,没有什么能出错。PicoContainer 做了一项合理的工作。
特性
我不想过多地讨论特性。如果您正在寻找许多胶水代码来减少您的开放源码收藏夹的集成或某特定 J2EE API,Spring 无疑是最佳选择。HiveMind 不尝试参与竞争。相反,它与 Spring 的服务兼容。PicoContainer 不构建而且也不尝试构建附加物,而是选择让开放源码项目为其提供服务。到目前为止,它的效果不太好。
哪一个最好?
目前,只有一个真正的答案。HiveMind 具有有趣的创新,PicoContainer 具有易于使用的模型(理论上),但社区似乎已经投票选择了 Spring Framework。随着时间的推移,新的容器可能会成长,HiveMind 可能不断获得市场份额,但目前,Spring 是您的最佳选择。
如果您愿意冒一些险,而使用不太成熟或不太流行的容器,您可能决定实现 HiveMind(如果需要模块级别的配置)或 PicoContainer(如果想要微小的容器)。如果需要许多胶水代码来集成持久引擎、事务处理策略和安全性等方面,Spring 具有最完整的组件堆。但请记住:您可以在 HiveMind 容器中使用 Spring 组件。
参考资料
学习