面試官:Spring MVC的處理流程是怎樣的_第1頁
面試官:Spring MVC的處理流程是怎樣的_第2頁
面試官:Spring MVC的處理流程是怎樣的_第3頁
面試官:Spring MVC的處理流程是怎樣的_第4頁
面試官:Spring MVC的處理流程是怎樣的_第5頁
已閱讀5頁,還剩8頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)

文檔簡介

面試官:SpringMVC的處理流程是怎樣的?提起SpringMVC,你的第一印象是什么?一個簡化Web開發(fā)的輕量級框架?實際上,現(xiàn)代開發(fā)過程中,開發(fā)流程與開發(fā)效率的不斷提高,同時伴隨著Restful與Json相結(jié)合的方式的興起,使得多個設(shè)備跨平臺的相互調(diào)用與訪問變得簡單了許多,所以SpringMVC簡化Web開發(fā)的使命也自然而然的變?yōu)榱撕喕?wù)端開發(fā)。那么今天我們就拋開繁雜的代碼,從宏觀的角度來看一看SpringMVC對于處理請求,簡化服務(wù)端開發(fā)的解決方案是如何實現(xiàn)的。1、曾經(jīng)的王者——Servlet在筆者剛接觸到使用Java進行Web開發(fā)的時候,SpringMVC遠沒有今天這么流行,君不見曾經(jīng)的王者Servlet繁盛一時的場面。現(xiàn)在回想起來,使用Servlet進行開發(fā)雖然不像現(xiàn)在這么容易,好多的事情需要自己做,但是Servlet使得開發(fā)的邏輯變得十分清晰,尤其是在Servlet與jsp很好的承擔(dān)了各自的角色之后,再加上mvc分層思想的流行。編寫Web應(yīng)用程序在那時是一件快樂而又簡單的事情。實際上Servlet做的事情并不是很多,筆者覺得Servlet想要完成的就是統(tǒng)一請求的接受、處理與響應(yīng)的流程。網(wǎng)絡(luò)編程中繞不開的一個東東想必不用說大家也猜得到,那就是Socket。但是網(wǎng)絡(luò)需要傳輸?shù)脑捠呛軓?fù)雜的,首先需要遵循一定的協(xié)議,現(xiàn)在我們一般使用Http與Https傳輸數(shù)據(jù),而Socket就是在一些網(wǎng)絡(luò)協(xié)議之上,屏蔽了底層協(xié)議的細節(jié),為使用者提供一個統(tǒng)一的api。但是Servlet認(rèn)為Socket做的還不夠,或者說我們還要進行相應(yīng)的處理。于是Servlet(就HttpServlet來說),他將網(wǎng)絡(luò)中的請求報文進行封裝轉(zhuǎn)化成為了Request表示,在Http通信過程之中就是HttpServletRequest,而將服務(wù)端處理請求后返回的響應(yīng)統(tǒng)一的封裝為了HttpServletResponse對象。這樣做的好處是什么呢?我們作為開發(fā)者,不必再去做一些處理網(wǎng)絡(luò)請求與響應(yīng)的繁瑣之事,而只需要關(guān)注于我們的業(yè)務(wù)邏輯開發(fā)。大家有沒有發(fā)現(xiàn),每一次框架效率的提升很多時候都是在將最最重要的業(yè)務(wù)邏輯與其他任務(wù)盡可能完全的分離開,使我們總可以全身心的投入到業(yè)務(wù)邏輯的開發(fā)之中,SpringAOP是不是就是一個很好的佐證呢!那么Servlet如何使用呢?沒有Servlet使用經(jīng)歷的同學(xué)可以聽我簡單的說一說:首先我們通常要編寫一個自己的Servlet然后繼承自HttpServlet,然后重寫其doGet()與doPost()方法。這兩個方法都會將HttpServletRequest與HttpServletResponse作為參數(shù)傳遞進去,然后我們從Request中提取前端傳來的參數(shù),在相應(yīng)的doXXX方法內(nèi)調(diào)用事先編寫好的Service接口,Dao接口即可將數(shù)據(jù)準(zhǔn)備好放置到Response中并跳轉(zhuǎn)到指定的頁面即可,跳轉(zhuǎn)的方式可以選擇轉(zhuǎn)發(fā)或者重定向。Servlet使用的是模板方法的設(shè)計模式,在Servlet頂層將會調(diào)用service方法,該方法會構(gòu)造HttpServletRequest與HttpServletResponse對象作為參數(shù)調(diào)用子類重寫的doXXX()方法。然后返回請求。最后我們需要將我們編寫的自定義Servlet注冊到web.xml中,在web.xml中配置servlet-mapping來為該servlet指定處理哪些請求。Servlet的使用就是這么簡單!事實上,在很長的一段時間內(nèi)他的流行也得益于他的簡單易用易上手。<web-app

xmlns="/xml/ns/j2ee"

xmlns:xsi="/2001/XMLSchema-instance"

xsi:schemaLocation="/xml/ns/j2ee/web-app_2_4.xsd"

version="2.4">

<servlet>

<servlet-name>ShoppingServletservlet-name>

<servlet-class>com.myTest.ShoppingServletservlet-class>

servlet>

<servlet-mapping>

<servlet-name>ShoppingServletservlet-name>

<url-pattern>/shop/ShoppingServleturl-pattern>

servlet-mapping>

web-app>2、想要更進一步當(dāng)我們使用Servlet來進行業(yè)務(wù)邏輯開發(fā)的時候,時常會感覺到爽歪歪,但是爽歪歪的同時也感覺到有那么一點點不適。不適的地方主要有以下幾點:每個Servlet只能處理一個請求,這樣當(dāng)系統(tǒng)比較大,業(yè)務(wù)比較復(fù)雜的時候可能會存在成百上千的Servlet,找起來都眼花。每次我們都需要手動的從Request中獲取請求參數(shù),然后封裝成我們想要的對象,這其中可能還要對參數(shù)進行校驗,在調(diào)用業(yè)務(wù)邏輯層獲取到數(shù)據(jù)之后,我們還要手動的設(shè)置到響應(yīng)中,同時手動的選擇轉(zhuǎn)發(fā)或者重定向進行跳轉(zhuǎn)。我們的請求的url是硬配置到web.xml中的,缺乏靈活性,如果可以動態(tài)的配置這種請求url與處理的對應(yīng)關(guān)系就好了。我們的Servlet與前端的渲染框架緊耦合在一塊,這樣當(dāng)前端換一種顯示技術(shù)的時候就需要改動較大的代碼,如果能把數(shù)據(jù)的處理與數(shù)據(jù)的顯示分離,讓其松散耦合就更好了。帶著這些思考,能不能進一步的來抽離業(yè)務(wù)邏輯的開發(fā)呢?在早期的時候筆者也曾進行一些嘗試,其大概思路就是編寫一個BaseServlet,然后我們自己定義的Servlet繼承自BaseServlet,前端的請求需要指定Servlet的哪個方法進行處理,這樣請求的時候?qū)⑿枰獛弦粋€method參數(shù),例如這樣:http://localhost:8080/myProject/MyServlet?method=getInfo在BaseServlet中將提取該參數(shù)信息,并使用反射的方法調(diào)用子類的該方法,子類方法統(tǒng)一返回String類型的結(jié)果,代表要返回的邏輯視圖名,也就是要跳轉(zhuǎn)的路徑,然后父類拿到結(jié)果,使用重定向或者轉(zhuǎn)發(fā)進行跳轉(zhuǎn)。說到這里,有小伙伴肯定不耐煩了,明明是講SpringMVC的,到現(xiàn)在連個SpringMVC的影都還沒見,全是在講Servlet。先別著急,理解這些對我們理解SpringMVC有很大的幫助,請往下看說到這里,其實是想說,如果我們想要在Servlet上更進一步,想要進一步的將業(yè)務(wù)邏輯與其他工作相分離,那么就需要在Servlet之上,構(gòu)建一個事無巨細,任勞任怨,神通過大,...(額想不起來。。。)的超級Servlet,來為我們做這些工作,我們暫且把這個Servlet叫做超級牛逼Servlet。而開發(fā)Spring的那些人是啥大佬,我們能想到這些,他們能想不到?于是他們動手開發(fā)了這個超級牛逼Servlet,并正式命名為DispatcherServlet。3、SpringMVC——兩級控制器方式接下來我們就要正式的開始SpringMVC之旅了,通過前面的了解,我們知道SpringMVC把那個超級牛逼Servlet叫做DispatcherServlet,這個Servlet可以說為簡化我們的開發(fā)操碎了心,我們稱之為_前端控制器?,F(xiàn)在我們不禁思考,前面我們寫的BaseServlet對應(yīng)現(xiàn)在的超級牛逼Servlet(DispatcherServlet)。那么定義我們業(yè)務(wù)邏輯的自定義Servlet叫啥呢?SpringMVC管定義我們的業(yè)務(wù)邏輯處理的類叫做Handler,只不過他不再是一個Servlet了,而是一個普普通通的類,這也很好理解,畢竟DispatcherServlet做了太多,而且那么牛逼,完全可以像對待Servlet一樣對待一個普通的類,而這個Handler就叫做次級控制器_。搜索公眾號頂級架構(gòu)師后臺回復(fù)“offer”,獲取一份驚喜禮包。這里可能有小伙伴持反對意見了,有的書上說了SpringMVC的次級控制器叫Controller,不是Handler。其實SpringMVC的次級控制器確實是叫Handler,只不過Hander是一個抽象的,而SpringMVC選擇使用Controller來實現(xiàn)Handler,講到這里,你覺得我們能不能自定義一個Handler實現(xiàn),叫做Lellortnoc呢?答案當(dāng)然是可以的!就好像List是一個抽象的接口,而List的實現(xiàn)有ArrayList,LinkedList一樣。4、DispatcherServlet——前端控制器DispatcherServlet是整個SpringMVC的核心,超級牛逼Servlet這個榮譽稱號他是名副其實。DispatcherServlet和其家族成員兄弟一起完成了很多的工作,包括請求參數(shù)的自動綁定,參數(shù)的自動校驗,請求url的自動匹配,邏輯視圖名到真實頁面的跳轉(zhuǎn),數(shù)據(jù)獲取與數(shù)據(jù)渲染顯示的分離等等。。。在此過程中他更像是一個指揮家,有條不紊的指揮著請求不斷的向前處理,并最終完成服務(wù)端的響應(yīng)數(shù)據(jù)。想要了解具體DispatcherServlet都是怎么指揮的,那就繼續(xù)往下看吧!5、HandlerMapper——請求映射專家想想我們在使用Servlet編寫代碼的時候,請求的映射工作是交給了web.xml。但是現(xiàn)在SpringMVC采用了兩級控制器的方式,就必須解決這個棘手的問題。首先DispatcherServlet也是一個Servlet,那么我們也應(yīng)該在web.xml中配置其處理的請求路徑。那么應(yīng)該配置什么路徑呢?我們說DispatcherServlet被稱為超級牛逼Serlvet,我們希望它能處理所有的請求,那么就可以讓DispatcherServlet接受所有請求的處理。像下面這樣配置:

<web-app

version="3.0"

xmlns="/xml/ns/javaee"

xmlns:xsi="/2001/XMLSchema-instance"

xsi:schemaLocation="/xml/ns/javaee

/xml/ns/javaee/web-app_3_0.xsd">

<servlet>

<servlet-name>Spring

MVCservlet-name>

<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>

<init-param>

<param-name>contextConfigLocationparam-name>

<param-value>classpath:Spring-servlet.xmlparam-value>

init-param>

<load-on-startup>1load-on-startup>

servlet>

<servlet-mapping>

<servlet-name>Spring

MVCservlet-name>

<url-pattern>/*url-pattern>

servlet-mapping>

web-app>現(xiàn)在所有的請求都被映射到了DispatcherServlet,那么DispatcherServlet現(xiàn)在就有責(zé)任將請求分發(fā)至具體的次級控制器,如何找到或者說如何保存請求到具體的次級控制器的這種映射關(guān)系呢?DispatcherServlet選擇請求他的好兄弟HandlerMapping。在HandlerMapping中,保存了特定的請求url應(yīng)該被哪一個Handler(也就是通常的Controller)所處理。HandlerMapping根據(jù)映射策略的不同,大概有下面幾種映射查找方式:org.springframework.web.servlet.handler.SimpleUrlHandlerMapping通過配置請求路徑和Controller映射建立關(guān)系,找到相應(yīng)的Controllerorg.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping通過Controller的類名找到請求的C.springframework.web.servlet.handler.BeanNameUrlHandlerMapping通過定義的beanName進行查找要請求的Controllerorg.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping通過注解@RequestMapping(“/userlist”)來查找對應(yīng)的Controller。想必現(xiàn)在最常用的就是第四種了吧,直接在對應(yīng)的Controller上以及其內(nèi)部的方法之上加上相應(yīng)的注解,就可以配置好請求的映射,簡直是香香的。6、Handler的攔路虎——HandlerInterceptor聊到這里,你以為DispatcherServlet把請求的url交給HandlerMapping,HandlerMapping根據(jù)請求查出對應(yīng)的Controller來交給DispatcherServlet,然后DispatcherServlet交給Controller執(zhí)行就完事了?那就Toyoungtonative了,這其中還有一些小插曲。比如我們不能啥請求不管三七二十一都交給Handler執(zhí)行吧,最起碼要過濾一下不合理的請求,比如跳轉(zhuǎn)頁面的時候檢查Session,如果用戶沒登錄跳轉(zhuǎn)到登錄界面啊,以及一些程序的異常以統(tǒng)一的方式跳轉(zhuǎn)等等,都需要對請求進行攔截。如果對Servlet了解的同學(xué)是不是有一點似曾相識的感覺?沒錯,Servlet中的Filter也可以完成請求攔截與過濾的功能,不過既然SpringMVC是兩級控制器結(jié)構(gòu),那么HandlerInterceptor就與Filter有一些細微的差別,其最主要的差別,筆者認(rèn)為HandlerInterceptor提供了更細粒度的攔截。畢竟Filter攔截的對象是Serlvet,而HandlerInterceptor攔截的則是Handler(Controller)。用一張圖可以生動的表現(xiàn)出來。HandlerInterceptor.jpg從圖中我們可以看出HandlerInteceptor可以配置多個,其中任何一個返回false的話,請求都將被攔截,直接返回。7、次級控制器——Handler前端控制器我們已經(jīng)很熟悉了,而次級控制器也就是Handler,是我們真正執(zhí)行業(yè)務(wù)邏輯的類。通常在SpringMVC中,這個Handler就是我們很熟悉的Controller。我們調(diào)用封裝好的業(yè)務(wù)邏輯接口就是在這里進行處理的。可以說SpringMVC已經(jīng)將業(yè)務(wù)邏輯與其他不相關(guān)的繁雜工作分離的較為徹底了。這樣,我們就在Handler(Controller)中專心的編寫我們的業(yè)務(wù)邏輯吧!8、Handler與HandlerInterceptor的橋梁——HandlerExecutionChain前面講到DispatherServlet求助HandlerMapping進行url與次級控制器的映射,但是DispatherServlet在將url交給特定的HandlerMapping之后,HandlerMapping在進行了一頓猛如虎的操作之后,返回給DispaterServlet的卻不是一個可執(zhí)行的Handler(Controller),而是一個HandlerExecutionChain對象。那么HandlerMapping究竟為什么要返回給這樣的一個對象而不是返回Handler對象呢?其實在看上面圖的時候,你有沒有納悶,HandlerInterceptor與Handler是怎樣聯(lián)系在一起的呢?答案就是HandlerExecutionChain。它就是若干的HandlerInterceptor與Handler的組合。那么是怎么組合的呢?搜索公眾號后端架構(gòu)師后臺回復(fù)“架構(gòu)整潔”,獲取一份驚喜禮包。這里就涉及到設(shè)計模式中的責(zé)任鏈設(shè)計模式,HandlerExecutionChain將HandlerInterceptor與Handler串成一個執(zhí)行鏈的形式,首先請求會被第一個HandlerInterceptor攔截,如果返回false,那么直接短路請求,如果返回true,那么再交給第二個HandlerInterceptor處理,直到所有的HandlerInterceptor都檢查通過,請求才到達Handler(Controller),交由Handler正式的處理請求。執(zhí)行完成之后再逐層的返回。而DispatcherServlet拿到的就是這樣一個串聯(lián)好的HandlerExecutionChain,然后順序的執(zhí)行請求。9、解耦的關(guān)鍵——ModelAndView到這里,請求終于來到了對應(yīng)的Handler。我們希望的是Handler只處理負(fù)責(zé)的業(yè)務(wù)邏輯即可,而一些url的跳轉(zhuǎn)等無需Handler負(fù)責(zé)。那么DispatcherServlet就使用了ModelAndView保存我們的數(shù)據(jù)和想要跳轉(zhuǎn)的路徑。我們調(diào)用業(yè)務(wù)邏輯層獲取數(shù)據(jù),并將數(shù)據(jù)封裝到ModelAndView中,同時設(shè)置ModelAndView的view邏輯視圖名稱。從ModelAndView的名稱可以看出,它保存了Handler執(zhí)行完成之后所需要發(fā)送到前端的數(shù)據(jù),以及需要跳轉(zhuǎn)的路徑。這些是DispatcherServlet需要用到的。10、視圖渲染查找——ViewResolver這一步是SpringMVC將數(shù)據(jù)的獲取與數(shù)據(jù)的顯示渲染相分離的關(guān)鍵,前端可能采用各種各樣的方式顯示數(shù)據(jù),可能是Jsp,可能是Html,也可能是其他的方式。DispatcherServlet已經(jīng)拿到了ModelAndView,這里面有執(zhí)行完成請求后返回的響應(yīng)結(jié)果數(shù)據(jù),還有邏輯視圖的路徑,這個時候DispatcherServlet就需要根據(jù)這個邏輯視圖的路徑去查找誰能把數(shù)據(jù)進行解析與渲染。比如說我們使用FreeMarker模板引擎渲染數(shù)據(jù),那么這個時候就要找到能夠勝任該工作的那個View實現(xiàn)類,那么問題來了,如何尋找呢?以什么策略尋找呢?這個就依賴我們的ViewResolver了。通常的尋找策略有以

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論