Spring MVC > DispatcherServlet的初始化

来源:互联网 发布:淘宝客服教程视频在线 编辑:程序博客网 时间:2024/05/22 10:53

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方法。它的继承结构如下:
这里写图片描述

我在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

0 0