Servlet是Server + Applet的縮寫,表示一個服務器應用.Servlet其實就是一套規範,我們按照這套規範寫的代碼就可以直接在Java的服務器上面運行.如下圖所示的是Servlet3.1中Servlet的結構圖
一,Servlet接口
public interface Servlet {
/*
init方法在容器啓動時被容器調用(注:load-on-startup可以指定Servlet被創建的時機,若爲負數或者不設置,
則在第一次請求時[即第一次調用該Servlet才被創建],若爲0或者正數,則在當前WEB應用被Servlet容器加載時創建實例,
且數越小越早被創建),只會被調用一次;
*/
public void init(ServletConfig config) throws ServletException;
/*
用於獲取ServletConfig,ServletConfig顧名思義指的是Servlet的配置,比如我們在web.xml中定義Servlet時通過init-param
標籤配置的參數就是通過ServletConfig來保存的.
*/
public ServletConfig getServletConfig();
/*
service方法用於具體處理一個請求
*/
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
/*
getServletInfo方法可以獲取一些Servlet相關的信息,若作者,版權等,這個方法需要自己實現,默認返回空字符串;
*/
public String getServletInfo();
/*
主要用於在Servlet銷燬(一般指關閉服務器)時釋放一些資源,也只會調用一次
*/
public void destroy(){}
二,ServletConfig
package javax.servlet;
import java.util.Enumeration;
public interface ServletConfig {
/*
用於獲取Servlet的名字,也就是我們在web.xml中定義的servlet-name
*/
public String getServletName();
/*
返回值ServletContext代表的是我們這個應用的本身,那麼ServletContext裏邊設置的參數就可以被當前應用的所有Servlet共享了[注:做項目時,參數可以保存在Session中,也可以在Application中,而後者很多時候就是保存在ServletContext中];
我們可以這麼理解,ServletConfig是Servlet級的,而ServletContext是Context(也就是Application)級的,另外我們可以獲取更高一層的站點級也就是Tomcat中的Host級的相應操作:在Servlet的標準中有這麼一個方法:public ServletContext getContext(String uripath),它可以根據路徑獲取到同一站點下的別的應用的ServletContext!由於安全原因,一般返回null,如果想使用需要進行一些設置.
*/
public ServletContext getServletContext();
//用於獲取init-param配置的參數
public String getInitParameter(String name);
//用於獲取配置的所有init-param的名字的集合
public Enumeration<String> getInitParameterNames();
}
三 GenericServlet
GenericServlet是Servlet的默認實現,主要做了三件事:
①實現了ServletConfig接口,可以直接調用ServletConfig中的方法;
②提供了無參的init方法;
③提供了log方法;
/*
GenericServlet實現了ServletConfig接口,當需要調用ServletConfig中方法的時候,可以直接調用.比如獲取ServletContext的時候可以直接調用getServletContext,而無須調用getServletConfig().getServletContext(),不過底層實現其實是內部調用,源碼如下:
*/
public ServletContext getServletContext() {
ServletConfig sc = getServletConfig();
if (sc == null) {
throw new IllegalStateException(
lStrings.getString("err.servlet_config_not_initialized"));
}
return sc.getServletContext();
}
/*
GenericServlet實現了Servlet的init(ServletConfig config)方法,在裏面將參數config設置給了內部變量config,然後調用了無參的init()方法;
這種做法有三個作用:
①將參數config設置給了內部屬性config,這樣就可以在ServletConfig的接口方法中直接調用config相應的方法來執行;
②當我們在寫Servlet的時候就可以只處理自己的初始化邏輯,而不需要關心config了;
③在重寫init()方法時不需要在調用super.init(config)了,如果在自己的Servlet中重寫了帶參的init方法,一定記着調用super.init(config),否則會報空指針異常
*/
//GenericServlet提供了兩個log方法,一個記錄日誌,一個記錄異常
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
public void init() throws ServletException {
}
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的基類,SpringMVC中的DispatchServlet就是繼承該類,HttpServlet主要重寫了service方法.在service方法中首先將ServletRequest和ServletResponse轉換爲了HttpServletRequest和HttpServletResponse ,然後根據HTTP請求的類型不同將請求路由到了不同的處理方法
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException
{
HttpServletRequest request;
HttpServletResponse response;
if (!(req instanceof HttpServletRequest &&
res instanceof HttpServletResponse)) {
throw new ServletException("non-HTTP request or response");
}
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
service(request, response);
}
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < lastModified) {
doGet()
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 {
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);
}
}
/*
具體處理方法時doXXX的結構,.doGet,doPost,doPut和doDelete方法都是模板方法,而且子類如果沒有實現將拋出異常,在調用doGet方法之前還對是否過期做了檢查,如果沒有過期則直接返回304狀態碼使用緩存;doHead調用了doGet的請求,然後返回空body的Response;doOption和doTrace主要用來做一些調試工作,doOtion返回所有支持的處理類型的集合,正常情況下可以禁用,doTrace是用來遠程診斷服務器的,它會將接收到的header原封不動地返回
*/
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);
}
}