即将面世的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对象属性的添加、更改和删除。该接口有以下方法:
这三个方法从servlet容器获得javax.servlet.servletrequestattributeevent类的一个实例。servletrequestattributeevent类扩展了servletrequestevent类,并添加了两个新方法:getname和getvalue。getname方法返回触发事件的属性的名称,getvalue返回属性的值。
代码清单1 给出这两个新的监听器的示例类。当servlet容器调用方法时二者都显示方法名。监听器经过编译后,它们的类文件必须被部署到web-inf/classes目录下。servletrequest中的新方法。在servlet 2.4中,javax.servlet.servletrequest接口增加了4个新方法:
请注意,在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_urijavax.servlet.forward.context_pathjavax.servlet.forward.servlet_pathjavax.servlet.forward.path_infojavax.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/modernjavax.servlet.forward.context_path : /myappjavax.servlet.forward.servlet_path : /modernjavax.servlet.forward.path_info : nulljavax.servlet.forward.query_string : null
将过滤器用于请求调度程序。servlet 2.4在部署描述符中添加了一个新的
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中,你可以在部署描述符中使用
<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 voidsetcharacterencoding(string charset)
使用setcharacterencoding,你可以只将字符编码指定为一个字符串,而不必先创建locale对象。不过,请注意,要让这种方法起作用,必须在调用getwriter方法之前以及响应提交之前调用它。
第二个新方法是getcontexttype,作为在servletresponse对象中调用setcontenttype、setlocale或setcharacterencoding方法的结果,它返回在servletresponse对象中使用的内容类型。
除了javax.servlet.servletresponse中的这两个方法之外,你还可以利用servlet 2.4在部署描述符中定义一个新元素:
<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有以下几点好处:
但是,为了向后兼容,要求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容器支持下面的名称-对象映射:
例如,下面的表达式表示参数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下的任何子目录。
下面是一个使用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应用程序的开发。
新闻热点
疑难解答