日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Spring MVC 到底是如何工作的

發(fā)布時間:2023/12/3 javascript 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring MVC 到底是如何工作的 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

轉(zhuǎn)載自??Spring MVC 到底是如何工作的

這篇文章將深入探討Spring框架的一部分——Spring Web MVC的強大功能及其內(nèi)部工作原理。

這篇文章的源代碼可以在GitHub上找到。

?

項目安裝

在本文中,我們將使用最新、最好的Spring Framework 5。我們將重點介紹Spring的經(jīng)典Web堆棧,該堆棧從框架的第一個版本中就嶄露頭角,并且現(xiàn)在依然是用Spring構(gòu)建Web應(yīng)用程序的主要方式。

對于初學(xué)者來說,為了安裝測試項目,最好使用Spring Boot和一些初學(xué)者依賴項;還需要定義parent:

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.0.M5</version><relativePath/> </parent> <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency> </dependencies>

請注意,為了使用Spring 5,我們還需要使用Spring Boot 2.x。截止到撰寫本文之時,這依然是里程碑發(fā)布版,可在Spring Milestone Repository中找到。讓我們把這個存儲庫添加到你的Maven項目中:

<repositories><repository><id>spring-milestones</id><name>Spring Milestones</name><url>https://repo.spring.io/milestone</url><snapshots><enabled>false</enabled></snapshots></repository> ???????</repositories>

你可以在Maven Central上查看Spring Boot的當前版本。

示例項目

為了理解Spring Web MVC是如何工作的,我們將通過一個登錄頁面實現(xiàn)一個簡單的應(yīng)用程序。為了顯示登錄頁面,我們需要為上下文根創(chuàng)建帶有GET映射的@Controller注解類InternalController。

hello()方法是無參數(shù)的。它返回一個由Spring MVC解釋為視圖名稱的String(在示例中是login.html模板):

import org.springframework.web.bind.annotation.GetMapping; @GetMapping("/") public String hello() {return "login"; }

為了處理用戶登錄,需要創(chuàng)建另一個用登錄數(shù)據(jù)處理POST請求的方法。然后根據(jù)結(jié)果將用戶重定向到成功或失敗的頁面。

請注意,login()方法接收域?qū)ο笞鳛閰?shù)并返回ModelAndView對象:

import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.servlet.ModelAndView; @PostMapping("/login") public ModelAndView login(LoginData loginData) {if (LOGIN.equals(loginData.getLogin()) && PASSWORD.equals(loginData.getPassword())) {return new ModelAndView("success", Collections.singletonMap("login", loginData.getLogin()));} else {return new ModelAndView("failure", Collections.singletonMap("login", loginData.getLogin()));} }

ModelAndView是兩個不同對象的持有者:

  • Model——渲染頁面數(shù)據(jù)的鍵值映射

  • View——填充模型數(shù)據(jù)的頁面模板

連接這些是為了方便,這樣控制器方法可以一次返回它們。

要渲染HTML頁面,使用Thymeleaf作為視圖模板引擎,該引擎具有可靠和開箱即用的與Spring的集成。

Servlet作為Java Web應(yīng)用程序的基礎(chǔ)

那么,當在瀏覽器中輸入http:// localhost:8080/時,按Enter鍵,然后請求到達Web服務(wù)器,實際發(fā)生了什么?你如何從這個請求中看到瀏覽器中的Web表單?

鑒于該項目是一個簡單的Spring Boot應(yīng)用程序,因此可以通過Spring5Application運行它。

Spring Boot默認使用Apache Tomcat。因此,運行應(yīng)用程序時,你可能會在日志中看到以下信息:

2017-10-16 20:36:11.626 INFO 57414 --- [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2017-10-16 20:36:11.634 INFO 57414 --- [main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2017-10-16 20:36:11.635 INFO 57414 --- [main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.5.23

由于Tomcat是一個Servlet容器,因此發(fā)送給Tomcat Web服務(wù)器的每個HTTP請求自然都由Java servlet處理。所以Spring Web應(yīng)用程序入口點是一個servlet,這并不奇怪。

簡單地說,servlet就是任何Java Web應(yīng)用程序的核心組件;它是低層次的,不會像MVC那樣在特定的編程模式中諸多要求。

一個HTTP servlet只能接收一個HTTP請求,以某種方式處理,然后發(fā)回一個響應(yīng)。

而且,從Servlet 3.0 API開始,你現(xiàn)在可以超越XML配置,并開始利用Java配置(只有很小的限制條件)。

DispatcherServlet作為Spring MVC的核心

作為一個Web應(yīng)用程序的開發(fā)人員,我們真正想要做的是抽象出以下繁瑣和模板化的任務(wù),并專注于有用的業(yè)務(wù)邏輯:

  • 將HTTP請求映射到某個處理方法

  • 將HTTP請求數(shù)據(jù)和標題解析成數(shù)據(jù)傳輸對象(DTO)或域?qū)ο?/p>

  • 模型 – 視圖 – 控制器集成

  • 從DTO、域?qū)ο蟮壬身憫?yīng)

Spring?DispatcherServlet能夠提供這些。它是Spring Web?MVC框架的核心;此核心組件接收所有請求到應(yīng)用程序。

正如你所看到的,DispatcherServlet是非常可擴展的。例如,它允許你插入不同的現(xiàn)有或新的適配器進行大量的任務(wù):

  • 將請求映射到應(yīng)該處理它的類或方法(HandlerMapping接口的實現(xiàn))

  • 使用特定模式處理請求,如常規(guī)servlet,更復(fù)雜的MVC工作流,或POJO bean中的方法(HandlerAdapter接口的實現(xiàn))

  • 按名稱解析視圖,允許你使用不同的模板引擎,XML,XSLT或任何其他視圖技術(shù)(ViewResolver接口的實現(xiàn))

  • 通過使用默認的Apache Commons文件上傳實現(xiàn)或編寫你自己的MultipartResolver來解析多部分請求

  • 使用任何LocaleResolver實現(xiàn)解決語言環(huán)境,包括cookie,會話,Accept HTTP頭,或任何其他確定用戶所期望的語言環(huán)境的方式

處理HTTP請求

首先,我們將簡單的HTTP請求的處理追蹤到在控制器層中的一個方法,然后返回到瀏覽器/客戶端。

DispatcherServlet具有很長的繼承層次結(jié)構(gòu);自上而下地逐個理解這些是有價值的。請求處理方法最讓我們感興趣。

?

理解HTTP請求,無論是在本地還是遠程的標準開發(fā)中,都是理解MVC體系結(jié)構(gòu)的關(guān)鍵部分。

GenericServlet

GenericServlet是Servlet規(guī)范的一部分,不直接關(guān)注HTTP。它定義了接收傳入請求并產(chǎn)生響應(yīng)的service()方法。

注意,ServletRequest和ServletResponse方法參數(shù)如何與HTTP協(xié)議無關(guān):

public abstract void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;

這是最終被任何請求調(diào)用到服務(wù)器上的方法,包括簡單的GET請求。

HttpServlet

顧名思義,HttpServlet類就是規(guī)范中定義的基于HTTP的Servlet實現(xiàn)。

更實際的說,HttpServlet是一個抽象類,有一個service()方法實現(xiàn),service()方法實現(xiàn)通過HTTP方法類型分割請求,大致如下所示:

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); // ...}

HttpServletBean

接下來,HttpServletBean是層次結(jié)構(gòu)中第一個Spring-aware類。它使用從web.xml或WebApplicationInitializer接收到的servlet init-param值來注入bean的屬性。

在請求應(yīng)用程序的情況下,doGet(),doPost()等方法應(yīng)特定的HTTP請求而調(diào)用。

FrameworkServlet

FrameworkServlet集成Servlet功能與Web應(yīng)用程序上下文,實現(xiàn)了ApplicationContextAware接口。但它也能夠自行創(chuàng)建Web應(yīng)用程序上下文。

正如你已經(jīng)看到的,HttpServletBean超類注入init-params為bean屬性。所以,如果在servlet的contextClass init-param中提供了一個上下文類名,那么這個類的一個實例將被創(chuàng)建為應(yīng)用程序上下文。否則,將使用默認的XmlWebApplicationContext類。

由于XML配置現(xiàn)在已經(jīng)過時,Spring Boot默認使用AnnotationConfigWebApplicationContext配置DispatcherServlet。但是你可以輕松更改。

例如,如果你需要使用基于Groovy的應(yīng)用程序上下文來配置Spring Web MVC應(yīng)用程序,則可以在web.xml文件中使用以下DispatcherServlet配置:

dispatcherServletorg.springframework.web.servlet.DispatcherServletcontextClassorg.springframework.web.context.support.GroovyWebApplicationContext

使用WebApplicationInitializer類,可以用更現(xiàn)代的基于Java的方式來完成相同的配置。

DispatcherServlet:統(tǒng)一請求處理

HttpServlet.service()實現(xiàn),會根據(jù)HTTP動詞的類型來路由請求,這在低級servlet的上下文中是非常有意義的。然而,在Spring MVC的抽象級別,方法類型只是可以用來映射請求到其處理程序的參數(shù)之一。

因此,FrameworkServlet類的另一個主要功能是將處理邏輯重新加入到單個processRequest()方法中,processRequest()方法反過來又調(diào)用doService()方法:

@Override protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {processRequest(request, response); } @Override protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {processRequest(request, response); }// …

DispatcherServlet:豐富請求

最后,DispatcherServlet實現(xiàn)doService()方法。在這里,它增加了一些可能會派上用場的有用對象到請求:Web應(yīng)用程序上下文,區(qū)域解析器,主題解析器,主題源等:

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());

另外,doService()方法準備輸入和輸出Flash映射。Flash映射基本上是一種模式,該模式將參數(shù)從一個請求傳遞到另一個緊跟的請求。這在重定向期間可能非常有用(例如在重定向之后向用戶顯示一次性信息消息):

FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);if (inputFlashMap != null) {request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap)); } request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());

然后,doService()方法調(diào)用負責(zé)請求調(diào)度的doDispatch()方法。

DispatcherServlet:調(diào)度請求

dispatch()方法的主要目的是為請求找到合適的處理程序,并為其提供請求/響應(yīng)參數(shù)。處理程序基本上是任何類型的object,不限于特定的接口。這也意味著Spring需要為此處理程序找到適配器,該處理程序知道如何與處理程序“交談”。

為了找到匹配請求的處理程序,Spring檢查HandlerMapping接口的注冊實現(xiàn)。有很多不同的實現(xiàn)可以滿足你的需求。

SimpleUrlHandlerMapping允許通過URL將請求映射到某個處理bean。例如,可以通過使用java.util.Properties實例注入其mappings屬性來配置,就像這樣:

/welcome.html=ticketController /show.html=ticketController

可能處理程序映射最廣泛使用的類是RequestMappingHandlerMapping,它將請求映射到@Controller類的@ RequestMapping注釋方法。這正是使用控制器的hello()和login()方法連接調(diào)度程序的映射。

請注意,Spring-aware方法使用@GetMapping和@PostMapping進行注釋。這些注釋依次用@RequestMapping元注釋標記。

dispatch()方法還負責(zé)其他一些HTTP特定任務(wù):

  • 在資源未被修改的情況下,GET請求的短路處理

  • 針對相應(yīng)的請求應(yīng)用多部分解析器

  • 如果處理程序選擇異步處理該請求,則會短路處理該請求

處理請求

現(xiàn)在Spring已經(jīng)確定了請求的處理程序和處理程序的適配器,是時候來處理請求了。下面是HandlerAdapter.handle()方法的簽名。請注意,處理程序可以選擇如何處理請求:

  • 自主地編寫數(shù)據(jù)到響應(yīng)對象,并返回null

  • 返回由DispatcherServlet呈現(xiàn)的ModelAndView對象

@NullableModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

有幾種提供的處理程序類型。以下是SimpleControllerHandlerAdapter如何處理Spring MVC控制器實例(不要將其與@ Controller注釋POJO混淆)。

注意控制器處理程序如何返回ModelAndView對象,并且不自行呈現(xiàn)視圖:

public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {return ((Controller) handler).handleRequest(request, response); }

第二個是SimpleServletHandlerAdapter,它將常規(guī)的Servlet作為請求處理器。

Servlet不知道任何有關(guān)ModelAndView的內(nèi)容,只是簡單地自行處理請求,并將結(jié)果呈現(xiàn)給響應(yīng)對象。所以這個適配器只是返回null而不是ModelAndView:

public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {((Servlet) handler).service(request, response); return null; }

我們碰到的情況是,控制器是有若干@RequestMapping注釋的POJO,所以任何處理程序基本上是包裝在HandlerMethod實例中的這個類的方法。為了適應(yīng)這個處理器類型,Spring使用RequestMappingHandlerAdapter類。

處理參數(shù)和返回處理程序方法的值

注意,控制器方法通常不會使用HttpServletRequest和HttpServletResponse,而是接收和返回許多不同類型的數(shù)據(jù),例如域?qū)ο?#xff0c;路徑參數(shù)等。

此外,要注意,我們不需要從控制器方法返回ModelAndView實例。可能會返回視圖名稱,或ResponseEntity,或?qū)⒈晦D(zhuǎn)換為JSON響應(yīng)等的POJO。

RequestMappingHandlerAdapter確保方法的參數(shù)從HttpServletRequest中解析出來。另外,它從方法的返回值中創(chuàng)建ModelAndView對象。

在RequestMappingHandlerAdapter中有一段重要的代碼,可確保所有這些轉(zhuǎn)換魔法的發(fā)生:

ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod); if (this.argumentResolvers != null) {invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); } if (this.returnValueHandlers != null) {invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers); }

argumentResolvers對象是不同的HandlerMethodArgumentResolver實例的組合。

有超過30個不同的參數(shù)解析器實現(xiàn)。它們允許從請求中提取任何類型的信息,并將其作為方法參數(shù)提供。這包括URL路徑變量,請求主體參數(shù),請求標頭,cookies,會話數(shù)據(jù)等。

returnValueHandlers對象是HandlerMethodReturnValueHandler對象的組合。還有很多不同的值處理程序可以處理方法的結(jié)果來創(chuàng)建適配器所期望的ModelAndViewobject。

例如,當你從hello()方法返回字符串時,ViewNameMethodReturnValueHandler處理這個值。但是,當你從login()方法返回一個準備好的ModelAndView時,Spring會使用ModelAndViewMethodReturnValueHandler。

渲染視圖

到目前為止,Spring已經(jīng)處理了HTTP請求并接收了ModelAndView對象,所以它必須呈現(xiàn)用戶將在瀏覽器中看到的HTML頁面。它基于模型和封裝在ModelAndView對象中的選定視圖來完成。

另外請注意,我們可以呈現(xiàn)JSON對象,或XML,或任何可通過HTTP協(xié)議傳輸?shù)钠渌麛?shù)據(jù)格式。我們將在即將到來的REST-focused部分接觸更多。

讓我們回到DispatcherServlet。render()方法首先使用提供的LocaleResolver實例設(shè)置響應(yīng)語言環(huán)境。假設(shè)現(xiàn)代瀏覽器正確設(shè)置了Accept頭,并且默認使用AcceptHeaderLocaleResolver。

在渲染過程中,ModelAndView對象可能已經(jīng)包含對所選視圖的引用,或者只是一個視圖名稱,或者如果控制器依賴于默認視圖,則什么都沒有。

由于hello()和login()方法兩者都指定所需的視圖為String名稱,因此必須用該名稱查找。所以,這是viewResolvers列表開始起作用的地方:

for (ViewResolver viewResolver : this.viewResolvers) {View view = viewResolver.resolveViewName(viewName, locale);if (view != null) {return view;} }

這是一個ViewResolver實例列表,包括由thymeleaf-spring5集成庫提供的ThymeleafViewResolver。該解析器知道在哪里搜索視圖,并提供相應(yīng)的視圖實例。

在調(diào)用視圖的render()方法后,Spring最終通過發(fā)送HTML頁面到用戶的瀏覽器來完成請求處理。

REST支持

除了典型的MVC場景之外,我們還可以使用框架來創(chuàng)建REST Web服務(wù)。

簡而言之,我們可以接受Resource作為輸入,指定POJO作為方法參數(shù),并使用@RequestBody對其進行注釋。也可以使用@ResponseBody注釋方法本身,以指定其結(jié)果必須直接轉(zhuǎn)換為HTTP響應(yīng):

import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.ResponseBody; @ResponseBody @PostMapping("/message") public MyOutputResource sendMessage(@RequestBody MyInputResource inputResource) {return new MyOutputResource("Received: " + inputResource.getRequestMessage()); }

歸功于Spring MVC的可擴展性,這也是可行的。

為了將內(nèi)部DTO編組為REST表示,框架使用HttpMessageConverter基礎(chǔ)結(jié)構(gòu)。例如,其中一個實現(xiàn)是MappingJackson2HttpMessageConverter,它可以使用Jackson庫將模型對象轉(zhuǎn)換為JSON或從JSON轉(zhuǎn)換。

為了進一步簡化REST API的創(chuàng)建,Spring引入了@RestController注解。默認情況下,這很方便地假定了@ResponseBody語義,并避免在每個REST控制器上的明確設(shè)置:

import org.springframework.web.bind.annotation.RestController; @RestController public class RestfulWebServiceController {@GetMapping("/message")public MyOutputResource getMessage() {return new MyOutputResource("Hello!");} }

結(jié)論

在這篇文章中,我們詳細了介紹在Spring MVC框架中請求的處理過程。了解框架的不同擴展是如何協(xié)同工作來提供所有魔法的,可以讓你能夠事倍功半地處理HTTP協(xié)議難題。

?


?

總結(jié)

以上是生活随笔為你收集整理的Spring MVC 到底是如何工作的的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。