一、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方法是否是覆蓋重寫來進行判斷。