SpringMVC source code parsing
Spring Web MVC is an original Web framework built on the Servlet API and was included in the Spring framework from the beginning. Its official name “Spring Web MVC” comes from the name of its source module (Spring-Web MVC), but it is more commonly known as “Spring MVC”. This section introduces Spring Web MVC.
(1) New servlet3.0 features
As you can see from the figure above, servlet3.0 provides us with a very nice specification, and as long as we follow this specification, we can remove web.xml when tomcat starts, and also initialize the spring environment.
- Defines a new specification, namely in the resource file meta-inf/services under the folder, there is a to javax.mail. Servlet. ServletContainerInitializer named file, which defines a your own the full class name of the class. At the same time the class implementation javax.mail. Servlet. ServletContainerInitializer interface, and rewrite the onStartup method
- Under the above specification, all servlet servers that follow the specification, such as Tomcat, will reflect the onStartup() method of this class when the service is started
- With this new specification, we do not need to initialize the configuration and environment like Spring in the web.xml file as the traditional method, so we can achieve zero configuration, which is the idea of SpringBoot.
(2) Simulate SpringBoot zero configuration, embedded Tomcat
In the figure above, you can see the simulation of SpringBoot with zero configuration and embedded Tomcat. The main points to note are:
- The difference between tomcat.addContext and Tomcat.addWebApp:
(1) addWebapp indicates that the project is a Web project. When Tomcat starts, it will load the JSP view parser by default, and then it will report an error without adding a dependency on the JSP view parser. (2) addContext simply adds a context to the WebApps directory of Tomcat. Tomcat will not load the JSP view parser, so it will not report that the JSP view parser dependency cannot be found. Springboot is basically no longer using JSP technology by default, such as thymeleaf, freemarker… And so on, so the bottom layer of SpringBoot will definitely not use the addWebapp method.
- Here does not use the above mentioned servlet3.0 new feature specification, main implementation WebApplicationInitializer this interface, tomcat startup to perform to the code for this class, must under the condition of the web project, will perform. So in the case of addContext, it’s not going to be executed on our class. The only way to do this is to initialize spring IOC, Spring MVC, and Tomcat in a single soft method.
Before starting spring MVC source code parsing, we need to have this concept:
- A request made through the browser can only request a method of a servlet, but it can’t directly request a method of a Java class, the controller class we often use.
- So the Spring MVC framework can have a request that goes to a method of the corresponding Controller class, which must be a request that goes to a servlet, which then calls a method of our Controller class.
request —-> ! Request —-> Servlet —-> Controller (only method call, otherwise it can’t be implemented) (method call, the bottom must be reflection technology: indexController:index()))
- DispatcherServlet is a servlet that is the core class of Spring MVC.
(3) Spring MVC source code parsing
- Above :SpringMVC core flow chart
Summary: (1) First, the DispatcherServlet is requested to enter the DispatcherServlet and the DispatcherServlet extracts the corresponding Handler from the HandlerMappings. (2) At this point, we only get the corresponding Handle, and then we have to find the corresponding adapter, namely: HandlerAdapter. (3) When we get the corresponding HandlerAdapter, we start to call the corresponding Handler to handle the business logic. When this is done, we return a ModeAndView (4), which is then handed over to our ViewResolver to find the corresponding view by name and return. (5) The final render view returns to the rendered view –> Respond to the request.
3.1Spring MVC initialization phase
Where do we start? From the above analysis, we know that the core Class of Spring MVC is DispatcherServlet, which is a servlet class, so if you look at this class, start with the init method of this class.
- Structure diagram of DispatcherServlet class
- Look at the DispatcherServlet class, there is no init method, so you can only look for the superclass of this class.
# 1.Execute the init() method of the superclass HttpServletBean// When Tomcat starts, this method is executed to initialize the DispatcherServlet
@Override
public final void init(a) throws ServletException {
// Set bean properties from init parameters.
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if(! pvs.isEmpty()) {try {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
throwex; }}// Initialize the Web environment (important)
initServletBean();
}
# 2.Execute the initServletBean() method of the FrameworkServlet subclass@Override
protected final void initServletBean(a) throws ServletException {
getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
long startTime = System.currentTimeMillis();
try {
// Initialize the Web environment
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}
catch (ServletException | RuntimeException ex) {
throwex; }} #3.Perform initWebApplicationContext FrameworkServlet class () methodprotected WebApplicationContext initWebApplicationContext(a) {
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext ! =null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if(! cwac.isActive()) {if (cwac.getParent() == null) {
cwac.setParent(rootContext);
}
// Configure and refresh the Spring container (important)
// This is nothing more than initializing the Spring IOC environment, creating beans, instantiating beans, etc
// This method also ends up calling the refresh() method, which is resolved in Spring source parsingconfigureAndRefreshWebApplicationContext(cwac); }}}if (wac == null) {
wac = findWebApplicationContext();
}
if (wac == null) {
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// Initialize the configuration of DispatcherServlet initStrategies() (emphasis)
onRefresh(wac);
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
# 4.Execute the onRefresh() method of the DispatcherServlet class to initialize the configuration of SpringMVCprotected void onRefresh(ApplicationContext context) {
// Initialize the springMVC configuration
initStrategies(context);
}
Copy the code
(1) The init method of DispatcherServlet executes the init method of the parent HttpServletBean, and then calls the initServletBean() method of the FrameworkServlet. HttpServletBean# init () – > FrameworkServlet# initServletBean () and (2) perform initWebApplicationContext () method, which is the initialization of the spring ioc environment. This leads to an interview question: What’s the difference between the Spring container and the Spring MVC container? Spring and Spring MVC are called by the same refresh() method, so there is no difference between the Spring container and spring MVC container, both refer to the same container. (3) Execute the onRefresh() method to initialize the DispatcherServlet, which is to initialize spring MVC.
# 1.Execute the initStrategies() method of the DispatcherServlet classprotected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);// Upload the file
initLocaleResolver(context);/ / the internationalization
initThemeResolver(context);// The theme style of the preceding paragraph
initHandlerMappings(context);// Initializes the HandlerMappings (request mapper) key
initHandlerAdapters(context);// Initialize HandlerAdapters
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);// View converter
initFlashMapManager(context);// Redirect the data manager
}
# 2.Execute the initHandlerMappings() methodprivate 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());
AnnotationAwareOrderComparator.sort(this.handlerMappings); }}else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm); }}// GetHandlerMappings (); // GetHandlerMappings (); // GetHandlerMappings ()
if (this.handlerMappings == null) {
// Use defaultStrategies to get the data
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); }} #3.Execute the getDefaultStrategies() methodprotected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
String key = strategyInterface.getName();
/ / defaultStrategies DispatcherServlet. Properties configuration files, static block in the static initialization
String value = defaultStrategies.getProperty(key);
if(value ! =null) {
String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
List<T> strategies = new ArrayList<>(classNames.length);
for (String className : classNames) {
try {
// Get the class bytecode fileClass<? > clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());// Create the object by calling Spring's getBean.
// The request map is assembled in this methodObject strategy = createDefaultStrategy(context, clazz); strategies.add((T) strategy); }}return strategies;
}
else {
return newLinkedList<>(); }}Copy the code
(1) The initHandlerMappings method is to initialize our handlerMapping. (2) The main function of handlerMapping is to find the controller method corresponding to the request path.
Such as: Then the handlerMapping, when initialized, has stored all controller’s request path maps in a set of maps. When the request comes in, it uses “/index” as a key. Find the corresponding controller index method from the map set.
(3) The default handlerMappings are handlerMappings, which are obtained directly from the defaultStrategies configuration file. (4) When is the value of defaultStrategies initialized?
Looking at the source code, the value of defaultStrategies is initialized by a static block of the DispatcherServlet class. As we all know, when a class is initialized, it executes the static block of that class.
# 1.The DispatcherServlet classstaticStatic code blockstatic {
try {
/** * load the default policy implementation from the DEFAULT_STRATEGIES_PATH file * you can count 8 in total: DispatcherServlet.properties == DEFAULT_STRATEGIES_PATH */
ClassPathResource resource = newClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class); defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); }} #2.DispatcherServlet. The properties file// The HandlerMapping and HandlerAdapter classes are displayed
org.springframework.web.servlet.HandlerMapping=
org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
org.springframework.web.servlet.HandlerAdapter=
org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
Copy the code
Can be seen from the DispatcherServlet. Properties configuration files, handlerMapping default is to have two: 1. BeanNameUrlHandlerMapping (main processing object) 2. The RequestMappingHandlerMapping (main processing method)
3.2Spring MVC request phase analysis
A request from the user is received by the servlet and then invoked step by step to the doService method of the DispatcherServlet.
# 1.DoService () method of the DispatcherServlet classprotected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
// Core method (emphasis)
doDispatch(request, response);
}
finally {
if(! WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {// Restore the original attribute snapshot, in case of an include.
if(attributesSnapshot ! =null) { restoreAttributesAfterInclude(request, attributesSnapshot); }}}} #2.Call the doDispatch() method of the DispatcherServlet classprotected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
// Asynchronous programming
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
// Define variables
ModelAndView mv = null;
Exception dispatchException = null;
try {
// Check whether the request has a file upload operationprocessedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest ! = request);// Determine the handler of the current request (emphasis), infer the type of controller and handler,
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Infer that adapters, different controller types, are assigned to different adapters
// If it is a bean, mappedHandler.gethandler () returns an object
// If it is a method, mappedhandler.gethandler () returns a method
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// At this point, Spring decides how do I reflect the call
// Pre-interceptor processing
if(! mappedHandler.applyPreHandle(processedRequest, response)) {return;
}
// Through the adapter, process the request (which can be understood as reflecting the call method) (emphasis)
// Actually invoke the handler.mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); applyDefaultViewName(processedRequest, mv); mappedHandler.applyPostHandle(processedRequest, response, mv); } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); }}Copy the code
Through the analysis of DispatcherServlet, the core processing method of the request is doDispatch(), mainly divided into several steps: (1) Check whether there is a file upload operation in the request (2) determine the handler for the current request (key) (3) infer the adapter, different controller type, (5) Through the found HandlerAdapter, the reflection executes the related business code controller method. (6) Return the result.
# 1.The getHandler() method of the DispatcherServlet classprotected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings ! =null) {
// Loop through all HandlerMappings
//this. HandlerMappings = "//"; //this. HandlerMappings = "//"; (key)
// When handlerMappings are initialized
for (HandlerMapping hm : this.handlerMappings) {
// Pass the request to see if we can get a handler
// Note: How do we get the handler to be related to the logic implemented by the handlerMapping itself
HandlerExecutionChain handler = hm.getHandler(request);
if(handler ! =null) {
returnhandler; }}}return null;
}
# 2.Execute the getHandler() method to AbstractHandlerMappingpublic final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// Get handler (important)
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request); CorsConfiguration handlerConfig = getCorsConfiguration(handler, request); CorsConfiguration config = (globalConfig ! =null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
Copy the code
(1) The getHandler() method, which mainly traverses the handlerMappings that were initialized during DispatcherServlet initialization, specifies the mappings that the DispatcherServlet initiates. (2) The main idea of this method is to match the corresponding controller for processing through the path of the request. (3) SpringMVC comes with two HandlerMapping for us to choose from. Why two HandlerMapping?
- There are two ways to register a Controller:
- (1) As a Bean: implement the Controller interface, override the handleRequest method, and request path to “/test”
@Component("/test")
public class TesrController implements org.springframework.web.servlet.mvc.Controller{
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("1");
return null; }}Copy the code
- (2) In Annotation form:
@Controller
public class AnnotationController {
@RequestMapping("/test2")
public Object test(a){
System.out.println("test");
return null; }}Copy the code
(1) through the test, can be in Bean controller, is through BeanNameUrlHandlerMapping to match (2) with annotation method of the controller, through the RequestMappingHandlerMapping to match
- BeanNameUrlHandlerMapping treatment bean source code analysis:
# 1.Perform to AbstractUrlHandlerMapping getHandlerInternal () methodprotected Object getHandlerInternal(HttpServletRequest request) throws Exception {
// Get the requested path
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
// Find the corresponding handler (important)
Object handler = lookupHandler(lookupPath, request);
if (handler == null) {
Object rawHandler = null;
if ("/".equals(lookupPath)) {
rawHandler = getRootHandler();
}
if (rawHandler == null) {
rawHandler = getDefaultHandler();
}
if(rawHandler ! =null) {
// Bean name or resolved handler?
if (rawHandler instanceof String) {
String handlerName = (String) rawHandler;
rawHandler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(rawHandler, request);
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null); }}return handler;
}
# 2.Perform to AbstractUrlHandlerMapping lookupHandler () methodprotected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
// Match the requested path in the handlerMap.
// When will the handlerMap value be filled? When init is initialized, it is already stored in the handlerMap class
Object handler = this.handlerMap.get(urlPath);
if(handler ! =null) {
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
return buildPathExposingHandler(handler, urlPath, urlPath, null); }... Ignore... }Copy the code
(1) The bean-style controller matches the path of the request through a handlerMap, which is relatively simple. (2) The question here is, when was the value of handlerMap put in?
BeanNameUrlHandlerMapping is realized through the analysis of source ApplicationContextAware interface. If you’re familiar with Spring source code, you know that Spring instance beans call back to the setApplicationContext() method of those classes.
# 1.Executes the setApplicationContext() method of the parent class's ApplicationObjectSupportpublic final void setApplicationContext(@Nullable ApplicationContext context) throws BeansException {
if (context == null && !isContextRequired()) {
// Reset internal context state.
this.applicationContext = null;
this.messageSourceAccessor = null;
}
else if (this.applicationContext == null) {
this.applicationContext = context;
this.messageSourceAccessor = new MessageSourceAccessor(context);
// Initialize the ApplicationContext, which will execute the methods in the subclass (emphasis)initApplicationContext(context); }} #2.Perform to AbstractDetectingUrlHandlerMapping class initApplicationContext () method@Override
public void initApplicationContext(a) throws ApplicationContextException {
super.initApplicationContext();
// The handler is detected
detectHandlers();
}
# 3.Perform to AbstractDetectingUrlHandlerMapping class detectHandlers () methodprotected void detectHandlers(a) throws BeansException {
ApplicationContext applicationContext = obtainApplicationContext();
// Get all the spring ioc beanName, then judge the beanName, those that start with "/"
String[] beanNames = (this.detectHandlersInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
applicationContext.getBeanNamesForType(Object.class));
for (String beanName : beanNames) {
// Then judge the beanName, which starts with "/"
String[] urls = determineUrlsForHandler(beanName);
if(! ObjectUtils.isEmpty(urls)) {// Register handler (emphasis)registerHandler(urls, beanName); #}}}4.Perform to AbstractUrlHandlerMapping registerHandler () methodprotected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException {
Assert.notNull(urlPaths, "URL path array must not be null");
for(String urlPath : urlPaths) { registerHandler(urlPath, beanName); }} #5.AbstractUrlHandlerMapping registerHandler () methodprotected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
Object resolvedHandler = handler;
// Finally put into the map set (omit other irrelevant code)
this.handlerMap.put(urlPath, resolvedHandler);
}
Copy the code
BeanNameUrlHandlerMapping bean processing ways of source code analysis, in fact, it is very simple: (1) when the class is initialized, have all realized the Controller interface Controller class, get their @ Componet (‘/test ‘) (2) then ‘/ test’ as the key, the Controller class as the value, Put into a set of maps. (3) When a request comes in, get the URI of the request, look in the map, and match it
- RequestMappingHandlerMapping processing source analysis of annotation:
# 1.AbstractHandlerMethodMapping#getHandlerInternal
/ / for RequestMappingHandlerMapping indexController. The index (), the method of mapping request path
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// Obtain the request path
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
this.mappingRegistry.acquireReadLock();
try {
// Obtain the handler from the request path
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return(handlerMethod ! =null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock(); }} #2.AbstractHandlerMethodMapping#lookupHandlerMethod
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
// Match the request path from the urlLookup of mappingRegistry
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if(directPathMatches ! =null) {
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// No choice but to go through all mappings...
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
if(! matches.isEmpty()) { Comparator<Match> comparator =new MatchComparator(getMappingComparator(request));
matches.sort(comparator);
if (logger.isTraceEnabled()) {
logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "]." + matches);
}
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
request.getRequestURL() + "' : {" + m1 + "," + m2 + "}");
}
}
handleMatch(bestMatch.mapping, lookupPath, request);
/ / returns the handler
return bestMatch.handlerMethod;
}
else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request); }} #3.AbstractHandlerMethodMapping.MappingRegistry#getMappingsByUrl
public List<T> getMappingsByUrl(String urlPath) {
return this.urlLookup.get(urlPath);
}
Copy the code
RequestMappingHandlerMapping annotation processing ways of source code analysis, is more complex, with a MappingRegistry maintain all request path map. The initialization of MappingRegistry is done when the bean is instantiated. The principle is similar to the previous one, matching from a set of maps. So I’m not going to do any parsing here.
Conclusion: getHandler ()
- The next step is to find the Apapter adapter
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters ! =null) {
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
if (ha.supports(handler)) {
returnha; }}}}Copy the code
In fact, you can see that he’s going through our adapter from a property in the handlerAdapters property where do we get these handlerAdapters? = “HandlerMappings”; “mappings” = “HandlerMappings”; “mappings” = “HandlerMappings”;
As for what is an adapter, we will combine the Handler, as we said in the beginning of the summary, at first we only found the Handler, and now we want to execute, but there is a problem, more than one Handler, the natural corresponding execution method is different, this is the concept of the adapter: Corresponding to different Handler execution schemes. When you find the right adapter, you’re almost done, because after you’ve made some decisions (such as determining the type of request), you can start executing your Handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
Copy the code
So this mv is our ModlAndView and actually after executing this line our Controller logic has been executed, all that’s left is to find the view rendering.
Conclusion: The key concept of SpringMVC is that handlers and adapters find the appropriate Handler to handle your Controller with a key HandlerMappings Then use HandlerAdapters to find a suitable HandlerAdapter to execute the logic in the Handler. Finally, go back to ModlAndView…
Overall, the source code of SpringMVC is still very complex, and this blog is only a rough description of the main execution process. Source code comment download address: github.com/llsydn/spri…