SpringMVC(三)DispatcherServlet

本文详细介绍了Spring MVC核心组件DispatcherServlet的初始化过程,包括MultipartResolver、HandlerMappings、HandlerAdapters等关键组件的初始化逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在Spring中,ContextLoaderListener只是辅助功能,用于创建WebApplicationContext类型实例,而真正的逻辑实现其实是在DispatcherServlet中进行的,DispatcherServlet是实现servlet接口的实现类。

servlet是一个java编写的程序,此程序是基于HTTP协议的,在服务器端运行的(如Tomcat),是按照servlet规范编写的一个java类。主要是处理客户端的请求并将其结果发送到客户端。servlet的生命周期是由servlet的容器来控制的,它可以分为3个阶段:初始化,运行和销毁。

(1)初始化阶段。

  • servlet容器加载servlet类,把servlet类的.class文件中的数据读到内存中。
  • servlet容器创建一个ServletConfig对象。ServletConfig对象包含了servlet的初始化配置信息。
  • servlet容器创建一个servlet对象
  • servlet容器调用servlet对象的init方法进行初始化


(2)运行阶段

当servlet容器接收到一个请求时,servlet容器会针对这个请求创建serlvetRequest和servletResponse对象,然后调用service方法。并把这两个参数传递给service方法。service方法通过servletRequest对象获得请求的信息。并处理该请求。再通过servletResponse对象生成这个请求的相应结果。然后销毁servletResponse和servletRequest对象。

(3)销毁阶段

当web应用终止时,servlet容器会先调用servlet对象的destory方法,然后再销毁servlet对象,同时销毁servlet关联的ServletConfig对象。我们可以在destroy方法的实现中,释放servlet所占用的资源,如关闭数据库连接,关闭输入输出流等。

1.DespatcherServlet的初始化

servlet初始化阶段会调用其init方法,所以我们看下DispatcherServle的init方法。在其父类HttpServletBean中找到了该方法。

public final void init() throws ServletException {

		if (logger.isDebugEnabled()) {
			logger.debug("Initializing servlet '" + getServletName() + "'");
		}

		// Set bean properties from init parameters.
		try {
			//解析init-parem并封装在pvs中
			PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
			//将当前的这个servlet类转化为一个beanWrapper,从而能够以Spring的方式来对init-param的值注入
			BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
			ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
			//注册自定义属性编辑器,一旦遇到Resource类型的属性将会使用ResourceEditor进行解析
			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.
		//留给子类扩展
		initServletBean();

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

DispatcherServlet的初始化过程主要是通过将当前的servlet类型实例转换为BeanWrapper类型实例,以便使用Srping中提供的注入功能进行对应属性的注入。这些属性如contextAttribute,contxtClass,nameSpace,contextConfigLocation等,都可以在web.xml文件中以初始化参数的方式配置在servlet声明中。DispatcherServlet继承自FramworkServlet,FrameworkServlet类上包含对应的同名属性,属性注入主要包含如下步骤:

1.封装及验证初始化参数

		public ServletConfigPropertyValues(ServletConfig config, Set<String> requiredProperties)
			throws ServletException {

			Set<String> missingProps = (requiredProperties != null && !requiredProperties.isEmpty()) ?
					new HashSet<String>(requiredProperties) : null;

			Enumeration en = config.getInitParameterNames();
			while (en.hasMoreElements()) {
				String property = (String) en.nextElement();
				Object value = config.getInitParameter(property);
				addPropertyValue(new PropertyValue(property, value));
				if (missingProps != null) {
					missingProps.remove(property);
				}
			}

			// Fail if we are still missing properties.
			if (missingProps != null && missingProps.size() > 0) {
				throw new ServletException(
					"Initialization from ServletConfig for servlet '" + config.getServletName() +
					"' failed; the following required properties were missing: " +
					StringUtils.collectionToDelimitedString(missingProps, ", "));
			}
		}
	}

2.将当前servlet实例转化成BeanWrapper实例

PropertyAccessorFactory.forBeanPropertyAccess是Spring中提供的工具方法,主要用于将指定实例转化为Spring中可以处理的BeanWrapper类型的实例。

3.注册相对于Resource的属性编辑器

4.属性注入

5.servletBean的初始化

在ContextLoaderListener加载的时候已经创建了WebApplicationContext实例,而在这个函数最重要的就是对这个实例进行进一步的补充初始化。

继续查看initServletBean()。父类FrameworkServlet覆盖了HttpServletBean中的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 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");
		}
	}

上面的函数设计了计时器来统计初始化的执行时间,而且提供了一个扩展方法initFrameworkServelt()用于子类的覆盖操作,而作为关键的初始化逻辑实现委托给了initWebApplicationContext().

WebApplicationContext的初始化

initWebApplicationContext函数的主要工作就是创建或者刷新WebApplicationContext实例并对servlet功能所使用的变量进行初始化。

	protected WebApplicationContext initWebApplicationContext() {
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;

		if (this.webApplicationContext != null) {
			// A context instance was injected at construction time -> use it
			//context实例在构造函数中被注入
			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
			//根据contextAttribute属性加载WebApplicationContext
			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,后面有这样一行代码,我们看下:
configureAndRefreshWebApplicationContext

	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");
		}
		ConfigurableWebApplicationContext wac =
				(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

		wac.setEnvironment(getEnvironment());
		wac.setParent(parent);
		wac.setConfigLocation(getContextConfigLocation());

		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...
				ServletContext sc = getServletContext();
				if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
					// Servlet <= 2.4: resort to name specified in web.xml, if any.
					String servletContextName = sc.getServletContextName();
					if (servletContextName != null) {
						wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + servletContextName +
								"." + getServletName());
					}
					else {
						wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + getServletName());
					}
				}
				else {
					// Servlet 2.5's getContextPath available!
					wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
							ObjectUtils.getDisplayString(sc.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);
		//加载配置文件及整合parent到wac
		wac.refresh();
	}

无论调用方式如何变化,只要是使用ApplicationContext所提供的功能,最后都免不了使用公共父类AbstractApplicationContext提供的refresh()进行配置文件加载

3.刷新

onRefresh是FrameworkServletl类中提供的模板方法,在其子类DispatcherServlet中进行了重写,主要用于刷新Spring在web功能实现中所必须使用的全局变量。下面我们会介绍他们的初始化过程以及使用场景,而至于具体的使用细节会在稍后的章节中再做详细介绍


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

	protected void initStrategies(ApplicationContext context) {
		//初始化MultipartResolver
		initMultipartResolver(context);
		//初始化LocaleResolver
		initLocaleResolver(context);
		//初始化ThemeResolver
		initThemeResolver(context);
		//初始化HandlerMappings
		initHandlerMappings(context);
		//初始化HandlerAdapter
		initHandlerAdapters(context);
		//初始化HandlerExcpetionResolvers
		initHandlerExceptionResolvers(context);
		//初始化RequestToViewNameTranslator
		initRequestToViewNameTranslator(context);
		//初始化ViewResolvers
		initViewResolvers(context);
		//初始化FlashMapManager
		initFlashMapManager(context);
	}

(1)初始化MultipartResolver.

在Spring中,MultipartResolver主要用来处理文件上传。默认情况下,Spring是没有multipart处理的,因为一些开发者想要自己处理它们。如果想使用Spring的multipart,则需要在Web应用的上下文中添加multipart解析器。这样,每个请求都被检查是否包含multipart.然而,如果请求中包含multipart,那么上下文顶一顶额MultipartResolver就会解析他,这样请求中的multipart属性就会像其他属性一样被处理。常用配置如下:

	<bean id="multipartResolver" 
		class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
			<!-- 设置上传文件大小的参数-->
		<property name="resolveLazily" value="true"/>  
		<!--btye为单位,5M-->	
		<property name="maxUploadSize" value="5242880"/>
		
    </bean>

当然,CommonsMultipartResolver还提供了其他功能用于帮助用户上传文件,有兴趣的可以进一步看下。

那么MultipartResolver就是在initMutipartResolver中被加入到DispatcherServlet中的。

	private void initMultipartResolver(ApplicationContext context) {
		try {
			this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
			if (logger.isDebugEnabled()) {
				logger.debug("Using MultipartResolver [" + this.multipartResolver + "]");
			}
		}
		catch (NoSuchBeanDefinitionException ex) {
			// Default is no multipart resolver.
			this.multipartResolver = null;
			if (logger.isDebugEnabled()) {
				logger.debug("Unable to locate MultipartResolver with name '" + MULTIPART_RESOLVER_BEAN_NAME +
						"': no multipart request handling provided");
			}
		}
	}

因为之前的步骤已经完成了Spring中配置文件的解析,所以在这里只要配置文件注册过都可以通过ApplicationContext提供了getBean方法获取对应的bean,进而初始化MultipartResolver中的multipartResolver变量。

(2)初始化HandlerMappings

当客户端发出Request时DispatcherServlet会将Request提交给HandlerMaping,然后HandlerMapping根据WebApplicationContext的配置来回传给DispatcherServlet相应的Controller.

在基于SpringMVC的web应用程序中,我们可以为DispatcherServlet提供多个HandlerMapping供其使用DispatcherServlet在选用HandlerMapping的过程中,将根据我们所制定的一系列HandlerMapping的优先级进行排序,然后优先使用优先级在前面的handlerMapping.如果当前的HandlerMapping能够返回可用的Handler,DispatcherSevlet则使用当前返回的Handler进行Web请求的处理。而不再继续询问其他的HandlerMapping。否则,DispatcherServlet将继续按照各个handlerMapping的优先级进行询问,直到获得一个可用的Handler为止。初始化配置如下:

	private void initHandlerMappings(ApplicationContext context) {
		this.handlerMappings = null;

		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<HandlerMapping>(matchingBeans.values());
				// We keep HandlerMappings in sorted order.
				OrderComparator.sort(this.handlerMappings);
			}
		}
		else {
			try {
				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.
		if (this.handlerMappings == null) {
			this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
			if (logger.isDebugEnabled()) {
				logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
			}
		}
	}

默认情况下,SpringMVC将加载当前系统中所有实现了HandlerMapping接口的bean。如果只期望SpringMVC加载指定的handlermapping时,额可以修改web.xml中的DispatcherServlet的初始化参数,将detectAllHandlerMappings的值设置为false:

<init-param>
      <param-name>detectAllHandlerMappings</param-name>
      <param-value>false</param-value>
</init-param>

此时,SpringMVC将查找名为“handlerMapping”的bean,并作为当前系统中唯一的handlermapping.如果没有没有定义handlerMapping的话,则SpringMVC将按照org.Springframeword.web.servlet.DispatcherServlet所在目录下的DispatcherServlet.properties中所定义的org.Springframeword.web.servlet.HandlerMapping的内容来加载默认的handlerMapping(用户没有自定义Strategies的情况下)

(3)初始化HandlerAdapters

从名字也能联想到这个一个典型的适配器模式的使用,在计算机编程中,适配器 模式将一个类的接口适配成用户所期待的。使用适配器,可以使接口而无法一起工作的的类协同工作,做法是将类自己的接口包裹在一个已经存在的类中。那么在处理handler中为什么会使用适配模式呢?我们看下他的初始化逻辑。

	private void initHandlerAdapters(ApplicationContext context) {
		this.handlerAdapters = null;

		if (this.detectAllHandlerAdapters) {
			// Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
			Map<String, HandlerAdapter> matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
			if (!matchingBeans.isEmpty()) {
				this.handlerAdapters = new ArrayList<HandlerAdapter>(matchingBeans.values());
				// We keep HandlerAdapters in sorted order.
				OrderComparator.sort(this.handlerAdapters);
			}
		}
		else {
			try {
				HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
				this.handlerAdapters = Collections.singletonList(ha);
			}
			catch (NoSuchBeanDefinitionException ex) {
				// Ignore, we'll add a default HandlerAdapter later.
			}
		}

		// Ensure we have at least some HandlerAdapters, by registering
		// default HandlerAdapters if no other adapters are found.
		if (this.handlerAdapters == null) {
			this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
			if (logger.isDebugEnabled()) {
				logger.debug("No HandlerAdapters found in servlet '" + getServletName() + "': using default");
			}
		}
	}

同样在初始化的过程中涉及了一个变量detectAllHandlerAdapters,detectAllhandlerAdapters作用和detectAllHandlerMappings类似,只不过作用对象为handlerAdapter。也可以通过如下配置强制系统只加载beanname为“handlerAdapteer”handlerAdapter。

<init-param>
     <param-name>detectAllhandlerAdapters</param-name>
     <param-value>false</param-value>
</init-param>

如果无法找得到对应的bean,那么系统会 尝试加载默认的适配器。
	protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
		String key = strategyInterface.getName();
		String value = defaultStrategies.getProperty(key);
		if (value != null) {
			String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
			List<T> strategies = new ArrayList<T>(classNames.length);
			for (String className : classNames) {
				try {
					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(
							"Error loading DispatcherServlet's default strategy class [" + className +
									"] for interface [" + key + "]: problem with class file or dependent class", err);
				}
			}
			return strategies;
		}
		else {
			return new LinkedList<T>();
		}
	}

在getDefaultStrategies函数中,Spring会尝试从defaultStrategies中加载对应的HandlerAdapter属性,那么defaultStrategies是如何初始化的呢?

在当前类DispatcherServlet中存在这样一段初始化代码块:

	static {
		// Load default strategy implementations from properties file.
		// This is currently strictly internal and not meant to be customized
		// by application developers.
		try {
			ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
			defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
		}
		catch (IOException ex) {
			throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + ex.getMessage());
		}
	}

由此得知,如果程序开发人员没有在配置文件中定义自己的适配器,那么Spring会默认加载配置文件中的3个适配器。

作为总控制器的派遣器servlet通过处理器映射得到处理器后,会轮询处理器适配器模块,查找能够处理当前HTTP请求的处理器适配器的实现,处理器适配器模块根据处理器映射返回的处理器类型,例如简单的控制器类型,注解器类型或者远程调用处理器类型,来选择某一个适当的处理器适配器实现,从而适配当前的HTTP请求。

  • HTTP请求处理器适配器(HttpRequestHandlerAdapter)。

HTTP请求处理器适配器仅仅支持对HTTP请求处理器的适配。它简单地将HTTP请求对象和响应对象传递给HTTP请求吹起的实现,它并不需要返回值。它主要应用在基于HTTP的远程调用的实现上。

  • 简单控制器处理器适配器(SimpleControllerHandlerAdapter)。

这个实现类将HTTP请求适配到一个控制器的实现进行处理。这里控制器的实现是要给简单的控制器的实现。简单控制器处理器适配器被设计成一个框架类的实现,不需要被改写,客户化的业务逻辑通常是在控制器接口的实现类中实现的。

  • 注解方法处理器适配器(AnnotationMethodHandlerAdapter)。

这个类的实现是基于注解的实现,它需要结合注解方法映射和注解方法处理器协同工作。它通过解析声明在注解控制器的请求映射信息来解析相应的处理器方法来处理当前的HTTP请求。在处理的过程中,它通过反射来发现探测处理器方法的参数,调用处理器方法,并且映射返回值到模型和控制器对象,最后返回模型和控制器对象给作为主控制器的派遣器Servlet.,那

(4)初始化RequestToViewNameTranslator。

当Controller处理器方法没有返回一个view对象或逻辑视图名称,并且在该方法中没有直接往response的输出流里面写数据的时候,Spring就会采用约定好的方式提供一个逻辑视图 名称。这个逻辑视图名称是通过Spring定义的org.Springframework.web.servlet.RequestToViewNameTranslator接口的getViewName方法来实现的,我们可以实现自己的Request ToViewNameTranslator接口来约定好没有返回视图名称的时候如何确定视图名称。Spring已经给我们提供了一个它自己的实现那就是org.Springframework.web.servlet.view.DefaultRequest ToViewNameTranslator。

(5)初始化ViewResolvers.

在SpringMVC中,当Controller将请求处理结果放入到ModelAndView中以后,DispatcherServlet会根据ModelAndView选择合适的视图进行渲染。那么在SpringMVC中是如何选择合适的View呢?View对象是如何创建的呢?答案在ViewResolver中。ViewResolver接口定义了resolverViewName方法,根据viewName创建合适类型的View实现。

那么如何配置ViewResolver呢?在Spring中,ViewResolver作为SpringBean存在,可以在配置文件中进行配置,例如下面的代码,配置了JSP相关的veiwResolver.

	<bean
		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/view/" />
		<property name="suffix" value=".jsp" />
	</bean>

viewResolver初始化的工作在initViewResolers中完成。

	private void initViewResolvers(ApplicationContext context) {
		this.viewResolvers = null;

		if (this.detectAllViewResolvers) {
			// Find all ViewResolvers in the ApplicationContext, including ancestor contexts.
			Map<String, ViewResolver> matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
			if (!matchingBeans.isEmpty()) {
				this.viewResolvers = new ArrayList<ViewResolver>(matchingBeans.values());
				// We keep ViewResolvers in sorted order.
				OrderComparator.sort(this.viewResolvers);
			}
		}
		else {
			try {
				ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class);
				this.viewResolvers = Collections.singletonList(vr);
			}
			catch (NoSuchBeanDefinitionException ex) {
				// Ignore, we'll add a default ViewResolver later.
			}
		}

		// Ensure we have at least one ViewResolver, by registering
		// a default ViewResolver if no other resolvers are found.
		if (this.viewResolvers == null) {
			this.viewResolvers = getDefaultStrategies(context, ViewResolver.class);
			if (logger.isDebugEnabled()) {
				logger.debug("No ViewResolvers found in servlet '" + getServletName() + "': using default");
			}
		}
	}

(6)初始化FlashMapManager。

SpringMVC Flash attributs提供了一个请求存储属性,可供其他请求使用。在使用重定向的时候非常必要,例如Post/Redirect/Get模式。Flash attributes在重定向之前暂存(就像存在session中)以便重定向之后还能使用,并立即删除。

SpringMVC有两个主要的抽象来支持flasth attributes。FlashMap用于保持flash attributes,而FlashMapManager用于存储,检索,管理FlashMap实例。

flash attribute支持默认开始(“on”)并不需要显示启用,它永远不会导致HTTPSession的创建。这两个FlashMap实例都可以通过静态方法RequestContextUtils从SpringMVC的任何位置访问

flashMapManager的初始化在initFlashMapManager中完成。

	private void initFlashMapManager(ApplicationContext context) {
		try {
			this.flashMapManager =
					context.getBean(FLASH_MAP_MANAGER_BEAN_NAME, FlashMapManager.class);
			if (logger.isDebugEnabled()) {
				logger.debug("Using FlashMapManager [" + this.flashMapManager + "]");
			}
		}
		catch (NoSuchBeanDefinitionException ex) {
			// We need to use the default.
			this.flashMapManager = getDefaultStrategy(context, FlashMapManager.class);
			if (logger.isDebugEnabled()) {
				logger.debug("Unable to locate FlashMapManager with name '" +
						FLASH_MAP_MANAGER_BEAN_NAME + "': using default [" + this.flashMapManager + "]");
			}
		}
	}

至此,关于DispatcherServlet的功能作用我们就说完了,下面一节我们一起讨论一下DispatcherServlet的逻辑实现



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值