一、简述
问题
web开发有很多静态资源,比如html、图片、css等。
以前的传统的spring项目(比如ssm框架的项目)里面有一个webapp目录,只要把静态资源放到该目录下就可以直接访问,但是SpringBoot工程没有这个目录,应该如何处理?
解决
Spring Boot 默认为我们提供了静态资源处理,使用 WebMvcAutoConfiguration 中的配置各种属性
二、读源码
之前在我们ssm项目用tomcat启动项目时,会默认加载很多配置文件,控制台能看到在读很多文件。
同理,springboot启动时,会默认加载很多xxxAutoConfiguration类(自动配置类)。
其中一个自动配置类:WebMvcAutoConfiguration,集中了大部分springMVC的功能
基于 springboot2.x
WebMvcAutoConfiguration
1.1如何找WebMvcAutoConfiguration
首先找到springboot启动类
@EnableAutoConfiguration
上一步点进来后可以看到很多自动配置类
往下滑,可以看到WebMvcAutoConfiguration
最终定位:
1.2 注册到容器中(ConditionalOnXXX)
WebMvcAutoConfiguration满足以下ConditionalOnxxx条件,类是生效的,并把其对象注册到容器中。
1.3 内部静态类 WebMvcAutoConfigurationAdapter
这是一个配置类,配置文件的属性和xxx进行了绑定
1.3.1 注解@EnableConfigurationProperties
WebMvcAutoConfigurationAdapter上方进行了注解@EnableConfigurationProperties
这个注解的作用是:使使用 @ConfigurationProperties 注解的类生效
@EnableConfigurationProperties,包含了三个类WebMvcProperties,ResourceProperties,WebProperties ,点进去会发现这三个类都用 @ConfigurationProperties 进行了注解
因此,WebMvcAutoConfigurationAdapter作用之一,就是使这三个类中进行声明了前缀的配置文件都生效
来看下三个类
1)WebMvcProperties
2)ResourceProperties
3)WebProperties
WebMvcProperties它是与配置文件前缀spring.mvc相关联的。
ResourceProperties它是与配置文件前缀spring.resources相关联。
WebProperties它是与配置文件前缀spring.web相关联。
1.3.2 构造器
构造器初始化后,我们可以从容器中拿到 配置文件的内容
主要是以下三个参数,构造器初始化后,可以拿到这三组配置文件的内容
ResourceProperties resourceProperties, WebProperties webProperties, WebMvcProperties mvcProperties
其他参数如要深究可以自行研究,这里没有展开了解
1.3.3 实现接口
WebMvcAutoConfigurationAdapter 是个适配器,实现了 WebMvcConfigurer 和 ServletContextAware 接口方法
WebMvcConfigurer 接口:
先来看下 WebMvcConfigurer 常用方法
/** 拦截器配置 */
void addInterceptors(InterceptorRegistry var1);
/** 视图跳转控制器 */
void addViewControllers(ViewControllerRegistry registry);
/**静态资源处理 */
void addResourceHandlers(ResourceHandlerRegistry registry);
/** 默认静态资源处理器 */
void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer);
/** 这里配置视图解析器 */
void configureViewResolvers(ViewResolverRegistry registry);
/** 配置内容裁决的一些选项*/
void configureContentNegotiation(ContentNegotiationConfigurer configurer);
/** 解决跨域问题 **/
public void addCorsMappings(CorsRegistry registry) ;
1.3.4 方法 addResourceHandlers
WebMvcAutoConfigurationAdapter 重写实现了方法 addResourceHandlers,这个方法是 静态资源进行处理的。
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//this.resourceProperties.isAddMappings()如果对resourceProperties进行配置,那么就会禁用默认资源处理
//resourceProperties.isAddMappings() 默认为true
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
} else {
this.addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
this.addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
registration.addResourceLocations(this.resourceProperties.getStaticLocations());
if (this.servletContext != null) {
ServletContextResource resource = new ServletContextResource(this.servletContext, "/");
registration.addResourceLocations(new Resource[]{resource});
}
});
}
}
1)先看 if 语句块
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
}
resourceProperties.isAddMappings() 默认为true
如果isAddMappings为false,就打印默认资源映射路径失效了。
isAddMappings方法其实就是返回一个addMappings变量(在WebProperties中)
addMappings的含义就是运行访问静态资源,如果你设置成false,就是禁用所有静态资源映射
2)再看ELSE 的webjars路径
this.addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
结论:
所有 /webjars/ ,都去 classpath:/META-INF/resources/webjars/ 找资源;**
webjars:以jar包的方式引入静态资源;
网址:https://www.webjars.org/
在webjars的官网里面找出对应资源的pom依赖,添加到pom文件中
<!-- jquery的webjar-->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.3.1</version>
</dependency>
举例:http://localhost:8888/webjars/jquery/3.3.1/jquery.js
3)再看 ELSE 的 mvcProperties 和 resourceProperties 文件路径
this.addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
registration.addResourceLocations(this.resourceProperties.getStaticLocations());
if (this.servletContext != null) {
ServletContextResource resource = new ServletContextResource(this.servletContext, "/");
registration.addResourceLocations(new Resource[]{resource});
}
});
结论:
所有 / 都去默认四个地址下找资源
四个地址:
classpath:/META-INF/resources/,
classpath:/resources/,
classpath:/static/,
classpath:/public/**
mvcProperties
this.mvcProperties.getStaticPathPattern()
mvcProperties是WebMvcProperties类的对象,查看WebMvcProperties 属性staticPathPattern的值是 /**
因此,我们访问静态资源的方式,是根目录/xxx(静态资源文件名),举例:http://localhost:8888/public.js
resourceProperties
resourceProperties的值在构造器初始化时设定了,来源于 ResourceProperties resourceProperties, WebProperties webProperties(前缀spring.resources和前缀spring.web配置文件)
可以看到resourceProperties 是Resources类对象,
Resources 构造器进行初始化时,初始化了一个参数:CLASSPATH_RESOURCE_LOCATIONS(静态资源地址)
可以看到 这个参数(静态资源地址)的值有四个:
classpath:/META-INF/resources/,
classpath:/resources/,
classpath:/static/,
classpath:/public/
因此我们可以在这个四个地址底下放静态资源,我们可以直接通过 /**方式直接访问
三、结论
1、所有 /webjars/ ,都去 classpath:/META-INF/resources/webjars/ 找资源;
举例:http://localhost:8888/webjars/jquery/3.3.1/jquery.js
2、所有 / 都去默认四个地址下找资源
四个地址:
classpath:/META-INF/resources/,
classpath:/resources/,
classpath:/static/,
classpath:/public/
访问优先级 从高到低
举例:http://localhost:8888/public.js
四、实践结论
1、实践 /webjars/**
1)去webjars官网找 jquery 最近版本的pom依赖
2)将 依赖加入我们springboot项目的pom.xml 文件
3)启动springboot项目,访问:
http://localhost:7029/webjars/jquery/3.6.0/jquery.js
成功
2、实践 /** 及优先级
1)在resources下 建立四个路径,分别建立 同名文件 a.js ,js文件的内容如图:
启动项目,访问:
classpath:/META-INF/resources/, 路径优先级最高,文件最先被访问到
2)删掉classpath:/META-INF/resources/下的文件,再启动项目访问
classpath:/resources/, 优先级第2
3)删掉classpath:/resources/下的文件,再启动项目访问
classpath:/static/,优先级第3
classpath:/public/ 优先级最后
五、参考资料
找到了springboot1.x 的源码解读,跟 spring2.x 有点差别,但是核心代码和逻辑没有变
https://blog.csdn.net/cristianoxm/article/details/112639177