最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

代碼分析Spring MVC的工作原理

 更新時間:2019年06月06日 11:35:02   投稿:laozhang  
在本篇文章里小編給大家整理了關于Spring MVC的工作原理的相關知識點以及實例代碼內容,需要的朋友們可以參考下。

遺留問題

在關于利用maven搭建ssm的博客,我們一起來探討下問的最多的問題中,我遺留了一個問題:Spring mvc是何時、何地、如何將Model中的屬性綁定到哪個作用域,這里的作用域指的是Servlet的四大作用域;不了解問題背景的可以回過頭去看看我的上篇博文。

明確的解答我會放到最后,在解答問題之前,我先和大家一起來捋一捋Spring mvc的工作原理。廢話不多說,開始我們神秘的探險之旅!

應用示例

在講工作原理之前,我們先看一個簡單的spring mvc(ssm)示例,以及實現(xiàn)的效果

工程代碼地址:ssm-web 

工程結構與效果如上所示,我們不做過多的探究,我們打起精神往下看本篇的重點

工作原理

準備 - 資源的加載與初始化

1、DispatcherServlet 靜態(tài)初始化

DispatcherServlet中有如下靜態(tài)塊

static {
 // Load default strategy implementations from properties file.
 // This is currently strictly internal and not meant to be customized
 // by application developers.
 try {
  ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
  defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
 }
 catch (IOException ex) {
  throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + ex.getMessage());
 }
 }

這里會將DispatcherServlet.properties中的內容讀取到DispatcherServlet的屬性:private static final Properties defaultStrategies中,DispatcherServlet.properties內容如下

# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
 org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
 org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
 org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter

org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\
 org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
 org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

指定了DispatcherServlet策略接口的默認實現(xiàn),后續(xù)DispatcherServlet初始化策略的時候會用到

2、interceptor定義的加載

spring啟動過程中會調用InterceptorsBeanDefinitionParser的parse方法來解析出我們自定義的interceptor定義,封裝成MappedInterceptor類型的bean定義,并放到spring容器中;我們可以簡單的認為spring容器中已經(jīng)存在了我們自定義的interceptor的bean定義

3、DispatcherServlet初始化策略:initStrategies

DispatcherServlet的繼承圖如下

DispatcherServlet是一個Servlet,tomcat啟動過程中會調用其init方法,一串的調用后,會調用DispatcherServlet的initStrategies方法

protected void initStrategies(ApplicationContext context) {
 initMultipartResolver(context);
 initLocaleResolver(context);
 initThemeResolver(context);
 initHandlerMappings(context);
 initHandlerAdapters(context);
 initHandlerExceptionResolvers(context);
 initRequestToViewNameTranslator(context);
 initViewResolvers(context);
 initFlashMapManager(context);
}

實例化步驟1中的默認實現(xiàn),并填充到DispatcherServlet各個屬性值中

4、DefaultAnnotationHandlerMapping的攔截器初始化

DispatcherServlet.properties種指定了兩個默認的HandlerMapping:BeanNameUrlHandlerMapping、DefaultAnnotationHandlerMapping,這兩者的類繼承圖如下(我們暫時只關注DefaultAnnotationHandlerMapping)

DefaultAnnotationHandlerMapping間接實現(xiàn)了ApplicationContextAware,那么在DefaultAnnotationHandlerMapping實例初始化過程中,會調用setApplicationContext(ApplicationContext applicationContext)方法,一串調用后,會來到AbstractUrlHandlerMapping的initApplicationContext()

@Override
protected void initApplicationContext() throws BeansException {
 extendInterceptors(this.interceptors);
 detectMappedInterceptors(this.mappedInterceptors);
 initInterceptors();
}

初始化了DefaultAnnotationHandlerMapping的攔截器:interceptor

我們來看下具體的初始化過程,看看上面的順序是否只是我個人的臆想?

可以看到,初始化順序就是我們上面說的,不是我個人的意淫;此時的DefaultAnnotationHandlerMapping中有我們自定義的MyInterceptor。初始化過程我們需要關注的就是上述這些,下面我們一起看看具體請求的過程

請求的處理

請求從servlet的service開始,一路到DispatcherServlet的doDispatch,如下圖

doDispatch

/**
 * Process the actual dispatching to the handler. 將請求分發(fā)到具體的handler,也就是我們的controller
 * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
 * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
 * to find the first that supports the handler class.
 * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
 * themselves to decide which methods are acceptable.
 * @param request current HTTP request
 * @param response current HTTP response
 * @throws Exception in case of any kind of processing failure
 */
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
 HttpServletRequest processedRequest = request;
 HandlerExecutionChain mappedHandler = null;
 boolean multipartRequestParsed = false;

 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

 try {
  ModelAndView mv = null;
  Exception dispatchException = null;

  try {
   processedRequest = checkMultipart(request);
   multipartRequestParsed = processedRequest != request;

   // Determine handler for the current request. 決定哪個handler來處理當前的請求
   // mappedHandler是由handler和interceptor集合組成的一個執(zhí)行鏈,有點類似FilterChain
   mappedHandler = getHandler(processedRequest);
   if (mappedHandler == null || mappedHandler.getHandler() == null) {
    noHandlerFound(processedRequest, response);
    return;
   }

   // Determine handler adapter for the current request. 決定哪個adapter來處理當前的請求
   // handlerMapping是找出適配的handler,而真正回調handler的是adapter
   HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

   // Process last-modified header, if supported by the handler.
   String method = request.getMethod();
   boolean isGet = "GET".equals(method);
   if (isGet || "HEAD".equals(method)) {
    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
    if (logger.isDebugEnabled()) {
     String requestUri = urlPathHelper.getRequestUri(request);
     logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
    }
    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
     return;
    }
   }

   // handler的前置處理,也就是調用適配當前url的interceptor的preHandler方法
   if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    return;
   }

   try {
    // Actually invoke the handler. 真正調用handler
    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
   }
   finally {
    if (asyncManager.isConcurrentHandlingStarted()) {
     return;
    }
   }

   applyDefaultViewName(request, mv);
   // handler的后置處理,也就是調用適配當前url的interceptor的postHandler方法
   mappedHandler.applyPostHandle(processedRequest, response, mv);
  }
  catch (Exception ex) {
   dispatchException = ex;
  }
  // 處理handler返回的結果,會調用適配當前url的interceptor的afterCompletion方法
  // 這里會將響應結果返回給請求者
  processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
 }
 catch (Exception ex) {
  triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
 }
 catch (Error err) {
  triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
 }
 finally {
  if (asyncManager.isConcurrentHandlingStarted()) {
   // Instead of postHandle and afterCompletion
   mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
   return;
  }
  // Clean up any resources used by a multipart request.
  if (multipartRequestParsed) {
   cleanupMultipart(processedRequest);
  }
 }
}

handlerMapping具體如何找到匹配當前url的handler(一般而言就是我們的controller)、handlerAdapter具體如何回調真正的handler,有興趣的可以自行去跟下,我就不跟了。我們具體看下processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); 這個與我們最初的疑問有關

processDispatchResult

可以看到model中的persons會被設置到request的attributes中,然后轉發(fā)請求到show_person.jsp,轉發(fā)過程中request作用域的變量仍然有效,所以show_person.jsp中的jstl標簽和el表達式能夠取到persons變量,最后將show_person.jsp中的內容填充好之后的靜態(tài)內容返回給請求者;至此就完成了一次請求的響應

問題解答

回到我們開篇的疑問:Spring mvc是何時、何地、如何將Model中的屬性綁定到哪個作用域?想必大家已經(jīng)知道答案了

Controller中的model、ModelMap的注入由spring mvc完成,這個不是請求傳入的參數(shù),用于綁定變量到Servlet作用域;默認情況下,在DispatcherServlet調用了真正的handler之后,將結果返回給請求者的過程中,將model、modelMap中的變量設置到了request的attributes中,轉發(fā)的過程中,request中的變量仍然有效,所以show_person.jsp中能取到persons這個變量,自此疑問得到解答

總結

1、Spring MVC工作原理圖

圖是用的別人的,具體是誰的我也不記得了(捂臉)

2、DefaultAnnotationHandlerMapping在spring3.2中被廢棄,替換成了RequestMappingHandlerMapping

相關文章

  • Java?Stream流的常見生成和操作方法總結

    Java?Stream流的常見生成和操作方法總結

    從Java1.8開始提出了Stream流的概念,本文將通過示例為大家詳細講解一下Stream流的常見生成和操作方法,感興趣的小伙伴可以了解一下
    2022-09-09
  • SpringBoot+MySQL+Jpa實現(xiàn)對數(shù)據(jù)庫的增刪改查和分頁詳解

    SpringBoot+MySQL+Jpa實現(xiàn)對數(shù)據(jù)庫的增刪改查和分頁詳解

    這篇文章主要介紹了SpringBoot+MySQL+Jpa實現(xiàn)對數(shù)據(jù)庫的增刪改查和分頁詳解,需要的朋友可以參考下
    2020-02-02
  • 關于swagger配置及踩坑@Api參數(shù)postion無效解決接口排序問題

    關于swagger配置及踩坑@Api參數(shù)postion無效解決接口排序問題

    這篇文章主要介紹了關于swagger配置及踩坑@Api參數(shù)postion無效解決接口排序問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • IDEA必備開發(fā)神器之EasyCode

    IDEA必備開發(fā)神器之EasyCode

    對于java程序員來說,日常工作中就是crud的操作,每次都要搭建MVC三層,還是很繁瑣,這里就出現(xiàn)了神器easycode的工具.可以快速生成代碼.并且還可以自定義模板.需要的朋友可以參考下
    2021-05-05
  • 從最基本的Java工程搭建SpringMVC+SpringDataJPA+Hibernate

    從最基本的Java工程搭建SpringMVC+SpringDataJPA+Hibernate

    本文會介紹從一個最基本的java工程,到Web工程,到集成Spring、SpringMVC、SpringDataJPA+Hibernate,本文介紹的非常詳細,具有參考借鑒價值,感興趣的朋友一起學習吧
    2016-05-05
  • Spring Cloud Feign組成配置過程解析

    Spring Cloud Feign組成配置過程解析

    這篇文章主要介紹了Spring Cloud Feign組成配置過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-03-03
  • 淺談java字符串比較到底應該用==還是equals

    淺談java字符串比較到底應該用==還是equals

    這篇文章主要介紹了淺談java字符串比較到底應該用==還是equals,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-12-12
  • 基于sharding-jdbc的使用限制

    基于sharding-jdbc的使用限制

    這篇文章主要介紹了sharding-jdbc的使用限制,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • springboot表單提交之validator校驗

    springboot表單提交之validator校驗

    在前臺表單驗證的時候,通常會校驗一些數(shù)據(jù)的可行性,比如是否為空,長度,身份證,郵箱等等,這篇文章主要給大家介紹了關于springboot表單提交之validator校驗的相關資料,需要的朋友可以參考下
    2021-05-05
  • Java Semaphore信號量使用分析講解

    Java Semaphore信號量使用分析講解

    Semaphore實際上是一種共享鎖,因為它允許多個線程并發(fā)獲取共享的資源,在Semaphore對象創(chuàng)建時必須設置可用令牌的初始數(shù)量permits,用于控制并發(fā)時同時獲取資源權限的線程數(shù)量,這篇文章主要介紹了Java中的Semaphore如何使用,需要的朋友可以參考下
    2022-12-12

最新評論

比如县| 唐海县| 小金县| 天长市| 固原市| 临泉县| 珲春市| 涪陵区| 黄陵县| 天水市| 托克托县| 兖州市| 水城县| 双峰县| 基隆市| 保亭| 讷河市| 武清区| 灵川县| 新兴县| 桂林市| 温州市| 上虞市| 集贤县| 奇台县| 东阳市| 石河子市| 梁山县| 榆中县| 毕节市| 郑州市| 陇川县| 宁南县| 东光县| 余庆县| 曲阳县| 岳阳市| 卓资县| 兴业县| 阆中市| 新巴尔虎左旗|