Spring Web MVC 源码分析(一)----- DispatcherServlet

一、概述

    整个Spring web MVC的设计思想都是围绕着 DispatcherServlet 分发requests 到 handlers 来设计的。 

    DispatcherServlet对于一个请求的处理工作流如下:

Spring Web MVC 源码分析(一)----- DispatcherServlet_第1张图片

    DispatcherServlet是一个真实的Servlet(继承自HttpServlet),所以在构建一个Spring MVC程序时,需要配置DispatcherServlet需要处理的请求,如在 web.xml里配置:

  
    dispatcher
    org.springframework.web.servlet.DispatcherServlet
    1
  
  
    dispatcher
    /
  

     在Web MVC框架中,每一个DispatcherServlet有自己的WebApplicationContext,这个Context会继承所有在root WebApplicationContext下定义的bean,一个典型的层次结构如下图:

    Spring Web MVC 源码分析(一)----- DispatcherServlet_第2张图片

    在DispatcherServlet的初始化时,Spring MVC 会在WEB-INF 文件夹下寻找文件名为 [servlet-name]-servlet.xml的配置文件,这个Spring配置创建新的bean,或重写在Root Context下的Bean,所以我们创建配置文件 dispatcher-servlet.xml 如下:



    
    
        
        
    
    

      

二、DispacherServlet初始化

    DispacherServlet的类图结构如下:

Spring Web MVC 源码分析(一)----- DispatcherServlet_第3张图片

FrameworkServlet:Spring web 框架的基类,提供与Spring ApplicationContext集成的功能。

HttpServletBean: 继承自 HttpServlet,是其简单的扩展,而初始化即是从此类重写的init方法开始的。

初始化流程如下:

Spring Web MVC 源码分析(一)----- DispatcherServlet_第4张图片

HttpServletBean的 init方法是DispatcherServlet初始化的入口方法:

    // 将配置文件里的参数写入servlet的bean属性中,并且调用子类的初始化方法
    @Override
	public final void init() throws ServletException {
		if (logger.isDebugEnabled()) {
			logger.debug("Initializing servlet '" + getServletName() + "'");
		}

		// 将必要的参数初始化进Bean中
		try {
			PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
			BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
			ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
			bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
			initBeanWrapper(bw);
			bw.setPropertyValues(pvs, true);
		}
		catch (BeansException ex) {
			logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
			throw ex;
		}

		// frameworkServlet初始化
		initServletBean();

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

FrameworkServlet 的initServletBean方法:

    // bean的Properties已经被设置好了,创建Servlet的WebApplicationContext
	@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 {
            // 初始化WebApplicationContext
			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");
		}
	}

可以看到,初始化主要调用FrameworkServlet的initWebApplicationContext()

    // 初始化WebApplicationContext
	protected WebApplicationContext initWebApplicationContext() {
        // 得到此web app的 root WebApplicationContext
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;
       
		if (this.webApplicationContext != null) {
			// 存在一个WebApplicationContext在Servlet创建时被注入
			wac = this.webApplicationContext;
			if (wac instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
				if (!cwac.isActive()) {
					// 当前context还没有refreshed -> 设置parent context, 设置application_context_id
					if (cwac.getParent() == null) {
                        // 没有父 context,设置为rootContext
						cwac.setParent(rootContext);
					}
                    // 配置wac,并且调用其refresh方法
                    // 此处完成IOC容器与ApplicationContext的初始化
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}
		if (wac == null) {
	        // 若没有在servlet创建时被注入,则查看在当前servlet context下是否存在wac。
            // 若有,意味着 已经完成了初始化,设置了Parent context和 contextId
			wac = findWebApplicationContext();
		}
		if (wac == null) {
			// 若servlet context没有wac,则创建一个
            // 在此方法内部亦会调用configureAndRefreshWebApplicationContext(cwac)
			wac = createWebApplicationContext(rootContext);
		}

		if (!this.refreshEventReceived) {
            // 判断有无 refresh过,调用 DispacherServlet的onRefresh方法
			onRefresh(wac);
		}
        
        // 若需要发布wac作为servletContext的一个属性
		if (this.publishContext) {
			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;
	}

    在《Spring core 源码分析 ---- IoC容器(一) 初始化》已详细说明了ApplicationContext初始化的过程,而在整个Web App的WebApplicationContext初始化后,调用了DispacherServlet的一个很关键的方法OnRefresh(wac),此方法才真正的开始Spring MVC的相关初始化,初始化Spring MVC里的所有组件。

    DispatcherServlet:

	@Override
	protected void onRefresh(ApplicationContext context) {
		initStrategies(context);
	}

	// 初始化所有servlet需要使用到的策略对象,此处初始化所有组件
    // 可能会被子类重写
	protected void initStrategies(ApplicationContext context) {
        // 初始化 getBean("multipartResolver")
        // 支持文件上传,spring mvc只提供一种CommonsFileupload
		initMultipartResolver(context);
        // 初始化 getBean("localeResolver")
        // 用来支持国际化
		initLocaleResolver(context);
        // 初始化 getBean("themeResolver")
        // 处理web页面的主题
		initThemeResolver(context);
        // 初始化 List 
        // 关键处理流程,映射requests到handlers 和一系列的pre-与post-处理器
		initHandlerMappings(context);
        // 初始化 List
        // 帮助servlet直接调用handler
		initHandlerAdapters(context);
        // 初始化 List
        // 处理所有unexpected exception,可以指定modelandView
        // 也可捕获标准异常 
		initHandlerExceptionResolvers(context);
        // 初始化 getBean("viewNameTranslator")
        // 若没有viewName时,匹配逻辑上的viewName,如localhost/register.html,就是register
        // 然后由ViewResolver可能变成localhost/register.jsp
		initRequestToViewNameTranslator(context);
        // 初始化 List
        // viewResolver解决viewName与view的真实关系,如本例配置的suffix=".jsp"
		initViewResolvers(context);
        // 初始化 getBean("flashMapManager")
        // 用于 存储与取回 input与output的FlashMap 
        // 通常在重定向时传递attributes到另一个请求
		initFlashMapManager(context);
	}

    initMultipartResolver ,initLocaleResolver,initThemeResolver,initRequestToViewNameTranslator的实现都是简单的getBean("**") ,若抛出异常,则根据DispatcherServlet.properties 取默认的Strategy 。

    所以我们仔细看initHandlerMappings(context)方法以及initHandlerAdapters(这也是后续处理request的关键初始化方法)。    

    // 初始化这个类使用的handlersMappings 
    // 如果BeanFactory 没有HandlerMapping, 我们默认使用BeanNameUrlHandlerMapping
	private void initHandlerMappings(ApplicationContext context) {
		this.handlerMappings = null;

		if (this.detectAllHandlerMappings) {
			// 找到ApplicationContext的所有HandlerMappings ,包括祖先Context
			Map matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
			if (!matchingBeans.isEmpty()) {
				this.handlerMappings = new ArrayList(matchingBeans.values());
				// 排序handlerMappings
				AnnotationAwareOrderComparator.sort(this.handlerMappings);
			}
		}
		else {
			try {
				HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
				this.handlerMappings = Collections.singletonList(hm);
			}
			catch (NoSuchBeanDefinitionException ex) {
				// 之后会添加一个默认的 HandlerMapping 
			}
		}

		// 如果初始化handlerMappings为空,则添加一个默认的HandlerMapping
		if (this.handlerMappings == null) {
            // DispatcherServlet同个包有DispatcherServlet.properties文件,根据这个文件来创建
            // 默认的HandlerMapping---- BeanNameUrlHandlerMapping 以及 DefaultAnnotationHandlerMapping
			this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
			if (logger.isDebugEnabled()) {
				logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
			}
		}
	}

 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

    initHandlerAdapters,initHandlerExceptionResolvers,initViewResolvers与initHandlerMapping的逻辑一样。

     至此,WebApplicationContext初始化完毕。

转载于:https://my.oschina.net/alextrz/blog/895668

你可能感兴趣的:(Spring Web MVC 源码分析(一)----- DispatcherServlet)