欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringMVC工作原理實例詳解

 更新時間:2020年03月03日 10:51:53   作者:逃離沙漠  
這篇文章主要介紹了SpringMVC工作原理實例詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下

介紹

SpringWeb MVC是Spring Framework中的一部分,當(dāng)我們需要使用spring框架創(chuàng)建web應(yīng)用的時候就需要引入springweb mvc。對于程序員來說,我們只需要增加@Controller ,@RequestMapping注解然后,瀏覽器中的請求就會到達(dá)springweb應(yīng)用。我們只需要在 controller中編寫相關(guān)邏輯即可。然而,請求是在哪里接收的?@Controller ,@RequestMapping注解做了什么?本文我們來探討一下。

從一個項目開始

本文假定你已經(jīng)能熟練的使用springmvc。為了展開后續(xù)的討論,假設(shè)我們新建了一個spring-mvc-demo的項目。并由此項目來展開討論。項目中有一個控制器,代碼如下:

@Controller
@RequestMapping("/app")
public class AppController {
 @RequestMapping(method=RequestMethod.GET,value="/hello")
  public @ResponseBody String hello(HttpServletRequest request,String name) {
  return "Hello,"+name;
 }
}

控制器寫好之后,我們將項目打車war包,放入tomcat容器中,并使用8080端口啟動tomcat,運行項目,然后在瀏覽器中輸入http://localhost:8080/app/hello?name=world.

我們在瀏覽器中可以看到:Hello,world的輸出。

我們先記住這個例子,下面我們帶著一些疑問繼續(xù)看,這個請求是怎么被接收到的?請求是怎么交給AppController處理的?

Servlet是Java Web應(yīng)用的基石

當(dāng)你在瀏覽器中輸入 http://loalhost:8080/ ,按下enter建,然后請求命中了服務(wù)器,這是怎么發(fā)生的?又如何從這個請求中得到瀏覽器中可見的頁面?

本例中,我們給出的是一個簡單的spring-mvc應(yīng)用,并放入了tomcat中(springboot 內(nèi)嵌tomcat啟動其實也是一樣的)。 Tomcat 是一個servlet容器,這點我想每個Java程序員都十分清楚,我們在沒有使用spring-mvc之前,就是使用servlet+jsp來開發(fā)web應(yīng)用。

由于Tomcat是一個web容器,每一個發(fā)送給Tomcat服務(wù)器的HTTP請求自然會被一個Java Servlet處理。所以,SpringMvc 必定有一個servlet,SpringWeb應(yīng)用的入口必定是一個Servlet,這應(yīng)該不難想到。

簡單來說,Servlet是任何Java Web應(yīng)用的核心組件(除非你不用servlet規(guī)范,比如你使用netty)。Servlet它是低層次的,并且不會像MVC那樣強(qiáng)加于特定的編程模式。它只是可以讓你寫一個偶一個Servlet,一個HTTP Servlet可以接受一個HTTP請求,然后處理它,并返回一個響應(yīng)。

而springmvc 就是使用了一個大的servlet,下面我們就來說這個大的servlet。

DispatcherServlet是Spring MVC的核心

上面我們已經(jīng)提到Servlet 是Java web應(yīng)用的基石,Spring應(yīng)用入口必定是一個Servlet,這個Servlet 其實就是DispatcherServlet。

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

  • 映射一個HTTP請求到某個處理方法。
  • 將HTTP請求數(shù)據(jù),和頭信息轉(zhuǎn)換成數(shù)據(jù)對象(DTO / domain object)。
  • 模型 - 視圖 - 控制器 之間的交互。
  • 從DTO,域?qū)ο蟮壬身憫?yīng)

Spring DispatcherServlet提供了這些。它是Spring Web MVC框架的核心, 這個核心組件接收所有請求到您的應(yīng)用程序。
DispatcherServlet具有很強(qiáng)的可擴(kuò)展性。 例如,它允許您插入不同的現(xiàn)有或新適配器以執(zhí)行大量任務(wù):

  • 將請求映射到應(yīng)該處理它的類或方法(HandlerMapping接口的實現(xiàn))
  • 使用特定模式處理請求,例如常規(guī)servlet,更復(fù)雜的MVC工作流或者POJO bean中的方法(HandlerAdapter接口的實現(xiàn))
  • 通過名稱解析視圖,允許您使用不同的模板引擎,XML,XSLT或任何其他視圖技術(shù)(ViewResolver接口的實現(xiàn))
  • 通過使用默認(rèn)的Apache Commons文件上傳實現(xiàn)或編寫自己的MultipartResolver來解析multipart請求
  • 使用任何LocaleResolver實現(xiàn)解決語言環(huán)境,包括Cookie,會話,Accept HTTP標(biāo)頭或用于確定用戶期望的語言環(huán)境的任何其他方式

處理HTTP請求

首先,讓我們來追蹤一個簡單的HTTP請求到達(dá)controller中的方法,然后返回到 瀏覽器/客戶端的處理過程。
DispatcherServlet 有一個很長的繼承關(guān)系。它的繼承關(guān)系是這樣的:

GenericServlet <- HttpServlet <- HttpServletBean <- FreamworkServlet <- DispatcherServlet

GenericServlet

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

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

注意,這里的參數(shù)中的ServletRequest,ServletResponse并不是和HTTP協(xié)議綁定的,Http有具體協(xié)議Servlet。

HttpServlet

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

更實際的是,HttpServlet是一個具有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);
    // ...
  }

根據(jù)請求的不同, get,post方法會分別被不同方法處理。

HttpServletBean

上面我們展示過 DispatcherServlet的繼承關(guān)系,在這個繼承鏈中,HttpServletBean是進(jìn)入spring的第一個層次。從HttpServletBean開始往下的幾個servlet都是spring中的類。HttpServletBean 就是一個servlet,它繼承自HttpServlet,就像是我們在使用servlet+jsp開發(fā)時候定義的servlet一樣。

根據(jù)servlet的生命周期我們知道,servlet會被容器初始化,初始化時候,其init()方法會被調(diào)用。在springmvc框架中 HttpServletBean使用從web.xml或WebApplicationInitializer收到的servlet init-param值來注入bean的屬性。
在請求應(yīng)用程序的情況下,為這些特定的HTTP請求調(diào)用doGet(),doPost()等方法。

FrameworkServlet

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

正如上面所說,F(xiàn)rameworkServlet的超類HttpServletBean將init-param注入為bean屬性。所以,如果servlet的contextClass init-param中提供了context類的名字,那么這個context類的實例將會被創(chuàng)建,用作應(yīng)用的context。否則,將會使用XmlWebApplicationContext作為默認(rèn)的。

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

有了上面鋪墊,我們這里可以開始關(guān)鍵的內(nèi)容,即DispatcherServlet統(tǒng)一請求處理。在Springmvc的項目中,我們通常會在web.xml配置一個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這個servlet類是應(yīng)用啟動入口。其生命周期的第一階段init()方法完成初始化工作。

doService()方法設(shè)置請求信息

DispatcherServlet 初始化之后,便可以工作了。當(dāng)請求到達(dá)之時,會調(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ù)的處理過程中會用到。設(shè)置完后,它調(diào)用doDispatch() 方法。

doDispatch()方法分發(fā)請求

doService()方法最終調(diào)用了doDispatch(),看名知意,這個方法是做分發(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 {
      // 刪除一些代碼
    }
}

這個方法主要作用就是找到合適的 handler 來處理請求。handler通常是一個某個類型的對象,并且不限定特定的接口。因此spring需要找到這個handler的適配器。這個Handler通常是一個HandlerMethod實例,

為了找到與請求匹配handler,spring需要從已注冊的HandlerMapping接口實現(xiàn)類里邊去找。這個查找過程就是在上面的getHandler() 方法完成得到的是一個HandlerExecutionChain。 這里體現(xiàn)了責(zé)任鏈模式。

這個getHandler() 會遍歷一個HandlerMapping的map。由于我們一般都使用注解形式:@Controller,@RequestMapping注解。因此這里找到HandlerMapping實現(xiàn)就是RequestMappingHandlerMapping

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

getHandlerAdapter()方法找到最終的handler適配器,找到的適配器就是RequestMappingHandlerAdapter,(因為我們使用的是@RequestMapping注解形式)。

本例中,我們定義了AppController 的hello()方法,并用@Controller,@RequestMapping對其分別進(jìn)行注解,因此這里得到的適配器HandlerAdapter 所適配HandlerMethod就是 AppController 的hello()方法的 。

HandlerAdapter處理請求

上面通過 確定了HandlerAdapter之后,就要執(zhí)行handle() 方法了,即上面代碼中,try語句塊里邊的ha.handle()。handle()方法定義為:

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

這個方法有兩種處理方式:

  • 自己將數(shù)據(jù)寫到response,然后return null
  • 返回一個ModelAndView 對象,讓DispatcherServlet自己來處理后面渲染工作。

HandlerAdapter有多重類型,例如

SimpleControllerHandlerAdapter處理spring mvc 的controller實例(注意,不要把這里的controller實例和@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é)渲染頁面,因此沒有返回ModelAndView,只是返回null:

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

RequestMappingHandlerAdapter 就是我們上面提到的,用來處理@Controller和@RequestMapping注解的handler。

渲染視圖

handle()方法調(diào)用之后, DispatcherServlet 可以得到一個ModelAndView,當(dāng)然也可能是null。對于ModelAndView不為null的時候,DispatcherServlet 將會調(diào)用render()方法。ModelAndView中可能已經(jīng)包含了一個view或者只是一個view的名字。如果controller方法指定的是一個字符串形式的視圖名字,那么就需要進(jìn)行試圖查找:

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

render()方法完成之后,最終的HTML頁面會被發(fā)送至瀏覽器端。

當(dāng)然,springmvc不僅能渲染出頁面,也可以返回JSON形式或者XML形式。這種情況controller方法一般都是由@RequestBody標(biāo)注的。這種情況就需要 HttpMessageConverter,例如渲染JSON的時候可以使用Jackson包,我們要返回的對象將由,MappingJackson2HttpMessageConverter來轉(zhuǎn)換。

到此,我們就大概說完了springmvc的整個流程。所以,springmvc其實就是一個大的Servlet,接收請求,分發(fā)執(zhí)行請求,我們的每一個controller中的方法都是一個handler,然后最終渲染視圖。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 帶你深入了解java-代理機(jī)制

    帶你深入了解java-代理機(jī)制

    Java 有兩種代理方式,一種是靜態(tài)代理,另一種是動態(tài)代理。如果我們在代碼編譯時就確定了被代理的類是哪一個,那么就可以直接使用靜態(tài)代理;如果不能確定,那么可以使用類的動態(tài)加載機(jī)制,在代碼運行期間加載被代理的類這就是動態(tài)代理
    2021-08-08
  • Java單鏈表的實現(xiàn)代碼

    Java單鏈表的實現(xiàn)代碼

    這篇文章主要介紹了Java單鏈表的實現(xiàn)代碼的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2016-07-07
  • springboot jta atomikos實現(xiàn)分布式事物管理

    springboot jta atomikos實現(xiàn)分布式事物管理

    這篇文章主要介紹了springboot jta atomikos實現(xiàn)分布式事物管理,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-12-12
  • SpringBoot整合Dozer映射框架流程詳解

    SpringBoot整合Dozer映射框架流程詳解

    dozer是用來兩個對象之間屬性轉(zhuǎn)換的工具,有了這個工具之后,我們將一個對象的所有屬性值轉(zhuǎn)給另一個對象時,就不需要再去寫重復(fù)的set和get方法了,下面介紹下SpringBoot中Dozer的使用,感興趣的朋友一起看看吧
    2022-07-07
  • 關(guān)于SpringSecurity認(rèn)證邏輯源碼分析

    關(guān)于SpringSecurity認(rèn)證邏輯源碼分析

    這篇文章主要介紹了關(guān)于SpringSecurity認(rèn)證邏輯源碼分析,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • Spring Controller接收前端JSON數(shù)據(jù)請求方式

    Spring Controller接收前端JSON數(shù)據(jù)請求方式

    這篇文章主要為大家介紹了Spring Controller接收前端JSON數(shù)據(jù)請求方式詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • 解決SpringBoot打成jar運行后無法讀取resources里的文件問題

    解決SpringBoot打成jar運行后無法讀取resources里的文件問題

    這篇文章主要介紹了解決SpringBoot打成jar運行后無法讀取resources里的文件問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • 詳解Java中的線程模型與線程調(diào)度

    詳解Java中的線程模型與線程調(diào)度

    這篇文章主要介紹了詳解Java中的線程模型與線程調(diào)度的相關(guān)資料,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下
    2021-02-02
  • Java OpenCV實現(xiàn)圖像鏡像翻轉(zhuǎn)效果

    Java OpenCV實現(xiàn)圖像鏡像翻轉(zhuǎn)效果

    這篇文章主要為大家詳細(xì)介紹了Java OpenCV實現(xiàn)圖像鏡像翻轉(zhuǎn)效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-07-07
  • 淺析Bean?Searcher?與?MyBatis?Plus?區(qū)別介紹

    淺析Bean?Searcher?與?MyBatis?Plus?區(qū)別介紹

    Bean?Searcher號稱任何復(fù)雜的查詢都可以一行代碼搞定,但?Mybatis?Plus?似乎也有類似的動態(tài)查詢功能,最近火起的?Bean?Searcher?與?MyBatis?Plus?倒底有啥區(qū)別?帶著這個問題一起通過本文學(xué)習(xí)下吧
    2022-05-05

最新評論