Premise points
Benpian articles used for the project SpringMVC source code analysis basics ------ (b) of the Code
1. The important DispatcherServlet
DispatcherServlet is the core class of SpringMVC, commonly known as the front controller.
All client requests will be forwarded to it for execution, and finally we can execute our url request (find the request method according to the url, and execute it by reflection)
2. Key source code analysis
We understand that DispatcherServlet is a front-end controller, so our front-end requests will definitely pass his processing. Let's take a look at the DispatcherServlet class.
Looking at the class diagram (ctrl+alt+u),
you can see that its parent class is FrameworkServlet, and the parent class of FrameworkServlet is HttpServlet. In Java, the operation of the subclass needs to execute the method of the parent first, so the method of HttpServlet is executed first. . See the service() method of HttpServlet.
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < lastModified) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
What we defined in the project are all GET methods, so this method executes to the doGet(req, resp) method.
Enter the doGet method of the subclass FrameworkServlet and
enter this method, you can see that
this doService() is the core method. Enter and have a look (after entering, we will find that we have come to the front controller, which is DispatcherServlet)
and then jump to doDispatch (request, response), enter this method.
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);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
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;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
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);
}
}
}
}
Now we take a look at this method doDispatch(HttpServletRequest request, HttpServletResponse response).
processedRequest = checkMultipart(request);
Determine whether it is a file uploadmappedHandler = getHandler(processedRequest);
Enter the getHandler() method to view.
We can view the HandlerMapping in the for loop through debug.
There are five in total, we just need to understand the first two. We mainly look at the first one. Check the data
this.handlerMapping[0] data, you can see that there are interceptors we defined, as well as url mapping. At this time we know that HandlerMapping stores all the information of mvc. The data of this.handlerMapping[1] is similar, he just collects xml startup configuration information. And the this.handlerMapping[0] data is the configuration information that is started by collecting annotations.
After seeing the
entry,
enter the
lookupPath variable to store the visited url.
We are back to DispatcherServlet.HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());寻找适配器
This method mainly judges whether it is a subclass of HandlerMethod, and the latter judgement of supportsInternal((HandlerMethod) handler) returns true when handler instanceof HandlerMethod is true.if (!mappedHandler.applyPreHandle(processedRequest, response))
PreHandle should be familiar. Isn't this the first method of our interceptor?
Enter to see the code.
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 获取所有的拦截器
HandlerInterceptor[] interceptors = getInterceptors();
// 拦截器判空
if (!ObjectUtils.isEmpty(interceptors)) {
// 遍历所有拦截器
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
// 调用拦截器的preHandle()方法
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
There are several important statements to
if (this.synchronizeOnSession)
determine whether to use the session
invokeHandlerMethod(request, response, handlerMethod);
execution target methodmappedHandler.applyPostHandle(processedRequest, response, mv);
The same as the fourth point, the PostHandle() method of the execution interceptor, the internal method is the same, so I won’t look at it.processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
Enter the viewing method. The method of
render(mv, request, response);
setting the rendering view
mappedHandler.triggerAfterCompletion(request, response, null);
is clicked to enter the view~~ (actually, if you look at this AfterCompletion familiar, you should know what it is)~~
It is easy to understand, this is the call of the interceptor's AfterCompletion() method, which also explains, we The question mentioned in the basic knowledge (2), why two are after the execution of the target method. In fact, the difference between them is that one (PreHandle) is set before rendering, and the other (AfterCompletion) is set after rendering.