Spring MVC 核心调用流程

作者: 享学课堂 | 来源:发表于2020-08-22 15:00 被阅读0次

    [十六] Spring MVC 核心调用流程

    DispatcherServlet 是什么

    ::: tip 概述
    DispatcherServlet作为Spring MVC的核心控制器,初始化组件,处理客户端发送的请求,并返回ModelAndView,进行视图渲染。主要是实现了父类FrameworkServlet的抽象方法doService()
    :::

    5223dffff8bc3a36ecb23f02b5be758.png

    DispatcherServlet 类图

    DispatcherServletinitStrategies方法如何被调用的

    入口类: AbstractApplicationContext #refresh

    • ├─ refresh <Badge text="① AbstractApplicationContext" type="danger"/>
    • │ ├─ finishRefresh <Badge text="② AbstractApplicationContext" type="danger"/>
    • │ │ └─ publishEvent <Badge text="③ AbstractApplicationContext" type="danger"/>
    • │ │ │ ├─ publishEvent <Badge text="④ ApplicationEventPublisher" type="danger"/>
    • │ │ │ └─ multicastEvent <Badge text="⑤ SimpleApplicationEventMulticaster" type="danger"/>
    • │ │ │ │ └─ invokeListener <Badge text="⑥ SimpleApplicationEventMulticaster" type="danger"/>
    • │ │ │ │ │ └─ doInvokeListener <Badge text="⑦ SimpleApplicationEventMulticaster" type="danger"/>
    • │ │ │ │ │ │ └─ onApplicationEvent <Badge text="⑧ FrameworkServlet的内部类ContextRefreshListener" type="danger"/>
    • │ │ │ │ │ │ │ └─ onRefresh <Badge text="⑨ FrameworkServlet" type="danger"/>
    • │ │ │ │ │ │ │ │ └─ onRefresh <Badge text="⑩ DispatcherServlet" type="danger"/>
    • │ │ │ │ │ │ │ │ │ └─ initStrategies <Badge text="⑪ DispatcherServlet" type="danger"/>

    进入onRefresh方法

    所在类:org.springframework.web.servlet.DispatcherServlet

        protected void onRefresh(ApplicationContext context) {
            initStrategies(context);
        }
    
        protected void initStrategies(ApplicationContext context) {
            // 初始化文件上传处理
            initMultipartResolver(context);
            // 初始化本地化 Resolver
            initLocaleResolver(context);
            // 初始化主题 Resolver
            initThemeResolver(context);
            // 初始化 URL映射关系
            initHandlerMappings(context);
            // 初始化Handler接口适配器
            initHandlerAdapters(context);
            // 初始化异常处理的 handler
            initHandlerExceptionResolvers(context);
            // 初始化请求路径转换
            initRequestToViewNameTranslator(context);
            // 初始化视图解析
            initViewResolvers(context);
            // 初始化 flashmap管理
            initFlashMapManager(context);
        }
    

    ::: warning 知识点
    initStrategies方法中的所有初始化组件中之所以可以拿到值,主要是通过@EnableWebMvc注解,调用到WebMvcConfigurationSupport类中的各个@Bean注解的方法,完成的实例化过程。
    :::

    请求调用流程

    当父子容器都启动完成后,开始进行请求的响应处理,

    • 请求http://localhost:9090/user/queryUser地址

    进入service方法

    所在类:javax.servlet.http.HttpServlet

    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            String method = req.getMethod();
            long lastModified;
            if (method.equals("GET")) {
                lastModified = this.getLastModified(req);
                if (lastModified == -1L) {
                    this.doGet(req, resp);
                } 
              // ...... 省略
        }
    

    进入doGet方法

    所在类:org.springframework.web.servlet.FrameworkServlet

    @Override
        protected final void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
    
            processRequest(request, response);
        }
    

    进入processRequest方法

    所在类:org.springframework.web.servlet.FrameworkServlet

    protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
    
            long startTime = System.currentTimeMillis();
            Throwable failureCause = null;
            
            doService(request, response);
    }
    

    进入doService方法

    所在类:org.springframework.web.servlet.DispatcherServlet

    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
            logRequest(request);
            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);
                    }
                }
            }
    }
    

    请求调用 核心入口

    请求最终进入doDispatch方法

    所在类:org.springframework.web.servlet.DispatcherServlet

        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 {
                    // 文件上传解析,如果请求类型是multipart将通过
                    // MultipartResolver进行文件上传解析
                    processedRequest = checkMultipart(request);
                    multipartRequestParsed = (processedRequest != request);
                    // 对当前请求匹配一个合适的 handler,重要方法
                    mappedHandler = getHandler(processedRequest);
                    if (mappedHandler == 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 (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) {
                    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 {
                    // 如果是multipart的请求,清空上传的multipart资源
                    if (multipartRequestParsed) {
                        cleanupMultipart(processedRequest);
                    }
                }
            }
        }
    

    ::: warning 知识点
    总结,getHandler方法的主要作用体现在以下几点:

    • 首先,从当前Request中拿到请求的URL
    • 然后,从映射关系中拿到HandlerMethod对象
    • 接着,把HandlerMethod对象封装到HandlerExecutionChain执行链中
    • 最后,在HandlerExecutionChain执行链的创建过程中会拿到整个容器中所有的拦截器(实现HandlerInterceptor接口的拦截器),和当前请求的URL进行匹配,如果匹配成功的话,就会把拦截器放到HandlerExecutionChain的数组中。
      :::

    进入getHandler方法

    所在类:org.springframework.web.servlet.DispatcherServlet

    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
            // handlerMappering 实例容器不为空
            if (this.handlerMappings != null) {
                for (HandlerMapping mapping : this.handlerMappings) {
                    // 获取 HandlerMethod 和过滤器链的包装类
                    HandlerExecutionChain handler = mapping.getHandler(request);
                    if (handler != null) {
                        return handler;
                    }
                }
            }
            return null;
        }
    

    进入getHandler方法

    所在类:org.springframework.web.servlet.handler.AbstractHandlerMapping

    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
            // 根据请求的 URL 拿到对应的 HandlerMethod 对象
            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);
            }
            // 获取 HandlerMethod 和过滤器链的包装类
            HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
    
            if (logger.isTraceEnabled()) {
                logger.trace("Mapped to " + handler);
            }
            else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
                logger.debug("Mapped to " + executionChain.getHandler());
            }
            // 是否是跨域请求,就是查看 request 请求头中是否有 Origin 属性
            if (CorsUtils.isCorsRequest(request)) {
                CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
                CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
                CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
                executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
            }
    
            return executionChain;
        }
    

    进入getHandlerInternal方法

    所在类:org.springframework.web.servlet.handler.AbstractHandlerMethodMapping

    protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
            // 从request对象中获取 URL,/common/query2
            String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
            this.mappingRegistry.acquireReadLock();
            try {
                // 根据 URL 从映射关系中找到对应的 HandlerMethod 对象
                HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
                // 执行beanFactory.getBean的过程,获取Controller实例
                return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
            }
            finally {
                this.mappingRegistry.releaseReadLock();
            }
        }
    

    ::: warning 知识点
    lookupHandlerMethod方法之所以可以从映射关系中拿到HandlerMethod对象,是因为AbstractHandlerMethodMapping类实现了InitializingBean接口,在afterPropertiesSet方法里建立好了映射关系。
    :::

    进入lookupHandlerMethod方法

    所在类:org.springframework.web.servlet.handler.AbstractHandlerMethodMapping

    protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
            List<Match> matches = new ArrayList<>();
            List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
            if (directPathMatches != null) {
                // 匹配过程,是否符合 RequestMappingInfo 里的属性值
                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);
                Match bestMatch = matches.get(0);
                if (matches.size() > 1) {
                    if (logger.isTraceEnabled()) {
                        logger.trace(matches.size() + " matching mappings: " + matches);
                    }
                    if (CorsUtils.isPreFlightRequest(request)) {
                        return PREFLIGHT_AMBIGUOUS_MATCH;
                    }
                    Match secondBestMatch = matches.get(1);
                    // 如果两个 RequestMappinginfo 什么都相同,报错
                    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);
            }
        }
    

    ::: warning 知识点
    addMatchingMappings方法,主要一个匹配过程,匹配@RequestMapping注解中的属性值是否满足

     /*
      * consumes:指定处理请求的提交内容类型(Content-Type),
      *     例如application/json, text/html;
      * produces: 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;
      * params: 指定request中必须包含某些参数值是,才让该方法处理。
      * headers: 指定request中必须包含某些指定的header值,才能让该方法处理请求。
      * */
    @RequestMapping(value = "/getUser",
        method = RequestMethod.GET,
        params = "username=jack",
        consumes = "application/json",
        produces = "application/json",
        headers = "Referer=http://www.xx.com/")
    

    :::

    返回getHandler,进入getHandlerExecutionChain方法

    所在类:org.springframework.web.servlet.handler.AbstractHandlerMapping

    protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
            // 如果没有获得则创建一个 HandlerExecutionChain
            HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
                    (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
            // 获取当前的请求地址: /user/xxx
            String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
            // 在 HandlerExecutionChain 中添加拦截器
            // 遍历 SpringMVC 容器的所有拦截器
            for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
                // 判断拦截器类型,如果是 MappedInterceptor 类型
                if (interceptor instanceof MappedInterceptor) {
                    MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
                    // 则先匹配路径后再添加到执行链
                    if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
                        chain.addInterceptor(mappedInterceptor.getInterceptor());
                    }
                }
                else {
                    // 否则直接添加到执行链
                    chain.addInterceptor(interceptor);
                }
            }
            return chain;
        }
    

    ::: warning 知识点
    getHandlerExecutionChain中的HandlerInterceptor拦截器是Spring MVC中的,Spring AOP中的拦截器是MethodInterceptor
    :::

    拿到当前请求对应的handler后,

    返回主流程,进入getHandlerAdapter方法

    所在类:org.springframework.web.servlet.DispatcherServlet

    /**
     * TODO : 根据 handlerMethod对象,找到合适的 HandlerAdapter对象,这里用到了策略模式
     */
    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
            if (this.handlerAdapters != null) {
                for (HandlerAdapter adapter : this.handlerAdapters) {
                    if (adapter.supports(handler)) {
                        // 返回一个可以支持的HandlerAdapter 处理程序实例
                        return adapter;
                    }
                }
            }
            throw new ServletException("No adapter for handler [" + handler +
                    "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
        }
    

    ::: warning 知识点

    HandlerAdapter 是什么
    HandlerAdapter是一个接口,充当自身与处理程序对象之间的桥梁,从而导致松散耦合设计。HandlerAdapter主要处理方法参数、相关注解、数据绑定、消息转换、返回值、调用视图解析器等。

    • RequestMappingHandlerMapping为当前的请求找到合适的处理程序方法。
    • RequestMappingHandlerAdapter执行这个处理程序方法,并为它提供反射调用所需要的参数。

    HandlerAdapter UML 图
    <a data-fancybox title="采集" href="/assets/image-20200209172256575.png"><img :src="$withBase('/assets/image-20200209172256575.png')" alt=""></a>

    HandlerAdapter的4个实现类:

    1. SimpleServletHandlerAdapter: 适配实现 Servlet 接口的Handler, 默认调用其service方法
    1. SimpleControllerHandlerAdapter: 适配实现Controller 接口的Handler, 默认调用其 handleRequest 方法
    2. HttpRequestHandlerAdapter: 适配实现HttpRequestHandler 接口的 Handler, 默认调用其 handleRequest 方法
    3. RequestMappingHandlerAdapter: 适配被@RequestMapping注释的方式, 一般都是解析一个一个参数, 并且通过反射进行激活

    HandlerAdapter 总结
    HandlerAdapterSpring MVC中扩展机制的非常好的一个体现,,通过 HandlerAdapter这种设计模式,DispatcherServlet 就可以支持任何格式的Handler(这里的可以支持指在不改变 DispatcherServlet 的情况下),第二是HandlerAdapter 基于不同Handler实现不同实现类(策略模式),最后也是最重要的就是参数的解析与返回值的解析。

    :::

    ::: danger 为什么要用HandlerAdapter适配器模式?
    首先, Controller的定义有多种 ,一种是带@Controller注解的, 还可以写一个servlet 当做controller, 所以用适配器做适配,不同子类实现HandlerAdapter接口,定义自己的业务逻辑,每个子类都是适配某一种类型的控制器,有了HandlerAdapter,你只需要调用自己实现的handle方法,屏蔽了不一致的细节,对用户来说直接找到对应的处理方法,无须关系哪个实现方法,否则只能在DispatcherServlet里面通过ifelse来处理了。
    :::

    前置过滤器

    返回主流程,进入applyPreHandle方法,前置过滤器

    所在类:org.springframework.web.servlet.DispatcherServlet

    /**
     * TODO : 调用所有的 HandlerInterceptor 拦截器并调用其 preHandler方法
     */
    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];
                    // 分别调用拦截器的 preHandle 方法
                    if (!interceptor.preHandle(request, response, this.handler)) {
                        triggerAfterCompletion(request, response, null);
                        return false;
                    }
                    // 如果失败,记录最后一次拦截器的位置,倒序释放
                    this.interceptorIndex = i;
                }
            }
            return true;
        }
    
    

    返回主流程,进入handle方法,调用具体Controller的方法

    最终会进入AbstractHandlerMethodAdapterhandle方法,

        public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
                throws Exception {
            return handleInternal(request, response, (HandlerMethod) handler);
        }
    

    进入handleInternal方法,

    所在类:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

    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
                    // 执行 HandlerMethod,返回 ModelAndView
                    mav = invokeHandlerMethod(request, response, handlerMethod);
                }
            }
            else {
                // No synchronization on session demanded at all...
                // 执行 HandlerMethod,返回 ModelAndView
                mav = invokeHandlerMethod(request, response, handlerMethod);
            }
    
            if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
                if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
                    applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
                }
                else {
                    prepareResponse(response);
                }
            }
    
            return mav;
        }
    

    进入invokeHandlerMethod方法,

    所在类:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

    protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
                HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    
            ServletWebRequest webRequest = new ServletWebRequest(request, response);
            try {
                // 获取数据绑定工厂  @InitBinder注解支持,
                WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
                // Model工厂,收集了@ModelAttribute注解的方法
                ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
                //可调用的方法对象
                ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
                if (this.argumentResolvers != null) {
                    //设置参数解析器
                    invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
                }
                if (this.returnValueHandlers != null) {
                    // 设置返回值解析器
                    invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
                }
                // 设置参数绑定工厂
                invocableMethod.setDataBinderFactory(binderFactory);
                // 设置参数名称解析类
                invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
    
                ModelAndViewContainer mavContainer = new ModelAndViewContainer();
                mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
                // 调用有 @ModelAttribute注解的方法。每次请求都会调用有 @ModelAttribute注解的方法
                //把 @ModelAttribute注解的方法的返回值存储到 ModelAndViewContainer对象的 map中了
                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();
                    LogFormatUtils.traceDebug(logger, traceOn -> {
                        String formatted = LogFormatUtils.formatValue(result, !traceOn);
                        return "Resume with async result [" + formatted + "]";
                    });
                    invocableMethod = invocableMethod.wrapConcurrentResult(result);
                }
                // Controller方法调用,重点看看
                invocableMethod.invokeAndHandle(webRequest, mavContainer);
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return null;
                }
    
                return getModelAndView(mavContainer, modelFactory, webRequest);
            }
            finally {
                webRequest.requestCompleted();
            }
        }
    

    ::: warning 知识点
    invokeHandlerMethod方法主要进行了数据和参数的绑定、创建ModelAndViewContainer视图容器,以及相关初始化工作。
    :::

    进入invokeAndHandle方法

    所在类:org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod

    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) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
                    mavContainer.setRequestHandled(true);
                    return;
                }
            }
            else if (StringUtils.hasText(getResponseStatusReason())) {
                mavContainer.setRequestHandled(true);
                return;
            }
    
            mavContainer.setRequestHandled(false);
            Assert.state(this.returnValueHandlers != null, "No return value handlers");
            try {
                // 返回值处理
                this.returnValueHandlers.handleReturnValue(
                        returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
            }
            catch (Exception ex) {
                if (logger.isTraceEnabled()) {
                    logger.trace(formatErrorForReturnValue(returnValue), ex);
                }
                throw ex;
            }
        }
    

    进入invokeForRequest方法

    所在类:org.springframework.web.method.support.InvocableHandlerMethod

    public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
                Object... providedArgs) throws Exception {
            // 获取参数数组,重点看
            Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
            if (logger.isTraceEnabled()) {
                logger.trace("Arguments: " + Arrays.toString(args));
            }
            return doInvoke(args);
        }
    

    进入getMethodArgumentValues方法

    所在类:org.springframework.web.method.support.InvocableHandlerMethod

    protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
                Object... providedArgs) throws Exception {
    
            if (ObjectUtils.isEmpty(getMethodParameters())) {
                return EMPTY_ARGS;
            }
            // 入参的包装类,里面包装了参数类型,参数名称,参数注解等等信息
            MethodParameter[] parameters = getMethodParameters();
            Object[] args = new Object[parameters.length];
            for (int i = 0; i < parameters.length; i++) {
                MethodParameter parameter = parameters[i];
                // 设置参数名称解析器
                parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
                args[i] = findProvidedArgument(parameter, providedArgs);
                if (args[i] != null) {
                    continue;
                }
                // 典型的策略模式,根据 parameter 能否找到对应参数的处理类,能找到就返回true
                if (!this.resolvers.supportsParameter(parameter)) {
                    throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
                }
                try {
                    // 具体参数值解析过程,重点看看
                    args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
                }
                catch (Exception ex) {
                    // Leave stack trace for later, exception may actually be resolved and handled..
                    if (logger.isDebugEnabled()) {
                        String error = ex.getMessage();
                        if (error != null && !error.contains(parameter.getExecutable().toGenericString())) {
                            logger.debug(formatArgumentError(parameter, error));
                        }
                    }
                    throw ex;
                }
            }
            return args;
        }
    

    进入resolveArgument方法

    所在类:org.springframework.web.method.support.HandlerMethodArgumentResolverComposite

    public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
                NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
            // 根据参数获取对应参数的解析类
            HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
            if (resolver == null) {
                throw new IllegalArgumentException(
                        "Unsupported parameter type [" + parameter.getParameterType().getName() + "]." +
                                " supportsParameter should be called first.");
            }
            // 策略模式去调用具体参数解析类
            return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
        }
    
    

    ::: warning 知识点
    MethodParameter类是对参数信息的封装,其中重要的几个属性包括:

    • parameterIndex:参数的索引位置
    • Parameter:具体参数的对象,包含参数名字
    • parameterType:参数的类型
    • parameterAnnotations:参数的注解数组,一个参数可以有多个注解
    • parameterName:参数名
      等等
      :::

    方法参数解析

    进入getArgumentResolver方法

    所在类:org.springframework.web.method.support.HandlerMethodArgumentResolverComposite

    private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
            // 先从缓存中拿到参数处理器对象
            HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
            // 如果缓存中没有
            if (result == null) {
                // 循环容器中 HandlerMethodArgumentResolver类型的所有解析器: List<HandlerMethodArgumentResolver>
                for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {
                    // 典型的策略模式匹配,拿到当前参数对应的处理解析类
                    if (methodArgumentResolver.supportsParameter(parameter)) {
                        // 赋值给 HandlerMethodArgumentResolver 对象
                        result = methodArgumentResolver;
                        // 放到缓存中
                        this.argumentResolverCache.put(parameter, result);
                        break;
                    }
                }
            }
            return result;
        }
    

    ::: warning 知识点
    HandlerMethodArgumentResolver参数解析器,最复杂的处理流程之一,Spring中默认有26种参数解析器,来对应完成某种参数的解析工作。添加过程是Spring MVC启动实例化后,通过RequestMappingHandlerAdapter类的afterPropertiesSet方法调用getDefaultArgumentResolvers添加到HandlerMethodArgumentResolver解析器中的。
    :::

    中置过滤器

    返回主流程,进入handleInternal方法,中置过滤器

    所在类:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

    void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
                throws Exception {
            // 获取所有拦截器
            HandlerInterceptor[] interceptors = getInterceptors();
            if (!ObjectUtils.isEmpty(interceptors)) {
                for (int i = interceptors.length - 1; i >= 0; i--) {
                    HandlerInterceptor interceptor = interceptors[i];
                    // 分别调用拦截器的 postHandle方法
                    interceptor.postHandle(request, response, this.handler, mv);
                }
            }
        }
    

    ::: warning 知识点
    中置过滤器的调用时序,是当ha.handle 掉完以后,也就是Controller 里面具体方法调用完以后才轮到中置过滤器调用。可以根据ModelAndView对象做视图修改。
    :::

    后置过滤器

    返回主流程,进入triggerAfterCompletion方法,后置过滤器

    所在类:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

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

    ::: warning 知识点
    applyPreHandle前置过滤器主要作用是对当前请求做一些初步的验证,内部执行顺序是正序遍历, applyPostHandle 中置过滤器和triggerAfterCompletion后置过滤器的执行顺序是倒序遍历,倒序是因为一但前置过滤器中断(拦截器preHndle返回false)那么请求终止,根据中断的数组坐标interceptorIndex,倒序释放执行已经过滤的的拦截方法。
    :::

    到此为止,主流程结束,以上完成了Spring MVC`从请求到处理的一系列过程,包括请求方法调用

    、参数解析、过滤器调用等,接下来就是处理返回值的解析工作。

    附:常见注解

    ::: danger 常见注解

    • @ModelAttribute:会在调用Controller的每个方法执行前被执行。

    • @RequestBody:用来处理content-typeapplication/json的类型,可以是对象。

    • @ResponseBody:一般用于返回 JSON 或 XML 数据。

    • @RequestPart:用来处理content-typemultipart/form-data类型的表单提交请求。

    • @ExceptionHandler: 用在方法上,在运行时有效,只捕获当前Controller 中发生的异常。

    • @ControllerAdvice:用在类上,@ControllerAdvice("com.xx.xx")只对这个包里 面的 Controller 生效,并将该类中所有使用了@ExceptionHandler 注解的方法都应用到请求处理方法上。

    • @Cacheable:若该缓存中没有存储该条记录,则执行该方法,有则从缓存取。

    • @CacheEvict:将该缓存下的所有记录都清空。

    • @CachePut:总是会执行该方法,每次都把返回结果更新进该缓存中。

    • @RequestParam:适用于所有类型的参数;

    • @RequestHeader:用于将请求头的信息数据映射到方法参数上 。

    • @CookieValue:用于将请求的 cookie 数据映射到功能处理方法的参数上。

    • @InitBinder:用于绑定表单数据的注解。

    • @RequestAttribute:用于获取 request作用域 中的数据。

    • @SessionAttribute:用于获取 session作用域中的数据。

    • @PathVariable:获取请求 URL 中的动态参数(路径参数),如:"/test4/{id}/{name}"。

    • @MatrixVariable:扩展了URL请求地址,多个请求参数可用 分开, 一般用于进行多条件的组合查询。

    • @CrossOrigin:用于不同域名访问,解决跨域问题。

    :::

    私聊我,添加小姐姐微信,发送暗号“Spring”

    即可领取全套

    【Java超硬核面试资料】

    相关文章

      网友评论

        本文标题:Spring MVC 核心调用流程

        本文链接:https://www.haomeiwen.com/subject/yzevjktx.html