欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SpringMVC的DispatcherServlet的默认策略

程序员文章站 2022-03-02 10:35:12
...

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所写)