参考文档:
1. 实现原理不同
过滤器和拦截器 底层实现方式大不相同,过滤器 是基于函数回调的,拦截器 则是基于 Java 的反射机制(动态代理)实现的。
这里重点说下过滤器!
在我们自定义的过滤器中都会实现一个 doFilter() 方法,这个方法有一个 FilterChain 参数,而实际上它是一个回调接口。ApplicationFilterChain 是它的实现类, 这个实现类内部也有一个 doFilter() 方法就是回调方法。
public interface FilterChain {
void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;
}
ApplicationFilterChain 里面能拿到我们自定义的 xxxFilter 类,在其内部回调方法 doFilter() 里调用各个自定义 xxxFilter 过滤器,并执行 doFilter() 方法。
public final class ApplicationFilterChain implements FilterChain {
@Override
public void doFilter(ServletRequest request, ServletResponse response) {
...//省略
internalDoFilter(request,response);
}
private void internalDoFilter(ServletRequest request, ServletResponse response){
if (pos < n) {
//获取第pos个filter
ApplicationFilterConfig filterConfig = filters[pos++];
Filter filter = filterConfig.getFilter();
...
filter.doFilter(request, response, this);
}
}
}
而每个 xxxFilter 会先执行自身的 doFilter() 过滤逻辑,最后在执行结束前会执行 filterChain.doFilter(servletRequest, servletResponse),也就是回调 ApplicationFilterChain 的 doFilter() 方法,以此循环执行实现函数回调。
2. 使用范围不同
我们看到过滤器 实现的是 javax.servlet.Filter 接口,而这个接口是在 Servlet 规范中定义的,也就是说过滤器 Filter 的使用要依赖于Tomcat 等容器,导致它只能在 web 程序中使用。
而拦截器 (Interceptor) 它是一个 Spring 组件,并由 Spring 容器管理,并不依赖 Tomcat 等容器,是可以单独使用的。不仅能应用在 web 程序中,也可以用于 Application、Swing 等程序中。
3. 触发时机不同
过滤器 和 拦截器的触发时机也不同,我们看下边这张图。
3. 触发时机不同
过滤器 Filter 是在请求进入容器后,但在进入 servlet 之前进行预处理,请求结束是在 servlet 处理完以后。
拦截器 Interceptor 是在请求进入 servlet 后,在进入 Controller 之前进行预处理的,Controller 中渲染了对应的视图之后请求结束。
执行顺序为:
Filter 处理中 (chain.doFilter 前)
Interceptor 前置 (preHandle)
Controller
Interceptor 处理中 (postHandle)
Interceptor 后置 (afterCompletion)
Filter 处理中 (chain.doFilter 后)
4. 请求范围不同
- 过滤器 Filter 执行了两次,拦截器 Interceptor 只执行了一次。
- 过滤器几乎可以对所有进入容器的请求起作用,而拦截器只会对 Controller 中请求或访问 static 目录下的资源请求起作用。
5. 过滤器实现方式
5.1 filter 注入容器
@Component // 加入容器会自动可用
@Slf4j
public class MyTestFilter 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("MyTestFilter doFilter");
chain.doFilter(request, response);
}
@Override
public void destroy() {
Filter.super.destroy();
}
}
5.2 FilterRegistrationBean
@Slf4j
public class MyTestFilter 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("MyTestFilter doFilter");
chain.doFilter(request, response);
}
@Override
public void destroy() {
Filter.super.destroy();
}
}
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<MyTestFilter> testFilterRegistration() {
FilterRegistrationBean<MyTestFilter> registration = new FilterRegistrationBean<>(new MyTestFilter());
registration.addUrlPatterns("/test/*");
registration.setName("testFilter");
registration.setOrder(1);
return registration;
}
}
5.3 @WebFilter
@Slf4j
@WebFilter(urlPatterns = "/test/*", filterName = "testFilter")
public class MyTestFilter 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("MyTestFilter doFilter");
chain.doFilter(request, response);
}
@Override
public void destroy() {
Filter.super.destroy();
}
}
6. 拦截器实现
public class MyTestInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
System.out.println("preHandle");
return true;
}
}
@Configuration
public class InterceptorConfig extends WebMvcConfigurationSupport {
@Override
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyTestInterceptor()).addPathPatterns("/test/**");
super.addInterceptors(registry);
}
}
7. 拦截器无法注入 bean 处理
如下代码:
@Component
public class MyTestInterceptor extends HandlerInterceptorAdapter {
@Autowired
private ServiceA serviceA;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
System.out.println("preHandle");
serviceA.methodC();
return true;
}
}
@Configuration
public class InterceptorConfig extends WebMvcConfigurationSupport {
@Override
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyTestInterceptor()).addPathPatterns("/test/**");
super.addInterceptors(registry);
}
}
我们会发现拦截器注入的 serviceA 为 null. 因为我们添加拦截器的时候,使用的是 new, 这个拦截器并没有加入 spring 容器,无法注入。
解决方法
将拦截器交给 spring ioc 管理即可
@Configuration
public class InterceptorConfig extends WebMvcConfigurationSupport {
@Autowired
private MyTestInterceptor myTestInterceptor;
@Override
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(myTestInterceptor).addPathPatterns("/test/**");
super.addInterceptors(registry);
}
}
8. 控制执行顺序
过滤器
实际开发过程中,会出现多个过滤器或拦截器同时存在的情况,不过,有时我们希望某个过滤器或拦截器能优先执行,就涉及到它们的执行顺序。
过滤器用 @Order 注解控制执行顺序,通过 @Order 控制过滤器的级别,值越小级别越高越先执行。
@Order(Ordered.HIGHEST_PRECEDENCE)
@Component
public class MyFilter2 implements Filter {}
拦截器
拦截器默认的执行顺序,就是它的注册顺序,也可以通过Order手动设置控制,值越小越先执行。
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor2()).addPathPatterns("/**").order(2);
registry.addInterceptor(new MyInterceptor1()).addPathPatterns("/**").order(1);
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").order(3);
}
看到输出结果发现,先声明的拦截器 preHandle() 方法先执行,而postHandle()方法反而会后执行。
postHandle() 方法被调用的顺序跟 preHandle() 居然是相反的!如果实际开发中严格要求执行顺序,那就需要特别注意这一点。
Interceptor1 前置
Interceptor2 前置
Interceptor 前置
我是controller
Interceptor 处理中
Interceptor2 处理中
Interceptor1 处理中
Interceptor 后置
Interceptor2 处理后
Interceptor1 处理后