Overall flow
- All requests are intercepted to
DispatcherServlet
, which is also a Servlet
, executing doService
.
- Snapshot all the parameters in the request and set some objects in the framework to the
request
object.
- Call the
doDispatch(request,response)
method.
- Call the
getHandler
method to get the corresponding Handler
.
- Call
getHandlerAdapter
to get the corresponding HandlerAdapter
.
- Apply the interceptor’s
PreHandler
, or return it directly if the interceptor’s PreHandeler
returns false.
- Call the
handler
of the HandlerAdapter
object to get the ModelAndView
object.
- Apply the
postHandle
method of the interceptor.
- Call
processDispatchResult
to process the result, which internally calls the interceptor’s afterCompletion
method.
Source code details
How do I get the corresponding Handler?
Getting the Handler
is done through the getHandler
method.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
|
This code looks very simple, it iterates through handlerMappings
, and gets HandlerExecutionChain
from HandlerMapping
which is our handler
.
This HandlerExecutionChain
contains the handler
and HandlerInterceptor
arrays. That is, we get handler
which is actually a chain of processes.
Why do we need HandlerAdapter and how does it get it?
SpringMVC’s handler
is implemented in more ways, such as by inheriting from Controller
, by annotating the controller, and by HttpRequestHandler
. Because the implementation of handler
is different, the way to call it is also uncertain. So HandlerAdapter
is introduced for adaptation.
The HandlerAdapter
interface has three methods.
1
2
3
4
|
//判断当前的HandlerAdapter是否支持HandlerMethod
boolean supports(Object handler);
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
long getLastModified(HttpServletRequest request, Object handler);
|
Getting a HandlerAdapter
is done through the getHandlerAdapter
method. By analyzing the HandlerAdapter
source code. We can see that HandlerAdapter
actually finds a currently supported handler
from the HandlerAdapter
list.
1
2
3
4
5
6
7
8
9
10
11
12
|
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
if (ha.supports(handler)) {
return ha;
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
|
This method simply iterates through the HandlerAdapter
list, finds one that supports the current handler
, and returns it.
The handler method execution process
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
//最终拿到了我们的Controller类
Class<?> clazz = ClassUtils.getUserClass(handler);
//判断是否使用了@SessionAttributes
Boolean annotatedWithSessionAttributes = this.sessionAnnotatedClassesCache.get(clazz);
if (annotatedWithSessionAttributes == null) {
annotatedWithSessionAttributes = (AnnotationUtils.findAnnotation(clazz, SessionAttributes.class) != null);
this.sessionAnnotatedClassesCache.put(clazz, annotatedWithSessionAttributes);
}
if (annotatedWithSessionAttributes) {
// Always prevent caching in case of session attribute management.
checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
// Prepare cached set of session attributes names.
}
else {
// 禁用缓存
checkAndPrepare(request, response, true);
}
// Execute invokeHandlerMethod in synchronized block if required.
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
return invokeHandlerMethod(request, response, handler);
}
}
}
return invokeHandlerMethod(request, response, handler);
}
|
Finally it comes to the invokerhandlerMethod
method.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
//获取处理请求的方法
Method handlerMethod = methodResolver.resolveHandlerMethod(request);
//创建各种组件
ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
ServletWebRequest webRequest = new ServletWebRequest(request, response);
ExtendedModelMap implicitModel = new BindingAwareModelMap();
//调用方法拿到结果
Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
//获取ModelAndView
ModelAndView mav =
methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
//更新view中的属性
methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
return mav;
}
|
Finally call mappedHandler.applyPostHandle(processedRequest, response, mv);
for post-processing. The post-processing process is to call all the post-interceptors for processing.
Filter and Interceptor
The way Filter is implemented
Implement the Filter
interface
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
@WebFilter(filterName = "filterOne", urlPatterns = {"/*"})
public class FilterOne implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("init");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("===========before doFilter");
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("===========after doFilter");
}
@Override
public void destroy() {
System.out.println("destroy");
}
}
|
Implementation of Interceptor
Implement the HandlerInterceptor
interface.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("Controller调用之前的拦截器。。。");
return true;
}
/**
* 该方法controller调用之后,页面渲染之前执行,要preHandler返回ture才会执行该方法
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("controller调用之后,页面渲染之前执行");
}
/**请求完成之后执行的拦截器,要preHandler返回ture才会执行该方法
*
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("请求完成之后执行的拦截器");
}
}
|
Configure it in the springmvc
configuration file.
1
2
3
4
5
6
7
8
9
|
<mvc:interceptors>
<!-- 使用bean定义一个Interceptor,直接定义在mvc:interceptors根下面的Interceptor将拦截所有的请求 -->
<bean class="com.host.app.web.interceptor.AllInterceptor"/>
<mvc:interceptor>
<mvc:mapping path="/test/number.do"/>
<!-- 定义在mvc:interceptor下面的表示是对特定的请求才进行拦截的 -->
<bean class="com.host.app.web.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
|
Similarities
- Both can intercept requests and filter them
- Both apply the filter (chain of responsibility) design pattern
Differences
Filter
filters are more accessible and are configured in web.xml
Interceptor
is smaller in scope and is configured in springmvc
- Before going to
springmvc
processing, first process web.xml
Reference https://zofun.github.io/2020/05/20/%E8%B7%9F%E8%B8%AASpringMVC%E8%AF%B7%E6%B1%82%E8%BF%87%E7%A8%8B/