Spring入門(十三):Spring MVC經常使用註解講解

在使用Spring MVC開發Web應用程序時,控制器Controller的開發很是重要,雖說視圖(JSP或者是Thymeleaf)也很重要,由於它纔是直接呈現給用戶的,不過因爲如今前端愈來愈重要,不少公司都開始採用先後端分離的開發模式,因此咱們暫時能夠將精力放在開發控制器上。前端

使用Spring MVC開發控制器主要使用如下7個註解:java

  1. @Controller
  2. @RequestMapping
  3. @ResponseBody
  4. @RequestParam
  5. @PathVariable
  6. @RequestBody
  7. @RestController

接下來,咱們依次講解每一個註解的使用方法。git

1. @Controller

先回顧下上篇博客中新建的簡單控制器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註解。後端

注意事項:程序員在閱讀技術書籍時,要多思考,多嘗試,由於書籍中講解的,極可能是錯的。數組

2. @RequestMapping

@RequestMapping註解用來映射Web請求,它有2種使用形式:

  1. 應用在方法級別,如上面的代碼中展現的那樣。
  2. 應用在類級別,當控制器在類級別上添加@RequestMapping註解時,這個註解會應用到控制器的全部處理器方法上,處理器方法上的@RequestMapping註解會對類級別上的@RequestMapping註解的聲明進行補充。

@RequestMapping註解經常使用的3個參數以下所示:

  1. value:指定映射的URL地址,如index
  2. method:指定映射的請求類型,如GET請求、POST請求等
  3. produces:指定返回的response的媒體類型和字符集,如application/json;charset=UTF-8。

指定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進行訪問:

3. @ResponseBody

在上面的代碼中,咱們的方法是返回邏輯視圖名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";
}
複製代碼

4. @RequestParam

@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,會看到以下報錯信息:

5. @PathVariable

@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;
}
複製代碼

6. @RequestBody

@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,效果以下所示:

7. @RestController

@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,效果以下所示:

8. 源碼及參考

源碼地址:github.com/zwwhnly/spr…,歡迎下載。

Craig Walls 《Spring實戰(第4版)》

汪雲飛《Java EE開發的顛覆者:Spring Boot實戰》

最後,歡迎關注個人微信公衆號:「申城異鄉人」,全部博客會同步更新。

相關文章
相關標籤/搜索