【Java】Java中过滤器(Filter)、拦截器(Interceptor)详解

153 阅读4分钟

1. 过滤器 和 拦截器是什么

过滤器

1. 简述:对web资源进行拦截,做一些处理后再交给下一个过滤器或servlet处理。
2. 使用场景:通常都是用来拦截request进行处理的,也可以对返回的response进行拦截处理。
3. 依赖及实现:依赖Servlet,基于 [方法回调] 实现。
4. 场景:过滤器一般用于登录权限验证、资源访问权限控制、敏感词汇过滤、字符编码转换等等操作。(即HTTP请求中的信息,包括request + response)

拦截器

1. 简述:拦截器,在AOP中用于在某个方法或字段被访问之前,进行拦截,然后在之前或之后加入某些操作,是AOP的一种实现策略。
2. 使用场景:包含了过滤器能获取到的所有信息,并能拿到 Controller的一些信息。
3. 依赖及实现:不依赖Servlet,但依赖Spring容器。
   -- 网上大部分说是基于 [Java反射-动态代理] 实现,但是其实不是,只是基于责任链模式,遍历进行实现。
4. 场景:过滤器能用的场景,拦截器都能替代。能拿到Controller的一些信息。

2. 过滤器 和 拦截器区别是什么

  1. 来源

    -- Filter

filter依赖.png

-- Interceptor

interceptor依赖.png

  1. 触发时机(位置请求的位置)

request-filter-interceptor-order.png

  1. 实现不同

    // 过滤器 基于[方法回调]实现
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest request2 = (HttpServletRequest) request;
        System.out.println(request2.getRequestURI());
        // 关键代码行
        chain.doFilter(request, response);
    }
    
    ===========================================================================================
    
    // 拦截器 网上大部分说是基于 [Java反射-动态代理] 实现,但是其实不是,只是基于责任链模式,遍历进行实现。
    Spring容器,SpringMVC中doDispatch时,会先匹配当前资源对应的所有拦截器,组成一个链式结构,循环有序调用我们重写的几个方法。
        
        // 关键源码位置:
        DispatcherServlet - doDispatch - applyPreHandle / applyPostHandle 
    
    
  2. 使用场景不同

    第一点:
    请求顺序上,会先过Filter,再过Interceptor。
    须知,资源传递是损耗性能的,那么能在开始就处理掉的,会优于在后面才处理。
    
    所以,如果Filter能处理掉切面要完成的事情,就不要用Spring提供的Interceptor。
    
    第二点:
    Spring提供的Interceptor,功能是强于Filter的。
    如果存在Filter处理不了,可看Interceptor是否能完成。(前者能获取到Request\Response之外,关于Controller的一些信息)
    

3. 怎么使用过滤器

  1. 基于@WebFilter实现(缺点,不支持自定义排序)
package com.example.demo.filter;

import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@Component
@WebFilter(urlPatterns = "/*")
public class WebFilterTest implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("过滤器:执行 init 方法。");
    }

    @Override
    public void doFilter(ServletRequest servletRequest,
                         ServletResponse servletResponse,
                         FilterChain filterChain) throws IOException, ServletException {
        System.out.println("过滤器:开始执行 doFilter 方法。");
        // 请求放行
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("过滤器:结束执行 doFilter 方法。");
    }

    @Override
    public void destroy() {
        System.out.println("过滤器:执行 destroy 方法。");
    }
}

  1. 基于向Spring注入FilterRegistrationBean实现(可进行排序) -- 【推荐使用】
package com.example.demo.filter;

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@Configuration
public class FilterRegistrationBeanTestFilter {

    @Bean
    public FilterRegistrationBean<MyFilter> testFilterRegistration() {
        FilterRegistrationBean<MyFilter> registration = new FilterRegistrationBean<>();
        registration.setFilter(new MyFilter());
        registration.addUrlPatterns("/*");
        registration.setName("FilterRegistrationBeanTestFilter");
        // 设置执行顺序,1为最高优先级
        registration.setOrder(1);
        return registration;
    }


    public class MyFilter implements Filter {

        @Override
        public void init(FilterConfig filterConfig) {

        }

        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                throws IOException, ServletException {
            HttpServletRequest request2 = (HttpServletRequest) request;
            System.out.println(request2.getRequestURI());
            chain.doFilter(request, response);
        }

        @Override
        public void destroy() {

        }
    }
}

4. 怎么使用拦截器

拦截器使用,分两步。第一步为定义拦截器,第二步为注册拦截器。

定义拦截器:

package com.example.demo.interceptor;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class TestInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("拦截器:执行 preHandle 方法。");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("拦截器:执行 postHandle 方法。");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("拦截器:执行 afterCompletion 方法。");
    }
}

注册拦截器

package com.example.demo.interceptor;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class AppConfig implements WebMvcConfigurer {

    // 注入拦截器
    @Autowired
    private TestInterceptor testInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(testInterceptor)
           	  // 拦截所有地址
              .addPathPatterns("/*");
    }
}

5. 总结

Java中的过滤器,以及Spring提供的拦截器,本质都是AOP思想,在请求来到的时候,在某个阶段进行预处理。
但是,过滤器所能读到的东西少一些,且两个组件处理的位置不同,所以在选用上要考虑下优先用哪个。

小tip:
1. 能用Filter处理的,优先用Filter处理。(因为资源能提前处理,在性能的角度看一般都是最佳的,这里也一样)
2. 可以写个demo,打个断点就能看到拦截器能额外拦截到的信息。

拓展:
Spring还提供了Spring AOP,其实本质上也是和上面两个组件一样的。
只是Spring AOP他处理的位置又不同了,处理上 位于 Spring提供的拦截器之后。
能获取到的也有不同,对比拦截器,能获取到传递过来参数的值,而获取不到Request/Response等信息。