Spring Boot: Ghi đè biểu tượng yêu thích


83

Làm cách nào để ghi đè biểu tượng yêu thích của Spring Boot?

LƯU Ý : Đây là một câu hỏi khác của tôi cung cấp một giải pháp khác không liên quan đến bất kỳ mã hóa nào: Spring Boot: Có thể sử dụng các tệp application.properties bên ngoài trong các thư mục tùy ý với một lọ béo không? Nó dành cho application.properties, nhưng nó cũng có thể được áp dụng cho favicon. Trên thực tế, tôi đang sử dụng phương pháp đó để ghi đè biểu tượng yêu thích.

Nếu tôi triển khai một lớp có @EnableWebMvc, thì lớp WebMvcAutoConfiguration của Spring Boot không tải và tôi có thể phục vụ biểu tượng yêu thích của riêng mình bằng cách đặt nó vào thư mục gốc của nội dung tĩnh.

Nếu không, WebMvcAutoConfiguration đăng ký đậu faviconRequestHandler, (xem nguồn https://github.com/spring-projects/spring-boot/blob/master/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ web / WebMvcAutoConfiguration.java ) và nó phục vụ biểu tượng 'lá xanh' được đặt trong thư mục tài nguyên chính của Spring Boot.

Làm cách nào để tôi có thể ghi đè nó mà không cần triển khai một lớp có @EnableWebMvc, do đó vô hiệu hóa toàn bộ chức năng cấu hình mặc định của lớp WebMvcAutoConfiguration của Spring Boot?

Ngoài ra, vì tôi muốn tệp biểu tượng được cập nhật càng sớm càng tốt ở phía máy khách (trình duyệt web), tôi muốn đặt khoảng thời gian lưu trong bộ nhớ cache của tệp favicon thành 0 (như mã sau, tôi đang sử dụng cho nội dung ứng dụng web 'tĩnh' và các tệp tập lệnh phải được cập nhật ở phía máy khách càng sớm càng tốt sau khi tôi thay đổi tệp.)

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

Vì vậy, chỉ để tìm nơi lưu tệp favicon.ico mà các danh hiệu faviconRequestHandler của Spring Boot có thể là không đủ.

CẬP NHẬT

Bây giờ tôi biết rằng tôi có thể ghi đè tệp mặc định bằng cách đặt tệp biểu tượng yêu thích vào thư mục src / main / resources. Nhưng vấn đề khoảng thời gian bộ nhớ cache vẫn còn.
Ngoài ra, bạn nên đặt tệp favicon vào thư mục chứa các tệp web tĩnh, hơn là thư mục tài nguyên.

CẬP NHẬT

Được rồi, tôi đã ghi đè cài đặt mặc định. Những gì tôi đã làm như sau:

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

Về cơ bản, tôi ghi đè cái mặc định bằng cách thêm trình xử lý tài nguyên có thứ tự cao nhất bằng cách gọi registry.setOrder (Integer.MIN_VALUE).

Vì giá trị mặc định trong Spring Boot có giá trị thứ tự (Integer.MIN_VALUE + 1), (xem lớp FaviconConfiguration trong https://github.com/spring-projects/spring-boot/blob/master/spring-boot-autoconfigure/ src / main / java / org / springframework / boot / autoconfigure / web / WebMvcAutoConfiguration.java ) trình xử lý của tôi thắng.

Cái này có ổn không? Có cách nào khác (điều gì đó nhẹ nhàng hơn những gì tôi đã làm) không?

CẬP NHẬT

Không ổn. Khi tôi gọi registry.setOrder(Integer.MIN_VALUE), thực sự tôi nâng mức ưu tiên của tất cả các trình xử lý tài nguyên. Vì vậy, khi tôi thêm mã sau vào một mã khác WebMvcConfigurerAdapter, thực tế tất cả yêu cầu http đều được chuyển hướng đến trình xử lý tài nguyên đó, ngăn chặn bất kỳ xử lý động nào bằng mã Java.

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

Một giải pháp khác là cần thiết.

CẬP NHẬT

Hiện tại, tôi không thể tìm thấy cách ghi đè chức năng biểu tượng yêu thích mà Spring Boot cung cấp.
Có lẽ có một cách để thêm HandlerMappingđậu của riêng tôi , nhưng tôi không biết làm thế nào để làm điều đó.

Bây giờ tôi có thể chọn một trong các tùy chọn sau:

  1. Có một lớp đã @EnableWebMvcvô hiệu hóa WebMvcAutoConfigurationlớp Spring Boot . (Tôi có thể sao chép mã của WebMvcAutoConfigurationlớp và xóa chức năng biểu tượng yêu thích)
  2. Từ bỏ quyền tự do đặt tệp favicon vào vị trí trọng tài và đặt nó vào thư mục tài nguyên như chức năng favicon của Spring Boot yêu cầu. Và bỏ qua vấn đề bộ nhớ đệm.

Nhưng không có lựa chọn nào là thỏa đáng.
Tôi chỉ muốn đặt tệp favicon với các tệp web tĩnh của mình (có thể là bất kỳ thư mục nào vì tôi có thể thay đổi gốc tài liệu) và giải quyết vấn đề bộ nhớ đệm.
Tui bỏ lỡ điều gì vậy?
Bất kỳ đề nghị sẽ được đánh giá rất cao.

CẬP NHẬT

BTW, lý do tôi muốn thay đổi vị trí của favicon và các tệp tĩnh khác như sau. Còn bây giờ chủ yếu là vấn đề môi trường phát triển.

Tôi đang xây dựng một ứng dụng web một trang (SPA).

Thư viện / Khung:

  • Đối với phía máy chủ, tôi sử dụng Spring. (tất nhiên)
  • Đối với phía máy khách (trình duyệt web), tôi sử dụng AngularJS.

Công cụ:

  • Đối với phía máy chủ, tôi sử dụng Spring Tool Suite.
  • Đối với phía khách hàng, tôi sử dụng WebStorm.

Cấu trúc thư mục chính:

ProjectRoot\
    src\
    bin\
    build\
    webapp\
    build.gradle
  • src: Nơi các tệp nguồn java Spring của tôi cư trú.
  • bin: Nơi Spring Tool Suite đặt đầu ra bản dựng của nó.
  • build: Nơi 'gradle build' đặt đầu ra của nó.
  • webapp: Nơi chứa các tệp nguồn khách hàng của tôi (.js, .css, .htm và favicon). Vì vậy, đây là thư mục dự án WebStorm. (Tôi có thể thay đổi tên thư mục nếu cần)

Điều tôi muốn là:

  • Để có thể sửa đổi và kiểm tra mã máy khách của tôi mà không cần xây dựng lại / khởi động lại ứng dụng máy chủ Spring của tôi. Vì vậy, mã máy khách không được đưa vào tệp jar. Dù sao thì Spring Tool Suite hoàn toàn không tạo một tệp jar (ít nhất là đối với cấu hình hiện tại)
  • Để có thể kiểm tra ứng dụng máy chủ Spring của tôi bằng mã máy khách, dễ dàng chuyển đổi giữa đầu ra Spring Tool Suite và đầu ra gradle. Vì vậy, mã máy khách phải có thể truy cập được từ cả ứng dụng máy chủ trong buildthư mục con (thực tế build\libs) và ứng dụng máy chủ trong binthư mục.
  • Khi tôi sửa đổi mã khách hàng, mã này phải có sẵn ngay lập tức cho trình duyệt web. Vì vậy, trình duyệt không được lưu vào bộ nhớ cache vô thời hạn và phải luôn yêu cầu máy chủ cập nhật.
  • Khi được triển khai, mã máy khách phải có thể sửa đổi được mà không cần xây dựng lại / khởi động lại ứng dụng máy chủ. Vì vậy mã máy khách không được đưa vào tệp jar.

Về vấn đề bộ nhớ cache:

Không có setCachePeriod (0) trên addResourceHandlers (), Google Chrome lưu tệp vào bộ nhớ cache vô thời hạn mà không yêu cầu máy chủ cập nhật. Nó thậm chí không kết nối với máy chủ. (Các kỹ sư của Google nói rằng hành vi đó là đúng.) Vì vậy, tất cả những gì tôi có thể làm là xóa bộ nhớ cache của trình duyệt theo cách thủ công. Nó gây thất vọng về môi trường phát triển, và không thể chấp nhận được về môi trường sản xuất.

Mô-đun BTW, express.js trên Node.js cung cấp tiêu đề HTTP mặc định hợp lý để Google Chrome yêu cầu máy chủ cập nhật. Khi tôi xem xét các tiêu đề HTTP mà Spring và express.js tạo ra bằng Fiddler, chúng khác nhau.

Bất kỳ đề xuất để cải thiện môi trường của tôi sẽ được đánh giá cao.
Vì tôi là một người mới bắt đầu mùa Xuân, tôi có thể thiếu một cái gì đó.

CẬP NHẬT

Cuối cùng tôi có một mã làm việc. Nó như sau:

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

Chú ý tên các loại đậu. Tôi đã thêm 'của tôi' để tránh đụng độ tên.
Bản thân ngữ cảnh ứng dụng Autowiring có vẻ khó xử, nhưng nó là cần thiết để bắt chước mã trong org.springframework.web.servlet.config.annotation.ResourceHandlerRegistration.addResourceLocations().

Bây giờ tôi có một trình xử lý biểu tượng yêu thích không gặp sự cố bộ nhớ đệm và tôi có thể đặt tệp biểu tượng yêu thích ở bất cứ đâu tôi muốn.
Cảm ơn.


1
Phương pháp của bạn có vẻ ổn. Tuy nhiên, nó không thay thế trình xử lý tài nguyên Spring MVC mặc định (vì vậy, các tài nguyên tĩnh sẽ không được cung cấp từ classpath:/staticbất kỳ nơi nào nữa). (Đó là lý do tại sao sự hỗ trợ favicon trong Boot là trong HandlerMapping riêng của mình tôi nghĩ.)
Dave Syer

@DaveSyer Bây giờ tôi nhận ra rằng nó không ổn. Tôi đã viết bản cập nhật thứ ba ở trên. Có lẽ HandlerMappingcần tách biệt .
zeodtr

@DaveSyer Vui lòng xem bản cập nhật thứ tư của tôi ở trên. Còn cách nào khác không? Ví dụ, tôi có thể đăng ký của riêng tôi HandlerMapping?
zeodtr

Có, bạn có thể thêm một HandlerMapping(chỉ cần sao chép một cái từ Boot và ưu tiên nó cao hơn). Tôi không thực sự chắc chắn tại sao bạn lại quan tâm đến vị trí của favicon.ico filenó nhưng chúng tôi có thể làm cho nó có thể định cấu hình nếu bạn muốn. Tất cả các trình duyệt cũng sẽ lưu vào bộ nhớ cache, vì vậy tôi nghi ngờ cài đặt bộ nhớ cache có vấn đề, nhưng rất vui khi được chứng minh là sai.
Dave Syer 19/1213

@DaveSyer Vui lòng xem câu hỏi cập nhật để biết lý do tại sao tôi muốn đặt các tệp tĩnh ở vị trí khác. Ngoài ra là vấn đề bộ nhớ cache.
zeodtr

Câu trả lời:


53

Bạn chỉ có thể đặt favicon.ico của riêng mình trong thư mục gốc của classpath hoặc trong bất kỳ vị trí tài nguyên tĩnh nào (ví dụ classpath:/static). Bạn cũng có thể tắt hoàn toàn độ phân giải biểu tượng yêu thích chỉ với một lá cờ spring.mvc.favicon.enabled=false.

Hoặc để kiểm soát hoàn toàn, bạn có thể thêm HandlerMapping (chỉ cần sao chép cái từ Boot và ưu tiên nó cao hơn), ví dụ:

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

Cảm ơn bạn. Tôi nghĩ rằng tôi đã thử nó, nhưng bây giờ tôi nghĩ lại, tôi đã bỏ qua @Configurationchú thích. BTW, tôi đã viết lý do tại sao tôi muốn đặt các tệp tĩnh ở vị trí khác trong câu hỏi.
zeodtr

BTW, vì tôi phải sử dụng ResourceLoader.gerResource ("/") để lấy tệp trong gốc tài liệu (phải không?), Tôi phải @Autowire ApplicationContext. Có Ok để DI bối cảnh ứng dụng không? Một số nói rằng đó là điều tồi tệ.
zeodtr

Tôi đang thử gợi ý của bạn. Một điều cần lưu ý: tên bean của tôi không được là 'faviconHandlerMapping', vì nó khiến tên bean xung đột với bean faviconHandlerMapping của Spring Boot. Chỉ một trong 2 bean sẽ được chọn bằng một phương thức được gọi bởi org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestor‌ s (). Phương thức này lại được gọi bởi org.springframework.web.servlet.initHandlerMappings (). Có vẻ như bean của tôi được tải trước đó sau đó bị ghi đè bởi tên clash.
zeodtr

Tên của đậu HandlerMapping đăng ký như sau: { faviconHandlerMapping, requestMappingHandlerMapping, viewControllerHandlerMapping, beanNameHandlerMapping, resourceHandlerMapping, defaultServletHandlerMapping, endpointHandlerMapping} Vì vậy, tôi phải tránh những cái tên đó.
zeodtr

2
Bạn nên đọc câu trả lời được cập nhật và cả hướng dẫn sử dụng. Nếu tất cả những gì bạn muốn làm là thay đổi biểu tượng thì đó không bao giờ là một thay đổi quá khó. Tôi không biết tại sao OP lại gặp nhiều rắc rối như vậy.
Dave Syer

75

Không điều gì trong số này là cần thiết đối với tôi.

Tại sao phải ghi đè mặc định khi bạn có thể nhóm một tài nguyên với JAR được tạo sẽ được ưu tiên cao hơn mặc định.

Để đạt được favicon.icotệp tùy chỉnh , tôi đã tạo một src/main/resourcesthư mục cho ứng dụng của mình và sau đó sao chép favicon.icotệp vào đó. Các tệp trong thư mục tài nguyên này được chuyển đến thư mục gốc của JAR đã biên dịch và do đó tùy chỉnh của bạn favicon.icođược tìm thấy trước khi Spring cung cấp.

Làm ở trên đạt được hiệu quả tương tự như giải pháp cập nhật của bạn ở trên.

Lưu ý rằng kể từ v1.2.0, bạn cũng có thể đưa tệp vào src/main/resources/static.


6
Lý do rất đơn giản. Tôi không muốn đặt tệp favicon vào JAR.
zeodtr

@zeodtr chỉ tò mò tại sao. Điều này có vẻ tự nhiên nhất đối với tôi. Tuy nhiên, tôi đã mong đợi nó sẽ đến trongsrc/main/resources/static/images
btiernay.

1
@btiernay Đó là vì máy chủ web là một thực thể riêng biệt với máy khách web. Và (các) biểu tượng yêu thích thuộc về khách hàng. Tệp chiến tranh của máy chủ web không có lý do gì để bao gồm các tệp thuộc về máy khách có thể được sửa đổi thường xuyên hơn theo nhu cầu của người dùng.
zeodtr

Chỉ là FYI, hiện có thể giải quyết favicon.icotừ static: github.com/spring-projects/spring-boot/commit/…
btiernay 10/10/14

1
src / main / resources / static path hoạt động tốt. Đây là cách đơn giản nhất và thường xuyên để làm.
pdem

29

Tôi thực sự thích Springboot vì nhìn chung nó có đầy đủ các giải pháp thông minh nhưng tôi từ chối đăng ký bean ứng dụng để cung cấp biểu tượng yêu thích vì điều đó thật ngu ngốc.

Tôi vừa thêm liên kết favicon của riêng mình vào đầu trang html của tôi như vậy.

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

Sau đó, tôi đổi tên biểu tượng yêu thích của mình và đặt nó ở

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

Bây giờ tôi có một biểu tượng trong các tab trình duyệt Chrome và Firefox và thanh địa chỉ Safari mà không cần phải sử dụng Spring và Java, và tôi không cần phải theo đuổi các thay đổi đối với Springboot trong các phiên bản mới hơn cho các chức năng tầm thường như vậy.


4
Bạn không cần phải loay hoay với các ánh xạ trình xử lý nữa (chỉ cần đặt favicon.ico trong tài nguyên tĩnh của bạn).
Dave Syer

Đơn giản vậy thôi! Cảm ơn. Tôi thêm nó vào phân đoạn của tôi.
Lay Leangsros

gì xảy ra nếu bạn đang sử dụng springboot chỉ như là một API dịch vụ phụ trợ, bạn không có bất kỳ trang html có: /
Paramvir Singh Karwal

@ParamvirSinghKarwal Nếu bạn chỉ có một dịch vụ, tại sao bạn lại muốn một biểu tượng yêu thích?
jeremyjjbrown

Nếu bạn nhấn trực tiếp vào api từ địa chỉ của trình duyệt, nó sẽ hiển thị một biểu tượng mặc định của ứng dụng mùa xuân giống như một chiếc lá xanh. Tôi muốn nó trở thành một biểu tượng yêu thích có thương hiệu. Tôi đã có thể làm được.
Paramvir Singh Karwal

13

Kể từ Spring Boot 1.2.2 và 1.1.11, bạn có thể dễ dàng vô hiệu hóa biểu tượng yêu thích mặc định bằng thuộc spring.mvc.favicon.enabled = falsetính.

Để biết thêm thông tin, hãy truy cập:

CẬP NHẬT: không còn thuộc spring.mvc.favicon.enabledtính kể từ Spring Boot 2.2.x


spring.mvc.favicon.enabled được gỡ bỏ khỏi khởi động mùa xuân 2.2. Bạn có biết nó được thay thế bằng gì không?
Shilan

1
@Shilan nó được thay thế bằng không vì không còn biểu tượng yêu thích mặc định nữa - ở tất cả. Nếu bạn muốn một cái, bạn phải định cấu hình ánh xạ cho biểu tượng yêu thích của riêng bạn.
Trynkiewicz Mariusz

1
vâng ở phần cuối, tôi phát hiện ra rằng tôi cần phải thực hiện việc này thay thế: @Override public void addResourceHandlers (đăng ký ResourceHandlerRegistry) {registry.addResourceHandler ("/ favicon.ico"). addResourceLocations ("classpath: / static /"); }
Shilan

2

Các phiên bản Boot mới hơn (chắc chắn là 1.2 nhưng cũng có thể là 1.1.8) cho phép bạn chỉ cần đặt favicon.ico trong tài nguyên tĩnh của mình.


2

Tôi đã thử rất nhiều tùy chọn / biến thể, nhưng chỉ có cách này mới hoạt động. (Khởi động mùa xuân 2.2.7)

Tệp Favicon.ico & robots.txt được đưa vào / 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/");
  }
}

Nếu nó hữu ích, hãy thích nó;)


1

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

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

Vị trí tệp robots.txt, favicon.ico: / src / main / resources

tôi đã sử dụng khởi động lò xo 1.2.1

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.