40.1 如何保存静态资源

在 Spring Boot 中,如果我们是从 https://start.spring.io 或者使用 IntelliJ IDEA 中的 Spring Boot 初始化工具创建的项目,默认都会存在 resources/static 目录。你应该也知道静态资源只要放到这个目录下就可以直接访问,除了这里还有没有其他可以放静态资源的位置呢?为什么放在这里就能直接访问了呢?

40.1.1 静态资源目录整体规划

在 Spring Boot 中默认情况下,一共有5个位置可以放静态资源:

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

这里前四个目录好理解,分别对应了 resources 目录下不同的目录,第5个 / 相对比较特别。在 Spring Boot 项目中默认是没有 webapp 这个目录的(当然需要使用JSP等场景下也可以自己添加),这里第5个 / 其实就是表示 webapp 目录中的静态资源也不被拦截。如果同一个文件分别出现在五个目录下,那么优先级也是按照上面列出的顺序。

虽然有5个存储目录,除了第5个用的较少外,其他四个系统默认创建了 classpath:/static/ , 你只需要将的静态资源放到这个目录下,、不需要额外去创建其他目录。例如在 classpath:/static/ 目录下放了一张名为1.png 的图片,那么的访问路径是:

  1. http://localhost:8080/1.png

需要注意的是,请求地址中并不需要 static,因为在路径映射中已经自动的添加上了/static了。

如果你不想将资源放在系统默认的这五个位置上,也可以自定义静态资源位置和映射。自定义的方式有两种,可以通过 application.properties 来定义,也可以在 Java 代码中来定义。

40.1.2 在配置文件中定义静文文件路径

在配置文件中定义的方式比较简单:

  1. spring:
  2. resources:
  3. static-locations: classpath:/
  4. mvc:
  5. static-path-pattern : /**

第一行配置表示定义资源位置,第二行配置表示定义请求 URL 规则。以上文的配置表示可以将静态资源放在 resources目录下的任意地方,我们访问的时候当然也需要写完整的路径,例如在resources/static目录下有一张名为1.png 的图片,那么访问路径就是 http://localhost:8080/static/1.png 。注意此时的static不能省略。

40.1.3 在代码中定义静态文件路径

当然,在Spring Boot中我们也可以通过 Java代码来自定义,方式和 Java 配置的 SSM 比较类似,如下:

  1. @Configuration
  2. public class WebMVCConfig implements WebMvcConfigurer {
  3. @Override
  4. public void addResourceHandlers(ResourceHandlerRegistry registry) {
  5. registry.addResourceHandler("/**").addResourceLocations("classpath:/aaa/");
  6. }
  7. }

40.2 Spring Boot 热部署

在实际开发过程中,每次修改代码就得将项目重启,重新部署,对于一些大型应用来说,重启时间需要花费大量的时间成本。而SpringBoot中启用热部署后就变成了非常简单的一件事,因为SpringBoot为我们提供了一个非常方便的工具 spring-boot-devtools,我们只需要把这个工具引入到工程。

40.2.1 添加依赖

添加 spring-boot-devtools 依赖

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-devtools</artifactId>
  4. <scope>runtime</scope>
  5. <optional>true</optional>
  6. </dependency>

40.2.2 增加配置参数

在application.yml中配置一下devtools

  1. spring:
  2. devtools:
  3. restart:
  4. enabled: true #设置开启热部署
  5. additional-paths: src/main/java #重启目录
  6. exclude: WEB-INF/**
  7. freemarker:
  8. cache: false #页面不加载缓存,修改即时生效

devtools会监听classpath下的文件变动,并且会立即重启应用(发生在保存时机)。因为其采用的虚拟机机制,该项重启是很快的。项目重启后会清空session中的值,也就是如果有用户登陆的话,项目重启后需要重新登陆。

默认情况下,/META-INF/maven,/META-INF/resources,/resources,/static,/templates,/public这些文件夹下的文件修改不会使应用重启,但是会重新加载(devtools内嵌了一个LiveReload server,当资源发生改变时,浏览器刷新)。

40.2.3 修改 IDEA 的设置

当我们修改了类文件后IDEA 并不会自动编译,修改修改idea设置。

修改项目的 Build Project automatically 设置
image.png

Windows 下按ctrl + shift + alt + / (Mac 系统下是 shift + option + comman + /),选择Registry,勾上 Compiler autoMake allow when app running
image.png
image.png

40.2.4 测试

  • 修改类–>保存:应用会重启
  • 修改配置文件–>保存:应用会重启
  • 修改页面–>保存:应用不会重启,但会重新加载,页面会刷新

40.2.5 热部署原理的讨论

Spring Boot 中热部署最最关键的原理就是两个不同的 classloader

  • base classloader
  • restart classloader

其中 base classloader 用来加载那些不会变化的类,例如各种第三方依赖。restart classloader 则用来加载那些会发生变化的类,例如你自己写的代码。Spring Boot 中热部署的原理就是当代码发生变化时,base classloader 不变,而 restart classloader 则会被废弃,被另一个新的 restart classloader 代替。在整个过程中,因为只重新加载了变化的类,所以启动速度要被重启快。

但是有另外一个问题,就是如何处理静态资源文件。

使用 devtools 的默认情况下,当静态资源发生变化时并不会触发项目重启。虽然我们可以通过配置解决这一问题,但是没有必要。因为静态资源文件发生变化后不需要编译,按理说保存后刷新下就可以访问到了。那么如何才能实现静态资源变化后,不编译就能自动刷新呢? LiveReload 可以帮助我们实现这一功能!

40.3 LiveReload

devtools 中默认嵌入了 LiveReload 服务器,利用 LiveReload 可以实现静态文件的热部署,LiveReload 可以在资源发生变化时自动触发浏览器更新,LiveReload 支持 Chrome、Firefox 以及 Safari 。以 Chrome 为例,在 Chrome 应用商店搜索 LiveReload ,结果如下图:
image.png
将第一个搜索结果添加到 Chrome 中,添加成功后,在 Chrome 右上角有一个 LiveReload 图标
image.png
在浏览器中打开项目的页面,然后点击浏览器右上角的 LiveReload 按钮,打开 LiveReload 连接。

注意:LiveReload 是和浏览器选项卡绑定在一起的,在哪个选项卡中打开了 LiveReload,就在哪个选项卡中访问页面,这样才有效果。


配置好以后,随便在 resources/static 目录下添加一个静态 html 页面,然后启动 Spring Boot 项目,在打开了 LiveReload 的选项卡中访问 html 页面。访问成功后,我们再去手动修改 html 页面代码,修改成功后,回到浏览器,不用做任何操作,就会发现浏览器自动刷新了,页面已经更新了。

整个过程中,我的 Spring Boot 项目并没有重启

如果开发者安装并且启动了 LiveReload 插件,同时也添加了 devtools 依赖,但是却并不想当静态页面发生变化时浏览器自动刷新,那么可以在 application.yaml 中添加如下代码进行配置:

  1. spring:
  2. devtools:
  3. livereload:
  4. enabled: false

建议开发者使用 LiveReload 策略而不是项目重启策略来实现静态资源的动态加载,因为项目重启所耗费时间一般来说要超过使用LiveReload 所耗费的时间。

Firefox 也可以安装 LiveReload 插件,装好之后和 Chrome 用法基本一致

40.4 进一步的讨论

在写了那么多的文字之后,这里不得不遗恨的指出,Spring Boot热部署是一个比较鸡肋的功能,对于一个有随手用热键执行保存代码的好习惯的人来说,每次保存都重新加载是一个很恼人的事情,尤其是代码还没有完成,甚至有错误的时候。所以,这个功能似乎不必急于使用。至于生产环境中的热部署,那是属于类似 Tomcat 这样的应用服务器的配置管理范围的工作了。

版权说明:本文由北京朗思云网科技股份有限公司原创,向互联网开放全部内容但保留所有权力。