简介
本文介绍SpringMVC(SpringBoot)中的过滤器、拦截器、AOP的区别及其用法。
如果监听器、过滤器、 拦截器、 AOP都存在,则它们的执行顺序为:监听器 => 过滤器=> 拦截器=> AOP。
过滤器/拦截器/AOP
简介
- 过滤器:Filter。拦截器:Interceptor 。
- 在Spring构架的程序中,要优先使用拦截器。几乎所有 Filter 能够做的事情,interceptor 都能够轻松的实现。
- AOP:可以自定义切入的点,有方法的参数,但拿不到http请求,可以通过RequestContextHolder等方式获得。
 AOP用法
调用顺序
过滤前=> 拦截前=> AOP=> Controller=> AOP=> 拦截后=> 过滤后


不同点
| 项 | 过滤器 | 拦截器 | 
| 使用场景 | 对请求/响应进行修改、判断等。一般用于过滤参数、登录权限验证、资源访问权限控制、敏感词汇过滤、字符编码转换。 | 在service或者一个方法前/后调用一个方法,或者在方法后调用一个方法。 | 
| 能力 | 可以拿到原始的http请求与响应,拿不到请求的控制器和请求控制器中的方法的信息。 可以修改请求、响应、参数:比如:修改字符编码、删除低俗文字、删除危险字符、修改参数 | 可以拿到你请求的控制器和方法,却拿不到请求与响应。 | 
| 顺序 | 可指定顺序。 | 可指定顺序。 | 
| 实现方式 | 回调函数 | AOP | 
| 使用范围 | 只能用于Web | 可用于Web、Application、Swing | 
| 作用范围 | 所有请求。 | 只能是controller请求。静态资源无法控制。 | 
| 使用位置 | controller前后、dispaterServlet前后 | controller前后 | 
| 规范定义 | Servlet 规范定义,Servlet 容器支持。 Filter 接口定义在 javax.servlet 包 | Spring容器内,Spring框架支持。 HandlerInterceptor 接口 定义在org.springframework.web.servlet 包 | 
| 灵活性/粒度 | 灵活性差(粒度大)。 不能够使用 Spring 容器资源 | 灵活性好(粒度小)。 能使用Spring里的任何资源、对象,例如 Service对象、数据源、事务管理等,通过IoC注入到拦截器即可。 | 
| 打断链路 | 打断方法:处理请求和响应对象来引发中断,需要额外的动作,比如将用户重定向到错误页面。 | 打断方法:preHandle方法内返回 false | 
| 执行次数 | 一个controller周期只调用一次:一个过滤器实例只能在容器初始化时调用一次。 | 一个controller周期可调用多次 | 
过滤器
拦截器
联合实例:过滤/拦截/@ControllerAdvice/AOP
代码
实体类
package com.example.entity;
import lombok.Data;
@Data
public class User {
    private Integer id;
    private String name;
    private Integer age;
}控制器
package com.example.controller;
import com.example.entity.User;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/hello")
public class HelloController {
    @RequestMapping("/test1")
    public User test1(User user) {
        System.out.println("HelloController.test1");
        return user;
    }
}过滤器
package com.example.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
public class MyFilter implements Filter {
    @Override
    public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
        System.out.println("MyFilter.init");
        System.out.println("  过滤器名:" + filterConfig.getFilterName());
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
                         FilterChain filterChain) throws IOException, ServletException {
        // do something 处理request 或response
        System.out.println("MyFilter.doFilter");
        if (servletRequest instanceof HttpServletRequest) {
            System.out.println("  URL:" + ((HttpServletRequest)servletRequest).getRequestURL());
        }
        // 调用filter链中的下一个filter
        filterChain.doFilter(servletRequest,servletResponse);
    }
    @Override
    public void destroy() {
        System.out.println("MyFilter.destroy");
    }
}package com.example.filter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FilterConfig {
    @Bean
    public FilterRegistrationBean registrationBean1() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new MyFilter());
        filterRegistrationBean.addUrlPatterns("/*");
        // filterRegistrationBean.setName("xxx");  //可设置过滤器名字
        return filterRegistrationBean;
    }
}拦截器
package com.example.interceptor;
import org.springframework.lang.Nullable;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
        System.out.println("MyInterceptor.preHandle");
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response,
                           Object handler, @Nullable ModelAndView modelAndView) throws Exception {
        System.out.println("MyInterceptor.postHandle");
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
                                Object handler, @Nullable Exception ex) throws Exception {
        System.out.println("MyInterceptor.afterCompletion");
    }
}package com.example.interceptor;
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 InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor());
    }
}@ControllerAdvice
package com.example.advice;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ModelAttribute;
@ControllerAdvice
public class GlobalControllerAdvice {
    @ModelAttribute
    public void authenticationUser() {
        System.out.println("GlobalControllerAdvice.authenticationUser");
    }
}对@RequestMapping的AOP
package com.example.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class RequestMappingAspect {
    @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
    public void pointcut() {
    }
    @Around("pointcut()")
    public Object around1(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("[RequestMappingAspect.around1]: around before");
        Object object = joinPoint.proceed();
        System.out.println("[RequestMappingAspect.around1]: around after");
        return object;
    }
}测试
启动SpringBoot
MyFilter.init
  过滤器名:myFilterpostman访问:http://localhost:8080/hello/test1?name=Tony
后端结果
MyFilter.doFilter
  URL:http://localhost:8080/hello/test1
MyInterceptor.preHandle
GlobalControllerAdvice.authenticationUser
[RequestMappingAspect.around1]: around before
HelloController.test1
[RequestMappingAspect.around1]: around after
MyInterceptor.postHandle
MyInterceptor.afterCompletion
postman结果
{
    "id": null,
    "name": "Tony",
    "age": null
}关闭SpringBoot
Disconnected from the target VM, address: '127.0.0.1:61631', transport: 'socket'
MyFilter.destroy
2020-09-05 17:49:01.499  INFO 17144 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'其他网址
Spring Interceptor vs Filter 拦截器和过滤器区别 | ASPIRE
过滤器:《JSP&Servlet学习笔记》=> 5.3过滤器
拦截器:《深入浅出Spring Boot2.x》










