Spring Boot: nadpisanie favicon

84

Jak mogę zmienić favicon Spring Boot?

UWAGA : Oto moje kolejne pytanie, które dostarcza innego rozwiązania, które nie wymaga żadnego kodowania: Spring Boot: Czy można używać zewnętrznych plików application.properties w dowolnych katalogach z fat jar? Jest przeznaczony dla application.properties, ale można go również zastosować do favicon. W rzeczywistości używam teraz tej metody do zastępowania favicon.

Jeśli zaimplementuję klasę, która ma @EnableWebMvc, klasa WebMvcAutoConfiguration Spring Boot nie ładuje się i mogę obsługiwać własną ikonę favicon, umieszczając ją w katalogu głównym zawartości statycznej.

W przeciwnym razie WebMvcAutoConfiguration rejestruje bean faviconRequestHandler, (patrz źródło https://github.com/spring-projects/spring-boot/blob/master/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ web / WebMvcAutoConfiguration.java ) i wyświetla ikonę „zielonego liścia”, która jest umieszczana w głównym katalogu zasobów Spring Boot.

Jak mogę to zastąpić bez implementowania klasy, która ma @EnableWebMvc, wyłączając w ten sposób całą domyślną funkcjonalność konfiguracji klasy WebMvcAutoConfiguration w Spring Boot?

Ponadto, ponieważ chcę jak najszybciej zaktualizować plik ikony po stronie klienta (przeglądarki internetowej), chcę ustawić okres pamięci podręcznej pliku favicon na 0. (podobnie jak poniższy kod, którego używam do mojego `` statyczna '' zawartość aplikacji internetowej i pliki skryptów, które należy zaktualizować po stronie klienta tak szybko, jak to możliwe po zmianie pliku).

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

Tak więc, aby znaleźć miejsce do zapisania pliku favicon.ico, które Spring Boot honoruje faviconRequestHandler, może nie wystarczyć.

AKTUALIZACJA

Teraz wiem, że mogę zastąpić domyślny, umieszczając plik favicon w katalogu src / main / resources. Ale problem z okresem pamięci podręcznej nadal pozostaje.
Ponadto lepiej jest umieścić plik favicon w katalogu, w którym umieszczane są statyczne pliki internetowe, a nie w katalogu zasobów.

AKTUALIZACJA

Ok, udało mi się nadpisać domyślny. Oto co zrobiłem:

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

Zasadniczo nadpisałem domyślny, dodając procedurę obsługi zasobów o najwyższej kolejności, wywołując register.setOrder (Integer.MIN_VALUE).

Ponieważ domyślny w Spring Boot ma wartość zamówienia (Integer.MIN_VALUE + 1), (patrz klasa FaviconConfiguration w https://github.com/spring-projects/spring-boot/blob/master/spring-boot-autoconfigure/ src / main / java / org / springframework / boot / autoconfigure / web / WebMvcAutoConfiguration.java ) mój program obsługi wygrywa.

Czy to jest ok? Czy jest inny sposób (coś delikatniejszego niż to, co zrobiłem)?

AKTUALIZACJA

Nie jest w porządku. Kiedy dzwonię registry.setOrder(Integer.MIN_VALUE), w rzeczywistości podnoszę priorytet wszystkich osób obsługujących zasoby. Tak więc, kiedy dodaję następujący kod do innego WebMvcConfigurerAdapter, efektywnie wszystkie żądania http są kierowane do tego modułu obsługi zasobów, zapobiegając wszelkiej dynamicznej obsłudze przez kod Java.

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

Potrzebne jest inne rozwiązanie.

AKTUALIZACJA

Na razie nie mogłem znaleźć sposobu na zastąpienie funkcji favicon, którą zapewnia Spring Boot.
Może jest sposób na dodanie własnego HandlerMappingziarna, ale nie wiem, jak to zrobić.

Teraz mogę wybrać jedną z następujących opcji:

  1. Mieć klasę, która @EnableWebMvcwyłącza w ten sposób WebMvcAutoConfigurationklasę Spring Boot . (Mogę skopiować kod WebMvcAutoConfigurationklasy i usunąć funkcjonalność favicon)
  2. Zrezygnuj ze swobody umieszczania pliku favicon w dowolnej lokalizacji i umieść go w katalogu zasobów, zgodnie z wymaganiami funkcji favicon Spring Boot. I zignoruj ​​problem z buforowaniem.

Ale żadna opcja nie jest zadowalająca.
Chcę tylko umieścić plik favicon z moimi statycznymi plikami sieciowymi (które mogą być dowolnym katalogiem, ponieważ mogę zmienić katalog główny dokumentu) i rozwiązać problem z buforowaniem.
Czy coś mi brakuje?
Każda sugestia byłaby bardzo mile widziana.

AKTUALIZACJA

BTW, powód, dla którego chcę zmienić lokalizację favicon i innych plików statycznych, jest następujący. Na razie jest to głównie kwestia środowiska programistycznego.

Buduję pojedynczą aplikację internetową (SPA).

Biblioteki / frameworki:

  • Po stronie serwera używam Spring. (oczywiście)
  • Po stronie klienta (przeglądarki internetowej) używam AngularJS.

Przybory:

  • Po stronie serwera używam Spring Tool Suite.
  • Po stronie klienta używam WebStorm.

Główna struktura katalogów:

ProjectRoot\
    src\
    bin\
    build\
    webapp\
    build.gradle
  • src: Gdzie znajdują się moje pliki źródłowe Java Spring.
  • bin: Gdzie Spring Tool Suite umieszcza swoje dane wyjściowe kompilacji.
  • build: Gdzie 'gradle build' umieszcza wynik kompilacji.
  • webapp: Gdzie znajdują się pliki źródłowe mojego klienta (.js, .css, .htm i favicon). Tak więc jest to katalog projektu WebStorm. (W razie potrzeby mogę zmienić nazwę katalogu)

Chcę:

  • Aby móc modyfikować i testować mój kod klienta bez przebudowywania / ponownego uruchamiania aplikacji serwera Spring. Tak więc kod klienta nie może zostać umieszczony w pliku jar. W każdym razie Spring Tool Suite w ogóle nie buduje pliku jar (przynajmniej dla bieżącej konfiguracji)
  • Aby móc przetestować moją aplikację serwerową Spring za pomocą kodu klienta, łatwo przełączam się między wyjściem Spring Tool Suite a wyjściem gradle. Zatem kod klienta musi być dostępny zarówno z aplikacji serwera w buildpodkatalogu (faktycznie build\libs), jak iz aplikacji serwera w binkatalogu.
  • Kiedy modyfikuję kod klienta, musi on być natychmiast dostępny dla przeglądarki internetowej. Dlatego przeglądarka nie może buforować go w nieskończoność i zawsze musi prosić serwer o aktualizację.
  • Po wdrożeniu kod klienta musi być modyfikowalny bez ponownego kompilowania / ponownego uruchamiania aplikacji serwera. Dlatego kod klienta nie może zostać umieszczony w pliku jar.

Odnośnie problemu z pamięcią podręczną:

Bez funkcji setCachePeriod (0) w addResourceHandlers () Google Chrome buforuje plik w nieskończoność, bez pytania serwera o aktualizacje. Nie łączy się nawet z serwerem. (Inżynierowie Google twierdzą, że to zachowanie jest prawidłowe). Więc jedyne, co mogę zrobić, to ręcznie wyczyścić pamięć podręczną przeglądarki. Jest to frustrujące w środowisku programistycznym i niedopuszczalne w środowisku produkcyjnym.

BTW, moduł express.js w Node.js podaje rozsądny domyślny nagłówek HTTP, dzięki czemu Google Chrome prosi serwer o aktualizacje. Kiedy przejrzałem nagłówki HTTP utworzone przez Spring i express.js przy użyciu programu Fiddler, były one różne.

Wszelkie sugestie dotyczące poprawy mojego środowiska będą mile widziane.
Ponieważ zaczynam od wiosny, być może czegoś mi brakuje.

AKTUALIZACJA

Wreszcie mam działający kod. Wygląda następująco:

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

Zwróć uwagę na nazwy ziaren. Dodałem „mój”, aby uniknąć konfliktu nazw.
Sam kontekst aplikacji autowiring wydaje się niewygodny, ale był niezbędny do naśladowania kodu w org.springframework.web.servlet.config.annotation.ResourceHandlerRegistration.addResourceLocations().

Teraz mam program obsługujący favicon wolny od problemu z buforowaniem i mogę umieścić plik favicon w dowolnym miejscu.
Dzięki.

zeodtr
źródło
1
Twoja metoda wygląda OK. Nie zastępuje jednak domyślnej obsługi zasobów Spring MVC (więc na przykład zasoby statyczne nie będą już obsługiwane classpath:/static). (Dlatego myślę, że obsługa favicon w Boot jest we własnym HandlerMapping.)
Dave Syer,
@DaveSyer Teraz zdałem sobie sprawę, że to nie jest w porządku. Trzecią aktualizację napisałem powyżej. Może HandlerMappingpotrzebny jest oddzielny .
zeodtr
@DaveSyer Zobacz moją czwartą aktualizację powyżej. Czy jest inny sposób? Na przykład, czy mogę zarejestrować własne HandlerMapping?
zeodtr
Tak, możesz dodać HandlerMapping(po prostu skopiuj ten z Boot i nadaj mu wyższy priorytet). Nie jestem pewien, dlaczego miałbyś przejmować się lokalizacją, favicon.ico fileale możemy ją skonfigurować, jeśli chcesz. Wszystkie przeglądarki również będą go buforować, więc wątpię, czy ustawienia pamięci podręcznej mają znaczenie, ale cieszę się, że udowodniono, że się mylą.
Dave Syer
@DaveSyer Zobacz zaktualizowane pytanie, dlaczego chcę umieścić pliki statyczne w innej lokalizacji. Również kwestia pamięci podręcznej.
zeodtr

Odpowiedzi:

54

Możesz po prostu umieścić swój własny plik favicon.ico w katalogu głównym ścieżki klas lub w dowolnej statycznej lokalizacji zasobów (np classpath:/static.). Możesz także całkowicie wyłączyć rozdzielczość favicon za pomocą jednej flagi spring.mvc.favicon.enabled=false.

Aby przejąć pełną kontrolę, możesz dodać HandlerMapping (po prostu skopiuj ten z Boot i nadaj mu wyższy priorytet), np.

@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;
}
}
Dave Syer
źródło
Dziękuję Ci. Myślałem, że już to wypróbowałem, ale teraz o tym myślę, przegapiłem @Configurationadnotację. BTW, napisałem powód, dla którego chcę umieścić pliki statyczne w innej lokalizacji w pytaniu.
zeodtr
BTW, ponieważ muszę użyć ResourceLoader.gerResource ("/"), aby uzyskać plik w katalogu głównym dokumentu (prawda?), Muszę @Autowire ApplicationContext. Czy mogę DI kontekst aplikacji? Niektórzy mówią, że to zła rzecz.
zeodtr
Próbuję twojej sugestii. Jedna uwaga: moja nazwa fasoli nie może brzmieć „faviconHandlerMapping”, ponieważ powoduje to konflikt nazwy fasoli z fasolą faviconHandlerMapping Spring Boot. Metoda wywoływana przez organ.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludeAncestor‌s (), która jest ponownie wywoływana przez org.springframework.web.servlet.initHandlerMappings (), wybierze tylko jedną z 2 fasoli. Wygląda na to, że moja fasola jest ładowana wcześniej, a następnie zastępowana przez konflikt nazw.
zeodtr
Nazwy HandlerMapping fasoli zarejestrowane są następujące: { faviconHandlerMapping, requestMappingHandlerMapping, viewControllerHandlerMapping, beanNameHandlerMapping, resourceHandlerMapping, defaultServletHandlerMapping, endpointHandlerMapping} Więc muszę unikać tych nazwisk.
zeodtr
2
Powinieneś przeczytać zaktualizowaną odpowiedź, a także podręcznik użytkownika. Jeśli wszystko, co chcesz zrobić, to zmienić ikonę, nigdy nie była to bardzo trudna zmiana. Nie wiem, dlaczego OP miał tyle kłopotów.
Dave Syer
75

Nic z tego nie było mi potrzebne.

Po co zastępować wartość domyślną, skoro można spakować zasób z wygenerowanym plikiem JAR, który będzie miał wyższy priorytet niż domyślny.

Aby uzyskać niestandardowy favicon.icoplik, utworzyłem src/main/resourceskatalog dla mojej aplikacji, a następnie skopiowałem tam favicon.icoplik. Pliki w tym katalogu zasobów są przenoszone do katalogu głównego skompilowanego pliku JAR i dlatego Twój niestandardowy plik favicon.icoznajduje się przed plikiem dostarczonym przez Spring.

Wykonanie powyższego przyniosło taki sam efekt, jak zaktualizowane rozwiązanie powyżej.

Zauważ, że od wersji 1.2.0 możesz również umieścić plik w formacie src/main/resources/static.

Ross
źródło
6
Powód jest prosty. Nie chcę umieszczać pliku favicon w JAR.
zeodtr
@zeodtr tylko ciekawy dlaczego. Wydaje mi się to najbardziej naturalne. Jednak spodziewałbym się, że będzie to insrc/main/resources/static/images
btiernay
1
@btiernay Dzieje się tak, ponieważ serwer WWW jest oddzielną jednostką od klienta WWW. A favicon (y) należy do klienta. Plik wojenny serwera WWW nie ma powodu, aby dołączać pliki należące do klienta, które mogą być modyfikowane znacznie częściej w zależności od potrzeb użytkownika.
zeodtr
Tylko do Twojej wiadomości, można teraz rozwiązać favicon.icoz static: github.com/spring-projects/spring-boot/commit/ ...
btiernay
1
src / main / resources / static path działa dobrze. To najprostszy i najbardziej regularny sposób.
pdem
29

Bardzo podoba mi się Springboot, ponieważ ogólnie jest pełen inteligentnych rozwiązań, ale odmawiam zarejestrowania komponentu bean aplikacji, aby zapewnić favicon, ponieważ jest to po prostu głupie.

Właśnie dodałem własny link do favicon w nagłówku mojej strony html.

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

Następnie zmieniłem nazwę mojej ulubionej ikony i umieściłem ją pod adresem

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

Teraz mam ikonę na kartach przeglądarek Chrome i Firefox oraz pasek adresu Safari bez konieczności używania Springa i Javy i nie powinienem gonić zmian w Springboot w nowszych wersjach, aby uzyskać tak trywialną funkcjonalność.

jeremyjjbrown
źródło
4
Nie musisz już grzebać w mapowaniu handlerów (po prostu umieść favicon.ico w swoich statycznych zasobach).
Dave Syer
To takie proste! Dzięki. Dodaję to w moim fragmencie.
Lay Leangsros
co, jeśli używasz springboota tylko jako
zaplecza
@ParamvirSinghKarwal Jeśli masz tylko usługę, po co miałbyś chcieć favicon?
jeremyjjbrown
Jeśli trafisz na api bezpośrednio z adresu przeglądarki, wyświetli się domyślna ikona aplikacji wiosennej, która przypomina zielony liść. Chcę, żeby to była markowa favicon. Udało mi się to zrobić.
Paramvir Singh Karwal
13

Od wersji Spring Boot 1.2.2 i 1.1.11 możesz łatwo wyłączyć domyślną ikonę favicon za pomocą spring.mvc.favicon.enabled = falsewłaściwości.

Więcej informacji na stronie:

AKTUALIZACJA: nie ma już spring.mvc.favicon.enabledwłaściwości od wersji Spring Boot 2.2.x.

Trynkiewicz Mariusz
źródło
spring.mvc.favicon.enabled jest usuwana z bagażnika sprężyny 2.2. Czy wiesz, czym jest zastąpiony?
Shilan,
1
@Shilan zostało zastąpione niczym, ponieważ nie ma już domyślnego favicon - w ogóle. Jeśli chcesz, musisz skonfigurować mapowanie dla swojej własnej ikony ulubionych.
Trynkiewicz Mariusz
1
tak na końcu okazało się, że muszę to zrobić zamiast tego: @Override public void addResourceHandlers (rejestr ResourceHandlerRegistry) {register.addResourceHandler ("/ favicon.ico"). addResourceLocations ("ścieżka klasy: / static /"); }
Shilan,
2

Nowsze wersje Boot (na pewno 1.2, ale może także 1.1.8) pozwalają po prostu umieścić favicon.ico w zasobach statycznych.

Dave Syer
źródło
2

Wypróbowałem wiele opcji / wariantów, ale tylko to działało. (Spring Boot 2.2.7)

Pliki Favicon.ico i plik robots.txt umieszczają w / 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();
    }
}

i

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

Jeśli to pomoże, polub to;)

Yury Sliznikov
źródło
1

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

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

robots.txt, lokalizacja pliku favicon.ico: / src / main / resources

użyłem buta sprężynowego 1.2.1

webmadeup
źródło