Spring MVC > DispatcherServlet的初始化

Spring MVC的逻辑实现主要在DispatcherServlet中,它是实现servlet接口的实现类。servlet在初始化时会调用init方法,所以DispatcherServlet也不例外。

默认情况下,Servlet在用户第一次请求时才会被servlet容器创建和初始化,而在使用Spring MVC时通常在web.xml定义DispatcherServlet的load-on-startup为1,即让容器(如tomcat)启动时就加载这个Servlet。(注:load-on-startup的详细介绍可参考:http://www.blogjava.net/xzclog/archive/2011/09/29/359789.html)

本文的目的就是要探究DispatcherServlet在init方法中做了哪些初始化动作。研究的源码版本是4.2.2。

在DispatcherServlet类的定义中,并未找到init方法,它一定是继承了父类中的init方法。它的继承结构如下:
Spring MVC > DispatcherServlet的初始化_第1张图片

我在HttpServletBean里面找到final修饰的init方法,这意味着它的子类只能继承,不能重写。init方法中关键的代码如下(省略了日志、异常处理等非必要信息)。我在分析代码时,本着先总后分的原则,先将程序分为几个主要的步骤,了解这几个步骤大致做了什么,再对重要的步骤深入。

public final void init() throws ServletException {
    //1)DispatcherServlet配置的init-param信息存放在当前Servlet对应的ServletConfig对象中,这里把init-param封装到PropertyValues类中;
    PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);

    //2)将当前这个Servlet类转化为一个BeanWrapper类型的实例,目的是方便使用Spring提供的注入功能,完成对对应属性的注入。之所以说是对应属性,因为DispatcherServlet的父类FrameworkServlet有和init-param同名的成员变量;
    BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);

    ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
    //3)注册自定义的属性编辑器,一旦遇到Resource类型的属性就会使用使用ResourceEditor进行解析;
    bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));

    //4)空实现
    initBeanWrapper(bw);
    //5)属性注入
    bw.setPropertyValues(pvs, true);

    //6)空实现
    initServletBean();
}

4)和6)的实现为什么是空的?原因是模板方法模式,1)到6)这六个步骤组成了一个模板方法,其中某些步骤的具体实现可以让子类实现。

HttpServletBean的子类FrameworkServlet实现了步骤6),且以final修饰,核心代码:

protected final void initServletBean() throws ServletException {
    //1)创建或刷新WebApplicationContext实例,并对servlet功能所使用的变量进行初始化;
    this.webApplicationContext = initWebApplicationContext();
    //2)空实现,又是模板方法的应用;
    initFrameworkServlet();
}

继续追踪代码到initWebApplicationContext()方法,核心代码:

protected WebApplicationContext initWebApplicationContext() {
    WebApplicationContext rootContext =WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    WebApplicationContext wac = null;

    //如果this.webApplicationContext!=null,可以判定this.webApplicationContext已经通过构造函数初始化;
    if (this.webApplicationContext != null) {
        wac = this.webApplicationContext;
        if (wac instanceof ConfigurableWebApplicationContext) {
            ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;

            //如果这个context(cwac)尚未被刷新过
            if (!cwac.isActive()) {
                if (cwac.getParent() == null) {
                    cwac.setParent(rootContext);
                }
                configureAndRefreshWebApplicationContext(cwac);
            }
        }
    }
    if (wac == null) {
        //根据web.xml中配置的servlet参数contextAttribute来查找ServletContext中对应的WebApplicationContext;
        wac = findWebApplicationContext();
    }

    //如果通过上面两种方式仍然无法找到WebApplicationContext实例,那么只能新创建一个了;
    if (wac == null) {
        wac = createWebApplicationContext(rootContext);
    }

    //如果得到的context不是一个支持刷新的ConfigurableApplicationContext或者构造函数中注入的context已经被刷新过了,那么这里手动触发一次onRefresh;
    if (!this.refreshEventReceived) {
        onRefresh(wac);
    }

    if (this.publishContext) {
        //attrName的值是:org.springframework.web.servlet.FrameworkServlet.CONTEXT.springMvc,将得到的WebApplicationContext实例放到ServletContext中;
        String attrName = getServletContextAttributeName();
        getServletContext().setAttribute(attrName, wac);
    }

    return wac;
}

这个方法的实现还是有些长的,我们拿几个关键点逐一分析。

1. wac = createWebApplicationContext(rootContext);

跟进createWebApplicationContext方法:

protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {
    return createWebApplicationContext((ApplicationContext) parent);
}

继续跟进:

protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {

    //contextClass是org.springframework.web.context.support.XmlWebApplicationContext
    Class<?> contextClass = getContextClass();

    //如果contextClass不是ConfigurableWebApplicationContext的子类
    if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
        throw new ApplicationContextException("...");
    }

    ConfigurableWebApplicationContext wac =(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

    wac.setEnvironment(getEnvironment());
    wac.setParent(parent);
    wac.setConfigLocation(getContextConfigLocation());

    configureAndRefreshWebApplicationContext(wac);

    return wac;
}

2. configureAndRefreshWebApplicationContext(wac) ??

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
    if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
        if (this.contextId != null) {
            wac.setId(this.contextId);
        }
        else {
    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()));

    ConfigurableEnvironment env = wac.getEnvironment();
    if (env instanceof ConfigurableWebEnvironment) {
        ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
    }

    postProcessWebApplicationContext(wac);
    applyInitializers(wac);
    wac.refresh();
}

3. onRefresh(wac);

FrameworkServlet的onRefresh方法又是空实现,DispatcherServlet覆盖了这个方法,具体实现:

protected void onRefresh(ApplicationContext context) {
    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);
}

这个方法里密密麻麻的初始化了很多变量,接下来逐一分析初始化的这些变量到底是干什么的。

1)initMultipartResolver(context)

MultipartResolver主要用来处理文件上传。

private void initMultipartResolver(ApplicationContext context) {
    this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
}

这里直接从ApplicationContext中获取id为multipartResolver,类型为MultipartResolver的Bean,赋给DispatcherServlet的成员变量multipartResolver。为什么取id为multipartResolver的Bean呢?因为Spring默认是没有multipart处理的,如果想用Spring的multipart,需要在web应用上下文中添加multipart解析器,如下:

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="maxUploadSize">
        <value>20971520</value>
        <!-- 上传文件大小限制为20M,20*1024*1024 -->
    </property>
</bean>

2) initLocaleResolver(context);

LocaleResolver用来解析用户地区。

3) initThemeResolver(context);

ThemeResolver用来解析主题。

4) initHandlerMappings(context);

HandlerMapping的作用:当请求发给DispatcherServlet时,DispatcherServlet会把请求转给HandlerMapping处理,HandlerMapping经过处理后会返回DispatcherServlet一个可用的Controller。

源码:

private void initHandlerMappings(ApplicationContext context) {
    this.handlerMappings = null;

    //默认情况下,Spring MVC会加载所有实现了HandlerMapping接口的bean;如果期望Spring MVC只加载指定的handlermapping,可以在DispatcherServlet的init-param中将detectAllHandlerMapping设置为false;
    if (this.detectAllHandlerMappings) {
        Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
        if (!matchingBeans.isEmpty()) {
            this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
            //对handlermappings进行排序;
    AnnotationAwareOrderComparator.sort(this.handlerMappings);
        }
    }
    else {
    //查找name为handlerMapping的bean,作为当前唯一的handlermapping;
        HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
        this.handlerMappings = Collections.singletonList(hm);
    }
    if (this.handlerMappings == null) {
        //DispatcherServlet所在目录的DispatcherServlet.properties中的org.springframework.web.servlet.HandlerMapping来加载handlermapping,值为org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
        this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
    }
}

5) initHandlerAdapters(context);

HandlerAdapter使用了适配器模式。作用:DispatcherServlet通过handlerMapping获取对应的handler后,会轮询HandlerAdapter,查找能够处理当前Http请求的HandlerAdapter实现,handlerAdapter根据handlerMapping返回的handler类型来选择适当的handlerAdapter实现,从而适配当前的Http请求。

主要代码:

private void initHandlerAdapters(ApplicationContext context) {
    this.handlerAdapters = null;

    if (this.detectAllHandlerAdapters) {
        // 加载所有实现了HandlerAdapter接口的bean;
        Map<String, HandlerAdapter> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
        if (!matchingBeans.isEmpty()) {
            this.handlerAdapters = new ArrayList<HandlerAdapter>(matchingBeans.values());
            // handlerAdapters排序
            AnnotationAwareOrderComparator.sort(this.handlerAdapters);
        }
    }
    else {

    //获取name为handlerAdapter的bean,作为唯一的handlerAdapter;
        HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
        this.handlerAdapters = Collections.singletonList(ha);
    }

    if (this.handlerAdapters == null) {
        //从DispatcherServlet所在目录的DispatcherServlet.properties文件的org.springframework.web.servlet.HandlerAdapter加载默认的三个handlerAdapter;
        this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
    }
}

DispatcherServlet所在目录的DispatcherServlet.properties文件的org.springframework.web.servlet.HandlerAdapter默认的三个handlerAdapter是:

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

6) initHandlerExceptionResolvers(context);
可以通过实现HandlerExceptionResolver接口来自定义异常的处理,然后把实现类在Spring Context中声明为bean。

和上面的处理流程大同小异,同样是如果detectAllHandlerExceptionResolvers为true,先加载所有实现了HandlerExceptionResolver接口的bean,否则加载name为handlerExceptionResolver的bean为唯一的handlerExceptionResolvers;如果依然没找到合适的handlerExceptionResolver,就去按照DispatcherServlet.properties里的org.springframework.web.servlet.HandlerExceptionResolver去加载。

private void initHandlerExceptionResolvers(ApplicationContext context) {
    this.handlerExceptionResolvers = null;

    if (this.detectAllHandlerExceptionResolvers) {
        Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
        if (!matchingBeans.isEmpty()) {
            this.handlerExceptionResolvers = new ArrayList<HandlerExceptionResolver>(matchingBeans.values());
            AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
        }
    }
    else {
        HandlerExceptionResolver her =
                context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
        this.handlerExceptionResolvers = Collections.singletonList(her);
    }

    if (this.handlerExceptionResolvers == null) {
        this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
    }
}

7) initRequestToViewNameTranslator(context);

当Controller处理器方法没有返回一个View对象或者逻辑视图名称,并且在该方法中没有直接往response的输出流里写数据的时候,Spring就会采用约定好的方式提供一个逻辑视图名,这个逻辑视图名是通过RequestToViewNameTranslator接口的getViewName方法来实现的。

8) initViewResolvers(context);

初始化步骤和上面类似。ViewResolver接口定义了resolveViewName方法,根据viewName创建合适类型的View实现。

9) initFlashMapManager(context);
Spring MVC Flash Attributes提供了请求存储功能,可供其他请求使用,在使用重定向时很必要,例如Post/Redirect/Get模式。Flash attributes在重定向之前暂存以便重定向之后还能使用,并立即删除。

Spring MVC有两个主要的抽象来支持flash attributes,FlashMap用于保持flash attributes,而FlashMapManager用于存储、检索、管理FlashMap实例。

private void initFlashMapManager(ApplicationContext context) {
    this.flashMapManager = context.getBean(FLASH_MAP_MANAGER_BEAN_NAME,FlashMapManager.class);
}

Spring MVC Flash Attributes可参考:http://www.open-open.com/lib/view/open1397266120028.html

你可能感兴趣的:(Spring MVC > DispatcherServlet的初始化)