Spring Boot在引入了Web模块后,就会在启动的时候自动加载与Web有关的配置,所有的配置内容可在
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration中查看。下面要说的是有关静态资源加载的逻辑。首先看一下这个类
org.springframework.boot.autoconfigure.web.ResourceProperties
@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
public class ResourceProperties {
可以看到这个类是在加载配置文件中有关静态资源的参数,比如缓存时间等。参数前缀是spring.resources
。
在WebMvcAutoConfiguration这个类中,可以找到addResourceHandlers这个资源映射方法:
@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));
}
}
在其中可以看到,所有包含有/webjars/**
的访问路径若没有处理器处理的话都会去classpath:/META-INF/resources/webjars/
这个目录下寻找资源。
Webjars官网。
WebJars是将Web前端js和CSS等资源打包成Java的Jar包,这样在Web开发中我们可以借助Maven对这些依赖库进行管理,保证这些Web资源版本唯一性,比如jQuery、Bootstrap等。
我们在项目的pom文件引入jquery.jar:
<dependency>
<groupId>org.webjarsgroupId>
<artifactId>jqueryartifactId>
<version>3.3.1version>
dependency>
可以展开看一下jquery-3.3.1.jar的资源结构:
可以看到,所有的资源都在classpath:/META-INF/resources/webjars/这个目录下。我们启动tomcat访问一下
localhost:8080/webjars/jquery/3.3.1/jquery.js
看看能否访问的到!
很顺利的访问出来了,说明上面说的所有包含有/webjars/**
的访问路径都会去classpath:/META-INF/resources/webjars/
这个目录下寻找资源是没错的。
接着看一下这个方法的后半部分的代码:
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();
这段代码得到的是"/**"
这个字符串,也就是说,你访问的任何路径若是没有处理器来处理的话 ,默认会去这里来找资源:this.resourceProperties.getStaticLocations()
,即:
@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
public class ResourceProperties {
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/META-INF/resources/", "classpath:/resources/",
"classpath:/static/", "classpath:/public/" };
/**
* Locations of static resources. Defaults to classpath:[/META-INF/resources/,
* /resources/, /static/, /public/].
*/
private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
"classpath:/META-INF/resources/",
"classpath:/resources/",
"classpath:/static/",
"classpath:/public/"
我们可以尝试在项目类路径下分别建立这些文件夹,并放入一些静态资源以作访问:
在浏览器中依次访问:http://localhost:8080/111.png
,确实是可以访问的到的。
在WebMvcAutoConfiguration这个类中,可以找到welcomePageHandlerMapping这个欢迎页映射方法:
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(
ApplicationContext applicationContext) {
return new WelcomePageHandlerMapping(
new TemplateAvailabilityProviders(applicationContext),
applicationContext, getWelcomePage(),
this.mvcProperties.getStaticPathPattern());
}
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;
}
private Optional<Resource> getWelcomePage() {
String[] locations = 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()
这个方法,其逻辑就是循环各个静态资源路径,在其后匹配上index.html,然后首先在哪个静态资源路径下找到了index.html就将此index.html返回。
我们可以在classpath:/static/这个目录下新建一个index.html文件,然后直接访问localhost:8080
,可以看到定位到了index.html文件。
在WebMvcAutoConfiguration这个类中,可以找到FaviconConfiguration这个图标配置的内部类:
@Configuration
@ConditionalOnProperty(value = "spring.mvc.favicon.enabled", matchIfMissing = true)
public static class FaviconConfiguration implements ResourceLoaderAware {
private final ResourceProperties resourceProperties;
private ResourceLoader resourceLoader;
public FaviconConfiguration(ResourceProperties resourceProperties) {
this.resourceProperties = resourceProperties;
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
@Bean
public SimpleUrlHandlerMapping faviconHandlerMapping() {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
mapping.setUrlMap(Collections.singletonMap("**/favicon.ico",
faviconRequestHandler()));
return mapping;
}
@Bean
public ResourceHttpRequestHandler faviconRequestHandler() {
ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler();
requestHandler.setLocations(resolveFaviconLocations());
return requestHandler;
}
private List<Resource> resolveFaviconLocations() {
String[] staticLocations = getResourceLocations(
this.resourceProperties.getStaticLocations());
List<Resource> locations = new ArrayList<>(staticLocations.length + 1);
Arrays.stream(staticLocations).map(this.resourceLoader::getResource)
.forEach(locations::add);
locations.add(new ClassPathResource("/"));
return Collections.unmodifiableList(locations);
}
}
逻辑和欢迎页差不多,只要你将你的图标置于静态资源文件夹中,就会将你自己的favicon.ico这个图标文件作为应用程序的图标。