Question
How can I override the favicon of Spring Boot?
NOTE : Here is my another question that provides another solution which does not involve any coding: [Spring Boot: Is it possible to use external application.properties files in arbitrary directories with a fat jar?](https://stackoverflow.com/questions/26140784/spring-boot-is-it- possible-to-use-external-application-properties-files-in-arbi) It's for application.properties, but it can also be applied to the favicon. In fact, I'm using that method for favicon overriding now.
If I implement a class that has @EnableWebMvc, WebMvcAutoConfiguration class of Spring Boot does not load, and I can serve my own favicon by placing it to the root directory of the static contents.
Otherwise, WebMvcAutoConfiguration registers faviconRequestHandler bean, (see source <https://github.com/spring-projects/spring-boot/blob/master/spring- boot- autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration.java>) and it serve the 'green leaf' icon which is placed in the Spring Boot's main resource directory.
How can I override it without implementing a class that has @EnableWebMvc myself, thus disabling whole default configuration functionality of WebMvcAutoConfiguration class of Spring Boot?
Also, since I want the icon file be updated as soon as possible on the client (web browser) side, I want to set the cache period of the favicon file to 0. (like the following code, which I'm using for my 'static' webapp content and script files which must be updated on the client side as soon as possible after I change the file.)
public void addResourceHandlers(ResourceHandlerRegistry registry)
{
registry.addResourceHandler("/**")
.addResourceLocations("/")
.setCachePeriod(0);
}
So, just to find the place to save the favicon.ico file that Spring Boot's faviconRequestHandler honors may not be sufficient.
UPDATE
Now I know that I can override the default one by placing a favicon file to
src/main/resources directory. But the cache period problem still remains.
Also, it is preferable to place the favicon file to the directory that static
web files are placed, rather than the resource directory.
UPDATE
Ok, I managed to override the default one. What I did is as follows:
@Configuration
public class WebMvcConfiguration
{
@Bean
public WebMvcConfigurerAdapter faviconWebMvcConfiguration()
{
return new FaviconWebMvcConfiguration();
}
public class FaviconWebMvcConfiguration extends WebMvcConfigurerAdapter
{
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry)
{
registry.setOrder(Integer.MIN_VALUE);
registry.addResourceHandler("/favicon.ico")
.addResourceLocations("/")
.setCachePeriod(0);
}
}
}
Basically, I overrode the default one by adding a resource handler with the highest order by calling registry.setOrder(Integer.MIN_VALUE).
Since the default one in Spring Boot has the order value (Integer.MIN_VALUE + 1), (see FaviconConfiguration class in <https://github.com/spring- projects/spring-boot/blob/master/spring-boot- autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration.java>) my handler wins.
Is this Ok? Is there another way (something gentler than what I did)?
UPDATE
It's not Ok. When I call registry.setOrder(Integer.MIN_VALUE)
, actually I
raise the priority of all resource handlers. So, when I add following code to
another WebMvcConfigurerAdapter
, effectively all http request is directed to
that resource handler, preventing any dynamic handling by Java code.
public void addResourceHandlers(ResourceHandlerRegistry registry)
{
registry.addResourceHandler("/**")
.addResourceLocations("/")
.setCachePeriod(0);
}
Another solution is needed.
UPDATE
For now, I could not find the way to override the favicon functionality Spring
Boot provides.
Maybe there is a way to add add my own HandlerMapping
bean, but I don't know
how to do it.
Now I can choose one of following options:
- Have a class that has
@EnableWebMvc
thus disabling Spring BootWebMvcAutoConfiguration
class. (I can copy the code ofWebMvcAutoConfiguration
class and delete the favicon functionality) - Give up the freedom of placing favicon file to arbitary location and place it to the resource directory as Spring Boot's favicon functionality requires. And ignore caching problem.
But neither option is satisfactory.
I just want to place the favicon file with my static web files (which can be
any directory since I can change the document root) and resolve the caching
problem.
Am I missing something?
Any suggestion would be greatly appreciated.
UPDATE
BTW, the reason I want to change the location of favicon and other static files is as follows. For now it is mainly the development environment issue.
I'm building a single page web application(SPA).
Libraries/Frameworks:
- For server side, I use Spring. (of course)
- For client (web browser) side, I use AngularJS.
Tools:
- For server side, I use Spring Tool Suite.
- For client side, I use WebStorm.
Main directory structure:
ProjectRoot\
src\
bin\
build\
webapp\
build.gradle
- src: Where my Spring java source files reside.
- bin: Where Spring Tool Suite places its build output.
- build: Where 'gradle build' places its build output.
- webapp: Where my client source files(.js, .css, .htm and favicon) reside. Thus this is the WebStorm project directory. (I can change the directory name if necessary)
What I want is:
- To be able to modify and test my client code without rebuilding/restarting my Spring server application. So, the client code must not be put into the jar file. Anyways Spring Tool Suite does not build a jar file at all (at least for the current configuration)
- To be able to test my Spring server application with the client code, easily switching between Spring Tool Suite output and gradle output. So, client code must be accessible from both the server application in
build
subdirectory (actuallybuild\libs
) and the server application inbin
directory. - When I modify the client code, it must be immediately available to the web browser. So browser must not cache it indefinitely, and must always ask for the server for update.
- When deployed, client code must be modifiable without rebuilding/restarting the server application. So the client code must not be put into the jar file.
Regarding cache issue:
Without setCachePeriod(0) on addResourceHandlers(), Google Chrome caches the file indefinitely, without asking the server for updates. It does not even connect to the server. (Google engineers say that the behavior is correct.) So, all I can do is to manually clear the browser cache. It is frustrating on development environment, and unacceptable on production environment.
BTW, express.js module on Node.js gives reasonable default HTTP header so that Google Chrome ask the server for updates. When I reviewed the HTTP headers that Spring and express.js produces using Fiddler, they were different.
Any suggestion for improving my environment would be appreciated.
Since I'm a Spring beginner, I may be missing something.
UPDATE
Finally I have a working code. It is as follows:
@Configuration
public static class FaviconConfiguration
{
@Bean
public SimpleUrlHandlerMapping myFaviconHandlerMapping()
{
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setOrder(Integer.MIN_VALUE);
mapping.setUrlMap(Collections.singletonMap("/favicon.ico",
myFaviconRequestHandler()));
return mapping;
}
@Autowired
ApplicationContext applicationContext;
@Bean
protected ResourceHttpRequestHandler myFaviconRequestHandler()
{
ResourceHttpRequestHandler requestHandler =
new ResourceHttpRequestHandler();
requestHandler.setLocations(Arrays
.<Resource> asList(applicationContext.getResource("/")));
requestHandler.setCacheSeconds(0);
return requestHandler;
}
}
Notice the bean names. I have added 'my' to avoid name clash.
Autowiring application context itself seems awkward, but it was neccessary for
mimicking the code in
org.springframework.web.servlet.config.annotation.ResourceHandlerRegistration.addResourceLocations()
.
Now I have a favicon handler free of caching problem, and I can place the
favicon file anywhere I want.
Thanks.
Answer
You can just put your own favicon.ico in the root of the classpath or in any
of the static resource locations (e.g. classpath:/static
). You can also
disable favicon resolution completely with a single flag
spring.mvc.favicon.enabled=false
.
Or to take complete control you can add a HandlerMapping (just copy the one from Boot and give it a higher priority), e.g.
@Configuration
public static class FaviconConfiguration {
@Bean
public SimpleUrlHandlerMapping faviconHandlerMapping() {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setOrder(Integer.MIN_VALUE);
mapping.setUrlMap(Collections.singletonMap("mylocation/favicon.ico",
faviconRequestHandler()));
return mapping;
}
@Bean
protected ResourceHttpRequestHandler faviconRequestHandler() {
ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler();
requestHandler.setLocations(Arrays
.<Resource> asList(new ClassPathResource("/")));
return requestHandler;
}
}