本文主要讲解三种拦截的实现方法,以及三者的不同之处。
Filter(过滤器)
filter 有两种配置方式
- 继承 Filter 接口,实现方法,并且使用 @Compoent 声明为 bean
- 自定义 config 中,配置 bean 的方式装配,参考 https://www.beeij.com/code/java/springboot-filter
本文使用第一方式,实现记录请求用时
@Component
public class TimeFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("time filter init");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("time filter start");
long start = System.currentTimeMillis();
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("time filter: " + (System.currentTimeMillis() - start));
System.out.println("time filter finish");
}
@Override
public void destroy() {
System.out.println("time filter destory");
}
}
init() 和 destory() 都是 default 修饰的,不是必须要实现的
如果需要指定拦截的 url,请使用第二种方式
执行效果:

局限性:Filter 是 J2EE 规范中定义,和 Spring 是没有关系,所以 Filter 是不知道具体是什么 Controller 来处理请求的
Interceptor(拦截器)
Interceptor 是 Spring 中定义的拦截器,Interceptor 可以拿到处理请求的 Controller
Interceptor 配置步骤
- 新建自定义类实现接口 HandlerInterceptor,根据需要覆盖接口中定义的 defult 方法,记得加 @Compoent
- 创建配置类实现接口 WebMvcConfigurer,覆盖 addInterceptors 方法,记得配置类加 @Configuration
Interceptor 定义:
@Component
public class TimeInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle");
System.out.println(((HandlerMethod)handler).getBean().getClass().getName());
System.out.println(((HandlerMethod)handler).getMethod().getName());
request.setAttribute("startTime", System.currentTimeMillis());
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
long startTime = (long) request.getAttribute("startTime");
System.out.println("time interceptor 耗时:" + (System.currentTimeMillis() - startTime));
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterHandle");
long startTime = (long) request.getAttribute("startTime");
System.out.println("ex:" + ex);
System.out.println("time interceptor 耗时:" + (System.currentTimeMillis() - startTime));
}
}
配置类:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private TimeInterceptor timeInterceptor;
String[] addPathPatterns={"/get/**"};
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(timeInterceptor)
.addPathPatterns(addPathPatterns);;
}
}
执行效果:

参数 Object handler 就是当前处理请求的 controller
当抛出异常的时候
- 如果抛出异常,那么 postHandle 就不会执行,直接跳到 afterHandle
- 不管抛不抛出异常,afterHandle 都会执行
- afterHandle 中的 Exception ex 可以获取到抛出的异常
抛出异常效果如下:


- 因为 interceptor 会拦截所有的 controller
- BasicErrorController 是 spring 提供给的错误处理器
局限性:不能获取到传入参数的值
Aspect(切面)
spring 框架核心功能之一

Aspect 配置步骤
- 新建切面类,添加注解 @Aspect、@Compoent
- 在方法上定义切点
@Aspect
@Component
public class TimeAspect {
@Around("execution(* com.ye.holdup.controller.UserController.*(..))")
public Object handleControllerMethod(ProceedingJoinPoint pjp) throws Throwable {
Object[] args = pjp.getArgs();
for (Object arg : args) {
System.out.println("arg is " + arg);
}
long start = System.currentTimeMillis();
Object object = pjp.proceed();
System.out.println("time aspect 耗时:" + (System.currentTimeMillis() - start));
System.out.println("time aspect end");
return object;
}
}
四个切点
- @Before():方法被调用之前
- @After():方法成功返回之后
- @AfterThrowing():在方法抛出异常之后
- @Around():包围,完全覆盖前面的三种,一般开发都使用@Around
@Around(“execution(* com.ye.holdup.controller.UserController.*(..))”)
当 UserController 中任意一个方法被调用这个切片都会生效
ProceedingJoinPoint:当前拦截住的方法的一些信息
更多 AspectJ 表达式实例参见官方文档
ctrl+F 搜索 Declaring an Aspect,即可快速定位
局限性:Aspect 不能获取到 HTTP 请求和相应
源码下载
评论后可见此区域内容
总结
三者执行的顺序:

如果有异常,则会从异常开始一层层向上抛出
HTTP请求和响应 | Controller | 请求参数 | |
Filter | ✔ | ❌ | ❌ |
Interceptor | ✔ | ✔ | ❌ |
Aspect | ❌ | ✔ | ✔ |