笔记25 传递模型数据到视图中

Posted 飘来荡去

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了笔记25 传递模型数据到视图中相关的知识,希望对你有一定的参考价值。

在 Spittr应用中,我们需要有一个页面展现最近提交的Spittle列表。因此,我们需要一个新的方法来处理这个页面。

 

1.首先,需要定义一个数据访问的Repository。为了实现解耦以及避免 陷入数据库访问的细节之中,我们将Repository定义为一个接口,并在稍后实现它我们只需要一个能够获取Spittle 列表的Repository,如下所示的SpittleRepository.java

findSpittles()方法接受两个参数。其中max参数代表所返回的 Spittle中,Spittle ID属性的最大值,而count参数表明要返回多少个Spittle对象,调用时以findSpittles(Long.MAX_VALUE,20)这样的方式。

 1 package spittr.data;
 2 
 3 import java.util.List;
 4 
 5 import org.springframework.stereotype.Component;
 6 
 7 import spittr.spittle.Spittle;
 8 
 9 @Component
10 public interface SpittleRepository {
11     List<Spittle> findSpittles(long max, int count);
12 
13 }

2.然后创建它的实现类JdbcSpittleRepository.java用来访问数据库,然后读取数据,但是目前还不需要对数据库进行操作,所以需要自己做一下假数据。

 1 package spittr.data;
 2 
 3 import java.util.ArrayList;
 4 import java.util.Date;
 5 import java.util.List;
 6 
 7 import spittr.spittle.Spittle;
 8 
 9 public class JdbcSpittleRepository implements SpittleRepository {
10 
11     private List<Spittle> createSpittleList(int count) {
12         List<Spittle> spittles = new ArrayList<Spittle>();
13         for (int i = 0; i < count; i++) {
14             spittles.add(new Spittle("Spittle" + i, new Date()));
15         }
16         return spittles;
17     }
18 
19     @Override
20     public List<Spittle> findSpittles(long max, int count) {
21         // TODO Auto-generated method stub
22 
23         return createSpittleList(count);
24     }
25 
26 }

有一个createSpittleList方法,根据传入的数值,创建相应条目的数据。然后由findSpittles进行返回。

3.创建Spittle类,包含消息内容、时间戳和位置信息

 1 package spittr.spittle;
 2 
 3 import java.util.Date;
 4 
 5 import org.apache.commons.lang3.builder.EqualsBuilder;
 6 import org.apache.commons.lang3.builder.HashCodeBuilder;
 7 
 8 public class Spittle {
 9     private final Long id;
10     private final String message;
11     private final Date time;
12     private Double latitude;
13     private Double longitude;
14 
15     public Spittle(String message, Date time) {
16         this(message, time, null, null);
17     }
18 
19     public Spittle(String message, Date time, Double latitude, Double longitude) {
20         this.id = null;
21         this.message = message;
22         this.time = time;
23         this.latitude = latitude;
24         this.longitude = longitude;
25     }
26 
27     public long getId() {
28         return id;
29     }
30 
31     public String getMessage() {
32         return message;
33     }
34 
35     public Date getTime() {
36         return time;
37     }
38 
39     public Double getLongitude() {
40         return longitude;
41     }
42 
43     public Double getLatitude() {
44         return latitude;
45     }
46 
47     @Override
48     public boolean equals(Object that) {
49         return EqualsBuilder.reflectionEquals(this, that, "id", "time");
50     }
51 
52     @Override
53     public int hashCode() {
54         return HashCodeBuilder.reflectionHashCode(this, "id", "time");
55     }
56 }

4.创建控制器,SpittleController.java

 1 package spittr.web;
 2 
 3 import java.util.List;
 4 
 5 import org.springframework.beans.factory.annotation.Autowired;
 6 import org.springframework.stereotype.Controller;
 7 import org.springframework.ui.Model;
 8 import org.springframework.web.bind.annotation.RequestMapping;
 9 import org.springframework.web.bind.annotation.RequestMethod;
10 
11 import spittr.data.JdbcSpitterRepository;
12 import spittr.data.SpittleRepository;
13 import spittr.spittle.Spittle;
14 
15 @Controller
16 
17 public class SpittleController {
18     private SpittleRepository spittleRepository;
19 
20     @Autowired
21     public SpittleController(SpittleRepository spittleRepository) { // 注入SpittleRepository
22         this.spittleRepository = spittleRepository;
23     }
24 
25     @RequestMapping(value = "spittles", method = RequestMethod.GET)
26     public String spittles(Model model) {
27         // 将spittle添加到模型中
28         SpittleRepository spittleRepository = new JdbcSpittleRepository();
29         List<Spittle> data = spittleRepository.findSpittles(Long.MAX_VALUE, 20);
30         model.addAttribute(data);
31         return "spittles"; // 返回视图名
32     }
33 
34 }

到SpittleController有一个构造器,这个构造器使 用了@Autowired注解,用来注入SpittleRepository。这 个SpittleRepository随后又用在spittles()方法中,用来获 取最新的spittle列表。在spittles()方法中给定了一个Model作为参数。这样,spittles()方法就能将Repository中获取到的 Spittle列表填充到模型中。Model实际上就是一个Map(也就是 key-value对的集合),它会传递给视图,这样数据就能渲染到客户端 了。当调用addAttribute()方法并且不指定key的时候,那么key会 根据值的对象类型推断确定。在本例中,因为它是一 个List<Spittle>,因此,键将会推断为spittleList。spittles()方法所做的最后一件事是返回spittles作为视图的名字,这个视图会渲染模型。也可以显式声明模型的key,如下代码:

1     public String spittles(Model model) {
2         // 将spittle添加到模型中
3         SpittleRepository spittleRepository = new JdbcSpittleRepository();
4         List<Spittle> data = spittleRepository.findSpittles(Long.MAX_VALUE, 20);
5         model.addAttribute("spittleList", data);
6         return "spittles"; // 返回视图名
7     }

也可以用java.util.Map来代替Model。

1 @RequestMapping(value = "spittles", method = RequestMethod.GET)
2     public String spittles(Map<String, List<Spittle>> model) {
3         SpittleRepository spittleRepository = new JdbcSpittleRepository();
4         List<Spittle> data = spittleRepository.findSpittles(Long.MAX_VALUE, 20);
5         model.put("spittles", data);
6         return "spittles";
7     }

不管你选择哪种方式来编写spittles()方法,所达成的结果都是相同的。模型中会存储一个Spittle列表,key为spittleList,然 后这个列表会发送到名为spittles的视图中。按照我们配 置InternalResourceViewResolver的方式,视图的JSP将会 是“/WEB-INF/views/spittles.jsp”。 

5.创建模型视图spittles.jsp

 1 <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
 2 <%@ taglib prefix="c"  uri="http://java.sun.com/jsp/jstl/core"%>
 3 <%@ taglib prefix="s" uri="http://www.springframework.org/tags"%>
 4 <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
 5 
 6 
 7 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
 8 <html>
 9   <head>
10     <title>Spitter</title>
11     <link rel="stylesheet" type="text/css" href="<c:url value="/resources/style.css" />" >
12   </head>
13   <body>
14     <div class="spittleForm">
15       <h1>Spit it out...</h1>
16       <form method="POST" name="spittleForm">
17         <input type="hidden" name="latitude">
18         <input type="hidden" name="longitude">
19         <textarea name="message" cols="80" rows="5"></textarea><br/>
20         <input type="submit" value="Add" />
21       </form>
22     </div>
23     <div class="listTitle">
24       <h1>Recent Spittles</h1>
25       <ul class="spittleList">
26         <c:forEach items="${spittleList}" var="spittle" >
27           <li id="spittle_<c:out value="spittle.id"/>">
28             <div class="spittleMessage"><c:out value="${spittle.message}" /></div>
29             <div>
30               <span class="spittleTime"><c:out value="${spittle.time}" /></span>
31               <span class="spittleLocation">(<c:out value="${spittle.latitude}" />, <c:out value="${spittle.longitude}" />)</span>
32             </div>
33           </li>
34         </c:forEach>
35       </ul>
36 
37     </div>
38   </body>
39 </html>

6.测试

单元测试:

单元测试类SpittleControllerTest.java

 1 package spittr.test;
 2 
 3 import static org.hamcrest.CoreMatchers.hasItems;
 4 import static org.mockito.Mockito.mock;
 5 import static org.mockito.Mockito.when;
 6 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
 7 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
 8 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
 9 
10 import java.util.ArrayList;
11 import java.util.Date;
12 import java.util.List;
13 
14 import org.junit.Test;
15 import org.springframework.test.web.servlet.MockMvc;
16 import org.springframework.test.web.servlet.setup.MockMvcBuilders;
17 import org.springframework.web.servlet.view.InternalResourceView;
18 
19 import spittr.data.SpittleRepository;
20 import spittr.spittle.Spittle;
21 import spittr.web.SpittleController;
22 
23 public class SpittleControllerTest {
24 
25     @Test
26     public void shouldShowRecentSpittles() throws Exception {
27         List<Spittle> expectedSpittles = createSpittleList(20);
28         SpittleRepository mockRepository = mock(SpittleRepository.class);
29         when(mockRepository.findSpittles(Long.MAX_VALUE, 20)).thenReturn(expectedSpittles);
30 
31         SpittleController controller = new SpittleController(mockRepository);
32 
33         MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller)
34                 .setSingleView(new InternalResourceView("/WEB-INF/views/spittles.jsp")).build();
35         mockMvc.perform(get("/spittles")).andExpect(view().name("spittles"))
36                 .andExpect(model().attributeExists("spittleList"))
37                 .andExpect(model().attribute("spittleList", hasItems(expectedSpittles.toArray())));
38     }
39 
40     private List<Spittle> createSpittleList(int count) {
41         List<Spittle> spittles = new ArrayList<Spittle>();
42         for (int i = 0; i < count; i++) {
43             spittles.add(new Spittle("Spittle" + i, new Date()));
44         }
45         return spittles;
46     }
47 }

这个测试首先会创建SpittleRepository接口的mock实现,这个 实现会从它的findSpittles()方法中返回20个Spittle对象。然后,它将这个Repository注入到一个新的SpittleController 实例中,然后创建MockMvc并使用这个控制器。

与HomeController不同,这个测试在MockMvc构 造器上调用了setSingleView()。这样的话,mock框架就不用解析 控制器中的视图名了。在很多场景中,其实没有必要这样做。但是对 于这个控制器方法,视图名与请求路径是非常相似的,这样按照默认 的视图解析规则时,MockMvc就会发生失败,因为无法区分视图路 径和控制器的路径。

在这个测试中,构建InternalResourceView 时所设置的实际路径是无关紧要的,但我们将其设置为 与InternalResourceViewResolver配置一致。这个测试对“/spittles”发起GET请求,然后断言视图的名称为spittles并 且模型中包含名为spittleList的属性,在spittleList中包含 预期的内容。 

服务器上测试:

结果

7.配置文件RootConfig.java、SpittrWebAppInitializer.java、WebConfig.java参照笔记23

以上是关于笔记25 传递模型数据到视图中的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Fragment 之间传递值

如何将图像视图从改造加载的片段传递给子片段?

如何将值从一个片段的回收器视图项传递到另一个片段

将数据从 RecyclerView.Adapter 传递到片段 onClick

在片段之间传递数据

如何在每个控制器中不重复代码的情况下将模型传递给我的主视图模板?