首先我们,给出一个demo来看下拦截器方法执行顺序:
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("1-1 preHandle: ");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("2-1 postHandle: ");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("3-1 afterCompletion: ");
}
}
import org.springframework.beans.factory.annotation.Autowired;
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 WebmvcConfig implements WebMvcConfigurer {
@Autowired
private MyInterceptor myInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(myInterceptor);
}
}
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
System.out.println("controller:");
return "hello, zhaoshuai-lc";
}
}
在Spring MVC中,拦截器(Interceptor)的三个主要方法preHandle
、postHandle
和afterCompletion
的执行顺序是严格规定的,这些方法的执行时机和顺序对于理解Spring MVC的请求处理流程至关重要。以下是这三个方法的执行顺序的详细解释:
执行顺序
-
preHandle:
- 调用时间:该方法在请求处理之前执行,即Controller方法调用之前。
- 执行顺序:如果有多个拦截器,它们会按照配置的顺序(即拦截器数组中的正向顺序)依次执行。
- 返回值:
- 返回
true
:表示当前拦截器处理通过,请求会继续传递给下一个拦截器(如果有的话),或者如果没有其他拦截器了,就会传递给Controller处理。 - 返回
false
:表示当前拦截器拦截请求,不会继续执行后续的拦截器或Controller方法,而是直接中断执行流程,并会根据情况(如配置)执行afterCompletion
方法(但需要注意的是,不是所有返回false
的拦截器都会执行afterCompletion
,这取决于拦截器的具体配置和是否发生异常)。
- 返回
-
postHandle:
- 调用前提:
preHandle
方法必须返回true
,即请求被正常放行到Controller方法执行之后,但在DispatcherServlet进行视图渲染之前。 - 调用时间:Controller方法处理完请求并返回ModelAndView之后,但在视图渲染之前。
- 执行顺序:如果有多个拦截器,它们会按照配置的逆序(即拦截器数组的反向顺序)执行。
- 注意:如果Controller方法执行过程中抛出异常,将跳过
postHandle
方法,直接执行afterCompletion
方法。
- 调用前提:
总结
方法名 | 调用时机 | 执行顺序(多个拦截器时) | 主要作用 |
---|---|---|---|
preHandle | 请求处理之前,Controller方法调用之前 | 正向顺序 | 决定是否继续执行后续的拦截器或Controller |
postHandle | Controller方法处理完请求后,视图渲染前 | 反向顺序(如果执行) | 对ModelAndView进行操作(如果需要) |
afterCompletion (不完备说法) | 整个请求处理结束之后,包括视图渲染之后 | 反向顺序 | 资源清理工作,如关闭数据库连接等 |
这个执行顺序和规则是Spring MVC框架中拦截器机制的核心,理解和掌握它们对于开发高质量的Web应用至关重要。
关于拦截器preHandle
方法返回false
时,afterCompletion
方法会被调用的描述是不准确的。在Spring MVC的拦截器机制中,如果preHandle
方法返回false
,则请求处理流程会立即中断,但并不会立即调用afterCompletion
方法。
实际上,afterCompletion
方法的调用时机是在整个请求处理流程结束后,无论是否发生了异常。但是,当preHandle
方法返回false
时,请求处理流程尚未到达处理器的执行阶段,因此后续的拦截器(如果有的话)的postHandle
和afterCompletion
方法,以及处理器的执行都不会发生。
只有当请求处理流程完全结束,且Spring MVC确定不再有其他拦截器或处理器的逻辑需要执行时,才会逆序调用每个拦截器的afterCompletion
方法。但是,由于preHandle
的返回值导致了流程中断,所以当前拦截器之后的拦截器(如果有的话)的afterCompletion
方法将不会被调用,而当前拦截器之前的拦截器(如果有的话)的afterCompletion
方法将会按照逆序被调用。
重要的是要理解,afterCompletion
方法的主要目的是进行资源清理或记录日志等操作,它应该能够在请求处理流程的任意点之后被调用,以确保资源的正确释放或日志的完整记录。但是,由于preHandle
的返回值导致了流程中断,所以与当前请求直接相关的处理器和后续拦截器的逻辑将不会被执行,但之前的拦截器(如果有的话)的afterCompletion
方法仍然会被调用。
总结一下,如果preHandle
方法返回false
,则:
- 当前拦截器之后的拦截器和处理器的逻辑将不会被执行。
- 当前拦截器之前的拦截器的
afterCompletion
方法(如果有的话)将按照逆序被调用。 - 当前拦截器的
afterCompletion
方法(如果有的话)通常不会被立即调用,但会在整个请求处理流程结束后,与之前的拦截器一起被调用(前提是它们之前没有被异常中断)。然而,由于preHandle
的返回值,与当前请求直接相关的逻辑已经中断,所以当前拦截器的afterCompletion
方法中的代码应该处理这种中断情况,并释放任何已分配的资源。