No ecossistema Spring MVC, o ciclo de vida de uma requisição HTTP atravsesa diversas camadas antes de atingir o controlador e retornar a resposta ao cliente. Inicialmente, a requisição passa pela cadeia de Filtros (Filters), executando o método doFilter() de cada compoennte configurado. Em seguida, entra no método service() do Servlet e é encaminhada pelo DispatcherServlet para o Controlador mapeado. Antes da execução do método do Controlador, o Interceptador (Interceptor) executa preHandle(). Após a execução do Controlador e antes da resolução da visualização, postHandle() é invocado. Por fim, afterCompletion() é chamado após a renderização, e o fluxo retorna pela cadeia de Filtros para processar a resposta final.
Filtros (Filters) no Nível do Servlet
Os Filtros operam no nível do contêiner Servlet, interceptando objetos HttpServletRequest e HttpServletResponse. Eles possuem um escopo mais amplo e são ideais para tarefas transversais como validação de tokens de segurança, configuração de cabeçalhos CORS, compressão de dados e bloqueio de requisições maliciosas. Um filtro implementa a interface Filter e gerencia seu ciclo de vida através dos métodos init(), doFilter() e destroy(), sendo inicializado e destruído junto com o contêiner.
Exemplo de implementação de um filtro de autenticação:
package com.exemplo.app.seguranca;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
public class FiltroAutenticacao implements Filter {
@Override
public void init(FilterConfig config) throws ServletException {
System.out.println("Inicializando o filtro de autenticação...");
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpReq = (HttpServletRequest) req;
String token = httpReq.getHeader("Authorization");
// Lógica de validação do token de acesso
System.out.println("Validando credenciais para a URI: " + httpReq.getRequestURI());
// Continua a cadeia de filtros
chain.doFilter(req, res);
}
@Override
public void destroy() {
System.out.println("Encerrando o filtro de autenticação.");
}
}
Para registrar este filtro no contexto do Spring Boot e integrá-lo ao contêiner, utilizamos o FilterRegistrationBean:
package com.exemplo.app.configuracao;
import com.exemplo.app.seguranca.FiltroAutenticacao;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ConfiguracaoFiltros {
@Bean
public FilterRegistrationBean<FiltroAutenticacao> registrarFiltroAutenticacao() {
FilterRegistrationBean<FiltroAutenticacao> registro = new FilterRegistrationBean<>();
registro.setFilter(new FiltroAutenticacao());
registro.addUrlPatterns("/api/*");
registro.setName("filtroDeAutenticacao");
registro.setOrder(1);
return registro;
}
}
Interceptadores (Interceptors) no Nível do Spring
Diferente dos filtros, os Interceptadores são componentes nativos do framework Spring e não dependem do contêiner Servlet. Baseados no paradigma de Programação Orientada a Aspectos (AOP), eles permitem interceptar chamadas de métodos específicos dos Controladores com uma granularidade muito maior. Por estarem integrados ao contexto Spring, podem injetar e utilizar outros beans gerenciados. A interface principal é a HandlerInterceptor, que fornece os métodos preHandle(), postHandle() e afterCompletion().
Exemplo de um interceptador para registro de métricas e logs de execução:
package com.exemplo.app.interceptadores;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class InterceptadorDeLog implements HandlerInterceptor {
private static final Logger log = LoggerFactory.getLogger(InterceptadorDeLog.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
log.info("Iniciando processamento do endpoint: {}", request.getRequestURI());
request.setAttribute("inicioExecucao", System.currentTimeMillis());
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
log.info("Processamento do endpoint concluído, preparando a resposta.");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
long inicio = (Long) request.getAttribute("inicioExecucao");
long duracao = System.currentTimeMillis() - inicio;
log.info("Tempo total de execução para {}: {} ms", request.getRequestURI(), duracao);
}
}
O registro do interceptador é feito implementando a interface WebMvcConfigurer e sobrescrevendo o método de configuração:
package com.exemplo.app.configuracao;
import com.exemplo.app.interceptadores.InterceptadorDeLog;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class ConfiguracaoWebMvc implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new InterceptadorDeLog())
.addPathPatterns("/api/**")
.excludePathPatterns("/api/publico/**");
}
}