您好,登錄后才能下訂單哦!
本篇內容介紹了“Spring MVC的處理流程是什么”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
在筆者剛接觸到使用Java進行Web開發的時候,Spring MVC遠沒有今天這么流行,君不見曾經的王者Servlet繁盛一時的場面。現在回想起來,使用Servlet進行開發雖然不像現在這么容易,好多的事情需要自己做,但是Servlet使得開發的邏輯變得十分清晰,尤其是在Servlet與jsp很好的承擔了各自的角色之后,再加上mvc分層思想的流行。編寫Web應用程序在那時是一件快樂而又簡單的事情。
實際上Servlet做的事情并不是很多,筆者覺得Servlet想要完成的就是統一請求的接受、處理與響應的流程。
網絡編程中繞不開的一個東東想必不用說大家也猜得到,那就是Socket。但是網絡需要傳輸的話是很復雜的,首先需要遵循一定的協議,現在我們一般使用Http與Https傳輸數據,而Socket就是在一些網絡協議之上,屏蔽了底層協議的細節,為使用者提供一個統一的api。但是Servlet認為Socket做的還不夠,或者說我們還要進行相應的處理。于是Servlet(就HttpServlet來說),他將網絡中的請求報文進行封裝轉化成為了Request表示,在Http通信過程之中就是HttpServletRequest,而將服務端處理請求后返回的響應統一的封裝為了HttpServletResponse對象。
這樣做的好處是什么呢?
我們作為開發者,不必再去做一些處理網絡請求與響應的繁瑣之事,而只需要關注于我們的業務邏輯開發。
大家有沒有發現,每一次框架效率的提升很多時候都是在將最最重要的業務邏輯與其他任務盡可能完全的分離開,使我們總可以全身心的投入到業務邏輯的開發之中,Spring AOP是不是就是一個很好的佐證呢!
那么Servlet如何使用呢?
沒有Servlet使用經歷的同學可以聽我簡單的說一說:
1. 首先我們通常要編寫一個自己的Servlet然后繼承自HttpServlet,然后重寫其doGet()與doPost()方法。這兩個方法都會將HttpServletRequest與HttpServletResponse作為參數傳遞進去,然后我們從Request中提取前端傳來的參數,在相應的doXXX方法內調用事先編寫好的Service接口,Dao接口即可將數據準備好放置到Response中并跳轉到指定的頁面即可,跳轉的方式可以選擇轉發或者重定向。
2. Servlet使用的是模板方法的設計模式,在Servlet頂層將會調用service方法,該方法會構造HttpServletRequest與HttpServletResponse對象作為參數調用子類重寫的doXXX()方法。然后返回請求。
3. 最后我們需要將我們編寫的自定義Servlet注冊到web.xml中,在web.xml中配置servlet-mapping來為該servlet指定處理哪些請求。
Servlet的使用就是這么簡單!事實上,在很長的一段時間內他的流行也得益于他的簡單易用易上手。
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4"> <servlet> <servlet-name>ShoppingServlet</servlet-name> <servlet-class>com.myTest.ShoppingServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>ShoppingServlet</servlet-name> <url-pattern>/shop/ShoppingServlet</url-pattern> </servlet-mapping> </web-app>
當我們使用Servlet來進行業務邏輯開發的時候,時常會感覺到爽歪歪,但是爽歪歪的同時也感覺到有那么一點點不適。不適的地方主要有以下幾點:
每個Servlet只能處理一個請求,這樣當系統比較大,業務比較復雜的時候可能會存在成百上千的Servlet,找起來都眼花。
每次我們都需要手動的從Request中獲取請求參數,然后封裝成我們想要的對象,這其中可能還要對參數進行校驗,在調用業務邏輯層獲取到數據之后,我們還要手動的設置到響應中,同時手動的選擇轉發或者重定向進行跳轉。
我們的請求的url是硬配置到web.xml中的,缺乏靈活性,如果可以動態的配置這種請求url與處理的對應關系就好了。
我們的Servlet與前端的渲染框架緊耦合在一塊,這樣當前端換一種顯示技術的時候就需要改動較大的代碼,如果能把數據的處理與數據的顯示分離,讓其松散耦合就更好了。
帶著這些思考,能不能進一步的來抽離業務邏輯的開發呢?
在早期的時候筆者也曾進行一些嘗試,其大概思路就是編寫一個BaseServlet,然后我們自己定義的Servlet繼承自BaseServlet,前端的請求需要指定Servlet的哪個方法進行處理,這樣請求的時候將需要帶上一個method參數,例如這樣:
http://localhost:8080/myProject/MyServlet?method=getInfo
在BaseServlet中將提取該參數信息,并使用反射的方法調用子類的該方法,子類方法統一返回String類型的結果,代表要返回的邏輯視圖名,也就是要跳轉的路徑,然后父類拿到結果,使用重定向或者轉發進行跳轉。
說到這里,有小伙伴肯定不耐煩了,明明是講Spring MVC的,到現在連個Spring MVC的影都還沒見,全是在講Servlet。先別著急,理解這些對我們理解Spring MVC有很大的幫助,請往下看
說到這里,其實是想說,如果我們想要在Servlet上更進一步,想要進一步的將業務邏輯與其他工作相分離,那么就需要在Servlet之上,構建一個事無巨細,任勞任怨,神通過大,...(額想不起來。。。)的超級Servlet,來為我們做這些工作,我們暫且把這個Servlet叫做超級牛逼Servlet。而開發Spring的那些人是啥大佬,我們能想到這些,他們能想不到?于是他們動手開發了這個超級牛逼Servlet,并正式命名為DispatcherServlet。
接下來我們就要正式的開始Spring MVC之旅了,通過前面的了解,我們知道Spring MVC把那個超級牛逼Servlet叫做DispatcherServlet,這個Servlet可以說為簡化我們的開發操碎了心,我們稱之為_前端控制器。現在我們不禁思考,前面我們寫的BaseServlet對應現在的超級牛逼Servlet(DispatcherServlet)。那么定義我們業務邏輯的自定義Servlet叫啥呢?Spring MVC管定義我們的業務邏輯處理的類叫做Handler,只不過他不再是一個Servlet了,而是一個普普通通的類,這也很好理解,畢竟DispatcherServlet做了太多,而且那么牛逼,完全可以像對待Servlet一樣對待一個普通的類,而這個Handler就叫做次級控制器_。
這里可能有小伙伴持反對意見了,有的書上說了Spring MVC的次級控制器叫Controller,不是Handler。
其實Spring MVC的次級控制器確實是叫Handler,只不過Hander是一個抽象的,而Spring MVC選擇使用Controller來實現Handler,講到這里,你覺得我們能不能自定義一個Handler實現,叫做Lellortnoc呢?答案當然是可以的!就好像List是一個抽象的接口,而List的實現有ArrayList,LinkedList一樣。
DispatcherServlet是整個Spring MVC的核心,超級牛逼Servlet這個榮譽稱號他是名副其實。DispatcherServlet和其家族成員兄弟一起完成了很多的工作,包括請求參數的自動綁定,參數的自動校驗,請求url的自動匹配,邏輯視圖名到真實頁面的跳轉,數據獲取與數據渲染顯示的分離等等。。。在此過程中他更像是一個指揮家,有條不紊的指揮著請求不斷的向前處理,并最終完成服務端的響應數據。
想要了解具體DispatcherServlet都是怎么指揮的,那就繼續往下看吧!推薦:250期面試題匯總
想想我們在使用Servlet編寫代碼的時候,請求的映射工作是交給了web.xml。但是現在Spring MVC采用了兩級控制器的方式,就必須解決這個棘手的問題。
首先DispatcherServlet也是一個Servlet,那么我們也應該在web.xml中配置其處理的請求路徑。那么應該配置什么路徑呢?我們說DispatcherServlet被稱為超級牛逼Serlvet,我們希望它能處理所有的請求,那么就可以讓DispatcherServlet接受所有請求的處理。像下面這樣配置:
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <servlet> <servlet-name>Spring MVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 表示啟動容器時初始化該servlet --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:Spring-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Spring MVC</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
現在所有的請求都被映射到了DispatcherServlet,那么DispatcherServlet現在就有責任將請求分發至具體的次級控制器,如何找到或者說如何保存請求到具體的次級控制器的這種映射關系呢?DispatcherServlet選擇請求他的好兄弟HandlerMapping。
在HandlerMapping中,保存了特定的請求url應該被哪一個Handler(也就是通常的Controller)所處理。HandlerMapping根據映射策略的不同,大概有下面幾種映射查找方式:
鴻蒙官方戰略合作共建——HarmonyOS技術社區
org.springframework.web.servlet.handler.SimpleUrlHandlerMapping 通過配置請求路徑和Controller映射建立關系,找到相應的Controller
org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping 通過 Controller 的類名找到請求的Controller。
org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping 通過定義的 beanName 進行查找要請求的Controller
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping 通過注解 @RequestMapping(“/userlist”) 來查找對應的Controller。
想必現在最常用的就是第四種了吧,直接在對應的Controller上以及其內部的方法之上加上相應的注解,就可以配置好請求的映射,簡直是香香的。推薦:250期面試題匯總
聊到這里,你以為DispatcherServlet把請求的url交給HandlerMapping, HandlerMapping根據請求查出對應的Controller來交給DispatcherServlet, 然后DispatcherServlet交給Controller執行就完事了?那就To young to native了,這其中還有一些小插曲。比如我們不能啥請求不管三七二十一都交給Handler執行吧,最起碼要過濾一下不合理的請求,比如跳轉頁面的時候檢查Session,如果用戶沒登錄跳轉到登錄界面啊,以及一些程序的異常以統一的方式跳轉等等,都需要對請求進行攔截。
如果對Servlet了解的同學是不是有一點似曾相識的感覺?沒錯,Servlet中的Filter也可以完成請求攔截與過濾的功能,不過既然Spring MVC是兩級控制器結構,那么HandlerInterceptor就與Filter有一些細微的差別,其最主要的差別,筆者認為HandlerInterceptor提供了更細粒度的攔截。畢竟Filter攔截的對象是Serlvet,而HandlerInterceptor攔截的則是Handler(Controller)。用一張圖可以生動的表現出來。
HandlerInterceptor.jpg
從圖中我們可以看出HandlerInteceptor可以配置多個,其中任何一個返回false的話,請求都將被攔截,直接返回。
前端控制器我們已經很熟悉了,而次級控制器也就是Handler,是我們真正執行業務邏輯的類。通常在Spring MVC中,這個Handler就是我們很熟悉的Controller。我們調用封裝好的業務邏輯接口就是在這里進行處理的。可以說Spring MVC已經將業務邏輯與其他不相關的繁雜工作分離的較為徹底了。這樣,我們就在Handler(Controller)中專心的編寫我們的業務邏輯吧!
前面講到DispatherServlet求助HandlerMapping進行url與次級控制器的映射,但是DispatherServlet在將url交給特定的HandlerMapping之后,HandlerMapping在進行了一頓猛如虎的操作之后,返回給DispaterServlet的卻不是一個可執行的Handler(Controller),而是一個HandlerExecutionChain對象。那么HandlerMapping究竟為什么要返回給這樣的一個對象而不是返回Handler對象呢?
其實在看上面圖的時候,你有沒有納悶,HandlerInterceptor與Handler是怎樣聯系在一起的呢?答案就是HandlerExecutionChain。它就是若干的HandlerInterceptor與Handler的組合。那么是怎么組合的呢?
這里就涉及到設計模式中的責任鏈設計模式,HandlerExecutionChain將HandlerInterceptor與Handler串成一個執行鏈的形式,首先請求會被第一個HandlerInterceptor攔截,如果返回false,那么直接短路請求,如果返回true,那么再交給第二個HandlerInterceptor處理,直到所有的HandlerInterceptor都檢查通過,請求才到達Handler(Controller),交由Handler正式的處理請求。執行完成之后再逐層的返回。
而DispatcherServlet拿到的就是這樣一個串聯好的HandlerExecutionChain,然后順序的執行請求。
到這里,請求終于來到了對應的Handler。我們希望的是Handler只處理負責的業務邏輯即可,而一些url的跳轉等無需Handler負責。那么DispatcherServlet就使用了ModelAndView保存我們的數據和想要跳轉的路徑。
我們調用業務邏輯層獲取數據,并將數據封裝到ModelAndView中,同時設置ModelAndView的view邏輯視圖名稱。從ModelAndView的名稱可以看出,它保存了Handler執行完成之后所需要發送到前端的數據,以及需要跳轉的路徑。這些是DispatcherServlet需要用到的。推薦:250期面試題匯總
這一步是Spring MVC將數據的獲取與數據的顯示渲染相分離的關鍵,前端可能采用各種各樣的方式顯示數據,可能是Jsp,可能是Html,也可能是其他的方式。DispatcherServlet已經拿到了ModelAndView,這里面有執行完成請求后返回的響應結果數據,還有邏輯視圖的路徑,這個時候DispatcherServlet就需要根據這個邏輯視圖的路徑去查找誰能把數據進行解析與渲染。
比如說我們使用FreeMarker模板引擎渲染數據,那么這個時候就要找到能夠勝任該工作的那個View實現類,那么問題來了,如何尋找呢?以什么策略尋找呢?這個就依賴我們的ViewResolver了。
通常的尋找策略有以下幾種:
BeanNameViewResolver :將邏輯視圖名解析為一個Bean,Bean的id等于邏輯視圖名。
XmlViewResolver:和BeanNameViewResolver類似,只不過目標視圖Bean對象定義在一個獨立的XML文件中,而非定義在DispatcherServlet上下文的主配置文件中
InternalResourceViewResovlver:將視圖名解析為一個URL文件,一般使用該解析器將視圖名映射為保存在WEB-INF目錄中的程序文件(如JSP)
XsltViewResolver:將視圖名解析為一個指定XSLT樣式表的URL文件
JasperReportsViewResolver:JasperReports是一個基于java的開源報表工具,該解析器將視圖名解析為報表文件對應的URL
FreeMarkerViewResolver:解析為基于FreeMarker模板技術的模板文件
VelocityViewResolver和VelocityLayoutViewResolver:解析為基于Velocity模板技術的模板文件
在根據邏輯視圖名借助ViewResolver查找到對應的View實現類之后,DispatcherServlet就會將ModelAndView中的數據交給View實現類來進行渲染,待該View渲染完成之后,會將渲染完成的數據交給DispatcherServlet,這時候DispatcherServlet將其封裝到Response返回給前端顯示。
至此,整個Spring MVC的處理流程就算完成了,當然這其中還會有對于國際化的支持,主題的定義與設置等等,但是這些是不常用的,Spring MVC最主要的處理流程所需要的用到的就是以上這些類。可以看到在此過程中,DispatcherServlet起到了至關重要的作用,所以說Spring MVC的核心就在于DispatcherServlet。
最后附上一張流程圖作為以上內容的總結。
“Spring MVC的處理流程是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。