SpringMVC的DispatcherServlet的默认策略
在使用SpringMVC的时候,我们知道需要HandlerMapping定义请求路径与处理器之间的映射,需要HandlerAdapter来调用处理器方法并返回一个ModelAndView对象,需要ViewResolver来解析视图。这些是SpringMVC中最基本的接口。通常我们都需要在SpringMVC的配置文件中定义好需要使用的HandlerMapping、HandlerAdapter和ViewResolver,基于注解的SpringMVC配置也是类似的。所以一般我们的SpringMVC配置文件会是如下这样:
<mvc:annotation-driven/>
<!-- 快速注册视图解析器 -->
<mvc:view-resolvers>
<mvc:jsp prefix="/WEB-INF/views/" suffix=".jsp"/>
</mvc:view-resolvers>
mvc命名空间是Spring提供的一个简化SpringMVC配置的命名空间,<mvc:annotation-driven/>
会自动在Spring的bean容器中注册HandlerMapping接口实现RequestMappingHandlerMapping类型的bean和HandlerAdapter接口实现类RequestMappingHandlerAdapter类型的bean。<mvc:view-resolvers/>
是用来快速定义ViewResolver实现的,其中<mvc:jsp/>
会自动定义一个InternalResourceViewResolver类型的ViewResolver。
针对这些比较基本接口,即使我们不定义它们的实现,SpringMVC内部也给了默认的定义,它管这些叫策略。SpringMVC把这些默认策略都定义在一个叫DispatcherServlet.properties
的文件中,它与DispatcherServlet在同一个包中。以下是笔者在使用的4.1.0版本中的DispatcherServlet.properties文件中的定义。
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.annotation.DefaultAnnotationHandlerMapping org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\ org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\ org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\ 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
从上面的定义中我们可以看出来,在使用SpringMVC时,即使你不定义HandlerMapping,SpringMVC默认也会给你定义一个BeanNameUrlHandlerMapping和DefaultAnnotationHandlerMapping。HandlerAdapter和ViewResolver也是一样的。还有一些其它的默认策略也请参考DispatcherServlet.properties中。如果默认策略不能满足你的要求,那么我们可以在bean容器中定义我们自己对应的实现,这个时候就会应用我们自己的实现了。我们拿初始化HandlerMapping为例,来看一下DispatcherServlet的源码。
/** * Initialize the HandlerMappings used by this class. * <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace, * we default to BeanNameUrlHandlerMapping. */ 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先会从绑定的ApplicationContext中获取对应的HandlerMapping定义,如果没有取到就会调用getDefaultStrategies(context, HandlerMapping.class)
从默认策略中获取。它的代码如下所示。
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>(); } }
其中的defaultStrategies就是对应的DispatcherServlet.properties
文件中的内容。
private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties"; private static final Properties defaultStrategies; 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()); } }
(注:本文是基于SpringMVC4.1.0所写)