Servlet工作原理:
1、首先簡單解釋一下Servlet接收和響應客戶請求的過程,首先客戶發(fā)送一個請求,Servlet是調用service()方法對請求進行響應的,通過源代碼可見,service()方法中對請求的方式進行了匹配,選擇調用doGet,doPost等這些方法,然后再進入對應的方法中調用邏輯層的方法,實現(xiàn)對客戶的響應。
2、每一個自定義的Servlet都必須實現(xiàn)Servlet的接口,Servlet接口中定義了五個方法,其中比較重要的三個方法涉及到Servlet的生命周期,分別是上文提到的init(),service(),destroy()方法。
3、Servlet接口和GenericServlet是不特定于任何協(xié)議的,而HttpServlet是特定于HTTP協(xié)議的類,所以HttpServlet中實現(xiàn)了service()方法,并將請求ServletRequest、ServletResponse 強轉為HttpRequest 和 HttpResponse。
1、Servlet執(zhí)行過程
用戶請求一個Servlet,Servlet容器自動構建請求和響應對象,然后執(zhí)行Servlet的service()方法,該方法會接收請求和響應對象,通過響應對象將處理結果發(fā)送給用戶。
2、怎么請求Servlet
用戶通過一個URL來請求一個Servlet。
3、怎么處理請求
當用戶請求Servlet時,容器構建ServletRequest對象request,并傳遞給Servlet的Service()方法,Service通過request對象獲取到請求的參數(shù),然后,根據(jù)參數(shù)做出相應的處理,通過ServletResponse對象來向客戶端發(fā)送響應內容。
4、怎么響應客戶端
當用戶請求一個Servlet時候,容器會自動創(chuàng)建ServletResponse對象response,然后通過response對象向客戶端發(fā)送響應內容。
5、Servlet的生命周期
a、Servlet的class經過部署,并啟動容器
b、(當請求該Servlet時候)容器自動創(chuàng)建Servlet對象xServlet,然后xServlet調用其init()方法。到此,Servlet初始化結束。
c、一旦客戶端請求該Servlet,xServlet自動調用service()來處理請求。
d、一旦很長時間都沒有請求該Servlet(或者說該Servlet超時),則容器會將xServlet從容器中清除掉。
servlet的工作機制如下:
①客戶端(瀏覽器)在地址欄輸入一個URL發(fā)起HTTP請求。
②服務器根據(jù)URL指定要執(zhí)行的Servlet。
③servlet運行service方法,并給服務器作出相應。
④服務器接收到了servlet的響應數(shù)據(jù),將數(shù)據(jù)返回給請求者。
⑤客戶端接受響應數(shù)據(jù),作出展示。
應該是tomcat里創(chuàng)建響應的socketServer線程類接收請求連接,然后在再創(chuàng)建或引用對應的servlet實例來處理請求連接。servlet是單例的,只創(chuàng)建一次。所以最好不要使用serlvet中的實例字段。。
hashmap面試經常會被問到底層的數(shù)據(jù)結構是什么,以及jdk1.7和1.8兩個版本hashmap的區(qū)別
AQS核心思想是,如果被請求的共享資源空閑,則將當前請求資源的線程設置為有效的工作線程,并且將共享資源設置為鎖定狀態(tài)。如果被請求的共享資源被占用,那么就需要一套線程阻塞等待以及被喚醒時鎖分配的機制,這個機制AQS是用CLH隊列鎖實現(xiàn)的,即將暫時獲取不到鎖的線程加入到隊列中。 AQS使用一個voliate int成員變量來表示同步狀態(tài),通過內置的FIFO隊列來完成獲取資源線程的排隊工作。AQS使用CAS對該同步狀態(tài)進行原子操作實現(xiàn)對其值的修改。
AQS定義了兩種資源獲取方式:獨占(只有一個線程能訪問執(zhí)行,又根據(jù)是否按隊列的順序分為公平鎖和非公平鎖,如ReentrantLock) 和共享(多個線程可同時訪問執(zhí)行,如Semaphore/CountDownLatch,Semaphore、CountDownLatCh、 CyclicBarrier )。ReentrantReadWriteLock 可以看成是組合式,允許多個線程同時對某一資源進行讀。
AQS底層使用了模板方法模式, 自定義同步器在實現(xiàn)時只需要實現(xiàn)共享資源 state 的獲取與釋放方式即可,至于具體線程等待隊列的維護(如獲取資源失敗入隊/喚醒出隊等),AQS已經在上層已經幫我們實現(xiàn)好了。
vue的底層原理面試題有,vue如何實現(xiàn)數(shù)據(jù)的響應式?利用object.defineObject來實現(xiàn)的。
dom_diff的算法?
還有v_model的實現(xiàn)原理?以及生命周期是怎樣實現(xiàn)的?
還有nextTick的實現(xiàn)原理等等,這些都是vue的底層面試題
當客戶端發(fā)出Web資源的請求時,Web服務器根據(jù)應用程序配置文件設置的過濾規(guī)則進行檢查,客戶請求滿足過濾規(guī)則,則對客戶請求/響應進行攔截,對請求頭和請求數(shù)據(jù)進行檢查或改動,并依次通過過濾器鏈,最后把請求/響應交給請求的Web資源處理。
請求信息在過濾器鏈中可以被修改,也可以根據(jù)條件讓請求不發(fā)往資源處理器,并直接向客戶機發(fā)回一個響應。
很多人可能和我當初一樣,把Servlet和太多東西聯(lián)系起來。其實Servlet本身在Tomcat中是“非常被動”的一個角色,處理的事情也很簡單。網絡請求與響應,不是他的主要職責,它其實更偏向于業(yè)務代碼。所謂的Request和Response是Tomcat傳給它,用來處理請求和響應的工具,但它本身不處理這些。
文章會比較長,但是看完會拔高你看待Servlet的視角。
主要內容:
類似于Servlet是Server Applet(運行在服務端的小程序)等其他博文已經提過的內容,這里就不重復了。它就是用來處理請求的業(yè)務邏輯的。
之前在Tomcat外傳中我們聊過,所謂Tomcat其實是Web服務器和Servlet容器的結合體。
什么是Web服務器?
比如,我當前在杭州,你能否用自己的電腦訪問我桌面上的一張圖片?恐怕不行。我們太習慣通過URL訪問一個網站、下載一部電影了。一個資源,如果沒有URL映射,那么外界幾乎很難訪問。而Web服務器的作用說穿了就是:將某個主機上的資源映射為一個URL供外界訪問。
什么是Servlet容器?
Servlet容器,顧名思義里面存放著Servlet對象。我們?yōu)槭裁茨芡ㄟ^Web服務器映射的URL訪問資源?肯定需要寫程序處理請求,主要3個過程:
任何一個應用程序,必然包括這三個步驟。其中接收請求和響應請求是共性功能,且沒有差異性。訪問淘寶和訪問京東,都是接收http://www.taobao.com/brandNo=1,響應給瀏覽器的都是JSON數(shù)據(jù)。于是,大家就把接收和響應兩個步驟抽取成Web服務器:
但處理請求的邏輯是不同的。沒關系,抽取出來做成Servlet,交給程序員自己編寫。
當然,隨著后期互聯(lián)網發(fā)展,出現(xiàn)了三層架構,所以一些邏輯就從Servlet抽取出來,分擔到Service和Dao。
但是Servlet并不擅長往瀏覽器輸出HTML頁面,所以出現(xiàn)了JSP。JSP的故事,我已經在怎樣學習JSP?講過了。
等Spring家族出現(xiàn)后,Servlet開始退居幕后,取而代之的是方便的SpringMVC。SpringMVC的核心組件DispatcherServlet其實本質就是一個Servlet。但它已經自立門戶,在原來HttpServlet的基礎上,又封裝了一條邏輯。
總之,很多新手程序員框架用久了,甚至覺得SpringMVC就是SpringMVC,和Servlet沒半毛錢關系。
不知道從什么時候開始,我們已經不再關心、甚至根本不知道到底誰調用了我寫的這個程序,反正我寫了一個類,甚至從來沒new過,它就跑起來了...
我們把模糊的記憶往前推一推,沒錯,就是在學了Tomcat后!從Tomcat開始,我們再也沒寫過main方法。以前,一個main方法啟動,程序間的調用井然有序,我們知道程序所有流轉過程。
但是到了Javaweb后,Servlet/Filter/Listener一路下來我們越學越沮喪。沒有main,也沒有new,寫一個類然后在web.xml中配個標簽,它們就這么兀自運行了。
其實,這一切的一切,簡單來說就是“注入”和“回調”。想象一下吧朋友們,Tomcat里有個main方法,假設是這樣的:
其實,編程學習越往后越是如此,我們能做的其實很有限。大部分工作,框架都已經幫我們做了。只要我們實現(xiàn)xxx接口,它會幫我們創(chuàng)建實例,然后搬運(接口注入)到它合適的位置,然后一套既定的流程下來,肯定會執(zhí)行到。我只能用中國一個古老的成語形容這種開發(fā)模式:閉門造車,出門合轍(敲黑板,成語本身是夸技術牛逼,而不是說某人瞎幾把搞)。
很多時候,框架就像一個傀儡師,我們寫的程序是傀儡,頂多就是給傀儡化化妝、打扮打扮,實際的運作全是傀儡師搞的。
了解到這個層面后,JavaWeb三大組件任何生命周期相關的方法、以及調用時Tomcat傳入的形參,這里就不再強調。肯定是程序的某處,在創(chuàng)建實例后緊接著就傳入?yún)?shù)調用了唄。沒啥神秘的。
首先,我們心里必須有一個信念:我們都是菜雞,框架肯定不會讓我們寫很難的代碼。
所以Servlet既然交給我們實現(xiàn),肯定是很簡單的!
(沒有網絡請求和響應需要我們處理,都封裝好了!)
你別不服。進入Tomcat階段后,我們開始全面面向接口編程。但是“面向接口編程”這個概念,最早其實出現(xiàn)在JDBC階段。我就問你,JDBC接口是你自己實現(xiàn)的嗎?別鬧了,你導入MySQL的驅動包,它給你搞定了一切。
真正的連接過程太難寫了,朋友們。底層就是TCP連接數(shù)據(jù)庫啊,你會嗎?寫Socket,然后進行數(shù)據(jù)庫校驗,最后返回Connection?這顯然超出我們的能力范圍了。我們這么菜,JDBC不可能讓我們自己動手的。所以各大數(shù)據(jù)庫廠商體貼地推出了驅動包,里面有個Driver類,調用
driver.connect(url, username, password);
即可得到Connection。
BUT,這一次難得Tomcat竟然這么瞧得起我黃某 ,僅僅提供了javax.servlet接口,這是打算讓我自己去實現(xiàn)?
不,不可能的,肯定是因為太簡單了。
查看接口方法:
五個方法,最難的地方在于形參,然而Tomcat會事先把形參對象封裝好傳給我...除此以外,既不需要我寫TCP連接數(shù)據(jù)庫,也不需要我解析HTTP請求,更不需要我把結果轉成HTTP響應,request對象和response對象幫我搞定了。
看吧,Tomcat是不是把我們當成智障啊。
Tomcat之所以放心地交給我們實現(xiàn),是因為Servlet里主要寫的代碼都是業(yè)務邏輯代碼。和原始的、底層的解析、連接等沒有絲毫關系。最難的幾個操作,人家已經給你封裝成形參傳進來了。
也就是說,Servlet雖然是個接口,但實現(xiàn)類只是個空殼,我們寫點業(yè)務邏輯就好了。
總的來說,Tomcat已經替我們完成了所有“菜雞程序員搞不定的騷操作”,并且傳入三個對象:ServletConfig、ServletRequest、ServletResponse。接下來,我們看看這三個傳進來都是啥。
ServletConfig
翻譯過來就是“Servlet配置”。我們在哪配置Servlet來著?web.xml嘛。請問你會用dom4j解析xml得到對象嗎?
可能...會吧,就是不熟練,嘿嘿嘿。
所以,Tomcat還真沒錯怪我們,已經幫“菜雞們”搞掂啦:
也就是說,servletConfig對象封裝了servlet的一些參數(shù)信息。如果需要,我們可以從它獲取。
Request/Response
兩位老朋友,不用多介紹了。也是題主最在意的網絡請求相關的內容,其實是Tomcat處理的并封裝好了的,不需要Servlet操心。很多人看待HTTP和Request/Response的眼光過于分裂。它們的關系就像菜園里的大白菜和餐桌上的酸辣白菜一樣。HTTP請求到了Tomcat后,Tomcat通過字符串解析,把各個請求頭(Header),請求地址(URL),請求參數(shù)(QueryString)都封裝進了Request對象中。通過調用
request.getHeader();
request.getUrl();
request.getQueryString();
...
等等方法,都可以得到瀏覽器當初發(fā)送的請求信息。
至于Response,Tomcat傳給Servlet時,它還是空的對象。Servlet邏輯處理后得到結果,最終通過response.write()方法,將結果寫入response內部的緩沖區(qū)。Tomcat會在servlet處理結束后,拿到response,遍歷里面的信息,組裝成HTTP響應發(fā)給客戶端。
Servlet接口5個方法,其中init、service、destroy是生命周期方法。init和destroy各自只執(zhí)行一次,即servlet創(chuàng)建和銷毀時。而service會在每次有新請求到來時被調用。也就是說,我們主要的業(yè)務代碼需要寫在service中。
但是,瀏覽器發(fā)送請求最基本的有兩種:Get/Post,于是我們必須這樣寫:
很煩啊。有沒有辦法簡化這個操作啊?我不想直接實現(xiàn)javax.servlet接口啊。
于是,菜雞程序員找了下,發(fā)現(xiàn)了GenericServlet,是個抽象類。
我們發(fā)現(xiàn)GenericServlet做了以下改良:
放棄GenericServlet。
于是我們繼續(xù)尋找,又發(fā)現(xiàn)了HttpServlet:
它繼承了GenericServlet。
GenericServlet本身是一個抽象類,有一個抽象方法service。查看源碼發(fā)現(xiàn),HttpServlet已經實現(xiàn)了service方法:
好了,也就是說HttpServlet的service方法已經替我們完成了復雜的請求方法判斷。
但是,我翻遍整個HttpServlet源碼,都沒有找出一個抽象方法。所以為什么HttpServlet還要聲明成抽象類呢?
看一下HttpServlet的文檔注釋:
一個類聲明成抽象方法,一般有兩個原因:
HttpServlet做成抽象類,僅僅是為了不讓new。
它為什么不希望被實例化,且要求子類重寫doGet、doPost等方法呢?
我們來看一下源碼:
如果我們沒重寫會怎樣?
瀏覽器頁面會顯示:405(http.method_get_not_supported)
也就是說,HttpServlet雖然在service中幫我們寫了請求方式的判斷。但是針對每一種請求,業(yè)務邏輯代碼是不同的,HttpServlet無法知曉子類想干嘛,所以就抽出七個方法,并且提供了默認實現(xiàn):報405、400錯誤,提示請求不支持。
但這種實現(xiàn)本身非常雞肋,簡單來說就是等于沒有。所以,不能讓它被實例化,不然調用doXxx方法是無用功。
Filter用到了責任鏈模式,Listener用到了觀察者模式,Servlet也不會放過使用設計模式的機會:模板方法模式。上面的就是。這個模式我在JDBC的博文里講過了:JDBC(中)
小結:
想看看Servlet與SpringMVC關系的朋友請戳:
bravo1988:Servlet(下)最近新寫的小冊,圖文并茂,通俗易懂:
bravo1988:中級Java程序員如何進階1、大學期間,我學的是法學專業(yè),在四年的專業(yè)學習中研讀了法律,作為一個優(yōu)秀的法律人,是我的理想。
2、法院作為守護正義的最后一道防衛(wèi)線,法官作為公平正義的守護神,一直是我向往的職業(yè)。我希望能夠將我的所學運用到工作中,在法院工作中更好的學習。