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

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

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

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

圖1-4 圖1-5所示例子使用的標簽組成的樹形結構

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

圖1-23 Servlet和JSP的關系及適用范圍
Servlet和JSP并非互相取代的,而是各有所長,實際中也經常將兩者配合使用。任何技術都有其長處和不足,Servlet和JSP也如此。學習技術不但要理解它的內容,更要懂得如何將技術應用在能發揮最大效用的地方。
1.4 小結
本章學習了Web的基礎知識和初步了解Servlet、JSP,現在來總結一下。
Web是以Internet為基礎的應用之一,它是以HTTP為通信協議,以客戶端/服務器的請求/響應模式來運行的。
在Web中,服務器的資源通過URL來定位。客戶端通過URL定位服務器,然后向服務器發出HTTP請求;服務器通過URL定位資源,然后將資源以HTTP響應的方式返回。
Web應用中有一類很重要的資源,稱為超文本頁面,或者簡稱頁面,它用HTML寫成,由瀏覽器解釋并顯示給用戶。頁面中包含有超鏈接、表單、文字、圖像等內容,可以提供豐富的信息,可以在資源間跳轉,也可以讓用戶跟服務器進行交互。正是其豐富的功能使得Web的應用如此廣泛。
Servlet和JSP是以Java技術為基礎的Web應用開發技術。Servlet常用于非文本動態資源的生成,以及實現控制邏輯;而JSP則常用文本類動態資源(如動態頁面)的生成,以簡化開發難度。JSP是以Servlet為基礎的,但兩者卻并非可以互相替代,而是相輔相成的。
- 手機安全和可信應用開發指南:TrustZone與OP-TEE技術詳解
- Advanced Splunk
- Learning Spring 5.0
- Python 3網絡爬蟲實戰
- Visual C++開發入行真功夫
- FPGA Verilog開發實戰指南:基于Intel Cyclone IV(進階篇)
- Scala Reactive Programming
- Elasticsearch Essentials
- Solutions Architect's Handbook
- Clojure High Performance Programming(Second Edition)
- WordPress Search Engine Optimization(Second Edition)
- C/C++代碼調試的藝術(第2版)
- Node.js應用開發
- Puppet 5 Beginner's Guide(Third Edition)
- Eclipse開發(學習筆記)