Java Servlet工作流程以及源碼解析

關(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)的爬骤。

大概流程

  1. 客戶(hù)端發(fā)起一個(gè)http請(qǐng)求充石,比如get類(lèi)型。
  2. Servlet容器接收到請(qǐng)求霞玄,根據(jù)請(qǐng)求信息調(diào)用相應(yīng)的Servlet骤铃。
  3. Servlet來(lái)處理具體的業(yè)務(wù)邏輯,也就是我們寫(xiě)的Servlet中的代碼坷剧。
  4. Servlet處理完成之后惰爬,返回給Servlet容器。
  5. Servlet容器將最后結(jié)果返回給客戶(hù)端听隐。

具體流程

  1. 客戶(hù)端發(fā)起一個(gè)http請(qǐng)求补鼻,比如get類(lèi)型。
  2. Servlet容器接收到請(qǐng)求,根據(jù)請(qǐng)求信息风范,封裝成HttpServletRequest和HttpServletResponse對(duì)象咨跌。
  3. Servlet容器調(diào)用HttpServlet的init()方法,init方法只在第一次請(qǐng)求的時(shí)候被調(diào)用硼婿。
  4. Servlet容器調(diào)用service()方法锌半。
  5. service()方法根據(jù)請(qǐng)求類(lèi)型,這里是get類(lèi)型寇漫,分別調(diào)用doGet或者doPost方法刊殉,這里調(diào)用doGet方法。
  6. doXXX方法中是我們自己寫(xiě)的業(yè)務(wù)邏輯州胳。
  7. 業(yè)務(wù)邏輯處理完成之后记焊,返回給Servlet容器,然后容器將結(jié)果返回給客戶(hù)端栓撞。
  8. 容器關(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è)階段:

  1. 加載和實(shí)例化,由容器負(fù)責(zé)加載和實(shí)例化Servlet虎囚。
  2. 初始化角塑,容器會(huì)調(diào)用init方法初始化Servlet對(duì)象蔫磨,init方法只會(huì)被調(diào)用一次淘讥。
  3. 處理請(qǐng)求,容器會(huì)調(diào)用service方法進(jìn)行請(qǐng)求的處理堤如,service會(huì)調(diào)用相應(yīng)的doXxx方法來(lái)處理蒲列。
  4. 服務(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ō)明拷泽,流程的解析就到這里疫鹊。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末袖瞻,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子拆吆,更是在濱河造成了極大的恐慌虏辫,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锈拨,死亡現(xiàn)場(chǎng)離奇詭異砌庄,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)奕枢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)娄昆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人缝彬,你說(shuō)我怎么就攤上這事萌焰。” “怎么了谷浅?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵扒俯,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我一疯,道長(zhǎng)撼玄,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任墩邀,我火速辦了婚禮掌猛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘眉睹。我一直安慰自己荔茬,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布竹海。 她就那樣靜靜地躺著慕蔚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪斋配。 梳的紋絲不亂的頭發(fā)上孔飒,一...
    開(kāi)封第一講書(shū)人閱讀 49,764評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音许起,去河邊找鬼十偶。 笑死,一個(gè)胖子當(dāng)著我的面吹牛园细,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播接校,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼猛频,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼狮崩!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起鹿寻,我...
    開(kāi)封第一講書(shū)人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤睦柴,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后毡熏,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體坦敌,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年痢法,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了狱窘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡财搁,死狀恐怖蘸炸,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情尖奔,我是刑警寧澤搭儒,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站提茁,受9級(jí)特大地震影響淹禾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜茴扁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一稀拐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧丹弱,春花似錦德撬、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至坯苹,卻和暖如春隆檀,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背粹湃。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工恐仑, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人为鳄。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓裳仆,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親孤钦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子歧斟,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容