Servlet源码学习

两个月前学习的Servlet的源码,一直放在草稿箱里。之所以看Servlet源码,是因为Spring源码太难了,又臭又长。接下来先来介绍源码都有哪些类和接口,servlet的源码主要是在javax.servlet和javax.servlet.http两个包下面,总共有34个左右的类和接口,下图是核心的类与接口之间的关系,其中有的类和接口中的方法太多,由于写出来太过于冗余,我只写了部分,下图只作记忆

在按流程读源码之前,我们先看Servlet中比较重要的类和接口的定义及概念,方便之后回头再看:

下面是就是Servlet的工作完整流程:

浏览器请求:

1、首先由客户端浏览器请求访问路径URL,向服务器发送一个HTTP请求,这个请求首先会到达Tomcat内置Web服务器

2、Tomcat内置Web服务器接收到请求后,会将请求发送到Servlet容器中

3、Servlet容器接受到请求后会加载Servlet,在web.xml中根据映射地址找到相应的servlet名,然后根据servlet名找到我们全限定类名,既我们自己写的类。

服务器创建对象:

服务器找到权限类名后,通过反射创建对象,同时创建了ServletConfig,里面也存放了一些初始化信息(注意服务器只会创建一次servlet对象,所以servlet也只会有一个)

调用init方法:

1、对象创建好之后,首先要执行init方法,但是我们发现我们自定义类下面没有init方法,所以程序会到其父类HttpServlet里面找(创建一个Servlet实例,我们一般都是继承HttpServlet,当然也可以继承GenericServlet)

2、我们发现HttpServlet里也没有init方法,所以继续向上找,既向其父类GenericServlet中继续寻找,在GenericServlet中,我们发现了init方法,则执行init方法(对接口Servlet中的init方法进行了重写)

注:在GenericServlet中执行public void init(ServletConfig config)方法的时候,又调用了自己无参方法体的init()方法,其目的是为了方便开发者,如果开发者在初始化的过程中需要实现一些功能,可以重写此方法

调用service方法:

1、接着服务器会自动默认的执行service方法,在你写的类中找service方法,但是找不到,那么会到其父类中寻找

2、到父类HttpServlet中发现有此方法,则直接调用此方法,发现方法依赖于两个参数,所以先去创建两个对象:ServletRequest请求对象和ServletResponse响应的对象,这两个对象用来封装浏览器的请求数据和封装向浏览器的响应数据,接着将这两个参数传入,并将这两个参数进行强制类型转换,随后调用HttpServlet下面另外的一个publci void service(HttpServletRequest req,HttpServletResponse resp),在这个方法体内进行了请求的判断,根据ServletRequest中获得客户的请求信息,然后调用相应的doXXX方法进行响应,再通过ServletResponse对象生成响应结果,发送给客户端,最后销毁创建的ServletRequest和ServletResponse

调用destory()

在源码中,GenericServlet这个类里面实现了父接口Servlet的方法destory(),但是没有方法体,我就很纳闷,没有方法体怎么销毁servlet对象呢?

原来这个方法只是起到一个通知的作用,手动调用这个方法不会销毁servlet实例对象,只有在web应用关闭终止时,servlet才会被销毁。服务器关闭时,调用这些servlet对象的destory()方法,通知这些对象,我要销毁你们了,当然还会销毁servlet创建的servletConfig对象。

1、Servlet接口

servlet接口是servlet源码中最高级的接口,接口中定义了servlet的初始化,处理请求和销毁

public interface Servlet {
    /**
     * 负责初始化Servlet对象
     * 在Servlet的生命周期中,该方法执行一次
     * 该方法执行在单线程的环境下,因此开发者不用考虑线程安全呢的问题
     */
    void init(ServletConfig var1) throws ServletException;
    /**
     * 返回Servlet配置和初始化参数
    */
    ServletConfig getServletConfig();
     /**
      * 负责响应客户的请求
      * 为了提高效率,Servlet规范要求一个Servlet实例必须能够同时服务于多个客户端请求
      * 即 service() 方法运行在多线程的环境下,Servlet 开发者必须保证该方法的线程安全性
      */
    void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
    /**
     * 返回有关servlet的信息,例如作者、版本、版权
     */
    String getServletInfo();
    /**
     * 当Servlet对象退出生命周期时,负责释放占用的资源
     */
    void destroy();
}

其中的init(ServletConfig congif)方法和destory()有点像构造函数和析构函数,从service()方法中的 参数可以看出,其方法依赖于ServletRequest和和ServletResponse两个相对高级的接口。

2、ServletConfig接口:

在应用初始化的时候,Web容器在创建Servlet对象时会自动将web.xml中的servlet配置这些初始化参数封装到ServletConfig对象中,并在调用servlet的init方法时,将ServletConfig对象传递给Servlet,所以我们可以通过ServletConfig对象就可以的到当前servlet的初始化参数信息,所以说,ServletConfig是容器向servlet传递参数的载体

public interface ServletConfig {

    /**
    * 返回此servlet实例的名称
    * 在web.xml对应于servlet-name 结点(<servlet-name></servlet-name>)
    * 使用注解@WebServlet(value="/hello",name="HelloServlet")中的name属性
    */
    String getServletName();
    //返回这个Servlet的ServletContext对象
    ServletContext getServletContext();
    /**获取具有给定名称的初始化参数的值
    * 在web.xml对应于<init-param><param-name></param-name></init-param>
    */
    String getInitParameter(String var1);
    //返回指定的参数的参数名
    Enumeration<String> getInitParameterNames();
}

从方法中可以看出ServletConfig又依赖于ServletContext这个接口

4、GenericServlet抽象类:

GenericServlet实现了Servlet和ServletConfig里的所有方法,并且额外提供了一个初始化的方法inti()//实现为空和两个打印日志的方法log(String msg) 与 log(String message,Throwable t),当然Generic也实现了Servlet中的个别方法

GenericServlet中的一个service()方法需要我们实现

 public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * 也需要一个ServletConfig,和HttpServlet相似
     */
    private transient ServletConfig config;

    public GenericServlet() {
    }

    public void destroy() {
    }

    public String getInitParameter(String name) {
        return this.getServletConfig().getInitParameter(name);
    }

    public Enumeration<String> getInitParameterNames() {
        return this.getServletConfig().getInitParameterNames();
    }

    public ServletConfig getServletConfig() {
        return this.config;
    }

    public ServletContext getServletContext() {
        return this.getServletConfig().getServletContext();
    }

    public String getServletInfo() {
        return "";
    }
    /**
     * 实例化Servlet中的init()方法
     */
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init();
    }
    /**
     * 提供了一个空构造
     */
    public void init() throws ServletException {
    }

    public void log(String msg) {
        this.getServletContext().log(this.getServletName() + ": " + msg);
    }

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

    public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

    public String getServletName() {
        return this.config.getServletName();
    }
}

GenericServlet中的大部分操作都依赖于ServletConfig,如其下:

private transient ServletConfig config;

5、ServletContext接口:

Tomcat在启动的时候,需要识别webapps下的各个WEB应用,识别各个WEB应用的同时为每个WEB应用创建对应的ServletContext对象,一个WEB应用对应于一个ServletContext对象,一个ServletContext对象表示了一个Web应用程序的上下文,也就是Servlet的上下文,每个ServletContext中都有一个很大的MAP,并且Tomcat在启动之初就向这个MAP中放入了大量的键值对数据,servletContext也称作是一个域对象,服务器启动的时候创建,服务器关闭的时候销毁

public interface ServletContext {
    /**
     * 代码略
    */
    //获取ServletContext对象
    ServletContext getContext(String var1);
    
    String getContextPath();
  
    Set<String> getResourcePaths(String var1);

    URL getResource(String var1) throws MalformedURLException;
    //获取服务端指定目录下指定文件的输入流对象
    InputStream getResourceAsStream(String var1);

    RequestDispatcher getRequestDispatcher(String var1);

    RequestDispatcher getNamedDispatcher(String var1);

    /** @deprecated */
    Servlet getServlet(String var1) throws ServletException;

    /** @deprecated */
    Enumeration<Servlet> getServlets();

    /** @deprecated */
    Enumeration<String> getServletNames();

    void log(String var1);

    /** @deprecated */
    void log(Exception var1, String var2);

    void log(String var1, Throwable var2);
    //获取服务端指定目录下指定资源/目录的真实路径

    String getRealPath(String var1);
    /**
     * servletContext读取全局参数核心方法如下(获取的是web.xml里设置好的参数):
     */
    String getServerInfo();
    //根据指定的参数名获取参数值
    String getInitParameter(String var1);
    //获取所有参数名称列表
    Enumeration<String> getInitParameterNames();
    boolean setInitParameter(String var1, String var2);

    //根据指定的key读取ServletContext域对象里面的数据
    Object getAttribute(String var1);
    //向ServletContext域对象中添加数据,添加时以Key-value形式添加
    void setAttribute(String var1, Object var2);
    //根据指定的key从ServletContext域对象中删除数据
    void removeAttribute(String var1);

    String getServletContextName();

    Dynamic addServlet(String var1, String var2);

    Dynamic addServlet(String var1, Servlet var2);

    Dynamic addServlet(String var1, Class<? extends Servlet> var2);

    <T extends Servlet> T createServlet(Class<T> var1) throws ServletException;

    javax.servlet.FilterRegistration.Dynamic addFilter(String var1, String var2);

    javax.servlet.FilterRegistration.Dynamic addFilter(String var1, Filter var2);

    javax.servlet.FilterRegistration.Dynamic addFilter(String var1, Class<? extends Filter> var2);

    <T extends Filter> T createFilter(Class<T> var1) throws ServletException;

    void addListener(String var1);

    <T extends EventListener> void addListener(T var1);

    void addListener(Class<? extends EventListener> var1);

    ClassLoader getClassLoader();

  
}

 

5、HttpServlet抽象类:

public abstract class HttpServlet extends GenericServlet {
   
   //定义了一些字符串常量
    public HttpServlet() {
    }

    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(405, msg);
        } else {
            resp.sendError(400, msg);
        }

    }

    protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String protocol = req.getProtocol();
        String msg = lStrings.getString("http.method_put_not_supported");
        if (protocol.endsWith("1.1")) {
            resp.sendError(405, msg);
        } else {
            resp.sendError(400, msg);
        }

    }

    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getMethod();
        long lastModified;
        if (method.equals("GET")) {
            lastModified = this.getLastModified(req);
            if (lastModified == -1L) {
                this.doGet(req, resp);
            } else {
                long ifModifiedSince;
                try {
                    ifModifiedSince = req.getDateHeader("If-Modified-Since");
                } catch (IllegalArgumentException var9) {
                    ifModifiedSince = -1L;
                }

                if (ifModifiedSince < lastModified / 1000L * 1000L) {
                    this.maybeSetLastModified(resp, lastModified);
                    this.doGet(req, resp);
                } else {
                    resp.setStatus(304);
                }
            }
        } else if (method.equals("HEAD")) {
            lastModified = this.getLastModified(req);
            this.maybeSetLastModified(resp, lastModified);
            this.doHead(req, resp);
        } else if (method.equals("POST")) {
            this.doPost(req, resp);
        } else if (method.equals("PUT")) {
            this.doPut(req, resp);
        } else if (method.equals("DELETE")) {
            this.doDelete(req, resp);
        } else if (method.equals("OPTIONS")) {
            this.doOptions(req, resp);
        } else if (method.equals("TRACE")) {
            this.doTrace(req, resp);
        } else {
            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[]{method};
            errMsg = MessageFormat.format(errMsg, errArgs);
            resp.sendError(501, errMsg);
        }

    }

    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        HttpServletRequest request;
        HttpServletResponse response;
        try {
            request = (HttpServletRequest)req;
            response = (HttpServletResponse)res;
        } catch (ClassCastException var6) {
            throw new ServletException("non-HTTP request or response");
        }

        this.service(request, response);
    }
}

6、ServletRequest接口:

public interface ServletRequest {
    Object getAttribute(String var1);

    Enumeration<String> getAttributeNames();

    String getCharacterEncoding();

    void setCharacterEncoding(String var1) throws UnsupportedEncodingException;

    int getContentLength();

    String getContentType();

    ServletInputStream getInputStream() throws IOException;

    String getParameter(String var1);

    Enumeration<String> getParameterNames();

    String[] getParameterValues(String var1);

    Map<String, String[]> getParameterMap();

    String getProtocol();

    String getScheme();

    String getServerName();

    int getServerPort();

    BufferedReader getReader() throws IOException;

    String getRemoteAddr();

    String getRemoteHost();

    void setAttribute(String var1, Object var2);

    void removeAttribute(String var1);

    Locale getLocale();

    Enumeration<Locale> getLocales();

    boolean isSecure();

    RequestDispatcher getRequestDispatcher(String var1);

    /** @deprecated */
    String getRealPath(String var1);

    int getRemotePort();

    String getLocalName();

    String getLocalAddr();

    int getLocalPort();

    ServletContext getServletContext();

    AsyncContext startAsync();

    AsyncContext startAsync(ServletRequest var1, ServletResponse var2);

    boolean isAsyncStarted();

    boolean isAsyncSupported();

    AsyncContext getAsyncContext();

    DispatcherType getDispatcherType();
}

7、ServletResponse接口:

public interface ServletResponse {

    //存放cookie
    void Cookie();
    String getCharacterEncoding();

    String getContentType();

    ServletOutputStream getOutputStream() throws IOException;

    PrintWriter getWriter() throws IOException;

    void setCharacterEncoding(String var1);

    void setContentLength(int var1);
    
    void setContentType(String var1);

    void setBufferSize(int var1);

    int getBufferSize();

    void flushBuffer() throws IOException;

    void resetBuffer();

    boolean isCommitted();

    void reset();

    void setLocale(Locale var1);

    Locale getLocale();
}

8、HttpServletRequest

public interface HttpServletRequest extends ServletRequest {
    String BASIC_AUTH = "BASIC";
    String FORM_AUTH = "FORM";
    String CLIENT_CERT_AUTH = "CLIENT_CERT";
    String DIGEST_AUTH = "DIGEST";

    String getAuthType();

    Cookie[] getCookies();

    long getDateHeader(String var1);

    String getHeader(String var1);

    Enumeration<String> getHeaders(String var1);

    Enumeration<String> getHeaderNames();

    int getIntHeader(String var1);

    String getMethod();

    String getPathInfo();

    String getPathTranslated();

    String getContextPath();

    String getQueryString();

    String getRemoteUser();

    boolean isUserInRole(String var1);

    Principal getUserPrincipal();

    String getRequestedSessionId();

    String getRequestURI();

    StringBuffer getRequestURL();

    String getServletPath();

    HttpSession getSession(boolean var1);

    HttpSession getSession();

    boolean isRequestedSessionIdValid();

    boolean isRequestedSessionIdFromCookie();

    boolean isRequestedSessionIdFromURL();

    /** @deprecated */
    boolean isRequestedSessionIdFromUrl();

    boolean authenticate(HttpServletResponse var1) throws IOException, ServletException;

    void login(String var1, String var2) throws ServletException;

    void logout() throws ServletException;

    Collection<Part> getParts() throws IOException, IllegalStateException, ServletException;

    Part getPart(String var1) throws IOException, IllegalStateException, ServletException;
}

9、HttpServletResponse

public interface HttpServletResponse extends ServletResponse {
    int SC_CONTINUE = 100;
    int SC_SWITCHING_PROTOCOLS = 101;
    int SC_OK = 200;
    int SC_CREATED = 201;
    int SC_ACCEPTED = 202;
    int SC_NON_AUTHORITATIVE_INFORMATION = 203;
    int SC_NO_CONTENT = 204;
    int SC_RESET_CONTENT = 205;
    int SC_PARTIAL_CONTENT = 206;
    int SC_MULTIPLE_CHOICES = 300;
    int SC_MOVED_PERMANENTLY = 301;
    int SC_MOVED_TEMPORARILY = 302;
    int SC_FOUND = 302;
    int SC_SEE_OTHER = 303;
    int SC_NOT_MODIFIED = 304;
    int SC_USE_PROXY = 305;
    int SC_TEMPORARY_REDIRECT = 307;
    int SC_BAD_REQUEST = 400;
    int SC_UNAUTHORIZED = 401;
    int SC_PAYMENT_REQUIRED = 402;
    int SC_FORBIDDEN = 403;
    int SC_NOT_FOUND = 404;
    int SC_METHOD_NOT_ALLOWED = 405;
    int SC_NOT_ACCEPTABLE = 406;
    int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
    int SC_REQUEST_TIMEOUT = 408;
    int SC_CONFLICT = 409;
    int SC_GONE = 410;
    int SC_LENGTH_REQUIRED = 411;
    int SC_PRECONDITION_FAILED = 412;
    int SC_REQUEST_ENTITY_TOO_LARGE = 413;
    int SC_REQUEST_URI_TOO_LONG = 414;
    int SC_UNSUPPORTED_MEDIA_TYPE = 415;
    int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
    int SC_EXPECTATION_FAILED = 417;
    int SC_INTERNAL_SERVER_ERROR = 500;
    int SC_NOT_IMPLEMENTED = 501;
    int SC_BAD_GATEWAY = 502;
    int SC_SERVICE_UNAVAILABLE = 503;
    int SC_GATEWAY_TIMEOUT = 504;
    int SC_HTTP_VERSION_NOT_SUPPORTED = 505;

    void addCookie(Cookie var1);

    boolean containsHeader(String var1);

    String encodeURL(String var1);

    String encodeRedirectURL(String var1);

    /** @deprecated */
    String encodeUrl(String var1);

    /** @deprecated */
    String encodeRedirectUrl(String var1);

    void sendError(int var1, String var2) throws IOException;

    void sendError(int var1) throws IOException;

    void sendRedirect(String var1) throws IOException;

    void setDateHeader(String var1, long var2);

    void addDateHeader(String var1, long var2);

    void setHeader(String var1, String var2);

    void addHeader(String var1, String var2);

    void setIntHeader(String var1, int var2);

    void addIntHeader(String var1, int var2);

    void setStatus(int var1);

    /** @deprecated */
    void setStatus(int var1, String var2);

    int getStatus();

    String getHeader(String var1);

    Collection<String> getHeaders(String var1);

    Collection<String> getHeaderNames();
}

我在上面贴了部分源码试图找找getAttribute、addCookie、转发、状态码等方法在哪里,就加深了对这些请求响应的理解

简单来说就是一下几句:

1、WebClient向Servlet容器(Tomcat)发出Http请求;

2、Servlet容器接受Web Client的请求;

3、Servlet容器创建一个HttpRequest对象,将Web Client请求的信息封装到这个对象中;

4、Servlet容器创建一个HttpResponse对象;

5、Servlet容器调用HttpServlet对象的service()方法,把HttpRequest对象与HttpResponse对象作为参数传给HttpServlet对象;

6、HttpServlet调用HttpRequest对象的有关方法,获取Http请求信息

7、HttpServlet调用HttpResponse对象的有关方法,生成响应数据;

8、Servlet容器把HttpServlet的响应结果传给Web Client

 

 

 

 

 

 

 

 

 

 

 

  • 6
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值