SpringMVC中Interceptor 拦截器讲解:
- 在SpringMVC中实现拦截器需要实现 HandlerInterceptor,其中包含三个需要的方法:preHandle()、postHandle()、afterCompletion()
- preHandle(): 在Controller method 之前执行,通常用于做参数校验、登录验证等。
- postHandle(): 在Controller method 之后执行,需要注意的是,当Controller中发生异常时,并不会执行postHandle,而是跳过后执行afterCompletion 方法。
- afterCompletion(): 在postHandle执行之后执行,发生异常也会执行,通常用于释放系统资源。
- HandlerInterceptor 代码示例 :
public interface HandlerInterceptor {
/**
* 在Controller method 之前执行,通常用于做参数校验、登录验证等
* 参数handler requestMapping 修饰的 Controller方法
*/
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception{
return true;
}
/**
* Controller method 之后执行,需要注意的是,当Controller中发生异常时,并不会执行
*/
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
/**
* 在postHandle执行之后执行,发生异常也会执行,通常用语释放系统资源
*/
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
}
}
论证上述关于Interceptor 三个方法执行的顺序:
- 首先需要搭建一个SpringMVC 项目,这里就不过多解释了。
- 新建一个拦截器 SimpleHandlerInterceptor,代码示例如下:
public class SimpleHandlerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
System.err.println("SimpleHandlerInterceptor.preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
System.err.println("SimpleHandlerInterceptor.postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
System.err.println("SimpleHandlerInterceptor.afterCompletion");
}
}
- 在SpringMVC 的配置文件加入下面拦截器配置:
<mvc:interceptors>
<bean class="com.springmvc.config.SimpleHandlerInterceptor" />
</mvc:interceptors>
- 新建一个SimpleController 用于调用:
@RestController
@RequestMapping("/simple")
public class SimpleController {
// 正常情况
@RequestMapping("/test")
public void testVoid(){
System.err.println(" 进入SimpleController.testStr 方法");
}
// 发生异常时
@RequestMapping("/exception")
public void testException(){
System.err.println(" 进入SimpleController.testException 方法");
// 模拟系统异常
int i = 1/0;
}
}
- 发送请求:
正常请求: http://localhost:8080/simple/test, 打印:
异常请求: http://localhost:8080/simple/exception, 打印:
- 上面打印结果已经论证了我们的想法,那为什么会有这样的执行结果那?
源码浅析:
- 众所周知,SpringMVC和核心组件是 DispatcherServlet 所有经Sringmvc的请求,都会执行doDispatch 方法,其实拦截器执行便是在其中设定的。
- 题外话: SpringMVC 和 Mybatis 相对而言是比较容易去阅读源码的框,它们都有统一的入口,一步步Debug 也可以看懂其中核心的执行流程,不过废话,执行源码搞起:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 获取RequestMapping修饰的Controller method 【包含调用链】
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 获取可以执行当前Handler的适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 注意: 执行Interceptor中PreHandle()方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 注意:执行Handle【包括我们的业务逻辑,当抛出异常时会被Try、catch到】
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
// 注意:执行Interceptor中PostHandle 方法【抛出异常时无法执行】
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
// 注意:其中会执行执行Interceptor中afterCompletion 方法【没有异常时】
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
// 注意:其中会执行执行Interceptor中afterCompletion 方法【没有异常时】
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
// 注意:其中会执行执行Interceptor中afterCompletion 方法【没有异常时】
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
- doDispatch中关于 Interceptor 执行顺序已经很清楚了,需要注意的是PostHandle 执行情况,以及afterCompletion 会在三种情况下被执行
以上仅是博主对 SpringMVC中Interceptor拦截器的简单理解,欢迎大家评论