在 Spring Boot 中使用 Java Record

本文将带你了解如何在 Spring Boot 应用中利用 Java Record 来提高其效率和可读性。

Java Record 是什么?

Java Record 是一种专为保存不可变数据而设计的类。它们自动提供 equals()hashCode()toString() 等方法的实现,大大减少了模板代码。因此,它们非常适合在 Spring Boot 应用中创建数据传输对象(DTO)、实体和其他模型类。

Java Record 示例

举一个简单的例子,用 Java Record 来表示一个 Person

public record Person(String name, int age) {}

在本例中,Person 是一个 Record,包含两个字段:nameage。Java 自动为该 Record 提供了以下内容:

  • 一个 public 构造函数:Person(String name, int age)
  • 属性的 public Getter 方法:name()age()
  • 实现了 equals()hashCode()toString() 方法

使用 Getter 方法

Record 的主要特点之一是提供了用于访问字段的隐式 Getter 方法。这些方法以字段本身命名。

创建 Person 实例并使用其 Getter 方法:

public class Main {
    public static void main(String[] args) {
        Person person = new Person("Alice", 30);

        // 调用 Getter 方法
        String name = person.name();
        int age = person.age();

        System.out.println("Name: " + name);
        System.out.println("Age: " + age);
    }
}

输出如下:

Name: Alice
Age: 30

在 Spring Boot 中使用 Record 的主要优势

  1. 模板代码:Record 自动生成构造器、Getter 方法、equals()hashCode()toString() 方法,大大减少了模板代码。
  2. 不可更改:Record 默认创建不可变的数据结构,因此非常适合数据完整性至关重要的使用场景。
  3. 职责明确:Record 的目的很明确 - 它只是数据的载体。这使代码更易于阅读和维护。
  4. 易于使用:开发人员只需一行代码就能定义一个 Record,从而使数据保存类的创建更简单、更高效。

Spring Boot 示例项目

软件版本:

  • JDK:16+
  • Spring Boot:3.0

你可以通过 Spring Initializr 初始化项目。添加 Spring WebSpring Data JPAH2 Database 等依赖。

示例项目由以下几层组成:

  • Controller 层:处理 HTTP 请求和响应。
  • Service 层:包含业务逻辑。
  • Repository 层:处理数据持久化。
  • Database:H2 内存数据库,以简化操作。

第 1 步:定义 Domain Record

首先,定义一个 User Record,它是 Domain 实体。

import jakarta.persistence.Entity;
import jakarta.persistence.Id;

@Entity
public record User(@Id Long id, String name, String email) {}

如上,使用 Java Record 来定义实体,从而使实体定义简洁且不可更改。

第 2 步:Repository 层

接着,创建一个用于数据访问的 Repository 接口:

import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {}

Spring Data JPA 会在运行时提供 Repository 实现。

第 3 步:Service 层

UserService 包含业务逻辑并与 UserRepository 交互:

import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserService {
    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public User createUser(User user) {
        return userRepository.save(user);
    }

    public List<User> getAllUsers() {
        return userRepository.findAll();
    }

    public User getUserById(Long id) {
        return userRepository.findById(id).orElseThrow();
    }

    public User updateUser(Long id, User userDetails) {
        User user = userRepository.findById(id).orElseThrow();
        User updatedUser = new User(user.id(), userDetails.name(), userDetails.email());
        return userRepository.save(updatedUser);
    }

    public void deleteUser(Long id) {
        userRepository.deleteById(id);
    }
}

该 Service 类使用 UserRepository 执行 CRUD 操作。

第 4 步:Controller 层

在 Controller 中,将 CRUD 操作公开为 HTTP 端点:

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/users")
public class UserController {
    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public User createUser(@RequestBody User user) {
        return userService.createUser(user);
    }

    @GetMapping
    public List<User> getAllUsers() {
        return userService.getAllUsers();
    }

    @GetMapping("/{id}")
    public User getUserById(@PathVariable Long id) {
        return userService.getUserById(id);
    }

    @PutMapping("/{id}")
    public User updateUser(@PathVariable Long id, @RequestBody User user) {
        return userService.updateUser(id, user);
    }

    @DeleteMapping("/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void deleteUser(@PathVariable Long id) {
        userService.deleteUser(id);
    }
}

UserController 处理 HTTP 请求,并调用 UserService 来完成 CRUD 操作。

第 5 步:使用 WebClient 进行测试

使用 WebClient 作为 REST 客户端来测试应用的 CRUD REST API。

首先,需要确保在 pom.xml 中包含了 spring-boot-starter-webflux 依赖。

下面是使用 WebClient 测试 CRUD 操作的完整代码:

import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

public class RestClient {
    private final WebClient webClient;

    public RestClient() {
        this.webClient = WebClient.create("http://localhost:8080");
    }

    public void createUser() {
        User user = new User(1L, "John Doe", "john@example.com");
        User createdUser = webClient.post()
                .uri("/users")
                .body(Mono.just(user), User.class)
                .retrieve()
                .bodyToMono(User.class)
                .block();
        System.out.println("Created User: " + createdUser);
    }

    public void getUser(Long id) {
        User user = webClient.get()
                .uri("/users/" + id)
                .retrieve()
                .bodyToMono(User.class)
                .block();
        System.out.println("User: " + user);
    }

    public void updateUser(Long id, User updatedUser) {
        User user = webClient.put()
                .uri("/users/" + id)
                .body(Mono.just(updatedUser), User.class)
                .retrieve()
                .bodyToMono(User.class)
                .block();
        System.out.println("Updated User: " + user);
    }

    public void deleteUser(Long id) {
        webClient.delete()
                .uri("/users/" + id)
                .retrieve()
                .bodyToMono(Void.class)
                .block();
        System.out.println("Deleted User with ID: " + id);
    }

    public static void main(String[] args) {
        RestClient client = new RestClient();

        // 测试创建用户
        client.createUser();

        // 测试检索用户
        client.getUser(1L);

        // 测试更新用户
        User updatedUser = new User(1L, "Jane Doe", "jane@example.com");
        client.updateUser(1L, updatedUser);

        // 测试删除用户
        client.deleteUser(1L);
    }

    private record User(Long id, String name, String email) {}
}

运行服务器,执行 RestClient 测试类,输出如下:

Created User: User[id=1, name=John Doe, email=john@example.com]
User: User[id=1, name=John Doe, email=john@example.com]
Updated User: User[id=1, name=Jane Doe, email=jane@example.com]
Deleted User with ID: 1

Ref:https://www.javaguides.net/2023/12/using-java-records-with-spring-boot.html