SpringMVC 源码分析(七)

上一篇文章SpringMVC 源码分析(六)我们主要看了DispatcherServlet如何根据我们的请求路径去寻找对应的执行器,本篇我们主要看下如何通过执行器执行到我们的Controller方法,即ha.handle方法。

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 {
                //检查是否是MultipartContent的请求,如果是的话就会转换为MultipartHttpServletRequest
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);

                // Determine handler for the current request.
                //为本次request请求寻找一个具体的Handler,并封装成一个执行器链
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // 为当前的handler寻找一个具体的HandlerAdapter,因为HandlerMapping有多个实现类,存储的Handler类型也各不相同
                // 需要适配器来提供一个统一供外部调用的方法入口
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                //如果支持last-modified,则处理
                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;
                }

                // 真正调用Controller的地方
                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);
            }
           //处理程序调用的结果,即ModelAndView或要解析为ModelAndView的异常
            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);
                }
            }
        }
    }

进入hadle方法后,发现调用的是父类AbstractHandlerMethodAdapter的handle方法,没有什么实现,直接调用了handleInternal方法。

继续进入handleInternal分析,这里只是简单的校验,最后调用invokeHandlerMethod方法

@Override
    protected ModelAndView handleInternal(HttpServletRequest request,
            HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

        ModelAndView mav;
        //校验,请求方式是否支持,是否需要验证Session
        checkRequest(request);

        // synchronizeOnSession可配置参数,如果指定为true,服务器只分配一个线程给该用户,确保安全,默认为false
        // 一般不会走这里
        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 {
            // synchronizeOnSession为false时,直接执行调用
            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主要设置一些调用Controller处理方法前所需的组件,例如设置WebDataBinderFactory,主要用来生成WebDataBinder对象,WebDataBinder的作用是将请求的参数值设置到对象的属性中进行绑定,还有请求参数解析器组件argumentResolvers等等,都是为最终调用Controller处理方法做准备,我们直接看执行调用invokeAndHandle方法。

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
            HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        //封装request,response为ServletWebRequest
        ServletWebRequest webRequest = new ServletWebRequest(request, response);
        try {
            //binderFactory为工厂模式,主要用来生成WebDataBinder对象,WebDataBinder的作用是将请求的参数值设置到对象的属性中进行绑定。
            WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
            ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

            ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
            // argumentResolvers内置了HandlerMethodArgumentResolver请求参数解析器,用于将请求参数按照接口接收的类型进行封装,
            // 例如使用@RequestBody注解的解析器RequestResponseBodyMethodProcessor,使用@RequestParam注解的解析器RequestParamMethodArgumentResolver等
            if (this.argumentResolvers != null) {
                invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
            }
            //returnValueHandlers内置了HandlerMethodReturnValueHandler返回参数处理器,用于响应数据的封装处理
            //例如ModelAndView、String、Map、@ResponseBody标记的方法等等,同请求参数解析器一样都有对应的处理器来处理
            if (this.returnValueHandlers != null) {
                invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
            }
            //设置binderFactory为工厂
            invocableMethod.setDataBinderFactory(binderFactory);
            //设置参数名解析器,主要用于HandlerMethodArgumentResolver请求参数解析器解析的值与名字映射,最终通过WebDataBinder进行关联
            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);

            //用于管理异步请求处理的中心类,主要用作SPI,通常不由应用程序类直接使用,可以直接跳过
            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);
            }

            //执行调用
            invocableMethod.invokeAndHandle(webRequest, mavContainer);
            if (asyncManager.isConcurrentHandlingStarted()) {
                return null;
            }
            //返回视图
            return getModelAndView(mavContainer, modelFactory, webRequest);
        }
        finally {
            webRequest.requestCompleted();
        }
    }

进入invokeAndHandle方法后,首先第一行invokeForRequest就是调用Controller方法,后面的都是返回后的处理,暂时先不看。

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()) {
                disableContentCachingIfNecessary(webRequest);
                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方法中,可以看到首先是先解析请求参数值并绑定到对应的参数上,最后再调用doInvoke方法,doInvoke是通过反射最终调用到Controller方法的,我们先看解析请求参数方法:getMethodArgumentValues。

    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方法中,首先是先获取Controller对应方法的参数列表,参数列表在应用启动的时候已经处理好了,这个时候是没有值的,所以需要然后for循环参数列表进行一一赋值。参数的解析并赋值调用的是这个方法:this.resolvers.resolveArgument。

protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {
        //获取Controller对应的方法所有参数,该部分在程序启动时就已经解析好了
        MethodParameter[] parameters = getMethodParameters();
        if (ObjectUtils.isEmpty(parameters)) {
            return EMPTY_ARGS;
        }

        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;
            }
            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 exMsg = ex.getMessage();
                    if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
                        logger.debug(formatArgumentError(parameter, exMsg));
                    }
                }
                throw ex;
            }
        }
        return args;
    }

进入到resolveArgument方法中,首先调用getArgumentResolver获取一个合适的解析器,我Demo示例的Controller是通过@RequestBody来传递参数的,所以匹配的就是RequestResponseBodyMethodProcessor,还有很多专门的解析器,比如@RequestParam注解的解析器RequestParamMethodArgumentResolver等,感兴趣的话可以去深入了解下。

public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
        //获取一个支持HandlerMethodArgument解析的的解析器,
        //我们Controller处理方法使用@RequestBody接受请求,所以对应RequestResponseBodyMethodProcessor
        HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
        if (resolver == null) {
            throw new IllegalArgumentException("Unsupported parameter type [" +
                    parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
        }
        //RequestResponseBodyMethodProcessor开始解析
        return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
    }

有人会问为啥会返回RequestResponseBodyMethodProcessor而不是其他的呢,其实内部实现逻辑就是遍历解析器集合,然后找到支持该参数解析的就返回,我们看下RequestResponseBodyMethodProcessor的supportsParameter实现发现该类支持解析@RequestBody注解,所以就返回RequestResponseBodyMethodProcessor。

接下来就是解析resolveArgument,RequestResponseBodyMethodProcessor解析由readWithMessageConverters方法处理的,主要是读取request的输入流,最后通过fastjson来完成参数与参数值的封装。我们简单列出代码的调用路径

public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
        //获取parameter
        parameter = parameter.nestedIfOptional();
        //parameter类型转换及赋值,parameter.getNestedGenericParameterType()在启动的时注册处理器映射时
        //由AbstractHandlerMethodMapping#createHandlerMethod设置
        Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
        //获取参数名
        String name = Conventions.getVariableNameForParameter(parameter);

        if (binderFactory != null) {
            WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
            if (arg != null) {
                validateIfApplicable(binder, parameter);
                if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
                    throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
                }
            }
            if (mavContainer != null) {
                mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
            }
        }
        //是否需要Optional包装,判断是否是Optional类型,是的话进行包装
        return adaptArgumentIfNecessary(arg, parameter);
    }

readWithMessageConverters方法完成了根据@RequestBody类型赋值,进入该方法可以看到先将我们的请求request再次进行包装为ServletServerHttpRequest类,后面读取请求值的输入流就是通过该类来获取,最终调用重载方法readWithMessageConverters完成参数值的解析与封装,再底层就不分析了,有兴趣的话可以继续深入了解。

protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
            Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

        HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
        Assert.state(servletRequest != null, "No HttpServletRequest");
        //将request进行包装,提供额外的功能方法等
        ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);
        //解析参数值封装到参数中
        Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
        if (arg == null && checkRequired(parameter)) {
            throw new HttpMessageNotReadableException("Required request body is missing: " +
                    parameter.getExecutable().toGenericString(), inputMessage);
        }
        return arg;
    }

同理,GET请求里的@RequestParam解析类RequestParamMethodArgumentResolver也是读取request的输入流进行参数的绑定,这一部分如果感兴趣也可以深入了解。

完成了参数解析后,我们再回到准备调用Controller方法的地方:

将解析好的参数传入doInvoke方法中,我们再看下doInvoke方法实现,其实就是获取到@RequestMapping当时绑定的方法,然后进行反射调用,最终调用的是java.lang.reflect.Method类的invoke方法:

protected Object doInvoke(Object... args) throws Exception {
        ReflectionUtils.makeAccessible(getBridgedMethod());
        try {
            //获取对应Controller对应的Method方法进行调用执行
            return getBridgedMethod().invoke(getBean(), args);
        }
        catch (IllegalArgumentException ex) {
            assertTargetBean(getBridgedMethod(), getBean(), args);
            String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
            throw new IllegalStateException(formatInvokeError(text, args), ex);
        }
        catch (InvocationTargetException ex) {
            // Unwrap for HandlerExceptionResolvers ...
            Throwable targetException = ex.getTargetException();
            if (targetException instanceof RuntimeException) {
                throw (RuntimeException) targetException;
            }
            else if (targetException instanceof Error) {
                throw (Error) targetException;
            }
            else if (targetException instanceof Exception) {
                throw (Exception) targetException;
            }
            else {
                throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
            }
        }
    }

进入invoke方法最终调用ma.invoke(obj, args),

public Object invoke(Object obj, Object... args)
        throws IllegalAccessException, IllegalArgumentException,
           InvocationTargetException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, obj, modifiers);
            }
        }
        MethodAccessor ma = methodAccessor;             // read volatile
        if (ma == null) {
            ma = acquireMethodAccessor();
        }
        return ma.invoke(obj, args);
    }

当ma.invoke发起调用的时候,就会到我们Controller的具体方法进行执行

好了,SpringMVC发起调用流程结束了,下一篇文章我们在一起看SpringMVC在调用完成后的结果处理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值