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

詳解springMVC容器加載源碼分析

 更新時(shí)間:2017年05月26日 09:08:48   作者:micro_hz  
這篇文章主要介紹了詳解springMVC容器加載源碼分析,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧

springmvc是一個(gè)基于servlet容器的輕量靈活的mvc框架,在它整個(gè)請(qǐng)求過程中,為了能夠靈活定制各種需求,所以提供了一系列的組件完成整個(gè)請(qǐng)求的映射,響應(yīng)等等處理。這里我們來分析下springMVC的源碼。

首先,spring提供了一個(gè)處理所有請(qǐng)求的servlet,這個(gè)servlet實(shí)現(xiàn)了servlet的接口,就是DispatcherServlet。把它配置在web.xml中,并且處理我們?cè)谡麄€(gè)mvc中需要處理的請(qǐng)求,一般如下配置:

<servlet>
    <servlet-name>spring-servlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>spring-servlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

DispatcherServlet也繼承了FrameworkServlet抽象類,這個(gè)抽象類為整個(gè)servlet的處理提供了spring容器的支持,讓原本servlet容器的DispatcherServlet擁有能夠利用spring容器組件的能力。上面servlet的初始化參數(shù)contextConfigLocation就是DispatcherServlet獲取spring容器的配置環(huán)境。FrameworkServlet繼承自org.springframework.web.servlet.HttpServletBean。HttpServletBean實(shí)現(xiàn)了servlet容器初始化會(huì)調(diào)用的init函數(shù)。這個(gè)init函數(shù)會(huì)調(diào)用FrameworkServlet的初始化加載spring容器方法。方法源碼:

@Override
  protected final void initServletBean() throws ServletException {
    getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
    if (this.logger.isInfoEnabled()) {
      this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
    }
    long startTime = System.currentTimeMillis();

    try {
     // 初始化spring-servlet容器
      this.webApplicationContext = initWebApplicationContext();
      initFrameworkServlet();
    }
    catch (ServletException ex) {
      this.logger.error("Context initialization failed", ex);
      throw ex;
    }
    catch (RuntimeException ex) {
      this.logger.error("Context initialization failed", ex);
      throw ex;
    }

    if (this.logger.isInfoEnabled()) {
      long elapsedTime = System.currentTimeMillis() - startTime;
      this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
          elapsedTime + " ms");
    }
  }

可以看到這里就觸發(fā)了spring的對(duì)web支持的容器初始化,這里使用的容器為WebApplicationContext.接下來我們就來分析一下整個(gè)容器的初始化過程:

protected WebApplicationContext initWebApplicationContext() {
// 查看是否在servlet上下文有所有容器需要繼承的根Web容器
    WebApplicationContext rootContext =
        WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    WebApplicationContext wac = null;
    // 如果容器已經(jīng)加載
    if (this.webApplicationContext != null) {
      // A context instance was injected at construction time -> use it
      wac = this.webApplicationContext;
      if (wac instanceof ConfigurableWebApplicationContext) {
        ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
        if (!cwac.isActive()) {
          // The context has not yet been refreshed -> provide services such as
          // setting the parent context, setting the application context id, etc
          if (cwac.getParent() == null) {
            // The context instance was injected without an explicit parent -> set
            // the root application context (if any; may be null) as the parent
            cwac.setParent(rootContext);
          }
          configureAndRefreshWebApplicationContext(cwac);
        }
      }
    }
    if (wac == null) {
      // No context instance was injected at construction time -> see if one
      // has been registered in the servlet context. If one exists, it is assumed
      // that the parent context (if any) has already been set and that the
      // user has performed any initialization such as setting the context id
      wac = findWebApplicationContext();
    }
    if (wac == null) {
      // 創(chuàng)建容器
      wac = createWebApplicationContext(rootContext);
    }

    if (!this.refreshEventReceived) {
      // Either the context is not a ConfigurableApplicationContext with refresh
      // support or the context injected at construction time had already been
      // refreshed -> trigger initial onRefresh manually here.
      onRefresh(wac);
    }

    if (this.publishContext) {
      // Publish the context as a servlet context attribute.
      String attrName = getServletContextAttributeName();
      getServletContext().setAttribute(attrName, wac);
      if (this.logger.isDebugEnabled()) {
        this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
            "' as ServletContext attribute with name [" + attrName + "]");
      }
    }

    return wac;
  }

如果已經(jīng)有容器被創(chuàng)建那就初始化。否則創(chuàng)建容器,創(chuàng)建邏輯:

protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
    Class<?> contextClass = getContextClass();
    if (this.logger.isDebugEnabled()) {
      this.logger.debug("Servlet with name '" + getServletName() +
          "' will try to create custom WebApplicationContext context of class '" +
          contextClass.getName() + "'" + ", using parent context [" + parent + "]");
    }
    // 判斷是否是可配置的容器
    if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
      throw new ApplicationContextException(
          "Fatal initialization error in servlet with name '" + getServletName() +
          "': custom WebApplicationContext class [" + contextClass.getName() +
          "] is not of type ConfigurableWebApplicationContext");
    }
    // 如果找到目標(biāo)容器,并且可配置,然后就開始獲取配置文件并開始初始化
    ConfigurableWebApplicationContext wac =
        (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

    wac.setEnvironment(getEnvironment());
    wac.setParent(parent);
    wac.setConfigLocation(getContextConfigLocation());
    // 這里是獲取配置文件和完成初始化web容器的入口
    configureAndRefreshWebApplicationContext(wac);

    return wac;
  }

這里完成了 web容器的相關(guān)準(zhǔn)備工作,開始正式讀取配置文件加載和初始化容器。

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
// 給容器設(shè)置一個(gè)id
    if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
      // The application context id is still set to its original default value
      // -> assign a more useful id based on available information
      if (this.contextId != null) {
        wac.setId(this.contextId);
      }
      else {
        // Generate default id...
        wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
            ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
      }
    }

    wac.setServletContext(getServletContext());
    wac.setServletConfig(getServletConfig());
    wac.setNamespace(getNamespace());
    wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

    // The wac environment's #initPropertySources will be called in any case when the context
    // is refreshed; do it eagerly here to ensure servlet property sources are in place for
    // use in any post-processing or initialization that occurs below prior to #refresh
    ConfigurableEnvironment env = wac.getEnvironment();
    if (env instanceof ConfigurableWebEnvironment) {
      ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
    }
    /* 這里在容器被激活后,
    并且容器還沒完成初始化之前可以對(duì)容器的相關(guān)配置做一些修改,
    默認(rèn)給了空實(shí)現(xiàn),
    這里子類可以去重寫,能夠獲得在容器初始化之前做  一些處理*/
    postProcessWebApplicationContext(wac);
    // 這里講容器的初始化信息放到一個(gè)列表
    applyInitializers(wac);
    // 這里就開始web容器的初始化
    wac.refresh();
  }

容器的初始化在AbstractApplicationContext,無論是其他的容器,最終都會(huì)調(diào)用到refresh()函數(shù),這個(gè)函數(shù)基本定義了整個(gè)容器初始化的整個(gè)脈絡(luò),這里不展開講,本博客之后應(yīng)該會(huì)詳細(xì)分析這塊的邏輯,這里大概的注釋一下每一個(gè)函數(shù)完成的操作:

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
      // 這里主要加載了容器當(dāng)中一些從其他配置文件讀取的變量
      prepareRefresh();

      // 獲取容器本身
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // 這里完成一些基礎(chǔ)組件的依賴
      prepareBeanFactory(beanFactory);

      try {
        // 添加 容器初始化之前的前置處理
        postProcessBeanFactory(beanFactory);

        // 調(diào)用 前置處理器,其中包含invokeBeanDefinitionRegistryPostProcessors與invokeBeanFactoryPostProcessors兩類前置處理器的調(diào)用
        invokeBeanFactoryPostProcessors(beanFactory);

        // 注冊(cè)bean被創(chuàng)建之前的前置處理器
        registerBeanPostProcessors(beanFactory);

        // 初始化容器的編碼源        
        initMessageSource();

        // 初始化一些事件監(jiān)聽器
        initApplicationEventMulticaster();

        // Initialize other special beans in specific context subclasses.
        onRefresh();

        // 注冊(cè)容器監(jiān)聽器
        registerListeners();

        // 初始化所有非懶加載的beans
        finishBeanFactoryInitialization(beanFactory);

        // Last step: 事件通知關(guān)心容器加載的相關(guān)組件
        finishRefresh();
      }

      // 部分代碼省略
      }
    }
  }

自此加載完畢核心容器,然后回到FramewordServlet的initWebApplicationContext函數(shù),在調(diào)用createWebApplicationContext完成一系列上面的操作之后,需要mvc servlet組件,入口就是onRefresh(ApplocationContext context)方法。它會(huì)調(diào)用另一個(gè)方法initStrategies(ApplicationContext context)。該方法如下:

protected void initStrategies(ApplicationContext context) {
    initMultipartResolver(context);
    initLocaleResolver(context);
    initThemeResolver(context);
    // 獲取所有的RequestMappings
    initHandlerMappings(context);
    // 不同handler的適配器
    initHandlerAdapters(context);
    // 異常解析器
    initHandlerExceptionResolvers(context);
    initRequestToViewNameTranslator(context);
    initViewResolvers(context);
    initFlashMapManager(context);
  }

這里我們重點(diǎn)講解initHandlerMappings與initHandlerAdapters函數(shù),因?yàn)檫@兩個(gè)是處理servlet請(qǐng)求的入口。

在spring mvc中任何類都可以處理request請(qǐng)求,因?yàn)镈ispacherServlet也是實(shí)現(xiàn)了HttpServlet的接口,所以處理請(qǐng)求也是doService里。doService會(huì)將請(qǐng)求交給doDispatcher函數(shù)處理。然后doDispacher的源碼:

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

        // 這里獲取當(dāng)前請(qǐng)求的處理handler
        mappedHandler = getHandler(processedRequest, false);
        if (mappedHandler == null || mappedHandler.getHandler() == null) {
          noHandlerFound(processedRequest, response);
          return;
        }

        /* 因?yàn)閔andler可以是任何類,
        但是我們的DispacherServlet需要一個(gè)統(tǒng)一的處理接口,這個(gè)接口就是HandlerAdapter,
        不同的HandlerMapping可以獲取到各種各樣的Handler,
        這個(gè)handler最后都必須得到HandlerAdapter的支持才能被DispacherServlet所調(diào)用。
        擴(kuò)充一個(gè)新的HandlerMapping只需要添加一個(gè)新的HandlerAdatper即可,有點(diǎn)抽象工廠模式的味道*/
        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()) {
            logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
          }
          if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
            return;
          }
        }

        if (!mappedHandler.applyPreHandle(processedRequest, response)) {
          return;
        }

        // Actually invoke the handler.
        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

        if (asyncManager.isConcurrentHandlingStarted()) {
          return;
        }

        applyDefaultViewName(request, mv);
        mappedHandler.applyPostHandle(processedRequest, response, mv);
      }
      catch (Exception ex) {
        dispatchException = ex;
      }
      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
        if (mappedHandler != null) {
          mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
        }
      }
      else {
        // Clean up any resources used by a multipart request.
        if (multipartRequestParsed) {
          cleanupMultipart(processedRequest);
        }
      }
    }
  }

但是我們發(fā)現(xiàn)獲取到的handler并不是Object而是一個(gè)HandlerExecutionChain,這個(gè)類可以進(jìn)去查看發(fā)現(xiàn)是一堆攔截器和一個(gè)handler,主要就是給每一個(gè)請(qǐng)求一個(gè)前置處理的機(jī)會(huì),這里值得一提的是一般來說攔截器和過濾器的區(qū)別就是攔截器可以終止后續(xù)執(zhí)行流程,而過濾器一般不終止。過濾器一般是容器級(jí)別的,這個(gè)handler前置攔截器可以做到更細(xì)級(jí)別的控制,例如過濾器只定義了init和doFilter,但是這個(gè)handler攔截器定義了preHandle和postHandle還有afterCompletion函數(shù),不難理解分別對(duì)應(yīng)不同請(qǐng)求階段的攔截粒度,更加靈活。

獲取處理handler的getHandler代碼:

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 這里獲取多個(gè)映射方式,一旦找到合適的處理請(qǐng)求的handler即返回
    for (HandlerMapping hm : this.handlerMappings) {
      if (logger.isTraceEnabled()) {
        logger.trace(
            "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
      }
      // HandlerExecutionChain包含了
      HandlerExecutionChain handler = hm.getHandler(request);
      if (handler != null) {
        return handler;
      }
    }
    return null;
  }

HandlerAdapter處理后返回統(tǒng)一被封裝成ModelAndView對(duì)象,這個(gè)就是包含試圖和數(shù)據(jù)的對(duì)象,在經(jīng)過適當(dāng)?shù)奶幚恚?/p>

processDispatchResult(HttpServletRequest request, HttpServletResponse response,
      HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception)

將頁面與返回的數(shù)據(jù)返回給瀏覽器完成整個(gè)請(qǐng)求過程。以上就是springMVC大概的啟動(dòng)過程和請(qǐng)求的處理過程,之后本博客還會(huì)陸續(xù)分析核心的spring源碼,博主一直認(rèn)為精讀著名框架源碼是在短時(shí)間提升代碼能力的捷徑,因?yàn)檫@些輪子包含太多設(shè)計(jì)思想和細(xì)小的代碼規(guī)范,這些讀多了都會(huì)潛移默化的形成一種本能的東西。設(shè)計(jì)模式也不斷貫穿其中。

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

相關(guān)文章

  • druid連接池的參數(shù)配置示例全面解析

    druid連接池的參數(shù)配置示例全面解析

    這篇文章主要為大家介紹了druid連接池的參數(shù)配置示例全面解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-09-09
  • Java雜談之代碼重構(gòu)的方法多長(zhǎng)才算長(zhǎng)

    Java雜談之代碼重構(gòu)的方法多長(zhǎng)才算長(zhǎng)

    關(guān)于代碼重構(gòu)的理解:在不改變軟件系統(tǒng)/模塊所具備的功能特性的前提下,遵循/利用某種規(guī)則,使其內(nèi)部結(jié)構(gòu)趨于完善。其在軟件生命周期中的價(jià)值體現(xiàn)主要在于可維護(hù)性和可擴(kuò)展性
    2021-10-10
  • Maven高級(jí)的聚合和繼承的實(shí)現(xiàn)

    Maven高級(jí)的聚合和繼承的實(shí)現(xiàn)

    在軟件開發(fā)中,隨著項(xiàng)目規(guī)模的擴(kuò)大,單個(gè)模塊的開發(fā)方式逐漸轉(zhuǎn)變?yōu)槎嗄K開發(fā),這種方式帶來了項(xiàng)目管理上的挑戰(zhàn),其中最常見的問題是模塊間的依賴管理和版本控制問題,本文就來介紹一下
    2024-10-10
  • MyBatis-Plus 如何單元測(cè)試的實(shí)現(xiàn)

    MyBatis-Plus 如何單元測(cè)試的實(shí)現(xiàn)

    這篇文章主要介紹了MyBatis-Plus 如何單元測(cè)試的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • 深入理解java自旋鎖

    深入理解java自旋鎖

    這篇文章主要介紹了如何深入理解java自旋鎖,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,下面和小編來一起學(xué)習(xí)下吧
    2019-05-05
  • 詳解SpringBoot中Controller接收對(duì)象列表實(shí)現(xiàn)

    詳解SpringBoot中Controller接收對(duì)象列表實(shí)現(xiàn)

    這篇文章主要介紹了詳解SpringBoot中Controller接收對(duì)象列表實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05
  • 基于IDEA 的遠(yuǎn)程調(diào)試 Weblogic的操作過程

    基于IDEA 的遠(yuǎn)程調(diào)試 Weblogic的操作過程

    這篇文章主要介紹了基于IDEA 的遠(yuǎn)程調(diào)試 Weblogic的操作過程,本文通過圖文實(shí)例相結(jié)合給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2021-09-09
  • MyBatis insert操作插入數(shù)據(jù)之后返回插入記錄的id

    MyBatis insert操作插入數(shù)據(jù)之后返回插入記錄的id

    今天小編就為大家分享一篇關(guān)于MyBatis插入數(shù)據(jù)之后返回插入記錄的id,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2019-03-03
  • Java面試題-實(shí)現(xiàn)復(fù)雜鏈表的復(fù)制代碼分享

    Java面試題-實(shí)現(xiàn)復(fù)雜鏈表的復(fù)制代碼分享

    這篇文章主要介紹了Java面試題-實(shí)現(xiàn)復(fù)雜鏈表的復(fù)制代碼分享,小編覺得還是挺不錯(cuò)的,具有參考價(jià)值,需要的朋友可以了解下。
    2017-10-10
  • @JsonSerialize序列化注解的使用

    @JsonSerialize序列化注解的使用

    這篇文章主要介紹了@JsonSerialize序列化注解的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08

最新評(píng)論