动力节点旗下在线教育品牌  |  咨询热线:400-8080-105 学Java全栈,上蛙课网
首页 > 文章

ModelAndView处理SpringMVC返回值

08-18 17:39 182浏览
举报 T字号
  • 大字
  • 中字
  • 小字

在我们频繁地使用SpringMVC框架开发项目的过程中,我们会遇到各种各样的问题,SpringMVC返回值问题无疑是其中值得拿出来单独说道的一个。本文我们就来讲用ModelAndView处理SpringMVC返回值问题。

1.ModelAndView的定义

我们先来看看官方给出的关于ModelAndView描述:

springmvc 框架中,ModelAndView 表示同时持有Model和ViewModel和View是完全不同的。这个类使一个控制器返回单独的一个值同时包含model和view成为一种可能。

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

View对象如果是通过一个字符串形式的 view name 获取到的,则能被 ViewResolver 对象解析。或者也可以直接指定 View 对象。model 是一个 Map 类型,可以使用多个对象的键值对。

2.为什么说通过 ModelAndView 来处理SpringMVC返回值问题

事实上,返回的类型可以是很多种,可以是 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) 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.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 域中。

SpringMVC通过ModelAndView解决了handler方法返回值问题,因为 handler 方法的返回值最终都会被转换成ModelAndView对象也详细的介绍了 Model 可以作为 handler 方法的入参使用,这里所说的 Model 也不仅仅是指 Model 这个类型,也指实现了 Model 或 Map 接口的类型。也明白了 Model 中存放的什么,存放到了哪里。当然,后续还有更多关于Spring MVC返回值的难点知识在等着我们学习探究。如果你觉得自己对这些知识掌握的不够透彻,可以观看本站的Spring MVC视频课程,当然,我们也坚信这将是你学习Spring MVC框架的转折点!

0人推荐
共同学习,写下你的评论
0条评论
jessy
程序员jessy

13篇文章贡献65011字

作者相关文章更多>

推荐相关文章更多>

Java数据结构

HelloWorld10-31 08:24

浅谈MySQL中SQL优化的常用方法

军哥08-12 23:29

五分钟读懂UML类图

江湖人称小李白12-10 10:41

MyBatis开发框架的四大核心

IT逐梦者08-17 21:43

一次搞定continue,break和return

HelloWorld11-06 11:19

发评论

举报

0/150

取消