都给整烦了!还在傻傻的问别人?简单易懂的来了!!!SpringMVC核心——返回值问题

在这里插入图片描述

都给整烦了!还在傻傻的问别人?简单易懂的来了!!!SpringMVC核心——返回值问题

SpringMVC核心——返回值问题

SpringMVC 使用 ModelAndView 来处理返回值问题。

1.ModelAndView

官方描述:

Holder for both Model and View in the web MVC framework. Note that
these are entirely distinct. This class merely holds both to make it
possible for a controller to return both model and view in a single
return value.

Represents a model and view returned by a handler, to be resolved by a DispatcherServlet. The view can take the form of a String view name which will need to be resolved by a ViewResolver object; alternatively a View object can be specified directly. The model is a Map, allowing the use of multiple objects keyed by name.

说明一下:

springmvc 框架中,ModelAndView 表示同时持有 Model 和 ViewModelView是完全不同的。

这个类使一个控制器返回单独的一个值同时包含 modelview成为一种可能。

同时也代表着一个处理器返回一个 modelview ,会被 DispatcherServlet解析。

View 对象如果是通过一个字符串形式的 view name获取到的,则能被 ViewResolver对象解析。

或者也可以直接指定View 对象。model 是一个Map 类型,可以使用多个对象的键值对。

2.通过 ModelAndView 来处理返回值问题
这里要搞明白一点,为什么说是通过ModelAndView来处理返回值问题,事实上,返回的类型可以是很多种,可以是 ModelAndView类型,也可以是String类型,还可以是View类型,还有很多。但是他们最终会被解析为 ModelAndView类型的对象。通过源码来证实一下:

org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter#invokeHandlerMethod

在这个方法中:

Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
ModelAndView mav = methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);

其中 result 就是我们调用 handler 方法后的返回值。

通过mehtodInvoker.getModelAndView 方法将result最终解析为 ModelAndView 对象。

来看看具体过程:

public ModelAndView getModelAndView(Method handlerMethod, Class<?> handlerType, Object returnValue,
                ExtendedModelMap implicitModel, ServletWebRequest webRequest) throws Exception {
    ResponseStatus responseStatusAnn = AnnotationUtils.findAnnotation(handlerMethod, ResponseStatus.class);
    if (responseStatusAnn != null) {
        HttpStatus responseStatus = responseStatusAnn.value();
        String reason = responseStatusAnn.reason();
        if (!StringUtils.hasText(reason)) {
            webRequest.getResponse().setStatus(responseStatus.value());
        }
        else {
            webRequest.getResponse().sendError(responseStatus.value(), reason);
        }

        // to be picked up by the RedirectView
        webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, responseStatus);

        responseArgumentUsed = true;
    }

    // Invoke custom resolvers if present...
    if (customModelAndViewResolvers != null) {
        for (ModelAndViewResolver mavResolver : customModelAndViewResolvers) {
            ModelAndView mav = mavResolver.resolveModelAndView(
                    handlerMethod, handlerType, returnValue, implicitModel, webRequest);
            if (mav != ModelAndViewResolver.UNRESOLVED) {
                return mav;
            }
        }
    }

    if (returnValue instanceof HttpEntity) {
        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<String, ?>) returnValue);
    }
    else if (returnValue instanceof String) {
        return new ModelAndView((String) returnValue).addAllObjects(implicitModel);
    }
    else if (returnValue == null) {
        // Either returned null or was 'void' return.
        if (this.responseArgumentUsed || webRequest.isNotModified()) {
            return null;
        }
        else {
            // Assuming view name translation...
            return new ModelAndView().addAllObjects(implicitModel);
        }
    }
    else if (!BeanUtils.isSimpleProperty(returnValue.getClass())) {
        // Assume a single model attribute...
        addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel);
        return new ModelAndView().addAllObjects(implicitModel);
    }
    else {
        throw new IllegalArgumentException("Invalid handler method return value: " + returnValue);
    }
}

这个方法很重要,它描述的是将 handler 方法返回值解析为 ModelAndView的过程,从中可以看出返回值可以为很多类型。建议大家都看看。这篇文章不对具体的每个返回值类型进行说明。

3.从官方描述中,也可以看出 ModelAndView 是分为两部分的,Model 和 View。这里就分两部分来说明。其中 View 部分其实是视图渲染问题。

4.Model 模型。

这里所说的 Model ,不单单指的就是Model具体这个类。而是描述的 SpringMVC如何将 数据存放到 Model中,以便在目标页面中使用。

  • 怎么存放

上面部分已经说过,通过 ModelAndView对象来处理返回值问题。那么就可以通过如下的方式向 Model中存入数据。如:

@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView() {
  ModelAndView mv = new ModelAndView();
  mv.setViewName("success");
  mv.addObject("testKey", "testValue");
  return mv;
}

通过ModelAndView对象的addObject 方法 可以向模型中添加数据。

事实上,可以在方法的入参处添加 Model类型 或 Map 类型的参数,可以通过向其中添加数据来完成向模型中数据的添加。如:

@RequestMapping("/testModelAndView02")
public String testModelAndView2(Map map) {
  map.put("testKey", "testValue");
  return "success";
}

为什么向 handler方法的入参处添加 Model类型或 Map 类型参数添加数据,就能完成 模型数据的添加。

通过断点发现传入目标方法的 Map实际类型是 BindingAwareModelMap 这个类型。

源码解析:

org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter#invokeHandlerMethod() 方法中:

ExtendedModelMap implicitModel = new BindingAwareModelMap(); // 在这里创建的
Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);

org.springframework.web.bind.annotation.support.HandlerMethodInvoker#resolveHandlerArguments() 方法中

if (Model.class.isAssignableFrom(paramType) || Map.class.isAssignableFrom(paramType)) {
    if (!paramType.isAssignableFrom(implicitModel.getClass())) {
        throw new IllegalStateException("Argument [" + paramType.getSimpleName() + "] is of type " +
                "Model or Map but is not assignable from the actual model. You may need to switch " +
                "newer MVC infrastructure classes to use this argument.");
    }
    args[i] = implicitModel;//然后在这个地方进行的赋值。
}

可以看出在目标方法处的 所有 Map 类型(包括其子类型)或是 Model 类型(包括其子类型)的参数都会被转换为
BindingAwareModelMap 这个类型。

这里对 BindingAwareModelMap 类型进行说明:
在这里插入图片描述

  • 存放什么

从上面可以看出,存放的是键值对类型的 Map 或 Model。

  • 存放到何处

看下面这个例子:

@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView() {
  ModelAndView mv = new ModelAndView();
  mv.setViewName("success");
  mv.addObject("testKey", "testValue");
  return mv;
}

success.jsp
在这里插入图片描述
通过测试,发现 Model 中的数据默认被存放到了 Request 域中。

5.总结

SpringMVC 通过ModelAndView 解决了 handler方法返回值问题,明白了 handler方法返回值可以是何种类型,为什么说 ModelAndView解决了handler方法返回值问题,因为 handler 方法的

返回值最终都会被转换成ModelAndView对象。也详细的介绍了 Model可以作为handler方法的入参使用,这里所说的 Model也不仅仅是指 Model 这个类型,也指实现了 ModelMap 接口的

类型。也明白了Model 中存放的什么,存放到了哪里。至此,SpringMVC 方法的返回值问题已经学习完。接下来要学习的是:既然返回值都已经有了,那么该如何去处理呢?——即handler 方法返回

值处理问题,也指视图渲染问题。另外,还有两个注解没有进行说明,@SessionAttribute@ModelAttribute,仔细来说,其实他们两个注解也可以算到 Model中,这里会再写一篇文章来单独说明它两。

【参考文献】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值