Spring Boot: Overriding favicon

zeodtr picture zeodtr · Dec 19, 2013 · Viewed 61.4k times · Source

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.

Answer

Ross picture Ross · Dec 30, 2013

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.