在 Spring Boot 中,@Controller 注解是專門用於處理 Http 請求處理的,是以 MVC 為核心的設計思想的控制層。@RestController 則是 @Controller 的衍生注解。
1 Spring Boot Controller
1.1 原理
Spring Boot 本身就 Spring MVC 的簡化版本。是在 Spring MVC 的基礎上實現了自動配置,簡化了開發人員開發過程。
Spring MVC 是通過一個叫 DispatcherServlet
前端控制器的來攔截請求的。而在 Spring Boot 中 使用自動配置把 DispatcherServlet
前端控制器自動配置到框架中。
例如,我們來解析 /users 這個請求
DispatcherServlet
前端控制器攔截請求 /usersservlet
決定使用哪個handler
處理- Spring 檢測哪個控制器匹配
/users
,Spring 從 @RquestMapping 中查找出需要的信息 - Spring 找到正確的 Controller 方法后,開始執行 Controller 方法
- 返回 users 對象列表
- 根據與客戶端交互需要返回 Json 或者 Xml 格式
1.2 相關注解
在 Spring Boot 中使用到 @Controller 及相關的注解如下,主要分為三個層面進行,請求前,處理中,返回。
應用場景 | 注解 | 注解說明 |
---|---|---|
處理請求 | @Controller | 處理 Http 請求 |
處理請求 | @RestController | @Controller 的衍生注解 |
路由請求 | @RequestMapping | 路由請求 可以設置各種操作方法 |
路由請求 | @GetMapping | GET 方法的路由 |
路由請求 | @PostMapping | POST 方法的路由 |
路由請求 | @PutMapping | PUT 方法的路由 |
路由請求 | @DeleteMapping | DELETE 方法的路由 |
請求參數 | @PathVariable | 處理請求 url 路徑中的參數 /user/{id} |
請求參數 | @RequestParam | 處理問號后面的參數 |
請求參數 | @RequestBody | 請求參數以json格式提交 |
返回參數 | @ResponseBody | 返回 json 格式 |
注意以上注解需要強調的是 @RestController 是 @Controller 的子集。
@GetMapping、@PostMapping、@PutMapping、@DeleteMapping 是 @RequestMapping 的子集。所以實際上我們只需要掌握 @Controller 和 @RequestMapping 就可以了。
1.3 @Controller 與 @RestController 區別
@Controller 包括了 @RestController。@RestController 是 Spring4 后新加的注解,從 RestController 類源碼可以看出 @RestController 是 @Controller 和 @ResponseBody 兩個注解的結合體。
@Controller=@RestController+@ResponseBody
如下 @RestController 的源碼可以看出他們的關系
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
@AliasFor(
annotation = Controller.class
)
String value() default "";
}
1.4 @Controller 與 @RestController應用場景
-
@Controller 一般應用在有返回界面的應用場景下.
例如,管理后台使用了 thymeleaf 作為模板開發,需要從后台直接返回 Model 對象到前台,那么這時候就需要使用 @Controller 來注解。
-
@RestController 如果只是接口,那么就用 RestController 來注解.
例如前端頁面全部使用了 Html、Jquery來開發,通過 Ajax 請求服務端接口,那么接口就使用 @RestController 統一注解。
1.5 @RequestMapping 說明
首先我們來看看 @RequestMapping 的源碼,我在上面加了注釋
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
String name() default "";
//指定請求的實際地址
@AliasFor("path")
String[] value() default {};
@AliasFor("value")
String[] path() default {};
//指定請求的method類型, GET、POST、PUT、DELETE等
RequestMethod[] method() default {};
//指定request中必須包含某些參數值是,才讓該方法處理。
String[] params() default {};
//指定request中必須包含某些指定的header值,才能讓該方法處理請求。
String[] headers() default {};
//指定處理請求的提交內容類型(Content-Type),例如application/json, text/html;
String[] consumes() default {};
//指定返回的內容類型,僅當request請求頭中的(Accept)類型中包含該指定類型才返回;
String[] produces() default {};
}
示例說明:
示例 | 說明 |
---|---|
@RequestMapping("/index") | 默認為 GET 方法的路由 /index |
@RequestMapping(value="/index",method = RequestMethod.GET) | 同上面一條 |
@RequestMapping(value="/add",method = RequestMethod.POST) | 路由為 /add 的 POST 請求 |
@RequestMapping(value="/add",method = RequestMethod.POST),consumes="application/json" | 路由為 /add 的 POST 請求,但僅僅處理 application/json 的請求 |
@RequestMapping(value="/add",method = RequestMethod.POST),produces="application/json" | 路由為 /add 的 POST 請求,強調返回為 JSON 格式 |
@RequestMapping(value="/add",method = RequestMethod.POST),params="myParam=xyz" | 路由為 /add 的 POST 請求,但僅僅處理頭部包括 myParam=xyz 的請求 |
@RequestMapping(value="/add",method = RequestMethod.POST),headers="Referer=http://www.xyz.com/" | 路由為 /add 的 POST 請求,但僅僅處理 來源為 www.xyz.com 的請求 |
2 @Controller 和 @RestController 示例
本章節,將對兩個注解配合其他注解編寫一系列示例,為了演示 @Controller 返回對應頁面功能,我們在示例中引入了 thymeleaf 模板。具體在 pom.xml 中有說明。
編號 | 路由 | Http方法 | 方法說明 |
---|---|---|---|
1 | /user/index | GET | 獲取用戶列表並返回列表頁面 |
1 | /user/add | GET | 用戶新增頁面 |
1 | /user/save | POST | 新增用戶的api |
1 | /user/edit | GET | 用戶編輯的頁面 |
1 | /user/update | POST | 編輯用戶的api |
1 | /user/del | GET | 刪除用戶頁面 |
1 | /user/deleted | POST | 刪除用戶頁面的api |
2.1 新建 Spring Boot 項目
- File > New > Project,如下圖選擇
Spring Initializr
然后點擊 【Next】下一步 - 填寫
GroupId
(包名)、Artifact
(項目名) 即可。點擊 下一步
groupId=com.fishpro
artifactId=restcontroller - 選擇依賴
Spring Web Starter
前面打鈎,在模板列中勾選thymeleaf
。 - 項目名設置為
spring-boot-study-restcontroller
.
2.2 依賴 Pom.xml 配置
本項目引入了 web 和 thymeleaf ,具體一人如下代碼:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
並把配置文件重命名為 application.yml ,修改默認測試端口
server:
port: 8087
2.3 基於 @Controller 的示例代碼
本代碼實例中新增了如下頁面,注意 templates
表示 /resources/tempalates/
,controller
表示 com.fishpro.restcontroller.controller
包。
- controller/UserController.java 控制層-用戶類
- domain/UserDO.java 用戶控實體類
- templates/user/index.html 視圖-用戶列表頁面
- templates/user/add.html 視圖-新增用戶頁面
- templates/user/edit.html 視圖-編輯用戶頁面
2.3.1 返回用戶列表信息 /user/index
首先構件一個虛擬的用戶數據
/**
* 模擬一組數據
* */
private List<UserDO> getData(){
List<UserDO> list=new ArrayList<>();
UserDO userDO=new UserDO();
userDO.setUserId(1);
userDO.setUserName("admin");
list.add(userDO);
userDO=new UserDO();
userDO.setUserId(2);
userDO.setUserName("heike");
list.add(userDO);
userDO=new UserDO();
userDO.setUserId(3);
userDO.setUserName("tom");
list.add(userDO);
userDO=new UserDO();
userDO.setUserId(4);
userDO.setUserName("mac");
list.add(userDO);
return list;
}
1 創建一個用戶列表頁面 /user/index
UserController 增加方法如下圖所示,使用 @RequestMapping 注解,注意 @RequestMapping(method = RequestMethod.GET,value = "/index")
等於 @RequestMapping(value = "/index")
也等於 @RequestMapping("/index")
也等於 @GetMapping("/index")
/**
* GET 返回用戶列表信息
* */
@RequestMapping(method = RequestMethod.GET,value = "/index")
public String index(Model model){
List<UserDO> list =getData();
model.addAttribute("list",list);//返回 用戶 list
return "user/index";
}
2 創建 templates/index.html 對應上面的路由 /user/index
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div class="title">
</div>
<div th:each="user :${list}">
用戶ID:<span th:text="${user.userId}"></span>
用戶名:<span th:text="${user.userName}"></span>
</div>
</body>
</html>
3 在瀏覽器中查看
右鍵 RestControllerApplication > Run 在瀏覽器輸入 http://localhost:8087/user/index
用戶ID:1 用戶名:admin
用戶ID:2 用戶名:heike
用戶ID:3 用戶名:tom
用戶ID:4 用戶名:mac
2.3.3 新增用戶頁面 /user/add
在 UserController 中增加 add 路由 /user/add
UserController 增加方法如下圖所示,使用 @RequestMapping 注解,並創建了兩個路由
- /user/add 對應前端頁面
- /user/save 對應前端頁面提交按鈕的接口
/**
* GET 返回add頁面
* @GetMapping("/add") = @RequestMapping(method = RequestMethod.GET,value = "/add")
* */
@GetMapping("/add")
public String add(){
return "user/add";
}
/**
* POST 新增用戶api
* @return 返回 map對象
* */
@RequestMapping(method = RequestMethod.POST,value = "/save")
@ResponseBody
public Object save(UserDO user){
List<UserDO> list= getData();
list.add(user);//模擬向列表中增加數據
Map<String,Object> map=new HashMap<>();
if(null==user){
map.put("status",3);
map.put("message","沒有傳任何對象");
return map;
}
map.put("status",0);
map.put("data",user);
return map;
}
創建 templates/add.htm 文件對應路由 /user/add
在對應的 templates/add.html中增加代碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用戶新增頁面</title>
<script src="https://cdn.bootcss.com/jquery/1.11.3/jquery.js"></script>
</head>
<body>
<form id="form1">
<div> <input name="userId" id="userId" placeholder="請輸入userid"></div>
<div> <input name="userName" id="userName" placeholder="請輸入username"></div>
<div> <input type="button" value="新增" id="btnSave"/></div>
</form>
<script>
$(function () {
$("#btnSave").click(function () {
$.ajax({
cache: true,
type: "POST",
url: "/user/save",
data:$('#form1').serialize(),
dataType:"json",
async: false,
error: function (request) {
console.log("Connection error");
},
success: function (data) {
if (data.code == 0) {
console.log("成功");
} else {
console.log("失敗");
}
}
});
});
});
</script>
</body>
</html>
在瀏覽器中測試效果
右鍵 RestControllerApplication > Run 在瀏覽器輸入 http://localhost:8087/user/add
2.3.3 編輯用戶頁面 /user/edit
這部分代碼原理同 2.3.2 代碼原理 通過構件一個編輯頁面,點擊編輯頁面的【保存】提交到后端的 api 中。
本示例中創建了2個接口 1個文件
- /user/edit 對應前端頁面
- /user/update 對應前端頁面提交按鈕的接口
- /templates/user/edit.html
/**
* GET 返回編輯頁面
* @GetMapping("/edit/{id}") = @RequestMapping(method = RequestMethod.GET,value = "/edit/{id}")
* @PathVariable("id") 表示路由中的動態參數部分
* @param id 表示要編輯的用戶id
* @param model 表示將要輸出到頁面的 Model 對象
* @return 返回到 user/edit頁面
* */
@GetMapping("/edit/{id}")
public String edit(@PathVariable("id") String id, Model model){
UserDO user =new UserDO();
user.setUserId(3);
user.setUserName("fishpro");
model.addAttribute("user",user);
return "user/edit";
}
/**
* POST 修改用戶api
* @RequestBody 表示參數使用 json 對象傳輸
* @return 返回 map對象
* */
@PostMapping("/update")
@ResponseBody
public Object update(@RequestBody UserDO user){
Map<String,Object> map=new HashMap<>();
if(null==user){
map.put("status",3);
map.put("message","沒有傳任何對象");
return map;
}
//更新邏輯
map.put("status",0);
return map;
}
edit.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用戶編輯頁面</title>
<script src="https://cdn.bootcss.com/jquery/1.11.3/jquery.js"></script>
</head>
<body>
<form id="form1">
<div> <input name="userId" id="userId" placeholder="請輸入userid" th:value=${user.userId}> </div>
<div> <input name="userName" id="userName" placeholder="請輸入username" th:value=${user.userName}></div>
<div> <input type="button" value="新增" id="btnSave"/></div>
</form>
<script>
$(function () {
$("#btnSave").click(function () {
$.ajax({
cache: true,
type: "POST",
url: "/user/update",
data:$('#form1').serialize(),
dataType:"json",
async: false,
error: function (request) {
console.log("Connection error");
},
success: function (data) {
if (data.code == 0) {
console.log("成功");
} else {
console.log("失敗");
}
}
});
});
});
</script>
</body>
</html>
2.3.3 刪除用戶 /user/delete
/**
* POST 修改用戶api
* @return 返回 map對象
* */
@PostMapping("/delete/{id}")
@ResponseBody
public Object delete(@PathVariable("id") Integer id){
List<UserDO> list= getData();
UserDO userDO=null;
for (UserDO user:list
) {
if(id.equals(user.getUserId().toString())){
//刪除用戶
userDO=user;
break;
}
}
Map<String,Object> map=new HashMap<>();
map.put("status",0);
map.put("data",userDO);
return map;
}
2.4 基於 @RestController 的示例代碼
本章節中是基於 2.3版本中的接口,其實是一樣的功能,詳細見下面的代碼,為了完成代碼功能示例,我們新建了 UserRestController.java,不同的是 我們給類加了 @RestController 修飾符。
@RestController
@RequestMapping("user2")
public class UserRestController {
//從 UserController 搬過來代碼即可
}
關聯閱讀: