Spring in Action——控制器(暫時)
Spittr應用簡介 我們這裡要構建一個簡單的微博應用,Spittr有兩個基本的領域概念: Spitter(應用的使用者)和Spittle(使用者釋出的簡單短狀態更新)。 在本章,我們會構建應用的Web層,建立展現Spittle的控制器以及處理使用者註冊成為Spitter的表單。
編寫基本的控制器
在SpringMVC中,控制器只是添加了@RequestMapping註解的類,這個註解聲明瞭他們所要處理的請求。 開始的時候,我們儘可能簡單,假設控制器類要處理對"/"的請求,並渲染應用的首頁。
package spittr.web;
import org.springframework.stereotype. Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
@Controller//宣告為一個控制器
public class HomeController {
@RequestMapping(value = "/", method = GET)//處理對"/"的get請求
public String home(){
return "home" ;//檢視名為home
}
}
@Controller
註解是宣告控制器的,但實際上這個註解對SpringMVC本身的影響並不大。
HomeController是一個構造器的註解,它基於@Component註解。在這裡,它的目的就是輔助實現元件掃描。因為HomeController帶有@Controller
註解,因此,元件掃描器會找到HomeController,並將其宣告為Spring應用上下文的一個bean。
所以實際上可以讓HomeController使用@Component
註解,實現效果一樣,但是表述行差。
它的方法home(),帶有RequestMapping註解,當它收到“/”的HTTP GET請求,就會呼叫這個方法。它返回一個簡單的String,這個String會被解讀為要渲染的檢視名稱。(/WEB-INF/views/home.jsp)
測試控制器
我們可以採用自動化測試,不需要構建並部署應用:
package spittr.web;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class HomeControllerTest {
@Test
public void home() throws Exception {
HomeController controller=new HomeController();
assertEquals("home",controller.home());
}
}
不過從Spring3.2開始,我們可以按照控制器的方式來測試Spring MVC中的控制器了,而不僅僅是作為POJO進行測試。Spring現在包含了一種mock Spring MVC並針對控制器執行HTTP請求的機制。這樣的話,在測試控制器的時候,級沒有必要再啟動Web伺服器和Web瀏覽器了: 為了闡述如何測試Spring MVC的控制器,我們重寫HomeControllerTest並使用Spring MVC中的新的測試特性。
package spittr.web;
import org.junit.Test;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*;
public class HomeControllerTest {
@Test
public void home() throws Exception {
HomeController controller=new HomeController();
MockMvc mockMvc= standaloneSetup(controller).build();
mockMvc.perform(get("/"))
.andExpect(view().name("home"));
}
}
用這個的好處在於,我們並沒有直接呼叫home(),而是發起了對“/”的get請求,並斷言檢視的名稱為home。
定義類級別的請求處理
現在已經為HomeController編寫了測試,那麼我們可以做一些重構,並通過測試來保證對功能不會有什麼破壞。 我們第一件事就可以拆分@RequestMapping,並將其的路徑對映放到類級別上:
package spittr.web;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
@Controller//宣告為一個控制器
@RequestMapping(value = {"/","homepage"})//對映到/或者/homepage
public class HomeController {
@RequestMapping(method = GET)//處理對"/"的get請求
public String home(){
return "home";//檢視名為home
}
}
傳遞模型資料到檢視中
到目前編寫的控制器來說,HomePageController已經是一個不錯的樣例了。但是大多數的控制器並不是這麼簡單。在Spittr應用中,我們需要一個頁面展現最近提交的spittle列表。因此我們需要一個新的方法來處理這個頁面。 首先需要一定一個數據訪問的Respository。為了實現解耦合以及避免陷入資料庫訪問的細節中,我們將Respository定義為一個介面,並在以後實現它。此時:
public interface SpittleRepository {
List<Spittle> findSpittles(long max, int count);
}
我們這裡使用Spring的MockMvc來斷言新的處理器方法中你所期望的行為:
public class HomeControllerTest {
@Test
public void home() throws Exception {
HomeController controller=new HomeController();
MockMvc mockMvc= standaloneSetup(controller).build();
mockMvc.perform(get("/"))
.andExpect(view().name("home"));
}
@Test
public void shouldShowRecentSpittles() throws Exception {
List<Spittle> expectedSpittles = createSpittleList(20);
SpittleRepository mockRepository = mock(SpittleRepository.class);
when(mockRepository.findSpittles(Long.MAX_VALUE, 20))
.thenReturn(expectedSpittles);
SpittleController controller = new SpittleController(mockRepository);
MockMvc mockMvc = standaloneSetup(controller)
.setSingleView(new InternalResourceView("/WEB-INF/views/spittles.jsp"))
.build();
mockMvc.perform(get("/spittles"))
.andExpect(view().name("spittles"))
.andExpect(model().attributeExists("spittleList"))
.andExpect(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;
}
}
這個測試過程因為我們重點不是這裡,所以暫時跳過。