Ngày 04 tháng 6 năm 2023 - Máy tính
Trong bài viết trước “Cách nhanh chóng thiết lập một dự án Spring Boot”, chúng ta đã tìm hiểu cách sử dụng Spring Initializr để tạo cấu trúc mẫu của dự án Spring Boot. Trong bài viết này, chúng ta sẽ tiếp tục tìm hiểu cách sử dụng Spring Boot để xây dựng một dịch vụ Web RESTful, tập trung vào cấu trúc dự án, cách sử dụng chú thích (annotations) và việc viết mã kiểm thử đơn vị (unit test), đồng thời khám phá triết lý thiết kế và cách sử dụng của Spring Boot.
Dịch vụ RESTful được thực hiện trong bài viết này cung cấp các chức năng thêm, xóa, sửa và tra cứu người dùng (User). Dữ liệu được lưu trữ bên trong sử dụng Java ArrayList.
Khi viết bài này, các phiên bản được sử dụng là:
- Phiên bản JDK: BellSoft Liberica JDK 17
- Phiên bản Maven: 3.9.2
- Phiên bản Spring Boot: 3.1.0
1. Cấu trúc Dự án
Bài viết này sử dụng kiến trúc ba lớp cho mã nguồn, với ba thư mục chính dưới src/main/java
: controller
, model
và service
. Các logic xử lý yêu cầu và phản hồi cần được đặt trong thư mục controller
; các lớp POJO đại diện cho đối tượng dữ liệu cần được đặt trong thư mục model
; và logic kinh doanh chính cần được trích xuất vào dịch vụ và sau đó đặt trong thư mục service
(thư mục service
chứa các giao diện, còn thư mục impl
chứa các lớp triển khai).
spring-boot-restful-service-demo
├─ src/main/java
│ └─ com.example.demo
│ ├─ controller
│ │ └─ UserController.java
│ ├─ model
│ │ └─ User.java
│ ├─ service
│ │ ├─ UserService.java
│ │ └─ impl
│ │ └─ UserServiceImpl.java
│ └─ DemoApplication.java
├─ src/test/java
│ └─ com.example.demo
│ └─ controller
│ └─ UserControllerTest.java
└─ pom.xml
Ngoài ra, src/test/java
được sử dụng để lưu trữ mã kiểm thử, mã kiểm thử nên có cùng tên gói với mã được kiểm thử.
2. Phân tích Mã nguồn
Sau đây chúng ta sẽ phân tích mã nguồn của dự án này để hiểu rõ hơn về cách sử dụng Spring Boot.
2.1 Mã nguồn pom.xml
Spring Boot cung cấp nhiều Starter đã đóng gói sẵn (được đặt tên theo định dạng spring-boot-starter-*
) để chúng ta sử dụng. Khi cần một phụ thuộc nào đó, chỉ cần tham chiếu Starter tương ứng trong pom.xml
.
Dự án này sử dụng Maven để quản truc tiep socolive lý phụ thuộc, mã nguồn pom.xml
như sau:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns=" xmlns:xsi="
xsi:schemaLocation="
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.0</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Như có thể thấy, ví dụ này sử dụng ba Starter: spring-boot-starter-web
, spring-boot-starter-validation
và spring-boot-starter-test
.
spring-boot-starter-web
bao gồm tất cả các phụ thuộc liên quan đến việc viết chương trình Spring Web, chẳng hạn như phụ thuộc liên quan đến giao diện RESTful, phụ thuộc liên quan đến Spring MVC, và phụ thuộc liên quan đến máy chủ thời gian chạy (mặc định là Apache Tomcat);spring-boot-starter-validation
bao gồm tất cả các phụ thuộc liên quan đến việc xác thực tham số yêu cầu;spring-boot-starter-test
bao gồm tất cả các phụ thuộc kiểm thử chương trình Spring Boot, chẳng hạn như JUnit Jupiter, Hamcrest và Mockito.
Ngoài ra, một plugin spring-boot-maven-plugin
cũng được sử dụng, cung cấp hỗ trợ cho việc đóng gói và chạy chương trình.
2.2 Mã nguồn Lớp Khởi động
Lớp khởi động chương trình src/main/java/com/example/demo/DemoApplication.java
có mã nguồn như sau:
package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
Từ lớp khởi động này, có thể thấy rằng ứng dụng Spring Boot không cần các tệp cấu hình dài dòng như web.xml
, mà 365 com ca cuoc có thể cấu hình bằng cách sử dụng chú thích thuần Java.
Có thể thấy, lớp này chỉ sử dụng một chú thích duy nhất: @SpringBootApplication
, đây là một chú thích tiện lợi, bao gồm ba chú thích sau:
@Configuration
: dùng để định nghĩa lớp cấu hình;@EnableAutoConfiguration
: dùng để tự động tải vào tất cả các Bean mà ứng dụng cần;@ComponentScan
: quét đường dẫn đã chỉ định, lắp ráp các lớp vào môi trường Spring.
2.3 Mã nguồn Controller
Lớp điều khiển src/main/java/com/example/demo/controller/UserController.java
có mã nguồn như sau:
package com.example.demo.controller; import com.example.demo.model.User; import com.example.demo.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping("/users") public class UserController { @Autowired private UserService userService; @GetMapping("/") public List<User> getUsers() { return userService.getUsers(); } @GetMapping("/{id}") public User getUser(@PathVariable("id") Integer id) { return userService.getUser(id); } @PostMapping("/") @ResponseStatus(HttpStatus.CREATED) public void addUser(@RequestBody @Validated User user) { userService.addUser(user); } @PatchMapping("/") @ResponseStatus(HttpStatus.NO_CONTENT) public void updateUser(@RequestBody @Validated User user) { userService.updateUser(user); } @DeleteMapping("/{id}") @ResponseStatus(HttpStatus.NO_CONTENT) public void deleteUser(@PathVariable("id") Integer id) { userService.deleteUser(id); } }
Trong lớp này:
- Chú thích
@RestController
tương đương với việc kết hợp hai chú thích@Controller
và@ResponseBody
, biểu thị rằng lớp này cung cấp giao diện Web, các phương thức trong lớp này sẽ xử lý yêu cầu HTTP và phản hồi JSON cho khách hàng; - Chú thích
@GetMapping
biểu thị rằng phương thức nhận yêu cầu HTTP GET, trong khi@PostMapping
,@PatchMapping
và@DeleteMapping
lần lượt biểu thị rằng phương thức nhận yêu cầu POST, PATCH và DELETE; - Chú thích
@ResponseStatus
được sử dụng để chỉ định mã trạng thái HTTP trả về; - Chú thích
@RequestBody
được sử dụng để chỉ định đối tượng nhận từ thân JSON yêu cầu,@PathVariable
được sử dụng để lấy giá trị tham số tương ứng từ đường dẫn URL.
2.4 Mã nguồn Service
Giao diện dịch vụ src/main/java/com/example/demo/service/UserService.java
có mã nguồn như sau:
package com.example.demo.service; import com.example.demo.model.User; import java.util.List; public interface UserService { List<User> getUsers(); User getUser(Integer id); void addUser(User user); void updateUser(User user); void deleteUser(Integer id); }
Lớp triển khai dịch vụ src/main/java/com/example/demo/service/impl/UserServiceImpl.java
có mã nguồn như sau:
package com.example.demo.service.impl; import com.example.demo.model.User; import com.example.demo.service.UserService; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; @Service public class UserServiceImpl implements UserService { private static final List<User> USERS = new ArrayList<>(); @Override public List<User> getUsers() { return USERS; } @Override public User getUser(Integer id) { int i = findUserIndex(id); if (i < USERS.size()) { return USERS.get(i); } return null; } @Override public void addUser(User user) { USERS.add(user); } @Override public void updateUser(User user) { int i = findUserIndex(user.id()); // update if (i < USERS.size()) { USERS.set(i, user); } } @Override public void deleteUser(Integer id) { int i = findUserIndex(id); // update if (i < USERS.size()) { USERS.remove(i); } } private int findUserIndex(Integer userId) { int i = 0; for (; i < USERS.size(); i++) { if (USERS.get(i).id().equals(userId)) { break; } } return i; } }
Có thể thấy, Service sử dụng ArrayList
để lưu trữ dữ liệu và cung cấp hỗ trợ cho các hoạt động thêm, xóa, sửa và tra cứu người dùng.
3. Chạy và Kiểm thử Chương trình
Mở dòng lệnh, trong thư mục gốc của chương trình, thực thi lệnh Maven sau để khởi động ứng dụng:
mvn spring-boot:run
Sau khi chương trình khởi động, thực thi các lệnh CURL sau để tạo ba người dùng:
curl -X POST -H "Content-Type: application/json" -d '{"id": 1, "name": "Larry", "age": 28}'
curl -X POST -H "Content-Type: application/json" -d '{"id": 2, "name": "Lucy", "age": 18}'
curl -X POST -H "Content-Type: application/json" -d '{"id": 3, "name": "Jacky", "age": 30}'
Thực thi lệnh CURL sau để truy vấn tất cả người dùng:
curl -X GET
[{"id":1,"name":"Larry","age":28},{"id":2,"name":"Lucy","age":18},{"id":3,"name":"Jacky","age":30}]
Thực thi lệnh CURL sau để cập nhật người dùng có ID là 3:
curl -X PATCH -H "Content-Type: application/json" -d '{"id": 3, "name": "Alan", "age": 29}'
Thực thi lệnh CURL sau để truy vấn người dùng có ID là 3, phát hiện thông tin đã được cập nhật:
curl -X GET
{"id":3,"name":"Alan","age":29}
Thực thi lệnh CURL sau để xóa iwin68.club phiên bản mới người dùng có ID là 3:
curl -X DELETE
Truy vấn lại tất cả người dùng, phát hiện người dùng có ID là 3 đã bị xóa:
curl -X GET
[{"id":1,"name":"Larry","age":28},{"id":2,"name":"Lucy","age":18}]
4. Thêm Mã Kiểm thử Đơn vị
Lớp kiểm thử điều khiển src/test/java/com/example/demo/controller/UserControllerTest.java
có mã nguồn như sau:
package com.example.demo.controller; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @SpringBootTest @AutoConfigureMockMvc public class UserControllerTest { @Autowired private MockMvc mvc; @BeforeEach public void createUser() throws Exception { String body = "{\"id\": 1, \"name\": \"Larry\", \"age\": 28}"; mvc.perform(MockMvcRequestBuilders.post("/users/").contentType(MediaType.APPLICATION_JSON).content(body)).andExpect(status().isCreated()); } @Test public void testGetUsers() throws Exception { mvc.perform(MockMvcRequestBuilders.get("/users/")).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$").isArray()); } @Test public void testGetUser() throws Exception { mvc.perform(MockMvcRequestBuilders.get("/users/{id}", 1)).andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$.name").value("Larry")); } @Test public void testAddUser() throws Exception { String body = "{\"id\": 2, \"name\": \"Lucy\", \"age\": 18}"; mvc.perform(MockMvcRequestBuilders.post("/users/").contentType(MediaType.APPLICATION_JSON).content(body)).andExpect(status().isCreated()); } @Test public void testUpdateUser() throws Exception { String body = "{\"id\": 1, \"name\": \"Larry\", \"age\": 29}"; mvc.perform(MockMvcRequestBuilders.patch("/users/").contentType(MediaType.APPLICATION_JSON).content(body)).andExpect(status().isNoContent()); } @Test public void testDeleteUser() throws Exception { mvc.perform(MockMvcRequestBuilders.delete("/users/{id}", 1)).andExpect(status().isNoContent()); } }
Có thể thấy, mã trên sử dụng MockMvc
để thực hiện kiểm thử đơn vị cho UserController
.
Tóm lại, bài viết này đã hoàn thành việc thiết lập dịch vụ RESTful bằng Spring Boot, hiểu được ý nghĩa và cách sử dụng của các chú thích phổ biến trong Spring Boot. Mã nguồn đầy đủ của dự án này đã được lưu trữ trên GitHub, chào mừng mọi người theo dõi hoặc Fork.
[1] Building a RESTful Web Service | Spring - spring.io [2] Building an Application with Spring Boot | Spring - spring.io [3] Spring Initializr | Spring - spring.io [4] Spring Boot | Spring - spring.io ![]( #Java #Spring #Maven