【Spring實戰(zhàn)——構(gòu)建Spring Web應用程序】1.7 編寫基本的控制器

  • 在Spring MVC中正蛙,控制器只是方法上添加了@RequestMapping注解 的類督弓,這個注解聲明了它們所要處理的請求。

  • 假設控制器類要處理對“/”的請求乒验, 并渲染應用的首頁愚隧。程序清單5.3所示的HomeController可能是最 簡單的Spring MVC控制器類了。
    程序清單5.3 HomeController:超級簡單的控制器

    * package com.spring.mvc.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    import static org.springframework.web.bind.annotation.RequestMethod.GET;
    
    /**
    
     * 聲明一個控制器
     * @author huyingqi
       */
       @Controller
       public class HomeController {
       @RequestMapping(value = "/",method = GET )
       public String home(){
           //視圖名為home
           return "home";  
       }
    
    }
    
  • 在上述內(nèi)容中锻全,我們可以注意到HomeController帶有@Controller注解奸攻。

    • 這個注解用于聲明控制器,但它對Spring MVC本身的影響并不大虱痕。
  • HomeController是一個構(gòu)造型注解睹耐,基于@Component注解。

    • 它的目的是輔助實現(xiàn)組件掃描部翘。
    • 由于HomeController帶有@Controller注解硝训,組件掃描器會自動找到它,并將其聲明為Spring應用上下文中的一個bean新思。
  • 實際上窖梁,你也可以讓HomeController帶有@Component注解,它的效果是一樣的夹囚。但在表意性上可能會有所差別纵刘,無法確定HomeController是什么類型的組件。

  • HomeController中唯一的方法是home()方法荸哟,它帶有@RequestMapping注解假哎。

    • value屬性
      • 指定了該方法要處理的請求路徑,
    • method屬性
      • 細化了它所處理的HTTP方法鞍历。
    • 在這個例子中舵抹,當收到對"/"的HTTP GET請求時,將調(diào)用home()方法劣砍。
  • 在home()方法中惧蛹,并沒有做太多的事情,它只是返回一個String類型的"home"刑枝。這個字符串將被Spring MVC解釋為要渲染的視圖名稱香嗓。DispatcherServlet會要求視圖解析器將這個邏輯名稱解析為實際的視圖。
    鑒于我們配置InternalResourceViewResolver的方式装畅,視圖 名“home”將會解析為“/WEB-INF/views/home.jsp”路徑的JSP 】坑椋現(xiàn)在, 我們會讓Spittr應用的首頁相當簡單洁灵,如下所示饱岸。
    程序清單5.4 Spittr應用的首頁掺出,定義為一個簡單的JSP
    [圖片]
    這個JSP并沒有太多需要注意的地方。它只是歡迎應用的用戶苫费,并提 供了兩個鏈接:一個是查看Spittle列表汤锨,另一個是在應用中進行 注冊。圖5.2展現(xiàn)了此時的首頁是什么樣子的百框。
    在本章完成之前闲礼,我們將會實現(xiàn)處理這些請求的控制器方法。但現(xiàn)在铐维,讓我們對這個控制器發(fā)起一些請求柬泽,看一下它是否能夠正常工作。測試控制器最直接的辦法可能就是構(gòu)建并部署應用嫁蛇,然后通過瀏 覽器對其進行訪問锨并,但是自動化測試可能會給你更快的反饋和更一致 的獨立結(jié)果。所以睬棚,讓我們編寫一個針對HomeController的測 試第煮。
    [圖片]
    圖5 .2 當前的Spittr首頁
    5.2.1 測試控制器
    讓我們再審視一下HomeController。如果你眼神不太好的話抑党,你 甚至可能注意不到這些注解包警,所看到的僅僅是一個簡單的POJO 。我 們都知道測試POJO是很容易的底靠。因此害晦,我們可以編寫一個簡單的類 來測試HomeController,如下所示:
    程序清單5.5 HomeControllerTest:測試HomeController

    package com.spring.mvc;
    
    import com.spring.mvc.controller.HomeController;
    import org.junit.Assert;
    import org.junit.Test;
    
    public class HomeControllerTest {
        @Test
        public void testHomePage() throws Exception {
            HomeController homeController = new HomeController();
            Assert.assertEquals("home",homeController.home());
        }
    }
    
    - 
    

    在程序清單5.5中的測試非常簡單暑中,它只測試了home()方法的行為壹瘟。測試直接調(diào)用home()方法,并斷言返回的字符串包含"home"值痒芝。這個測試并沒有從Spring MVC控制器的角度進行測試俐筋,它沒有斷言當接收到針對"/"的GET請求時會調(diào)用home()方法。由于返回的值正好是"home"严衬,所以也沒有真正判斷"home"是否是視圖的名稱。

  • 然而笆呆,從Spring 3.2開始请琳,我們可以使用控制器的方式來測試Spring MVC中的控制器,而不僅僅作為POJO進行測試赠幕。Spring現(xiàn)在提供了一種模擬Spring MVC并執(zhí)行HTTP請求的機制俄精。這樣,在測試控制器時榕堰,就不需要啟動Web服務器和Web瀏覽器了竖慧。

  • 為了演示如何測試Spring MVC控制器嫌套,我們重寫了HomeControllerTest,并使用了Spring MVC中的新測試特性圾旨。程序清單5.6展示了新的HomeControllerTest踱讨。
    程序清單5.6 改進HomeControllerTest


    file
  • 新版本的測試相比之前的版本只多了幾行代碼,但它更完整地測試了HomeController砍的。這次測試不是直接調(diào)用home()方法并測試返回值痹筛,而是發(fā)起了對"/"的GET請求,并斷言結(jié)果視圖的名稱為"home"廓鞠。首先帚稠,它使用HomeControllerMockMvcBuilders.standaloneSetup()創(chuàng)建一個MockMvc實例,并調(diào)用build()方法進行構(gòu)建床佳。然后滋早,使用MockMvc實例執(zhí)行針對"/"的GET請求,并設置期望得到的視圖名稱砌们。
    5.2.2 定義類級別的請求處理

  • 現(xiàn)在馆衔,已經(jīng)為HomeController編寫了測試,那么我們可以做一些重構(gòu)怨绣,并通過測試來保證不會對功能造成什么破壞角溃。

  • 我們可以做的一件事就是拆分@RequestMapping,并將其路徑映射部分放到類級別 上篮撑。

  • 程序清單5.7展示了這個過程减细。
    程序清單5.7 拆分HomeController中的@RequestMapping

    - package com.spring.mvc.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    import static org.springframework.web.bind.annotation.RequestMethod.GET;
    
    /**
    
     * 聲明一個控制器
     * @author huyingqi
       */
       @Controller.       ——>將控制器映射到“/”
       @RequestMapping("/)
       public class HomeController {
       @RequestMapping(method = GET )   //處理GET請求
       public String home(){
           //視圖名為home
           return "home";    ——> 視圖名為home
       }
    
    }
    
  • 在這個新版本的HomeController中,路徑被轉(zhuǎn)移到類級別的@RequestMapping上赢笨,而HTTP方法仍然映射在方法級別上未蝌。

    • 當控制器在類級別上添加@RequestMapping注解時,這個注解會應用到控制器的所有處理器方法上茧妒。
    • 處理器方法上的@RequestMapping注解會補充類級別上的@RequestMapping的聲明萧吠。
  • 對于HomeController來說,只有一個控制器方法桐筏。

    • 在與類級別的@RequestMapping合并之后纸型,該方法的@RequestMapping注解明確了它將處理對"/"路徑的GET請求。
  • 換句話說梅忌,我們實際上沒有改變?nèi)魏喂δ苷纾皇菍⒁恍┐a移動了位置,但HomeController所做的事情與之前相同牧氮。由于我們現(xiàn)在有了測試琼腔,可以確保在這個過程中沒有破壞原有的功能。

  • 當我們在修改@RequestMapping時踱葛,還可以對HomeController 做另外一個變更丹莲。

    • @RequestMapping的value屬性能夠接受一 個String類型的數(shù)組光坝。

    • 到目前為止,我們給它設置的都是一個 String類型的“/” 甥材。

    • 但是盯另,我們還可以將它映射到對“/homepage”的 請求,只需將類級別的@RequestMapping改為如下所示:

      - - @Controller.       ——>將控制器映射到“/”
          @RequestMapping("/","/homepage")
          public class HomeController {
      
      }
      

      現(xiàn)在擂达,HomeController的home ()方法能夠映射到對“/”和“/homepage”的GET請求土铺。
      5.2.3 傳遞模型數(shù)據(jù)到視圖中

    1. HomeController是一個簡單的控制器樣例,但大多數(shù)控制器并不是這么簡單板鬓。
    1. 在Spittr應用中悲敷,需要有一個頁面來展示最近提交的Spittle列表,因此需要一個新的方法來處理這個頁面俭令。
    1. 首先后德,需要定義一個數(shù)據(jù)訪問的Repository。為了解耦和避免陷入數(shù)據(jù)庫訪問的細節(jié)抄腔,將Repository定義為一個接口瓢湃,并在稍后實現(xiàn)它(在第10章中)。
    1. 目前赫蛇,只需要一個能夠獲取Spittle列表的Repository绵患,下面是一個足夠?qū)崿F(xiàn)功能的SpittleRepository的示例。

      public. interface Spilttlerepository{
       List<Spittle>   findSpittleRepository(long max,int count);
      }
      
  • findSpittles ()方法接受兩個參數(shù)悟耘。

    • 其中max參數(shù)代表所返回的 Spittle中落蝙,Spittle ID屬性的最大值,
    • 而count參數(shù)表明要返回 多少個Spittle對象暂幼。
      為了獲得最新的20個Spittle對象筏勒,我們可 以這樣調(diào)用findSpittles ():
      List<Spittle> findSpittleRepository(long max,int count);
      現(xiàn)在,我們讓Spittle類盡可能的簡單旺嬉,如下面的程序清單5.8所 示管行。它的屬性包括消息內(nèi)容、時間戳以及Spittle發(fā)布時對應的經(jīng)緯 度邪媳。
      程序清單5.8 Spittle類:包含消息內(nèi)容捐顷、時間戳和位置信息
      [圖片]
  1. Spittle是一個基本的POJO數(shù)據(jù)對象,沒有復雜的內(nèi)容悲酷。
  2. 在Spittle類中套菜,我們使用了Apache Common Lang包來實現(xiàn)equals()和hashCode()方法。
  3. 這些方法除了常規(guī)的作用外设易,當我們?yōu)榭刂破鞯奶幚砥鞣椒ň帉憸y試時,它們也是有用的蛹头。
    既然我們說到了測試顿肺,那么我們繼續(xù)討論這個話題并為新的控制器方 法編寫測試戏溺。如下的程序清單使用Spring的MockMvc來斷言新的處理 器方法中你所期望的行為。
    程序清單5.9 測試SpittleController處理針對“/spittles”的GET請求
    [圖片]
    1. 首先屠尊,測試會創(chuàng)建一個SpittleRepository接口的mock實現(xiàn)旷祸。
    1. 這個mock實現(xiàn)會在其findSpittles()方法中返回20個Spittle對象。
    1. 接下來讼昆,將這個mock實現(xiàn)注入到一個新的SpittleController實例中托享。
    1. 然后,創(chuàng)建一個MockMvc并使用這個控制器進行測試浸赫。

根據(jù)您提供的內(nèi)容闰围,我將嘗試梳理一下:

  1. 首先,與HomeController不同既峡,這個測試在MockMvc構(gòu)造器上調(diào)用了setSingleView()方法羡榴。
  • 優(yōu)點
    • 這樣可以避免mock框架解析控制器中的視圖名。在很多場景中运敢,這樣做并不是必需的校仑。
  • 缺點
    • 但是對于這個控制器方法來說,視圖名與請求路徑非常相似传惠。按照默認的視圖解析規(guī)則迄沫,MockMvc會發(fā)生失敗,因為無法區(qū)分視圖路徑和控制器的路徑卦方。
  • 在這個測試中羊瘩,設置InternalResourceView的實際路徑與InternalResourceViewResolver的配置一致是無關(guān)緊要的。
  1. 這個測試對"/spittles"發(fā)起GET請求愿汰,并斷言視圖的名稱為"spittles"困后,并且模型中包含名為"spittleList"的屬性,該屬性包含預期的內(nèi)容衬廷。
  2. 當然摇予,如果此時運行測試,它將會失敗吗跋。它不是在運行時失敗侧戴,而是在編譯時就會失敗。這是因為我們還沒有編寫SpittleController〉穑現(xiàn)在酗宋,我們需要創(chuàng)建SpittleController。
    總結(jié)概要,我梳理了以下內(nèi)容:與HomeController不同疆拘,這個測試在MockMvc構(gòu)造器上調(diào)用了setSingleView()方法蜕猫,避免了視圖名的解析問題。測試對"/spittles"發(fā)起GET請求哎迄,并斷言視圖的名稱為"spittles"回右,并且模型中包含名為"spittleList"的屬性隆圆,該屬性包含預期的內(nèi)容。然而翔烁,由于還沒有編寫SpittleController渺氧,測試將在編譯時失敗。請注意蹬屹,以上內(nèi)容是用中文輸出的侣背。如果您有任何其他問題或需要進一步的解釋,請隨時告訴我慨默。

讓它滿足程序清單5.9的預期贩耐。如下的SpittleController實現(xiàn)將 會滿足以上測試的要求。
程序清單5.10 SpittleController:在模型中放入最新的spittle 列表
[圖片]

  1. 首先业筏,SpittleController類有一個構(gòu)造器憔杨,使用@Autowired注解來注入SpittleRepository。這個SpittleRepository隨后在spittles()方法中使用蒜胖,用于獲取最新的spittle列表消别。
  2. 在spittles()方法中,我們給定了一個Model作為參數(shù)台谢。這樣寻狂,spittles()方法可以將Repository中獲取到的Spittle列表填充到模型中。Model實際上是一個Map(鍵值對的集合)朋沮,它會傳遞給視圖蛇券,從而使數(shù)據(jù)能夠渲染到客戶端。
  3. 當調(diào)用addAttribute()方法并且不指定key時樊拓,key會根據(jù)值的對象類型進行推斷纠亚。在這個例子中,因為值是一個List<Spittle>筋夏,所以鍵會被推斷為spittleList蒂胞。
  4. spittles()方法的最后一步是返回spittles作為視圖的名稱,這個視圖將用于渲染模型中的數(shù)據(jù)条篷。
    如果你希望顯式聲明模型的key的話骗随,那也盡可以進行指定。例如赴叹, 下面這個版本的spittles ()方法與程序清單5. 10中的方法作用是一樣的:
    [圖片]
    如果你希望使用非Spring類型的話鸿染,那么可以用java.util.Map來 代替Model紊选。下面這個版本的spittles ()方法與之前的版本在功能 上是一樣的:
    [圖片]
    既然我們現(xiàn)在提到了各種可替代的方案庆寺,那下面還有另外一種方式來 編寫spittles ()方法:
    [圖片]
  5. 首先,這個版本與其他版本有一些差別津坑。它沒有返回視圖名稱,也沒有顯式地設置模型丢烘,而是直接返回了Spittle列表柱宦。
  6. 當處理器方法像這樣返回對象或集合時些椒,這個值會放到模型中播瞳,模型的key會根據(jù)其類型推斷得出(在本例中,是spittleList)免糕。
  7. 邏輯視圖的名稱將根據(jù)請求路徑推斷得出赢乓。因為這個方法處理針對“/spittles”的GET請求,所以視圖的名稱將是spittles(去掉開頭的斜線)石窑。
  8. 無論選擇哪種方式編寫spittles()方法牌芋,結(jié)果都是相同的。模型中會存儲一個Spittle列表松逊,key為spittleList躺屁,然后將這個列表發(fā)送到名為spittles的視圖中。
  9. 根據(jù)配置的InternalResourceViewResolver经宏,視圖的JSP文件將是“/WEB-INF/views/spittles.jsp”犀暑。
  10. 現(xiàn)在,數(shù)據(jù)已經(jīng)放到模型中烁兰,那么在JSP中如何訪問它呢耐亏?實際上,當視圖是JSP時沪斟,模型數(shù)據(jù)會作為請求屬性放到請求(request)中广辰。因此,在spittles.jsp文件中可以使用JSTL(JavaServer Pages Standard Tag Library)的<c:forEach>標簽來渲染spittle列表主之。

[圖片]
圖5.3為顯示效果择吊,能夠讓你對它在Web瀏覽器中是什么樣子有個可視 化的印象。
盡管SpittleController很簡單槽奕,但是它依然比 HomeController更進一步了几睛。不過,SpittleController和 HomeController都沒有處理任何形式的輸入∈非蹋現(xiàn)在枉长,讓我們擴 展SpittleController,讓它從客戶端接受一些輸入琼讽。
圖5 .3 控制器中的Spittle模型數(shù)據(jù)將會作為請求參數(shù)必峰,并在Web頁面上渲染為列 表的形式

本文由博客一文多發(fā)平臺 OpenWrite 發(fā)布!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末钻蹬,一起剝皮案震驚了整個濱河市吼蚁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖肝匆,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件粒蜈,死亡現(xiàn)場離奇詭異,居然都是意外死亡旗国,警方通過查閱死者的電腦和手機枯怖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來能曾,“玉大人度硝,你說我怎么就攤上這事∈倜幔” “怎么了蕊程?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長驼唱。 經(jīng)常有香客問我藻茂,道長,這世上最難降的妖魔是什么玫恳? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任辨赐,我火速辦了婚禮,結(jié)果婚禮上纽窟,老公的妹妹穿的比我還像新娘肖油。我一直安慰自己,他們只是感情好臂港,可當我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布森枪。 她就那樣靜靜地躺著,像睡著了一般审孽。 火紅的嫁衣襯著肌膚如雪县袱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天佑力,我揣著相機與錄音式散,去河邊找鬼。 笑死打颤,一個胖子當著我的面吹牛暴拄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播编饺,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼乖篷,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了透且?” 一聲冷哼從身側(cè)響起撕蔼,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后鲸沮,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體琳骡,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年讼溺,在試婚紗的時候發(fā)現(xiàn)自己被綠了楣号。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡肾胯,死狀恐怖竖席,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情敬肚,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布束析,位于F島的核電站艳馒,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏员寇。R本人自食惡果不足惜弄慰,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蝶锋。 院中可真熱鬧陆爽,春花似錦、人聲如沸扳缕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽躯舔。三九已至驴剔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間粥庄,已是汗流浹背丧失。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留惜互,地道東北人布讹。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像训堆,于是被迫代替她去往敵國和親描验。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內(nèi)容