zoukankan      html  css  js  c++  java
  • SpringMVC 执行过程分析

    1. SpringMVC 大致过程测试

    1. 新建filter

    package cn.xm.filter;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import java.io.IOException;
    
    @WebFilter(filterName = "testFilter", urlPatterns = "/*")
    public class TestFilter implements Filter {
        public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
                throws IOException, ServletException {
    
            System.out.println("cn.xm.filter.TestFilter start");
    
            chain.doFilter(req, res);
    
            System.out.println("cn.xm.filter.TestFilter end");
        }
    
        @Override
        public void destroy() {
    
        }
    
        @Override
        public void init(FilterConfig arg0) throws ServletException {
    
        }
    
    }

    2. 新建一个SpringMVC拦截器

    package cn.xm.inteceptor;
    
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public class MyInteceptor implements HandlerInterceptor {
    
        /**
         * 在请求处理之前进行调用(Controller方法调用之前)
         */
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("被 MyInterceptor1  preHandle拦截,放行...");
            return true;
        }
    
        /**
         * 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
         */
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("被 MyInterceptor1  postHandle,放行...");
        }
    
        /**
         * 在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行 (主要是用于进行资源清理工作)
         */
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("被 MyInterceptor1 afterCompletion 拦截,放行...");
        }
    }

    3. 配置类,加入到Spring的拦截器链中

    package cn.xm.config;
    
    
    import cn.xm.inteceptor.MyInteceptor;
    import cn.xm.inteceptor.MyInteceptor2;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.web.servlet.ServletRegistrationBean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.Ordered;
    import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
    
    import javax.annotation.PostConstruct;
    
    /**
     * 1.JSON返回实体类报错2.设置页面的默认页面
     *
     * @author Administrator
     */
    @Configuration
    public class MVCConfig extends WebMvcConfigurerAdapter {
    
        @Autowired
        private ServletRegistrationBean servletRegistrationBean;
    
        @PostConstruct
        public void postConstruct() {
            servletRegistrationBean.getUrlMappings().clear();
            servletRegistrationBean.getUrlMappings().add("*.do");
            System.out.println("cn.xm.config.MVCConfig.postConstruct======");
        }
    
        /**
         * 解决JSON返回实体类报错
         */
        @Override
        public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
            configurer.favorPathExtension(false);
        }
    
        /**
         * 设置页面的默认页面
         */
        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
            registry.addViewController("/").setViewName("forward:/index.html");
            registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
            super.addViewControllers(registry);
        }
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            /**
             * 拦截器按照顺序执行, 不写addPathPatterns 默认匹配所有
             */
            registry.addInterceptor(new MyInteceptor()).addPathPatterns("/test2/**");
    
            super.addInterceptors(registry);
        }
    }

    4. 新建一个Controller

    package cn.xm.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    @RequestMapping("test2")
    @Controller("testController2")
    public class TestController {
    
        @GetMapping("test1")
        public String test1() {
            System.out.println("cn.xm.controller.TestController.test1 方法调用");
            return "test1";
        }
    
        @GetMapping("test2")
        @ResponseBody
        public String test2() {
            System.out.println("cn.xm.controller.TestController.test2 方法调用");
            return "test2";
        }
    }

    5. curl test2 方法进行测试

    $ curl http://localhost:8088/test2/test2
      % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                     Dload  Upload   Total   Spent    Left  Speed
    100     5  100     5    0     0     36      0 --:--:-- --:--:-- --:--:--    37test2

    5. 查看控制台日志如下:

    cn.xm.filter.TestFilter start
    被 MyInterceptor1  preHandle拦截,放行...
    cn.xm.controller.TestController.test2 方法调用
    被 MyInterceptor1  postHandle,放行...
    被 MyInterceptor1 afterCompletion 拦截,放行...
    cn.xm.filter.TestFilter end

    6. 过程分析: 

      其执行过程大致可以分为下面过程:

    -filter链chain.doFilter(req, res); 之前

    -inteceptor preHandle

    -controller 

    -inteceptor postHandle

    -inteceptor afterCompletion 

    -filter链chain.doFilter(req, res); 之后

    2. Spring MVC DispatcherServlet配置分析

     1. 创建DispatcherServlet  (DispatcherServletConfiguration 配置类是创建Servlet)

     org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration.DispatcherServletConfiguration#dispatcherServlet

            @Bean(
                name = {"dispatcherServlet"}
            )
            public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
                DispatcherServlet dispatcherServlet = new DispatcherServlet();
                dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
                dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
                dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
                dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
                dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
                return dispatcherServlet;
            }

    2. DispatcherServletRegistrationConfiguration  配置DispatcherServlet的注册类-Servlet实例是要被添加(注册)到如tomcat这样的ServletContext里的,这样才能够提供请求服务。所以,DispatcherServletRegistrationConfiguration将生成一个Bean,负责将DispatcherServlet给注册到ServletContext中。

    org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration.DispatcherServletRegistrationConfiguration#dispatcherServletRegistration

            @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
            @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
            public DispatcherServletRegistrationBean dispatcherServletRegistration(
                    DispatcherServlet dispatcherServlet) {
                DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(
                        dispatcherServlet, this.serverProperties.getServlet().getPath());
                registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
                registration.setLoadOnStartup(
                        this.webMvcProperties.getServlet().getLoadOnStartup());
                if (this.multipartConfig != null) {
                    registration.setMultipartConfig(this.multipartConfig);
                }
                return registration;
            }

    默认的path是/ , 会匹配所有请求

            /**
             * Path of the main dispatcher servlet.
             */
            private String path = "/";

    3. 修改请求路径:

    (1) 修改允许根据后缀请求

    @Configuration
    public class UrlMatchConfig extends WebMvcConfigurationSupport {
    
        @Override
        public void configurePathMatch(PathMatchConfigurer configurer) {
            // setUseSuffixPatternMatch 后缀模式匹配
            configurer.setUseSuffixPatternMatch(true);
            // setUseTrailingSlashMatch 自动后缀路径模式匹配
            configurer.setUseTrailingSlashMatch(true);
        }
    }

    (2) 修改允许的请求路径

        /**
         * 设置匹配*.action后缀请求
         * 
         * @param dispatcherServlet
         * @return
         */
        @Bean
        public ServletRegistrationBean servletRegistrationBean(DispatcherServlet dispatcherServlet) {
            ServletRegistrationBean servletServletRegistrationBean = new ServletRegistrationBean(dispatcherServlet);
            // 参数接受可变类型的多个参数支持多种后缀的匹配
            servletServletRegistrationBean.addUrlMappings("*.action", "*.do");
            return servletServletRegistrationBean;
        }

    3. SpringMVC DispatcherServlet 执行过程分析

      DispatcherServlet 也叫中央处理器,是处理请求中比较核心的一个类。

     1. 其doService方法如下:

        protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
            if (logger.isDebugEnabled()) {
                String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
                logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
                        " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
            }
    
            // Keep a snapshot of the request attributes in case of an include,
            // to be able to restore the original attributes after the include.
            Map<String, Object> attributesSnapshot = null;
            if (WebUtils.isIncludeRequest(request)) {
                attributesSnapshot = new HashMap<String, Object>();
                Enumeration<?> attrNames = request.getAttributeNames();
                while (attrNames.hasMoreElements()) {
                    String attrName = (String) attrNames.nextElement();
                    if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
                        attributesSnapshot.put(attrName, request.getAttribute(attrName));
                    }
                }
            }
    
            // Make framework objects available to handlers and view objects.
            request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
            request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
            request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
            request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
    
            FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
            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 {
                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. 前面做了一堆处理之后将处理转交给doDispatch方法

        protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
            HttpServletRequest processedRequest = request;
            HandlerExecutionChain mappedHandler = null;
            boolean multipartRequestParsed = false;
    
            WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    
            try {
                ModelAndView mv = null;
                Exception dispatchException = null;
    
                try {
                    processedRequest = checkMultipart(request);
                    multipartRequestParsed = (processedRequest != request);
    
                    // Determine handler for the current request.
                    mappedHandler = getHandler(processedRequest);
                    if (mappedHandler == null || mappedHandler.getHandler() == null) {
                        noHandlerFound(processedRequest, response);
                        return;
                    }
    
                    // Determine handler adapter for the current request.
                    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    
                    // Process last-modified header, if supported by the handler.
                    String method = request.getMethod();
                    boolean isGet = "GET".equals(method);
                    if (isGet || "HEAD".equals(method)) {
                        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                        if (logger.isDebugEnabled()) {
                            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;
                    }
    
                    // Actually invoke the handler.
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }
    
                    applyDefaultViewName(processedRequest, mv);
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                }
                catch (Exception ex) {
                    dispatchException = ex;
                }
                catch (Throwable err) {
                    // As of 4.3, we're processing Errors thrown from handler methods as well,
                    // making them available for @ExceptionHandler methods and other scenarios.
                    dispatchException = new NestedServletException("Handler dispatch failed", err);
                }
                processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
            }
            catch (Exception ex) {
                triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
            }
            catch (Throwable err) {
                triggerAfterCompletion(processedRequest, response, mappedHandler,
                        new NestedServletException("Handler processing failed", err));
            }
            finally {
                if (asyncManager.isConcurrentHandlingStarted()) {
                    // Instead of postHandle and afterCompletion
                    if (mappedHandler != null) {
                        mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                    }
                }
                else {
                    // Clean up any resources used by a multipart request.
                    if (multipartRequestParsed) {
                        cleanupMultipart(processedRequest);
                    }
                }
            }
        }

    接下来以这个方法为入口进行研究

    1》  请求经过一系列filter验证之后到达DispatcherServlet

    2》 先调用org.springframework.web.servlet.DispatcherServlet#getHandler 获取到一个HandlerExecutionChain, 方法如下:

        protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
            for (HandlerMapping hm : this.handlerMappings) {
                if (logger.isTraceEnabled()) {
                    logger.trace(
                            "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
                }
                HandlerExecutionChain handler = hm.getHandler(request);
                if (handler != null) {
                    return handler;
                }
            }
            return null;
        }

    org.springframework.web.servlet.HandlerExecutionChain 核心属性如下:

        private final Object handler;
    
        private HandlerInterceptor[] interceptors;
    
        private List<HandlerInterceptor> interceptorList;
    
        private int interceptorIndex = -1;

    this.handlerMappings 包含如下对象:

     2.1》到index为2 的org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping 获取到满足条件的HandlerExecutionChain ,其方法继承自父类org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler:

        public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
            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 = getApplicationContext().getBean(handlerName);
            }
    
            HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
            if (CorsUtils.isCorsRequest(request)) {
                CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
                CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
                CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
                executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
            }
            return executionChain;
        }

    (1)getHandlerInternal(request); 获取 handler ;也就是是根据请求的URI获取到的Controller的信息,如下:

    (2) 然后根据获取到的handler去获取对应的inteceptor信息和request 去获取inteceptors 拦截器,org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandlerExecutionChain

        protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
            HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
                    (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
    
            String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
            for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
                if (interceptor instanceof MappedInterceptor) {
                    MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
                    if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
                        chain.addInterceptor(mappedInterceptor.getInterceptor());
                    }
                }
                else {
                    chain.addInterceptor(interceptor);
                }
            }
            return chain;
        }

    this.adaptedInterceptors 如下: 也就是我们自己的拦截器和Spring默认提供的几个拦截器。 获取到之后会调用下面的mappedInterceptor.matches(lookupPath, this.pathMatcher) 方法来验证是否该请求是否满足拦截器拦截的路径

    注意,这里看到我们自己的MyInteceptor变为了MappedInteceptor,其改变是在:org.springframework.web.servlet.config.annotation.InterceptorRegistration#getInterceptor

        protected Object getInterceptor() {
            if (this.includePatterns.isEmpty() && this.excludePatterns.isEmpty()) {
                return this.interceptor;
            }
    
            String[] include = toArray(this.includePatterns);
            String[] exclude = toArray(this.excludePatterns);
            MappedInterceptor mappedInterceptor = new MappedInterceptor(include, exclude, this.interceptor);
    
            if (this.pathMatcher != null) {
                mappedInterceptor.setPathMatcher(this.pathMatcher);
            }
    
            return mappedInterceptor;
        }

    可以看到是根据是否有patterns决定是否变为MapperInteceptor ,是MappedInteceptor在后续需要根据Uri进行match,否则匹配所有的请求,所以在注入的时候会有两种情况,如下:

    @Configuration
    public class MVCConfig extends WebMvcConfigurerAdapter {
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            /**
             * 拦截器按照顺序执行, 不写addPathPatterns 默认匹配所有
             */
            // 如果有addPathPatternsexcludePathPatterns 会用MappedInteceptor进行包裹
            registry.addInterceptor(new MyInteceptor()).addPathPatterns("/test2/**").excludePathPatterns("");
            // 没有pattern,直接匹配所有的请求,会直接放到拦截器链chain中
            registry.addInterceptor(new MyInteceptor2());
    
            super.addInterceptors(registry);
        }
    }

    (3)返回去给DispatcherServlet的 mappedHandler 如下:

     3》 然后根据handler获取到对应的handlerAdapter对象

    org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter

        protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
            for (HandlerAdapter ha : this.handlerAdapters) {
                if (logger.isTraceEnabled()) {
                    logger.trace("Testing handler adapter [" + ha + "]");
                }
                if (ha.supports(handler)) {
                    return ha;
                }
            }
            throw new ServletException("No adapter for handler [" + handler +
                    "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
        }

     最后调supports方法(),如下:org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#supports

        public final boolean supports(Object handler) {
            return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
        }

    最后返回给Dispatcher的HandlerAdapter如下:

    4》 接下来调用org.springframework.web.servlet.HandlerExecutionChain#applyPreHandle 处理Inteceptor的前置请求(这里需要注意,如果前置请求有返回false则直接请求结束)

        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;
        }

    inteceprors 如下:

     然后正向遍历这个数组,依次调用其preHandle 方法,如果全部返回true则返回下一步;如果有某个返回false,则调用org.springframework.web.servlet.HandlerExecutionChain#triggerAfterCompletion 方法:(可以看到使用的下标是interceptorIndex,这个是在上面方法调用成功的次数。 也就是preHandle 返回true的Inteceptor,然后逆序执行其interceptorIndexafterCompletion方法)

        void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
                throws Exception {
    
            HandlerInterceptor[] interceptors = getInterceptors();
            if (!ObjectUtils.isEmpty(interceptors)) {
                for (int i = this.interceptorIndex; i >= 0; i--) {
                    HandlerInterceptor interceptor = interceptors[i];
                    try {
                        interceptor.afterCompletion(request, response, this.handler, ex);
                    }
                    catch (Throwable ex2) {
                        logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
                    }
                }
            }
        }

    5》 调用RequestMappingHandlerAdapter#handle 进行处理, 是继承自父类org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle:

        public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
                throws Exception {
    
            return handleInternal(request, response, (HandlerMethod) handler);
        }
    
        protected ModelAndView handleInternal(HttpServletRequest request,
                HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    
            ModelAndView mav;
            checkRequest(request);
    
            // Execute invokeHandlerMethod in synchronized block if required.
            if (this.synchronizeOnSession) {
                HttpSession session = request.getSession(false);
                if (session != null) {
                    Object mutex = WebUtils.getSessionMutex(session);
                    synchronized (mutex) {
                        mav = invokeHandlerMethod(request, response, handlerMethod);
                    }
                }
                else {
                    // No HttpSession available -> no mutex necessary
                    mav = invokeHandlerMethod(request, response, handlerMethod);
                }
            }
            else {
                // No synchronization on session demanded at all...
                mav = invokeHandlerMethod(request, response, handlerMethod);
            }
    
            if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
                if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
                    applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
                }
                else {
                    prepareResponse(response);
                }
            }
    
            return mav;
        }

    (1)核心在handleInternal 方法,方法内部先检查request 是否合法:

        protected final void checkRequest(HttpServletRequest request) throws ServletException {
            // Check whether we should support the request method.
            String method = request.getMethod();
            if (this.supportedMethods != null && !this.supportedMethods.contains(method)) {
                throw new HttpRequestMethodNotSupportedException(
                        method, StringUtils.toStringArray(this.supportedMethods));
            }
    
            // Check whether a session is required.
            if (this.requireSession && request.getSession(false) == null) {
                throw new HttpSessionRequiredException("Pre-existing session required but none found");
            }
        }

    (2) 然后调用org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod 反射调用我们Controller的方法:

        protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
                HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    
            ServletWebRequest webRequest = new ServletWebRequest(request, response);
            try {
                WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
                ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
    
                ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
                invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
                invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
                invocableMethod.setDataBinderFactory(binderFactory);
                invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
    
                ModelAndViewContainer mavContainer = new ModelAndViewContainer();
                mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
                modelFactory.initModel(webRequest, mavContainer, invocableMethod);
                mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
    
                AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
                asyncWebRequest.setTimeout(this.asyncRequestTimeout);
    
                WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
                asyncManager.setTaskExecutor(this.taskExecutor);
                asyncManager.setAsyncWebRequest(asyncWebRequest);
                asyncManager.registerCallableInterceptors(this.callableInterceptors);
                asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
    
                if (asyncManager.hasConcurrentResult()) {
                    Object result = asyncManager.getConcurrentResult();
                    mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
                    asyncManager.clearConcurrentResult();
                    if (logger.isDebugEnabled()) {
                        logger.debug("Found concurrent result value [" + result + "]");
                    }
                    invocableMethod = invocableMethod.wrapConcurrentResult(result);
                }
    
                invocableMethod.invokeAndHandle(webRequest, mavContainer);
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return null;
                }
    
                return getModelAndView(mavContainer, modelFactory, webRequest);
            }
            finally {
                webRequest.requestCompleted();
            }
        }

    (2.1) invocableMethod.invokeAndHandle(webRequest, mavContainer);  里面反射调用方法===这个方法反射调用方法,然后根据是否是ResponseBody进行处理,如果是ResponseBody会在这个处理直接将结果写到客户端

    org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle 如下:

        public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
                Object... providedArgs) throws Exception {
    
            Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
            setResponseStatus(webRequest);
    
            if (returnValue == null) {
                if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) {
                    mavContainer.setRequestHandled(true);
                    return;
                }
            }
            else if (StringUtils.hasText(this.responseReason)) {
                mavContainer.setRequestHandled(true);
                return;
            }
    
            mavContainer.setRequestHandled(false);
            try {
                this.returnValueHandlers.handleReturnValue(
                        returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
            }
            catch (Exception ex) {
                if (logger.isTraceEnabled()) {
                    logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
                }
                throw ex;
            }
        }

    org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest 方法解析到参数之后反射进行方法调用,解析参数会交给对应的参数解析器进行解析(如果是@RequestBody 会交给RequestResponseBodyMethodProcessor 处理)

        public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
                Object... providedArgs) throws Exception {
    
            Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
            if (logger.isTraceEnabled()) {
                logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
                        "' with arguments " + Arrays.toString(args));
            }
            Object returnValue = doInvoke(args);
            if (logger.isTraceEnabled()) {
                logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
                        "] returned [" + returnValue + "]");
            }
            return returnValue;
        }

    doInvoke 会反射调用方法,然后获取到方法的返回值。

    (2.2) 接下来处理回传结果,对应的处理代码:this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);

    org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite#handleReturnValue

        public void handleReturnValue(Object returnValue, MethodParameter returnType,
                ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
    
            HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
            if (handler == null) {
                throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
            }
            handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
        }

    获取到的handler如下:

     然后调用org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#handleReturnValue 方法

        public void handleReturnValue(Object returnValue, MethodParameter returnType,
                ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
                throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
    
            mavContainer.setRequestHandled(true);
            ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
            ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
    
            // Try even with null return value. ResponseBodyAdvice could get involved.
            writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
        }

      然后调用org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor#writeWithMessageConverters  里面用一坨代码判断,如果是String类型直接用 text/plain 写回去,如果是其他类型则转JSON写回去。

      所以可以看到如果是ResponseBody的请求,那么invocableMethod.invokeAndHandle(webRequest, mavContainer); 方法走完客户端就可以收到数据。

    (3)  getModelAndView(mavContainer, modelFactory, webRequest);  返回ModelAndView 对象给DispatcherServlet。 这里需要注意,如果是ResponseBody返回的JSON数据,那么返回的ModelAndView对象是null,否则会返回一个带视图名称的对象,org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#getModelAndView:

        private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
                ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
    
            modelFactory.updateModel(webRequest, mavContainer);
            // ResponseBody声明的在前面已经处理过,直接返回null
            if (mavContainer.isRequestHandled()) {
                return null;
            }
            ModelMap model = mavContainer.getModel();
            ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
            if (!mavContainer.isViewReference()) {
                mav.setView((View) mavContainer.getView());
            }
            if (model instanceof RedirectAttributes) {
                Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
                HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
                RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
            }
            return mav;
        }    

    比如/test2/test1 请求返回的ModelAndView对象如下:

     6》接下来调用mappedHandler.applyPostHandle(processedRequest, response, mv); 方法处理拦截器的postHandle 方法

    mappedHandler.applyPostHandle(processedRequest, response, mv):  可以看到是和前置相反的顺序执行,也就是preHandle顺序是(inteceptor1-》inteceptor2-》inteceptor3), postHandle是 从3-》1 这样的顺序执行

        void applyPostHandle(HttpServletRequest request, HttpServletResponse response, 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);
                }
            }
        }

    7》接下来processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); 方法处理ModelAndView结果,如下:

        private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
                HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
    
            boolean errorView = false;
    
            if (exception != null) {
                if (exception instanceof ModelAndViewDefiningException) {
                    logger.debug("ModelAndViewDefiningException encountered", exception);
                    mv = ((ModelAndViewDefiningException) exception).getModelAndView();
                }
                else {
                    Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
                    mv = processHandlerException(request, response, handler, exception);
                    errorView = (mv != null);
                }
            }
    
            // Did the handler return a view to render?
            if (mv != null && !mv.wasCleared()) {
                render(mv, request, response);
                if (errorView) {
                    WebUtils.clearErrorRequestAttributes(request);
                }
            }
            else {
                if (logger.isDebugEnabled()) {
                    logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
                            "': assuming HandlerAdapter completed request handling");
                }
            }
    
            if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
                // Concurrent handling started during a forward
                return;
            }
    
            if (mappedHandler != null) {
                mappedHandler.triggerAfterCompletion(request, response, null);
            }
        }

    7.1》如果是回传JSON数据,ModelAndView是null,则不会走render方法,否则会走render方法:

    org.springframework.web.servlet.DispatcherServlet#render

        protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
            // Determine locale for request and apply it to the response.
            Locale locale = this.localeResolver.resolveLocale(request);
            response.setLocale(locale);
    
            View view;
            if (mv.isReference()) {
                // We need to resolve the view name.
                view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
                if (view == null) {
                    throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
                            "' in servlet with name '" + getServletName() + "'");
                }
            }
            else {
                // No need to lookup: the ModelAndView object contains the actual View object.
                view = mv.getView();
                if (view == null) {
                    throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
                            "View object in servlet with name '" + getServletName() + "'");
                }
            }
    
            // Delegate to the View object for rendering.
            if (logger.isDebugEnabled()) {
                logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
            }
            try {
                if (mv.getStatus() != null) {
                    response.setStatus(mv.getStatus().value());
                }
                view.render(mv.getModelInternal(), request, response);
            }
            catch (Exception ex) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
                            getServletName() + "'", ex);
                }
                throw ex;
            }
        }

    org.springframework.web.servlet.DispatcherServlet#resolveViewName 解析获取view 视图信息:

        protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,
                HttpServletRequest request) throws Exception {
    
            for (ViewResolver viewResolver : this.viewResolvers) {
                View view = viewResolver.resolveViewName(viewName, locale);
                if (view != null) {
                    return view;
                }
            }
            return null;
        }

    比如: test2/test1 接口返回的view信息如下:

    接下来调用 view.render(mv.getModelInternal(), request, response); 渲染视图, 这个里面就是找对应的thymeleaf 模板然后渲染数据,如果渲染不到进行报错。

    视图渲染完成之后会调用  mappedHandler.triggerAfterCompletion(request, response, null); 方法调用拦截器链的afterCompletion 方法(也是逆向进行)

        void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
                throws Exception {
    
            HandlerInterceptor[] interceptors = getInterceptors();
            if (!ObjectUtils.isEmpty(interceptors)) {
                for (int i = this.interceptorIndex; i >= 0; i--) {
                    HandlerInterceptor interceptor = interceptors[i];
                    try {
                        interceptor.afterCompletion(request, response, this.handler, ex);
                    }
                    catch (Throwable ex2) {
                        logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
                    }
                }
            }
        }

    处理完成则doDispatch方法结束,继续执行过滤器后半部分代码。

    总结:

    1. 整个DispatcherServlet 处理过程可以用下图表示:

     

     2. 根据请求的uri获取HandlerExecutionChaindler 是从一个维护URL与地址信息的map中获取的

    RequestMappingHandlerMapping获取HandlerExecutionChaindler的方法getHandler是继承自父类的方法:org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler,内部获取处理器HandlerMethod的方法是:org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal

        protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
            String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
            if (logger.isDebugEnabled()) {
                logger.debug("Looking up handler method for path " + lookupPath);
            }
            this.mappingRegistry.acquireReadLock();
            try {
                HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
                if (logger.isDebugEnabled()) {
                    if (handlerMethod != null) {
                        logger.debug("Returning handler method [" + handlerMethod + "]");
                    }
                    else {
                        logger.debug("Did not find handler method for [" + lookupPath + "]");
                    }
                }
                return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
            }
            finally {
                this.mappingRegistry.releaseReadLock();
            }
        }

    然后lookupHandlerMethod 是org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#lookupHandlerMethod:

        protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
            List<Match> matches = new ArrayList<Match>();
            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));
                Collections.sort(matches, 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);
                return bestMatch.handlerMethod;
            }
            else {
                return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
            }
        }

    (1)List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath); 获取到请求的路径和方法,org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#getMappingsByUrl是从自己的属性里面获取, urlLookup 是一个维护了URL路径信息的map

     (2) 然后加到上面的matches 里面

     

     (3) 然后获取到集合第一个元素之后返回其handlerMethod对象(包装了在Spring容器中的beanName以及其方法相关信息), 用于之后反射调用方法进行处理

    补充:org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#requestMappingHandlerMapping 注入Spring中

        public RequestMappingHandlerMapping requestMappingHandlerMapping() {
            RequestMappingHandlerMapping handlerMapping = createRequestMappingHandlerMapping();
            handlerMapping.setOrder(0);
            handlerMapping.setInterceptors(getInterceptors());
            handlerMapping.setContentNegotiationManager(mvcContentNegotiationManager());
            handlerMapping.setCorsConfigurations(getCorsConfigurations());
    
            PathMatchConfigurer configurer = getPathMatchConfigurer();
            if (configurer.isUseSuffixPatternMatch() != null) {
                handlerMapping.setUseSuffixPatternMatch(configurer.isUseSuffixPatternMatch());
            }
            if (configurer.isUseRegisteredSuffixPatternMatch() != null) {
                handlerMapping.setUseRegisteredSuffixPatternMatch(configurer.isUseRegisteredSuffixPatternMatch());
            }
            if (configurer.isUseTrailingSlashMatch() != null) {
                handlerMapping.setUseTrailingSlashMatch(configurer.isUseTrailingSlashMatch());
            }
            UrlPathHelper pathHelper = configurer.getUrlPathHelper();
            if (pathHelper != null) {
                handlerMapping.setUrlPathHelper(pathHelper);
            }
            PathMatcher pathMatcher = configurer.getPathMatcher();
            if (pathMatcher != null) {
                handlerMapping.setPathMatcher(pathMatcher);
            }
    
            return handlerMapping;
        }

    设置了一系列的参数,其中包括获取拦截器getInterceptors(),如下:

    org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#getInterceptors

        protected final Object[] getInterceptors() {
            if (this.interceptors == null) {
                InterceptorRegistry registry = new InterceptorRegistry();
                addInterceptors(registry);
                registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService()));
                registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider()));
                this.interceptors = registry.getInterceptors();
            }
            return this.interceptors.toArray();
        }

     补充: 根据请求获取request的规则

    有时候一个请求会匹配到多个满足条件的handler,比如:

    GET /v1/v2

    PUT /v1/v2

    GET /v1/{path}

    PUT /v1/{path}

    1. 这时候访问/v1/v2 的时候会匹配到两个路径,SpringMVC 处理逻辑是先根据全路径去查找,然后后根据方法再次过滤

    org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#lookupHandlerMethod

        protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
            List<Match> matches = new ArrayList<>();
            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()) {
                Match bestMatch = matches.get(0);
                if (matches.size() > 1) {
                    Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
                    matches.sort(comparator);
                    bestMatch = matches.get(0);
                    if (logger.isTraceEnabled()) {
                        logger.trace(matches.size() + " matching mappings: " + matches);
                    }
                    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();
                        String uri = request.getRequestURI();
                        throw new IllegalStateException(
                                "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
                    }
                }
                request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
                handleMatch(bestMatch.mapping, lookupPath, request);
                return bestMatch.handlerMethod;
            }
            else {
                return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
            }
        }

    (1) org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#getMappingsByUrl 根据全路径找

            @Nullable
            public List<T> getMappingsByUrl(String urlPath) {
                return this.urlLookup.get(urlPath);
            }

    比如我们访问GET /v1/v2 问找到两个RequestMappingInfo

    接下来拿找到的两个到addMatchingMappings 进行筛选:

        private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
            for (T mapping : mappings) {
                T match = getMatchingMapping(mapping, request);
                if (match != null) {
                    matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
                }
            }
        }

    接下来调用:org.springframework.web.servlet.mvc.method.RequestMappingInfo#getMatchingCondition

        public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
            RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);
            if (methods == null) {
                return null;
            }
            ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);
            if (params == null) {
                return null;
            }
            HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);
            if (headers == null) {
                return null;
            }
            ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request);
            if (consumes == null) {
                return null;
            }
            ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request);
            if (produces == null) {
                return null;
            }
            PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request);
            if (patterns == null) {
                return null;
            }
            RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request);
            if (custom == null) {
                return null;
            }
    
            return new RequestMappingInfo(this.name, patterns,
                    methods, params, headers, consumes, produces, custom.getCondition());
        }

      然后这里面就是根据对应的方法、参数、头、consumes(可以接收的消息类型)、produces(返回去的消息类型)等条件进行判断。 会筛选到index为1的RequestMappingInfo。

      然后到下面找到最佳匹配的bestMatch, 然后获取handleMethod。

    (2)    如果我们获取GET /v1/v3。 

    首先根据全路径找找不到匹配的,

    然后获取到系统所有的包括变量型路径:

    org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#getMappings:mappingLookup  实际是容器启动时扫描到的保存的所有信息,T类型是RequestMappingInfo

            private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
    
            public Map<T, HandlerMethod> getMappings() {
                return this.mappingLookup;
            }

    然后遍历系统所有的RequestMappingInfo,调用方法org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#addMatchingMappings 遍历所有的RequestMappingInfo进行匹配,并且匹配到不会停止,会返回一个满足条件的集合。

        private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
            for (T mapping : mappings) {
                T match = getMatchingMapping(mapping, request);
                if (match != null) {
                    matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
                }
            }
        }

     找到之后返回去,如果数量大于1,做排序比较后拿到最优解。只有一个那直接返回其handlerMethod。

    (3) 如果这一步没找到handler,则走后面的org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping#handleNoMatch 进行抛异常会其他处理,这里可以覆盖。

        @Override
        protected HandlerMethod handleNoMatch(
                Set<RequestMappingInfo> infos, String lookupPath, HttpServletRequest request) throws ServletException {
    
            PartialMatchHelper helper = new PartialMatchHelper(infos, request);
            if (helper.isEmpty()) {
                return null;
            }
    
            if (helper.hasMethodsMismatch()) {
                Set<String> methods = helper.getAllowedMethods();
                if (HttpMethod.OPTIONS.matches(request.getMethod())) {
                    HttpOptionsHandler handler = new HttpOptionsHandler(methods);
                    return new HandlerMethod(handler, HTTP_OPTIONS_HANDLE_METHOD);
                }
                throw new HttpRequestMethodNotSupportedException(request.getMethod(), methods);
            }
    
            if (helper.hasConsumesMismatch()) {
                Set<MediaType> mediaTypes = helper.getConsumableMediaTypes();
                MediaType contentType = null;
                if (StringUtils.hasLength(request.getContentType())) {
                    try {
                        contentType = MediaType.parseMediaType(request.getContentType());
                    }
                    catch (InvalidMediaTypeException ex) {
                        throw new HttpMediaTypeNotSupportedException(ex.getMessage());
                    }
                }
                throw new HttpMediaTypeNotSupportedException(contentType, new ArrayList<>(mediaTypes));
            }
    
            if (helper.hasProducesMismatch()) {
                Set<MediaType> mediaTypes = helper.getProducibleMediaTypes();
                throw new HttpMediaTypeNotAcceptableException(new ArrayList<>(mediaTypes));
            }
    
            if (helper.hasParamsMismatch()) {
                List<String[]> conditions = helper.getParamConditions();
                throw new UnsatisfiedServletRequestParameterException(conditions, request.getParameterMap());
            }
    
            return null;
        }

    总结:

    1. MappingRegistry 有两个重要的缓存map

            private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
    
            private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();

    mappingLookup  存放系统中所有的RequestMappingInfo和HandlerMethod 映射关系。RequestMappingInfo是封装我们方法上的@RequestMapping 注解的类,handlerMethod 则是记录我们方法的信息。(这里存放所有的,包括带变量的和不带变量的)

    urlLookup  是存放URI与RequestMappingInfo 关系,MultiValueMap 是内部是一个集合,一个key对一个集合。(这里只存全路径,对于路径带变量的不会存到这里)

    2. 请求获取handlerMethod 如下:

    (1)一个请求进来先根据URI到urlLookup  获取RequestMappingInfo (只能获取全路径的,带变量的获取不到)

    》获取到就拿获取的调用方法org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#addMatchingMappings 判断是否满足条件,满足加到matches集合中

    (2) 如果走完上面第一步matches为空,就拿mappingLookup 的Key集合,也就是系统所有的RequestMappingInfo 去遍历找是否有满足的(会处理带变量的匹配)

    (3)如果上面第二步返回的matches不会空,处理matches 集合,拿到最佳匹配, 其比较器如下:org.springframework.web.servlet.mvc.method.RequestMappingInfo#compareTo

        public int compareTo(RequestMappingInfo other, HttpServletRequest request) {
            int result;
            // Automatic vs explicit HTTP HEAD mapping
            if (HttpMethod.HEAD.matches(request.getMethod())) {
                result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);
                if (result != 0) {
                    return result;
                }
            }
            result = this.patternsCondition.compareTo(other.getPatternsCondition(), request);
            if (result != 0) {
                return result;
            }
            result = this.paramsCondition.compareTo(other.getParamsCondition(), request);
            if (result != 0) {
                return result;
            }
            result = this.headersCondition.compareTo(other.getHeadersCondition(), request);
            if (result != 0) {
                return result;
            }
            result = this.consumesCondition.compareTo(other.getConsumesCondition(), request);
            if (result != 0) {
                return result;
            }
            result = this.producesCondition.compareTo(other.getProducesCondition(), request);
            if (result != 0) {
                return result;
            }
            // Implicit (no method) vs explicit HTTP method mappings
            result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);
            if (result != 0) {
                return result;
            }
            result = this.customConditionHolder.compareTo(other.customConditionHolder, request);
            if (result != 0) {
                return result;
            }
            return 0;
        }

    (4) 如果获取到的matches为空,则走org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#handleNoMatch 方法。

    补充: SpringMVC 注册Handler 过程 

    1. org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport 这个类里面注入了 RequestMappingHandlerMapping。这个配置类是被EnableWebMvc 注解引入的。

    2. org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#afterPropertiesSet 实现的InitializingBean 的方法,这个方法是在bean 初始化过程中会被调用

        public void afterPropertiesSet() {
            this.config = new BuilderConfiguration();
            this.config.setUrlPathHelper(this.getUrlPathHelper());
            this.config.setPathMatcher(this.getPathMatcher());
            this.config.setSuffixPatternMatch(this.useSuffixPatternMatch());
            this.config.setTrailingSlashMatch(this.useTrailingSlashMatch());
            this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch());
            this.config.setContentNegotiationManager(this.getContentNegotiationManager());
            super.afterPropertiesSet();
        }

    然后调用其父类方法org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#afterPropertiesSet

        public void afterPropertiesSet() {
            this.initHandlerMethods();
        }

    initHandlerMethods() 方法如下:

        protected void initHandlerMethods() {
            String[] var1 = this.getCandidateBeanNames();
            int var2 = var1.length;
    
            for(int var3 = 0; var3 < var2; ++var3) {
                String beanName = var1[var3];
                if (!beanName.startsWith("scopedTarget.")) {
                    this.processCandidateBean(beanName);
                }
            }
    
            this.handlerMethodsInitialized(this.getHandlerMethods());
        }

      主要逻辑是:根据容器中的对象,遍历对象获取其class,判断是否有@Controller 注解

    this.processCandidateBean(beanName);

        protected void processCandidateBean(String beanName) {
            Class beanType = null;
    
            try {
                beanType = this.obtainApplicationContext().getType(beanName);
            } catch (Throwable var4) {
                if (this.logger.isTraceEnabled()) {
                    this.logger.trace("Could not resolve type for bean '" + beanName + "'", var4);
                }
            }
    
            if (beanType != null && this.isHandler(beanType)) {
                this.detectHandlerMethods(beanName);
            }
    
        }

    1》 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#isHandler 判断是否是Handler的方法。 

        protected boolean isHandler(Class<?> beanType) {
            return AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class);
        }

    2》 org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#detectHandlerMethods    解析handler里面的方法

        protected void detectHandlerMethods(Object handler) {
            Class<?> handlerType = handler instanceof String ? this.obtainApplicationContext().getType((String)handler) : handler.getClass();
            if (handlerType != null) {
                Class<?> userType = ClassUtils.getUserClass(handlerType);
                Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (method) -> {
                    try {
                        return this.getMappingForMethod(method, userType);
                    } catch (Throwable var4) {
                        throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, var4);
                    }
                });
                if (this.logger.isTraceEnabled()) {
                    this.logger.trace(this.formatMappings(userType, methods));
                }
    
                methods.forEach((method, mapping) -> {
                    Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
                    this.registerHandlerMethod(handler, invocableMethod, mapping);
                });
            }
    
        }

     (1) org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#getMappingForMethod 解析获取RequestMappingInfo 对象

        @Nullable
        protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
            RequestMappingInfo info = this.createRequestMappingInfo(method);
            if (info != null) {
                RequestMappingInfo typeInfo = this.createRequestMappingInfo(handlerType);
                if (typeInfo != null) {
                    info = typeInfo.combine(info);
                }
    
                String prefix = this.getPathPrefix(handlerType);
                if (prefix != null) {
                    info = RequestMappingInfo.paths(new String[]{prefix}).options(this.config).build().combine(info);
                }
            }
    
            return info;
        }

    创建方法如下:

        @Nullable
        private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
            RequestMapping requestMapping = (RequestMapping)AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
            RequestCondition<?> condition = element instanceof Class ? this.getCustomTypeCondition((Class)element) : this.getCustomMethodCondition((Method)element);
            return requestMapping != null ? this.createRequestMappingInfo(requestMapping, condition) : null;
        }

    如果方法带了RequestMapping 注解会创建RequestMappingInfo 对象然后返回。 继续调用org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#createRequestMappingInfo(org.springframework.web.bind.annotation.RequestMapping, org.springframework.web.servlet.mvc.condition.RequestCondition<?>):

       protected RequestMappingInfo createRequestMappingInfo(RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
            Builder builder = RequestMappingInfo.paths(this.resolveEmbeddedValuesInPatterns(requestMapping.path())).methods(requestMapping.method()).params(requestMapping.params()).headers(requestMapping.headers()).consumes(requestMapping.consumes()).produces(requestMapping.produces()).mappingName(requestMapping.name());
            if (customCondition != null) {
                builder.customCondition(customCondition);
            }
    
            return builder.options(this.config).build();
        }

    创建对象时获取了方法上的一些注解信息(包括头、consumes(可以接受的消息类型)、produces 可以生产的消息类型)

     3. org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#registerHandlerMethod调用这个注册mapping 信息

        protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
            super.registerHandlerMethod(handler, method, mapping);
            this.updateConsumesCondition(mapping, method);
        }

     4. org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#register

            public void register(T mapping, Object handler, Method method) {
                if (KotlinDetector.isKotlinType(method.getDeclaringClass())) {
                    Class<?>[] parameterTypes = method.getParameterTypes();
                    if (parameterTypes.length > 0 && "kotlin.coroutines.Continuation".equals(parameterTypes[parameterTypes.length - 1].getName())) {
                        throw new IllegalStateException("Unsupported suspending handler method detected: " + method);
                    }
                }
    
                this.readWriteLock.writeLock().lock();
    
                try {
                    HandlerMethod handlerMethod = AbstractHandlerMethodMapping.this.createHandlerMethod(handler, method);
                    this.validateMethodMapping(handlerMethod, mapping);
                    this.mappingLookup.put(mapping, handlerMethod);
                    List<String> directUrls = this.getDirectUrls(mapping);
                    Iterator var6 = directUrls.iterator();
    
                    while(var6.hasNext()) {
                        String url = (String)var6.next();
                        this.urlLookup.add(url, mapping);
                    }
    
                    String name = null;
                    if (AbstractHandlerMethodMapping.this.getNamingStrategy() != null) {
                        name = AbstractHandlerMethodMapping.this.getNamingStrategy().getName(handlerMethod, mapping);
                        this.addMappingName(name, handlerMethod);
                    }
    
                    CorsConfiguration corsConfig = AbstractHandlerMethodMapping.this.initCorsConfiguration(handler, method, mapping);
                    if (corsConfig != null) {
                        this.corsLookup.put(handlerMethod, corsConfig);
                    }
    
                    this.registry.put(mapping, new AbstractHandlerMethodMapping.MappingRegistration(mapping, handlerMethod, directUrls, name));
                } finally {
                    this.readWriteLock.writeLock().unlock();
                }
    
            }

    这里维护到相关的map中。加到了mappingLookup<RequestMappingInfo, HandlerMethod> 和 urlLookup

    【当你用心写完每一篇博客之后,你会发现它比你用代码实现功能更有成就感!】
  • 相关阅读:
    [Machine Learning]Numpy
    [LeetCode]Valid Palindrome
    [LeetCode]Remove Linked List Elements
    [LeetCode]Reverse Linked List
    [LeetCode]Palindrome Number
    Spring绑定请求参数过程以及使用@InitBinder来注册自己的属性处理器
    servlet温故知新
    线程池简单实现
    JAVA NIO学习笔记
    XSS攻击简单介绍
  • 原文地址:https://www.cnblogs.com/qlqwjy/p/14521994.html
Copyright © 2011-2022 走看看