SpringMVC loads the process and attaches the process source code

Start by stealing (stealing) a picture on the Internet:
insert image description here
similar pictures can be seen everywhere, as long as they talk about the principle of SpringMVC, they are basically the same. Here I will analyze it from the perspective of source code.

From the figure, it can be seen that the DispatcherServlet class acts as a commander. The client needs to pass through it when it initiates a request, and every processing link in the middle also needs to go through it before proceeding to the next link.

Step 1, the user initiates a request and will first enter here DispatcherServlet#doDispatch

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;
				}
	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    
    
		if (this.handlerMappings != null) {
    
    
			for (HandlerMapping mapping : this.handlerMappings) {
    
    
				HandlerExecutionChain handler = mapping.getHandler(request);
				if (handler != null) {
    
    
					return handler;
				}
			}
		}
		return null;
	}

The above code returns the HandlerExecutionChain object after HandlerMapping analysis, which corresponds to steps 2 and 3 in the above figure.
Where is this.handlerMappings? Let's make a breakpoint and take a look.
insert image description here
We can see that there are several processor mappers, which also correspond to the part of the processor mapper in the above figure. These things can be understood as which mapper to use for different types of requests.

SimpleUrlHandlerMapping is implemented by configuring a set of URL Pattern-to-handler mappings, and its configuration is as follows:

<beans ...>
    <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
       <property name="mappings">
        <props>
           <prop key="/a.do">nController</prop>
           <prop key="/b.do">bController</prop>
         </props>
       </property>
    </bean>
    
    <bean id="nController" class="com.NController" />
    <bean id="bController" class="com.BController" />
</beans>

The other processor, RequestMappingHandlerMapping, is more commonly used by us, and it is a request for @Controller and @RequestMapping annotations.

Ok, continue to analyze, because RequestMappingHandlerMapping finally inherits the AbstractHandlerMethodMapping class, so the above process will enter AbstractHandlerMethodMapping#getHandlerInternal

	protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    
    
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
		this.mappingRegistry.acquireReadLock();
		try {
    
    
			HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
			return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
		}
		finally {
    
    
			this.mappingRegistry.releaseReadLock();
		}
	}

Where lookupPath is the URI we requested, such as "/hello/test"
to pass in this URI to get HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);

	protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
    
    
		List<Match> matches = new ArrayList<>();
		List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
public List<T> getMappingsByUrl(String urlPath) {
    
    
			return this.urlLookup.get(urlPath);
		}

This code uses the Map of the internal class MappingRegistry to obtain the directPathMatches object according to the key uri, as shown in the figure below:
insert image description here
you can see that it is a RequestMappingInfo object. I believe that everyone must have a question here, when was this object generated, and when was the mapping relationship in the above urlLookup put in.

If there is any doubt, let's solve it. . .

First of all, it is currently a popular spring boot project, because its automatic assembly is fragrant (it is indeed fragrant), Springmvc can be completed as long as a starter is introduced, and the core class of SpringMVC automatic assembly is WebMvcAutoConfiguration, and it is found that it contains RequestMappingHandlerMapping and Beans such as RequestMappingHandlerAdapter, students who are familiar with spring ioc know that these beans will eventually be injected into the spring ioc container.

So in the spring application startup process, the bean RequestMappingHandlerMapping will be instantiated and initialized, because it implements the InitializingBean interface, so the initialization process will execute its afterPropertiesSet method, and finally enter AbstractHandlerMethodMapping#initHandlerMethods

	protected void initHandlerMethods() {
    
    
		for (String beanName : getCandidateBeanNames()) {
    
    
			if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
    
    
				processCandidateBean(beanName);
			}
		}
		handlerMethodsInitialized(getHandlerMethods());
	}

Enter processCandidateBean, there is a

if (beanType != null && isHandler(beanType)) {
    
    
	detectHandlerMethods(beanName);
}

protected boolean isHandler(Class<?> beanType) {
    
    
		return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
				AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
	}

Have you seen the @Controller and @RequestMapping annotations, that is, the Controller we usually write, well, follow the detectHandlerMethods method, the logic of which is to traverse all the methods in the Controller to see if the method contains the @RequestMapping annotation, and then put Go to methods, as shown below:

insert image description here
The key is a Method object, and the value is a RequestMappingInfo object. Well, now we know that this object was generated here.

Then enter the registerHandlerMethod method, and finally enter the following code:

public void register(T mapping, Object handler, Method method) {
    
    
			this.readWriteLock.writeLock().lock();
			try {
    
    
				HandlerMethod handlerMethod = createHandlerMethod(handler, method);
				assertUniqueMethodMapping(handlerMethod, mapping);
				this.mappingLookup.put(mapping, handlerMethod);

				List<String> directUrls = getDirectUrls(mapping);
				for (String url : directUrls) {
    
    
					this.urlLookup.add(url, mapping);
				}

Finally saw this.mappingLookup.put(mapping, handlerMethod) and this.urlLookup.add(url, mapping);

That is to say, all the methods that use the annotation @RequestMapping in our custom Controller classes will be dumped into these two Maps in advance to prepare for subsequent requests to be taken out for processing.

Well, we have answered the two remaining questions above.

Then go back to the above MVC process
Object handler = getHandlerInternal(request);
From the above, the handler is a HandlerMethod object, which is passed into the following method

HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

Convert to HandlerExecutionChain object and return, then go down to HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    
    
		if (this.handlerAdapters != null) {
    
    
			for (HandlerAdapter adapter : this.handlerAdapters) {
    
    
				if (adapter.supports(handler)) {
    
    
					return adapter;
				}
			}
		}
	}

Among them, this.handlerAdapters is the processor adapter, here is to find the appropriate adapter and then return, the adapter found at this time is RequestMappingHandlerAdapter.class
and then continue above:

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

Method applyPreHandle

	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];
				if (!interceptor.preHandle(request, response, this.handler)) {
    
    
					triggerAfterCompletion(request, response, null);
					return false;
				}
				this.interceptorIndex = i;
			}
		}
		return true;
	}

Here I see the very familiar preHandle method, isn't it one of the interceptor methods that are usually used, its function is to end if it returns false, and continue to go down if it is true, it does it here.

// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

Finally enter RequestMappingHandlerAdapter#handleInternal

protected ModelAndView handleInternal(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    
    

		ModelAndView mav;
		checkRequest(request);

		// 省略代码

		return mav;
	}

The processor adapter returns ModelAndView after the processing is completed. Here, it returns if it exists. If it does not return null, if you use @ResponseBody to return null, because the json object needs to be returned, unless you want to return a jsp page, etc.
At the same time, this position is also step 7 in the above figure.

Go back to the central controller DispatchServlet and see
mappedHandler.applyPostHandle(processedRequest, response, mv);
here the postHandle method of the interceptor is executed.

Then execute the sentence
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
corresponding to the last steps 8, 9, and 10 in the above figure.

At this point the whole process ends.

Maybe you still have a question, that is, what is the interceptor object HandlerInterceptor put in, in fact, the timing is obtained from WebMvcConfigurer when the Spring IOC container instantiates the RequestMappingHandlerMapping object.
The key code is in:
org.springframework.web.servlet.config.annotation.WebMvcConfigurerComposite#addInterceptors

Guess you like

Origin blog.csdn.net/huangdi1309/article/details/123545519