Por que existem 2 maneiras de manipular resources estáticos no Spring (addResourceHandlers e o Servlet padrão do contêiner)?

Eu sou novo no Spring. Percebi que ao manipular resources estáticos, existem duas opções disponíveis:


Opção 1:

Se o DispatcherServlet do Spring for mapeado para / com o código abaixo, o que o torna o “Servlet Padrão”, é possível mapear determinados resources estáticos para os manipuladores Spring com a anotação RequestMapping (substituindo a class AbstractAnnotationConfigDispatcherServletInitializer ):

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

Então, ainda podemos ativar o “Servlet Padrão” do contêiner para manipular esses resources estáticos cujo padrão de URL não é coberto pelo mapeamento de solicitação do Spring (substituindo a class WebMvcConfigurerAdapter ):

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

Isso basicamente usa o “Servlet Padrão” do contêiner do servlet como o pega-tudo para lidar com todos os resources estáticos perdidos pelo DispatcherServlet do Spring.


Opção 2:

(substituindo a class WebMvcConfigurerAdapter )

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

  • Por que existem duas opções?
  • Quais são as principais diferenças entre essas abordagens?
  • Existem outras opções?

Eu costumo pegar a opção 2 porque eu quero ficar com a spring, mas sei que não é uma razão forte.


Alguma referência relacionada ao manuseio de resources estáticos:

  • Servir resources estáticos com mola
  • Spring Framework 4.1 – lidando com resources da Web estáticos
  • Spring MVC – Como include arquivos JS ou CSS em uma página JSP

ADICIONAR 1

Parece que a opção 2 oferece muito mais flexibilidade em relação ao mapeamento de resources. E até mesmo resources dentro WEB-INF pasta WEB-INF podem ser mapeados.

Aqui está um exemplo concreto de quando Falling Back no servlet “padrão” para servir resources não é aplicável.

Esta é uma implementação típica da abordagem acima:

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

No entanto, a melhor prática atual para lidar com erros 404 no Spring 4 parece ser usar setThrowExceptionIfNoHandlerFound :

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

Infelizmente, de acordo com a documentação do DispatcherServlet :

Observe que, se DefaultServletHttpRequestHandler for usado, as solicitações sempre serão encaminhadas para o servlet padrão e uma NoHandlerFoundException nunca será lançada nesse caso.

Realmente, esse é o caso. Combinar as duas abordagens acima não resulta em um NoHandlerFoundException sendo acionado, e isso, por sua vez, impede que minha página de erro 404 customizada seja resolvida. Agora, se eu fosse comentar meu método configureDefaultServletHandling , o NoHandlerFoundException é lançado e meu tratamento de erros (via @ControllerAdvice como mostrado na resposta vinculada) resolve o meu personalizado ‘notFoundPage’.

Infelizmente, isso agora significa que meus resources estáticos (isto é, ‘default.css’) não são resolvidos:

 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 

Eu não vejo nenhuma maneira de conciliar essas duas abordagens para que elas não interfiram umas nas outras. Minha conclusão é que a abordagem “Servlet Padrão” não é apropriada para servir resources estáticos neste caso, o que nos deixa com o método addResourceHandlers .

Entre os benefícios do uso do método addResourceHandlers estão:

  • … serve resources estáticos de locais diferentes da raiz do aplicativo da Web, incluindo locais no caminho de class .
  • A propriedade cache-period pode ser usada para definir headers de expiração futuros distantes para que eles sejam utilizados de forma mais eficiente pelo cliente.
  • O manipulador também avalia adequadamente o header Last-Modified (se presente) para que um código de status 304 seja retornado conforme apropriado, evitando sobrecarga desnecessária para resources já armazenados em cache pelo cliente.

Veja também esta resposta para um exemplo mais complicado de como lidar com resources estáticos com o servlet padrão pode causar efeitos colaterais indesejados.