SpringBoot静态资源配置原理(源码分析)

 更新时间:2021年01月14日 10:08:22   作者:MrYushiwen  
这篇文章主要介绍了SpringBoot静态资源配置原理(源码分析),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

前言:

我们都知道,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语句的内容都得到了执行,下面我们看它是怎么配置静态资规则的。

2.源码分析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秒:

在这里插入图片描述

3.源码分析默认静态资源路径的底层规则

我们在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/“。这就解释了静态资源路径为什么默认为这四个路径。

看到这里你有没有恍然大悟,

到此这篇关于SpringBoot静态资源配置原理(源码分析)的文章就介绍到这了,更多相关SpringBoot静态资源配置内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 基于BIO的Java Socket通信详解

    基于BIO的Java Socket通信详解

    这篇文章主要为大家详细介绍了基于BIO的Java Socket通信相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-12-12
  • Spring自动扫描无法扫描jar包中bean的解决方法

    Spring自动扫描无法扫描jar包中bean的解决方法

    在日常开发中往往会对公共的模块打包发布,然后调用公共包的内容。然而,最近对公司的公共模块进行整理发布后。spring却无法扫描到相应的bean,下面这篇文章主要给大家介绍了关于Spring自动扫描时无法扫描jar包中bean的解决方法,需要的朋友可以参考下。
    2017-06-06
  • springboot 整合sentinel的示例代码

    springboot 整合sentinel的示例代码

    本文主要介绍了springboot 整合sentinel的示例代码,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-02-02
  • 详解Java数据结构和算法(有序数组和二分查找)

    详解Java数据结构和算法(有序数组和二分查找)

    本篇文章主要介绍了详解Java数据结构和算法(有序数组和二分查找),具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-09-09
  • springboot+jwt+微信小程序授权登录获取token的方法实例

    springboot+jwt+微信小程序授权登录获取token的方法实例

    本文主要介绍了springboot+jwt+微信小程序授权登录获取token的方法实例,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03
  • 吊打Java面试官之Lambda表达式 Stream API

    吊打Java面试官之Lambda表达式 Stream API

    这篇文章主要介绍了吊打Java之jdk8的新特性包括Lambda表达式、函数式接口、Stream API全面刨析,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-09-09
  • java如何将pdf转换成image

    java如何将pdf转换成image

    这篇文章主要为大家详细介绍了java如何将pdf转换成image,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-05-05
  • 彻底搞懂Java多线程(三)

    彻底搞懂Java多线程(三)

    这篇文章主要给大家介绍了关于Java面试题之多线程和高并发的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用java具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2021-07-07
  • Spring在多线程下保持事务的一致性的方法实现

    Spring在多线程下保持事务的一致性的方法实现

    当Spring在多线程环境下运行时,确保事务一致性是非常重要的,本文主要介绍了Spring在多线程下保持事务的一致性的方法实现,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧
    2024-01-01
  • 使用synchronized实现一个Lock代码详解

    使用synchronized实现一个Lock代码详解

    这篇文章主要介绍了使用synchronized实现一个Lock代码详解,具有一定借鉴价值,需要的朋友可以参考下。
    2017-12-12

最新评论