Spring MVC 提供了以下几种途径输出模型数据:
代码示例中配置web.xml中DispatcherServlet以及Servlet对应的<servlet-name>-servlet.xml省略。
1.ModelAndView
处理方法返回值类型为 ModelAndView时, 方法体即可通过该对象添加模型数据
配置ModelAndView数据,注意这里RequestMapping返回的是ModeAndView对象,而不是view的名字。
package spring;
import java.util.Date;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class TestModeData {
private static final String viewName="success";
@RequestMapping("/testModeAndView")
public ModelAndView testModeAndView(){
String localViewName = this.viewName;
ModelAndView modelAndView = new ModelAndView(localViewName);
//将Model数据作为request.attribute Foward到下一个页面。
//正常作业时应该是从数据库中获取的数据,这里仅作演示
//其他方法 addAllObject, setViewName(String),setView(View)
modelAndView.addObject("time", new Date());
//注意这里不再是返回viewName,而是ModelAndView对象
return modelAndView;
}
}
2.Map 及 Model
入参为org.springframework.ui.Model、org.springframework.ui.ModelMap 或 java.uti.Map 时,处理方法返回时,Map中的数据会自动添加到模型中。
@RequestMapping("/testMap")
public String testMap(Map<String, Object> map){
//参数是Map接口,但是实际上传进来的是ExtendedModelMap类型
//也就是说参数也可以是父接口Model、父接口ModelMap、类ExtendedModeMap
//Map中的数据会自动加入到模型中,在view页面可以通过${requestScope.time }直接获取。
map.put("time", new Date());
return viewName;
}
3.@SessionAttributes
将模型中的某个属性暂存到HttpSession 中,以便多个请求之间可以共享这个属性
//SessionAttributes仅可以作用于类型,其作用是从request中选择需要的属性加入到Session
//可以用注解的默认参数value,在SessionAttributes注解中也可以把value用他的别名names替换。
//另外,用types,还可以把request中指定类型的所有属性加到Session中
//下例中会把request中的time属性加入到sessoin,所有的String属性加入到Session
@SessionAttributes(names={"time"},types={String.class})
@Controller
public class TestModeData {
private static final String viewName="success";
@RequestMapping("/testSessionAttribute")
public String testSessionAttribute(Map<String, Object> map){
//参数是Map接口,但是实际上传进来的是ExtendedModelMap类型
//也就是说参数也可以是父接口Model、父接口ModelMap、类ExtendedModeMap
//Map中的数据会自动加入到模型中,在view页面可以通过${requestScope.time }直接获取。
map.put("time", new Date());
map.put("username", "high2");
return viewName;
}
}
测试sessoin的结果,viewName为successs.jsp的代码为;
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Success2!</h1>
request time:${requestScope.time }<br>
session time:${sessionScope.time }<br>
<br>
request string:${requestScope.username }<br>
session string:${sessionScope.username }<br>
</body>
</html>
4.SpringMVC确定目标方法POJO类型入参的过程
1)确定key的方法:
a)若目标方法的POJO类型的参数没有使用@ModelAttribute作为修饰,则key为POJO类名首字母小写。
b)若使用了@ModleAttribute来修饰,则key为@ModelAttribute注解的value属性值。
2)在implicitModel中查找key对应的对象,若存在,则作为入参传入。若在@ModelAttribute标记的方法中的Map或Model保存过,且key和上面1)中确定的key一致,则获取并使用1)中创建的数据。
3)若implicitModel中不存在key对应的对象,则检查当前的hanlder中是否被@SessionAttributes注解修饰。若使用了个注解,且@SessionAttributes注解的value属性值包含了key,则会从HttpSession中来获取key的对象。若存在则直接作为目标方法的入参使用,若不存在则抛出异常。
4)若Handler没有表示@SessionAttributes注解活@SessionAttributes注解的value值不包含key则会通过反射来创建POJO类型的参数,传入目标方法作为参数。
5)SpringMVC会把key和POJO类型的对象保存到implicitModel中,进而会保存到request中。
5.@ModelAttribute
使用场景:数据表在更新的过程中,有些字段是不想更新的,而我们在做映射的时候通常是把所有的字段一起映射为一个对象。如果有些字段不更新,就会涉及到对象的某些属性不更新的问题。这些属性怎么才能优雅的做到不更新,是在网页中不提交数据,还是在创建对象时不对其赋值,这样需要把原始数据备份,在更新数据表前进行跟新,每次操作会很麻烦。这里@ModelAttribute提供了一个优雅的解决方案。
1)ModelAttribute注解的函数会在每次RequestMapping的方法执行前被调用。
2)如果参数是Map,或者Model类型或者其子类型时,SpringMVC会将该参数放进相当于Request范围内的一个变量进行维护。
ModelAttribute注解的方法由于每次都会在ReqeustMapping注解的方法前调用,利用这个特性,可以将从数据库获取的数据,
通过map.put("target", Object)的形式保存这个数据。
3)在@RequestMapping对应的方法执行时,操作具有同样键值的map对象,是和@ModelAttribute的对象等效的。
另外可以通过参数的前面显示声明 @ModelAttribute("target234")显示的将某一个变量绑定在一起。
也就是说在原始数据的基础上,如果3)不做修改的话,就可以达到保持不变的效果。
4)标有@ModelAttribute("targe2") Person person 这个值会自动匹配request范围内的target2键值的数据,如果不存在该数据,
则会进一步匹配@SessionAttributes中指定的target2键值,如果还是不存在该值时,则会抛出找不到session值target2异常。
追加实例:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h4>由于ID是固定的,所以在修改数据时想办法ID不会被修改。</h4>
<form action="/spring/testModelAttribute" method="post">
name:<input type="text" name="name" value="newName"><br>
age:<input type="text" name="age" value="20"><br>
<input type="submit" value="submit">
</form>
</body>
</html>
TestModelAttribute.java
package spring;
import java.util.Map;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;
@SessionAttributes("person3") //如果没有使用ModelAttribute("person3")不会抛出异常,若使用且person3键值没有对应的对象则抛异常
@Controller
public class TestModeData {
public TestModeData(){}
private static final String viewName="success";
@ModelAttribute
public void getPerson(@RequestParam(value="name", required=false) String name,
Map<String, Object> map){
System.out.println("ModelAttributeMethod");
if(name != null){
Person xyz = new Person(4L, "high", 20); //模拟从数据库中获取的数据
map.put("xyz", xyz);
}
}
@RequestMapping("/testModelAttribute")
public String testModelAttribute(String ptr, @ModelAttribute("xyz")Person person){
System.out.println("变更后:" + person);//若不加ModelAttribute则会自动关联类名首字母小写的键值
return viewName;
}
}
<完>