Servlet 2.4:又有哪些新变化(转)
概要
2003.3.7 SUN发布了Servlet 2.4的PFD2,在本文中Jason Hunter介绍了2.4和2.3版本的一些区别,并解释了这样改变的一些原因,同时还介绍利用和使用这些新的特性...
作者Jason Hunter
英文原文
2003
年3月7日Sun Microsystems发布了Servlet 2.4标准的"Proposed Final Draft 2"。由于它仍然处于
Proposed Final Draft阶段,规范还没有完全完成,某些技术细节可能会改变。甚至有可能会制订
Proposed Final Draft 3。然而,在这个标准的最终发布之前将不会再出现重大的改变,事实上,server开发商已经开始实现这些新
特性。现在正是学习Servlet 2.4的新内容和检查它与J2EE(Java 2平台企业版)1.4的集成关系的好时机。
在这篇文章中,我描述了2.3和2.4之间的不同之处。并且解释了这些变化的原因,还告诉你没有按原先目标制定的一些事情。
Servlet 2.4
缺乏一些例如在过去的发布中能够令人兴奋的事物。Servlet 2.2引入了self-contained Web applications的概念。
Servlet 2.3增加了filters和filter chains的功能。Servlet 2.4加入了几个引起关注的特性,没有特别突出的新内
容,而是在推敲和阐明以前存在的一些特性上花费了更多的功夫—对一些不严谨的地方进行了校验。工作的效果是使得按照2.4规范实现的servers将比以
往任何servers更具有互操作性。但是这并不暗示Servlet 2.4一点新的东西也没有!以下便是更新之处:
· Servlets需要支持HTTP/1.1和J2SE(Java 2平台标准版)1.3,并且能够运行在J2EE 1.4中
· ServletRequest增加了查询client connection的新方法
· 国际化和选择字符集方面新的支持
· RequestDispatcher增加了新特性和说明
· 新的ServletRequest listener类和方法
· 不再推荐使用SingleThreadModel类
· 明确HttpSession中有关logins的细节和交互
· 明确Classloading和welcome-file的行为
· web.xml文件使用XML Schema并且增加了许多新的元素
在
我们开始着眼于这些改变之前,我要说明大多数的servers仍然没有兼容于Servlet 2.4。如果你想试验这些特性的话,你最好下载官方推荐参考
的实现server,Apache Tomcat 5.0。Apache Tomcat 5.0开放源代码,你可以免费下载。Tomcat 5.0将会是
第一个支持Servlet 2.4的Tomcat版本,但是现在最新release的Tomcat还是一个alpha版本。
对HTTP,J2SE,和J2EE的升级支持
Servlet 2.4
现在需要支持HTTP/1.1和J2SE 1.3。在以前,servlets是建立在HTTP/1.0和J2SE 1.2的基础上的。2.4对这些最低需
求的升级意味着servlet的开发者们能够信赖的依靠HTTP/1.1和J2SE 1.3的新特征。与此同时,由于HTTP/1.1中存在更多特殊的情
况并且比HTTP/1.0更复杂,这些必要条件也使得servlet container的开发者的任务更加复杂化。一部分的servers已经对
HTTP/1.1提供了支持,但是那些还没有提供支持的servers将不得不花费时间来升级它们的servers。请注意,以下的想法是存在问题的,使
用J2SE作为一个最低需求并不意味着如果你使用J2SE 1.2实现了Servlet 2.4,就成功的完成了任务。这种做法破坏了servlet容器
开发者之间的约定,请记住这个约定的内容,当开发Servlet 2.4时,可以使用并依赖于J2SE 1.3的特性。
另一个变化是
Servlet 2.4将会成为即将出台的J2EE 1.4的一部分(实际上这两个标准将会同时发布,很有可能是在JavaOne 2003的时候)。请
注意servlets自然也可以独立的运行;并不需要购买一个完整的J2EE container来运行servlets。例如
Apache Tomcat就没有实现J2EE的所有功能。但是当把servlets作为J2EE 1.4的一部分运行的时候,你能?利用那些额外的特性
和额外的显露JNDI(Java命名和目录接口)资源的发布描述符元素,EJB(企业JavaBeans),消息队列,和JAX-RPC (基于XML的
远程过程调用Java API)服务。这些元素将会在后面的章节被提到。
升级到HTTP/1.1导致了一个地方的代码的改变。
Servlets用一个新的静态的常量HttpServletResponse.SC_FOUND表示status code 302。Found是在
HTTP/1.1中所使用的名字,在HTTP/1.0叫做Moved temporarily。
HttpServletResponse.SC_MOVED_TEMPORARILY仍然存在并代表302,但是现在SC_FOUND却是更合适的选择。
不再推荐使用SC_MOVED_TEMPORARILY,但是不再推荐使用变量,而且还是public类型的常量,在Java中以技术这个角度来说是不可
能的。
新的ServletRequest方法
在Servlet 2.4中ServletRequest接口(和ServletRequestWrapper类)添加了四个新的方法:
· getRemotePort():获得发送request的客户端或是最后一个代理服务器的端口号
· getLocalName():获得接收到request的IP地址机器的主机名
· getLocalAddr():获得接受到request的IP地址
· getLocalPort():获得接受到request的IP的端口号
这
些方法提供了查询low-level的IP connections的细节和理解connections是如何被路由的一种机制。
getRemotePort()方法与原先存在的getRemoteAddr()和getRemoteHost()方法组合在一起,用于获得client
端的IP connections。新的getLocalPort(),getLocalAddr()和getLocalPort()方法用于获得
server端的IP connections。原先存在的getServerName()和getServerPort()方法被重新定义用来获得
HTTP层的细节,它们解析HTTP Host头并返回"主机名:端口"。在一个虚拟主机或用于负载平衡的系统上,这些方法提供了如何知道有哪些
clients,proxies或起负载平衡作用的设备与servlet服务端通信的方式,而不论是物理上的直接连接还是以一种虚拟的模式进行连接。
国际化
在Servlet 2.4中,ServletResponse接口(和ServletResponseWrapper类)添加了两个新方法:
· setCharacterEncoding(String encoding):
设置response的字符编码。这个方法对向setContentType(String)方法传递一个字符集参数或向
setLocale(Locale)方法传递一个Locale参数提供了一种代替的方式。如果在调用getWriter()方法后或是response提
交后调用这个方法,则没有任何作用。
· getContentType():获得response的内容类型。返回值中可能包含一个使用
setContentType()、setLocale()或setCharacterEncoding()方法设置的字符集的参数。如果没有指定类型的
话,方法返回一个空值。
setCharacterEncoding()方法与原先存在的getCharacterEncoding()方
法组成一对,为设置和查看response的字符集编码(charset)提供了一种简单的方式。从现在开始可以避免使用难看的
setContentType("text/html; charset=UTF-8")方法调用来设置字符集了。
新的
getContentType()方法与原先存在的setContentType()方法组成一对,把曾经赋值的内容类型显示出来。以前,这没有太大意
思,但是现在MIME类型可以通过调用setContentType()、setLocale()和setCharacterEncoding()方法进
行动态的组合性的设置,而这个方法提供了查看生成的字符串类型的方式。
setLocale()和
setCharacterEncoding()那个更好?这视情况而定。在以前由你来指定locale,如对日本则指定ja,但是确定一个合适的字符集的
事情却留给container来处理。这虽然很方便,但是许多字符集只在某种特定的locale才能运行正常,并且开发者没有其他的选择。最新的这个方法
为选择一个特定的字符集提供了一个暂新而且容易的途径,例如你可以使用Shift_JIS字符集重载container中设定的EUC-JP字符集。
对
国际化的支持还有其他内容。Servlet 2.4还在web.xml发布描述符中引入了一个新的<locale-encoding-
mapping-list>元素,可以让发布人员在servlet代码外面指定locale到charset的映射。如下所失:
[pre]
<locale-encoding-mapping-list>
<locale-encoding-mapping>
<locale>ja</locale>
<encoding>Shift_JIS</encoding>
</locale-encoding-mapping>
<locale-encoding-mapping>
<locale>zh_TW</locale>
<encoding>Big5</encoding>
</locale-encoding-mapping>
</locale-encoding-mapping-list>
[/pre]
现
在在Web application中,任何被指定为ja locale的response将使用Shift_JIS字符集,而任何被指定为
zh_TW Chinese/Taiwan locale的response将使用Big5字符集。也可以在以后设置成UTF-8以对更多的
clients都适用。没有被列出的locales将象以前一样使用由容器所指定的缺省值。
RequestDispatcher的变化
Servlet 2.4
增加了五个新的request属性用来在调用RequestDispatcher的forward()方法时支持获得额外的信息。你是否已经忘记了,当你
用forward()传递request给servlet时,servlet container改变目的servlet的路径环境就好像目标
servlet是第一个被调用的servlet。getRequestURI()、getContextPath()、
getServletPath()、getPathInfo()和getQueryString()方法的返回值全都是基于传递给
getRequestDispatcher()方法的那个URI(同一资源标识符)。然而,某些情况下一个forward()方法的目标servlet可
能会需要知道真正原始的request URI。为此,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
观
察一个forwarded servlet你会看到getRequestURI()方法总是返回目标servlet的路径,但是现在如果你想得到原始的路
径,你可以调用request.getAttribute("javax.servlet.forward.request_uri")。这有一个特殊的
告诫:如果forward()方法是由调用getNamedDispatcher()方法后触发的,这些属性不会有你想要的值因为在这种情况下原始的路径
元素根本就没有改变。
这组属性可能会使你想起在Servlet 2.2中加入的request属性:
· javax.servlet.include.request_uri
· javax.servlet.include.context_path
· javax.servlet.include.servlet_path
· javax.servlet.include.path_info
· javax.servlet.include.query_string
然
而,这些属性的目的正好与forward()方法属性的目的相对立。在一个include()方法中,路径元素并不改变,因此这些include属性是用
来访问目标servlet路径元素的一种特殊的途径。与改变路径元素的forward()方法相比,forward属性是用来访问原始路径元素的一种方
法。不可否认的是,这使得事情变复杂了。但只要servlets开始使用URI空间作为一种内部派发的机制,使事情复杂化的机会便出现了。
在
另一个我们所看到复杂化的地方是RequestDispatcher和filters之间的交互作用。在传递request和包含request的时候是
否调用filters?对于在<error-page>机制中所指定的URIs的调用又是如何进行的?在Servlet 2.4之前,这些问
题作为公开讨论的主题而被遗留了下来。现在Servlet 2.4 为开发者做出了一个选择,在部署描述符中新增加了一
个<dispatcher>元素,这个元素有四个可能的值REQUEST,FORWARD,INCLUDE,和ERROR。你可以向下面那样
在一个<filter-mapping>元素中加入任意数目的<dispatcher>条目:
[pre]
<filter-mapping>
<filter-name>Logging Filter</filter-name>
<url-pattern>/products/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
[/pre]
这
说明filter将会作用于forward requests和直接来自client端requests。如果再加上INCLUDE和ERROR这两个值
则表示filter也将会对include requests和<error-page> requests起作用。通过混合和匹配设置你想
要的方式,如果没有指定任何<dispatcher>元素,缺省值是REQUEST。
关于
RequestDispatcher最后的改变是第一次允许在调用request.getRequestDispatcher()方法的时候使用相对路
径。路径相对于当前request的路径,尽管这是一个微小的变化,但是在dispatch到相邻的servlet的时候是派得上用场的。
监听器
Servlet 2.3
引入了context和session listeners的概念,当context 或session被初始化或者被将要被释放的时候,和当向
context或session中绑定属性或解除绑定的时候,可以对类进行监测。Servlet 2.4对模型进行扩展并且加入了
request listeners,允许开发者(或者更可能是工具开发商)在requests被建立和释放的时候和在request中添加和移去属性的
时候,进行观察活动。Servlet 2.4中加入了下面的这些类:
· ServletRequestListener
· ServletRequestEvent
· ServletRequestAttributeListener
· ServletRequestAttributeEvent
这
些类与熟知的ServletContextListener、ServletContextEvent、
ServletContextAttributeListener和 ServletContextAttributeEvent的设计思想类似,并且使
用相同的<listener>元素来指定它们的执行。增加request listeners主要的任务是用来帮助调试工具对request
的处理进行跟踪,实际中的应用程序使用它们用于别的用途的时候很少见,在这不讨论细节。
Servlet 2.4同样试图澄清当一个
listener掷出异常的时候发生什么事情,因为listener经常调用到了service()方法范围之外的堆栈,异常不能传播给servlet进
行处理。这个问题在Proposed Final Draft 2中仍然还没有确定下来,但是可能的结果是如果<error-
page>directive存在的话,listener异常将会由directive进行处理,如果directive不存在的话,将会产生一个
简单的错误号为500的错误显示给client。
Session的变化
也许在Servlet 2.4中增加的最受
欢迎的方法是:HttpSession.logout()。这个方法规定了一种可靠的对以标准的<auth-method>机制
(BASIC、DIGEST、FORM、CLIENT-CERT)之一登录的用户进行注销的机制。 然而不幸的是,这个方法也可能是在2.4的最终发布版
本中必须被抛弃的方法。把logout()方法的调用点放在session中是在假定session管理login的时候,但是当使用
FORM logins的时候,session并不对其进行管理。在BASIC、DIGEST和CLIENT-CERT方式的认证机制中,client保
存着login的证明信息并且在request的时候把这些信息告诉server,你可以调用logout()方法并清空session,但是
client将会仍然发送有效的认证信息。Server并没有一种使client端停止发送这些信息的一种可靠的方法。这就是为什么有如此多的
Webpages在使用form-based的登录方式,因为这允许通过使session无效或者删除client端的cookie进行注销处理。
2.4
中另一个session的改变是允许在<session-timeout>元素中设置0或者负值用来说明sessions将永不超时。通常来
说,避免使用极限值,但是在某些场合下,在一些可以信赖的情况下手动的调用invalidate()方法也被证明是非常好用的,但要谨慎的使用。
最
后,Servlet 2.4的改变是如果分布式session所拥有的一个对象不能被序列化或者不能被正常的传递,则这个session必须掷出
IllegalArgumentException异常,而不是原先所规定的有可能掷出IllegalArgumentException。这个附加的约
束将会通过避免产生无记载的故障而提高可移植性。
各种明确和说明
在Servlet 2.4中,我最可能喜欢的澄清
之处就是:缺省的欢迎文件可以是servlets。这意味着具有*.tea处理器的index.tea文件可以象index.html或
index.jsp文件一样成为缺省被访问的文件。大多数的servers已经支持这一点,也有不支持的servers,如果要实现
Servlet 2.4,则所有的servers都必须支持这一点。
其他明确的说明是任何不在WEB-INF结构中而对
container可见的库文件(如Tomcat中从$CATALINA_HOME/shared/lib路径下所加载的JARs文件)必须以相同的
classloader并在一个独立的得JVM中进行加载。这可以避免潜在的ClassCastException问题,而加强inter-Web应用程
序的application之间的联系.
不推荐使用的对象
在Servlet 2.4中不再推荐使用的只有一个
类,确实不再应该使用这个类。从开始就不正当的设计SingleThreadModel (STM),在2.4中不再推荐使用。STM被废弃了!多线程编
程万岁!尽管STM给人的第一眼的印象很好,但是由于STM所强加的可替换的生命周期实际上除了线程安全以外没有提供其它的有用之处,其实它只不过是对安
全的一种错误理解。专家组全体一致决定不在使用它。
Schema
我所谈到的最后的一些变化不是代码上的改变,而是
格式上的一些改变。以前web.xml文件使用文档类型定义(DTD),而现在使用的是由W3C(World Wide Web Consortium)
的XML Schema语言定义。版本为2.4的servers必须仍然可以认可2.2和2.3的发布描述格式,但是所有新的元素是在Schema中所单
独进行指定的。
Schema 是一种比DTDs更详细的语言,相比来说,某些地方表达得更清楚,而有些地方则相反。增加了一些约束,
如<role-name>应该是唯一的;也放松了一些约束,如在<web-app>中直接的子元素的顺序不再是固定的,还
有<distributable/>元素可以多次出现而不报错. 还有<description/>标签现在支持
xml:lang类型的属性用来指出在description中如果不使用英语的话,使用哪一种语言。
简单的
servlet containers并不需要对Schema进行验证,但是J2EE containers需要。规范提醒开发者,"发布描述符中所使用
的Schema必须是有效的,"这对增强可移植性是一个很好地建议。在大多数地方,从DTDs到XML Schema的转变不会影响到普通的
servlet编程者,但是如果你不加入一些帮助说明的话,对web.xml文件格式的理解将会变得困难。
Schema的一个特征(或者
说是bug,这主要依赖于你看它的方式)就是在web.xml文件中的元素可以在其它的J2EE规范的Schema文档中进行定义。因此当
Servlet 2.4的web.xml 的schema提到<message-destination>、<message-
destination-ref>,<service-ref>的时候并说明它们可以出现在web.xml文件中的时候,这些元素和其
子元素的实际的定义是从外部引进来的。同样,一些在以前的的Servlet规范中定义元素虽然已经被删除,但是仍然可以用名字引用这些现在却是定义在
J2EE标准中的元素。这些元素包括<env-entry>、<ejb-ref>、<ejb-local-
ref>、<resource-ref>、<resource-env-ref>和它们所有可能存在的子元素。你将会看
到<jsp-config>元素的定义已经被移到JSP (JavaServer Pages)标准中,尽管它的名字仍然在
Servlet schema中出现。
假定引入这些元素,那么如何进行管理?这还没有一个明确的解释,并且这一定看起来有些奇怪,一个在
栈底的技术如servlets要去参考它上面的技术,例如说在JAX-PRC中是如何引入<service-ref>元素的。这有点像理解
TCP/IP就必须需要知道HTTP。Sun发布言论说一个独立的servlets不是被非常需要优先进行考虑的,这可以用来解释这种集成性的设计吧。让
我们密切注意这种紧密的结合方式随着标准的继续更新是如何被处理的吧。
没有哪些
版本 2.4中去掉了一些曾经讨论
过的提议。一个就是Schema的可扩充性,它一直存在到Public Final Draft阶段但是在Public Final Draft 2被删
掉了。这个可扩充性提议的意图是提供一种方式,这种方式可以向web.xml文件中加入第三方的元素。但是在专家组的要求下被去掉了,因为只使用一个单一
的文件进行配置管理很快就会产生一堆难于运转或不能正常工作的烦人事,就像把你所有的源代码都放到一个文件中一样。
一小部分其他的内容被
延期。其中一个就是新I/O(输入/输出)API,这个J2SE中令人兴奋的特征能够极大的加速client和server的通讯。它是一种新的
channel概念,让你在系统内存和内存映像文件中进行缓冲处理,平衡DMA (直接内存访问),和扩展/聚集硬件设备的I/O特征。不幸的是,如果
Servlet 2.4使用新的I/O 和channels处理,J2SE 1.4将会成为最低的必要条件,但我们认为这样做还是不成熟的。尽管
Server开发商可以使用新的I/O去实现,但是servlets将不能充分利用新I/O直到它们能够使用真正的channel机制和client进行
通信的时候。
还有一部分没有被包含的规定如在同一个SERVER中有关HTTP和HTTPS之间的交互性是什么样的,Sessions应
该是相同的,还是必须有所区别? 是否可以使用forward()和include()方法,还是应该使用sendRedirect()方法?也许这些问
题会在下一个发布的版本中被确定下来。
总结Servlet 2.4
从前面所描述的,Servlet 2.4增加
了新的最低需求,监测request的新方法,处理response的新方法,新的国际化支持,RequestDispatcher的几个处理,新的
request listener类,session的描述,和一个新的基于Schema的并拥有J2EE元素的发布描述符。这份文档规范全面而严格的进
行了修订,除去了一些可能会影响到跨平台发布的模糊不清的因素。总而言之,这份规范增加了四个新类,七个新方法,一个新常量,不再推荐使用一个类。从
2.3到2.4的变化一览,请看下面这个表
[pre]
2.3到2.4的改变
不推荐使用的类 SingleThreadModel
不推荐使用的方法 无
新增加的类 ServletRequestAttributeListener,ServletRequestAttributeEvent,
ServletRequestListener,ServletRequestEvent
新增加的方法 ServletRequest.getRemotePort(),ServletRequest.getLocalName(),
ServletRequest.getLocalAddr(),ServletRequest.getLocalPort(),
HttpSession.logout() (有可能被去掉),ServletResponse.getContentType()
ServletResponse.setCharacterEncoding()
新增加的常量 SC_FOUND 对应302,因为HTTP/1.1改名
新增加的标签 <dispatcher>,<locale-encoding-mapping-list>,<locale-encoding-
mapping>,<locale>,<encoding>
Broken-out 标签 <env-entry>,<ejb-ref>,<ejb-local-ref>,<resource-ref>,<resource-
env-ref>,<jsp-config>,和他们的子元素
新增加的J2EE标签 <message-destination>,<message-destination-ref>,<service-ref>,
和他们的子元素
[/pre]