The default strategy of SpringMVC's DispatcherServlet

The default strategy of SpringMVC's DispatcherServlet

When using SpringMVC, we know that HandlerMapping is required to define the mapping between the request path and the processor, the HandlerAdapter is required to call the processor method and return a ModelAndView object, and the ViewResolver is required to resolve the view. These are the most basic interfaces in SpringMVC. Usually we need to define the HandlerMapping, HandlerAdapter and ViewResolver to be used in the SpringMVC configuration file, and the annotation-based SpringMVC configuration is similar. So generally our SpringMVC configuration file will be as follows:

<mvc:annotation-driven/>
    <!-- Quickly register view resolvers -->
<mvc:view-resolvers>
    <mvc:jsp prefix="/WEB-INF/views/" suffix=".jsp"/>
</mvc:view-resolvers>

The mvc namespace is a namespace provided by Spring that simplifies SpringMVC configuration. It <mvc:annotation-driven/>will automatically register the HandlerMapping interface in the Spring bean container to implement the RequestMappingHandlerMapping type of bean and the HandlerAdapter interface to implement the RequestMappingHandlerAdapter type of bean. <mvc:view-resolvers/>It is used to quickly define the ViewResolver implementation, which <mvc:jsp/>will automatically define a ViewResolver of type InternalResourceViewResolver.

For these relatively basic interfaces, even if we do not define their implementations, SpringMVC internally gives default definitions, which are called strategies. SpringMVC defines these default strategies in a DispatcherServlet.propertiesfile called DispatcherServlet in the same package. The following is the definition in the DispatcherServlet.properties file in the version 4.1.0 I am using.

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

 

From the above definition, we can see that when using SpringMVC, even if you do not define HandlerMapping, SpringMVC will define a BeanNameUrlHandlerMapping and DefaultAnnotationHandlerMapping for you by default. HandlerAdapter and ViewResolver are the same. There are also some other default strategies, please refer to DispatcherServlet.properties. If the default strategy does not meet your requirements, then we can define our own corresponding implementation in the bean container, and our own implementation will be applied at this time. Let's take the initialization of HandlerMapping as an example and take a look at the source code of 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");
        }
    }
}

 

From the source code, we can see that SpringMVC will first obtain the corresponding HandlerMapping definition from the bound ApplicationContext, and if it is not obtained, it will be called getDefaultStrategies(context, HandlerMapping.class)from the default strategy. Its code is shown below.

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>();
    }
}

 

The defaultStrategies is DispatcherServlet.propertiesthe content in the corresponding file.

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());
    }
}

 

(Note: This article is written based on SpringMVC4.1.0)

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326157117&siteId=291194637