一、SpringMVC的启动过程

SpringMVC的启动过程即为Servlet的启动和Spring的启动两部分。

我们对SpringMVC的典型配置,是在web.xml中配置一个DispatcherServlet

因此我们需要从DispatcherServlet的启动说起。

1.1、DispatcherServlet的继承结构

DispatcherServlet的继承结构简单来说如下:

DispatcherServlet->FrameworkServlet->HttpServletBean->HttpServlet

因此如果要看DispatcherServlet是如何启动的,需要从init()方法开始。

即:HttpServletBean.init();

1.2、DispatcherServlet的启动过程

有人喜欢用时序图来画启动过程,而我喜欢用调用顺序来画。

DispatcherServlet.init()
    ├new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties)//读取配置信息
    └DispatcherServlet.initServletBean()初始化ServletBean
        └FrameworkServlet.initServletBean()
            ├FrameworkServlet.webApplicationContext = initWebApplicationContext()//启动Spring
            │    ├FrameworkServlet.wac = createWebApplicationContext(rootContext);
            │    │    ├wac =(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(XmlWebApplicationContext.class);
            │    │    └FrameworkServlet.configureAndRefreshWebApplicationContext(wac)
            │    │        └wac.refresh()//根据springXML文件启动Spring
            │    └FrameworkServlet.onRefresh(wac)
            │        └DispatcherServlet.onRefresh(wac)
            │            └DispatcherServlet.initStrategies(wac)
            │                ├DispatcherServlet.initMultipartResolver(context)//从wac中获取对象
            │                ├DispatcherServlet.initLocaleResolver(context)//从wac中获取对象
            │                ├DispatcherServlet.initThemeResolver(context)//从wac中获取对象
            │                ├DispatcherServlet.initHandlerMappings(context)//从wac中获取对象
            │                ├DispatcherServlet.initHandlerAdapters(context)//从wac中获取对象
            │                ├DispatcherServlet.initHandlerExceptionResolvers(context)//从wac中获取对象
            │                ├DispatcherServlet.initRequestToViewNameTranslator(context)//从wac中获取对象
            │                ├DispatcherServlet.initViewResolvers(context)//从wac中获取对象
            │                └DispatcherServlet.initFlashMapManager(context)//从wac中获取对象
            └FrameworkServlet.initFrameworkServlet()//从wac中获取对象

1.3、DispatcherServlet的默认配置

DispatcherServlet的默认配置在DispatcherServlet.properties中

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

分别代表了不同模块的实现类,我们需要注意的、最重要的有如下几种(其他的一些类也有作用,但是对我个人而言,最重要的还是这三种):

handlerMapping:RequestMappingHandlerMapping

handlerAdapter:RequestMappingHandlerAdapter

viewResolver:FreeMarkerViewResolver

1.4、Spring的启动过程wac.refresh()

Spring的启动过程是一致的,从spring.xml开始加载,例如:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:util="http://www.springframework.org/schema/util"  
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:mongo="http://www.springframework.org/schema/data/mongo"
    xmlns:task="http://www.springframework.org/schema/task"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/aop  
        http://www.springframework.org/schema/aop/spring-aop.xsd  
        http://www.springframework.org/schema/beans  
        http://www.springframework.org/schema/beans/spring-beans.xsd  
        http://www.springframework.org/schema/context  
        http://www.springframework.org/schema/context/spring-context.xsd  
        http://www.springframework.org/schema/tx  
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/task  
        http://www.springframework.org/schema/task/spring-task-3.1.xsd
        http://www.springframework.org/schema/util 
        http://www.springframework.org/schema/util/spring-util-3.1.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">

    <mvc:default-servlet-handler />
    <mvc:annotation-driven ignoreDefaultModelOnRedirect="true"/>
    <aop:aspectj-autoproxy proxy-target-class="true"/>
    <context:annotation-config></context:annotation-config>
    <context:component-scan base-package="cn.com.gome.sn.admin" />

    <!-- 文件上传解析器 -->  
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">  
        <property name="defaultEncoding" value="utf-8"></property>  
        <property name="maxInMemorySize" value="10960"></property>  
    </bean>
    <bean id="springBeanFactory" class="cn.com.gome.frame.util.SpringBeanFactory"/>
    <util:properties id="SN_admin" location="classpath:props/SN-admin.properties" />
    <!--登录验证-->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**/*.dhtml"/>
            <bean class="cn.com.gome.sn.admin.intercepter.SessionInterceptor">
                <property name="allowUrls">
                    <list>
                        <value>/login/login.dhtml</value>
                    </list>
                </property>
            </bean>
        </mvc:interceptor>
    </mvc:interceptors>
</beans>

可以看到其中比较重要的Bean如下:

经查看SpringMVC的NamespaceHandler为org.springframework.web.servlet.config.MvcNamespaceHandler

1、default-servlet-handler的解析过程如下:

DefaultServletHandlerBeanDefinitionParser.parse()
    ├defaultServletHandlerDef=new RootBeanDefinition(DefaultServletHttpRequestHandler.class);
    ├handlerMappingDef=new RootBeanDefinition(SimpleUrlHandlerMapping.class);
    └MvcNamespaceUtils.registerDefaultComponents(parserContext, source);
        ├registerBeanNameUrlHandlerMapping(parserContext, source);
        │    └beanNameMappingDef = new RootBeanDefinition(BeanNameUrlHandlerMapping.class);
        ├registerHttpRequestHandlerAdapter(parserContext, source);
        │    └handlerAdapterDef = new RootBeanDefinition(HttpRequestHandlerAdapter.class);
        └registerSimpleControllerHandlerAdapter(parserContext, source);
            └handlerAdapterDef = new RootBeanDefinition(SimpleControllerHandlerAdapter.class);

在使用该标签之后,注册了一些BeanDefinition到SpringFactory中

2、annotation-driven的解析过程如下:

AnnotationDrivenBeanDefinitionParser.parse()
    ├handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
    ├bindingDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);
    ├handlerAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
    ├csInterceptorDef = new RootBeanDefinition(ConversionServiceExposingInterceptor.class);
    ├exceptionHandlerExceptionResolver = new RootBeanDefinition(ExceptionHandlerExceptionResolver.class);
    ├responseStatusExceptionResolver = new RootBeanDefinition(ResponseStatusExceptionResolver.class);
    └defaultExceptionResolver = new RootBeanDefinition(DefaultHandlerExceptionResolver.class);

3、aspectj-autoproxy的解析过程:

纯技术上说aspectj-autoproxy与SpringMVC没关系,此处仅说明一个参数:
<aop:aspectj-autoproxy proxy-target-class="true"/>

a)  proxy-target-class如果为true,则代理使用cglib实现
b)  proxy-target-class如果为false,则代理使用java原生接口代理实现

1.5、回头来看DispatcherServlet的启动过程

DispatcherServlet.init()
    ├new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties)//读取配置信息
    └DispatcherServlet.initServletBean()初始化ServletBean
        └FrameworkServlet.initServletBean()
            ├FrameworkServlet.webApplicationContext = initWebApplicationContext()//启动Spring
            │    ├FrameworkServlet.wac = createWebApplicationContext(rootContext);
            │    │    ├wac =(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(XmlWebApplicationContext.class);
            │    │    └FrameworkServlet.configureAndRefreshWebApplicationContext(wac)
            │    │        └wac.refresh()//根据springXML文件启动Spring
            │    └FrameworkServlet.onRefresh(wac)
            │        └DispatcherServlet.onRefresh(wac)
            │            └DispatcherServlet.initStrategies(wac)
            │                ├DispatcherServlet.initMultipartResolver(context)//从wac中获取对象
            │                ├DispatcherServlet.initLocaleResolver(context)//从wac中获取对象
            │                ├DispatcherServlet.initThemeResolver(context)//从wac中获取对象
            │                ├DispatcherServlet.initHandlerMappings(context)//从wac中获取对象
            │                ├DispatcherServlet.initHandlerAdapters(context)//从wac中获取对象
            │                ├DispatcherServlet.initHandlerExceptionResolvers(context)//从wac中获取对象
            │                ├DispatcherServlet.initRequestToViewNameTranslator(context)//从wac中获取对象
            │                ├DispatcherServlet.initViewResolvers(context)//从wac中获取对象
            │                └DispatcherServlet.initFlashMapManager(context)//从wac中获取对象
            └FrameworkServlet.initFrameworkServlet()//从wac中获取对象

在DispatcherServlet.initStrategies(wac)中

├DispatcherServlet.initMultipartResolver(context)//从wac中获取对象
├DispatcherServlet.initLocaleResolver(context)//从wac中获取对象
├DispatcherServlet.initThemeResolver(context)//从wac中获取对象
├DispatcherServlet.initHandlerMappings(context)//从wac中获取对象
├DispatcherServlet.initHandlerAdapters(context)//从wac中获取对象
├DispatcherServlet.initHandlerExceptionResolvers(context)//从wac中获取对象
├DispatcherServlet.initRequestToViewNameTranslator(context)//从wac中获取对
├DispatcherServlet.initViewResolvers(context)//从wac中获取对象
└DispatcherServlet.initFlashMapManager(context)//从wac中获取对象

所获取的HandlerMappings、HandlerAdapters等信息,都是通过

<mvc:default-servlet-handler />

<mvc:annotation-driven ignoreDefaultModelOnRedirect="true"/>

定义出来,然后在DispatcherServlet的启动过程的最后一步初始化的。

1.6、SpringMVC的@RequestMapping自动扫描过程

SpringMVC的自动扫描是通过RequestMappingHandlerMapping.java的afterPropertiesSet()方法实现的。

在RequestMappingHandlerMapping实例化后,会调用initControllerAdviceCache()方法,在此方法内,会遍历全部的bean,判断其方法是否有Annotation注解

ControllerAdvice.class
RequestMapping.class
ModelAttribute.class

然后将对应的方法设置为bridgedMethod添加到modelAttributeAdviceCache中或者initBinderAdviceCache中