笔记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 传递模型数据到视图中的主要内容,如果未能解决你的问题,请参考以下文章