静态资源

Spring Boot 内置静态资源处理,无需额外配置即可托管 CSS、JS、图片等文件。

默认资源目录

Spring Boot 按如下优先级顺序查找静态资源:

classpath:/META-INF/resources/
classpath:/resources/
classpath:/static/            ← 最常用
classpath:/public/

即将文件放在 src/main/resources/static/ 下,访问 /style.css 即可自动映射到该文件。

src/main/resources/
└── static/
    ├── css/
    │   └── main.css          → GET /css/main.css
    ├── js/
    │   └── app.js            → GET /js/app.js
    └── images/
        └── logo.png          → GET /images/logo.png

自定义资源路径

# application.yml
spring:
  web:
    resources:
      static-locations:
        - classpath:/static/
        - classpath:/my-assets/     # 追加自定义目录
        - file:/opt/app/uploads/    # 文件系统路径(生产环境上传文件)

或通过 Java 配置更细粒度地控制:

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
 
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        // 将 /uploads/** 映射到文件系统目录
        registry.addResourceHandler("/uploads/**")
            .addResourceLocations("file:/opt/app/uploads/");
 
        // 将 /docs/** 映射到 classpath 内的目录
        registry.addResourceHandler("/docs/**")
            .addResourceLocations("classpath:/static/docs/");
    }
}

缓存控制

默认行为

Spring Boot 默认为静态资源设置 1 年缓存(Cache-Control: max-age=31536000)。

自定义缓存策略

spring:
  web:
    resources:
      cache:
        period: 3600             # 缓存时长(秒),0 = 禁用缓存
        cachecontrol:
          max-age: 7d            # 浏览器缓存 7 天
          cache-public: true     # 允许 CDN 缓存
          no-cache: false
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/static/**")
        .addResourceLocations("classpath:/static/")
        .setCacheControl(CacheControl.maxAge(Duration.ofDays(30))
            .cachePublic());
 
    // 开发环境:禁用缓存,方便调试
    registry.addResourceHandler("/dev/**")
        .addResourceLocations("classpath:/static/")
        .setCacheControl(CacheControl.noStore());
}

内容版本化(Cache Busting)

文件内容发生变化时,URL 自动变化,使 CDN/浏览器缓存失效:

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
 
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**")
            .addResourceLocations("classpath:/static/")
            .setCacheControl(CacheControl.maxAge(Duration.ofDays(365)))
            .resourceChain(true)
                .addResolver(new VersionResourceResolver()
                    .addContentVersionStrategy("/**"));  // 按内容 MD5 生成版本号
    }
 
    // 注册 ResourceUrlEncodingFilter,使 Thymeleaf 中的 @{} 自动加版本号
    @Bean
    public ResourceUrlEncodingFilter resourceUrlEncodingFilter() {
        return new ResourceUrlEncodingFilter();
    }
}

Thymeleaf 模板中引用资源时会自动转换:

<!-- 源代码 -->
<link th:href="@{/static/css/main.css}" rel="stylesheet">
 
<!-- 渲染输出(URL 包含内容 hash) -->
<link href="/static/css/main-a3f7c9b2.css" rel="stylesheet">

Thymeleaf 模板详见 Thymeleaf

WebJars

通过 Maven/Gradle 引入前端库,无需手动管理 JS/CSS 文件:

// build.gradle
dependencies {
    implementation 'org.webjars:bootstrap:5.3.0'
    implementation 'org.webjars:jquery:3.7.0'
    implementation 'org.webjars.npm:vue:3.3.4'
    // webjars-locator 省去版本号前缀,推荐
    implementation 'org.webjars:webjars-locator-core:0.55'
}

webjars-locator-core 存在时,可以省略 URL 中的版本号:

<!-- 不使用 webjars-locator(需带版本号) -->
<script src="/webjars/jquery/3.7.0/jquery.min.js"></script>
 
<!-- 使用 webjars-locator(自动解析版本) -->
<script src="/webjars/jquery/jquery.min.js"></script>

欢迎页(Welcome Page)

Spring Boot 自动将静态资源目录下的 index.html 作为根路径欢迎页:

static/index.html  →  GET /

也可以通过 Controller 自定义欢迎页:

@Controller
public class HomeController {
 
    @GetMapping("/")
    public String index() {
        return "forward:/index.html";  // 转发到静态文件
    }
}

favicon.ico

favicon.ico 放到以下任意位置即可自动生效:

src/main/resources/static/favicon.ico
src/main/resources/favicon.ico

禁用 favicon(减少不必要请求):

spring:
  mvc:
    favicon:
      enabled: false   # Spring Boot 2.x(3.x 默认由浏览器处理)

与 Spring Security 配合

Spring Security 默认会拦截所有请求,需要显式放行静态资源:

@Configuration
@EnableWebSecurity
public class SecurityConfig {
 
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(auth -> auth
            // 放行静态资源
            .requestMatchers(
                PathRequest.toStaticResources().atCommonLocations()
            ).permitAll()
            // 放行自定义路径
            .requestMatchers("/uploads/**", "/webjars/**").permitAll()
            .anyRequest().authenticated()
        );
        return http.build();
    }
}

PathRequest.toStaticResources().atCommonLocations() 自动匹配 /css/**/js/**/images/**/webjars/**/favicon.ico 等标准路径。

Spring Security 详见 安全

生产环境:Nginx 托管静态资源

Spring Boot 内置的静态资源处理适合开发和小规模部署,生产环境建议用 Nginx 直接托管:

server {
    listen 80;
 
    # 静态资源由 Nginx 直接返回,不经过 Spring Boot
    location /static/ {
        alias /opt/app/static/;
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
 
    location /uploads/ {
        alias /opt/app/uploads/;
        expires 7d;
    }
 
    # 其余请求代理到 Spring Boot
    location / {
        proxy_pass http://127.0.0.1:8080;
    }
}

构建时将 src/main/resources/static/ 复制到 /opt/app/static/,与应用部署解耦。

容器化部署详见 Docker部署

相关链接

  • Thymeleaf — 模板中的 @{} 资源链接语法与版本化 URL
  • 安全 — Spring Security 放行静态资源
  • MVCWebMvcConfigurerResourceHandlerRegistry
  • Docker部署 — 静态资源与应用的分离部署