随着平台的容器支持、开发人员工具、培训资源和开发人员社区体验等方面的改善,推动组织采用 Java™ Platform, Enterprise Edition 5(Java EE 5)的动力正在稳定增长。本文从较高层面介绍 Java EE 5 中新的生产率特性和 API 改进,并通过一个 Web 服务示例展示简化的开发模型。
简介
Java EE 技术是 Java 语言平台的扩展,支持开发人员创建可伸缩的强大的可移植企业应用程序。它为应用程序组件定义了四种容器:Web、Enterprise JavaBean(EJB)、应用程序客户机和 applet。一个应用服务器规范详细描述了这些容器和它们必须支持的 Java API,这促使 Java EE 产品相互竞争,同时保证符合这个规范的应用程序可以在服务器之间移植(参见 Java EE 简史)。
这个平台的最新版本 Java EE 5 已经于 2006 年 5 月发布。Java EE 5 主要关注提高开发人员的生产率,它提供了更简单的编程模型,而没有牺牲平台的任何功能。更简单的开发模型主要由两个机制提供 —— Java 注解和更好的默认行为。主要的功能性改进包括改进了 Web 服务支持以及在平台中集成了 JavaServer Faces(JSF)和 Java Standard Tag Library(JSTL)。
![]() |
|
本文介绍 Java EE 5 中的特性,重点介绍自上一个 J2EE 版本以来的变化。(超出本文范围的改进包括 StAX API — 一个用来解析 XML 的 API — 和跨许多 API 的中小程度的改进。)我将全面讨论四个特性类别:Web 服务、Web 应用程序、企业应用程序以及管理和安全性。然后,为了避免读者觉得厌烦,我们讨论一个使用 Java EE 5 特性的小应用程序,以此说明新的开发模型如何简化面向服务架构(SOA) Web 应用程序的开发。
本文假设读者熟悉企业级软件系统,并希望全面了解 Java EE 5 的概况。了解 Java 编程和 J2EE 对于理解特性的意义会有帮助,但不是必需的。对示例应用程序的讨论涉及一些技术细节,有 J2EE(至少是 Java 编程)背景可能比较容易理解。
Web 服务技术
在 Java EE 5 中引入了注解(annotation)特性,这简化了复杂 Web 服务端点和客户机的开发,与以前的 Java EE 版本相比,代码更少,学习过程更短了。注解(最早在 Java SE 5 中引入)是可以作为元数据添加到代码中的修饰性代码。它们并不直接影响程序的语义,但是编译器、开发工具和运行时库可以通过处理它们生成额外的 Java 语言源文件、XML 文档或其他工件和行为,这些对包含注解的程序起辅助作用(参见 参考资料)。在本文后面,会看到如何通过添加简单的注解,将常规的 Java 类转换为 Web 服务。
Web 服务支持方面的飞跃
Java EE 5 中 Web 服务支持的基础是 JAX-WS 2.0,它是一种替代 JAX-RPC 1.1 的技术。这两种技术都支持创建 REST 风格和基于 SOAP 的 Web 服务,而不必直接处理 Web 服务固有的 XML 处理和数据绑定细节。开发人员可以继续使用 JAX-RPC(这仍然需要 Java EE 5 容器),但是强烈建议迁移到 JAX-WS。刚开始学习 Java Web 服务的开发人员可以跳过 JAX-RPC,直接使用 JAX-WS。它们都支持 SOAP 1.1 over HTTP 1.1,所以完全兼容:JAX-WS Web 服务客户机可以访问 JAX-RPC Web 服务端点,反之亦然。
与 JAX-RPC 相比,JAX-WS 有许多优点。JAX-WS:
通过阅读文章 “Web 服务提示与技巧: JAX-RPC 与 JAX-WS 的比较”,可以进一步了解这些差异。
JAX-WS 中的 wsimport
工具自动地处理 Web 服务开发的许多细节,并以跨平台方式集成到构建过程中,这让开发人员可以将注意力集中于实现或使用服务的应用程序逻辑。它生成各种工件,包括服务、服务端点接口(SEI)、异步响应代码、基于 WSDL 错误的异常以及通过 JAXB 绑定到模式类型的 Java 类。
JAX-WS 还能够提高 Web 服务的性能。在文章 “Implementing High Performance Web Services Using JAX-WS 2.0”(参见 参考资料 中的链接)中,对基于 JAX-WS 的 Web 服务实现(使用了 Java EE 5 中的另外两个 Web 服务特性 — JAXB 和 StAX)和基于 J2EE 1.4 中的 JAX-RPC 的服务实现做了性能对比。这项研究发现在不同负载下 JAX-WS 在各个功能领域产生了 40% 到 1000% 的性能改进。
![]() ![]() |
![]()
|
Web 应用程序技术
除了现有的 JavaServer Pages 和 Servlet 规范,Java EE 5 引入了两种前端技术 — JSF 和 JSTL。JSF 是一组 API,支持以基于组件的方式开发用户界面。JSTL 是一组标记库,支持在 JSP 中嵌入过程式逻辑、对 JavaBean 的访问方法、SQL 命令、本地化格式指令和 XML 处理。JSF、JSTL 和 JSP 的最新版本支持一种统一表达式语言(expression language,EL),这使这些技术更容易集成在一起(参见 参考资料)。
JSF 1.2
JSF 为常见的 UI 问题提供了内置支持,比如组件状态管理、事件处理、导航、用户输入检验和国际化。有经验的开发人员可以创建定制的强大的可重用组件,还可以为 Web 浏览器之外的其他客户机设备创建定制的组件。技术经验不足的用户可以在 Sun Java Studio Creator 等可视编程环境中重用定制组件,包括用于 HTML 界面的默认 JSF 标记库。这让编程新手也能够创建复杂的 Web 表示层。
在开放源码领域和受许可协议限制的软件领域中,第三方 JSF 组件越来越多了。在 Web 上搜索 “JSF components” 或 “JSF component libraries” 会找到几十种组件。许多组件都依赖于 Asynchronous JavaScript + XML(Ajax)技术,这种技术是 “Web 2.0” 运动背后的主要驱动力。Web 程序员可以使用它们创建出比传统 Web 应用程序更好的用户体验,同时避免了从头编写 Ajax 组件的麻烦。
JSP 2.1
JSP 技术是从 J2EE 2.1 开始出现的。它使用 Java Servlet 规范支持声明式的 UI 编程。它支持以文档形式编写 UI,Web 应用程序容器将这些文档转换为 Java servlet 并编译,然后调用它们来响应请求。这些文档通常将 JSP 指令和脚本片段与某种标记语言(比如 HTML)混在一起。JSP 可以使用老式语法(使用以 <%
开头、以 %>
结束的特殊标记),也可以使用新的语法(良构的 XML)。它们通常作为 Model-View-Controller(MVC) UI 框架的 “View” 部分。
与以前的版本相比,JSP 2.1 和 JSF 1.2 之间的兼容性更好,这主要是因为它们的 EL 语法已经集成为统一 EL。EL 支持的操作包括:
过去,JSP 和 JSF EL 语法有差异,而且容器计算它们的方式也不一样。统一 EL 消除了这些差异,还增加了一些特性,比如:
对于 JSP 标记库开发人员来说,好消息是标记处理器现在支持用注解注入资源,所以大大简化了执行 Java Naming and Directory Interface(JNDI)所需的资源配置和代码。
JSTL 1.2
JSTL 已经存在多年了,但是在 Java EE 5 之前,Java EE 还不包含它。JSTL 标记支持在 JSP 中嵌入以下类型的元素:
if
/else
结构。JSTL 1.2 是一个维护版本,它支持统一 EL 并解决了在同一个 JSP 页面中混用 JSF 标记和 JSTL 迭代标记时遇到的问题。
Java Servlet 2.5
Java Servlet 规范是 Java Web 层技术的核心,它的历史与 Java EE 技术本身一样长。设计这个规范是为了提供一种高效率的基于组件的 Web 应用程序开发方法,并确保 Web 应用程序可以移植到实现这个规范的任何服务器上。
Java EE 5 所需的 Servlet 2.5 规范是一个维护版本,它对 2.4 版做了一些次要的改进。它在 Java 5 平台上引入了一些依赖项,还引入了一些注解,它们可以减少对 Web 应用程序部署描述符配置文件(web.xml)的配置需求。还增加了一些方便的配置特性,例如可以用通配符和多个 url-pattern
元素更灵活地配置 servlet。
![]() ![]() |
![]()
|
企业应用程序技术
有大量技术属于企业应用程序的范围,其中许多在 Java EE 5 中没有变化或者不适合在本文中详细讨论。这里主要关注两个改进:对 EJB 开发的简化和新的持久化特性。
EJB 3.0
EJB 规范是 Java EE 平台的核心。它定义如何封装应用程序的业务逻辑,并以高度可伸缩、可靠且感知事务的方式分布业务逻辑,确保并发的数据访问不会破坏数据。
EJB 有三种基本类型:
在过去,EJB 的开发很复杂很麻烦,开发人员常常不得不依靠工具来管理实现 EJB 所需的所有接口和部署描述符。规范为业务逻辑代码规定了许多限制,要求扩展特定的类或实现特定的接口。为了获得一个简单的 EJB 引用,就需要许多样板代码。这些问题使 EJB 在开发社区中名声很差;在许多情况中,EJB 确实很糟糕。
EJB 3.0 大大改进了 EJB 编程模型,这是提高 Java EE 5 开发人员生产率的主要因素之一。EJB 现在可以是一个加了注解的 “普通 Java 对象(plain old Java object,POJO)”,它不需要扩展特定的类。它只需要实现一个远程接口,您可以自己定义这个接口,也可以让 IDE 自动生成它。不再需要部署描述符了,因为 EJB 容器可以从 EJB 上的注解提取出所需的所有信息。
本文的 实践:RideSynergy 应用程序 一节通过示例代码给出这些改进的具体示例。如果需要了解更深入的细节,可以在 参考资料 中找到两篇文章的链接,这两篇文章提供了更有说服力的示例,说明了这个最新版本对 EJB 开发的改进是多么显著。
Java Persistence API(JPA 1.0)
JPA 引入了一个用于 Java 对象持久化的对象-关系映射(object-relational mapping,ORM)框架。在开发它时主要考虑 EJB 的需要,但是它可以用于任何 Java 对象。可以使用注解指定哪些对象和字段应该持久化,以及它们应该映射到哪些数据库表和字段。JPA 支持一种与 SQL 相似的查询语言。这种查询语言可以:
JOIN
语句(但是,如果您愿意,也可以使用 JOIN
语句)。LIKE
语句、BETWEEN
语句等等),定义如何对待结果集(使用 DISTINCT
、ORDER BY
、GROUP BY
等操作符)。 JPA 给 Java EE 平台提供了新功能,解决了与手工持久化和容器持久化相关的许多麻烦。文章 “使用 EJB 3.0 Java Persistence API 设计企业应用程序” 提供了更多信息。
![]() ![]() |
![]()
|
管理和安全性
Java EE 5 需要三个与以前版本相同的管理和安全性规范:
在 Java EE 5 中,这些规范都是维护版本(版本号都从 J2EE 1.4 中的 1.0 版升到 1.1 版),做了一些次要改进,这些超出了本文的范围。更多信息参见 参考资料 中的 Java EE Management and Security Technologies 链接。
![]() ![]() |
![]()
|
实践:RideSynergy 应用程序
本节通过几个示例展示 Java EE 5 中的简化编程模型,说明这些模型如何提高开发人员的生产率。您可以看到如何通过这些模型快速开发一个示例应用程序,这个程序包含一个 Web 服务端点和客户机,使用 EJB 实现业务逻辑,使用 JSF 作为 Web 前端。
![]() |
|
我将使用一个简单的 Web 应用程序演示 Java EE 5 技术,这个程序是一个称为 RideSynergy 的虚构的服务(下载 中提供了源代码)。RideSynergy 服务帮助人们在网上安排合作用车。我使用 NetBeans 5.5 开发这个服务,并在 Sun Application Server 9.0_01 和 WebSphere Application Server(Community Edition)2.0 上测试过。
RideSynergy 的工作方式如下:
访问 RideSynergy 的用户使用图 1 所示的页面提供或请求车辆,需要指定旅行起点和终点的 ZIP 编码并输入一个电子邮件地址。这个页面还提供查看当地天气报告的选项。
如果提交一个车辆供应,结果页面(见图 2)会列出匹配的所有车辆请求。如果提交一个车辆请求,会列出匹配的供应。只有在供应和请求页面上选择 Check weather 复选框,才会显示天气预报(注意,在实际的应用程序中,显示五天的天气预报数据。为了简单,图 2 被截短了)。天气预报数据是从一个公共 Web 服务(http://www.webservicex.net)获得的。
RideSynergy 背后的代码展示了 Java EE 5 的简单 Web 服务编程模型:它使用 JAX-WS 建立一个 Web 服务端点定义(其中包含一个注解),并用 wsimport
特性创建一个 Web 服务客户机。它还展示了 Java EE 5 中的简单 EJB 编程模型和 JSF 的基本原理。
注解:用更少的代码做更多工作
RideSynergy 以 Web 服务的形式提供统计数据,这个特性很好地说明了 Java EE 5 让我们能够用更少的代码做更多工作。这个特性是在 RideStatistics
类中实现的,它演示了最简单的 Java EE 5 注解形式。但是,简单并不意味着功能不强:它们说明,与 J2EE 1.4 方式相比,用 Java EE 5 方式实现这些特性要简单得多。
清单 1 中的 RideStatistics
类实现一个 Web 服务,它使用无状态 RideManagerBean
会话 bean,根据 Web 服务客户机指定的起点和终点 ZIP 编码查询匹配的车辆供应数量。RideManagerRemote
接口定义 RideManagerBean
上可供客户机代码使用的操作,客户机代码可以在同一个 JVM 中运行,也可以在其他 JVM 中运行。
package com.ridesynergy; import java.util.Set; import javax.ejb.EJB; import javax.jws.WebService; /** * Web Service that exposes a count of ride offers made to and from specific * ZIP codes. * * @author smoore */ @WebService public class RideStatistics { @EJB RideManagerRemote rideManager; /** Creates a new instance of RideStatistics */ public RideStatistics() { } public Integer rideOffersFromZipCode(Integer zipCode) { Set<Ride< results = rideManager.findCloseOfferMatches(zipCode, 0); return new Integer(results.size()); } public Integer rideOffersToZipCode(Integer zipCode) { Set<Ride< results = rideManager.findCloseOfferMatches(0, zipCode); return new Integer(results.size()); } } |
清单 1 包含两个注解:@WebService
和 @EJB
。首先,我要讨论如何通过 @EJB
注解用依赖项注入(dependency injection) 技术访问 EJB。然后讨论如何通过 @WebService
注解将一个 POJO 变成完整的 Web 服务端点。
依赖项注入
如果您熟悉 J2EE 1.4 中的 EJB 编程,那么在看到 清单 1 时可能会问:真的 这么容易就获得了一个 EJB 的引用吗?是的,因为 @EJB
注解提供了一种基于依赖项注入的简单编程模型。
有了 @EJB
注解,就不再需要编写 J2EE 1.4 中的那些复杂代码(比如清单 2 中的代码):
. . . Context initial = new InitialContext(); Context myEnv = (Context) initial.lookup("java:comp/env"); Object obj = myEnv.lookup("ejb/RideManager"); RideManagerHome home = (RideManagerHome) PortableRemoteObject.narrow( obj, RideManagerRemote.class); RideManager manager = home.create(); . . . |
在 Java EE 5 支持的 EJB 3.0 编程模型中,这个 @EJB
注解注入 RideStatistics
对 RideManagerRemote
的依赖项,这样 RideStatistics
就不需要用 JNDI 查找引用。
它还避免了直接依赖于包含 RideManagerRemote
的包。看一下 import
语句;这里没有针对 RideManagerRemote
的 import
语句(但是,它却可以通过编译)。所以,可以将 RideManagerRemote
重构到另一个包中,而不需要更新和重新编译 RideStatistics
。
注解还给依赖项的另一方面带来许多好处:实际 EJB 提供 RideManagerRemote
背后的实现并告诉 Java EE 5 容器用它做什么。我将稍后解释。
复杂的运行时行为
当部署到 Java EE 5 容器时,JAX-WS 处理 清单 1 中的 @WebService
注解,并将 RideStatistics
类转换为一个完整的 Web 服务端点,这个端点包含两个操作:rideOffersFromZipCode
和 rideOffersFromToZipCode
。JAX-WS 处理提供 Web 服务所需的所有工作,包括生成 Web Services Description Language(WSDL),让 Web 上的其他应用程序能够发现并使用这个 Web 服务,还提供机制响应对 Web 服务的客户机请求。
JAX-WS 为 RideStatistics Web 服务生成 WSDL 的默认位置是 http://server:port/ridesynergy2-war/RideStatisticsService?WSDL。按照以下步骤查看这个 WSDL:
更复杂的注解
清单 1 中的注解只是简单的注解。注解还可以接受命名元素(named element),这种元素与方法参数相似,但是参数的次序和数量不重要,因为每个参数都有名称。使用命名元素就像是将一个映射传递给注解,其中包含的键/值对可以决定处理注解的方式。
WeatherForecastSoap
接口(见清单 3)是由 JAX-WS 中的 wsimport
工具创建的,其中包含接受命名元素的注解。清单 3 给出 WeatherForecastSoap
接口:
. . . @WebMethod(operationName = "GetWeatherByZipCode", action = "http://www.webservicex.net/GetWeatherByZipCode") public WeatherForecasts getWeatherByZipCode( @WebParam(name = "ZipCode", targetNamespace = "http://www.webservicex.net") String zipCode); . . . |
在清单 3 中,getWeatherByZipCode()
方法上有一个 @WebMethod
注解,这个注解有两个命名元素:operationName
和 action
。getWeatherByZipCode()
的 zipCode
参数上有一个 @WebParam
注解,这个注解包含命名元素 name
和 targetNamespace
(注意在实际应用程序中,getWeatherByZipCode()
还有其他注解,这里省略掉了)。
定义注解的代码指定注解接受哪些命名元素(如果有的话)。细节参见 参考资料 中的注解初级教程链接。
声明无状态会话 bean
清单 4 给出 RideManagerBean
的类声明,这个无状态会话 bean 实现了 清单 1 所示的 RideManagerRemote
接口:
. . . @Stateless public class RideManagerBean implements RideManagerRemote { . . . |
在 J2EE 1.4 中,EJB 必须实现 SessionBean
接口,这个接口要求实现六个方法。在许多情况下,这些方法实现都是空的,它们之所以存在只是为了满足接口的要求,让代码能够通过编译,这使代码很杂乱。EJB 3.0 通过提供生命周期注解 @PostConstruct
、@PreDestroy
、@PostActivate
和 @PrePassivate
消除了这种混乱。可以根据需要将这些注解添加到适当的方法上,从而实现对生命周期事件的响应;只要求这些方法是公共方法,没有参数并返回 void
。
用注解替代部署描述符
Java EE 5 中的注解还可以消除以前的 Java EE 版本所需的大量配置代码。例如,清单 4 中的 @Stateless
注解可以替代 EJB 部署描述符,EJB 部署描述符是一个 XML 配置文件,它向容器提供 EJB 的细节。在以前的 Java EE 平台中,必须在一个符合 EJB 2.1 模式的 XML 文件中包含这样的描述符。清单 5 给出配置 RideManagerBean
和所需接口的代码片段:
<display-name>RideManagerJAR</display-name> <enterprise-beans> <session> <ejb-name>RideManagerBean</ejb-name> <home>com.ridesynergy.RideManagerHome</home> <remote>com.ridesynergy.RideManager</remote> <ejb-class>com.ridesynergy.RideManagerBean</ejb-class> <session-type>Stateless</session-type> <transaction-type>Bean</transaction-type> <security-identity> <use-caller-identity/> </security-identity> </session> </enterprise-beans> |
Java EE 5 向后兼容以前的 EJB 部署描述符。如果愿意,甚至可以混合使用这两种方式,让遗留代码用描述符指定 EJB,而用注解声明新的 EJB。
除了减少所需的代码量之外,注解还在维护方面有好处,因为配置信息就放在源代码中,Sun 的架构师 Graham Hamilton 将这称为 “truth-in-source-code”(参见 参考资料):对于正确地加上注解的类,不需要同时查看源代码和配置文件,就能够理解其运行方式,因为注解在源代码中直接定义了特殊行为。
在 Roland Barcia 的文章 “了解 Java EE 5” 中,通过许多示例展示了 Java EE 5 中的注解如何简化应用程序开发。
合理的默认行为
我们只添加了一个简单的注解,就把 RideStatistics
变成了一个 Web 服务,这也展示了另一个 Java EE 5 设计原则:提供合理的默认行为,从而使编程模型更简单。
在这个示例中,JAX-WS 假设在带 @WebService
注解的类中所有公共方法都应该转换为 Web 服务操作,并以方法名作为操作名。在处理这些方法的输入参数和输出参数时,也会做相似的假设。如果默认行为不适合您的需要,那么可以通过在方法上加注解来修改。但是在许多情况下,都希望 Web 服务中使用的名称与实现 Web 服务的类匹配,所以 JAX-WS 的默认行为是很合理的,这大大简化了 Web 服务的开发。
![]() ![]() |
![]()
|
结束语
在过去,Java EE 技术虽然很强大,但是也很麻烦;开发人员必须忍受它的复杂性,或者使用开发工具 “驯服” 它,才能享受到它的好处。这让大家觉得使用 Java EE 非常累人,只有在大型的企业级平台确实需要它的强大特性,而且组织拥有能够应付 Java EE 开发的资源的情况下,才会考虑使用这种平台。
Java EE 5 试图消除这种坏名声,使它成为适合企业应用程序开发的强大且易用的平台,采取的措施包括提供注解等新的语言特性,采用合理的默认行为等设计目标,以及强调更简单的编程模型。另外,更简单的编程模型减少了不必要的复杂性,降低了开发人员对第三方工具的需求。因此,开发强大的企业应用程序的成本现在显著降低了。开发人员花在与平台 “搏斗” 上的时间更少了,可以集中更多精力开发需要的实际功能,开发速度大大提高了。
以前被 Java EE 技术吓退的开发团队应该重新审视 Java EE 5,现有 J2EE 应用程序的开发人员和维护人员应该研究 Java EE 5 中的众多特性,从而使自己的工作更轻松。
![]() ![]() |
![]()
|
下载
描述 | 名字 | 大小 | 下载方法 |
---|---|---|---|
RideSynergy 示例应用程序的源代码 | j-jee5.zip | 10KB | HTTP |
RideSynergy 企业存档文件 | j-jee5.ear | 10KB | HTTP |
![]() |
||||
![]() |
关于下载方法的信息 | ![]() |
参考资料
学习