我们在进行 Web 应用开发时,时常需要对请求进行拦截或处理,故 Spring 为我们提供了过滤器和拦截器来应对这种情况。
那么两者之间有什么不同呢?本文将详细讲解两者的区别和对应的使用场景。
过滤器
过滤器是一种在 Java Web 应用中用于处理请求和响应的组件。它可以拦截客户端发起的请求,也可以拦截服务器返回的响应,对它们进行处理或者修改。
过滤器属于Servlet
规范的一部分,过滤器是用于执行过滤任务的对象,它可以在请求到达 Servlet 之前或响应发送给客户端之前执行一些额外的逻辑。
应用场景
日志记录
: 过滤器常用于记录请求和响应的日志,包括请求的路径、参数、处理时间等信息。
身份验证和授权
: 过滤器可以用于实现身份验证和授权逻辑,例如检查用户是否已登录,是否具有足够的权限访问某个资源。
防御性编程
: 过滤器可以用于对请求进行安全检查,防止潜在的攻击,比如阻止恶意请求、XSS(跨站脚本攻击)等。
性能监控
: 过滤器可以用于收集请求的处理时间、资源使用等信息,用于性能监控和优化。
如何创建过滤器
Filter 的生命周期对应的三个关键方法:
-
init():当请求发起时,会调用 init() 方法初始化 Filter 实例,仅初始化一次。若需要设置初始化参数的时可调用该方法。
-
doFilter():拦截要执行的请求,对请求和响应进行处理。
-
destroy():请求结束时调用该方法销毁 Filter 的实例。
下面将介绍二种方法创建 Filter。
实现 Filter 接口
1. 创建 Filter 处理类,实现 Filter 接口,加上 @WebFilter 注解配置拦截 Url,但是不能指定过滤器执行顺序,也可通过 web.xml 配置。
@WebFilter(urlPatterns = "/*")
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 用于完成 Filter 的初始化
Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("过滤器已经拦截成功!!!");
// 执行该方法之前,即对用户请求进行预处理;执行该方法之后,即对服务器响应进行后处理。
chain.doFilter(request,response);
}
@Override
public void destroy() {
// 用于 Filter 销毁前,完成某些资源的回收;
Filter.super.destroy();
}
}
2. 在启动类添加注解 @ServletComponentScan ,让 Spring 可以扫描到。
@SpringBootApplication
@ServletComponentScan
public class MyFilterDemoApplication {
public static void main(String[] args) {
SpringApplication.run(MyFilterDemoApplication.class, args);
}
}
3. 创建 Controller 发起 Url 请求。
@RestController
public class MyFilterController {
@GetMapping("/testFilter")
public String testFilter(){
return "Hello World";
}
}
通过@Component
1. 创建 Filter 处理类,实现 javax.servlet.Filter 接口,加 @Component 注解。
可以使用 @Order 注解保证过滤器执行顺序,不加则按照类名排序。且过滤器不能指定拦截的url , 只能默认拦截全部。
@Component
@Order(1)
public class MyComponentFilter1 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("我是过滤器1已经拦截成功!!!");
chain.doFilter(request,response);
}
@Override
public void destroy() {
Filter.super.destroy();
}
}
@Component
@Order(2)
public class MyComponentFilter2 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
System.out.println("我是过滤器2已经拦截成功!!!");
chain.doFilter(request,response);
}
@Override
public void destroy() {
Filter.super.destroy();
}
}
2. 2-3 步骤同 1.2.1,结果如下。
拦截器
拦截器(Interceptor)是一种在应用程序处理请求和响应的过程中,插入自定义处理逻辑的组件。拦截器是一种常见的设计模式,它允许在核心处理逻辑之前或之后执行额外的操作。
一般出现在Spring MVC中,Spring MVC 中的拦截器实现原理主要基于 Spring 框架的 AOP和 HandlerInterceptor 接口。
应用场景
敏感字检测
:过滤器可以用于检测请求中的文本内容,包括表单提交、请求参数等,以查找是否包含敏感字。异常处理
: 拦截器可以用于捕获和处理在请求处理过程中发生的异常。这使得开发者可以集中处理异常情况,返回合适的错误响应或记录异常信息。日志记录
: 拦截器可用于记录请求和响应的日志信息,包括请求参数、响应状态码、执行时间等。国际化和本地化
: 拦截器可以用于根据请求的语言或地区设置合适的国际化或本地化信息,以提供多语言支持。
如何实现
1. 创建 Interceptor 类,实现 HandlerInterceptor 接口,重写 3 个方法,加@Component注解。
@Component
public class MyHandlerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//请求开始时间
long startTime = System.currentTimeMillis();
request.setAttribute("startTime", startTime);
System.out.println("startTime : " + new Date(startTime));
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
long startTime = (Long)request.getAttribute("startTime");
long endTime = System.currentTimeMillis();
// 统计耗时
long executeTime = endTime - startTime;
System.out.println("executeTime : " + executeTime + "ms");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
2. 配置拦截器,实现 WebMvcConfigurer 接口,加@ Configuration 注解并重写 addInterceptors 方法。
@Configuration
public class MyWebConfigurer implements WebMvcConfigurer {
@Resource
private MyHandlerInterceptor myHandlerInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
List<String> patterns = new ArrayList<>();
patterns.add("/test/handlerInterceptor");
registry.addInterceptor(myHandlerInterceptor)
.addPathPatterns(patterns) // 需要拦截的请求
.excludePathPatterns(); // 不需要拦截的请求
}
}
拦截结果如下:
Spring 项目如何实现?可通过使用
mvc:interceptors
标签来声明需要加入到 SpringMVC 拦截器链中的拦截器。
过滤器 VS 拦截器区别
过滤器和拦截器的区别主要体现在以下 5 点:
-
出身不同;
-
触发时机不同;
-
实现不同;
-
支持的项目类型不同;
-
使用的场景不同。
接下来,我们一一来看。
出身不同
过滤器来自于 Servlet,而拦截器来自于 Spring 框架,从上面代码中我们也可以看出,过滤器在实现时导入的是 Servlet 相关的包,如下图所示:
而拦截器在实现时,导入的是 Spring 相关的包,如下图所示:
触发时机不同
请求的执行顺序是:请求进入容器 > 进入过滤器 > 进入 Servlet > 进入拦截器 > 执行控制器(Controller),如下图所示:
所以过滤器和拦截器的执行时机也是不同的,过滤器会先执行,然后才会执行拦截器,最后才会进入真正的要调用的方法。
实现不同
过滤器是基于方法回调实现的,我们在上面实现过滤器的时候就会发现,当我们要执行下一个过滤器或下一个流程时,需要调用 FilterChain 对象的 doFilter 方法进行回调执行,如下图所示:
由此可以看出,过滤器的实现是基于方法回调的。而拦截器是基于动态代理(底层是反射)实现的,它的实现如下图所示:
代理调用的效果如下图所示:
总结
过滤器和拦截器都是基于 AOP 思想实现的,用来处理某个统一的功能的,但二者又有 5 点不同:出身不同、触发时机不同、实现不同、支持的项目类型不同以及使用的场景不同。过滤器通常是用来进行全局过滤的,而拦截器是用来实现某项业务拦截的。