Popular framework series - "core source code" of Spring M-V-C;

Posted by Drumminxx on Wed, 17 Nov 2021 22:36:40 +0100

@TOC #Popular frame series
Record every step of the program___ auth:huf

Start with a new chapter; Chapter reading needs attention first; Because I want to participate in the selection of technical articles; Need a certain amount of fans; The number of fans has reached a certain number. All article reading restrictions will be fully liberalized; Thank you for your support~ We have started a new chapter from today. This series is a long chapter; An article is a knowledge point. The article serial number is the serial number of the article written by the author; It is not the same sequence number as the Spring principle series; Each chapter is not particularly closely related; Why do I want to talk about Spring M-V-C? Because the source code of Spring M-V-C is automatically assembled in our SpringBoot; The core idea of Spring M-V-C is adopted; SpringBoot is the basis of the components we will talk about later. Or the native distributed processing scheme of SpringCloud. It is one of the core elements of the most basic architecture. SpringBoot is automatically configured; It makes us plug and play. It's very convenient; We also know that there are many articles on spring MVC on the Internet, which are very excellent; The author tries to analyze Spring m-v-c from the perspective of Spring. Is this chapter about Spring MVC? I don't think so. What kind of technical point does our book describe?; Is SpringBoot Spring MVC? Yes! But not exactly! Is Spring MVC a SpringBoot? no Not at all! SpringBoot in my understanding; SpringBoot is an assembly idea; It just happens to be equipped with Spring M-V-C;

What did spring MVC do when it started? Be patient to read it; It will be more delicate with what you usually understand; More transparent;

First of all, in our Spring source code chapter, we introduce an interface called InitializingBean, which implements a method called afterpropertieset, which we use as the InitializingBean implementation class; When Spring is started and the class is initialized, we will call afterpropertieset;
Our Spring MVC did such a thing when it was started;

AbstractHandlerMethodMapping implements InitializingBean. After propertiesset method is executed during startup;

The following is the diagram
After propertieset will be called when starting, so we'll study it step by step based on this;

Here, get the names of all beans through getCandidateBeanNames(). Match them one by one; Here we read quickly; Don't remember this;

When we judge by isHandler in processcandidate bean (string beanname) method; There are two main judgments here;

The first one is whether the type is Controller or RequestMapping; If yes, return true;

If True is returned, we will enter the detectHandlerMethods(Object handler) method;

protected void detectHandlerMethods(Object handler) {
		Class<?> handlerType = (handler instanceof String ?
				obtainApplicationContext().getType((String) handler) : handler.getClass());

		if (handlerType != null) {
			Class<?> userType = ClassUtils.getUserClass(handlerType);
			"He will pass this selectMethods Find the Controller All of method"
			"The following points are very important. It will Method Object as Key, Path as value Save in this Map in"
			"There will be graphic explanation below"
			Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
					(MethodIntrospector.MetadataLookup<T>) method -> {
						try {
							return getMappingForMethod(method, userType);
						}
						catch (Throwable ex) {
							throw new IllegalStateException("Invalid mapping on handler class [" +
									userType.getName() + "]: " + method, ex);
						}
					});
			if (logger.isTraceEnabled()) {
				logger.trace(formatMappings(userType, methods));
			}
			else if (mappingsLogger.isDebugEnabled()) {
				mappingsLogger.debug(formatMappings(userType, methods));
			}
			"Start traversing the Controller All method objects. "
			methods.forEach((method, mapping) -> {
				Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
				"Then enter this registerHandlerMethod method; "
				registerHandlerMethod(handler, invocableMethod, mapping);
			});
		}
	}

Then register(T mapping, Object handler, Method method) method is scheduled. The following is the source code

public void register(T mapping, Object handler, Method method) {
			"Lock.We'll have a chapter later JUC Lock mechanism inside;Concurrent;And its use"
			this.readWriteLock.writeLock().lock();
			try {
				"This encapsulates the key HandlerMethod"
				HandlerMethod handlerMethod = createHandlerMethod(handler, method);
				validateMethodMapping(handlerMethod, mapping);
				"Get its path"
				Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
				"Take the path as Key  mapping As Key Save."
				"mapping There are actually many different types Condition"
				"There will be an explanation below;"
				for (String path : directPaths) {
					this.pathLookup.add(path, mapping);
				}
				
				String name = null;
				"set up HandlerMethod"
				if (getNamingStrategy() != null) {
					name = getNamingStrategy().getName(handlerMethod, mapping);
					addMappingName(name, handlerMethod);
				}
				""
				CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
				if (corsConfig != null) {
					corsConfig.validateAllowCredentials();
					this.corsLookup.put(handlerMethod, corsConfig);
				}

				this.registry.put(mapping,
						new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null));
			}
			finally {
				this.readWriteLock.writeLock().unlock();
			}
		}

Viewing mapping parameters;

mapping will set getMappingForMethod in the previous detectHandlerMethods callback method, and finally the builder (Bean will be instantiated through design mode here;)

This is the whole process of loading a Controller into a container or assembling it into a pathLookup in Spring startup; This is the pre knowledge point; It must also be understood; Many students also asked me in private; When explaining Spring; I hope it can be more detailed; The author also tries to explain this thing thoroughly; The next step is the basic knowledge of Spring MVC

From deep to shallow; Above, we explained how to load the Controller into pathLookup as a configuration when Spring starts. We use pathLookup as a stepping stone; Start to analyze the startup process of our Spring MVC in detail; What does pathLookup do?

What is the lifecycle of a Servlet?

We use automatic configuration for SpringBoot. But the core principle remains unchanged. Our spring MVC core class: DispatcherServlet;; When we play spring MVC, the first thing we need to do is to configure dispatcher servlet in web.xml; Use its methods to intercept all our requests; Distribute all our requests; We assume that our container has been started successfully; From now on, we will make a request now


(the computer at home is about to be scrapped; there is no Google or Firefox; we use a browser at will. It does not affect our traceability to the principle)

DispatcherServlet;

Isn't this class strange? The parent class of this class DispatcherServlet is - > the parent class of frameworkservlet is - > the parent class of httpservlet bean is - > the parent class of httpservlet is - > genericservlet implements the life cycle of the above servlets, that is, the life cycle of DispatcherServlet; But its function is more powerful; We asked to come in and be captured;

Let's start the main process of spring MVC:

All URLs we request will be intercepted by the service of the FrameworkServlet; Yes, all requests; Whether the request path behind the Url is correct or not; All requests from this port (including project name) will be intercepted by this method
The logic of the source code is very simple. Get the httpmethod in the request information; The heetmethod contains Post,Get,Put,Patch,Delete, etc; In this method, all htteMethod == null or PATCH will call processRequest; Others call HttpServlet for distribution;

Is to Get what the request is, Post or Get or other; When we finally enter super.service, we will actually enter the service method of HttpServlet;

 protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		"obtain httpServlet Just get it Get perhaps Post  The following source code is very simple."
		"Please read by yourself; We take Post As an example;"
        String method = req.getMethod();
        long lastModified;
        if (method.equals("GET")) {
            lastModified = this.getLastModified(req);
            if (lastModified == -1L) {
                this.doGet(req, resp);
            } else {
                long ifModifiedSince;
                try {
                    ifModifiedSince = req.getDateHeader("If-Modified-Since");
                } catch (IllegalArgumentException var9) {
                    ifModifiedSince = -1L;
                }

                if (ifModifiedSince < lastModified / 1000L * 1000L) {
                    this.maybeSetLastModified(resp, lastModified);
                    this.doGet(req, resp);
                } else {
                    resp.setStatus(304);
                }
            }
        } else if (method.equals("HEAD")) {
            lastModified = this.getLastModified(req);
            this.maybeSetLastModified(resp, lastModified);
            this.doHead(req, resp);
        } else if (method.equals("POST")) {

		"Suppose we are Post Request for. Eventually called HttpServlet of doPost method "
		"And will request response afferent;"
		"First of all, let me ask. this this Which object is it?"
		"yes HttpServlet Are you? Or no HttpServlet? The following figure gives the answer"
		"But the author hopes that readers can look down with an answer in their heart"
            this.doPost(req, resp);
        } else if (method.equals("PUT")) {
            this.doPut(req, resp);
        } else if (method.equals("DELETE")) {
            this.doDelete(req, resp);
        } else if (method.equals("OPTIONS")) {
            this.doOptions(req, resp);
        } else if (method.equals("TRACE")) {
            this.doTrace(req, resp);
        } else {
            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[]{method};
            errMsg = MessageFormat.format(errMsg, errArgs);
            resp.sendError(501, errMsg);
        }

    }

But the dispatcher servlet has no doPost method! Only its parent class implements the doPost method, that is, our FrameworkServlet
Come to our FrameworkServlet.doPost method:

processRequest is called directly

The annotation here is very clear. Only the request will be processed, and the result will not be handed over to the Do service for processing;
Before doService, ServletRequestAttributes are encapsulated, which is actually request response;
Don't look at the source code here; Because it is not very much related to the overall process. Learning source code is the scenery along the highway; Or there are warning signs; Or there is a gas station; We have to follow the process. Finally reach the destination; Where we don't understand, we can go back and study it in detail;

Let's move on to doService, which is the doService method of our dispatcher servlet;

@Override
	protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
		logRequest(request);

		Map<String, Object> attributesSnapshot = null;
		if (WebUtils.isIncludeRequest(request)) {
			attributesSnapshot = new HashMap<>();
			Enumeration<?> attrNames = request.getAttributeNames();
			while (attrNames.hasMoreElements()) {
				String attrName = (String) attrNames.nextElement();
				if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
					attributesSnapshot.put(attrName, request.getAttribute(attrName));
				}
			}
		}
		"Here request Context parameters are installed in. Web_application_context"
		"Leave a suspense here; This suspense is very important;  If you have seen the source code before, you should know"
		"what is it? Then there will be a detailed article"
		request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
		request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
		request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
		request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

		if (this.flashMapManager != null) {
			FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
			if (inputFlashMap != null) {
				request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
			}
			request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
			request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
		}
		try {
		"We look at our do Service The way they are Request Set up a pile of Attribute"
		"Then entrust doDispatch dispatch"
			doDispatch(request, response);
		}
		finally {
			if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
				// Restore the original attribute snapshot, in case of an include.
				if (attributesSnapshot != null) {
					restoreAttributesAfterInclude(request, attributesSnapshot);
				}
			}
		}
	}

Start scheduling our core method DispatcherServlet doDispatch. All the principles of spring MVC in the market interviews are basically carried out around this doDispatch. If you ask about the pre knowledge of doDispatch and what you did in front of doDispatch; In terms of details, it is estimated that not many people will answer; Below, I will mark all the principles with ① ② ③... Points, and then match them with a picture on the Internet; It will be clear;

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		"1:We asked to come in DispatcherServlet "
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;
		"WebAsyncManager It is mainly used to manage the processing of asynchronous requests"
		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

		try {
			"ModelAndView Is the return model; At present, it is just a statement that there is no specific explanation here modelAndview"
			ModelAndView mv = null;
			Exception dispatchException = null;

			try {
				"Encapsulates the current incoming request Request information;"
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				"We can follow the previous one in this method pathLookup Corresponding in getHandler"
				"stay getHandler Method internal,Will be based on url go pathLookup Inside take MappingInfo"
				
				"2 according to URL To find HandlerMapping Then return mappedHandler come back DispatcherServlet"
				mappedHandler = getHandler(processedRequest);
				"If according to url No corresponding MappingInfo Information throw exception;"
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				"3:Our basis mappedHandler Go find HandlerAdapter "
				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 (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				"4: adopt HandlerAdapter Call our specific requestMapping Request method"
				"that is Controller Inside method mv that is ModelAndView  return ModelAndView"
				"End of execution handle Method is then returned to the viewer;"
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());	

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}
				"Find the corresponding graph name and put it in the ModelAndView of ViewName inside"
				applyDefaultViewName(processedRequest, mv);
				"Inside execution HandlerInterceptor Interceptor;"
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			catch (Throwable err) {
				dispatchException = new NestedServletException("Handler dispatch failed", err);
			}
			"5:  DispatcherServlet take ModelAndView Pass to ViewReslover view resolver "
			"ViewReslover Return specific information after parsing View"
			"6: I won't elaborate on this render method, adopt render Method is returned after parsing View"
			" DispatcherServlet yes View Render view (i.e. model data) model Fill into view)"
			"DispatcherServlet Respond to the user."
			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);
				}
			}
		}
	}
The following is the flow diagram:

In this way, we understand the core source code of spring MVC in detail; Finally, we summarize a wave:

1. How spring talks about seamless undertaking of spring MVC; (keynote speaker)

Use InitializingBean to schedule afterpropertieset to encapsulate all requestMapping requests in the Controller when the Spring container is started; Encapsulated as a MappingInfo object in a Map called pathLookup;

2. The principle of handling requests by doDispatch of spring MVC; (follow up)

DispatcherServlet: the front-end scheduler is responsible for intercepting the request and distributing it to each controller method. HandlerMapping: it is responsible for matching according to the requested URL and the configuration @ RequestMapping mapping. If it is matched, the Handler (the method of the specific controller) will be returned

HandlerAdaper: responsible for calling Handler - specific method - return the name of the view, and Handler encapsulates it into

Modelandview (encapsulates the view name and the data of the request field)

Viewdissector: find the specific jsp encapsulated in the View object according to the View name and address in ModelAndView

View: the client that performs view rendering (converting jsp into html content -- this is the business of Servlet container) and finally responds to it

Then we will extend the spring MVC parent-child container;

see you !

Topics: Java Spring Back-end