84
votes

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:

  1. Have a class that has @EnableWebMvc thus disabling Spring Boot WebMvcAutoConfiguration class. (I can copy the code of WebMvcAutoConfiguration class and delete the favicon functionality)
  2. 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 (actually build\libs) and the server application in bin 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.

7
Your method looks OK. Doesn't it replace the default Spring MVC resource handler though (so static resources will not be served from classpath:/static any more for instance). (That's why the favicon support in Boot is in its own HandlerMapping I think.)Dave Syer
@DaveSyer Now I realized that it's not Ok. I wrote third update above. Maybe separate HandlerMapping is needed.zeodtr
@DaveSyer Please see my fourth update above. Is there any other way? For example, can I register my own HandlerMapping?zeodtr
Yes, you can add a HandlerMapping (just copy the one from Boot and give it a higher priority). I'm not really sure why you would care about the location of the favicon.ico file but we can make it configurable if you like. All browsers will cache it as well, so I doubt the cache settings matter, but happy to be proved wrong.Dave Syer
@DaveSyer Please see the updated question for the reason why I want to place the static files in different location. Also the cache issue.zeodtr

7 Answers

54
votes

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;
}
}
75
votes

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.

29
votes

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.

14
votes

Since Spring Boot 1.2.2 and 1.1.11 you can easily disable default favicon using spring.mvc.favicon.enabled = false property.

For more informations visit:

UPDATE: there is no longer spring.mvc.favicon.enabled property since Spring Boot 2.2.x

2
votes

Newer versions of Boot (1.2 for sure but maybe also 1.1.8) allow you to just put a favicon.ico in your static resources.

2
votes

I tried a lot of options/variants, but only this worked. (Spring Boot 2.2.7)

Favicon.ico files & robots.txt put in /resources/static

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
  protected void configure(HttpSecurity http) throws Exception { 
    http
      .authorizeRequests()
        .antMatchers("/favicon.ico").permitAll()
        .antMatchers("/robots.txt").permitAll()
        .antMatchers("/").permitAll()
        .anyRequest().authenticated();
    }
}

and

@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
@Override
  public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler(
      "/favicon.ico",
      "/robots.txt")
      .addResourceLocations(
        "classpath:/static/");
  }
}

If it helps, please like it ;)

1
votes

registry.addResourceHandler("/robots.txt").addResourceLocations("/");

registry.addResourceHandler("/favicon.ico").addResourceLocations("/");

robots.txt, favicon.ico file location : /src/main/resources

i used spring boot 1.2.1