Interceptadores Spring são usados para interceptar solicitações do cliente e processá-las. Às vezes, queremos interceptar a solicitação HTTP e realizar algum processamento antes de entregá-la aos métodos do manipulador do controlador. É aí que os Interceptor do Spring MVC são úteis.
Interceptador Spring
Assim como temos os Interceptadores Struts2, podemos criar nosso próprio interceptador Spring, seja implementando a interface
org.springframework.web.servlet.HandlerInterceptor
ou substituindo a classe abstrata org.springframework.web.servlet.handler.HandlerInterceptorAdapter
, que fornece a implementação base da interface HandlerInterceptor.
Interceptor Spring – HandlerInterceptor
O HandlerInterceptor do Spring declara três métodos com base onde desejamos interceptar a solicitação HTTP.
- boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler): Este método é usado para interceptar a solicitação antes de ser entregue ao método manipulador. Este método deve retornar ‘true’ para informar ao Spring que processe a solicitação por meio de outro interceptor Spring ou a envie para o método manipulador se não houver mais interceptadores Spring. Se este método retornar ‘false’, o framework Spring assume que a solicitação foi tratada pelo próprio interceptor Spring e nenhum processamento adicional é necessário. Devemos usar o objeto de resposta para enviar uma resposta à solicitação do cliente nesse caso. O objeto handler é o objeto manipulador escolhido para lidar com a solicitação. Este método também pode lançar uma exceção, nesse caso, o tratamento de exceção do Spring MVC deve ser útil para enviar uma página de erro como resposta.
- void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView): Este método de interceptor HandlerInterceptor é chamado quando o HandlerAdapter invocou o manipulador, mas o DispatcherServlet ainda não renderizou a visualização. Este método pode ser usado para adicionar um atributo adicional ao objeto ModelAndView a ser usado nas páginas de visualização. Podemos usar este método de interceptor Spring para determinar o tempo que o método manipulador leva para processar a solicitação do cliente.
- void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex): Este é um método de retorno de chamada do HandlerInterceptor que é chamado uma vez que o manipulador é executado e a visualização é renderizada.
Se houver vários interceptadores de primavera configurados, o método preHandle() é executado na ordem de configuração, enquanto os métodos postHandle() e afterCompletion() são invocados na ordem reversa. Vamos criar uma aplicação Spring MVC simples onde configuraremos um Interceptor Spring para registrar os tempos de execução do método do manipulador do controlador. Nosso projeto de exemplo final do Interceptor Spring será semelhante à imagem abaixo, vamos analisar os componentes que nos interessam.
Interceptor Spring – Classe do Controlador
package com.journaldev.spring;
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* Handles requests for the application home page.
*/
@Controller
public class HomeController {
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
@RequestMapping(value = "/home", method = RequestMethod.GET)
public String home(Locale locale, Model model) {
logger.info("Welcome home! The client locale is {}.", locale);
//adicionando algum atraso de tempo para verificar a execução do interceptor
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Date date = new Date();
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
String formattedDate = dateFormat.format(date);
model.addAttribute("serverTime", formattedDate );
logger.info("Before returning view page");
return "home";
}
}
I am just adding some processing time in the execution of the handler method to check our spring interceptor methods in action.
Interceptador Spring MVC – Implementação do HandlerInterceptorAdapter
Para simplicidade, estou estendendo a classe abstrata HandlerInterceptorAdapter
. HandlerInterceptorAdapter é uma classe adaptadora abstrata para a interface HandlerInterceptor, para uma implementação simplificada de interceptadores pré/pós-apenas.
package com.journaldev.spring;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
public class RequestProcessingTimeInterceptor extends HandlerInterceptorAdapter {
private static final Logger logger = LoggerFactory
.getLogger(RequestProcessingTimeInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
long startTime = System.currentTimeMillis();
logger.info("Request URL::" + request.getRequestURL().toString()
+ ":: Start Time=" + System.currentTimeMillis());
request.setAttribute("startTime", startTime);
//se retornar falso, precisamos garantir que 'response' seja enviado
return true;
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("Request URL::" + request.getRequestURL().toString()
+ " Sent to Handler :: Current Time=" + System.currentTimeMillis());
//podemos adicionar atributos no modelAndView e usá-los na página de visualização
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
long startTime = (Long) request.getAttribute("startTime");
logger.info("Request URL::" + request.getRequestURL().toString()
+ ":: End Time=" + System.currentTimeMillis());
logger.info("Request URL::" + request.getRequestURL().toString()
+ ":: Time Taken=" + (System.currentTimeMillis() - startTime));
}
}
A lógica é realmente simples, estou apenas registrando os tempos de execução do método do manipulador e o tempo total gasto no processamento da solicitação, incluindo a renderização da página de visualização.
Configuração do Interceptor Spring MVC
Temos que conectar o interceptor spring às solicitações, podemos usar o elemento mvc:interceptors para conectar todos os interceptadores. Também podemos fornecer um padrão de URI para corresponder antes de incluir o interceptor spring para a solicitação por meio do elemento mapping. Nosso arquivo de configuração final do bean spring (spring.xml) parece assim.
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="https://www.springframework.org/schema/mvc"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns:beans="https://www.springframework.org/schema/beans"
xmlns:context="https://www.springframework.org/schema/context"
xsi:schemaLocation="https://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
https://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing
infrastructure -->
<!-- Enables the Spring MVC @Controller programming model -->
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving
up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Resolves views selected for rendering by @Controllers to .jsp resources
in the /WEB-INF/views directory -->
<beans:bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<!-- Configuring interceptors based on URI -->
<interceptors>
<interceptor>
<mapping path="/home" />
<beans:bean class="com.journaldev.spring.RequestProcessingTimeInterceptor"></beans:bean>
</interceptor>
</interceptors>
<context:component-scan base-package="com.journaldev.spring" />
</beans:beans>
I will not explain all other components of the web application, because we are not interested in them and they don’t have any specific spring interceptor related configuration.
Testando a Aplicação com Interceptor Spring MVC
Basta implantar a aplicação no contêiner de servlet e invocar o controlador principal, você verá a saída do registro algo como abaixo.
INFO : com.journaldev.spring.RequestProcessingTimeInterceptor - Request URL::https://localhost:9090/SpringInterceptors/home:: Start Time=1396906442086
INFO : com.journaldev.spring.HomeController - Welcome home! The client locale is en_US.
INFO : com.journaldev.spring.HomeController - Before returning view page
Request URL::https://localhost:9090/SpringInterceptors/home Sent to Handler :: Current Time=1396906443098
INFO : com.journaldev.spring.RequestProcessingTimeInterceptor - Request URL::https://localhost:9090/SpringInterceptors/home:: End Time=1396906443171
INFO : com.journaldev.spring.RequestProcessingTimeInterceptor - Request URL::https://localhost:9090/SpringInterceptors/home:: Time Taken=1085
A saída confirma que os métodos de intercepção da mola são executados na ordem definida. Isso é tudo para usar interceptadores da mola, você pode baixar o projeto de exemplo do Spring Interceptor no link abaixo e tentar ter vários interceptadores e verificar por diferentes ordens de configuração.