Gerenciando Recursos Estáticos em Aplicações Spring MVC

Ao desenvolver aplicações web com Spring MVC, um desafio comum surge quando o DispatcherServlet é configurado para interceptar todas as requisições (por exemplo, usando um padrão de URL como /). Nesses casos, os arquivos estáticos, como imagens, folhas de estilo CSS e scripts JavaScript, também são interceptados pelo Spring, resultando em erros 404, pois o DispatcherServlet não encontra nenhum controlador para esses recursos.

Este artigo explora diferentes abordagens para garantir que o Spring MVC possa servir recursos estáticos de forma eficaz, permitindo que a aplicação exiba corretamente seu conteúdo visual e funcional.

  1. Delegação ao Servlet Padrão do Contêiner via web.xml

Uma maneira de contornar a interceptação total pelo DispatcherServlet é configurar o web.xml para que o servlet padrão do contêiner (como o Tomcat) lide com tipos de arquivos estáticos antes que as requisições cheguem ao Spring. Esta solução envolve mapear extensões de arquivo específicas para o servlet padrão diretamente no descritor de implantação.

É crucial que esta configuração de mapeamento de servlet para os recursos estáticos seja definida antes da configuração do DispatcherServlet no arquivo web.xml. Dessa forma, o contêiner web processa esses arquivos estáticos primeiro, e as requisições para eles nunca chegam ao Spring MVC.

Exemplo de Configuração web.xml:


<!-- Mapeia extensões de arquivos estáticos para o servlet padrão -->
<servlet-mapping>
   <servlet-name>default</servlet-name>
   <url-pattern>*.jpg</url-pattern>
</servlet-mapping>
<servlet-mapping>
   <servlet-name>default</servlet-name>
   <url-pattern>*.png</url-pattern>
</servlet-mapping>
<servlet-mapping>
   <servlet-name>default</servlet-name>
   <url-pattern>*.css</url-pattern>
</servlet-mapping>
<servlet-mapping>
   <servlet-name>default</servlet-name>
   <url-pattern>*.js</url-pattern>
</servlet-mapping&

<!-- Configuração do DispatcherServlet do Spring MVC -->
<servlet>
   <servlet-name>springMvcDispatcher</servlet-name>
   <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
   <init-param>
       <param-name>contextConfigLocation</param-name>
       <param-value>/WEB-INF/spring-mvc-config.xml</param-value>
   </init-param>
   <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
   <servlet-name>springMvcDispatcher</servlet-name>
   <url-pattern>/</url-pattern>
</servlet-mapping>

O nome do servlet padrão pode variar entre os servidroes de aplicação:

  • Tomcat, Jetty, JBoss, GlassFish: default
  • Google App Engine: _ah_default
  • Resin: resin-file
  • WebLogic: FileServlet
  • WebSphere: SimpleFileServlet
  1. Configuração de Recursos Estáticos com <mvc:resources/>

A partir do Spring 3.0.4, o framework oferece uma maneira mais integrada de gerenciar recursos estáticos através do elemento <mvc:resources/>. Esta abordagem permite que o Spring mapeie explicitamente padrões de URL para localizações de recursos no sistema de arquivos, classpath ou mesmo dentro de JARs.

Para utilizar <mvc:resources/>, é necessário habilitar as configurações do Spring MVC baseadas em anotações com <mvc:annotation-driven/>. Sem esta tag, requisições que normalmente seriam tratadas por controladores podem falhar com 404.

Exemplo de Configuração XML do Spring:


<!-- Habilita o suporte a anotações do Spring MVC -->
<mvc:annotation-driven/>

<!-- Mapeia URLs que começam com /static/ para a pasta /resources/ no projeto -->
<mvc:resources mapping="/static/**" location="/resources/" />

<!-- Outros exemplos: -->
<!-- Mapeia /img/** para /WEB-INF/images/ -->
<mvc:resources mapping="/img/**" location="/WEB-INF/images/" />
<!-- Mapeia /assets/** para a pasta 'assets' no classpath -->
<mvc:resources mapping="/assets/**" location="classpath:/assets/" />

A tag <mvc:resources/> registra um SimpleUrlHandlerMapping que mapeia o padrão mapping (e.g., /static/**) para um ResourceHttpRequestHandler. Este manipulador é responsável por servir os arquivos da location especificada, suportando acesso a recursos em diversos locais, incluindo o classpath e dentro de arquivos JAR.

É importante evitar configurar um defaultHandler para o SimpleUrlHandlerMapping gerado por <mvc:resources/>, pois o ResourceHttpRequestHandler já atua como o manipulador padrão para esses URIs estáticos.

  1. Delegação ao Servlet Padrão do Contêiner via <mvc:default-servlet-handler/>

Esta é outra solução fornecida pelo Spring para delegar requisições de recursos estáticos diretamente ao servlet padrão do contêiner. Semelhante a <mvc:resources/>, ela também exige que <mvc:annotation-driven/> esteja configurado.

A principal diferença para a abordagem 1 é que a delegação é orquestrada pelo Spring, em vez de ser uma regra de mapeamento direta no web.xml. O Spring configura um DefaultServletHttpRequestHandler que, por sua vez, repassa a requisição para o servlet padrão do contêiner.

Exemplo de Configuração XML do Spring:


<!-- Habilita o suporte a anotações do Spring MVC -->
<mvc:annotation-driven/>

<!-- Delega requisições de recursos estáticos ao servlet padrão do contêiner -->
<mvc:default-servlet-handler/>

Esta configuração registra um SimpleUrlHandlerMapping para o padrão /**, delegando a responsabilidade de servir os recursos ao org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler. Este manipulador utiliza o servlet padrão do contêiner web para processar a requisição, garantindo que arquivos estáticos sejam servidos corretamente.

Ordem de Execução dos HandlerMappings no Spring

Compreender a ordem de processamento dos HandlerMappings no Spring é fundamental para entender como essas soluções funcionam em conjunto. O Spring processa os manipuladores com base em seus valores de ordem (order), do menor para o maior.

  • DefaultAnnotationHandlerMapping (utilizado por @Controller): order = 0
  • SimpleUrlHandlerMapping (registrado por <mvc:resources/>): order = 2147483646 (um valor alto, mas menor que o próximo)
  • SimpleUrlHandlerMapping (registrado por <mvc:default-servlet-handler/>): order = 2147483647 (o maior valor padrão)

Quando uma requisição para um arquivo estático (por exemplo, /images/logo.png) chega ao DispatcherServlet, o Spring tenta encontrar um manipulador na seguinte ordem:

  1. Primeiro, o DefaultAnnotationHandlerMapping tenta encontrar um controlador (@Controller) que corresponda a /images/logo.png. Como não existe, a requisição segue em frente.
  2. Em seguida, o SimpleUrlHandlerMapping configurado por <mvc:resources/> (se presente) verifica se o padrão /images/** (ou similar) corresponde à requisição. Se sim, o ResourceHttpRequestHandler serve o arquivo.
  3. Finalmente, se nenhuma das opções anteriores tratou a requisição, o SimpleUrlHandlerMapping registrado por <mvc:default-servlet-handler/> intercepta a requisição com seu padrão genérico /**. Este manipulador então delega a requisição ao servlet padrão do contêiner para que ele sirva o recurso estático.

Essa hierarquia de ordem garante que o Spring tente primeiro encontrar um controlador específico e, se não encontrar, gradualmente delega a responsabilidade para mecanismos mais genéricos de tratamento de recursos estáticos.

Tags: Spring MVC Recursos Estáticos Web.xml DispatcherServlet mvc:resources

Publicado em 7-25 15:56