In my Spring Boot applications (version 1 and 2), my static resources are at a single place :
src/main/resources/static
static
being a folder recognized by Spring Boot to load static resources.
Then the idea is to customize the Spring MVC configuration.
The simpler way is using Spring Java configuration.
I implement WebMvcConfigurer
to override addResourceHandlers()
.
I add in a single ResourceHandler
to the current ResourceHandlerRegistry
.
The handler is mapped on every request and I specify classpath:/static/
as resource location value (you may of course adding others if required).
I add a custom PathResourceResolver
anonymous class to override getResource(String resourcePath, Resource location)
.
And the rule to return the resource is the following : if the resource exists and is readable (so it is a file), I return it. Otherwise, by default I return the index.html
page. Which is the expected behavior to handle HTML 5 urls.
Spring Boot 1.X Application :
Extending org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter
is the way.
The class is an adapter of the WebMvcConfigurer
interface
with empty methods allowing sub-classes to override only the methods they're interested in.
Here is the full code :
import java.io.IOException;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.resource.PathResourceResolver;
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**/*")
.addResourceLocations("classpath:/static/")
.resourceChain(true)
.addResolver(new PathResourceResolver() {
@Override
protected Resource getResource(String resourcePath,
Resource location) throws IOException {
Resource requestedResource = location.createRelative(resourcePath);
return requestedResource.exists() && requestedResource.isReadable() ? requestedResource
: new ClassPathResource("/static/index.html");
}
});
}
}
Spring Boot 2.X Application :
org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter
was deprecated.
Implementing directly WebMvcConfigurer
is the way now as it is still an interface but it has now default methods (made possible by a Java 8 baseline) and can be implemented directly without the need for the adapter.
Here is the full code :
import java.io.IOException;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.resource.PathResourceResolver;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**/*")
.addResourceLocations("classpath:/static/")
.resourceChain(true)
.addResolver(new PathResourceResolver() {
@Override
protected Resource getResource(String resourcePath,
Resource location) throws IOException {
Resource requestedResource = location.createRelative(resourcePath);
return requestedResource.exists() && requestedResource.isReadable() ? requestedResource
: new ClassPathResource("/static/index.html");
}
});
}
}
EDIT to address some comments :
For those that store their static resources at another location as src/main/resources/static
, change the value of the var args parameter of addResourcesLocations()
consequently.
For example if you have static resources both in static
and in the public
folder (no tried) :
registry.addResourceHandler("/**/*")
.addResourceLocations("classpath:/static/", "/public")