Spring Boot:如何覆盖网站图标(favicon)?

86
我该如何覆盖Spring Boot的网站图标?
提示:这是我的另一个问题,提供了另一种解决方案,不涉及任何编码: Spring Boot:是否可以在fat jar中的任意目录中使用外部application.properties文件?它是针对application.properties的,但也适用于网站图标。事实上,我现在正在使用这种方法来覆盖网站图标。
如果我实现了一个带有@EnableWebMvc注释的类,则Spring Boot的WebMvcAutoConfiguration类不会加载,并且我可以通过将其放置在静态内容的根目录中来提供自己的网站图标。
否则,WebMvcAutoConfiguration会注册faviconRequestHandler bean,(请参见源代码https://github.com/spring-projects/spring-boot/blob/master/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration.java),并为放置在Spring Boot主资源目录中的“绿色叶子”图标提供服务。
如何在不实现带有@EnableWebMvc注解的类的情况下覆盖它,从而禁用Spring Boot的WebMvcAutoConfiguration类的整个默认配置功能?
另外,由于我希望在客户端(Web浏览器)尽快更新图标文件,因此我想将favicon文件的缓存期设置为0。(就像下面的代码一样,我正在使用它来更新必须在我更改文件后尽快在客户端上更新的“静态”Web应用程序内容和脚本文件。)
public void addResourceHandlers(ResourceHandlerRegistry registry)
{
    registry.addResourceHandler("/**")
        .addResourceLocations("/")
        .setCachePeriod(0);
}

因此,仅仅找到保存Spring Boot的faviconRequestHandler所需的favicon.ico文件的位置可能不足。

更新

现在我知道可以通过将favicon文件放置在src/main/resources目录中来覆盖默认值。但缓存期问题仍然存在。
此外,最好将favicon文件放置在静态网页文件所在的目录中,而不是资源目录。

更新

好的,我成功地覆盖了默认值。 我的做法如下:

@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);
        }
    }
}

基本上,我通过添加具有最高顺序的资源处理程序来覆盖默认处理程序,调用registry.setOrder(Integer.MIN_VALUE)。

由于Spring Boot中的默认处理程序具有order值(Integer.MIN_VALUE +1),(请参见https://github.com/spring-projects/spring-boot/blob/master/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration.java中的FaviconConfiguration类),因此我的处理程序胜出。

这样可以吗? 是否还有其他方式(比我所做的温和一些)?

更新

不行。 当我调用registry.setOrder(Integer.MIN_VALUE)时,实际上我提高了所有资源处理程序的优先级。 因此,当我将以下代码添加到另一个WebMvcConfigurerAdapter中时,所有http请求实际上都会被重定向到该资源处理程序,从而防止任何动态Java代码处理。

public void addResourceHandlers(ResourceHandlerRegistry registry)
{
    registry.addResourceHandler("/**")
        .addResourceLocations("/")
        .setCachePeriod(0);
}

需要另一个解决方案。
更新
目前,我找不到覆盖Spring Boot提供的favicon功能的方法。也许有一种方法可以添加自己的HandlerMapping bean,但我不知道该怎么做。
现在我可以选择以下选项之一:
1.拥有一个类,其中包含@EnableWebMvc,从而禁用Spring Boot的WebMvcAutoConfiguration类。(我可以复制WebMvcAutoConfiguration类的代码并删除favicon功能) 2.放弃将favicon文件放置在任意位置的自由,并将其放置在资源目录中,因为Spring Boot的favicon功能需要。并忽略缓存问题。
但是,这两个选项都不令人满意。我只想将favicon文件与我的静态网页文件放在一起(可以是任何目录,因为我可以更改文档根目录)并解决缓存问题。我错过了什么吗?任何建议将不胜感激。
更新
顺便说一下,我想更改favicon和其他静态文件的位置的原因如下。目前主要是开发环境问题。
我正在构建一个单页Web应用程序(SPA)。
库/框架:
- 服务器端,我使用Spring。(当然) - 客户端(Web浏览器)端,我使用AngularJS。
工具:
- 服务器端,我使用Spring Tool Suite。 - 客户端,我使用WebStorm。
主目录结构:
ProjectRoot\
    src\
    bin\
    build\
    webapp\
    build.gradle
  • src:我的Spring Java源文件所在的位置。
  • bin:Spring Tool Suite放置其构建输出的位置。
  • build:'gradle build'放置其构建输出的位置。
  • webapp:我的客户端源文件(.js, .css, .htm和favicon)所在的位置。因此,这是WebStorm项目目录。(如果需要,我可以更改目录名称)

我想要的是:

  • 能够在不重新构建/重启Spring服务器应用程序的情况下修改和测试客户端代码。因此,客户端代码不能放入jar文件中。无论如何,Spring Tool Suite根本不会构建jar文件(至少对于当前配置而言)。
  • 能够使用客户端代码测试我的Spring服务器应用程序,轻松地在Spring Tool Suite输出和gradle输出之间切换。因此,客户端代码必须可从位于build子目录(实际上是build\libs)和位于bin目录中的服务器应用程序访问。
  • 当我修改客户端代码时,它必须立即可供Web浏览器使用。因此,浏览器不能无限期缓存它,并且必须始终向服务器请求更新。
  • 在部署时,必须能够修改客户端代码而无需重新构建/重启服务器应用程序。因此,客户端代码不能放入jar文件中。

关于缓存问题:

如果没有在addResourceHandlers()上设置setCachePeriod(0),Google Chrome会无限期缓存该文件,而不会向服务器请求更新。它甚至不连接到服务器。(Google工程师表示这种行为是正确的。)因此,我所能做的就是手动清除浏览器缓存。这在开发环境中很令人沮丧,在生产环境中则是不可接受的。

顺便提一下,Node.js上的express.js模块会给出合理的默认HTTP头,以便Google Chrome请求服务器进行更新。当我使用Fiddler查看Spring和express.js生成的HTTP头时,它们是不同的。

如果有任何改进我的环境的建议,将不胜感激。
由于我是Spring的初学者,可能会遗漏一些东西。

更新

最终我有了一个可行的代码。如下所示:

@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;
    }
}

注意bean的名称。我已经加上了“my”以避免名称冲突。
自动装配应用程序上下文本身似乎很尴尬,但这是模仿org.springframework.web.servlet.config.annotation.ResourceHandlerRegistration.addResourceLocations()中的代码所必需的。
现在我有一个无缓存问题的favicon处理程序,我可以将favicon文件放在任何地方。
谢谢。

1
你的方法看起来还不错。但是它是否会替换掉默认的Spring MVC资源处理程序(因此静态资源将不再从classpath:/static中提供)。(这就是为什么Boot中的favicon支持在自己的HandlerMapping中的原因,我想。) - Dave Syer
@DaveSyer 请查看我上面的第四次更新。还有其他方法吗?例如,我可以注册自己的 HandlerMapping 吗? - zeodtr
是的,您可以添加一个HandlerMapping(只需复制Boot中的一个并赋予更高的优先级)。我不太确定您为什么关心favicon.ico文件的位置,但如果您愿意,我们可以使其可配置。所有浏览器都会将其缓存,因此我认为缓存设置并不重要,但很乐意被证明是错误的。 - Dave Syer
@DaveSyer请查看更新的问题,了解我想将静态文件放置在不同位置的原因。还有缓存问题。 - zeodtr
哦,等等,我明白了。Chrome 强制缓存了它。我不得不将页面加入书签才能重新加载。 - Chris DaMour
显示剩余3条评论
8个回答

75

对我来说,这一切都是不必要的。

当你可以在生成的JAR文件中捆绑一个资源,它会比默认设置更优先,为什么要覆盖默认设置呢。

要实现自定义的 favicon.ico 文件,我创建了一个 src/main/resources 目录,并将 favicon.ico 文件拷贝到其中。这个资源目录中的文件会被移动到编译后的 JAR 文件的根目录下,因此您的自定义 favicon.ico 将会在 Spring 提供的图标之前被找到。

通过上述步骤也可以达到与上面更新的解决方案相同的效果。

请注意,自 v1.2.0 起,您还可以将文件放在 src/main/resources/static 中。


7
原因很简单,我不想将网站图标文件放入JAR包中。 - zeodtr
@zeodtr 只是好奇为什么。这对我来说似乎最自然。但是,我本来期望它在 src/main/resources/static/images 中。 - btiernay
1
@btiernay 这是因为Web服务器是与Web客户端分开的实体。而网站图标属于客户端。Web服务器的war文件没有理由包含属于客户端的文件,因为这些文件需要根据用户需求进行更频繁的修改。 - zeodtr
只是提醒一下,现在可以从“static”中解决favicon.ico的问题:https://github.com/spring-projects/spring-boot/commit/a3527521a25eee700a2197264b7cbd24d0ef38b2 - btiernay
1
src/main/resources/static路径正常工作。这是最简单和常规的方法。 - pdem
显示剩余2条评论

54

您可以将自己的favicon.ico放在类路径的根目录或任何静态资源位置中(例如 classpath:/ static )。 您还可以使用单个标记 spring.mvc.favicon.enabled = false 完全禁用favicon解析。

或者,您可以添加一个HandlerMapping来完全控制它(只需复制Boot中的HandlerMapping并为其分配更高的优先级),例如:

@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;
}
}

顺便提一下,由于我必须使用ResourceLoader.gerResource("/")来获取文档根目录中的文件(对吧?),所以我必须@Autowire ApplicationContext。将应用程序上下文注入是否可以?有人说这是不好的事情。 - zeodtr
我正在尝试您的建议。需要注意的一点是:我的bean名称不能为“faviconHandlerMapping”,因为它会与Spring Boot的faviconHandlerMapping bean发生冲突。由org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestor()调用的方法将选择其中一个bean,而该方法又被org.springframework.web.servlet.initHandlerMappings()调用。似乎我的bean比名称冲突更早地加载了。 - zeodtr
已注册的HandlerMapping bean的名称如下:{faviconHandlerMappingrequestMappingHandlerMappingviewControllerHandlerMappingbeanNameHandlerMappingresourceHandlerMappingdefaultServletHandlerMappingendpointHandlerMapping}。因此,我必须避免使用这些名称。 - zeodtr
另外,faviconRequestHandler bean 必须命名不同以避免名称冲突。现在它正在工作! - zeodtr
2
你应该阅读更新后的答案,以及用户指南。如果你只想改变图标,这并不是一个非常困难的改变。我不知道为什么原帖作者要费这么大劲。 - Dave Syer
显示剩余4条评论

29

我非常喜欢Springboot,因为它总体上充满了聪明的解决方案,但我拒绝注册应用程序bean来提供favicon,因为那太愚蠢了。

我只是在我的HTML页面头部添加了自己的favicon链接,像这样。

<link rel="icon" type="image/png" href="images/fav.png">

然后我将我的网站图标文件的名称更改,并将其放置在

<ProjFolder>/src/main/resources/static/images/fav.png

现在我在Chrome和Firefox浏览器标签以及Safari地址栏中都有图标,而不需要使用Spring和Java,对于这种琐碎的功能,我不应该在新版本的Springboot中追踪更改。


4
您不再需要处理程序映射(只需在静态资源中放置一个favicon.ico即可)。 - Dave Syer
如果您仅将Spring Boot用作API服务后端,而没有任何HTML页面,那该怎么办呢 :/ - Paramvir Singh Karwal
@ParamvirSinghKarwal 如果您只有一个服务,为什么要想要一个网站图标? - jeremyjjbrown
如果您直接从浏览器地址访问API,则会显示Spring应用程序的默认Favicon,类似于绿色叶子。我希望它成为品牌化的Favicon。我已经成功实现了这一点。 - Paramvir Singh Karwal
这是正确的答案。特别是当您有一个 .png 文件而不是 .ico 文件时。 - ACV

14

spring.boot 2.2中已经移除了spring.mvc.favicon.enabled这个属性。你知道它被什么属性替换掉了吗? - Shilan
1
@Shilan,现在默认的favicon已经被删除了,所以它不再被替换为任何东西。如果你想要一个favicon,你需要配置映射来使用你自己的favicon。 - Trynkiewicz Mariusz
1
最后我发现需要这样做:@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/favicon.ico").addResourceLocations("classpath:/static/"); } - Shilan

2

较新版本的 Boot(1.2 确定支持,但可能也适用于 1.1.8)允许您将 favicon.ico 直接放在静态资源中。


2

我尝试了很多选项/变体,但只有这个有效。(Spring Boot 2.2.7)

Favicon.ico文件和robots.txt放在/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();
    }
}

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

如果有帮助,请点赞;)

1

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

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

robots.txt和favicon.ico文件的位置为:/src/main/resources

我使用的是Spring Boot 1.2.1版本。


1
我正在使用Spring Boot版本2.5.2。在我的情况下,我只需要一个jpg/png文件作为网站图标,而不是favicon.ico文件。

将你的favicon.jpg/favicon.png文件放在类路径下的资源文件夹中 - src/main/resources/favicon.png 或者在类路径下的静态文件夹中的资源文件夹中 - src/main/resources/static/favicon.png

在资源文件夹中的模板文件夹中的每个HTML页面中,分别在标签内添加:
<link rel="icon" type="image/png" href="../favicon.png"> 或者
<link rel="icon" type="image/png" href="../static/favicon.png">

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接