Почему существует 2 способа обработки статических ресурсов в Spring (addResourceHandlers и контейнер Servert по умолчанию)?

Я новичок в Spring. Я заметил, что при обработке статических ресурсов доступны два варианта:


Вариант 1:

Если Spring DispatcherServlet сопоставляется с / с приведенным ниже кодом, что делает его "Servlet по умолчанию", можно сопоставить определенные статические ресурсы с обработчиками Spring с RequestMapping аннотация (переопределение класса AbstractAnnotationConfigDispatcherServletInitializer):

@Override
protected String[] getServletMappings() {
    return new String[]{"/"};
}

Затем мы все же можем включить контейнер "Servlet по умолчанию" для обработки тех статических ресурсов, шаблон URL которых не покрывается Spring отображением запроса (переопределение класса WebMvcConfigurerAdapter):

@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
    configurer.enable();
}

В основном используется контейнер сервлета "Сервлет по умолчанию" в качестве catch-all для обработки всех статических ресурсов пропущенных Spring DispatcherServlet.


Вариант 2:

(переопределение класса WebMvcConfigurerAdapter)

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    super.addResourceHandlers(registry);
    registry.addResourceHandler("*.efi").addResourceLocations("/");
}

  • Почему существуют два варианта?
  • Каковы основные различия между этими подходами?
  • Есть ли другие варианты?

Обычно я беру опцию 2, потому что хочу придерживаться Spring, но я знаю, что это не сильная причина.


Некоторая ссылка, относящаяся к обработке статических ресурсов:


ДОБАВИТЬ 1

Кажется, что вариант 2 обеспечивает гораздо большую гибкость в отношении сопоставления ресурсов. И даже ресурсы в папке WEB-INF могут быть отображены.

Ответ 1

Вот конкретный пример того, когда Падение назад на "Сервлет по умолчанию" для обслуживания ресурсов не применимо.

Это типичная реализация вышеупомянутого подхода:

@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer)
{
    configurer.enable();
    return;
}

Однако актуальная передовая практика для обработки 404 ошибок в Spring 4 заключается в использовании setThrowExceptionIfNoHandlerFound:

@Override
protected DispatcherServlet createDispatcherServlet(WebApplicationContext servletAppContext)
{
    DispatcherServlet dispatcherServlet = (DispatcherServlet) super.createDispatcherServlet(servletAppContext);
    dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
    return dispatcherServlet;
}

К сожалению, согласно документации для DispatcherServlet:

Обратите внимание, что если DefaultServletHttpRequestHandler, то запросы всегда будет перенаправлен на сервлет по умолчанию и NoHandlerFoundException никогда не будет брошен в этом случае.

В самом деле, это так. Объединение обоих этих подходов не приводит к срабатыванию NoHandlerFoundException, и это, в свою очередь, препятствует разрешению моей страницы ошибок 404. Теперь, если я должен был прокомментировать мой метод configureDefaultServletHandling, будет сброшен NoHandlerFoundException, и моя обработка ошибок (через @ControllerAdvice, как показано в связанном ответе) разрешится к моей пользовательской 'notFoundPage'.

К сожалению, теперь это означает, что мои статические ресурсы (т.е. "default.css" ) не разрешены:

DEBUG org.springframework.web.servlet.DispatcherServlet - Handler execution resulted in exception - forwarding to resolved error view: ModelAndView: reference to view with name 'notFoundPage'; model is {}
org.springframework.web.servlet.NoHandlerFoundException: No handler found for GET /webapp-test/style/default.css

Я не вижу никакого способа примирить эти два подхода, чтобы они не мешали друг другу. Мое заключение заключается в том, что подход "Сервлет по умолчанию" не подходит для обслуживания статических ресурсов в этом случае, что оставляет нам метод addResourceHandlers.

Среди преимуществ использования метода addResourceHandlers:

  • ... обслуживать статические ресурсы из других мест, отличных от корня веб-приложения, включая местоположения в пути к классам.
  • Свойство cache-period может использоваться для установки больших будущих заголовков срока действия, чтобы клиент мог более эффективно использовать его.
  • Обработчик также правильно оценивает заголовок Last-Modified (если он есть), так что код состояния 304 будет возвращен соответствующим образом, избегая ненужных накладных расходов для ресурсов, которые уже кэшированы клиентом.