About the core process of the entire SpringMVC framework + core source code analysis

SpringMVC is widely used in enterprise-level web application development. Let's take a look at its charm.

Whether it is structs or springmvc, first of all, from the core of web development, it has done a lot of packaging and added a lot of convenient development functions in the original servlet processing method. One of the most well-known and important points of SpringMVC is that it has a central controller. When it comes to this, we have to mention design patterns. In fact, many design patterns can be regarded as third-party operation modes. The so-called third-party is in the original function. A console is added to the module, such as the factory pattern, to achieve loose coupling in this way. Here is a sketch I drew myself:


All operation processes are directly related to DispatcherServlet in the SpringMVC framework.

After receiving the client's resquest request, the first step is to get the corresponding Handler (that is, the usual Controller) from the HandlerMapping object. When this HandlerMapping is not supported by annotations, the most common way is to configure it in the xml file. After understanding the annotations, RequestMappingHandlerMapping is basically used. Its function can be purely understood as mapping the url from the resquest to the corresponding handler written by ourselves and then performing the corresponding operation. What is returned in the DispatcherServlet source code is not a pure handler:

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception

In the returned instance, a lot of interceptors are actually added. After getting the handler instance, hand it over to the handler adapter, return a modelAndView, and finally hand it over to the View's view parser for rendering, and finally return it to the console and submit it to the user . Each class simply does the same thing, greatly reducing coupling and improving code readability and scalability.

The more important methods in the DispatcherServlet class are doService and doDispatch :

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        if (this.logger.isDebugEnabled()) {
            String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";//Asynchronous communication is cached
            this.logger.debug("DispatcherServlet with name '" + this.getServletName() + "'" + resumed + " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
        }

        Map<String, Object> attributesSnapshot = null; //Snapshot---cache
        if (WebUtils.isIncludeRequest(request)) {
            attributesSnapshot = new HashMap();
            Enumeration attrNames = request.getAttributeNames();

            label108: //Advanced usage of break, often used to jump out of multi-layer loops
            while(true) {
                String attrName;
                do {
                    if (!attrNames.hasMoreElements()) {
                        break label108;
                    }

                    attrName = (String)attrNames.nextElement();
                } while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));

                attributesSnapshot.put(attrName, request.getAttribute(attrName));
            }
        }

        request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());
        request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
        request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
        request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource());  //属性注入
        FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);//Get the possible cache status
        if (inputFlashMap != null) {
            request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
        }

        request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
        request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);

        try {
            this.doDispatch(request, response);
        } finally {
            if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {
                this.restoreAttributesAfterInclude(request, attributesSnapshot); //Save again
            }

        }

    }

You can see that the doDispatch method is called directly in the doService method:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try { //processing core
            try {
                ModelAndView mv = null;
                Exception dispatchException = null;

                try {
                    processedRequest = this.checkMultipart(request); //try to split the request
                    multipartRequestParsed = processedRequest != request; //It does contain multiple requests
                    mappedHandler = this.getHandler(processedRequest);
                    if (mappedHandler == null || mappedHandler.getHandler() == null) {
                        this.noHandlerFound(processedRequest, response);
                        return;
                    }

                    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());//The corresponding handler has been obtained at this time
                    String method = request.getMethod();
                    boolean isGet = "GET".equals(method);
                    if (isGet || "HEAD".equals(method)) { //If it is a get request
                        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                        if (this.logger.isDebugEnabled()) {
                            this.logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                        }

                        if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                            return;
                        }
                    }

                    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
                    }

                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); //return a modelAndView through the adapter
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }

                    this.applyDefaultViewName(processedRequest, mv);
                    mappedHandler.applyPostHandle(processedRequest, response, mv);//Execute handler (which contains the interceptor's postHandler operation)
                } catch (Exception var19) {
                    dispatchException = var19;
                }

                this.processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);//Execute render after ensuring that mv is not an empty view
            } catch (Exception var20) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, var20);
            } catch (Error var21) {
                this.triggerAfterCompletionWithError(processedRequest, response, mappedHandler, var21);
            }

        } finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            } else if (multipartRequestParsed) {
                this.cleanupMultipart(processedRequest);
            }

        }
    }

The postHandler in the interceptor will be called before the DispatcherServlet returns and renders the view, so we can operate on the ModelAndView object processed by the Controller in this method. The rendering operation is performed through the View object (the view used here is the InternalResourceResolved specified in the configuration file), mainly through view.render(mv.getModelInternal(), request, response); operation object.


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325764323&siteId=291194637