SpringBoot静态资源配置原理(源码一步步分析,详细易懂)

简介: 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();
        }
AI 代码解读

我们来看下它的参数:

第一个参数是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()));
                }

            }
        }
AI 代码解读

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()));
                }
AI 代码解读

也就是说我们访问/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()));
                }
AI 代码解读

把刚刚的前缀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/“。这就解释了静态资源路径为什么默认为这四个路径。
看到这里你有没有恍然大悟,如果文章对你有帮助就请点个赞收藏一下吧!

目录
打赏
0
0
0
0
5
分享
相关文章
springboot自动配置原理
Spring Boot 自动配置原理:通过 `@EnableAutoConfiguration` 开启自动配置,扫描 `META-INF/spring.factories` 下的配置类,省去手动编写配置文件。使用 `@ConditionalXXX` 注解判断配置类是否生效,导入对应的 starter 后自动配置生效。通过 `@EnableConfigurationProperties` 加载配置属性,默认值与配置文件中的值结合使用。总结来说,Spring Boot 通过这些机制简化了开发配置流程,提升了开发效率。
63 17
springboot自动配置原理
SpringBoot自动配置及自定义Starter
Java程序员依赖Spring框架简化开发,但复杂的配置文件增加了负担。SpringBoot以“约定大于配置”理念简化了这一过程,通过引入各种Starter并加载默认配置,几乎做到开箱即用。
120 10
SpringBoot自动配置及自定义Starter
【潜意识Java】javaee中的SpringBoot在Java 开发中的应用与详细分析
本文介绍了 Spring Boot 的核心概念和使用场景,并通过一个实战项目演示了如何构建一个简单的 RESTful API。
41 5
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)
自动装配是现在面试中常考的一道面试题。本文基于最新的 SpringBoot 3.3.3 版本的源码来分析自动装配的原理,并在文未说明了SpringBoot2和SpringBoot3的自动装配源码中区别,以及面试回答的拿分核心话术。
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)
SpringBoot配置跨模块扫描问题解决方案
在分布式项目中,使用Maven进行多模块开发时,某些模块(如xxx-common)没有启动类。如何将这些模块中的类注册为Spring管理的Bean对象?本文通过案例分析,介绍了两种解决方案:常规方案是通过`@SpringBootApplication(scanBasePackages)`指定扫描路径;推荐方案是保持各模块包结构一致(如com.xxx),利用SpringBoot默认扫描规则自动识别其他模块中的组件,简化配置。
SpringBoot配置跨模块扫描问题解决方案
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
155 14
|
3月前
|
SpringBoot入门(7)- 配置热部署devtools工具
SpringBoot入门(7)- 配置热部署devtools工具
157 1
SpringBoot入门(7)- 配置热部署devtools工具
springboot自动配置原理
启动类@SpringbootApplication注解下,有三个关键注解 (1)@springbootConfiguration:表示启动类是一个自动配置类 (2)@CompontScan:扫描启动类所在包外的组件到容器中 (3)@EnableConfigutarion:最关键的一个注解,他拥有两个子注解,其中@AutoConfigurationpackageu会将启动类所在包下的所有组件到容器中,@Import会导入一个自动配置文件选择器,他会去加载META_INF目录下的spring.factories文件,这个文件中存放很大自动配置类的全类名,这些类会根据元注解的装配条件生效,生效
springboot中路径默认配置与重定向/转发所存在的域对象
Spring Boot 提供了简便的路径默认配置和强大的重定向/转发机制,通过合理使用这些功能,可以实现灵活的请求处理和数据传递。理解并掌握不同域对象的生命周期和使用场景,是构建高效、健壮 Web 应用的关键。通过上述详细介绍和示例,相信读者能够更好地应用这些知识,优化自己的 Spring Boot 应用。
80 3
深入实践springboot实战 蓄势待发 我不是雷锋 我是知识搬运工
springboot,说白了就是一个集合了功能的大类库,包括springMVC,spring,spring data,spring security等等,并且提供了很多和可以和其他常用框架,插件完美整合的接口(只能说是一些常用框架,基本在github上能排上名次的都有完美整合,但如果是自己写的一个框架就无法实现快速整合)。
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等