ServletContext

2.5 Servlet上下文
运行在Java虚拟机中的每个Web应用程序都有一个与之相关的Servlet上下文。Java Servlet API提供了一个ServletContext接口用来表示上下文。在这个接口中定义了一组方法,Servlet可使用这些方法与它的Servlet容器进行通讯,例如,获得文件的MIME类型,转发请求,或者向日志文件中写入日志消息。
ServletContext对象是Web服务器中的一个已知路径的根。对于本章的实例,Servlet上下文被定位于http://localhost:8080/ch02。以/ch02请求路径(称为上下文路径)开始的全部请求被发送到与此ServletContext关联的Web应用程序。
Servlet容器提供商负责提供 ServletContext接口的实现。Servlet容器在Web应用程序加载时建立ServletContext对象,做为Web应用程序的运行时表示,ServletContext对象能够被Web应用程序中全部的Servlet所访问。
2.5.1  ServletContext接口
一个ServletContext对象表示了一个 Web应用程序的上下文。Servlet容器在Servlet初始化期间,向其传递ServletConfig对象,能够经过ServletConfig 对象的getServletContext()方法来获得ServletContext对象。也能够经过GenericServlet类的 getServletContext()方法获得ServletContext对象,不过GenericServlet类的 getServletContext()也是调用ServletConfig对象的getServletContext()方法来获得这个对象的。
ServletContext接口定义了下面的这些方法,Servlet容器提供了这个接口的实现。
Ø public java.lang.Object getAttribute(java.lang.String name)
Ø  public java.util.Enumeration getAttributeNames()
Ø  public void removeAttribute(java.lang.String name)
Ø  public void setAttribute(java.lang.String name, java.lang.Object object)
上面4个方法用于读取、移除和设置共享属性,任何一个Servlet均可以设置某个属性,而同一个Web应用程序的另外一个Servlet能够读取这个属性,无论这些Servlet是否为同一个客户进行服务。
Ø  public ServletContext getContext(java.lang.String uripath)
该方法返回服务器上与指定的URL相对应的 ServletContext对象。给出的uripath参数必须以斜杠(/)开始,被解释为相对于服务器文档根的路径。出于安全方面的考虑,若是调用该方法访问一个受限制的ServletContext对象,那么该方法将返回null。
Ø  public String getContextPath()
该方法是在Servlet 2.5规范中新增的,用于返回Web应用程序的上下文路径。上下文路径老是以斜杠(/)开头,但结束没有斜杠(/)。在默认(根)上下文中,这个方法返回空字符串""。
Ø  public java.lang.String getInitParameter(java.lang.String name)
Ø  public java.util.Enumeration getInitParameterNames()
能够为Servlet上下文定义初始化参数,这些参数被整个Web应用程序所使用。能够在部署描述符(web.xml)中使用<context-param>元素来定义上下文的初始化参数,上面两个方法用于访问这些参数。
Ø  public int getMajorVersion()
Ø  public int getMinorVersion()
上面两个方法用于返回Servlet容器支持的Java Servlet API的主版本和次版本号。例如,对于听从Servlet 2.4版本的容器,getMajorVersion()方法返回2,getMinorVersion()方法返回4。
Ø  public java.lang.String getMimeType(java.lang.String file)
该方法返回指定文件的MIME类型,若是类型是未知的,这个方法将返回null。MIME类型的检测是根据Servlet容器的配置,也能够在Web应用程序的部署描述符中指定。
Ø  public RequestDispatcher getRequestDispatcher(java.lang.String path)
该方法返回一个RequestDispatcher 对象,做为指定路径上的资源的封装。可使用RequestDispatcher对象将一个请求转发(forward)给其余资源进行处理,或者在响应中包含(include)资源。要注意的是,传入的参数path必须以斜杠(/)开始,被解释为相对于当前上下文根(context root)的路径。
Ø  public RequestDispatcher getNamedDispatcher(java.lang.String name)
该方法与getRequestDispatcher()方法相似。不一样之处在于,该方法接受一个在部署描述符中以<servlet-name>元素给出的Servlet(或JSP页面)的名字做为参数。
Ø  public java.lang.String getRealPath(java.lang.String path)
在一个Web应用程序中,资源用相对于上下文路径的路径来引用,这个方法能够返回资源在服务器文件系统上的真实路径(文件的绝对路径)。返回的真实路径的格式应该适合于运行这个Servlet容器的计算机和操做系统(包括正确的路径分隔符)。若是Servlet容器不可以将虚拟路径转换为真实的路径,这个方法将会返回null。
Ø  public java.net.URL getResource(java.lang.String path) throws java.net.Malformed URLException
该方法返回被映射到指定路径上的资源的URL。传入的参数path必须以斜杠(/)开始,被解释为相对于当前上下文根(context root)的路径。这个方法容许Servlet容器从任何来源为Servlet生成一个可用的资源。资源能够是在本地或远程文件系统上,在数据库中,或者在WAR文件中。若是没有资源映射到指定的路径上,该方法将返回null。
Ø  public java.io.InputStream getResourceAsStream(java.lang.String path)
该方法与getResource()方法相似,不一样之处在于,该方法返回资源的输入流对象。另外,使用getResourceAsStream()方法,元信息(如内容长度和内容类型)将丢失,而使用getResource()方法,元信息是可用的。
Ø  public java.util.Set getResourcePaths(java.lang.String path)
该方法返回资源的路径列表,参数path必须以斜杠(/)开始,指定用于匹配资源的部分路径。例如,一个Web应用程序包含了下列资源:
—  /welcome.html
—  /catalog/index.html
—  /catalog/products.html
—  /catalog/offers/books.html
—  /catalog/offers/music.html
—  /customer/login.jsp
—  /WEB-INF/web.xml
— /WEB-INF/classes/com.acme.OrderServlet.class
若是调用 getResourcePaths("/"),将返回[/welcome.html, /catalog/, /customer/, /WEB-INF/]。若是调用getResourcePaths("/catalog/"),将返回[/catalog/index.html, /catalog/products.html, /catalog/offers/]。
Ø  public java.lang.String getServerInfo()
该方法返回运行Servlet的容器的名称和版本。
Ø  public java.lang.String getServletContextName()
该方法返回在部署描述符中使用<display-name>元素指定的对应于当前ServletContext的Web应用程序的名称。
Ø  public void log(java.lang.String msg)
Ø  public void log(java.lang.String message, java.lang.Throwable throwable)
ServletContext接口提供了上面两个记录日志的方法,第一个方法用于记录通常的日志,第二个方法用于记录指定异常的栈跟踪信息。

2.5.2  页面访问量统计实例
有时候,咱们可能须要统计Web站点上的一个特定页面的访问次数,考虑这样一个场景,你为了宣传一个产品,在某个门户网站花钱作了一个连接,你但愿知道产品页面天天的访问量,借此了解广告的效果。要完成上述功能,可使用ServletContext对象来保存访问的次数。咱们知道一个Web应用程序只有一个ServletContext对象,并且该对象能够被Web应用程序中的全部Servlet所访问,所以使用ServletContext对象来保存一些须要在Web应用程序中共享的信息是再合适不过了。
要在ServletContext对象中保存共享信息,能够调用该对象的setAttribute()方法,要获取共享信息,能够调用该对象的getAttribute()方法。针对本例,咱们能够调用 setAttribute()方法将访问计数保存到上下文对象中,新增一次访问时,调用getAttribute()方法从上下文对象中取出访问计数加 1,而后再调用setAttribute()方法保存回上下文对象中。这个实例的开发主要有下列步骤
Step1:编写CounterServlet类
在%CATALINA_HOME%\webapps\ch02\src目录下新建CounterServlet.java,代码如例2-14所示。
package servlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class CounterServlet extends HttpServlet

{
  @Override
   public void doGet(HttpServletRequest request, HttpServletResponse response)
       throws ServletException, IOException
  {
    ServletContext context = getServletContext();
    Integer count = null;
     synchronized (context)
    {
      count = (Integer) context.getAttribute( "counter");
       if ( null == count)
      {
        count = new Integer(1);
      }
       else
      {
        count = new Integer(count.intValue() + 1);
      }
      context.setAttribute( "counter", count);

    }
    response.setContentType( "text/html;charset=gb2312");
    PrintWriter out = response.getWriter();
    out.println( "<html><head>");
    out.println( "<title>页面访问统计</title>");
    out.println( "</head><body>");
    out.println( "该页面已被访问了" + "<b>" + count + "</b>" + "次");
    out.println( "</body></html>");
    out.close();
  }

}

在程序代码的第17行,调用getServletContext()方法(从GenericServlet类间接继承而来)获得Web应用程序的上下文对象。为了不线程安全的问题,咱们在第19行使用synchronized关键字对context对象进行同步。第21行,调用上下文对象的getAttribute()方法获取counter属性的值。第21~29行,判断count是否为null,若是为null,则将它的初始值设为1。当这个Servlet第一次被访问的时候,在上下文对象中尚未保存counter属性,因此获取该属性的值将返回null。若是 count不为null,则将count加1。第30行,将count做为counter属性的值保存到ServletContext对象中。当下一次访问这个Servlet时,调用getAttribute()方法取出counter属性的值不为null,因而执行第28行的代码,将count加1,此时count为2,代表页面被访问了两次。
第39行,输出count,显示该页面的访问次数。
Step2:编译CounterServlet.java
打开命令提示符,进入%CATALINA_HOME%\webapps\ch02\src目录,而后执行:
javac -d ..\WEB-INF\classes CounterServlet.java
在WEB-INF\classes\org\sunxin\ch02\servlet目录中生成类文件CounterServlet.class。
Step3:部署CounterServlet
编辑WEB-INF目录下的web.xml文件,添加对本例中的Servlet的配置