1

在学习 Spring 框架时,我注意到在Spring in Action一书中,作者没有ModelandView在控制器中使用方法返回类型。作者将控制器方法声明为返回类型,String并且方法中的 return 子句只是返回一个字符串,例如return "/views/theview";

有人可以详细说明其工作原理的内部差异吗?

4

3 回答 3

4

这是一个深入的了解。

Spring 提供了一个DispatcherServlet类,通常可以处理您的所有请求。它在它的doDispatch(HttpServletRequest request, HttpServletResponse response)方法中做到了这一点

// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

mv最终ModelAndView对象在哪里,ha是您的控制器方法的包装器,用@RequestMapping.

这通常会经过一堆方法调用,最终在ServletInvocableHandlerMethod.invokeAndHandle

at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle
at org.springframework.web.servlet.DispatcherServlet.doDispatch

看源头

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

returnValue是您的@RequestMapping方法返回的对象。它通过

this.returnValueHandlers.handleReturnValue

Spring 确定HandlerMethodReturnValueHandler处理该对象的位置。

public void handleReturnValue(
        Object returnValue, MethodParameter returnType,
        ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
        throws Exception {

    HandlerMethodReturnValueHandler handler = getReturnValueHandler(returnType); // returns the appropriate handler
    Assert.notNull(handler, "Unknown return value type [" + returnType.getParameterType().getName() + "]");
    handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}

getReturnValueHandler(returnType);返回适当的处理程序。HandlerMethodReturnValueHandler是一个接口,如果处理程序支持该类型,则返回一个方法(、supportsReturnType、等(查找支持的返回类型))。因此,该方法返回它找到的第一个支持该类型的处理程序并运行它。trueStringViewResponseEntity

Spring 在初始化时注册了HandlerMethodReturnValueHandler. 基本上所有已知的实现类都在其 javadoc中。

例如,如果您返回一个 String,Spring 将使用 aViewNameMethodReturnValueHandler来处理响应。

现在,使用哪种返回类型取决于您。如果您想返回 aModel以便可以在 jsp 视图中使用请求属性,您可以让 Spring 将Model实例传递给您的方法,或者您可以Model自己创建对象并将其传递给ModelAndView您返回的 a。在大多数情况下,这是一个风格问题。

于 2013-08-14T21:29:05.327 回答
0

功能方面没有区别,两者是等价的:

@RequestMapping(..)
public String requestMapping1(Model model){
    model.addAttribute("attr1", attr1);
    return "viewName";
}

@RequestMapping(..)
public ModelAndView requestMapping2(){
    ModelAndView modelAndView = new ModelAndView("viewName");
    modelAndView.addObject("attr1", attr1);
    return modelAndView;
}

然而,首选的方法是前者,这就是作者没有在书样中使用后者的原因。

于 2013-08-14T21:30:17.350 回答
0

在spring源码中可以看到这个类org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter。在方法 public ModelAndView getModelAndView(...)中,您可以了解 sping-mvc 如何生成ModelAandView对象。

if (returnValue instanceof HttpEntity) { // returnValue is returned Value of Handler method
            handleHttpEntityResponse((HttpEntity<?>) returnValue, webRequest);
            return null;
        }
        else if (AnnotationUtils.findAnnotation(handlerMethod, ResponseBody.class) != null) {
            handleResponseBody(returnValue, webRequest);
            return null;
        }
        else if (returnValue instanceof ModelAndView) {
            ModelAndView mav = (ModelAndView) returnValue;
            mav.getModelMap().mergeAttributes(implicitModel);
            return mav;
        }
        else if (returnValue instanceof Model) {
            return new ModelAndView().addAllObjects(implicitModel).addAllObjects(((Model) returnValue).asMap());
        }
        else if (returnValue instanceof View) {
            return new ModelAndView((View) returnValue).addAllObjects(implicitModel);
        }
        else if (AnnotationUtils.findAnnotation(handlerMethod, ModelAttribute.class) != null) {
            addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel);
            return new ModelAndView().addAllObjects(implicitModel);
        }
        else if (returnValue instanceof Map) {
            return new ModelAndView().addAllObjects(implicitModel).addAllObjects((Map) returnValue);
        }
        else if (returnValue instanceof String) { // String is here, return new ModelAndView
            return new ModelAndView((String) returnValue).addAllObjects(implicitModel);
        }

所以在这个方法中你可以了解到spring-mvc可以处理很多返回类型的handler方法来构建ModleAndView对象。

于 2013-08-15T01:32:58.493 回答