一、控制器定義
控制器提供訪問應用程序的行為,通常通過服務接口定義或注解定義兩種方法實現。 控制器解析用戶的請求並將其轉換為一個模型。在Spring MVC中一個控制器可以包含多個Action(動作、方法)。
使用注解@Controller定義控制器
org.springframework.stereotype.Controller注解類型用於聲明Spring類的實例是一個控制器(在講IOC時還提到了另外3個注解);Spring可以使用掃描機制來找到應用程序中所有基於注解的控制器類,為了保證Spring能找到你的控制器,需要在配置文件中聲明組件掃描。
創建一個名了Bar的類,定義為一個控制器,類的具體實現如下:
package com.zhangsan.springmvc01; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; @org.springframework.stereotype.Controller public class Controller { @RequestMapping("/bar") public String hi(Model model){ model.addAttribute("msg","這是通過注解定義的一個控制器中的Action"); return "hi"; } }
還要需要修改Spring mvc配置文件,啟用自動組件掃描功能,在beans中增加如下配置:
<!-- 自動掃描包,實現支持注解的IOC --> <context:component-scan base-package="com.zhangsan.springmvc01" />
運行結果如下:
小結:從代碼與運行結果可以看出BarController與FooController同時都指定了一個視圖hi.jsp,但是頁面結果的結果是不一樣的,從這里可以看出視圖是被復用的,而控制器與視圖之間是弱偶合關系。
二、@RequestMapping詳解
@RequestMapping注釋用於映射url到控制器類或一個特定的處理程序方法。可用於類或方法上。用於類上,表示類中的所有響應請求的方法都是以該地址作為父路徑。該注解共有8個屬性,注解源碼如下:
package org.springframework.web.bind.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.concurrent.Callable; import org.springframework.core.annotation.AliasFor; /** * 用於映射url到控制器類或一個特定的處理程序方法. */ //該注解只能用於方法或類型上 @Target({ ElementType.METHOD, ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented @Mapping public @interface RequestMapping { /** * 指定映射的名稱 */ String name() default ""; /** * 指定請求的路徑映射,指定的地址可以是uri模板,別名為path */ @AliasFor("path") String[] value() default {}; /** 別名為value,使用path更加形象 * 只有用在一個Servlet環境:路徑映射URI(例如“/myPath.do”)。 * Ant風格的路徑模式,同時也支持(例如,“/myPath/*.do”)。在方法層面,在主要的映射在類型級別表示相對路徑(例如,“edit.do”) * 的支持。路徑映射的URI可能包含占位符(例如“/$ {}連接”) */ @AliasFor("value") String[] path() default {}; /** * 指定請求謂詞的類型如GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE. 收窄請求范圍 The * HTTP request methods to map to, narrowing the primary mapping: GET, POST, * HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE. */ RequestMethod[] method() default {}; /** * 映射請求的參數,收窄請求范圍 The parameters of the mapped request, narrowing the * primary mapping. */ String[]params() default {}; /** * 映射請求頭部,收窄請求范圍 The headers of the mapped request, narrowing the primary * mapping. RequestMapping(value = "/something", headers = * "content-type=text/*") */ String[] headers() default {}; /** * 指定處理請求的提交內容類型(Content-Type),例如application/json, text/html,收窄請求范圍 The * consumable media types of the mapped request, narrowing the primary * mapping. */ String[] consumes() default {}; /** * 指定返回的內容類型,僅當request請求頭中的(Accept)類型中包含該指定類型才返回 The producible media types * of the mapped request, narrowing the primary mapping. produces = * "text/plain" produces = {"text/plain", "application/*"} produces = * "application/json; charset=UTF-8" */ String[] produces() default {}; }
從上面的源碼可以發現除了name基本都是數組類型,在設置時我們可以指定單個值,如@RequestMapping(value="/foo");也可以同時指定多個值如:@RequestMapping(value={"/foo","/bar"})。
2.1、指定具體路徑字符
2.1.1 只注解方法
@Controller public class Controller { @RequestMapping("/h1") public String h1(){ return "h1"; } }
訪問路徑:http://localhost:8080/h1
2.1.2 同時注解類與方法
@Controller @RequestMapping("/foo") public class Controller { @RequestMapping("/h1") public String h1(){ return h1"; } }
訪問路徑:http://localhost:8080/foo/h1
需要先指定類的路徑再指定方法的路徑
2.1.3 @RequestMapping 來處理多個 URI
@Controller @RequestMapping("/foo") public class Controller { @RequestMapping(value = {"/hi","/a/b/c"}) public String h2(Model model){ model.addAttribute("msg","@RequestMapping 來處理多個 URI!"); return "hi"; } }
訪問路徑:1、http://localhost:8080/foo/hi
2、http://localhost:8080/foo/a/b/c
2.1.4、路徑變量占位,URI模板模式
在Spring MVC可以使用@PathVariable 注釋方法參數的值綁定到一個URI模板變量。
@Controller @RequestMapping("/foo") public class Controller { @RequestMapping(value = {"/h3/{name}/{id}"}) public String h3(Model model, @PathVariable String name,@PathVariable int id){ model.addAttribute("msg","路徑變量占位,URI模板模式!\n名稱:"+name+"\n編號:"+id); return "hi"; } }
訪問路徑:http://localhost:8080/foo/h3/張三/20
2.1.5、正則表達式模式的URI模板
@Controller @RequestMapping("/foo") public class Controller { @RequestMapping(value = "/h4/{id:\\d{6}}-{name:[a-z]{3}}") public String h4(Model model, @PathVariable String name,@PathVariable int id){ model.addAttribute("msg","正則表達式模式的URI模板!\n名稱:"+name+"\n編號:"+id); return "hi"; } }
訪問路徑:http://localhost:8080/foo/h4/123456-abc
正則要求id必須為6位的數字,而name必須為3位小寫字母,訪問結果如下:
2.1.6、Ant風格路徑模式
@RequestMapping注解也支持ant風格的路徑模式,如/foo/h5/*.do,示例代碼如下:
@Controller @RequestMapping("/foo") public class Controller { @RequestMapping(value = "/h5/*.do") public String h5(Model model){ model.addAttribute("msg","Ant風格路徑模式"); return "hi"; } }
訪問路徑:http://localhost:8080/foo/h5/任意符號.do
ANT通配符有三種:
2.2、method屬性指定謂詞類型
用於約束請求的謂詞類型,可以收窄請求范圍。指定請求謂詞的類型如GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE,如下代碼所示:
@Controller @RequestMapping("/foo") public class Controller { @RequestMapping(value = "/h6",method = {RequestMethod.POST,RequestMethod.DELETE}) public String h6(Model model){ model.addAttribute("msg","請求謂詞只能是POST與DELETE"); return "hi"; } }
要訪問h6請求謂詞類型必須是POST或者為DELETE,當我們從瀏覽器的URL欄中直接請求時為一個GET請求,則結果是405,如下所示:
如果將POST修改為GET則正常了,如下所示:
@Controller @RequestMapping("/foo") public class Controller { @RequestMapping(value = "/h6",method = {RequestMethod.GET}) public String h6(Model model){ model.addAttribute("msg","請求謂詞只能是GET"); return "hi"; } }
Spring MVC 的 @RequestMapping 注解能夠處理 HTTP 請求的方法, 比如 GET, PUT, POST, DELETE 以及 PATCH。
所有的請求默認都會是 HTTP GET 類型的。
為了能降一個請求映射到一個特定的 HTTP 方法,你需要在 @RequestMapping 中使用 method 來聲明 HTTP 請求所使用的方法類型,如下所示:
@RestController @RequestMapping("/home") public class IndexController { @RequestMapping(method = RequestMethod.GET) String get() { return "Hello from get"; } @RequestMapping(method = RequestMethod.DELETE) String delete() { return "Hello from delete"; } @RequestMapping(method = RequestMethod.POST) String post() { return "Hello from post"; } @RequestMapping(method = RequestMethod.PUT) String put() { return "Hello from put"; } @RequestMapping(method = RequestMethod.PATCH) String patch() { return "Hello from patch"; } }
在上述這段代碼中, @RequestMapping 注解中的 method 元素聲明了 HTTP 請求的 HTTP 方法的類型。
所有的處理處理方法會處理從這同一個 URL( /home)進來的請求, 但要看指定的 HTTP 方法是什么來決定用哪個方法來處理。
例如,一個 POST 類型的請求 /home 會交給 post() 方法來處理,而一個 DELETE 類型的請求 /home 則會由 delete() 方法來處理。
你會看到 Spring MVC 將使用這樣相同的邏輯來映射其它的方法。
2.3、consumes屬性指定請求的Content-Type
@RequestMapping 注解的 produces 和 consumes 這兩個元素來縮小請求映射類型的范圍,達到處理生產和消費對象的目的。
指定處理請求的提交內容類型(Content-Type),例如application/json, text/html,收窄請求范圍,如果用戶發送的請求內容類型不匹配則方法不會響應請求,具體使用如下代碼所示:
@Controller @RequestMapping("/foo") public class Controller { @RequestMapping(value = "/h7",consumes = "text/html") public String h7(Model model){ model.addAttribute("msg", "請求的提交內容類型(Content-Type)是text/html"); return "hi"; } }
在h7的注解中約束發送到服務器的Content-Type必須是text/html類型,如果類型不一致則會報錯(415),測試結果如下:
請求的提交內容類型(Content-Type)是text/html
注意:可以使用!號,如consumes="!text/html"
2.4、produces屬性指定響應的Content-Type,約束Accept類型
指定返回的內容類型,僅當request請求頭中的(Accept)類型中包含該指定類型才返回,方法才處理客戶端的請求否則會報406錯誤,常用設置如下:
produces = "text/plain" //客戶端只接收純文本
produces = {"text/plain", "application/*"} //客戶端接收純文本與application/*類型的內容
produces = "application/json; charset=UTF-8" //客戶端接收json且編碼為utf-8/;
@Controller @RequestMapping("/foo") public class Controller { @RequestMapping(value = "/h8",produces = "application/json;charset=UTF-8") public String h8(Model model){ model.addAttribute("msg", "客戶端可以接收的類型是application/json; charset=UTF-8"); return "hi"; } }
運行結果:
注意:可以使用!號,如produces="!text/html"