spring mvc的拦截器大家应该都不陌生,可以在进入响应controller之前以及之后进行一些处理。
但有些情况下,拦截器中的preHandle方法总会执行两次,这是为何?
在解答此问题之前,我们先创建一个简单的controller
@RestController
public class HelloWorld {
@GetMapping("/hello")
public String hello() {
System.out.println("== Hello ==");
return "hello";
}
}
为了更加清楚的描述问题,这里创建两个拦截器
@Configuration
public class WebConfig extends DelegatingWebMvcConfiguration {
@Override
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new HandlerInterceptorAdapter() {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("====> Pre Handle A " + Thread.currentThread().getId());
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
System.out.println("After Completion A <==== " + Thread.currentThread().getId());
}
@Override
public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("After Concurrent Started A <==== " + Thread.currentThread().getId());
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
System.out.println("Post Handle A <==== " + Thread.currentThread().getId());
}
}).addPathPatterns("/**").order(1);
registry.addInterceptor(new HandlerInterceptorAdapter() {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("====> Pre Handle B " + Thread.currentThread().getId());
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
System.out.println("After Completion B <==== " + Thread.currentThread().getId());
}
@Override
public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("After Concurrent Started B <==== " + Thread.currentThread().getId());
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
System.out.println("Post Handle B <==== " + Thread.currentThread().getId());
}
}).addPathPatterns("/**").order(2);
super.addInterceptors(registry);
}
}
在拦截器实现中,我们特意打印了当前线程的线程号。
访问 /hello
,我们可以得到如下输出
====> Pre Handle A 68
====> Pre Handle B 68
== Hello ==
Post Handle B <==== 68
Post Handle A <==== 68
After Completion B <==== 68
After Completion A <==== 68
可以看到,spring interceptor的运行机制就像剥洋葱一样,且对于同一个请求整个运行过程在同一个线程内完成。
接下来定义另一个controller,并返回StreamingResponseBody
@RestController
public class HelloWorld {
@GetMapping("/streaming")
public StreamingResponseBody streaming() {
System.out.println("== Streaming ==");
return (OutputStream outputStream) -> {
outputStream.write("streaming".getBytes());
outputStream.flush();
outputStream.close();
};
}
@GetMapping("/hello")
public String hello() {
System.out.println("== Hello ==");
return "hello";
}
}
此时访问 /streaming
,可以得到如下输出
====> Pre Handle A 74
====> Pre Handle B 74
== Streaming ==
After Concurrent Started B <==== 74
After Concurrent Started A <==== 74
====> Pre Handle A 75
====> Pre Handle B 75
Post Handle B <==== 75
Post Handle A <==== 75
After Completion B <==== 75
After Completion A <==== 75
从输出中可以看出,整个请求过程使用了两个线程,并且调用了拦截器中的afterConcurrentHandlingStarted方法。到这里各位看官应该就明白了,对于concurrent类型的返回值,spring会启用一个新的线程来处理concurrent类型消息,在新的线程中会重新调用preHandle方法。
那,postHandle方法是哪个线程在调用呢?各位如果感兴趣的话可以深入研究一下!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。