SpringMVC返回数据到视图

通过ModelAndView对象返回数据到视图

在SpringMVC中有一个ModelAndView对象,如其名,Model代表模型,View代表视图,这个名字就很好地解释了该类的作用——它用来存储模型数据以及显示该数据的视图名称。在控制器中调用完模型层处理完用户的请求后,我们可以把结果数据存储在该对象的model属性中,把要返回的视图信息存储在该对象的view属性中,然后让把ModelAndView对象返回给SpringMVC框架。框架则会通过调用Spring配置文件中定义的视图解析器,对该对象进行解析,最后把结果数据传递到指定的视图上,这样我们就可以在视图中获得结果数据并显示出来了。

Spring的配置文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>
    <context:component-scan base-package="org.zero01"/>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          p:prefix="/WEB-INF/pages/" p:suffix=".jsp"
    />

</beans>

下例将简单介绍如何使用ModelAndView来存储数据,控制器代码如下:

package org.zero01.test;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class Test {

    @RequestMapping("/test.do")
    // SpringMVC会自动把 ModelAndView 对象传递到方法参数上
    public ModelAndView testModelAndView(ModelAndView modelAndView){
        // 设置视图名称
        modelAndView.setViewName("index");
        // 添加数据
        modelAndView.addObject("name","Jon");
        modelAndView.addObject("age","15");
        modelAndView.addObject("address","USA");

        return modelAndView;
    }
}

SpringMVC最后会把ModelAndView里的数据拿出来存储到request对象中,所以在视图中我们可以通过EL表达式中直接获取数据,index.jsp内容如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Test</title>
</head>
<body>
<div>
    <p>name::
        <span>${requestScope.name}</span>
    </p>
    <p>age::
        <span>${requestScope.age}</span>
    </p>
    <p>address::
        <span>${requestScope.address}</span>
    </p>
</div>
</body>
</html>

浏览器访问结果如下:

如果不想在方法上声明ModelAndView参数,也可以自己new一个,并且可以直接在构造器中指定视图名称,示例:

package org.zero01.test;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class Test {

    @RequestMapping("/test.do")
    public ModelAndView testModelAndView(){
        // 构造器中可以设置视图名称
        ModelAndView modelAndView = new ModelAndView("index");

        // 添加数据
        modelAndView.addObject("name","Jon");
        modelAndView.addObject("age","15");
        modelAndView.addObject("address","USA");

        return modelAndView;
    }
}

以上只是使用到了其中一个构造器,ModelAndView总共提供了7个构造器,这些多样的构造器让ModelAndView使用起来更便利。

例如,如果当我们只需要返回一个模型数据时,可以使用以下这个构造器:

public class ModelAndView {
    ...
    public ModelAndView(String viewName, String modelName, Object modelObject) {
        this.view = viewName;
        this.addObject(modelName, modelObject);
    }
    ...
}

示例:

package org.zero01.test;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class Test {

    @RequestMapping("/test.do")
    public ModelAndView testModelAndView() {
        return new ModelAndView("index","name","Jon");
    }
}

如果模型层处理完数据之后,返回的是一个Map的实现类对象,例如HashMap集合等,就可以使用以下这个构造器:

public class ModelAndView {
    ...
    public ModelAndView(String viewName, Map<String, ?> model) {
        this.view = viewName;
        if (model != null) {
            this.getModelMap().addAllAttributes(model);
        }
    }
    ...
}

示例:

package org.zero01.test;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import java.util.Map;

@Controller
public class Test {

    @RequestMapping("/test.do")
    public ModelAndView testModelAndView() {
        // 模型层返回了一个Map的实现类对象
        Map<String, Object> dataMap = new TestModule().getModuleData();

        return new ModelAndView("index", dataMap);
    }
}

通过Model返回数据到视图

除了以上介绍的ModelAndView可以返回数据到视图之外,SpringMVC中的Model也可以返回数据到视图。虽然两者都可以完成返回数据到视图的任务,但是它们区别挺大的,ModelAndView是一个实体类,而Model则是一个接口,Model没有指定视图的功能,也就是不能像ModelAndView那样指定视图名称。

而且执行到AnnotationMethodHandlerAdapter类中的invokeHandlerMethod方法时,Model中的数据最终还是会被存储到ModelAndView里。而作为存储模型数据以及视图名称的ModelAndView对象会在DispatcherServlet中被取出,然后DispatcherServlet会先把模型数据存储在request对象中,接着通过视图解析器转发到具体的视图上。

虽然Model是个接口,不过我们并不需要去实现Model接口,只需要在方法参数上进行声明,SpringMVC就会自动帮我们把Model对象传递过来,然后调用相应的方法存储数据即可。

代码示例:

package org.zero01.test;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class Test {

    @RequestMapping("/test.do")
    public String testModel(Model model) {
        model.addAttribute("name","Jon");
        model.addAttribute("age","15");
        model.addAttribute("address","USA");

        return "index";
    }
}

我们来看一下详细的执行过程,看看模型数据最后是否真的会被存储在request对象中。在以上代码中的 return "index"; 那一行打个断点,然后通过debug运行。如下,我们可以看到,在DispatcherServlet的doDispatch方法中视图名称以及模型数据是存储在ModelAndView对象中的,而不是Model中:

而ModelAndView对象中的模型数据则会被存储在HttpServletRequest对象中,所以我们才可以在视图上直接获取结果数据。这一点我们也可以通过debug来看到:

1.首先ModelAndView对象被拿出来之后,就会调用processDispatchResult方法,将ModelAndView对象传递到该方法中进行处理:

2.在processDispatchResult方法中,如果ModelAndView对象不为空的话,就会调用render方法,并把ModelAndView对象传递过去:

3.在render方法中,会把ModelAndView对象中的模型数据拿出来,传递到View对象中的render方法中(这个View的实现类是AbstractView):

4.在view对象中的render方法中,会把模型数据传递到createMergedOutputModel方法中进行合并:

5.在createMergedOutputModel方法中会把几个数据合并到一个集合里,但是这里除了model之外其他都为空,所以只合并了model数据:

在控制台中可以看到mergedModel对象里的数据如下:

6.得到mergedModel对象后,继续往下执行,接着就会调用renderMergedOutputModel方法,把mergedModel、request以及response对象都传递过去:

7.但是renderMergedOutputModel是一个抽象方法,所以该方法的调用被传递到了它的一个子类中(该子类是InternalResourceView),这个子类实现的renderMergedOutputModel方法中调用了exposeModelAsRequestAttributes方法并把模型数据和request对象传递了过去:

8.而exposeModelAsRequestAttributes方法没有被子类重写,所以调用的是父类的,也就是AbstractView类的,所以调用被传递到了AbstractView类的exposeModelAsRequestAttributes方法中。就是在这个方法中,模型数据被一个一个的放入到了HttpServletRequest对象中:

我们可以来看看将模型数据添加到request对象中的具体过程:
第一个数据:

控制台:

第二个数据:

控制台:

第三个数据:

控制台:

以上的一系列复杂的流程走完之后,我们在视图中,才可以直接使用EL表达式进行拿值:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Test</title>
</head>
<body>
<div>
    <p>name::
        <span>${requestScope.name}</span>
    </p>
    <p>age::
        <span>${requestScope.age}</span>
    </p>
    <p>address::
        <span>${requestScope.address}</span>
    </p>
</div>
</body>
</html>

浏览器访问结果:


通过Map返回数据到视图

使用Map返回数据与使用Model类似,也是只需要在方法上声明Map参数,然后添加数据即可。SpringMVC会自动把对象传递进来,而且返回的数据也是一样会存储到request对象中,示例:

package org.zero01.test;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.Map;

@Controller
public class Test {

    @RequestMapping("/test.do")
    public String testMap(Map map) {
        map.put("name","Jon");
        map.put("age","15");
        map.put("address","USA");

        return "index";
    }
}

jsp代码和之前一样,略。浏览器访问结果如下:


@SessionAttributes注解

从以上的实验中,我们可以得知,默认情况下SpringMVC会将模型中的数据存储到request对象中。而request对象里存储的数据是一次性的,当一个请求结束后,数据就失效了,如果要跨页面使用,那么就需要使用到session了。@SessionAttributes注解就是用来将模型中的数据存储一份到session对象中,这个注解是写在类上的。

这个注解中有两个属性:names和types,names属性用于指定哪些名称的数据需要存储到session对象中,如下示例:

package org.zero01.test;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttributes;

@SessionAttributes(names = {"name", "age", "address"})
@Controller
public class Test {

    @RequestMapping("/test.do")
    public String testModel(Model model) {
        model.addAttribute("name", "Max");
        model.addAttribute("age", "20");
        model.addAttribute("address", "北京");

        return "index";
    }
}

index.jsp内容如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Test</title>
</head>
<body>
<div>
    <p>request_name::
        <span>${requestScope.name}</span>
    </p>
    <p>request_age::
        <span>${requestScope.age}</span>
    </p>
    <p>request_address::
        <span>${requestScope.address}</span>
    </p>
    <hr>
    <p>session_name::
        <span>${sessionScope.name}</span>
    </p>
    <p>session_age::
        <span>${sessionScope.age}</span>
    </p>
    <p>session_address::
        <span>${sessionScope.address}</span>
    </p>
</div>
</body>
</html>

浏览器访问结果如下:

types属性则是指定哪些类型的数据需要存储到session对象中,如下示例:

package org.zero01.test;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttributes;

// 只要是Student类型的数据就存储一份到session中
@SessionAttributes(types = Student.class)
@Controller
public class Test {

    @RequestMapping("/test.do")
    public String testModel(Model model) {
        Student student = new Student();
        student.setSname("Max");
        student.setAge(20);
        student.setAddress("北京");

        model.addAttribute("student", student);

        return "index";
    }
}

index.jsp内容如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Test</title>
</head>
<body>
<div>
    <p>request_name::
        <span>${requestScope.student.sname}</span>
    </p>
    <p>request_age::
        <span>${requestScope.student.age}</span>
    </p>
    <p>request_address::
        <span>${requestScope.student.address}</span>
    </p>
    <hr>
    <p>session_name::
        <span>${sessionScope.student.sname}</span>
    </p>
    <p>session_age::
        <span>${sessionScope.student.age}</span>
    </p>
    <p>session_address::
        <span>${sessionScope.student.address}</span>
    </p>
</div>
</body>
</html>

浏览器访问结果和之前一样,略。


@SessionAttribute注解

这个@SessionAttribute注解与上面介绍的@SessionAttributes注解名字相似但作用相反,它用于在session对象中取值,并且是写在方法参数上的,如下示例:

package org.zero01.test;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttribute;
import org.springframework.web.bind.annotation.SessionAttributes;

@SessionAttributes(types = Student.class)
@Controller
public class Test {

    @RequestMapping("/test.do")
    public String testModel(Model model) {
        Student student = new Student();
        student.setSname("Max");
        student.setAge(20);
        student.setAddress("北京");

        model.addAttribute("student", student);

        return "redirect:/test2.do";
    }

    @RequestMapping("/test2.do")
    public String testModel2(@SessionAttribute Student student) {

        System.out.println(student.getSname());
        System.out.println(student.getAge());
        System.out.println(student.getAddress());

        return "index";
    }
}

先访问/test.do,控制台打印结果如下:

Max
20
北京

@RequestAttribute注解

@RequestAttribute注解使用在方法的参数上,该注解可以从request对象中拿取预先存在的数据,然后绑定到配置该注解的参数上。

我们需要一个过滤器事先在request对象中存储一些数据,过滤器代码如下:

package org.zero01.test;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter("/test.do")
public class TestFilter implements Filter {
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        request.setAttribute("name", "zero");
        request.setAttribute("age", 15);
        chain.doFilter(request, response);
    }

    public void destroy() {

    }
}

控制器代码如下:

package org.zero01.test;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class Test {

    @RequestMapping("/test.do")
    public String testModel(
        @RequestAttribute("name") String name,
        @RequestAttribute("age") int age
    ) {
        System.out.println("name is: " + name);
        System.out.println("age is: " + age);

        return "index";
    }
}

控制台打印结果如下:

name is: zero
age is: 15

注意:这个注解在Spring MVC5版本以上才支持,5以下的版本是不支持的,例如4版本虽然也有这个注解,但却是无效的,无法获取到request对象中的数据。


@ModelAttribute注解

这个@ModelAttribute注解可以写在方法上或参数上,当该注解写在方法上时,那么配置了该注解的方法就会比配置@RequestMapping注解的方法要先执行。所以我们通过这个注解的特性可以事前配置一些公共的数据,或补全一些数据参数什么的。如果该注解是写在方法参数上,则是从Model对象中取出预先存在的数据绑定对应的参数上。示例:

package org.zero01.test;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;

@Controller
public class Test {

    @ModelAttribute
    public void beforeTestModel(HttpServletRequest request, Model model) {
        System.out.println("beforeTestModel方法执行了...");
        request.setAttribute("name", "zero");
        model.addAttribute("age", 15);
    }

    @RequestMapping("/test.do")
    public String testModel(@RequestAttribute("name") String name, @ModelAttribute("age") String age) {
        System.out.println("testModel方法执行了...");
        System.out.println("reques --- name is: " + name);
        System.out.println("model --- age is: " + age);

        return "index";
    }
}

控制台打印结果如下:

beforeTestModel方法执行了...
testModel方法执行了...
reques --- name is: zero
model --- age is: 15

如上,从控制台打印结果的结果,可以看到,@ModelAttribute注解配置的方法的确是先执行的。如果存在多个@ModelAttribute注解配置的方法,则是会按从上至下的顺序进行执行。

原文地址:http://blog.51cto.com/zero01/2088722

时间: 2024-10-03 16:51:26

SpringMVC返回数据到视图的相关文章

SpringMVC返回数据给jsp页面(EL表达式取值)

第一种方式(通过request域) 通过request域返回字符串“resok”到success.jsp页面. @RequestMapping(value = "test") public String test( HttpServletRequest request) { request.setAttribute("res", "resok"); return "/pages/front/success.jsp"; } su

SpringMVC返回JSON数据以及文件上传、过滤静态资源

返回JSON数据 在如今前后端分离的趋势下,后端基本不需要再去关心前端页面的事情,只需要把数据处理好并通过相应的接口返回数据给前端即可.在SpringMVC中,我们可以通过@ResponseBody注解来返回JSON数据或者是XML数据. 这个注解的作用是将控制器方法返回的对象通过适当的转换器转换为指定的格式之后,写入到response对象的body区,也就是HTTP响应的内容体,一般我们都是用来返回JSON数据,因为默认是按JSON格式进行转换的. 需要注意的是,在使用此注解之后不会再走视图解

SpringMVC返回json数据的三种方式

SpringMVC返回json数据的三种方式:http://blog.csdn.net/shan9liang/article/details/42181345 上述第三种方法:可能会出现这个jar包没有的情况,引入即可,下面pom引入即可 java.lang.NoClassDefFoundError: com/fasterxml/jackson/core/JsonProcessingException

【Spring学习笔记-MVC-3.1】SpringMVC返回Json数据-方式1-扩展

<Spring学习笔记-MVC>系列文章,讲解返回json数据的文章共有3篇,分别为: [Spring学习笔记-MVC-3]SpringMVC返回Json数据-方式1:http://www.cnblogs.com/ssslinppp/p/4528892.html [Spring学习笔记-MVC-4]返回Json数据-方式2:http://www.cnblogs.com/ssslinppp/p/4530002.html [Spring学习笔记-MVC-3.1]SpringMVC返回Json数据-

springmvc返回json数据的工具类

在ssm框架下,MVC向前端返回数据的json工具类代码如下: public class JsonResult<T> { public static final int SUCCESS=0; public static final int ERROR=1; private int state; private T data; private String message; public JsonResult(int state,Throwable e){ this.state=state; t

六、Spring MVC之返回数据

前面几篇文章重点说明了一下怎么从前台传递参数到目标方法,还没有说怎么把结果返回给前台.本篇来讲述一下spring mvc怎么返回结果数据. 从使用形式上看,有以下几种方式:ModelAndView.Map.Model,这几种方式都可以返回数据到前台,本质上来看都是通过map的形式返回数据的.本系列文章着重于使用,深层次的原因,感兴趣的同志可以去调适源代码,这里只是结合使用方式,做简单的样例和分析. 1.ModelAndView的使用 /**  * 方法的返回值可以是ModelAndView类型,

SpringMVC-------2.接受参数,保存数据和返回数据

1.springmvc接受参数 1.1直接把表单的参数写在Controller相应的方法的形参中  1.2 通过HttpServletRequest接收 1.3通过一个bean来接收,post方式和get方式都可以. 创建user实体类 1.4用注解@RequestParam绑定请求参数到方法入参 当表单元素的name属性和方法参数名不同时,通过@RequestParam来绑定参数 1.5当接收的参数是时间类型的参数 1.5.1接收单个时间参数,在controller中添加initBinder注

springmvc 返回xml

需求: 1.springmvc返回xml: 技术及环境: Spring 4.3.1.RELEASE JDK 1.8 IDEA 15.0.6 Maven 3 实现: spirngxml的配置主要如下: 添加项目依赖: <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.3.1.RELEASE

springMVC返回map和返回json的例子

spring mvc 支持如下的返回方式:ModelAndView, Model, ModelMap, Map,View, String, void. Map   @RequestMapping("/demo2/show") public Map<String, String> getMap() { Map<String, String> map = new HashMap<String, String>(); map.put("key1&