配置MVC时SpringBoot为我们配置了什么

在加入SpringBoot的Web的starter后,自动配置类做了如下工作:

  1. 内容协商解析器和BeanName视图解析器
  2. 静态资料(包括webjars)
  3. 自动注册Convert、GenericConvert、Formatter
  4. 自动注册HttpMessageConverts
  5. 自动注册MessageCodesResolver(用于国际化,目前已经很少使用这个方案了)
  6. 静态index.html支持
  7. 自定义Favicon
  8. 自动使用ConfigurableWebBindingInitializer(DataBinder负责将请求数据绑定到JavaBean上)

静态资源访问

只要静态资源防止在/static/public/resources/META-INF/resources目录下,可以通过当前项目根路径 / 静态资源名访问

原理是静态映射/**。请求进来后,先去找Controller看能不能处理。不能处理的所有请求又都交给静态资源处理器。静态资源处理器也找不到则相应404页面。

可进行如下配置:

1
2
3
4
5
6
7
8

spring:
  mvc:
    static-path-pattern: /res/**

  resources:
    static-locations: [classpath:/haha/]

配置了前缀后,访问项目根路径 / static-path-pattern / 静态资源名后,会自动在各个静态目录下查找。配置了static-locations后,则需要将静态文件放置在该目录中。一般来说,我们会配置static-path-pattern,方便进行权限管理(前后端分离项目中,基本用不到这个东西)。

webjars是一个非常有意思的技术,就是将一些js文件通过jar包的方式进行管理,这些js文件会被自动放置在webjars目录下。可用该地址访问:http://localhost:8080/webjars/jquery/3.5.1/jquery.js

1
2
3
4
5
6
7
8


<dependency>
    <groupId>org.webjars</groupId>
    <artifactId>jquery</artifactId>
    <version>3.5.1</version>
</dependency>

静态资源路径下的index.html将会被作为欢迎页。这个地方有个Bug,可以配置静态资源路径,但是不可以配置静态资源的访问前缀,否则导致index.html不能被默认访问。

1
2
3
4
5
6
7
8

spring:
#  mvc:
#    static-path-pattern: /res/**   这个会导致welcome page功能失效

  resources:
    static-locations: [classpath:/haha/]

将favicon放在静态资源目录下即可被浏览器自动访问到。

静态资源配置原理

如何进行配置的,首先WebMvcAutoConfiguration配置生效:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12

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

}

这个类并没有配置太核心的东西,但是这个类里面的静态内部类WebMvcAutoConfigurationAdapter集成了WebMVCConfigurer,才是配置的核心:

1
2
3
4
5
6
7
8
9

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

}

WebMvcAutoConfigurationAdapter构造函数接受的参数如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

/*
    resourceProperties:获取和spring.resources绑定的所有的值的对象
    mvcProperties:获取和spring.mvc绑定的所有的值的对象
    beanFactory:Spring的beanFactory
    messageConvertersProvider:找到所有的HttpMessageConverters
    resourceHandlerRegistrationCustomizerProvider:找到资源处理器的自定义器
    dispatcherServletPath:
    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;
}

WebMvcAutoConfigurationAdapter配置资源处理的默认规则:

  1. 针对webjars进行了配置
  2. 针对静态资源进行了配置
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    if (!this.resourceProperties.isAddMappings()) {
        logger.debug("Default resource handling disabled");
        return;
    }
    Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
    CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
    if (!registry.hasMappingForPattern("/webjars/**")) {
        customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/")
                .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
    }
    String staticPathPattern = this.mvcProperties.getStaticPathPattern();
    if (!registry.hasMappingForPattern(staticPathPattern)) {
        customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
                .addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
                .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
    }
}

WebMvcAutoConfigurationAdapter配置欢迎页处理规则:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12

@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;
}