一文掌握SpringBoot:HTTP服务开发从入门到部署

在Java后端开发领域,SpringBoot无疑是最受欢迎的技术框架之一。其通过"约定优于配置"的理念,极大地简化了Spring应用的初始搭建和开发过程。这种简化不仅体现在代码层面,更深刻地改变了Java Web应用的部署范式------SpringBoot与Tomcat形成的嵌入式集成关系,将传统需要独立安装配置的应用服务器直接内置于应用之中,使得复杂的Web应用部署转变为简单的可执行JAR包运行。

回顾传统的Java Web开发,开发者需要经历繁琐的环境配置:独立安装Tomcat服务器、配置server.xml、部署WAR包、管理应用服务器生命周期。而SpringBoot的革命性设计彻底改变了这一局面,其默认将Tomcat作为内置服务器 打包在应用中,让每个SpringBoot应用都成为一个自包含、可独立运行的执行单元。这种设计带来了真正的开箱即用 体验------开发者无需单独安装配置Tomcat,只需一行java -jar命令即可启动完整的Web服务。

本文我们将基于SpringBoot+Gradle技术栈,从零开始构建一个简单的HTTP服务,逐步实现从项目初始化、API开发、业务逻辑构建到最终部署上线的全流程。

一、环境准备

首先选择一个适合自己的集成开发环境,例如IntelliJ IDEAVSCode,并使用对应的开发环境创建一个SpringBoot应用程序。

我创建的Demo程序应用结构如下所示:

bash 复制代码
SpringBootDemo/
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── com/xiaxl/demo/
│   │   │       ├── DemoApplication.java
│   │   │       └── controller/
│   │   └── resources/
│   │       └── application.properties
│   └── test/
├── build.gradle
└── settings.gradle

settings.gradle 是Gradle的项目设置文件,用于定义项目的基本信息和模块配置,这里定义了项目的根模块名称。

bash 复制代码
# 项目名称,影响构建产物的命名
rootProject.name = 'SpringBootDemo'

build.gradle 是Gradle构建脚本的核心文件,定义了项目的构建配置、依赖管理和插件应用。

bash 复制代码
plugins {
    # 应用Java插件,提供编译、测试、打包等任务
    id 'java'  
    # SpringBoot插件,提供bootJar、bootRun等任务
    id 'org.springframework.boot' version '3.3.3'  
    # 依赖管理插件,简化Spring依赖版本管理
    id 'io.spring.dependency-management' version '1.1.6'  
}  
  
group = 'com.xiaxl'        # 组织标识,通常使用公司域名
version = '0.0.1-SNAPSHOT' # 项目版本,SNAPSHOT表示开发中版本
  
java {  
    toolchain {  
       languageVersion = JavaLanguageVersion.of(23)  # 指定JDK版本为23
    }  
}  
  
repositories {  
    // 配置国内镜像加速依赖下载(按需使用)
    maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' }  
    maven { url 'https://mirrors.cloud.tencent.com/nexus/repository/maven-public/' }  
    maven { url 'https://developer.huawei.com/repo/' }  
    // maven仓库
    mavenCentral()  
}  
  
dependencies {
    // 核心依赖:SpringBoot Web Starter,包含:
    // - Spring MVC (Web框架)
    // - Tomcat (嵌入式Web服务器)
    // - Jackson (JSON处理)
    // - 验证、数据绑定等
    // Spring Boot Web Starter  
    implementation 'org.springframework.boot:spring-boot-starter-web'  
    // 数据校验  
    implementation 'org.springframework.boot:spring-boot-starter-validation'  
    // Lombok简化代码
    compileOnly 'org.projectlombok:lombok'  
    annotationProcessor 'org.projectlombok:lombok'  
    //
    testImplementation 'org.springframework.boot:spring-boot-starter-test'  
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'  
}  
  
tasks.named('test') {  
    useJUnitPlatform()  
}

// 可选:配置JAR包输出名称
bootJar {
    archiveFileName = 'springboot-demo.jar'
}

application.properties 是Spring Boot应用的核心配置文件,用于定义应用的各种配置参数,如服务器端口、应用名称、数据库连接等。

java 复制代码
spring.application.name=SpringBootDemo              # 应用名称
spring.servlet.multipart.max-file-size=-1           # 单个文件大小限制,-1表示不限制
spring.servlet.multipart.max-request-size=-1        # 请求总大小限制,-1表示不限制
server.port=8081           # 服务端口

DemoApplication.java SpringBoot 是Spring Boot应用的主启动类,是应用程序的入口点。通过@SpringBootApplication注解标记,包含了自动配置、组件扫描等Spring Boot核心功能。

java 复制代码
package com.xiaxl.demo;  
  
import org.springframework.boot.SpringApplication;  
import org.springframework.boot.autoconfigure.SpringBootApplication;  
// 使用@SpringBootApplication注解标记该类为Spring Boot应用的主配置类  
@SpringBootApplication  
public class DemoApplication {  
    // main方法是Java应用程序的入口点  
    // 当JVM启动时,首先执行这个方法  
    // args参数用于接收命令行参数  
    public static void main(String[] args) {  
       // SpringApplication.run()是启动Spring Boot应用的核心方法  
       // 第一个参数:主配置类(通常是带有@SpringBootApplication注解的类)  
       // 第二个参数:命令行参数,会传递给Spring应用上下文  
       // 这个方法执行以下操作:  
       // 1. 创建Spring应用上下文(ApplicationContext)  
       // 2. 启用自动配置  
       // 3. 启动嵌入式Web服务器(如Tomcat)  
       // 4. 扫描并注册所有Spring组件  
       // 5. 返回应用上下文对象(此处未接收返回值)  
       SpringApplication.run(DemoApplication.class, args);  
    }  
}

HelloController

创建一个HelloController,用于验证环境是否OK。

java 复制代码
import org.springframework.web.bind.annotation.GetMapping;  
import org.springframework.web.bind.annotation.RestController;  
  
import java.util.ArrayList;  
import java.util.Arrays;  
import java.util.List;  
  
// 访问地址:GET http://localhost:8081/api/hello  
// @RestController:标记这个类为一个 REST 控制器,意味着类中的方法会处理 HTTP 请求并返回数据。 
@RestController  
public class HelloController {
    @GetMapping("/api/hello")
    public HelloResponse getHelloMessages() {  
        HelloResponse response = new HelloResponse();  
        response.setStartupMessage("欢迎来到精彩世界!");  
        response.setNewMessages(Arrays.asList("望长城内外惟余莽莽,大河上下顿失滔滔。"));  
        response.setStatusCode(200);  
        return response;  
    }  
  
    public static class HelloResponse {  
        private String startupMessage;  
        private List<String> newMessages;  
        private int statusCode;  
  
        public String getStartupMessage() {  
            return startupMessage;  
        }  
  
        public void setStartupMessage(String startupMessage) {  
            this.startupMessage = startupMessage;  
        }  
  
        public List<String> getNewMessages() {  
            return newMessages;  
        }  
  
        public void setNewMessages(List<String> newMessages) {  
            this.newMessages = newMessages;  
        }  
  
        public int getStatusCode() {  
            return statusCode;  
        }  
  
        public void setStatusCode(int statusCode) {  
            this.statusCode = statusCode;  
        }  
  
        public void addNewMessage(String message) {  
            if (this.newMessages == null) {  
                this.newMessages = new ArrayList<>();  
            }  
            this.newMessages.add(message);  
        }  
    }  
}

使用示例: 访问 http://localhost:8081/api/hello

二、基础接口实现

掌握 SpringBoot 基础环境构建后,将学习如何实现常用的 HTTP 接口。涵盖 GET 和 POST 两种最常用的 HTTP 方法。

ApiResponse.java - 统一响应格式封装类

在构建 RESTful API 时,保持统一的响应格式非常重要。这有助于提高代码的可维护性。这里我们创建一个通用的 ApiResponse类来封装所有接口的响应。

这个类用于标准化所有 API 接口的响应格式,包含成功状态、消息、数据、时间戳和状态码,确保前后端交互的一致性。

java 复制代码
import com.fasterxml.jackson.annotation.JsonInclude;  
import lombok.AllArgsConstructor;  
import lombok.Data;  
import lombok.NoArgsConstructor;  
  
import java.time.LocalDateTime;  
  
// 使用Lombok注解自动生成getter、setter、toString、equals和hashCode方法  
@Data  
// 生成无参构造函数  
@NoArgsConstructor  
// 生成全参构造函数  
@AllArgsConstructor  
// Jackson序列化注解:当字段值为null时,不包含在JSON输出中。这样可以减少响应体大小,避免前端处理null值。  
@JsonInclude(JsonInclude.Include.NON_NULL)  
public class ApiResponse<T> {  
    private boolean success;  
    private String message;  
    private T data;  
    private LocalDateTime timestamp;  
    private Integer code;  
  
    // 成功响应快捷方法  
    public static <T> ApiResponse<T> success(T data) {  
        return new ApiResponse<>(true, "操作成功", data, LocalDateTime.now(), 200);  
    }  
  
    public static <T> ApiResponse<T> success(String message, T data) {  
        return new ApiResponse<>(true, message, data, LocalDateTime.now(), 200);  
    }  
  
    // 失败响应快捷方法  
    public static <T> ApiResponse<T> error(String message) {  
        return new ApiResponse<>(false, message, null, LocalDateTime.now(), 500);  
    }  
  
    public static <T> ApiResponse<T> error(String message, Integer code) {  
        return new ApiResponse<>(false, message, null, LocalDateTime.now(), code);  
    }  
}

UserDTO.java - 用户数据传输对象

为了在不同层之间传递数据,我们使用 DTO(Data Transfer Object)模式。UserDTO类用于封装用户相关的数据传输。

这个类定义了用户数据的结构,包含用户的基本信息和业务字段,支持链式调用提高代码可读性。

java 复制代码
import lombok.AllArgsConstructor;  
import lombok.Data;  
import lombok.NoArgsConstructor;  
import lombok.experimental.Accessors;  
  
import java.time.LocalDateTime;  
import java.util.List;  
  
// 使用Lombok注解自动生成getter、setter、toString、equals和hashCode方法  
@Data  
// 生成无参构造函数  
@NoArgsConstructor  
// 生成全参构造函数  
@AllArgsConstructor  
// 使用Lombok的@Accessors注解,开启链式调用(chain = true)。  
// 使得setter方法返回当前对象,可以连续调用。如:user.setId(1L).setName("张三")  
@Accessors(chain = true) // 支持链式调用  
public class UserDTO {  
    private Long id;  
    private String name;  
    private String email;  
    private Integer age;  
    private String phone;  
    private String address;  
    private Boolean active;  
    private LocalDateTime createTime;  
    private LocalDateTime updateTime;  
    private List<String> roles;  
  
    // 静态工厂方法  
    public static UserDTO createDefault() {  
        return new UserDTO()  
                .setId(1L)  
                .setName("张三")  
                .setEmail("zhangsan@example.com")  
                .setAge(25)  
                .setPhone("13800138000")  
                .setAddress("北京市海淀区")  
                .setActive(true)  
                .setCreateTime(LocalDateTime.now())  
                .setUpdateTime(LocalDateTime.now())  
                .setRoles(List.of("USER", "EDITOR"));  
    }  
}

UserController.java - 用户控制器基类

下面创建控制器基类,并初始化一些模拟数据用于演示。在实际项目中,这些数据通常来自数据库。

这是处理用户相关请求的控制器,使用内存 Map 模拟数据库存储,包含数据初始化方法。

java 复制代码
import com.xiaxl.demo.model.response.ApiResponse;  
import com.xiaxl.demo.model.response.UserDTO;  
import lombok.extern.slf4j.Slf4j;  
import org.springframework.http.ResponseEntity;  
import org.springframework.web.bind.annotation.GetMapping;  
import org.springframework.web.bind.annotation.RestController;  
  
import java.time.LocalDateTime;  
import java.util.ArrayList;  
import java.util.HashMap;  
import java.util.List;  
import java.util.Map;  
  
@Slf4j  
@RestController  
public class UserController {  
  
    // 内存存储模拟数据(替代数据库)  
    private final Map<Long, UserDTO> mUserMap = new HashMap<>();  
    private Long idCounter = 1L;  
  
    // 初始化一些测试数据  
    public UserController() {  
        initMockData();  
    }  
  
    private void initMockData() {  
        for (int i = 1; i <= 3; i++) {  
            UserDTO user = new UserDTO()  
                    .setId((long) i)  
                    .setName("用户" + i)  
                    .setEmail("user" + i + "@example.com")  
                    .setAge(20 + i)  
                    .setPhone("1380013800" + i)  
                    .setAddress("地址" + i)  
                    .setActive(true)  
                    .setCreateTime(LocalDateTime.now())  
                    .setUpdateTime(LocalDateTime.now())  
                    .setRoles(List.of("USER"));  
            mUserMap.put(user.getId(), user);  
            idCounter++;  
        }  
    }  
}

1. GET 接口

GET 请求是 HTTP 方法中最常用的一种,主要用于从服务器请求数据。在实际的 Web 开发中,根据不同的数据检索需求,GET 接口的实现也分为多种形式。本文将详细介绍三种最常见的 GET 接口实现方式。

1.1 基础 GET 接口

这种接口用于获取某一类资源的完整集合或列表,通常不需要任何输入参数。它是最简单、最直接的 GET 接口形式,适用于像"获取所有用户"、"查询所有文章"这样的场景。
关键注解@GetMapping用于将 HTTP GET 请求映射到特定的控制器方法。

java 复制代码
/**  
 * 简单的GET请求 - 获取所有用户列表  
 * 访问地址:GET http://localhost:8081/api/users  
 */
@GetMapping("/api/users")  
public ResponseEntity<ApiResponse<List<UserDTO>>> getAllUsers() {  
    log.info("获取所有用户列表");  
    List<UserDTO> userList = new ArrayList<>(mUserMap.values());  
    return ResponseEntity.ok(ApiResponse.success("获取用户列表成功", userList));  
}  

使用示例:

  • 访问 http://localhost:8081/api/users
1.2 路径参数 GET 接口

实际开发中,我们经常需要获取某个特定ID(或唯一标识)的资源详情。这时,将标识符作为URL路径的一部分(路径参数)是最佳实践。这种方式语义清晰,符合RESTful架构风格。
关键注解@PathVariable用于将URL中的模板变量绑定到方法的参数上。

java 复制代码
/**  
 * GET请求 - 根据ID获取单个用户  
 * 访问地址:GET http://localhost:8081/api/users/{id}  
 * 例如:GET http://localhost:8081/api/users/1  
 */
@GetMapping("/api/users/{id}")  
public ResponseEntity<ApiResponse<UserDTO>> getUserById(@PathVariable Long id) {  
    log.info("根据ID查询用户,ID: {}", id);  
  
    UserDTO user = mUserMap.get(id);  
    if (user == null) {  
        return ResponseEntity  
                .status(HttpStatus.NOT_FOUND)  
                .body(ApiResponse.error("用户不存在", 404));  
    }  
  
    return ResponseEntity.ok(ApiResponse.success("获取用户成功", user));  
}
  • 访问 http://localhost:8081/api/users/1
1.3 查询参数 GET 接口

实际开发中,对于复杂的检索需求,例如根据多个条件过滤结果或进行分页查询,使用查询参数是最灵活的方式。查询参数以 ?开始,以 key=value的形式拼接在URL后面,多个参数用 &连接。这种接口非常适合实现搜索功能。

关键注解@RequestParam用于将请求参数绑定到控制器的方法参数。required=false表示参数非必填,defaultValue提供默认值。

java 复制代码
/**  
 * GET请求 - 带查询参数的用户搜索  
 * 访问地址:GET http://localhost:8081/api/users/search?name=用户1&active=true  
 */@GetMapping("/api/users//search")  
public ResponseEntity<ApiResponse<List<UserDTO>>> searchUsers(  
        @RequestParam(required = false) String name,  
        @RequestParam(required = false) Boolean active,  
        @RequestParam(defaultValue = "1") Integer page,  
        @RequestParam(defaultValue = "10") Integer size) {  
  
    log.info("搜索用户 - name: {}, active: {}, page: {}, size: {}",  
            name, active, page, size);  
  
    List<UserDTO> result = mUserMap.values().stream()  
            .filter(user -> name == null || user.getName().contains(name))  
            .filter(user -> active == null || user.getActive().equals(active))  
            .skip((page - 1) * (long) size)  
            .limit(size)  
            .toList();  
  
    return ResponseEntity.ok(ApiResponse.success("搜索成功", result));  
}
  • 访问 http://localhost:8081/api/users/search?name=用户1&active=true

2. POST接口

POST 请求用于向服务器提交数据,通常用于创建新资源。在RESTful架构中,POST方法对应CRUD中的Create操作,用于向服务器添加新的资源。

在正式开始介绍前,先介绍一个UserRequest类,这是一个数据验证DTO(Data Transfer Object)类,用于接收和验证前端传递的用户创建请求数据。

UserRequest.java

UserRequest通过Jakarta Validation API提供了字段级别的数据验证规则,确保输入数据的合法性。

java 复制代码
import jakarta.validation.constraints.*;  
import lombok.Data;  
  
@Data  
// 用户创建请求DTO(Data Transfer Object)类  
// 用于接收前端传递的用户创建请求数据  
// 包含各种验证注解,确保数据合法性  
public class UserRequest {  
  
    @NotBlank(message = "用户名不能为空")  
    @Size(min = 2, max = 20, message = "用户名长度必须在2-20个字符之间")  
    private String name;  
  
    @NotBlank(message = "邮箱不能为空")  
    @Email(message = "邮箱格式不正确")  
    private String email;  
  
    @NotNull(message = "年龄不能为空")  
    @Min(value = 1, message = "年龄必须大于0")  
    @Max(value = 150, message = "年龄不能超过150")  
    private Integer age;  
  
    @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")  
    private String phone;  
  
    @NotBlank(message = "地址不能为空")  
    private String address;  
  
    private String remark;  
  
    @AssertTrue(message = "必须同意用户协议")  
    private Boolean agreeTerms;  
}
2.1 POST 接口处理 JSON 数据

以下举例展示如何处理 JSON 格式的 POST 请求。JSON是目前RESTful API最常用的数据交换格式,具有结构清晰、跨平台、易于解析的优点。本接口演示了完整的用户创建流程,包括参数验证、业务逻辑处理和响应返回。

java 复制代码
/**  
 * POST请求 - 创建用户(JSON格式请求体)  
 * 访问地址:POST http://localhost:8081/api/post/users  
 * 请求头:Content-Type: application/json  
 * 请求体示例:  
 * {  
 * "name": "李四",  
 * "email": "lisi@example.com", 
 * "age": 30, * "phone": "13900139000", 
 * "address": "上海市浦东新区",  
 * "agreeTerms": true 
 * } 
 * @param request       接收并验证请求体中的JSON数据,自动反序列化为UserRequest对象,  
 *                      Valid注解触发对UserRequest对象中字段的验证规则  
 * @param bindingResult 接收参数验证结果,包含所有验证错误信息  
 * @return ApiResponse<UserDTO>  
 */  
@PostMapping("/api/post/users")  
public ResponseEntity<ApiResponse<UserDTO>> createUser(  
        @Valid @RequestBody UserRequest request,  
        BindingResult bindingResult) {  
  
    log.info("创建用户 - 请求数据: {}", request);  
  
    // 参数校验  
    if (bindingResult.hasErrors()) {  
        Map<String, String> errors = new HashMap<>();  
        for (FieldError error : bindingResult.getFieldErrors()) {  
            errors.put(error.getField(), error.getDefaultMessage());  
        }  
        return ResponseEntity  
                .badRequest()  
                .body(ApiResponse.error(errors.toString(), 400));  
    }  
  
    // 检查用户名是否已存在  
    boolean exists = mUserMap.values().stream()  
            .anyMatch(user -> user.getName().equals(request.getName()));  
    if (exists) {  
        return ResponseEntity  
                .badRequest()  
                .body(ApiResponse.error("用户名已存在", 400));  
    }  
  
    // 创建用户  
    UserDTO newUser = new UserDTO()  
            .setId(idCounter++)  
            .setName(request.getName())  
            .setEmail(request.getEmail())  
            .setAge(request.getAge())  
            .setPhone(request.getPhone())  
            .setAddress(request.getAddress())  
            .setActive(true)  
            .setCreateTime(LocalDateTime.now())  
            .setUpdateTime(LocalDateTime.now())  
            .setRoles(List.of("USER"));  
  
    mUserMap.put(newUser.getId(), newUser);  
  
    log.info("用户创建成功,ID: {}", newUser.getId());  
    return ResponseEntity  
            .status(HttpStatus.CREATED)  
            .body(ApiResponse.success("用户创建成功", newUser));  
}

使用 Postman 测试:

bash 复制代码
POST http://localhost:8081/api/post/users
Headers:Content-Type: application/json  
Body:
{
"name":"李四",
"email":"lisi@example.com",
"age":30,
"phone":"13900139000",
"address":"上海市浦东新区",
"agreeTerms":true
}
2.2 POST 接口处理表单请求

除了 JSON 格式,表单提交是另一种常见的 POST 请求方式,常用于文本表单提交。表单提交通常用于传统的Web应用,通过application/x-www-form-urlencoded格式传递键值对数据。这种方式适合简单的数据传输,如登录、搜索等场景。

java 复制代码
/**  
 * POST表单请求 - 用户登录(application/x-www-form-urlencoded)  
 * 访问地址:POST http://localhost:8081/api/users/login  
 * 请求头:Content-Type: application/x-www-form-urlencoded  
 * 请求体:name=张三&password=123456  
 */
 @PostMapping(value = "/api/users/login", consumes = "application/x-www-form-urlencoded")  
public ResponseEntity<ApiResponse<Map<String, Object>>> login(  
        @RequestParam String name,  
        @RequestParam String password) {  
  
    log.info("用户登录 - name: {}", name);  
  
    // 模拟用户验证  
    Optional<UserDTO> userOptional = mUserMap.values().stream()  
            .filter(user -> user.getName().equals(name))  
            .findFirst();  
  
    if (userOptional.isEmpty()) {  
        return ResponseEntity  
                .badRequest()  
                .body(ApiResponse.error("用户名或密码错误", 401));  
    }  
  
    // 模拟密码验证(实际项目中使用加密验证)  
    UserDTO user = userOptional.get();  
    if (!"123456".equals(password)) { // 模拟固定密码  
        return ResponseEntity  
                .badRequest()  
                .body(ApiResponse.error("用户名或密码错误", 401));  
    }  
  
    // 生成模拟token  
    String token = "Bearer " + UUID.randomUUID();  
    Map<String, Object> loginResult = new HashMap<>();  
    loginResult.put("user", user);  
    loginResult.put("token", token);  
    loginResult.put("expiresIn", 3600);  
  
    log.info("用户登录成功,生成token: {}", token);  
    return ResponseEntity.ok(ApiResponse.success("登录成功", loginResult));  
}

使用 Postman 测试:

bash 复制代码
POST http://localhost:8081/api/users/login  
Headers:Content-Type: application/x-www-form-urlencoded  
Body:name=张三&password=123456  
2.3 POST 接口文件请求

文件上传是Web应用中常见的功能,通过multipart/form-data格式支持二进制文件传输。这种方式允许在单个请求中同时传输文本字段和二进制文件,非常适合需要上传图片、文档等文件的场景。

java 复制代码
/**  
 * 处理多个文件POST上传  
 * POST http://localhost:8081/api/uploadFiles  
 * Content-Type: multipart/form-data; boundary=4235013262151947840 * <p>  
 * ----4235013262151947840  
 * Content-Disposition: form-data; name="files"; filename="111.png" * Content-Type: image/png * <p>  
 * 111.png字节流数据  
 * ----4235013262151947840  
 * Content-Disposition: form-data; name="files"; filename="222.png" * Content-Type: image/png * <p>  
 * 222.png字节流数据  
 * ----4235013262151947840  
 * * @param files 文件列表  
 * @return  
 */  
@PostMapping("/api/uploadFiles")  
public ResponseEntity<ApiResponse<List<String>>> uploadFiles(@RequestParam("files") MultipartFile[] files) {  
    // 创建一个列表  
    List<String> fileUris = new ArrayList<>();  
    // 循环输入的文件  
    for (MultipartFile file : files) {  
        try {  
            // 获取并清理文件名  
            String fileName = StringUtils.cleanPath(file.getOriginalFilename());  
            // 构建文件上传路径  
            Path uploadPath = Paths.get(UPLOAD_DIR + fileName);  
            // 创建上传目录  
            File uploadDir = new File(UPLOAD_DIR);  
            if (!uploadDir.exists()) {  
                uploadDir.mkdirs();  
            }  
            // 创建输出流,写入文件  
            try (FileOutputStream fos = new FileOutputStream(uploadPath.toFile())) {  
                fos.write(file.getBytes());  
            }  
            // 上传后的文件路径  
            String fileUri = UPLOAD_DIR + fileName;  
            // 缓存到文件列表中  
            fileUris.add(fileUri);  
        } catch (IOException e) {  
            e.printStackTrace();  
            return ResponseEntity  
                    .badRequest()  
                    .body(ApiResponse.error(e.getMessage(), 500));  
        }  
    }  
    return ResponseEntity.ok(ApiResponse.success("上传成功", fileUris));  
}

使用 Postman 测试:

bash 复制代码
POST http://localhost:8081/api/uploadFiles
Content-Type: multipart/form-data; boundary=4235013262151947840

----4235013262151947840
Content-Disposition: form-data; name="files"; filename="111.png"
Content-Type: image/png

111.png字节流数据
----4235013262151947840
Content-Disposition: form-data; name="files"; filename="222.png"
Content-Type: image/png

222.png字节流数据
----4235013262151947840

三、数据库集成

在现代Web应用开发中,数据库集成是至关重要的一环。Spring Boot通过Spring Data JPA提供了简洁高效的数据访问解决方案,可以大大减少样板代码的编写。本章将详细介绍如何在Spring Boot项目中集成SQLite数据库,并实现完整的用户管理功能。

本示例使用SQLite,因为它无需单独安装数据库服务器,且数据存储在单一文件中,便于开发和测试。同时,我们将采用分层架构设计,确保代码的可维护性和扩展性。

1. 环境准备

在开始编码之前,需要配置项目依赖和数据库连接。以下是具体的配置步骤:

build.gradle​ 添加SQLite数据库相关依赖库,这些依赖将为我们提供数据库连接、JPA支持以及必要的工具类。

bash 复制代码
dependencies {   
    // SQLite数据库(关键依赖)  
    implementation 'org.xerial:sqlite-jdbc:3.45.1.0'  
    // 添加以下依赖以支持SQLite方言  
    implementation 'org.hibernate.orm:hibernate-community-dialects:6.3.1.Final'  
    // Spring Data JPA  
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'  
    // JSON序列化  
    implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'  
}

application.properties 添加数据库相关配置,这些配置项定义了数据库连接参数、JPA行为以及SQL日志输出等设置。

bash 复制代码
spring.datasource.url=jdbc:sqlite:demo.db  
spring.datasource.driver-class-name=org.sqlite.JDBC  
spring.datasource.username=admin  
spring.datasource.password=123456  
spring.jpa.database-platform=org.hibernate.community.dialect.SQLiteDialect  
spring.jpa.hibernate.ddl-auto=update  
spring.jpa.show-sql=true  
spring.jpa.properties.hibernate.dialect=org.hibernate.community.dialect.SQLiteDialect  
spring.sql.init.mode=never

DemoApplication.java 增加注解EnableJpaRepositories,启用JPA仓库功能,这是Spring Data JPA的核心配置。

java 复制代码
import org.springframework.boot.SpringApplication;  
import org.springframework.boot.autoconfigure.SpringBootApplication;  
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;  

@SpringBootApplication  
@EnableJpaRepositories  
public class DemoApplication {  
    public static void main(String[] args) {  
       SpringApplication.run(DemoApplication.class, args);  
    }  
}

UserEntity.java 创建一个UserEntity类,该类对应数据库中的users表,使用JPA注解定义实体与表的映射关系。

java 复制代码
import jakarta.persistence.*;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;

import java.time.LocalDateTime;
import java.util.List;

@Entity
@Table(name = "users")
@Data
// 生成无参构造函数
@NoArgsConstructor
// 生成全参构造函数
@AllArgsConstructor
public class UserEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, unique = true)
    private String name;

    @Column(nullable = false)
    private String email;

    private String password;

    @Column(name = "nick_name")
    private String nickname;

    private Integer age;

    private String phone;

    private String address;

    private Boolean active;

    private List<String> roles;

    @Column(name = "create_time")
    private LocalDateTime createTime;

    @Column(name = "update_time")
    private LocalDateTime updateTime;

    // 自动设置时间
    @PrePersist
    protected void onCreate() {
        createTime = LocalDateTime.now();
        updateTime = LocalDateTime.now();
    }

    @PreUpdate
    protected void onUpdate() {
        updateTime = LocalDateTime.now();
    }
}

UserDbController

创建一个UserDbController,作为用户管理的RESTful API控制器,处理HTTP请求并调用服务层方法。

java 复制代码
import com.xiaxl.demo.model.response.ApiResponse;
import com.xiaxl.demo.model.response.UserDTO;
import com.xiaxl.demo.model.request.UserRequest;
import com.xiaxl.demo.service.UserService;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.*;

import java.util.*;

@Slf4j
@RestController
// Lombok注解,自动生成一个包含所有final字段的构造函数
// 这通常用于依赖注入,Spring会自动调用此构造函数注入所需的依赖
public class UserDbController {

    // 声明一个UserService类型的final常量,使用依赖注入方式初始化
    // UserService是Spring Data JPA接口,用于操作用户数据表
    // final关键字确保该引用在构造函数初始化后不能被修改
    private final UserService mUserService;

    // 使用构造器注入(推荐)
    @Autowired
    public UserDbController(UserService userService) {
        this.mUserService = userService;
    }
}

UserService

创建一个UserService,作为业务逻辑层,处理用户相关的业务操作,包括数据转换和业务规则验证。

java 复制代码
import com.xiaxl.demo.model.db.UserEntity;
import com.xiaxl.demo.model.response.ApiResponse;
import com.xiaxl.demo.model.response.UserDTO;
import com.xiaxl.demo.model.request.UserRequest;
import com.xiaxl.demo.repository.UserRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service
public class UserService {

    // 声明一个UserRepository类型的final常量,使用依赖注入方式初始化
    // UserRepository是Spring Data JPA接口,用于操作用户数据表
    // final关键字确保该引用在构造函数初始化后不能被修改
    private final UserRepository mUserRepository;

    // 使用构造器注入(推荐)
    @Autowired
    public UserService(UserRepository userRepository) {
        this.mUserRepository = userRepository;
    }


    /**
     * 实体转DTO
     *
     * @param userEntity
     * @return
     */
    public UserDTO convertToDTO(UserEntity userEntity) {
        UserDTO dto = new UserDTO();
        BeanUtils.copyProperties(userEntity, dto);
        return dto;
    }

    /**
     * @param userDTO
     * @return
     */
    public UserEntity convertToEntity(UserDTO userDTO) {
        UserEntity userEntity = new UserEntity();
        BeanUtils.copyProperties(userDTO, userEntity);
        return userEntity;
    }
}

UserRepository.java

创建一个UserRepository接口,继承JpaRepository,获得基本的CRUD操作能力,并定义自定义查询方法。

java 复制代码
import com.xiaxl.demo.model.db.UserEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;

@Repository  // 添加 @Repository 注解
public interface UserRepository extends JpaRepository<UserEntity, Long> {

    // 根据用户名查找用户
    Optional<UserEntity> findByName(String name);

    // 根据邮箱查找用户
    Optional<UserEntity> findByEmail(String email);

    // 查找所有用户按创建时间倒序
    @Query("SELECT u FROM UserEntity u ORDER BY u.createTime DESC")
    List<UserEntity> findAllOrderByCreatedAtDesc();
}

2. Post创建用户接口

现在来实现创建用户的API接口。这个接口将接收POST请求,验证输入数据,并将用户信息保存到数据库中。

UserDbController中添加如下方法,创建用于客户端post请求创建用户。

java 复制代码
/**  
 * POST请求 - 创建用户(JSON格式请求体)  
 * 访问地址:POST http://localhost:8081/api/post/users  
 * 请求头:Content-Type: application/json  
 * 请求体示例:  
 * {  
 * "name": "李四",  
 * "email": "lisi@example.com", * "age": 30, * "phone": "13900139000", * "address": "上海市浦东新区",  
 * "agreeTerms": true * } * * @param request       接收并验证请求体中的JSON数据,自动反序列化为UserCreateRequest对象,  
 *                      Valid注解触发对UserCreateRequest对象中字段的验证规则  
 * @param bindingResult 接收参数验证结果,包含所有验证错误信息  
 * @return ApiResponse<UserDTO>  
 */  
@PostMapping("/api/post/create/user")  
public ResponseEntity<ApiResponse<UserDTO>> createUser(  
        @Valid @RequestBody UserRequest request,  
        BindingResult bindingResult) {  
  
    log.info("创建用户 - 请求数据: {}", request);  
    // 参数校验  
    if (bindingResult.hasErrors()) {  
        Map<String, String> errors = new HashMap<>();  
        for (FieldError error : bindingResult.getFieldErrors()) {  
            errors.put(error.getField(), error.getDefaultMessage());  
        }  
        return ResponseEntity  
                .badRequest()  
                .body(ApiResponse.error(errors.toString(), 400));  
    }  
    return mUserService.createUser(request);  
}

UserService中添加如下方法,创建用于客户端post请求创建用户。

java 复制代码
/**  
 * 创建用户  
 *  
 * @param request  
 * @return  
 */  
public ResponseEntity<ApiResponse<UserDTO>> createUser(UserRequest request) {  
    // 检查用户名是否已存在  
    if (mUserRepository.findByName(request.getName()).isPresent()) {  
        return ResponseEntity.badRequest()  
                .body(ApiResponse.error("用户名已存在"));  
    }  
  
    // 检查邮箱是否已存在  
    if (mUserRepository.findByEmail(request.getEmail()).isPresent()) {  
        return ResponseEntity.badRequest()  
                .body(ApiResponse.error("邮箱已存在"));  
    }  
  
    // 调用内部事务方法  
    UserEntity savedUserEntity = saveUserEntity(request);  
  
    return ResponseEntity.status(HttpStatus.CREATED)  
            .body(ApiResponse.success("用户创建成功", convertToDTO(savedUserEntity)));  
}

@Transactional  
public UserEntity saveUserEntity(UserRequest request) {  
    log.info("saveUserEntity: ", request);  
    try {  
        // 实际的创建逻辑,带有事务  
        UserEntity newUserEntity = new UserEntity();  
        newUserEntity.setName(request.getName());  
        newUserEntity.setEmail(request.getEmail());  
        newUserEntity.setAge(request.getAge());  
        newUserEntity.setPhone(request.getPhone());  
        newUserEntity.setAddress(request.getAddress());  
        newUserEntity.setActive(true);  
        newUserEntity.setCreateTime(LocalDateTime.now());  
        newUserEntity.setUpdateTime(LocalDateTime.now());  
        newUserEntity.setRoles(List.of("USER"));  
        log.info("saveUserEntity: ", request);  
        return mUserRepository.save(newUserEntity);  
    }catch (Exception e){  
        e.printStackTrace();  
        log.info("Exception: ", e.getMessage());  
    }  
    return null;  
}

使用 Postman 测试: 通过Postman发送POST请求到创建用户接口,可以验证接口功能是否正常。

3. 返回所有用户

接下来实现获取所有用户信息的接口。当用户数量较多时,直接返回所有用户可能导致性能问题,但在开发阶段,这个功能对于测试和数据验证非常有用。

UserDbController中添加如下方法,处理获取所有用户的GET请求。

python 复制代码
/**  
 * 获取所有用户  
 * GET http://localhost:8081/api/get/users  
 */@GetMapping("/api/get/users")  
public ResponseEntity<ApiResponse<List<UserDTO>>> getAllUsers() {  
    log.info("getAllUsers");  
    return mUserService.getAllUsers();  
}

UserService中添加如下方法,实现获取所有用户的业务逻辑。

python 复制代码
/**  
 * 返回全部用户  
 *  
 * @return  
 */  
public ResponseEntity<ApiResponse<List<UserDTO>>> getAllUsers() {  
    List<UserDTO> users = mUserRepository.findAllOrderByCreatedAtDesc().stream()  
            .map(this::convertToDTO)  
            .collect(Collectors.toList());  
  
    return ResponseEntity.ok(ApiResponse.success("获取成功", users));  
}

使用 Postman 测试: 通过Postman测试获取所有用户接口,可以查看当前数据库中的所有用户信息。

至此,已经完成了Spring Boot与SQLite数据库的基本集成,实现了用户的创建和查询功能。这种分层架构的设计使得代码结构清晰,各层职责分明,便于后续的功能扩展和维护。

三、案例代码

以上完整的案例代码,请见以下地址:
https://download.csdn.net/download/aiwusheng/92620719

该案例代码是一个完整的Spring Boot项目示例,展示了如何实现文件上传功能并集成SQLite数据库。项目采用标准的MVC分层架构,包含Controller层、Service层、Repository层和Entity层,代码结构清晰,便于学习和扩展。

项目结构说明:

  1. Controller层:处理HTTP请求,接收参数并返回响应
  2. Service层:实现业务逻辑,包括用户创建、查询等操作
  3. Repository层:继承JpaRepository,提供数据访问接口
  4. Entity层:定义数据表映射实体类
  5. 配置文件:包含Gradle依赖配置和数据库连接配置

主要功能:

  1. 多文件上传接口(POST /api/uploadFiles)
  2. 用户创建接口(POST /api/post/create/user)
  3. 用户查询接口(GET /api/get/users)
  4. 参数校验和异常处理
  5. 事务管理
  6. 日志记录

技术栈:

  1. Spring Boot 3.x
  2. Spring Data JPA
  3. SQLite数据库
  4. Lombok简化代码
  5. 参数校验注解
  6. Spring Web(文件上传支持)

项目可直接运行,适合作为Spring Boot文件上传和数据库集成的入门学习参考。

相关推荐
过期动态13 小时前
Java开发中的@EnableWebMvc注解和WebMvcConfigurer接口
java·开发语言·spring boot·spring·tomcat·maven·idea
坐怀不乱杯魂14 小时前
Linux网络 - HTTP协议
网络·网络协议·http
野犬寒鸦14 小时前
从零起步学习并发编程 || 第一章:初步认识进程与线程
java·服务器·后端·学习
我爱娃哈哈14 小时前
SpringBoot + Flowable + 自定义节点:可视化工作流引擎,支持请假、报销、审批全场景
java·spring boot·后端
韩师学子--小倪15 小时前
SpringBoot 优雅停服
spring boot·tomcat
李梨同学丶16 小时前
0201好虫子周刊
后端
思想在飞肢体在追16 小时前
Springboot项目配置Nacos
java·spring boot·后端·nacos
JavaGuide19 小时前
推荐一个基于 Spring Boot 4.0 + Java 21 + Spring AI 2.0 的大模型项目!
java·spring boot·spring
Loo国昌19 小时前
【垂类模型数据工程】第四阶段:高性能 Embedding 实战:从双编码器架构到 InfoNCE 损失函数详解
人工智能·后端·深度学习·自然语言处理·架构·transformer·embedding