關(guān)于Servlet的學(xué)習(xí)還是在上學(xué)的時(shí)候,自學(xué)Java植影,也就是只是了解了Servlet是什么以及怎么使用裳擎,現(xiàn)在慢慢的明白很多很多的框架等等都是在Servlet上做的擴(kuò)展,也開(kāi)始明白自己的基礎(chǔ)不好∷急遥現(xiàn)在回頭來(lái)學(xué)習(xí)一下Servlet的相關(guān)知識(shí)句惯。
Servlet的使用
對(duì)于Servlet怎么寫(xiě),以及在Servlet容器(這里特指Tomcat)中怎么配置幾乎都忘記了支救,先回頭了下一個(gè)最簡(jiǎn)單的Servlet的開(kāi)發(fā)抢野。
這里以一個(gè)最簡(jiǎn)單的自定義Servlet顯示一個(gè)頁(yè)面來(lái)作為例子。
首先寫(xiě)一個(gè)TestServlet各墨,繼承HttpServlet:
package me.cxis.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* Created by cheng.xi on 2017-04-12 23:42.
*/
public class TestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doGet...");
resp.setContentType( "text/html;charset=UTF-8" );
PrintWriter out = resp.getWriter();
try {
out.println( "<html>" );
out.println( "<head>" );
out.println( "<title>TestServlet</title>" );
out.println( "</head>" );
out.println( "<body>" );
out.println( "<h2>testServlet</h2>" );
out.println( "</body>" );
out.println( "</html>" );
} finally {
out.close();
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doPost...");
this.doGet(req,resp);
}
}
然后在web.xml中配置剛才寫(xiě)的servlet:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<servlet>
<servlet-name>testServlet</servlet-name>
<servlet-class>me.cxis.servlet.TestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>testServlet</servlet-name>
<url-pattern>/testServlet</url-pattern>
</servlet-mapping>
</web-app>
這就可以了指孤,啟動(dòng)Servlet容器,這里是tomcat贬堵,然后訪問(wèn)http://localhost:8080/testServlet
就可以看到結(jié)果了恃轩。
寫(xiě)到這里感覺(jué)大學(xué)時(shí)光又回來(lái)了,那時(shí)候做項(xiàng)目的時(shí)候黎做,可沒(méi)少寫(xiě)了servlet叉跛,老師,學(xué)長(zhǎng)蒸殿,實(shí)驗(yàn)室筷厘,三年,幾個(gè)人每天就寫(xiě)代碼宏所,太單純酥艳!
Servlet的工作流程
這里只看下一個(gè)請(qǐng)求到來(lái)時(shí)候Servlet是怎么處理的流程,不涉及容器的啟動(dòng)初始化之類(lèi)的爬骤。
大概流程
- 客戶(hù)端發(fā)起一個(gè)http請(qǐng)求充石,比如get類(lèi)型。
- Servlet容器接收到請(qǐng)求霞玄,根據(jù)請(qǐng)求信息調(diào)用相應(yīng)的Servlet骤铃。
- Servlet來(lái)處理具體的業(yè)務(wù)邏輯,也就是我們寫(xiě)的Servlet中的代碼坷剧。
- Servlet處理完成之后惰爬,返回給Servlet容器。
- Servlet容器將最后結(jié)果返回給客戶(hù)端听隐。
具體流程
- 客戶(hù)端發(fā)起一個(gè)http請(qǐng)求补鼻,比如get類(lèi)型。
- Servlet容器接收到請(qǐng)求,根據(jù)請(qǐng)求信息风范,封裝成HttpServletRequest和HttpServletResponse對(duì)象咨跌。
- Servlet容器調(diào)用HttpServlet的init()方法,init方法只在第一次請(qǐng)求的時(shí)候被調(diào)用硼婿。
- Servlet容器調(diào)用service()方法锌半。
- service()方法根據(jù)請(qǐng)求類(lèi)型,這里是get類(lèi)型寇漫,分別調(diào)用doGet或者doPost方法刊殉,這里調(diào)用doGet方法。
- doXXX方法中是我們自己寫(xiě)的業(yè)務(wù)邏輯州胳。
- 業(yè)務(wù)邏輯處理完成之后记焊,返回給Servlet容器,然后容器將結(jié)果返回給客戶(hù)端栓撞。
- 容器關(guān)閉時(shí)候遍膜,會(huì)調(diào)用destory方法
這其中的2,3,4,5,6使我們要關(guān)注的,其他的步驟是容器實(shí)現(xiàn)的瓤湘,先不了解具體信息瓢颅。
注意:
- 同一個(gè)Servlet只會(huì)被初始化一次,也就是init方法只會(huì)被調(diào)用一次弛说。
- 同一個(gè)Servlet只存在一個(gè)實(shí)例挽懦,而可能會(huì)有多個(gè)請(qǐng)求同時(shí)請(qǐng)求一個(gè)Servlet,所以存在線程安全問(wèn)題木人。
Servlet生命周期
Servlet生命周期由容器來(lái)管理信柿,大致包含了四個(gè)階段:
- 加載和實(shí)例化,由容器負(fù)責(zé)加載和實(shí)例化Servlet虎囚。
- 初始化角塑,容器會(huì)調(diào)用init方法初始化Servlet對(duì)象蔫磨,init方法只會(huì)被調(diào)用一次淘讥。
- 處理請(qǐng)求,容器會(huì)調(diào)用service方法進(jìn)行請(qǐng)求的處理堤如,service會(huì)調(diào)用相應(yīng)的doXxx方法來(lái)處理蒲列。
- 服務(wù)銷(xiāo)毀,即一個(gè)Servlet實(shí)例從服務(wù)中被移除的時(shí)候搀罢,會(huì)調(diào)用destory方法蝗岖,這個(gè)方法也只會(huì)被執(zhí)行一次。
下面我們解析的是從初始化開(kāi)始榔至,對(duì)于加載和實(shí)例化不做說(shuō)明抵赢。
Servlet流程的源碼分析
初始化,init
請(qǐng)求到來(lái)時(shí)候,會(huì)由容器先處理铅鲤,然后調(diào)用Servlet的init方法進(jìn)行初始化划提,init方法在GenericServlet中:
@Override
public void init(ServletConfig config) throws ServletException {
//config是由容器處理的,里面包含了請(qǐng)求和Servlet的相關(guān)配置信息
this.config = config;
//由子類(lèi)進(jìn)行實(shí)現(xiàn)的init方法
//我們可以重寫(xiě)init方法邢享,進(jìn)行一些資源的初始化等的操作
this.init();
}
有關(guān)init方法只會(huì)執(zhí)行一次的問(wèn)題鹏往,這里并沒(méi)有體現(xiàn)到,這是具體的實(shí)現(xiàn)骇塘,而有關(guān)判斷是在容器的StandardWrapper類(lèi)中伊履,會(huì)判斷是否已經(jīng)實(shí)例化,沒(méi)有實(shí)例化就調(diào)用實(shí)例化方法實(shí)例Servlet款违,這就會(huì)調(diào)用init方法唐瀑。
處理請(qǐng)求service
當(dāng)初始化完成之后,容器會(huì)進(jìn)行處理插爹,然后容器再去調(diào)用Servlet的service方法去進(jìn)行請(qǐng)求的處理介褥,首先進(jìn)入HttpServlet的service方法:
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//請(qǐng)求類(lèi)型方法
String method = req.getMethod();
//對(duì)每種類(lèi)型分別進(jìn)行處理
if (method.equals(METHOD_GET)) {//get方法
//get方法涉及到緩存的問(wèn)題,會(huì)對(duì)最后修改時(shí)間進(jìn)行判斷
long lastModified = getLastModified(req);
//不支持lastModified递惋,直接調(diào)用doGet方法進(jìn)行處理
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
//支持lastModified
long ifModifiedSince;
try {
//從請(qǐng)求頭中獲取If-Modified-Since屬性的值
ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
} catch (IllegalArgumentException iae) {
// Invalid date header - proceed as if none was set
ifModifiedSince = -1;
}
//比較時(shí)間
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
//Servlet的修改時(shí)間晚柔滔,表示數(shù)據(jù)比客戶(hù)端新
//需要修改時(shí)間,并調(diào)用get方法處理
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
//沒(méi)有修改 直接返回狀態(tài)給客戶(hù)端
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {//Head方法
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {//post方法
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {//put方法
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {//delete方法
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {//options方法
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {//trace方法
doTrace(req,resp);
} else {
//servlet不支持其他類(lèi)型方法
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);
}
}
可以看到萍虽,service方法處理請(qǐng)求睛廊,會(huì)根據(jù)請(qǐng)求的類(lèi)型分別進(jìn)行處理,get會(huì)涉及到最后修改時(shí)間問(wèn)題杉编。這些doXxx方法都會(huì)有默認(rèn)的實(shí)現(xiàn)超全,如果子類(lèi)不做重寫(xiě)就會(huì)執(zhí)行默認(rèn)方法。
請(qǐng)求處理完成之后會(huì)回到容器中由容器進(jìn)行其他的處理邓馒。
銷(xiāo)毀方法destory
如果我們重寫(xiě)了destory方法嘶朱,在容器關(guān)閉或者Servlet實(shí)例移除的時(shí)候,會(huì)回調(diào)我們的destory方法光酣,一般用來(lái)釋放資源疏遏,這個(gè)方法也只會(huì)被調(diào)用一次。
上面只是最簡(jiǎn)單的一個(gè)Servlet流程救军,沒(méi)有涉及到更多的內(nèi)容财异,比如上下文,比如session唱遭,filter等等戳寸,還有一個(gè)就是線程安全問(wèn)題,這個(gè)問(wèn)題會(huì)在下面文章繼續(xù)說(shuō)明拷泽,流程的解析就到這里疫鹊。