跳到主要内容
Spring Boot RESTful API 开发与测试实战 | 极客日志
Java java
Spring Boot RESTful API 开发与测试实战 Spring Boot RESTful API 开发涉及资源设计、分层架构实现及自动化测试。了 Controller-Service-Repository 标准结构,展示了如何使用 JPA 和 H2 快速构建数据接口。重点对比了单元测试、集成测试与 Mock 测试的适用场景,并深入探讨了 Spring Security 基础认证与 JWT 无状态鉴权的实现差异。通过修复代码格式、优化注释及整合测试用例,提供了一套可直接参考的生产级开发模板,帮助开发者高效构建安全可靠的 Web 服务。
活在当下 发布于 2026/3/29 更新于 2026/6/17 16 浏览Spring Boot RESTful API 开发与测试
核心概念与 HTTP 规范
RESTful API 是目前 Java 后端开发的主流设计风格。它基于 REST 架构风格,核心在于资源(Resource)的抽象、表现层(Representation)的标准化以及状态转移(State Transfer)。
在设计时,我们主要关注两点:
URI 设计 :用路径表示资源,例如 /api/products。
HTTP 方法 :用动词表示操作,GET 获取,POST 创建,PUT 更新,DELETE 删除。PATCH 用于部分更新。
响应状态码同样关键,200 代表成功,201 是资源创建成功,400 到 500 系列则对应客户端错误或服务器异常。理解这些基础,才能写出规范的接口。
项目搭建与分层开发
开发一个标准的 Spring Boot RESTful 服务,通常遵循 Controller-Service-Repository 的分层架构。这样能保持代码清晰,便于维护。
依赖配置
首先,在 pom.xml 中引入必要的依赖。Web 模块负责处理请求,Data JPA 简化数据库操作,H2 作为内存数据库方便演示,测试依赖必不可少。
<dependencies >
<dependency >
<groupId > org.springframework.boot</groupId >
<artifactId > spring-boot-starter-web</artifactId >
</dependency >
<dependency >
<groupId > org.springframework.boot</groupId >
<artifactId > spring-boot-starter-data-jpa</artifactId >
</dependency >
com.h2database
h2
runtime
org.springframework.boot
spring-boot-starter-test
test
<dependency >
<groupId >
</groupId >
<artifactId >
</artifactId >
<scope >
</scope >
</dependency >
<dependency >
<groupId >
</groupId >
<artifactId >
</artifactId >
<scope >
</scope >
</dependency >
</dependencies >
实体类定义 实体类映射数据库表。注意使用 JPA 注解来描述字段关系和主键策略。
import javax.persistence.*;
@Entity
@Table(name = "product")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String productId;
private String productName;
private double price;
private int sales;
public Product () {}
public Product (String productId, String productName, double price, int sales) {
this .productId = productId;
this .productName = productName;
this .price = price;
this .sales = sales;
}
public Long getId () { return id; }
public void setId (Long id) { this .id = id; }
public String getProductId () { return productId; }
public void setProductId (String productId) { this .productId = productId; }
public String getProductName () { return productName; }
public void setProductName (String productName) { this .productName = productName; }
public double getPrice () { return price; }
public void setPrice (double price) { this .price = price; }
public int getSales () { return sales; }
public void setSales (int sales) { this .sales = sales; }
@Override
public String toString () {
return "Product{" +
"id=" + id +
", productId='" + productId + '\'' +
", productName='" + productName + '\'' +
", price=" + price +
", sales=" + sales +
'}' ;
}
}
数据访问层 (Repository) 继承 JpaRepository 可以自动获得基础的 CRUD 能力。如果需要自定义查询,可以直接在接口方法名中约定规则。
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface ProductRepository extends JpaRepository <Product, Long> {
List<Product> findBySalesGreaterThan (int sales) ;
}
业务逻辑层 (Service) Service 层负责事务管理和复杂逻辑。注意加上 @Transactional 注解,确保数据一致性。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;
@Transactional
public void addProduct (Product product) {
productRepository.save(product);
}
@Transactional
public void updateProduct (Product product) {
productRepository.save(product);
}
@Transactional
public void deleteProduct (Long id) {
productRepository.deleteById(id);
}
@Transactional(readOnly = true)
public List<Product> getAllProducts () {
return productRepository.findAll();
}
@Transactional(readOnly = true)
public List<Product> getTopSellingProducts (int topN) {
List<Product> products = productRepository.findBySalesGreaterThan(0 );
products.sort((p1, p2) -> p2.getSales() - p1.getSales());
if (products.size() > topN) {
return products.subList(0 , topN);
}
return products;
}
}
控制层 (Controller) Controller 暴露 HTTP 接口。使用 @RestController 组合了 @Controller 和 @ResponseBody。返回 ResponseEntity 可以更灵活地控制状态码。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/products")
public class ProductController {
@Autowired
private ProductService productService;
@GetMapping("/")
public ResponseEntity<List<Product>> getAllProducts () {
List<Product> products = productService.getAllProducts();
return new ResponseEntity <>(products, HttpStatus.OK);
}
@PostMapping("/")
public ResponseEntity<Void> addProduct (@RequestBody Product product) {
productService.addProduct(product);
return new ResponseEntity <>(HttpStatus.CREATED);
}
@PutMapping("/{id}")
public ResponseEntity<Void> updateProduct (@PathVariable Long id, @RequestBody Product product) {
product.setId(id);
productService.updateProduct(product);
return new ResponseEntity <>(HttpStatus.OK);
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteProduct (@PathVariable Long id) {
productService.deleteProduct(id);
return new ResponseEntity <>(HttpStatus.NO_CONTENT);
}
@GetMapping("/top-selling")
public ResponseEntity<List<Product>> getTopSellingProducts (@RequestParam int topN) {
List<Product> products = productService.getTopSellingProducts(topN);
return new ResponseEntity <>(products, HttpStatus.OK);
}
}
自动化测试策略 测试是保证代码质量的关键。Spring Boot 提供了丰富的测试支持,主要分为单元测试、集成测试和 Mock 测试。
单元测试 单元测试关注单个 Service 方法的功能。不需要启动整个 Web 容器,速度最快。
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest
class ProductServiceTests {
@Autowired
private ProductService productService;
@Test
void testAddProduct () {
Product product = new Product ("P006" , "平板" , 2000.0 , 70 );
productService.addProduct(product);
List<Product> products = productService.getAllProducts();
assertThat(products).hasSize(6 );
}
}
集成测试 集成测试验证 Controller 与数据库、网络层的交互。使用 RANDOM_PORT 模拟真实环境。
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ProductControllerTests {
@LocalServerPort
private int port;
@Autowired
private TestRestTemplate restTemplate;
@Test
void testGetAllProducts () {
List<Product> products = restTemplate.getForObject(
"http://localhost:" + port + "/api/products/" , List.class);
assertThat(products).hasSize(5 );
}
}
Mock 测试 Mock 测试专注于 Controller 层,隔离 Service 依赖。使用 @WebMvcTest 和 @MockBean 可以快速验证请求路由和参数绑定,无需连接数据库。
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import java.util.Arrays;
import java.util.List;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@WebMvcTest(ProductController.class)
class ProductControllerMockTests {
@Autowired
private MockMvc mockMvc;
@MockBean
private ProductService productService;
@Test
void testGetAllProducts () throws Exception {
List<Product> products = Arrays.asList(
new Product ("P001" , "手机" , 1000.0 , 100 ),
new Product ("P002" , "电脑" , 5000.0 , 50 )
);
when (productService.getAllProducts()).thenReturn(products);
mockMvc.perform(get("/api/products/" ))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON));
verify(productService, times(1 )).getAllProducts();
}
}
安全认证与授权 生产环境的 API 必须考虑安全性。Spring Security 提供了强大的框架,支持 Basic Auth 和 JWT 等多种模式。
Spring Security 基础 通过配置类设置用户认证和权限控制。这里演示了简单的内存用户配置。
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure (AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.passwordEncoder(NoOpPasswordEncoder.getInstance())
.withUser("admin" ).password("admin123" ).roles("ADMIN" )
.and()
.withUser("user" ).password("user123" ).roles("USER" );
}
@Override
protected void configure (HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/api/products/top-selling" ).hasRole("ADMIN" )
.antMatchers("/api/products/**" ).hasRole("USER" )
.and().httpBasic();
}
}
JWT 无状态认证 对于前后端分离的项目,JWT (JSON Web Token) 更为常用。客户端登录后获取 Token,后续请求携带该 Token 即可。
需要引入 jjwt 依赖,并编写工具类生成和解析 Token。
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
@Component
public class JwtUtil {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
public String generateToken (String username) {
Map<String, Object> claims = new HashMap <>();
return createToken(claims, username);
}
private String createToken (Map<String, Object> claims, String subject) {
return Jwts.builder()
.setClaims(claims)
.setSubject(subject)
.setIssuedAt(new Date (System.currentTimeMillis()))
.setExpiration(new Date (System.currentTimeMillis() + expiration * 1000 ))
.signWith(SignatureAlgorithm.HS256, secret)
.compact();
}
public Boolean validateToken (String token, String username) {
final String extractedUsername = extractUsername(token);
return (extractedUsername.equals(username) && !isTokenExpired(token));
}
}
配合过滤器 JwtRequestFilter 拦截请求,解析 Token 并设置 Spring Security 上下文,即可实现无状态认证。
实际应用场景 RESTful API 的应用非常广泛,从商品管理到订单系统,再到博客平台,核心逻辑都是资源的增删改查。
在实际项目中,建议根据业务复杂度选择合适的测试策略和安全方案。例如,内部管理系统可用 Basic Auth,而面向公众的 App 则推荐 JWT。
运行应用后,访问 http://localhost:8080/api/products/ 即可查看产品列表,带上 Authorization 头可测试权限控制效果。
总结 本章涵盖了 Spring Boot RESTful API 的全流程开发。从 REST 规范的理解,到分层架构的代码实现,再到多层次的测试策略和安全机制,这些都是构建高质量后端服务的基石。掌握这些内容,你就能从容应对大多数 Java Web 开发需求。
相关免费在线工具 Keycode 信息 查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
Escape 与 Native 编解码 JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
JavaScript / HTML 格式化 使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
JavaScript 压缩与混淆 Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online