Servlet
IDEA的安裝與使用
? IDEA 全稱(chēng) IntelliJ IDEA,由JetBrains公司開(kāi)發(fā),是java編程語(yǔ)言開(kāi)發(fā)的集成環(huán)境。在業(yè)界被公認(rèn)為最好的java開(kāi)發(fā)工具,尤其在智能代碼助手、代碼自動(dòng)提示、重構(gòu)、J2EE支持、各類(lèi)版本工具(git、svn等)、JUnit、CVS整合、代碼分析、 創(chuàng)新的GUI設(shè)計(jì)等方面的功能可以說(shuō)是超常的。
智能選取
豐富的導(dǎo)航模式
歷史記錄功能
編碼輔助
靈活的排版功能
代碼檢查
完美的自動(dòng)代碼完成
版本控制的支持
…
在瀏覽器中IntelliJ IDEA百度一下,打開(kāi)如下官網(wǎng)
進(jìn)入官網(wǎng),單擊DOWNLOAD
選擇指定版本,點(diǎn)擊DOWNLOAD
點(diǎn)擊保存,進(jìn)行下載
下載之后的文件
雙擊運(yùn)行安裝程序,點(diǎn)擊 “Next” 下一步
修改安裝路徑(也可使用默認(rèn)路徑),點(diǎn)擊 "Next”
根據(jù)自己電腦的操作系統(tǒng),來(lái)進(jìn)行相應(yīng)的選擇
默認(rèn)即可,直接選擇 “Install” ,進(jìn)行安裝
安裝成功,可選擇運(yùn)行IDEA,點(diǎn)擊 “Finish” 完成安裝
若之前安裝過(guò)其他版本的IDEA,會(huì)提示是否導(dǎo)入原來(lái)的配置(選擇不導(dǎo)入即可)
選擇自己喜歡的主題,然后一直選擇 “Next”。
選擇 "Activation code"方式,輸入激活碼,點(diǎn)擊 “OK”
出現(xiàn)如下畫(huà)面,則表示激活成功
點(diǎn)擊 “Create New Project”
添加新的 JDK 版本 (idea默認(rèn)使用自帶的版本)
選擇JDK版本,然后 “Next”
選擇 “Next”
設(shè)置項(xiàng)目名稱(chēng)及工作空間
項(xiàng)目目錄結(jié)構(gòu)及提示信息 (提示信息可選擇"Close")
點(diǎn)擊 “src” —> “new” —> “package”,創(chuàng)建一個(gè)文件包
設(shè)置包名,與Eclipse的包類(lèi)似
在包下面創(chuàng)建 Java 類(lèi)文件,點(diǎn)擊包名 —> “New” —> “Java Class”
選擇類(lèi)型,并設(shè)置類(lèi)的名稱(chēng)
在類(lèi)中寫(xiě)一個(gè)main方法
方式一:代碼左側(cè)的綠色三角符號(hào)
方式二:在類(lèi)中右鍵,選擇Run或Debug
方式三:點(diǎn)擊最上方菜單欄的Run
出現(xiàn)以下彈框,點(diǎn)擊要運(yùn)行的文件名,這里是 Hello
運(yùn)行結(jié)果
? 使用IDEA時(shí),可以對(duì)它進(jìn)行一些簡(jiǎn)單的設(shè)置,通過(guò)設(shè)置用戶(hù)的偏好設(shè)置,可提高使用者的體驗(yàn)感。
選擇右下角的 “Configure”,選擇"Settings" (或在IDEA中,選擇左上角的 “File”,選擇"Settings")
進(jìn)入設(shè)置頁(yè)面
在Settings窗口中,點(diǎn)擊 “Editor” —> “Color Scheme”
選擇 “Color Scheme Font”,設(shè)置字體風(fēng)格和字體大小,設(shè)置完之后選擇 “Apply” 應(yīng)用
設(shè)置中文字體
Idea更新2019.2后
中文字體的默認(rèn)效果
修改字體之后的效果
解決方案: “Editor” —> “Font” —> “Fallback font”, 選擇 SimHei
選擇 “Appearance & Behavior”,選擇 “System Settings”
選擇 “Editor”,選擇 “File Encoding”,設(shè)置編碼為 “UTF-8”
可以通過(guò)按住 “Ctrl”,滾動(dòng)鼠標(biāo)滾輪改變字體大小。
選擇 “Editor”,選擇 “General”
選擇 “Build,Execution,Deployment”,選擇 “Compiler”
快捷鍵作用
Alt+Insert生成代碼(如get, set方法,構(gòu)造函數(shù)等)
Alt+↑/ ↓在方法間快速定位
Alt+【F3】查找相同文本,并高亮顯示
Ctrl+B快速打開(kāi)光標(biāo)處的類(lèi)或方法
Ctrl+J自動(dòng)代碼(main方法)
Ctrl+N查找類(lèi)
Ctrl+Y刪除行
Ctrl+D復(fù)制行
Ctrl+O重寫(xiě)方法
Ctrl+E最近打開(kāi)的文件
Ctrl+F查找文本
Ctrl+R替換文本
Ctrl+P方法參數(shù)提示
Ctrl+/單行注釋//
Ctrl+Shift+/多行注釋/* */
Ctrl+Shift+N查找文件
Ctrl+Alt+L格式化代碼
Ctrl+Shift+↑/ ↓代碼向上/向下移動(dòng)
Shift+F6重構(gòu)-重命名
? HTTP 協(xié)議(Hypertext Transfer Protocol, 超文本傳輸協(xié)議),是一個(gè)客戶(hù)端請(qǐng)求和響應(yīng)的標(biāo)準(zhǔn)協(xié)議,這個(gè)協(xié)議詳細(xì)規(guī)定了瀏覽器和萬(wàn)維網(wǎng)服務(wù)器之間互相通信的規(guī)則。用戶(hù)輸入地址和端口號(hào)之后就可以從服務(wù)器上取得所需要的網(wǎng)頁(yè)信息。
? 通信規(guī)則規(guī)定了客戶(hù)端發(fā)送給服務(wù)器的內(nèi)容格式,也規(guī)定了服務(wù)器發(fā)送給客戶(hù)端的內(nèi)容格式。客戶(hù)端發(fā)送給服務(wù)器的格式叫"請(qǐng)求協(xié)議";服務(wù)器發(fā)送給客戶(hù)端的格式叫"響應(yīng)協(xié)議"。
在瀏覽器中 F12可查看
? 服務(wù)器端資源需要通過(guò)瀏覽器進(jìn)行,此時(shí)由瀏覽器將我們給出的請(qǐng)求解析為滿(mǎn)足 HTTP 協(xié)議的格式并發(fā)出。我們發(fā)出的請(qǐng)求格式需要按照瀏覽器規(guī)定的格式來(lái)書(shū)寫(xiě),在瀏覽器中書(shū)寫(xiě)格式如下:
? 當(dāng)瀏覽器獲取到信息以后,按照特定格式解析并發(fā)送即可。接收到服務(wù)器端給出的響應(yīng)時(shí),也按照 HTTP 協(xié)議進(jìn)行解析獲取到各個(gè)數(shù)據(jù),最后按照特定格式展示給用戶(hù)。
支持客戶(hù)/服務(wù)器模式。
簡(jiǎn)單快速:客戶(hù)向服務(wù)器請(qǐng)求服務(wù)時(shí),只需傳送請(qǐng)求方法和路徑。請(qǐng)求方法常用的 有 GET、POST。每種方法規(guī)定了客戶(hù)與服務(wù)器聯(lián)系的類(lèi)型不同。由于 HTTP 協(xié)議簡(jiǎn)單,使得HTTP服務(wù)器的程序規(guī)模小,因而通信速度很快。
靈活:HTTP 允許傳輸任意類(lèi)型的數(shù)據(jù)對(duì)象。傳輸?shù)念?lèi)型由Content-Type加以標(biāo)記。
無(wú)連接:無(wú)連接是表示每次連接只處理一個(gè)請(qǐng)求。服務(wù)器處理完客戶(hù)的請(qǐng)求,并收到客戶(hù)的應(yīng)答后,即斷開(kāi)連接。采用這種方式可以節(jié)省傳輸時(shí)間。
HTTP1.1 版本后支持可持續(xù)連接。通過(guò)這種連接,就有可能在建立一個(gè) TCP 連接后,發(fā)送請(qǐng)求并得到回應(yīng),然后發(fā)送更多的請(qǐng)求并得到更多的回應(yīng).通過(guò)把建立和釋放 TCP 連接的開(kāi)銷(xiāo)分?jǐn)偟蕉鄠€(gè)請(qǐng)求上,則對(duì)于每個(gè)請(qǐng)求而言,由于 TCP 而造成的相對(duì)開(kāi)銷(xiāo)被大大地降低了。而且, 還可以發(fā)送流水線(xiàn)請(qǐng)求,也就是說(shuō)在發(fā)送請(qǐng)求 1 之后的回應(yīng)到來(lái)之前就可以發(fā)送請(qǐng)求 2.也可以認(rèn)為,一次連接發(fā)送多個(gè)請(qǐng)求,由客戶(hù)機(jī)確認(rèn)是否關(guān)閉連接,而服務(wù)器會(huì)認(rèn)為這些請(qǐng)求分別來(lái)自不同的客戶(hù)端。
無(wú)狀態(tài):HTTP 協(xié)議是無(wú)狀態(tài)協(xié)議。無(wú)狀態(tài)是指協(xié)議對(duì)于事務(wù)處理沒(méi)有記憶能力。缺少狀態(tài)意味著如果后續(xù)處理需要前面的信息,則它必須重傳,這樣可能導(dǎo)致每次連接傳送 的數(shù)據(jù)量增大。另一方面,在服務(wù)器不需要先前信息時(shí)它的應(yīng)答就較快。
? HTTP(超文本傳輸協(xié)議)是一個(gè)基于請(qǐng)求與響應(yīng)模式的、應(yīng)用層的協(xié)議,?;?TCP 的連接方式,絕大多數(shù)的 Web 開(kāi)發(fā),都是構(gòu)建在 HTTP 協(xié)議之上的 Web 應(yīng)用。
? HTTP URL (URL 是一種特殊類(lèi)型的 URI,包含了用于查找某個(gè)資源的足夠的信息)的格式 如下:
http://host[:port]/[abc_path]
http://IP(主機(jī)名/域名):端口/訪(fǎng)問(wèn)的資源路徑
http 表示要通過(guò) HTTP 協(xié)議來(lái)定位網(wǎng)絡(luò)資源;
host 表示合法的 Internet 主機(jī)域名或 者 IP 地址;
port 指定一個(gè)端口號(hào),為空則使用缺省端口 80;
abs_path 指定請(qǐng)求資源的 URI; 如果 URL 中沒(méi)有給出 abs_path,那么當(dāng)它作為請(qǐng)求 URI 時(shí),必須以“/”的形式給出,通常 這個(gè)工作瀏覽器自動(dòng)幫我們完成。
? HTTP 請(qǐng)求由三部分組成,分別是:請(qǐng)求行、請(qǐng)求頭、請(qǐng)求正文。
? 通過(guò)chrome瀏覽器, F12 —> Network查看。
Get 請(qǐng)求(沒(méi)有請(qǐng)求體)
Post 請(qǐng)求
格式
請(qǐng)求行
請(qǐng)求頭1
請(qǐng)求頭2
…
請(qǐng)求空行
請(qǐng)求體
請(qǐng)求行以一個(gè)方法符號(hào)開(kāi)頭,以空格分開(kāi),后面跟著請(qǐng)求的 URI 和協(xié)議的版本。
? 格式如下:Method Request-URI HTTP-Version CRLF
? Method 表示請(qǐng)求方法;
? Request-URI 是一個(gè)統(tǒng)一資源標(biāo)識(shí)符;
? HTTP-Version 表示請(qǐng) 求的 HTTP 協(xié)議版本;
? CRLF 表示回車(chē)和換行;
? 在接收和解釋請(qǐng)求消息后,服務(wù)器返回一個(gè) HTTP 響應(yīng)消息。HTTP 響應(yīng)也是由三個(gè)部分組成,分別是:狀態(tài)行、消息報(bào)頭、響應(yīng)正文。
格式
狀態(tài)行
響應(yīng)頭1
響應(yīng)頭2
…
響應(yīng)空行
響應(yīng)體
? HTTP 消息由客戶(hù)端到服務(wù)器的請(qǐng)求和服務(wù)器到客戶(hù)端的響應(yīng)組成。請(qǐng)求消息和響應(yīng)消息都是由開(kāi)始行(對(duì)于請(qǐng)求消息,開(kāi)始行就是請(qǐng)求行,對(duì)于響應(yīng)消息,開(kāi)始行就是狀態(tài)行), 消息報(bào)頭(可選),空行(只有 CRLF 的行),消息正文(可選)組成。
? 每一個(gè)報(bào)頭域都是由?名字+":"+空格+值?組成,消息報(bào)頭域的名字是大小寫(xiě)無(wú)關(guān)的。
請(qǐng)求頭
? 請(qǐng)求報(bào)頭允許客戶(hù)端向服務(wù)器端傳遞請(qǐng)求的附加信息以及客戶(hù)端自身的信息。
Referer:該請(qǐng)求頭指明請(qǐng)求從哪里來(lái) 。
? 如果是地址欄中輸入地址訪(fǎng)問(wèn)的都沒(méi)有該請(qǐng)求頭 地址欄輸入地址,通過(guò)請(qǐng)求可以看到,此時(shí)多了一個(gè) Referer 的請(qǐng)求頭,并且后面的值 為該請(qǐng)求從哪里發(fā)出。比如:百度競(jìng)價(jià),只能從百度來(lái)的才有效果,否則不算;通常用來(lái)做統(tǒng)計(jì)工作、 防盜鏈。
響應(yīng)頭
? 響應(yīng)報(bào)頭允許服務(wù)器傳遞不能放在狀態(tài)行中的附加響應(yīng)信息,以及關(guān)于服務(wù)器的信息和 對(duì) Request-URI 所標(biāo)識(shí)的資源進(jìn)行下一步訪(fǎng)問(wèn)的信息。
Location:Location響應(yīng)報(bào)頭域用于重定向接受者到一個(gè)新的位置。
? Location響應(yīng)報(bào)頭域,常用在更換域名的時(shí)候。
response.sendRedirect("http://www.baidu.com");
Refresh:自動(dòng)跳轉(zhuǎn)(單位是秒),可以在頁(yè)面通過(guò)meta標(biāo)簽實(shí)現(xiàn),也可在后臺(tái)實(shí)現(xiàn)。
<metahttp-equiv="refresh"content="3;url=http://www.baidu.com">
? Tomcat 是一個(gè)符合 JavaEE WEB 標(biāo)準(zhǔn)的最小的?WEB 容器,所有的 JSP 程序一定要有 WEB 容器的支持才能運(yùn)行,而且在給定的 WEB 容器里面都會(huì)支持事務(wù)處理操作。
? Tomcat 是由 Apache 提供的(www.apache.org)提供的可以用安裝版和解壓版,安裝版可以在服務(wù)中出現(xiàn)一個(gè) Tomcat 的服務(wù),免安裝沒(méi)有,開(kāi)發(fā)中使用免安裝版。 Tomcat 簡(jiǎn)單的說(shuō)就是一個(gè)運(yùn)行 Java 的網(wǎng)絡(luò)服務(wù)器,底層是 Socket 的一個(gè)程序,它也是 JSP 和 Servlet 的一個(gè)容器。 Tomcat 是 Apache 軟件基金會(huì)(Apache Software Foundation)的 Jakarta 項(xiàng)目中的一個(gè)核心項(xiàng)目,由 Apache、Sun和其他一些公司及個(gè)人共同開(kāi)發(fā)而成。
? 由于有了 Sun 的參與和支持,最新的 Servlet 和 JSP 規(guī)范總是能在 Tomcat 中得到體現(xiàn)。因?yàn)?Tomcat 技術(shù)先進(jìn)、性能穩(wěn)定,而且免費(fèi),因而深受 Java 愛(ài)好者的喜愛(ài)并得到了部分軟件開(kāi)發(fā)商的認(rèn)可,成為目前比較流行的?Web 應(yīng)用服務(wù)器。
? Tomcat 服務(wù)器是一個(gè)免費(fèi)的開(kāi)放源代碼的 Web 應(yīng)用服務(wù)器,屬于輕量級(jí)應(yīng)用服務(wù)器, 在中小型系統(tǒng)和并發(fā)訪(fǎng)問(wèn)用戶(hù)不是很多的場(chǎng)合下被普遍使用,是開(kāi)發(fā)和調(diào)試 JSP 程序的首選。 對(duì)于一個(gè)初學(xué)者來(lái)說(shuō),可以這樣認(rèn)為,當(dāng)在一臺(tái)機(jī)器上配置好 Apache 服務(wù)器,可利用它響應(yīng) HTML(標(biāo)準(zhǔn)通用標(biāo)記語(yǔ)言下的一個(gè)應(yīng)用)頁(yè)面的訪(fǎng)問(wèn)請(qǐng)求。實(shí)際上 Tomcat 部分是 Apache 服務(wù)器的擴(kuò)展,但它是獨(dú)立運(yùn)行的,所以當(dāng)你運(yùn)行 tomcat 時(shí),它實(shí)際上作為一個(gè)與 Apache 獨(dú)立的進(jìn)程單獨(dú)運(yùn)行的。
? 當(dāng)配置正確時(shí),Apache 為 HTML 頁(yè)面服務(wù),而 Tomcat 實(shí)際上是在運(yùn)行 JSP 頁(yè)面和 Servlet。另外,Tomcat 和 IIS 等 Web 服務(wù)器一樣,具有處理 HTML 頁(yè)面的功能,另外它還是 一個(gè) Servlet 和 JSP 容器,獨(dú)立的 Servlet 容器是 Tomcat 的默認(rèn)模式。不過(guò),Tomcat 處理靜態(tài) HTML 的能力不如 Apache 服務(wù)器。目前 Tomcat 最新版本為 9.0。
? 運(yùn)行 Tomcat 需要 JDK 的支持【Tomcat 會(huì)通過(guò)?JAVA_HOME?找到所需要的 JDK】。 安裝就是解壓縮過(guò)程。啟動(dòng) Tomcat,能訪(fǎng)問(wèn)則算安裝好了
解壓Tomcat8的壓縮包
解壓后目錄結(jié)構(gòu)
啟動(dòng) Tomcat (在 tomcat 的安裝目錄下的 bin 目錄 使用命令行啟動(dòng) tomcat)
方式一:雙擊腳本文件啟動(dòng)
方式二:使用腳本命令啟動(dòng)
服務(wù)器啟動(dòng)成功
注:
Tomcat默認(rèn)占用端口8080。(注意端口沖突問(wèn)題)
如果需要使用服務(wù)器,啟動(dòng)成功后,該啟動(dòng)窗口不要關(guān)閉。
打開(kāi)瀏覽器,輸入http://localhost:8080/ 訪(fǎng)問(wèn)
調(diào)用 shutdown 命令關(guān)閉Tomcat服務(wù)器
bin:?jiǎn)?dòng)和關(guān)閉 tomcat 的 bat 文件
conf:配置文件server.xml 該文件用于配置 server 相關(guān)的信息,比如 tomcat 啟動(dòng)的端口號(hào),配置主機(jī)(Host) ;web.xml 文件配置與 web 應(yīng)用(web 應(yīng)用相當(dāng)于一個(gè) web站點(diǎn));tomcat-user.xml 配置用戶(hù)名密碼和相關(guān)權(quán)限
lib:該目錄放置運(yùn)行 tomcat 運(yùn)行需要的 jar 包
logs:存放日志,當(dāng)我們需要查看日志的時(shí)候,可以查詢(xún)信息
webapps:放置我們的 web 應(yīng)用
work 工作目錄:該目錄用于存放 jsp 被訪(fǎng)問(wèn)后生成對(duì)應(yīng)的 server 文件和.class 文件
選擇 “Appliction Servers”,點(diǎn)擊右側(cè)的 “+” 號(hào),選擇 “Tomcat Server”
設(shè)置 Tomcat 的安裝目錄(/idea_28.png)]
設(shè)置好之后
配置Tomcat服務(wù)器完成
? Servlet 是 Server 與 Applet 的縮寫(xiě),是服務(wù)端小程序的意思。使用 Java 語(yǔ)言編寫(xiě)的服務(wù)器端程序,可以像生成動(dòng)態(tài)的 WEB 頁(yè),Servlet 主要運(yùn)行在服務(wù)器端,并由服務(wù)器調(diào)用執(zhí)行, 是一種按照 Servlet 標(biāo)準(zhǔn)來(lái)開(kāi)發(fā)的類(lèi)。 是 SUN 公司提供的一門(mén)用于開(kāi)發(fā)動(dòng)態(tài) Web 資源的技術(shù)。(言外之意:要實(shí)現(xiàn) web 開(kāi)發(fā),需要實(shí)現(xiàn) Servlet 標(biāo)準(zhǔn))
? Servlet 本質(zhì)上也是 Java 類(lèi),但要遵循 Servlet 規(guī)范進(jìn)行編寫(xiě),沒(méi)有 main()方法,它的創(chuàng)建、使用、銷(xiāo)毀都由 Servlet 容器進(jìn)行管理(如 Tomcat)。(言外之意:寫(xiě)自己的類(lèi),不用寫(xiě) main 方法,別人自動(dòng)調(diào)用)
? Servlet 是和 HTTP 協(xié)議是緊密聯(lián)系的,其可以處理 HTTP 協(xié)議相關(guān)的所有內(nèi)容。這也是 Servlet 應(yīng)用廣泛的原因之一。
? 提供了 Servlet 功能的服務(wù)器,叫做 Servlet 容器,其常見(jiàn)容器有很多,如 Tomcat, Jetty, WebLogic Server, WebSphere, JBoss 等等。
選擇 “File” —> “New” —> “Project”
設(shè)置項(xiàng)目的相關(guān)信息,選擇 “Next”
設(shè)置項(xiàng)目名稱(chēng)及工作空間
web項(xiàng)目目錄結(jié)構(gòu)如下
點(diǎn)擊 “src” —> “new” —> “package”,創(chuàng)建一個(gè)文件包
在包下面創(chuàng)建 Java 類(lèi)文件,點(diǎn)擊包名 —> “New” —> “Java Class”
創(chuàng)建一個(gè)普通的 Java 類(lèi)
如下
packagecom.xxxx.servlet;publicclassServlet01{}
? 實(shí)現(xiàn) Servlet 規(guī)范,即繼承 HttpServlet 類(lèi),并到如響應(yīng)的包,該類(lèi)中已經(jīng)完成了通信的規(guī)則,我們只需要進(jìn)行業(yè)務(wù)的實(shí)現(xiàn)即可。
packagecom.xxxx.servlet;importjavax.servlet.http.HttpServlet;publicclassServlet01extendsHttpServlet{}
? 滿(mǎn)足 Servlet 規(guī)范只是讓我們的類(lèi)能夠滿(mǎn)足接收請(qǐng)求的要求,接收到請(qǐng)求后需要對(duì)請(qǐng)求進(jìn)行分析,以及進(jìn)行業(yè)務(wù)邏輯處理,計(jì)算出結(jié)果,則需要添加代碼,在規(guī)范中有一個(gè)叫做 service的方法,專(zhuān)門(mén)用來(lái)做請(qǐng)求處理的操作,業(yè)務(wù)代碼則可以寫(xiě)在該方法中。
packagecom.xxxx.servlet;importjavax.servlet.ServletException;importjavax.servlet.http.HttpServlet;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.io.IOException;publicclassServlet01extendsHttpServlet{@Overrideprotectedvoidservice(HttpServletRequest req,HttpServletResponse resp)throwsServletException,IOException{System.out.println("Hello Servlet!");resp.getWriter().write("Hello World");}}
? 在完成好了一切代碼的編寫(xiě)后,還需要向服務(wù)器說(shuō)明,特定請(qǐng)求對(duì)應(yīng)特定資源。
? 開(kāi)發(fā)servlet項(xiàng)目,使用@WebServlet將一個(gè)繼承于javax.servlet.http.HttpServlet 的類(lèi)定義為Servlet組件。在Servlet3.0中 , 可以使用@WebServlet注解將一個(gè)繼承于javax.servlet.http.HttpServlet的類(lèi)標(biāo)注為可以處理用戶(hù)請(qǐng)求的 Servlet。
packagecom.xxxx.servlet;importjavax.servlet.ServletException;importjavax.servlet.annotation.WebServlet;importjavax.servlet.http.HttpServlet;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.io.IOException;@WebServlet("/ser01")publicclassServlet01extendsHttpServlet{@Overrideprotectedvoidservice(HttpServletRequest req,HttpServletResponse resp)throwsServletException,IOException{System.out.println("Hello Servlet!");resp.getWriter().write("Hello World");}}
用注解配置 Servlet
@WebServlet(name="Servlet01",value="/ser01")
@WebServlet(name="Servlet01",urlPatterns="/ser01")
也可以配置多個(gè)訪(fǎng)問(wèn)路徑
@WebServlet(name="Servlet01",value={"/ser01",'/ser001'})
@WebServlet(name="Servlet01",urlPatterns={"/ser01",'/ser001'})
發(fā)布項(xiàng)目并啟動(dòng)服務(wù)
? 到此,需要編寫(xiě)和配置的地方已經(jīng)完成,項(xiàng)目已經(jīng)完整了,但是如果需要外界能夠訪(fǎng)問(wèn), 還需要將項(xiàng)目發(fā)布到服務(wù)器上并運(yùn)行服務(wù)器。
設(shè)置項(xiàng)目的站點(diǎn)名(項(xiàng)目對(duì)外訪(fǎng)問(wèn)路徑)
設(shè)置項(xiàng)目的Tomcat配置
啟動(dòng)服務(wù)器
? 在項(xiàng)目正確發(fā)布到服務(wù)器上之后,用戶(hù)即可通過(guò)瀏覽器訪(fǎng)問(wèn)該項(xiàng)目中的資源。注意 url 的 格式正確,tomcat 的端口為 8080。
? 瀏覽器訪(fǎng)問(wèn)地址:http://localhost:8080/s01/ser01
頁(yè)面效果
后臺(tái)結(jié)果
到這里我們的第一個(gè) Servlet 就實(shí)現(xiàn)了!
通過(guò)請(qǐng)求頭獲知瀏覽器訪(fǎng)問(wèn)的是哪個(gè)主機(jī)
再通過(guò)請(qǐng)求行獲取訪(fǎng)問(wèn)的是哪個(gè)一個(gè)web應(yīng)用
再通過(guò)請(qǐng)求行中的請(qǐng)求路徑獲知訪(fǎng)問(wèn)的是哪個(gè)資源
通過(guò)獲取的資源路徑在配置中匹配到真實(shí)的路徑,
服務(wù)器會(huì)創(chuàng)建servlet對(duì)象,(如果是第一次訪(fǎng)問(wèn)時(shí),創(chuàng)建servlet實(shí)例,并調(diào)用init方法進(jìn)行初始化操作)
調(diào)用service(request, response)方法來(lái)處理請(qǐng)求和響應(yīng)的操作
調(diào)用service完畢后返回服務(wù)器 由服務(wù)器講response緩沖區(qū)的數(shù)據(jù)取出,以http響應(yīng)的格式發(fā)送給瀏覽器
? Servlet沒(méi)有 main()方法,不能獨(dú)立運(yùn)行,它的運(yùn)行完全由 Servlet 引擎來(lái)控制和調(diào)度。 所謂生命周期,指的是 servlet 容器何時(shí)創(chuàng)建 servlet 實(shí)例、何時(shí)調(diào)用其方法進(jìn)行請(qǐng)求的處理、 何時(shí)并銷(xiāo)毀其實(shí)例的整個(gè)過(guò)程。
實(shí)例和初始化時(shí)機(jī)
當(dāng)請(qǐng)求到達(dá)容器時(shí),容器查找該 servlet 對(duì)象是否存在,如果不存在,則會(huì)創(chuàng)建實(shí)例并進(jìn)行初始化。
就緒/調(diào)用/服務(wù)階段
有請(qǐng)求到達(dá)容器,容器調(diào)用 servlet 對(duì)象的 service()方法,處理請(qǐng)求的方法在整個(gè)生命周期中可以被多次調(diào)用; HttpServlet 的 service()方法,會(huì)依據(jù)請(qǐng)求方式來(lái)調(diào)用 doGet()或者 doPost()方法。但是, 這兩個(gè) do 方法默認(rèn)情況下,會(huì)拋出異常,需要子類(lèi)去 override。
銷(xiāo)毀時(shí)機(jī)
當(dāng)容器關(guān)閉時(shí)(應(yīng)用程序停止時(shí)),會(huì)將程序中的 Servlet 實(shí)例進(jìn)行銷(xiāo)毀。
上述的生命周期可以通過(guò) Servlet 中的生命周期方法來(lái)觀察。在 Servlet 中有三個(gè)生命周 期方法,不由用戶(hù)手動(dòng)調(diào)用,而是在特定的時(shí)機(jī)有容器自動(dòng)調(diào)用,觀察這三個(gè)生命周期方法 即可觀察到 Servlet 的生命周期。
init?方法,在 Servlet 實(shí)例創(chuàng)建之后執(zhí)行(證明該 Servlet 有實(shí)例創(chuàng)建了)
publicvoidinit(ServletConfig config)throwsServletException{System.out.println("實(shí)例創(chuàng)建了...");}
service?方法,每次有請(qǐng)求到達(dá)某個(gè) Servlet 方法時(shí)執(zhí)行,用來(lái)處理請(qǐng)求(證明該Servlet 進(jìn)行服務(wù)了)
protectedvoidservice(HttpServletRequest req,HttpServletResponse resp)throwsServletException,IOException{System.out.println("服務(wù)調(diào)用了...");}
destroy?方法,Servlet 實(shí)例銷(xiāo)毀時(shí)執(zhí)行(證明該 Servlet 的實(shí)例被銷(xiāo)毀了)
publicvoiddestroy(){System.out.println("實(shí)例銷(xiāo)毀了...");}
? Servlet 的生命周期,簡(jiǎn)單的概括這就分為四步:servlet 類(lèi)加載–>實(shí)例化–>服務(wù)–>銷(xiāo)毀。
? 下面我們描述一下 Tomcat 與 Servlet 是如何工作的,看看下面的時(shí)序圖:
? 1. Web Client 向 Servlet 容器(Tomcat)發(fā)出 Http 請(qǐng)求
? 2. Servlet 容器接收 Web Client 的請(qǐng)求
? 3. Servlet 容器創(chuàng)建一個(gè) HttpServletRequest 對(duì)象,將 Web Client 請(qǐng)求的信息封裝到這個(gè)對(duì)象 中
? 4. Servlet 容器創(chuàng)建一個(gè) HttpServletResponse 對(duì)象
? 5. Servlet 容器調(diào)HttpServlet 對(duì)象service 方法,把 Request 與 Response 作為參數(shù),傳給 HttpServlet
? 6. HttpServlet 調(diào)用 HttpServletRequest 對(duì)象的有關(guān)方法,獲取 Http 請(qǐng)求信息
? 7. HttpServlet 調(diào)用 HttpServletResponse 對(duì)象的有關(guān)方法,生成響應(yīng)數(shù)據(jù)
? 8. Servlet 容器把 HttpServlet 的響應(yīng)結(jié)果傳給 Web Client
? HttpServletRequest 對(duì)象:主要作用是用來(lái)接收客戶(hù)端發(fā)送過(guò)來(lái)的請(qǐng)求信息,例如:請(qǐng)求的參數(shù),發(fā)送的頭信息等都屬于客戶(hù)端發(fā)來(lái)的信息,service()方法中形參接收的是 HttpServletRequest 接口的實(shí)例化對(duì)象,表示該對(duì)象主要應(yīng)用在 HTTP 協(xié)議上,該對(duì)象是由 Tomcat 封裝好傳遞過(guò)來(lái)。
? HttpServletRequest 是 ServletRequest 的子接口,ServletRequest 只有一個(gè)子接口,就是 HttpServletRequest。既然只有一個(gè)子接口為什么不將兩個(gè)接口合并為一個(gè)?
? 從長(zhǎng)遠(yuǎn)上講:現(xiàn)在主要用的協(xié)議是 HTTP 協(xié)議,但以后可能出現(xiàn)更多新的協(xié)議。若以后想要支持這種新協(xié)議,只需要直接繼承 ServletRequest 接口就行了。
? 在 HttpServletRequest 接口中,定義的方法很多,但都是圍繞接收客戶(hù)端參數(shù)的。但是怎么拿到該對(duì)象呢?不需要,直接在 Service 方法中由容器傳入過(guò)來(lái),而我們需要做的就是取出對(duì)象中的數(shù)據(jù),進(jìn)行分析、處理。
方法
示例
// 獲取客戶(hù)端請(qǐng)求的完整URL (從http開(kāi)始,到?前面結(jié)束)String url=request.getRequestURL().toString();System.out.println("獲取客戶(hù)端請(qǐng)求的完整URL:"+url);//? 獲取客戶(hù)端請(qǐng)求的部分URL (從站點(diǎn)名開(kāi)始,到?前面結(jié)束)String uri=request.getRequestURI();System.out.println("獲取客戶(hù)端請(qǐng)求的部分URL:"+uri);// 獲取請(qǐng)求行中的參數(shù)部分String queryString=request.getQueryString();System.out.println("獲取請(qǐng)求行中的參數(shù)部分:"+queryString);// 獲取客戶(hù)端的請(qǐng)求方式String method=request.getMethod();System.out.println("獲取客戶(hù)端的請(qǐng)求方式:"+method);// 獲取HTTP版本號(hào)String protocol=request.getProtocol();System.out.println("獲取HTTP版本號(hào):"+protocol);// 獲取webapp名字 (站點(diǎn)名)String webapp=request.getContextPath();System.out.println("獲取webapp名字:"+webapp);
方法
示例
// 獲取指定名稱(chēng)的參數(shù),返回字符串String uname=request.getParameter("uname");System.out.println("uname的參數(shù)值:"+uname);// 獲取指定名稱(chēng)參數(shù)的所有參數(shù)值,返回?cái)?shù)組String[]hobbys=request.getParameterValues("hobby");System.out.println("獲取指定名稱(chēng)參數(shù)的所有參數(shù)值:"+Arrays.toString(hobbys));
? 由于現(xiàn)在的 request 屬于接收客戶(hù)端的參數(shù),所以必然有其默認(rèn)的語(yǔ)言編碼,主要是由于在解析過(guò)程中默認(rèn)使用的編碼方式為 ISO-8859-1(此編碼不支持中文),所以解析時(shí)一定會(huì)出現(xiàn)亂碼。要想解決這種亂碼問(wèn)題,需要設(shè)置 request 中的編碼方式,告訴服務(wù)器以何種方式來(lái)解析數(shù)據(jù)。或者在接收到亂碼數(shù)據(jù)以后,再通過(guò)相應(yīng)的編碼格式還原。
方式一:
request.setCharacterEncoding("UTF-8");
這種方式只針對(duì) POST 有效(必須在接收所有的數(shù)據(jù)之前設(shè)定)
方式二:
newString(request.getParameter(name).getBytes("ISO-8859-1"),"UTF-8");
借助了String 對(duì)象的方法,該種方式對(duì)任何請(qǐng)求有效,是通用的。
Tomcat8起,以后的GET方式請(qǐng)求是不會(huì)出現(xiàn)亂碼的。
? 請(qǐng)求轉(zhuǎn)發(fā),是一種服務(wù)器的行為,當(dāng)客戶(hù)端請(qǐng)求到達(dá)后,服務(wù)器進(jìn)行轉(zhuǎn)發(fā),此時(shí)會(huì)將請(qǐng)求對(duì)象進(jìn)行保存,地址欄中的?URL 地址不會(huì)改變,得到響應(yīng)后,服務(wù)器端再將響應(yīng)發(fā)送給客戶(hù)端,從始至終只有一個(gè)請(qǐng)求發(fā)出。
實(shí)現(xiàn)方式如下,達(dá)到多個(gè)資源協(xié)同響應(yīng)的效果。
request.getRequestDispatcher(url).forward(request,response);
? 通過(guò)該對(duì)象可以在一個(gè)請(qǐng)求中傳遞數(shù)據(jù),作用范圍:在一次請(qǐng)求中有效,即服務(wù)器跳轉(zhuǎn)有效。
// 設(shè)置域?qū)ο髢?nèi)容request.setAttribute(String name,String value);// 獲取域?qū)ο髢?nèi)容request.getAttribute(String name);// 刪除域?qū)ο髢?nèi)容request.removeAttribute(String name);
? request 域?qū)ο笾械臄?shù)據(jù)在一次請(qǐng)求中有效,則經(jīng)過(guò)請(qǐng)求轉(zhuǎn)發(fā),request 域中的數(shù)據(jù)依然存在,則在請(qǐng)求轉(zhuǎn)發(fā)的過(guò)程中可以通過(guò) request 來(lái)傳輸/共享數(shù)據(jù)。
? Web服務(wù)器收到客戶(hù)端的http請(qǐng)求,會(huì)針對(duì)每一次請(qǐng)求,分別創(chuàng)建一個(gè)用于代表請(qǐng)求的 request 對(duì)象和代表響應(yīng)的 response 對(duì)象。
? request 和 response 對(duì)象代表請(qǐng)求和響應(yīng):獲取客戶(hù)端數(shù)據(jù),需要通過(guò) request 對(duì)象;向客戶(hù)端輸出數(shù)據(jù),需要通過(guò) response 對(duì)象。
? HttpServletResponse 的主要功能用于服務(wù)器對(duì)客戶(hù)端的請(qǐng)求進(jìn)行響應(yīng),將 Web 服務(wù)器處理后的結(jié)果返回給客戶(hù)端。service()方法中形參接收的是 HttpServletResponse 接口的實(shí)例化對(duì)象,這個(gè)對(duì)象中封裝了向客戶(hù)端發(fā)送數(shù)據(jù)、發(fā)送響應(yīng)頭,發(fā)送響應(yīng)狀態(tài)碼的方法。
? 接收到客戶(hù)端請(qǐng)求后,可以通過(guò) HttpServletResponse 對(duì)象直接進(jìn)行響應(yīng),響應(yīng)時(shí)需要獲取輸出流。
? 有兩種形式:
??getWriter()?獲取字符流(只能響應(yīng)回字符)
??getOutputStream()?獲取字節(jié)流(能響應(yīng)一切數(shù)據(jù))
? 響應(yīng)回的數(shù)據(jù)到客戶(hù)端被瀏覽器解析。
??注意:兩者不能同時(shí)使用。
// 字符輸出流PrintWriter writer=response.getWriter();writer.write("Hello");writer.write("<h2>Hello</h2>");
// 字節(jié)輸出流ServletOutputStream out=response.getOutputStream();out.write("Hello".getBytes());out.write("<h2>Hello</h2>".getBytes());
? 設(shè)置響應(yīng)類(lèi)型,默認(rèn)是字符串
// 設(shè)置響應(yīng)MIME類(lèi)型response.setHeader("content-type","text/html");// html
? 在響應(yīng)中,如果我們響應(yīng)的內(nèi)容中含有中文,則有可能出現(xiàn)亂碼。這是因?yàn)榉?wù)器響應(yīng)的數(shù)據(jù)也會(huì)經(jīng)過(guò)網(wǎng)絡(luò)傳輸,服務(wù)器端有一種編碼方式,在客戶(hù)端也存在一種編碼方式,當(dāng)兩端使用的編碼方式不同時(shí)則出現(xiàn)亂碼。
getWriter()的字符亂碼
? 對(duì)于 getWriter()獲取到的字符流,響應(yīng)中文必定出亂碼,由于服務(wù)器端在進(jìn)行編碼時(shí)默認(rèn)會(huì)使用 ISO-8859-1 格式的編碼,該編碼方式并不支持中文。
? 要解決該種亂碼只能在服務(wù)器端告知服務(wù)器使用一種能夠支持中文的編碼格式,比如我們通常用的"UTF-8"。
response.setCharacterEncoding("UTF-8");
? 此時(shí)還只完成了一半的工作,要保證數(shù)據(jù)正確顯示,還需要指定客戶(hù)端的解碼方式。
response.setHeader("content-type","text/html;charset=UTF-8");
? 兩端指定編碼后,亂碼就解決了。一句話(huà):保證發(fā)送端和接收端的編碼一致
// 設(shè)置服務(wù)端的編碼response.setCharacterEncoding("UTF-8");// 設(shè)置客戶(hù)端的響應(yīng)類(lèi)型及編碼response.setHeader("content-type","text/html;charset=UTF-8");// 得到字符輸出流PrintWriter writer=response.getWriter();writer.write("<h2>你好</h2>");
以上兩端編碼的指定也可以使用一句替代,同時(shí)指定服務(wù)器和客戶(hù)端
response.setContentType("text/html;charset=UTF-8");
getOutputStream()字節(jié)亂碼
? 對(duì)于 getOutputStream()方式獲取到的字節(jié)流,響應(yīng)中文時(shí),由于本身就是傳輸?shù)淖止?jié), 所以此時(shí)可能出現(xiàn)亂碼,也可能正確顯示。當(dāng)服務(wù)器端給的字節(jié)恰好和客戶(hù)端使用的編碼方式一致時(shí)則文本正確顯示,否則出現(xiàn)亂碼。無(wú)論如何我們都應(yīng)該準(zhǔn)確掌握服務(wù)器和客戶(hù)端使用的是那種編碼格式,以確保數(shù)據(jù)正確顯示。
??指定客戶(hù)端和服務(wù)器使用的編碼方式一致。
response.setHeader("content-type","text/html;charset=UTF-8");
// 設(shè)置客戶(hù)端的編碼及響應(yīng)類(lèi)型ServletOutputStream out=response.getOutputStream();response.setHeader("content-type","text/html;charset=UTF-8");out.write("<h2>你好</h2>".getBytes("UTF-8"));
? 同樣也可以使用一句替代
// 設(shè)置客戶(hù)端與服務(wù)端的編碼response.setContentType("text/html;charset=UTF-8");
總結(jié):要想解決響應(yīng)的亂碼,只需要保證使用支持中文的編碼格式。并且保證服務(wù)器端 和客戶(hù)端使用相同的編碼方式即可。
? 重定向是一種服務(wù)器指導(dǎo),客戶(hù)端的行為??蛻?hù)端發(fā)出第一個(gè)請(qǐng)求,被服務(wù)器接收處理后,服務(wù)器會(huì)進(jìn)行響應(yīng),在響應(yīng)的同時(shí),服務(wù)器會(huì)給客戶(hù)端一個(gè)新的地址(下次請(qǐng)求的地址 response.sendRedirect(url);),當(dāng)客戶(hù)端接收到響應(yīng)后,會(huì)立刻、馬上、自動(dòng)根據(jù)服務(wù)器給的新地址發(fā)起第二個(gè)請(qǐng)求,服務(wù)器接收請(qǐng)求并作出響應(yīng),重定向完成。
? 從描述中可以看出重定向當(dāng)中有兩個(gè)請(qǐng)求存在,并且屬于客戶(hù)端行為。
// 重定向跳轉(zhuǎn)到index.jspresponse.sendRedirect("index.jsp");
? 通過(guò)觀察瀏覽器我們發(fā)現(xiàn)第一次請(qǐng)求獲得的響應(yīng)碼為 302,并且含有一個(gè) location 頭信息。并且地址欄最終看到的地址是和第一次請(qǐng)求地址不同的,地址欄已經(jīng)發(fā)生了變化。
請(qǐng)求轉(zhuǎn)發(fā)與重定向的區(qū)別
? 請(qǐng)求轉(zhuǎn)發(fā)和重定向比較:
? 兩者都可進(jìn)行跳轉(zhuǎn),根據(jù)實(shí)際需求選取即可。
? Cookie是瀏覽器提供的一種技術(shù),通過(guò)服務(wù)器的程序能將一些只須保存在客戶(hù)端,或者在客戶(hù)端進(jìn)行處理的數(shù)據(jù),放在本地的計(jì)算機(jī)上,不需要通過(guò)網(wǎng)絡(luò)傳輸,因而提高網(wǎng)頁(yè)處理的效率,并且能夠減少服務(wù)器的負(fù)載,但是由于 Cookie 是服務(wù)器端保存在客戶(hù)端的信息, 所以其安全性也是很差的。例如常見(jiàn)的記住密碼則可以通過(guò) Cookie 來(lái)實(shí)現(xiàn)。
? 有一個(gè)專(zhuān)門(mén)操作Cookie的類(lèi)?javax.servlet.http.Cookie。隨著服務(wù)器端的響應(yīng)發(fā)送給客戶(hù)端,保存在瀏覽器。當(dāng)下次再訪(fǎng)問(wèn)服務(wù)器時(shí)把Cookie再帶回服務(wù)器。
? Cookie 的格式:鍵值對(duì)用“=”鏈接,多個(gè)鍵值對(duì)間通過(guò)“;”隔開(kāi)。
? 通過(guò) new Cookie(“key”,“value”);來(lái)創(chuàng)建一個(gè) Cookie 對(duì)象,要想將 Cookie 隨響應(yīng)發(fā)送到客戶(hù)端,需要先添加到 response 對(duì)象中,response.addCookie(cookie);此時(shí)該 cookie 對(duì)象則隨著響應(yīng)發(fā)送至了客戶(hù)端。在瀏覽器上可以看見(jiàn)。
// 創(chuàng)建Cookie對(duì)象Cookie cookie=newCookie("uname","zhangsan");// 發(fā)送Cookie對(duì)象response.addCookie(cookie);
F12 查看
? 在服務(wù)器端只提供了一個(gè) getCookies()的方法用來(lái)獲取客戶(hù)端回傳的所有 cookie 組成的一個(gè)數(shù)組,如果需要獲取單個(gè) cookie 則需要通過(guò)遍歷,getName()獲取 Cookie 的名稱(chēng),getValue()獲取 Cookie 的值。
// 獲取Cookie數(shù)組Cookie[]cookies=request.getCookies();// 判斷數(shù)組是否為空if(cookies!=null&&cookies.length>0){// 遍歷Cookie數(shù)組for(Cookie cookie:cookies){System.out.println(cookie.getName());System.out.println(cookie.getValue());}}
? 除了 Cookie 的名稱(chēng)和內(nèi)容外,我們還需要關(guān)心一個(gè)信息,到期時(shí)間,到期時(shí)間用來(lái)指定該 cookie 何時(shí)失效。默認(rèn)為當(dāng)前瀏覽器關(guān)閉即失效。我們可以手動(dòng)設(shè)定 cookie 的有效時(shí)間(通過(guò)到期時(shí)間計(jì)算),通過(guò) setMaxAge(int time);方法設(shè)定 cookie 的最大有效時(shí)間,以秒為單位。
到期時(shí)間的取值
負(fù)整數(shù)
若為負(fù)數(shù),表示不存儲(chǔ)該 cookie。
cookie 的 maxAge 屬性的默認(rèn)值就是-1,表示只在瀏覽器內(nèi)存中存活,一旦關(guān)閉瀏覽器窗口,那么 cookie 就會(huì)消失。
正整數(shù)
若大于 0 的整數(shù),表示存儲(chǔ)的秒數(shù)。
表示 cookie 對(duì)象可存活指定的秒數(shù)。當(dāng)生命大于 0 時(shí),瀏覽器會(huì)把 Cookie 保存到硬盤(pán)上,就算關(guān)閉瀏覽器,就算重啟客戶(hù)端電腦,cookie 也會(huì)存活相應(yīng)的時(shí)間。
零
若為 0,表示刪除該 cookie。
cookie 生命等于 0 是一個(gè)特殊的值,它表示 cookie 被作廢!也就是說(shuō),如果原來(lái)瀏覽器已經(jīng)保存了這個(gè) Cookie,那么可以通過(guò) Cookie 的 setMaxAge(0)來(lái)刪除這個(gè) Cookie。 無(wú)論是在瀏覽器內(nèi)存中,還是在客戶(hù)端硬盤(pán)上都會(huì)刪除這個(gè) Cookie。
設(shè)置Cookie對(duì)象指定時(shí)間后失效
// 創(chuàng)建Cookie對(duì)象Cookie cookie=newCookie("uname","zhangsan");// 設(shè)置Cookie 3天后失效cookie.setMaxAge(3*24*60*60);// 發(fā)送Cookie對(duì)象response.addCookie(cookie);
Cookie保存在當(dāng)前瀏覽器中。
在一般的站點(diǎn)中常常有記住用戶(hù)名這樣一個(gè)操作,該操作只是將信息保存在本機(jī)上,換電腦以后這些信息就無(wú)效了。而且 cookie 還不能跨瀏覽器。
Cookie存中文問(wèn)題
Cookie 中不能出現(xiàn)中文,如果有中文則通過(guò) URLEncoder.encode()來(lái)進(jìn)行編碼,獲取時(shí)通過(guò) URLDecoder.decode()來(lái)進(jìn)行解碼。
String name="姓名";String value="張三";// 通過(guò) URLEncoder.encode()來(lái)進(jìn)行編碼name=URLEncoder.encode(name);value=URLEncoder.encode(value);// 創(chuàng)建Cookie對(duì)象Cookie cookie=newCookie(name,value);// 發(fā)送Cookie對(duì)象response.addCookie(cookie);
// 獲取時(shí)通過(guò) URLDecoder.decode()來(lái)進(jìn)行解碼URLDecoder.decode(cookie.getName());URLDecoder.decode(cookie.getValue());
同名Cookie問(wèn)題
如果服務(wù)器端發(fā)送重復(fù)的Cookie那么會(huì)覆蓋原有的Cookie。
瀏覽器存放Cookie的數(shù)量
不同的瀏覽器對(duì)Cookie也有限定,Cookie的存儲(chǔ)有是上限的。Cookie是存儲(chǔ)在客戶(hù)端(瀏覽器)的,而且一般是由服務(wù)器端創(chuàng)建和設(shè)定。后期結(jié)合Session來(lái)實(shí)現(xiàn)回話(huà)跟蹤。
? Cookie的setPath設(shè)置cookie的路徑,這個(gè)路徑直接決定服務(wù)器的請(qǐng)求是否會(huì)從瀏覽器中加載某些cookie。
**情景一:**當(dāng)前服務(wù)器下任何項(xiàng)目的任意資源都可獲取Cookie對(duì)象
/* 當(dāng)前項(xiàng)目路徑為:s01 */Cookie cookie=newCookie("xxx","XXX");// 設(shè)置路徑為"/",表示在當(dāng)前服務(wù)器下任何項(xiàng)目都可訪(fǎng)問(wèn)到Cookie對(duì)象cookie.setPath("/");response.addCookie(cookie);
**情景二:**當(dāng)前項(xiàng)目下的資源可獲取Cookie對(duì)象 (默認(rèn)不設(shè)置Cookie的path)
/* 當(dāng)前項(xiàng)目路徑為:s01 */Cookie cookie=newCookie("xxx","XXX");// 設(shè)置路徑為"/s01",表示在當(dāng)前項(xiàng)目下任何項(xiàng)目都可訪(fǎng)問(wèn)到Cookie對(duì)象cookie.setPath("/s01");// 默認(rèn)情況,可不設(shè)置path的值response.addCookie(cookie);
**情景三:**指定項(xiàng)目下的資源可獲取Cookie對(duì)象
/* 當(dāng)前項(xiàng)目路徑為:s01 */Cookie cookie=newCookie("xxx","XXX");// 設(shè)置路徑為"/s02",表示在s02項(xiàng)目下才可訪(fǎng)問(wèn)到Cookie對(duì)象cookie.setPath("/s02");// 只能在s02項(xiàng)目下獲取Cookie,就算cookie是s01產(chǎn)生的,s01也不能獲取它response.addCookie(cookie);
**情景四:**指定目錄下的資源可獲取Cookie對(duì)象
/* 當(dāng)前項(xiàng)目路徑為:s01 */Cookie cookie=newCookie("xxx","XXX");// 設(shè)置路徑為"/s01/cook",表示在s02/cook目錄下才可訪(fǎng)問(wèn)到Cookie對(duì)象cookie.setPath("/s01/cook");response.addCookie(cookie);
? 如果我們?cè)O(shè)置path,如果當(dāng)前訪(fǎng)問(wèn)的路徑包含了cookie的路徑(當(dāng)前訪(fǎng)問(wèn)路徑在cookie路徑基礎(chǔ)上要比cookie的范圍?。ヽookie就會(huì)加載到request對(duì)象之中。
? cookie的路徑指的是可以訪(fǎng)問(wèn)該cookie的頂層目錄,該路徑的子路徑也可以訪(fǎng)問(wèn)該cookie。
??總結(jié):當(dāng)訪(fǎng)問(wèn)的路徑包含了cookie的路徑時(shí),則該請(qǐng)求將帶上該cookie;如果訪(fǎng)問(wèn)路徑不包含cookie路徑,則該請(qǐng)求不會(huì)攜帶該cookie。
??HttpSession對(duì)象是 javax.servlet.http.HttpSession 的實(shí)例,該接口并不像 HttpServletRequest 或 HttpServletResponse 還存在一個(gè)父接口,該接口只是一個(gè)純粹的接口。這因?yàn)?session 本身就屬于 HTTP 協(xié)議的范疇。
? 對(duì)于服務(wù)器而言,每一個(gè)連接到它的客戶(hù)端都是一個(gè) session,servlet 容器使用此接口創(chuàng)建 HTTP 客戶(hù)端和 HTTP 服務(wù)器之間的會(huì)話(huà)。會(huì)話(huà)將保留指定的時(shí)間段,跨多個(gè)連接或來(lái)自用戶(hù)的頁(yè)面請(qǐng)求。一個(gè)會(huì)話(huà)通常對(duì)應(yīng)于一個(gè)用戶(hù),該用戶(hù)可能多次訪(fǎng)問(wèn)一個(gè)站點(diǎn)。可以通過(guò)此接口查看和操作有關(guān)某個(gè)會(huì)話(huà)的信息,比如會(huì)話(huà)標(biāo)識(shí)符、創(chuàng)建時(shí)間和最后一次訪(fǎng)問(wèn)時(shí)間。在整個(gè) session 中,最重要的就是屬性的操作。
? session 無(wú)論客戶(hù)端還是服務(wù)器端都可以感知到,若重新打開(kāi)一個(gè)新的瀏覽器,則無(wú)法取得之前設(shè)置的 session,因?yàn)槊恳粋€(gè) session 只保存在當(dāng)前的瀏覽器當(dāng)中,并在相關(guān)的頁(yè)面取得。
? Session 的作用就是為了標(biāo)識(shí)一次會(huì)話(huà),或者說(shuō)確認(rèn)一個(gè)用戶(hù);并且在一次會(huì)話(huà)(一個(gè)用戶(hù)的多次請(qǐng)求)期間共享數(shù)據(jù)。我們可以通過(guò) request.getSession()方法,來(lái)獲取當(dāng)前會(huì)話(huà)的 session 對(duì)象。
// 如果session對(duì)象存在,則獲??;如果session對(duì)象不存在,則創(chuàng)建HttpSession session=request.getSession();
? Session 既然是為了標(biāo)識(shí)一次會(huì)話(huà),那么此次會(huì)話(huà)就應(yīng)該有一個(gè)唯一的標(biāo)志,這個(gè)標(biāo)志就是 sessionId。
? 每當(dāng)一次請(qǐng)求到達(dá)服務(wù)器,如果開(kāi)啟了會(huì)話(huà)(訪(fǎng)問(wèn)了 session),服務(wù)器第一步會(huì)查看是否從客戶(hù)端回傳一個(gè)名為 JSESSIONID 的 cookie,如果沒(méi)有則認(rèn)為這是一次新的會(huì)話(huà),會(huì)創(chuàng)建 一個(gè)新的 session 對(duì)象,并用唯一的 sessionId 為此次會(huì)話(huà)做一個(gè)標(biāo)志。如果有 JESSIONID 這 個(gè)cookie回傳,服務(wù)器則會(huì)根據(jù) JSESSIONID 這個(gè)值去查看是否含有id為JSESSION值的session 對(duì)象,如果沒(méi)有則認(rèn)為是一個(gè)新的會(huì)話(huà),重新創(chuàng)建一個(gè)新的 session 對(duì)象,并標(biāo)志此次會(huì)話(huà); 如果找到了相應(yīng)的 session 對(duì)象,則認(rèn)為是之前標(biāo)志過(guò)的一次會(huì)話(huà),返回該 session 對(duì)象,數(shù)據(jù)達(dá)到共享。
? 這里提到一個(gè)叫做 JSESSIONID 的 cookie,這是一個(gè)比較特殊的 cookie,當(dāng)用戶(hù)請(qǐng)求服務(wù)器時(shí),如果訪(fǎng)問(wèn)了 session,則服務(wù)器會(huì)創(chuàng)建一個(gè)名為 JSESSIONID,值為獲取到的 session(無(wú)論是獲取到的還是新創(chuàng)建的)的 sessionId 的 cookie 對(duì)象,并添加到 response 對(duì)象中,響應(yīng)給客戶(hù)端,有效時(shí)間為關(guān)閉瀏覽器。
? 所以 Session 的底層依賴(lài) Cookie 來(lái)實(shí)現(xiàn)。
? Session 用來(lái)表示一次會(huì)話(huà),在一次會(huì)話(huà)中數(shù)據(jù)是可以共享的,這時(shí) session 作為域?qū)ο蟠嬖?,可以通過(guò) setAttribute(name,value) 方法向域?qū)ο笾刑砑訑?shù)據(jù),通過(guò) getAttribute(name) 從域?qū)ο笾蝎@取數(shù)據(jù),通過(guò) removeAttribute(name) 從域?qū)ο笾幸瞥龜?shù)據(jù)。
// 獲取session對(duì)象 HttpSession session=request.getSession();// 設(shè)置session域?qū)ο髎ession.setAttribute("uname","admin");// 獲取指定名稱(chēng)的session域?qū)ο骃tring uname=(String)request.getAttribute("uname");// 移除指定名稱(chēng)的session域?qū)ο髎ession.removeAttribute("uname");
? 數(shù)據(jù)存儲(chǔ)在 session 域?qū)ο笾?,?dāng) session 對(duì)象不存在了,或者是兩個(gè)不同的 session 對(duì)象時(shí),數(shù)據(jù)也就不能共享了。這就不得不談到 session 的生命周期。
? 當(dāng)客戶(hù)端第一次請(qǐng)求 servlet 并且操作 session 時(shí),session 對(duì)象生成,Tomcat 中 session 默認(rèn)的存活時(shí)間為 30min,即你不操作界面的時(shí)間,一旦有操作,session 會(huì)重新計(jì)時(shí)。
? 那么 session 的默認(rèn)時(shí)間可以改么?答案是肯定的。
? 可以在 Tomcat 中的 conf 目錄下的 web.xml 文件中進(jìn)行修改。
<!-- session 默認(rèn)的最大不活動(dòng)時(shí)間。單位:分鐘。 --><session-config><session-timeout>30</session-timeout></session-config>
? 當(dāng)然除了以上的修改方式外,我們也可以在程序中自己設(shè)定 session 的生命周期,通過(guò)session.setMaxInactiveInterval(int) 來(lái)設(shè)定 session 的最大不活動(dòng)時(shí)間,單位為秒。
// 獲取session對(duì)象 HttpSession session=request.getSession();// 設(shè)置session的最大不活動(dòng)時(shí)間session.setMaxInactiveInterval(15);// 15秒
? 當(dāng)然我們也可以通過(guò) getMaxInactiveInterval() 方法來(lái)查看當(dāng)前 Session 對(duì)象的最大不活動(dòng)時(shí)間。
// 獲取session的最大不活動(dòng)時(shí)間inttime=session.getMaxInactiveInterval();
? 或者我們也可以通過(guò) session.invalidate() 方法讓 session 立刻失效
// 銷(xiāo)毀session對(duì)象session.invalidate();
? 從前面的 JESSIONID 可知道,session 的底層依賴(lài) cookie 實(shí)現(xiàn),并且該 cookie 的有效時(shí)間為關(guān)閉瀏覽器,從而 session 在瀏覽器關(guān)閉時(shí)也相當(dāng)于失效了(因?yàn)闆](méi)有 JSESSION 再與之對(duì)應(yīng))。
? 當(dāng)關(guān)閉服務(wù)器時(shí),session 銷(xiāo)毀。
? Session 失效則意味著此次會(huì)話(huà)結(jié)束,數(shù)據(jù)共享結(jié)束。
? 每一個(gè) web 應(yīng)用都有且僅有一個(gè)ServletContext 對(duì)象,又稱(chēng) Application 對(duì)象,從名稱(chēng)中可知,該對(duì)象是與應(yīng)用程序相關(guān)的。在 WEB 容器啟動(dòng)的時(shí)候,會(huì)為每一個(gè) WEB 應(yīng)用程序創(chuàng)建一個(gè)對(duì)應(yīng)的 ServletContext 對(duì)象。
? 該對(duì)象有兩大作用,第一、作為域?qū)ο笥脕?lái)共享數(shù)據(jù),此時(shí)數(shù)據(jù)在整個(gè)應(yīng)用程序中共享; 第二、該對(duì)象中保存了當(dāng)前應(yīng)用程序相關(guān)信息。例如可以通過(guò) getServerInfo() 方法獲取當(dāng)前服務(wù)器信息 ,getRealPath(String path) 獲取資源的真實(shí)路徑等。
? 獲取 ServletContext 對(duì)象的途徑有很多。比如:
通過(guò) request 對(duì)象獲取
ServletContext servletContext=request.getServletContext();
通過(guò) session 對(duì)象獲取
ServletContext servletContext=request.getSession().getServletContext();
通過(guò) servletConfig 對(duì)象獲取,在 Servlet 標(biāo)準(zhǔn)中提供了 ServletConfig 方法
ServletConfig servletConfig=getServletConfig();ServletContext servletContext=servletConfig.getServletContext();
直接獲取,Servlet 類(lèi)中提供了直接獲取 ServletContext 對(duì)象的方法
ServletContext servletContext=getServletContext();
常用方法
// 獲取項(xiàng)目存放的真實(shí)路徑String realPath=request.getServletContext().getRealPath("/");// 獲取當(dāng)前服務(wù)器的版本信息String serverInfo=request.getServletContext().getServerInfo();
? ServletContext 也可當(dāng)做域?qū)ο髞?lái)使用,通過(guò)向 ServletContext 中存取數(shù)據(jù),可以使得整個(gè)應(yīng)用程序共享某些數(shù)據(jù)。當(dāng)然不建議存放過(guò)多數(shù)據(jù),因?yàn)?ServletContext 中的數(shù)據(jù)一旦存儲(chǔ)進(jìn)去沒(méi)有手動(dòng)移除將會(huì)一直保存。
// 獲取ServletContext對(duì)象ServletContext servletContext=request.getServletContext();// 設(shè)置域?qū)ο髎ervletContext.setAttribute("name","zhangsan");// 獲取域?qū)ο骃tring name=(String)servletContext.getAttribute("name");// 移除域?qū)ο髎ervletContext.removeAttribute("name");
Servlet的三大域?qū)ο?/b>
request域?qū)ο?/p>
在一次請(qǐng)求中有效。請(qǐng)求轉(zhuǎn)發(fā)有效,重定向失效。
session域?qū)ο?/p>
在一次會(huì)話(huà)中有效。請(qǐng)求轉(zhuǎn)發(fā)和重定向都有效,session銷(xiāo)毀后失效。
servletContext域?qū)ο?/p>
在整個(gè)應(yīng)用程序中有效。服務(wù)器關(guān)閉后失效。
? 在上網(wǎng)的時(shí)候我們常常遇到文件上傳的情況,例如上傳頭像、上傳資料等;當(dāng)然除了上傳,遇見(jiàn)下載的情況也很多,接下來(lái)看看我們 servlet 中怎么實(shí)現(xiàn)文件的上傳和下載。
? 文件上傳涉及到前臺(tái)頁(yè)面的編寫(xiě)和后臺(tái)服務(wù)器端代碼的編寫(xiě),前臺(tái)發(fā)送文件,后臺(tái)接收并保存文件,這才是一個(gè)完整的文件上傳。
? 在做文件上傳的時(shí)候,會(huì)有一個(gè)上傳文件的界面,首先我們需要一個(gè)表單,并且表單的請(qǐng)求方式為?POST;其次我們的 form 表單的 enctype 必須設(shè)為"multipart/form-data",即?enctype=“multipart/form-data”,意思是設(shè)置表單的類(lèi)型為文件上傳表單。默認(rèn)情況下這個(gè)表單類(lèi)型是 “application/x-www-form-urlencoded”, 不能用于文件上傳。只有使用了multipart/form-data 才能完整地傳遞文件數(shù)據(jù)。
<!--
? ? 文件上傳表單
? ? ? ? 1. 表單提交類(lèi)型 method="post"
? ? ? ? 2. 表單類(lèi)型 enctype="multipart/form-data"
? ? ? ? 3. 表單元素類(lèi)型? 文件域設(shè)置name屬性值
--><formmethod="post"action="uploadServlet"enctype="multipart/form-data">姓名:<inputtype="text"name="uname"><br>文件:<inputtype="file"name="myfile"><br><buttontype="submit">提交</button></form>
? 使用注解?@MultipartConfig?將一個(gè) Servlet 標(biāo)識(shí)為支持文件上傳。 Servlet 將 multipart/form-data 的 POST 請(qǐng)求封裝成 Part,通過(guò) Part 對(duì)上傳的文件進(jìn)行操作。
packagecom.xxxx.servlet;importjavax.servlet.ServletException;importjavax.servlet.annotation.MultipartConfig;importjavax.servlet.annotation.WebServlet;importjavax.servlet.http.HttpServlet;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjavax.servlet.http.Part;importjava.io.IOException;@WebServlet("/uploadServlet")@MultipartConfig// 如果是文件上傳表單,一定要加這個(gè)注解publicclassUploadServletextendsHttpServlet{@Overrideprotectedvoidservice(HttpServletRequest request,HttpServletResponse response)throwsServletException,IOException{// 設(shè)置請(qǐng)求的編碼格式request.setCharacterEncoding("UTF-8");// 獲取普通表單項(xiàng) (文本框)String uname=request.getParameter("uname");// "uname"代表的是文本框的name屬性值// 通過(guò) getPart(name) 方法獲取Part對(duì)象 (name代表的是頁(yè)面中file文件域的name屬性值)Part part=request.getPart("myfile");// 通過(guò)Part對(duì)象,獲取上傳的文件名String fileName=part.getSubmittedFileName();// 獲取上傳文件需要存放的路徑 (得到項(xiàng)目存放的真實(shí)路徑)String realPath=request.getServletContext().getRealPath("/");// 將文件上傳到指定位置part.write(realPath+fileName);}}
? 文件下載,即將服務(wù)器上的資源下載(拷貝)到本地,我們可以通過(guò)兩種方式下載。第一種是通過(guò)超鏈接本身的特性來(lái)下載;第二種是通過(guò)代碼下載。
? 當(dāng)我們?cè)?HTML 或 JSP 頁(yè)面中使用a標(biāo)簽時(shí),原意是希望能夠進(jìn)行跳轉(zhuǎn),但當(dāng)超鏈接遇到瀏覽器不識(shí)別的資源時(shí)會(huì)自動(dòng)下載;當(dāng)遇見(jiàn)瀏覽器能夠直接顯示的資源,瀏覽器就會(huì)默認(rèn)顯示出來(lái),比如 txt、png、jpg 等。當(dāng)然我們也可以通過(guò)?download 屬性規(guī)定瀏覽器進(jìn)行下載。但有些瀏覽器并不支持。
默認(rèn)下載
<!-- 當(dāng)超鏈接遇到瀏覽器不識(shí)別的資源時(shí),會(huì)自動(dòng)下載 --><ahref="test.zip">超鏈接下載</a>
指定 download 屬性下載
<!-- 當(dāng)超鏈接遇到瀏覽器識(shí)別的資源時(shí),默認(rèn)不會(huì)下載。通過(guò)download屬性可進(jìn)行下載 --><ahref="test.txt"download>超鏈接下載</a>
? download 屬性可以不寫(xiě)任何信息,會(huì)自動(dòng)使用默認(rèn)文件名。如果設(shè)置了download屬性的值,則使用設(shè)置的值做為文件名。當(dāng)用戶(hù)打開(kāi)瀏覽器點(diǎn)擊鏈接的時(shí)候就會(huì)直接下載文件。
實(shí)現(xiàn)步驟
需要通過(guò) response.setContentType 方法設(shè)置 Content-type 頭字段的值, 為瀏覽器無(wú)法使用某種方式或激活某個(gè)程序來(lái)處理的 MIME 類(lèi)型,例 如 “application/octet-stream” 或 “application/x-msdownload” 等。
需要通過(guò) response.setHeader 方法設(shè)置 Content-Disposition 頭的值 為 “attachment;filename=文件名”
讀取下載文件,調(diào)用 response.getOutputStream 方法向客戶(hù)端寫(xiě)入附件內(nèi)容。
packagecom.xxxx.servlet;importjavax.servlet.ServletException;importjavax.servlet.ServletOutputStream;importjavax.servlet.http.HttpServlet;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.io.File;importjava.io.FileInputStream;importjava.io.IOException;importjava.io.InputStream;publicclassDownloadServletextendsHttpServlet{protectedvoidservice(HttpServletRequest request,HttpServletResponse response)throwsServletException,IOException{// 設(shè)置請(qǐng)求的編碼request.setCharacterEncoding("UTF-8");// 獲取文件下載路徑String path=getServletContext().getRealPath("/");// 獲取要下載的文件名String name=request.getParameter("fileName");// 通過(guò)路徑得到file對(duì)象File file=newFile(path+name);// 判斷file對(duì)象是否存在,且是否是一個(gè)標(biāo)準(zhǔn)文件if(file.exists()&&file.isFile()){// 設(shè)置響應(yīng)類(lèi)型 (瀏覽器無(wú)法使用某種方式或激活某個(gè)程序來(lái)處理的類(lèi)型)response.setContentType("application/x-msdownload");// 設(shè)置頭信息response.setHeader("Content-Disposition","attachment;filename="+name);// 得到輸入流InputStream is=newFileInputStream(file);// 得到輸出流ServletOutputStream os=response.getOutputStream();// 定義byte數(shù)組byte[]car=newbyte[1024];// 定義長(zhǎng)度intlen=0;// 循環(huán) 輸出while((len=is.read(car))!=-1){os.write(car,0,len);}// 關(guān)閉流 釋放資源os.close();is.close();}else{System.out.println("文件不存在,下載失??!");}}}