詳解Java開發(fā)Web應用程序的底層原理
前言
前面一篇文章,我從整個應用程序的整體以及跟運行環(huán)境的關系簡單聊了一下我們現(xiàn)在常用的Spring框架的設計基礎和原則,其中主要是控制反轉(zhuǎn)和依賴注入,以及容器化編程等概念。
這里我不想去復述這些概念的定義,因為那些東西網(wǎng)上隨便都能百度到,我想通過我的描述將這些概念串聯(lián)起來,讓大家更好的去立即它們知道為什么要這樣去做,我們每天開發(fā)使用的框架到底是個什么東西,它的設計思想以及規(guī)范的由來。做到知其然還知其所以然,能夠讓我們在開發(fā)過程中更好的去使用它們,面對問題知道它大概的解決方向。
本文我想繼續(xù)沿著前面的思路來談談基于Web的應用程序需要使用Spring框架的容器化管理開發(fā)相關的理解。
Web應用程序與Servlet規(guī)范
當然說起應用程序開發(fā)來,我們都熟悉,現(xiàn)在應用程序有很多種分類,最初的控制臺程序,服務組件程序,到桌面應用程序,到基于HTTP訪問協(xié)議的web應程序等。
其實它們的本質(zhì)就是基于某種輸入/輸出過程處理的程序。比如我們最常見但是實際應用中很少的控制臺應用程序,它就是基于標準的I/O實現(xiàn)類的應用程序,接收命令行作為輸入流,控制臺作為標準輸出形式的應用程序。它的運行只需要有一個進程殼來構(gòu)建輸入和輸出流即可。
而對于我們今天要詳細談的Web應用程序,其實它是起源于一種運行在操作系統(tǒng)上在組件程序,只不過它們的數(shù)據(jù)輸入輸出是基于網(wǎng)絡數(shù)據(jù)流的。
網(wǎng)絡基礎
從基礎的網(wǎng)絡知識我們知道,網(wǎng)絡上傳輸數(shù)據(jù)需要通過一個7層模型,也就是從最初的網(wǎng)絡硬件抽象定義到最高級的應用程序這個層級穿透而來。
要將兩臺物理的機器連接起來,我們需要對兩個機器進行標識命名,這些靠的是IP和端口,而網(wǎng)絡鏈路上傳輸?shù)臄?shù)據(jù)都是字節(jié)數(shù)據(jù)流,要知道這些數(shù)據(jù)流是沒有什么具體格式的,但是到了網(wǎng)絡層時,我們必須要知道它來自哪里要發(fā)給誰,所以我們需要對其進行一定的格式限制,這種抽象是通過電報格式定義來完成的。
比如我們需要定義發(fā)送的長度,標記為,是否有順序等,這些字節(jié)流就被包裹成一個個數(shù)據(jù)報文,然后我們必須定義每個發(fā)送端和接收端之間的約定,就是告訴對方我發(fā)送的是什么,你該如何接收它,比如多長是一個完整數(shù)據(jù)包,數(shù)據(jù)包的先后順序等,這些都是在我們知道了兩個通信店的IP地址以及如何連接也就是我們說的傳輸控制協(xié)議TCP的基礎上我們定義了更高級的應用協(xié)議,比如HTTP,F(xiàn)ILE,MAIL等協(xié)議,當然最常見的協(xié)議就是HTTP協(xié)議了。
HTTP協(xié)議
它是在基礎的報文格式定義基礎上的一個更高級的抽象,它能告訴通信雙方我們通信的數(shù)據(jù)如何解析。就拿超文本傳輸協(xié)議來說吧,它規(guī)定了頭信息和內(nèi)容信息,它還規(guī)定了處理這些信息的方法以及結(jié)果反饋代碼,這就是我們常說的GET,POST,DELETE,OPTIONS等,返回代碼比如從100,到500系列,當然這些已經(jīng)進入到了應用協(xié)議部分。
我們所有的網(wǎng)絡應用程序都是基于點對點的通信的應用。也就是說要創(chuàng)建這樣的程序我們必須首先標識出能夠連接的兩個點,首先是主機名或者IP地址,然后就是我們要連接的具體應用,它一般會體現(xiàn)在哪個端口上或者端口下面的哪個路徑上。有了對等點的定義描述,我們就可以定義其控制傳輸?shù)某橄?,套接字概念?/p>
編解碼問題
其實質(zhì)上就是創(chuàng)建一個輸入輸出流的通道,網(wǎng)絡數(shù)據(jù)流都是通過Socket這個概念來定義和描述的。而我們編程,特別是Java的編程,我們只需要在我們應用程序所管理的空間中定義一個可以連接網(wǎng)絡Socket的通道,同時在內(nèi)存中劃出一塊緩沖區(qū),讓通道能夠有可操作的空間,然后利用不同緩沖區(qū)之間數(shù)據(jù)流動的過程對數(shù)據(jù)進行相應形式的變化,比如最基礎的是如何將網(wǎng)絡傳輸?shù)淖止?jié)流,轉(zhuǎn)變?yōu)槲覀兏呒壵Z言定義高級數(shù)據(jù)類型的過程,這個過程通常被稱為解碼,同樣當我們需要將應用程序能夠理解的各種數(shù)據(jù)類型轉(zhuǎn)變?yōu)榭捎猛ㄟ^網(wǎng)絡傳輸給其它地方的字節(jié)流時,這個過程被稱為編碼。
因為硬件能夠搞懂的目前就兩種狀態(tài),這兩種狀態(tài)用數(shù)字表示就是二進制的0和1,所以我們使用的眾多高級編程語言里,哪些復雜的數(shù)據(jù)類型都需要轉(zhuǎn)變成二進制的字節(jié)形式才可以被CPU理解,被網(wǎng)絡硬件傳輸。因此我們的編程不可避免的就需要去完成這種編碼和解碼處理。當然隨著高級語言的不斷進化,各類常用的處理都已經(jīng)變身為各種語言標準的類庫或者功能包了,我們只需要拿來用即可。除非你想編寫自己的通信協(xié)議或者定義特殊的數(shù)據(jù)格式,否則對于編解碼來說一般不會涉及到。
了解了所有應用程序都是對數(shù)據(jù)流的處理這個基礎后,我們再來看Web應用程序,它是一種基于網(wǎng)絡的服務和獨立訪問結(jié)構(gòu)的應用程序,也就是我們常說的Server-Client模式。
關于Servlet
這里之所以定義出服務端和客戶端其實主要還是一個功能上的區(qū)分,但是底層實質(zhì)上還是兩臺計算機的連接,通過字節(jié)流交換數(shù)據(jù),通過協(xié)議來規(guī)定傳輸控制和數(shù)據(jù)解碼,而我們的web應用程序就是基于HTTP協(xié)議的網(wǎng)絡應用程序,因為涉及到的網(wǎng)絡處理,所以技術人員將有關網(wǎng)絡處理部分獨立出來規(guī)定了很多規(guī)范,比如端點描述規(guī)范,數(shù)據(jù)傳輸格式規(guī)范,如何利用所在計算機操作系統(tǒng)環(huán)境的設置的規(guī)范等,這些反應到Java編程里就是我們都熟悉的Servlet規(guī)范。
這個規(guī)范首先告訴我們基于Web的應用程序的基礎網(wǎng)絡部分需要在每臺聯(lián)網(wǎng)計算機上有一個角色來負責,我們稱這個角色為容器,或者說是web服務器。
它就是要實現(xiàn)對計算機網(wǎng)絡的標識,連接,規(guī)定解析數(shù)據(jù)格式等工作,當然后來我們將其發(fā)展成綜合性的服務器,一邊要處理HTTP協(xié)議,一邊還可以通過一些接口更操作系統(tǒng)進行交互調(diào)用操作系統(tǒng)的功能組件來處理。比如網(wǎng)卡,文件的輸入輸出控制器等等。
我們可以簡單的描述一下一個Servlet容器的實現(xiàn)功能,首先它需要對運行自己的主機信息有一個抽象,能夠讓運行的程序了解它以及使用它可以使用的資源。
然后,它需要將基于網(wǎng)絡的字節(jié)流進行高級語言數(shù)據(jù)類型的轉(zhuǎn)換,比如將基于字節(jié)流解析成遵循HTTP協(xié)議的數(shù)據(jù)格式,HttpRequest,HttpResponse以及HttpServletRequest,HttpServletResponse等。
同時將對于宿主服務器環(huán)境參數(shù)抽象后引入到該容器中用于跟我們的應用程序交互。所以只要實現(xiàn)了Servlet的規(guī)范,就可以作為操作系統(tǒng)和我們應用程序之間的媒介。
Servlet容器
市場上有許多成熟Servlet容器產(chǎn)品比如Tomcat,Jetty,Weblogic,Glassfish等。這里面有很多輕量級的,只負責將輸入的網(wǎng)絡數(shù)據(jù)流轉(zhuǎn)換為我們應用程序能夠理解和處理的數(shù)據(jù)形式,而這個過程都是通過創(chuàng)建輸入和輸出數(shù)據(jù)流的過程來完成的。有一些商業(yè)應用的實現(xiàn)附加的內(nèi)容比較多,比如對系統(tǒng)環(huán)境資源的抽象繼承,比如數(shù)據(jù)庫連接資源,文件輸入輸出組件等。
我們開發(fā)的應用程序根據(jù)我們設計開發(fā)原則,我們首先將應用分解能功能組件,將每個功能組件設計成一個可以在容器中獨立運行的組件,該組件就是HttpServlet請求處理組件。
我們會根據(jù)請求的目標地址來標記各種功能,然后將這些唯一的目標地址和HTTP方法來標識運行的目標組件,而這個組件可以通過容器來計算機環(huán)境進行交互。
所以Servlet的頂層抽象就是一個service方法,該方法的輸入?yún)?shù)就是由容器進行封裝過的請求體和回復體以及環(huán)境變量對象等。
當然我們會根據(jù)HTTP協(xié)議來具體的細化其支持的HTTP方法,所以我們可以來通過doGet,doPost等方法來完成具體的處理。
有了我們這樣一個基礎規(guī)范的功能實現(xiàn),我們就有了一個可以包容和管理具體功能應用組件的容器,這個容器就是我們所說的web服務器。
如果你清楚了Servlet規(guī)范的本質(zhì)就是對網(wǎng)絡數(shù)據(jù)流的封裝和編解碼處理,你可以自己動手從基礎的二進制數(shù)據(jù)流的封裝和編解碼轉(zhuǎn)換開始設計自己的web應用服務器。
也就是去實現(xiàn)點對點的通信處理,這里說一下,目前的微服務架構(gòu)的基礎就是對web服務器基礎網(wǎng)絡實現(xiàn)的重新分解設計。
Servlet 3.0 引入了反應流概念,就是通過接收方控制來管理大批量數(shù)據(jù)流的輸入輸出。
Spring框架和Web應用設計
了解了上面有關Web應用程序的結(jié)構(gòu)后,我們再來看看Spring框架在web應用程序開發(fā)中扮演的角色是什么。
我們知道Java企業(yè)級開發(fā)中有Java EE框架,其實就是基于Servlet容器來的,它只是將企業(yè)級應用開發(fā)的所有基礎功能都組件化了,比如容器化依賴注入,JPA等,當然必須有匹配的Web應用服務器來支持其運行。
同樣的Spring框架的核心部分就是組件容器,它的功能是通過更加有效更加輕量級的去組織和管理應用程序各功能組件。
其巧妙之處在于將整個組件設計成了一個Servlet組件實現(xiàn),這就是Spring框架里最為核心的DispatchServlet,跟所有Servlet定義規(guī)范一樣,我們需要用一個請求的目標路徑來標識這個Servlet,然后讓Servlet容器在啟動時將它加載,并綁定到目標路徑上,以此在一個對根目錄請求的處理器中啟動一個應用程序組件管理容器,并將其處理器handler實現(xiàn)成一個前端控制模式,負責對其根目錄后的URL部分進行識別和匹配,以此來實現(xiàn)對Spring容器中負責處理后續(xù)URL資源的處理器的路由。
簡單說來,就是當外部訪問請求通過網(wǎng)絡到達Web服務器時,會將其根據(jù)Servlet規(guī)范和HTTP協(xié)議將其解碼成HttpServlet的請求和回復數(shù)據(jù)結(jié)構(gòu)類型,然后解析其訪問的目標資源URL,來匹配我們在Spring容器中注冊的用于處理它的組件和方法名稱,從而完成對該Servlet請求的處理。
由于現(xiàn)在我們開發(fā)應用程序時除了連續(xù)的文件上傳下載處理外,大多都是將二進制轉(zhuǎn)換為JSON數(shù)據(jù)格式或者XML格式,如此我們只需要在Spring容器中注冊相應的處理組件即可。
總結(jié)
說到這里想說的東西還沒說完,但是文章長度已經(jīng)超出了預期,所以就此打住吧,只能在接下來另辟文章繼續(xù)講了。
本篇文章簡單的講了一下從Web應用程序的特點,以及能夠輔助Web應用程序運行的基礎容器服務規(guī)范,進而到了Spring框架的設計原則和結(jié)構(gòu)實現(xiàn)設計。
這里希望能夠帶大家從Web應用程序有別于其他類型的應用程序的特點開始,到支持Web應用程序運行的Servlet規(guī)范實現(xiàn),在到Spring框架應用在Web應用程序時扮演的角色等內(nèi)容過了一遍。接下來我會繼續(xù)沿著這個思路,講一下MVC模式,以及反應流處理模式等內(nèi)容。