Spring Boot: Overriding favicon
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? 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.
None of this was necessary for me.
Why override the default when you can bundle a resource with the generated JAR that will take higher precedence than the default one.
To achieve a custom favicon.ico
file, I created a src/main/resources
directory for my application and then copied the favicon.ico
file into there. The files in this resources directory are moved to the root of the compiled JAR and therefore your custom favicon.ico
is found before the Spring provided one.
Doing the above achieved the same effect as your updated solution above.
Note that since v1.2.0 you can also put the file in src/main/resources/static
.
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;
}
}
I really like Springboot because overall it is full of smart solutions but I refuse to register an application bean to provide a favicon because that is just plain stupid.
I just added my own favicon link in my html page head like so.
<link rel="icon" type="image/png" href="images/fav.png">
Then I renamed my favicon and placed it at
<ProjFolder>/src/main/resources/static/images/fav.png
Now I have an icon in Chrome and Firefox browser tabs and the Safari address bar without having to use Spring and Java, and I should not have to chase changes to Springboot in newer versions for such trivial functionality.