跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava

Spring Boot RESTful API 开发与测试实战

Spring Boot RESTful API 开发涉及资源设计、分层架构实现及自动化测试。了 Controller-Service-Repository 标准结构,展示了如何使用 JPA 和 H2 快速构建数据接口。重点对比了单元测试、集成测试与 Mock 测试的适用场景,并深入探讨了 Spring Security 基础认证与 JWT 无状态鉴权的实现差异。通过修复代码格式、优化注释及整合测试用例,提供了一套可直接参考的生产级开发模板,帮助开发者高效构建安全可靠的 Web 服务。

活在当下发布于 2026/3/29更新于 2026/10/1580 浏览
Spring Boot RESTful API 开发与测试实战

Spring Boot RESTful API 开发与测试

RESTful API 架构示意图

核心概念与 HTTP 规范

RESTful API 是目前 Java 后端开发的主流设计风格。它基于 REST 架构风格,核心在于资源(Resource)的抽象、表现层(Representation)的标准化以及状态转移(State Transfer)。

在设计时,我们主要关注两点:

  1. URI 设计:用路径表示资源,例如 /api/products。
  2. HTTP 方法:用动词表示操作,GET 获取,POST 创建,PUT 更新,DELETE 删除。PATCH 用于部分更新。

响应状态码同样关键,200 代表成功,201 是资源创建成功,400 到 500 系列则对应客户端错误或服务器异常。理解这些基础,才能写出规范的接口。

项目搭建与分层开发

开发一个标准的 Spring Boot RESTful 服务,通常遵循 Controller-Service-Repository 的分层架构。这样能保持代码清晰,便于维护。

依赖配置

首先,在 pom.xml 中引入必要的依赖。Web 模块负责处理请求,Data JPA 简化数据库操作,H2 作为内存数据库方便演示,测试依赖必不可少。

<dependencies>
    <!-- Web 依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- Data JPA 依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <!-- H2 数据库依赖 -->
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>runtime</scope>
    </dependency>
    <!-- 测试依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</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;

    // 构造器、Getter 和 Setter 省略
    public Product() {}
    
    public Product(String productId, String productName, double price, int sales) {
        this.productId = productId;
        this.productName = productName;
        this.price = price;
        this.sales = sales;
    }
    
    // Getter and Setters...
    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 开发需求。

目录

  1. Spring Boot RESTful API 开发与测试
  2. 核心概念与 HTTP 规范
  3. 项目搭建与分层开发
  4. 依赖配置
  5. 实体类定义
  6. 数据访问层 (Repository)
  7. 业务逻辑层 (Service)
  8. 控制层 (Controller)
  9. 自动化测试策略
  10. 单元测试
  11. 集成测试
  12. Mock 测试
  13. 安全认证与授权
  14. Spring Security 基础
  15. JWT 无状态认证
  16. 实际应用场景
  17. 总结

更多推荐文章

查看全部
  • 代码与数据开源:提升论文接收率与引用率的隐形要求
  • Qwen3-Embedding-4B 推荐方案:llama.cpp 集成部署教程
  • 大模型基本概念详解:定义、分类与发展历程
  • 预训练语言模型与BERT实战应用
  • GitHub 镜像加速实战:FastGit 与 Zhimg 部署指南
  • FPGA实现UART串口通信
  • Android System WebView 版本对照与内核更新指南
  • GitHub Copilot 网络代理配置与优化实战指南
  • VS Code 集成 Git 使用指南:从配置到协作实战
  • ESLint 深度指南:从原理配置到工程化实践
  • 在 Windows 上安装 Claude Code 并接入智谱模型
  • Qwen3.5 核心特性详解:原生多模态与 Agent 能力解析
  • 超越 import 与 pip:深入 Python 模块化与依赖管理核心
  • 推荐系统基本原理与协同过滤算法详解
  • NISP 证书含金量深度解析与报考指南
  • GitHub 学生认证与 PyCharm 配置 Copilot 流程指南
  • 解决 VS Code 中 GitHub Copilot 模型不可用问题
  • KaiwuDB 3.1.0 在 Ubuntu 22.04 单机部署实战:TLS 配置与性能基线
  • CMake与Abseil整合:现代C++工程依赖管理实践
  • Java 大数据在智能家居能源消耗趋势预测与节能策略优化中的应用

相关免费在线工具

  • 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