1、SpringMVC功能的自动配置类 WebMvcAutoConfiguration 类。

  • SpringBoot启动默认加载 xxxAutoConfiguration 类(自动配置类)。
  • SpringMVC功能的自动配置类 WebMvcAutoConfiguration,生效。

image.png
image.png
1、@Configuration``(proxyBeanMethods = ``false``) 这是一个多实例的配置类。
2、@ConditionalOnWebApplication``(type = Type.``_SERVLET_``) Servlet web应用。
3、@ConditionalOnClass``({ Servlet.``class``, DispatcherServlet.``class``, WebMvcConfigurer.``class ``})三个组件类。
4、@ConditionalOnMissingBean``(WebMvcConfigurationSupport.``class``) 容器中没有 WebMvcConfigurationSupport.``class 这个组件时,WebMvcAutoConfiguration 类中的所有配置才生效,反之所有配置不生效。

  1. @Configuration(proxyBeanMethods = false)
  2. @ConditionalOnWebApplication(type = Type.SERVLET)
  3. @ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
  4. @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
  5. @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
  6. @AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
  7. ValidationAutoConfiguration.class })
  8. public class WebMvcAutoConfiguration {

2、SpringMVC功能的自动配置类 **WebMvcAutoConfiguration** 类,给容器配置了什么?
**WebMvcAutoConfiguration** 类中的内部类 WebMvcAutoConfigurationAdapte``r
1、@Configuration``(proxyBeanMethods = ``false``) 这是一个配置类多实例配置类。
2、@Import``(EnableWebMvcConfiguration.``class``)导入 EnableWebMvcConfiguration 组件
3、@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class }) 配置类与 WebMvcProperties``.``classResourceProperties``.``class 绑定。
1)、WebMvcProperties``.``class 由源码得出,与 spring.mvc 绑定。

  1. @ConfigurationProperties(prefix = "spring.mvc")
  2. public class WebMvcProperties {

2)、ResourceProperties.``class 由源码得出,与 spring.resources 绑定。

  1. @ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
  2. public class ResourceProperties {

4、禁用所有静态资源规则(true开启禁用,false关闭禁用)
image.png

  1. spring:
  2. # mvc:
  3. # static-path-pattern: /res/**
  4. #
  5. web:
  6. resources:
  7. static-locations: [classpath:/haha/]
  8. #设置开启或禁用静态资源规则
  9. add-mappings: true

5、自定义静态资源缓存时长(单位秒)。

spring:
#  mvc:
#    static-path-pattern: /res/**
#
  web:
    resources:
      static-locations: [classpath:/haha/]
      add-mappings: true
    #自定义缓存时长
    cache:
      period: 1

**WebMvcAutoConfiguration** 类中的内部类 WebMvcAutoConfigurationAdapte``r

    @Configuration(proxyBeanMethods = false)
    @Import(EnableWebMvcConfiguration.class)
    @EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
    @Order(0)
    public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {

        private static final Log logger = LogFactory.getLog(WebMvcConfigurer.class);

        private final ResourceProperties resourceProperties;

        private final WebMvcProperties mvcProperties;

        private final ListableBeanFactory beanFactory;

        private final ObjectProvider<HttpMessageConverters> messageConvertersProvider;

        private final ObjectProvider<DispatcherServletPath> dispatcherServletPath;

        private final ObjectProvider<ServletRegistrationBean<?>> servletRegistrations;

        final ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer;

        //只有一个有参构造器时,所有参数的值都会从容器中确定。
        //ResourceProperties resourceProperties:获取@EnableConfigurationProperties注解与 spring.resources 绑定的所有的值的对象。
        //WebMvcProperties mvcProperties:获取@EnableConfigurationProperties注解 与 spring.mvc 绑定的所有的值的对象。
        //ListableBeanFactory beanFactory:Spring的bean工厂beanFactory。
        //ObjectProvider<HttpMessageConverters> messageConvertersProvider 找到所有messageConvertersProvider。
        //ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider 找到资源处理器的自定义器。
        //ObjectProvider<DispatcherServletPath> dispatcherServletPath 资源处理的路径。
        //ObjectProvider<ServletRegistrationBean<?>> servletRegistrations 给应用注册Servlet、Filter等等。。。
        public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties, WebMvcProperties mvcProperties,
                ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider,
                ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider,
                ObjectProvider<DispatcherServletPath> dispatcherServletPath,
                ObjectProvider<ServletRegistrationBean<?>> servletRegistrations) {
            this.resourceProperties = resourceProperties;
            this.mvcProperties = mvcProperties;
            this.beanFactory = beanFactory;
            this.messageConvertersProvider = messageConvertersProvider;
            this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
            this.dispatcherServletPath = dispatcherServletPath;
            this.servletRegistrations = servletRegistrations;
        }

        @Override
        public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
            this.messageConvertersProvider
                    .ifAvailable((customConverters) -> converters.addAll(customConverters.getConverters()));
        }

        @Override
        public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
            if (this.beanFactory.containsBean(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME)) {
                Object taskExecutor = this.beanFactory
                        .getBean(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME);
                if (taskExecutor instanceof AsyncTaskExecutor) {
                    configurer.setTaskExecutor(((AsyncTaskExecutor) taskExecutor));
                }
            }
            Duration timeout = this.mvcProperties.getAsync().getRequestTimeout();
            if (timeout != null) {
                configurer.setDefaultTimeout(timeout.toMillis());
            }
        }

        @Override
        @SuppressWarnings("deprecation")
        public void configurePathMatch(PathMatchConfigurer configurer) {
            configurer.setUseSuffixPatternMatch(this.mvcProperties.getPathmatch().isUseSuffixPattern());
            configurer.setUseRegisteredSuffixPatternMatch(
                    this.mvcProperties.getPathmatch().isUseRegisteredSuffixPattern());
            this.dispatcherServletPath.ifAvailable((dispatcherPath) -> {
                String servletUrlMapping = dispatcherPath.getServletUrlMapping();
                if (servletUrlMapping.equals("/") && singleDispatcherServlet()) {
                    UrlPathHelper urlPathHelper = new UrlPathHelper();
                    urlPathHelper.setAlwaysUseFullPath(true);
                    configurer.setUrlPathHelper(urlPathHelper);
                }
            });
        }

        private boolean singleDispatcherServlet() {
            return this.servletRegistrations.stream().map(ServletRegistrationBean::getServlet)
                    .filter(DispatcherServlet.class::isInstance).count() == 1;
        }

        @Override
        @SuppressWarnings("deprecation")
        public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
            WebMvcProperties.Contentnegotiation contentnegotiation = this.mvcProperties.getContentnegotiation();
            configurer.favorPathExtension(contentnegotiation.isFavorPathExtension());
            configurer.favorParameter(contentnegotiation.isFavorParameter());
            if (contentnegotiation.getParameterName() != null) {
                configurer.parameterName(contentnegotiation.getParameterName());
            }
            Map<String, MediaType> mediaTypes = this.mvcProperties.getContentnegotiation().getMediaTypes();
            mediaTypes.forEach(configurer::mediaType);
        }

        @Bean
        @ConditionalOnMissingBean
        public InternalResourceViewResolver defaultViewResolver() {
            InternalResourceViewResolver resolver = new InternalResourceViewResolver();
            resolver.setPrefix(this.mvcProperties.getView().getPrefix());
            resolver.setSuffix(this.mvcProperties.getView().getSuffix());
            return resolver;
        }

        @Bean
        @ConditionalOnBean(View.class)
        @ConditionalOnMissingBean
        public BeanNameViewResolver beanNameViewResolver() {
            BeanNameViewResolver resolver = new BeanNameViewResolver();
            resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10);
            return resolver;
        }

        @Bean
        @ConditionalOnBean(ViewResolver.class)
        @ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
        public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
            ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
            resolver.setContentNegotiationManager(beanFactory.getBean(ContentNegotiationManager.class));
            // ContentNegotiatingViewResolver uses all the other view resolvers to locate
            // a view so it should have a high precedence
            resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
            return resolver;
        }

        @Bean
        @ConditionalOnMissingBean
        @ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
        public LocaleResolver localeResolver() {
            if (this.mvcProperties.getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
                return new FixedLocaleResolver(this.mvcProperties.getLocale());
            }
            AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
            localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
            return localeResolver;
        }

        @Override
        public MessageCodesResolver getMessageCodesResolver() {
            if (this.mvcProperties.getMessageCodesResolverFormat() != null) {
                DefaultMessageCodesResolver resolver = new DefaultMessageCodesResolver();
                resolver.setMessageCodeFormatter(this.mvcProperties.getMessageCodesResolverFormat());
                return resolver;
            }
            return null;
        }

        @Override
        public void addFormatters(FormatterRegistry registry) {
            ApplicationConversionService.addBeans(registry, this.beanFactory);
        }


        //资源处理的默认规则
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {

            //ResourceProperties与配置文件spring.resources绑定。
            //isAddMappings()的值,决定资源是否被禁用,true不禁用,false禁用。
            if (!this.resourceProperties.isAddMappings()) { //判断isAddMappings静态资源是否被禁用,
                logger.debug("Default resource handling disabled"); //判断结果为false提示:静态资源被禁用。
                return;
            }

            //getPeriod()静态资源缓存,保存多长时间,可在配置文件中自定义时长。单位:秒。
            Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
            //获取缓存。
            CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();

            //hasMappingForPattern("/webjars/**") 判断判断路径是否存在的方法,是ResourceHandlerRegistry与spring.resources配置文件绑定的类中定义的方法。
            //registry.hasMappingForPattern("/webjars/**") 获取spring.resources配置文件中是否存在路径"/webjars/**" 路径。
            if (!registry.hasMappingForPattern("/webjars/**")) {//"/webjars/**"判断给registry里注册访问规则"/webjars/**" 。
                customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")//访问"/webjars/**"下的所有请求时,都会注册/webjars/** 下的所有请求。
                        .addResourceLocations("classpath:/META-INF/resources/webjars/")
                        .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl)); //访问/webjars/** 下的请求,都会去"classpath:/META-INF/resources/webjars/"路径下找。
            }

            //通过mvcProperties获取与getStaticPathPattern()绑定的 spring.mvc 配置文件信息中的配置路径 static-path-pattern: /res/** 。
            //没有使用默认路径访问规则 /**。
            String staticPathPattern = this.mvcProperties.getStaticPathPattern();
            if (!registry.hasMappingForPattern(staticPathPattern)) {//获取路径,并给registry注册路径规则。
                customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)//注册访问路径下的,被访问的请求。
                        .addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))//getStaticLocations()下的默认值: "classpath:/META-INF/resources/",
            "classpath:/resources/", "classpath:/static/", "classpath:/public/"
                        .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));//staticPathPattern 下的请求,都会去"classpath:/META-INF/resources/webjars/"路径下找。
            }
        }

        private Integer getSeconds(Duration cachePeriod) {
            return (cachePeriod != null) ? (int) cachePeriod.getSeconds() : null;
        }

        private void customizeResourceHandlerRegistration(ResourceHandlerRegistration registration) {
            if (this.resourceHandlerRegistrationCustomizer != null) {
                this.resourceHandlerRegistrationCustomizer.customize(registration);
            }
        }

        @Bean
        @ConditionalOnMissingBean({ RequestContextListener.class, RequestContextFilter.class })
        @ConditionalOnMissingFilterBean(RequestContextFilter.class)
        public static RequestContextFilter requestContextFilter() {
            return new OrderedRequestContextFilter();
        }

    }

欢迎页的处理规则

HandlerMapping:处理器映射。保存了每一个Handler能处理哪些请求。  

    @Bean
        public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,
                FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
            WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(
                    new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),
                    this.mvcProperties.getStaticPathPattern());
            welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
            welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations());
            return welcomePageHandlerMapping;
        }

    WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders,
            ApplicationContext applicationContext, Optional<Resource> welcomePage, String staticPathPattern) {
        if (welcomePage.isPresent() && "/**".equals(staticPathPattern)) {
            //要用欢迎页功能,必须是/**
            logger.info("Adding welcome page: " + welcomePage.get());
            setRootViewName("forward:index.html");
        }
        else if (welcomeTemplateExists(templateAvailabilityProviders, applicationContext)) {
            // 调用Controller  /index
            logger.info("Adding welcome page template: index");
            setRootViewName("index");
        }
    }