惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
V2EX
C
Check Point Blog
博客园_首页
B
Blog
D
Docker
U
Unit 42
量子位
I
InfoQ
有赞技术团队
有赞技术团队
Martin Fowler
Martin Fowler
GbyAI
GbyAI
L
LangChain Blog
云风的 BLOG
云风的 BLOG
博客园 - Franky
美团技术团队
T
The Blog of Author Tim Ferriss
阮一峰的网络日志
阮一峰的网络日志
月光博客
月光博客
Vercel News
Vercel News
Recent Announcements
Recent Announcements
雷峰网
雷峰网
大猫的无限游戏
大猫的无限游戏
小众软件
小众软件
Google DeepMind News
Google DeepMind News

博客园 - wastonl

maven选择settings.xml策略 RocketMQ消费机制整理 RocketMQ重平衡机制 @JacksonXmlElementWrapper使用限制 JSR303和SpringBoot参数校验机制 同一Java进程生产者和消费者与MqClientInstance对应关系 RocketMQ自动创建主题机制 SpringMVC异步请求 tomcat异步请求机制 Spring异步任务和定时任务 Spring ResolvableType说明 Jvm内存以及垃圾回收相关知识 RocketMQ如何保证消息可靠性 RocketMQ整体架构 mybatis-plus易忘点笔记 SpringMVC使用Resource实现二进制传输(下载) Spring事件异步执行设计与实现 Spring Bean销毁机制 Spring Lifecycle组件 Zipkin Brave使用 Spring Boot日志系统简要介绍 spring cloud sleuth基本使用 Spring懒加载与@Lazy注解 tomcat自动刷新响应输出流缓冲区 https碎碎念 ES脚本使用 Maven插件运行方式 如何使用Maven将项目中的依赖打进jar包 时区以及时区对于Java时间类格式化的影响 SpringMVC处理请求头、响应头、编码行为
SpringMVC静态资源处理
wastonl · 2024-10-11 · via 博客园 - wastonl

基本使用

@Component
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/asset/**")
            .addResourceLocations("classpath:/asset/");
    }
}

只需要实现WebMvcConfigurer接口,重写其中的addResourceHandlers方法即可。

其中路径遵循Ant Match语法规则,资源位置(resourceLocations)遵循Spring Resource路径语法,内部会将字符串转成Resource实例。

简要介绍Ant Match语法

  • ?:匹配单个字符

  • *:匹配零个或者多个字符

  • ******:匹配零个或者多个目录(包括子目录)

定位静态资源逻辑

光看样例其实对于资源的定位逻辑其实很迷惑,就和nginx的反向代理一样。

定位资源取决于两个要素,一个是从请求路径获取资源的相对路径部分,另外就是配置的资源所在的目录。

获取资源相对路径规则

  • 如果没有通配符,就是请求路径,比如映射配置为/asset/doc.html,那么就是/asset/doc.html。

  • 如果存在通配符,那么就是取通配符匹配的那部分。

    举例如下

    映射配置为/asset/**,请求路径为/asset/doc.html,那么取得的值为doc.html

    映射配置为/asset/**,请求路径为/asset/js/main.js,那么取得的值为js/main.js

    映射配置为/asset/*.html,请求路径为/asset/index.html,那么取得值index.html

通配符取相对路径值可参考AntPathMatcher.extractPathWithinPattern方法

获取资源最终路径

将获取到的相对资源路径和配置的资源所在的目录结合就是最终资源位置。

使用Resource.createRelative方法获取,对于ClassPathResource来说,如果当前资源路径存在/,则拼接,如果不存在,则只使用参数部分。

Resource resource = new ClassPathResource("asset/");
// 表示类路径下的asset/doc.html
Resource relative = resource.createRelative("doc.html");

Resource resource = new ClassPathResource("asset");
// 表示类路径下的doc.html,丢弃了asset,因为后面没有/
Resource relative = resource.createRelative("doc.html");

因此对于上面示例,若请求路径为/asset/js/index.js,则定位的资源即类路径下的/asset/js/index.js

常用例子

/**
 * /asset/js/index.js
 * 最终资源: js/index.js + /static/ = /static/js/index.js
 */
registry.addResourceHandler("/asset/**").addResourceLocations("classpath:/static/");

/**
 * /asset/view/index.html
 * 最终资源: view/index.html + /static/ = /static/view/index.html
 *
 * /asset/index.html
 * 最终资源: index.html + /static/ = /static/index.html
 */
registry.addResourceHandler("/asset/**/*.html").addResourceLocations("classpath:/static/");

/**
 * 无通配符
 * 请求路径: /asset/index.html
 * 最终资源: /asset/index.html + /static/ = /static/asset/index.html
 * 多余的前导斜杠会自动去掉
 */
registry.addResourceHandler("/asset/index.html").addResourceLocations("classpath:/static/");

/**
 * 资源目录后面少写斜杠,资源目录丢弃
 * /asset/js/index.js
 * 最终资源: js/index.js = js/index.js
 * 
 * 注: 这样子即便找到了该资源,springmvc会做一个检查,检查这个资源是否在允许的目录下,这个允许的
 * 目录默认就是配的资源路径目录即/static,因此仍然会报404,资源不存在的。
 * 因此资源目录务必末尾添加斜杠
 */
registry.addResourceHandler("/asset/**").addResourceLocations("classpath:/static");

注:资源目录路径末尾务必要增加斜杠,用于暗示是目录,否则会丢掉这个目录,导致出现资源找不到现象。

Spring Boot默认配置的资源映射

在Spring Boot中,默认为配置请求路径为/**,即拦截所有路径,资源目录位置为以下几个

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

寻找的优先级从上到下,即classpath:/META-INF/resources/优先级最高

自动配置类为WebMvcAutoConfiguration,找到addResourceHandlers方法即可。

关闭默认的资源映射配置

spring:
  web:
    resources:
      # 不要添加默认的ResourceHandler(/**等)
      add-mappings: false

底层依赖类

方便读取源码

  • HandleMapping为SimpleUrlHandlerMapping。
  • Handler为ResourceHttpRequestHandler。
  • HandlerAdapter为HttpRequestHandlerAdapter。
  • 自动配置为WebMvcConfigurationSupport类的resourceHandlerMapping方法,注册了一个SimpleUrlHandlerMapping。