Java Spring Boot 过滤器的使用与拦截器对比
在 web 应用中,早期在 servlet 中使用 filter过滤器,随着 spring 的发展,不同于依托 servlet容器,拦截器依托 Spring框架 应用也很广泛。
今天主要内容分两部分:
- 1.filter的使用
- 2.filter和interceptors对比
filter的使用
导入依赖
<!-- Lombok 工具 --> <!-- @Data && slf4j --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!-- 导入配置文件处理器,在配置springboot相关文件时候会有提示 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>
编写filter
package com.example.springbootfilterdemo.boot.filter; import jakarta.servlet.*; import jakarta.servlet.annotation.WebFilter; import lombok.extern.slf4j.Slf4j; import java.io.IOException; @Slf4j @WebFilter(urlPatterns = "/*") public class MyFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { log.info("过滤器开始过滤..."); log.info("requestID: " + request.getRequestId()); filterChain.doFilter(request, response); } }
上面过滤器逻辑简单,只是简单将请求的顺序号打印了。
可以看到,我们的 MyFilter 只是实现了 Filter 接口,我们看看 FIlter接口:
package jakarta.servlet; import java.io.IOException; public interface Filter { default void init(FilterConfig filterConfig) throws ServletException { } void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException; default void destroy() { } }
其中主要的方法 void doFilter() 有三个参数:
- 请求
- 响应
- 过滤链
当该过滤器处理结束后,就会将请求沿着过滤链传递。
为了效果直观,我们可以添加个 监听器。
编写监听器 MyListener
package com.example.springbootfilterdemo.boot.listener; import jakarta.servlet.ServletContextEvent; import jakarta.servlet.ServletContextListener; import jakarta.servlet.annotation.WebListener; import lombok.extern.slf4j.Slf4j; @Slf4j @WebListener public class MyListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { log.info("监听器开始初始化..."); ServletContextListener.super.contextInitialized(sce); } @Override public void contextDestroyed(ServletContextEvent sce) { log.info("监听器开始销毁"); ServletContextListener.super.contextDestroyed(sce); } }
监听器也是实现 ServletContextListener接口 ,可以看看这个接口:
package jakarta.servlet; import java.util.EventListener; public interface ServletContextListener extends EventListener { default void contextInitialized(ServletContextEvent sce) { } default void contextDestroyed(ServletContextEvent sce) { } }
主要就是声明环境初始化和环境销毁两个方法,我们实现时加入打印信息,便于在日志中看到。
注册过滤器
package com.example.springbootfilterdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.ServletComponentScan; @SpringBootApplication @ServletComponentScan public class SpringBootFilterDemoApplication { public static void main(String[] args) { SpringApplication.run(SpringBootFilterDemoApplication.class, args); } }
我们只需要在项目启动程序中加入 @ServletComponentScan注解,过滤器就可以使用了。
然后我们再添加几个简单的接口,分别是:
- index
- 注册用户
- 获取所有用户信息
- 删除用户
控制器类
index控制器
package com.example.springbootfilterdemo.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class IndexController { @GetMapping("/index") public Object index() { return "Hello, this is Spring Boot index api"; } }
用户类控制器
package com.example.springbootfilterdemo.controller; import com.example.springbootfilterdemo.model.User; import org.springframework.web.bind.annotation.*; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @RestController @RequestMapping("/user") public class UserController { private static Map<String, Object> users = new ConcurrentHashMap<>(); @PostMapping("/add") public Object add(@RequestBody User user) { users.put(user.getName(), user); return "add user ok!"; } @GetMapping("/allUsers") public Object getAllUsers() { return users; } @DeleteMapping("/delete") public Object delete(@RequestBody User user) { String name = user.getName(); users.remove(name); return "delete user ok!"; } }
这里的 User 就是个 Bean:
package com.example.springbootfilterdemo.model; import lombok.Data; @Data public class User { private String name; private Integer age; private String password; }
测试结果:
这样看过滤器的使用是不是很简单呢。
过滤器与拦截器的对比
开头一张图,直接看看不同[2]:
从图中也可以看出,通常 过滤器 的处理先于 拦截器 的处理。
过滤器:
过滤器是Java Web中的一种技术,它在请求到达Servlet之前或之后进行处理。因此,过滤器是最先执行的拦截器。在过滤器中可以进行一些通用的业务处理,例如鉴权、数据校验、请求日志记录等等。Spring Boot应用程序中注册的过滤器按照注册的顺序依次执行。
拦截器:
拦截器是Spring MVC框架提供的一种技术,它在请求到达Controller之前或之后进行处理。因此,拦截器在过滤器之后执行,但在请求到达Controller之前。在拦截器中可以进行请求的修改或者一些通用的业务逻辑处理。Spring Boot应用程序中注册的拦截器按照注册的顺序依次执行。
二者的区别[3]
Spring的拦截器与Servlet的Filter有相似之处,比如二者都是AOP编程思想的体现,都能实现权限检查、日志记录等。
不同的是:
- 使用范围不同:Filter是Servlet规范规定的,只能用于Web程序中。而拦截器既可以用于Web程序,也可以用于Application、Swing程序中。
- 规范不同: Filter是在Servlet规范中定义的,是Servlet容器支持的。而拦截器是在Spring容器内的,是Spring框架支持的。
- 使用的资源不同:同其他的代码块一样,拦截器也是一个Spring的组件,归Spring管理,配置在Spring文件中,因此能使用Spring里的任何资源、对象,例如Service对象、数据源、事务管理等,通过loC注入到拦截器即可:而Filter则不能。
- 深度不同:Filter在只在Servlet前后起作用。而拦截器能够深入到方法前后、异常抛出前后等,因此拦截器的使用具有更大的弹性。所以在Spring构架的程序中,要优先使用拦截器。
注意:过滤器的触发时机是容器后,servlet之前,所以过滤器的doFilter(ServletRequest request, ServletResponse response, FilterChain chain)的入参是ServletRequest,而不是HttpServletRequest,因为过滤器是在HttpServlet之前。下面这个图,可以让你对Filter和Interceptor的执行时机有更加直观的认识。
只有经过DispatcherServlet 的请求,才会走拦截器链,自定义的Servlet请求是不会被拦截的,比如我们自定义的Servlet地址。
过滤器依赖于Servlet容器,而Interceptor则为SpringMVC的一部分。过滤器能够拦截所有请求,而Interceptor只能拦截Controller的请求,所以从覆盖范围来看,Filter应用更广一些。但是在Spring逐渐一统Java框架、前后端分离越演越烈,实际上大部分的应用场景,拦截器都可以满足了。
参考:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
2021-12-14 单链表及双向链表的代码实现-golang