- Java EE項(xiàng)目應(yīng)用開發(fā)
- 劉勇軍 王電鋼編著
- 3604字
- 2018-12-30 08:37:20
1.1 Web應(yīng)用架構(gòu)
1.1.1 Web應(yīng)用模型
Web應(yīng)用是基于B/S結(jié)構(gòu)的,也就是瀏覽器/服務(wù)器結(jié)構(gòu)。
基于B/S的應(yīng)用程序部署在服務(wù)器端,客戶端通過(guò)瀏覽器訪問(wèn)應(yīng)用程序。如圖1-1所示。

圖1-1 服務(wù)器客戶端模型
從圖1-1中可以看到,客戶端發(fā)送HTTP請(qǐng)求消息傳給服務(wù)器,服務(wù)器將請(qǐng)求傳遞給服務(wù)器中所部署的Web應(yīng)用程序,Web應(yīng)用程序處理請(qǐng)求,并把響應(yīng)的HTML頁(yè)面通過(guò)服務(wù)器傳給客戶端顯示。
在應(yīng)用中,人們常用的瀏覽器有微軟的IE,網(wǎng)景公司的Netscape Navigator,Mozilla的Firefox,以及目前國(guó)內(nèi)用戶常用的360瀏覽器等。
服務(wù)器分兩種:Web服務(wù)器和Web應(yīng)用服務(wù)器。
● Web服務(wù)器
可以解析HTTP協(xié)議。當(dāng)Web服務(wù)器接收到一個(gè)HTTP請(qǐng)求,會(huì)返回一個(gè)HTTP響應(yīng),例如送回一個(gè)HTML頁(yè)面。為了處理一個(gè)請(qǐng)求,Web服務(wù)器可以響應(yīng)一個(gè)靜態(tài)頁(yè)面或圖片,進(jìn)行頁(yè)面跳轉(zhuǎn),或者把動(dòng)態(tài)響應(yīng)的產(chǎn)生委托給一些其他的程序,如CGI腳本,JSP腳本,Servlets, ASP腳本,服務(wù)器端JavaScript,或者一些其他的服務(wù)器端技術(shù)。無(wú)論它們的目的如何,這些服務(wù)器端的程序通常產(chǎn)生一個(gè)HTML的響應(yīng)來(lái)讓瀏覽器可以瀏覽。
Web服務(wù)器不支持事務(wù)處理或數(shù)據(jù)庫(kù)連接池。但它可以配置各種策略來(lái)實(shí)現(xiàn)容錯(cuò)性和可擴(kuò)展性,如負(fù)載平衡,緩沖。
常用的Web服務(wù)器有IBM的HTTP Server,微軟的IIS,還有Apache的Web服務(wù)器。本書將采用Apache Tomcat的Web服務(wù)器來(lái)部署各工程應(yīng)用樣例。
● Web應(yīng)用服務(wù)器
可以通過(guò)各種協(xié)議,包括HTTP,把商業(yè)邏輯暴露給客戶端應(yīng)用程序。Web服務(wù)器主要是處理向?yàn)g覽器發(fā)送HTML以供瀏覽,而Web應(yīng)用服務(wù)器提供訪問(wèn)商業(yè)邏輯的途徑以供客戶端應(yīng)用程序使用。應(yīng)用程序使用這些商業(yè)邏輯就好像是調(diào)用對(duì)象的一個(gè)方法一樣。應(yīng)用程序服務(wù)器的客戶端(包括有圖形用戶界面GUI的)可能會(huì)運(yùn)行在一臺(tái)PC、一個(gè)Web服務(wù)器甚至是其他的應(yīng)用程序服務(wù)器上。
在大多數(shù)情形下,Web應(yīng)用服務(wù)器是通過(guò)組件的應(yīng)用程序接口(API)把商業(yè)邏輯暴露給客戶端應(yīng)用程序的,例如基于Java EE應(yīng)用程序服務(wù)器的EJB組件模型。此外,Web應(yīng)用服務(wù)器可以管理自己的資源,如安全、事務(wù)處理、資源池、消息等,就像Web服務(wù)器一樣,Web應(yīng)用服務(wù)器配置了多種可擴(kuò)展和容錯(cuò)技術(shù)。
常用的Web應(yīng)用服務(wù)器有IBM的WebSphere Application Server,BEA公司的WebLogic, Oracle的IAS,以及市場(chǎng)使用較為廣泛的開源應(yīng)用服務(wù)器Jboss,等等。
1.1.2 HTTP請(qǐng)求/響應(yīng)模型
HTTP是一個(gè)屬于應(yīng)用層的面向?qū)ο蟮膮f(xié)議,由于簡(jiǎn)潔、快速的方式,適用于分布式超媒體信息系統(tǒng)。
HTTP協(xié)議基于請(qǐng)求/響應(yīng)模型,因此存在兩種HTTP消息:請(qǐng)求消息和響應(yīng)消息。一個(gè)完整的HTTP會(huì)話過(guò)程如圖1-2所示。

圖1-2 HTTP請(qǐng)求響應(yīng)模型
首先,客戶端與Web服務(wù)器建立連接,通常通過(guò)默認(rèn)的80端口:服務(wù)器Server偵聽HTTP協(xié)議端口80等待客戶端Browser的Socket連接請(qǐng)求;Browser向Server發(fā)出Socket連接請(qǐng)求,兩邊Socket建立連接。
建立連接后,客戶端向Web服務(wù)器發(fā)送HTTP請(qǐng)求消息:Browser通過(guò)Socket連接的OutputStream向Server發(fā)送HTTP請(qǐng)求消息。
Web服務(wù)器處理請(qǐng)求,并將響應(yīng)消息傳送給客戶端:Server通過(guò)Socket連接的InputStream得到請(qǐng)求,分析處理后通過(guò)Socket連接的OutputStream返回響應(yīng)消息。
這樣一個(gè)來(lái)回后,這個(gè)連接就關(guān)閉了(Browser/Server關(guān)閉Socket連接)。HTTP協(xié)議默認(rèn)使用80端口進(jìn)行訪問(wèn)。
HTTP協(xié)議是一個(gè)無(wú)狀態(tài)的協(xié)議。也就是說(shuō),每當(dāng)客戶端訪問(wèn)Web服務(wù)器上某個(gè)Web頁(yè)面時(shí),都要建立與服務(wù)器的一個(gè)獨(dú)立的連接。服務(wù)器不保留前一次訪問(wèn)的任何信息。Web服務(wù)器將客戶端對(duì)某個(gè)頁(yè)面的每次訪問(wèn)都當(dāng)作相互無(wú)關(guān)的訪問(wèn)來(lái)處理;服務(wù)器不會(huì)自動(dòng)保留客戶機(jī)的狀態(tài)信息。所以HTTP協(xié)議是一個(gè)無(wú)狀態(tài)的協(xié)議。正因?yàn)槿绱?,服?wù)器端需要采取一定的措施來(lái)保留用戶的狀態(tài)數(shù)據(jù),這就是狀態(tài)管理。
請(qǐng)求消息和響應(yīng)消息格式都包括起始行、零個(gè)或多個(gè)題頭域、一個(gè)空行后的消息體。這里空行表示消息題頭的結(jié)束,消息體是可選的。
請(qǐng)求消息的起始行就是請(qǐng)求行。它通常都是請(qǐng)求消息的首行,包含三個(gè)域:HTTP方法、通用資源標(biāo)示符(URI)、HTTP協(xié)議版本。
盡管有幾種HTTP方法可以從服務(wù)器中檢索數(shù)據(jù),但是最常用的通常僅有GET和POST方法。GET方法向服務(wù)器請(qǐng)求資源,由請(qǐng)求URI指示請(qǐng)求地址。如果URI指向生成數(shù)據(jù)的資源,如Servlet,則數(shù)據(jù)在響應(yīng)消息中返回。如果要提交表單數(shù)據(jù),可用POST方法。盡管GET方法可以在查詢字符串中傳遞信息,但是一般使用POST方法明確傳遞服務(wù)器數(shù)據(jù),這些數(shù)據(jù)可以被請(qǐng)求URI用來(lái)進(jìn)行處理。
URI標(biāo)志用來(lái)處理請(qǐng)求的資源。它可以是絕對(duì)路徑,也可以是相對(duì)路徑。無(wú)效的URI請(qǐng)求會(huì)返回錯(cuò)誤代碼(通常是404)。
HTTP請(qǐng)求協(xié)議版本告訴服務(wù)器:請(qǐng)求遵循了那個(gè)HTTP規(guī)范版本。
HTTP請(qǐng)求可能包含零個(gè)或多個(gè)題頭域。請(qǐng)求題頭域允許客戶端向服務(wù)器傳遞有關(guān)請(qǐng)求和客戶端本身的一些附加信息。請(qǐng)求消息和響應(yīng)消息的題頭域的格式是相同的,首先是題頭域的名稱,接著是冒號(hào)和值。如果對(duì)同一題頭域規(guī)定了多個(gè)值,它們必須用逗號(hào)隔開。如表1-1是常見的題頭域。
表1-1 HTTP消息題頭域

下面來(lái)構(gòu)造一個(gè)實(shí)例查看請(qǐng)求消息格式:編寫一個(gè)Java應(yīng)用程序HttpRequestViewer,該應(yīng)用程序模擬Web服務(wù)器監(jiān)聽瀏覽器發(fā)出的HTTP請(qǐng)求,截獲HTTP請(qǐng)求消息并在控制臺(tái)將請(qǐng)求消息格式打印出來(lái)。詳見【代碼1-1】。
【代碼1-1】 HttpRequestViewer.java。
/**此示例使用Java網(wǎng)絡(luò)編程技術(shù)實(shí)現(xiàn)*/ public class HttpRequestViewer{ public static void main(String args[]){ try{ ServerSocket serversocket=new ServerSocket(8080);//服務(wù)器套接字, 綁定8080端口 Socket socket=serversocket.accept();//與客戶端建立Socket連接 InputStream is=socket.getInputStream();//從套接字對(duì)象中得到輸入流對(duì)象 InputStreamReader isr=new InputStreamReader(is);//字節(jié)流轉(zhuǎn)換為字符流 BufferedReader br=new BufferedReader(isr);//包裝成緩沖流 char charry[]=new char[1024];//定義緩沖區(qū) int charrylength=br.read(charry);//讀取內(nèi)容 for(int i=0;i<charrylength;i++){//將讀取內(nèi)容循環(huán)輸出到控制臺(tái) System.out.print(charry[i]); } br.close(); //關(guān)閉流對(duì)象 socket.close();serversocket.close();//關(guān)閉套接字連接 }catch(Exception e){ System.out.println("異常發(fā)生:"+e.getMessage()); } } }
編譯并運(yùn)行該Java應(yīng)用程序,此時(shí)該應(yīng)用程序?qū)⒔壎?080端口進(jìn)行監(jiān)聽。打開IE瀏覽器,在地址欄中向該模擬服務(wù)器發(fā)出資源請(qǐng)求,確保是向8080端口請(qǐng)求,例如,輸入:http://localhost:8080/testweb/testhello.html,此時(shí)Java應(yīng)用程序模擬的Web服務(wù)器正偵聽8080端口,當(dāng)與瀏覽器建立連接后,程序就會(huì)獲取瀏覽器發(fā)出的請(qǐng)求消息,并打印在控制臺(tái)上??稍诳刂婆_(tái)看到如圖1-3所標(biāo)示的請(qǐng)求消息格式。

圖1-3 請(qǐng)求消息格式
一旦服務(wù)器接收和處理了請(qǐng)求消息,它就必須向客戶端返回一條響應(yīng)消息。響應(yīng)消息的起始行稱為狀態(tài)行,此外同樣包含零個(gè)或多個(gè)題頭域,空行后一個(gè)可選的消息體。
HTTP響應(yīng)的狀態(tài)行包括響應(yīng)消息所采用的HTTP協(xié)議版本,之后是響應(yīng)狀態(tài)碼和狀態(tài)描述。這些字段之間以空格隔開。狀態(tài)碼是三位數(shù)字值,用于描述服務(wù)器的響應(yīng)狀態(tài)。如表1-2所示是常見的HTTP響應(yīng)狀態(tài)碼。
表1-2 HTTP響應(yīng)狀態(tài)碼

下面同樣來(lái)構(gòu)造一個(gè)實(shí)例查看響應(yīng)消息格式:編寫一個(gè)Java應(yīng)用程序HttpResponseViewer,模擬瀏覽器查看服務(wù)器返回的HTTP響應(yīng),在控制臺(tái)將HTTP響應(yīng)消息格式打印出來(lái),詳見【代碼1-2】。
【代碼1-2】 HttpResponseViewer.java。
public class HttpResponseViewer{ public static void main(String args[]){ try{ Socket socket=new Socket("localhost",8080); //構(gòu)建套接字向服務(wù) 器發(fā)出連接請(qǐng)求 InputStreamReader isr=new InputStreamReader(socket. getInputStream()); BufferedReader br=new BufferedReader(isr); PrintWriter pw=new PrintWriter(socket.getOutputStream());// 從套接字中得到輸出流并包裝 pw.println("GET /index.html HTTP/1.1");//向服務(wù)器發(fā)送請(qǐng)求消息 pw.println("HOST:localhost:8080"); pw.println(); pw.flush(); char charry[]=new char[1024*100]; int charrylength=br.read(charry);//讀取從服務(wù)器反饋的響應(yīng)消息 for(int i=0;i<charrylength;i++){//在控制器臺(tái)環(huán)把響應(yīng)消息打印出來(lái) System.out.print(charry[i]); } br.close(); pw.close(); socket.close(); }catch(Exception e){ System.out.println("異常發(fā)生:"+e.getMessage()); } } }
編譯該Java應(yīng)用程序得到字節(jié)碼文件,首先啟動(dòng)一個(gè)Web服務(wù)器如Tomcat6.0,這個(gè)Web服務(wù)器默認(rèn)會(huì)偵聽8080端口的HTTP請(qǐng)求,接著使用Java解釋器解釋執(zhí)行Java應(yīng)用程序的字節(jié)碼文件,模擬瀏覽器向Web服務(wù)器發(fā)出請(qǐng)求,并且接受Web服務(wù)器發(fā)出的響應(yīng)消息??梢栽诳刂婆_(tái)看到如圖1-4所示的響應(yīng)消息格式。

圖1-4 HTTP響應(yīng)消息格式
1.1.3 Web應(yīng)用發(fā)展
早期的或者最簡(jiǎn)單的Web站點(diǎn)只提供Web頁(yè)面的靜態(tài)信息訪問(wèn),如圖1-5所示。也就是說(shuō),Web服務(wù)器所做的工作僅僅是將客戶端發(fā)來(lái)的HTTP請(qǐng)求映射到文件系統(tǒng)的某個(gè)頁(yè)面,然后把這個(gè)頁(yè)面,通常就是普通的HTML頁(yè)面?zhèn)魉徒o客戶端。應(yīng)用程序不會(huì)根據(jù)用戶傳入數(shù)據(jù)的不同而修改Web頁(yè)面。

圖1-5 靜態(tài)Web
后來(lái)出現(xiàn)了Applet,Applet被稱為客戶端的Java小應(yīng)用程序,它必須嵌入到網(wǎng)頁(yè)中,由瀏覽器內(nèi)嵌的JVM執(zhí)行。當(dāng)用戶發(fā)出HTTP請(qǐng)求后,Applet隨著Web頁(yè)面一起被下載到客戶端,Applet在瀏覽器的JVM中運(yùn)行,能夠提供動(dòng)態(tài)的頁(yè)面內(nèi)容,從一定程度上擴(kuò)展了Web服務(wù)器的功能,如圖1-6所示。

圖1-6 Applet
但是,基于安全性考慮,Applet被限制不能訪問(wèn)后臺(tái)數(shù)據(jù)。這就出現(xiàn)了運(yùn)行在服務(wù)器端的Servlet,如圖1-7所示。

圖1-7 Servlet
Servlet同樣也是一段Java程序,稱為服務(wù)器端Java小應(yīng)用程序,它會(huì)根據(jù)用戶提交的數(shù)據(jù)不同而產(chǎn)生不同的響應(yīng)頁(yè)面,從而提供與用戶動(dòng)態(tài)的交互。
Servlet是運(yùn)行在服務(wù)器上的Web Container中,也就是Web容器。Web應(yīng)用服務(wù)器提供Container,用于管理像Servlet這樣的服務(wù)器端組件。
所以,當(dāng)一個(gè)HTTP請(qǐng)求傳到服務(wù)器,Web Server Plugin會(huì)檢測(cè)這次請(qǐng)求的是靜態(tài)資源還是動(dòng)態(tài)資源。如果是靜態(tài)資源,請(qǐng)求會(huì)傳遞給Web Server,Web Server將請(qǐng)求直接映射到文件系統(tǒng)中的某個(gè)頁(yè)面,原封不動(dòng)地將頁(yè)面?zhèn)骰亟o客戶端。但如果請(qǐng)求的是動(dòng)態(tài)資源,Web Server Plugin會(huì)將請(qǐng)求傳遞給Web Container,Web Container會(huì)調(diào)用相應(yīng)的Servlet,根據(jù)用戶提交數(shù)據(jù)的不同,將動(dòng)態(tài)內(nèi)容傳回給客戶端。
由于這些動(dòng)態(tài)內(nèi)容是由Servlet通過(guò)硬編碼方式輸出HTML頁(yè)面,這帶來(lái)一個(gè)問(wèn)題:就是僅使用Servlet往往會(huì)把業(yè)務(wù)邏輯和顯示邏輯混合在一起。
為了更好地分離視圖、控制和業(yè)務(wù)邏輯,JSP技術(shù)應(yīng)運(yùn)而生。通常用JSP頁(yè)面來(lái)顯示給用戶的數(shù)據(jù),用Servlet控制頁(yè)面的流程,如圖1-8所示。Servlet會(huì)根據(jù)請(qǐng)求信息的不同或應(yīng)用程序邏輯的設(shè)定而調(diào)用相應(yīng)的JSP頁(yè)面。JSP和Servlet都可以調(diào)用JavaBean,從而產(chǎn)生動(dòng)態(tài)內(nèi)容。

圖1-8 JSP
EJB即Enterprise JavaBean,它提供了對(duì)業(yè)務(wù)邏輯數(shù)據(jù)的封裝。如圖1-9所示,JavaBean可以通過(guò)Web Container來(lái)訪問(wèn)EJB,Java應(yīng)用程序客戶端也可以訪問(wèn)EJB。

圖1-9 EJB的訪問(wèn)
當(dāng)應(yīng)用程序規(guī)模較大、有較多的訪問(wèn)請(qǐng)求時(shí),就需要提供更好的可擴(kuò)展性,更好的訪問(wèn)性能。如圖1-10所示,需要將應(yīng)用程序部署在服務(wù)器集群上,通過(guò)網(wǎng)絡(luò)路由器等來(lái)實(shí)現(xiàn)負(fù)載均衡,從而提供更強(qiáng)的靈活性。

圖1-10 Web擴(kuò)展性
- Learning NServiceBus(Second Edition)
- Oracle從新手到高手
- 劍指Offer(專項(xiàng)突破版):數(shù)據(jù)結(jié)構(gòu)與算法名企面試題精講
- Elastic Stack應(yīng)用寶典
- Effective Python Penetration Testing
- Flutter跨平臺(tái)開發(fā)入門與實(shí)戰(zhàn)
- JavaCAPS基礎(chǔ)、應(yīng)用與案例
- Kubernetes進(jìn)階實(shí)戰(zhàn)
- Hacking Android
- Clojure Web Development Essentials
- Java Web動(dòng)態(tài)網(wǎng)站開發(fā)(第2版·微課版)
- Splunk Essentials
- Web開發(fā)新體驗(yàn)
- Spring Boot 2+Thymeleaf企業(yè)應(yīng)用實(shí)戰(zhàn)
- jQuery基礎(chǔ)教程(第4版)