拦截器Interceptor和过滤器Filter都是基于AOP(Aspect Oriented Programming,面向切面编程)思想实现的,用来解决项目中某一类问题的两种“工具”,两者在使用上有时候可能会分不清,希望能够通过本章解决这个痛点。

# 一、拦截器

拦截器InterceptorSpringMVC中实现的一种基于Java反射(动态代理)机制的方法增强工具,是面向切面AOP编程中应用的一种统一处理方案。拦截器的实现是继承HandlerInterceptor接口,并实现接口的preHandlepostHandleafterCompletion方法,对目标方法进行增强。可以访问action上下文、堆栈里的对象,可以多次被调用。
【1】preHandle:请求方法前置拦截,该方法会在Controller处理之前进行调用,Spring中可以有多个Interceptor,这些拦截器会按照设定的Order顺序调用,当有一个拦截器在preHandle中返回false的时候,请求就会终止。
【2】postHandlepreHandle返回结果为true时,在Controller方法执行之后,视图渲染之前被调用。
【3】afterCompletion:在preHandle返回ture,并且整个请求结束之后,也就是在DispatcherServlet渲染了对应的视图之后执行。此方法主要用来进行资源清理。

@Component
public class UserInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 获取用户信息
        EmpsInformationEntity ee = CommonTool.getEmpsInfo();

        if (ee == null || StringUtils.isEmpty(ee.getEid())) {
            // 跳转登录
            redirectToLoginPage(request, response);
            return false;
        }

        // 模块权限
        PermissionConfig permission = getPermissionConfig(handler);
        boolean hasPermission = checkPermission(request, response, permission);
        if (!hasPermission) {
            if (isAjax(request, permission)) {
                response.getWriter().write("permission error");
            } else {
                List<String> codes = getModuleCode(permission);
                if (CollectionUtils.isNotEmpty(codes)) {
                    response.sendRedirect(String.format(screenPopupConfig.getIamErrorPage(), codes.get(0), ee.getEid()));
                    return false;
                }
                response.sendRedirect(request.getContextPath() + "/noPermisson");
            }
            return false;
        }

        request.setAttribute("Eid", ee.getEid());
        request.setAttribute("Ename", ee.getEmpName());
        /**
         * 多语言环境
         */
        if (request.getRequestURI().indexOf("/lastOrderId") == -1) {
            // 取值
            CultureHelper.initPreRequest(request, response, ee.getDept());
        }
        accessMetric(ee, request);
        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");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

编写完拦截器之后,通过一个配置类设置拦截器,并且可以通过addPathPatternsexcludePathPatterns执行哪些请求需要被拦截,哪些不需要被拦截。

@Configuration
public class MvcConfig implements WebMvcConfigurer {
    @Autowired
    private UserInterceptor userInterceptor;

    // 或者通过@Bean注入
    /** @Bean
     * public UserInterceptor getUserInterceptor() {
     *     return new UserInterceptor();
     **/ }


    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(userInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/error");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

应用场景: 主要是用在插件上,扩展件上比如hibernate spring struts2等有点类似面向切片的技术,在用之前先要在配置文件即xml文件里声明一段的那个东西。
【1】日志记录:记录请求操作日志(用户ip,访问时间等)
【2】权限检查:判断用户是否有权限访问资源,如校验token日志记录
【3】性能监控:记录请求->响应时间,preHandle:记录开始时间,afterCompletion:记录结束时间,开始时间相减去结束时间
【4】登录验证:判断用户是否登录
【5】sign校验,封禁校验等
【6】处理cookie,主题,国际化,本地化等
【7】filter可以实现的功能intercepter基本上都能实现

# 二、过滤器

过滤器Filter基于Servlet实现,过滤器的主要应用场景是对字符编码、跨域等问题进行过滤。Servlet的工作原理是拦截配置好的客户端请求,然后对RequestResponse进行处理。Filter过滤器随着web应用的启动而启动,只初始化一次。Filter的使用比较简单,继承Filter接口,实现对应的initdoFilter以及destroy方法即可。
【1】init:在容器启动时调用初始化方法,只会初始化一次。
【2】doFilter:每次请求都会调用doFilter方法,通过FilterChain调用后续的方法。
【3】destroy:当容器销毁时,执行destory方法,只会被调用一次。

public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("初始化拦截器");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        //做一些处理
        chain.doFilter(request,response);
    }

    @Override
    public void destroy() {
        System.out.println("销毁拦截器");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

Filter配置类

@Slf4j
@Configuration
public class FilterConfiguration {//不拦截路径
    private static List<String> exclusionUrlList=new ArrayList<>();
    //拦截路径
    private static List<String> inclusionUrlList=new ArrayList<>();

    static {
        exclusionUrlList.add("/favicon.ico");
        exclusionUrlList.add("/**/*.css");
        exclusionUrlList.add("/**/*.js");
        exclusionUrlList.add("/ok");
        inclusionUrlList.add("/api/**");
    }

    @Bean
    public FilterRegistrationBean filterRegistration() {
        FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
        registration.setFilter(new MyFilter());
        registration.addInitParameter(FILTER_INIT_PARAM_EXCLUSION_URLS,String.join(",", exclusionUrlList));
        registration.addInitParameter(AJAX_URL_PATTERNS,String.join(",", inclusionUrlList));
        registration.addUrlPatterns("/*");
        registration.setName("testFilter");
     return registration;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

过滤器Filter的三种配置方式:
【1】通过@WebFilter注解配置

1.初始化Filter

@WebFilter(urlPatterns = "/test001")
@Order(1) //order值越小,过滤器越靠前,此处配置无效
public class MyFilter implements Filter {
    @Override
    public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
        System.out.println("##############TestFilter init##############");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //在DispatcherServlet之前执行
        System.out.println("##############doFilter before##############");
        filterChain.doFilter(servletRequest, servletResponse);
        // 在视图页面返回给客户端之前执行,但是执行顺序在Interceptor之后
        System.out.println("##############doFilter after##############");
    }

    @Override
    public void destroy() {
        System.out.println("##############TestFilter destroy##############");
    }
}

//2.在启动类添加 @ServletComponentScan

@SpringBootApplication 
@ServletComponentScan 
public class TestbootApplication { 
  public static void main(String[] args) { 
    SpringApplication.run(TestbootApplication.class, args); 
  } 
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

【2】通过@Bean来配置:初始化Filter

@Component
public class MyFilter implements Filter {
    @Override
    public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
        System.out.println("##############Filter3 init##############");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //在DispatcherServlet之前执行
        System.out.println("##############doFilter3 before##############");
        filterChain.doFilter(servletRequest, servletResponse);
        // 在视图页面返回给客户端之前执行,但是执行顺序在Interceptor之后
        System.out.println("##############doFilter3 after##############");
    }

    @Override
    public void destroy() {
        System.out.println("##############Filter3 destroy##############");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

注册到config

@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean testFilter3RegistrationBean() {
        FilterRegistrationBean registration = new FilterRegistrationBean(new MyFilter());
        registration.addUrlPatterns("/hello");
        registration.setOrder(1); // 值越小越靠前,此处配置有效
        return registration;
    }

    @Bean
    public FilterRegistrationBean testFilter4RegistrationBean() {
        FilterRegistrationBean registration = new FilterRegistrationBean(new MyFilter1());
        registration.addUrlPatterns("/hello");
        registration.setOrder(2);
        return registration;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

【3】Spring MVCweb.xml进行配置:1、初始化Filter;2、web.xml文件中配置Filter

<filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
        <param-name>forceEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>

<filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

应用场景: 传入的request,response提前过滤掉一些信息,或者提前设置一些参数,然后再传入servlet或者strutsaction进行业务逻辑处理。例如在过滤器中修改字符编码CharacterEncodingFilter、在过滤器中修改HttpServletRequest的一些参数XSSFilter(自定义过滤器),如:过滤低俗文字、危险字符、敏感词过滤、响应信息压缩、控制权限、控制转向、做一些业务逻辑判断等

# 三、拦截器与过滤器的区别

拦截器与过滤器执行时机不同: 请求进入Servlet之前,过滤器的doFilter方法进行过滤,进入Servlet容器之后,执行Controller方法之前,拦截器的preHandle方法进行拦截,执行Controller之后,视图渲染之前postHandle方法进行拦截,请求结束之后,拦截器的postHandle方法执行。

拦截器基于Java发射机制实现,过滤器基于函数回调方式实现。在实际应用中拦截器主要用于权限控制、日志打印、参数校验,过滤器主要用于跨域问题解决、编码转换

AOP
区别 过滤器 拦截器 备注
配置位置 配置在web.xml 配置在springmvc.xml
定义位置 Filter定义在java.servlet包下 接口HandlerInterceptor定义在org.springframework.web.servlet包下
使用范围 FilterServlet规范规定的 而拦截器既可以用于Web程序,也可以用于ApplicationSwing程序中。
遵循规范 Filter是遵循Servlet规范 而拦截器是在Spring容器内的,是Spring框架支持的。
实现方式 Filter基于函数回掉 Interceptor基于java反射
作用 Filter在只在Servlet前后起作用,Filter通常不考虑servlet的实现 拦截器能够深入到方法前后、异常抛出前后等,因此拦截器的使用具有更大的弹性。允许用户介入hook into请求的生命周期 Spring构架的程序中,要优先使用拦截器。几乎所有Filter能够做的事情,interceptor都能够轻松的实现
调用方 Filter是被Server(like Tomcat)调用 Interceptor是被Spring调用 因此Filter总是优先于Interceptor执行
Spring关系 Filter不能够使用Spring容器资源 Interceptor是被Spring调用 Spring中使用Interceptor更容易

#

(adsbygoogle = window.adsbygoogle || []).push({});