官方文档

📖 boot-features-spring-mvc-static-content

引入依赖

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-web</artifactId>
  4. <version>2.4.0</version>
  5. </dependency>

静态资源

classpath目录下的:

  • META-INF/resources
  • public
  • resources
  • static

以上四个路径全都不拦截,可以直接访问。
访问资源时不需要带上任何路径,直接+<资源名称>即可。

请求处理

如果同时有动态接口和静态资源请求路径相同,则优先使用动态接口处理。
不能处理的所有请求又都交给静态资源处理器,静态资源也找不到就会404。

访问前缀

默认无前缀,自定义前缀:

  1. spring:
  2. mvc:
  3. static-path-pattern: /parak/**

访问路径: ://

改变路径

查看spring.resources.static-locations源代码:

  1. private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{"classpath:/META-INF/resources/",
  2. "classpath:/resources/",
  3. "classpath:/static/",
  4. "classpath:/public/"};

修改默认的静态资源文件路径:

  1. spring:
  2. resources:
  3. static-locations: [classpath:/parak/]

但是我配置的好像没有什么『卵用』,而且META-INF/resources下的文件还是可以访问,这个玩意还是不推荐大家使用了。

webjar

官网:https://www.webjars.org
添加相关jar包后后可以自动映射相关的静态资源。
例如,添加jquery相关jar包:

  1. <dependency>
  2. <groupId>org.webjars</groupId>
  3. <artifactId>jquery</artifactId>
  4. <version>3.5.1</version>
  5. </dependency>

访问地址:http://localhost:8080/webjars/jquery/3.5.1/jquery.js

首页支持

只需要在任意静态资源路径下加入一个index.html即可,但是不可以配置静态资源的访问前缀,否则导致index.html不能被默认访问。
自定义logo:只需要将一个图片文件命名为favicon.ico放到静态资源文件目录即可。

配置原理

主要查看:org.springframework.boot.autoconfigure.web.servlet
springboot-静态资源 - 图1

  • DispatcherServletAutoConfiguration: 配置SpringMVC的DispatcherServlet规则
  • HttpEncodingAutoConfiguration: 配置Http的编解码
  • MultipartAutoConfiguration: 配置文件上传
  • ServletWebServerFactoryAutoConfiguration: 配置servlet的web服务器
  • WebMvcAutoConfiguration: 配置SpringMVC功能

重点关注WebMvcAutoConfiguration:

  1. @Configuration(proxyBeanMethods = false)
  2. @ConditionalOnWebApplication(type = Type.SERVLET) // 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

其中有一个内部类WebMvcAutoConfigurationAdapter:

  1. @Configuration(proxyBeanMethods = false)
  2. @Import(EnableWebMvcConfiguration.class)
  3. @EnableConfigurationProperties({ WebMvcProperties.class,
  4. org.springframework.boot.autoconfigure.web.ResourceProperties.class, WebProperties.class })
  5. @Order(0)
  6. public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer

@EnableConfigurationProperties将配置文件与配置类进行绑定,类与配置值的前缀映射如下:

  • WebMvcProperties: spring.mvc
  • ResourceProperties: spring.resources
  • WebProperties: spring.web

    配置类只有一个有参构造器,构造器的所有参数都会默认从容器中寻找。

  1. // 有参构造器的参数全部从IOC容器中确定
  2. // resorurceProperties:获取和spring.resource绑定的所有的值的对象
  3. // webMvcProperties:获取和spring.mvc绑定的所有值的对象
  4. // webProperties:获取和spring.web绑定的所有值的对象
  5. // beanFactory:Spring的beanFactory
  6. // messageConvertersProvider:找到所有的HttpMessageConverters
  7. // resourceHandlerRegistrationCustomizerProvider:找到资源处理器的自定义器
  8. // dispatcherServletPath: DisapatchServlet的处理路径
  9. // servletRegistrations:给应用注册Servlet、Filter
  10. public WebMvcAutoConfigurationAdapter(
  11. org.springframework.boot.autoconfigure.web.ResourceProperties resourceProperties,
  12. WebProperties webProperties, WebMvcProperties mvcProperties, ListableBeanFactory beanFactory,
  13. ObjectProvider<HttpMessageConverters> messageConvertersProvider,
  14. ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider,
  15. ObjectProvider<DispatcherServletPath> dispatcherServletPath,
  16. ObjectProvider<ServletRegistrationBean<?>> servletRegistrations) {
  17. this.resourceProperties = resourceProperties.hasBeenCustomized() ? resourceProperties
  18. : webProperties.getResources();
  19. this.mvcProperties = mvcProperties;
  20. this.beanFactory = beanFactory;
  21. this.messageConvertersProvider = messageConvertersProvider;
  22. this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
  23. this.dispatcherServletPath = dispatcherServletPath;
  24. this.servletRegistrations = servletRegistrations;
  25. this.mvcProperties.checkConfiguration();
  26. }

主要关注资源处理的相关方法addResourceHandlers

  1. @Override
  2. public void addResourceHandlers(ResourceHandlerRegistry registry) {
  3. if (!this.resourceProperties.isAddMappings()) {
  4. // 配置是否启用静态资源映射
  5. // 对应application.properties的spring.web.resources.add-mappings
  6. // 一旦设置为false,后面配置静态文件映射规则的代码逻辑将不会执行
  7. logger.debug("Default resource handling disabled");
  8. return;
  9. }
  10. // 配置静态资源缓存时间
  11. // 对应application.properties的spring.web.resources.cache.period
  12. // 配置后该时间段内资源缓存在浏览器中,状态码为304,不再请求服务器
  13. Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
  14. CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
  15. // 配置webjars映射规则
  16. // 全部从classpath:/META-INF/resources/webjars/下面寻找
  17. if (!registry.hasMappingForPattern("/webjars/**")) {
  18. customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
  19. .addResourceLocations("classpath:/META-INF/resources/webjars/")
  20. .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl)
  21. .setUseLastModified(this.resourceProperties.getCache().isUseLastModified()));
  22. }
  23. // 配置静态路径映射规则,其实就是在URL上添加访问路径前缀
  24. // 对应application.properties的spring.mvc.static-path-pattern
  25. String staticPathPattern = this.mvcProperties.getStaticPathPattern();
  26. if (!registry.hasMappingForPattern(staticPathPattern)) {
  27. customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
  28. // staticLocations默认值就是前面我们提到的CLASSPATH_RESOURCE_LOCATIONS
  29. .addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
  30. .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl)
  31. .setUseLastModified(this.resourceProperties.getCache().isUseLastModified()));
  32. }
  33. }

再看欢迎页规则的配置:

  1. // 注册bean
  2. @Bean
  3. public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,
  4. FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
  5. WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(
  6. new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),
  7. this.mvcProperties.getStaticPathPattern());
  8. welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
  9. welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations());
  10. return welcomePageHandlerMapping;
  11. }
  12. // 构造器
  13. WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders,
  14. ApplicationContext applicationContext, Optional<Resource> welcomePage, String staticPathPattern) {
  15. // 访问前缀必须为/**,否则欢迎页失效
  16. if (welcomePage.isPresent() && "/**".equals(staticPathPattern)) {
  17. logger.info("Adding welcome page: " + welcomePage.get());
  18. setRootViewName("forward:index.html");
  19. }
  20. else if (welcomeTemplateExists(templateAvailabilityProviders, applicationContext)) {
  21. logger.info("Adding welcome page template: index");
  22. setRootViewName("index");
  23. }
  24. }