当前位置:首页 > 开发 > 编程语言 > Servlet > 正文

读源码学Servlet(1)GenericServlet 源码分析

发表于: 2009-10-23   作者:jzinfo   来源:转载   浏览:
摘要: Servlet API的核心就是javax.servlet.Servlet接口,所有的Servlet 类(抽象的或者自己写的)都必须实现这个接口。在Servlet接口中定义了5个方法,其中有3个方法是由Servlet 容器在Servlet的生命周期的不同阶段来调用的特定方法。     先看javax.servlet.servlet接口源码:  package

Servlet API的核心就是javax.servlet.Servlet接口,所有的Servlet 类(抽象的或者自己写的)都必须实现这个接口。在Servlet接口中定义了5个方法,其中有3个方法是由Servlet 容器在Servlet的生命周期的不同阶段来调用的特定方法。

 

 

先看javax.servlet.servlet接口源码:
 

package javax.servlet;   //Tomcat源码版本:6.0.20
import java.io.IOException;

public interface Servlet {   


//负责初始化Servlet对象。容器一旦创建好Servlet对象后,就调用此方法来初始化Servlet对象  
   public void init(ServletConfig config) throws ServletException;    

 

 //方法负责响应客户的请求,提供服务。当容器接收到客户端要求访问特定的servlet请求时,就会调用Servlet的service方法  
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;    
  
    
  /*
    Destroy()方法负责释放Servlet 对象占用的资源,当servlet对象结束生命周期时,servlet容器调用此方法来销毁servlet对象.
  **/
    public void destroy();
    
    
    /*
    说明:Init(),service(),destroy() 这三个方法是Servlet生命周期中的最重要的三个方法。
    **/   
    
    
    /*
    返回一个字符串,在该字符串中包含servlet的创建者,版本和版权等信息
    **/
    public String getServletInfo();  
    
    /*
   GetServletConfig: 返回一个ServletConfig对象,该对象中包含了Servlet初始化参数信息 
   **/    
    public ServletConfig getServletConfig();  
}

 

  GenericServlet抽象类实现了Servlet接口,它只是通用的实现,与任何网络应用层协议无关。
同时,HttpServlet这个抽象类继承了GenericServlet抽象类,在Http协议上提供了通用的实现。

 

 

查看GenericSerlvet抽象类源码:

package javax.servlet;

import java.io.IOException;
import java.util.Enumeration;

//抽象类GenericServlet实现了Servlet接口的同时,也实现了ServletConfig接口和Serializable这两个接口 
public abstract class GenericServlet 
    implements Servlet, ServletConfig, java.io.Serializable
{
    //私有变量,保存 init()传入的ServletConfig对象的引用
    private transient ServletConfig config;
    
    //无参的构造方法
    public GenericServlet() { }
    

    /*
    ------------------------------------
    以下方法实现了servlet接口中的5个方法
    实现Servlet接口方法开始
    ------------------------------------
    */    
    
    
    /*
    实现接口Servlet中的带参数的init(ServletConfig Config)方法,将传递的ServletConfig对象的引用保存到私有成员变量中,
    使得GenericServlet对象和一个ServletConfig对象关联.
    同时它也调用了自身的不带参数的init()方法
    **/
    
    public void init(ServletConfig config) throws ServletException {
      this.config = config;
      this.init();   //调用了无参的 init()方法
    }

    //无参的init()方法
    public void init() throws ServletException {

    }
    
    
    //空实现了destroy方法
    public void destroy() { }    
        
     
    //实现了接口中的getServletConfig方法,返回ServletConfig对象
    public ServletConfig getServletConfig() 
    {
       return config;
    }    

    //该方法实现接口<Servlet>中的ServletInfo,默认返回空字符串
    public String getServletInfo() {
       return "";
    }
    
    
    //唯一没有实现的抽象方法service(),仅仅在此声明。交由子类去实现具体的应用 
   //在后来的HttpServlet抽象类中,针对当前基于Http协议的Web开发,HttpServlet抽象类具体实现了这个方法
   //若有其他的协议,直接继承本类后实现相关协议即可,具有很强的扩展性
      
    public abstract void service(ServletRequest req, ServletResponse res)
 throws ServletException, IOException;
    
    /*
    ------------------------------------
    实现Servlet接口方法结束
    ------------------------------------
    */
    
    
    
    
    
    

  /*
  ---------------------------------------------
   *以下四个方法实现了接口ServletConfig中的方法
   实现ServletConfig接口开始
  ---------------------------------------------
   */
 
  //该方法实现了接口<ServletConfig>中的getServletContext方法,用于返回servleConfig对象中所包含的servletContext方法
    public ServletContext getServletContext() {
       return getServletConfig().getServletContext();
    }
    
    //获取初始化参数
    public String getInitParameter(String name) {
     return getServletConfig().getInitParameter(name);
    }
    
    //实现了接口<ServletConfig>中的方法,用于返回在web.xml文件中为servlet所配置的全部的初始化参数的值
    public Enumeration getInitParameterNames() {
       return getServletConfig().getInitParameterNames();
   
   //获取在web.xml文件中注册的当前的这个servlet名称。没有在web.xml 中注册的servlet,该方法直接放回该servlet的类名。
   //法实现了接口<ServleConfig>中的getServletName方法  
    public String getServletName() {
        return config.getServletName();
    }
    
   /*
  ---------------------------------------------
   实现ServletConfig接口结束
  ---------------------------------------------
 */ 
        

    public void log(String msg) {
       getServletContext().log(getServletName() + ": "+ msg);
    }  
   
   
    public void log(String message, Throwable t) {
       getServletContext().log(getServletName() + ": " + message, t);
    }
}

 


HttpServlet是继承了GenericServlet抽象类的一个抽象类,但是他的里面并没有任何抽象方法,这就是说他并不会强迫我们去做什么。我们只是按需选择,重写HttpServlet中的的部分方法就可以了。


下面是抽象类HttpServlet的源码:

 

package javax.servlet.http;
.....  //节约篇幅,省略导入包

public abstract class HttpServlet extends GenericServlet
    implements java.io.Serializable 
{

    private static final String METHOD_GET = "GET";
    private static final String METHOD_POST = "POST";
    ......
   
    
    /**
     * Does nothing, because this is an abstract class.
     * 抽象类HttpServlet有一个构造函数,但是空的,什么都没有
     */
    public HttpServlet() { }
    

    
    /*分别执行doGet,doPost,doOpitions,doHead,doPut,doTrace方法
    在请求响应服务方法service()中,根据请求类型,分贝调用这些doXXXX方法
    所以自己写的Servlet只需要根据请求类型覆盖响应的doXXX方法即可。
    */
    
    //doXXXX方法开始
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        String protocol = req.getProtocol();
        String msg = lStrings.getString("http.method_get_not_supported");
        if (protocol.endsWith("1.1")) {
            resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
        } else {
            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
        }
    }
    

    protected void doHead(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        .......
    }  
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        String protocol = req.getProtocol();
        String msg = lStrings.getString("http.method_post_not_supported");
        if (protocol.endsWith("1.1")) {
            resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
        } else {
            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
        }
    }   
    protected void doPut(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException  {
        //todo
    }   
    
        
    protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
       //todo
    }
        
    protected void doTrace(HttpServletRequest req, HttpServletResponse resp) 
        throws ServletException, IOException   {       
      //todo
    }   
 
    protected void doDelete(HttpServletRequest req,
                            HttpServletResponse resp)
        throws ServletException, IOException   {
        //todo
    }   
    //doXXXX方法结束
    
    

    //重载的service(args0,args1)方法
    protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        String method = req.getMethod();

        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 / 1000 * 1000)) {
                    // 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);
                }
            }

        } 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 {
            //
            // Note that this means NO servlet supports whatever
            // method was requested, anywhere on this server.
            //

            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);
        }
    }
   
    
   //实现父类的service(ServletRequest req,ServletResponse res)方法
   //通过参数的向下转型,然后调用重载的service(HttpservletRequest,HttpServletResponse)方法

    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);  //调用重载的service()方法
    }
   
    ......//其他方法
}

 

值得一说的是,很多介绍servlet的书中,讲解servlet第一个实例时候,都习惯去覆盖HttpServlet的service(HttpServletRequest request,HttpServletResponse response)方法来演示servlet.

 

当然,直接覆盖sevice()方法,作为演示,可以达到目的。 因为servlet首先响应的就是调用service()方法,直接在此方法中覆盖没问题。但在实际的开发中,我们只是根据请求的类型,覆盖响应的doXXX方法即可。最常见的是doGet和doPost方法。

 

从HtttpServlet的源码中关于service() 方法的实现,可以看出,它最终也是根据请求类型来调用的各个doxxx 方法来完成响应的。如果自己写的servlet覆盖了service()方法,若没对相应的请求做处理,则系统无法调用相关的doxxx方法来完成提交的请求任务。

读源码学Servlet(1)GenericServlet 源码分析

  • 0

    开心

    开心

  • 0

    板砖

    板砖

  • 0

    感动

    感动

  • 0

    有用

    有用

  • 0

    疑问

    疑问

  • 0

    难过

    难过

  • 0

    无聊

    无聊

  • 0

    震惊

    震惊

编辑推荐
对于Java程序来说,JVM是一台完整的机器,可是对于真正的机器或OS来说,JVM只是其中一个进程而已。作
水平有限,如有不足请留言交流,互相提高,谢谢!转载请提供出处,http://treblesoftware.iteye.com/
对于Java程序来说,JVM是一台完整的机器,可是对于真正的机器或OS来说,JVM只是其中一个进程而已。作
CobarClient是阿里巴巴公司开发一个的开源的、基于iBatis和Spring的分布式数据库访问层。为了支持iB
threadLocal能够保证变量在每个线程中单独存在,各个线程互相不会影响。 通常我们这么写threadLocal
这是本人对于Dubbo源码分析的系列一,没有说明Dubbo是什么,不清楚请先了解,此处只是为了给自己做个
Lua源码分析(1) -- 简介收藏 新一篇: Lua源码分析(2) -- 对象表示 | 旧一篇: Ruby 在 VC Express 20
LIRC是Linux Infrared remote control的简写, 是红外信号解码和发送的开源套件。 首先我们看一下LI
1.U-Boot系统加载器 U-Boot是一个规模庞大的开源Bootloader软件,最初是由denx(www.denx.de)发起
本系列笔记将以DotLucene的命名空间组织行文,阅读的DotLucene版本是1.9.RC1,并且以更好的使用而非
版权所有 IT知识库 CopyRight © 2009-2015 IT知识库 IT610.com , All Rights Reserved. 京ICP备09083238号