SpringMVC是如何启动的

欢迎访问我的个人博客:http://zhangshuai.name/

目录

  • DispatcherServlet的类图
  • 与JavaEE规范相关的接口
  • Aware相关的类
  • SpringMVC中自定义的Servlet接口
  • 总结

1.DispatcherServlet的类图

首先在配置SpingMVC的时候,我们要么在web.xml中配置DispatcherServlet,要么采用Java config的方式继承AbstractAnnotationConfigDispatcherServletInitializer,这两种方式都离不开一个关键的类DispatcherServlet,所以来看看DispatcherServlet初始化是怎么完成的,以及它如何处理请求的。学习一个类的方式第一步当然是看看它的类图了,所以先来看看:

从这个类图中可以看到DispatcherServlet的超类主要可以分为三类

  1. JavaEE规范中的Servlet,ServletConfig接口,GenericServlet与HttpServlet。
  2. 与容器相关的Aware类。
  3. 最为核心的三个类HttpServletBean,FrameworkServelt,DispacherServlet。
    在接下我们就按照这个类图关系来看看SpingMVC是如何被启动的。

2.与JavaEE规范相关的接口

首先是图中的起点,Servlet接口:

它的三个方法与是与Servlet生命周期相关的,至于具体作用就是方法名字一样,init方法会在servlet被实例化的时候调用,且整个servlet生命周期只调用一次(可通过配置load-on-startup来设置实例化的时间,概括为不设置或者设置为负数时则在第一次请求该Servlet时实例化,否者在web容器启动时进行初始化,需要注意的是当值为正数时,值越小优先级越高)。destory当然就是在容器关闭的时候调用了,也是一次就好。service方法就是提供业务逻辑的地方了,可以多次执行,参数就是接收请求,返回结果。而在init方法中可以看到参数ServletConfig,这个就是对Servlet的一些配置,比如在web.xml中一般都会给DispatcherServlet配置contextConfigLocation,这个contextConfigLocation的值就是保存到ServletConfig中的。至此,我们就可以清楚为什么配置DispatcherServlet时要这么配置了:

<servlet>
	<servlet-name>dispatcherServletservlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
	<init-param>
		<param-name>contextConfigLocationparam-name>
		<param-value>classpath:webConfig.xmlparam-value>
	init-param>
	<load-on-startup>1load-on-startup>
servlet>

那接下的自然就是ServletConfig了:

其中方法getServletName就是用于获取在web.xml中给Servlet配置的 ,方法getInitParamterNames就是获取之前配置的所有的的键,然后通过方法getInitparameter来获取到配置参数的值。最后的一个就是getServletContext了,其返回值ServletContext是java四大域对象中的一个,代表的是整个应用,可以通过这个对象在所有的Servlet中来共享数据。除此之外我们知道SpingMVC容器有个父容器(题外话:@Value不生效的一种常见原因就是没有将@PropertySource设置到合适的容器上),因此在web.xml中除了配置DispatcherServlet之外,我们往往还显示的配置它的父容器,也就是再配置一个ContextLoaderListener,而这个ContextLoaderListener我们一般也需要指定其配置文件的所在地址,就是这个父容器的contextConfigLocation,那么web.xml整体的配置就变成了:


<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">

  <context-param>
    <param-name>contextConfigLocationparam-name>
    <param-value>classpath:applicationContext.xmlparam-value>
  context-param>

  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
  listener>

  <servlet>
    <servlet-name>dispatcherServletservlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
    <init-param>
      <param-name>contextConfigLocationparam-name>
      <param-value>classpath:webConfig.xmlparam-value>
    init-param>
    <load-on-startup>1load-on-startup>
  servlet>

  <servlet-mapping>
    <servlet-name>dispatcherServletservlet-name>
    <url-pattern>*.dourl-pattern>
  servlet-mapping>
web-app>

至此,就能够知道平时为什么要这么去配置web.xml了。在这个文件中我们看到了两个contextConfigLocation,除了其中一个是给spring容器的,一个是给springMVC容器的,另一个区别就是spring容器的配置是写在中的,而这个值最终就是保存到ServletContext中的,而Servelet里面的是保存在ServletConfig里面的,他们的作用域就不一样了。
接下来再看看GenericServlet:

GenericServlet继承了Servlet和ServletConfig,所有它就同时拥有了上面介绍过的所有方法,就可以直接调用ServletConfig里面的方法,另外就是它拥有一个无参的init方法,还提供log方法。
再看哈HttpServlet:

HttpServlet是用Http协议实现的Servlet的基类,在图中可以看到它有许多的doXXX的方法,而这些XXX就是Http的请求方法,比如常见的get,post,put,与delete。它的Service方法为:

    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        HttpServletRequest request;
        HttpServletResponse response;
        try {
            request = (HttpServletRequest)req;
            response = (HttpServletResponse)res;
        } catch (ClassCastException var6) {
            throw new ServletException("non-HTTP request or response");
        }

        this.service(request, response);
    }

可以从方法上看出,它对参数做了一个强制类型转换,然后调用的service的一个重载函数

    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		//获取请求的方法类型
        String method = req.getMethod();
		//与浏览器缓存,304状态码有关,可看哈Last-Modified,If-modified-since的具体含义
        long lastModified;
		//根据不同的请求方法路由到不同的实现函数上,这里使用了模版模式来简化子类的实现
        if (method.equals("GET")) {
            lastModified = this.getLastModified(req);
            if (lastModified == -1L) {
                this.doGet(req, resp);
            } else {
                long ifModifiedSince;
                try {
                    ifModifiedSince = req.getDateHeader("If-Modified-Since");
                } catch (IllegalArgumentException var9) {
                    ifModifiedSince = -1L;
                }

                if (ifModifiedSince < lastModified / 1000L * 1000L) {
                    this.maybeSetLastModified(resp, lastModified);
                    this.doGet(req, resp);
                } else {
                    resp.setStatus(304);
                }
            }
        } else if (method.equals("HEAD")) {
            lastModified = this.getLastModified(req);
            this.maybeSetLastModified(resp, lastModified);
            this.doHead(req, resp);
        } else if (method.equals("POST")) {
            this.doPost(req, resp);
        } else if (method.equals("PUT")) {
            this.doPut(req, resp);
        } else if (method.equals("DELETE")) {
            this.doDelete(req, resp);
        } else if (method.equals("OPTIONS")) {
            this.doOptions(req, resp);
        } else if (method.equals("TRACE")) {
            this.doTrace(req, resp);
        } else {
            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[]{method};
            errMsg = MessageFormat.format(errMsg, errArgs);
			//HTTP 501 Not Implemented 服务器错误响应码表示请求的方法不被服务器支持
            resp.sendError(501, errMsg);
        }

    }

我们挑一个最常见的get方法来看看是什么样的:

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String protocol = req.getProtocol();
        String msg = lStrings.getString("http.method_get_not_supported");
        if (protocol.endsWith("1.1")) {
            resp.sendError(405, msg);
        } else {
            resp.sendError(400, msg);
        }

    }

首先它是protected修饰符修饰的,意味着期望子类来实现具体业务逻辑,如果不进行重写的话,则直接返回客户端错误。其余的请求方法与该函数是类似的,但对doTrace和doOptions有默认的一个实现。
至此,DispactcherServlet的超类中与Sevlet规范相关的接口和类就理解清楚了,在这个部分就能够理解平时配置web.xml为什么需要这么配置了。

3.Aware相关的类

首先Aware是一个标记接口,其本身是没有定义方法的,它的作用是仅用于标记某个类可以通过回调的方式被Spring容器或者某个特定的框架对象通知。XXXAware可以理解为某个类可以感知XXX,如果某个类需要使用Sping容器的的XXX,就可以实现XXXAware接口,这个接口有个setXXX方法,通过这个方法就可以获取到Spring容器提供的XXX,由此可以知道Dispatchervlet是可以获取到容器的Enviroment和ApplicationContext的,而关于这两个类的具体含义会在Sping源码分析里面再做总结。而XXXCapable则表示某个类拥有一个xxx,并且可以通过getXXX将其暴露出去。而在HttpServlet中暴露的Enviroment对象是StandardServletEnvironment,它不仅包括ServletConfig,ServletContext,JNDI的参数,还有这系统参数和系统属性,这些参数和值都封装在其PropertySources下。

4.SpringMVC中自定义的Servlet接口

现在终于进入到了正题,SpringMVC自定义了三个Servlet,分别是HttpServlet,FrameworkServlet,DispatcherServlet。我们已经知道对于一个Servlet来说,它的生命周期开始于init()方法,所以看哈HttpServletBean的init()方法。

	public final void init() throws ServletException {
		if (logger.isDebugEnabled()) {
			logger.debug("Initializing servlet '" + getServletName() + "'");
		}

		// Set bean properties from init parameters.
		PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
		if (!pvs.isEmpty()) {
			try {
				BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
				ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
				bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
				//这是一个模版方法,在HttpServletBean中设置为空,子类重写这个方法进行一些初始化
				initBeanWrapper(bw);
				bw.setPropertyValues(pvs, true);
			}
			catch (BeansException ex) {
				if (logger.isErrorEnabled()) {
					logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
				}
				throw ex;
			}
		}

		// Let subclasses do whatever initialization they like.
		//FrameworkServlet的入口方法
		initServletBean();

		if (logger.isDebugEnabled()) {
			logger.debug("Servlet '" + getServletName() + "' configured successfully");
		}
	}

pvs中保存的是设置的值,

resourceLoader保存了servletContext对象,

BeanWrapper是Spring提供的用于操作JavaBean属性的工具,使用它可以直接的修改一个对象的属性,这整个init方法就可以概括为对DispatcherServlet的一个初始化,然后再调用initServletBean()方法。
因此,转入到FrameworkServlet,可以看到其initServletBean()方法为:

	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 {
			this.webApplicationContext = initWebApplicationContext();
			initFrameworkServlet();
		}
		catch (ServletException | 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");
		}
	}

除开日志之外,就只有两个方法了,一个是用于初始化WebApplicationContext,另一个是用于初始化FrameworkServlet,而由

	protected void initFrameworkServlet() throws ServletException {
	}
可知initFrameworkServlet是一个模版方法,在FrameworkServlet中还并没有用到它,并且DispatcherServlet也没有用到它。因此FrameworkServlet的主要作用就是用于初始化WebApplicationContext。主要完成的工作是初始化WebApplicationContext,根据情况调用onRefresh方法,将WebApplicationContext注册到ServletContext中。
进入该函数可以看到:
```java
	protected WebApplicationContext initWebApplicationContext() {
	   //默认情况下Spring会将自己的容器设置成ServletContext的属性,因此可以从ServletContext中获取到根WebApplicationContext
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;

		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()) {
					// The context has not yet been refreshed -> provide services such as
					// setting the parent context, setting the application context id, etc
					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) {
			// No context instance was injected at construction time -> see if one
			// has been registered in the servlet context. If one exists, it is assumed
			// that the parent context (if any) has already been set and that the
			// user has performed any initialization such as setting the context id
			wac = findWebApplicationContext();
		}
		if (wac == null) {
			// No context instance is defined for this servlet -> create a local one
			wac = createWebApplicationContext(rootContext);
		}

		if (!this.refreshEventReceived) {
			// Either the context is not a ConfigurableApplicationContext with refresh
			// support or the context injected at construction time had already been
			// refreshed -> trigger initial onRefresh manually here.
			onRefresh(wac);
		}

		if (this.publishContext) {
			// Publish the context as a servlet context attribute.
			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;
	}

从上面可以看到初始化WebApplicationContext根据不同的情况一共有三种方式:
第一种是WebApplicationContext已经通过FrameworkServlet的构造函数直接注入到这个对象中,这时仅仅需要对其进行一些配置,配置的具体为先判断是否已经激活,如果没有的话,就设置其父容器,然后调用configureAndRefreshWebApplicationContext方法,这个方法稍后再进行介绍。
第二种是webApplicationContext已经在ServletContext中,这个和之前获取父WebApplicationContext类似,都是在ServletContext中去获取。
第三种,也是最常见的一种,是需要在Framework自己通过createWebApplicationContext方法去创建一个WebApplicationContext并对其完成初始化,

	protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
		//默认类型为XmlWebApplicationContext
		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");
		}
		ConfigurableWebApplicationContext wac =
				(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

		wac.setEnvironment(getEnvironment());
		wac.setParent(parent);
		String configLocation = getContextConfigLocation();
		if (configLocation != null) {
			wac.setConfigLocation(configLocation);
		}
		configureAndRefreshWebApplicationContext(wac);

		return wac;
	}

	protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
		if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
			// The application context id is still set to its original default value
			// -> assign a more useful id based on available information
			if (this.contextId != null) {
				wac.setId(this.contextId);
			}
			else {
				// Generate default id...
				wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
						ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
			}
		}

		wac.setServletContext(getServletContext());
		wac.setServletConfig(getServletConfig());
		wac.setNamespace(getNamespace());
		wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

		// The wac environment's #initPropertySources will be called in any case when the context
		// is refreshed; do it eagerly here to ensure servlet property sources are in place for
		// use in any post-processing or initialization that occurs below prior to #refresh
		ConfigurableEnvironment env = wac.getEnvironment();
		if (env instanceof ConfigurableWebEnvironment) {
			((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
		}

		postProcessWebApplicationContext(wac);
		applyInitializers(wac);
		wac.refresh();
	}

可以看出流程还是很简单的,就是通过反射创建一个类型为contextClass的对象,然后设置一些属性,再调用configureAndRefreshWebApplicationContext来设置一些属性,就返回了。中间需要注意的就是

wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

为web容器注册了一个监听器ContextRefreshListener,这个监听器是FramwworkServlet的内部类,监听的事件是ContextRefreshedEvent。其动作是:

	private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
		@Override
		public void onApplicationEvent(ContextRefreshedEvent event) {
			FrameworkServlet.this.onApplicationEvent(event);
		}
	}

	public void onApplicationEvent(ContextRefreshedEvent event) {
		this.refreshEventReceived = true;
		onRefresh(event.getApplicationContext());
	}

可以看出当ContextRefreshedEvent事件发生时,将标志位refreshEventReceived设置为true,并调用了onRefresh方法。再回到FrameworkServlet,可以看到在获取并设置了WebApplicationContext后,根据refreshEventReceived来决定了是否调用onrefresh方法,然后根据publishContext标志判断是否将这个对象放入到ServletContext中。根据前面的流程我们知道最终一定会执行onrefresh方法的,这也是个模版方法,留给子类来实现。所以终于要进入到核心DispatcherServletle了,其onRefresh方法为:

	protected void onRefresh(ApplicationContext context) {
		initStrategies(context);
	}

	/**
	 * Initialize the strategy objects that this servlet uses.
	 * 

May be overridden in subclasses in order to initialize further strategy objects. */ protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); }

可以看到DispatcherServlet的onrefresh方法只是简单的调用了initStrategies,这个方法就是用于初始化SpringMVC的九大组件:先来个简单介绍,

  1. HandlerMapping //用于将http请求转换为handlerExcutionChain
  2. HandlerAdapter //Handler的适配器,Handler只要是Object就满足条件,太过广泛,所有定义了适配器组件
  3. ViewResolver //视图解析器,将逻辑视图名找到视图
  4. HandlerExceptionResolver //用于处理请求处理过程中的异常,渲染时的异常不在器管理范围内
  5. LocaleResolver //用于支持国际化的
  6. ThemeResolver //主题组件,可以通过配置拦截器来切换主题
  7. RequestToViewNameTranslator //从request请求中得到视图名
  8. MultipartResolver //文件上传组件
  9. FlashMapManager //用于重定向时参数的传递
    我们选一个最开始用到的HandlerMapping看看其init方法是什么样的,其余也是类似的,
	private void initHandlerMappings(ApplicationContext context) {
		//handlerMappings是一个List,也就是说容器中可以有多个HandlerMapping
		this.handlerMappings = null;

		//this.detectAllHandlerMappings默认为true
		if (this.detectAllHandlerMappings) {
			// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
			Map<String, HandlerMapping> matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
			if (!matchingBeans.isEmpty()) {
				this.handlerMappings = new ArrayList<>(matchingBeans.values());
				// We keep HandlerMappings in sorted order.
				//平时设置的@Order在这里生效
				AnnotationAwareOrderComparator.sort(this.handlerMappings);
			}
		}
		else {
			try {
				//从容器中获得id为"handlerMapping"的bean,且它的类型必须为HandlerMapping;
				HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
				this.handlerMappings = Collections.singletonList(hm);
			}
			catch (NoSuchBeanDefinitionException ex) {
				// Ignore, we'll add a default HandlerMapping later.
			}
		}

		// Ensure we have at least one HandlerMapping, by registering
		// a default HandlerMapping if no other mappings are found.
		//如果没有这个组件那么就设置一个默认的Bean当做这个组件的默认值
		if (this.handlerMappings == null) {
			this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
			if (logger.isDebugEnabled()) {
				logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
			}
		}
	}

可以看到initHandlerMappings的主要内容就是找到一个或者多个HandlerMapping的实现类,然后将其设置到属性this.handlerMappings,它会先从容器中去找,看是否有设置的Bean,如果没有的话就设置一个默认值,至于这个默认值的来源可以看到:

	protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
		//key就是HandlerMapping的类名称
		String key = strategyInterface.getName();
		//defaultStrategies是一个Properties类型,这个Properties的资源来源于DispatcherServlet.properties文件
		String value = defaultStrategies.getProperty(key);
		if (value != null) {
			//将key用逗号分割成数组
			String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
			List<T> strategies = new ArrayList<>(classNames.length);
			for (String className : classNames) {
				try {
					//通过反射 根据类名创建Class对象
					Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
					Object strategy = createDefaultStrategy(context, clazz);
					strategies.add((T) strategy);
				}
				catch (ClassNotFoundException ex) {
					throw new BeanInitializationException(
							"Could not find DispatcherServlet's default strategy class [" + className +
							"] for interface [" + key + "]", ex);
				}
				catch (LinkageError err) {
					throw new BeanInitializationException(
							"Unresolvable class definition for DispatcherServlet's default strategy class [" +
							className + "] for interface [" + key + "]", err);
				}
			}
			return strategies;
		}
		else {
			return new LinkedList<>();
		}
	}

这个方法主要就是从一个Properties对象里面根据组件名找到默认组件实现的类全名称,然后通过反射的方式创建该组件,再返回给DispatcherServlet。而这个Properties内容的来源是DispatcherServlet.properties。所以我们看看这个文件的内容,

# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.

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.method.annotation.RequestMappingHandlerMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
	org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
	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

这里就是很多个键值对,键是组件的接口名,值是组件的默认实现类,这里面没有MultipartResolver,因为不一定需要上传文件,上传文件的话也可以通过别的方式实现。需要注意的是这个只是默认配置,并不是最优的配置,也不是推荐的配置,其中有的类都已经被标记过时了。
至此,DispatcherServlet在容器初试它是做的初试化工作就已经全部完成了。来做个总结吧。

总结

在前文中,我们根据DispatcherServlet的类图,和Servlet的生命周期的定义,看了其启动流程。知道了平时配置SpringMVC时为什么需要这么配置。也知道了

  1. HttpServletBean主要是对Servlet参数的一个设置,比如设置ServletConfig,ServletContext,Enviroment这些内容 入口是init()会调用模版方法initServletBean()
  2. FrameWorkServlet主要的就是初始化和配置WebApplicationContext 入口是initServletBean()会调用initWebApplicationContext(),这个方法会调用模版方法onRefresh()
  3. DispatcherServlet主要就是初始化其九大组件 入口是onRefresh()

你可能感兴趣的:(SpringMVC)