SpringMVC源码解析之Servlet
从类名上就能看出,GenericServlet是一个一般性的,与协议无关的Servlet类。
GenericServlet作为Servlet接口的默认实现,主要实现了下面功能
GenericServlet实现了ServletConfig接口,可以通过Genericervlet直接调用ServletConfig的方法。
当然,虽然可以通过Genericervlet直接调用ServletConfig的方法,但实际上还是先获取到ServletConfig对象在调用其方法实现的,如getInitParameter方法,其它ServletConfig方法也是如此。
public String getInitParameter(String name) {
return getServletConfig().getInitParameter(name);
}
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方法,默认也是不进行处理。
GenericServlet提供了两个日志方法,分别为记录日志和记录异常。
//记录日志
public void log(String msg) {
getServletContext().log(getServletName() + ": " + msg);
}
//记录异常
public void log(String message, Throwable t) {
getServletContext().log(getServletName() + ": " + message, t);
}
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方法是否是覆盖重写来进行判断。