Spring MVC is the most widely used framework for web applications. This article will analyze the implementation principle of Spring MVC in detail from the perspective of source code.
Spring MVC basic process
Before going into details, let’s briefly introduce the process of Spring MVC from a macro perspective, and understand the complete process from the user making a request to getting a response.
Process Introduction:
-
The user initiates the request to the DispatcherServlet (front controller)
-
The DispatcherServlet uses HandlerMapping to find the Handler that the user is requesting
-
HandlerMapping returns the execution chain, which consists of two parts:
- Handler object:
Handler
HandlerInterceptor
(interceptors)
- Handler object:
-
The front controller uses the HandlerAdapter (processor adapter) to package the Handler
-
Call the methods in the wrapped Handler to handle the business
-
When the business is completed, return the ModelAndView object, which contains two parts
Model
: Model dataView
: View name, not the real view
-
The DispatcherServlet gets the processed ModelAndView object
-
The DispatcherServlet gives the view name to the ViewResolver and looks for the view
-
The ViewResolver returns the actual view object to the DispatcherServlet
-
The DispatcherServlet passes the Model (data Model) to the view object for rendering
-
Returns the rendered view
-
The final view is returned to the user, generating a response
Spring MVC nine components
HandlerMapping
HandlerMapping is used to find a Handler, which can be a class or method. For example, each method labeled @RequestMapping can be treated as a Handler that handles the actual request processing. After the request arrives, the HandlerMapping is used to find the corresponding Handler and Interceptors for the request.
HandlerAdapter
By name, this is an adapter. Because the Handler in Spring MVC can be any form, as long as it can handle the request. However, when the request is given to servlets, since the method structure of servlets is in the form of doService(HttpServletRequest req, HttpServletResponse resp), Let the fixed Servlet handling method call Handler to handle, this step is the HandlerAdapter to do.
HandlerExceptionResolver
From its name, this is the component used to handle exceptions generated during Handler processing. Specifically, the function of this component is to set up the ModelAndView based on the exception, which is then rendered by the render() method, which in turn renders the ModelAndView into a page. However, HandlerExceptionResolver is only used to resolve exceptions generated during the processing phase of the request, and not during the rendering phase. This is one of the principles of Spring MVC component design.
ViewResolver
View resolver, which I’m sure you’re all familiar with. In the Spring MVC configuration file, there is usually an implementation class for this interface to resolve the view. The main purpose of this component is to resolve the String View name and Locale to a View of type View. This interface has only one resolveViewName() method. As you can see from the method definition, the View name returned by the Controller layer, which is a String, will eventually be resolved here to become a View. The View is used to render the page, that is, it fills the template with the parameters and data returned by the program to generate an HTML file. In this process, the ViewResolver does two important things, that is, the ViewResolver will find the template to use for rendering. And the technology used (in fact, the type of view, such as JSP, etc.). The default situation Condition, Spring MVC will automatically configure an InternalResourceViewResolver for us, this is the needle on the JSP type view.
RequestToViewNameTranslator
This component retrieves the viewName from the Request. Because ViewResolver is according to the ViewName lookup View, but some of the Handler after processing is completed, there is no set View no set ViewName, will find ViewName from the Request by the component.
LocaleResolver
Above we see the resolveViewName() method of the ViewResolver, which takes two arguments. So where does the second argument, Locale, come from? That’s what LocaleResolver does. The LocaleResolver is used to parse a Locale from a request. In mainland China, the Locale would be zh-CN or something like that, which is used to represent a region. This class is also the basis for i18N.
ThemeResolver
As the name suggests, this class is used to resolve topics. A theme is a collection of styles, images, and the display effects they create.
MultipartResolver
This is actually a familiar components, MultipartResolver used to process the upload Request, by ordinary Request packaging into MultipartHttpServletRequest. MultipartHttpServletRequest can getFile () direct access to the file, if it is multiple file upload, can also Map is obtained by invoking getFileMap such structure. The function of MultipartResolver is to encapsulate a common request, so that it has the function of processing file upload.
FlashMapManager
FlashMap is used to transfer parameter data before the Redirect. Will transfer the data to the request (by ServletRequestAttributes. GetRequest () to obtain) the properties of the OUTPUT_FLASH_MAP_ATTRIBUTE, so after the redirect handler, Spring automatically sets it up in the Model, and you can then retrieve the data directly from the Model. The FlashMapManager is used to manage flashMaps.
Spring MVC source analysis
Initialization phase
For Spring-MVC, the core class is the DispatcherServlet, which forwards requests to individual controllers for processing. The class diagram is as follows:
As you can see from the class diagram, DispatcherServlet actually ends up inheriting HttpServlet, which is the core class that the Web container starts with, and which has an init() method to do some initialization. Init () is rewritten in HttpServletBean as follows:
As you can see, in the init() method, the actual logic that does the initializing of the container is actually in the initServletBean() method, which is implemented in its subclass FrameworkServlet.
Clear from the above code, real initialization web IoC container is in initWebApplicationContext () method. The specific code is as follows:
In this method, the establishment of a father and son relationship of container, and ultimately calls the configureAndRefreshWebApplicationContext (cwac) method to initialize the IoC container. Methods ultimately calls the AbstractApplicationContext refresh () method.
Parent container (Root WebApplicationContext) : the three-tier architecture of the service layer, dao layer configuration, such as business beans, DataSource (DataSource). Typically, the name of the configuration file is ApplicationContext.xml. In web applications, it is usually loaded by ContextLoaderListener.
Child container (Servlet WebApplicationContext) : Configure the Web layer in the three-tier architecture, such as controller, View Resolvers and related beans. The configuration is loaded through the DispatchServlet provided in Spring MVC, and typically the configuration file is named Spring-servlet.xml.
For more information about parent and child containers, see Spring and SpringMVC Parent and child Containers.
After the IoC container is initialized, it finally callsDispatcherServlet
theonRefresh()
Methods,onRefresh()
Method is called directlyinitStrategies()
Method initializes the nine components of SpringMVC.
Run the call phase
Once the current initialization is complete, Spring MVC is ready to accept and process the request. Let’s take a look at the execution of a request as an example. For Spring MVC, all requests go through the doService() method of the DispatcherServlet. While the core logic in doService() is implemented by doDispatch(), the source code is as follows:
As can be seen from the source code, the core steps of request processing are as follows:
- Determines the handler for the current request
- Gets the processing adapter for the current request
- Interceptor preprocessing
- Real call handler
- Interceptor postprocessing
Let’s analyze it step by step.
Determines the handler for the current request
GetHandler (HttpServletRequest Request)
Can be seen from the source, traverse handlerMappings directly, the first match of the HandlerMapping object, then get the processor from HandlerMapping object. “HandlerMappings” = “handlerMappings”; What do they do when they instantiate.
As we know, there are nine components that are initialized during initialization, and one of them is HandlerMapping. It is at this point that handlerMappings are ready. HandlerMapping is an interface, it has a number of implementation, but in practice, we are more common RequestMappingHandlerMapping. Let’s take RequestMappingHandlerMapping were analyzed, and the class diagram is as follows:
A Bean instance RequestMappingHandlerMapping itself is the Spring container. Can be seen in the class diagram, the abstract parent class implements the AbstractHandlerMethodMapping implements InitializingBean interface, so you can be sure in its afterPropertiesSet () to do some initialization process.
In the detectHandlerMethods(beanName) method, the method logic to detect and register the processor method is implemented. Here, the URL is mapped to the handler and method.
Go back to HandlerMapping and call your getHandler() method to match HandlerMapping: AbstractHandlerMapping = getHandler();
As you can see, there are two key steps:
- The first step is based on
request
Find out the specificHandler
(This is actually a method processor), and it makes sense. In simple terms, the url is used to find the previous registrationMethod
andHandler
(Here is theController
Instance), and wrapped intoHandlerMethod
To return. - The second step is based on
request
Find the corresponding interceptor instance and wrap the processor and interceptor into a processor execution chainHandlerExecutionChain
To return.
At this point, you have identified the handler for the current request, which contains the method handler and the interceptor chain.
Gets the processing adapter for the current request
Gets the handler adapter that processes the request. For this example is to obtain RequestMappingHandlerAdapter, don’t do too much here.
Interceptor preprocessing
The preprocessing method preHandle() executes the HandlerInterceptor.
Real call handler
Execution call adapter AbstractHandlerMethodAdapter handle () method, the real call logic processing, this method is called directly the subclass handleInternal () method.
As you can see, the most important thing is that the invokeHandlerMethod() method is invoked.
In invocableMethod. InvokeAndHandle () in complete parameters and methods on the data of the Request of binding. Spring MVC provides two ways to bind Request parameters to method parameters: by annotating @requestparam and by parameter name.
To bind with annotations, we simply declare before the method parameters@RequestParam("name")
, you canrequest
In the parametername
The value of is bound to that parameter of the method.To bind with parameter names, you must get the names of the parameters in the method. Spring implements the implementation of obtaining parameters at runtime based on ASM technology.
At this point, the list of parameter values for the method is obtained, the method reflection call is finished, and the return value of the method is obtained. Behind the need to deal with return results, continue to follow up handleReturnValue () method, for common return json response body, the realization of the actual call RequestResponseBodyMethodProcessor method:
Here, according to the actual message converter, the method return value is written to response, and the result is output directly after the subsequent operation is completed.
Interceptor postprocessing
Executes the postprocessing method postHandle() of the HandlerInterceptor.
conclusion
In general, what Spring MVC does is relatively clear. In the initialization phase, the main thing is to map the request URL to the execution instance and the execution method. In the call phase, according to the previous mapping relationship, find the corresponding method to execute, and write the returned result in response. Of course, in the process, in order to solve the various complexity of the problem, the introduction of extraordinary design to deal with. But as long as we understand the core implementation, we can leave the details alone.
It is not easy to be original. If you think you have written a good article, click 👍 to encourage you
Welcome to my open source project: a lightweight HTTP invocation framework for SpringBoot