Apache Geronimo 小组已经成功实现了新的 Java™ Platform, Enterprise Edition (Java EE) 5.0 规范。Java EE 5 拥有许多值得关注的特性,其中之一就是新的 Java Standard Tag Library (JSTL) 1.2 规范。JSTL 1.2 的关键是统一表达式语言,它允许我们在 JavaServer Faces (JSF) 中结合使用 JSTL 的最佳特性。本期的叛逆者将介绍 JSTL 1.2 的重要性,探究 Java Web 技术的历史以及 Geronimo 小组如何利用 GlassFish JSTL 1.2 实现将 JSTL 12 支持添加到 Geronimo 中。
Java Web 技术的演化
Web 技术一直都是 Enterprise Java 语言的一部分。它们从 servlet 开始,并在此基础上发展演化。
Servlets
Servlets 最初的目的是用于响应 HTTP 请求。通常,编写 servlet 是一件相当麻烦的事情。请看清单 1。
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletOutputStream out = response.getOutputStream(); out.println("<!DOCTYPE html PUBLIC /"-//W3C//DTD XHTML 1.0 Transitional//EN/" /"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd/">"); out.println("<html xmlns=/"http://www.w3.org/1999/xhtml/">"); out.println("<head>"); out.println("<meta http-equiv=/"Content-Type/" content=/"text/html; charset=ISO-8859-1/" />"); out.println("<title>All Users</title>"); out.println("</head>"); out.println("<body>"); out.println(" <table>"); out.println(" <tr>"); out.println(" <td>UserID</td>"); out.println(" <td>UserName</td>"); out.println(" <td>Name</td>"); out.println(" </tr>"); UserDao dao = new UserDao(); List users = dao.getAllUsers(); for (int i=0;i<users.size();i++){ User user = (User) users.get(i); out.println(" <tr>"); out.println(" <td>"+user.getId()+"</td>"); out.println(" <td>"+user.getUserName()+"</td>"); out.println(" <td>"+user.getFirstName()+' '+user.getLastName()+"</td>"); out.println(" </tr>"); } out.println(" </table>"); out.println("</body>"); out.println("</html>"); } |
Servlets 涉及到大量嵌入到 Java 代码中的 HTML(例如)。与开发 servlets 相比,对它们进行维护是更困难的事情。假设您希望修改清单 1 中的代码,使表具有一个边界。要实现这一目的,您需要修改 Java 代码并重新编译 servlet。所幸的是,servlets 很快就通过 JavaServer Pages (JSP) 技术得到了扩展。
JavaServer Pages
JSP 技术增强了 Java servlets。JSP 组件对 servlets 提供了许多改进,其中包括允许混合原生标记和 Java 代码。清单 2 显示了与 清单 1 相同的作为 JSP 组件的 servlet。
<?xml version="1.0" encoding="ISO-8859-1" ?> <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ page import="org.developerworks.*" %> <%@ page import="java.util.List" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" /> <title>All Users</title> </head> <body> <table> <tr> <td>UserID</td> <td>UserName</td> <td>Name</td> </tr> <% UserDao dao = new UserDao(); List users = dao.getAllUsers(); for (int i=0;i<users.size();i++){ User user = (User) users.get(i); %> <tr> <td><%= user.getId() %></td> <td><%= user.getUserName() %></td> <td><%= user.getFirstName() %> <%= user.getLastName() %></td> </tr> <% } %> </c:forEach> </table> </body> </html> |
显然,清单 2 中的代码在 servlet 的基础上得到了重大的改进。JSP 组件仍然要编译为 servlet,不过这需要通过 servlet 容器来完成(或者可以作为构建的一部分来完成)。因此,JSP 组件可以提供与 servlet 相同的性能。PHP 语法类似于 Active Server Page (ASP) 和 PHP 页面,但是编译为 servlet 使 JSP 组件相比其他技术拥有了显著的性能优势。
![]() ![]() |
![]()
|
JSP Model 2
清单 2 中的代码仍然存在着一些不可忽视的问题。它在其中使用了一段 scriptlet,即一小段 Java 代码。无论是站在设计还是实际的角度,使用 scriptlet 都会带来一些问题。JSP 组件可以随意将业务逻辑(检索用户列表)与表示混合在一起。经过发展演化,JSP Model 2 架构解决了这个问题,使 servlet 可以与 JSP 组件结合在一起使用。请看清单 3。
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { UserDao dao = new UserDao(); List users = dao.getAllUsers(); request.setAttribute("users", users); request.getRequestDispatcher("/user.jsp").forward(request, response); } |
servlet 可以首先处理请求和执行业务逻辑。然后可以将结果保存在 HttpServletRequest
对象中,并将其转发给 JSP 组件。这使得 JSP 组件得以简化,如清单 4 所示。
<?xml version="1.0" encoding="ISO-8859-1" ?> <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ page import="org.developerworks.*" %> <%@ page import="java.util.List" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" /> <title>All Users</title> </head> <body> <table> <tr> <td>UserID</td> <td>UserName</td> <td>Name</td> </tr> <% List users = (List) request.getAttribute("users"); for (int i=0;i<users.size();i++){ User user = (User) users.get(i); %> <tr> <td><%= user.getId() %></td> <td><%= user.getUserName() %></td> <td><%= user.getFirstName() %> <%= user.getLastName() %></td> </tr> <% } %> </c:forEach> </table> </body> </html> |
这解决了一些架构方面的问题。但是,仍然存在着一个实际问题。Java 代码与 HTML 的混合提高了 JSP 组件的门槛,不具备 HTML 知识的 Java 开发人员和不懂 Java 语言的 Web 设计人员在使用 JSP 组件时都会遇到困难。
JavaServer Pages Standard Tag Library (JSTL)
清除 JSP 组件中的 Java scriptlet 成为了 JSP 技术的一个目标。这一探索的最终结果便是 JSTL。JSTL 引入了 HTML 样式的标记,用于访问 Java 对象和执行 Java 语言中的众多构建任务,比如说迭代集合、条件逻辑和格式化文本。JSTL 允许 JSP 组件进一步发展深化,如清单 5 如示。
<?xml version="1.0" encoding="ISO-8859-1" ?> <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" /> <title>All Users</title> </head> <body> <table> <tr> <td>UserID</td> <td>UserName</td> <td>Name</td> </tr> <c:forEach items="${users}" var="user"> <tr> <td><c:out value="${user.id}"/></td> <td><c:out value="${user.userName}"/></td> <td><c:out value="${user.firstName}"/> <c:out value="${user.lastName}"/></td> </tr> </c:forEach> </table> </body> </html> |
<c:forEach>
标记允许对用户列表进行迭代。<c:out>
标记允许对 Java 对象及其数据输出进行访问。
标记中所使用的各种表达式,如 ${users}
和 ${user.id}
,都是通过 JSTL Expression Language (EL) 解释。比如说,EL 解释字符串 ${users}
并在可访问的各个对象(如 pageContext
、request
、session
和 servlet
(应用程序)上下文)中查找 users
属性。经过进一步发展,JSP 组件允许从 JSTL 标记外部访问 EL。这使 JSP 组件的代码再次改头换面,如清单 6 所示。
<?xml version="1.0" encoding="ISO-8859-1" ?> <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" /> <title>All Users</title> </head> <body> <table> <tr> <td>UserID</td> <td>UserName</td> <td>Name</td> </tr> <c:forEach items="${users}" var="user"> <tr> <td>${user.id}</td> <td>${user.userName}</td> <td>${user.firstName} ${user.lastName}</td> </tr> </c:forEach> </table> </body> </html> |
这是一种更新式的 JSTL,并且 EL 首次出现在了 JSP 2.0 规范中。通过 Model 2 构架(通常由各种 UI 框架实现,比如 Apache Struts 等等),结合 JSTL 和 EL 能够在不使用 Java 的情况下构建 JSP 组件。这允许非 Java 程序员操作 JSP 组件,并且让 Java 开发人员能够专注于实现他们应用程序的业务逻辑。
JavaServer Faces 技术
但是,JSP 技术并不是惟一一项构成 Enterprise Java 架构的 Web 技术。JSP 2.0 规范登场之后,JSF 技术也紧随其后。JSF 旨在成为一个组件架构。Web 页面上的各种对象都视为具有生命周期的组件,并与 Java 对象绑定在一起。因此,在本文的 JSP 示例中,我们可以使用 JSF 直接将 Java 对象绑定到视图组件。产生的 JSP 组件如清单 7 所示。
<?xml version="1.0" encoding="ISO-8859-1" ?> <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <%@ taglib uri="http:.//java.sun.com/jsf/core" prefix="f" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" /> <title>All Users</title> </head> <body> <f:view> <h:dataTable id="users" value="#{UserBean.users}" var="user"> <h:column> <f:facet name="header">UserID</f:facet> <h:outputText value="#{user.id}"/> </h:column> <h:column> <f:facet name="header">UserName</f:facet> <h:outputText value="#{user.userName}"/> </h:column> <h:column> <f:facet name="header">Name</f:facet> <h:outputText value="#{user.firstName}"/> <h:outputText value="#{user.lastName}"/> </h:column> </h:dataTable> </f:view> </body> </html> |
注意我们是如何将 dataTable
绑定到 Java 对象的。然后,我们只需定义表的列,组件自己知道迭代表的行。我们假定使用该数据创建了一个托管的后台 bean (backing bean)(UserBean
)。 dataTable
组件将为我们创建 HTML,因此无需为表指定任何 HTML。这是 JSF 的优势之一。
![]() ![]() |
![]()
|
JSF 和 JSP 1.2
JSF 一直都在使用 JSP 技术。但是,当引入 JSF 时,JSP 1.2 是 JSP 技术使用最为广泛的一个版本。因此 JSF 基于 JSP 1.2,这样 JSF 便不支持 JSTL 和 EL。您可能已经注意到了 <h:dataTable>
和 <h:outputText>
的值属性。它们看上去类似于 JSTL 所使用的 EL。但是,在 JSF 1.0 中,它们只是表面相同。JSF 拥有自己的 EL,其工作原理与 JSTL 中引入的 EL 极为相似,并且随后成为 JSP 技术的一部分。但是,JSF EL 与 JSTL EL 并不兼容 — 直到现在。
统一表达式语言
Java EE 5 的关键点之一就是统一表达式语言。这样,JSTL 和 JSF 所使用的 EL 便合为一体。现在已经可以将 JSTL 和 JSF 混合在一起了,如清单 8 所示。
<f:view> <c:forEach items="${UserBean.groups}" var="group"> ${group.groupName} <h:dataTable id="#{group.groupId}" value="#{group.users}" var="user"> <h:column> <f:facet name="header">UserID</f:facet> ${user.id} </h:column> <h:column> <f:facet name="header">UserName</f:facet> ${user.userName} </h:column> <h:column> <f:facet name="header">Name</f:facet> ${user.firstName} ${user.lastName}" </h:column> </h:dataTable> </c:forEach> </f:view> |
清单 8 中的示例演示了如何使用 JSTL 迭代多个组。然后,我们为各个组都创建了一个显示表,用于列出组中的用户。注意如何使用不同的 EL 分别为 JSTL 标记(<c:forEach>
)和 JSF 组件(<h:dataTable>
)引用数据,并作为直接在 JSP 组件中使用的 EL 表达式。通过混合 JSTL 和 JSF EL,我们获得了双剑合壁的效果。
EL 作为 Java EE 5 的一部分
Java EE 5 对 EL 还做出了一个主要修改。JSTL 实现并不要求成为 J2EE 1.4 规范的一部分。Web 应用程序开发人员可以在应用程序中选择使用哪个 JSTL 实现。当然,他们也可以选择只在应用程序中使用 JSF 实现。
Java EE 5 规范要求使用 JSTL 实现。Web 应用程序开发人员 再也不用为应用程序中的实现操心了。相反,他们可以毫不犹豫地选择使用 JSTL。他们还可以利用 JSF 的强大功能。统一表达式语言也成为了 Java EE 5 规范的一部分。
![]() ![]() |
![]()
|
Geronimo 和 GlassFish JSTL
上一节介绍了这样一个事实:在过去,Web 应用程序开发人员可以选择是否包括 JSTL 技术。如果选择是,那么需要在应用程序中选择要使用的 JSTL 实现。可用的 JSTL 实现多种多样,不一而足。
如今,在 Java EE 5 中,JSTL 实现附带在应用服务器中。因此,Java EE 5 规范的任何实现都必须包括 JSTL 实现。当 Apache Geronimo 开发人员开始操作 Java EE 5 实现时 —Geronimo 2.0— 他们需要包括一个 JSTL 实现。
但是,他们不能随意选取一个已有的实现。统一表达式语言是 JSTL 实现的一个重要要求。许多 JSTL 实现在设计时都不能与 JSF 一同运行。所幸的是,Geronimo 小组并没有自己实现 JSTL 和统一表达式语言。他们可以利用 Sun 公司的 GlassFish。
您对 GlassFish 可能还比较陌生,它是 Sun 公司 Java EE 5 规范的参考实现。它是开源的应用程序,并且同时通过了 Sun 的 Common Development and Distribution License (CDDL) 和 GNU General Public License (GPL) 许可。Sun 一直都提供了一个 JSF 参考实现,因此他们很快便提供了一个含有统一表达式语言的 JSTL 参考实现。GlassFish 的开源性质使 Geronimo 小组能够利用这一成果并在 Geronimo 2.0 中包含 GlassFish JSTL 实现。这个里程碑式的发行版为:Geronimo 2.0-M1。
许可考虑
由于 GlassFish 同时通过了 CDDL 和 GPL 许可,因此可以将其包含在 Geronimo 中。但是,它的许可没有像其余 Geronimo 的 Apache 样式的许可那样,而是对 Geronimo 小组附加了一些限制。
基本上,Geronimo 包括 GlassFish JSTL,但是并没有包括其源代码。此外,Geronimo 小组无法修改其源代码,但是他们显然可以对 GlassFish JSTL 做贡献并使用 Geronimo 打包出一个新的程序。
Geronimo 的一个非凡之处就是,您可以修改其代码并将自己定制的 Geronimo 版本发布出去。但是,JSTL 实现显然是一个例外。它的源代码并没有包含在 Geronimo 中,并且其许可为发布自定义版本的 Geronimo 施加了一些限制。比如说,如果您修改了 GlassFish 的源代码,那么其修改只在相同的 GlassFish 许可下才可以使用。
![]() ![]() |
![]()
|
结束语
随着 Java 技术中各种 Web 技术的发展和演化,开发人员从中收获了巨大的利益。统一表达式语言是 Web 技术的最新发展,它允许开发人员混合 JSTL 和 JSF 技术,并且允诺开发人员将继续从中受益。如今,统一表达式语言是 Java EE 5 规范的一个重要部分,这使其也成为了 Geronimo 的一个重要部分。Geronimo 不仅实现了其规范,而且还使用了该规范的参考实现,再一次为开发人员提供了便利。使用 GlassFish JSTL 和 Geronimo 2.0,开发人员在构建 Java Web 应用程序时将拥有更多的选择。