SpringBoot当然可以存放页面、图片等静态资源,但这些静态资源放在哪个文件中,又如何去访问它们呢?可以用源码来解读:
先进入WebMvcAutoConfiguration类中,这个类包含了SpringMVC的web配置,其中有一个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));
}
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));
}
}
}
可以看到首先调方法isAddMappings()判断是否做了新增映射,点进这个方法:
public boolean isAddMappings() {
return this.addMappings;
}
这个方法属于ResourceProperties类,在看这个类的属性与构造:
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/"};
private String[] staticLocations;
private boolean addMappings;
private final ResourceProperties.Chain chain;
private final ResourceProperties.Cache cache;
public ResourceProperties() {
this.staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
this.addMappings = true;
this.chain = new ResourceProperties.Chain();
this.cache = new ResourceProperties.Cache();
}
发现在构造ResourceProperties类时addMappings属性就已经置为了true,所以之前WebMvcAutoConfiguration类的addResourceHandlers方法直接进入else代码块,并且registry调用addResourceHandler()新增了一个资源处理器,所有的/webjars/**都要去classpath:/META-INF/resources/webjars/这个路径下找资源,webjars本质就是以jar包的方式引入静态资源,可以引入
webjars中的jquery依赖:
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.3.1</version>
</dependency>
导入依赖后发现在外部包中存在:
再回到ResourceProperties类中,ResourceProperties类中的第一个属性CLASSPATH_RESOURCE_LOCATIONS:
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/"};
与ResourceProperties类中appendSlashIfNecessary()方法:
private String[] appendSlashIfNecessary(String[] staticLocations) {
String[] normalized = new String[staticLocations.length];
for(int i = 0; i < staticLocations.length; ++i) {
String location = staticLocations[i];
normalized[i] = location.endsWith("/") ? location : location + "/";
}
return normalized;
}
表明有五个路径指向可以存放静态资源的位置,第五个是项目根目录下:
- “classpath:/META-INF/resources/”
- “classpath:/resources/”
- “classpath:/static/”
- “classpath:/public/”
- “/”
而且访问优先级先从 classpath:/resources/ ==> classpath:/static/ ==> “classpath:/public/”
静态资源的存放路径清楚后,可以看到WebMvcAutoConfiguration类中有welcomePageHandlerMapping()欢迎页面的映射方法:
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext, FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(new TemplateAvailabilityProviders(applicationContext), applicationContext, this.getWelcomePage(), this.mvcProperties.getStaticPathPattern());
welcomePageHandlerMapping.setInterceptors(this.getInterceptors(mvcConversionService, mvcResourceUrlProvider));
return welcomePageHandlerMapping;
}
里面有一个getWelcomePage()方法,再点进去:
private Optional<Resource> getWelcomePage() {
String[] locations = WebMvcAutoConfiguration.getResourceLocations(this.resourceProperties.getStaticLocations());
return Arrays.stream(locations).map(this::getIndexHtml).filter(this::isReadable).findFirst();
}
private Resource getIndexHtml(String location) {
return this.resourceLoader.getResource(location + "index.html");
}
getWelcomePage()方法当中调用了getIndexHtml()方法,获取index页面,可得出结论:静态资源下的所有index页面被/**所映射,在浏览器访问localhost:8080/后会直接跳转到静态文件目录下的index.html。
如果在全局配置当中配置了:
spring.resources.static-locations=classpath:xxx
默认的配置全部失效,只认自己配置的静态资源目录。