一、簡介
Servlet是server+Applet的縮寫,表示一個服務器應用。Servlet就是一套規範,按照這套規範寫的代碼就可以直接在Java服務器上面運行。
二、Servlet接口
Servlet是一套規範,那麼在Java中規範則是接口。
2.1 Servlet3.1中Servlet的接口定義如下
public interface Servlet {
public void init(ServletConfig config) throws ServletException;
public ServletConfig getServletConfig();
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
public String getServletInfo();
public void destroy();
}
Servlet接口介紹
web.xml配置
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.lbx.servlet.HelloServlet</servlet-class>
<init-param>
<param-name>initParam</param-name>
<param-value>initValue</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
1 public void init(ServletConfig config) throws ServletException;
<load-on-startup>值</load-on-startup>
- init()方法初始化內容,被調用時,web容器會把config依賴傳進去。
- load-on-startup的值不爲負數時,web容器啓動時會調用init方法。
- 沒有填寫load-on-startup標籤或者標籤裏面的值是負數的話,訪問該Servlet的時候會纔會調用init方法。
- 同一個Servlet的init方法在整個流程中只會調用一次。
2 public ServletConfig getServletConfig();
getServletConfig()方法用來獲取Servlet的配置,比如上面 <init-param>標籤裏面的參數。下面會詳細介紹這個方法。
3 public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
service方法用於處於請求,web容器會解析HTTP協議,封裝成對象傳進去。
4 public String getServletInfo();
getServletInfo()獲取servlet相關的信息,如作者、版權等,這個方法在需要自己實現,默認返回空字符串。
5 public void destroy();
destory()方法主要用於Servlet的銷燬,當應用從tomcta移除或者關閉服務器時會被調用,用於釋放資源,只會調用一次。
2.2 ServletConfig接口定義
public interface ServletConfig {
public String getServletName();
public ServletContext getServletContext();
public String getInitParameter(String name);
public Enumeration<String> getInitParameterNames();
}
ServeltConfig接口介紹
- getServeltName():用於獲取Servlet的名字,也就是web.xml中定義的servlet-name。
- getServeltContext():獲取ServeltContext對象,全局共享這個對象,一個應用中只能有一個ServletContext對象。可獲取<context-param>裏面內容。
- getInitParameter(): 獲取init-param配置的值。
- getInitParameterNames():獲取配置的所有Init-param的名字集合。
三、GenericServlet
GenericServlet這個類實現了Servlet和ServletConfig接口(service()方法用abstract修飾了),可以直接調用Servlet和ServletConfig裏的方法,比如獲取ServletConfig中的方法時候可以直接調用,而無須調用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()方法,這個方法是模板方法,在子類中可以通過覆蓋它來完成自己的初始化工作。
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
public void init() throws ServletException {
}
這種做法有三個作用:
- 將參數config設置給內部屬性config,這樣有其他地方需要這個對象就可直接調用。
- 做初始化操作時,不用關心config對象。
- 重寫init()方法不需要調用super.init(config)。
四、HttpServlet
這個類是我們最常使用的類,繼承了GenericServlet,寫servlet直接繼承就可以了,無需重新實現Servlet接口,這個類主要作用是如何處理請求。
看代碼:
@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");
}
//向下轉型爲HttpServletRequest
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
//調用http的處理方法
service(request, response);
}
問題一:爲什麼可以將ServletRequest 強轉爲HttpServletRequest呢?
因爲在tomcat內部創建的這個request它就是httpServletRequest接口的子類。
問題二:爲什麼要將ServletRequest 強轉爲HttpServletRequest呢?
- 因爲ServletRequest中只提供了獲取基本信息的方法。沒有獲取用戶請求類型的方法,而且還包含了許多方法。
執行完上面方法,最後會調用service方法,調用的不是用一個service,因爲傳入參數類型不一樣,重載方法
這個方法的作用是:獲取Http請求類型,將不同請求類型路由到不同的處理方法。具體方法都是doXXX的結構,doGet,doPost,doPut,doDelete方法都是模板方法,而且如果子類沒有實現將返回404錯誤頁面。
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) {
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);
}
}
流程圖