详细介绍一下什么是Spring Boot3中的静态文件管理?

在Spring Boot3中通过内部的ResourceHandler提供了强大的静态资源管理支持,我们可以通过这些静态资源管理能力来管理例如HTML、CSS、JavaScript、图片、字体、JSON文件等各种数据,不需要任何的配置就可以通过静态资源管理能力来实现静态资源的直接访问。下面我们就来详细介绍一下Spring Boot3中提供的静态资源管理机制。

默认静态资源路径

??在Spring Boot3中,默认情况下会自动映射到如下四个资源路径下的静态资源。

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

??我们可以将任意的静态资源文件放置在这些资源路径下,如下所示,SpringBoot就会自动提供这些静态资源路径下的资源访问映射。

src/main/resources/static/index.html   ->  访问: http://localhost:8080/index.html
src/main/resources/public/style.css    ->  访问: http://localhost:8080/style.css
src/main/resources/resources/logo.png  ->  访问: http://localhost:8080/logo.png

??不过这里需要注意,在Spring Boot3中不会解析templates/ 目录下的 HTML 文件,因为这个文件夹下的文件默认是被Thymeleaf 模板引擎控制并非静态资源的规则管理范畴。

自定义静态资源路径

??上面我们介绍了默认的文件加载规则,当然如果我们在实际使用过程中想要修改这个静态资源的路径,我们可以在配置文件中通过如下的配置来进行调整修改。

spring.web.resources.static-locations=classpath:/my-static/,file:/opt/app/static/

??其中**classpath:/my-static/表示 src/main/resources/my-static/ 目录。file:/opt/app/static/**表示存放在磁盘上的外部目录 /opt/app/static/

??通过这种方式,我们就可以调整静态文件的存放位置,然后通过http://localhost:8080/example.css的连接就可以访问到 my-static/example.css目录下的对应文件。

访问 WebJars 资源

??在Spring Boot3中内部还支持了基于WebJars(用于管理前端库,如 Bootstrap、jQuery)资源的访问机制,我们可以通过如下的方式来添加对应的依赖配置。


    org.webjars
    bootstrap
    5.3.0

??在默认情况下,WebJars 资源默认会被映射到 /webjars/ 目录中,如下所示。

http://localhost:8080/webjars/bootstrap/5.3.0/css/bootstrap.min.css

自定义静态资源映射

??上面我们介绍了对于静态资源路径的管理,这里我们介绍一下对于静态资源的自定义映射路径的配置,我们可以通过扩展 WebMvcConfigurer来实现对于静态资源的自定义映射访问,如下所示。

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/files/**")  // 访问 URL: /files/xxx.jpg
                .addResourceLocations("classpath:/my-files/") // 资源存放路径
                .setCachePeriod(3600);  // 设置缓存时间(秒)
    }
}

??这样,我们就可以通过http://localhost:8080/files/example.png,来访问到`my-files/` 目录下的文件,这种方案经常被用来完成一些文件上传回显的访问实现。

读取静态资源文件

??有时候可能还会遇到需要再Java代码中读取静态文件的操作需求,例如读取JSON文件、读取配置文件等等,在Spring中可以通过ResourceLoader来实现对于文件的读取操作,如下所示。

import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

@Service
public class StaticFileReader {

    private final ResourceLoader resourceLoader;

    public StaticFileReader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    public String readFile(String filePath) throws IOException {
        Resource resource = resourceLoader.getResource("classpath:/static/" + filePath);
        return new String(Files.readAllBytes(Paths.get(resource.getURI())));
    }
}

??在实际使用的时候,我们可以通过如下的方式来实现对于静态文件的调用。

String content = staticFileReader.readFile("example.json");
System.out.println(content);

??这里需要注意,classpath:/static/ 代表 src/main/resources/static/ 目录。如果文件存放在外部磁盘,如 /opt/app/config.json中,可以通过如下的方式来实现读取操作。

Resource resource = resourceLoader.getResource("file:/opt/app/config.json");

静态资源缓存

??在Spring Boot3中默认提供了静态文件的缓存机制,我们可以通过如下的配置来对静态文件的缓存进行配置。

# 设置缓存时间(单位:秒)
spring.web.resources.cache.period=3600

# 让浏览器缓存资源(使用 ETag)
spring.web.resources.chain.cache=true
spring.web.resources.chain.strategy.content.enabled=true
spring.web.resources.chain.strategy.content.paths=/**

??这样,静态文件会缓存1个小时,并且通过ETag机制进行请求的优化。

处理静态资源 404 问题

??有这样的需求,就是在找不到资源路径的时候服务器应用会响应404的请求报错,这个时候我们可以创建一个error/404.html 文件,如下所示。

src/main/resources/static/error/404.html

??这种情况下,Spring Boot会在找不到资源的时候自动显示该页面。如果想要实现全局的自定义404请求处理,我们可以通过如下的方式来实现。

import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import jakarta.servlet.http.HttpServletRequest;

@Controller
public class CustomErrorController implements ErrorController {

    @RequestMapping("/error")
    @ResponseBody
    public String handleError(HttpServletRequest request) {
        return "Oops! 页面未找到 (404)";
    }
}

前后端分离时处理静态资源

??现在互联网应用都是前后端分离的项目,也就是说前端是通过Vue或者是React进行构建的,这个时候,可能会需要通过Spring Boot转发请求的需求,例如转发所有未匹配的路径index.html,就可以通过如下的方式来实现。

@Configuration
public class FrontendForwardingConfig implements WebMvcConfigurer {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/{spring:\\w+}")
                .setViewName("forward:/index.html");
        registry.addViewController("/**/{spring:\\w+}")
                .setViewName("forward:/index.html");
    }
}

??这样当前端路由就不会再返回404,而是正确的加载index.html页面。

总结

??根据上面的介绍Spring Boot 3 在静态资源管理方面提供了丰富的功能支持,既可以适用于普通 Web 应用,也能很好地支持前后端分离部署,有兴趣的读者可以深入了解,有问题可以在评论区讨论