springmvc的核心是DispatcherServlet,它是前端控制器,负责拦截客户端发过来的请求,然后解析请求进行分发。
DispatcherServlet是基于Servlet的,所以使用springmvc先在web.xml中配置DispatcherServlet
<!-- 配置DisaptcherServlet -->
<servlet>
<servlet-name>springMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 初始化参数,配置springmvc配置文件 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springMVC.xml</param-value>
</init-param>
<!-- web容器启动时加载该Servlet -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
配置文件解析:可以看到在web.xml配置了一个名叫DispatcherServlet的Servlet,所有的请求都会在它那里执行,并且它有一个名叫contextConfigLocation的参数,在这里赋值为classpath:springMVC.xml(即mvc的配置文件位置所在)
接下来看DispatcherServlet主要的继承关系
图中每个类中的方法是待会分析初始化过程会调用的主要方法,可以看到DispatcherServlet本质还是一个Servlet,上面说到在web容器启动时会会加载DispatcherServlet,每个Servlet在第一次加载时都会调用其init()方法,但是DispatcherServlet本身没有这个方法,所以系统会去它父类寻找init()方法,最后在HttpServletBean找到,调用,以下是init()方法源码
1.HttpServletBean的init()方法
//DispatcherServlet第一次加载时调用init方法
@Override
public final void init() throws ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Initializing servlet '" + getServletName() + "'");
}
// Set bean properties from init parameters.
/**
*概括来说:
*try语句块的作用就是获取刚刚在web.xml配置的初始化参数<init-param>
*并将这些参数设置到DispatcherServlet中
*/
try {
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
throw ex;
}
// Let subclasses do whatever initialization they like.
//模版方法,此方法在HttpServletBean本身是空的,但是因为调用方法的对象是DispatcherServlet
//所以优先在DispatcherServlet找,找不到再去父类找,最后在FrameworkServlet找到
initServletBean();
if (logger.isDebugEnabled()) {
logger.debug("Servlet '" + getServletName() + "' configured successfully");
}
}
总结HttpServletBean的作用:
- 获取web.xml的中配置DispatcherServlet的初始化参数,存放到一个参数容器ServletConfigPropertyValues中
- 根据传进来的this创建对象包裹者(BeanWrapper),本质上它就是DispatcherServlet
- 最后通过bw.setPropertyValues(pvs,true);把参数设置到bw(即DispatcherServlet)里面去,最后调用子类的initServletBean()
2.FrameworkServlet的initServletBean()方法源码
@Override
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
if (this.logger.isInfoEnabled()) {
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
}
long startTime = System.currentTimeMillis();
try {
//重要代码,创建springmvc的ioc容器实例
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}
catch (ServletException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
catch (RuntimeException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
if (this.logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
elapsedTime + " ms");
}
}
看到代码中通过initWebApplicationContext()就返springMVC的容器实例了,接下来看initWebApplicationContext()源码
protected WebApplicationContext initWebApplicationContext() {
//首先通过ServletContext获得spring容器,因为子容器springMVC要和父容器spring容器进行关联
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
//定义springMVC容器wac
WebApplicationContext wac = null;
//判断容器是否由编程式传入(即是否已经存在了容器实例),存在的话直接赋值给wac,给springMVC容器设置父容器
//最后调用刷新函数configureAndRefreshWebApplicationContext(wac),作用是把springMVC.xml的配置信息加载到容器中去
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent -> set
// the root application context (if any; may be null) as the parent
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
// 在ServletContext中寻找是否有springMVC容器,初次运行是没有的,springMVC初始化完毕ServletContext就有了springMVC容器
//具体原因看下面46行代码
wac = findWebApplicationContext();
}
//当wac既没有没被编程式注册到容器中的,也没在ServletContext找得到,此时就要新建一个springMVC容器
if (wac == null) {
// 创建springMVC容器
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
//到这里mvc的容器已经创建完毕,接着才是真正调用DispatcherServlet的初始化方法onRefresh(wac)
onRefresh(wac);
}
if (this.publishContext) {
//将springMVC容器存放到ServletContext中去,方便下次取出来
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
"' as ServletContext attribute with name [" + attrName + "]");
}
}
return wac;
}
接下来看创建springMVC 的ioc容器方法createWebApplicationContext(WebApplicationContext parent)
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
Class<?> contextClass = getContextClass();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Servlet with name '" + getServletName() +
"' will try to create custom WebApplicationContext context of class '" +
contextClass.getName() + "'" + ", using parent context [" + parent + "]");
}
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException(
"Fatal initialization error in servlet with name '" + getServletName() +
"': custom WebApplicationContext class [" + contextClass.getName() +
"] is not of type ConfigurableWebApplicationContext");
}
//实例化空白的ioc容器
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
//给容器设置环境
wac.setEnvironment(getEnvironment());
//给容器设置父容器(就是spring容器),两个ioc容器关联在一起了
wac.setParent(parent);
//给容器加载springMVC.xml的配置信息,bean注入,注解,扫描等等
wac.setConfigLocation(getContextConfigLocation());
//上面提到过这方法,刷新容器,根据springMVC.xml配置文件完成初始化操作,此时springMVC容器创建完成
configureAndRefreshWebApplicationContext(wac);
return wac;
}
以上就是FrameworkServlet创建容器的过程
总结FrameworkServlet作用:
- 创建springMVC的ioc容器根据配置文件实例化里面各种bean,并将之与spring的ioc容器进行关联
- 把创建出来的mvc容器存放到ServletContext中
- 接下来看DispatcherServlet的onRefresh(ApplicationContext context)方法
3.DispatcherServlet的onRefresh(ApplicationContext context)方法
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
很简单,调用了initStrategies(context),初始化策略组件
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);//文件上传解析
initLocaleResolver(context);//本地解析
initThemeResolver(context);//主题解析
initHandlerMappings(context);//url请求映射
initHandlerAdapters(context);//初始化真正调用controloler方法的类
initHandlerExceptionResolvers(context);//异常解析
initRequestToViewNameTranslator(context);
initViewResolvers(context);//视图解析
initFlashMapManager(context);
}
至此,整个springMVC初始化的过程就结束了
总结
- web应用启动时扫描web.xml文件,扫描到DispatcherServlet,对其进行初始化
- 调用DispatcherServlet父类的父类HttpServletBean的init()方法,把配置DispatcherServlet的初始化参数设置到DispatcherServlet中,调用子类FrameworkServlet的initServletBean()方法
- initServletBean()创建springMVC容器实例并初始化容器,并且和spring父容器进行关联,使得mvc容器能访问spring容器里面的bean,之后调用子类DispatcherServlet的onRefresh(ApplicationContex context)方法
- onRefresh(ApplicationContext context)进行DispatcherServlet的策略组件初始化工作,url映射初始化,文件解析初始化,运行适配器初始化等等。