When the web application receives a request, it will send the request to
org.springframework.web.servlet.DispatcherServlet#doDispatch
The flow of MVC, that is, the flow of the doDispatch method
Find Handler
mappedHandler = getHandler(processedRequest);
Find HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler. getHandler());
execute handler
mv = ha. handle(processedRequest, response, mappedHandler. getHandler());
result processing
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
In the step of finding the handler, how do you do it?
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; }
In fact, it is to traverse the List list, and the initialization of the list is done in the init when the Servlet is loaded for the first time. That is, the init trigger call in the DispatcherServlet parent class HttpServletBean will eventually be transferred to
org.springframework.web.servlet.DispatcherServlet#initHandlerMappings
private void initHandlerMappings(ApplicationContext context) { this. handlerMappings = null; if (this. detectAllHandlerMappings) { // Find all HandlerMappings in the ApplicationContext, including ancestor contexts. Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans. isEmpty()) { this.handlerMappings = new ArrayList<>(matchingBeans.values()); // We keep HandlerMappings in sorted order. AnnotationAwareOrderComparator.sort(this.handlerMappings); } } else { try { HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class); this.handlerMappings = Collections.singletonList(hm); } catch (NoSuchBeanDefinitionException ex) { // Ignore, we'll add a default HandlerMapping later. } } // Ensure we have at least one HandlerMapping, by registering // a default HandlerMapping if no other mappings are found. if (this. handlerMappings == null) { this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); if (logger. isTraceEnabled()) { logger.trace("No HandlerMappings declared for servlet '" + getServletName() + "': using default strategies from DispatcherServlet.properties"); } } }
The code is very clearly commented:
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts. // Ensure we have at least one HandlerMapping, by registering a default HandlerMapping if no other mappings are found.
Get it from the container first, and then get it from the configuration file if it is not found in the container. You can refer to the configuration in the DispatcherServlet.properties file.
The spring boot project has completed the loading of HandlerMapping in spring-boot-autoconfigure.
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
So, after the container starts, the container has already loaded the default
org.springframework.web.servlet.HandlerMapping
for
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
After instantiation, calls
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#afterPropertiesSet
This method will extract the handler in the container
protected boolean isHandler(Class<?> beanType) { return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class)); }
After finding the handler, find all the mapper methods of the handler and package them into
RequestMappingInfo, last registered to
private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
key is url, value is RequestMappingInfo
After these preparations are completed, the first step of finding the Handler can be completed. These preparations are also completed when the application starts.
Find HandlerAdapter, similar to Find Handler.
At this point, the basic logic of MVC is clear. The first is to start the container and load the mapping between Handler and url. When a request comes in, find the hanlder according to the url. Once found, execute it. Of course, corresponding to hander, there are two extension points
org.springframework.web.servlet.HandlerExecutionChain#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; }
org.springframework.web.servlet.HandlerExecutionChain#applyPostHandle
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils. isEmpty(interceptors)) { for (int i = interceptors. length - 1; i >= 0; i--) { HandlerInterceptor interceptor = interceptors[i]; interceptor.postHandle(request, response, this.handler, mv); } } }
As you can see, it will be executed before and after executing the handler
org.springframework.web.servlet.HandlerInterceptor
This is when the interceptor is executed.
(unfinished)