這篇文章主要介紹SpringMVC中工作原理的示例分析,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!
成都創(chuàng)新互聯(lián)專注于企業(yè)營(yíng)銷型網(wǎng)站、網(wǎng)站重做改版、趙縣網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、HTML5、商城網(wǎng)站制作、集團(tuán)公司官網(wǎng)建設(shè)、外貿(mào)網(wǎng)站制作、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁(yè)設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性價(jià)比高,為趙縣等各大城市提供網(wǎng)站開(kāi)發(fā)制作服務(wù)。從一個(gè)項(xiàng)目開(kāi)始
本文假定你已經(jīng)能熟練的使用springmvc。為了展開(kāi)后續(xù)的討論,假設(shè)我們新建了一個(gè)spring-mvc-demo的項(xiàng)目。并由此項(xiàng)目來(lái)展開(kāi)討論。項(xiàng)目中有一個(gè)控制器,代碼如下:
@Controller @RequestMapping("/app") public class AppController { @RequestMapping(method=RequestMethod.GET,value="/hello") public @ResponseBody String hello(HttpServletRequest request,String name) { return "Hello,"+name; } }
控制器寫(xiě)好之后,我們將項(xiàng)目打車war包,放入tomcat容器中,并使用8080端口啟動(dòng)tomcat,運(yùn)行項(xiàng)目,然后在瀏覽器中輸入http://localhost:8080/app/hello?name=world.
我們?cè)跒g覽器中可以看到:Hello,world的輸出。
我們先記住這個(gè)例子,下面我們帶著一些疑問(wèn)繼續(xù)看,這個(gè)請(qǐng)求是怎么被接收到的?請(qǐng)求是怎么交給AppController處理的?
Servlet是Java Web應(yīng)用的基石
當(dāng)你在瀏覽器中輸入 http://loalhost:8080/ ,按下enter建,然后請(qǐng)求命中了服務(wù)器,這是怎么發(fā)生的?又如何從這個(gè)請(qǐng)求中得到瀏覽器中可見(jiàn)的頁(yè)面?
本例中,我們給出的是一個(gè)簡(jiǎn)單的spring-mvc應(yīng)用,并放入了tomcat中(springboot 內(nèi)嵌tomcat啟動(dòng)其實(shí)也是一樣的)。 Tomcat 是一個(gè)servlet容器,這點(diǎn)我想每個(gè)Java程序員都十分清楚,我們?cè)跊](méi)有使用spring-mvc之前,就是使用servlet+jsp來(lái)開(kāi)發(fā)web應(yīng)用。
由于Tomcat是一個(gè)web容器,每一個(gè)發(fā)送給Tomcat服務(wù)器的HTTP請(qǐng)求自然會(huì)被一個(gè)Java Servlet處理。所以,SpringMvc 必定有一個(gè)servlet,SpringWeb應(yīng)用的入口必定是一個(gè)Servlet,這應(yīng)該不難想到。
簡(jiǎn)單來(lái)說(shuō),Servlet是任何Java Web應(yīng)用的核心組件(除非你不用servlet規(guī)范,比如你使用netty)。Servlet它是低層次的,并且不會(huì)像MVC那樣強(qiáng)加于特定的編程模式。它只是可以讓你寫(xiě)一個(gè)偶一個(gè)Servlet,一個(gè)HTTP Servlet可以接受一個(gè)HTTP請(qǐng)求,然后處理它,并返回一個(gè)響應(yīng)。
而springmvc 就是使用了一個(gè)大的servlet,下面我們就來(lái)說(shuō)這個(gè)大的servlet。
DispatcherServlet是Spring MVC的核心
上面我們已經(jīng)提到Servlet 是Java web應(yīng)用的基石,Spring應(yīng)用入口必定是一個(gè)Servlet,這個(gè)Servlet 其實(shí)就是DispatcherServlet。
作為WEB應(yīng)用的開(kāi)發(fā)人員,我們真正想做的是抽象出以下繁瑣和模板化的任務(wù),并專注于有用的業(yè)務(wù)邏輯:
映射一個(gè)HTTP請(qǐng)求到某個(gè)處理方法。
將HTTP請(qǐng)求數(shù)據(jù),和頭信息轉(zhuǎn)換成數(shù)據(jù)對(duì)象(DTO / domain object)。
模型 - 視圖 - 控制器 之間的交互。
從DTO,域?qū)ο蟮壬身憫?yīng)
Spring DispatcherServlet提供了這些。它是Spring Web MVC框架的核心, 這個(gè)核心組件接收所有請(qǐng)求到您的應(yīng)用程序。
DispatcherServlet具有很強(qiáng)的可擴(kuò)展性。 例如,它允許您插入不同的現(xiàn)有或新適配器以執(zhí)行大量任務(wù):
將請(qǐng)求映射到應(yīng)該處理它的類或方法(HandlerMapping接口的實(shí)現(xiàn))
使用特定模式處理請(qǐng)求,例如常規(guī)servlet,更復(fù)雜的MVC工作流或者POJO bean中的方法(HandlerAdapter接口的實(shí)現(xiàn))
通過(guò)名稱解析視圖,允許您使用不同的模板引擎,XML,XSLT或任何其他視圖技術(shù)(ViewResolver接口的實(shí)現(xiàn))
通過(guò)使用默認(rèn)的Apache Commons文件上傳實(shí)現(xiàn)或編寫(xiě)自己的MultipartResolver來(lái)解析multipart請(qǐng)求
使用任何LocaleResolver實(shí)現(xiàn)解決語(yǔ)言環(huán)境,包括Cookie,會(huì)話,Accept HTTP標(biāo)頭或用于確定用戶期望的語(yǔ)言環(huán)境的任何其他方式
處理HTTP請(qǐng)求
首先,讓我們來(lái)追蹤一個(gè)簡(jiǎn)單的HTTP請(qǐng)求到達(dá)controller中的方法,然后返回到 瀏覽器/客戶端的處理過(guò)程。
DispatcherServlet 有一個(gè)很長(zhǎng)的繼承關(guān)系。它的繼承關(guān)系是這樣的:
GenericServlet <- HttpServlet <- HttpServletBean <- FreamworkServlet <- DispatcherServlet
GenericServlet
GenericServlet是Servlet規(guī)范的一部分,它并不直接關(guān)注HTTP。它定義了一個(gè)service()方法用來(lái)接收傳遞過(guò)來(lái)的請(qǐng)求,并產(chǎn)生響應(yīng)。(這里的請(qǐng)求和響應(yīng)不是指HTTP請(qǐng)求)
public abstract void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
注意,這里的參數(shù)中的ServletRequest,ServletResponse并不是和HTTP協(xié)議綁定的,Http有具體協(xié)議Servlet。
HttpServlet
顧名思義,HttpServlet類就是規(guī)范中定義的基于HTTP的Servlet實(shí)現(xiàn)。
更實(shí)際的是,HttpServlet是一個(gè)具有service()方法實(shí)現(xiàn)的抽象類,它通過(guò)HTTP方法類型分割請(qǐng)求,大致如下所示:
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); if (method.equals(METHOD_GET)) { // ... doGet(req, resp); } else if (method.equals(METHOD_HEAD)) { // ... doHead(req, resp); } else if (method.equals(METHOD_POST)) { doPost(req, resp); // ... }
根據(jù)請(qǐng)求的不同, get,post方法會(huì)分別被不同方法處理。
HttpServletBean
上面我們展示過(guò) DispatcherServlet的繼承關(guān)系,在這個(gè)繼承鏈中,HttpServletBean是進(jìn)入spring的第一個(gè)層次。從HttpServletBean開(kāi)始往下的幾個(gè)servlet都是spring中的類。HttpServletBean 就是一個(gè)servlet,它繼承自HttpServlet,就像是我們?cè)谑褂胹ervlet+jsp開(kāi)發(fā)時(shí)候定義的servlet一樣。
根據(jù)servlet的生命周期我們知道,servlet會(huì)被容器初始化,初始化時(shí)候,其init()方法會(huì)被調(diào)用。在springmvc框架中 HttpServletBean使用從web.xml或WebApplicationInitializer收到的servlet init-param值來(lái)注入bean的屬性。
在請(qǐng)求應(yīng)用程序的情況下,為這些特定的HTTP請(qǐng)求調(diào)用doGet(),doPost()等方法。
FrameworkServlet
FrameworkServlet將Servlet功能與Web應(yīng)用程序上下文集成,實(shí)現(xiàn)ApplicationContextAware接口。但它也能夠自行創(chuàng)建Web應(yīng)用程序上下文。
正如上面所說(shuō),F(xiàn)rameworkServlet的超類HttpServletBean將init-param注入為bean屬性。所以,如果servlet的contextClass init-param中提供了context類的名字,那么這個(gè)context類的實(shí)例將會(huì)被創(chuàng)建,用作應(yīng)用的context。否則,將會(huì)使用XmlWebApplicationContext作為默認(rèn)的。
DispatcherServlet: 統(tǒng)一請(qǐng)求處理
有了上面鋪墊,我們這里可以開(kāi)始關(guān)鍵的內(nèi)容,即DispatcherServlet統(tǒng)一請(qǐng)求處理。在Springmvc的項(xiàng)目中,我們通常會(huì)在web.xml配置一個(gè)servlet,如下:
<servlet> <servlet-name>spring</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/spring-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
上面我們提到,DispatcherServlet繼承關(guān)系,其父類中正兒八經(jīng)的servlet類是HttpServletBean這個(gè)servlet類是應(yīng)用啟動(dòng)入口。其生命周期的第一階段init()方法完成初始化工作。
doService()方法設(shè)置請(qǐng)求信息
DispatcherServlet 初始化之后,便可以工作了。當(dāng)請(qǐng)求到達(dá)之時(shí),會(huì)調(diào)用其doService()方法。doService()方法的代碼如下:
@Override protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { // 刪除一下‘非核心代碼'方便查看 // Make framework objects available to handlers and view objects. request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver); request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver); request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource()); try { doDispatch(request, response); } finally { if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { return; } // Restore the original attribute snapshot, in case of an include. if (attributesSnapshot != null) { restoreAttributesAfterInclude(request, attributesSnapshot); } } }
可以看到,doService()方法先設(shè)置一些 request信息,這些信息在后續(xù)的處理過(guò)程中會(huì)用到。設(shè)置完后,它調(diào)用doDispatch() 方法。
doDispatch()方法分發(fā)請(qǐng)求
doService()方法最終調(diào)用了doDispatch(),看名知意,這個(gè)方法是做分發(fā)工作的。其代碼如下:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { //刪除一些代碼方便閱讀 HandlerExecutionChain mappedHandler = null; try { ModelAndView mv = null; Exception dispatchException = null; try { // 刪除一些代碼方便閱讀 mappedHandler = getHandler(processedRequest, false); HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); try { // Actually invoke the handler. mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); } finally { if (asyncManager.isConcurrentHandlingStarted()) { return; } } applyDefaultViewName(request, mv); mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; // 這里捕獲了異常TypeMismatchException } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { } finally { // 刪除一些代碼 } }
這個(gè)方法主要作用就是找到合適的 handler 來(lái)處理請(qǐng)求。handler通常是一個(gè)某個(gè)類型的對(duì)象,并且不限定特定的接口。因此spring需要找到這個(gè)handler的適配器。這個(gè)Handler通常是一個(gè)HandlerMethod實(shí)例,
為了找到與請(qǐng)求匹配handler,spring需要從已注冊(cè)的HandlerMapping接口實(shí)現(xiàn)類里邊去找。這個(gè)查找過(guò)程就是在上面的getHandler() 方法完成得到的是一個(gè)HandlerExecutionChain。 這里體現(xiàn)了責(zé)任鏈模式。
這個(gè)getHandler() 會(huì)遍歷一個(gè)HandlerMapping的map。由于我們一般都使用注解形式:@Controller,@RequestMapping注解。因此這里找到HandlerMapping實(shí)現(xiàn)就是RequestMappingHandlerMapping
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
getHandlerAdapter()方法找到最終的handler適配器,找到的適配器就是RequestMappingHandlerAdapter,(因?yàn)槲覀兪褂玫氖茾RequestMapping注解形式)。
本例中,我們定義了AppController 的hello()方法,并用@Controller,@RequestMapping對(duì)其分別進(jìn)行注解,因此這里得到的適配器HandlerAdapter 所適配HandlerMethod就是 AppController 的hello()方法的 。
HandlerAdapter處理請(qǐng)求
上面通過(guò) 確定了HandlerAdapter之后,就要執(zhí)行handle() 方法了,即上面代碼中,try語(yǔ)句塊里邊的ha.handle()。handle()方法定義為:
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
這個(gè)方法有兩種處理方式:
自己將數(shù)據(jù)寫(xiě)到response,然后return null
返回一個(gè)ModelAndView 對(duì)象,讓DispatcherServlet自己來(lái)處理后面渲染工作。
HandlerAdapter有多重類型,例如
SimpleControllerHandlerAdapter處理spring mvc 的controller實(shí)例(注意,不要把這里的controller實(shí)例和@Controller注解POJO混淆,這里controller 指的是org.springframework.web.servlet.mvc.Controller ),并返回ModelAndView,代碼如下:
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return ((Controller) handler).handleRequest(request, response); }
SimpleServletHandlerAdapter 適配的是 Servlet作為request handler的情況,Servlet是不知道MovelAndView的,所以,它的方法并不負(fù)責(zé)渲染頁(yè)面,因此沒(méi)有返回ModelAndView,只是返回null:
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { ((Servlet) handler).service(request, response); return null; }
RequestMappingHandlerAdapter 就是我們上面提到的,用來(lái)處理@Controller和@RequestMapping注解的handler。
渲染視圖
handle()方法調(diào)用之后, DispatcherServlet 可以得到一個(gè)ModelAndView,當(dāng)然也可能是null。對(duì)于ModelAndView不為null的時(shí)候,DispatcherServlet 將會(huì)調(diào)用render()方法。ModelAndView中可能已經(jīng)包含了一個(gè)view或者只是一個(gè)view的名字。如果controller方法指定的是一個(gè)字符串形式的視圖名字,那么就需要進(jìn)行試圖查找:
for (ViewResolver viewResolver : this.viewResolvers) { View view = viewResolver.resolveViewName(viewName, locale); if (view != null) { return view; } }
render()方法完成之后,最終的HTML頁(yè)面會(huì)被發(fā)送至瀏覽器端。
當(dāng)然,springmvc不僅能渲染出頁(yè)面,也可以返回JSON形式或者XML形式。這種情況controller方法一般都是由@RequestBody標(biāo)注的。這種情況就需要 HttpMessageConverter,例如渲染JSON的時(shí)候可以使用Jackson包,我們要返回的對(duì)象將由,MappingJackson2HttpMessageConverter來(lái)轉(zhuǎn)換。
到此,我們就大概說(shuō)完了springmvc的整個(gè)流程。所以,springmvc其實(shí)就是一個(gè)大的Servlet,接收請(qǐng)求,分發(fā)執(zhí)行請(qǐng)求,我們的每一個(gè)controller中的方法都是一個(gè)handler,然后最終渲染視圖。
以上是“SpringMVC中工作原理的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)網(wǎng)站建設(shè)公司行業(yè)資訊頻道!
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)建站jinyejixie.com,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。
網(wǎng)站題目:SpringMVC中工作原理的示例分析-創(chuàng)新互聯(lián)
URL地址:http://jinyejixie.com/article26/ccsjcg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供響應(yīng)式網(wǎng)站、網(wǎng)站排名、網(wǎng)站設(shè)計(jì)、網(wǎng)站導(dǎo)航、全網(wǎng)營(yíng)銷推廣、網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容