1、根上下文初始化
DispatcherServlet分发请求,可以配置多个,定义了URL映射;ContextLoaderListener是Spring MVC的启动类,被定义为一个与Web服务生命周期相关的监听器,负责完成IOC容器在Web环境中的启动。DispatcherServlet和ContextLoaderListener提供了在Web容器中对Spring的接口,这些接口与容器的耦合通过ServletContext实现,ServletContext为Spring的IOC容器提供了宿主环境,在宿主环境中Spring MVC建立起一个IOC容器体系,通过ContextLoaderListener初始化。
IOC容器的启动过程就是建立(根)上下文的过程,根上下文和ContextLoaderListener相伴而生。还有一个上下文用来保存DispatcherServlet需要的MVC对象(DispatcherServlet的上下文,子上下文),是根上下文的子上下文。。在ContextLoader中,完成了两个IOC容器的建立,一个是建立起根上下文的双亲IOC容器,一个是建立WebApplicationContext(根上下文)。如下是ContextLoaderListener建立根上下文的过程。
在启动过程中,Spring默认使用XmlWebApplicationContext,loadBeanDefinitions时,调用reader.loadBeanDefinitions(configLocation),定义了getDefaultCofigLocations方法,默认从/WEB-INF/applicationContext.xml读取。然后通过XmlBeanDefinitionReader载入Bean信息,完成根上下文初始化。
下面来看看上面的时序图,ContextLoaderListener通过ContextLoader完成实际的初始化。如下,载入根上下文的双亲上下文(loadParentContext),初始化根上下文(createWebApplicationContext),并使用ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE作为key将根上下文存储在ServletContext中。
如下是初始化根上下文(createWebApplicationContext)的过程,determineContextClass决定容器类型,通过设置CONTEXT_CLASS_PARAM参数来完成。默认使用XmlWebApplicationContext,defaultStrategies.getProperty,策略模式。
初始化根上下文之后,被存储到ServletContext中,DipatcherServlet初始化自己的上下文时,取出根上下文,并设置为DipatcherServlet自带上下文的双亲上下文。
2、DispatcherServlet初始化
DipatcherServlet选取BeanNameUrlHandlerMapping作为默认的HandlerMapping(映射处理器),除了不同的HandlerMapping,还定制了不同的Handler(Controller),实现handleRequest接口方法,返回ModelAndView。Spring MVC还提供了各种视图(JSP、freemarker、EXCEL、PDF),拦截器,国际化等。
DipatcherServlet会建立自己的上下文来持有Spring MVC的bean对象(无则去父容器查找),在建立自己的容器时,从ServletContext中得到根上下文作为双亲上下文,再对自己的上下文初始化,存入ServletContext。
从上图看到,DipatcherServlet的作用主要有初始化和请求分发两部分,在initStrategies()里,DipatcherServlet对handlerMapping、ViewResolver等也进行了初始化;作为一个Servlet,Web容器调用doGet/doPost之后,调用DipatcherServlet的doService,最终调用doDispatch。
作为Servlet,在Servlet的初始化时调用init()。DipatcherServlet初始化时,首先调用HttpServletBean的init(),继而调用自己的initServletBean()。在初始化过程中,这个DipatcherServlet的上下文被设置为根上下文的子上下文,根上下文是和Web应用对应的上下文,DipatcherServlet持有的上下文是和Servlet对应的,在一个Web应用中,可以有多个Servlet。根上下文中的Bean被子上下文共享,DipatcherServlet持有的上下文(容器)建立起来之后,通过refresh方法初始化。
取得根上下文的过程是从ServletContext中取得ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE的值,然后建立DipatcherServlet的上下文,传入parent,利用反射实例化,设置双亲上下文、配置文件位置等,最终refresh。
initWebApplicationContext方法中还调用了onRefresh,这个方法触发了initStrategies,对MVC框架的各种元素初始化,LocalResolver、HandlerMapping、ViewResolver等。以HandlerMapping初始化为例,从IOC容器中读入配置(getBean),建立在IOC容器初始化的基础上,其他的初始化与之类似。
3、请求分发
初始化完成后,所有HandlerMappings都被加在了,被放在一个List中并被排序。以SimpleUrlHandlerMapping为例,定义了map来持有映射关系,HandlerMapping接口定义了HandlerExecutionChain getHandler (HttpServletRequest request)方法,HandlerExecutionChain 持有Handler和List<HandlerInterceptor>。
getHandler需要提前配置好一个反应映射关系的handlerMap,是一个LinkedHashMap,通过BeanPostProcessor完成。最终getHandler根据request从handlerMap中取,如果取不到,便利找到最匹配的Handler。
下面看看如何分发处理请求,doService最终调用doDispatch分发请求。handler的请求处理过程是典型的command模式。可能有多个handlerMapping,getHandler时需要遍历。调用handler获得ModelAndView之后,交给视图类,就可以进行视图名的转换、视图的渲染和展现。
doDispatch方法里,获得ModelAndView之后,还调用了render方法渲染视图。render先对视图名称进行解析,得到视图对象,再展示数据。取得视图调用getBean。