Spring MVC DispatcherServlet 策略初始化 -- initHandlerExceptionResolvers

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

概述

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

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

DispatcherServlet准备HandlerExceptionResolver的流程如下 :

  1. 从容器获取HandlerExceptionResolver对象;

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

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

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

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

源代码解析

方法 #initHandlerExceptionResolvers

	/**
	 * Initialize the HandlerExceptionResolver used by this class.
	 * <p>If no bean is defined with the given name in the BeanFactory for this namespace,
	 * we default to no exception resolver.
	 */
	private void initHandlerExceptionResolvers(ApplicationContext context) {
		// 初始化记录 handlerExceptionResolvers 对象的属性变量为null
		this.handlerExceptionResolvers = null;

		// 根据属性 detectAllHandlerExceptionResolvers 决定是检测所有的 HandlerExceptionResolver 对象,还是
		// 使用指定名称的 HandlerExceptionResolver 对象
		if (this.detectAllHandlerExceptionResolvers) {
			// Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts.
			Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
					.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
			if (!matchingBeans.isEmpty()) {
				this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values());
				// We keep HandlerExceptionResolvers in sorted order.
				// 排序,关于这里的排序,可以参考   WebMvcConfigurationSupport 类中对各种 
				// HandlerExceptionResolver bean 进行定义时所使用的 order 属性,顺序属性很关键,
				// 因为它涉及到 HandlerExceptionResolver 使用时的优先级
				AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
			}
		}
		else {
			// 获取名称为  handlerExceptionResolver 的 HandlerExceptionResolver bean 
			// 并记录到 handlerExceptionResolvers
			try {
				HandlerExceptionResolver her =
						context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
				this.handlerExceptionResolvers = Collections.singletonList(her);
			}
			catch (NoSuchBeanDefinitionException ex) {
				// Ignore, no HandlerExceptionResolver is fine too.
			}
		}

		// 如果上面步骤从容器获取 HandlerExceptionResolver 失败,则使用缺省策略创建 
		// HandlerExceptionResolver 对象记录到 handlerExceptionResolvers	
		// Ensure we have at least some HandlerExceptionResolvers, by registering
		// default HandlerExceptionResolvers if no other resolvers are found.
		if (this.handlerExceptionResolvers == null) {
			this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
			if (logger.isTraceEnabled()) {
				logger.trace("No HandlerExceptionResolvers declared in servlet '" + getServletName() +
						"': using default strategies from DispatcherServlet.properties");
			}
		}
	}

方法 #getDefaultStrategies

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

该方法会使用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、付费专栏及课程。

余额充值