JavaWeb Servlet

回眸只為那壹抹淺笑 提交于 2020-03-19 13:42:18

1. Servlet介绍

1.1. 百度百科

Servlet是在服务器上运行的小程序。这个词是在Java applet的环境中创造的,Java applet是一种当作单独文件跟网页一起发送的小程序,它通常用于在客户端运行,结果得到为用户进行运算或者根据用户互作用定位图形等服务。

服务器上需要一些程序,常常是根据用户输入访问数据库的程序。这些通常是使用公共网关接口(CGI(Common Gateway Interface))应用程序完成的。然而,在服务器上运行Java,这种程序可使用Java编程语言实现。在通信量大的服务器上,Javaservlet的优点在于它们的执行速度更快于CGI程序。各个用户请求被激活成单个程序中的一个线程,而无需创建单独的进程,这意味着服务器端处理请求的系统开销将明显降低。

1.2. 维基百科

Servlet(Server Applet),全称Java Servlet,未有中文译文。是用Java编写的服务器端程序。其主要功能在于交互式地浏览和修改数据,生成动态Web内容。狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。

Servlet运行于支持Java的应用服务器中。从实现上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。

最早支持Servlet标准的是JavaSoft的Java Web Server。此后,一些其它的基于Java的Web服务器开始支持标准的Servlet。

1.3. 什么是Servlet

ServletJavaEE三大组件之一,是使用Java语言编写服务器端的程序,主要用来处理Web应用程序中的请求-响应。Servlet并没有main之类的执行方法,当用户访问服务器的时候,Tomcat是通过调用Servlet的某些方法来完成整个处理过程的。Servlet是运行在Tomcat服务器提供的Servlet容器中的,所以Servlet是不用程序指定调用的。

1.4. TomcatJavaEE

JavaEE的版本与Tomcat服务器的版本是存在一种对应关系的,在开发Web应用程序的时候,需要注意对应版本关系,不然可能引起Web程序报错。

JavaEETomcat服务器的版本对应关系如下表:

Tomcat服务器版本

Servlet\JSP版本

JavaEE版本

Java运行环境

Tomcat 4.1

Servlet 2.3\JSP 1.2

JavaEE 1.3

JDK 1.3

Tomcat 5.0

Servlet 2.4\JSP 2.0

JavaEE 1.4

JDK 1.4

Tomcat 5.5\6.0

Servlet 2.5\JSP 2.1

JavaEE 5.0

JDK 5.0

Tomcat 7.0

Servlet 3.0\JSP 2.2

JavaEE 6.0

JDK 6.0

如果使用Tomcat服务器的高版本时,可以向下兼容Servlet\JSPJavaEEJava运行环境的版本。目前我们学习研究Servlet,主要是以2.5版本为主,所以使用的JavaEE版本可以是JavaEE 5.0版本。

Tomcat服务器与JavaEE中,分别提供了有关Servlet的帮助文档信息。如果在Tomcat服务器中查看Servlet的相关信息在Tomcat服务器的安装目录中,webapps目录中的docs目录中名为servletapi文件夹中查看。如果在JavaEE中查看有关Servlet的相关信息,需要下载对应JavaEE版本的API帮助文档。

需要注意的是,Servlet 3.0版本在Tomcat服务器和JavaEE都提供帮助文档,但Servlet 2.5版本只有在JavaEE提供了帮助文档,Tomcat并没有提供Servlet 2.5版本的帮助文档。

2. 编写一个Servlet

2.1. 通过Eclipse创建Servlet

开发工具EclipseMyEclipse本身提供了创建Servlet的功能,下面我们首先利用开发工具来创建一个Servlet,具体步骤如下:

  • 首先,我们创建一个Web工程。

 

  • Web工程的src目录下,鼠标右键点击“New”选项,选择“Servlet”选项。

 

  • 弹出创建Servlet的界面窗口,具体内容如下:

 

  • 输入Servlet的包名、名称及相关方法后,点击“Next”按钮进行下一步操作。

 

该界面是配置Servletweb.xml文件中的相关信息,具体内容如下:

    • Servlet/JSP Class NameServlet的完整路径。
    • Servlet/JSP NameServlet的名称。
    • Servlet/JSP Mapping URL:配置Servlet拦截的路径,客户端通过该路径访问Servlet
    • File Path of web.xml:当前工程的web.xml配置文件保存路径。
    • Display Name:显示名称。
    • Description:描述名称。
  • 配置完毕之后,点击Finish”按钮,完成Servlet的创建工作。

创建完成之后,当前Web工程的变化是在src目录下多了一个ServletJava文件,在WEB-INF目录中的web.xml文件中多了Servlet相关配置信息。

创建的ServletJava文件内容:

public class FirstServlet extends HttpServlet {

    public void init() throws ServletException {}
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("<!DOCTYPE HTML >");
        out.println("<HTML>");
        out.println("  <HEAD><TITLE>A Servlet</TITLE></HEAD>");
        out.println("  <BODY>");
        out.println ("    This is "+ this.getClass()+", using the GET method");
        out.println("  </BODY>");
        out.println("</HTML>");
        out.flush();
        out.close();
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("<!DOCTYPE HTML >");
        out.println("<HTML>");
        out.println("  <HEAD><TITLE>A Servlet</TITLE></HEAD>");
        out.println("  <BODY>");
        out.println ("    This is "+ this.getClass()+", using the GET method");
        out.println("  </BODY>");
        out.println("</HTML>");
        out.flush();
        out.close();
    }
    public void destroy() {}
}

Web工程的配置文件web.xml内容:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
    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_2_5.xsd">
  <display-name></display-name>
  <servlet>
    <description>This is the description of my J2EE component</description>
    <display-name>This is the display name of my J2EE component</display-name>
    <servlet-name>FirstServlet</servlet-name>
    <servlet-class>app.java.servlet.FirstServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>FirstServlet</servlet-name>
    <url-pattern>/servlet/FirstServlet</url-pattern>
  </servlet-mapping>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>

将当前Web应用程序发布到Tomcat服务器,并启动Tomcat服务器运行测试当前创建的Servlet内容。

  • 通过EclipseMyEclipse将当前Web工程发布到Tomcat服务器,并启动Tomcat服务器。

 

  • 打开浏览器,在地址栏中输入http://localhost:8080/08_servlet/servlet/FirstServlet,访问服务器端的Servlet内容。

2.2. 手动编写一个Servlet

通过EclipseMyEclipse创建Servlet虽然简单,但我们并不知道相关代码是什么含义。所以,下面我们需要研究一下生成出来的代码。

首先, 我们来研究一下创建的Servlet文件源代码,会发现如下内容:

  • 是继承于HttpServlet类。
  • 包含init()doGet()doPost()destroy()方法。

根据上述内容,我们可以手动创建一个ServletJava文件,如下面的代码所示:

public class SecondServlet extends HttpServlet {
    @Override
    public void init() throws ServletException {
        System.out.println("这是init()方法...");
    }
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        System.out.println("这是doGet()方法...");
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("这是doPost()方法...");
    }
    @Override
    public void destroy() {
        System.out.println("这是destroy()方法...");
    }
}

其次,web.xml配置文件增加的内容:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
    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_2_5.xsd">
  <display-name></display-name>
  <!-- 
        servlet标签:用于配置Servlet的名称和完整路径.
         * servlet-name标签:用于为对应的Servlet设置名称.
         * servlet-class标签:用于设置对应的Servlet的完整路径.
   -->
  <servlet>
    <servlet-name>SecondServlet</servlet-name>
    <servlet-class>app.java.servlet.SecondServlet</servlet-class>
  </servlet>
  <!-- 
        servlet-mapping标签:用于配置Servlet拦截客户端请求路径.
         * servlet-name标签:用于设置对应使用的Servlet名称.
         * url-pattern标签:用于设置客户端请求的拦截路径.
           * 相对路径:/servlet/FirstServlet
           * 绝对路径:http://localhost:8080/08_servlet/servlet/FirstServlet
   -->
  <servlet-mapping>
    <servlet-name>SecondServlet</servlet-name>
    <url-pattern>/servlet/SecondServlet</url-pattern>
  </servlet-mapping>
      
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>

将当前Web应用程序发布到Tomcat服务器,并启动Tomcat服务器运行测试当前创建的Servlet内容。

  • 通过EclipseMyEclipse将当前Web工程发布到Tomcat服务器,并启动Tomcat服务器。
  • 打开浏览器,在地址栏中输入http://localhost:8080/08_servlet/servlet/SecondServlet,访问服务器端的Servlet内容。

2.3. Servlet的继承链

通过创建ServletJava文件中的代码内容,可以发现创建的Servlet是继承于HttpServlet类,查看JavaEE帮助文档中的HttpServlet内容。

Method Summary

protected  void

doGet(HttpServletRequest req, HttpServletResponse resp) 
          Called by the server (via the service method) to allow a servlet to handle a GET request.

protected  void

doPost(HttpServletRequest req, HttpServletResponse resp) 
          Called by the server (via the service method) to allow a servlet to handle a POST request.

protected  void

service(HttpServletRequest req, HttpServletResponse resp) 
          Receives standard HTTP requests from the public service method and dispatches them to the doXXX methods defined in this class.

 void

service(ServletRequest req, ServletResponse res) 
          Dispatches client requests to the protected service method.

通过帮助文档提供的内容,我们可以知道创建Servlet中的doGetdoPost方法是继承于HttpServlet提供的,但还有initdestroy方法没有找到。我们发现HttpServlet是继承于GenericServlet,查看JavaEE帮助文档中的GenericServlet内容。

Method Summary

 void

destroy() 
          Called by the servlet container to indicate to a servlet that the servlet is being taken out of service.

 void

init() 
          A convenience method which can be overridden so that there's no need to call super.init(config).

abstract  void

service(ServletRequest req, ServletResponse res) 
          Called by the servlet container to allow the servlet to respond to a request.

通过帮助文档提供的内容,我们可以知道initdestroy方法是源于GenericServlet。但是其实GenericServlet都实现了Servlet接口。

Method Summary

 void

destroy() 
          Called by the servlet container to indicate to a servlet that the servlet is being taken out of service.

 ServletConfig

getServletConfig() 
          Returns a ServletConfig object, which contains initialization and startup parameters for this servlet.

 String

getServletInfo() 
          Returns information about the servlet, such as author, version, and copyright.

 void

init(ServletConfig config) 
          Called by the servlet container to indicate to a servlet that the servlet is being placed into service.

 void

service(ServletRequest req, ServletResponse res) 
          Called by the servlet container to allow the servlet to respond to a request.

所以,initdestroy方法是Servlet接口提供的方法。通过上述的查找,我们已经很清晰的知道了Servlet的继承关系,具体如下图:

 

2.4. Servlet工作流程

利用Servlet完成的Web应用的实际工作流程是通过Tomcat服务器发布服务,客户端与服务器端之间的交互遵循Http协议完成的。具体工作流程如下:

  • 客户端浏览器向服务器端发送请求。
  • 服务器端由Tomcat服务器提供的Servlet容器解析接收到的请求。
  • 通过Web应用程序的配置文件web.xml,解析到对应执行的Servlet
  • Servlet完成客户端发送的请求逻辑,并完成向客户端发送的响应内容。
  • Tomcat服务器提供的Servlet容器向客户端浏览器进行响应。

我们也可以通过下面的图来理解Servlet具体的工作流程。

 

2.5. Servlet注意事项

值得注意的是Servlet内容是属于JavaEE内容,和使用JavaSE内容一样,都是需要引入JAR包的。使用EclipseMyEclipse创建Web应用程序的时候,会发现已经导入了JavaEE所需要的JAR包。其中javaee.jar包中包含了使用Servlet的所有内容。

但是,当把Web应用程序发布到Tomcat服务器的时候,发现对应的目录中并没有javaee.jar包。我们知道无论是编译还是运行都是需要这些JAR包的,这说明Tomcat服务器本身提供了Servlet运行所需要的环境。在Tomcat服务器的安装目录中的lib目录中可以找到servlet-api.jar包,该JAR包也提供了Servlet运行所需的环境。

我们如果想要手动编译Servlet的话,需要做以下及步:

  • Tomcat安装目录中的webapps目录创建Web工程名称及目录结构。
  • 在命令行中利用命令编译Servlet文件。
javac -classpath C:\Tools\apache-tomcat-7.0.55\lib\servlet-api.jar -d . Servlet.java
  • 在对应Web工程目录的WEB-INF目录中的web.xml进行配置。

3. 深入掌握Servlet

3.1. Servlet的生命周期

一般情况下,自定义Servlet都是继承HttpServlet。但通过HttpServlet的继承链,我们知道HttpServlet是实现了Servlet接口,下面列表是Servlet接口提供的所有方法。

Method Summary

 void

destroy() 
          Called by the servlet container to indicate to a servlet that the servlet is being taken out of service.

 ServletConfig

getServletConfig() 
          Returns a ServletConfig object, which contains initialization and startup parameters for this servlet.

 String

getServletInfo() 
          Returns information about the servlet, such as author, version, and copyright.

 void

init(ServletConfig config) 
          Called by the servlet container to indicate to a servlet that the servlet is being placed into service.

 void

service(ServletRequest req, ServletResponse res) 
          Called by the servlet container to allow the servlet to respond to a request.

上述所有方法中,init()service()destroy()方法叫做Servlet的生命周期。下面我们分别讨论一下有关生命周期的三个方法:

  • init()方法
    • Servlet实例化之后,Servlet容器会调用init()方法,主要是用来完成处理客户端请求之前的初始化工作。
    • init()方法在Servlet的生命周期中只被执行一次。
  • service()方法
    • Servlet容器调用service()方法来处理客户端发送的请求。在service()方法被调用之前,必须保证init()方法被正确执行。
    • service()方法在每次客户端发送请求之后,会被执行一次。
  • destroy()方法
    • Servlet容器检测到当前Servlet实例被移除时,会调用destroy()方法,以便让Servlet实例可以释放所使用的所有资源。
    • destroy()方法在Servlet的生命周期中也只被执行一次。

下面我们通过实际操作来讨论关于Servlet的生命周期是怎么样的:

  • 首先,创建一个Servlet文件,具体如下。
public class LifeServlet implements Servlet {

    public LifeServlet(){
        System.out.println("这里创建了一个Servlet实例对象...");
    }

    public void init(ServletConfig config) throws ServletException {
        System.out.println("这是init()方法...");
    }

    public void service(ServletRequest req, ServletResponse res)
            throws ServletException, IOException {
        System.out.println("这是service()方法...");
    }

    public void destroy() {
        System.out.println("这是destroy()方法...");
    }
    
    public ServletConfig getServletConfig() {
        return null;
    }
    public String getServletInfo() {
        return null;
    }
}
  • web.xml文件中,配置有关Servlet信息。
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
    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_2_5.xsd">
  <servlet>
    <servlet-name>LifeServlet</servlet-name>
    <servlet-class>com.servlet.LifeServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>LifeServlet</servlet-name>
    <url-pattern>/servlet/LifeServlet</url-pattern>
  </servlet-mapping>
</web-app>
  • Web应用程序发布到Tomcat服务器,并启动Tomcat服务器。
  • 启动Tomcat服务器之后,我们可以查看Tomcat服务器启动的日志内容,并没有有关Servlet信息。

 

十一月 20, 2017 1:22:48 下午 org.apache.catalina.core.AprLifecycleListener init
INFO: Loaded APR based Apache Tomcat Native library 1.2.12.
十一月 20, 2017 1:22:48 下午 org.apache.catalina.core.AprLifecycleListener init
INFO: APR capabilities: IPv6 [true], sendfile [true], accept filters [false], random [true].
十一月 20, 2017 1:22:49 下午 org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["http-apr-8080"]
十一月 20, 2017 1:22:49 下午 org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["ajp-apr-8009"]
十一月 20, 2017 1:22:49 下午 org.apache.catalina.startup.Catalina load
INFO: Initialization processed in 1914 ms
十一月 20, 2017 1:22:49 下午 org.apache.catalina.core.StandardService startInternal
INFO: Starting service Catalina
十一月 20, 2017 1:22:49 下午 org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet Engine: Apache Tomcat/7.0.27
十一月 20, 2017 1:22:49 下午 org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deploying web application directory E:\apache-tomcat-7.0.27\webapps\08_servlet
十一月 20, 2017 1:22:50 下午 com.sun.faces.config.ConfigureListener contextInitialized
INFO: 初始化上下文 '/08_servlet' 的 Mojarra 2.0.3 (FCS b03)
十一月 20, 2017 1:22:51 下午 com.sun.faces.spi.InjectionProviderFactory createInstance
INFO: JSF1048:有 PostConstruct/PreDestroy 注释。标有这些注释的 ManagedBeans 方法将表示注释已处理。
十一月 20, 2017 1:22:51 下午 org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deploying web application directory E:\apache-tomcat-7.0.27\webapps\docs
十一月 20, 2017 1:22:51 下午 org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deploying web application directory E:\apache-tomcat-7.0.27\webapps\examples
十一月 20, 2017 1:22:51 下午 org.apache.catalina.core.ApplicationContext log
INFO: ContextListener: contextInitialized()
十一月 20, 2017 1:22:51 下午 org.apache.catalina.core.ApplicationContext log
INFO: SessionListener: contextInitialized()
十一月 20, 2017 1:22:51 下午 org.apache.catalina.core.ApplicationContext log
INFO: ContextListener: attributeAdded('org.apache.jasper.compiler.TldLocationsCache', 'org.apache.jasper.compiler.TldLocationsCache@526509bf')
十一月 20, 2017 1:22:51 下午 org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deploying web application directory E:\apache-tomcat-7.0.27\webapps\host-manager
十一月 20, 2017 1:22:51 下午 org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deploying web application directory E:\apache-tomcat-7.0.27\webapps\manager
十一月 20, 2017 1:22:51 下午 org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deploying web application directory E:\apache-tomcat-7.0.27\webapps\ROOT
十一月 20, 2017 1:22:52 下午 org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["http-apr-8080"]
十一月 20, 2017 1:22:52 下午 org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["ajp-apr-8009"]
十一月 20, 2017 1:22:52 下午 org.apache.catalina.startup.Catalina start
INFO: Server startup in 2333 ms
  • 打开浏览器,在地址栏中输入http://localhost:8080/08_servlet/servlet/LifeServlet,并查看控制台信息。

 

  • 重新刷新页面,再次发送请求,调用Servlet内容。

 

  • 停止Tomcat服务器,并查看控制台信息。

 

通过上述操作,我们可以发现:在第一次向Servlet发送请求时,Tomcat服务器的Servlet容器首先创建Servlet实例对象,再进行Servlet初始化工作,最后调用service()方法来处理请求。第二次向Servlet发送请求时,只调用了service()方法,并没有执行Servlet的构造方法和init()方法。在停止Tomcat服务器时,Servletdestroy()方法被调用,释放所使用的资源。

3.2. Servlet的线程安全

Servlet的整个生命周期中,构造方法只被执行一次。也就是说,在Servlet的整个生命周期中,只存在一个Servlet实例对象。这说明Servlet是单例多线程的,可能会引起线程安全问题。

所谓线程安全就是一个Servlet实例对象会同时处理多个请求,这样的Servlet工作效率的确很高。但如果Servlet中包含成员变量的话,可能一个线程对该成员变量进行写操作,而另一个线程对该成员变量进行读操作。所以,单例多线程的Servlet不能创建成员变量。

3.3. 服务器启动创建Servlet

通过之前的测试操作,我们发现Servlet并不是在Tomcat服务器启动时创建实例对象,而是在第一次请求之后才一并创建实例对象、初始化并处理逻辑的。Servlet又是单例的,在整个生命周期中只创建一个Servlet实例对象。如果可以在Tomcat服务器启动时,进行创建实例对象并完成初始化工作,在请求之后只调用service()方法处理请求逻辑即可。

可以在web.xml配置文件增加相关配置即可,具体增加内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
    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_2_5.xsd">
  <servlet>
    <servlet-name>LifeServlet</servlet-name>
    <servlet-class>app.java.servlet.LifeServlet</servlet-class>
    <!-- 
        load-on-startup标签:配置当前Servlet在启动时创建实例对象
         * 标签中的数字,表示级别(0-9级),一般设置为0即可.
     -->
    <load-on-startup>0</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>LifeServlet</servlet-name>
    <url-pattern>/servlet/LifeServlet</url-pattern>
  </servlet-mapping>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>

增加配置信息之后,重新启动Tomcat服务器,查看控制台信息

通过查看控制台打印的信息,可以看出在启动Tomcat服务器时,已经创建并初始化了Servlet实例对象。

3.4. url-pattern的三种配置

web.xml配置文件中配置有关Servlet的时候,<url-pattern>标签是用于配置当前Servlet拦截的路径,也就是说,客户端浏览器访问<url-pattern>标签配置的路径才能访问对应Servlet内容。

关于拦截路径的配置方式其实有三种方式:

  • 完全路径匹配:是以/”开始,路径中间不能包含通配符“*”,例如:/firstServclet,表示访问路径为http://localhost:8080/08_servlet/firstServlet
  • 目录匹配:是以/”开始,以“/*”结尾的,例如:/firstServlet/*,表示访问路径为http://localhost:8080/08_servlet/firstServlet路径下任意内容。
  • 扩展名匹配:是以*”开始,不能以“/”开始,以“.xxx”结尾,例如:*.do,表示访问路径为所有扩展名为“.do”的路径。

值得注意的问题:

  • 在一个<servlet-mapping>标签中,可以配置多个<url-pattern>标签。也就是说,一个Servlet可以拦截多个不同路径的访问。
  • 上述三种配置路径方式具有优先级:完全路径匹配 -> 目录匹配 -> 扩展名匹配。

下面通过一些测试,来看看路径配置的三种方式:

如下有一些映射关系:

  • Servlet1 映射到 /abc/*
  • Servlet2 映射到 /*
  • Servlet3 映射到 /abc
  • Servlet4 映射到 *.do

问题:

  • 当请求URL为“/abc/a.html”,“/abc/*”和“/*”都匹配,哪个servlet响应?Servlet1
  • 当请求URL为“/abc”时,“/abc/*”和“/abc”都匹配,哪个servlet响应?Servlet3
  • 当请求URL为“/abc/a.do”时,“/abc/*”和“*.do”都匹配,哪个servlet响应?Servlet1
  • 当请求URL为“/a.do”时,“/*”和“*.do”都匹配,哪个servlet响应?Servlet2
  • 当请求URL为“/xxx/yyy/a.do”时,“/*”和“*.do”都匹配,哪个servlet响应?Servlet2

如果客户端浏览器请求的路径是错误时,页面会显示404错误内容。这是因为所有发布到Tomcat服务器的Web应用程序的web.xml文件都继承了Tomcat服务器安装目录中conf目录中的web.xml文件。当访问路径是错误的,或者对应Servlet没有配置,实际上会执行Tomcat服务器中的web.xml的相关配置,具体内容如下:

<servlet>
    <servlet-name>default</servlet-name>
    <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
    <init-param>
        <param-name>debug</param-name>
        <param-value>0</param-value>
    </init-param>
    <init-param>
        <param-name>listings</param-name>
        <param-value>true</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

3.5. 相对路径与绝对路径

之前我们开发的Servlet,在客户端浏览器中都是直接在地址栏中输入路径来访问的。如果创建一个页面来访问Servlet应该怎么样呢?下面我们来看一看:

  • Web工程中的WebRoot目录下,创建一个新目录名为“html”,然后在该目录下创建一个新的HTML页面。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>01.html</title>
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="this is my page">
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <h1>相对路径访问Servlet</h1><br>
    <a href="">相对路径访问Servlet</a>
    <h1>绝对路径访问Servlet</h1><br>
    <a href="">绝对路径访问Servlet</a>
  </body>
</html>
  • Web工程中的WebRoot目录下,创建一个新的HTML页面。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>02.html</title>
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="this is my page">
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <h1>相对路径访问Servlet</h1><br>
    <a href="">相对路径访问Servlet</a>
    <h1>绝对路径访问Servlet</h1><br>
    <a href="">绝对路径访问Servlet</a>
  </body>
</html>
  • Web工程中创建一个Servlet
public class PathServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("成功访问到Servlet...");
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}
  • 配置Web工程的web.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
    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_2_5.xsd">
  <servlet>
    <servlet-name>PathServlet</servlet-name>
    <servlet-class>app.java.servlet.PathServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>PathServlet</servlet-name>
    <url-pattern>/pathServlet</url-pattern>
  </servlet-mapping>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>
  • 将当前Web工程发布到Tomcat服务器,并启动Tomcat服务器。
  • 打开浏览器,分别访问01.html02.htmlPathServlet
    • 访问01.html的路径:http://localhost:8080/08_servlet/html/01.html
    • 访问02.html的路径:http://localhost:8080/08_servlet/02.html
    • 访问PathServlet的路径:http://localhost:8080/08_servlet/pathServlet
  • 根据上述的访问路径,可以知道在01.html02.html页面中,通过绝对路径访问PathServlet是相同的。
  • 01.html02.html页面中,通过相对路径访问PathServlet是不同的。
    • 01.html页面中利用相对路径访问PathServlet应该是../pathServlet。原因是pathServlet是在01.html页面的父级目录中。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>01.html</title>
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="this is my page">
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <h1>相对路径访问Servlet</h1><br>
    <a href="../pathServlet">相对路径访问Servlet</a>
    <h1>绝对路径访问Servlet</h1><br>
    <a href="http://localhost:8080/08_servlet/pathServlet">绝对路径访问Servlet</a>
  </body>
</html>
    • 01.html页面中利用相对路径访问PathServlet应该是./pathServlet或直接访问拦截名称pathServlet。原因是pathServlet02.html页面处在同一级别的目录中。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>02.html</title>
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="this is my page">
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <h1>相对路径访问Servlet</h1><br>
    <a href="pathServlet">相对路径访问Servlet</a>
    <h1>绝对路径访问Servlet</h1><br>
    <h1>绝对路径访问Servlet</h1><br>
    <a href="http://localhost:8080/08_servlet/pathServlet">绝对路径访问Servlet</a>
  </body>
</html>

什么是绝对路径与相对路径:

  • 绝对路径:就是无论当前资源在什么位置,都能通过当前资源访问到目标资源。
  • 相对路径:就是判断当前资源与目标资源的相对位置,找出相对当前资源可以访问到目标资源的路径。

4. 研究掌握HttpServlet

4.1. HttpServlet概述

在大多数的Web应用程序中,客户端都是通过Http协议去访问服务器端的资源,而我们编写的Servlet主要是用于Http协议的请求和响应处理。为了快速开发应用于Http协议的Servlet类,Sun公司在javax.servlet.http包中提供了一个抽象类HttpServlet,它继承于GenericServlet,用于创建适合基于Http协议的Web Servlet

public abstract class HttpServlet extends GenericServlet {}

下列表中罗列了HttpServlet的所有方法:

Method Summary

protected  void

doDelete(HttpServletRequest req, HttpServletResponse resp) 
          Called by the server (via the service method) to allow a servlet to handle a DELETE request.

protected  void

doGet(HttpServletRequest req, HttpServletResponse resp) 
          Called by the server (via the service method) to allow a servlet to handle a GET request.

protected  void

doHead(HttpServletRequest req, HttpServletResponse resp) 
          Receives an HTTP HEAD request from the protected service method and handles the request.

protected  void

doOptions(HttpServletRequest req, HttpServletResponse resp) 
          Called by the server (via the service method) to allow a servlet to handle a OPTIONS request.

protected  void

doPost(HttpServletRequest req, HttpServletResponse resp) 
          Called by the server (via the service method) to allow a servlet to handle a POST request.

protected  void

doPut(HttpServletRequest req, HttpServletResponse resp) 
          Called by the server (via the service method) to allow a servlet to handle a PUT request.

protected  void

doTrace(HttpServletRequest req, HttpServletResponse resp) 
          Called by the server (via the service method) to allow a servlet to handle a TRACE request.

protected  long

getLastModified(HttpServletRequest req) 
          Returns the time the HttpServletRequest object was last modified, in milliseconds since midnight January 1, 1970 GMT.

protected  void

service(HttpServletRequest req, HttpServletResponse resp) 
          Receives standard HTTP requests from the public service method and dispatches them to the doXXX methods defined in this class.

 void

service(ServletRequest req, ServletResponse res) 
          Dispatches client requests to the protected service method.

下面我们就针对HttpServlet抽象类中提供的各个方法进行讨论。

4.2. 重写的service()方法

HttpServlet类中提供了两种重载的service()方法:

public abstract class HttpServlet extends GenericServlet {
    @Override
    public void service(ServletRequest req, ServletResponse res)
        throws ServletException, IOException {
        HttpServletRequest  request;
        HttpServletResponse response;
        try {
            request = (HttpServletRequest) req;
            response = (HttpServletResponse) res;
        } catch (ClassCastException e) {
            throw new ServletException("non-HTTP request or response");
        }
        service(request, response);
}
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getMethod();
        if (method.equals(METHOD_GET)) {
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doGet(req, resp);
        } else if (method.equals(METHOD_HEAD)) {
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doHead(req, resp);
        } else if (method.equals(METHOD_POST)) {
            doPost(req, resp);
        } else if (method.equals(METHOD_PUT)) {
            doPut(req, resp);
        } else if (method.equals(METHOD_DELETE)) {
            doDelete(req, resp);
        } else if (method.equals(METHOD_OPTIONS)) {
            doOptions(req,resp);
        } else if (method.equals(METHOD_TRACE)) {
            doTrace(req,resp);
        } else {
            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[1];
            errArgs[0] = method;
            errMsg = MessageFormat.format(errMsg, errArgs);
            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
        }
  }
}

通过查看HttpServlet的源代码我们可以知道,第一个service()方法是GenericServlet类中的Service()方法的实现。在该service()方法中将ServletRequest和ServletResponse对象转换成HttpServletRequest(继承于ServletRequest接口)和HttpServletResponse(继承于ServletResponse接口),然后调用了第二个service()方法,对客户端的请求进行处理。

在第二个service()方法中,针对Http 1.1协议中定义的7种请求方式GetPostHeadPutDeleteTraceOptions提供了7种处理方法7种方法的参数类型及异常抛出类型与HttpServlet类中的第二个service()方法是一致的。当Servlet容器接收到一个针对HttpServlet对象的请求时,调用该对象的方法顺序如下:

  • 调用公用service()方法,将参数类型转换成HttpServletRequest和HttpServletResponse,然后调用受保护的service()方法。
  • 在受保护的service()方法中,获取Http请求方法的名字,然后根据请求方法的类型,调用响应的doXXX()方法。

因此,我们在自定义Servlet继承于HttpServlet的时候,通常不需要重写service()方法,只需重写响应的doXXX()方法即可。

4.3. 七种请求处理方法

HttpServlet抽象类中提供了针对Http 1.1协议中定义的7种请求方式GetPostHeadPutDeleteTraceOptions提供了7种处理方法:

  • protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
  • protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
  • protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
  • protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
  • protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
  • protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
  • protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException

7种处理请求的方法中,HttpServlet类对TraceOptions方法做了适当的实现,因此我们不需要重写doTrace()doOptions()方法。而对于其他的5种请求处理方法,HttpServlet类提供的实现都是返回Http错误。对于Http 1.0的客户端请求,这些方法返回的状态码为400,表示客户端发送的请求在语法上是错误的。对于Http 1.1的客户端请求,这些方法返回的状态码为405,表示对于指定资源的请求方法不被允许。

而对于Http协议的实际应用来讲,我们最常用的是GetPost,所以在自定义Servlet时,常重写doGet()doPost()两个请求处理方法。其中doGet()方法用来处理Get方式的请求,doPost()方法用来处理Post方式的请求,下面我们通过一个案例来看一看:

  • 创建一个HTML页面用于发送客户端请求。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>index.html</title>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <h1>GET方式请求</h1>
    <form id="userinfo" method="get" action="threeServlet">
        用户名:<input type="text" id="username" name="username">
        <input type="submit" id="submit" value="提交">
    </form>
    <h1>POST方式请求</h1>
    <form id="userinfo" method="post" action="threeServlet">
        用户名:<input type="text" id="username" name="username">
        <input type="submit" id="submit" value="提交">
    </form>
  </body>
</html>
  • 创建一个Servlet用于处理请求。
public class ThreeServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("这是GET方式发送的请求,Servlet使用doGet()方法来处理.");
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("这是POST方式发送的请求,Servlet使用doPost()方法来处理.");
    }
}
  • 配置web.xml文件。
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
    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_2_5.xsd">
  <servlet>
<servlet-name>ThreeServlet</servlet-name>
    <servlet-class>app.java.servlet.ThreeServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>ThreeServlet</servlet-name>
    <url-pattern>/threeServlet</url-pattern>
  </servlet-mapping>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>
  • Web工程发布到Tomcat服务器,并启动Tomcat服务器。
  • 打开浏览器,在地址栏中输入http://localhost:8080/08_servlet/index.html

利用GET方式发送请求,控制台会打印doGet()方法的内容。利用POST方式发送请求,控制台会打印doPost()方法的内容。

4.4. RequestResponse

HttpServlet提供的service()方法与7种处理请求的方法都接收两个参数,一个是HttpServletRequest继承于ServletRequest,是用于处理Http协议中的请求,一个是HttpServletResponse继承于ServletResponse,是用于处理Http协议中的响应。

关于HttpServletRequest和HttpServletResponse的具体内容,我们会在后面的知识中学到。

5. 研究掌握GenericServlet

5.1. GenericServlet概述

如果我们直接通过实现Servlet接口来编写一个Servlet类,就需要实现Servlet接口定义的5种方法,为了简化Servlet的编写,在javax.servlet包中提供了一个抽象类GenericServlet,该类提供了除service()方法外的其他4种方法的简单实现。GenericServlet类定义了一个通用的、不依赖于具体协议的Servlet,它实现了Servlet接口和ServletConfig接口。

public abstract class GenericServlet implements Servlet, ServletConfig

下列表中罗列了GenericServlet的所有方法:

Method Summary

 void

destroy() 
          Called by the servlet container to indicate to a servlet that the servlet is being taken out of service.

 ServletConfig

getServletConfig() 
          Returns this servlet's ServletConfig object.

 ServletContext

getServletContext() 
          Returns a reference to the ServletContext in which this servlet is running.

 void

init() 
          A convenience method which can be overridden so that there's no need to call super.init(config).

 void

init(ServletConfig config) 
          Called by the servlet container to indicate to a servlet that the servlet is being placed into service.

abstract  void

service(ServletRequest req, ServletResponse res) 
          Called by the servlet container to allow the servlet to respond to a request.

5.2. 重写的init()方法

如果我们要自定义一个通用的Servlet,只需要从GenericServlet类继承,并实现其中的抽象方法service()方法。

GenericServlet类中,提供了两种重载的init()方法:

  • public void init(ServletConfig config) throws ServletException
  • public void init() throws ServletException

第一种init()方法是Servlet接口中init()方法的实现。在这种方法中,首先将ServletConfig对象保存在一个transient实例变量中,然后调用第二种不带参数的init()方法。

通常我们在编写继承于GenericServlet的自定义Serlvet时,只需要重写第二中不带参数的init()方法就可以了。

5.3. ServletConfig对象

javax.servlet包中,定义了ServletConfig接口。Servlet容器使用ServletConfig对象在Servlet初始化时向其传递配置信息。

所谓的Serlvet配置信息,就是在Web应用程序中web.xml文件中配置有关Servlet的内容。

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
    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_2_5.xsd">
  <display-name></display-name>
  <servlet>
    <description>This is the description of my J2EE component</description>
    <display-name>This is the display name of my J2EE component</display-name>
    <servlet-name>FirstServlet</servlet-name>
    <servlet-class>app.java.servlet.FirstServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>FirstServlet</servlet-name>
    <url-pattern>/servlet/FirstServlet</url-pattern>
  </servlet-mapping>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>

要想从ServletConfig对象获取有关Servlet的配置信息,首先需要获得ServletConfig对象,方式有以下几种:

  • 通过继承GenericServlet,并重写init(ServletConfig config)方法来获取ServletConfig对象。
public class ConfigServlet extends GenericServlet {
    private ServletConfig config;
    
    @Override
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
    }
    
    @Override
    public void service(ServletRequest req, ServletResponse res)
            throws ServletException, IOException {
    }
}
  • 通过继承GenericServlet,并调用getServletConfig()方法来获取ServletConfig对象。
ServletConfig servletConfig = getServletConfig();

ServletConfig接口提供了以下方法供使用:

Method Summary

 String

getInitParameter(String name) 
          Returns a String containing the value of the named initialization parameter, or null if the parameter does not exist.

 Enumeration

getInitParameterNames() 
          Returns the names of the servlet's initialization parameters as an Enumeration of String objects, or an empty Enumeration if the servlet has no initialization parameters.

 ServletContext

getServletContext() 
          Returns a reference to the ServletContext in which the caller is executing.

 String

getServletName() 
          Returns the name of this servlet instance.

下面我们一一来讨论上述方法的作用:

  • getServletName()方法:获取web.xml文件中配置的Servlet名称。
  <servlet>
    <servlet-name>ConfigServlet</servlet-name>
    <servlet-class>app.java.servlet.ConfigServlet</servlet-class>
  </servlet>

我们通过获取到的ServletConfig对象调用getServletName()方法来验证。

public void service(ServletRequest req, ServletResponse res)
            throws ServletException, IOException {
    ServletConfig servletConfig = getServletConfig();
        
    String servletName = servletConfig.getServletName();
    System.out.println(servletName);
}

运行Web应用程序,在控制台中打印“ConfigServlet”。

  • getInitParameter(String name)方法:返回指定名称的初始化参数的值,如果参数不存在则返回null值。

首先,我们需要在web.xml文件中有关Servlet配置信息中,增加初始化参数的内容。

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
    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_2_5.xsd">
  <servlet>
    <servlet-name>ConfigServlet</servlet-name>
    <servlet-class>app.java.servlet.ConfigServlet</servlet-class>
    <init-param>
        <param-name>name</param-name>
        <param-value>123</param-value>
    </init-param>
    <init-param>
        <param-name>baidu</param-name>
        <param-value>http://www.baidu.com</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>ConfigServlet</servlet-name>
    <url-pattern>/servlet/ConfigServlet</url-pattern>
  </servlet-mapping>
</web-app>

然后,我们在自定义Servletservice()方法中获取对应初始化参数。

public void service(ServletRequest req, ServletResponse res)
            throws ServletException, IOException {
    ServletConfig servletConfig = getServletConfig();
        
    String name = servletConfig.getInitParameter("name");
    String blog = servletConfig.getInitParameter("baidu");
    System.out.println(name + baidu);
}

运行Web应用程序,在控制台中打印“123 http://www.baidu.com”。

  • getInitParameterNames()方法:返回Servlet配置的所有初始化参数名称的枚举集合。
    • EnumerationIterator的前身,用法与Iterator一致。
public void service(ServletRequest req, ServletResponse res)
            throws ServletException, IOException {
    ServletConfig servletConfig = getServletConfig();
        
    Enumeration enumeration = servletConfig.getInitParameterNames();
    while (enumeration.hasMoreElements()) {
        String element = (String) enumeration.nextElement();
        String value = servletConfig.getInitParameter(element);
        System.out.println(element + ": " + value);
    }
}

运行Web应用程序,在控制台中打印:

name: 123

blog: http://www.baidu.com

  • getServletContext()方法:返回一个ServletContext对象,后面具体学习。

ServletConfig有哪些实际作用呢?在struts 1 框架中就运行了ServletConfig内容。

<!-- Standard Action Servlet Configuration (with debugging) -->
<servlet>
    <servlet-name>action</servlet-name>
    <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
    <init-param>
      <param-name>config</param-name>
      <param-value>
          /WEB-INF/struts-config.xml,
          /WEB-INF/struts-config-Wildcard.xml
       </param-value>
    </init-param>
    <load-on-startup>2</load-on-startup>
</servlet>
<!-- Standard Action Servlet Mapping -->
<servlet-mapping>
    <servlet-name>action</servlet-name>
    <url-pattern>*.do</url-pattern>
</servlet-mapping>

6. ServletContext对象

6.1. ServletContext概述

ServletContext对象是Servlet三大域对象之一,每个Web应用程序都拥有一个ServletContext对象,该对象是Web应用程序的全局对象或者上下文。Tomcat服务器在启动时,会自动创建一个ServletContext对象,在关闭时,会自动销毁这个ServletContext对象。每个Web应用程序只拥有一个ServletContext对象,ServletContext对象可以在整个Web应用中共享数据资源。

下列是ServletContext提供的方法列表:

Method Summary

 Object

getAttribute(String name) 
          Returns the servlet container attribute with the given name, or null if there is no attribute by that name.

 Enumeration

getAttributeNames() 
          Returns an Enumeration containing the attribute names available within this servlet context.

 String

getInitParameter(String name) 
          Returns a String containing the value of the named context-wide initialization parameter, or null if the parameter does not exist.

 Enumeration

getInitParameterNames() 
          Returns the names of the context's initialization parameters as an Enumeration of String objects, or an empty Enumeration if the context has no initialization parameters.

 String

getMimeType(String file) 
          Returns the MIME type of the specified file, or null if the MIME type is not known.

 String

getRealPath(String path) 
          Returns a String containing the real path for a given virtual path.

 String

getServletContextName() 
          Returns the name of this web application corresponding to this ServletContext as specified in the deployment descriptor for this web application by the display-name element.

 Enumeration

getServletNames() 
          Deprecated. As of Java Servlet API 2.1, with no replacement.

 void

log(String msg) 
          Writes the specified message to a servlet log file, usually an event log.

 void

removeAttribute(String name) 
          Removes the attribute with the given name from the servlet context.

 void

setAttribute(String name, Object object) 
          Binds an object to a given attribute name in this servlet context.

6.2. 获取ServletContext对象

在自定义Servlet中有以下几种方式获取到ServletContext对象:

  • 通过ServletConfig对象的getServletContext()方法获取。
  • 通过继承GenericServlet类或HttpServlet类,调用GenericServlet类或HttpServlet类的getServletContext()方法获取。

我们通过一个案例来讨论一下。

  • 首先,创建一个自定义Servlet,用来获取ServletContext对象。
public class AServlet extends GenericServlet {
    @Override
    public void service(ServletRequest req, ServletResponse res)
            throws ServletException, IOException {
        ServletConfig config = getServletConfig();
        ServletContext context1 = config.getServletContext();
        context1.log("这是通过ServletConfig对象获取到的ServletContext对象.");
        ServletContext context2 = getServletContext();
        context2.log("这是通过继承GenericServlet类获取到的ServletContext对象.");
    }
}
  • web.xml文件中配置Servlet相关信息。
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
    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_2_5.xsd">
  <servlet>
    <servlet-name>AServlet</servlet-name>
    <servlet-class>app.java.context.AServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>AServlet</servlet-name>
    <url-pattern>/servlet/AServlet</url-pattern>
  </servlet-mapping>
</web-app>
  • Web应用程序发布到Tomcat服务器,并启动Tomcat服务器。
  • 打开浏览器,在地址栏中输入http://localhost:8080/08_servlet/servlet/AServlet,在控制台打印相关信息。

通过ServletContext对象的log(Stirng msg)方法,可以向控制台打印信息。

6.3. 配置全局初始化参数

web.xml文件中,使用<init-param>定义的初始化参数,只能在当前Servlet中使用,而其他Servlet是无权限访问当前Servlet下配置的初始化参数的。而可以使用ServletContextweb.xml文件中配置全局初始化参数,这样当前Web应用程序中的所有Servlet都可以访问。

  • web.xml文件中使用<context-param>来定义全局初始化参数。
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
    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_2_5.xsd">
  <context-param>
      <param-name>weixin</param-name>
      <param-value>longestory</param-value>
  </context-param>
  <servlet>
    <servlet-name>AServlet</servlet-name>
    <servlet-class>app.java.context.AServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>AServlet</servlet-name>
    <url-pattern>/servlet/AServlet</url-pattern>
  </servlet-mapping>
  <servlet>
    <servlet-name>BServlet</servlet-name>
    <servlet-class>app.java.context.BServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>BServlet</servlet-name>
    <url-pattern>/servlet/BServlet</url-pattern>
  </servlet-mapping>
</web-app>
  • 在两个自定义Servlet中,分别利用ServletContext对象获取全局初始化参数。
public class BServlet extends GenericServlet {
    @Override
    public void service(ServletRequest req, ServletResponse res)
            throws ServletException, IOException {
        ServletContext context = getServletContext();
        
        String weixin = context.getInitParameter("weixin");
        System.out.println(weixin);
    }
}
  • Web应用程序发布到Tomcat服务器,并启动Tomcat服务器。
  • 打开浏览器,在地址栏中分别输入http://localhost:8080/08_servlet/servlet/AServlethttp://localhost:8080/08_servlet/servlet/BServlet,在控制台打印相关信息。

在自定义Servlet中,可以通过ServletContext对象的getInitParameter(String name)方法获取对应参数名称的全局初始化参数值,也可以通过ServletContext对象的getInitParameterNames()方法获取所有全局初始化参数的名称。

还可以通过ServletContext对象的getMineType(String file)方法根据文件扩展名获取文件MIME类型。

public class BServlet extends GenericServlet {
    @Override
    public void service(ServletRequest req, ServletResponse res)
            throws ServletException, IOException {
        ServletContext context = getServletContext();
        
        String html = context.getMimeType("1.html");
        String css = context.getMimeType("2.css");
        String javascript = context.getMimeType("3.js");
        System.out.println("HTML的文件类型为"+html+", CSS的文件类型为"+css+", javascript的文件类型为"+javascript);
    }
}

发布Web应用程序,并启动Tomcat服务器,在控制台中打印:

HTML的扩展名为text/html, CSS的扩展名为text/css, javascript的扩展名为application/javascript

ServletContext对象的getMineType(String file)方法会自动读取Tomcat安装目录中conf目录中的web.xml文件。

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
    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_2_5.xsd">
    <mime-mapping>
        <extension>html</extension>
        <mime-type>text/html</mime-type>
</mime-mapping>
<mime-mapping>
    <extension>css</extension>
    <mime-type>text/css</mime-type>
</mime-mapping>
    <mime-mapping>
        <extension>js</extension>
        <mime-type>application/javascript</mime-type>
</mime-mapping>
</web-app>

6.4. 多个Servlet共享数据

在同一个Web应用程序中,多个Servlet之间可以共享ServletContext对象中的数据信息。主要是通过ServletContext对象的setAttribute(String name, Object object)方法和getAttribute(String name)方法完成,下面我们来实现统计网站访问次数的案例。

  • 创建一个VisitServlet用来获取访问次数,并存储在ServletContext对象中。
public class VisitServlet extends HttpServlet {
    @Override
    public void init() throws ServletException {
        ServletContext context = getServletContext();
        context.setAttribute("times", 0);
    }
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ServletContext context = getServletContext();
        
        int times = (Integer)context.getAttribute("times");
        times ++;
        context.setAttribute("times", times);
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}
  • 创建一个ShowTimeServlet用来显示访问次数。
public class ShowTimeServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ServletContext context = getServletContext();
        
        int times = (Integer)context.getAttribute("times");
        
        response.setContentType("text/html;charset=utf-8");
        PrintWriter out = response.getWriter();
        out.println("<h1>VisitServlet共被访问了"+times+"次</h1>");
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}
  • 配置web.xml文件中有关Servlet信息。
<web-app version="2.5" 
    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_2_5.xsd">
  <servlet>
    <servlet-name>VisitServlet</servlet-name>
    <servlet-class>app.java.context.VisitServlet</servlet-class>
  </servlet>
  <servlet>
    <servlet-name>ShowTimeServlet</servlet-name>
    <servlet-class>app.java.context.ShowTimeServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>VisitServlet</servlet-name>
    <url-pattern>/visit</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
    <servlet-name>ShowTimeServlet</servlet-name>
    <url-pattern>/show</url-pattern>
  </servlet-mapping>
</web-app>
  • 发布Web应用程序到Tomcat服务器,并启动Tomcat服务器。
  • 打开浏览器,在地址栏输入http://localhost:8080/08_servlet/visit,访问VisitServlet
  • 再新打开浏览器,在地址栏输入http://localhost:8080/08_servlet/show,显示访问次数。

 

6.5. 读取Web工程中资源文件

读取工程中的资源文件,Java中的IO流其实就可以完成,下面使用Java中的IO流完成读取资源文件。

  • 首先在Web工程中,创建四个资源文件。
    • Web工程的根目录下创建1.txt
    • Web工程的WebRoot目录下创建2.txt
    • Web工程的WebRoot目录的WEB-INF目录下创建3.txt
    • Web工程的src目录下创建4.txt
  • 创建一个Java文件用于读取上述的四个资源文件。
public class ReaderFileTest {
    // 编写readfile()方法完成资源文件的读取工作.
    public static void readfile(String fileName) throws Exception{
        BufferedReader reader = new BufferedReader(new FileReader(fileName));
        String line;
        while ((line = reader.readLine()) != null) {
            System.out.println(line);
        }
        reader.close();
    }
    
    public static void main(String[] args) throws Exception {
        // 读取1.txt
        String filename1 = "1.txt";
        readfile(filename1);
        // 读取2.txt
        String filename2 = "WebRoot/2.txt";
        readfile(filename2);
        // 读取3.txt
        String filename3 = "WebRoot/WEB-INF/3.txt";
        readfile(filename3);
        // 读取4.txt
        String filename4 = "src/4.txt";
        readfile(filename4);
    }
}
  • 运行该Java文件会在控制台打印响应信息。

 

如果要想利用Servlet API的内容来读取Web工程中的资源文件,又要如何来做呢?ServletContext对象的getRealPath()方法可以来完成此项工作。

  • 创建一个自定义Servlet,使用ServletContext对象的getRealPath()方法来完成读取资源文件。
public class ReadFileServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=utf-8");
        OutputStream out = response.getOutputStream();
        /*
         *  读取1.txt
         *   * 因为1.txt资源文件在Web工程的根目录.
         *   * Web工程的WebRoot目录发布到Tomcat服务器.
         *   * 所以,1.txt资源文件是不会发布到Tomcat服务器的,Servlet无法读取.
         */
        // 读取2.txt
        String filename2 = getServletContext().getRealPath("/2.txt");
        InputStream in2 = new FileInputStream(new File(filename2));
        IOUtils.copy(in2, out);
        // 读取3.txt
        String filename3 = getServletContext().getRealPath("/WEB-INF/3.txt");
        InputStream in3 = new FileInputStream(new File(filename3));
        IOUtils.copy(in3, out);
        // 读取4.txt
        String filename4 = getServletContext().getRealPath("/WEB-INF/classes/4.txt");
        InputStream in4 = new FileInputStream(new File(filename4));
        IOUtils.copy(in4, out);
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}
  • 发布Web应用程序到Tomcat服务器,并启动Tomcat服务器。
  • 打开浏览器,在地址栏中分别输入http://localhost:8080/08_servlet/read,在控制台打印相关信息。

 

除了可以使用ServletContext对象的getRealPath()方法之外,还可以使用ServletContext对象的getResourceAsStream()方法来完成读取资源文件的工作。

public class ReadFileServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse 
response) throws ServletException, IOException {
        InputStream in = getServletContext().getResourceAsStream("/WEB-INF/classes/4.txt");
        IOUtils.copy(in, out);
    }
    
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}

还有一种通用的方法:利用Class类的getResource()方法也可以完成读取资源文件的工作。

public class ReadFileServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 利用类加载器读取Web工程的资源文件
        String filename = ReadFileServlet.class.getResource("/4.txt").getFile();
        InputStream in = new FileInputStream(new File(filename));
        IOUtils.copy(in, out);
    }
    
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!