SpringMVC源码解析之GenericServlet和HttpServlet

SpringMVC源码解析之Servlet

一、GenericServlet

从类名上就能看出,GenericServlet是一个一般性的,与协议无关的Servlet类。
GenericServlet作为Servlet接口的默认实现,主要实现了下面功能

1. 实现了ServletConfig接口

GenericServlet实现了ServletConfig接口,可以通过Genericervlet直接调用ServletConfig的方法。
当然,虽然可以通过Genericervlet直接调用ServletConfig的方法,但实际上还是先获取到ServletConfig对象在调用其方法实现的,如getInitParameter方法,其它ServletConfig方法也是如此。

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

2. 提供生命周期方法的默认实现

Servlet有两个生命周期方法,分别是初始化Servlet#init(ServletConfig)和销毁方法Servlet#destroy()。

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

对于init方法,提供了无参的init方法,默认不进行处理。GenericServlet的子类可以直接覆盖无参的init的方法以自定义初始化逻辑。
(1)定义了成员变量config,将参数赋给config。方便了ServletConfig接口的方法可以通过config对象来实现
(2)子类对于init逻辑的自定义时可以不用关注config对象的处理,只需要覆盖无参的init方法。如果对有参的init方法进行了覆盖,那么无参init方法将会失效,而且需要调用super.init(config),否则与config对象相关的方法都需要覆盖重写。

public void destroy() {
}

对于destroy方法,默认也是不进行处理。

3. log功能

GenericServlet提供了两个日志方法,分别为记录日志和记录异常。

//记录日志
public void log(String msg) {
    getServletContext().log(getServletName() + ": " + msg);
}
//记录异常
public void log(String message, Throwable t) {
    getServletContext().log(getServletName() + ": " + message, t);
}

二、HttpServlet

HttpServlet是针对HTTP协议的Servlet实现类,继承了GenericServlet类。
HttpServlet类最主要的作用是重写了service方法,定义了对HTTP请求的处理方法。

public void service(ServletRequest req, ServletResponse res)
        throws ServletException, IOException {
    HttpServletRequest request;
    HttpServletResponse response;

	//将ServletRequest和ServletResponse转化成HttpServletRequest和HttpServletResponse进行处理
    try {
        request = (HttpServletRequest) req;
        response = (HttpServletResponse) res;
    } catch (ClassCastException e) {
        throw new ServletException("non-HTTP request or response");
    }
    service(request, response);
}
//根据HTTP请求类型进行不同的处理
protected void service(HttpServletRequest req, HttpServletResponse resp)
		throws ServletException, IOException {
	
	String method = req.getMethod();

	//GET请求
	if (method.equals(METHOD_GET)) {
		//lastModified服务器端资源的修改时间
		long lastModified = getLastModified(req);
		//不支持if-modified-since,进入doGet
		if (lastModified == -1) {
			// servlet doesn't support if-modified-since, no reason
			// to go through further expensive logic
			doGet(req, resp);
		} else {
			//If-Modified-Since,浏览器端缓存的修改时间
			long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
			//服务器端在浏览器之后发生过修改,缓存无效,进入doGet
			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 {
				//服务器端在浏览器之后没有发生过修改,返回304,直接使用缓存
				resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
			}
		}

	} else if (method.equals(METHOD_HEAD)) {
		//HEAD请求,和GET类似,只是不需要返回响应
		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.
		//
		//异常,HttpServlet不支持该方式的请求
		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);
	}
}

对于GET请求,需要考虑是否可以使用缓存的情况。
除了可能使用缓存以外的场景,最终都会进入doXXX方式进入处理。
doGet,doPost, doDelete,doPut的方法都类似,只是一个例子,实际使用时在子类中需要进行覆盖。

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);
    }
}

HEAD请求可以简单理解成不需要相应的GET请求

protected void doHead(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
    NoBodyResponse response = new NoBodyResponse(resp);

    doGet(req, response);
    response.setContentLength();
}

OPTIONAL和TRACE都用于调试,因此HttpServlet中给出了默认实现。
TRACEQ请求是将请求内容在服务器中进行显示。

protected void doTrace(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {

    int responseLength;

    String CRLF = "\r\n";
    String responseString = "TRACE " + req.getRequestURI() +
            " " + req.getProtocol();

    Enumeration reqHeaderEnum = req.getHeaderNames();

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

    responseString += CRLF;

    responseLength = responseString.length();

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

OPTIONAL请求返回服务器支持的请求类型。

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)
        if (allow == null) 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);
}
private Method[] getAllDeclaredMethods(Class c) {
    if (c.getName().equals("javax.servlet.http.HttpServlet"))
        return null;

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

    if (parentMethods != null) {
        Method[] allMethods =
                new Method[parentMethods.length + thisMethods.length];
        for (int i = 0; i < parentMethods.length; i++) {
            allMethods[i] = parentMethods[i];
            j = i;
        }
        j++;
        for (int i = j; i < thisMethods.length + j; i++) {
            allMethods[i] = thisMethods[i - j];
        }
        return allMethods;
    }
    return thisMethods;
}

可以看到,OPTIONAL和TRACE请求是默认支持的,对于其他类型的请求则根据HttpServlet中的相应doXXX方法是否是覆盖重写来进行判断。

你可能感兴趣的:(SpringMVC)