4 HandlerAdapter

第三章将 HandlerMapping的时候,我们看到抽象类 AbstractUrlHandlerMapping 中的映射的handler都是Object类型的,而抽象类 AbstractHandlerMethodMapping 的最终实现类的handler必须是方法,类必须有@Controller@RequestMapping注解,对应的方法也得有@RequestMapping注解。

所以说 AbstractHandlerMethodMapping 的最终实现类生成的handler应该有特定的 HandlerAdapter 实现类去运行,而 AbstractUrlHandlerMapping 的最终实现类生成的handler,如果没有使用标准的接口或抽象类,那么就没有对应的 HandlerAdapter,原因是SpringMVC并不知道你的类应该运行哪个方法,怎么设置参数等等。

接下来让我们来认识认识 HandlerAdapter 吧。

4.1 HandlerAdapter 接口

HandlerAdapter 接口实际上有三个方法,我们比较关注的是其中两个。

第一个是supports方法,在2.4.2节中,我们看到在DispatcherServlet中doDispatch方法执行时,如果拿到了持有handler的执行链,会循环调用每一个 HandlerAdapter 的supports方法判断当前的 HandlerAdapter 实现类是否支持这个handler,如果支持,就返回该 HandlerAdapter 实现类。最后,如果都没有支持的,就会抛出异常。

第二个是handle方法,还是在2.4.2节,通过第一个方法得到了handler对应的adpater之后,会先调用执行链的前处理方法,在3.1节中有讲到是如何调用的。调用完前处理方法后,会用找到的adapter调用handle方法返回 ModelAndView。

以下的几个小节都会围绕这个两个方法展开。

还有,为了对应 HandlerMapping 中的顺序,HandlerAdapter 子类也是有顺序的。

4.2 SimpleServletHandlerAdapter 分析

说实话 SimpleServletHandlerAdapter 实在没什么分析的。但是!!!,你一定要理解我在 HandlerMapping 分析中讲的,handler除了在 AbstractHandlerMethodMapping 是被SpringMVC规范的,其他handler都是object,具体怎么用全靠adapter说了算。

好了,直接看源码吧

public class SimpleServletHandlerAdapter implements HandlerAdapter {

    @Override
    public boolean supports(Object handler) {
        return (handler instanceof Servlet);
    }

    @Override
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {

        ((Servlet) handler).service(request, response);
        return null;
    }

    @Override
    public long getLastModified(HttpServletRequest request, Object handler) {
        return -1;
    }

}

惊不惊喜?意不意外?就这么简单,如果你给 AbstractUrlHandlerMapping 最终实现类中设置的bean是实现Servlet接口的或者间接实现Servlet接口的,那么他会直接调用Servlet接口的service方法。

4.3 HttpRequestHandlerAdapter 分析

哇,这个类了不起啊,这个类几乎和4.2节 SimpleServletHandlerAdapter 一模一样。它对应的接口是 HttpRequestHandler,这个接口也了不起,除了没有init和destroy方法,其他和Servlet接口差不多。HttpRequestHandler 接口是SpringMVC自定义的一个接口,入参是request和response,具体这个接口怎么使用,后面的章节会讲到,但是我在这里告诉你关于静态资源文件的映射的handler就是实现这个接口的。

类代码如下:

public class HttpRequestHandlerAdapter implements HandlerAdapter {

    @Override
    public boolean supports(Object handler) {
        return (handler instanceof HttpRequestHandler);
    }

    @Override
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {

        ((HttpRequestHandler) handler).handleRequest(request, response);
        return null;
    }

    @Override
    public long getLastModified(HttpServletRequest request, Object handler) {
        if (handler instanceof LastModified) {
            return ((LastModified) handler).getLastModified(request);
        }
        return -1L;
    }

}

4.4 SimpleControllerHandlerAdapter 分析

经过前两个类分析,可以总结出一个规律,那就是使用 AbstractUrlHandlerMapping 基于类的URI映射都是比较简单的。那么这个类也一样,但是需要额外提一下的是 HttpRequestHandlerAdapter 跟我们的业务逻辑handler关系不大,但你写业务逻辑的handler可能就会用到 SimpleControllerHandlerAdapter。因为这个类对应的handler,其实现的接口是Controller,注意是接口不是注解,在最开始学些SpringMVC开发时,你可能接触过。

以下是类代码:

public class SimpleControllerHandlerAdapter implements HandlerAdapter {

    @Override
    public boolean supports(Object handler) {
        return (handler instanceof Controller);
    }

    @Override
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {

        return ((Controller) handler).handleRequest(request, response);
    }

    @Override
    public long getLastModified(HttpServletRequest request, Object handler) {
        if (handler instanceof LastModified) {
            return ((LastModified) handler).getLastModified(request);
        }
        return -1L;
    }

}

4.5 AbstractHandlerMethodAdapter 分析

前边三个adapter实现类都很容易理解,但是接下来的实现类都是基于方法映射的了,有点难度,大家打起精神来,攻克adapter最后的难关。

AbstractHandlerMethodAdapter 抽象类相比 AbstractHandlerMethodMapping 抽象类要简单的多,原因是mapping需要指导映射规则生成和匹配,而adapter这边只需要支持方法执行。

实际上 AbstractHandlerMethodAdapter 只增加了可排序接口,并且指定了handler最起码需要是HandlerMethod,这里简单看看源码就行。

/**
 * This implementation expects the handler to be an {@link HandlerMethod}.
 * @param handler the handler instance to check
 * @return whether or not this adapter can adapt the given handler
 */
@Override
public final boolean supports(Object handler) {
    return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}

/**
 * Given a handler method, return whether or not this adapter can support it.
 * @param handlerMethod the handler method to check
 * @return whether or not this adapter can adapt the given method
 */
protected abstract boolean supportsInternal(HandlerMethod handlerMethod);

/**
 * This implementation expects the handler to be an {@link HandlerMethod}.
 */
@Override
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
    return handleInternal(request, response, (HandlerMethod) handler);
}

/**
 * Use the given handler method to handle the request.
 * @param request current HTTP request
 * @param response current HTTP response
 * @param handlerMethod handler method to use. This object must have previously been passed to the
 * {@link #supportsInternal(HandlerMethod)} this interface, which must have returned {@code true}.
 * @return ModelAndView object with the name of the view and the required model data,
 * or {@code null} if the request has been handled directly
 * @throws Exception in case of errors
 */
protected abstract ModelAndView handleInternal(HttpServletRequest request,
        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception;

4.6 RequestMappingHandlerAdapter 分析

到这了务必打起精神,因为这块的难度可能要比DispatcherServlet还要难,这里是SpringMVC除了DispatcherServlet外最精髓的地方,可以和bean创建的代码一较高低。由于在这个类中引入了比较多的接口和类,所以我先带大家简单梳理一下用到的接口和类。这些接口和类后面的章节也会讲到,这里只不过是提前熟悉一下。

4.6.1 HandlerMethodArgumentResolver 分析

这个接口是用来解析方法入参的,能够将从request和配置中的信息解析为方法入参。而 HandlerMethodArgumentResolverComposite 则是该接口的一个实现类,它的作用是代理所有的接口实现类。向 HandlerMethodArgumentResolverComposite 添加一组方法入参解析器,而后只调用 HandlerMethodArgumentResolverComposite 的相关接口方法,HandlerMethodArgumentResolverComposite 会自动匹配它包含的所有解析器中最合适的,并代理执行。

4.6.2 HandlerMethodReturnValueHandler 分析

HandlerMethodReturnValueHandler 接口用来处理方法的返回参数。HandlerMethodReturnValueHandlerComposite 则是该接口的一个实现类,它的作用是代理一组 HandlerMethodReturnValueHandler 接口实现类,挑选最合适的实现类执行。

4.6.3 HttpMessageConverter 分析

HttpMessageConverter 是用来解析request中的数据和向response中写入数据的。

4.6.4 ParameterNameDiscoverer 分析

ParameterNameDiscoverer 接口是从方法和构造函数中获取参数名的。别以为参数名好搞,你在IDE中写的方法入参名假设是methodParameter1,等编译完毕,你再用IDE的反编译功能反编译一下,会发现方法入参名变成var1/var2之类的了。

所以,Spring采用的办法是使用ASM字节码操作框架直接从本地变量表中获取方法入参名,相关的类是LocalVariableTableParameterNameDiscoverer

而从JDK1.8起,JDK中自带了java.lang.reflect.Parameter类,可以直接从这里获取方法入参名,所以Spring会先判断JVM使用的版本环境,如果包含java.lang.reflect.Executable类,说明是JDK1.8,就默认使用StandardReflectionParameterNameDiscoverer类获取变量,而不是用ASM字节码操作框架。

4.6.5 ModelAndViewContainer 分析

ModelAndViewContainer 是 ModelAndView 产生之前的包装器,例如方法返回了model,则需要判断是否自动添加view,如果返回了view,需要生成一个model出来等等。

4.6.6 ServletInvocableHandlerMethod 和 InvocableHandlerMethod 分析

还记得3.8.1节讲到的 HandlerMethod 吗?HandlerMethod 只是一些数据的组合,而 ServletInvocableHandlerMethod 和 InvocableHandlerMethod 则给 HandlerMethod 装上了方法,使 HandlerMethod 中的method、bean等数据可以被调用。

4.6.7 RequestMappingHandlerAdapter 初始化分析

由于要处理request解析入参、方法反射调用、返回结果处理等等事情,所以这个类初始化的东西也很多。我们来找一找初始化点吧。

public RequestMappingHandlerAdapter() {
    StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
    stringHttpMessageConverter.setWriteAcceptCharset(false);  // see SPR-7316
    
    this.messageConverters = new ArrayList<HttpMessageConverter<?>>(4);
    this.messageConverters.add(new ByteArrayHttpMessageConverter());
    this.messageConverters.add(stringHttpMessageConverter);
    this.messageConverters.add(new SourceHttpMessageConverter<Source>());
    this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
}

首先第一个就是构造函数,在构造函数中,初始化了消息转换器。

  • StringHttpMessageConverter的作用是把body部分的字节数组转化为String,将String类型的数据写入到response的outputStream中。
  • ByteArrayHttpMessageConverter的作用是把body部分的字节数组转到新的字节数组中,将字节数组写入到response的outputStream中。
  • SourceHttpMessageConverter的作用是body部分的字节数组转化为xml对象,将xml对象写入到response的outputStream中。
  • AllEncompassingFormHttpMessageConverter的作用是将body中xml或json部分数据转化为对象,将多个xml对象或多个json对象写入到response的outputStream中。
@Override
public void afterPropertiesSet() {
    // Do this first, it may add ResponseBody advice beans
    initControllerAdviceCache();
    if (this.argumentResolvers == null) {
        List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
        this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }
    if (this.initBinderArgumentResolvers == null) {
        List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
        this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }
    if (this.returnValueHandlers == null) {
        List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
        this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
    }
}

/**
 * Return the list of argument resolvers to use including built-in resolvers
 * and custom resolvers provided via {@link #setCustomArgumentResolvers}.
 */
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
    List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();
    // Annotation-based argument resolution
    resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
    resolvers.add(new RequestParamMapMethodArgumentResolver());
    resolvers.add(new PathVariableMethodArgumentResolver());
    resolvers.add(new PathVariableMapMethodArgumentResolver());
    resolvers.add(new MatrixVariableMethodArgumentResolver());
    resolvers.add(new MatrixVariableMapMethodArgumentResolver());
    resolvers.add(new ServletModelAttributeMethodProcessor(false));
    resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
    resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
    resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
    resolvers.add(new RequestHeaderMapMethodArgumentResolver());
    resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
    resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
    resolvers.add(new SessionAttributeMethodArgumentResolver());
    resolvers.add(new RequestAttributeMethodArgumentResolver());
    // Type-based argument resolution
    resolvers.add(new ServletRequestMethodArgumentResolver());
    resolvers.add(new ServletResponseMethodArgumentResolver());
    resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
    resolvers.add(new RedirectAttributesMethodArgumentResolver());
    resolvers.add(new ModelMethodProcessor());
    resolvers.add(new MapMethodProcessor());
    resolvers.add(new ErrorsMethodArgumentResolver());
    resolvers.add(new SessionStatusMethodArgumentResolver());
    resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
    // Custom arguments
    if (getCustomArgumentResolvers() != null) {
        resolvers.addAll(getCustomArgumentResolvers());
    }
    // Catch-all
    resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
    resolvers.add(new ServletModelAttributeMethodProcessor(true));
    return resolvers;
}


/**
 * Return the list of return value handlers to use including built-in and
 * custom handlers provided via {@link #setReturnValueHandlers}.
 */
private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
    List<HandlerMethodReturnValueHandler> handlers = new ArrayList<HandlerMethodReturnValueHandler>();
    // Single-purpose return value types
    handlers.add(new ModelAndViewMethodReturnValueHandler());
    handlers.add(new ModelMethodProcessor());
    handlers.add(new ViewMethodReturnValueHandler());
    handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters()));
    handlers.add(new StreamingResponseBodyReturnValueHandler());
    handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
            this.contentNegotiationManager, this.requestResponseBodyAdvice));
    handlers.add(new HttpHeadersReturnValueHandler());
    handlers.add(new CallableMethodReturnValueHandler());
    handlers.add(new DeferredResultMethodReturnValueHandler());
    handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));
    // Annotation-based return value types
    handlers.add(new ModelAttributeMethodProcessor(false));
    handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
            this.contentNegotiationManager, this.requestResponseBodyAdvice));
    // Multi-purpose return value types
    handlers.add(new ViewNameMethodReturnValueHandler());
    handlers.add(new MapMethodProcessor());
    // Custom return value types
    if (getCustomReturnValueHandlers() != null) {
        handlers.addAll(getCustomReturnValueHandlers());
    }
    // Catch-all
    if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
        handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
    }
    else {
        handlers.add(new ModelAttributeMethodProcessor(true));
    }
    return handlers;
}

第二个初始化点在 afterPropertiesSet 方法。在这个方法中,我们重点关注两个内容,一个是方法入参解析器加载,另一个是返回参数处理器加载。

在方法入参解析器加载中,把解析器分为四种类型,第一种是基于注解的解析器,第二种是基于类型的解析器,第三种是通用解析器,最后一种是默认解析器。

在返回结果处理器加载中,把处理器分为五种类型,第一种是单目的类型处理器,第二种是使用注解类型处理器,第三种是多目的类型处理器,第四种是通用类型处理器,最后一种是默认处理器。

在生成这些解析器处理器时,有的构造方法引入了web应用上下文,有的引入了属性messageConverters。能说明什么问题呢?说明这些解析器处理器需要从web应用上下文中获取bean供它们使用,需要使用messageConverter对入参或者返回结果进行转换。至于这些解析器处理器分别都是干什么的,目前先不展开,否则你会迷失在 RequestMappingHandlerAdapter。

4.6.8 RequestMappingHandlerAdapter 处理请求分析

前面提到 AbstractHandlerMethodAdapter 抽象类只确定了handler必须是HandlerMethd类型,其余的都留给子类实现。

RequestMappingHandlerAdapter 中,针对判断是不是handler直接返回了true,说明handler只要是HandlerMethd类型的,都可以处理。

下面是重写的handleInternal方法

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

我们看到重写的handleInternal又调用了invokeHandlerMethod方法,看来这个方法才是重点。

/**
 * Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}
 * if view resolution is required.
 * @since 4.2
 * @see #createInvocableHandlerMethod(HandlerMethod)
 */
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();
    }
}

代码从第11行到第28行都是在做准备工作,除开异步响应相关代码,主要内容有初始化 ServletInvocableHandlerMethod 对象,初始化 ModelAndViewContainer 对象。可以看到,在 RequestMappingHandlerAdapter 初始化的属性:方法入参解析器代理类,返回结果处理器代理类、方法入参名探索器等等。

在第38行则调用了 ServletInvocableHandlerMethod 对象的invokeAndHandle方法,这个方法应该干的就是:方法入参解析、反射方法调用、返回结果处理等。

在第42行,则是调用getModelAndView方法生成ModelAndView,假如方法返回null、方法中已经向response写入数据、返回结果处理器向response写入数据,都返回null,也就是不需要 ViewResolver 进行渲染。

既然大致步骤我们都知道了,接下来就是休闲时刻,来看看反射方法调用吧。

ServletInvocableHandlerMethod.java

/**
 * Invokes the method and handles the return value through one of the
 * configured {@link HandlerMethodReturnValueHandler}s.
 * @param webRequest the current request
 * @param mavContainer the ModelAndViewContainer for this request
 * @param providedArgs "given" arguments matched by type (not resolved)
 */
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;
    }
}

在这个方法里可以看到,如果返回参数为null且已经响应response,或者mavContainer属性responseReason不为空,则设置mavContainer属性requestHandled为true,可见,如果属性requestHandled为true,adpater就不会生成 ModelAndView。

执行invokeForRequest方法得到返回结果后,会调用returnValueHandlers对结果进行处理,这个处理可能是添加模型数据,又或者是直接写出数据等等。

接下来看invokeForRequest方法吧

InvocableHandlerMethod.java

/**
 * Invoke the method after resolving its argument values in the context of the given request.
 * <p>Argument values are commonly resolved through {@link HandlerMethodArgumentResolver}s.
 * The {@code providedArgs} parameter however may supply argument values to be used directly,
 * i.e. without argument resolution. Examples of provided argument values include a
 * {@link WebDataBinder}, a {@link SessionStatus}, or a thrown exception instance.
 * Provided argument values are checked before argument resolvers.
 * @param request the current request
 * @param mavContainer the ModelAndViewContainer for this request
 * @param providedArgs "given" arguments matched by type, not resolved
 * @return the raw value returned by the invoked method
 * @exception Exception raised if no suitable argument resolver can be found,
 * or if the method raised an exception
 */
public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
        Object... providedArgs) throws Exception {
    Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
    if (logger.isTraceEnabled()) {
        StringBuilder sb = new StringBuilder("Invoking [");
        sb.append(getBeanType().getSimpleName()).append(".");
        sb.append(getMethod().getName()).append("] method with arguments ");
        sb.append(Arrays.asList(args));
        logger.trace(sb.toString());
    }
    Object returnValue = doInvoke(args);
    if (logger.isTraceEnabled()) {
        logger.trace("Method [" + getMethod().getName() + "] returned [" + returnValue + "]");
    }
    return returnValue;
}

/**
 * Get the method argument values for the current request.
 */
private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,
        Object... providedArgs) throws Exception {
    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);
        GenericTypeResolver.resolveParameterType(parameter, getBean().getClass());
        args[i] = resolveProvidedArgument(parameter, providedArgs);
        if (args[i] != null) {
            continue;
        }
        if (this.argumentResolvers.supportsParameter(parameter)) {
            try {
                args[i] = this.argumentResolvers.resolveArgument(
                        parameter, mavContainer, request, this.dataBinderFactory);
                continue;
            }
            catch (Exception ex) {
                if (logger.isDebugEnabled()) {
                    logger.debug(getArgumentResolutionErrorMessage("Error resolving argument", i), ex);
                }
                throw ex;
            }
        }
        if (args[i] == null) {
            String msg = getArgumentResolutionErrorMessage("No suitable resolver for argument", i);
            throw new IllegalStateException(msg);
        }
    }
    return args;
}


/**
 * Invoke the handler method with the given argument values.
 */
protected Object doInvoke(Object... args) throws Exception {
    ReflectionUtils.makeAccessible(getBridgedMethod());
    try {
        return getBridgedMethod().invoke(getBean(), args);
    }
    catch (IllegalArgumentException ex) {
        assertTargetBean(getBridgedMethod(), getBean(), args);
        String message = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
        throw new IllegalStateException(getInvocationErrorMessage(message, 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 {
            String msg = getInvocationErrorMessage("Failed to invoke controller method", args);
            throw new IllegalStateException(msg, targetException);
        }
    }
}

操作相当简单, 先通过parameterNameDiscoverer获取方法的入参名,再通过argumentResolvers获取入参,设置方法为可访问方法,反射调用方法,返回结果。

4.6.9 小结

RequestMappingHandlerAdapter 之所以比其他adpater复杂,不仅仅是因为它是基于方法的映射,前面的adpater只能根据URI进行映射,而 RequestMappingHandlerAdapter 还同时可以根据Header内容进行规则匹配映射。

从request到方法入参,再到执行,再到返回结果处理,RequestMappingHandlerAdapter 设计了一套精密的结构将这些功能组合到一起,如果让我们自己来写一个基于方法映射的处理器适配器,我觉得难度不是一点点,最起码要耗费比较长的时间才能开发完。

4.7 小结

HandlerAdapter 它是不用管映射规则的,只用管从 HandlerMapping 中获取到的handler,尽管去执行就好,所以是不是感觉模块划分还算比较合理?要不然映射handler和处理handler放到一起写,那不得愁死了,既要管映射规则,还要管不同的handler不同的处理方式。还要注意一点,handle方法要么返回 ModelAndView 要么返回null,如果返回的是 ModelAndView,那 ViewResolver 必须处理,处理不了就得报错。还有一点,如果你自定义了handler,那么也得自定义一个 HandlerAdapter,要不然对应不上 adapter就会抛出异常。

HandlerAdapter 就先讲到这里了,大家是不是对 ModelAndView 是如何转为视图渲染的更加有兴趣了?也更加有信心了?

标签: java, springmvc, spring

添加新评论