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文件中
- 定义一个DispatcherServlet并且拦截所有请求
- 向ServletContext注册参数文件为applicationContext.xml
- 注册监听器ContextLoaderListener
通过这样的设置ServletContext就为spring IOC 提供了一个宿主环境。IOC容器通过ContextLoaderListener的初始化来建立,IOC容器初始化完成后,利用DispatcherServlet作为Spring MVC处理请求的转发器,来转发HTTP请求。
初始化过程
- ContextLoaderListener是初始化Spring IOC容器的关键,类图如下
-
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请求都需要通过它来处理,进行转发、匹配、数据处理后返回
初始化
- 在Servlet的初始化过程中,Servlet的init方法会被调用,DispatcherServlet执行初始化
- DispatcherServlet持有的IoC容器的初始化过程,创建DispatcherServlet持有的上下文,该上下文是web应用上下文的子下文,因为getBean方法会先检索父上下文对象获取bean
Dispatcher过程
-
Dispatcher是利用HandlerMapping可以持有一系列从URL请求到Controller的映射,通过URL路径在handleMap中获取到handler对象
-
doDispatch方法是DispatcherServlet完成Disp*atcher的主要方法,包括
- 准备ModelAndView*
- 调用getHandler来响应HTTP请求
- 执行Handler的处理来得到返回的ModelAndView结果,
- 最后把这个ModelAndView对象交给相应的视图对象去呈现
总结
- 通过ContextLoaderListener实现servlert接口,加载webApplicationContex,reflesh IOC容器
- 初始化DispatcherServlet对象作为webApplicationContex的子容器
- 初始化HandlerMapping得到url对应的handler之间的映射关系
- 通过servlet拦截请求转发到DispatcherServlet上执行**doDispatch()*方法
4.1. 准备ModelAndView
4.2. 调用getHandler来响应HTTP请求
4.3. 执行Handler的处理来得到返回的ModelAndView结果,
4.4. 最后把这个ModelAndView对象交给相应的视图对象去呈现
参考资料
- Spring技术内幕——深入解析Spring架构与设计原理(第2版)