Servlet3教程

1. Servlet介绍

Servlet是基于Java技术的web组件,容器托管的,用于生成动态内容。Servlet容器是web server或application server 的一部分,提供基于请求/响应发送模型的网络服务,解码基于 MIME 的请求,并且格式化基于 MIME 的响应。 Servlet 容器也包含了管理 Servlet 生命周期。

Servlet有三大组件,Filter过滤器、Listener监听器、Servlet,以及ServletContext上下文。

Servlet3.1需支持Http/1.0、Http/1.1、及RFC2612缓存机制,所谓缓存即可能在将客户端请求交给 Servlet 处理之前修改它们,也可能在将 Servlet 生成的响应发送给客户端之前修改它们,或者可能根据 RFC2616 规范直接对请求作出响应而不交给 Servlet 进行处理。

Servlet容器即可以运行在Web服务器的同一个进程中,也可以运行在不同进程中,甚至可以运行在不同主机的进程中。

Servlet3.0支持以下几个主要特性:

  1. 异步Servlet
  2. 注解配置
  3. 可插拔性(pluggability)支持
  4. ServletContext运行时动态部署
  5. 支持上传文件
  6. 非阻塞IO

1.1 Servlet接口

Servlet接口是 Java Servlet API 的核心抽象。所有Servlet 类必须直接或间接的实现该接口,或者更通常做法是通过继承一个实现了该接口的类从而复用许多共性功能。

  • GenericServlet
  • HttpServlet

HttpServlet抽象子类在 Servlet 接口基础之上添加了些协议相关的方法。其service方法支持“有条件的GET请求”,即如果没有修订,则直接返回304,不调用GET方法,使用客户端缓存。

if (method.equals(METHOD_GET)) {
    long lastModified = getLastModified(req);
    if (lastModified == -1) {
        // servlet doesn't support if-modified-since, no reason
        // to go through further expensive logic
        doGet(req, resp);
    } else {
        long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
        if (ifModifiedSince < lastModified) {
            // If the servlet mod time is later, call doGet()
            // Round down to the nearest second for a proper compare
            // A ifModifiedSince of -1 will always be less
            maybeSetLastModified(resp, lastModified);
            doGet(req, resp);
        } else {
            resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
        }
    }
 } 

注意:在非分布式环境中,Servlet实例在容器中一般是单例的,即Servlet默认是线程不安全的,需要开发人员处理多线程问题。但如果实现了SingleThreadModel接口(作用是保证一个特定 servlet实例的service方法在一个时刻仅能被一个线程执行),则允许在存在多个实例,一般而言看容器如何实现,比如可以使用syncronize进行同步,或采用servlet实例池,该接口在servlet2.4中被废弃,不建议使用。

1.2 Servlet生命周期

Servlet容器负责加载和实例化Servlet。加载和实例化可以发生容器启动时,或者延迟初始化直到容器决定有请求需要处理时。使用的是普通Java类加载机制,可以从本地文件系统,也可以从网络获取。

Created with Raphaël 2.1.0 init init service service destroy destroy 初始化实例后 开始服务 容器开始销毁 释放实例资源

Servlet在请求处理时实例化或在部署时立即实例化。在后一种情况,以它们的load-on-startup元素表示的顺序实例化。

1.2.1 init初始化

初始化一般用户获取一些代价高昂的资源,比如JDBC,或者执行一些一次性动作。初始化方法为init(ServletConfig config),其中config是每一个servlet实例都会有一个独立的对象,用于获取当前部署描述文件web.xml中配置的信息,如servlet名字、初始化参数、ServletContext对象,如下所示:

<servlet>
    <servlet-name>xxxservlet-name>
    <servlet-class>xxxservlet-class>
    <init-param>
        <param-name>xxxparam-name>
        <param-value>xxxparam-value>
    init-param>
    //用于支持文件上传
    <multipart-config>multipart-config>
servlet>

初始化过程中可能抛出UnavailableException异常,而这个异常是说servlet目前暂时无法初始化,需要等待其他资源就绪,请等一段时间再初始化。

//seconds表示等待时间,<=0表示无法预知时间
UnavailableException(String msg, int seconds)

1.2.2 service请求处理

会接收ServletRequest,响应ServletResponse,在Http协议中默认是HttpServletRequest和HttpServletResponse。

注意

  • 如果抛出UnavailableException表示的是一个永久性不可用,则servlet容器,需要将该servlet从容器中移除,并调用destroy方法。之后会返回一个 SC_NOT_FOUND (404) 响应。

  • 如果 UnavailableException 表示的是一个临时性的不可用,容器可以选择在临时不可用的这段时间内路由任何请求到Servlet。所以在这段时间内被容器拒绝的请求,都会返回一个 SC_SERVICE_UNAVAILABLE (503)响应状态码,且同时会返回一个 Retry-After 头指示此 Servlet 什么时候可用。

  • 容器可以选择忽略永久性和临时性不可用的区别,并把UnavailableExceptions 视为永久性的,从而 Servlet 抛出UnavailableException 后需要把它从服务中移除。

1.2.2.1 upGrade协议升级处理

使用HttpServletRequest.upgrade和HttpUpgradeHandler实现HTTP/1.1协议升级,如升级到Websocket等等.

Client可以在header中发送Upgrade字段,并指定其需要的其他协议,比如HTTP/2.0,此时服务器如果支持需返回101编码,并包含Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11响应。

在Servlet3.0中,可以调用HttpServletRequest.upgrade(HttpUpgradeHandler)方法,进行升级响应过程,即返回101.

一次简单的WebSocket握手的过程:

当Web应用程序调用new WebSocket(url)接口时,Browser就开始了与地址为url的WebServer建立握手连接的过程。

  • Browser与WebSocket服务器通过TCP三次握手建立连接,如果这个建立连接失败,那么后面的过程就不会执行,Web应用程序将收到错误消息通知。
  • 在TCP建立连接成功后,Browser/UA通过http协议传送WebSocket支持的版本号,协议的字版本号,原始地址,主机地址等等一些列字段给服务器端。
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key:dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat,superchat
Sec-WebSocket-Version: 13
  • WebSocket服务器收到Browser/UA发送来的握手请求后,如果数据包数据和格式正确,客户端和服务器端的协议版本号匹配等等,就接受本次握手连接,并给出相应的数据回复,同样回复的数据包也是采用http协议传输。
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept:s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat

参考:
http://www.cnblogs.com/wooer/p/5588993.html websocket简介

1.2.2.2 servlet异步请求

Servlet3教程_第1张图片

从Servlet3.0开始,具备异步请求响应编程模型。即servlet实例所在线程可以不用等待response写返回,而直接释放serlvet线程,因为有些response有比较大的开销,可能会消耗过大的时间,造成线程阻塞,比如等待JDBC连接,等待网络请求等。

注意:默认情况下不支持JSP的servlet,如果需要使用,请在servlet异步执行线程中,调用ctx.dispatch(“xxxx.jsp”)。

子线程处理完成后,可以调用complete方法直接返回,或者调用dispatch方法(DispatcherType.ASYNC,不同于 RequestDispatcher.forward(ServletRequest, ServletResponse) 分派,响应的缓冲区和头信息将不会重置),转发到其他的servlet中继续处理,如下所示:

// REQUEST dispatch to /url/A
AsyncContext ac = request.startAsync();
...
ac.dispatch(); // ASYNC dispatch to /url/A

// REQUEST to /url/A
// FORWARD dispatch to /url/B
request.getRequestDispatcher("/url/B").forward(request,response);
// Start async operation from within the target of the FORWARD
// dispatch
ac = request.startAsync();
...
ac.dispatch(); // ASYNC dispatch to /url/A

// REQUEST to /url/A
// FORWARD dispatch to /url/B
request.getRequestDispatcher("/url/B").forward(request,response);
// Start async operation from within the target of the FORWARD
// dispatch
ac = request.startAsync(request,response);
...
ac.dispatch(); // ASYNC dispatch to /url/B

如何启用子线程?

有两种方法:

  • 采用线程池
private ExecutorService executorService = Executors.newFixedThreadPool(10);
AsyncContext ctx=request.startAsync();
ctx.setTimeout(30*1000);
executorService.submit(new Runnable(){

    @Override
   public void run() {
      // TODO Auto-generated method stub
      try {
        //do something
        ctx.complete();//提交写响应
      } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
  }
});
  • 不采用线程池
final AsyncContext ctx=request.startAsync();
ctx.setTimeout(30*1000);
ctx.start(new Runnable(){

    @Override
   public void run() {
      // TODO Auto-generated method stub
      try {
        //do something
        ctx.complete();//提交写响应
      } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
  }
});

1.2.2.3 如何配置异步

在Servlet3.0中需要对servlet和filter分别都使用@WebServlet和@WebFilter注解,并标识asyncSupported=true,默认是false。从一个同步 Servlet 分派到另一个异步 Servlet 是非法的,一个servlet要么同步要么异步,不能两者兼有。

1.3 destroy销毁

只有servlet成功初始化实例后,才会调用该方法。

1.4 default servlet

如下示例所示,常见的default servlet配置方式,一般用于配置静态资源(static resource)或文件目录列表服务(listings),与dispatch servlet进行区分,如下配置需要注意两点:

  1. default servlet不是必须放置在dispatch servlet的前面,可以参看“13. 路径映射规则”
  2. 如果有多个静态路径需要进行映射,则可以在一个servlet-mapping节点中配置多个url-pattern
    <servlet>
        <servlet-name>dispatcherservlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
        <init-param>
            <param-name>contextConfigLocationparam-name>
            <param-value>/WEB-INF/dispatcher-servlet.xmlparam-value>
        init-param>
        <load-on-startup>2load-on-startup>
    servlet>
    <servlet-mapping>
        <servlet-name>dispatcherservlet-name>
        <url-pattern>/url-pattern>
    servlet-mapping>

    
    <servlet-mapping>
        <servlet-name>defaultservlet-name>
        <url-pattern>/assets/*url-pattern>
        <url-pattern>/tables/*url-pattern>
    servlet-mapping>
  • tomcat中default servlet的实现

官方文献说明:

The default servlet is the servlet which serves static resources as well as serves the directory listings (if directory listings are enabled).

默认情况下default servlet配置在$CATALINA_BASE/conf/web.xml文件中,且默认情况下是随webapp一起启动,并默认开启listings服务,debug模式处于关闭状态。通常建议把listings服务关闭,因为会消耗比较大资源。如果需要替换默认配置,你可以自己覆写一个default servlet,或者使用 localXsltFile or globalXsltFile 。

    <servlet>
        <servlet-name>defaultservlet-name>
        <servlet-class>
          org.apache.catalina.servlets.DefaultServlet
        servlet-class>
        <init-param>
            <param-name>debugparam-name>
            <param-value>0param-value>
        init-param>
        <init-param>
            <param-name>listingsparam-name>
            <param-value>trueparam-value>
        init-param>
        <load-on-startup>1load-on-startup>
    servlet>

    ...

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

如下列表服务开启的示例
Servlet3教程_第2张图片

  • jetty中的default servlet实现
    <servlet>
        <servlet-name>defaultservlet-name>
        <servlet-class>
            org.eclipse.jetty.servlet.DefaultServlet
        servlet-class>
        <init-param>
            
            <param-name>acceptRangesparam-name>
            <param-value>trueparam-value>
        init-param>
        <init-param>
            
            <param-name>dirAllowedparam-name>
            <param-value>trueparam-value>
        init-param>
        <init-param>
            
            <param-name>gzipparam-name>
            <param-value>trueparam-value>
        init-param>
        <load-on-startup>1load-on-startup>
    servlet>
  • 如何在dispatch servlet中处理静态资源

如果不想使用servlet的default servlet技术,那在spring 3.0.4及以后版本中提供了mvc:resources标签,可以达到一样的效果。

参考:

http://tomcat.apache.org/tomcat-6.0-doc/default-servlet.html Apache Tomcat 6.0 Default Servlet Reference
http://blog.csdn.net/zdhcumt/article/details/6867264 DefaultServlet
http://blog.csdn.net/u012814506/article/details/45095419 servlet必知细节(三)– DefaultServlet
http://blog.csdn.net/wxwzy738/article/details/19072909 web.xml中出现default是什么意思?
/org/eclipse/jetty/servlet/DefaultServlet.java”>http://www.boyunjian.com/javasrc/net.sf.ehcache/ehcache/2.8.1//org/eclipse/jetty/servlet/DefaultServlet.java DefaultServlet源码

2. Listener监听器

在Servlet3.0之前,web.xml定义的监听器顺序是以随机顺序执行的,现在是以配置顺序调用初始化,并以相反顺序进行销毁。

在 servlet2.5 中 metadata-complete 仅影响部署时的注解扫描。然而,在 servlet 3.0 和后来版本中,在运行时,metadata-complete 将影响扫描指定部署信息的所有注解和 web-fragments。web-fragments是在servlet3.0才有xml。

  1. ServletContextListener
  2. ServletContextAttributeListener
  3. ServletRequestListener
  4. ServletRequestAttributeListener
  5. HttpSessionListener
  6. HttpSessionAttributeListener
  7. HttpSessionIdListener

如果ServletContext传到了ServletContainerInitializer的onStartup方法,则给定名字的类可以实现ServletContextListener,否则只能实现其他。

监听器分类:

  1. Servlet上下文事件监听器,用来管理应用的资源或JVM级别持有的状态。
  2. Http会话监听器,用来管理从相同客户端或用户进入web应用的一系列请求关联的状态或资源。通知会话监听器session失效在通知上下文监听器关闭之前。
  3. Http请求监听器,用来管理整个Servlet请求生命周期的状态。
  4. 异步事件监听器,用来管理异步事件,例如超时和完成异步处理。

参考:

http://blog.csdn.net/luccs624061082/article/details/41644899 Servlet 3.0之装配来自web.xml,web-fragment.xml和注解的描述符

3. Http协议

3.1 Request请求

路径请求参数优先于POST中的Body请求参数,例如:如果请求由查询字符串 a =hello 和 POST 数据 a=goodbye&a=world 组成,得到的参数集合顺序将是=(hello,goodbye,world)。

POST请求参数,其类型必须是application/x-www-form-urlencoded。

3.2 文件上传

Servlet3.0配置文件上传,需满足以下条件:

  • servlet应@MultipartConfig注解
  • 部署描述文件中,应包含multipart-config。

3.2.1 容器支持multipart/form-data 格式数据的处理

  • public Collection getParts()
  • public Part getPart(String name)

Part 类代表从 multipart/form-data 格式的 POST 请求中接收到的一个部分或表单项。 每个 part 都可通过 Part.getInputStream 方法访问头部,相关的内容类型和内容。

3.2.2 如果容器不支持multipart/form-data 格式数据的处理

  • 数据必须通过HttpServletReuqest.getInputStream得到,需要自行解析。

3.3 Attribute属性

只有一个属性值可与一个属性名称相关联。以前缀 java.和 javax.开头的属性名称是本规范的保留定义。同样地, 以前缀 sun.和 com.sun.,oracle 和 com.oracle 开头的属性名是 Oracle Corporation 的保留定义。建议属性集中所有属性的命名与 Java 编程语言的规范 1 为包命名建议的反向域名约定一致。

3.4 路径参数

  • ContextPath:是与servletContext对应的路径前缀,可以视为rootPath。如果这个上下文是基于 Web服务器的 URL 命名空间基础上的“默认”上下文,那么这个路径将是一个空字符串。否则,如果上下文不是基于服务器的命名空间,那么这个路径以/字符开始,但不以/字符结束。
  • Servlet Path:路径部分直接与激活请求的映射对应。这个路径以“/”字符开头,如果请求与“/ *”或“”模式匹配,在这种情况下,它是一个空字符串。
  • PathInfo:请求路径的一部分,不属于 Context Path 或Servlet Path。如果没有额外的路径,它要么是 null,要么是以’/’开头的字符串,可以以”/”结尾。

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

解析如下

请求路径 路径元素
/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

3.5 非阻塞IO

仅仅针对于异步Servlet和Upgrade升级处理有效,如果在非前所述两者中调用ServletInputStream.setReadListener或ServletOutputStream.setWriteListener方法时将抛出 IllegalStateException。

HttpOnly cookie 暗示客户端它们不会暴露给客户端脚本代码(它没有被过滤掉,除非客户端知道如何查找此属性)。HttpOnly属性对大小写不敏感.

Set-Cookie: USER=123; expires=Wednesday, 09-Nov-99 23:12:40 GMT; HttpOnly

参考:

http://desert3.iteye.com/blog/869080 HttpOnly介绍以及防止XSS攻击时的作用

3.7 数据编码

如果客户端没有指定编码字符集,则servlet必须默认使用“ISO-8859-1”,而且调用getCharacterEncoding将会返回null。

开发人员可以通过调用setCharacterEncoding(String enc)方法来覆盖由容器提供的字符编码。必须在解析任何 post 数据或从请求读取任何输入之前调用此方法。此方法一旦调用,将不会影响已经读取的数据的编码。

3.8 Request对象

容器一般会重用request对象,以节省创建开销。

3.9 Dispatcher分发

  • include

它只能把信息写到response对象的ServletOutputStream或Writer中,或提交在最后写保留在response缓冲区中的内容,或通过显式地调用ServletResponse接口的flushBuffer方法。它不能设置响应头部信息或调用任何影响响应头部信息的方法,HttpServletRequest.getSession()和HttpServletRequest.getSession(boolean)方法除外。include后,request的以下参数可以通过request.getAttribute访问。

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
// AsyncContext 的 dispatch
javax.servlet.async.request_uri
javax.servlet.async.context_path
javax.servlet.async.servlet_path
javax.servlet.async.path_info
javax.servlet.async.query_string
  • forward

forward转发后,request的以下参数可以通过request.getAttribute访问。

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

4. ServletContext上下文

定义了servlet在web应用中的视图。可以用来记录事件,获取资源和配置属性等。对应于ContextPath,即应用的根路径,也称为上下文路径。每个web应用都有一个与之对应的ServletContext。

可以使用getInitParameter和getInitParameterNames获取应用部署描述符文件中的配置初始化信息。

从Servlet3.0开始,可以使用注解和web分片技术,实现web服务可插性支持(使用JAR Services API ServiceLoader.load(Class)),解决webapp划分模块问题,即各独立web模块可以不用再在一个总的web.xml中注册相关信息,只需要在各自独立的模块中配置即可。

注意:该对象支持在运行时动态部署 Servlet、过滤器、监听器,以及为 Servlet 和过滤器增加 URL 映射等。但只能在webapp启动在初始化时进行完成注册,且不能动态销毁,在运行时为了安全原因,无法完成注册。这些方法只能从 ServletContextListener实现的contexInitialized方法或者ServletContainerInitializer实现的onStartup方法进行的应用初始化过程中调用。

  • ServletRegistration.Dynamic addServlet(String servletName,Class

4.1 Code-based Configuration编程初始化配置

  • ServletContextListener(Servlet2.3)
  • ServletContainerInitializer的onStartUp方法(Servlet3.0),借助metadata-complete和web-fragment,容器使用该类用于免除用户的web.xml配置。

Initializer使用方式需满足几点:

  1. 实现ServletContainerInitializer接口的实现类必须使用HandlersType进行注解,用以指示可以被初始化器处理的类。
  2. 使用Java自身的服务发现机制或容器发现机制(使用JAR Services API ServiceLoader.load(Class)),在META-INF/services目录下建立“javax.servlet.ServletContainerInitializer”文件,里面包含有实现类的限定名称。也就是说只要这个jar包在classpath目录下,容器就可以发现新这个实现,并进行调用。
  3. 如果需要限制scan的范围,可以在web.xml中使用metadata-complete或\

4.2 Web Fragments Web分片

是web应用的一个逻辑分区,代表web.xml的部分或全部,即可以将web.xml应用部署描述文件进行拆分放置到不同的jar包中,然后在web.xml中指定加载顺序,碎片文件的名字必须是web-fragment.xml,并放置在jar包的META-INF目录中,比如spring-web项目

Servlet3教程_第3张图片

META-INF/web-fragment.xml


<web-fragment xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd"
    version="3.0" metadata-complete="true">

    <name>spring_webname>
    <distributable/>

web-fragment>

web.xml

<absolute-ordering>
    <name>some_web_fragmentname>
    <name>spring_webname>
    <others/>//规定在顺序中没有命名的其他资源加载,最多一个
absolute-ordering>

注意:

  • web-fragment.xml可以包含全部任何web.xml一样的element,包括absolute-ordering。
  • 不过描述符的顶级元素必须是web-fragment且对应的描述符文件必须被称为web-fragment.xml,相关元素的顺序在web-fragment.xml和 web.xml也是不同的。
  • 只有绑定到web应用的WEB-INF/lib目录中的JAR文件,但不是那些在类装载委托链中更高的,才会扫描其web-fragment.xml。
  • 如果需要考虑加载顺序,则必须指定\

4.3 metadata-complete可插拔性(pluggability)开关

指定当前的部署描述文件是否是完全的,true-表示完全,false-表示不完全,需要从其他地方获取,默认是false不完全。

  • true:容器将使用部署描述符文件(web.xml)为应用配置信息。
  • false:设置为false或不指定,则在部署期间容器将扫描注解或web分片,为应用配置信息。
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
          http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0" metadata-complete="true">
web-app>

参考:

https://www.ibm.com/developerworks/cn/java/j-lo-servlet30/ Servlet 3.0 新特性详解

4.4 可插拔性示例

此处的可插拔指的是war应用可以在不停止服务器(例如tomcat)的情况下部署。

为一个 Web 应用增加一个 Servlet 配置有如下三种方式 ( 过滤器、监听器与 Servlet 三者的配置都是等价的,故在此以 Servlet 配置为例进行讲述,过滤器和监听器具有与之非常类似的特性 ):

  • 编写一个类继承自 HttpServlet,将该类放在 classes 目录下的对应包结构中,修改 web.xml,在其中增加一个 Servlet 声明。这是最原始的方式;
  • 编写一个类继承自 HttpServlet,并且在该类上使用 @WebServlet 注解将该类声明为 Servlet,将该类放在 classes 目录下的对应包结构中,无需修改 web.xml 文件。
  • 编写一个类继承自 HttpServlet,将该类打成 JAR 包,并且在 JAR 包的 META-INF 目录下放置一个 web-fragment.xml 文件,该文件中声明了相应的 Servlet 配置。web-fragment.xml 文件示例如下:

<web-fragment 
    xmlns=http://java.sun.com/xml/ns/javaee
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.0"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd"
    metadata-complete="true">
    <servlet>
        <servlet-name>fragmentservlet-name>
        <servlet-class>footmark.servlet.FragmentServletservlet-class>
    servlet>
    <servlet-mapping>
        <servlet-name>fragmentservlet-name>
        <url-pattern>/fragmenturl-pattern>
    servlet-mapping>
web-fragment>

web-fragment.xml 包含了两个可选的顶层标签,\

<web-fragment...>
    <name>FragmentAname>
    <ordering>
        <after>
            <name>FragmentBname>
            <name>FragmentCname>
        after>
    <before>
        <others/>
    before>
    ordering>
    ...
web-fragment>

参考:

http://www.xdemo.org/web-fragment-module-springmvc/ Web-fragment高度模块化Java项目(集成SpringMVC)
https://blogs.oracle.com/swchan/entry/servlet_3_0_web_fragment Servlet 3.0 web-fragment.xml

4.5 上下文属性

Servlet可以共享ServletContext上下文属性,即通过setAttribute、getAttribute、getAttributeNames、removeAttribute获取设置属性。

在 JVM 中创建的上下文属性是本地的,这可以防止从一个分布式容器的共享内存存储中获取 ServletContext属性。当需要在运行在分布式环境的 Servlet 之间共享信息时,该信息应该被放到:

  • session
  • 或者存储到数据库
  • 或者设置到企业级 JavaBean 组件( Enterprise JavaBeans™)。

4.6 静态资源

  • getResource
  • getResourceAsStream

需要以“/“作为输入参数的开头,意思是相对于上下文的根路径,调用时,首先会查找根路径,其次会查看WEB-INF/lib中的jar包中的META-INFO/resource目录,jar包的扫描顺序是不定的。可以用于扫描文件系统、jar/war等归档文件、远程服务器资源等。

  • getResourcePaths(String path)

该方法将返回Web应用中的完整资源列表。在Web应用程序层次结构
中的/index.html文件,或在WEB-INF/lib目录下的JAR文件中的META-INF/resources目录下包括的index.html文件,可以满足从/catalog/index.html送达的请求,优先使用根目录中的文件。

注意:除静态资源、Jsp文件外,WEB-INF的内容不允许直接供给客户端访问,只能通过使用servlet代码调用ServletContext的getResource和getResourceAsStream方法来访问,并可使用RequestDispatcher调用公开这些内容。

Servlet3教程_第4张图片

5. 安全

安全模型不适用与使用RequestDispatch调用静态资源或forward、include资源到servlet的情况。有两种安全使用方式:

  1. 声明式安全配置,即web.xml配置
  2. 注解式安全配置,例如@ServletSecurity
  3. 编程式安全配置,包括HttpServletRequest的authenticate、login、logout、getRemoteUser-获取远端登录用户名字、isUserInRole-获取用户是否在角色、getUserPricipal,ServletRegistration接口的setServletSecurity方法

javax.security.Principal用于表示一个实体,例如个人、企业或登录ID。
如果没有用户通过身份认证,getRemoteUser方法返回null,isUserInRole方法总返回false,getUserPrincipal方法总返回null。
@ServletSecurity 注解不应用到 ServletRegistration 使用 ServletContext 接口的 addServlet(String, Servlet)方法创建的 url-pattern,除非该 Servlet 是由 ServletContext 接口的 createServlet 方法构建的。

  • URL级别安全性

Servlet3.0允许使用@SecurityServlet配置或web.xml中指定servlet的安全模型,包括角色,访问控制和认证要求。

@WebServlet("/account")
@ServletSecurity(
    //指定所有其他方法可以被属于角色R1的用户调用。
    value=@HttpConstraint(rolesAllowed = {"R1"}),
    httpMethodConstraints={
        @HttpMethodConstraint(value="GET",   rolesAllowed="R2"),
        @HttpMethodConstraint(value="POST",   rolesAllowed={"R3", "R4"})
    }
)
public class AccountServlet extends javax.servlet.http.HttpServlet {
    //. . .
}
<security-constraint>
    <web-resource-collection>
        <url-pattern>/account/*url-pattern>
        <http-method>GEThttp-method>
    web-resource-collection>
    <auth-constraint>
        <role-name>managerrole-name>
    auth-constraint>
    <user-data-constraint>
        <transport-guarantee>INTEGRITYtransport-guarantee>
    user-data-constraint>
    //用于拒绝对未覆盖的HTTP方法请求拒绝, 请求返回一个403(SC_FORBIDDEN)状态码
    <deny-uncovered-http-methods/>
security-constraint>
  • 方法级别安全性

    1. @RolesAllowed
    2. @DenyAll
    3. @PermitAll
    4. @TransportProtected
@RolesAllowed("R2")
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
    //. . .
}

@RolesAllowed,@DenyAll或@PermitAll中只能有一个在目标指定。而@TransportProtected注释可能出现的组合@RolesAllowed 或@PermitAll注解。

参考:

https://my.oschina.net/u/2315511/blog/381810 Servlet的Web Fragments和安全性

6. Response响应

规范并没有规定必须使用缓存输出流,但一般情况下是会实现的,而且ServletResponse有方法设置缓存大小。

6.1 非阻塞IO

仅仅针对于异步Servlet和Upgrade升级处理有效,如果在非前所述两者中调用ServletInputStream.setReadListener或ServletOutputStream.setWriteListener方法时将抛出 IllegalStateException。

当且仅当ServletOutputStream的isReady方法返回false,容器随后将调用onWritePossible方法,之后再调用isReady返回true。

ServletOutputstream写会成功,则isReady返回true,否则返回false(意思是已经准备好写东西了,请触发写事件给监听器)。

7. Filter过滤器

一般不用来产生响应或类似于Servlet一样的响应,常用于包装请求或相应,用于修订或调整到资源的请求,修订或调整从资源的响应,覆盖行为执行过滤任务。匹配请求的过滤器链的顺序是它们在web.xml中声明的顺序。

7.1 生命周期

容器必须确保它为过滤器列表中的每一个都实例化了一个适当类的过滤器,并调用其init(FilterConfig config)方法。FilterConfig包含有init参数,也有servletContext引用。

注意:

  • 每个Filter类型,在每个JVM容器中,只会实例化一个。
  • service方法必须和应用到本次请求的所有filter实例都处在相同线程中。
  • 如果某个Filter抛出UnavailableException,且没有指定为永久,则会在指定时间后,重试整个链。
  • 如果开发人员对同一个过滤器类声明了两次,则容器将实例化两个相同的过滤器类的实例。

7.2 关联配置

    1. 关联到URL
<filter-mapping>
    <filter-name>Logging Filterfilter-name>
    <url-pattern>/*url-pattern>
filter-mapping>
    1. 关联到Sevlet
<filter-mapping>
    <filter-name>Image Filterfilter-name>
    <servlet-name>ImageServletservlet-name>
filter-mapping>

注意:

  • 解析时,优先解析url获取过滤器映射并执行,再解析sevlet配置方式获取过滤器映射并执行。
  • 可以在一个filter-mapping中即包含url方式、也包含servlet方式,其等价于拆开。
  • 过滤器链路的获取是动态的,依据uri动态获取,如果想要高性能web容器,则需要进行缓存。
  • 如果需要制定disptcher情况下的过滤器是否启用,需要制定\
<filter-mapping>
    <filter-name>Image Filterfilter-name>
    <servlet-name>ImageServletservlet-name>
    <dispatcher>FORWARDdispatcher>
    <dispatcher>REQUESTdispatcher>//直接请求
    <dispatcher>ASYNCdispatcher>//异步请求
filter-mapping>

8. Session会话

与每个会话相关联是一个包含唯一标识符的字符串,也被称为会话 ID。会话 ID 的值能通过调用javax.servlet.http.HttpSession.getId() 获 取 , 且能在创建后通过调用javax.servlet.http.HttpServletRequest.changeSessionId()改变。

HttpSession对象,绝不能在容器上下文之间中共享,只能限定应用上下文,即单个容器上下文之内有效。并为了安全,容器需要给定默认的超时时间,或用户使用setMaxInactiveInterval设置超时时间。这些方法的超时时间以秒为单位。根据定义,如果超时时间设置为 0 或更小的值,会话将永不过期

<session-config>
    <cookie-config>
        <comment>sessioncomment>
        <domain>xxxxdomain>
        <http-only>truehttp-only>
        //seconds 默认 -1
        <max-age>100max-age>
        //JSESSIONID default
        <name>JSESSIONIDname>
        <path>/path>
        <secure>truesecure>
    cookie-config>
    //分钟,0或-1表示不过期
    <session-timeout>1session-timeout>
    <tracking-mode>COOKIEtracking-mode>
    <tracking-mode>URLtracking-mode>
    <tracking-mode>SSLtracking-mode>
session-config>

8.1 Cookie方式

所有会话跟踪Cookie的名字都是JSESSIONID。容器也允许自定义名字。所有servlet容器必须提供能够配置容器是否标记会话跟踪 cookie为HttpOnly

8.2 SSL会话

待续

8.3 URL重写

URL 重写是会话跟踪的最低标准,即在Cookie在客户端不支持时,需要采用将会话ID写入URL的方式,例如:

http://www.myserver.com/catalog/index.html;jsessionid=1234

URL 重写在日志、书签、 referer header、缓存的 HTML、 URL 工具条中暴露会话标识。在支持 cookie 或 SSL会话的情况下,不应该使用 URL 重写作为会话跟踪机制。

8.4 绑定Session属性

可以在HttpSession对象上绑定servlet之间共享的属性对象。属性的添加和删除,可以通过实现HttpSessionBindingListener获得通知,其有两个方法:valueBound和valueUnbound。

8.9 会话活化与钝化

activated活化(从硬盘到内存)和passivated钝化(从内存到硬盘)。容器需要支持会话迁移存储对象机制,且附加会话属性时,属性对象应实现Serializable接口。容器必须在迁移会话时通知实现了 HttpSessionActivationListener的所有会话属性。它们必须在序列化会话之前通知钝化监听器,在反序列化之后通知激活监听器。可用于持久化或者从一个VM迁移到另一个VM。

例如:在用户访问的时候,假如服务器突然关闭了,这个时候,用户的session就不存在了,假如是购物网站,也就相当于,用户好不容易选好的物品,刚刚添加到购物车,结果,因为服务器的突然关闭一下,什么都没了,这样很不好,于是我们就需要实现会话的持久化。可以让我们在重新启动服务器之后用户的session还在服务器中存在!

public class Person implements Serializable, HttpSessionActivationListener {
    private String name;

    public Person(String name) {
        super();
        this.name = name;
    }

    @Override
    public void sessionWillPassivate(HttpSessionEvent se) {
        System.out.println(this + "保存到硬盘了...");
    }

    @Override
    public void sessionDidActivate(HttpSessionEvent se) {
        System.out.println(this + "从硬盘读取并活化了...");
    }

    @Override
    public String toString() {
        return "Perosn [name=" + name + "]---"+super.toString();
    }

}

写分布式应用的开发人员应该意识到容器可能运行在多个 Java 虚拟机中,开发人员不能依赖静态变量存储应用状态。他们应该用企业Bean或数据库存储这种状态。

参考:

http://blog.csdn.net/qq_26525215/article/details/52262566 JavaWeb-会话的持久化:HttpSessionActivationListener
http://memorynotfound.com/httpsessionactivationlistener-example-use-case/ Httpsessionactivationlistener Example Use Case

9. Annotation注解

注解必须在WEB-INF/classes或者WEB-INF/lib下的jar包中,才能被处理。如果要启用注解,请在web.xml中确保metadata-complete为false,常用注解如下图所示:

Servlet3教程_第5张图片

xml配置优先级高于注解。

9.1 @WebServlet

用于定义servlet组件,其中必须指定的是urlPatterns或value,两者不能同时使用,可以指定多个url,例如urlPatterns={“/foo”, “/bar”}。没有指定name时,默认采用全限定类名(例如com.acme.Foo)。如果同一个 Servlet 类以不同的名字声明在部署描述符中,必须实例化一个新的Servlet实例。注意使用该注解的前提是必须继承HttpServlet

可以使用@WebInitParam配置ServletConfig参数。

\false\如果指定了该参数,则不允许使用rul,即该servlet无效。

9.2 @WebFilter

用于定义filter过滤器组件,如果没有指定name,则默认采用全限定类名(例如com.acme.Foo)。注解的urlPatterns属性,servletNames属性或value属性必须被指定,同时使用value和urlPatterns是非法的。注意使用该注解的前提是必须实现javax.servlet.Filter接口

可以使用@WebInitParam配置FilterConfig参数。

9.3 @WebListener

用于定义listener监听器组件,获取web应用上下文中的操作事件。注意该注解的前提是必须实现以下的监听器接口

  1. ServletContextListener
  2. ServletContextAttributeListener
  3. ServletRequestListener
  4. ServletRequestAttributeListener
  5. HttpSessionListener
  6. HttpSessionAttributeListener
  7. HttpSessionIdListener

9.4 @MultipartConfig

该注解主要是为了辅助 Servlet 3.0 中 HttpServletRequest 提供的对上传文件的支持。该注解标注在 Servlet 上面,以表示该 Servlet 希望处理的请求的 MIME 类型是 multipart/form-data。另外,它还提供了若干属性用于简化对上传文件的处理

属性名 类型 是否可选 描述
fileSizeThreshold int 当数据量大于该值时,内容将被写入文件。
location String 存放生成的文件地址。
和\的\元素被解析为一个绝对路径且默认为 javax.servlet.context.tempdir。
如果指定了相对地址,它将是相对于tempdir位置。
绝对路径与相对地址的测试必须使用java.io.File.isAbsolute。
maxFileSize long 允许上传的文件最大值。默认值为 -1,表示没有限制。
maxRequestSize long 针对该 multipart/form-data 请求的最大数量,默认值为 -1,

9.5 注解和资源注入

待定

10. 其他标签

10.1 icon元素

\用于指定GUI图标,支持GIF, JPEG, PNG例如:

<icon>
    <small-icon>employee-service-icon16x16.jpgsmall-icon>
    <large-icon>employee-service-icon32x32.jpglarge-icon>
icon>

10.2 jsp-config元素

用于指定jsp资源控制信息,包括是否开启el表达式、jsp文件编码等。

<jsp-config>
    <jsp-property-group>
        <url-pattern>url-pattern>
        <el-ignored>trueel-ignored>
    jsp-property-group>
jsp-config>

10.3 resource-ref元素和injection-target元素

资源注入配置,用于引用外部资源(an external resource),可以包括一个可选描述、资源控制器、验证类型、是否可共享(Shareable or Unshareable)、注入对象等。主要的作用是将容器中需要用到的资源或组件与应用服务器定义的资源或组件关联。作用等同于在类上使用资源引用注解@Resource,@Resources,@EJB,@EJBs,@WebServiceRef,@WebServiceRefs,@PersistenceContext,@PersistenceContexts,@PersistenceUnit,@PersistenceUnits

<resource-ref>
    //is the name of a component dependency  and is relative to java:comp/env/jdbc/EmployeeAppDB
    <res-ref-name>jdbc/EmployeeAppDBres-ref-name>
    <res-type>javax.sql.DataSourceres-type>
    <res-auth>Containerres-auth>
    <res-sharing-scope>Shareableres-sharing-scope>
    <injection-target>//可以多个
        <injection-target-class>com.acme.ServletAinjection-target-class>
        <injection-target-name>dsinjection-target-name>
    injection-target>
resource-ref>

等同于

public class ServletA{

    @Resource(name="jdbc/EmployeeAppDB")
    private DataSource ds;

}

等同于

public class ServletA{

    {
        InitialContext ic=new InitialContext();
        DataSource ds=(DataSource)ic.lookup("java:comp/env/jdbc/EmployeeAppDB");
    }
}

优先级为annotation < standard.xml < sun-*.xml。
其实只要在serlvet中可以使用Spring容器,就没必要用这种方式。

参考:

http://www.bubuko.com/infodetail-452903.html EJB和Web容器中的资源或组件是如何查找的?

10.4 welcome-file-list标签

欢迎文件的局部URI有序列表,默认的根路径对于的欢迎页面,如果第一个没有找到,则找第二个,依次类推。当一个对应到WAR文件中一个目录条目的请求URI没有映射到一个Web组件时,允许部署者为容器用于添加URI指定局部URI有序列表。这种请求被认为是有效的局部请求。

<welcome-file-list>
    <welcome-file>index.jspwelcome-file>
    <welcome-file>second.jspwelcome-file>
welcome-file-list>

如果请求host:port/webapp/directory/,该条目无法映射到servlet或jsp,则会默认添加host:port/webapp/directory/index.jsp返回给客户端。

10.5 error-page标签

错误页面机制,可以通过reponse code,也可以使用exception,这种语法允许当servlet或过滤器调用response的sendError方法,发送4xx和5xx状态的响应(2xx与3xx无效),这样错误机制才可能会被调用,或如果servlet产生一个异常或错误传播给容器时,由容器返回资源配置。

容器抛出的异常包括三种:

  1. RuntimeException或RuntimeErrorException
  2. ServletException或其子类
  3. IOException或其子类

例如:

<error-page>
    <error-code>500error-code>
    <location>/error.jsplocation>
error-page>
<error-page>
    <exception-type>java.lang.NullExceptionexception-type>
    <location>/error.jsplocation>
error-page>

注意:error-code与exception-type必须唯一。如果抛出的异常并没有定义exception-type,则会被包装成ServletException再次寻找。如果还是没有处理,则会抛出500.

参考:

http://blog.csdn.net/dclove/article/details/11727073 在web.xml中配置error-page

10.6 distributable标签

指定该站台是否可分布式处理,如果web.xml中出现这个元素,则代表站台在开发时已经被设计为能在多个JSP Container 之间分散执行.

\

11. JSP容器与可插拔

ServletContainerInitializer和编程式注册特性可以在Servlet和JSP容器之间提供一个清晰的职责分离,通过由Servlet 容器只负责解析web.xml和web-fragment.xml资源,而解析标签库描述符( TLD)资源委托给JSP容器。

JSP也是一种Servlet,JSP容器是Servlet子容器,是内嵌到一个 Servlet3.0 兼容的 Servlet 容器中,可以提供它自己的ServletContainerInitializer 实现,搜索传递到它的 onStartup 方法的 ServletContext 参数寻找任何 TLD 资源,扫描这些资源寻找 Listener 声明,并向 ServletContext 注册相关的 Listener。

ServletContext.getJspConfigDescriptor 方法得到应用的 web.xml和web-fragment.xml 部署描述符中的任何 jsp-config 相关的配置

12. Web应用实例化

  1. 扫描应用部署描述符文件,实例化listener;
  2. 调用监听器的contextInitialized方法;
  3. 实例化filter对象;
  4. 调用filter过滤器的init方法;
  5. 对于包含有load-on-startup的servlet进行实例化,并依据load-on-startup顺序进行实例化。

13. 路径映射规则

URL会剔除掉上下文路径和参数,匹配时会区分大小写,并且一旦匹配到一个不会再进行更多尝试。匹配步骤如下:

  1. 容器将尝试找到一个请求路径到servlet路径的精确匹配。成功匹配则选择该servlet。
  2. 容器将递归地尝试匹配最长路径前缀。这是通过一次一个目录的遍历路径树完成的,使用‘/’字符作为路径分隔符。最长匹配确定选择的servlet。
  3. 如果URL最后一部分包含一个扩展名(如.jsp),servlet容器将试图匹配为扩展名处理请求的Servlet。扩展名定义在最后一部分的最后一个‘.’字符之后。
  4. 如果前三个规则都没有产生一个servlet匹配,容器将试图为请求资源提供相关的内容。如果应用中定义了一个“default” servlet,它将被使用。许多容器提供了一种隐式的default servlet用于提供内容

你可能感兴趣的:(教程系列)