1. 程式人生 > >SpringMVC 學習(四)——處理模型資料

SpringMVC 學習(四)——處理模型資料

理模型資料

Spring MVC 提供了以下幾途徑出模型資料:

ModelAndView: 理方法返回值類 ModelAndView

, 方法體即可通過該對象新增模型資料

Map Model: 入參

org.springframework.ui.Modelorg.springframework.ui.  ModelMap java.uti.Map 理方法返回Map  中的資料會自新增到模型中。

@SessionAttributes: 將模型中的某個屬性存到

HttpSession 中,以便多個求之

可以共享個屬性

@ModelAttribute: 方法入參註解後, 入參的象 就會放到資料模型中

 

 

ModelAndView

控制器理方法的返回如果ModelAndView, 既 包含檢視資訊,也包含模型資料資訊

新增模型資料:

MoelAndView addObject(String attributeName, Object  attributeValue)

ModelAndView addAllObject(Map<String, ?> modelMap)

檢視:

void setView(View view)

void setViewName(String viewName)j

 

Map Model

Spring MVC 在內部使用了一個

org.springframework.ui.Model 介面存

模型資料

具體步驟

Spring MVC 調用方法前會建一個含的模型象作模型資料的存容器。

如果方法的入參 Map Model

Spring MVC 會將含模型的引用傳 遞給這些入參。在方法體內,開發者可以 通過這個入參訪問到模型中的所有數 據,也可以向模型中新增新的屬性資料

Map Model 示 例

 

 

@SessionAttributes

若希望在多個求之共用某個模型屬性資料可以在 控制器注一個 @SessionAttributes, Spring MVC  將在模型中對應的屬性存到 HttpSession 中。

@SessionAttributes 除了可以通屬性名指定需要放到會 中的屬性外,可以通模型屬性的指定些 模型屬性需要放到會

@SessionAttributes(types=User.class) 會將含模型中所有

User.class 的屬性新增到會中。

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

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

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

 

 

@SessionAttributes 示例

 

 

 

 

P1717. ModelAttribute註解之使用場景

 

 

 

 

@ModelAttribute

在方法定上使用 @ModelAttribute 註解Spring MVC

調用目標處理方法前,會先逐個調用在方法注了

@ModelAttribute 的方法。

在方法的入參前使用 @ModelAttribute 註解

–  可以從象中含的模型資料中象,再將求引數

定到象中,再入入參

–  將方法入參象新增到模型中

 

@SessionAttributes

org.springframework.web.HttpSessionRequiredExceptionSession attribute 'user' required - not found in session

如果在義處標注了@SessionAttributes(“xxx”)則 嘗試從會屬性,並將其賦給該入參,然後再用 求訊息填充入參象。如果在會中找不到對應的屬 性,丟擲 HttpSessionRequiredException

 

如何避免@SessionAttributes

 

程式碼示例:

user,java

package com.xuehj.springmvc.entity;

/**
 * @program: SpringMVC-1
 * @description: TODO
 * @author: Mr.Xue
 * @create: 2018-12-24 19:13
 **/
public class User {

    private Integer id;

    private String username;
    private String password;

    private String email;
    private int age;

    private Address address;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "User [id=" + id + ", username=" + username + ", password="
                + password + ", email=" + email + ", age=" + age + "]";
    }

    public User(Integer id, String username, String password, String email,
                int age) {
        super();
        this.id = id;
        this.username = username;
        this.password = password;
        this.email = email;
        this.age = age;
    }

    public User() {
    }
}

test.java

package com.xuehj.springmvc.handler;

import com.xuehj.springmvc.entity.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.Writer;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;

/**
 * @program: SpringMVC
 * @description: TODO
 * @author: Mr.Xue
 * @create: 2018-12-24 09:36
 **/
@SessionAttributes(value = {"user"}, types = {String.class})
@Controller
@RequestMapping("/view")
public class HelloWorld {

    /**
     * 目標方法的返回值可以是 ModelAndView 型別。
     * 其中可以包含檢視和模型資訊
     * SpringMVC 會把 ModelAndView 的 model 中資料放入到 request 域物件中.
     *
     * @return
     */
    @RequestMapping("/testModelAndView")
    public ModelAndView testModelAndView() {
        String viewName = "success.jsp";
        ModelAndView modelAndView = new ModelAndView(viewName);

        //新增模型資料到 ModelAndView 中.
        modelAndView.addObject("time", new Date());

        return modelAndView;
    }

    /**
     * 目標方法可以新增 Map 型別(實際上也可以是 Model 型別或 ModelMap 型別)的引數.
     *
     * @param map
     * @return
     */
    @RequestMapping("/testMap")
    public String testMap(Map<String, Object> map) {
        System.out.println(map.getClass().getName());
        map.put("names", Arrays.asList("Tom", "Jerry", "Mike"));
        return "success.jsp";
    }


    /**
     * @SessionAttributes 除了可以通過屬性名指定需要放到會話中的屬性外(實際上使用的是 value 屬性值),
     * 還可以通過模型屬性的物件型別指定哪些模型屬性需要放到會話中(實際上使用的是 types 屬性值)
     * <p>
     * 注意: 該註解只能放在類的上面. 而不能修飾放方法.
     */
    @RequestMapping("/testSessionAttributes")
    public String testSessionAttributes(Map<String, Object> map) {
        User user = new User(1, "Tom", "123456", "[email protected]", 15);
        map.put("user", user);
        map.put("school", "atguigu");
        return "success.jsp";
    }

    /**
     * 1. 有 @ModelAttribute 標記的方法, 會在每個目標方法執行之前被 SpringMVC 呼叫!
     * 2. @ModelAttribute 註解也可以來修飾目標方法 POJO 型別的入參, 其 value 屬性值有如下的作用:
     * 1). SpringMVC 會使用 value 屬性值在 implicitModel 中查詢對應的物件, 若存在則會直接傳入到目標方法的入參中.
     * 2). SpringMVC 會一 value 為 key, POJO 型別的物件為 value, 存入到 request 中.
     */
    @ModelAttribute
    public void getUser(@RequestParam(value = "id", required = false) Integer id,
                        Map<String, Object> map) {
        System.out.println("modelAttribute method");
        if (id != null) {
            //模擬從資料庫中獲取物件
            User user = new User(1, "Tom", "123456", "[email protected]", 12);
            System.out.println("從資料庫中獲取一個物件: " + user);

            map.put("user", user);
        }
    }

    /**
     * 執行流程:
     * 1. 執行 @ModelAttribute 註解修飾的方法: 從資料庫中取出物件, 把物件放入到了 Map 中. 鍵為: user
     * 2. SpringMVC 從 Map 中取出 User 物件, 並把表單的請求引數賦給該 User 物件的對應屬性.
     * 3. SpringMVC 把上述物件傳入目標方法的引數.
     * <p>
     * 注意: 在 @ModelAttribute 修飾的方法中, 放入到 Map 時的鍵需要和目標方法入參型別的第一個字母小寫的字串一致!
     * <p>
     * SpringMVC 確定目標方法 POJO 型別入參的過程
     * 1. 確定一個 key:
     * 1). 若目標方法的 POJO 型別的引數木有使用 @ModelAttribute 作為修飾, 則 key 為 POJO 類名第一個字母的小寫
     * 2). 若使用了  @ModelAttribute 來修飾, 則 key 為 @ModelAttribute 註解的 value 屬性值.
     * 2. 在 implicitModel 中查詢 key 對應的物件, 若存在, 則作為入參傳入
     * 1). 若在 @ModelAttribute 標記的方法中在 Map 中儲存過, 且 key 和 1 確定的 key 一致, 則會獲取到.
     * 3. 若 implicitModel 中不存在 key 對應的物件, 則檢查當前的 Handler 是否使用 @SessionAttributes 註解修飾,
     * 若使用了該註解, 且 @SessionAttributes 註解的 value 屬性值中包含了 key, 則會從 HttpSession 中來獲取 key 所
     * 對應的 value 值, 若存在則直接傳入到目標方法的入參中. 若不存在則將丟擲異常.
     * 4. 若 Handler 沒有標識 @SessionAttributes 註解或 @SessionAttributes 註解的 value 值中不包含 key, 則
     * 會通過反射來建立 POJO 型別的引數, 傳入為目標方法的引數
     * 5. SpringMVC 會把 key 和 POJO 型別的物件儲存到 implicitModel 中, 進而會儲存到 request 中.
     * <p>
     * 原始碼分析的流程
     * 1. 呼叫 @ModelAttribute 註解修飾的方法. 實際上把 @ModelAttribute 方法中 Map 中的資料放在了 implicitModel 中.
     * 2. 解析請求處理器的目標引數, 實際上該目標引數來自於 WebDataBinder 物件的 target 屬性
     * 1). 建立 WebDataBinder 物件:
     * ①. 確定 objectName 屬性: 若傳入的 attrName 屬性值為 "", 則 objectName 為類名第一個字母小寫.
     * *注意: attrName. 若目標方法的 POJO 屬性使用了 @ModelAttribute 來修飾, 則 attrName 值即為 @ModelAttribute
     * 的 value 屬性值
     * <p>
     * ②. 確定 target 屬性:
     * > 在 implicitModel 中查詢 attrName 對應的屬性值. 若存在, ok
     * > *若不存在: 則驗證當前 Handler 是否使用了 @SessionAttributes 進行修飾, 若使用了, 則嘗試從 Session 中
     * 獲取 attrName 所對應的屬性值. 若 session 中沒有對應的屬性值, 則丟擲了異常.
     * > 若 Handler 沒有使用 @SessionAttributes 進行修飾, 或 @SessionAttributes 中沒有使用 value 值指定的 key
     * 和 attrName 相匹配, 則通過反射建立了 POJO 物件
     * <p>
     * 2). SpringMVC 把表單的請求引數賦給了 WebDataBinder 的 target 對應的屬性.
     * 3). *SpringMVC 會把 WebDataBinder 的 attrName 和 target 給到 implicitModel.
     * 近而傳到 request 域物件中.
     * 4). 把 WebDataBinder 的 target 作為引數傳遞給目標方法的入參.
     */
    @RequestMapping("/testModelAttribute")
    public String testModelAttribute(@ModelAttribute("user") User user) {
        System.out.println("修改: " + user);
        return "success.jsp";
    }
}

sucess.jsp

<%--
  Created by IntelliJ IDEA.
  User: 薛恆傑
  Date: 2018/12/24
  Time: 9:39
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isErrorPage="true" %>
<html>
    <head>
        <title>Title</title>
    </head>
    <body>
        <h1>Hello World</h1>

        time: ${requestScope.time }
        <br><br>

        names: ${requestScope.names }
        <br><br>

        request user: ${requestScope.user }
        <br><br>

        session user: ${sessionScope.user }
        <br><br>

        request school: ${requestScope.school }
        <br><br>

        session school: ${sessionScope.school }
        <br><br>

        abc user: ${requestScope.abc }
        <br><br>

        mnxyz user: ${requestScope.mnxyz }
        <br><br>
    </body>
</html>

index.jsp

<%--
  Created by IntelliJ IDEA.
  User: 薛恆傑
  Date: 2018/12/24
  Time: 9:21
  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>
        <a href="view/testModelAndView">Test ModelAndView</a>
        <br><br>

        <a href="view/testMap">Test Map</a>
        <br><br>

        <a href="view/testSessionAttributes">Test SessionAttributes</a>
        <br><br>

        <!--
		模擬修改操作
		1. 原始資料為: 1, Tom, 123456,[email protected],12
		2. 密碼不能被修改.
		3. 表單回顯, 模擬操作直接在表單填寫對應的屬性值
	    -->
        <form action="view/testModelAttribute" method="Post">
            <input type="hidden" name="id" value="1"/>
            username: <input type="text" name="username" value="Tom"/>
            <br>
            email: <input type="text" name="email" value="[email protected]"/>
            <br>
            age: <input type="text" name="age" value="12"/>
            <br>
            <input type="submit" value="Submit"/>
        </form>
        <br><br>
    </body>
</html>