Spring MVC DispatcherServlet 策略初始化 -- initViewResolvers

本文深入解析Spring MVC框架中DispatcherServlet的ViewResolver初始化过程。详细介绍了如何根据detectAllViewResolvers属性选择ViewResolver,以及在未定义ViewResolver时的默认处理策略。

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

本文所用源代码基于 Spring Web MVC 5.1.4.RELEASE

概述

DispatcherServlet#initViewResolvers方法用于准备DispatcherServlet处理请求时所使用的ViewResolver对象(可以有多个)。这些ViewResolver对象获取之后记录在DispatcherServlet的实例成员属性viewResolvers中。

DispatcherServlet另有一个实例成员属性detectAllViewResolvers,该属性缺省为true

DispatcherServlet准备ViewResolver的流程如下 :

  1. 从容器获取ViewResolver对象;

    • detectAllViewResolverstrue时,从容器(以及祖先容器)获取所有类型为ViewResolverbean组件,记录到viewResolvers并排序;
    • detectAllViewResolversfalse时,从容器(以及祖先容器)获取名称为viewResolverbean组件,记录到viewResolvers,这种情况下viewResolvers中最多有一个元素;
  2. 如果上面步骤结束时viewResolvers为空则创建缺省ViewResolver对象记录到viewResolvers;

经过上面的流程,我们可以看出:

  1. DispatcherServlet的属性viewResolvers中总是会有至少一个可供使用的ViewResolver对象。
  2. 如果想定制DispatcherServlet所使用的ViewResolver,开发人员可以注册相应的ViewResolver对象到容器;
  3. 如果想指定DispatcherServlet为某一个ViewResolver,开发人员可以注册自定义的ViewResolver到容器,但必须使用名称viewResolver,并且需要先设置DispatcherServlet的属性detectAllViewResolversfalse

到这里,你可能会有疑问,缺省情况下,开发人员并没有往容器中定义ViewResolver组件,但实际上DispatcherServlet能够从容器获得ViewResolver组件,这又是为什么呢?关于这一点,请参考 :

源代码解析

方法 #initViewResolvers

	/**
	 * Initialize the ViewResolvers used by this class.
	 * If no ViewResolver beans are defined in the BeanFactory for this
	 * namespace, we default to InternalResourceViewResolver.
	 */
	private void initViewResolvers(ApplicationContext context) {
		// 初始化记录 viewResolvers 对象的属性变量为null
		this.viewResolvers = null;

		// 根据属性 detectAllViewResolvers 决定是检测所有的 ViewResolver 对象,还是
		// 使用指定名称的 ViewResolver 对象
		if (this.detectAllViewResolvers) {
			// Find all ViewResolvers in the ApplicationContext, including ancestor contexts.
			// 从容器及其祖先容器查找所有类型为 ViewResolver 的 ViewResolver 对象,记录到 viewResolvers 
			// 并排序
			Map<String, ViewResolver> matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, 
															ViewResolver.class, true, false);
			if (!matchingBeans.isEmpty()) {
				this.viewResolvers = new ArrayList<>(matchingBeans.values());
				// We keep ViewResolvers in sorted order.
				// 排序,关于这里的排序,可以参考   WebMvcConfigurationSupport 类中对各种 ViewResolver bean
				// 进行定义时所使用的 order 属性,顺序属性很关键,因为它涉及到 ViewResolver 使用时的优先级	
				AnnotationAwareOrderComparator.sort(this.viewResolvers);
			}
		}
		else {
			// 获取名称为  viewResolver 的 ViewResolver bean 并记录到 viewResolvers
			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.
			}
		}
		
		// 如果上面步骤从容器获取 ViewResolver 失败,则使用缺省策略创建 ViewResolver 对象记录到
		// viewResolvers		
		// 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.isTraceEnabled()) {
				logger.trace("No ViewResolvers declared for servlet '" + getServletName() +
						"': using default strategies from DispatcherServlet.properties");
			}
		}
	}

方法 #getDefaultStrategies

该方法使用指定的策略接口 strategyInterface 创建一组策略对象。上面的方法initViewResolvers就是使用该方法创建了一组缺省的ViewResolver策略对象。

该方法会使用DispatcherServlet缺省配置文件DispatcherServlet.properties获取缺省要使用的策略实现类。

	/**
	 * Create a List of default strategy objects for the given strategy interface.
	 * The default implementation uses the "DispatcherServlet.properties" file (in the same
	 * package as the DispatcherServlet class) to determine the class names. It instantiates
	 * the strategy objects through the context's BeanFactory.
	 * @param context the current WebApplicationContext
	 * @param strategyInterface the strategy interface
	 * @return the List of corresponding strategy objects
	 */
	@SuppressWarnings("unchecked")
	protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
		 // 策略接口长名称作为 key
		String key = strategyInterface.getName();
		// 这里 defaultStrategies 是一个类静态属性,指向classpath resource 文件 DispatcherServlet.properties
		// 该行获取策略接口对应的实现类,是','分割的实现类的长名称
		String value = defaultStrategies.getProperty(key);
		if (value != null) {
          // 
			String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
			List<T> strategies = new ArrayList<>(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(
							"Unresolvable class definition for DispatcherServlet's default strategy class [" +
							className + "] for interface [" + key + "]", err);
				}
			}
			return strategies;
		}
		else {
			return new LinkedList<>();
		}
	}

方法 #createDefaultStrategy

该方法使用AutowireCapableBeanFactory#createBean创建策略实现类的一个实例对象。

	 /**
	 * Create a default strategy.
	 * The default implementation uses
	 * org.springframework.beans.factory.config.AutowireCapableBeanFactory#createBean.
	 * @param context the current WebApplicationContext
	 * @param clazz the strategy implementation class to instantiate
	 * @return the fully configured strategy instance
	 * @see org.springframework.context.ApplicationContext#getAutowireCapableBeanFactory()
	 * @see org.springframework.beans.factory.config.AutowireCapableBeanFactory#createBean
	 */
	protected Object createDefaultStrategy(ApplicationContext context, Class<?> clazz) {
		return context.getAutowireCapableBeanFactory().createBean(clazz);
	}

文件DispatcherServlet.properties

文件DispatcherServlet.properties是一个属性文件。每个属性的key是一个策略接口的长名称,而valuekey指定的策略接口的多个实现类的长名称,每个类名称之间使用,分割。

该文件位于包org.springframework.web.servlet,和DispatcherServlet在同一个pacakge路径下面。具体内容如下 :

# 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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值