从 SpringBoot 源码看资源映射原理
上边的注释写的很清楚,重写这个方法来增加一个静态资源的映射。那么具体怎么写呢?
我们再按照注释看一下 ResourceHandlerRegistry 的源码,重点看一下对这个类的注释,如下。
/* <p>To create a resource handler, use {@link #addResourceHandler(String...)} providing the URL path patterns
/ * for which the handler should be invoked to serve static resources (e.g. {@code "/resources/**"}).
大概意思就是为了创建资源的处理器,要调用 addResourceHandler 方法来提供 url 的表达式,这个方法是为了服务静态资源的(ps:英语水平也一般,了解大意即可)
然后我们去看 addResourceHandler 方法:
/**
Add a resource handler for serving static resources based on the specified URL path patterns.
The handler will be in
voked for every incoming request that matches to one of the specified
path patterns.
<p>Patterns like {@code "/static/**"} or {@code "/css/{filename:\w+\.css}"} are allowed.
See {@link org.springframework.util.AntPathMatcher} for more details on the syntax.
@return a {@link ResourceHandlerRegistration} to use to further configure the
registered resource handler
*/
public ResourceHandlerRegistration addResourceHandler(String... pathPatterns) {
ResourceHandlerRegistration registration = new ResourceHandlerRegistration(pathPatterns);
this.registrations.add(registration);
return registration;
}
我们看到这个方法执行后返回了一个新的类 ResourceHandlerRegistration
那我们再来看一下这个类中的核心方法
/**
Add one or more resource locations from which to serve static content.
Each location must point to a valid directory. Multiple locations may
be specified as a comma-separated list, and the locations will be checked
for a given resource in the order specified.
<p>For example, {{@code "/"}, {@code "classpath:/META-INF/public-web-resources/"}}
allows resources to be served both from the web application root and
from any JAR on the classpath that contains a
{@code /META-INF/public-web-resources/} directory, with resources in the
web application root taking precedence.
<p>For {@link org.springframework.core.io.UrlResource URL-based resources}
(e.g. files, HTTP URLs, etc) this method supports a special prefix to
indicate the charset associated with the URL so that relative paths
appended to it can be encoded correctly, e.g.
{@code [charset=Windows-31J]https://example.org/path}.
@return the same {@link ResourceHandlerRegistration} instance, for
chained method invocation
*/
public ResourceHandlerRegistration addResourceLocations(String... resourceLocations) {
this.locationValues.addAll(Arrays.asList(resourceLocations));
return this;
}
上边注释的大意就是增加一个或者多个静态资源路径,并举了一些例子。源码我们就看到这里。
所以我们可以像这样实现资源的映射:
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
/**
@author liumeng
@Date: 2020/9/25 09:20
@Description:
*/
@Configuration
public class SpringMvcConfig extends WebMvcConfigurationSupport {
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/**").addResourceLocations("/");
}
}
关于 SpringMVC 中的资源映射部分就介绍到这,那么我们继续来看 SpringBoot 的资源映射吧。
SpringBoot 的资源映射
===================
其实 SpringBoot 的资源映射也是一脉相承的,当我们初始化一个 SpringBoot 项目后,静态资源会默认存在 resource/static 目录中,那么 SpringBoot 的底层是怎么实现的呢,接下来我们就去源码里探索一下。
SpringBoot 的源码在 WebMvcAutoConfiguration 这个类中,我们发现了熟悉的代码:
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
} Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
if (!registry.hasMappingForPattern("/webjars/**")) {
customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/")
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl)); } String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern) .addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl)); } }
这里面我们重点看下边这部分
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern) .addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl)); }
我们看一下 getStaticPathPattern()方法的实现,发现获得的就是
/**
Path pattern used for static resources.
*/
private String staticPathPattern = "/**";
这个属性的值,默认是/**。
然后我们再看 this.resourceProperties.getStaticLocations()方法,发现获得的是
private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
而 CLASSPATH_RESOURCE_LOCATIONS 是一个常量,值如下:
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/",
"classpath:/resources/", "classpath:/static/", "classpath:/public/" };
在这里我们看到了四个值,static 就是其中一个,到这里我们就明白了 SpringBoot 的静态资源为什么会存在 resource/static 这个目录下,而且放在以上 4 个目录中是都可以读取到的。
实际上 SpringBoot 默认的静态资源是 5 个,我们再来看 getResourceLocations 方法,如下:
static String[] getResourceLocations(String[] staticLocations) {
String[] locations = new String[staticLocations.length + SERVLET_LOCATIONS.length];
System.arraycopy(staticLocations, 0, locations, 0, staticLocations.length);
System.arraycopy(SERVLET_LOCATIONS, 0, locations, staticLocations.length, SERVLET_LOCATIONS.length);
return locations;
}
可以看到,除了我们之前看到的 4 个路径,这个方法里还新增了一个 SERVLET_LOCATIONS 的路径,点进去看一下
private static final String[] SERVLET_LOCATIONS = { "/" };
发现就是一个"/"。
所以实际上 SpringBoot 的默认静态资源路径有 5 个:
"classpath:/META-INF/resources/" , "classpath:/resources/", "classpath:/static/", "classpath:/public/",“/”
自定义配置
=========
好了,到现在我们已经知道了 SpringBoot 的默认资源映射来源,那么我们如何配置自定义的资源映射路径呢?
评论