静态资源映射

静态资源映射

SpringBoot 2.7.5

对应的自动配置类是 WebMvcAutoConfiguration,我们之前在《SpringBoot 基础篇 -2- 自动配置.md》中的 WebMvcAutoConfiguration 小节中,对其就有一定的介绍。

静态资源访问

官方文档

Static Content

《SpringMVC- 第十篇:基于注解配置 SpringMVC.md》中的 addResourceHandlers 小节

《SpringMVC 中的静态资源处理.md

静态文件位置和 URL 映射

默认情况下,Spring Boot 从类路径(src/main/resources)中的 /static/public/resources/META-INF/resources 目录或 ServletContext 的根目录中提供静态内容。它使用来自 Spring MVC 的 ResourceHttpRequestHandler,因此您可以通过添加您自己的 WebMvcConfigurer 并重写 addResourceHandlers 方法来修改该行为。

需要继承 WebMvcConfigurer 然后重写 addResourceHandlers 方法,请查看《SpringMVC- 第十篇:基于注解配置 SpringMVC.md》中的 addResourceHandlers 小节

如果需要为不同的静态资源位置配置不同的 URL 匹配模板,估计就得通过这种方式了,具体请参考《SpringBoot-Web 开发 -11- 定制化原理.md

在独立的 web 应用中,来自容器(比如 Tomcat)的默认 servlet 是不启用的。它可以设置 server.servlet.register-default-servlet 属性来启用

关于容器中默认的 Servlet 在 SpringMVC 中的开启方式请查看《SpringMVC- 第十篇:基于注解配置 SpringMVC.md》中的 configureDefaultServletHandling 小节

默认情况下,资源映射在 /** 这个 URL 上,但你可以spring.mvc.static-path-pattern 这个属性来自定义 URL 映射位置。例如,将所有资源重新定位到 /resources/** 可以通过以下方式实现:

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

您还可以使用 spring.web.resources.static-locations 属性自定义静态资源的位置。(用目录位置列表替换默认值)。root servlet context path / 也会自动添加为资源位置。

root servlet context path 和上面提到的 ServletContext 的根目录 到底是啥路径,是 src/main/webapp 这个路径吗?感觉是的

除了前面提到的标准的 4 个静态资源位置之外,还为 Webjar 内容做了一个特例。任何 URL 路径匹配 /webjars/** 模板的请求都会从 jar 文件中提供资源,如果这些资源本身被打包成 Webjars 格式的话。

Webjar,就是将 js、css 这些静态资源打成 jar 包来用

用的不多,懒得实践了

如果你的应用程序被打包成 jar,不要使用 src/main/webapp 目录。尽管该目录是一个通用标准,但它只适用于 war 打包,如果生成jar,大多数构建工具都会忽略它。

Spring Boot 还支持 Spring MVC 提供的高级资源处理特性,比如我们在《SpringMVC 中的静态资源处理.md》中学习过的,带版本信息的资源请求,基于文件内容自动生成版本信息或者基于固定的版本,这个 Spring Boot 都可以支持配置。具体请参考 Static Content 官方文档和《SpringMVC 中的静态资源处理.md

简单实践

src/main/resources 目录下新建 config/application.yaml

server:
  # 当前应用所在的Web服务器监听的本地端口
  port: 8081
  servlet:
    # 应用的上下文路径
    context-path: /SpringBoot-WebSimple
    # 启用默认的Servlet
    register-default-servlet: true

src/main/resources 目录下新建 /static/public/resources/META-INF/resources 四个目录,在这四个为目录下均添加一个子目录 image,并分别添加文件 aaa.jpgbbb.jpgccc.jpgddda.jpg

在浏览器中依次输入 http://localhost:8081/SpringBoot-WebSimple/image/aaa.jpghttp://localhost:8081/SpringBoot-WebSimple/image/bbb.jpghttp://localhost:8081/SpringBoot-WebSimple/image/ccc.jpghttp://localhost:8081/SpringBoot-WebSimple/image/ddd.jpg 均可正常访问。

如果在四个路径中存在同路径下的同名资源,那么资源优先级

WebMvcAutoConfigurationWebMvcAutoConfigurationAdapter 内部类的 addResourceHandlers 方法可以得知

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    if (!this.resourceProperties.isAddMappings()) {
        logger.debug("Default resource handling disabled");
        return;
    }
    // 关于webjar的资源配置
    // 排在默认的默认的静态资源位置的配置前面
    addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
    // 默认的静态资源位置的配置
    // this.mvcProperties.getStaticPathPattern() 中,对应的配置是 spring.mvc.static-path-pattern ,默认值是 "/**"
    addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
        // this.resourceProperties.getStaticLocations() 中,如果用户没有设置 spring.web.resources.static-locations 则默认值是 { "classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/" } 
        // 因为资源的添加顺序就是查找顺序,所以,默认的资源查找路径的查找顺序,是
        // "classpath:/META-INF/resources/"
        // "classpath:/resources/" 
        // "classpath:/static/"
        // "classpath:/public/"
        registration.addResourceLocations(this.resourceProperties.getStaticLocations());
        if (this.servletContext != null) {
            ServletContextResource resource = new ServletContextResource(this.servletContext, SERVLET_LOCATION);
            registration.addResourceLocations(resource);
        }
    });
}

添加配置,自定义静态资源位置

server:
  # 当前应用所在的Web服务器监听的本地端口
  port: 8081
  servlet:
    # 应用的上下文路径
    context-path: /SpringBoot-WebSimple
    # 启用默认的Servlet
    register-default-servlet: true

spring:
  web:
    resources:
      # 这里可以传入单个,也可以传入列表 
      static-locations: classpath:/myResources/

注意,每一个路径都需要指定是 classpath: 开头还是以 file: 开头,否则找不到资源。当然最后后面带个 /,这样表义明确一些。

很奇怪的一点是,配置了 spring.web.resources.static-locations 之后,默认静态资源位置中的 /static/public/resources 下面的资源已经无法访问了,但是 META-INF/resources 依然有效,下面的资源依然可以访问。而且优先级依然排在自定义配置的位置的前面

添加配置,自定义静态资源的 URL 匹配模板,不自定义的时候是 /**,直接从应用上下文路径后面开始匹配,这样不方便通过路径进行拦截。静态资源的访问都带一个前缀是最好的,这样可以方便根据路径做拦截判断,比如静态资源就不需要拦截做登录判断。

server:
  # 当前应用所在的Web服务器监听的本地端口
  port: 8081
  servlet:
    # 应用的上下文路径
    context-path: /SpringBoot-WebSimple
    # 启用默认的Servlet
    register-default-servlet: true

spring:
  mvc:
    static-path-pattern: /resources/**
#  web:
#    resources:
#      # 这里可以传入单个,也可以传入列表
#      static-locations: classpath:/myResources/

现在需要通过 http://localhost:8081/SpringBoot-WebSimple/resources/image/aaa.jpg 才能访问到资源

额外注意一点:

如果有 Controller 的映射路径跟静态资源的路径重复了,那么此 URL 会访问 Controller 而不会访问静态资源,这是因为 SimpleUrlHandlerMapping 排在 RequestMappingHandlerMapping 的后面,因此,如果 url 同时匹配控制器方法和 ResourceHttpRequestHandler,那么会优先匹配控制器方法。

ResourceHttpRequestHandler 通常由 SimpleUrlHandlerMapping 进行映射。

详细分析请看《SpringMVC 控制器方法(handler)的映射 - HandlerMapping.md

欢迎页支持 & 自定义 Favicon

官方文档:欢迎页自定义Favicon

Favicon 就是浏览器标签页的图标 ,例如

Spring Boot 既支持静态欢迎页面,也支持模板化欢迎页面(模板指的是视图模板,需要控制器方法跳转或者 ViewController 跳转)。SpringBoot 系统首先在配置的静态资源位置中查找 index.html 文件。如果没有找到,就到 templates 目录下查找 index 视图模板。如果找到其中一个,它将自动用作应用程序的欢迎页面。

static 目录下创建 index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
<h1>Hello World</h1>
Spring Boot 真的和好用!
</body>
</html>

访问 http://localhost:8081/SpringBoot-WebSimple/

注意,此时不能指定静态资源的 URL 映射模板 spring.mvc.static-path-pattern,否则,静态资源目录下的 index.html 无法生效。因此,我们只能尝试通过编写控制器方法处理 /indextemplates 创建 index 视图,来设置欢迎页。

这个我们在《SpringBoot-Web 相关自动配置类源码解析 -WebMvcAutoConfiguration.md》中的 welcomePageHandlerMapping 小节有解释

与其他静态资源一样,Spring Boot 在配置的静态资源位置中检查 favicon.ico。如果存在这样一个文件,它将自动用作应用程序的 favicon。

访问首页

注意,此时,前端对 favicon.ico 的请求路径是 http://localhost:8081/favicon.ico,也就是说,要想让静态资源目录下的 favicon.ico 生效,不能指定应用的请求的上下文路径 server.servlet.context-path,也不能指定静态资源的 URL 映射模板 spring.mvc.static-path-pattern,所以这个功能有点鸡肋。

对 favicon 的请求是浏览器的默认行为,发送的请求的 URL 也是固定的,当然也是可以改的,有时间再研究吧,TODO

相关源码分析

请看《SpringBoot-Web 相关自动配置类源码解析 -WebMvcAutoConfiguration.md》