SpringBoot - 第五章 | 常用註解
在前面文章中我們大量使用到Spring Boot的annotation(@註解),例如:@RestController、@Controller、@RequestMapping注解,這邊會盡量把一些常用的@註解做使用方式和使用場景的介紹。
常用註解
@SpringBootApplication
在前面幾章中,在啟動類裡面,都會看到這個啟動類註解,此註解是集合以下這些註解,@SpringBootConfiguration、@EnableAutoConfiguration 和 @ComponentScan 注解。
@SpringBootConfiguration 繼承 @Configuration,對於熟悉spring的開發者而言,此註解當前類是配置類,並會將當前類內聲明的一個或多個以@Bean註解標記的方法的實例納入到Spring容器中,並且實例名就是方法名。
@EnableAutoConfiguration 這個註解就是SpringBoot能自動配置的所在了。通過此註解,能將所有符合自動配置條件的bean的定義加載到Spring容器中。這個在後面會再拉出來特別說。
@ComponentScan 顧名思義,就是掃描當前包及其子包下被 @Component、@Controller、@Service、@Repository 等註解的類並加入到Spring容器中進行管理。
@Controller 和 @RestController
@RestController Spring4之後加入的註解,原來在@Controller中返回json需要@ResponseBody來配合,如使用@RestController就不需要再配置@ResponseBody。
@RequestMapping
處理請求網址對應的註解,可用於類或方法上。用於類上,表示類中的所有響應請求的方法都是以該網址作為父路徑。
參數 |
說明 |
value |
指定請求的網址 |
method |
請求方法類型 |
consumes |
請求的提交內容類型 |
produces |
指定返回的內容類型 僅當request請求頭中的(Accept)類型中包含該指定類型才返回 |
params |
指定request中必須包含某些引數值 |
headers |
指定request中必須包含指定的header值 |
name |
指定給mapping一個名稱,沒有什麼作用 |
1 2 3 4 5 6 7 8 9 10
| @RequestMapping(value = "/get" , method = RequestMethod.GET) @RequestMapping(value = "/post" , method = RequestMethod.POST) @RequestMapping(value = "/put" , method = RequestMethod.PUT) @RequestMapping(value = "/delete" , method = RequestMethod.DELETE)
@GetMapping(value = "/get") @PostMapping(value = "/post") @PutMapping(value = "/put") @DeleteMapping (value = "/delete")
|
@RequestBody 和 @ResponseBody
@PathVariable、@RequestParam、@RequestAttribute
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.jj.learning.springboot.chapter5.controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DemoController {
@RequestMapping(value = "/path/{id}", method = RequestMethod.GET)
public String pathVariable(@PathVariable("id") String id) {
return "ID:" + id;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.jj.learning.springboot.chapter5.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DemoController {
@RequestMapping(value = "/path", method = RequestMethod.GET)
public String requestParamGet(@RequestParam("id") String id){
return "ID:" + id;
}
@RequestMapping(value = "/path", method = RequestMethod.POST)
public String requestParamPost(@RequestParam("id") String id){
return "ID:" + id;
}
}
- @RequestAttribut,用於取得過濾器或攔截器建立的、預先存在的請求屬性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.jj.learning.springboot.chapter5.controller;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DemoController {
@ModelAttribute
void beforeInvokingHandlerMethod(HttpServletRequest request) {
request.setAttribute("id", "Hello world!");
}
@RequestMapping(value = "/req/attr", method = RequestMethod.GET)
public String requestAttribute(@RequestAttribute("id") String id) {
return "ID:" + id;
}
}
@Component、@Service、@Repository
註:通常一些類無法確定是使用@Service還是@Component時,註解使用@Component,比如Redis的配置類..等
@ModelAttribute
@ModelAttribute註解可被應用在方法或方法參數上。
方法使用 @ModelAttribute
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.jj.learning.springboot.chapter5.controller;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DemoController {
@ModelAttribute
void beforeInvokingHandlerMethod(HttpServletRequest request) {
request.setAttribute("id", "Hello world!");
}
@RequestMapping(value = "/req/attr", method = RequestMethod.GET)
public String requestAttribute(@RequestAttribute("id") String id) {
return "ID:" + id;
}
}
方法使用 @ModelAttribute通常被用來加入一些公共的屬性或資料,比如一個下拉列表所預設的幾種狀態,或者寵物的幾種類型,或者去取得一個HTML表單渲染所需要的命令對象,比如Account等。
方法參數使用 @ModelAttribute
方法參數使用 @ModelAttribute 說明了該方法參數的值將由model中取得。如果model中找不到,那麼該參數會先被實例化,然後被添加到model中。在model中存在以後,請求中所有名稱匹配的參數都會填充到該參數中。
-它可能因為@SessionAttributes註記的使用已經存在於model中
-它可能因為在同個控制器中使用了@ModelAttribute方法已經存在於model中
-它可能是由URI模板變量和類型轉換中取得的
-它可能是調用了自身的默認構造器被實例化出來的
簡單範例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.jj.learning.springboot.chapter5.controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ModelAttributeController {
@RequestMapping(value = "/ma")
public String modelAttribute(@ModelAttribute("id") String id) {
return "ID:" + id;
}
@RequestMapping(value = "/ma/{id}")
public String modelAttributeByPath(@ModelAttribute("id") String id) {
return "ID:" + id;
}
}
建立RESTful API與單元測試
RESTful API具體設計如下
類型 |
URL |
說明 |
GET |
/customers |
查詢客戶列表 |
POST |
/customers |
新增一個客戶 |
GET |
/customers/id |
根據id查詢一個客戶 |
PUT |
/customers/id |
根據id更新一個客戶 |
DELETE |
/customers/id |
根據id刪除一個客戶 |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.jj.learning.springboot.chapter5.model;
public class Customer {
private Long id;
private String name;
private Integer age;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package com.jj.learning.springboot.chapter5.controller;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.jj.learning.springboot.chapter5.model.Customer;
@RestController
// 這個配置表示下面的網址對應都在/customers下
@RequestMapping(value = "/customers")
public class CustomerController {
// 創建執行緒(thread)安全的Map
static Map<Long, Customer> customers = Collections.synchronizedMap(new HashMap<Long, Customer>());
// 處理"/customers/"的GET請求,查詢客戶列表
@RequestMapping(value = "/", method = RequestMethod.GET)
public List<Customer> getCustomerList() {
List<Customer> r = new ArrayList<Customer>(customers.values());
return r;
}
// 處理"/customers/"的POST請求,新增一個客戶
@RequestMapping(value = "/", method = RequestMethod.POST)
public String postCustomer(@ModelAttribute Customer customer) {
customers.put(customer.getId(), customer);
return "success";
}
// 處理"/customers/{id}"的GET請求,用來獲取URL中該id值的Customer資料
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public Customer getCustomer(@PathVariable Long id) {
return customers.get(id);
}
// 處理"/customers/{id}"的PUT請求,用来更新Customer資料
@RequestMapping(value = "/{id}", method = RequestMethod.PUT)
public String putCustomer(@PathVariable Long id, @ModelAttribute Customer customer) {
Customer u = customers.get(id);
u.setName(customer.getName());
u.setAge(customer.getAge());
customers.put(id, u);
return "success";
}
// 處理"/customers/{id}"的DELETE請求,用来删除Customer
@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
public String deleteUser(@PathVariable Long id) {
customers.remove(id);
return "success";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package com.jj.learning.springboot.chapter5.controller;
import static org.hamcrest.Matchers.equalTo;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
@RunWith(SpringRunner.class)
// 此為測試案例,設定Port為隨機,避免了不必要的Port衝突。
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class CustomerControllerTest {
// 建立一個空的WebApplicationContext
@Autowired
private WebApplicationContext webApplicationContext;
// 建立 MockMvc 的物件
private MockMvc mvc;
@Before
public void setUp() throws Exception {
// 使用 webApplicationContext 建立初始化 MockMvc
mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
@Test
public void testCustomerController() throws Exception {
RequestBuilder request = null;
// 確認目前的Customers是否為空
request = get("/customers/");
mvc.perform(request)
.andExpect(status().isOk())
.andExpect(content().string(equalTo("[]")));
// 新增一個Customer
request = post("/customers/")
.param("id", "1")
.param("name", "jjHuang")
.param("age", "99");
// 確認Customer是否新增成功
mvc.perform(request)
.andExpect(content().string(equalTo("success")));
// 取得目前Customer清單,確認剛剛新增的客戶是否存在
request = get("/customers/");
mvc.perform(request)
.andExpect(status().isOk())
.andExpect(content().string(equalTo("[{\"id\":1,\"name\":\"jjHuang\",\"age\":99}]")));
// 根據id更新客戶資料
request = put("/customers/1")
.param("name", "newJJHuang")
.param("age", "18");
// 確認Customer是否更新成功
mvc.perform(request)
.andExpect(content().string(equalTo("success")));
// 根據id取得Customer,確認更新資料是否正確
request = get("/customers/");
mvc.perform(request)
.andExpect(status().isOk())
.andExpect(content().string(equalTo("[{\"id\":1,\"name\":\"newJJHuang\",\"age\":18}]")));
// 根據id刪除Customer
request = delete("/customers/1");
// 確認Customer是否刪除成功
mvc.perform(request)
.andExpect(content().string(equalTo("success")));
// 最後確認目前的Customers是否為空
request = get("/customers/");
mvc.perform(request)
.andExpect(status().isOk())
.andExpect(content().string(equalTo("[]")));
}
}
