[ZT]Java Servlet Specification Version 2.3笔记

来源:互联网 发布:怎么防止淘宝店铺关闭 编辑:程序博客网 时间:2024/06/10 03:42


____________________________________________________________________________

Java Servlet Specification  Version 2.3

第一章 概要

1.1 什么是servlet
        servlet是一种基于web组件的java技术,由容器管理,产生动态内容。象其他基于java的组件一样,servlet是不依赖平台的的java类,被编译为中间字节码,可被动态装载运行于支持java的web服务器上。这里说的容器,有时也称它为servlet引擎,提供Servlet功能的web服务器扩展,servelt通过一种由servlet容器实现的request/response范式(paradigm)与web客户机交互。

1.2什么是servlet容器
        servlet容器是web服务器或应用服务器的一部分,它们提供处理处理request并发送response的网络服务,解析基于MIME的request,准备基于MIME的response。servlet容器包含并管理着servlet对象的一生。
        servlet容器可以被嵌入web服务器主机,或者通过服务器本地扩展API安装为web服务器的一个组件。同时,它也可以被嵌入到支持web功能的应用服务器中。
        所有的servlet容器必须支持以HTTP作为request和response的协议,但额外的协议如HTTPS可以支持。应支持的HTTP最低版本为HTTP/1.0,强烈议也支持HTTP1.1规范。
        servlet容器可以在其执行环境中放置安全制约。Java2平台,标准1.2或者J2EE1.3环境,应当使用由java2平台定义的许可架构下的约束。例如,高端应用服务器可以限制线程对象的创建,确保容器内的其他组件免收影响。J2SE1.2是构建servlet容器的最低版本。
        
1.3 例子
以下是一系列典型的场景序列:
        1.客户机(即,浏览器)访问web服务器,并发送HTTP请求。
        2.web服务器接收到了这个请求,并向下传递给了servlet容器。servlet容器可以与web服务器运行在同一进程里,也可以在同一主机上的不同进程里,或者运行于不同主机上。
        3.servlet容器使用servlet配置信息而决定调用哪一个servlet,同时向其传递request和response对象参数。
        4.servlet利用request对象得到远端用户信息,HTTP POST参数是什么,以及其他相关数据。servlet执行其编码逻辑,并产生数据,通过response对象送回客户机。
        5.一旦servlet完成处理request,容器应确保response对象被flush,并将控制权返回web服务器。

1.4 和其他技术的比较
        功能上,servlet居于CGI和服务器扩展(如Netscape Server API或 Apache modules)之间。
        但servlet还具有一些其他服务器扩展技术没有的先进性:
        .它们通常比CGI脚本运行快。
        .它们使用很多web服务器都支持的标准API。
        .它们具有所有java编程语言具有的优点,包括开发简单平台独立等。
        .它们可以访问java平台大量的API。

1.5与J2EE的关系
        Servlet API v2.3 是JavaTM 2 平台, 企业版 v1.31的必需的API 。servlet容器和发布servlet必须满足其他的需求在J2EE规范中都有描述。

第二章 Servlet接口

        servlet接口是servlet主要抽象的API。所有servlet都需要直接实现这一接口或者继承实现了该接口的类。servlet API中有两个类实现了Servlet接口,GenericServlet和HttpServlet。大多数情况下,开发人员只需要在这两个类的基础上扩展来实现他们自己的Servlet。

2.1 request处理方法
        servlet接口定义了service方法来处理客户机的请求。当容器将每个请求传递给servlet实例处理时都会调用该方法。为应付同时到达的请求,通常要求web开发人员编写的service方法可以多线程执行。
        开发人员在不同线程内并发执行service方法来处理同时到达同一servlet的多个请求。

2.1.1 HTTP规范request处理方法
        HttpServlet,实现了Servlet接口的抽象类,添加了一些附加的方法处理HTTP请求,由service方法自动调用。这些方法是:
        .doGet处理HTTP GET request
        .doPost处理HTTP POST request
        .doPut处理HTTP PUT request
        .doDelete处理HTTP DELETE request
        .doHead处理HTTP HEAD request
        .doOption处理HTTP OPTION request
        .doTrace处理HTTP TRACE request
        
        在开发HTTP Servlet的时候,开发人员一般关注doGet和doPost方法.其他方法在HTTP开发时很少用.

2.1.2 附加方法
        doGet和doPost方法允许开发人员支持HTTP1.1特性.doHead()方法实际是一种特殊的doGet方法,它只返回由doGet产生的头信息.doOption
方法返回servlet支持的所有HTTP方法的信息.doTrace方法产生的响应包含TRACE请求发送的所有头实例.
        对于仅支持HTTP/1.0的容器,只需支持doGet,doPost,doHead方法.因为HTTP/1.0没有定义PUT,DELETE,OPTIONS,TRACE方法.

2.1.3 条件Get支持
        HttpServlet接口定义了getLastModified方法,目的是支持有条件的GET操作.一个有条件的GET操作所请求的资源只有在指定时间内被修改了的情况下才需要发送.在某些情形下,实现该方法可以更加有效地利用网络资源.

2.2 实例的数量
        servlet声明包含在web应用配置描述内,控制着容器如何提供servlet实例。因为servlet不运行于分布式环境(缺省),容器为每个Servlet声明维护一个实例.然而,当servlet实现SingleThreadModel接口时,容器可以持有多个servlet实例来应付繁重的请求,但要保证一个实例每次只处理一个请求.

        当servlet被发布到分布式应用时,容器可以在每个虚拟机中为每个servlet声明持有一个实例.如果实现SingleThreadModel,可以在每个虚拟机中为每个声明持有多个servlet实例.

2.2.1 注意单线程模式
        SingleThreadModel只允许某一时刻只能有一个线程执行指定的servlet实例的service方法.应该注意到,此保证只针对servlet实例而言,因为容器可以选择池化这些对象.多个servlet实例可以同时访问的对象,例如HttpSession对象,在任意时候对多个servlet都是可用的,哪怕这些servlet实现了singleThreadModel.

2.3 servlet生命周期
        servlet的生命周期定义了装载,实例化,初始化,处理客户机请求,卸载等等。用API表示生命周期就是init,service,和destroy方法.

2.3.1 装载和实例化
        容器负责servlet的装载和实例化.它们可以发生在容器启动时,也可以延迟到容器认为需要该servlet处理request时.
        当容器启动时,容器必须可以定位所需要的servlet类,并使用Java类装载机构装载servlet类.从本地文件系统中,或从远程文件系统中或者从其他网络服务中都可以装载servlet类.
        装载完成后,容器实例化该类.
        servlet对象被实例化后,容器必须在使用它处理客户机的请求之前初始化这个servlet实例.初始化的意义是srvlet对象可以读取一些持久性的配置数据,或者初始化某些重量级型资源(比如JDBC连接),或是执行某些一次性的行为.容器使用init方法并传递一个单例SrvletConf ig对象参数(每个servlet声明只有一个).该配置对象允许servlet访问应用配置信息中的初始化参数,同时提供了一个实现了ServletContext接口的对象来访问servlet运行环境的信息.

2.3.2.1 初始化的错误条件
        在初始化期间,servlet实例可以抛出UnavailabelException或者ServletException异常.发生异常时不能将servlet放进可用服务中,容器必须释放它,因为未被成功初始化,所以无需调用destroy方法.
        初始化失败后,应重新创建一个新的实例并初始化它.规则是,当UnavailabelException指示了最小不可用时间,容器必须等待这个最小时间才创建并初始化新的实例.

2.3.2.2 Tool Considerations
        工具装载并内省web应用而触发类的静态初始化方法有别于调用servlet的init方法.在没有调用init方法前,开发人员不应假定sevlet是在一个活跃的servlet容器运行环境中。也就是说,servlet不应在静态初始化方法里试图建立数据库连接等.

2.3.3处理请求
        当servlet被正确初始化后,容器就可以用它处理客户机的请求了.请求用ServletRequest类型的对象表示,servlet通过调用ServletResponse的对象上的方法填充该请求的响应。这些对象由service方法作为参数传递进来.
        在HTTP请求中,容器提供的这些对象类型为HttpServletRequest和HttpServletResponse.
        一个被容器放进服务序列的servlet实例也有可能在整个生命周期中都不处理请求.

2.3.3.1多线程
        servlet容器可以向servlet的service方法发送并发请求.为了处理这些请求,开发人员必须在他们的service方法中遵守有关的多线程并发处理规则.
        否则,一个可替代的办法是实现SingleThreadModel接口,它需要容器保证在service方法一次只有处理一个请求.容器通过序列化一个servlet上的请求来满足这个需求,还可以通过持有一个servlet实例池的方式.若servlet属于分布式应用的一部分,容器可以在应用分布的每个虚拟机上持有一个servlet实例池.
        若servlet没有实现SingleThreadModel,而是使用synchronized关键字定义了service方法(或doGet,doPost),容器就不会使用实例池,但必须序列式地处理到达此servlet的请求,强烈建议开发人员不要同步service方法,否则会严重影响性能.
        
2.3.3.2 处理请求期间的异常
        servlet在处理request期间可以抛出ServletException或UnavailableException异常. ServletException表明在处理request时发生了一些错误,此时容器应使用适当的方式清除此请求.
        UnavailableException显示该servlet不能处理请求,有可能是暂时的,也有可能是永久性的.
        如果是永久性的不可用,容器必须从服务中移除该servlet实例,并调用它的destroy()方法,然后释放实例.
        如果是暂时性的不可用,容器可以在不可用期间不发送任何请求到该servlet处理.
        在此期间被容器拒绝的请求,必须以SERVICE_UNAVAILABLE (503)状态码返回,同时在HTTP响应头Retry-After中指明何时服务可用.
        容器也可以选择忽略这两种可用性的区别,将所有UnavailableExceptions当作永久性的,而直接移除该servlet实例.

2.3.3.3 线程安全
        request和response对象不是线程安全的,这意味着它们应该只能在处理该请求南叱棠诓渴褂?
        在其他线程中使用被引用的request和response对象将引起不可预期的后果.

2.3.4 终止服务
        容器并不需要永久持有servlet. servlet实例在容器中可能只存在数毫秒,也可能在整个容器的生命周期中都存在(可能是几天,个月,几年)
        当容器决定应该从服务中移除servlet时,它调用servlet接口上的destroy()方法,这样,servlet可以释放它所使用的资源,或者存储任何持久性的状态.
        在容器调用的destroy()方法之前.必须允许正在运行service方法的线程完成执行,或者延迟运行一段服务器定义的时限.
        一旦servlet实例的destroy()方法开始被调用,容器不用再发送其他请求到该servlet实例处理.即使容器需要重新激活该servlet,它也必须重新创建一个实例。
        当destroy()方法调用完成后,容器必须释放该实例,以便它可以被垃圾收集.

第三章 Servlet Context

3.1 介绍ServletContext接口
        ServletContext接口定义了web应用的servlet视图.容器提供者有责任提供对ServletContext接口的实现.通过ServletContext对象,servlet可以记录日志,获得资源的URL引用,存储其他servlet也可以访问的属性.
        ServletContext以Web服务器中一条已知的路径为根,例如,servlet context可以位于http://www.mycorp.com/catalog,所有以/catalog开始的请求被发送到与此ServletContext相关联的web应用.

3.2 ServletContext接口的范围
        每个应用都会有一个ServletContext对象与之关联,当容器分布在在多个虚拟机上时,web应用在所分布的每个虚拟机上都拥有一个ServletContext实例.缺省情况下,ServletContext不是分布式的,并且只存在于一个虚拟机上。
        
3.3 初始化参数
        以下ServletContext接口中的方法允许servlet访问开发人员在配置描述中定义的web应用初始化参数:
        . getInitParameter
        . getInitParameterNames
        应用开发人员使用初始化参数传递构建信息,例如管理员的邮件地址等.
        
3.4 context属性
        可以在context中用名称邦定一个对象属性.context中的任何属性对于同一web应用下的其他servlet都是可用的.下列方法完成了这样的功能:

        . setAttribute
        . getAttribute
        . getAttributeNames
        . removeAttribute

3.4.1 在分布式容器中的context属性
        对于创建它们的虚拟机来说context属性是本地的. 这使得ServletContext属性不能以共享内存的方式存储在分布式的容器中.如果有些信息需要在不同的机器上运行的servlet之间共享的话,它们应该被存储在session中,数据库中,或EJB组件中.

3.5 资源
        使用ServletContext接口可以直接访问web应用中的静态内容文档结构.包括HTML,GIF和JPEG文件.如以下方法:
        .getResource
        .getResourceAsStream
        这两个方法的参数都是以"/"开头的字符串,表示资源相对于context根的相对路径.文档结构可以存在于服务器文件系统,或是war包中,或是在远程服务器上,抑或其他位置.
        以上方法不可以用来获得动态资源,比如,getResource("/index.jsp"),这个方法将返回该jsp文件的源码,而不是动态页面.可以用"Dispatching Requests"获得动态内容.
        列出web应用中可被访问的资源,可以使用getResourcePaths(String path)方法。
        
3.6多主机和servlet context
        web服务器支持在一台机器上共享一个IP的多个逻辑主机,这种能力被称为"虚拟主机",每个逻辑主机都拥有它自己的servlet context。servlet context不能跨虚拟主机共享.

3.7 重新装载
         虽然容器提供重新装载的实现对于开发有益,但不是必须的。这些实现必须确保所有的servlet、和它们所使用的类在单独的类装载器范围内被装载。此机制保证了应用正常运行。
         Previous generations of containers created new class loaders to load a servlet,distinct from class loaders used to load other servlets or classes used in the servlet context. This could cause object references within a servlet context to point at unexpected classes or objects, and cause unexpected behavior. The requirement is needed to prevent problems caused by demand generation of new class loaders.
         (不确定的参考译文:
         容器用来装载servlet的类装载器和ServletContext用来装载servlet和其他类的装载器不同,这导致在servlet上下文之内的对象引用指向不可预期的类或对象, 和不可预期的行为。需要防止由新的类装载器产生的问题.)

3.8 临时工作目录
        每个servlet context都有它自己的临时存储目录,容器必须为每个servlet conetxt提供一个私有的临时目录,存储于context属性javax.servlet.context.tempdir.该对象必须是java.io.File类型.容器启动时,不需要维护该临时目录的内容,但必须确保一个servlet context临时目录的内容对其他web应用(servlet context)是不可见的.

第四章 Request

4.1 HTTP协议参数
        servlet的请求参数是客户机发送给servlet容器的字符串,它是请求的一部分.当请求是一个HttpServletRequest对象时,且条件设置合适时,容器从URI请求字符串和POST数据中组装参数.
        参数的存储为名称-值成对形式,对于同一个名称可能存在多参数值.以下ServletRequest接口方法可以访问参数:
        .getParameter
        .getParameterNames
        .getParameterValues
        getParameterValues方法返回一个字符串对象数组,包含有和指定名称关联的所有参数值.getParameter方法返回的是该数组的第一个参数值.
        查询字符串和post body 包含的数据被组装到request参数集合中,query string中的数据顺序要优先于post body中的数据.比如:
如果一个请求使用查询字符串 a=hello 和post body a=goodbye&a=world,最后的参数集顺序是a=(hello,goodbye,world).
        路径参数需要使用getRequestURI和getPathInfo方法获得.

4.1.1 什么时候参数可用
        POST form 中的数据被放进参数集之前,必须符合以下条件:
        1.必须是HTTP或HTTPS请求
        2.HTTP方法是POST
        3.内容类型是application/x-www-form-urlencoded
        4.servlet 调用了request对象上的getParameter之类与参数相关的方法.
        如上述条件不满足的话,被POST的form中的数据不会包含在参数集中,但数据仍可以通过request对象的输入流得到。否则数据不能通过request对象的输入流得到。

4.2 属性
        属性是和request关联的对象。它一般被容器用作存储不能通过API获得的信息,或者是用作与其他servlet的通讯信息(通过RequestDispatcher)。属性可以用以下方法访问:
        .getAttribute
        .getAttributeNames
        .setAttribute
        一个属性名只能和一个属性值关联。
        以“java.”,"javax."开始的属性名是本规范的保留定义,同样,以"sun.","com.sun."开头的属性名是Sun公司的保留定义。建议所有的属性名以包命名规范。

4.3 头(Headers)
        servlet通过以下方法访问HTTP request的头信息。
        .getHeader
        .getHeaders
        .getHeaderNames
        .getHeader方法返回与头名称对应的头信息。同一个名称可能有多个头信息对应。比如:Cache-Control头。如果这样,该方法应该                返回第一个。要取得所有信息,则使用getHeaders方法。
        头信息也许是某些整型或日期型,可以直接使用下面方法读取:
        .getIntHeader
        .getDateHeader
        如果getIntHeader方法不能将值转换为整数,将抛出NumberFormatException异常。如果.getDateHeader方法不能将值转换为日期对象,将抛出IllegalArgumentException异常。

4.4 请求路径
        请求路径由许多重要的部分组成。下列元素从请求URI获得,并通过request对象暴露。

        .Context Path:和此servlet所在的ServletContext关联的路径前缀,如果context是"default" context,那么该路径为"",另外,如果该context不是root,那么路径以"/"开头,但不能以"/"结束。
        .servlet path:此部分和请求的映射相关,以"/"开始,但如果该映射是"/*",那么servlet路径此时为""
        .PathInfo:此部分是context path 和servlet path 的一部分。如没有额外路径它为null,否则是以"/"开始的字符串。

        HttpServletRequest接口中的以下方法可以访问这些信息:
        .getContextPath
        .getServletPath
        .getPathInfo
        
        值得注意的是,URI和路径之间除了URL编码区别外,以下等式是成立的:
        requestURI = contextPath + servletPath + pathInfo
        例子:
        Context Path         /catalog
        Servlet Mapping         Pattern: /lawn/*
                        Servlet: LawnServlet

        Servlet Mapping         Pattern: /garden/*
                        Servlet: GardenServlet

        Servlet Mapping         Pattern: *.jsp
                        Servlet: JSPServlet

        我们发现:
        Request Path                         Path Elements

        /catalog/lawn/index.html                 ContextPath: /catalog
                                        ServletPath: /lawn
                                        PathInfo: /index.html

        /catalog/garden/implements/                 ContextPath: /catalog
                                        ServletPath: /garden
                                        PathInfo: /implements/

        /catalog/help/feedback.jsp                 ContextPath: /catalog
                                        ServletPath: /help/feedback.jsp
                                        PathInfo: null

4.5 路径转换方法
        API中有两个方便的方法可以让开发人员获得和某个路径相关的文件系统路径:
        .ServletContext.getRealPath
        .HttpServletRequest.getPathTranslated
        getRealPath接受一个字符串参数,并返回和该路径相关联的文件系统路径。getPathTranslated方法则可以得到该request的PathInfo对应的真实路径。下列情况下,servlet容器不能判别合法的文件路径,如,web应用以war包形式运行,或者不是本地文件路径,或者在数据库中,这些方法将返回NULL.

4.6 cookie
        HttpServletRequest接口提供getCookies方法获得该request中的cookies数组。这些cookies是客户机每次发送给服务器请求中的数据,其形式为cookie名和cookie值。当cookie被送回给客户机时也可设置其他属性,比如注释,但一般是不会送回服务器的。

4.7 SSL属性
        如果请求是通过安全协议传输的,如:HTTPS,那么ServletRequest接口的isScure方法可以得到。

4.8 国际化
        客户机可以向web服务器提示它以何种语言接受服务器的响应.HTTP1.1规范中指出在头信息Accept-Language表示。以下方法可以得到发送者的地域信息:
        .getLocale
        .getLocales

        如果客户机没有指定地域信息,servlet容器将会使用默认的地域信息设定。

4.9 请求数据编码
        目前,许多浏览器在Content-Type头中不会指定字符编码方式,那么容器就会使用"ISO-8859-1"方式解析POST数据,而此时,为了向开发人员提示字符编码方式未指定,容器将会在getCharacterEncoding返回null.
        如果客户机没有设置字符编码信息,但是request数据又以和缺省编码方式不同的方式编码,就会发生数据破坏。setCharacterEncoding(String enc)方法可以防止这种状况发生,但是必须在解析数据或从request中读取数据之前调用。否则调用该方法不会有任何效果。

4.10 request对象的生命周期
        request对象只在servlet的service方法范围内有效。或者是在filter的doFilter方法内,开发人员必须确信在范围之外引用request对象行为不会导致不可预计的后果。

第五章 Response

        response对象包装了所有从服务器发送到客户机的的信息,在HTTP协议中,这些信息被分为HTTP头和消息体的两部分。
        
5.1 缓冲
        servlet容器被允许但不被要求,缓冲发送到客户机的输出.一般情况下容器都会做缓冲,并且允许servlet定制缓冲参数。下列方法和缓冲相关。
        .getBufferSize
        .setBufferSize
        .isCommitted
        .reset
        .resetBuffer
        .flushBuffer
        这些方法对ServletOutputStream和Writer有效。
        getBufferSize返回正在使用的缓冲大小,如果没有使用缓冲,返回整数0。可以通过setBufferSize设定缓冲大小,缓冲分配的ad校可以与servlet请求的不同,但至少应不比所请求的小。此方法应在调用ServletOutputStream或Writer向客户机发送数据之前调用。如果有任何数据已经被发送,此方法将抛出IllegalStateException异常.
        isCommitted方法返回一个boolean值显示是否有任何response字节被发送回客户机。flushBuffer强制将buffer中的内容发送给客户机。reset方法在response还没有被commit以前将buffer的内容清空,头信息和状态码也被清除。resetBuffer则只清除buffer内容。
        如果response已经被commit,调用reset和resetBuffer将抛出IllegalStateException异常,同时response对象不会发生改变。
        一旦数据被开始发送给客户机,response对象就被认为是committed状态。

5.2 头
        servlet用下面的方法设置HTTP头信息。
        .setHeader
        .addHeader        
        setHeader方法会替换已经被设置的值,而addHeader则会添加一个值到同名头的值集合中。
        为保证成功地将头信息传送到客户机,必须在response对象被提交以前,设置HTTP头信息。
        servlet开发人员有责任确保Content-type头被正确设置,HTTP1.1规范并不要求HTTP response中设置该头。当开发人员没有设置该值,        容器也不要设置缺省值。

5.3 方便的方法
        .sendRedirect
        .sendError
        sendRedirect方法会自动设置合适的头与内容,引导客户机跳转到另一个URL去。 使用相对路径作为参数也是合法的,但容器必须将相对路径转换为绝对路径送回客户机。如果由于某些原因不能转换,必须抛出IllegalArgumentException异常.
        sendError方法自动设置返回客户机的头信息和错误信息,一个可选的字符串类型的变量,可用来显示错误信息的内容部分。
        这两个方法对于response的commit都有影响,如response还没被提交,将终止提交。这些方法后调用的输出都将被忽略。
        若数据被写进缓冲,但还没有发送到客户机,这些数据将被这两个方法发送的数据代替,如果response已经被提交,将抛出                        IllegalStateException异常.

5.4 国际化
        setLocale方法可以设置response对象的语言属性,此方法必须在开发人员调用getWriter之前使用。
        注意。setContentType方法将覆盖setLocale设定的值。

5.5 response对象的关闭
        当response被关闭时,容器必须立即发送所有缓冲中的数据到客户机,下列情形显示response对象被关闭了。
        .servlet的service方法结束。
        .当发送完由setContentLength设定的长度的内容后。
        .调用了sendError方法。
        .调用了sendRedirect方法。

5.6 response对象的生命周期
        每个response对象只在service方法范围内可用,或者是filter的doFilter方法内。容器通常会回收response对象以避免频繁创建response对象造成的性能过载。开发人员在可用范围外引用response对象时,必须确保不会引起不可预知的行为。

第六章  filter

        Filter是规范2.3新提供的特性,它使得请求与响应过程的信息转换很敏捷。

6.1 什么是Filter
        Filter是一段可重用的,转换HTTP请求,响应内容和头信息的代码。Filter通常不象servlet那样会创建response或对请求作出响应。它们只是修改对资源的请求或对资源的响应。
        Filter对静态和动态资源都可以起作用。filter的一般作用:
        .在request到达它调用的资源之前访问该资源。
        .在request到达它调用的资源之前处理该资源。
        .修改request头和数据,通过重新包装request对象。
        .修改response头和数据,通过重新包装response对象。
        .拦截监听资源的调用。
        .以一定顺序对一个或一组servlet或者静态资源作用。

6.1.1 Filter的例子
        .认证filter
        .日志与审核filter
        .图像转换filter
        .数据压缩filter
        .加密Filter
        .令牌filter
        .出发资源访问事件的filter
        .转换XML文档的XSLT filter
        .MIME-type 链filter
        .缓存filter

6.2 主要概念
        开发人员实现javax.servlet.Filter接口来创建filter,同时提供一个公共无参数的构造函数。该类应该被包含在web应用中。在发布文件中,以“filter”,"filter-mapping"元素声明配置。

6.2.1 filter 生命周期
        web应用发布完成之后,在request访问web资源之前,容器必须定位需要应用到该资源上的filter列表。容器需要确保实例化列表中的每个filter并调用它的init方法。如不能正确执行,filter可以抛出异常。
配置描述文件中每个filter的声明在每个虚拟机中仅仅只有一个实例。容器提供filter配置秘书中声明的config对象, 它包含了该web应用的ServletContext引用,和filter初始参数。

        当容器接收到request,将取得列表中的第一个filter实例,并调用它的doFilter方法。将传入ServletRequest,ServletResponse和filterchain的引用。

        doFilter方法遵循以下模式:
        1.该方法检测request的头信息
        2.可能会用一个定制的ServletRequest或HttpServletRequest包装request对象。
        3.可能会用一个定制的ServletResponse或HttpServletResponse包装response对象。
        4.filter可以调用filter链中的下一个入口,可能是一个filter,也可能是目的web资源。
        5.调用链中的下一个filter之后,filter可以检测响应头信息。
        6.处理过程中,filter可能抛出异常。如果在doFilter中抛出UnavailableException,容器不要继续filter链的处理,如飞永久性异        常,它可以选择在稍晚的时候重新执行整个filter链。
        
6.2.2 包装request和response
        filter的核心概念就是包装request和response对象,以便于执行filter任务时,覆盖其行为。在此模型中,开发人员不仅能够覆盖request和response对象上已有的方法,还可以为某个特定的过滤任务在整个链上提供新的API。例如,开发人员可能想在response对象上使用比out流和writer更高级的输出对象,例如,允许DOM对象写回客户机的API。
        为了支持这种类型的filter,容器必须满足下列需求。当一个filter调用filter chain的doFilter方法时,容器必须确保传到下一Filter或web资源入口的request和response对象,和调用filter的doFilter方法传入的是同一个对象。
        同样的,被包装的request、response对象和传入的也必须是一致的。

6.2.3 filter环境
        配置描述中可以使用一系列参数初始化filter,描述符为"init-param",filter在运行时可以使用FilterConfig对象上的getInitParameter和getInitParameterNames方法得到参数名和值。FilterConfig也提供对ServletContext的访问。

6.2.4 在web应用中配置filter
        在配置描述中,filter以filter元素定义。
        filter-name:用来映射该filter到一个servlet或者URL
        filter-class:容器用来识别filter类型
        init-params:初始化参数
        图标,文本描述,显示名称都是可选项描述,主要给县骨干工具软件使用。对于每个filter声明,容器必须正确地实例化一个filter对象,如果对于一个filter类定义了多个声明,容器将会生成多个实例。
例子:
        filter声明:
        <filter>
                <filter-name>Image Filter</filter-name>
                <filter-class>com.acme.ImageServlet</filter-class>
        </filter>
        filter声明后,应使用filter-mapping元素定义该filter要应用的servlet或是静态资源。
        <filter-mapping>
                <filter-name>Image Filter</filter-name>
                <servlet-name>ImageServlet</servlet-name>
        </filter-mapping>

        filter也可以使用URL匹配模式和一组servlet或静态资源关联起来。

        <filter-mapping>
                <filter-name>Logging Filter</filter-name>
                <url-pattern>/*</url-pattern>
        </filter-mapping>
        
        此处,Logging filter被应用到applicationlide所有servlet和静态资源上了,因为所有请求URI都匹配"/*"。
        当用url-pattern处理filter-mapping时,容器必须用路径映射规则判断所请求的URI是否匹url-pattern。
        容器针对某个被请求的URI构建的filter链的顺序为:
        1.首先是url-pattern匹配的filter,如有多个,其顺序是配置描述的顺序。
        2.然后是servlet-name匹配的filter,如有多个,其顺序是配置描述的顺序。
        
        以上两点意味着容器:
        .根据匹配规则11.2确定被访问的目标Web资源
        .如果通过servlet名称匹配到了filter,且web资源拥有servlet名称,容器按照配置描述的顺序构建filter链。链的底部就是最后一个通过servlet名称匹配的filter,并且由它调用目的web资源。
        .如果有url-pattern匹配的request URI的filter,容器以配置描述中的顺序构建url-pattern匹配的filter链.链的底部是最后一个url-pattern匹配到的filter,并且该filter将调用第一个servlet-name匹配的filter,如没有则直接调用目的web资源.
        若希望提高容器性能,最好缓存filter链,以免每次请求都要重新计算.

第七章 会话 sessions
        超文本传输协议(HTTP)被设计成无状态的协议.为了构建有效率的web应用,将从某个特定客户机上发送来的request互相关联起来是很必要的,会话跟踪的技术已经发展了很长时间,但对于开发人员直接使用来说都是很困难的.
        本规范定义了HttpSession接口,允许servlet容器使用好几种方式跟踪用户会话,而开发人员不会陷入不同方式的细节.

7.1  会话跟踪机制
        通过HTTP cookie跟踪会话时比较常用的办法,所有servlet容器都要求实现这一技术.
        容器发送cookie到客户机,客户机在接下来的请求中都将该cookie返回给容器.这样就很清楚地将请求和会话关联起来,用于会话跟踪的cookie名称一定是JSESSIONID.

7.1.2 SSL Session

7.1.3 URL重写
        URL重写式会话跟踪的最低公分母?,当客户机不接受cookie时,server就使用URL重写作为会话跟踪的基本方式.URL重写,添加了附加数据(会话ID)到请求的URL路径上.
        会话ID必须被编码作为该URL字符串中的路径参数。该参数的名称为jsessionid,例如:        
        http://www.myserver.com/catalog/index.html;jsessionid=1234


7.1.4会话完整性
        Web容器必须能支持HTTP会话,当处理不支持cookie使用的客户机发送来的HTTP请求时,web容器通常使用URL重写机制。

7.2 创建会话
        一个会话被认为是新的,只有当它是唯一一个预期会话并还没没有被建立。因为,HTTP是基于request-response的协议,HTTP会话被认为是新的,直到有客户机加入它.当会话跟踪信息返回服务器时,意味着一个会话已经被建立了,此时,客户机加入了会话.直到客户机加入会话为止,它都不能假定下一个来自该客户机的请求隶属于某个会话的一部分.只要下列条件成立,会话被认为是新的:
        .客户机还不知道该会话
        .客户机选择不加入会话
        
        这些情况定义了servlet容器没有机制将一个请求和上一个请求关联起来.
        servlet开发人员必须将它的应用设计为可以处理客户机没有,不能,不会加入会话的情形.

7.3 会话范围
        HttpSession对象在应用级别(servlet context)范畴.
        下列机制,例如cookie,对于不同context可以是相同的,但该对象引用,包扩属性,容器必须按context区别开来.
        用一个例子来说明:如果一个servlet使用RequestDispatcher调用属于另一个web应用的servlet,任何对于执行调用的servlet创建的,可        见的会话信息,必须不同于被调用的servlet.

7.4 在会话中绑定属性
        servlet可以通过名称绑定对象属性到HttpSession的实现中.任何绑定对象对于同一servlet context下在同一会话中处理请求的其他servlet都是可用的.
        会话中的一些对象被放入或是移出时,可能需要获得通知,这可以通过实现HttpSessionBindingListener接口的对象获得.该接口定义了下列方法,当对象被绑定或解除绑定时,发出信号.

        .valueBound
        .valueUnbound

        valueBound方法必须在该对象通过执行HttpSession的setAttribute方法而可用之前被调用valueUnbound方法必须在该对象通过执行HttpSession的setAttribute方法而不再可用之前被调用.

7.5 会话超时
        在HTTP协议中,客户机不再活跃时没有明确的终止信号.这意味着只有超时才是标识客户机不再活跃的唯一机制.
        缺省的会话超时由servlet容器定义,在HttpSession接口上调用getMaxInactiveIntervalfangaf得到该值.开发人员可以使用HttpSession接口上的setMaxInactiveInterval方法改变该值.时间单位是秒.若设置为-1,表示该会话永不过期.

7.6最后访问时间
        HttpSession接口上的getLastAccessedTime方法允许servlet判定该会话在本次请求前被访问的最后时间.当容器第一次处理隶属于该会话的请求时,认为该会话被访问.
        
7.7 重要的会话语义
7.7.1 线程
        多个servlet执行请求线程也许同时使用一个会话对象,开发人员有责任同步对会话资源的访问.

7.7.2 分布式环境
        在分布式应用中,所有隶属于某个会话的请求在某一时间必须同一个虚拟机处理,容器必须能适当地处理使用setAttribute或putValue方法放进HttpSession实例的所有对象.下列约束是强制的:
        .接受的对象应实现Serializable接口
        .容器可以选择支持在HttpSession中存储其他指定的对象,例如:EJB和事务
        .移动会话将由容器具体设施处理

        若放进会话的对象未实现Serializable接口,或对该类对象的支持还不可用,servlet容器可以抛出IllegalArgumentException异常,只有在容器不能支持移动会话时抛出该异常.
        这些约束表明开发人员被保证没有发生在非分布式容器之外的并发问题.

        通过拥有移动会话对象的能力,从分布式系统中的任一个活跃节点移动到系统中的另一个节点,容器提供者能保证服务的可伸缩性和品质,负载均衡和故障转移等特性.
        如果分布式容器坚持或移动会话来提供服务特性的品质,就不强求在序列化HttpSession和它们的属性时使用本地虚拟机的序列化机制.容器不会保证调用会话属性上的readObject和writeObject方法.但是保证它们的属性可序列化关闭将被保存.

        在移动会话期间,容器必须通知实现了HttpSessionActivationListener接口的会话属性.通知钝化监听器必须优先于序列化监听器,活化则要迟于会话反序列化.

        应用开发人员编写分布式应用,应该明白因为允许容器运行在多个java虚拟机上,他就不能依赖静态变量来存储应用程序状态.应使用EJB或数据库.

7.7.3 客户机语义
        实际上cookie和SSL认证都是由web浏览器控制的,而不是和浏览器的某个特定窗口相关联,也许从所有窗口发送到servlet容器的请求是属于同一个会话.为了尽量方便,开发人员总是假定客户机的所有窗口参加的是同一个会话.

八 转发请求
        使用RequestDispatcher接口,向另一个servlet转发请求,或者在response中包含另一个servlet的输出.

8.1 获得RequestDispatcher
        从ServletContext上可以获得RequestDispatcher的实现对象:
        .getRequestDispatcher
        .getNamedDispatcher
        getRequestDispatcher方法的参数为一个字符串变量,描述在ServletContext范围内的路径.该路径必须相对于ServletContext的根.并且以"/"开始.该方法使用此路径寻找到servlet后,用一个RequestDispatcher对象包装返回.如果在该路径上没有找到对应的servelt,一个包装了该路径内容的RequestDispatcher被返回.
        
        getNamedDispatcher方法参数为一个字符串变量,它表示ServletContext内已知servlet的名称,如找到该servelt,用RequestDispatcher对象包装后返回,如未找到,返回null。
        
        使用相对于当前请求的相对路径也可以获得RequestDispatcher对象,以下方法在ServletRequest接口提供:
        .getRequestDispatcher
        该方法的行为类似于ServletContext中同名函数,servlet容器使用request对象中的信息转换给定的相对路径为完整的路径。例如:
在context“/”和一个到“/garden/tools.html”的请求,此时,调用request上的getRequestDispatcher("header.html")方法效果等同于ServletContext.getRequestDispatcher("/garden/header.html")。

8.1.1 转发请求路径里的查询字符串
        ServletContext和ServletRequest里使用路径参数获得RequestDispatcher的方法,允许添加查询字符串到该路径上。例如,开发人员用以下代码:
        String path = “/raisons.jsp?orderno=5”;
        RequestDispatcher rd = context.getRequestDispatcher(path);
        rd.include(request, response);
        查询字符串里指定的参数,顺序要优先于同名的其他被传进的参数。该参数与RequestDispatcher相关联,只在include或forward调用中有效。

8.2 使用request Dispatcher

        使用request Dispatcher,调用该接口上的include或forward方法。参数可以是通过service方法传入的request,response对象,也可以是它们被包装后的对象。
        容器提供者应确保将请求分发到目的servlet必须发生在原始请求所在的虚拟机上。

8.3 include方法
        该方法必须可以在任意时刻调用,目标servlet的include方法可以五约束地访问request对象,但在访问response对象上有一些限制。它只能向response对象的ServletOutputStream或Writer写入信息,在response的缓冲写完后提交response,或直接调用flushBuffer方法提交response,但不能设置头信息,或者调用任何可能改变头信息的方法。任何此类操作将被忽略。

8.3.1 Included参数
        除了使用getNamedDispatcher获得的servlet外,a servlet being used from within an include has access to the path by which it was invoked.
        以下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        
        被包含的servlet可以调用getAttribute访问这些属性。
        以getNamedDispatcher方式获得的被包含servlet不能得到这些属性。
        
8.4 Forward方法
        在负责调用的servlet还没有向客户机提交输出的时候,才可以调用RequestDispatcher上的Forward方法。在目标servlet的service方法调用前,如果response缓冲中存在还没提交的数据,这些缓冲内容必须被清空,如果response已经提交,将抛出IllegalStateException异常。request对象暴露给目的servlet的路径元素必须反射用于获得RequestDispatcher路径。唯一的例外是,如果RequestDispatcher是通过
getNamedDispatcher方法获得的。        
        在forward方法返回前,response内容必须被发送和提交,并被容器关闭。
        
8.4.1query string
        在forward或include请求的时候,请求转发机制负责组合查询字符串。

8.5 错误处理
        如果request dispatcher的目标servlet扔出运行时错误或一个被检查类型的意外ServletException或IOException,它应当被传播到调用它的servlet。所有其他异常则应被包装为ServletException,被传播以前,异常引起的最初原因应被设置到原始异常。


第九章 web 应用
        web应用是web server中某个特定的路径。例如,一个catalog应用可能在http://www.mycorp.com/catalog.所有以此路径开始的请求都被发送到catalog应用处理。
        servlet容器能建立自动产生web应用的规则,例如,~user/ 可以映射到基于/home/user/public_html/的一个web应用。
        默认情形下,在任意时间内,一个web应用的实例必须运行在一个虚拟机上。但如果是分布式应用,这是可以改变的,分布式应用需要遵循一套更严格的规则,这些规则不在本规范内。

9.2 和ServletContext的关系
        servlet 容器在web应用和Servlet context之间必须强制一对一的对应关系。ServletContext对象相servelt提供了整个应用视图。

9.3 web应用的元素
        web应用可以由以下元素构成:
        .servlet
        .JSP页面
        .工具类(Utility class)
        .静态文档(html,image,sound 等)
        .客户端java applet,beans,和类
        .组合所有以上元素的描述元信息

9.4发布层次
        本规范定义了一个层次结构,用于发布和打包,可以文件系统形式存在,也可以以压缩包文件形式。建议servlet容器支持此结构为运行时表示。

9.5 目录结构
        web应用以结构化层次目录形式存在。该层次结构的根为文档的根。例如,context路径为/catalog的web应用,应用根路径下的index.html文件可以用/catalog/index.html请求来访问。映射的规则在第11章有叙述。因为应用的context路径决定了内容的URL名字空间,web容器必须拒绝可能引起URL名字空间潜在冲突的context定义。这有可能发生,例如试图以相同的context路径发布另一个web应用,或者其中一个context路径是另一个context的一部份。匹配是大小写敏感的,所以,冲突检测也是一样的。

        应用层次中有个特殊的目录叫“WEB-INF”,此目录包含了不在应用根文档的所有东西。WEB-INF节点不是应用文档树的公开部分,不能被客户机直接访问。但该目录的内容对于servlet代码是可见的,可使用ServletContext接口上的getResource或getResourceAsStream方法访问。因此,开发人员如果需要在servlet代码访问,又不希望暴露给客户机的信息,他可以放置在此目录。WEB-INF目录的内容有:
        ./WEB-INF/web.xml
        ./WEB-INF/classes/目录存储servlet和应用类,这些类必须可以对application class loader是可用的。
        ./WEB-INF/lib/*.jar存储java存档文件,这些文件包含servlet,bean,和其他可用类。application class loader必须也能装载这些存档文件中的类。
        装载次序是先WEB-INF/classes目录,再WEB-INF/lib目录。

9.6 web应用存档文件
        web应用可以使用标准java存档工具打包为war文件.

9.7 web 应用配置描述
        应该包含以下内容:
        .ServletContext 初始化参数
        .会话配置
        .Servlet / JSP定义
        .Servlet / JSP映射
        .MIME类型映射
        .Welcome文件列表
        .Error页面
        .Security

9.7.1 扩展依赖
        当大量的应用使用相同的代码和资源,它们在容器中一般被安装为库文件。这些文件通常是通用或标准的API.
        应用开发人员需要知道web容器上安装了什么扩展部件,容器则需要知道servlet依赖什么样的库。web容器应有一种机制,让web应用可以知道JAR文件包含的资源与代码是可用的,并使它们为应用所用。容器应当提供一种简便的过程可以编辑配置库文件或是扩展部分。
        推荐开发人员在war文件中使用META-INF/MANIFEST.MF列出扩展部分。若web容器不能满足这种方式声明的依赖,它必须拒绝应用程序,并给出错误信息。

9.7.2 web应用classloader
        容器用来装载WAR中的servlet的Classloader必须允许开发人员以J2SE语义使用getResource装载任何包含在war中JAR库内的资源。必须防止war覆盖J2SE或java servlet API类。该classloader应该不允许servlet访问web容器的实现类。推荐让应用类装载器优先装载WAR中的类和资源,再装载JAR中的类和资源。

9.8 替换web应用
        服务器应该能够在不重新启动容器的情况下替换一个web应用。当应用被替换时,容器应当保持会话数据。

9.9 错误处理
9.9.1 request属性
        web应用必须能够指定错误发生时应用使用的其他资源,这些资源的规范在配置描述中叙述。
        如果错误定位为servlet或JSP页面,下列request属性必须设定:

        Request Attributes                                                                                 Type
        javax.servlet.error.status_code                         java.lang.Integer
        javax.servlet.error.exception_type                 java.lang.Class
        javax.servlet.error.message                                         java.lang.String
        javax.servlet.error.exception                                 java.lang.Throwable
        javax.servlet.error.request_uri                         java.lang.String
        javax.servlet.error.servlet_name                         java.lang.String

        这些属性允许servlet产生一个描述详细错误信息的页面。

9.9.2 错误页面        
        当sertvlet产生错误时,为了允许开发人员定制返回客户机的页面,配置描述文件中定义了一系列错误页面的描述。该语法允许容器返回配置的资源,或者当servlet设置状态码来表明response的错误,或者servlet产生传播到容器的异常和错误.

        若在response上设置了表明错误的状态码,容器参考错误页面声明的列表,试图取得一个匹配的结果.
        servlet处理请求时可以抛出下列异常:
        
        .runtime exceptions or errors
        .ServletExceptions or subclasses thereof
        .IOExceptions or subclasses thereof
        
        web应用也可以使用exception-type元素声明错误页面.容器通过比较所抛出的异常和错误页面中exception-type元素的定义,返回匹配的资源.在类层次最接近的将获得匹配.如果得不到匹配,将抛出ServletException或subclass thereof,容器将展开被包装的异常.A second pass is made over the errorpage declarations, again attempting the match against the error page declarations,but using the wrapped exception instead.
        exception-type元素必须是exception-type的class名称唯一.类似的,status-code元素的状套码也必须是唯一的.
        错误页面机制不回干扰使用RequestDispatcher调用的servlet发生错误.
        如果servlet产生的错误没有错误页处理,容器将会产生状态码为500的response.

9.10 WelCome file
        开发人员可以在配置描述文件中定义一个welcaome文件的顺序列表.本机制的目的是允许开发人员制定目录入口的缺省资源,如目录缺省资源未定义,那么到目录入口的请求将返回一个404的response.

9.11 web应用环境
        J2EE定义了命名环境,允许应用很容易地访问资源和外部信息,webu需要显式了解外部信息具体是如何命名和组织的.
        正如servlet是J2EE技术的一个组件,也制定允许servlet获得资源和EJB的引用的规范,这些元素是:

        .env-entry
        .ejb-ref
        .ejb-local-ref
        .resource-ref
        .resource-env-ref

        ( 本节略)

第十章 应用生命周期事件

10.1 介绍
        本规范支持应用级别的事件.这些应用事件给了开发人员更大的控制能力,比使用ServletContext和HttpSession对象.

10.2 事件监听器
        应用事件监听器是实现了一个或多个和servlet 事件监听接口的类,在web应用被发布的时候被实例化并在web容器中注册。它们应由开发人员提供。
        Servlet事件监听器支持当ServletContext和HttpSession对象的状态改变时,进行事件通知。servlet context监听器用来管理应用程序的虚拟机级的资源和状态。HTTP会话监听器用来管理应用程序中与某个客户机或用户关联的一些列请求的资源与状态。
        对于每种事件类型,可能有多个监听器监听它们。开发人员也可以指定容器调用监听器的顺序。
        
10.3 事件类型与监听器接口

事件类型                 描述                                 监听器接口
__________________________________________________________________________________________
Servlet Context
事件

生命周期                 servlet context 刚刚被创建         javax.servlet.ServletContextListener
                                                它准备好处理它的第一个请求
                                                或者context即将被关闭                        


属性改变                 servlet        context上的属性已经        javax.servlet.ServletContextAttributesListener
                                                被添加,删除,替换
____________________________________________________________________________________

HTTP会话事件

生命周期                HttpSession对象已经被创建,           javax.servlet.http.HttpSessionListener                
                                                实效,或过期

属性改变                HttpSession对象上的属性已经           javax.servlet.HttpSessionAttributesListener        
                                                被添加、删除或替换

10.2.2 使用监听器的例子
        为举例说明事件的使用,我们假定一个简单的web应用,包含很多用到数据库的servlet。开发人员已提供了一个servlet context监听器管理数据库联接。

        1.当应用启动,监听器类被通知,应用登陆数据库,并将联接存储在servlet context内。
        2.web应用中的servlet可以在需要的时候访问该联接。
        3.当web服务器关闭,或者web应用被删除,该监听器被通知,接着数据库联接被关闭。

10.3 监听器类配置

10.3.1 提供监听器类
        应用开发人员听实现以上四个接口的监听器类,每个类必须有不带参数的公共构造函数。该类被打包进WAR中,也可以放在WEB-INF/classes目录下或者是WEB-INF/lib下的JAR中。

10.3.2 发布声明
        在web应用配置描述文件中,使用listener元素声明监听器类。以类名按照调用的顺序被列出。

10.3.3 注册监听器
        web容器在应用处理第一个请求前,创建每个监听器的实例并注册它。容器根据监听器实现的接口以及在配置描述文件中的顺序注册实例。容器以监听器注册的顺序执行它们。

10.3.4 关闭时通知
        当应用被关闭时,监听器以声明时相反的顺序得到通知,,先处理会话监听,然后是context监听.

10.4 Example
        下面例子是关于如何注册两个servlet context 监听器和一个 会话监听器的语法.
        假定com.acme.MyConnectionManager和com.acme.MyLoggingModule都实现了javax.servlet.ServletContextListener,并且com.acme.MyLoggingModule
        还实现了javax.servlet.HttpSessionListener,以下是配置描述:
        <web-app>
        <display-name>MyListeningApplication</display-name>
                <listener>
                        <listener-class>com.acme.MyConnectionManager</listenerclass>
                </listener>
                <listener>
                        <listener-class>com.acme.MyLoggingModule</listener-class>
                </listener>
                
                <servlet>
                        <display-name>RegistrationServlet</display-name>
                </servlet>
</web-app>
        
10.5 监听器实例与线程
        容器需要在应用程序处理第一个请求之前,完成实例化监听器类.在完成最后一个服务请求之前,容器必须维护每个监听器实例的引用.
        ServletContext和HttpSession属性的改变是可以并发的.对属性监听器通知,容器不需要同步.维持状态的监听器类有责任保持数据的完整性,并应当明确地处理这种情况.

10.6 分布式容器
        在分布式web容器中,HttpSession实例存在于处理该会话的请求的特定虚拟机范围,ServletContext对象的范围则在web容器所在的虚拟机内.分布式容器不需要将servlet context和session上的        事件传播到其他虚拟机.监听器实例范围则在每个虚拟机上的每个部署声明对应一个.
        
10.7 会话事件
        开发人员使用监听器类跟踪应用会话.它的作用很大,想知道哪个会话因容器使会话超时,或是因为被调用了invalidate方法而失效了。区别在于是直接使用HTTPSession的API还是间接使用监听器。

原创粉丝点击