spring MVC分析


spring MVC分析

本章主要分析一下spring MVC的过程,主要从一下几个方面:

spring MVC概述

如果要在web环境中使用IOC容器,需要spring IOC设计一个启动过程把web容器导入。在这个过程中一方面处理Web容器的启动,另一方面通过设计特定的web容器拦截器将IOC容器加载到web环境中进行初始化。

  • web.xml文件描述
<servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <description>spring mvc 配置文件</description>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:/spring-mvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
</context-param>

通过在web.xml文件中

  1. 定义一个DispatcherServlet并且拦截所有请求
  2. 向ServletContext注册参数文件为applicationContext.xml
  3. 注册监听器ContextLoaderListener
    通过这样的设置ServletContext就为spring IOC 提供了一个宿主环境。IOC容器通过ContextLoaderListener的初始化来建立,IOC容器初始化完成后,利用DispatcherServlet作为Spring MVC处理请求的转发器,来转发HTTP请求。

初始化过程

  • ContextLoaderListener是初始化Spring IOC容器的关键,类图如下

8m7l28.png

  • ServletContextListener是Servlet规范中定义的接口

  • ContextLoader是spring用来进行初始化的类

  • ContextLoaderListener代码,调用ContextLoader进行初始化

@Override
public void contextInitialized(ServletContextEvent event) {
	initWebApplicationContext(event.getServletContext());
}
   
  • ContextLoader代码
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
		if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
			throw new IllegalStateException(
					"Cannot initialize context because there is already a root application context present - " +
					"check whether you have multiple ContextLoader* definitions in your web.xml!");
		}
        //省略代码...
		try {
			// Store context in local instance variable, to guarantee that
			// it is available on ServletContext shutdown.
			if (this.context == null) {
				this.context = createWebApplicationContext(servletContext);
			}
			if (this.context instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
				if (!cwac.isActive()) {
					//上下文尚未刷新->提供诸如设置父上下文,设置应用程序上下文ID等服务。
					if (cwac.getParent() == null) {
						// 上下文实例是在没有显式父级的情况下注入的-> 确定根Web应用程序上下文的父级(如果有)
						ApplicationContext parent = loadParentContext(servletContext);
						cwac.setParent(parent);

                    configureAndRefreshWebApplicationContext(cwac, servletContext);

					}
				}
			}
            //省略代码
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
			return this.context;
		}
	}

initWebApplicationContext主要是创建WebApplicationContext对象并进行初始化设置上下文环境,然后在将WebApplicationContext对象放到servletContext域中

  • createWebApplicationContext方法
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
	Class<?> contextClass = determineContextClass(sc);
	if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
		throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
				"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
	}
	return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}
  • determineContextClass(sc)方法获取到是param中设置的contextClass,默认是返回WebApplicationContext.class

默认定义的是XmlWebApplicationContext.class

org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
  • **BeanUtils.instantiateClass(contextClass)**返回XmlWebApplicationContext

初始化完成上下文对象后会去调用configureAndRefreshWebApplicationContext方法

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
    //省略代码...
    wac.setServletContext(sc);
    customizeContext(sc, wac);
    wac.refresh();
}

在这一步骤中,将ServletContext对象设置在了ApplicationContext中,并且进行了**refresh()**方法

DispatcherServlet分析

DispatcherServlet作为一个前端控制器,所有的Web请求都需要通过它来处理,进行转发、匹配、数据处理后返回

初始化

  1. 在Servlet的初始化过程中,Servlet的init方法会被调用,DispatcherServlet执行初始化
  2. DispatcherServlet持有的IoC容器的初始化过程,创建DispatcherServlet持有的上下文,该上下文是web应用上下文的子下文,因为getBean方法会先检索父上下文对象获取bean

Dispatcher过程

  • Dispatcher是利用HandlerMapping可以持有一系列从URL请求到Controller的映射,通过URL路径在handleMap中获取到handler对象

  • doDispatch方法是DispatcherServlet完成Disp*atcher的主要方法,包括

  1. 准备ModelAndView*
  2. 调用getHandler来响应HTTP请求
  3. 执行Handler的处理来得到返回的ModelAndView结果,
  4. 最后把这个ModelAndView对象交给相应的视图对象去呈现

总结

  1. 通过ContextLoaderListener实现servlert接口,加载webApplicationContex,reflesh IOC容器
  2. 初始化DispatcherServlet对象作为webApplicationContex的子容器
  3. 初始化HandlerMapping得到url对应的handler之间的映射关系
  4. 通过servlet拦截请求转发到DispatcherServlet上执行**doDispatch()*方法
    4.1. 准备ModelAndView

    4.2. 调用getHandler来响应HTTP请求
    4.3. 执行Handler的处理来得到返回的ModelAndView结果,
    4.4. 最后把这个ModelAndView对象交给相应的视图对象去呈现

参考资料

  • Spring技术内幕——深入解析Spring架构与设计原理(第2版)

  TOC