当前位置: 首页 > news >正文

【SpringBoot】Spring Boot + RESTful 技术实战指南

在当今的软件开发领域,Spring Boot 与 RESTful API 的结合已成为构建高效、可扩展 Web 应用的标配。本文将通过一个完整的项目示例,从知识铺垫到部署上线,带你一步步掌握 Spring Boot + RESTful 的开发流程。

一、知识铺垫

1.1 Spring Boot 简介

Spring Boot 是基于 Spring 框架的快速开发工具,它简化了 Spring 应用的初始搭建和开发过程。通过自动配置和起步依赖,开发者可以快速构建独立运行的、生产级别的 Spring 应用。

核心优势

  • 自动配置:根据项目依赖自动配置 Spring 组件,减少手动配置。
  • 起步依赖:提供预定义的依赖组合,简化依赖管理。
  • 嵌入式服务器:内置 Tomcat、Jetty 等服务器,无需额外部署。

1.2 RESTful API 简介

RESTful 是一种基于 HTTP 协议的软件架构风格,它强调资源的无状态操作和统一的接口设计。RESTful API 通过 HTTP 方法(GET、POST、PUT、DELETE 等)对资源进行操作,具有简洁、易扩展的特点。

核心原则

  • 资源:将数据抽象为资源,通过 URI 唯一标识。
  • 无状态:每个请求包含所有必要信息,服务器不存储客户端状态。
  • 统一接口:使用标准的 HTTP 方法对资源进行操作。

二、技术介绍

2.1 技术栈选择

  • Spring Boot:作为后端框架,提供快速开发和自动配置能力。
  • Spring Data JPA:简化数据库操作,支持 ORM 映射。
  • H2 数据库:内存数据库,便于开发和测试。
  • JUnit 5:单元测试框架。
  • Maven:项目构建和依赖管理工具。

2.2 项目需求

我们将开发一个简单的图书管理系统,提供以下 RESTful API:

  • 获取所有图书:GET /api/books
  • 根据 ID 获取图书:GET /api/books/{id}
  • 添加图书:POST /api/books
  • 更新图书:PUT /api/books/{id}
  • 删除图书:DELETE /api/books/{id}

三、开发环境搭建

3.1 创建 Spring Boot 项目

使用 Spring Initializr 快速生成项目骨架(也可以建立maven项目手动配置pom文件):

  • Project:Maven Project
  • Language:Java
  • Spring Boot:这里使用最新稳定版
  • Dependencies:添加 Spring WebSpring Data JPAH2 Database

3.2 项目结构

src/
├── main/
│   ├── java/com/example/bookstore/
│   │   ├── controller/      # 控制器层
│   │   ├── model/           # 实体类
│   │   ├── repository/      # 数据访问层
│   │   └── BookstoreApplication.java  # 主启动类
│   └── resources/
│       ├── application.properties  # 配置文件
│       └── data.sql       # 初始化数据(可选)
└── test/                    # 测试代码

3.3 配置文件

application.properties 中配置 H2 数据库:

# H2 数据库配置
spring.datasource.url=jdbc:h2:mem:bookstore
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.h2.console.enabled=true  # 启用 H2 控制台

四、核心功能实现

4.1 实体类定义

创建 Book 实体类:

package com.example.bookstore.model;import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;@Entity
public class Book {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String title;private String author;private Double price;public Book() {}public Book(String title, String author, Double price) {this.title = title;this.author = author;this.price = price;}// Getters and Setterspublic Long getId() { return id; }public void setId(Long id) { this.id = id; }public String getTitle() { return title; }public void setTitle(String title) { this.title = title; }public String getAuthor() { return author; }public void setAuthor(String author) { this.author = author; }public Double getPrice() { return price; }public void setPrice(Double price) { this.price = price; }
}

4.2 数据访问层

创建 BookRepository 接口,继承 JpaRepository

package com.example.bookstore.repository;import com.example.bookstore.model.Book;
import org.springframework.data.jpa.repository.JpaRepository;public interface BookRepository extends JpaRepository<Book, Long> {// 继承 JpaRepository 后,自动拥有基本的 CRUD 方法,除特殊查询方式外,不用单独写
}

4.3 控制器层

创建 BookController,实现 RESTful API:

package com.example.bookstore.controller;import com.example.bookstore.model.Book;
import com.example.bookstore.repository.BookRepository;
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;
import java.util.Optional;@RestController
@RequestMapping("/api/books")
public class BookController {@Autowiredprivate BookRepository bookRepository;// 获取所有图书@GetMappingpublic List<Book> getAllBooks() {return bookRepository.findAll();}// 根据 ID 获取图书@GetMapping("/{id}")public ResponseEntity<Book> getBookById(@PathVariable Long id) {Optional<Book> book = bookRepository.findById(id);return book.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build());}// 添加图书@PostMappingpublic Book createBook(@RequestBody Book book) {return bookRepository.save(book);}// 更新图书@PutMapping("/{id}")public ResponseEntity<Book> updateBook(@PathVariable Long id, @RequestBody Book bookDetails) {return bookRepository.findById(id).map(book -> {book.setTitle(bookDetails.getTitle());book.setAuthor(bookDetails.getAuthor());book.setPrice(bookDetails.getPrice());Book updatedBook = bookRepository.save(book);return ResponseEntity.ok(updatedBook);}).orElseGet(() -> ResponseEntity.notFound().build());}// 删除图书@DeleteMapping("/{id}")public ResponseEntity<Void> deleteBook(@PathVariable Long id) {return bookRepository.findById(id).map(book -> {bookRepository.delete(book);return ResponseEntity.noContent().<Void>build();}).orElseGet(() -> ResponseEntity.notFound().build());}
}

4.4 初始化数据(可选)

src/main/resources/data.sql 中添加初始化数据:

INSERT INTO book (title, author, price) VALUES ('Spring in Action', 'Craig Walls', 49.99);
INSERT INTO book (title, author, price) VALUES ('Effective Java', 'Joshua Bloch', 45.00);

五、测试与部署

5.1 单元测试

使用 JUnit 5 编写单元测试,测试 BookController

package com.example.bookstore.controller;import com.example.bookstore.model.Book;
import com.example.bookstore.repository.BookRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;import java.util.Arrays;
import java.util.List;
import java.util.Optional;import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;class BookControllerTest {@Mockprivate BookRepository bookRepository;@InjectMocksprivate BookController bookController;@BeforeEachvoid setUp() {MockitoAnnotations.openMocks(this);}@Testvoid getAllBooks_ShouldReturnAllBooks() {// 准备测试数据Book book1 = new Book("Book 1", "Author 1", 10.0);Book book2 = new Book("Book 2", "Author 2", 20.0);List<Book> books = Arrays.asList(book1, book2);// 模拟行为when(bookRepository.findAll()).thenReturn(books);// 调用方法List<Book> result = bookController.getAllBooks();// 验证结果assertEquals(2, result.size());assertEquals("Book 1", result.get(0).getTitle());verify(bookRepository, times(1)).findAll();}@Testvoid getBookById_ShouldReturnBookWhenExists() {// 准备测试数据Book book = new Book("Book 1", "Author 1", 10.0);book.setId(1L);// 模拟行为when(bookRepository.findById(1L)).thenReturn(Optional.of(book));// 调用方法ResponseEntity<Book> response = bookController.getBookById(1L);// 验证结果assertEquals(HttpStatus.OK, response.getStatusCode());assertEquals("Book 1", response.getBody().getTitle());verify(bookRepository, times(1)).findById(1L);}@Testvoid getBookById_ShouldReturnNotFoundWhenBookDoesNotExist() {// 模拟行为when(bookRepository.findById(1L)).thenReturn(Optional.empty());// 调用方法ResponseEntity<Book> response = bookController.getBookById(1L);// 验证结果assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());verify(bookRepository, times(1)).findById(1L);}@Testvoid createBook_ShouldSaveBook() {// 准备测试数据Book book = new Book("New Book", "New Author", 30.0);Book savedBook = new Book("New Book", "New Author", 30.0);savedBook.setId(1L);// 模拟行为when(bookRepository.save(book)).thenReturn(savedBook);// 调用方法Book result = bookController.createBook(book);// 验证结果assertEquals(1L, result.getId());assertEquals("New Book", result.getTitle());verify(bookRepository, times(1)).save(book);}@Testvoid updateBook_ShouldUpdateBookWhenExists() {// 准备测试数据Book existingBook = new Book("Old Book", "Old Author", 10.0);existingBook.setId(1L);Book updatedBookDetails = new Book("Updated Book", "Updated Author", 20.0);Book updatedBook = new Book("Updated Book", "Updated Author", 20.0);updatedBook.setId(1L);// 模拟行为when(bookRepository.findById(1L)).thenReturn(Optional.of(existingBook));when(bookRepository.save(existingBook)).thenReturn(updatedBook);// 调用方法ResponseEntity<Book> response = bookController.updateBook(1L, updatedBookDetails);// 验证结果assertEquals(HttpStatus.OK, response.getStatusCode());assertEquals("Updated Book", response.getBody().getTitle());verify(bookRepository, times(1)).findById(1L);verify(bookRepository, times(1)).save(existingBook);}@Testvoid updateBook_ShouldReturnNotFoundWhenBookDoesNotExist() {// 准备测试数据Book updatedBookDetails = new Book("Updated Book", "Updated Author", 20.0);// 模拟行为when(bookRepository.findById(1L)).thenReturn(Optional.empty());// 调用方法ResponseEntity<Book> response = bookController.updateBook(1L, updatedBookDetails);// 验证结果assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());verify(bookRepository, times(1)).findById(1L);verify(bookRepository, never()).save(any());}@Testvoid deleteBook_ShouldDeleteBookWhenExists() {// 准备测试数据Book book = new Book("Book 1", "Author 1", 10.0);book.setId(1L);// 模拟行为when(bookRepository.findById(1L)).thenReturn(Optional.of(book));doNothing().when(bookRepository).delete(book);// 调用方法ResponseEntity<Void> response = bookController.deleteBook(1L);// 验证结果assertEquals(HttpStatus.NO_CONTENT, response.getStatusCode());verify(bookRepository, times(1)).findById(1L);verify(bookRepository, times(1)).delete(book);}@Testvoid deleteBook_ShouldReturnNotFoundWhenBookDoesNotExist() {// 模拟行为when(bookRepository.findById(1L)).thenReturn(Optional.empty());// 调用方法ResponseEntity<Void> response = bookController.deleteBook(1L);// 验证结果assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());verify(bookRepository, times(1)).findById(1L);verify(bookRepository, never()).delete(any());}
}

5.2 启动应用

运行 BookstoreApplicationmain 方法,启动 Spring Boot 应用。

5.3 部署应用

  • 打包:运行 mvn clean package 生成 JAR 文件。
  • 运行:执行 java -jar target/bookstore-0.0.1-SNAPSHOT.jar 启动应用。
  • 部署到云服务器:将 JAR 文件上传到云服务器(如阿里云、AWS),使用类似命令启动。

六、总结

通过本文,我们完成了以下任务:

  1. 知识铺垫:了解了 Spring Boot 和 RESTful API 的基本概念。
  2. 开发环境搭建:使用 Spring Initializr 创建项目,配置 H2 数据库。
  3. 核心功能实现:定义实体类、数据访问层和控制器层,实现完整的 CRUD 操作。
  4. 测试与部署:编写单元测试,启动应用并测试 API,最后打包部署。

http://www.lqws.cn/news/528841.html

相关文章:

  • 数据结构进阶 - 第二章 线性表
  • 缓存与加速技术实践-MongoDB数据库应用
  • React:利用计算属性名特点更新表单值
  • Spark SQL to_json 函数介绍
  • LLM复杂记忆存储-多会话隔离案例实战
  • Flink Oracle CDC 总结
  • Spring 框架
  • Python+selenium自动化生成测试报告
  • 在一个成熟产品中,如何设计数据库架构以应对客户字段多样化,确保系统的可维护性、可扩展性和高性能。
  • 智慧城市云计算大数据中心项目设计方案
  • 技术调研:时序数据库(一)
  • ASP.NET Core Web API 实现 JWT 身份验证
  • 【人工智能与机器人研究】基于ROS的多传感器融合巡检机器人系统研究
  • Android 16系统源码_无障碍辅助(二)Android 的无障碍框架
  • 人工智能中的集成学习:从原理到实战
  • PDF Kit 使用示例(HarmonyOS)
  • 跟着AI学习C#之项目实战-电商平台 Day1
  • Web3解读:解锁去中心化网络的潜力
  • MessagesPlaceholder和多轮AI翻译助手实战
  • 【强化学习】《Reinforcement Learning: An Introduction》(第二版)概述
  • 杰理-可视化sdk-耳机灯效添加
  • Windows中使用createdump创建进程dump文件的基本用法
  • 开疆智能CCLinkIE转ModbusTCP网关连接PCA3200电能表配置案例
  • 人工智能编程三大核心流程详解--机器学习、神经网络、NLP自然语言处理
  • SQL Server 如何实现高可用和读写分离技术架构
  • SQL学习笔记3
  • AI矢量图与视频无痕修复:用Illustrator与After Effects解锁创作新维度
  • Android14音频子系统-Framework分析
  • Python 常用正则表达式大全
  • Spark 之 QueryStage