Spring
提供的拦截器Interceptor
与Servlet
中的Filter
不同的是, Interceptor
采用AOP
的方式在Servlet
的service
方法执行之前进行拦截, 可以进行更精细的控制.
在Spring
中定义一个Interceptor
只需要实现HandlerInterceptor
接口, Spring
已经为我们提供了一个HandlerInterceptorAdapter
, 我们只需要继承它, 覆盖想要重写的方法.
Interceptor
中有如下方法:
preHandle
: 在Controller
处理之前调用, 返回false
时整个请求结束postHandle
: 在Controller
调用之后执行, 但它会在DispatcherServlet
进行视图的渲染之前执行, 也就是说在这个方法中你可以对ModelAndView
进行操作afterCompletion
: 在整个请求完成之后执行, 也就是DispatcherServlet
已经渲染了视图之后执行; 这个方法的主要作用是用于清理资源的afterConcurrentHandlingStarted
: 这个方法是AsyncHandlerInterceptor
接口中添加的. 当Controller
中有异步请求方法的时候会触发该方法, 异步请求先支持preHandle
、然后执行afterConcurrentHandlingStarted
, 异步线程完成之后执行会再执行preHandle、postHandle、afterCompletion
关于最后那个方法, 举个列子:
@RestController
public class ExampleController {
@RequestMapping("/")
DeferredResult<String> home() {
DeferredResult<String> dr = new DeferredResult<String>();
dr.setResult("成功");
return dr;
}
}
上面这样的Controller
里面有个异步结果, 则拦截器的执行顺序将是: preHandle -> afterConcurrentHandlingStarted -> preHandle -> postHandle -> afterCompletion
.
如果把dr.setResult("成功");
这句删掉, 将只执行preHandle -> afterConcurrentHandlingStarted
可以认为, afterConcurrentHandlingStarted
是返回异步结果时调用(此时异步结果里不需要有数据), 而postHandle
必须是返回的结果执行完, 异步结果中有数据了(dr.setResult
)才调用.
首先我们先定义我们自己的拦截器, 方式还是继承HandlerInterceptorAdapter
, 覆盖想要的方法:
@Component
public class MyInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
// ...
}
像这样我们把MyInterceptor
作为一个Bean
, Spring boot
并不会帮我们注册到拦截器列表中. 就像添加消息转换器一样, 我们可以在继承WebMvcConfigurerAdapter
的配置类里, 通过覆盖方法来添加:
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
// 拦截器需要手动加入到调用链中
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor());
}
}