代码结构

wKioL1gn3ETBS3cNAACjyJiKF44361.jpg

域对象 spittle.java

package _5BuildingSpringwebapplications.domain;

import java.util.Date;

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;

public class Spittle {

	private final Long id;
	private final String message;
	private final Date time;
	private Double latitude;
	private Double longitude;
	

	public Spittle(String message, Date time) {
		this(message, time, null, null);
	}

	public Spittle(String message, Date time, Double longitude, Double latitude) {
		this.id = null;
		this.message = message;
		this.time = time;
		this.longitude = longitude;
		this.latitude = latitude;
	}

	public long getId() {
		return id;
	}

	public String getMessage() {
		return message;
	}

	public Date getTime() {
		return time;
	}

	public Double getLongitude() {
		return longitude;
	}

	public Double getLatitude() {
		return latitude;
	}

	@Override
	public boolean equals(Object that) {
		return EqualsBuilder.reflectionEquals(this, that, "id", "time");
	}

	@Override
	public int hashCode() {
		return HashCodeBuilder.reflectionHashCode(this, "id", "time");
	}

}

dao层SpittleRepositoryImpl.java,示例中没有没有内容,但是在源码中有实际内容,即相关的sql查询,但是本章还没有涉及到系统的后端,这样的话,只能通过mockito校验controller层,不能在浏览器中操作连接.

package _5BuildingSpringwebapplications.repository;

import java.util.List;
import org.springframework.stereotype.Repository;

import _5BuildingSpringwebapplications.domain.Spittle;

@Repository
public class SpittleRepositoryImpl implements SpittleRepository{

	@Override
	public List<Spittle> findSpittles(long max, int count) {

		return null;
	}

	@Override
	public Spittle findOne(long spittleId) {
		// TODO Auto-generated method stub
		return null;
	}

}

controller层,SpittleController.java,这里没有明确的区别controller层和service层

package _5BuildingSpringwebapplications.web;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import _5BuildingSpringwebapplications.domain.Spittle;
import _5BuildingSpringwebapplications.repository.SpittleRepository;

@Controller
@RequestMapping({ "/spittles" })
public class SpittleController {

	private static final String MAX_LONG_AS_STRING = "9223372036854775807";
	
	private SpittleRepository spittleRepository;
	

	@Autowired
	public SpittleController(SpittleRepository spittleRepository) {
		// TODO Auto-generated constructor stub
		this.spittleRepository = spittleRepository;
	}

//	@RequestMapping(method = RequestMethod.GET)
//	public String spittles(Model model) {
//		model.addAttribute("spittleList",spittleRepository.findSpittles(Long.MAX_VALUE, 20));/* 默认的属性名是spittleList */
//		return "spittles";/*这个默认的View名就是spittles*/
//	}
	
	@RequestMapping(method = RequestMethod.GET)
	public List<Spittle> spittles(@RequestParam(value = "max",defaultValue = MAX_LONG_AS_STRING) long max,
			@RequestParam(value = "count", defaultValue = "20") int count) {
		return spittleRepository.findSpittles(max, count);
	}
	
	@RequestMapping(value = "/show", method = RequestMethod.GET)
	public String showSpittle(@RequestParam("spittle_id") long spittleId, Model model) {
		model.addAttribute(spittleRepository.findOne(spittleId));
		return "spittle";
	}

	//explicit about the model key
	// @RequestMapping(method = RequestMethod.GET)
	// public String spittles(Model model) {
	// model.addAttribute("spittleList",
	// spittleRepository.findSpittles(Long.MAX_VALUE, 20));
	// return "spittles";
	// }

	//work with a non-Spring type
//	@RequestMapping(method = RequestMethod.GET)
//	public String spittles(Map model) {
//		model.put("spittleList", spittleRepository.findSpittles(Long.MAX_VALUE, 20));
//		return "spittles";
//	} 

	//total different from the others,the model key is inferred from its type (spittleList, as in the other examples
//	@RequestMapping(method=RequestMethod.GET)
//	public List<Spittle> spittles() {
//	return spittleRepository.findSpittles(Long.MAX_VALUE, 20));
//	}

	@RequestMapping(value = "/{spittleId}", method = RequestMethod.GET)
	public String spittle(@PathVariable("spittleId") long spittleId, Model model) {
		model.addAttribute(spittleRepository.findOne(spittleId));
		return "spittle";
	}
	
	
}

对contrller层的校验,主要校验了是否能够返回正确的view名等.源码中mockito的相关方法只写了名字,eclipse不能自动识别,因为方法都是静态的,如果加上类名,即能正确识别.mokito很方便,实际上在用junit运行下面的代码时,不要求service层对应的方法有实际的内容.

package _5BuildingSpringwebapplications.web.test;

import static org.hamcrest.Matchers.hasItems;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.hamcrest.Matchers;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.servlet.view.InternalResourceView;

import _5BuildingSpringwebapplications.domain.Spittle;
import _5BuildingSpringwebapplications.repository.SpittleRepository;
import _5BuildingSpringwebapplications.web.SpittleController;

public class SpittleControllerTest {
	@Test
	public void shouldShowRecentSpittles() throws Exception {
		List<Spittle> expectedSpittles = createSpittleList(20);
		SpittleRepository mockRepository = Mockito.mock(SpittleRepository.class);
		Mockito.when(mockRepository.findSpittles(Long.MAX_VALUE, 20)).thenReturn(expectedSpittles);
		SpittleController controller = new SpittleController(mockRepository);
		MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller)
				.setSingleView(new InternalResourceView("/WEB-INF/views/spittles.jsp")).build();
		mockMvc.perform(MockMvcRequestBuilders.get("/spittles"))
				.andExpect(MockMvcResultMatchers.view().name("spittles"))
				.andExpect(MockMvcResultMatchers.model().attributeExists("spittleList")).andExpect(MockMvcResultMatchers
						.model().attribute("spittleList", hasItems(expectedSpittles.toArray())));
	}

	private List<Spittle> createSpittleList(int count) {
		List<Spittle> spittles = new ArrayList<Spittle>();
		for (int i = 0; i < count; i++) {
			spittles.add(new Spittle("Spittle " + i, new Date()));
		}
		return spittles;
	}

	@Test
	public void shouldShowPagedSpittles() throws Exception {
		List<Spittle> expectedSpittles = createSpittleList(50);
		SpittleRepository mockRepository = Mockito.mock(SpittleRepository.class);
		Mockito.when(mockRepository.findSpittles(238900, 50)).thenReturn(expectedSpittles);
		SpittleController controller = new SpittleController(mockRepository);
		MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller)
				.setSingleView(new InternalResourceView("/WEB-INF/views/spittles.jsp")).build();
		mockMvc.perform(MockMvcRequestBuilders.get("/spittles?max=238900&count=50"))
				.andExpect(MockMvcResultMatchers.view().name("spittles"))
				.andExpect(MockMvcResultMatchers.model().attributeExists("spittleList")).andExpect(MockMvcResultMatchers
						.model().attribute("spittleList", Matchers.hasItems(expectedSpittles.toArray())));
	}


	@Test
	public void testSpittle() throws Exception {
		Spittle expectedSpittle = new Spittle("hello", new Date());
		SpittleRepository mockRepository = Mockito.mock(SpittleRepository.class);
		Mockito.when(mockRepository.findOne(12345)).thenReturn(expectedSpittle);

		SpittleController controller = new SpittleController(mockRepository);
		MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller).build();

		mockMvc.perform(MockMvcRequestBuilders.get("/spittles/12345"))
				.andExpect(MockMvcResultMatchers.view().name("spittle"))
				.andExpect(MockMvcResultMatchers.model().attributeExists("spittle"))
				.andExpect(MockMvcResultMatchers.model().attribute("spittle", expectedSpittle));
	}

}

junit运行结果

wKiom1gn3s2S_K2YAABJmHXustM266.jpg

本示例没有贴出SpittleRepository.java即SpittleRepositoryImpl实现的接口.其实很简单,自己实现即可.

域对象spitter.java

package _5BuildingSpringwebapplications.domain;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.hibernate.validator.constraints.Email;

public class Spitter {
	private Long id;

	@NotNull
	@Size(min = 5, max = 16)
	private String username;

	@NotNull
	@Size(min = 5, max = 25)
	private String password;

	@NotNull
	@Size(min = 2, max = 30)
	private String firstName;

	@NotNull
	@Size(min = 2, max = 30)
	private String lastName;

	@NotNull
	@Email
	private String email;

	public Spitter() {
	}

	public Spitter(String username, String password, String firstName, String lastName, String email) {
		this(null, username, password, firstName, lastName, email);
	}

	public Spitter(Long id, String username, String password, String firstName, String lastName, String email) {
		this.id = id;
		this.username = username;
		this.password = password;
		this.firstName = firstName;
		this.lastName = lastName;
		this.email = email;
	}

	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 Long getId() {
		return id;
	}

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

	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	public String getEmail() {
		return email;
	}

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

	@Override
	public boolean equals(Object that) {
		return EqualsBuilder.reflectionEquals(this, that, "firstName", "lastName", "username", "password", "email");
	}

	@Override
	public int hashCode() {
		return HashCodeBuilder.reflectionHashCode(this, "firstName", "lastName", "username", "password", "email");
	}
}

spitter对象的service层SpitterRepositoryImpl.java,与spittle的dao层一样,这里的方法都没有具体的内容,即没有与数据库的交互,这不影响对本章的理解.

package _5BuildingSpringwebapplications.repository;

import org.springframework.stereotype.Repository;

import _5BuildingSpringwebapplications.domain.Spitter;

@Repository
public class SpitterRepositoryImpl implements SpitterRepository{

	@Override
	public void save(Spitter spitter) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public Spitter findByUsername(String username) {
		// TODO Auto-generated method stub
		return null;
	}

	
}

spitter的controller层SpitterController.java

package _5BuildingSpringwebapplications.web;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import _5BuildingSpringwebapplications.domain.Spitter;
import _5BuildingSpringwebapplications.repository.SpitterRepository;

@Controller
@RequestMapping("/spitter")
public class SpitterController {

	private SpitterRepository spitterRepository;

	public SpitterController() {
		// TODO Auto-generated constructor stub
	}

	@Autowired
	public SpitterController(SpitterRepository spitterRepository) {
		this.spitterRepository = spitterRepository;
	}

	@RequestMapping(value = "/register", method = RequestMethod.GET)
	public String showRegistrationForm() {
		return "registerForm";
	}

	@RequestMapping(value = "/register", method = RequestMethod.POST)
	public String processRegistration(@Valid Spitter spitter, Errors errors) {
		if (errors.hasErrors()) {
			return "registerForm";
		}
		spitterRepository.save(spitter);
		return "redirect:/spitter/" + spitter.getUsername();
	}

	@RequestMapping(value = "/{username}", method = RequestMethod.GET)
	public String showSpitterProfile(@PathVariable String username, Model model) {
		Spitter spitter = spitterRepository.findByUsername(username);
		model.addAttribute(spitter);
		return "profile";
	}
}

代码中没有提到model层和view层.controller直接调的dao,本章的源码中也没有model层.可能因为涉及到的请求都比较简单,不需要太复杂的逻辑运行,直接由dao层返回,由view显示即可.

本章的内容除了介绍springmvc的一些知识点外,如何对controller进行单元测试也是一大亮点.