53、Spring Boot 详细讲义(十)(Spring Boot 高级主题)
Spring Boot 高级主题
一、Spring Boot 与 RESTful API 讲义
1. 引言
在现代软件开发中,RESTful API 已经成为构建分布式系统和微服务架构的主要方式。Spring Boot通过内置的支持和简化的配置,帮助开发者快速构建高效、灵活的RESTful API。本讲将详细介绍如何在Spring Boot中设计和实现RESTful API,包括设计原则、实现方法、最佳实践以及相关的安全认证和文档生成。
2. RESTful API 设计原则
2.1 Resource(资源)概念
RESTful API的核心是资源,所有的操作都是围绕资源展开的。资源可以是实体(如用户、产品),也可以是操作(如生成报告)。
示例
- 实体资源:
/users
表示用户资源。 - 操作资源:
/reports/generate
表示生成报告的操作。
2.2 URI 设计
- 使用名词来表示资源,尽量使用单数形式。
- 使用路径分段来表示资源之间的层次关系。
- 使用查询参数来表示过滤、排序等操作。
示例
// 查询所有用户
GET /users // 根据ID查询单个用户
GET /users/{id} // 根据用户名查询用户
GET /users?username=test
2.3 HTTP 方法
HTTP方法用于表示操作类型:
- GET:查询资源。
- POST:创建新资源。
- PUT:更新资源。
- DELETE:删除资源。
- PATCH:部分更新资源。
示例
// 创建新用户
POST /users // 更新用户信息
PUT /users/{id} // 删除用户
DELETE /users/{id}
2.4 状态码
使用适当的HTTP状态码表示操作结果:
- 200 OK:请求成功。
- 201 Created:资源创建成功。
- 400 Bad Request:请求格式错误。
- 404 Not Found:资源不存在。
- 500 Internal Server Error:服务器内部错误。
2.5 版本控制
为了兼容性和向后兼容性,可以对API进行版本控制。
常见版本控制方式
-
URI版本控制
GET /api/v1/users
-
自定义头版本控制
GET /users Accept: application/vnd.myapp.v1+json
3. Spring Boot 实现 RESTful API
3.1 项目设置
3.1.1 添加依赖
在pom.xml
中添加Spring Boot Web依赖。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId>
</dependency>
3.1.2 项目结构
com.example.springbootrest
├── controller
│ └── UserController.java
├── service
│ └── UserService.java
├── repository
│ └── UserRepository.java
├── model
│ └── User.java
└── exception └── ResourceNotFoundException.java
3.2 控制器实现
3.2.1 基本CRUD操作
@RestController
@RequestMapping("/api/users")
public class UserController { @Autowired private UserService userService; // 查询所有用户 @GetMapping public List<User> findAll() { return userService.findAll(); } // 根据ID查询用户 @GetMapping("/{id}") public User findOne(@PathVariable Long id) { return userService.findOne(id); } // 创建用户 @PostMapping public User createUser(@RequestBody User user) { return userService.createUser(user); } // 更新用户 @PutMapping("/{id}") public User updateUser(@PathVariable Long id, @RequestBody User user) { return userService.updateUser(id, user); } // 删除用户 @DeleteMapping("/{id}") public void deleteUser(@PathVariable Long id) { userService.deleteUser(id); }
}
3.2.2 支持分页和排序
// 分页查询
@GetMapping
public Page<User> findAll(Pageable pageable) { return userService.findAll(pageable);
} // 自定义排序
@GetMapping("/sorted")
public List<User> findAll(@RequestParam(defaultValue = "id") String[] sort) { return userService.findAll(Sort.by(sort));
}
3.3 服务层实现
3.3.1 业务逻辑
@Service
public class UserService { @Autowired private UserRepository userRepository; public List<User> findAll() { return userRepository.findAll(); } public User findOne(Long id) { return userRepository.findById(id) .orElseThrow(() -> new ResourceNotFoundException("User not found with id: " + id)); } public User createUser(User user) { return userRepository.save(user); } public User updateUser(Long id, User user) { User existingUser = findOne(id); existingUser.setName(user.getName()); existingUser.setEmail(user.getEmail()); return userRepository.save(existingUser); } public void deleteUser(Long id) { userRepository.deleteById(id); }
}
3.3.2 异常处理
@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException { public ResourceNotFoundException(String message) { super(message); }
}
3.4 Repository 层实现
这里使用Spring Data JPA实现
public interface UserRepository extends JpaRepository<User, Long> { // 根据用户名查询用户 Optional<User> findByUsername(String username);
}
4. 高级功能
4.1 异步处理
在Spring Boot中,可以使用@Async
注解实现异步处理,避免阻塞主线程。
示例
@Service
public class AsyncService { @Async public void asyncTask() { // 异步执行的任务逻辑 System.out.println("Async task executed."); }
}
4.2 数据验证
使用Hibernate Validator对请求参数进行验证。
示例
public class User { @NotEmpty(message = "Username cannot be empty") private String username; @Email(message = "Invalid email format") private String email; // getters and setters
}
4.3 文件上传和下载
4.3.1 文件上传
@PostMapping("/upload")
public String uploadFile(@RequestParam("file") MultipartFile file) { try { byte[] bytes = file.getBytes(); Path path = Paths.get(UPLOAD_FOLDER + file.getOriginalFilename