This deep analysis of the execution process of SpringMVC will help you improve to a higher level immediately after reading it!

Follow the WeChat public account “Java Architecture Stack”, reply [Interview Collection], and immediately receive a full set of testing resources.

Preface

Hello everyone, I am Brother Qianfengwen. In the previous article, Brother Wen introduced the construction process of the SpringMVC entry case. I believe that everyone has a basic understanding of the basic use of SpringMVC. Today, Wenge will use the source code based on the entry case we built for you last time. Angle to analyze the execution process of SpringMVC. Through reading this article, friends must have a deeper understanding of the execution process of SpringMVC. Well, now let’s start to explore the underlying process of SpringMVC!

Several core components needed for SpringMVC execution

Before in-depth analysis of the SpringMVC execution process, we first need to understand several core components that need to be used in the SpringMVC execution process. Because the entire execution process of SpringMVC is implemented around these core components. Now Brother Wen will lead you to understand what are the core components needed for SpringMVC execution.

1. DispatcherServlet front controller

The front controller is the core of the entire SpringMVC execution process. We use the front controller to receive requests sent by front-end users, and pass the requests to various components through the front controller for processing. When we built the SpringMVC entry environment, we configured the front controller. Do you guys remember where it was configured? It is configured in web.xml.

2.HandlerMapping processor mapper

After the front controller receives the user’s request, the front controller itself will not process the user’s request. Instead, the user’s request is handed over to the processor mapper for processing. The processor mapper will process the user’s request and process it into a Handler. After that, the processor mapper will return the processed Handler to the front controller. In SpringMVC, the official processor mapper is designed as an interface. Post the official part of the source code for everyone:

public interface HandlerMapping {
    String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";
    String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";
    String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";
    String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";
    String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";
    String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";

    @Nullable
    HandlerExecutionChain getHandler(HttpServletRequest var1) throws Exception;
}

Why is it designed as an interface? Think about it, everyone, in fact, the reason for this design is that we can implement many different types of processor mappers, and choose the most suitable processor mapper for processing on different occasions.

3.HandlerAdapter processor adapter

After the processor mapper processes the request, it returns the processed Handler to the front controller. The front controller will not do further processing by itself, but hand the Handler to the processor adapter HandlerAdapter for further processing. The processor adapter will process the Handler into a logical view of ModelAndView. In the SpringMVC source code, SpringMVC officially designed the processor adapter as an interface, and we posted some source code to show you:

public interface HandlerAdapter {
    boolean supports(Object var1);

    @Nullable
    ModelAndView handle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;

    long getLastModified(HttpServletRequest var1, Object var2);
}

Why is it designed as an interface, just like the processor mapper, we can implement many different types of processor adapters. In this way, under different occasions, we can use the most matching processor adapter to process the Handler.

4.ViewResolver view resolver

After the processor adapter processes the Handler into a ModelView logical view, the processor adapter will return the ModelAndView to the front controller. At this time, the front controller will pass the ModelAndView to the view parser for further analysis, and finally resolve it into a view for rendering. When we build the SpringMVC environment, we will configure the view parser in the SpringMVC configuration file springmvc.xml, as follows:

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/pages/"></property>
    <property name="suffix" value=".jsp"></property>
</bean>

In the official source code, SpringMVC officially designed the view parser as an interface. Brother Wen posted part of the official source code for everyone:

public interface ViewResolver {
    @Nullable
    View resolveViewName(String var1, Locale var2) throws Exception;
}

5.View view object

When the view parser parses the ModelAndView into a View. Views need to be rendered to actually be seen by the user. Therefore, in the SpringMVC source code, SpringMVC officially designed View as an interface, so that various types of views can be realized, and views can be rendered in different formats according to user needs. Brother Wen posted some official source code for everyone:

public interface View {
    String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus";
    String PATH_VARIABLES = View.class.getName() + ".pathVariables";
    String SELECTED_CONTENT_TYPE = View.class.getName() + ".selectedContentType";

    @Nullable
    String getContentType();

    void render(@Nullable Map<String, ?> var1, HttpServletRequest var2, HttpServletResponse var3) throws Exception;
}

So far, Brother Wen has introduced several key components required for SpringMVC execution. Next, Brother Wen will lead you to analyze the execution process of SpringMVC from the source code level.

SpringMVC execution process source code analysis

We use the case written for you in the previous article as an entry point to analyze the entire execution process. The controller code we wrote in the previous article is as follows:

@Controller
public class HelloController {

    @RequestMapping(value = "hello") //map url request
    public String hello(){
        return "success";
    }
}

We resolve by sending localhost:8080/hello. Brother Wen will show you the whole process at the break point, so that you can understand the steps and details of SpringMVC execution very intuitively.

1. The front controller intercepts the user’s request

We first look at the inheritance system of the front controller, and we find that the front controller is a Servlet. We open the inheritance diagram of the front-end control:

We found that the front controller DispatcherServlet indirectly inherits the Servlet interface, so we think that the front controller is a Servlet! In javaweb we learned Servlet, there is a method called service. This method is the method of providing services, that is, receiving and processing user operations are all implemented in the service method. In SpringMVC, the Servlet of the front controller is also needed to receive and process user requests. What is this method? It is processed by a method called doService.

In the doService method, there is a core method to further process the request:

Through source code analysis, we found that the user’s request is further handed over to a method called doDispatcher in the front controller for processing. We start the project in debug mode and the browser sends the request. By means of breakpoints, it is judged whether the user’s request has been handed over to the doDispatcher method for processing:

After actual testing, we found that the user’s request was indeed handed over to the front controller’s doDispatcher method for interception.

2. The processor mapper executes the user’s request

Through the content of the previous section, it is found that the user’s request is intercepted by the doDispatcher method of the front controller. We use F8 to release the breakpoint and release the breakpoint to the following code:

What does this code do? It is to use the processing mapper to further process the user’s request. How to deal with it, we press the F7 shortcut key to enter the interior of the current method:

We found that the entire process of the processor mapper processing user requests is to first obtain all the processor mappers, and then loop through all the processor mappers to process the user’s request and obtain the corresponding Handler. Some friends may have to ask, how many processor mappers does SpringMVC initialize?

Through the source code discovery, in the end it was the RequestMappingHandlerMapping processor mapper that helped us process the request. The processed result is encapsulated into the HandlerExecutionChain:

We found that the processor mapper encapsulates the Handler we process into a HandlerExecutionChain. And there is a handler object in the HandlerExecutionChain object, which is of the HandlerMethod type, which is the Handler object that the processor mapper finally processes our request into!

3. Get the processor adapter HandlerAdpater

Through the content of the previous section, we confirmed that the user’s request is processed into a Handler object by the processor mapper RequestMappingHandlerMapping. Next, we continue to use the F8 shortcut key to release our code, and release the code as follows:

What is the function of this code? We can all guess from the method name, which is to obtain the processor adapter to prepare for the Handler’s processing. How to get HandlerApater? We use the F7 shortcut key to enter the interior of the getHandlerAdapter method:

Through the above source code, we found that the acquisition principle of the processor adapter is to loop through all processor adapters, and then determine whether the current processor adapter supports adaptation processing, and if so, return it to the processor adapter. The classic design pattern is used here, which is the adapter pattern. SpringMVC initializes the following 3 processor adapters for us:

These adapters all implement the class HandlerApater interface:

public interface HandlerAdapter {
    boolean supports(Object var1);

    @Nullable
    ModelAndView handle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;

    long getLastModified(HttpServletRequest var1, Object var2);
}

There are two very important methods here, Brother Wen will explain to you:

  • supports method Determines whether the processor supports the method of adapting the Handler, and the return value is a Boolean value. true means support, false means not support.

  • handle method The method used by the processor adapter to process the Handler object, and the return value is the logical view of ModelAndView.

Which processor adapter supports the adaptation of the current Handler? Through source code tracking, we found that this adapter is RequestMappingHandlerAdapter.

4. The processor adapter processes the Handler

In the previous section, we found that SpringMVC first helped us find the most suitable processor adapter to prepare for further processing by Handler. We continue to use the F8 shortcut key to release the breakpoint to the following position:

The meaning of this code is to get the current Handler object, use the processor adapter RequestMappingHandlerAdapter to process it, and finally process the Handler object into ModelAndView. How to deal with it? We use the F7 shortcut key to enter the handle method:

We found that the handleInternal method is called inside the handle method to further process the Handler, and we use the F7 shortcut key to continue entering the current method:

We released the breakpoint and found that the invokeHandlerMethod method was called inside the handleInternal method. This method processes the Handler and finally returns the ModelAndView. How does invokeHandlerMethod handle our Handler? We continue to enter the interior of this method:

This method is the method of processing Handler. We use F7 to enter the method and check the specific processing details:

Continue to enter the invokeForRequest method:

Through the above source code execution process, we found that the processing of our Handler by the processor adapter is based on the reflection mechanism! Finally, return the processing result to ModelAndView.

5. Processing distribution results

Through the previous section, we found that SpringMVC processes Handler into ModelAndView through a processor adapter. Next, we continue to release the breakpoint to the following position:

In fact, it is to further process our logical view ModelAndView, how to deal with it? Let’s go inside this method to see the details:

We found that there is a method called render to process the logical view. Let’s enter this method to see the specific processing process:

So how to get the View view object, it must be obtained by using the view resolver to help us. Let’s go inside the resolveViewName method to see the specific implementation details:

We found through the source code that the acquisition of the view resolver is to loop through the initialized view resolver for parsing and processing, and finally obtain a View view object. This view resolver is the view resolver InternalResourceViewResolver we configured in the springmvc.xml configuration file. The View object finally processed by the processor adapter is of type InternalResourceView.

After we use the view parser to get the View object, how to deal with the View, we continue to use the F8 shortcut key to release the breakpoint:

We use the F7 shortcut key to enter this method:

public void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
    if (this. logger. isTraceEnabled()) {
        this.logger.trace("Rendering view with name '" + this.beanName + "' with model " + model + " and static attributes " + this.staticAttributes);
    }

    Map<String, Object> mergedModel = this.createMergedOutputModel(model, request, response);
    this. prepareResponse(request, response);
    this.renderMergedOutputModel(mergedModel, this.getRequestToExpose(request), response);
}

In fact, the content of the view is responded to the browser through a Response object, and the result of the final response is what the user sees. At this point, Brother Wen demonstrated the execution process of SpringMVC from the perspective of source code. Do the friends have a deeper understanding of the execution process of SpringMVC?