前言
在构建 Web 应用时,我们经常需要对请求进行拦截和处理,以实现诸如身份验证、授权、日志记录等功能。在 Spring Boot 中,为我们提供了两种强大的工具来实现这些功能:过滤器(Filter)和拦截器(Interceptor)。尽管这两者在某些方面的功能相似,它们在使用场景、处理层级和实现方式上却有所不同。在本文中,我们将详细介绍过滤器和拦截器的区别、各自的优势,以及如何在实际项目中使用这两种组件来处理请求。这将帮助您了解在不同情况下应该选择哪种组件来满足您的需求,以便更好地构建和维护您的 Web 应用。
拦截器
在Spring Boot中,拦截器分为两类:
一种是对请求进来的url进行拦截,HandlerInterceptor接口;
一种是对发送出去的请求进行拦截,ClientHttpRequestInterceptor。
HandlerInterceptor接口
要创建一个请求拦截器,你需要执行以下步骤:
- 创建一个类,实现org.springframework.web.servlet.HandlerInterceptor接口。
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 RequestTimingInterceptor implements HandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(RequestTimingInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
long startTime = System.currentTimeMillis();
request.setAttribute("startTime", startTime);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) {
long startTime = (Long) request.getAttribute("startTime");
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
logger.info("{} {} took {} ms", request.getMethod(), request.getRequestURI(), duration);
}
}
preHandle(HttpServletRequest request, HttpServletResponse response, Object handler):在请求处理之前被调用。如果返回true,则继续处理请求;如果返回false,则中止请求。
postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView):在请求处理之后,但在视图渲染之前被调用。可以对模型和视图进行进一步处理。
afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex):在请求处理完成且视图渲染之后被调用。可以进行资源清理等操作。
- 注册拦截器:要让Spring Boot知道你的拦截器,需要创建一个配置类,该类实现WebMvcConfigurer接口,并覆盖addInterceptors(InterceptorRegistry registry)方法,然后将拦截器实例添加到注册表中。
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 WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new RequestTimingInterceptor());
}
}
在addInterceptors方法中,你可以使用registry.addInterceptor(yourInterceptor).addPathPatterns("/**")指定拦截器应用于哪些URL模式。
这样,每次满足指定URL模式的请求时,Spring Boot就会调用你的拦截器。
ClientHttpRequestInterceptor接口
对于服务间调用,你可以使用ClientHttpRequestInterceptor接口来拦截和处理发送出去的请求。这通常用于处理微服务之间的通信,例如添加认证信息、自定义请求头等。
要实现一个发送出去的请求拦截器,请按照以下步骤操作:
- 创建一个类,实现org.springframework.http.client.ClientHttpRequestInterceptor接口。
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import java.io.IOException;
public class CustomHeaderInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException {
request.getHeaders().set("Custom-Header", "CustomHeaderValue");
return execution.execute(request, body);
}
}
在这个类中,覆盖并实现intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)方法。在这个方法中,你可以访问和修改请求对象,然后调用execution.execute(request, body)继续执行请求。
- 创建一个配置类,将拦截器添加到RestTemplate或WebClient中。这两个类都是Spring框架中用于发起HTTP请求的客户端。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.web.client.RestTemplate;
import java.util.ArrayList;
import java.util.List;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
interceptors.add(new CustomHeaderInterceptor());
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
}
现在,每当你使用 RestTemplate 发起请求时,CustomHeaderInterceptor 将被调用。拦截器会在请求中添加一个名为 "Custom-Header" 的自定义请求头,值为 "CustomHeaderValue"。
HandlerInterceptor和ClientHttpRequestInterceptor的区别
HandlerInterceptor 和 ClientHttpRequestInterceptor 都是 Spring 框架中的拦截器,但它们的作用和使用场景不同。下面是它们之间的主要区别:
作用范围:
- HandlerInterceptor 主要用于拦截处理 Spring 应用中接收到的 HTTP 请求。它允许你在请求处理之前、处理之后、以及视图渲染完成后执行自定义逻辑。
- ClientHttpRequestInterceptor 主要用于拦截和处理 Spring 应用中使用 RestTemplate 或 WebClient 发送的 HTTP 请求。它让你在请求发送之前,对请求头、请求体等进行自定义处理。
使用场景:
- HandlerInterceptor 通常用于身份验证、授权、日志记录、跨域资源共享(CORS)等应用内请求处理相关的功能。
- ClientHttpRequestInterceptor 通常用于处理服务间通信,例如在请求头中添加认证信息、自定义请求头、请求数据处理、重试策略等。
接口定义:
- HandlerInterceptor 是 org.springframework.web.servlet.HandlerInterceptor 接口,需要实现 preHandle、postHandle 和 afterCompletion 方法。
- ClientHttpRequestInterceptor 是 org.springframework.http.client.ClientHttpRequestInterceptor 接口,需要实现 intercept 方法。
简而言之,HandlerInterceptor 主要关注应用内的请求处理,而 ClientHttpRequestInterceptor 主要关注应用间的通信。根据你的需求和使用场景,可以选择使用合适的拦截器来实现自定义的请求处理逻辑。
特征 | HandlerInterceptor | ClientHttpRequestInterceptor |
---|---|---|
作用范围 | 拦截接收到的 HTTP 请求 | 拦截使用 RestTemplate 或 WebClient 发送的 HTTP 请求 |
使用场景 | 身份验证、授权、日志记录、CORS 等应用内请求处理功能 | 添加认证信息、自定义请求头、请求数据处理、重试策略等服务间通信功能 |
接口定义 | org.springframework.web.servlet.HandlerInterceptor | org.springframework.http.client.ClientHttpRequestInterceptor |
需要实现的方法 | preHandle、postHandle 和 afterCompletion | intercept |
这个表格概括了 HandlerInterceptor
和 ClientHttpRequestInterceptor
之间的主要区别。根据你的需求和使用场景,可以选择使用合适的拦截器来实现自定义的请求处理逻辑。
过滤器
在 Spring Boot 中,过滤器(Filter)是用于在 Servlet 容器级别拦截和处理 HTTP 请求的组件。它们通常用于实现诸如身份验证、授权、日志记录、请求和响应的数据转换等功能。过滤器位于整个请求处理链的最前端,因此在请求到达 Spring 应用的任何其他组件之前,都会先经过过滤器处理。
要在 Spring Boot 中创建一个过滤器,你需要执行以下步骤:
- 创建一个类,实现 javax.servlet.Filter 接口。
- 在这个类中,覆盖并实现 init(FilterConfig filterConfig)、doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 和 destroy() 方法。
init(FilterConfig filterConfig):初始化方法,在过滤器实例化时调用。
doFilter(ServletRequest request, ServletResponse response, FilterChain chain):处理请求的核心方法。在这个方法中,你可以访问和修改请求和响应对象,并通过调用 chain.doFilter(request, response) 继续处理请求。
destroy():销毁方法,在过滤器实例被销毁时调用。 - 注册过滤器:将过滤器作为一个 Bean 注册到 Spring 应用中。
以下是一个简单的过滤器示例,用于记录每个请求的处理时间:
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@WebFilter(urlPatterns = "/*")
public class RequestTimingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
long startTime = System.currentTimeMillis();
try {
chain.doFilter(request, response);
} finally {
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
HttpServletRequest httpRequest = (HttpServletRequest) request;
System.out.println(String.format("%s %s took %d ms", httpRequest.getMethod(), httpRequest.getRequestURI(), duration));
}
}
@Override
public void destroy() {
}
}
要将此过滤器添加到 Spring Boot 应用中,请将其注册为 Bean:
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<RequestTimingFilter> requestTimingFilter() {
FilterRegistrationBean<RequestTimingFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new RequestTimingFilter());
registrationBean.addUrlPatterns("/*");
return registrationBean;
}
}
现在,每次收到请求时,RequestTimingFilter 都会被调用。过滤器将记录请求的开始时间,然后在请求完成后计算处理时间,并将其输出到控制台。
过滤器和拦截器各自优势与区别?
过滤器(Filter)和拦截器(Interceptor)在某些方面的功能确实相似,但它们在使用场景、处理层级和实现方式上有所不同。以下是过滤器和拦截器之间的主要区别和各自的优势:
处理层级:
-
过滤器(Filter)基于 Java Servlet 规范,在 Servlet 容器级别处理请求。过滤器在整个请求处理链的最前端,因此在请求到达 Spring 应用的任何其他组件之前,都会先经过过滤器处理。
-
拦截器(Interceptor)是 Spring MVC 的一部分,用于处理 Spring 应用中接收到的请求。拦截器在 Spring 处理请求的过程中起作用,位于过滤器之后。
使用场景:
- 过滤器(Filter)通常用于实现通用的、与框架无关的功能,如身份验证、授权、日志记录、请求和响应的数据转换等。
- 拦截器(Interceptor)通常用于实现与 Spring 框架相关的功能,如验证用户身份、授权、请求参数处理、异常处理等。
优势:
- 过滤器(Filter)可以处理任何 Web 应用的请求,不局限于 Spring。过滤器允许你在请求到达任何框架组件之前对其进行处理,提供了更大的控制范围。
- 拦截器(Interceptor)更紧密地集成在 Spring MVC 中,因此可以更方便地使用 Spring 提供的功能。拦截器可以访问 Spring 上下文,让你能够轻松地使用其他 Spring 组件和服务。
实现方式:
- 过滤器(Filter)需要实现 javax.servlet.Filter 接口,并实现 init、doFilter 和 destroy 方法。
- 拦截器(Interceptor)需要实现 HandlerInterceptor 接口(对于请求拦截)或 ClientHttpRequestInterceptor 接口(对于客户端请求拦截),并实现相应的方法。
总的来说,过滤器和拦截器在功能上有一定的重叠,但它们的使用场景、处理层级和实现方式有所不同。选择使用过滤器还是拦截器取决于你的具体需求和场景。当你需要处理与框架无关的请求时,可以使用过滤器;当你需要实现与 Spring 框架相关的功能时,拦截器可能是更好的选择。
特征 | 过滤器(Filter) | 拦截器(Interceptor) |
---|---|---|
处理层级 | Servlet 容器级别 | Spring MVC 层级 |
使用场景 | 通用功能,如身份验证、授权、日志记录等 | 与 Spring 框架相关的功能,如身份验证、请求参数处理等 |
优势 | 处理任何 Web 应用的请求,更大的控制范围 | 紧密集成于 Spring MVC,可以访问 Spring 上下文和其他组件 |
实现接口 | javax.servlet.Filter | org.springframework.web.servlet.HandlerInterceptor 或 org.springframework.http.client.ClientHttpRequestInterceptor |
这个表格概括了过滤器(Filter)和拦截器(Interceptor)之间的主要区别。根据你的需求和使用场景,可以选择使用合适的组件来实现自定义的请求处理逻辑。
结语
总之,过滤器(Filter)和拦截器(Interceptor)都是 Spring Boot 中强大的请求处理工具,它们分别适用于不同的场景和需求。通过对这两种组件的深入了解,我们可以根据具体需求选择使用过滤器还是拦截器,以实现优雅、高效的请求处理逻辑。在实际项目中,我们可能需要根据不同的功能和场景组合使用过滤器和拦截器,以充分发挥它们的优势。希望本文能够帮助您更好地理解过滤器和拦截器的区别与应用,从而为您的 Web 应用提供更为稳定和安全的请求处理方案。