在使用Spring MVC開發Web應用程序時,控制器Controller的開發很是重要,雖說視圖(JSP或者是Thymeleaf)也很重要,由於它纔是直接呈現給用戶的,不過因爲如今前端愈來愈重要,不少公司都開始採用先後端分離的開發模式,因此咱們暫時能夠將精力放在開發控制器上。前端
使用Spring MVC開發控制器主要使用如下7個註解:java
接下來,咱們依次講解每一個註解的使用方法。git
先回顧下上篇博客中新建的簡單控制器HelloController:程序員
package chapter05.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class HelloController {
@RequestMapping(value = "index", method = RequestMethod.GET)
public String hello() {
// 這裏返回的邏輯視圖名
return "index";
}
}
複製代碼
這裏@Controller
註解的做用是用來聲明控制器,它的源碼以下所示:github
package org.springframework.stereotype;
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;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
String value() default "";
}
複製代碼
這裏值得注意的是,@Controller
註解使用了@Component
註解,而@Component
註解咱們並不陌生,它用來聲明一個Bean。web
雖然有些書中說能夠把@Controller
註解替換爲@Component
註解,運行沒有差異,只是表意性差一點,可是若是你將上面代碼中的@Controller
註解修改成@Component
註解,而後從新打包發佈到Tomcat,會發現訪問地址http://localhost:8080/spring-action-1.0-SNAPSHOT/index時,報以下所示的404錯誤:spring
將@Component
註解還原爲@Controller
註解,而後從新打包發佈到Tomcat,再次訪問地址http://localhost:8080/spring-action-1.0-SNAPSHOT/index時,訪問正常:json
因此,在Spring MVC中聲明控制器時,推薦使用@Controller
註解。後端
注意事項:程序員在閱讀技術書籍時,要多思考,多嘗試,由於書籍中講解的,極可能是錯的。數組
@RequestMapping
註解用來映射Web請求,它有2種使用形式:
@RequestMapping
註解時,這個註解會應用到控制器的全部處理器方法上,處理器方法上的@RequestMapping
註解會對類級別上的@RequestMapping
註解的聲明進行補充。@RequestMapping
註解經常使用的3個參數以下所示:
指定method值時使用org.springframework.web.bind.annotation.RequestMethod枚舉:
package org.springframework.web.bind.annotation;
public enum RequestMethod {
GET,
HEAD,
POST,
PUT,
PATCH,
DELETE,
OPTIONS,
TRACE;
private RequestMethod() {
}
}
複製代碼
指定produces值時通常使用org.springframework.http.MediaType類下的常量:
public static final String APPLICATION_JSON_VALUE = "application/json";
public static final MediaType APPLICATION_JSON_UTF8 = valueOf("application/json;charset=UTF-8");
public static final String APPLICATION_JSON_UTF8_VALUE = "application/json;charset=UTF-8";
複製代碼
爲了更好的理解,咱們在HelloController類上添加以下代碼:
package chapter05.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("/hello")
public class HelloController {
@RequestMapping(value = "index", method = RequestMethod.GET)
public String hello() {
// 這裏返回的邏輯視圖名
return "index";
}
}
複製代碼
從新打包並部署到Tomcat中,此時的訪問地址從以前的http://localhost:8080/spring-action-1.0-SNAPSHOT/index變成了http://localhost:8080/spring-action-1.0-SNAPSHOT/hello/index,以下所示:
@RequestMapping
註解的value屬性還支持接受一個String類型的數組,以下所示:
@RequestMapping({"/hello", "/index"})
public class HelloController {
// 省略其它代碼
}
複製代碼
此時也能夠經過地址http://localhost:8080/spring-action-1.0-SNAPSHOT/index/index進行訪問:
在上面的代碼中,咱們的方法是返回邏輯視圖名index,而後由視圖解析器最終找到運行時的/WEB-INF/classes/views/index.jsp視圖,但有時咱們不須要返回一個頁面,而是直接返回數據給到前端。
此時咱們可使用@ResponseBody
註解,該註解能夠放在返回值前或者方法上,用於將返回值放在response體內,而不是返回一個頁面。
爲了更好的理解,咱們新建個DemoAnnoController控制器以下所示:
package chapter05.controller;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
@Controller
@RequestMapping("/anno")
public class DemoAnnoController {
@RequestMapping(value = "/index", method = RequestMethod.GET, produces = MediaType.TEXT_PLAIN_VALUE)
public @ResponseBody
String index(HttpServletRequest request) {
return "url:" + request.getRequestURI() + " can access";
}
}
複製代碼
從新打包並部署到Tomcat中,訪問地址http://localhost:8080/spring-action-1.0-SNAPSHOT/anno/index,效果以下所示:
也能夠將@ResponseBody
註解放在方法上,以下所示:
@RequestMapping(value = "/index", method = RequestMethod.GET, produces = MediaType.TEXT_PLAIN_VALUE)
@ResponseBody
public String index(HttpServletRequest request) {
return "url:" + request.getRequestURI() + " can access";
}
複製代碼
@RequestParam
註解用於接收URL中的參數信息。
爲了更好的理解 ,咱們在DemoAnnoController控制器中添加以下方法:
@RequestMapping(value = "/requestParam", method = RequestMethod.GET, produces = "text/plain;charset=UTF-8")
@ResponseBody
public String passRequestParam(@RequestParam("id") Long id, @RequestParam("name") String name, HttpServletRequest request) {
return "url:" + request.getRequestURI() + " can access,id: " + id + ",name=" + name;
}
複製代碼
從新打包並部署到Tomcat中,訪問地址http://localhost:8080/spring-action-1.0-SNAPSHOT/anno/requestParam?id=1&name=zwwhnly ,效果以下所示:
注意事項:上面示例中,Url中的參數名稱和方法中的變量名稱徹底一致,因此能夠省略掉
@RequestParam
註解,不過爲了代碼的易讀性,建議保留@RequestParam
註解。
若是不傳遞參數,訪問地址http://localhost:8080/spring-action-1.0-SNAPSHOT/anno/requestParam,則會提示以下信息:
或者只傳遞其中1個參數,訪問地址http://localhost:8080/spring-action-1.0-SNAPSHOT/anno/requestParam?id=1,則會提示以下信息:
由此也說明,使用了
@RequestParam
註解的參數,在Url中必須傳遞。
不過,@RequestParam
註解提供了defaultValue屬性,能夠給參數指定默認值,好比咱們給參數id設置默認值1,給參數name設置默認值zwwhnly,而後訪問地址http://localhost:8080/spring-action-1.0-SNAPSHOT/anno/requestParam,效果以下所示:
或者訪問地址http://localhost:8080/spring-action-1.0-SNAPSHOT/anno/requestParam?id=2,效果以下所示:
不過,仍是有一個異常場景須要注意,就是Url中傳遞的參數和方法中定義的參數類型不匹配,好比咱們將id的值傳錯,訪問地址http://localhost:8080/spring-action-1.0-SNAPSHOT/anno/requestParam?id=zwwhnly&name=zwwhnly,會看到以下報錯信息:
@PathVariable
註解也是用於接收URL中的參數信息,不過和@RequestParam
註解稍有不一樣。
@PathVariable
註解用於解析Url中的路徑參數,如https://www.cnblogs.com/zwwhnly/中的zwwhnly部分,而@RequestParam
註解用於解析Url中的查詢參數,如https://i.cnblogs.com/posts?page=2中的page部分。
爲了更好的理解 ,咱們在DemoAnnoController控制器中添加以下方法:
@RequestMapping(value = "/pathvar/{str}", produces = "text/plain;charset=UTF-8")
public @ResponseBody
String demoPathVar(@PathVariable("str") String str, HttpServletRequest request) {
return "url:" + request.getRequestURI() + " can access,str: " + str;
}
複製代碼
從新打包並部署到Tomcat中,訪問地址http://localhost:8080/spring-action-1.0-SNAPSHOT/anno/pathvar/zwwhnly ,效果以下所示:
注意事項:若是
@PathVariable
註解中指定value屬性的話,它會假設佔位符的名稱與方法的參數名相同。
由於這裏方法的參數名正好與佔位符的名稱相同,因此咱們能夠去掉@PathVariable
註解的value屬性:
@RequestMapping(value = "/pathvar/{str}", produces = "text/plain;charset=UTF-8")
public @ResponseBody
String demoPathVar(@PathVariable String str, HttpServletRequest request) {
return "url:" + request.getRequestURI() + " can access,str: " + str;
}
複製代碼
@RequestBody
註解容許request的參數在request體中,而不是直接連接在地址後面,該註解放在參數前。
爲了更好的理解 ,咱們在DemoAnnoController控制器中添加以下方法:
@RequestMapping(value = "/obj", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@ResponseBody
public String passObj(@RequestBody DemoObj demoObj, HttpServletRequest request) {
return "url:" + request.getRequestURI() + " can access,demoObj id:" + demoObj.getId() +
" demoObj name:" + demoObj.getName();
}
複製代碼
從新打包並部署到Tomcat中,而後使用Postman工具調用接口http://localhost:8080/spring-action-1.0-SNAPSHOT/anno/passObj,效果以下所示:
@RestController
是一個組合註解,它組合了@Controller
註解和@ResponseBody
註解,源碼以下所示:
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 org.springframework.stereotype.Controller;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
String value() default "";
}
複製代碼
所以,若是某個控制器中全部的方法都只是返回數據而不是頁面的話,就可使用@RestController
註解。
爲了更好的理解 ,咱們舉個具體的示例。
首先,在pom.xml中添加以下依賴,用於對象和json之間的轉換:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.9</version>
</dependency>
複製代碼
而後新建控制器DemoRestController以下所示:
package chapter05.controller;
import chapter05.model.DemoObj;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/rest")
public class DemoRestController {
@RequestMapping(value = "/getjson", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public DemoObj getjson(@RequestBody DemoObj demoObj) {
return new DemoObj(demoObj.getId(), demoObj.getName());
}
}
複製代碼
由於使用@RestController
註解,至關於同時使用了@Controller
註解和@ResponseBody
註解,因此上面的代碼等價於下面的代碼:
package chapter05.controller;
import chapter05.model.DemoObj;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
@Controller
@ResponseBody
@RequestMapping("/rest")
public class DemoRestController {
@RequestMapping(value = "/getjson", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public DemoObj getjson(@RequestBody DemoObj demoObj) {
return new DemoObj(demoObj.getId(), demoObj.getName());
}
}
複製代碼
從新打包並部署到Tomcat中,而後使用Postman工具調用接口http://localhost:8080/spring-action-1.0-SNAPSHOT/rest/getjson,效果以下所示:
源碼地址:github.com/zwwhnly/spr…,歡迎下載。
Craig Walls 《Spring實戰(第4版)》
汪雲飛《Java EE開發的顛覆者:Spring Boot實戰》
最後,歡迎關注個人微信公衆號:「申城異鄉人」,全部博客會同步更新。