SpringMVC之DispatchServlet初始化過程


  SpringMvc最核心的類就是前端控制器DispatchServlet,作為一個Servlet,是整個SpringMvc的入口,用於調度其他的各組件工作,如Controller、HandlerMapping、ViewResolver等,控制着整個處理用戶請求的流程,本篇首先來總結一下DispatchServlet的初始化過程,及進行具體處理請求前的預准備

作為一個Servlet的主要繼承關系:HttpServletBean -- > FrameworkServlet -- > DispatchServlet,分別分析這三個類,各自所做的工作,就基本上總結了整個DispatchServlet的初始過程

1、HttpServletBean

重寫了GenericServlet的init方法,servelt實例化時init方法會被調用:

    @Override
	public final void init() throws ServletException {
		if (logger.isDebugEnabled()) {
			logger.debug("Initializing servlet '" + getServletName() + "'");
		}

		//將Servlet的初始化參數(initParam)保存在PropertyValues,並檢查Servlet的初始化參數包含了所必須的參數,子類可以將在this.requiredProperties指定必須的參數
		PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
		if (!pvs.isEmpty()) {
			try {
                      //BeanWrapper就是對象的包裹者,主要用於對象的屬性設置,這里的this,就是DispatchServlet
				BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
                      //ServletContext的資源加載器,使用本質上是使用ServletContext對象來獲取與當前Servlet相關的資源
				ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
                      //自定義了屬性編輯器,用於編輯被包裹對象的屬性
				bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
                       //空方法留給子類自定義初始化BeanWrapper
				initBeanWrapper(bw);
              	        //將Servlet的初始化參數設置到BeanWrapper中
				bw.setPropertyValues(pvs, true);
			}
			catch (BeansException ex) {
				if (logger.isErrorEnabled()) {
					logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
				}
				throw ex;
			}
		}

		//在FrameworkServlet中實現
		initServletBean();

		if (logger.isDebugEnabled()) {
			logger.debug("Servlet '" + getServletName() + "' configured successfully");
		}
	}

總結:主要使用ServletConfig配置信息初始化DispatchServlet的一些屬性,如使用web.xml時配置的contextConfigLocation

 

2、FrameworkServlet

1)重寫initServletBean()初始化IOC容器

調用鏈:HttpServletBean.init()-->FrameworkServlet.initServletBean()

    @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 {
                  //初始化ioc容器(設置父子容器並刷新)
			this.webApplicationContext = initWebApplicationContext();
                  //空方法,留給子類擴展
			initFrameworkServlet();
		}
		...
	}

核心方法initWebApplicationContext():

protected WebApplicationContext initWebApplicationContext() {
  		//根據ServletContext.getAttribute來獲取到WebApplicationContext(根容器),而獲取到的根容器是之前由ContextLoaderListener(ServletContextListener)的contextInitialized方法將根容器保存在了ServletContext
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;
  		//this.webApplicationContext為子容器(保存SpringMVC組件),如果我們使用的是配置類的方式即繼承AbstractAnnotationConfigDispatcherServletInitializer來指定創建父子容器,那么在Servlet容器啟動的時侯webApplicationContext就被創建了
		if (this.webApplicationContext != null) {     
			wac = this.webApplicationContext;
			if (wac instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
				if (!cwac.isActive()) {
					if (cwac.getParent() == null) {
                     	          //將根容器設置為子容器(保存SpringMVC組件)的父容器
						cwac.setParent(rootContext);
					}
                                //配置並且刷新容器(啟動初始化容器過程),之前的父子容器只是被創建沒有調用refresh
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}
  		
		if (wac == null) {
                 //如果子容器還沒又被創建,嘗試去ServletContext中以獲取
			wac = findWebApplicationContext();
		}
		if (wac == null) {
                 //如果子容器還為空,就通過web.xml配置的參數contextConfigLocation指定的Xml配置文件路徑來創建一個XmlWebApplicationContext類型的ioc子容器,設置父子容器關系,並刷新。
			wac = createWebApplicationContext(rootContext);
		}
		
		if (!this.refreshEventReceived) {
                 //在DispatchServlet中實現
			onRefresh(wac);
		}
		//this.publishContext指定是否將web容器發布在ServletContext中,默認為ture
		if (this.publishContext) {
			String attrName = getServletContextAttributeName();
          	    //將初始化好的ioc容器放入ServletContext中
			getServletContext().setAttribute(attrName, wac);
			if (this.logger.isDebugEnabled()) {
				this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
						"' as ServletContext attribute with name [" + attrName + "]");
			}
		}

		return wac;
	}

FrameworkServlet的initServletBean方法主要就是初始化IOC容器,包括一個子容器(保存springmvc組件,如Controller、ViewResolver、HandlerMapping等等)和一個父容器(保存業務邏輯組件,如service,dao),

2)重寫了HttpServelt的service方法
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {

	HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
	if (HttpMethod.PATCH == httpMethod || httpMethod == null) {
      	    //處理PATCH請求
		processRequest(request, response);
	}
	else {
      	    //如果不是PATCH請求,調用HttpServelt.service()
		super.service(request, response);
	}
}

FrameworkServlet也重寫了doGet、doPost、xxx等對應處理各類型請求的方法,最終都是調用了processRequest(request, response)來處理:

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

   long startTime = System.currentTimeMillis();
   Throwable failureCause = null;
   //獲取之前LocaleContext(主要作用是封裝請求的 Locale 信息,主要就是語言信息)默認不存在
   LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
  //創建本次請求的localeContext
   LocaleContext localeContext = buildLocaleContext(request);
	//獲取之前RequestAttributes(封裝request,response)默認不存在
   RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
  	//創建本次請求的requestAttributes
   ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
	//與異步請求相關的處理
   WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
   asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
	//將localeContext、requestAttributes分別保存在LocaleContextHolder、RequestContextHolder中,兩者都是使用ThreadLocal與當前線程綁定
   initContextHolders(request, localeContext, requestAttributes);

   try {
     //真正的處理請求過程在DispatchServlet中實現
      doService(request, response);
   }
   catch (ServletException ex) {
      failureCause = ex;
      throw ex;
   }
   catch (IOException ex) {
      failureCause = ex;
      throw ex;
   }
   catch (Throwable ex) {
      failureCause = ex;
      throw new NestedServletException("Request processing failed", ex);
   }

   finally {
     //完成請求默認移除requestAttributes和localeContext
      resetContextHolders(request, previousLocaleContext, previousAttributes);
      if (requestAttributes != null) {
         requestAttributes.requestCompleted();
      }

      if (logger.isDebugEnabled()) {
         if (failureCause != null) {
            this.logger.debug("Could not complete request", failureCause);
         }
         else {
            if (asyncManager.isConcurrentHandlingStarted()) {
               logger.debug("Leaving response open for concurrent processing");
            }
            else {
               this.logger.debug("Successfully completed request");
            }
         }
      }
	  //無論請求是否成功都會發布請求處理完成事件(我們可以向容器中添加相應的事件監聽器)
      publishRequestHandledEvent(request, response, startTime, failureCause);
   }
}

總結:1、在Servlet初始化階段,初始化了IOC容器

           2、在處理請求階段,做了一些提前的准備工作

 

3、DispatcherServlet

1)重寫onRefresh,初始化SpringMvc工作組件

HttpServletBean.init()-->FrameworkServlet.initServletBean()-->FrameworkServlet.initWebApplicationContext()-->DispatcherServlet.onRefresh(ApplicationContext context)

@Override
protected void onRefresh(ApplicationContext context) {
  //context就是在FrameworkServlet.initWebApplicationContext()中完成初始化工作的IOC容器
   initStrategies(context);
}
//完成各組件的初始化
protected void initStrategies(ApplicationContext context) {
  //初始化文件上傳的處理類 initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); }

這里我們以處理器映射器的初始化initHandlerMappings(context)為例分析,其他組件的初始化處理也類似

private void initHandlerMappings(ApplicationContext context) {
		this.handlerMappings = null;
  		//this.detectAllHandlerMappings默認為true
		if (this.detectAllHandlerMappings) {
			//在容器中找到所有的HandlerMapping
			Map<String, HandlerMapping> matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
			if (!matchingBeans.isEmpty()) {
              //將所有的HandlerMapping保存在handlerMappings中
				this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
				// 排序
				AnnotationAwareOrderComparator.sort(this.handlerMappings);
			}
		}
		else {
			try {
				HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
				this.handlerMappings = Collections.singletonList(hm);
			}
			catch (NoSuchBeanDefinitionException ex) {
				// Ignore, we'll add a default HandlerMapping later.
			}
		}

		//如果在IOC容器中沒有找到任何的HandlerMapping,獲取默認的HandlerMapping
		if (this.handlerMappings == null) {
			this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
			if (logger.isDebugEnabled()) {
				logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
			}
		}
	}

getDefaultStrategies(context, HandlerMapping.class)獲取默認的組件:

protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
  //這的key為org.springframework.web.servlet.HandlerMapping
   String key = strategyInterface.getName();
  //在defaultStrategies獲取我們需要創建的組件的類型,多個的話,使用逗號隔開
   String value = defaultStrategies.getProperty(key);
   if (value != null) {
     //使用逗號為分隔符,轉化成數組
      String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
      List<T> strategies = new ArrayList<T>(classNames.length);
     //遍歷classNames利用反射創建對象
      for (String className : classNames) {
         try {
            Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
           //創建並添加到IOC容器中
            Object strategy = createDefaultStrategy(context, clazz);
            strategies.add((T) strategy);
         }
         catch (ClassNotFoundException ex) {
            throw new BeanInitializationException(
                  "Could not find DispatcherServlet's default strategy class [" + className +
                        "] for interface [" + key + "]", ex);
         }
         catch (LinkageError err) {
            throw new BeanInitializationException(
                  "Error loading DispatcherServlet's default strategy class [" + className +
                        "] for interface [" + key + "]: problem with class file or dependent class", err);
         }
      }
      return strategies;
   }
   else {
      return new LinkedList<T>();
   }
}
//保存了各組件接口對應的默認實現類
private static final Properties defaultStrategies;
//靜態代碼塊,用於初始化defaultStrategies
static {
   try {
     //private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties",創建了一個與DispatcherServlet.class處於同一包下的DispatcherServlet.properties文件的資源對象
      ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
      //創建Properties對象,保存了組件接口的對應的默認實現類
      defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
   }
   catch (IOException ex) {
      throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
   }
}

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

各個SpringMvc工作組件的初始化過程為:首先從IOC容器中找對應接口類型的組件,如果沒到,就創建一個在DispatcherServlet.properties中指定的默認組件接口實現類的實例

2)重寫doService,進入核心方法doDispatch(request, response)
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
   if (logger.isDebugEnabled()) {
      String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
      logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
            " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
   }

 	//這里與RequestDispatcher.include()相關
   Map<String, Object> attributesSnapshot = null;
   if (WebUtils.isIncludeRequest(request)) {
      attributesSnapshot = new HashMap<String, Object>();
      Enumeration<?> attrNames = request.getAttributeNames();
      while (attrNames.hasMoreElements()) {
         String attrName = (String) attrNames.nextElement();
         if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
            attributesSnapshot.put(attrName, request.getAttribute(attrName));
         }
      }
   }

   //將一些組件設置到request中
   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());

   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());
   request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);

   try {
     //最終調用了
      doDispatch(request, response);
   }
   finally {
      if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
         // Restore the original attribute snapshot, in case of an include.
         if (attributesSnapshot != null) {
            restoreAttributesAfterInclude(request, attributesSnapshot);
         }
      }
   }
}

最終進入doDispatch(request, response),作為DispatchServlet最核心方法,調度着各工作組件進行具體的請求處理。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM