Servlet和JSP迈上新台阶

即将面世的J2EE 1.4提供用Java开发Web应用程序的新的Servlet 2.4和JavaServer Pages (JSP) 2.0技术。本文展示了这两种技术的新特性,并在适当的地方提供每个特性的示例代码。本文假设读者熟悉以前的 Servlet 2.3和JSP 1.2版本。给出的例子已用Tomcat 5(包含在Java Web Services Developer Pack 1.2中)进行了测试。

Servlet和JSP毫无疑问是两种应用最广的J2EE技术。Servlet技术是用Java进行Web应用编程的基础,也是JSP的基础。但是,servlet编程可能会非常麻烦。特别是当你不得不发送一个没多少代码的长HTML页面时更是如此。每个HTML标记必须嵌入到字符串中,用PrintWriter对象的显示方式发送。是一种工作单调乏味而烦人的工作。使用servlet的另一个缺点是每一处改变都需要servlet程序员介入。

Sun公司了解到这一问题之后便开发了JSP作为解决方案。在JSP中,程序员和页面设计员的分工变得容易多了,并且当JSP页面更改时会自动进行编译。不过请注意,JSP是servlet技术的一个扩展,而不是废弃servlet。在实际应用当中,servlet和JSP页面一起使用。

Servlet 2.4的新特性

Servlet 2.4提供了几个新类,且不支持javax.servlet.SingleThreadModel接口。这一版本只支持HTTP 1.1,所以Servlet 2.4应用程序不适用于HTTP 1.0客户程序。2.4版增加了请求监听器和请求属性监听器,并能在一个应用程序中将servlet用作欢迎页面。另外,Servlet 2.4还提供了更好的ServletRequest和RequestDispatcher对象,并更好地支持国际化。此外,现在是根据模式而不是文档类型定义(document-type definition,DTD)文件来验证部署描述符是否有效。这就意味着支持部署描述符的可扩展性。

下面具体说明Servlet 2.4的新特性。请求监听器和请求属性监听器。Servlet 2.3增加了servlet上下文相关监听器和会话相关监听器。Servlet 2.4增加了新的javax.servlet.ServletRequestListener和javax.servlet.ServletRequestAttributeListener两种接口,它们会通知你与Request对象有关的事件。如果你对每个Request对象的初始化和撤消感兴趣,你可以实施ServletRequestListener接口。这个接口有两个方法:requestInitialized()和requestDestroyed()。当需要一个Request对象时,servlet容器便调用requestInitialized方法。当不再需要Request对象时,servlet容器便调用requestDestroyed方法。

这两个方法都从servlet容器接收一个javax.servlet.ServletRequestEvent对象。可以从ServletRequestEvent实例获得servlet上下文和servlet请求。

第二个监听器接口ServletRequestAttributeListener处理Request对象属性的添加、更改和删除。该接口有以下方法:

  • attributeAdded。向Request对象添加新属性时由servlet容器调用。

  • attributeRemoved。从Request对象中删除属性时由servlet容器调用。

  • attributeReplaced。Request对象中现有属性值被替换时由servlet容器调用。

这三个方法从servlet容器获得javax.servlet.ServletRequestAttributeEvent类的一个实例。ServletRequestAttributeEvent类扩展了ServletRequestEvent类,并添加了两个新方法:getName和getValue。getName方法返回触发事件的属性的名称,getValue返回属性的值。

代码清单1 给出这两个新的监听器的示例类。当servlet容器调用方法时二者都显示方法名。监听器经过编译后,它们的类文件必须被部署到WEB-INF/classes目录下。ServletRequest中的新方法。在Servlet 2.4中,javax.servlet.ServletRequest接口增加了4个新方法:

  • getRemotePort。返回发送请求的客户机或最后一个代理服务器的Internet Protocol(IP)源端口。

  • getLocalName。返回从中接收请求的IP接口的主机名。

  • getLocalAddr。返回从中接收请求的接口的IP地址。

  • getLocalPort。返回从中接收请求的接口的IP端口号。

请注意,在Servlet 2.3中,getServerName和getServerPort方法返回的值就是现在getLocalName和getLocalPort返回的值。在2.4版中,getServerName和getServerPort已重新定义。欲了解更多的信息,请查看API文档。

将一个JSP页面中的代码示例如下--

out.println("<br>Remote Port : " + 
  request.getRemotePort());
out.println("<br>Local Name : " + 
  request.getLocalName());
out.println("<br>Local Addr : " + 
  request.getLocalAddr());
out.println("<br>Local Port : " +

  request.getLocalPort());

--该代码生成这样的内容:

Remote Port : 3303 
Local Name : localhost 
Local Addr : 127.0.0.1 
Local Port : 8080 

请求调度程序的新特性。使用请求调度程序可将当前请求传递给一个新的资源,或从当前页面引入另一个资源。Servlet 2.4增加了一些属性,它们将被添加到传递给另一个资源的一个Request对象上:

javax.servlet.forward.request_uri
javax.servlet.forward.context_path
javax.servlet.forward.servlet_path
javax.servlet.forward.path_info
javax.servlet.forward.query_string

如果一个Request对象未被传递,则这些属性的值为null。另一方面,在所传递来对象的资源中这些属性将具有非null值。当某一个资源必须只能通过另一个资源调用而不能直接调用时,这些属性值很有用。

举个例子,在一个叫做myApp的Context(上下文)中有一个名为ModernServlet的servlet, ModernServlet被传递给TargetServlet。 在TargetServlet中,显示代码清单2中的代码。

myApp的部署描述符包含以下 和 元素:

<servlet>
   <servlet-name>Modern</servlet-name>
   <servlet-class>ModernServlet
   </servlet-class>
</servlet>
<servlet-mapping>
   <servlet-name>Modern</servlet-name>
   <url-pattern>/Modern</url-pattern> 
</servlet-mapping>
<servlet>
   <servlet-name>Target</servlet-name>
   <servlet-class>TargetServlet
   </servlet-class>

</servlet>
<servlet-mapping>
   <servlet-name>Target</servlet-name>
    <url-pattern>/Target</url-pattern>
</servlet-mapping>

下面是调用ModernServlet时控制台显示的结果:

javax.servlet.forward.request_uri : /myApp/Modern
javax.servlet.forward.context_path : /myApp
javax.servlet.forward.servlet_path : /Modern
javax.servlet.forward.path_info : null
javax.servlet.forward.query_string : null

将过滤器用于请求调度程序。Servlet 2.4在部署描述符中添加了一个新的 元素,以便servlet程序员决定是否将过滤器(filters)应用于请求调度程序。元素的值可以是REQUEST(默认值)、FORWARD、INCLUDE和ERROR:

  • REQUEST。如果请求直接来自客户机则使用过滤器。

  • FORWARD。如果请求正由请求调度程序进行处理,表示与 或 相匹配的Web组件使用传递调用,则使用过滤器。

  • INCLUDE。只有在请求正由请求调度程序进行处理,表示与 或 相匹配的Web组件使用包含(include)调用时,才使用过滤器。

  • ERROR。只有在请求正由错误页面机制处理为一个与 元素相匹配的错误资源时才使用过滤器。

Servlet 2.4只支持HTTP 1.1客户机。Servlet 2.3既支持HTTP 1.0,又支持HTTP 1.1,而Servlet 2.4与Servlet 2.3不同,它只支持HTTP 1.1客户机。作为过渡,HTTP/1.0状态码302(暂时建议)仍然存在,而且仍然由javax.servlet.http.HttpServletResponse接口中的SC_MOVED_TEMPORARILY表示。HTTP 1.1具有Found的状态码302,它由HttpServletResponse接口中的静态SC_FOUND表示。

Servlet用作欢迎页面。在Servlet 2.3中,你可以在部署描述符中使用 元素列出欢迎文件--当收到一个不完整的URL时将显示的文件。但是,在Servlet 2.3中,在 元素中只能使用HTML文件或JSP文件。在Servlet 2.4中,如今可以将一个servlet用作欢迎页面。下例为一个叫做Modern的servlet,它的类为ModernServlet.class,并已被映射到path /Modern。

<servlet>
  <servlet-name>Modern</servlet-name>
  <servlet-class>ModernServlet
  </servlet-class>
</servlet>
<servlet-mapping>
  <servlet-name>Modern</servlet-name>
  <url-pattern>/Modern</url-pattern>
</servlet-mapping>
<welcome-file-list>

  <welcome-file>Modern</welcome-file>
</welcome-file-list>

此时,若用户键入诸如http://domain/context/(不带资源文件)的URL时,就会调用ModernServlet。

对国际化的新支持。在Servlet 2.3中,没有办法直接告诉客户浏览器应当使用什么字符编码。要实现这一目的,你必须把一个java.util.Locale对象传递给javax.servlet.ServletResponse接口的setLocale方法,如下所示:

response.setLocale(locale);

这意味着你必须首先创建一个Locale对象。

另外一种办法是,在Servlet 2.3中,你可以使用setContentType方法来传递内容类型和字符集,如:

setContentType('text/html;
               charset=UTF-8');

在Servlet 2.4中,javax.servlet.ServletResponse接口中有两个支持国际化的新方法。第一个方法是setCharacterEncoding,它的用法如下:

public void
setCharacterEncoding(String charset)

使用setCharacterEncoding,你可以只将字符编码指定为一个字符串,而不必先创建Locale对象。不过,请注意,要让这种方法起作用,必须在调用getWriter方法之前以及响应提交之前调用它。

第二个新方法是getContextType,作为在ServletResponse对象中调用setContentType、setLocale或setCharacterEncoding方法的结果,它返回在ServletResponse对象中使用的内容类型。

除了javax.servlet.ServletResponse中的这两个方法之外,你还可以利用Servlet 2.4在部署描述符中定义一个新元素: 它使servlet程序员不必在他/她的servlet中指定locale-to-charset映射。如何使用这一新元素的例子如下:

<locale-encoding-mapping-list>
  <locale-encoding-mapping>
    <locale>ja</locale>
    <encoding>ISO-2022-JP</encoding>

  </locale-encoding-mapping>
</locale-encoding-mapping-list>

部署描述符的可扩展性。在Servlet 2.3应用程序中,根据DTD文件对部署描述符进行验证。现在Servlet 2.4支持根据模式对部署描述符进行验证。使用模式比使用DTD有以下几点好处:

  • 通过模式可以继承另一个模式(可扩展的)的语法。

  • 模式比DTD更精确。

  • 通过模式可以指定每个元素的内容的实际数据类型。

  • 模式可以用于多个名字空间。

  • 通过模式可以指定一个元素出现的最多和最少次数。

但是,为了向后兼容,要求Servlet 2.4容器支持Servlet 2.3和Servlet 2.2 DTD。

不支持javax.servlet.SingleThreadModel接口。SingleThreadModel接口没有方法,它用于向servlet容器指明,它必须保证不会有两个线程同时执行实施该接口的servlet的服务方法。从servlet技术开始出现到现在,人们普遍误解了这个接口。现在大家都反对用它,因为它会造成混乱,并且在考虑线程安全时在安全性方面给servlet程序员一个错觉。在任何新的开发工作中决不应再使用这个接口。

JSP 2.0中的新特性

JSP 2.0(最初称为JSP 1.3)比JSP 1.2有了重要改进。当然,增加的最重要内容是JSP 2.0容器中加入了对表达式语言(EL)的支持。

EL最初是由JSP标准标记库(JSTL)1.0规范定义的,它可协助从JSP页面中删除Java代码。javax.servlet.jsp.el包中所描述的API揭示EL的语义。EL表达式的语义与Java表达式的语义类似;表达式的值计算出来后被插入到当前的输出中。EL可用于标准的或定制的操作的属性值以及模板文本中。下面是EL表达式的结构(其中expr为表达式):

${expr}

对于包含字符序列"${"的文字值,JSP 2.0提供了一种方法,通过使用序列"${'${'"进行换码。例如,下面的字符序列被转换为文字值${expr}:

${'${'}expr}

此外,由于JSP 2.0以前的版本不支持EL,所以JSP应用程序将忽略任何Web应用程序中的EL,这些应用程序的web.xml根据Servlet 2.2或Servlet 2.3 DTD进行验证。为了测试此处讲到的JSP页面中的表达式,你只需从应用程序中删除web.xml文件。

实际上,EL是一种简单的语言,它帮助页面创作者访问JSP隐含对象,进行反复操作以及不包含Java代码的条件操作--这些在JSP 1.2中是无法实现的。

为了访问隐含对象,JSP容器支持下面的名称-对象映射:

  • pageContext。PageContext对象

  • pageScope。将页面范围的属性名映射到它们的值

  • requestScope。将请求范围的属性名映射到它们的值

  • sessionScope。将会话范围的属性名映射到它们的值

  • applicationScope。将应用程序范围的属性名映射到它们的值

  • param。将参数名映射到一个单一串参数值

  • paramValues。将参数名映射到该参数所有值的一个字符串数组

  • header。将标头名映射到一个单一串标头值

  • headerValues。将标头名映射到该标头所有值的一个字符串数组

  • cookie。将cookie名映射到一个单一cookie对象

  • initParam。将上下文初始化参数名映射到其字符串参数值

例如,下面的表达式表示参数userName的值:

${param.userName}

下面的表达式返回Session对象的productId属性的值:

${sessionScope.productId}

更简单的SimpleTag接口操作过程。JSP 2.0提供了一个新的接口javax.servlet.jsp.tagext.SimpleTag,它是编写标记处理器(tag handler)的一种更简单的方法。在JSP 1.2中,标记处理器必须直接或间接地实施avax.servlet.jsp.tagext包中的下列接口之一:Tag、IterationTag或BodyTag。对于实施Tag接口的标记处理器来说,最基本的情况是,JSP容器每次遇到JSP页面中的一个标记时就调用doStartTag和doEndTag两个方法。利用JSP 2.0,JSP程序员可以通过实施新的SimpleTag接口来选择实施过程更简单的标记处理器。JSP容器并不调用实施Tag接口的标记处理器的两个方法,而只需要调用SimpleTag接口中的一个方法:doTag。所有标记逻辑、反复操作和主体评估等都用这一个方法来执行。所以,SimpleTag与javax.servlet.jsp.tagext.BodyTag功能一样强大,但操作过程更简单。

为了支持需要实施SimpleTag接口的标记处理器的编写,javax.servlet.jsp.tagext包提供了一个名为SimpleTagSupport的支持类。如果你要扩展这个类,则你只需提供一个执行方法:doTag。

代码清单3给出了一个扩展SimpleTagSupport的标记处理器的例子。

使用标记文件更轻松地开发标记库。众所周知,JSP 1.2中的自定义标记库需要花很多时间来开发。开发工作涉及标记处理器和标记库描述符(TLD)文件的开发,以及标记库在web.xml文件中的注册。JSP 2.0通过提供一种新的编写自定义标记库的方法解决了这个问题。使用标记文件,标记扩展可类似于JSP文件。无需编译,无需编辑web.xml文件,而且不再需要TLD。要做的是你必须把标记文件复制到WEB-INF/ tags目录中,而这一点很容易做到。剩下的事都交给JSP容器去做,它会把WEB-INF/tags目录中找到的每个标记文件转换为标记处理器。程序员完全摆脱了构建标记处理器的复杂工作。

下面举个例子。这是标记库最简单的形式,其中标记文件只是简单地把一个字符串写到隐含对象中。

<%— example1.tag file, must reside in 
      WEB-INF/tags —%>

<%
  out.println("Hello from tag file.");
%>

使用JSP页面中的标记库再简单不过了。和平常一样,你只需taglib指令,通过前缀属性在整个页面中识别标记库。现在你有一个tagdir属性,而不是uri属性。tagdir属性引用WEB-INF/tags目录或WEB-INF/tags下的任何子目录。

下一步

阅读
关于Servlet技术的更多内容

java.sun.com/products/servlet

关于JSP技术的更多内容
/java.sun.com/products/jsp

关于JSTL的更多内容
otn.oracle.com/oramag/oracle/03-may/o33java.html

下面是一个使用example1.tag文件的JSP页面的例子。

<%@ taglib prefix="easyTag" 

          tagdir="/WEB-INF/tags" %>
<easyTag:example1>
</easyTag:example1>

调用该JSP页面浏览器上就会显示下面的字符串:

Hello from tag file.

结合上面讲到的表达式语言,你就可以真正快速构建无脚本的JSP页面。再举一个例子,下面的标记文件(叫做example2.tag)通过调用JSP页面接收一个属性,并将它转换为大写字母。

<%— example2.tag file, must reside 
    in WEB-INF/tags —%>
<%@ attribute name="x" %>
<%
  x = x.toUpperCase();
  out.println(x);
%>

下面是使用该标记文件的JSP页面:

<%@ taglib prefix="easyTag" 

          tagdir="/WEB-INF/tags" %>
<easyTag:example2 x="hello">
</easyTag:example2>

下面是另一个例子,其中没有Java代码:

<%— example3.tag file, must 
    reside in WEB-INF/tags —%>
<%@ variable name-given="x"
            scope="AT_BEGIN" %>
<%@ taglib prefix="c"            
          uri="http://java.sun.com
/jsp/jstl/core" %>
<c:set var="x" value="3"/>
After: ${x}
<jsp:doBody/>

该标记文件用于下面的JSP页面:

<%@ taglib prefix="c"
          uri="http://java.sun.com
/jsp/jstl/core" %>
<%@ taglib prefix="easyTag"
          tagdir="/WEB-INF/tags" %>
<c:set var="x" value="1"/>

Before: ${x}<br>
<easyTag:example3/>
请注意,要运行本示例,在WEB-INF/lib目录下要有JSTL库。

最后一个标记文件示例还表明,不熟悉Java编程语言的页面创作者仍能利用标记扩展的强大功能。即便是Java程序员,使用标记文件也比编写实施javax.servlet.jsp.tagext包中的某个接口的Java类要方便。

结论

本文简要阐述了Servlet 2.4和JSP 2.0规范中的新特性,它们将包含在即将面世的J2EE 1.4中。Servlet 2.4和JSP 2.0无疑将会加快Web应用程序的开发。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值