Servlet源码解析:Servlet接口及其重要子类



Servlet是基于Java技术的,容器托管的,用于生成动态内容的web组件。Servlet接口是Java Servlet API的核心抽象。首先我们来看看它的源码:

public interface Servlet {
    /**
     * 被servlet容器在实例化servlet后调用该方法表示该servlet已经被装载,以服务的形式提供给客户
     * @param config
     * @throws ServletException
     */
    public void init(ServletConfig config) throws ServletException;
    /**
     * 返回一个ServletConfig对象,该对象包含了
     * Servlet初始化和启动参数
     * @return
     */
    public ServletConfig getServletConfig();
    /**
     * 用户request的响应函数
     * @param req
     * @param res
     * @throws ServletException
     * @throws IOException
     */
    public void service(ServletRequest req, ServletResponse res)
            throws ServletException, IOException;
    /**
     * 返回servlet的作者,版本和版权等信息
     * @return
     */
    public String getServletInfo();
    /**
     * servlet容器要销毁该servlet时调用该函数
     * @return
     */
    public void destroy();
}

Servlet接口定义了一个Servlet所要拥有的行为集合。接下来我们再看看它的子类GenericServlet:

public abstract class GenericServlet implements Servlet, ServletConfig,
        java.io.Serializable {

    private static final long serialVersionUID = 1L;

    private transient ServletConfig config;

    /**
     * 没有任何行为,所有初始化操作在init函数里
     */
    public GenericServlet() {
        // NOOP
    }
    @Override
    public void destroy() {
        // NOOP by default
    }
    /**
     * 获取Servlet的初始化参数
     */
    @Override
    public String getInitParameter(String name) {
        return getServletConfig().getInitParameter(name);
    }

    /**
     * 获取Servlet的初始化参数名字
     */
    @Override
    public Enumeration<String> getInitParameterNames() {
        return getServletConfig().getInitParameterNames();
    }
    @Override
    public ServletConfig getServletConfig() {
        return config;
    }

    /**
     * 返回该Servlet的运行上下文对象
     */
    @Override
    public ServletContext getServletContext() {
        return getServletConfig().getServletContext();
    }

    /**
     * 默认信息是空字符串
     */
    @Override
    public String getServletInfo() {
        return "";
    }
    @Override
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init();
    }

    public void init() throws ServletException {
        // NOOP by default
    }
    /**
     * 在servlet的log file中记录日志信息
     */
    public void log(String msg) {
        getServletContext().log(getServletName() + ": " + msg);
    }
    public void log(String message, Throwable t) {
        getServletContext().log(getServletName() + ": " + message, t);
    }

    @Override
    public abstract void service(ServletRequest req, ServletResponse res)
            throws ServletException, IOException;
    @Override
    public String getServletName() {
        return config.getServletName();
    }
}

GenerticServlet抽象类实际没做什么具体的工作,只是提供了一些便捷函数获取上下文、servlet参数以及进行日志记录。仍然没看见Request和Response的出现,这会在它的子类中提及吗?让我们来看看它的子类HttpServlet:

public abstract class HttpServlet extends GenericServlet {

    private static final long serialVersionUID = 1L;

    private static final String METHOD_DELETE = "DELETE";
    private static final String METHOD_HEAD = "HEAD";
    private static final String METHOD_GET = "GET";
    private static final String METHOD_OPTIONS = "OPTIONS";
    private static final String METHOD_POST = "POST";
    private static final String METHOD_PUT = "PUT";
    private static final String METHOD_TRACE = "TRACE";

    private static final String HEADER_IFMODSINCE = "If-Modified-Since";
    private static final String HEADER_LASTMOD = "Last-Modified";

    private static final String LSTRING_FILE =
        "javax.servlet.http.LocalStrings";
    private static final ResourceBundle lStrings =
        ResourceBundle.getBundle(LSTRING_FILE);

    public HttpServlet() {
        // NOOP
    }

    /**
     * 处理HTTP GET请求,需要用户实现具体逻辑,否则会返回HTTP ERROR信息
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
    	//获取协议
        String protocol = req.getProtocol();
        //获取http.method_get_not_supported的国际化字符串
        String msg = lStrings.getString("http.method_get_not_supported");
        if (protocol.endsWith("1.1")) {
        	//如果是HTTP/1.1,返回405禁止访问方法错误
            resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
        } else {
        	//如果不是HTTP/1.1,返回400错误的请求错误
            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
        }
    }
    /**
     * 获取最后被修改时间
     * @param req
     * @return
     */
    protected long getLastModified(HttpServletRequest req) {
        return -1;
    }
    /**
     * 处理HTTP HEAD请求
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    protected void doHead(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {

        if (DispatcherType.INCLUDE.equals(req.getDispatcherType())) {
        	//如果DispatcherType是INCLUDE则当成GET请求处理
            doGet(req, resp);
        } else {
        	//否则封装不带BODY信息的response返回给客户端
            NoBodyResponse response = new NoBodyResponse(resp);
            doGet(req, response);
            response.setContentLength();
        }
    }
    /**
     * 处理HTTP POST请求,@see doGet
     * @param req
     * @param resp
     * @throws ServletException
     * @throws 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);
        }
    }
    /**
     * 处理HTTP PUT请求
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    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(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
        } else {
            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
        }
    }
    /**
     * 处理HTTP DELETE请求
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    protected void doDelete(HttpServletRequest req,
                            HttpServletResponse resp)
        throws ServletException, IOException {

        String protocol = req.getProtocol();
        String msg = lStrings.getString("http.method_delete_not_supported");
        if (protocol.endsWith("1.1")) {
            resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
        } else {
            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
        }
    }

    /**
     * 获取除了javax.servlet.http.HttpServlet以外的任意类c的所有声明方法
     * @param c
     * @return
     */
    private static Method[] getAllDeclaredMethods(Class<?> c) {

        if (c.equals(javax.servlet.http.HttpServlet.class)) {
            return null;
        }

        Method[] parentMethods = getAllDeclaredMethods(c.getSuperclass());
        Method[] thisMethods = c.getDeclaredMethods();

        if ((parentMethods != null) && (parentMethods.length > 0)) {
            Method[] allMethods =
                new Method[parentMethods.length + thisMethods.length];
            System.arraycopy(parentMethods, 0, allMethods, 0,
                             parentMethods.length);
            System.arraycopy(thisMethods, 0, allMethods, parentMethods.length,
                             thisMethods.length);

            thisMethods = allMethods;
        }

        return thisMethods;
    }

    /**
     * 处理HTTP OPTIONS请求
     */
    protected void doOptions(HttpServletRequest req,
            HttpServletResponse resp)
        throws ServletException, IOException {

        Method[] methods = getAllDeclaredMethods(this.getClass());

        boolean ALLOW_GET = false;
        boolean ALLOW_HEAD = false;
        boolean ALLOW_POST = false;
        boolean ALLOW_PUT = false;
        boolean ALLOW_DELETE = false;
        boolean ALLOW_TRACE = true;
        boolean ALLOW_OPTIONS = true;

        for (int i=0; i<methods.length; i++) {
            Method m = methods[i];

            if (m.getName().equals("doGet")) {
                ALLOW_GET = true;
                ALLOW_HEAD = true;
            }
            if (m.getName().equals("doPost"))
                ALLOW_POST = true;
            if (m.getName().equals("doPut"))
                ALLOW_PUT = true;
            if (m.getName().equals("doDelete"))
                ALLOW_DELETE = true;
        }

        String allow = null;
        if (ALLOW_GET)
            allow=METHOD_GET;
        if (ALLOW_HEAD)
            if (allow==null) allow=METHOD_HEAD;
            else allow += ", " + METHOD_HEAD;
        if (ALLOW_POST)
            if (allow==null) allow=METHOD_POST;
            else allow += ", " + METHOD_POST;
        if (ALLOW_PUT)
            if (allow==null) allow=METHOD_PUT;
            else allow += ", " + METHOD_PUT;
        if (ALLOW_DELETE)
            if (allow==null) allow=METHOD_DELETE;
            else allow += ", " + METHOD_DELETE;
        if (ALLOW_TRACE)
            if (allow==null) allow=METHOD_TRACE;
            else allow += ", " + METHOD_TRACE;
        if (ALLOW_OPTIONS)
            if (allow==null) allow=METHOD_OPTIONS;
            else allow += ", " + METHOD_OPTIONS;

        resp.setHeader("Allow", allow);
    }

    /**
     * 处理HTTP TRACE请求
     */
    protected void doTrace(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {

        int responseLength;

        String CRLF = "\r\n";
        StringBuilder buffer = new StringBuilder("TRACE ").append(req.getRequestURI())
            .append(" ").append(req.getProtocol());

        Enumeration<String> reqHeaderEnum = req.getHeaderNames();

        while( reqHeaderEnum.hasMoreElements() ) {
            String headerName = reqHeaderEnum.nextElement();
            buffer.append(CRLF).append(headerName).append(": ")
                .append(req.getHeader(headerName));
        }

        buffer.append(CRLF);

        responseLength = buffer.length();

        resp.setContentType("message/http");
        resp.setContentLength(responseLength);
        ServletOutputStream out = resp.getOutputStream();
        out.print(buffer.toString());
        out.close();
        return;
    }

    /**
     * 处理HTTP REQUEST的实际调用函数,通过它再进一步的调用doGet、doPost方法
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
        //获取http request的method参数,其实就是html的form标签
    	//中method属性对应的字符串
        String method = req.getMethod();
        //如果是GET请求
        if (method.equals(METHOD_GET)) {
        	//获取最后被修改时间
            long lastModified = getLastModified(req);
            if (lastModified == -1) {
                /**如果servlet不支持http request header的if-modified-since属性
            	 * 则继续处理
            	 **/
                doGet(req, resp);
            } else {
            	//如果支持这个属性
                long ifModifiedSince;
                try {
                    ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                } catch (IllegalArgumentException iae) {
                    // Invalid date header - proceed as if none was set
                    ifModifiedSince = -1;
                }
                /**
                 * 如果客户端的文件最后修改时间和服务器端的文件最后修改时间一致则返回304不需要修改状态
                 * 这样服务器就不返回html,浏览器读取本地缓存文件,否则重新获取服务器端的对应html文件
                 */
                if (ifModifiedSince < (lastModified / 1000 * 1000)) {
                    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 {
            //如果HTTP HEADER的method属性超出了get、head、post、put、delete、
        	//options、trace范围则返回501不支持该请求错误
            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);
        }
    }
    private void maybeSetLastModified(HttpServletResponse resp,
                                      long lastModified) {
        if (resp.containsHeader(HEADER_LASTMOD))
            return;
        if (lastModified >= 0)
            resp.setDateHeader(HEADER_LASTMOD, lastModified);
    }

    /**
     * 覆盖实现父类的service方法,如果开发者继承该类没必要实现该方法,只需要实现protected
     * 的service方法
     */
    @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);
    }
}

HttpServlet开始就有了doService的具体实现细节了,它使浏览器的本地缓存文件的功能真正的开始发挥作用,提高了HTTP访问效率。doService的具体实现细节需要由Service

时间: 2025-01-04 10:40:08

Servlet源码解析:Servlet接口及其重要子类的相关文章

源码解析Servlet和HttpServlet

在编写 Servlet 时需要用到两个用于所有 Servlet 的基本软件包:javax.servlet 和 javax.servlet.http.下面主要介绍 javax.servlet 提供的 Servlet 以及 javax.servlet.http 提供的 HttpServlet 应用编程接口. Servlet源码: package javax.servlet; public interface Servlet {     public abstract void init(Servle

Servlet源码解析:Session、Request以及Response

首先我们来看看ServletRequest的源码: public interface ServletRequest { //获取request的属性(注意不是请求参数) public Object getAttribute(String name); //获取request的所有属性的名字 public Enumeration<String> getAttributeNames(); //获取request编码 public String getCharacterEncoding(); //设

Spring源码解析 - BeanFactory接口体系解读

不知道为什么看着Spring的源码,感触最深的是Spring对概念的抽象,所以我就先学接口了. BeanFactory是Spring IOC实现的基础,这边定义了一系列的接口,我们通过这些接口的学习,可以大致了解BeanFactory体系各接口如何分工合作. 为学习具体实现打下基础.毕竟这边逻辑复杂,涉及的概念很多. BeanFactory 是Spring bean容器的根接口.提供获取bean,是否包含bean,是否单例与原型,获取bean类型,bean 别名的api. -- Autowire

pytorch源码解析-动态接口宏

动态库接口定义: gcc: 定义在动态库的显示属性: 作用对象: 函数.变量.模板以及C++类 default: 表示在动态库内可见 hidden: 表示不可见 #define EXPORT __attribute__((__visibility__("default"))) 微软: #define C10_EXPORT __declspec(dllexport) 制作dll时候用 #define C10_IMPORT __declspec(dllimport) 调用dll时候用 放到

深入Jetty源码之Servlet框架及实现(Servlet、Filter、Registration)

概述 Servlet是Server Applet的缩写,即在服务器端运行的小程序,而Servlet框架则是对HTTP服务器(Servlet Container)和用户小程序中间层的标准化和抽象.这一层抽象隔离了HTTP服务器的实现细节,而Servlet规范定义了各个类的行为,从而保证了这些"服务器端运行的小程序"对服务器实现的无关性(即提升了其可移植性).在Servlet规范有以下几个核心类(接口):ServletContext:定义了一些可以和Servlet Container交互的

Servlet源码初探

年底,公司的事情告一段落,就来捣鼓一下这个Servlet源码,为下一步的spingmvc源码初探做准备 1.Servlet接口 public interface Servlet { void init(ServletConfig var1) throws ServletException; ServletConfig getServletConfig(); void service(ServletRequest var1, ServletResponse var2) throws Servlet

Spring-cloud &amp; Netflix 源码解析:Eureka 服务注册发现接口 ****

http://www.idouba.net/spring-cloud-source-eureka-client-api/?utm_source=tuicool&utm_medium=referral *************************** 先关注下netflix eureka server 原生提供的接口.https://github.com/Netflix/eureka/wiki/Eureka-REST-operations 这是对非java的服务使用eureka时可以使用的r

IdentityServer4源码解析_4_令牌发放接口

目录 identityserver4源码解析_1_项目结构 identityserver4源码解析_2_元数据接口 identityserver4源码解析_3_认证接口 identityserver4源码解析_4_令牌发放接口 identityserver4源码解析_5_查询用户信息接口 identityserver4源码解析_6_结束会话接口 identityserver4源码解析_7_查询令牌信息接口 identityserver4源码解析_8_撤销令牌接口 协议 Token接口 oidc服

IdentityServer4源码解析_5_查询用户信息接口

目录 IdentityServer4源码解析_1_项目结构 IdentityServer4源码解析_2_元数据接口 IdentityServer4源码解析_3_认证接口 IdentityServer4源码解析_4_令牌发放接口 IdentityServer4源码解析_5_查询用户信息接口 [IdentityServer4源码解析_6_结束会话接口] [IdentityServer4源码解析_7_查询令牌信息接口] [IdentityServer4源码解析_8_撤销令牌接口] 协议简析 UserI