【Java Web】Filter vs Interceptor

我们说的 Filter 和 Interceptor 是啥?

  • Filter:javax.servlet.Filter

  • Interceptor:org.springframework.web.servlet.HandlerInterceptor

参考:

简介

Filter

Java代码

 

  1. public interface Filter {  

  2.     public void init(FilterConfig filterConfig) throws ServletException;  

  3.   

  4.     public void doFilter(ServletRequest request, ServletResponse response,  

  5.                          FilterChain chain)  

  6.             throws IOException, ServletException;  

  7.   

  8.     public void destroy();  

  9. }  

根据代码附属的文档:

Filter 用于过滤对资源的请求和响应。一个Filter可以同时用于过滤请求和响应。

目标资源可以是 Servlet 或 静态资源。

Filter 的 doFilter 方法执行具体的过滤操作。

Filter 可以通过 FilterConfig 对象获取初始化参数。还可通过 FilterConfig 提供的 ServletContext 引用获取更多资源信息用于过滤操作。

可通过 web descriptor 文件 web.xml 配置Filter,也可以用 @WebFilter 注解配置。

(标准的默认 web descriptor 文件是 WEB-INF/web.xml)

典型使用场景:

身份验证

日志、审计

图像转换

数据压缩

加解密

访问令牌处理

触发资源访问事件2

XSL 转换(XSL/T)

Mime 类型链式过滤

HandlerInterceptor

Java代码

 

  1. public interface HandlerInterceptor {  

  2.     boolean preHandle(HttpServletRequest request, HttpServletResponse response,  

  3.                           Object handler)  

  4.             throws Exception;  

  5.   

  6.     void postHandle(HttpServletRequest request, HttpServletResponse response,  

  7.                         Object handler, ModelAndView modelAndView)  

  8.             throws Exception;  

  9.   

  10.     void afterCompletion(HttpServletRequest request, HttpServletResponse response,  

  11.                              Object handler, Exception ex)  

  12.             throws Exception;  

  13. }  

根据代码附属文档:

HandlerInterceptor 用于自定义执行链。

HandlerInterceptor 会在相应的 HandlerAdaptor 被调用前执行,所以可用于“预处理”。

例:授权检查、区域设置、主题更改 等。

它的主要目的就是提取出可复用的 Handler 代码。

作为Spring中的概念,HandlerInterceptor 可以像一般的Bean那样配置在 Application Context 中。

HandlerInterceptor 与 Servlet Filter 很相似。

但是 Interceptor 只允许通过预处理禁止 Handler 本身运行,或执行一些后续处理(post-processing)。

Filter 更强大。如,允许交换传递给后续处理链的 request 和 response 对象。

基本准则:

  • 细粒度的 Handler相关 预处理操作 可以用 HandlerInterceptor 实现。

    尤其是 授权检查 和 公用的 Handler 代码。

  • Filter 适合用于处理 request 和 view 内容。如,Multipart 表单、Gzip压缩。

    典型的作法就是处理特定的 Content-Type。

对比

最本质的区别是,Filter 是 Servlet 中的概念,HandlerInterceptor 是 Spring Web 中的概念。

它们是两个不同体系中的概念。因为它们都能实现对 HTTP Request 和 Response 的一些自定义处理,所以会被一些无聊的人强行拿来作比较,甚至成为考题。

其实在一些常见业务常见中,两种技术实现方式并不存在孰优孰劣。只有结合具体业务场景,才能真正比较出哪种方式更合适。

Filter 相关流程

HandlerInterceptor 相关流程

现在的编程模式都是 遇到 新型 业务需求时先搜索一下别人是如何实现的。

我认为这是最普遍、性价比最高、几乎每个人都默认使用、对个人自身成长提升最快的模式。

注意我的用词“新型”。没有人天生就会;自己埋头苦研,不借助巨人力量的,都是傻子;而且肯定不会得到值得推广的成功。编程就是“抄”!

只要清楚它们在 HTTP Request Response 流程中的角色,看到别人给的常规实现方式,就自然会明白 为什么选其中一种而不选另一种。

示例:HandlerInterceptor 用于授权检查

每个项目可能有自己独特的授权检查需求。此示例仅供参考。

很多项目会用Session来存放用户信息(Session可能存储在Redis等中间件中,以实现分布式架构)

RequestContext:存储用户信息的帮助类

此处使用 ThreadLocal 存储的用户信息,不用担心OOM。因为:

  • 我们会在后续的HandlerInterceptor中清理用户信息

  • 即使清理时遇到异常,线程中的用户信息也不会无限增长。

    线程接到下一次用户请求时相关信息又会被设置为新请求的数据

Java代码

 

  1. public abstract class RequestContext {  

  2.   private static final ThreadLocal userIdHolder = new ThreadLocal();  

  3.   // 可根据需要添加各种用户信息字段  

  4.   

  5.   public static String getUserId() { return userIdHolder.get(); }  

  6.   public static void setUserId(String id) { userIdHolder.set(id); }  

  7.   

  8.   public static void clear() {  

  9.     userIdHolder.remove();  

  10.   }  

  11. }  

AuthInterceptor:执行授权检查

Java代码

 

  1. public class AuthInterceptor implements HandlerInterceptor {  

  2.   @Override  

  3.   public boolean preHandle(  

  4.       HttpServletRequest request, HttpServletResponse response, Object handler) {  

  5.     // 从 response 获取用户相关标识,并进行授权检查 ...  

  6.     // 通过授权检查,并获得 User ID。如果未通过,可设置response相关内容,并返回 false  

  7.     String userId = ...  

  8.       

  9.     // 保存用户信息,供后续 Controller 业务使用  

  10.     RequestContext.setUserId(userId);  

  11.   }  

  12.   

  13.   @Override  

  14.   public void afterCompletion(  

  15.       HttpServletRequest request, HttpServletResponse response,  

  16.       Object handler, @Nullable Exception ex) throws Exception {  

  17.     // 清除之前保存的用户信息。  

  18.     // 为什么在 afterCompletion() 方法中清理,而不是在 postHandle() 中?  

  19.     // 因为如果业务代码中抛出异常,将跳过 postHandle() 方法,但不会跳过 afterCompletion().  

  20.     // 详见 DispatcherServlet.doDispatch() 方法  

  21.     RequestContext.clear();  

  22.   }  

  23. }  

应用 AuthInterceptor

Java代码

 

  1. @Configuration  

  2. public class MyWebAppConfigurer implements WebMvcConfigurer {  

  3.   @Override  

  4.   public void addInterceptors(InterceptorRegistry registry) {  

  5.     registry.addInterceptor(new AuthInterceptor())  

  6.       .addPathPatterns("/**");  

  7.   }  

  8. }  

使用Request用户信息

Java代码

 

  1. @RestController  

  2. public class TestController {  

  3.   @GetMapping("test")  

  4.   public void test() {  

  5.     String userId = RequestContext.getUserId();  

  6.     ...  

  7.   }  

  8. }  

发布了219 篇原创文章 · 获赞 3 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/hchaoh/article/details/103904644
今日推荐