前言:
我们都知道,SpringBoot启动会默认加载很多xxxAutoConfiguration类(自动配置类)
其中SpringMVC的大都数功能都集中在WebMvcAutoConfiguration类中,根据条件ConditionalOnxxx注册类对象;WebMvcAutoConfiguration满足以下ConditionalOnxxx条件,类是生效的,并把其对象注册到容器中。

那WebMvcAutoConfiguration生效给容器中配置了什么呢?
WebMvcAutoConfigurationAdapter静态内部类

一、配置文件前缀

我们来看WebMvcAutoConfiguration类中的WebMvcAutoConfigurationAdapter静态内部类:

这是一个配置类,配置文件的属性和xxx进行了绑定。
再看@EnableConfigurationProperties({WebMvcProperties.class, ResourceProperties.class, WebProperties.class})
我们来看当中的WebMvcProperties、ResourceProperties和WebProperties的字节码文件
分别点进这三个类的字节码文件中:

可以看到:
WebMvcProperties它是与配置文件前缀spring.mvc相关联的。
ResourceProperties它是与配置文件前缀spring.resources相关联。
WebProperties它是与配置文件前缀spring.web相关联。

二、只有一个有参构造器

WebMvcAutoConfigurationAdapter静态内部配置类只有一个有参数的构造器,那它会带来什么特性呢?
它的有参构造器中所有参数的值都会从容器中确定

public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties, WebProperties webProperties, WebMvcProperties mvcProperties, ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider, ObjectProvider<WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider, ObjectProvider<DispatcherServletPath> dispatcherServletPath, ObjectProvider<ServletRegistrationBean<?>> servletRegistrations) {this.resourceProperties = (Resources)(resourceProperties.hasBeenCustomized() ? resourceProperties : webProperties.getResources());this.mvcProperties = mvcProperties;this.beanFactory = beanFactory;this.messageConvertersProvider = messageConvertersProvider;this.resourceHandlerRegistrationCustomizer = (WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer)resourceHandlerRegistrationCustomizerProvider.getIfAvailable();this.dispatcherServletPath = dispatcherServletPath;this.servletRegistrations = servletRegistrations;this.mvcProperties.checkConfiguration();}

我们来看下它的参数:

  • 第一个参数是ResourceProperties resourceProperties 就是我们上面提到的@EnableConfigurationProperties({WebMvcProperties.class,ResourceProperties.class,
    WebProperties.class})中注册开启的第二个类,获取和spring.resources绑定的所有的值的对象
  • 第二个参数是WebProperties webProperties 就是我们上面提到的@EnableConfigurationProperties({WebMvcProperties.class,
    ResourceProperties.class,WebProperties.class})中注册开启的第三个类,获取和spring.web绑定的所有的值的对象
  • 第三个参数是WebMvcProperties mvcProperties 就是我们上面提到的@EnableConfigurationProperties({WebMvcProperties.class,ResourceProperties.class,
    WebProperties.class})中注册开启的第一个类,获取和spring.mvc绑定的所有的值的对象
  • 第四个参数是ListableBeanFactory beanFactory ,这个是Spring的beanFactory,也就是我们的容器。
  • 第五个参数是ObjectProvider
    messageConvertersProvider,找到所有的HttpMessageConverters
  • 第六个参数是ObjectProvider<WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider,找到资源处理器的自定义器
  • 第七个参数是ObjectProvider dispatcherServletPath,相当于找dispatcherServlet能处理的路径
  • 第八个参数是ObjectProvider<ServletRegistrationBean<?>> servletRegistrations
    ,给应用注册原生的Servlet、Filter等等

构造器初始化后,我们已经把所有的东西从容器中拿到了

三、源码分析addResourceHandlers方法

所有的资源处理默认规则都在addResourceHandlers方法中,如下:

public void addResourceHandlers(ResourceHandlerRegistry registry) {if (!this.resourceProperties.isAddMappings()) {logger.debug("Default resource handling disabled");} else {Duration cachePeriod = this.resourceProperties.getCache().getPeriod();CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();if (!registry.hasMappingForPattern("/webjars/**")) {this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{"/webjars/**"}).addResourceLocations(new String[]{"classpath:/META-INF/resources/webjars/"}).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl).setUseLastModified(this.resourceProperties.getCache().isUseLastModified()));}String staticPathPattern = this.mvcProperties.getStaticPathPattern();if (!registry.hasMappingForPattern(staticPathPattern)) {this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{staticPathPattern}).addResourceLocations(WebMvcAutoConfiguration.getResourceLocations(this.resourceProperties.getStaticLocations())).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl).setUseLastModified(this.resourceProperties.getCache().isUseLastModified()));}}}
  1. 禁用掉静态资源的路径映射
    我们打上断点看它的默认规则是怎么起作用的,首先调用resourcePropertoes的isAddMappings()方法

判断this.resourcePropertoes的isAddMappings()方法是不是不为true,

  • this.resourcePropertoes我们刚才在2中讲构造器时讲到的ResourceProperties
    resourceProperties 就是我们上面提到的@EnableConfigurationProperties({WebMvcProperties.class,
    ResourceProperties.class, WebProperties.class})中注册开启的第二个类,获取和spring.resources绑定的所有的值的对象
  • isAddMappings()方法返回的是this.addMappings的值,如下:

也就是说我们可以通过设置addMappings的值是false还是true来让这个if语句是否执行
我们可以在配置文件中进行设置:

默认它是true,如果是false,那么他就进入if语句中,执行logger.debug("Default resource handling disabled");后结束该方法,else中的所有配置都不生效

else中的什么配置/webjars/**去哪找等等一些规则都不生效了。
也就是说我们通过设置add-mappings: false 来禁用掉了静态资源的路径映射。
禁用后所有的静态资源都访问不了了。

addMappings的值如果是true,那么他就不会进入if语句中,而是进入到else语句中,那么else语句的内容都得到了执行,下面我们看它是怎么配置静态资规则的。

  1. 源码分析webjars的底层规则
    进入到else语句中,第一行是Duration cachePeriod = this.resourceProperties.getCache().getPeriod();,它从resourceProperties里面获取到关于缓存的相关值。我们在yaml配置文件中配置一下这个值:

缓存时间是以秒为单位的,如下:

意思就是我们所有的静态资源默认可以缓存存储多少秒

我们debug接着往下走,看到cachePeriod中取到了刚刚yaml中设置的6666,以后我们的浏览器就会把我们的静态资源缓存6666秒:

debug接着往下走,我们到了注册"/webjars/**"这个规则的地方:

if (!registry.hasMappingForPattern("/webjars/**")) {this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{"/webjars/**"}).addResourceLocations(new String[]{"classpath:/META-INF/resources/webjars/"}).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl).setUseLastModified(this.resourceProperties.getCache().isUseLastModified()));}

也就是说我们访问/webjars下面的所有请求都找我们的classpath:/META-INF/resources/webjars/路径,其中还设置了其静态资源的缓存时间为6666秒。
拿jquery来举例,为什么我们导入jquery之后,我们只需要访问/webjars/jquery/3.5.1/jquery.js就能够访问到/META-INF/resources/webjars/jquery/3.5.1/jquery.js,如下:

其缓存时间也可以在浏览器中看到为6666秒:

  1. 源码分析默认静态资源路径的底层规则
    我们在else里面接着往下debug,接着我们用mvcProperties属性调用其getStaticPathPattern()方法
  • this.mvcProperties我们刚才在2中讲构造器时讲到的WebMvcProperties mvcProperties 就是我们上面提到的@EnableConfigurationProperties({WebMvcProperties.class, ResourceProperties.class, WebProperties.class})中注册开启的第一个类,获取和spring.mvc绑定的所有的值的对象
  • getStaticPathPattern()方法,这个方法返回的是staticPathPattern的值,如下:

staticPathPattern的这个值可以在我们的配置文件中进行配置,它的默认值是/**,如下:

我们也可以把前缀配置成/resource/**,如下:

debug接着往下走,接下来调用的方法与上面的webjars是一样的方法,只不过参数有所不同:

接下来我们具体看代码:

String staticPathPattern = this.mvcProperties.getStaticPathPattern();if (!registry.hasMappingForPattern(staticPathPattern)) {this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{staticPathPattern}).addResourceLocations(WebMvcAutoConfiguration.getResourceLocations(this.resourceProperties.getStaticLocations())).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl).setUseLastModified(this.resourceProperties.getCache().isUseLastModified()));}

把刚刚的前缀staticPathPattern得到后作为实参传入hasMappingForPattern方法中,注册前缀这个规则,刚刚我们在yaml中设置了前缀为/resource/**,也就是说我们访问/resource/**下面的所有请求都找我们的this.resourceProperties.getStaticLocations() 路径,其中也设置了其静态资源的缓存时间为6666秒。
this.resourceProperties.getStaticLocations()方法返回的值是什么呢?我们点进去看一下:

this.resourceProperties.getStaticLocations()返回的是this.staticLocations,这个staticLocations定义如下:

可以看到它是一个字符串数组,在无参构造器中进行了初始化,初始化的值是CLASSPATH_RESOURCE_LOCATIONS常量,常量的值为:

  • “classpath:/META-INF/resources/”,
  • “classpath:/resources/”,
  • “classpath:/static/”,
  • "classpath:/public/“

这就解释了静态资源路径为什么默认为这四个路径。

四、欢迎页的处理规则

HandlerMapping:处理器映射。保存了每一个Handler能处理哪些请求。    @Beanpublic WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),this.mvcProperties.getStaticPathPattern());welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations());return welcomePageHandlerMapping;}WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders,ApplicationContext applicationContext, Optional<Resource> welcomePage, String staticPathPattern) {if (welcomePage.isPresent() && "/**".equals(staticPathPattern)) {//要用欢迎页功能,必须是/**logger.info("Adding welcome page: " + welcomePage.get());setRootViewName("forward:index.html");}else if (welcomeTemplateExists(templateAvailabilityProviders, applicationContext)) {// 调用Controller  /indexlogger.info("Adding welcome page template: index");setRootViewName("index");}
}

SpringBoot静态资源配置原理(源码)相关推荐

  1. Alian解读SpringBoot 2.6.0 源码(十):启动流程之自动装配原理

    目录 一.背景 1.1.主类的加载 1.2.后置处理器的获取 二.配置类后处理器 2.1.获取配置类 2.2. 2.3.解析主类 2.3.1.整体解析过程 2.3.2.核心解析过程 2.3.3.延迟导 ...

  2. SpringBoot Jar包构建源码分析

    我们知道,SpringBoot仅凭一个Jar包就能将我们构建的整个工程跑起来,如果你也想知道这个能跑起来的jar内部结构是如何构建出来的,请耐心读完本篇,本篇内容可能有点多,但包你有收获.如果读完没有 ...

  3. Alian解读SpringBoot 2.6.0 源码(七):启动流程分析之准备应用上下文

    目录 一.背景 1.1.run方法整体流程 1.2.本文解读范围 二.准备应用上下文 2.1.整体流程 2.2.设置环境 2.3.应用上下文进行后置处理 2.4.应用所有初始化器 2.5.发布应用上下 ...

  4. Alian解读SpringBoot 2.6.0 源码(六):启动流程分析之创建应用上下文

    目录 一.背景 1.1.run方法整体流程 1.2.本文解读范围 二.创建应用上下文 2.1.初始化入口 2.2.初始化AbstractApplicationContext 2.3.初始化Generi ...

  5. Alian解读SpringBoot 2.6.0 源码(八):启动流程分析之刷新应用上下文(下)

    目录 一.背景 1.1.刷新的整体调用流程 1.2.本文解读范围 二.初始化特定上下文子类中的其他特殊bean 2.1.初始化主体资源 2.2.创建web服务 三.检查监听器bean并注册它们 四.实 ...

  6. Alian解读SpringBoot 2.6.0 源码(九):启动流程分析之应用上下文刷新后处理(启动完成事件,Runner运行器,就绪事件)

    目录 一.背景 1.1.run方法整体流程 1.2.本文解读范围 二.应用上下文刷新后置处理 三.时间信息.输出日志记录执行主类名 四.发布应用上下文启动完成事件 4.1.ApplicationSta ...

  7. SpringBoot物流管理项目(源码下载)

    SpringBoot物流管理项目(源码下载) https://mp.weixin.qq.com/s?__biz=Mzg3NjI4OTg1Mw==&mid=100016342&idx=1 ...

  8. 基于javaweb框架的springboot mybatis宠物商城源码含论文设计文档

    在互联网高速发展.信息技术步入人类生活的情况下,电子贸易也得到了空前发展.网购几乎成为了人人都会进行的活动.近几年来,养宠物更是成为人们生活中重要的娱乐内容之一, 人们越来越多的讲感情也寄托给了宠物, ...

  9. ava Springboot养老院信息管理系统源码

    Java Springboot养老院信息管理系统源码 开发语言 : JAVA 数据库 : MySQL 开发工具 : IDEA 源码类型 : WebForm 开源地址:http://www.taoydm ...

最新文章

  1. 分析Booking的150种机器学习模型,我总结了六条成功经验
  2. Eclipse中dropins干什么用的文件夹啊?
  3. 机器学习笔记: Discriminative vs Generative Models
  4. 【LeetCode】70 - Climbing Stairs
  5. Eclipse中svn插件:Subclipse插件安装
  6. jQuery的ajax()、post()方法提交数组,参数[] 问题
  7. Servlet读取文件的最好的方式
  8. WKWebview的内存问题
  9. PCB特征阻抗计算工具——Polar SI9000软件下载与安装教程
  10. shufflenetv1详解
  11. java ssh框架是什么_JAVA SSH框架介绍
  12. 天池大数据竞赛口碑商家客流量预测——时序预测(python-numpy-arima)
  13. 14个创意lut电影大片级别调色预设
  14. weblogic子服务器显示状态RESUMING,长期等待状态
  15. 存储过程中的关键字--AS、GO的含义
  16. 解决Pixel手机时间不能自动同步
  17. 运维-jenkins发版执行远程脚本连不上ssh
  18. android第三方视频解码器Vitamio SDK使用后的感觉(2014.03.11)
  19. Kafka,amq,RabbitMq
  20. esxi硬盘分区格式_Esxi 虚拟机硬盘LVM分区扩容(图文)

热门文章

  1. 数据算法 --hadoop/spark数据处理技巧 --(二次排序问题和TopN问题)
  2. AutoMapper入门使用
  3. LinuxMint(Ubuntu)安装文泉驿家族黑体字
  4. python入门编程之mysql编程
  5. net::ERR_ABORTED ,引入js文件出现报错的解决方法
  6. cas中总是得不到返回的属性
  7. Python学习-基础篇4 模块与包与常用模块
  8. 图片保存路径更改 python
  9. 吴恩达 coursera ML 第二课总结+作业答案
  10. R学习笔记:运行时间记录