- JSP網(wǎng)絡(luò)編程(學(xué)習(xí)筆記)
- 傅進(jìn)勇 鄧少烽 李波編著
- 14197字
- 2019-01-01 07:17:31
第一篇 為“JSP應(yīng)用開發(fā)基礎(chǔ)”
第1章 為什么要使用Servlet&JS P
JSP是J2EE平臺(tái)體系中最重要的技術(shù),是Java Web開發(fā)的核心。本章將闡述Servlet和JSP在網(wǎng)站開發(fā)中的作用,以及與其他動(dòng)態(tài)頁(yè)面技術(shù)的比較和區(qū)別。
通過本章的學(xué)習(xí),讀者將對(duì)Web有個(gè)總體認(rèn)識(shí),明白Web的運(yùn)作過程和所用的技術(shù),對(duì)Web容器、Servlet和JSP有個(gè)初步認(rèn)識(shí),懂得Web容器在開發(fā)Web程序中的地位和作用,知道Servlet、JSP的概念,以及它們和Web容器是如何協(xié)作實(shí)現(xiàn)Web服務(wù)的。
本章內(nèi)容包括:
★ Web的概念和Web的運(yùn)作機(jī)制。
★ Web應(yīng)用的兩個(gè)重要技術(shù)——HTTP和HTML。
★ 靜態(tài)頁(yè)面和動(dòng)態(tài)頁(yè)面。
★ Web容器的概念。
★ 初步認(rèn)識(shí)Servlet和JSP。
進(jìn)入第01章
1.1 Web網(wǎng)站
這里的Web是World Wide Web(縮寫為WWW)的簡(jiǎn)稱,也稱為萬(wàn)維網(wǎng)。簡(jiǎn)單來說,Web是建立在Internet(國(guó)際互聯(lián)網(wǎng),也稱因特網(wǎng))之上的一種應(yīng)用,它用URI(Uniform Resource Identifier,統(tǒng)一資源標(biāo)識(shí)符)來標(biāo)識(shí)分布在世界各地機(jī)器上的資源,如文本、圖像等,客戶可以根據(jù)URI來訪問這些機(jī)器上的資源,以實(shí)現(xiàn)全球網(wǎng)絡(luò)資源的共享。
注意:
人們經(jīng)常混淆Internet(國(guó)際互聯(lián)網(wǎng))和World Wide Web(萬(wàn)維網(wǎng))的概念。實(shí)際上,Internet指的是用TCP/IP協(xié)議聯(lián)系起來的一個(gè)全球性互聯(lián)網(wǎng)絡(luò),而Web是建立在Internet之上的應(yīng)用之一,使用HTTP協(xié)議進(jìn)行通信,如圖1-1所示。其他的Internet應(yīng)用還有電子郵件服務(wù)、FTP服務(wù)等。

圖1-1 架構(gòu)在Internet上的Web應(yīng)用
從技術(shù)上來看,Web利用HTTP協(xié)議,采用客戶端/服務(wù)器(Client/Server,簡(jiǎn)稱C/S)的模式進(jìn)行交互。提供Web服務(wù)的服務(wù)器一般稱為Web網(wǎng)站,或者簡(jiǎn)稱網(wǎng)站;而客戶端最常見的就是瀏覽器,如Internet Explorer、Firefox等。
1.1.1 Web的運(yùn)作機(jī)制
Web是以客戶端/服務(wù)器的模式,通過HTTP協(xié)議來交互的。具體交互方式如下:
★ 一次通信過程包括請(qǐng)求和響應(yīng)兩個(gè)步驟。請(qǐng)求由客戶端發(fā)起,請(qǐng)求信息中帶有所需資源的URI和其他所需的信息。
★ 服務(wù)器收到請(qǐng)求后,根據(jù)URI和其他信息來決定如何處理,如可能找到資源文件并直接返回,也可能交給某個(gè)服務(wù)程序處理后再返回信息。
★ 如果成功,服務(wù)器返回響應(yīng)信息和資源內(nèi)容;如果出現(xiàn)問題,如資源不存在、客戶無權(quán)限、服務(wù)器處理過程出現(xiàn)錯(cuò)誤等,服務(wù)器則返回錯(cuò)誤消息通知客戶。
★ 不管獲取資源是否成功,經(jīng)過上述的請(qǐng)求/響應(yīng)的步驟后,這次HTTP通信都會(huì)結(jié)束。后續(xù)的通信將發(fā)起新一輪的請(qǐng)求/響應(yīng),與本次無關(guān)。
運(yùn)作過程如圖1-2所示。

圖1-2 Web的運(yùn)作過程
資源的種類多種多樣,因此服務(wù)器在返回資源內(nèi)容時(shí),會(huì)在響應(yīng)信息中附帶資源類型的說明,如說明這是圖像、HTML頁(yè)面還是一般的二進(jìn)制文件等,客戶程序會(huì)根據(jù)資源類型而采用相應(yīng)的方式反饋給用戶。如當(dāng)客戶程序是瀏覽器時(shí),如果資源類型是HTML頁(yè)面,它會(huì)將其解釋出來并顯示給用戶;如果是一般的二進(jìn)制文件,則可能彈出對(duì)話框讓用戶選擇保存的地點(diǎn)。而當(dāng)客戶程序是下載工具時(shí),則可能對(duì)任何資源類型都將它保存起來。
HTML頁(yè)面,或者簡(jiǎn)稱頁(yè)面,是Web中應(yīng)用得最廣泛的一類資源。HTML頁(yè)面也稱為超文本(Hypertext),顧名思義,它是傳統(tǒng)純文本內(nèi)容的擴(kuò)充。本質(zhì)上它只是一個(gè)普通的文本文件,但它用HTML(Hypertext Markup Language,超文本標(biāo)記語(yǔ)言)寫成,可以在頁(yè)面中定義文字、圖片等豐富多彩的內(nèi)容。瀏覽器接收到頁(yè)面后,會(huì)解釋它并將定義的內(nèi)容顯示出來。頁(yè)面中可以定義一種稱為超鏈接(HyperLink)的內(nèi)容,它包含其他資源的URI,用戶單擊它就能使瀏覽器轉(zhuǎn)向該資源,以實(shí)現(xiàn)資源間的鏈接。
1.1.2 客戶端和Web服務(wù)器
由前面內(nèi)容知道,Web通信——或者說HTTP協(xié)議,是使用客戶端/服務(wù)器模式的,這樣就有客戶端和服務(wù)器的概念。但它們不是指具體的機(jī)器,而是指實(shí)現(xiàn)客戶端或服務(wù)器端功能的軟件。
客戶端軟件種類很多,如最常用的瀏覽器、下載工具等,甚至是用戶自己定制的程序。只要它能發(fā)送HTTP請(qǐng)求,并且能正確處理HTTP響應(yīng)信息,就算是客戶端。Web服務(wù)器也如此,能接受HTTP請(qǐng)求,并能返回HTTP響應(yīng)的就可稱之為服務(wù)器。本書中如無特別說明,客戶端、服務(wù)器指的也是軟件概念。
一般情況下,一臺(tái)機(jī)器只充當(dāng)一個(gè)角色。但既然客戶端、服務(wù)器只是軟件概念,一臺(tái)機(jī)器能同時(shí)運(yùn)行多個(gè)程序,當(dāng)然也能同時(shí)充當(dāng)這兩個(gè)角色。Web代理正是這樣的例子,如圖1-3所示。

圖1-3 Web代理既作為客戶端也作為服務(wù)器
圖1-3中,客戶程序,如瀏覽器,需要從某服務(wù)器獲取資源,但因?yàn)槟承┰虿荒苤苯釉L問,如這個(gè)網(wǎng)站被屏蔽了,于是,它請(qǐng)求Web代理協(xié)助。
瀏覽器發(fā)送HTTP請(qǐng)求給Web代理,在請(qǐng)求信息中包含實(shí)際要訪問的服務(wù)器地址。這時(shí),瀏覽器充當(dāng)客戶角色,而Web代理則充當(dāng)服務(wù)器的角色。
代理服務(wù)器可以和真正的服務(wù)器通信,于是,它根據(jù)請(qǐng)求信息,向真正的Web服務(wù)器發(fā)出請(qǐng)求,Web服務(wù)器接受請(qǐng)求并返回響應(yīng)信息。這時(shí),代理服務(wù)器扮演的是客戶角色,而Web服務(wù)器則處于服務(wù)器的位置。
最后,代理服務(wù)器將收到的響應(yīng)信息返回給瀏覽器,過程完成。在整個(gè)過程中,代理服務(wù)器既充當(dāng)客戶端,又充當(dāng)服務(wù)器。
當(dāng)然,Web代理和瀏覽器、Web代理和真正的Web服務(wù)器這兩次通信是互相獨(dú)立的HTTP通信過程。在瀏覽器的角度來看,“Web代理和真正的服務(wù)器通信”這一步可以抽象成圖1-2中的“進(jìn)行處理”這部分。
注意
為方便講解,如無特殊說明,本章后面用“地址”代替URI,用“瀏覽器”代表客戶程序。相信這更符合習(xí)慣,不會(huì)影響閱讀。
1.2 HTML和HTTP
在上一節(jié)了解到,頁(yè)面(或超文本)是一類很重要且應(yīng)用最廣泛的資源,它可以將文字、圖像、超鏈接等豐富的內(nèi)容呈現(xiàn)給用戶。它之所以能實(shí)現(xiàn)這么強(qiáng)大的功能,是HTML和瀏覽器配合的結(jié)果。頁(yè)面用HTML編寫,可以定義各種格式的文字、圖像、超鏈接等內(nèi)容,而瀏覽器解釋這些內(nèi)容并將它顯示給用戶。
此外,Web是使用HTTP協(xié)議(Hypertext Transfer Protocol,超文本傳輸協(xié)議)進(jìn)行通信的。HTTP這個(gè)名字可能讓人認(rèn)為它只能傳輸超文本類型的資源。實(shí)際上這個(gè)命名是因?yàn)樵搮f(xié)議誕生時(shí),Web主要用于頁(yè)面?zhèn)鬏敚坏珜?shí)際上它也能傳輸其他如圖像、文件等非頁(yè)面數(shù)據(jù)。讀者如有很豐富的網(wǎng)絡(luò)使用經(jīng)驗(yàn),對(duì)這一點(diǎn)也會(huì)深有體會(huì)。
本節(jié)簡(jiǎn)單介紹HTML和HTTP,目的是給讀者一個(gè)大概的認(rèn)識(shí),以方便后面的學(xué)習(xí)。讀者如果有深入學(xué)習(xí)的需要,可以參考其他更詳細(xì)的書籍和資料。
1.2.1 HTML標(biāo)記語(yǔ)言
HTML是一種利用標(biāo)簽,用嵌套方式來描述內(nèi)容的語(yǔ)言。“嵌套方式”是什么意思呢?如圖1-4所示,它是由如圖1-5所示例子使用的標(biāo)簽組成的樹形結(jié)構(gòu)。

圖1-4 圖1-5所示例子使用的標(biāo)簽組成的樹形結(jié)構(gòu)

圖1-5 HTML文檔例子
HTML文檔是用標(biāo)記來定義內(nèi)容的,標(biāo)記就是類似 <tag></tag> 形式的結(jié)構(gòu),前者是一個(gè)開始標(biāo)記,后者則是結(jié)束標(biāo)記。一般的標(biāo)記都要求開始和結(jié)束配對(duì)使用,如<html></html>,<body></body>等。但因?yàn)闅v史原因,HTML的語(yǔ)法并不嚴(yán)格,有的標(biāo)記,如例子中的<hr>,則沒有結(jié)束標(biāo)記,但可以將它看作是<hr></hr>這樣一個(gè)完整標(biāo)記的縮寫。
標(biāo)記之間只有兩種關(guān)系,一種是互不重疊,如<tag1></tag1><tag2></tag2>;另一種是嵌套,如<tag1><tag2></tag2></tag1>,這時(shí)就說<tag1>嵌套<tag2>。除此之外,其他方式都是錯(cuò)誤的,如<tag1><tag2></tag1></tag2>這種交叉嵌套的關(guān)系是非法的。
標(biāo)簽的這兩種關(guān)系使得頁(yè)面中定義的內(nèi)容組成一個(gè)樹形結(jié)構(gòu),如圖1-4所示就是圖1-5所示例子使用的標(biāo)簽所組成的樹形結(jié)構(gòu)。這種規(guī)定使得HTML頁(yè)面能用簡(jiǎn)單的規(guī)則定義結(jié)構(gòu)非常復(fù)雜的內(nèi)容。
現(xiàn)在請(qǐng)看圖1-5所示的例子,它給出了HTML文檔和瀏覽器顯示的內(nèi)容。
HTML文檔定義的所有內(nèi)容都包含在 <html></html>中,其中包含<head></head>和<body></body>。<head></head>中主要定義一些頁(yè)面的描述信息,如圖1-5中的<title>定義了文檔的標(biāo)題,一般瀏覽器都將它顯示在窗口的標(biāo)題欄上。下一節(jié)提到的腳本語(yǔ)言和CSS樣式表也可以在<head>中定義。<body></body>中定義頁(yè)面要顯示的內(nèi)容。例子中,<a>所定義的是超鏈接、錨點(diǎn),<hr>定義的是水平線,<table>定義的是表格,<form>定義的是表單等,都完全包含在<body></body>中。
從圖1-5中可以看出,HTML允許在開始標(biāo)簽中添加屬性,它們以“名”=“值” 的形式寫出,一般用于對(duì)標(biāo)簽指定更詳細(xì)的信息。如<a href=“..”>定義了一個(gè)超鏈接,而href這個(gè)屬性值指定它鏈接到的地址。當(dāng)用戶單擊時(shí),瀏覽器就會(huì)跳轉(zhuǎn)到這個(gè)地址。
開始和結(jié)束標(biāo)簽之間可以包含文字,它們能在頁(yè)面上顯示出來。比如圖中的“<h1>例子</h1>”這行的“例子”兩個(gè)字會(huì)顯示出來,而<h1>標(biāo)簽則指示瀏覽器用一號(hào)字顯示它。
開始和結(jié)束標(biāo)簽之間可以混合包含文字和嵌套標(biāo)簽,如圖中定義表單的<form>標(biāo)簽中就是如此。
HTML頁(yè)面中最有價(jià)值的標(biāo)簽之一是 “超鏈接”,它是通過 <a href=“URL”>顯示的文字</a> 這樣的形式來定義的,瀏覽器會(huì)顯示標(biāo)簽中包含的“顯示的文字”,如例子中定義了“到位置1”、“到位置2”這兩個(gè)超鏈接。而當(dāng)用戶單擊該超鏈接時(shí),瀏覽器會(huì)跳轉(zhuǎn)到href=“URL”所指定的地址。
頁(yè)面不只是顯示內(nèi)容,還可以讓用戶填寫數(shù)據(jù)并提交給服務(wù)器。<form>表單標(biāo)記正是為此而設(shè),表單中定義了各種收集數(shù)據(jù)的元素,瀏覽器以GUI顯示給用戶,如文本框、復(fù)選框等,用戶在這些地方輸入數(shù)據(jù),然后單擊“提交”按鈕,即可將信息發(fā)送到服務(wù)器。本章后面將討論更多有關(guān)<form>標(biāo)簽的內(nèi)容。
注意
HTML從誕生到現(xiàn)在已經(jīng)歷了多個(gè)標(biāo)準(zhǔn),最新的是HTML 4.01——實(shí)際上已經(jīng)是最后一個(gè)版本了。由于HTML誕生初期的不嚴(yán)格,為了兼容性瀏覽器一般又采取了很多特殊的容錯(cuò)解釋方式,有時(shí)同一個(gè)頁(yè)面在不同的瀏覽器上的顯示效果會(huì)不同。雖然有標(biāo)準(zhǔn)制定出來,但不同瀏覽器對(duì)某些標(biāo)簽的實(shí)現(xiàn)略有差別,甚至有的瀏覽器擴(kuò)充了標(biāo)簽的類型。
HTML的后繼者是XHTML,它的語(yǔ)法等要求嚴(yán)格很多,將來可望取代HTML,很可惜現(xiàn)在最新的瀏覽器對(duì)它的支持還不夠,并且一般都默認(rèn)禁止解釋XHTML類型的頁(yè)面。但在目前,如果要開發(fā)兼容多種瀏覽器的頁(yè)面,調(diào)試工作是很重要但又很煩人的一環(huán)。
1.2.2 腳本語(yǔ)言JavaScript
傳統(tǒng)的HTML頁(yè)面,瀏覽器將其解釋并顯示后,它的內(nèi)容就固定不再變化,用戶不能跟頁(yè)面做進(jìn)一步的交互。但隨著Web應(yīng)用的不斷增加,這些限制越來越不能滿足要求。如當(dāng)在表單輸入數(shù)據(jù)時(shí),希望能在客戶端就進(jìn)行部分的檢查,而不是發(fā)送到服務(wù)器檢查后,再返回出錯(cuò)信息,以提高交互性——這些用HTML是不可能實(shí)現(xiàn)的,但腳本語(yǔ)言就能派上用場(chǎng)。又如當(dāng)頁(yè)面有很多超鏈接時(shí),為了界面友好,希望當(dāng)用戶的鼠標(biāo)移動(dòng)到其中一個(gè)上面時(shí),將它用高亮度的顏色顯示以提醒用戶——這些用HTML也是不可能做到的,但CSS卻能。
提示
人們常將HTML稱為靜態(tài)HTML,而將HTML、腳本語(yǔ)言、CSS結(jié)合起來的技術(shù)稱為DHTML(Dynamic HTML,動(dòng)態(tài)HTML)。
這里腳本語(yǔ)言是指的嵌在頁(yè)面中的小程序,由瀏覽器解釋執(zhí)行,可以跟頁(yè)面的內(nèi)容做交互——或者更準(zhǔn)確地說,頁(yè)面定義的內(nèi)容組成了一個(gè)DOM(Document Object Model,文檔對(duì)象模型),而腳本語(yǔ)言可以讀取和修改DOM,從而達(dá)到和頁(yè)面內(nèi)容交互的目的。
HTML頁(yè)面最常用的腳本語(yǔ)言是JavaScript,它最開始是Netscape瀏覽器的擴(kuò)展,經(jīng)過幾次改名后確定為JavaScript。IE也支持JavaScript,但卻稱之為JScript,同時(shí)IE也支持VBScript。這里只介紹JavaScript。
HTML頁(yè)面中有兩種方法可以定義腳本,先看如圖1-6所示的例子。

圖1-6 JavaScript腳本例子
本例中,粗體部分的代碼就是腳本語(yǔ)言的定義,其中展示了兩種定義方式。
★ 一種是包含在<script></script>標(biāo)簽中。本例中是通過function關(guān)鍵字定義了一個(gè)函數(shù),可以被其他代碼調(diào)用。瀏覽器允許<script>標(biāo)記嵌入在任何的頁(yè)面標(biāo)簽中,但一般情況下是將它放到<head></head>中。
★ 另一種方式是在HTML標(biāo)簽形如onXXX的屬性值中定義。本例在<form>標(biāo)簽的onsubmit這個(gè)事件屬性中添加了腳本,調(diào)用之前在<script>標(biāo)簽中定義的函數(shù)。
提示
除了用事件觸發(fā)的方式使用腳本外,也可以直接將執(zhí)行代碼寫在<script>標(biāo)簽中(而不是如圖1-6所示例子中包含在函數(shù)定義中),這樣,當(dāng)瀏覽器解釋頁(yè)面時(shí)遇到它們就馬上執(zhí)行,不必等待某個(gè)事件被觸發(fā)。但這時(shí)頁(yè)面還未完全解釋,DOM模型還未完全建立,很容易出現(xiàn)錯(cuò)誤,使用時(shí)應(yīng)盡量避免。
如圖1-6所示的HTML頁(yè)面定義了一個(gè)表單,它僅有一個(gè)“點(diǎn)擊提交”按鈕,用戶單擊“點(diǎn)擊提交”按鈕時(shí)瀏覽器會(huì)執(zhí)行腳本。為了簡(jiǎn)單起見,這里僅彈出消息框通知用戶腳本被觸發(fā)。運(yùn)行結(jié)果如圖1-7所示。

圖1-7 圖1-6所示例子的運(yùn)行結(jié)果
腳本程序調(diào)用的流程如下。
1 在事件屬性中添加腳本
HTML的很多標(biāo)簽都定義了形如onXXX的屬性,當(dāng)這些事件被觸發(fā)時(shí),瀏覽器就會(huì)執(zhí)行屬性值中的代碼。如本例中定義了“javascript:return checkSubmit();”,“javascript”這個(gè)標(biāo)號(hào)告訴瀏覽器“:”后面的代碼是JavaScript腳本,缺省時(shí)默認(rèn)也是JavaScript。冒號(hào)后面的部分就是JavaScript代碼了。
2 觸發(fā)事件
onXXX對(duì)應(yīng)著什么事件,這些事件是在什么情況下產(chǎn)生的,在HTML標(biāo)準(zhǔn)中有明確的定義。在本例中,<form>的屬性“onsubmit”對(duì)應(yīng)著“表單提交”這個(gè)事件,而這個(gè)事件在用戶單擊表單的“點(diǎn)擊提交”按鈕時(shí)產(chǎn)生。
3 瀏覽器執(zhí)行事件屬性值中的代碼
當(dāng)事件被觸發(fā)后,瀏覽器就會(huì)執(zhí)行事件屬性值中的代碼。在本例中,這部分代碼調(diào)用之前定義的函數(shù)“submitCheck”,而該函數(shù)僅是彈出一個(gè)消息框。在實(shí)際開發(fā)中,可以在這里獲取用戶在表單輸入的數(shù)據(jù)并檢查是否有效。
4 代碼通過返回值指示瀏覽器的后續(xù)動(dòng)作
腳本程序結(jié)束時(shí),可以返回狀態(tài)值給瀏覽器,以指示瀏覽器后續(xù)的動(dòng)作。如在本例中,“onsubmit”事件之后應(yīng)該是瀏覽器將表單數(shù)據(jù)發(fā)送給服務(wù)器。如果程序返回true,瀏覽器確實(shí)會(huì)這樣做;但本例中的程序返回false,這時(shí)瀏覽器會(huì)取消表單提交的動(dòng)作。實(shí)際開發(fā)時(shí),可以在“submitCheck”中檢查用戶的數(shù)據(jù),當(dāng)不符合要求時(shí)彈出消息框通知用戶,并返回false讓瀏覽器取消提交動(dòng)作;如果符合要求,則返回true讓瀏覽器提交。這樣做將會(huì)大幅提高頁(yè)面的交互能力。
注意
腳本語(yǔ)言也可以單獨(dú)寫在其他文件中,然后在頁(yè)面中用<link>標(biāo)記包含進(jìn)來。如果同一段代碼在多個(gè)頁(yè)面中都用到,這種做法可以減少很多代碼量。讀者可查閱HTML的資料確定該標(biāo)記的用法。
1.2.3 CSS樣式表
在圖1-5所示的HTML例子中可以看到,傳統(tǒng)的HTML是將內(nèi)容、顯示風(fēng)格混在一起的,例如<h1>例子</h1> 這一句,內(nèi)容是“例子”兩字,<h1>標(biāo)簽則是告訴瀏覽器用一號(hào)字顯示。這種將內(nèi)容和風(fēng)格混雜在一起是不好的做法,原因如下:
★ 復(fù)雜性。將內(nèi)容和顯示方式混合使得頁(yè)面的內(nèi)容不清晰,結(jié)構(gòu)復(fù)雜。現(xiàn)代的開發(fā)思想是盡量松耦合,而將內(nèi)容風(fēng)格混合的做法將使維護(hù)難度增大,難以擴(kuò)展。
★ 靈活性。如果要改變頁(yè)面顯示風(fēng)格時(shí),需要對(duì)每個(gè)顯示內(nèi)容添加顯示相關(guān)的標(biāo)簽和屬性,即使同一類內(nèi)容的顯示風(fēng)格一樣,也要對(duì)每個(gè)內(nèi)容都做修改。
CSS的出現(xiàn)在一定程度上解決了這些問題。將HTML和CSS配合使用,可以令頁(yè)面專注于內(nèi)容的定義,而通過CSS定義各種內(nèi)容(標(biāo)簽)的顯示風(fēng)格。
現(xiàn)在看如圖1-8所示的例子,它演示了如何在頁(yè)面中使用CSS。這個(gè)例子定義了三個(gè)超鏈接,但卻有不同的顯示風(fēng)格。

圖1-8 CSS例子
例子中列舉了兩種定義CSS的方法。
一種是用<style></style>標(biāo)簽來包含,其內(nèi)容用標(biāo)簽名{樣式名:樣式值; …} 的形式來書寫。標(biāo)簽名指定了頁(yè)面中的哪些標(biāo)簽可以用這個(gè)樣式,樣式名指定了要定義哪種顯示風(fēng)格,樣式值則說明顯示風(fēng)格的類型。可以同時(shí)定義多個(gè)樣式,它們之間用分號(hào)(;)分隔。
本例中,a{font-size:9pt; text-decoration:underline;}定義了頁(yè)面中<a>標(biāo)簽所用的樣式,這時(shí)<a>標(biāo)簽將默認(rèn)使用這種風(fēng)格。font-size定義了字體大小為9pt(pt表示磅,是一種字體大小的計(jì)量單位),text-decoration定義了字體用下畫線(underline)來修飾。
例子中還使用了標(biāo)簽名.類型名{樣式定義} 的方式,當(dāng)標(biāo)簽通過class屬性指定類型名時(shí),將使用這里定義的風(fēng)格。
本例中,a.special{font-size:12pt; text-decoration;}為<a>標(biāo)簽定義了另外的風(fēng)格,其類型名為special。當(dāng)用<a class=“special”>定義標(biāo)簽時(shí),這種風(fēng)格將應(yīng)用在這個(gè)標(biāo)簽上。注意這只能用在<a>標(biāo)簽中,其他標(biāo)簽,如指定<h1 class=“special”>時(shí)是沒有任何效果的。
還有一種定義方式是直接作為屬性值寫在標(biāo)簽中,如本例中最后一個(gè)鏈接就是如此。
注意
CSS樣式也可以單獨(dú)寫在另一個(gè)文件中,然后用<link>標(biāo)簽包含到本頁(yè)面。當(dāng)這個(gè)樣式在多個(gè)頁(yè)面都使用時(shí),可減少大量的重復(fù)工作。
1.2.4 什么是HTTP協(xié)議
HTTP是Hypertext Transfer Protocol的縮寫,是Web應(yīng)用的通信協(xié)議。它的命名并不表示它只能傳輸超文本頁(yè)面,實(shí)際上它可以傳輸任何的數(shù)據(jù)類型,如圖片、可執(zhí)行文件等。
HTTP是一種基于文本并以請(qǐng)求/響應(yīng)模式運(yùn)行的協(xié)議,相應(yīng)地,HTTP也有請(qǐng)求、響應(yīng)兩種消息類型,它們都是由消息頭(header)和消息體(body)組成的。基于文本是指消息頭的數(shù)據(jù)都是文字字符,可以直接用文本編輯工具查看其中的數(shù)據(jù)。HTTP一次運(yùn)行過程的例子如圖1-9所示。

圖1-9 HTTP通信過程
如圖1-9所示的例子演示了瀏覽器從服務(wù)器獲取test.html這個(gè)頁(yè)面的過程。需要注意的是,HTTP是一個(gè)應(yīng)用級(jí)的協(xié)議,只關(guān)注與應(yīng)用相關(guān)的信息,至于如何找到服務(wù)器的地址這些網(wǎng)絡(luò)連接細(xì)節(jié)由底層的網(wǎng)絡(luò)協(xié)議處理。如在Internet上,TCP/IP協(xié)議負(fù)責(zé)維護(hù)連接的細(xì)節(jié)問題,它通過域名服務(wù)系統(tǒng)(DNS),根據(jù)主機(jī)的地址查到服務(wù)器實(shí)際的IP地址,然后再處理連接。如本例中服務(wù)器地址是mywebsite.com。但在HTTP協(xié)議的角度無須理會(huì)這些細(xì)節(jié),因此圖1-9中簡(jiǎn)化成瀏覽器和服務(wù)器的直接通信。
當(dāng)需要請(qǐng)求資源時(shí),瀏覽器向服務(wù)器發(fā)送請(qǐng)求信息,完整的請(qǐng)求消息由消息頭和消息體組成。
資源的內(nèi)容(本例中為test.html這個(gè)頁(yè)面的內(nèi)容)在消息體中發(fā)送,因此在消息頭中用Content-Length記錄消息體內(nèi)容的長(zhǎng)度,而Content-Type則表示資源的類型。值得一提的是“Content-Type: text/html; charset=GBK”這一行,它告訴瀏覽器“資源類型是HTML頁(yè)面,用GBK編碼”。
消息頭結(jié)束后,用一個(gè)空行和消息體分隔。消息體包含資源的內(nèi)容,如當(dāng)請(qǐng)求的是圖片時(shí),則它返回圖片文件的二進(jìn)制數(shù)據(jù);本例中則是test.html這個(gè)頁(yè)面的內(nèi)容。
1.2.5 何謂URL
到目前為止,本章出現(xiàn)過URI、URL等詞,Web應(yīng)用通過URI、URL為資源標(biāo)識(shí),發(fā)送請(qǐng)求時(shí)用其指定資源。但它們究竟有什么區(qū)別呢,本節(jié)將說明這部分內(nèi)容。
實(shí)際上,URI是Uniform Resource Identifier的縮寫,而URL是Uniform Resource Locator(統(tǒng)一資源定位器)的縮寫。它們的聯(lián)系和區(qū)別如圖1-10所示。

圖1-10 URI和URL關(guān)系圖示
嚴(yán)格來說,URI是指資源的標(biāo)識(shí)符,但具體用什么方式標(biāo)識(shí),怎么解釋標(biāo)識(shí)符的含義,并沒有做出明確規(guī)定;而URL是URI的一種具體實(shí)現(xiàn),明確地用資源的地址來標(biāo)識(shí)它。除URL外,URI也有其他具體實(shí)現(xiàn)方式,如URN等。
URI跟URL的關(guān)系好比標(biāo)識(shí)一個(gè)人的身份所用的方式,URI就像是一個(gè)泛的概念,任何可以標(biāo)識(shí)個(gè)人的方式都可以叫URI;而URL則類似利用住址來標(biāo)識(shí)這個(gè)人——當(dāng)然這里假設(shè)這些人都有很濃厚的定居意識(shí),不會(huì)輕易遷徙。
注意
在Web中一般只使用URL方式定位資源,所以很多關(guān)于Web的文獻(xiàn)中,URI、URL都是不加區(qū)分地使用,甚至有時(shí)候它們的含義略有變化。本書也將不加區(qū)分地使用,在有區(qū)別的時(shí)候會(huì)特別說明。
現(xiàn)在來講講URL的組成,最完整的URL的例子如圖1-11所示。

圖1-11 URL的組成
URL是用協(xié)議名開頭的,用“://”和其他部分分隔。本例是HTTP協(xié)議,這也是Web使用的協(xié)議。
服務(wù)器在處理某些資源時(shí),可能需要一些參數(shù),根據(jù)參數(shù)而做出不同的響應(yīng)。如在一個(gè)發(fā)布新聞的網(wǎng)站中,可能將所有新聞都用同一個(gè)資源地址定位,然后根據(jù)參數(shù)中的新聞ID返回具體的內(nèi)容。資源地址和參數(shù)間用問號(hào)(?)分隔,參數(shù)之間用“&”分隔,參數(shù)是用name=value這樣的形式給出的。
在URL的最后可以附上錨點(diǎn)名。使用錨點(diǎn)可以將頁(yè)面定位到頁(yè)面的某個(gè)指定位置。當(dāng)頁(yè)面返回后,瀏覽器會(huì)將該錨點(diǎn)位置的內(nèi)容馬上顯示在可視窗口中。要注意的是,該錨點(diǎn)名并不影響服務(wù)器返回的內(nèi)容,服務(wù)器還是將資源地址相關(guān)的資源內(nèi)容全部返回,而不是只返回錨點(diǎn)名后面的內(nèi)容。
在HTML頁(yè)面中經(jīng)常要用到URL,例如<a>標(biāo)簽等,URL可以用“絕對(duì)”和“相對(duì)”兩種方法寫出。絕對(duì)URL需要包括協(xié)議名、服務(wù)器地址和資源地址;而相對(duì)URL只需要包含資源地址的一部分即可。如圖1-12所示的例子。

圖1-12 頁(yè)面中URL的寫法
示例頁(yè)面中有三個(gè)超鏈接,第一個(gè)里面的是絕對(duì)URL,單擊該超鏈接時(shí)瀏覽器就直接利用該URL獲取資源。
第二個(gè)是相對(duì)URL,單擊時(shí)瀏覽器會(huì)首先將它跟該頁(yè)面“參考URL的目錄部分”合并成絕對(duì)URL。如在例子中,參考路徑是http://mywebsite.com/mytest/file.html,其中目錄部分是http://mywebsite.com/mytest/,超鏈接中的URL就是相對(duì)于這個(gè)地址。
默認(rèn)情況下,頁(yè)面的參考URL就是瀏覽器請(qǐng)求頁(yè)面時(shí)所用的URL。如這里通過http://mywebsite.com/example/link.html獲得該頁(yè)面,則它自動(dòng)成為參考路徑。但通過<base>標(biāo)記可以另外指定,如本例中將它改變?yōu)?a >http://mywebsite.com/mytest/。注意<base>標(biāo)記不需要</base>來結(jié)束。
頁(yè)面的第三個(gè)超鏈接同樣是相對(duì)URL,但它和第二個(gè)略有不同,它是以斜杠(/)開頭的。這樣表示它是相對(duì)于參考URL的服務(wù)器地址部分,或者說是相對(duì)于服務(wù)器的根目錄,而不是相對(duì)于目錄部分了。本例中<base>改變了參考URL為http://mywebsite.com/mytest/,而第三個(gè)URL為 /b.html,則合成的絕對(duì)URL為http://mywebsite.com/b.html。
1.2.6 HTML表單 和Web請(qǐng)求
很多情況下,需要在頁(yè)面中讓用戶填寫數(shù)據(jù)并提交到服務(wù)器。如在網(wǎng)站的注冊(cè)頁(yè)面中,需要填寫用戶名、密碼等信息發(fā)送到服務(wù)器。為此,HTML提供了<form>標(biāo)記,在里面可以定義收集數(shù)據(jù)的表單元素,瀏覽器將它以GUI形式顯示,用戶輸入數(shù)據(jù)后提交表單,瀏覽器就會(huì)將表單數(shù)據(jù)附帶在HTTP請(qǐng)求信息中發(fā)送到服務(wù)器去。
如圖1-13所示的例子列舉了頁(yè)面中常用的表單元素。

圖1-13 表單元素使用示例
<form>標(biāo)簽的action屬性的值是一個(gè)URL,表示表單提交時(shí)請(qǐng)求信息發(fā)送到該地址。該URL可以是絕對(duì)或相對(duì)的,其含義可參考上一節(jié)的說明。
method屬性是指發(fā)送這些數(shù)據(jù)所用的方法,這個(gè)方法就是HTTP請(qǐng)求時(shí)在第一行指定的方法。瀏覽器一般支持GET和POST兩種,默認(rèn)時(shí)是GET。下一節(jié)將討論這兩種方法的差異。
表單元素包含在<form></form>中,瀏覽器用GUI方式將它們顯示出來。在本例中,使用了<input>、<select>、<textarea>三種標(biāo)簽,這些標(biāo)簽都有name屬性。當(dāng)往服務(wù)器發(fā)數(shù)據(jù)時(shí),數(shù)據(jù)是用“名=值”這樣的形式封裝在HTTP請(qǐng)求中的,name屬性的值就是這個(gè)“名”。
上面代碼中僅列舉了最常用的幾個(gè)元素,表單中可以包含其他元素,如圖像、文件上傳框等。表單元素提供了很豐富的屬性設(shè)置,還可以和腳本語(yǔ)言、CSS樣式等結(jié)合。讀者如有需要可參考更專門的資料,后面需要時(shí)也會(huì)提到。
1.2.7 GET和POST
在HTTP請(qǐng)求消息的第一行中有一個(gè)“請(qǐng)求方法”。而<form>的屬性method的也告訴瀏覽器在請(qǐng)求時(shí)使用哪種請(qǐng)求方法。Web應(yīng)用最常用的兩種請(qǐng)求方法是GET和POST,本節(jié)將說明它們的實(shí)際含義和區(qū)別。
在HTTP請(qǐng)求信息中,GET方式表示不帶有消息體,只含消息頭——當(dāng)然所有HTTP信息都帶有消息頭的;而請(qǐng)求方法為POST時(shí)則說明帶有消息體。
對(duì)<form>來說,當(dāng)指定了method=“GET”,提交表單時(shí),瀏覽器會(huì)將要發(fā)送的數(shù)據(jù)附帶在URL后,以請(qǐng)求參數(shù)的形式發(fā)送過去;而指定method=“POST”時(shí),發(fā)送的數(shù)據(jù)在消息體中發(fā)送。
表單提 交時(shí),使用GET方式發(fā)送參數(shù)如圖1-14所示,使用POST方式發(fā)送參數(shù)如圖1-15所示。

圖1-15 表單指定POST方式時(shí)發(fā)送參數(shù)圖示
提示
可能讀者會(huì)問,能否在POST方式的請(qǐng)求中,既在第一行的URL中附加參數(shù)(像GET那樣),同時(shí)又在消息體中附帶其他參數(shù)呢?這是可以的<form action=“/test.jsp?id=1” method=“POST”>就可以做到。
實(shí)際使用時(shí)該如何選擇方法呢?下面從以下幾個(gè)方面看。
★ GET方式是附加在URL后面的,雖然HTTP協(xié)議對(duì)消息頭的長(zhǎng)度沒有限制,但瀏覽器和服務(wù)器一般都有,因此用GET方式不能傳送大量的數(shù)據(jù),而POST方式則恰好相反,適合傳送大量數(shù)據(jù);
★ 用GET方式發(fā)送字符時(shí),在非英語(yǔ)系的操作系統(tǒng)中,如果使用了非ASCII標(biāo)準(zhǔn)的字符編碼,一般有兩種發(fā)送方式。一是直接將字符直接附在URL后,如:
http://mywebsite.com/a.jsp?name=參數(shù)值
另一種是將它們用“%xx”的形式表示,%xx是該字符在頁(yè)面編碼中的字節(jié)值的16進(jìn)制表示,如“參數(shù)值”這三個(gè)字的GBK編碼(中文操作系統(tǒng)常用的編碼)是“B2 CE CA FD D6 B5”,則發(fā)送時(shí)的形式可能如:
http://mywebsite.com/a.jsp?name=%B2%CE%CA%FD%D6%B5
這兩種形式的差別給服務(wù)器進(jìn)行編碼轉(zhuǎn)換帶來不便。特別是在Java系統(tǒng)中,需要將所有字符都轉(zhuǎn)換成Unicode編碼,由于請(qǐng)求信息中是沒有指定用哪種方式的,服務(wù)器很難決定如何處理,需要在設(shè)計(jì)時(shí)預(yù)先約定可行的規(guī)則。相反,POST方式則比較一致。
綜上所述,GET方式應(yīng)該用于參數(shù)很少,不帶有非ASCII字符,且不會(huì)修改服務(wù)器狀態(tài)的情況中,特別是對(duì)速度要求較高時(shí)要盡量考慮GET;而POST則相反,用于發(fā)送大量數(shù)據(jù),或者帶有非ASCII字符,或者需修改服務(wù)器狀態(tài)等情況,或者在信息的準(zhǔn)確性比速度更重要的時(shí)候。當(dāng)然,這并非絕對(duì),根據(jù)具體的需求做出合理的選擇才是上策。
注意
HTTP中,除了GET,POST外,還定義了HEAD,PUT,DELETE等方法。但它們相對(duì)來說很少使用,而且瀏覽器一般只支持GET,POST,所以這里不再討論。有需要可查閱資料,后面如果有需要也會(huì)提到。
1.3 動(dòng)態(tài)頁(yè)面
瀏覽器是通過URL來向服務(wù)器請(qǐng)求特定資源的,但資源的具體內(nèi)容怎樣得來,是由服務(wù)器決定的。資源可能只是某個(gè)文件的內(nèi)容,服務(wù)器只是讀取并返回;但也可能需要收集一些信息,組合后再生成具體內(nèi)容。這就引出資源的兩種生成方式——靜態(tài)和動(dòng)態(tài);同樣,頁(yè)面作為資源的一種,自然也有靜態(tài)頁(yè)面和動(dòng)態(tài)頁(yè)面之分。
1.3.1 靜態(tài)頁(yè)面和動(dòng)態(tài)頁(yè)面
在Web應(yīng)用產(chǎn)生之初,主要用于科學(xué)家之間進(jìn)行信息交流,那時(shí)的信息量相對(duì)較少,而且最主要是信息的更新速度不快。當(dāng)時(shí)Web主要用于頁(yè)面?zhèn)鬏敚藗儗⑿畔㈩A(yù)先寫在HTML文件中并配置在服務(wù)器上,每當(dāng)瀏覽器請(qǐng)求時(shí),服務(wù)器就讀取頁(yè)面的內(nèi)容并返回。這種方式生成的頁(yè)面稱為靜態(tài)頁(yè)面,其內(nèi)容是預(yù)先確定的,對(duì)不同的請(qǐng)求都返回相同的內(nèi)容。靜態(tài)頁(yè)面的獲取流程如圖1-16所示。

圖1-16 獲取靜態(tài)頁(yè)面示意圖
然而,隨著Web應(yīng)用范圍的擴(kuò)大,靜態(tài)頁(yè)面越來越不能滿足要求了。
★ 對(duì)于信息更新速度較快的應(yīng)用,如果還使用靜態(tài)頁(yè)面,則每當(dāng)信息變化時(shí)都要手動(dòng)修改HTML文件。如果直接修改原來的文件,需要保證修改過程中服務(wù)器還能處理對(duì)該頁(yè)面的請(qǐng)求;而修改后,又需要配置服務(wù)器使用更新后的版本,并且還要考慮修改過程中的人為錯(cuò)誤等影響;
★ 對(duì)于信息更新不定時(shí)的應(yīng)用,如24小時(shí)都有可能更新的,為了保證及時(shí)性,需要有專人24小時(shí)守護(hù)。但這樣的開銷是很大的,特別是在信息更新頻率不高的場(chǎng)合,為了那不定時(shí)的少量修改而耗費(fèi)大量時(shí)間是無法接受的;
★ 對(duì)于有一定規(guī)模的應(yīng)用,數(shù)據(jù)往往是分散在多個(gè)地方的,單是關(guān)注信息的更新和收集就不容易,更不用說以上兩點(diǎn)的影響了。
動(dòng)態(tài)頁(yè)面就在這樣的需求下產(chǎn)生了。它和靜態(tài)頁(yè)面不同,服務(wù)器將請(qǐng)求交給專門的程序處理,程序在指定的地方收集數(shù)據(jù),如通過數(shù)據(jù)庫(kù)獲取,或者通過網(wǎng)絡(luò)獲取遠(yuǎn)在萬(wàn)里之外的數(shù)據(jù),然后組織在一起后再生成HTML格式的文本,最后服務(wù)器再組織成HTTP格式返回。動(dòng)態(tài)頁(yè)面的獲取流程如圖1-17所示。

圖1-17 獲取動(dòng)態(tài)頁(yè)面的示意圖
依靠計(jì)算機(jī)的快速處理能力,動(dòng)態(tài)頁(yè)面使Web應(yīng)用的范圍不斷擴(kuò)大至今天的規(guī)模。
注意
不要混淆動(dòng)態(tài)頁(yè)面和DHTML(動(dòng)態(tài)HTML)。動(dòng)態(tài)頁(yè)面是指頁(yè)面的文檔動(dòng)態(tài)生成,是由服務(wù)器處理的;而DHTML是HTML、腳本語(yǔ)言和CSS的結(jié)合,使瀏覽器顯示的頁(yè)面內(nèi)容可動(dòng)態(tài)變化,使瀏覽器處理的。兩者沒有必然的聯(lián)系。
1.3.2 Web容器的功能
通過前面的介紹,相信讀者已經(jīng)了解到Web應(yīng)用的概貌了,也明白了Web應(yīng)用的基礎(chǔ)技術(shù)。而從開發(fā)的角度來看,Web服務(wù)器至少需要處理如下問題。
★ 因?yàn)閃eb是使用HTTP協(xié)議的,因此Web服務(wù)器必須能處理HTTP協(xié)議。這包括,能解釋HTTP請(qǐng)求信息,提取出URL和附帶的數(shù)據(jù);能將資源內(nèi)容封裝成HTTP格式返回;
★ 瀏覽器只知道發(fā)送URL獲取內(nèi)容,而URL和具體資源的對(duì)應(yīng)需要由服務(wù)器處理。如果是靜態(tài)資源,則需要讀取資源內(nèi)容(如讀取文件內(nèi)容)并返回;如果是動(dòng)態(tài)資源,則需要調(diào)用合適的處理程序,獲取它生成的內(nèi)容再返回;
★ 服務(wù)器必須能同時(shí)處理多個(gè)請(qǐng)求,因此要實(shí)現(xiàn)多個(gè)處理的同時(shí)進(jìn)行,這需要服務(wù)器至少能管理線程(或進(jìn)程)的建立、關(guān)閉等工作;
★ 處理請(qǐng)求的程序經(jīng)常要在不同類型的地方獲取數(shù)據(jù),如文件、數(shù)據(jù)庫(kù)、網(wǎng)絡(luò)等。如果將和各類數(shù)據(jù)源的連接、獲取數(shù)據(jù)、關(guān)閉等處理細(xì)節(jié)都下放到大量的程序中,將使程序變得很復(fù)雜。軟件發(fā)展(其實(shí)很多方面也是如此)的一個(gè)方向就是抽象,將多種多樣的處理細(xì)節(jié)隱藏起來,而提供給上層一個(gè)簡(jiǎn)單、一致的接口,這會(huì)使Web程序的編寫變得方便,因此服務(wù)器需要提供這方面的功能。
可以看出,要考慮的內(nèi)容真是又多又煩。如果一切都需要開發(fā)人員從頭做起,Web絕對(duì)不會(huì)像今天這樣應(yīng)用得如此廣泛。但分析一下上面列出的需求,會(huì)發(fā)現(xiàn)其中很多是通用的——如處理HTTP信息、管理線程的創(chuàng)建和關(guān)閉、對(duì)靜態(tài)資源文件內(nèi)容的讀取等。而解決這些共同問題的方法就是使用Web容器了,其功能示意如圖1-18所示。

圖1-18 Web容器的功能
如圖所示,Web容器解決了Web應(yīng)用中的共同問題,剩下的只是與應(yīng)用相關(guān)的問題,需要開發(fā)人員定制程序處理,這樣使得Web開發(fā)、維護(hù)等工作更加高效快速。
當(dāng)然了,定制的程序要能夠和Web容器協(xié)調(diào)運(yùn)作,它們之間必須遵守一定的協(xié)議。實(shí)際上,Web容器(或者說是它支持的技術(shù),)就好比是Web應(yīng)用開發(fā)的框架,開發(fā)人員必須按照這些技術(shù)的要求編寫才能使程序正常運(yùn)行。軟件開發(fā)的困難之一就是從總體上做出正確的規(guī)劃,定制出好的架構(gòu),從頭做起的話是需要深厚的功底和豐富的經(jīng)驗(yàn)的。而各種新技術(shù)的產(chǎn)生,不管它們是全新出現(xiàn)的還是建立在舊技術(shù)之上的,其中很重要的一個(gè)作用就是為開發(fā)人員定義出合適的框架,這樣可以降低技術(shù)門檻,縮短開發(fā)流程,從而擴(kuò)大應(yīng)用范圍。Web開發(fā)的各種新技術(shù)也不例外,而Web容器就是實(shí)現(xiàn)這些技術(shù)的軟件產(chǎn)品。
1.3.3 Servlet的概念及其運(yùn)作
Servlet直接解釋的意思是“服務(wù)器端小程序”,它既是一種Web程序技術(shù)的名稱,也是使用這些技術(shù)編寫的程序的總稱。先看一個(gè)具體的Servlet程序例子,該例子是返回一個(gè)非常簡(jiǎn)單的HTML頁(yè)面內(nèi)容,代碼如圖1-19所示。

圖1-19 Servlet示例
注意
如果是初學(xué)者,可能會(huì)有很多看不明白的地方。這不要緊,這里最重要的是看看Servlet究竟“長(zhǎng)什么樣”,目的是有個(gè)具體認(rèn)識(shí),后面的章節(jié)會(huì)逐步解釋它的結(jié)構(gòu)和含義。
Servlet本質(zhì)上是一個(gè)普通的Java類,但必須繼承自javax.servlet.http.HttpServlet,否則Web容器不認(rèn)識(shí)它。本例的Servlet名為HiServlet。
HttpServlet預(yù)定義了很多方法,其中覆蓋doGet這個(gè)方法就可以處理GET類型的HTTP請(qǐng)求。還有其他如doPost、doXXX等方法,當(dāng)收到不同類型的請(qǐng)求時(shí)由Web容器自動(dòng)調(diào)用合適的方法。更詳細(xì)的機(jī)制將在后續(xù)章節(jié)逐步學(xué)習(xí)。
根據(jù)這個(gè)例子,可以知道Servlet只不過是一個(gè)有特定要求的Java類而已,Web容器會(huì)調(diào)用合適的方法并傳入?yún)?shù),根據(jù)目的重載某些方法,就能實(shí)現(xiàn)和Web容器的合作了。當(dāng)然,這只是從大體上來看,實(shí)際的機(jī)制、功能等內(nèi)容是非常豐富的,后續(xù)章節(jié)會(huì)逐步學(xué)習(xí)。
在圖1-17和圖1-18中已經(jīng)得知Web容器是如何與處理程序合作而生成動(dòng)態(tài)資源的,Servlet作為一種具體技術(shù),它的運(yùn)作過程自然也類似,如圖1-20所示。

圖1-20 Servlet運(yùn)作過程
圖1-20中可看出,當(dāng)接受請(qǐng)求時(shí),要確定和該URL關(guān)聯(lián)的資源。如果該URL相關(guān)聯(lián)的是Servlet,則由Web容器調(diào)用Servlet進(jìn)行處理。因此使用Servlet技術(shù)時(shí)要考慮兩個(gè)問題,一是如何部署配置Web容器,使得URL跟Servlet聯(lián)系起來;二是如何應(yīng)用Servlet技術(shù)實(shí)現(xiàn)目的。
1.3.4 JSP的概念及其運(yùn)作
在講JSP前,先看看Servlet有什么不足。雖然現(xiàn)在還沒正式學(xué)習(xí)Servlet,但這些討論在前面的例子中也可以看出來,不會(huì)妨礙理解。
由前一節(jié)知道,Servlet只是一個(gè)有特定要求的Java類,因此它的編寫跟普通的Java代碼沒什么兩樣。在圖1-19的中還看到,當(dāng)Servlet返回HTML文檔時(shí),是通過大量pw.println()之類的語(yǔ)句來實(shí)現(xiàn)的,可以看出,原來HTML的結(jié)構(gòu)已經(jīng)在這里分解得支離破碎了,生成動(dòng)態(tài)頁(yè)面的Servlet看起來就像是在Java代碼中嵌入HTML代碼。
實(shí)際開發(fā)中,HTML頁(yè)面一般是比較大的,通常由專門的美工人員進(jìn)行頁(yè)面的設(shè)計(jì),他們一般對(duì)Java程序不太熟悉;而編寫業(yè)務(wù)邏輯的開發(fā)人員對(duì)頁(yè)面設(shè)計(jì)又不如美工人員專業(yè)。這樣,設(shè)計(jì)出來的HTML代碼需要由開發(fā)人員逐個(gè)加到Servlet的println語(yǔ)句中。不難想像,這樣的工作量是很大的,而且這樣寫出來的Servlet很難看,已經(jīng)基本看不出原來HTML的結(jié)構(gòu)了。而當(dāng)頁(yè)面需要修改時(shí),又需要重復(fù)這樣的過程。
可能我們會(huì)想,將HTML代碼嵌入Java代碼確實(shí)不好,但能否反過來,在HTML代碼中嵌入Java代碼呢?JSP正是按這一想法提出來的技術(shù)。先看一個(gè)具體的JSP例子,如圖1-21所示。

圖1-21 JSP例子
注意
跟Servlet的例子類似,現(xiàn)在讀者可能會(huì)有很多東西看不明白。但最主要是看看JSP的樣子,看它跟普通的HTML相比有哪些特殊東西即可。
從這里看出,JSP頁(yè)面跟HTML頁(yè)面很相似,只是其中多了一些特殊的標(biāo)記<%%>,如<%@%>、<%=%>等。
JSP是Java Server Page(Java服務(wù)器端頁(yè)面)的縮寫。“頁(yè)面”的得名是因?yàn)樗砻婵磥硎窃贖TML中嵌入Java代碼,和普通的HTML頁(yè)面很類似;而“服務(wù)器端”是指頁(yè)面會(huì)在服務(wù)器端進(jìn)行處理,而普通的HTML頁(yè)面只在瀏覽器中才被解釋,服務(wù)器只是返回內(nèi)容,并不關(guān)注具體的內(nèi)容是什么。
明白JSP頁(yè)面中哪些內(nèi)容會(huì)被Web容器處理、怎么處理,是掌握J(rèn)SP的一個(gè)關(guān)鍵。實(shí)際上,只有包含在<%%>標(biāo)簽中的內(nèi)容才會(huì)被Web容器處理;在JSP 2.0規(guī)范中新提出了表達(dá)式語(yǔ)言(Expression Language,簡(jiǎn)稱EL),在頁(yè)面中是以“${表達(dá)式}”的方式來描述的,支持JSP 2.0規(guī)范的Web容器也會(huì)處理這些內(nèi)容。除此以外,Web容器忽略其他內(nèi)容,只將它看成是普通的字符數(shù)據(jù)。本書第三部分是專門討論JSP的,到時(shí)會(huì)再詳細(xì)討論相關(guān)機(jī)制和細(xì)節(jié),現(xiàn)在只需要知道JSP的大概即可。
注意
初學(xué)者很容易分不清服務(wù)器腳本和頁(yè)面腳本的區(qū)別,而將寫在JSP頁(yè)面中的JavaScript代碼誤認(rèn)為是在服務(wù)器端執(zhí)行的,如上例中的“<body onload=“alert(‘hello!’)”>”。但正如上一段所講,Web容器只關(guān)注<%%>標(biāo)記內(nèi)的內(nèi)容,而對(duì)支持JSP 2.0規(guī)范的Web容器則也關(guān)心 “${} ”中的內(nèi)容,其他則一概不管,直接返回給瀏覽器。
表面看來,JSP似乎是一種全新的動(dòng)態(tài)資源技術(shù),用于解決Servlet的不足并取代它。但實(shí)際上不是這樣的,JSP是以Servlet為基礎(chǔ)的。Web容器在處理JSP時(shí),會(huì)首先將它編譯成Servlet,然后再調(diào)用Servlet進(jìn)行實(shí)際的處理工作的。JSP的運(yùn)作過程如圖1-22所示。

圖1-22 JSP的運(yùn)作過程
從圖1-22中可以看到,實(shí)際進(jìn)行處理的還是Servlet,但JSP和Servlet的維護(hù)關(guān)系由Web容器負(fù)責(zé)。
當(dāng)請(qǐng)求JSP時(shí),Web容器會(huì)檢查當(dāng)前JSP和Servlet是否一致。這通常是用文件修改日期的先后來判斷的,如果這個(gè)JSP還沒轉(zhuǎn)成Servlet,或者在前一次轉(zhuǎn)換之后又經(jīng)過修改,導(dǎo)致JSP文件的修改日期比Servlet的要遲,Web容器會(huì)重新執(zhí)行Servlet的生成過程。它將JSP轉(zhuǎn)換成等價(jià)的Servlet源文件,然后調(diào)用Java編譯器編譯成Class文件,再重新加載并調(diào)用這個(gè)Servlet進(jìn)行請(qǐng)求。但如果兩者是一致的,Web容器就直接調(diào)用之前生成的Servlet進(jìn)行處理。
從圖1-22中還可以看到,生成Servlet后,請(qǐng)求都是直接由Servlet處理的,中間花費(fèi)在文件日期比較的時(shí)間只是以微秒級(jí)計(jì)算(硬盤操作),像數(shù)據(jù)庫(kù)、網(wǎng)絡(luò)這些以毫秒級(jí)計(jì)算的此類操作幾乎可以忽略不計(jì)。但盡管如此,不少Web容器還提供了關(guān)閉JSP到Servlet自動(dòng)更新的功能,這樣在Web程序運(yùn)行期間,修改后的JSP就不能馬上生效了。這一般需要配合預(yù)編譯功能,并用在幾乎不會(huì)修改的情況下。
提示
從JSP生成Servlet一般需要較長(zhǎng)的時(shí)間,如幾秒甚至幾十秒。如果JSP頁(yè)面是新部署到Web服務(wù)器的,處理第一次請(qǐng)求時(shí)將要進(jìn)行轉(zhuǎn)換工作,這樣第一次請(qǐng)求需要等待的時(shí)間較長(zhǎng)。為此,不少Web容器都提供了預(yù)編譯的功能,可以在部署時(shí)就將JSP轉(zhuǎn)成Servlet,無須等到第一次請(qǐng)求。
現(xiàn)在知道,Servlet和JSP都是動(dòng)態(tài)資源生成的技術(shù),并且JSP是以Servlet為基礎(chǔ)的。從功能上來看,JSP跟Servlet的作用是等價(jià)的。
技術(shù)的出現(xiàn)的主要目的是為了方便使用,Servlet可以生成任何類型的動(dòng)態(tài)資源,非常靈活,但對(duì)于動(dòng)態(tài)頁(yè)面的生成,雖然理論上可以做到,但實(shí)際應(yīng)用時(shí)卻顯得很笨拙。為了解決這個(gè)問題,產(chǎn)生了JSP技術(shù)。雖然JSP最終還是要轉(zhuǎn)成Servlet的,但從使用角度來看,它就像是在HTML代碼中嵌入Java代碼,使得動(dòng)態(tài)頁(yè)面程序的編寫變得相當(dāng)直觀,比起實(shí)現(xiàn)同等功能的Servlet,無論開發(fā)效率還是維護(hù)性都得到提高。實(shí)際上,JSP技術(shù)很適合用于生成文本類型的動(dòng)態(tài)資源。
但Servlet也并非從此就退居幕后,Web應(yīng)用中非文本類的動(dòng)態(tài)資源也很常用,如現(xiàn)在不少網(wǎng)站都提供了驗(yàn)證碼的功能,顯示驗(yàn)證碼的圖片就是動(dòng)態(tài)生成的,這時(shí)用Servlet編寫將更合適;用JSP的話,頁(yè)面中只有代碼而沒有標(biāo)簽,顯得很奇特。而且,實(shí)際的Web程序中除了生成動(dòng)態(tài)資源外,往往還需要很多其它的控制功能,如跳轉(zhuǎn)到其他處理程序、過濾非法用戶的請(qǐng)求等,這時(shí)使用Servlet將是更合理的選擇。如圖1-23所示為兩種技術(shù)的關(guān)系及其適用范圍。

圖1-23 Servlet和JSP的關(guān)系及適用范圍
Servlet和JSP并非互相取代的,而是各有所長(zhǎng),實(shí)際中也經(jīng)常將兩者配合使用。任何技術(shù)都有其長(zhǎng)處和不足,Servlet和JSP也如此。學(xué)習(xí)技術(shù)不但要理解它的內(nèi)容,更要懂得如何將技術(shù)應(yīng)用在能發(fā)揮最大效用的地方。
1.4 小結(jié)
本章學(xué)習(xí)了Web的基礎(chǔ)知識(shí)和初步了解Servlet、JSP,現(xiàn)在來總結(jié)一下。
Web是以Internet為基礎(chǔ)的應(yīng)用之一,它是以HTTP為通信協(xié)議,以客戶端/服務(wù)器的請(qǐng)求/響應(yīng)模式來運(yùn)行的。
在Web中,服務(wù)器的資源通過URL來定位。客戶端通過URL定位服務(wù)器,然后向服務(wù)器發(fā)出HTTP請(qǐng)求;服務(wù)器通過URL定位資源,然后將資源以HTTP響應(yīng)的方式返回。
Web應(yīng)用中有一類很重要的資源,稱為超文本頁(yè)面,或者簡(jiǎn)稱頁(yè)面,它用HTML寫成,由瀏覽器解釋并顯示給用戶。頁(yè)面中包含有超鏈接、表單、文字、圖像等內(nèi)容,可以提供豐富的信息,可以在資源間跳轉(zhuǎn),也可以讓用戶跟服務(wù)器進(jìn)行交互。正是其豐富的功能使得Web的應(yīng)用如此廣泛。
Servlet和JSP是以Java技術(shù)為基礎(chǔ)的Web應(yīng)用開發(fā)技術(shù)。Servlet常用于非文本動(dòng)態(tài)資源的生成,以及實(shí)現(xiàn)控制邏輯;而JSP則常用文本類動(dòng)態(tài)資源(如動(dòng)態(tài)頁(yè)面)的生成,以簡(jiǎn)化開發(fā)難度。JSP是以Servlet為基礎(chǔ)的,但兩者卻并非可以互相替代,而是相輔相成的。
- Android項(xiàng)目開發(fā)入門教程
- Arduino開發(fā)實(shí)戰(zhàn)指南:LabVIEW卷
- Developing Middleware in Java EE 8
- Learning Python Design Patterns(Second Edition)
- Mastering LibGDX Game Development
- C語(yǔ)言程序設(shè)計(jì)同步訓(xùn)練與上機(jī)指導(dǎo)(第三版)
- 區(qū)塊鏈技術(shù)與應(yīng)用
- 持續(xù)集成與持續(xù)交付實(shí)戰(zhàn):用Jenkins、Travis CI和CircleCI構(gòu)建和發(fā)布大規(guī)模高質(zhì)量軟件
- C++語(yǔ)言程序設(shè)計(jì)
- 軟件體系結(jié)構(gòu)
- Emotional Intelligence for IT Professionals
- 邊玩邊學(xué)Scratch3.0少兒趣味編程
- Python網(wǎng)絡(luò)爬蟲實(shí)例教程(視頻講解版)
- 數(shù)據(jù)結(jié)構(gòu):Python語(yǔ)言描述
- 3D Printing Designs:Octopus Pencil Holder