跳到主要内容
极客日志极客日志面向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/7/3029 浏览
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. 总结
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • OpenClaw 多飞书机器人与多 Agent 团队实战复盘
  • WebView 冷启动并发初始化竞争风险分析
  • OpenClaw 多 Agent 与多飞书机器人配置指南
  • SpringAI Agent 实战:利用 Skills 构建代码评审智能体
  • GLM-4.6V-Flash-WEB 食品包装识别准确性验证
  • OpenClaw 30+ 真实场景全拆解:AI Agent 落地实践
  • Copilot 与 Codex 中文乱码问题自动化修复方案
  • OpenClaw 开源 AI 执行引擎:架构、安装与实战详解
  • 利用腾讯云 HAI 与 DeepSeek 快速搭建个人网页
  • AI 辅助生成专业级 UI 工具:UI UX Pro Max 实战指南
  • QClaw 接入微信背后的 AI 进化:从对话到任务执行
  • C++ 测试与调试实战:保障代码质量与稳定性
  • 未岚大陆 CES 2026 发布 Navimow 标准:零转全驱与激光雷达技术解析
  • YOLO26:Ultralytics 发布端到端视觉 AI 新突破
  • WPF + .NET6 WebAPI + SqlSugar 权限管理系统架构与实现
  • 阿里开源 PageAgent:让 AI 住进网页,用自然语言操控界面
  • 机器学习常见名词汇总
  • 基于 OpenClaw 搭建 QQ AI 办公机器人:关键词触发与邮件集成
  • WhisperX:70 倍实时语音转录、词级时间戳与多说话人分离技术
  • Flutter 三方库 flutter_dropzone 的鸿蒙化适配指南

相关免费在线工具

  • 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