JavaWeb—Servlet基礎(chǔ)(細(xì)節(jié)版笋鄙,相當(dāng)細(xì)節(jié))
?
什么是Servlet师枣?
Servlet就是一個(gè)普通的類(lèi),只不過(guò)這個(gè)類(lèi)能夠接受和處理請(qǐng)求萧落,并且做出響應(yīng)践美。提到Servlet就繞不開(kāi)Servlet容器,那么什么又是Servlet容器呢找岖?通俗的講就是實(shí)現(xiàn)Servlet標(biāo)準(zhǔn)管理輔助Servlet類(lèi)工作的工具陨倡。Servlet和Servlet容器在我看來(lái)就是子彈和槍的關(guān)系,通過(guò)對(duì)標(biāo)準(zhǔn)化接口的實(shí)現(xiàn)互相配合许布,彼此依存又獨(dú)立發(fā)展兴革。在大部分的情況下我們又稱(chēng)Servlet容器為服務(wù)器,常用的有Tomcat等蜜唾。
一個(gè)HTTP來(lái)了又走經(jīng)歷了什么杂曲?
HTTP(超文本傳輸協(xié)議):是互聯(lián)網(wǎng)通信的基礎(chǔ),屬于 TCP/IP 模型中的應(yīng)用層協(xié)議袁余,當(dāng)瀏覽器與服務(wù)器進(jìn)行互相通信時(shí)擎勘,需要先建立TCP 連接,之后服務(wù)器才會(huì)接收瀏覽器的請(qǐng)求信息(request)颖榜,當(dāng)接收到信息之后棚饵,服務(wù)器返回相應(yīng)的信息(response)煤裙。最后瀏覽器接受對(duì)服務(wù)器的信息應(yīng)答后,對(duì)這些數(shù)據(jù)進(jìn)行解釋執(zhí)行(解析HTML文件和各種資源進(jìn)行展示)噪漾。
HTTP是一個(gè)無(wú)狀態(tài)的連接協(xié)議硼砰。
所謂無(wú)狀態(tài):
根據(jù)早期的HTTP協(xié)議,每次request-reponse時(shí)欣硼,都要重新建立TCP連接题翰。TCP連接每次都重新建立,所以服務(wù)器無(wú)法知道上次請(qǐng)求和本次請(qǐng)求是否來(lái)自于同一個(gè)客戶(hù)端分别。因此遍愿,HTTP通信是無(wú)狀態(tài)(stateless)的。服務(wù)器認(rèn)為每次請(qǐng)求都是一個(gè)全新的請(qǐng)求耘斩,無(wú)論該請(qǐng)求是否來(lái)自同一地址≌犹睿現(xiàn)在,雖然HTTP協(xié)議允許TCP連接復(fù)用括授,以節(jié)省建立連接所耗費(fèi)的時(shí)間坞笙,但無(wú)狀態(tài)的特性依舊被保留。
準(zhǔn)備階段
為了迎接HTTP的到來(lái)荚虚,首先我們需要有一個(gè)Servlet類(lèi)薛夜,并且告訴Servlet容器自己的存在,這兩個(gè)準(zhǔn)備步驟就是創(chuàng)建Servlet類(lèi)和寫(xiě)入配置文件版述。
正如上文所講梯澜,類(lèi)和Servlet容器之間的配合是通過(guò)接口實(shí)現(xiàn)的,一個(gè)類(lèi)只需要實(shí)現(xiàn)特定的接口渴析,就可以稱(chēng)為一個(gè)Servlet類(lèi)晚伙,并且能夠被Servlet所接受,嗯俭茧,想來(lái)這就是接口的解耦和咆疗。
準(zhǔn)備一個(gè)Servlet類(lèi)
擁有一個(gè)Servlet類(lèi)的三種方案
- 直接實(shí)現(xiàn)Servlet接口(interface)
- 繼承GenericServlet類(lèi)(abstract)
- 繼承HttpServlet類(lèi)(abstract)
在直接實(shí)現(xiàn)或者間接實(shí)現(xiàn)Servlet接口之后我們需要重寫(xiě)其中的service方法,到此Servlet就準(zhǔn)備好了母债。
寫(xiě)入配置文件
配置文件是一個(gè)固定的寫(xiě)法午磁,主要就是為了告訴Servlet容器自己在哪
<servlet>
<servlet-name>自定義Servlet的別名</servlet-name>
<servlet-class>Servlet類(lèi)的全類(lèi)名</servlet-class>
</servlet>
<servlet-mapping>
<servelt-name>自定義Servlet的別名</servelt-name>
<url-pattern>自定義路徑</url-pattern>
</servlet-mapping>
接受請(qǐng)求
Servlet容器開(kāi)啟服務(wù)之后就可以迎接request的到來(lái)了,當(dāng)這個(gè)HTTP請(qǐng)求到達(dá)Servlet容器(以Tomcat為例)的時(shí)候毡们,Tomcat看到有HTTP來(lái)迅皇,就把它帶到要去的那個(gè)地方(項(xiàng)目名),到了地點(diǎn)之后衙熔,Tomcat會(huì)拿出花名冊(cè)(web.xml)讓request挑一個(gè)(0.0)喧半。
結(jié)果,不用挑有指定的青责,那就好辦了挺据。
Tomcat在部署文件中找 servlet-mapping 中與之匹配的 url-pattern,根據(jù)這個(gè) url-pattern 的 servlet-name 映射到真正的 servlet-class ,然后調(diào)用相應(yīng)的 Servlet 類(lèi)脖隶。
擴(kuò)展內(nèi)容:xml
xml全稱(chēng)是Extensible Markup Language可擴(kuò)展標(biāo)記語(yǔ)言扁耐,看到這個(gè)難免會(huì)想起來(lái)HTML,他們有什么關(guān)系呢产阱?為什么有了HTML語(yǔ)言還要xml語(yǔ)言呢婉称?
認(rèn)真的講他們最大的關(guān)系就是都以ml結(jié)尾了,哈哈构蹬,開(kāi)個(gè)玩笑王暗。
HTML我們是很熟悉的,在使用的時(shí)候不難發(fā)現(xiàn)其中的標(biāo)簽都是定義好的庄敛,全世界的HTML文檔用的是同一套標(biāo)記的語(yǔ)法俗壹。
而xml更具有個(gè)性化,其中的標(biāo)簽不僅可以用別人的定義好的藻烤,也可以自己定義绷雏。書(shū)寫(xiě)一個(gè)xml有兩個(gè)相關(guān)的規(guī)則,一個(gè)是標(biāo)簽規(guī)則怖亭,另一個(gè)就是校檢規(guī)則涎显,校檢規(guī)則是用來(lái)告訴程序標(biāo)簽之間的規(guī)則,這個(gè)東西被稱(chēng)之為文檔類(lèi)型定義 Document Type Definition , 簡(jiǎn)稱(chēng) DTD 這兩個(gè)規(guī)則都是可以自定義的(所謂擴(kuò)展)兴猩,所以我們?cè)跁?shū)寫(xiě)不同的xml文件的時(shí)候期吓,會(huì)發(fā)現(xiàn)標(biāo)簽規(guī)則是不一樣的。
從這些層面上來(lái)說(shuō)HTML語(yǔ)言也可以說(shuō)是xml中的一種倾芝,HTML5就是最新的校檢規(guī)則(不知道這么理解有沒(méi)有問(wèn)題讨勤??蛀醉?)
<!-- 我們不難發(fā)現(xiàn)很多xml文檔的文檔聲明中都有聲明其文檔符合的校檢規(guī)則 --> <!-- web.xml --> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> XML Schema是dtd的改進(jìn)版 <!-- mybatis-config.xml --> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd" >
生命周期
通過(guò)以上的步驟Tomcat就找到了HTTP想要見(jiàn)到的那個(gè)Servlet了悬襟,但是這個(gè)類(lèi)也許準(zhǔn)備好了,也許沒(méi)有拯刁,我們假定這個(gè)request是第一次來(lái)脊岳。這時(shí)候就開(kāi)始了Servlet的生命周期了。
因?yàn)槭堑谝淮握?qǐng)求垛玻,Tomcat會(huì)調(diào)用Servlet類(lèi)的無(wú)參構(gòu)造方法割捅,創(chuàng)建這個(gè)Servlet的對(duì)象。
之后初始化帚桩,會(huì)調(diào)用init方法亿驾,這個(gè)方法會(huì)對(duì)Servlet類(lèi)做一些初始化的工作,需要注意的這個(gè)方法在Servlet的一生中只會(huì)執(zhí)行這么一次账嚎。像初始化這么重要的事兒只進(jìn)行一次是有現(xiàn)實(shí)意義的莫瞬,畢竟如果可以多次的話(huà)儡蔓,我早就一米八了。
-
初始化之后一個(gè)Servlet就正式的進(jìn)入服務(wù)狀態(tài)可以接客了疼邀,這時(shí)候就會(huì)調(diào)用service方法喂江,接受HTTP的request,并對(duì)這個(gè)請(qǐng)求做一些服務(wù)項(xiàng)目旁振,剪個(gè)頭發(fā)之類(lèi)啊获询,最后再把面目全非的請(qǐng)求送走,不拐袜,這時(shí)候應(yīng)該叫響應(yīng)response吉嚣。聽(tīng)說(shuō)每次剪頭發(fā)都像整容,可惜好久沒(méi)有剪過(guò)頭發(fā)了蹬铺。
經(jīng)過(guò)第一個(gè)請(qǐng)求之后尝哆,再有HTTP過(guò)來(lái)的時(shí)候,Servlet會(huì)直接調(diào)用service方法為其服務(wù)丛塌,畢竟誰(shuí)一輩子也不能接一個(gè)客戶(hù)初始化一次吧较解。
最后當(dāng)服務(wù)關(guān)閉的時(shí)候,會(huì)銷(xiāo)毀這個(gè)對(duì)象赴邻,在銷(xiāo)毀前會(huì)調(diào)用destroy方法印衔。
其他細(xì)節(jié)
會(huì)話(huà)跟蹤技術(shù)
寫(xiě)到會(huì)話(huà)跟蹤要先從HTTP開(kāi)始說(shuō)起,在之前我們說(shuō)過(guò)HTTP是無(wú)狀態(tài)的姥敛。因?yàn)槠錈o(wú)狀態(tài)的特性奸焙,服務(wù)器不能以狀態(tài)來(lái)區(qū)分和管理請(qǐng)求和響應(yīng),而且一次請(qǐng)求響應(yīng)之后就會(huì)斷開(kāi)連接彤敛,所以服務(wù)器也不需要保存狀態(tài)信息与帆,雖然這樣簡(jiǎn)單不占資源,適用性廣墨榄,但是不利之處在于我們沒(méi)有辦法根據(jù)HTTP本身對(duì)請(qǐng)求做一些區(qū)分玄糟。
所以為了在保留無(wú)狀態(tài)協(xié)議這個(gè)特征的同時(shí)又解決類(lèi)似記錄狀態(tài)的矛盾問(wèn)題,出現(xiàn)了Cookie袄秩。
Cookie
從上圖我們知道阵翎,有幾個(gè)關(guān)鍵性的步驟是需要我們來(lái)做的:
-
創(chuàng)建Cookie
//參數(shù)是cookie的標(biāo)記和值,必須是英文 Cookie cookie = new Cookie(flag,value);
-
響應(yīng)信息中加上Cookie
response.addCookie(cookie);
-
再次請(qǐng)求到來(lái)的時(shí)候檢查Cookie
//獲取request中所有的cookie信息 Cookies[] cookies = request.getCookies(); //遍歷檢查cookie if(cookies != null){ for(Cookie c : cookies){ String name = c.getName(); String value = c.getValue(); } }
注
-
cookie是有有效期的之剧,一般會(huì)在瀏覽器關(guān)閉的時(shí)候自動(dòng)清空
設(shè)置cookie有效期郭卫,調(diào)用方法setMaxAge(60)
cookie中的數(shù)據(jù)是不安全的,畢竟保存到本地背稼,可以顯式查看
Session
session是服務(wù)器為每一個(gè)瀏覽器建立的私人存儲(chǔ)空間贰军,其中(session作用域)可以存儲(chǔ)瀏覽器的屬性和一些配置信息,當(dāng)瀏覽器拿到session之后(沒(méi)有更換持有的session)蟹肘,在不同的Servlet之間跳轉(zhuǎn)的時(shí)候词疼,可以隨時(shí)取出放在作用域中的數(shù)據(jù)俯树。
上圖是session的基本實(shí)現(xiàn)原理,我們可以看到session是通過(guò)cookie來(lái)實(shí)現(xiàn)的贰盗,具體的步驟是這樣的:
瀏覽器把登錄信息放入HTTP請(qǐng)求報(bào)文的實(shí)體部分聘萨,通常是以POST 方法把請(qǐng)求發(fā)送給服務(wù)器
服務(wù)器創(chuàng)建session,并將用戶(hù)信息和session進(jìn)行綁定記錄在服務(wù)器童太,然后把處理好的session放入cookie中隨著響應(yīng)發(fā)給瀏覽器。
瀏覽器收到服務(wù)器響應(yīng)的帶有session信息的cookie時(shí)胸完,會(huì)將cookie存在本地书释,下次請(qǐng)求的時(shí)候自動(dòng)攜帶,服務(wù)器會(huì)通過(guò)接受其中的session對(duì)用戶(hù)進(jìn)行驗(yàn)證赊窥。
GET和POST的區(qū)別
從本質(zhì)上來(lái)講爆惧,區(qū)別只有兩點(diǎn):
- 參數(shù)的位置不同,GET是url后拼接參數(shù)锨能,POST是請(qǐng)求報(bào)文主體傳遞參數(shù)
- 傳遞數(shù)據(jù)的過(guò)程不一樣扯再,GET產(chǎn)生一個(gè)TCP數(shù)據(jù)包,POST產(chǎn)生兩個(gè)TCP數(shù)據(jù)包址遇。
那么我們常提到的區(qū)別是從哪里來(lái)的呢熄阻?
從解釋上面的區(qū)別開(kāi)始。
早期的HTTP協(xié)議只有GET方法倔约。根據(jù)HTTP協(xié)議秃殉,服務(wù)器接收到GET請(qǐng)求后,會(huì)將特定資源響應(yīng)給瀏覽器浸剩,GET方法是通過(guò)改寫(xiě)URL實(shí)現(xiàn)的钾军,在URL后面加上要傳遞的數(shù)據(jù)(格式是URL?key=value&key=value......)绢要。所以在使用GET方法請(qǐng)求資源的時(shí)候吏恭,請(qǐng)求往往是沒(méi)有主體的。
那么問(wèn)題來(lái)了重罪,什么是主體樱哼?
所謂主體就是request的報(bào)文主體,我們知道HTTP請(qǐng)求的報(bào)文格式(響應(yīng)雷同)是這樣的
l 起始行
l 頭信息
l 主體
GET方法的請(qǐng)求報(bào)文信息一般只有起始行和頭信息蛆封,如下:
<!-- 拼接后的url -->
http://localhost:8801/zhibaxm/servlet/LoginAction?username=%E5%BE%90%E9%80%9A%E8%BE%BE&pwd=123
<!-- 請(qǐng)求頭 -->
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Host:localhost:8801
Origin:http://localhost:8801
Referer:http://localhost:8801/zhibaxm/login.html
User-Agent:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36
而對(duì)于POST方法唇礁,URL不再被改寫(xiě),相關(guān)的表單數(shù)據(jù)會(huì)位于http請(qǐng)求的主體惨篱。如下:
<!-- url -->
http://localhost:8801/zhibaxm/servlet/LoginAction
<!-- 請(qǐng)求頭 -->
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Host:localhost:8801
Origin:http://localhost:8801
Referer:http://localhost:8801/zhibaxm/login.html
User-Agent:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36
<!-- POST的主體信息 -->
username=%E5%BE%90%E9%80%9A%E8%BE%BE&pwd=123
我們知道每一個(gè)請(qǐng)求都是可以有三部分的:起始行盏筐,請(qǐng)求頭,主體砸讳,也就是說(shuō)琢融,GET和POST的區(qū)別不是語(yǔ)法上的界牡,而是規(guī)范上的,簡(jiǎn)單的說(shuō)就是漾抬,你在使用POST的時(shí)候如果把參數(shù)寫(xiě)在url上也是沒(méi)有問(wèn)題的宿亡。
但是,我們?cè)谑褂弥写_實(shí)有很多的不同纳令,咋回事兒呢挽荠?這些區(qū)別并不是語(yǔ)法本身的不同,而是由于瀏覽器和服務(wù)器差異造成的使用上的區(qū)別平绩,例如:大部分瀏覽器的url長(zhǎng)度限制在2K個(gè)字節(jié)圈匆,而大部分服務(wù)器最多處理64K大小的url。在使用GET方法時(shí)捏雌,如果你在報(bào)文主體寫(xiě)入了數(shù)據(jù)跃赚,那么不同服務(wù)器的處理方式也是不同的,有些服務(wù)器會(huì)接受有些不會(huì)性湿。
造成區(qū)別的原因更多不是來(lái)自語(yǔ)法本身纬傲,而是不同瀏覽器服務(wù)器的限制。
扯完了這些肤频,補(bǔ)充一下應(yīng)用上區(qū)別叹括,畢竟遇到面試的時(shí)候,使用上的差別也是不能夠忘記的着裹,使用上的區(qū)別如下:
GET | POST |
---|---|
只可以接受字符串 | 沒(méi)有限制 |
不安全 | 相對(duì)安全 |
有長(zhǎng)度限制 | 沒(méi)有長(zhǎng)度限制 |
... | ... |