Spring Boot资源文件问题总结(Spring Boot的静态资源访问,配置文件外置)

简介: Spring Boot资源文件问题总结(Spring Boot的静态资源访问,配置文件外置)

文件系统是我们开发过程中常常会接触的问题。那么在Spring Boot框架中,文件的访问又是什么样的呢?今天在此做一个总结。

1,fileclasspath

存放在电脑上实际位置的文件,在Spring Boot中用file:开头表示。例如:

  • file:a.txt 当前目录下的a.txt文件。当前路径在开发环境下一般为Maven项目的目录下(与pom.xml同目录下),在打包为jar文件后当前路径即为运行jar文件时的运行路径。
  • file:D:\a.txt 表示绝对路径,在此不多赘述。

而在jar文件内部中,我们一般把文件路径称为classpath,所以读取内部的文件就是从classpath内读取,classpath指定的文件不能解析成File对象,但是可以解析成InputStream。例如:

  • classpath:/a.txt jar包根目录下的a.txt。classpath/开头表示绝对路径,即为jar包根目录。

2,Spring Boot的静态资源访问

我们都知道Spring Boot工程文件夹中的src/main/resources是用于存放资源的地方。默认时Spring Boot打包之后静态资源位置如下:

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

在Spring Boot中classpath的根目录就对应工程文件夹下的src/main/resources

可以先看这个例子:

在工程文件夹下src/main/resources/static下放入图片qiqi.png

网络异常,图片无法展示
|

运行,访问127.0.0.1:8080/qiqi.png,效果如下:

网络异常,图片无法展示
|

这个例子可见外部访问的资源路径和Spring Boot工程中资源文件路径的一一对应关系。即外部访问时的“根目录”即对应着上述的四个静态资源位置(classpath)

还可以新建一个Controller类,写如下方法:

@GetMapping("/pic")
publicStringshowPic() {
return"/qiqi.png";
}

运行,访问127.0.0.1:8080/pic,效果同上。

在这个Controller方法中,上面@GetMapping是路由路径,return的是对应的资源路径。

其实这个默认的资源路径是可以修改的。

我们需要知道在配置文件application.properties中可以加入下列两个配置项:

spring.mvc.static-path-pattern
spring.web.resources.static-locations

我们来逐一进行讲解。

(1) spring.mvc.static-path-pattern - 指定资源访问路径

这个spring.mvc.static-path-pattern代表的是应该以什么样的路径来访问静态资源,也就是只有静态资源满足什么样的匹配条件,Spring Boot才会处理静态资源请求。说白了就是资源的外部访问路径。根据上述例子我们知道了这个配置默认值为/**。假设在上述工程配置文件中加入:

spring.mvc.static-path-pattern=/resources/**

那么再访问我们那个图片就要访问网址:127.0.0.1:8080/resources/qiqi.png

网络异常,图片无法展示
|

好了,我们如果现在想使用Controller类的@GetMapping进行路由的话,如果还是像上面那么写:

@GetMapping("/pic")
publicStringshowPic() {
return"/qiqi.png";
}

访问127.0.0.1:8080/pic,你会发现:

网络异常,图片无法展示
|

为什么这时就不行了呢?

这是因为我们改变了spring.mvc.static-path-pattern配置的值,那么我们对应的Controller类方法中的返回值,也要对应改变

之前spring.mvc.static-path-pattern没有配置那默认就是/**,那访问/qiqi.png就可以找到图片。现在这个配置改为/resources/**,那很显然要访问/resources/qiqi.png了,再访问/qiqi.png当然访问不到了!因此对应的Controller类方法中也要做出对应修改。

上述配置spring.mvc.static-path-pattern/resources/**,那么我们修改Controller方法如下:

@GetMapping("/pic")
publicStringshowPic() {
return"/resources/qiqi.png";
}

可见返回值改成了/resources/qiqi.png

网络异常,图片无法展示
|

可见,Controller类中的方法的返回值,并非是资源文件的实际的相对路径,而是对应的资源的外部访问路径。这一点也是我和我身边许多朋友容易混淆的一点。

(2) spring.web.resources.static-locations - 指定静态资源查找路径

再者,spring.web.resources.static-locations用于指定静态资源文件的查找路径,查找文件是会依赖于配置的先后顺序依次进行。根据上述例子可见这个值默认是:

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

其实上面提到了Spring Boot默认资源文件位置,实质上就是这个配置的值。

假设在上述工程中配置文件写入:

spring.web.resources.static-locations=classpath:/myRes

那么我们要将qiqi.png放在项目文件夹的src\main\resources\myRes文件夹中,才能访问:

网络异常,图片无法展示
|

配置此项后,默认值将失效!

还可以使用磁盘路径例如:

spring.web.resources.static-locations=file:res

即指定资源文件在项目文件夹中的res目录中(即打包后运行jar文件时的运行路径下的res文件夹中)。也可以使用绝对路径。

网络异常,图片无法展示
|

网络异常,图片无法展示
|

通俗地讲,spring.mvc.static-path-pattern配置指定了我们外部访问的路径,而访问这个外部路径时就会去spring.web.resources.static-locations配置的路径中找对应的资源

(3) 集成Spring Security之后导致上述配置失效

今天在维护一个项目的时候发现:即使是正确配置上述的静态资源配置,访问静态资源时一直报404,我也很纳闷:之前好好的啊!怎么就不行了呢?经查阅各种资料发现:若配置了拦截器,则会导致上述配置失效。这个项目使用了Spring Security,可能是因为其中的拦截器配置导致这个资源配置失效了。

之前一个项目配置了Swagger之后也出现了这个配置失效的问题,我想应该是同一个原因导致。

经参考官方文档之后,发现还有一个方式可以配置静态资源访问路径和对应位置。我们新建一个配置类,重写WebMvcConfigurer中的addResourceHandlers方法即可

因此,在一些外部依赖自带拦截器的情况下,就很有可能覆盖我们上述资源路径配置,导致我们上述资源配置失效。因此这个时候,我们就不能通过写上述配置文件的方式配置静态资源访问了!就要通过写配置类的方式

我们先来看一个例子,我这里项目目录结构如下:

网络异常,图片无法展示
|

然后我们新建一个软件包config,在里面写配置类如下:

packagecom.example.resourcetest.config;
importorg.springframework.context.annotation.Configuration;
importorg.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
importorg.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/*** 自定义MVC配置器*/@ConfigurationpublicclassMyWebMvcConfigimplementsWebMvcConfigurer {
/*** 重写资源路径配置*/@OverridepublicvoidaddResourceHandlers(ResourceHandlerRegistryregistry) {
registry.addResourceHandler("/image/**").addResourceLocations("file:res/image/");
   }
}

可见我们只需要调用addResourceHandlers方法参数registry的方法也可以实现资源路径配置,配置效果和上面是一样的

其中两个方法的意义如下:

  • addResourceHandler 等同于上述配置spring.mvc.static-path-pattern,代表的是应该以什么样的路径来访问静态资源
  • addResourceLocations 等同于上述配置spring.web.resources.static-locations,用于指定静态资源文件的查找路径

同样地,配置此项后,默认的资源搜索路径将失效!

可见我们要先调用addResourceHandler再调用addResourceLocations,两者是一一对应的,上述代码意思就是:外部访问路径/image/xxx时,就会去当前路径下res/image/目录下找xxx

现在,访问127.0.0.1:8080/image/gz-12.png,可见访问成功:

网络异常,图片无法展示
|

可见通过配置类的方式,我们仍然可以实现上述在配置文件中实现的效果。不过这里需要注意的是,和配置文件中不同,指定静态文件查找路径时,若路径是个目录则必须以/结尾!否则也会出现404的情况。

当然,在addResourceHandleraddResourceLocations方法中都可以添加多个路径,例如:

registry.addResourceHandler("/image/**").addResourceLocations("file:res/image/", "classpath:/static/");

也就是说外部访问/image/xxx时,会去当前目录下res/image/和类路径/static/中去寻找xxx,上面也讲了类路径classpath了,这里对应的也是一样的。

还可以这样:

registry.addResourceHandler("/image/**", "/img/**").addResourceLocations("file:res/image/");

也就是写了多个外部访问路径,表示访问/image/xxx/img/xxx时,都会到当前路径下的res/image/下去查找xxx

当然,事实上配置类配置的方式也会更加高级,上述配置文件中我们只能配置一个外部访问路径,对应其它多个实际资源查找路径。而在配置类中,我们可以定义多个外部访问路径,对应不同的资源查找路径,例如我将代码改如下:

packagecom.example.resourcetest.config;
importorg.springframework.context.annotation.Configuration;
importorg.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
importorg.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/*** 自定义MVC配置器*/@ConfigurationpublicclassMyWebMvcConfigimplementsWebMvcConfigurer {
/*** 重写资源路径配置*/@OverridepublicvoidaddResourceHandlers(ResourceHandlerRegistryregistry) {
// 访问/image/xxx对应去res/image查找资源registry.addResourceHandler("/image/**").addResourceLocations("file:res/image/");
// 访问/web/xxx对应去res/web下查找资源registry.addResourceHandler("/web/**").addResourceLocations("file:res/web/");
   }
}

这时,我访问http://127.0.0.1:8080/image/gz-12.png可以访问到res/image中的图片,然后访问http://127.0.0.1:8080/web/test.html可以访问到res/web中的网页。

可见,配置类提供了更加灵活的配置方式,还能够解决我们配置被其它依赖覆盖的问题。

需要注意的是,通常一个项目只能有一个类实现WebMvcConfigurer,否则会造成覆盖产生问题。除了这里配置静态资源之外,之前做用户登录的拦截器也要实现这个接口中的方法,这些方法是可以写在一个配置类中的,毕竟实现的是一个接口。

然后同样地,如果是要自定义Controller路由资源,也要和上述配置文件中的一样注意return的访问路径问题。例如我要自定义res/image中图片访问路径,由于配置了addResourceHandler("/image/**"),那对应的Controller方法如下:

@GetMapping("/rabbit-halloween")
publicStringimage() {
return"/image/gz-12.png";
}

然后访问http://127.0.0.1:8080/rabbit-halloween也可以访问到图片。

所以说如果发现配置文件配置资源路径不起作用,我们就可以删掉配置文件中的相关配置,通过编写配置类的方式来实现资源路径自定义,包括更加灵活的情况下例如需要多个访问路径对应各自不同的资源文件查找路径,也需要用到配置类方式。

3,Spring Boot的配置文件位置指定

我们也知道Spring Boot的配置文件默认是位于classpath:/application.properties,默认会被打包进jar文件。

其实我们也可以修改这个配置文件的位置。

在我们的Spring主类上加入如下注解:

@PropertySource(value={"自定义配置文件路径"})

value表示配置文件位置,也可以填多个:

@PropertySource(value={"配置1路径", "配置2路径"})

此处以我的主类全部代码为例:

packagecom.example.demo;
importorg.springframework.boot.SpringApplication;
importorg.springframework.boot.autoconfigure.SpringBootApplication;
importorg.springframework.context.annotation.PropertySource;
@SpringBootApplication@PropertySource(value={"file:self.properties"})
publicclassDemoApplication {
publicstaticvoidmain(String[] args) {
SpringApplication.run(DemoApplication.class, args);
   }
}

即定义配置文件为项目文件夹下的self.properties文件(打包后运行jar文件时的运行目录下的self.properties文件)。

4,多环境的配置文件外置方案

我们知道,默认情况下,我们可以在src/main/resources文件夹下创建多个配置文件以对应多个不同环境下的配置,方便灵活切换,例如开发-生产环境下,我们一共有三个配置文件:

application.properties
application-dev.properties
application-prod.properties

然后在主配置文件application.properties里面配置一个配置项,即可一键切换配置环境:

# 指定当前使用开发环境配置文件
spring.profiles.active=dev

我们默认的配置文件名是application,因此多环境的情况下,配置文件命名如下:

application-环境配置名.properties

主配置文件就是application.properties,在里面配置:

spring.profiles.active=环境配置名

运行时即可使用指定环境的配置文件。

这个时候想配置文件外置,如果按照上述第3部分的方法来,发现就不行了。那么多环境配置的情况下,配置文件如何外置呢?

我们需要先知道,其实Spring Boot会默认在这四个位置扫描配置文件:

file:./config/
file:./
classpath:/config/
classpath:/

我们可以指定spring.config.location属性,来实现自定义Spring Boot的配置文件扫描路径。spring.config.location属性不仅可以设定扫描指定的配置文件,还可以指定扫描指定文件夹

在我们的main方法中最开头,使用System.setProperty方法即可设定,下面给几个例子:

// 扫描项目文件夹(jar运行目录)中Resources/config目录中所有的配置文件System.setProperty("spring.config.location", "file:Resources/config/");
// 扫描项目文件夹(jar运行目录)中Resources/config/app.properties文件System.setProperty("spring.config.location", "file:Resources/config/app.properties");
// 扫描项目文件夹(jar运行目录)中Resources/config目录和Resources/config2目录中所有的配置文件System.setProperty("spring.config.location", "file:Resources/config/, file:Resources/config2/");

可见spring.config.location属性比较灵活,既可以设定文件还可以指定文件夹,注意指定的如果是文件夹,路径最后一定要以/结尾。指定多个文件或者文件夹时路径中间以英文逗号分隔。

好了,知道了spring.config.location属性,我们就知道多环境配置文件外置的方法了。例如我想把所有配置文件application.propertiesapplication-dev.propertiesapplication-prod.properties放到项目文件夹下的Resources/config目录下,那么我的完整主类代码如下:

packagecom.gitee.swsk33.test;
importorg.springframework.boot.SpringApplication;
importorg.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplicationpublicclassTestApplication {
publicstaticvoidmain(String[] args) {
// 设定配置文件扫描目录System.setProperty("spring.config.location", "file:Resources/config/");
SpringApplication.run(TestApplication.class, args);
   }
}

可见很简单,只在主方法main中最头部写了System.setProperty("spring.config.location", "file:Resources/config/");这一行代码即可完成配置。

然后我的三个配置文件放在项目文件夹下的Resources/config目录下:

网络异常,图片无法展示
|

再在主配置文件里面配置:

spring.profiles.active=dev

运行,即可使用开发环境的配置文件application-dev.properties了。

5,配置文件改名

我们也知道,Spring Boot配置文件默认名字是application.properties,Spring Boot默认情况下也是通过搜寻这个名字的文件找到配置文件的。

如果说想改配置文件的名字怎么做呢?其实除了上述直接指定配置文件路径以外,还可以修改属性spring.config.name来实现,也是使用System.setProperty方法来修改。例如在main方法最前面写上:

System.setProperty("spring.config.name", "config");

那么Spring Boot就会去搜寻名为config.properties的文件作为配置文件。

因此可见spring.config.name的默认值为application,这个值不需要写扩展名,扩展名会在Spring Boot中自动适配。

上面修改了spring.config.name属性为config,那么如果说是多环境配置,我们的其余环境的配置文件也要跟着改为如下:

config-dev.propertiesconfig-prod.properties

6,总结

Spring Boot的资源文件访问和我们普通Java程序可能有所不同,大家一定要注意资源文件配置,以及配置文件的加载。

本文参考的官方文档:

相关文章
|
4月前
|
安全 Java Apache
微服务——SpringBoot使用归纳——Spring Boot中集成 Shiro——Shiro 身份和权限认证
本文介绍了 Apache Shiro 的身份认证与权限认证机制。在身份认证部分,分析了 Shiro 的认证流程,包括应用程序调用 `Subject.login(token)` 方法、SecurityManager 接管认证以及通过 Realm 进行具体的安全验证。权限认证部分阐述了权限(permission)、角色(role)和用户(user)三者的关系,其中用户可拥有多个角色,角色则对应不同的权限组合,例如普通用户仅能查看或添加信息,而管理员可执行所有操作。
157 0
|
4月前
|
安全 Java 数据安全/隐私保护
微服务——SpringBoot使用归纳——Spring Boot中集成 Shiro——Shiro 三大核心组件
本课程介绍如何在Spring Boot中集成Shiro框架,主要讲解Shiro的认证与授权功能。Shiro是一个简单易用的Java安全框架,用于认证、授权、加密和会话管理等。其核心组件包括Subject(认证主体)、SecurityManager(安全管理员)和Realm(域)。Subject负责身份认证,包含Principals(身份)和Credentials(凭证);SecurityManager是架构核心,协调内部组件运作;Realm则是连接Shiro与应用数据的桥梁,用于访问用户账户及权限信息。通过学习,您将掌握Shiro的基本原理及其在项目中的应用。
155 0
|
3月前
|
前端开发 Java Maven
Spring 和 Spring Boot 之间的比较
本文对比了标准Spring框架与Spring Boot的区别,重点分析两者在模块使用(如MVC、Security)上的差异。Spring提供全面的Java开发基础设施支持,包含依赖注入和多种开箱即用的模块;而Spring Boot作为Spring的扩展,通过自动配置、嵌入式服务器等功能简化开发流程。文章还探讨了两者的Maven依赖、Mvc配置、模板引擎配置、启动方式及打包部署等方面的异同,展示了Spring Boot如何通过减少样板代码和配置提升开发效率。总结指出,Spring Boot是Spring的增强版,使应用开发、测试与部署更加便捷高效。
415 12
|
4月前
|
消息中间件 存储 Java
微服务——SpringBoot使用归纳——Spring Boot中集成ActiveMQ——ActiveMQ安装
本教程介绍ActiveMQ的安装与基本使用。首先从官网下载apache-activemq-5.15.3版本,解压后即可完成安装,非常便捷。启动时进入解压目录下的bin文件夹,根据系统选择win32或win64,运行activemq.bat启动服务。通过浏览器访问`http://127.0.0.1:8161/admin/`可进入管理界面,默认用户名密码为admin/admin。ActiveMQ支持两种消息模式:点对点(Queue)和发布/订阅(Topic)。前者确保每条消息仅被一个消费者消费,后者允许多个消费者同时接收相同消息。
111 0
微服务——SpringBoot使用归纳——Spring Boot中集成ActiveMQ——ActiveMQ安装
|
4月前
|
消息中间件 Java 微服务
微服务——SpringBoot使用归纳——Spring Boot中集成ActiveMQ——发布/订阅消息的生产和消费
本文详细讲解了Spring Boot中ActiveMQ的发布/订阅消息机制,包括消息生产和消费的具体实现方式。生产端通过`sendMessage`方法发送订阅消息,消费端则需配置`application.yml`或自定义工厂以支持topic消息监听。为解决点对点与发布/订阅消息兼容问题,可通过设置`containerFactory`实现两者共存。最后,文章还提供了测试方法及总结,帮助读者掌握ActiveMQ在异步消息处理中的应用。
141 0
|
4月前
|
消息中间件 网络协议 Java
微服务——SpringBoot使用归纳——Spring Boot中集成ActiveMQ——ActiveMQ集成
本文介绍了在 Spring Boot 中集成 ActiveMQ 的详细步骤。首先通过引入 `spring-boot-starter-activemq` 依赖并配置 `application.yml` 文件实现基本设置。接着,创建 Queue 和 Topic 消息类型,分别使用 `ActiveMQQueue` 和 `ActiveMQTopic` 类完成配置。随后,利用 `JmsMessagingTemplate` 实现消息发送功能,并通过 Controller 和监听器实现点对点消息的生产和消费。最后,通过浏览器访问测试接口验证消息传递的成功性。
130 0
|
4月前
|
消息中间件 Java API
微服务——SpringBoot使用归纳——Spring Boot中集成ActiveMQ—— JMS 和 ActiveMQ 介绍
本文介绍如何在Spring Boot中集成ActiveMQ,首先阐述了JMS(Java消息服务)的概念及其作为与具体平台无关的API在异步通信中的作用。接着说明了JMS的主要对象模型,如连接工厂、会话、生产者和消费者等,并指出JMS支持点对点和发布/订阅两种消息类型。随后重点讲解了ActiveMQ,作为Apache开源的消息总线,它完全支持JMS规范,适用于异步消息处理。最后,文章探讨了在Spring Boot中使用队列(Queue)和主题(Topic)这两种消息通信形式的方法。
98 0
|
XML Java 数据格式
Spring_总结_02_依赖注入
一、前言 本文承接上一节:Spring_总结_01_Spring概述 在上一节中,我们了解了Spring的最根本使命、四大原则、六大模块以及Spring的生态。 这一节我们开始了解Spring的第二大原则中的依赖注入,这属于六大模块中的核心容器部分。
1745 0
|
3天前
|
Java Spring 容器
SpringBoot自动配置的原理是什么?
Spring Boot自动配置核心在于@EnableAutoConfiguration注解,它通过@Import导入配置选择器,加载META-INF/spring.factories中定义的自动配置类。这些类根据@Conditional系列注解判断是否生效。但Spring Boot 3.0后已弃用spring.factories,改用新格式的.imports文件进行配置。
33 0
|
4月前
|
前端开发 Java 数据库
微服务——SpringBoot使用归纳——Spring Boot集成Thymeleaf模板引擎——Thymeleaf 介绍
本课介绍Spring Boot集成Thymeleaf模板引擎。Thymeleaf是一款现代服务器端Java模板引擎,支持Web和独立环境,可实现自然模板开发,便于团队协作。与传统JSP不同,Thymeleaf模板可以直接在浏览器中打开,方便前端人员查看静态原型。通过在HTML标签中添加扩展属性(如`th:text`),Thymeleaf能够在服务运行时动态替换内容,展示数据库中的数据,同时兼容静态页面展示,为开发带来灵活性和便利性。
122 0

热门文章

最新文章