SpringMVC source code: Initialization of DispatcherServlet (3)

In the previous part, FrameworkServlet left onRefresh to DispatcherServlet to complete,

So go into DispatcherServlet # onRefresh

Refresh the Spring container (the sub-container is refreshed here)

@Override
protected void onRefresh(ApplicationContext context) {<!-- -->
    initStrategies(context); //The parameter context here is the SpringWeb container created before (and later passed as a parameter to the initialization function of each component)
}

Initialize nine major components: initStrategies()

The following 9 components are initialized. SpringMVC relies on them to complete the entire request processing process. Will be analyzed in detail later

protected void initStrategies(ApplicationContext context) {<!-- -->
    initMultipartResolver(context);
    initLocaleResolver(context);
    initThemeResolver(context);
    initHandlerMappings(context);
    initHandlerAdapters(context);
    initHandlerExceptionResolvers(context);
    initRequestToViewNameTranslator(context);
    initViewResolvers(context);
    initFlashMapManager(context);
}

Important components in DispatcherServlet:

private MultipartResolver multipartResolver; //File upload
private LocaleResolver localeResolver;
private ThemeResolver themeResolver;
private List<HandlerMapping> handlerMappings; //processor mapper
private List<HandlerAdapter> handlerAdapters; //processor adapter
private List<HandlerExceptionResolver> handlerExceptionResolvers; //Exception handler
private RequestToViewNameTranslator viewNameTranslator;
private FlashMapManager flashMapManager; //snapshot
private List<ViewResolver> viewResolvers; //View resolver

The above components are very important and are the core of the entire SpringMVC work. Here we first introduce their initialization (how the values are set), and then analyze what each component is responsible for.

Example: Initialization of the processor mapper HandlerMapping component: initHandlerMappings()

private void initHandlerMappings(ApplicationContext context) {<!-- -->
    
    this.handlerMappings = null;

    //The logic of this if-else is to search for HandlerMapping from the SpringWeb container, including those included in the parent container.
    // If we configure a HandlerMapping in xml, or configure <mvc:annotation-driven/>, it will be found in the container. No longer enter the "default policy" logic behind
    if (this.detectAllHandlerMappings) {<!-- -->
        Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
        if (!matchingBeans.isEmpty()) {<!-- -->
            this.handlerMappings = new ArrayList<>(matchingBeans.values());
            AnnotationAwareOrderComparator.sort(this.handlerMappings);
        }
    }else {<!-- -->
        try {<!-- -->
            HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class); //The logic of the getBean() method will be found from the parent container....
            this.handlerMappings = Collections.singletonList(hm);
        }//...
    }

    // If not found, use the default registered HandlerMapping (at least make sure there is a HandlerMapping available)
    if (this.handlerMappings == null) {<!-- -->
        this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
        //...
    }
}

Default component registration strategy (default configuration, strategy mode): (Equivalent to an adaptation method: initHandlerMappings(), initHandlerAdapters(),… will all be adapted here)

protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {<!-- -->
    String key = strategyInterface.getName(); // key is HandlerMapping.clss (passed may also be: HandlerAdapter.class, ViewResolver.class,...)
    String value = defaultStrategies.getProperty(key); // Get from external defaultStrategies
    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); // Create HandlerMapping object
                strategies.add((T) strategy); // Add to collection
            }//...
        }
        return strategies;
    }
    else {<!-- -->
        return new LinkedList<>();
    }
}

The defaultStrategies here are determined by the external properties file, and defaultStrategies is assigned in the static code block of the DispatcherServlet class:

private static final Properties defaultStrategies;

static {<!-- -->
       //Load the default initialization interface from the properties file (developers cannot customize the properties file)
       try {<!-- -->
           ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class); // Load from "DispatcherServlet.properties"
           defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
       }//...
   }

DispatcherServlet.properties file content (the file is located in the same directory as the source code DispatcherServlet.class)

# 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

As you can see, different component types are indeed defined here. A total of 8 components are defined. The upload processing component MutpartResolver has no default configuration. This is also easy to understand. Not every application needs the upload function, even if it needs to be uploaded. It is not necessary to use MultipartResolver, so MultipartResolver does not require default configuration. In addition, multiple HandlerMapping, HandlerAdapter and HandlerExceptionResolver are configured. In fact, there can be multiple View-Resolvers, but the default configuration is only one.

What needs to be noted here is:

The default configuration is not the optimal configuration, nor is it the recommended configuration of spring, but it can have a default value when there is no configuration, so that it will not be empty.

The default configuration is used when the corresponding component is not configured in springmvc.xml. For example, we configured the HandlerMapping component in springmvc.xml:

<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>

Then this BeanNameUrlHandlerMapping will be used as the HandlerMapping component, and the default configuration will no longer take effect. (There will no longer be a RequestMappingHandlerMapping in the default configuration)

In addition, when “< mvc:annotation-driven/>” is used, not all default configurations will be used. Because it configures HandlerMapping, HandlerAdapter and Handler-ExceptionResolver, and also does a lot of other work. For more details, you can view org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser (The bottom layer is to Spring Add BeanDefinition to the container)

Summary:

Through this stage, DispatcherServlet has initialized 9 internal components (if these components are not configured in the container, the initialization strategy will be determined from the external file “DispatcherServlet.properties” by default)

DispatcherServlet will frequently rely on these components to complete the entire request service during subsequent request processing.

Reference book “See Through SpringMVC Source Code Analysis and Practice”