Springboot 项目中使用 Filter 全指南
目录
一、Filter与Interceptor有什么区别
二、Filter接口
三、使用方法
3.1 使用@WebFilter注解方式
3.2 使用FilterRegistrationBean配置方式
3.3 使用DelegatingFilterProxy方式
四、Filter的典型应用场景
4.1 权限验证
4.2 跨域处理:设置CORS相关响应头
4.3 日志记录:记录请求信息、处理时间等
4.4 编码设置:统一设置请求和响应的编码
五、最佳实践建议
一、Filter与Interceptor有什么区别
Filter(过滤器)是Servlet规范(J2EE标准)定义的组件,基于Servlet容器(如Tomcat)的回调机制实现。它不依赖于任何特定框架,可以在任何Java Web工程中使用,通过实现javax.servlet.Filter接口来定义。
Filter的工作层级处于Servlet容器层面,能够拦截进入容器的所有请求,包括静态资源(HTML/CSS/JS等)。
Interceptor(拦截器)是Spring MVC框架提供的机制,基于Java反射和动态代理技术实现。它必须依赖Spring框架环境,通过实现HandlerInterceptor接口来定义。Interceptor工作在框架层面,只能拦截Spring MVC处理的Controller请求,无法拦截静态资源请求。
特性 | Filter | Interceptor |
---|---|---|
规范 | Servlet规范 | Spring框架提供 |
拦截范围 | 所有资源(包括静态资源) | 只拦截Controller方法 |
执行位置 | Servlet之前执行 | Controller方法前后执行 |
依赖关系 | 不依赖Spring容器 | 是Spring的一部分 |
配置方式 | web.xml或@WebFilter或注入Bean | 实现HandlerInterceptor接口 |
二、Filter接口
Filter接口
package javax.servlet;import java.io.IOException;public interface Filter {public default void init(FilterConfig filterConfig) throws ServletException {}public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException;public default void destroy() {}
}
三、使用方法
3.1 使用@WebFilter注解方式
@WebFilter(filterName = "myFilter", urlPatterns = "/*")
public class MyFilter implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {// 请求预处理逻辑System.out.println("进入MyFilter过滤器");// 放行请求chain.doFilter(request, response);// 响应后处理逻辑}}
注意:使用 @WebFilter 需要在启动类上添加@ServletComponentScan注解
@SpringBootApplication
@ServletComponentScan
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}
这种方式的特点是:
-
配置简单,直接在Filter类上使用注解
-
执行顺序由Filter类名的字母顺序决定,无法自定义
-
适合简单的单过滤器场景
3.2 使用FilterRegistrationBean配置方式
这是更灵活的方式,可以精确控制过滤器的顺序和拦截规则
@Configuration
public class FilterConfig {@Beanpublic FilterRegistrationBean<MyFilter> otherRegistration() {FilterRegistrationBean<MyFilter> registration = new FilterRegistrationBean<>();registration.setFilter(new MyFilter());// 添加过滤路径(只对路径过滤)registration.addUrlPatterns("/user");// 设置执行顺序,数字越小优先级越高registration.setOrder(2);// 设置过滤器名称registration.setName("myFilter");return registration;}@Beanpublic FilterRegistrationBean<MyFilter2> myFilterRegistration() {FilterRegistrationBean<MyFilter2> registration = new FilterRegistrationBean<>();registration.setFilter(new MyFilter2());// 添加过滤路径(只对路径过滤)registration.addUrlPatterns("/*");// 设置执行顺序,数字越小优先级越高registration.setOrder(1);// 设置过滤器名称registration.setName("myFilter2");return registration;}
}
这种方式的特点是:
-
可以精确控制过滤器的执行顺序
-
可以配置多个URL模式
-
适合需要多个过滤器且有顺序要求的场景
-
可以在Filter中注入Spring管理的Bean(需要将Filter也声明为Bean)
3.3 使用DelegatingFilterProxy方式
@Component
public class MySpringFilter implements Filter {// 可以在这里注入Spring管理的Bean@Autowiredprivate SomeService someService;@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {// 使用注入的BeansomeService.doSomething();chain.doFilter(request, response);}
}
@Component
public class HtFilter implements Filter {@Autowiredprivate ExcelImportService excelImportService;@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {String test = excelImportService.getTest();System.out.println("HT 过滤器" + test);}
}
@Configuration
public class FilterConfig2 {@Beanpublic DelegatingFilterProxyRegistrationBean delegatingFilterProxy() {DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean("mySpringFilter ");registration.addUrlPatterns("/*");registration.setOrder(1);return registration;}@Beanpublic DelegatingFilterProxyRegistrationBean delegatingFilterProxy2() {DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean("htFilter");registration.addUrlPatterns("/user");registration.setOrder(2);return registration;}
}
说明
避免混合使用不同配置方式,防止出现异常!
在doFilter()
方法中,必须调用chain.doFilter(request, response)
来放行请求,否则请求会被阻断。
四、Filter的典型应用场景
4.1 权限验证
检查用户是否登录或是否有权限访问资源
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {HttpServletRequest req = (HttpServletRequest) request;HttpSession session = req.getSession();if(session.getAttribute("user") == null) {// 用户未登录,返回错误或重定向(前后端分离项目一般不用重定向而是选择返回错误json即可)HttpServletResponse resp = (HttpServletResponse) response;resp.sendRedirect("/login");return;}chain.doFilter(request, response);
}
4.2 跨域处理:设置CORS相关响应头
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {HttpServletResponse resp = (HttpServletResponse) response;resp.setHeader("Access-Control-Allow-Origin", "*");resp.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");resp.setHeader("Access-Control-Max-Age", "3600");resp.setHeader("Access-Control-Allow-Headers", "x-requested-with,content-type");chain.doFilter(request, response);
}
4.3 日志记录:记录请求信息、处理时间等
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {long startTime = System.currentTimeMillis();HttpServletRequest req = (HttpServletRequest) request;String requestURI = req.getRequestURI();chain.doFilter(request, response);long elapsed = System.currentTimeMillis() - startTime;System.out.println("请求"+requestURI+"处理时间:"+elapsed+"ms");
}
4.4 编码设置:统一设置请求和响应的编码
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {request.setCharacterEncoding("UTF-8");response.setContentType("text/html;charset=UTF-8");response.setCharacterEncoding("UTF-8");chain.doFilter(request, response);
}
五、最佳实践建议
-
简单场景:使用@WebFilter + @ServletComponentScan方式
-
复杂场景:使用FilterRegistrationBean方式,可以更好控制顺序和配置
-
需要依赖注入:使用DelegatingFilterProxy方式
-
性能考虑:在Filter中避免耗时操作,特别是高优先级Filter
-
异常处理:在Filter中妥善处理异常,避免影响整个应用