在当今的软件开发领域,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.pswp.cn/web/86712.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/86712.shtml
英文地址,请注明出处:http://en.pswp.cn/web/86712.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

安卓中静态和动态添加子 View 到容器

1.静态添加子View 在XML布局文件中直接定义子View&#xff1a; <!-- activity_main.xml --> <LinearLayoutxmlns:android"http://schemas.android.com/apk/res/android"android:id"id/container"android:layout_width"match_parent"a…

【NLP】自然语言项目设计03

目录 03模型构建 代码架构核心设计说明 初步构建模型并进行训练时遇到的一些问题 问题一&#xff1a;模型欠拟合 使用1 model - lstm 解释使用lstm时无法正常的进行cudnn加速 使用2 model - transformer 项目简介 训练一个模型&#xff0c;实现歌词仿写生成 任务类型&am…

WebRTC(十二):DTLS

在 WebRTC 中的作用 DTLS&#xff08;Datagram Transport Layer Security&#xff09;是 TLS 的 UDP 版本&#xff0c;在 WebRTC 中用于&#xff1a; 安全协商加密密钥对等验证&#xff08;基于 X.509 证书 fingerprint&#xff09;为 SRTP/SRTCP 提供密钥材料 WebRTC 不直接…

北大肖臻《区块链技术与应用》学习笔记

区块链学习笔记 \huge{区块链学习笔记} 区块链学习笔记 这是关于北京大学肖臻老师的《区块链技术与应用》课程的学习笔记。 BTC的数据结构 hash pointers&#xff1a;既保存结构体的对应地址位置&#xff08;指针&#xff09;&#xff0c;又保存结构体对应映射的hash值&#…

MongoDB 驱动升级性能测试报告

测试背景 将 MongoDB Java 驱动从 4.11.5 升级至 5.5.1&#xff0c;并配合 Reactor Core 3.8.0-M4 进行性能对比测试。测试主要围绕插入、查询、更新和删除四个核心操作进行。 环境配置 操作系统: Windows 11CPU: Intel Core™ i7-14700F, 28 核心, 2.10 GHzJDK: OpenJDK 21.…

淘宝商品评论实时采集 API 接入指南:从零开始实战开发

在电商数据分析领域&#xff0c;商品评论数据蕴含着用户对产品的真实反馈&#xff0c;对商家优化产品、提升服务质量具有重要价值。本文将详细介绍如何接入淘宝 API&#xff0c;实现商品评论的实时采集&#xff0c;从环境搭建到代码实现进行全流程讲解。 1. 淘宝api概述 淘宝…

ffpaly播放 g711a音频命令

ffpaly播放 g711a音频命令 ffplay 播放 G.711 A-law (8kHz, mono, 16bit) 音频的命令&#xff1a; ffplay -f alaw -ar 8000 -ac 1 input.g711a 或ffplay -f alaw -ar 8000 -ac 1 audio_chn0.g711a 各参数说明&#xff1a; -f alaw&#xff1a;指定输入音频格式为 G.711 A-law…

composer全局配置

composer配置 composer查看全局配置 composer config -l -gcomposer 更新慢 composer下载不下来问题解决 更换composer镜像源&#xff0c;可以执行尝试以下几种&#xff1a; 1、更换成阿里镜像&#xff1a; composer config -g repo.packagist composer https://mirrors.al…

ivx创建一个测试小案例

文章目录 前端后端提交信息服务提交信息事件跳转列表页事件下载事件详情页事件 https://editor.ivx.cn/ 主题选择一下 前端 在前台新建一个页面名为提交页&#xff0c;内边距左和内边距右都设置为40&#xff0c;水平居中和垂直居中设置一下&#xff1b; 新建两个输入框&#x…

【MongoDB】MongoDB从零开始详细教程 核心概念与原理 环境搭建 基础操作

MongoDB从零开始详细教程 核心概念与原理 环境搭建 基础操作 一、核心概念与原理1. 核心组件2. MongoDB vs 关系型数据库 二、环境搭建&#xff08;Windows/Linux/CentOS&#xff09;1. Windows安装2. CentOS安装3. 连接验证 三、基础操作&#xff08;CRUD&#xff09;1. 数据库…

GeoTools 结合 OpenLayers 实现属性查询

前言 在GIS开发中&#xff0c;属性查询是非常普遍的操作&#xff0c;这是每一个GISer都要掌握的必备技能。实现高效的数据查询功能可以提升用户体验&#xff0c;完成数据的快速可视化表达。 本篇教程在之前一系列文章的基础上讲解如何将使用GeoTools工具结合OpenLayers实现Post…

vue-27(实践练习:将现有组件重构为使用组合式 API)

实践练习:将现有组件重构为使用组合式 API 理解重构过程 重构是任何开发者的关键技能,尤其是在采用新范式如 Vue.js 中的 Composition API 时。它涉及在不改变外部行为的情况下重新组织现有代码,旨在提高可读性、可维护性和可重用性。在从 Options API 迁移到 Composition…

基于Uniapp+SpringBoot+Vue 的在线商城小程序

开发系统:Windows10 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,jquery,html,vue 角色:用户 商家 管理员 用户菜单:首页:商…

华为云Flexus+DeepSeek征文|利用华为云一键部署的Dify平台构建高效智能电商客服系统实战

目录 前言 1 华为云快速搭建 Dify-LLM 应用平台 1.1 一键部署简介 1.2 设置管理员账号登录dify平台 2 接入 DeepSeek 大模型与 Reranker 模型 2.1 接入自定义 LLM 模型 2.2 设置 Reranker 模型 3 构建电商知识库 3.1 数据源选择 3.2 分段设置与清洗 3.3 处理并完成 …

python应用day07---pyechars模块详解

1.pyecharts安装: pip install pyecharts 2.pyecharts入门: # 1.导入模块 from pyecharts.charts import Line# 2.创建Line对象 line Line() # 添加数据 line.add_xaxis(["中国", "美国", "印度"]) line.add_yaxis("GDP数据", [30…

高档背景色

https://andi.cn/page/622250.html

教学视频画中画播放(PICTURE-IN-PICTURE)效果

视频平台的画中画&#xff08;PIP&#xff09;功能通过小窗播放提升用户体验&#xff1a;1&#xff09;支持多任务处理&#xff0c;如边看教程边操作文档&#xff1b;2&#xff09;减少应用跳出率&#xff0c;增强用户粘性&#xff1b;3&#xff09;优化屏幕空间利用&#xff1…

MySQL (一):数据类型,完整性约束和表间关系

在当今数据驱动的时代&#xff0c;数据库作为数据存储与管理的核心工具&#xff0c;其重要性不言而喻。MySQL 作为一款广泛应用的开源数据库&#xff0c;凭借其高性能、高可靠性和丰富的功能&#xff0c;深受开发者喜爱。本文作为 MySQL 系列博客的开篇&#xff0c;将带你深入了…

【软考高项论文】信息系统项目的资源管理

摘要 本文围绕信息系统项目的资源管理展开论述。首先阐述了项目资源管理的基本过程&#xff0c;包括资源规划、估算、获取、配置、监控和释放等关键步骤&#xff0c;并给出资源分解结构示例。接着结合2024年参与管理的某信息系统项目实际情况&#xff0c;详细说明资源管理的具…

阿里云Ubuntu服务器上安装MySQL并配置远程连接

1. 安装MySQL 首先连接到你的Ubuntu服务器&#xff0c;然后执行&#xff1a; # 更新软件包列表 sudo apt update# 安装MySQL服务器 sudo apt install mysql-server# 启动MySQL服务 sudo systemctl start mysql# 设置MySQL开机自启 sudo systemctl enable mysql# 检查MySQL状态…