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:
@EnableWebMvc
thus disabling Spring Boot WebMvcAutoConfiguration
class. (I can copy the code of WebMvcAutoConfiguration
class and delete the favicon functionality)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:
Tools:
Main directory structure:
ProjectRoot\
src\
bin\
build\
webapp\
build.gradle
What I want is:
build
subdirectory (actually build\libs
) and the server application in bin
directory.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
.