选择语言
durumis AI 总结的文章
- Filter 是一種 J2EE 標準規格功能,它可以在請求傳遞給 DispatcherServlet 前後,針對所有符合 URL 模式 的請求處理額外任務。Filter 執行於 Web 容器中。Interceptor 是 Spring 提供的技術,它可以在 DispatcherServlet 呼叫控制器前後參照或處理請求和回應。Interceptor 執行於 Spring 上下文 中。
- Argument Resolver 可以間接地將請求傳入的參數值轉換為所需的物件。
- Filter 和 Interceptor 在執行位置上有所差異,Filter 執行於 Web 容器中,Interceptor 執行於 Spring 容器中。Filter 以 DispatcherServlet 為基準處理請求,Interceptor 以控制器為基準 處理請求。Argument Resolver 執行於 Interceptor 之後,並返回特定物件。
什麼是过滤器 (Filter)?
过滤器是 J2EE 标准规范功能,它允许在将请求传递到调度程序 Servlet (Dispatcher Servlet) 之前或之后处理与 URL 模式匹配的所有请求的附加操作。也就是说,过滤器由 Tomcat 等 Web 容器而不是 Spring 容器管理,因此它是在请求到达调度程序 Servlet 之前处理请求的。
过滤器 (Filter) 实现
为了添加过滤器,您需要实现 javax.servlet 的 Filter 接口,该接口包含以下三个方法。
- init 方法
- 这是一个初始化过滤器对象并将其添加到服务的方法。 Web 容器调用 init 方法一次以初始化过滤器对象,之后所有请求都将通过 doFilter() 方法处理。
- doFilter 方法
- 当所有与 URL 模式匹配的 HTTP 请求在传递到调度程序 Servlet 之前由 Web 容器执行,并且在调度程序 Servlet 从客户端返回 HTTP 响应之前由 Web 容器执行。
doFilter() 的参数是 FilterChain,它允许通过 FilterChain 的 doFilter() 将请求传递给下一个目标。通过在 chain.doFilter() 之前和之后添加我们需要的处理过程,我们可以执行我们想要的处理。
- 当所有与 URL 模式匹配的 HTTP 请求在传递到调度程序 Servlet 之前由 Web 容器执行,并且在调度程序 Servlet 从客户端返回 HTTP 响应之前由 Web 容器执行。
- destory 方法
- 这是一个从服务中删除过滤器对象并返回所用资源的方法。它由 Web 容器调用一次,此后不再由 doFilter() 处理。
示例代码 - Servlet 规范
@WebFilter("/*")
public class dolphagoFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws IOException, ServletException {
// 在这里进行预处理
request.setCharacterEncoding("UTF-8");
System.out.println("doFilter() 前....");
filterChain.doFilter(request, response);
// 在这里进行后处理
System.out.println("doFilter() 后....");
}
@Override
public void destroy() { }
示例代码 - @Component
@Order(1)
@Component
public class CustomFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException { }
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
chain.doFilter(request, response);
}
@Override
public void destroy() { }
- @Component:可以将过滤器注册为 Spring Bean。
- @Order:如果有多个过滤器,则可以定义它们的顺序。
- 将与 Servlet 规范匹配的过滤器注册为 Spring Bean 后,就可以使用与 Spring 规范匹配的其他 Bean。
示例代码 - @Configuration
@Configuration
public class CustomRegistrationBean {
@Bean
public FilterRegistrationBean customFilterBean() {
FilterRegistrationBean registration = new FilterRegistrationBean<>();
registration.setFilter(new CustomFilter());
registration.setOrder(1);
registration.addUrlPatterns("/api/*");
return registration;
}
如果要使过滤器仅在特定 URI 上运行,可以使用 FilterRegistrationBean 将过滤器注册为 Spring Bean。
过滤器 (Filter) 用途
主要负责验证和处理请求参数本身。
- 与安全相关的通用操作
- 由于过滤器在 Web 容器中运行,因此可以进行安全检查(XSS、CSRF 防御等),并阻止任何不正确的请求。阻止请求到达 Spring 容器可以提高安全性。
- 所有请求的日志记录
- 图像/数据压缩和字符串编码
- 过滤器实现了在 Web 应用程序中普遍使用功能,例如图像或数据的压缩或字符串编码。
- ServletRequest 自定义
- HttpServletRequest 只能读取一次 Body 中的内容。因此,Filter 或 Interceptor 无法读取 Body。要记录 Body,您可以创建一个自定义的 ServletRequest。
拦截器 (Interceptor)
拦截器 (Interceptor) 含义
拦截器 (Interceptor) 不同于 J2EE 标准规范中的过滤器 (Filter),它是 Spring 提供的技术,可以在调度程序 Servlet 调用控制器之前和之后引用或处理请求和响应。也就是说,与在 Web 容器中运行的过滤器不同,拦截器是在 Spring 上下文中运行的。
调度程序 Servlet 通过处理映射查找适当的控制器以进行请求,并将执行链 (HandlerExecutionChain) 返回。因此,如果此执行链注册了一个或多个拦截器,则它将按顺序遍历拦截器以执行控制器,否则将直接执行控制器。
拦截器 (Interceptor) 实现
为了添加拦截器,您需要实现 org.springframework.web.servlet.HandlerInterceptor 接口,该接口包含以下三个方法。
- preHandle 方法
- preHandle 方法在调用控制器之前执行。因此,它可以用于在控制器之前处理任何预处理任务或处理和添加请求信息。
- preHandle 方法的第三个参数 handler 是一个抽象对象,它包含带 @RequestMapping 的方法的信息。
- preHandle 方法的返回值是 boolean,如果返回值为 true,则将继续执行到下一步,如果返回值为 false,则将停止执行,后续任务(下一个拦截器或控制器)将不会被执行。
- postHandle 方法
- postHandle 方法在调用控制器之后执行。因此,它可以用于处理控制器之后的任何后处理任务。
- afterCompletion 方法
- afterCompletion 方法顾名思义,在所有工作完成后执行,包括从所有视图生成最终结果。
示例代码
@Component
public class CustomInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
}
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new CustomInterceptor());
registry.addInterceptor(new LoggingInterceptor());
}
- 将创建的拦截器注册为 Bean。
- 将创建的拦截器注册到 WebMvcConfigurer 接口的 addInterceptors() 方法中。
- 拦截器将按照在 InterceptorRegistry 中注册的顺序执行。
拦截器 (Interceptor) 用途
主要负责使用服务逻辑处理请求信息的完整性。
- 身份验证/授权等通用操作
- 例如,可以在将请求传递到控制器之前检查身份验证或授权等与客户端请求相关的操作。
- API 调用的日志记录
- 通过接收到的 HttpServletRequest 和 HttpServletResponse 对象,可以记录客户端的信息。
- 处理传递给控制器的的数据
- 可以处理接收到的 HttpServletRequest 和 HttpServletResponse 对象并将其传递给控制器。
- 模仿 AOP
- 您可以使用 preHandle() 方法的第三个参数 HandlerMethod 来了解要执行的方法的签名和其他信息,以便决定是否执行逻辑。
过滤器 (Filter) 与拦截器 (Interceptor)
- 过滤器在 Web 上下文中运行,而拦截器在 Spring 上下文中运行,因此它们的执行时间不同。
- 过滤器可以处理调度程序 Servlet 的前后,而拦截器可以处理控制器的前后。
- 如果需要全局处理与 Spring 无关的任务,或者需要验证输入的请求参数本身,或者需要使用 ServletRequest 而不是 HttpServletRequest,则建议使用过滤器。
- 如果需要在 Spring 级别全局处理与客户端请求相关的任务,或者需要混合使用服务逻辑,则建议使用拦截器。
- 拦截器可以使用 @ControllerAdvice 和 @ExceptionHandler 处理异常,但过滤器无法使用它们来处理异常。
- 过滤器通常使用 try~catch 语句将 doFilter() 方法包装起来,以立即处理该时间点发生的异常。
参数解析器
参数解析器含义
参数解析器可以间接地将请求到达控制器时,从请求中提取的值转换为所需的对象。
参数解析器用途
例如,假设请求与 JWT 令牌一起到达。我们需要验证令牌是否有效,然后从令牌中提取 id 并将其转换为登录用户对象。
在这种情况下,如果不使用参数解析器,则需要在每个控制器中实现验证令牌和将令牌转换为登录用户对象的步骤。这样,在需要用户验证的控制器中就会出现重复代码,并且控制器的责任会增加。参数解析器可以解决这些问题。
参数解析器实现
参数解析器可以通过实现 HandlerMethodArgumentResolver 来使用。该接口指定了要实现的以下两个方法。
boolean supportsParameter(MethodParameter parameter);
@Nullable
- supportsParameter 方法
- 在要执行 ArgumentResolver 的参数前面创建一个特定注解并将其附加。supportsParameter() 方法检查请求方法的参数是否包含所需的注解,如果包含,则返回 true。
- resolveArgument 方法
- 如果 supportsParameter 返回 true,即存在带有特定注解的方法,则该方法是将参数绑定到所需形式并返回该形式信息的方法。
使用参数解析器时,控制器的实现如下。
@GetMapping("/me")
public ResponseEntity findMemberOfMine(@AuthenticationPrincipal LoginMember loginMember) {
MemberResponse memberResponse = memberService.findMember(loginMember.getId());
return ResponseEntity.ok().body(memberResponse);
参数解析器与拦截器 (Interceptor) 的区别
- 参数解析器在拦截器之后执行,当请求到达控制器时,它将从请求中提取的值转换为所需的对象。
- 另一方面,拦截器在实际控制器执行之前拦截请求,并且无法返回特定对象。它只能返回 boolean 或 void 返回类型。
来源
- https://mangkyu.tistory.com/173
- https://junhyunny.github.io/spring-boot/filter-interceptor-and-aop/
- https://supawer0728.github.io/2018/04/04/spring-filter-interceptor/
- https://tecoble.techcourse.co.kr/post/2021-05-24-spring-interceptor/
- https://baeldung-cn.com/spring-mvc-handlerinterceptor-vs-filter
- https://www.baeldung.com/spring-boot-add-filter
预期面试问题和答案
什么是过滤器?
过滤器是在将请求传递到调度程序 Servlet 之前或之后,针对与 URL 模式匹配的所有请求处理附加操作的功能,它在 Servlet 容器中运行。
过滤器何时使用?
过滤器可以处理与 Spring 无关的全局任务。或者,当需要验证请求参数本身时使用它。
它主要处理与安全相关的通用操作,所有请求的日志记录,图像/数据压缩和字符串编码,以及 ServletRequest 自定义操作。
什么是拦截器?
拦截器是在调度程序 Servlet 调用控制器之前和之后引用或处理请求和响应的功能,它在 Spring 容器中运行。
拦截器何时使用?
拦截器可以处理与客户端请求相关的全局任务,并且通常在需要调用服务逻辑进行验证时使用。
它主要处理身份验证/授权等通用操作,API 调用的日志记录,以及传递给控制器的数据的处理。
过滤器和拦截器的区别?
过滤器在 Servlet 容器中运行,而拦截器在 Spring 容器中运行。
过滤器可以处理调度程序 Servlet 的前后,而拦截器可以处理控制器的前后。
因此,建议使用过滤器来全局处理与 Spring 无关的任务,而建议使用拦截器来全局处理与客户端请求相关的任务。
什么是参数解析器?
参数解析器是在请求到达控制器时,从请求中提取的值并将其转换为所需对象的功能。
参数解析器何时使用?
当请求与 JWT 令牌一起到达时,可以使用它来验证令牌是否有效,然后从令牌中提取 id 并将其转换为 LoginMember 对象。
拦截器和参数解析器的区别?
参数解析器在请求到达控制器时,将从请求中提取的值转换为所需的对象。另一方面,拦截器无法像这样返回对象,并且它在拦截器执行之后执行。