美文网首页spring
Java Servlet源码走读一:Servlet接口

Java Servlet源码走读一:Servlet接口

作者: 测试你个头 | 来源:发表于2017-03-20 18:12 被阅读0次

    servlet:Server Applet 服务端应用程序,是java web开发的基础

    首先需要理解web容器的概念:

    • web容器:web容器是一种服务程序,在服务器一个端口就有一个提供相应服务的程序,而这个程序就是处理从客户端发出的请求,比如tomcat、webSphere、weblogic
    Servlet源码结构

    Servlet的源码主要包括2个package:
    1.在javax.servlet包中定义了所有的Servlet类都必须实现或扩展的的通用接口和类。
    2.在javax.servlet.http包中定义了采用HTTP通信协议的HttpServlet类。

    源码目录结果
    Servlet接口

    javax.servlet.Servlet是核心接口,见Servlet接口注释的第一句:定义了所有servlet都要实现的方法

    Servlet接口注释

    Servlet接口中定义的方法:


    • init:只会调用一次,在创建servlet时由web容器调用
    • getServletConfig:返回ServletConfig对象,包括servlet的初始化和启动参数信息
    • service:web容器调用service()来处理来自客户端的请求,service()会根据请求类型,在适当的时候调用doGetdoPost等方法
    • getServletInfo:见注释说明:返回作者、版本、版权等servlet信息
    • destroy:
      只会调用一次,在 Servlet 生命周期结束时被调用。destroy()可以让 Servlet 关闭数据库连接、停止后台线程、把 Cookie 列表或点击计数器写入到磁盘,并执行其他类似的清理活动。在调用destroy()方法之后,servlet 对象被标记为垃圾回收
    Servlet的生命周期

    Servlet 通过调用 init ()方法进行初始化。
    Servlet 调用 service()方法来处理客户端的请求。
    Servlet 通过调用 destroy()方法终止(结束)。
    最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。

    ServletContext/ServletRequest/ServletResponse

    3个接口,接口的主要注释分别如下

    /**
     * Defines a set of methods that a servlet uses to communicate with its
     * servlet container, for example, to get the MIME type of a file, dispatch
     * requests, or write to a log file.
     */
    public interface ServletContext {
    }
    
    /**
     * Defines an object to provide client request information to a servlet. The
     * servlet container creates a <code>ServletRequest</code> object and passes it
     * as an argument to the servlet's <code>service</code> method.
     */
    public interface ServletRequest {
    }
    
    /**
     * Defines an object to assist a servlet in sending a response to the client.
     * The servlet container creates a <code>ServletResponse</code> object and
     * passes it as an argument to the servlet's <code>service</code> method.
     */
    public interface ServletResponse {
    }
    

    从注释看:
    1、ServletContext定义了用来和servlet容器通信的方法(获取servlet容器的一些信息)
    2、ServletRequest由servlet容器创建,包含了客户端请求的信息
    3、ServletResponse由servlet容器创建,帮助servlet向客户端发送应答信息

    HttpServlet

    先看类图:


    • GenericServlet:

    GenericServlet中并没有实现destroy()service()2个接口,而是留给了具体应用的servlet实现类去扩展

    GenericServlet中对于init的实现:

    public void init(ServletConfig config) throws ServletException {
      this.config = config;
      this.init();
    }
    
    public void init() throws ServletException {
    
    }
    

    具体的servlet实现类只需要重写init()方法即可,而不需要重写init(ServletConfig)

    以spring mvc为例,HttpServletBean重写了GenericServletinit()方法来实现自己独特的初始化逻辑

    • HttpServlet

    HttpServlet实现了service()接口

        /**
         * Receives standard HTTP requests from the public
         * <code>service</code> method and dispatches
         * them to the <code>do</code><i>XXX</i> methods defined in 
         * this class. This method is an HTTP-specific version of the 
         * {@link javax.servlet.Servlet#service} method. There's no
         * need to override this method.
         *
         * @param req   the {@link HttpServletRequest} object that
         *                  contains the request the client made of
         *                  the servlet
         *
         * @param resp  the {@link HttpServletResponse} object that
         *                  contains the response the servlet returns
         *                  to the client                                
         *
         * @exception IOException   if an input or output error occurs
         *                              while the servlet is handling the
         *                              HTTP request
         *
         * @exception ServletException  if the HTTP request
         *                                  cannot be handled
         * 
         * @see javax.servlet.Servlet#service
         */
        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) {
                        // 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);
            }
        }
    

    从源码可以看到,HttpServletservice()方法中主要做的事情是根据request的method类型调用具体的doXXX()方法进行处理,起到的是一个根据method类型分发的作用

    对于更下面一层的实现类,需要重写HttpServletdoXXX()方法来实现自己的特殊处理逻辑:
    举例:spring mvc中FrameworkServlet重写了HttpServletdoXXX()方法来实现对Http请求的拦截

    总结下:
    1、Servlet的重点在于定义了init(ServletConfig),destroy(),service()这3个接口
    2、GenericServlet重点在于对init(ServletConfig)接口的重写和提供了init()接口以供扩展
    3、HttpServlet重点在于service()方法中的分发逻辑,以及定义了doGet/doPost等一系列接口以供扩展

    具体的HttpServlet实现类通过重写上面的一系列接口实现自己的独特逻辑

    遗留问题:
    web容器是在什么时候,如何去调用servlet定义的生命周期方法init(),service(),destroy()?

    相关文章

      网友评论

        本文标题:Java Servlet源码走读一:Servlet接口

        本文链接:https://www.haomeiwen.com/subject/ocazwttx.html