Simple combing of spring boot MVC process

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)