SpringMVC 处理模型数据

版权声明:最终解释权归属Hern、HernSong(hernsong)、苍鹭、www.hernsong.com所有! https://blog.csdn.net/qq_36761831/article/details/88895550

Spring MVC 提供了输出模型数据的途径

  • ModelAndView: 处理方法返回值类型为 ModelAndView 时, 方法体即可通过该对象添加模型数据
  • Map 及 Model: 入参为 org.springframework.ui.Model、org.springframework.ui. ModelMap 或 java.uti.Map 时,处理方法返回时,Map 中的数据会自动添加到模型中。
  • @SessionAttributes: 将模型中的某个属性暂存到 HttpSession 中,以便多个请求之间可以共享这个属性
  • @ModelAttribute: 方法入参标注该注解后, 入参的对象 就会放到数据模型中

ModelAndView

• 控制器处理方法的返回值如果为 ModelAndView, 则其既 包含视图信息,也包含模型数据信息。

•SpringMVC 会把 ModelAndView 的 model 中数据放入到request 域对象中。

• 添加模型数据:

  •   MoelAndView addObject(String attributeName, Object attributeValue)
  •   ModelAndView addAllObject(Map modelMap)

• 设置视图:

示例:

  •   void setView(View view)
  •   void setViewName(String viewName)

index.jsp

<%--
  Created by IntelliJ IDEA.
  User: 23369
  Date: 2019/3/24
  Time: 18:29
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=gbk" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
    <a href="TestModelAndView/modelAndView">TestModelAndView</a>
  </body>
</html>

success.jsp

<%--
  Created by IntelliJ IDEA.
  User: 23369
  Date: 2019/3/24
  Time: 18:33
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>success</h1>
time:${requestScope.time}
</body>
</html>

TestModelAndView.class

package com.helloworld;


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

import java.util.Date;

@Controller
@RequestMapping("TestModelAndView")
public class TestModelAndView {

    private String success = "success";

    @RequestMapping("modelAndView")
    public ModelAndView modelAndView(){
        ModelAndView modelAndView = new ModelAndView(success);

        modelAndView.addObject("time",new Date());

        return modelAndView;
    }
}

Map 及 Model

• Spring MVC 在内部使用了一个 org.springframework.ui.Model 接口存 储模型数据

• SpringMVC 会把 Model 的数据放入到 ModelAndView 的 model 中。

• 具体步骤

– Spring MVC 在调用方法前会创建一个隐 含的模型对象作为模型数据的存储容器。

– 如果方法的入参为 Map 或 Model 类 型,Spring MVC 会将隐含模型的引用传 递给这些入参。在方法体内,开发者可以 通过这个入参对象访问到模型中的所有数 据,也可以向模型中添加新的属性数据

示例:

index.jsp

<%--
  Created by IntelliJ IDEA.
  User: 23369
  Date: 2019/3/24
  Time: 18:29
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=gbk" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
    <a href="TestModel/testModel">TestModel&Map</a>
  </body>
</html>

success.jsp

<%--
  Created by IntelliJ IDEA.
  User: 23369
  Date: 2019/3/24
  Time: 18:33
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>success</h1>
 names:${requestScope.names}
</body>
</html>

TestModel .class

package com.helloworld;


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

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Controller
@RequestMapping("TestModel")
public class TestModel {

    private String success = "success";

    @RequestMapping("testModel")
    public String testModel(Map<Object,Object> map){

        List<Object> list = new ArrayList<>();
        list.add("Hern");
        list.add("Hern2");
        list.add("Hern3");

        map.put("names",list);

        return success;
    }
}

@SessionAttributes 

注意:@SessionAttributes注解只能在类(class)上面,不能在方法上面!!!

• 若希望在多个请求之间共用某个模型属性数据,则可以在控制器类(@Controller)上标注一个 @SessionAttributes, Spring MVC 将在模型中对应的属性暂存到 HttpSession 中。

• @SessionAttributes 除了可以通过属性名指定需要放到会话中的属性外(实际上使用的是 value 属性值),还可以通过模型属性的对象类型指定哪些模型属性需要放到会话中(实际上使用的是 types 属性值):

– @SessionAttributes(types=User.class) 会将隐含模型中所有类型 为 User.class 的属性添加到会话中。

– @SessionAttributes(value={“user1”, “user2”})

– @SessionAttributes(types={User.class, Dept.class})

– @SessionAttributes(value={“user1”, “user2”}, types={Dept.class})

示例

index.jsp

<%--
  Created by IntelliJ IDEA.
  User: 23369
  Date: 2019/3/24
  Time: 18:29
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=gbk" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
    <a href="TestSessionAttributes/testSessionAttributes">TestSessionAttributes</a>
  </body>
</html>

success.jsp

<%--
  Created by IntelliJ IDEA.
  User: 23369
  Date: 2019/3/24
  Time: 18:33
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>success</h1>
 request user:${requestScope.user}
<br>
session user:${sessionScope.user}
<br>
session school:${sessionScope.school}
<br>
request school:${requestScope.school}
</body>
</html>

TestSessionAttributes.class

package com.helloworld;

import com.user.Address;
import com.user.UserInfo;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttribute;
import org.springframework.web.bind.annotation.SessionAttributes;

import java.util.Map;

@SessionAttributes(value = {"user"}, types = {String.class})
@Controller
@RequestMapping("TestSessionAttributes")
public class TestSessionAttributes {

    private String success = "success";

    @RequestMapping("testSessionAttributes")
    public String testSessionAttributes(Map<Object, Object> map){

        String username = "Hern";
        String userpass = "123456";
        String useremail = "[email protected]";
        String userage = "23";

        String procty = "shangdopmng";
        String city = "zibo";

        Address address = new Address(procty, city);


        UserInfo userInfo = new UserInfo(username, userpass, useremail, userage,address);
        System.out.println(userInfo.toString());

        map.put("user", userInfo);
        map.put("school","Hern");

        return success;
    }
}

@SessionAttributes引发的异常:

错误信息:org.springframework.web.HttpSessionRequiredException: Session attribute 'user' required - not found in session

• 如果在处理类定义处标注了@SessionAttributes(“xxx”),则 尝试从会话中获取该属性,并将其赋给该入参,然后再用 请求消息填充该入参对象。如果在会话中找不到对应的属 性,则抛出 HttpSessionRequiredException 异常

避免@SessionAttributes引发的异常的方法:

@Controller
@RequestMapping("/user")
@SessionAttributes(“user”)
public class UserController {

    @ModelAttribute("user")
    public User getUser(){
        User user = new User();
        return user;
    }

    @RequestMapping(value = "/handle71")
    public String handle71(@ModelAttribute(“user”) User user){
         ...
    }

    @RequestMapping(value = "/handle72")
    public String handle72(ModelMap modelMap,SessionStatus sessionStatus){
         ...
    }
}

@ModelAttribute

注意:对于一个controller映射多个URL的用法来说,要谨慎使用!!!

• 在方法定义上使用 @ModelAttribute 注解:Spring MVC 在调用目标处理方法前,会先逐个调用在方法级上标注了@ModelAttribute 的方法。 运用在方法上,会在每一个@RequestMapping标注的方法前执行,如果有返回值,则自动将该返回值加入到ModelMap中。

•@ModelAttribute注解也可以来修饰目标方法POJO类型的入参,其value属性值有如下的作用

    — SpringMVC 会使用value 属性值在implicitModel中查找对应的对象,若存在则会直接传入到目标方法的入参中。

    — SpringMVC会将value为key,POJO类型的对象为value,存入到request中。

• 在方法的入参前使用 @ModelAttribute 注解,即运用在参数上,会将客户端传递过来的参数按名称注入到指定对象中,并且会将这个对象自动加入ModelMap中,便于View层使用:

– 可以从隐含对象中获取隐含的模型数据中获取对象,再将请求参数 绑定到对象中,再传入入参

– 将方法入参对象添加到模型中

使用 @ModelAttribute 注解方法的流程:(以示例中的代码为例)

1、执行@ModelAttribute 注解修饰的方法:从数据库中取出对象,把对象放入到Map中,键值为userinfoModel。

2、SpringMVC从Map中取出UserInfo对象,并把表单的请求参数赋给UserInfo对象的对应属性。

3、SpringMVC把上述对象传入目标方法的参数。

注意:在@ModelAttribute修饰的方法中,放入到Map时的键需要和目标方法入参类型的第一个字母小写的字符串一致!

@ModelAttribute 源代码分析流程

1、调用@ModelAttribute注解修饰的方法,实际上把@ModelAttribute方法中Map中的数据放在了implicitModel中。

2、解析请求处理器的目标参数,实际上该目标参数来自WebDataBinder对象的target属性:

      — 创建WebDataBinder对象:

          (1)、确定objectName属性:若传入的attrName属性值为"",则objectName为类名第一个字母小写。注意:attrName,若目标方法中POJO属性使用了@ModelAttribute来修饰,则attrName值即为@ModelAttribute的value属性值!

       (2)、确定target属性:在implicitModel中查找attrName对应的属性值,若存在则继续;若不存在,则验证当前Handler是否使用了@SessionAttributes进行修饰,若使用了,则尝试从Session中获取attrName所对应的属性值,若session中没有对应的属性值,则抛出异常。若Handler没有使用@SessionAttributes进行修饰,或@SessionAttributes中没有使用value值指定的键(key),和attrName相匹配,则通过反射创建POJO对象。

     — SpringMVC把表单的请求参数赋值给了 WebDataBinder 的 target 对应的属性。

     — SpringMVC会把 WebDataBinder 的 attrName 和 target 给到 implicitModel。近而传到 request 域对象中。

     — 把 WebDataBinder 的 target 作为参数传递给目标方法的入参。

Spring MVC 确定目标方法POJO类型入参的过程

1、确定一个键key。若目标方法的POJO类型的参数没有使用@ModelAttribute作为修饰,则key为POJO类名的第一个字母的小写;若使用了@ModelAttribute来进行修饰,则key为@ModelAttribute注解的value属性值。

2、在implicitModel中查找key对应的对象,若存在,则作为入参传入。若在@ModelAttribute标记的方法中在Map中保存过,且key 和 值确定的key一致,则会获取到。

3、若implicitModel中不存在key对应的对象,则检查当前Handler 是否使用@SessionAttributes注解修饰,若使用了该注解,且@SessionAttributes注解的value属性值中包含了key,则会从HTTPSession中来获取key所对应的value值;若存在则直接传入到目标方法的入参中;若不存在则抛出异常。

4、若Handler没有标识@SessionAttributes注解或@SessionAttributes注解的value值中不包含key,则会通过反射来创建POJO类型的参数,传入为目标方法的参数。

5、Spring MVC会把key 和POJO类型的对象保存到implicitModel中,进而会保存到request中。

示例

index.jsp

<%--
  Created by IntelliJ IDEA.
  User: 23369
  Date: 2019/3/24
  Time: 18:29
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=gbk" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
    <form action="TestModelAttribute/testUserInfo" method="post">
       <input type="text" name="username" placeholder="请输入用户名">
       <br>
      <input type="password" name="userpass" placeholder="请输入密码">
      <br>
      <input type="text" name="useremail" placeholder="请输入邮箱">
      <br>
      <input type="text" name="userage" placeholder="请输入年龄">
      <br>
      <input type="text" name="address.procity" placeholder="请输入省份">
      <br>
      <input type="text" name="address.city" placeholder="请输入城市">
      <br>
      <input type="submit" value="提交">
    </form>
  </body>
</html>

success.jsp

<%--
  Created by IntelliJ IDEA.
  User: 23369
  Date: 2019/3/24
  Time: 18:33
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>success</h1>
 request 用户信息:${requestScope.userinfo}
<br>
session 用户信息:${sessionScope.userinfo}
<br>
<br>
request 用户信息修改:${requestScope.userinfoModel}
<br>
session 用户信息修改:${sessionScope.userinfoModel}
<br>
</body>
</html>

TestModelAttribute.class 

package com.helloworld;

import com.user.Address;
import com.user.UserInfo;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

@SessionAttributes(value = {"userinfo"}, types = String.class)
@Controller
@RequestMapping("TestModelAttribute")
public class TestModelAttribute {

    private String success = "success";

    @ModelAttribute
    public void getUserInfo(@RequestParam(value = "userpass", required = false) Integer id, Map<Object, Object> map){

        System.out.println(id);
        UserInfo userInfo = new UserInfo("Hern","123","123456","35",new Address("aaa","bbb"));

        map.put("userinfoModel",userInfo);
    }

    @RequestMapping(value = "testUserInfo", method = RequestMethod.POST)
    public String testUserInfo(UserInfo userInfo,Map<Object, Object> map){
        System.out.println(userInfo);
        map.put("userinfo",userInfo);
        return success;
    }


/*
这种方法可以避免因为POJO类型对象造成的无法传参问题,可以不使用,因为SpringMVC能够自动映射
    @RequestMapping(value = "testUserInfo", method = RequestMethod.POST)
    public String testUserInfo(@ModelAttribute("userinfo") UserInfo userInfo,Map<Object, Object> map){
        System.out.println(userInfo);
        map.put("userinfo",userInfo);
        return success;
    }

*/


}

猜你喜欢

转载自blog.csdn.net/qq_36761831/article/details/88895550