Java | record | Controller逻辑

java 复制代码
@RestController
@RequestMapping("/api/students")
public class StudentController {

    private final StudentService service;

    public StudentController(StudentService service) {
        this.service = service;
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public StudentResponse create(@RequestBody CreateStudentRequest request) {
        return StudentResponse.from(service.createStudent(
                request.name(), request.grade(), request.notes()));
    }

    @GetMapping
    public List<StudentResponse> list() {
        return service.listStudents().stream()
                .map(StudentResponse::from).toList();
    }

    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Map<String, String> invalidInput(IllegalArgumentException exception) {
        return Map.of("message", exception.getMessage());
    }

    @ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Map<String, String> invalidJson() {
        return Map.of("message", "请求体必须是有效的学生 JSON 对象");
    }

    public record CreateStudentRequest(
            String name, String grade, String notes) {}

    public record StudentResponse(
            Long id, String name, String grade, String notes) {
        static StudentResponse from(Student student) {
            return new StudentResponse(
                    student.getId(), student.getName(),
                    student.getGrade(), student.getNotes());
        }
    }
}

首先看第一段代码:

java 复制代码
 @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public StudentResponse create(@RequestBody CreateStudentRequest request) {
        return StudentResponse.from(service.createStudent(
                request.name(), request.grade(), request.notes()));
    }

@RestController表示这个类是一个 Spring MVC 的 REST 控制器,可以接受HTTP请求

@RequestMapping("/api/students")表示这个 Controller 里的所有接口,都统一以/api/students

开头。

@PostMapping表示接收 POST /api/students 的请求

首先在类中使用record构造了两个类:

复制代码
CreateStudentRequest

用来便于操作网页端传来的json字符串。

复制代码
StudentResponse

用于将数据库的数据有选择的封装响应对象返回给前端。

为什么要有选择,因为它是专门用来返回给前端的响应对象。实体类Student中的某些字段不方便返回给前端,实体类和接口返回结构最好不要强耦合。如果强耦合,直接返回Student实体类,会将数据库内部结构暴漏出去。这个类控制了前端能看到什么。

这个类中存在的静态方法

java 复制代码
static StudentResponse from(Student student)

就是实现了把 Student 实体转换成 StudentResponse,进行有选择的数据封装。

java 复制代码
@RequestBody CreateStudentRequest request

表示把 HTTP 请求体里的 JSON,自动转换成一个 CreateStudentRequest 对象。

java 复制代码
@ResponseStatus(HttpStatus.CREATED)

表示这个接口成功后返回 HTTP 状态码:201 Created

每次 HTTP 请求最终都要有状态码;成功走正常响应,失败走异常处理或默认错误处理。


java 复制代码
return StudentResponse.from(
    service.createStudent(
        request.name(),
        request.grade(),
        request.notes()
    )
);

可以拆成3步:第一步先让 Service 创建学生

java 复制代码
Student student = service.createStudent(
    request.name(),
    request.grade(),
    request.notes()
);

第二步把实体对象转换成响应对象

java 复制代码
StudentResponse response =
    StudentResponse.from(student);

第三步将响应对象返回给前端。

接着看第二段代码:

java 复制代码
 @GetMapping
    public List<StudentResponse> list() {
        return service.listStudents().stream()
                .map(StudentResponse::from).toList();
    }

@GetMapping表示接收 Get /api/students 的请求

Service返回的是List<Student>,但 Controller 最终要返回List<StudentResponse>,所以需要进行类型转换。

java 复制代码
.map(StudentResponse::from)

是方法引用。

它等价于:

复制代码
.map(student -> StudentResponse.from(student))

意思就是:

复制代码
Student1 → StudentResponse1
Student2 → StudentResponse2
Student3 → StudentResponse3

接着看第三段代码:

java 复制代码
@ExceptionHandler(IllegalArgumentException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Map<String, String> invalidInput(IllegalArgumentException exception) {
       return Map.of("message", exception.getMessage());
}

这是一个异常处理。

对应StudentSercice里的

java 复制代码
@Transactional
	public Student createStudent(String name, String grade, String notes) {
		String normalizedName = name == null ? null : name.strip();
		if (normalizedName == null || normalizedName.isEmpty() || normalizedName.length() > 20) {
			throw new IllegalArgumentException("Student name must contain 1 to 20 characters");
		}
		if (notes != null && notes.length() > 200) {
			throw new IllegalArgumentException("Student notes must not exceed 200 characters");
		}

		String normalizedGrade = grade == null || grade.isBlank() ? null : grade.strip();
		return repository.save(new Student(normalizedName, normalizedGrade, notes));
	}

Service 抛出:

复制代码
IllegalArgumentException

Controller 捕获到以后:

复制代码
@ExceptionHandler(IllegalArgumentException.class)

就会调用:

复制代码
invalidInput(...)

然后返回:

复制代码
{
  "message": "Student name must contain 1 to 20 characters"
}

HTTP 状态码:

复制代码
400 Bad Request

接着看第四段代码:

java 复制代码
@ExceptionHandler(HttpMessageNotReadableException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Map<String, String> invalidJson() {
     return Map.of("message", "请求体必须是有效的学生 JSON 对象");
}

这个处理的是:

前端发来的 JSON 根本解析不了。

例如发:

复制代码
{name: 张三

这种非法 JSON。

Spring 在进入:

复制代码
create(...)

之前就可能解析失败。

于是抛:

复制代码
HttpMessageNotReadableException

这里捕获之后返回:

复制代码
{
  "message": "请求体必须是有效的学生 JSON 对象"
}

也是:

复制代码
400 Bad Request

注解 请求含义 常见用途
@GetMapping 获取 查询数据
@PostMapping 提交 新增数据
@PutMapping 更新 整体修改数据
@PatchMapping 更新 部分修改数据
@DeleteMapping 删除 删除数据

GET、POST、PUT、DELETE 都是客户端发给服务器的不同类型请求;服务器处理后都会返回响应。


关于record

record 是 Java 专门用来表示只承载数据的简单对象的一种语法。

java 复制代码
public record CreateStudentRequest(
        String name,
        String grade,
        String notes) {
}

如果不用 record,你以前可能会写:

java 复制代码
public class CreateStudentRequest {

    private String name;
    private String grade;
    private String notes;

    public String getName() {
        return name;
    }

    ...
}

但 record 可以直接简化成:

java 复制代码
record CreateStudentRequest(
    String name,
    String grade,
    String notes
) {}

Java 会自动帮你生成:

  • 构造器
  • name()
  • grade()
  • notes()
  • equals()
  • hashCode()
  • toString()

所以这里:

java 复制代码
request.name()
request.grade()
request.notes()

其实就是在取 record 里面的数据。


整份Controller流程

java 复制代码
StudentController

1. POST /api/students
   ↓
   接收创建学生 JSON
   ↓
   调 StudentService
   ↓
   返回 StudentResponse

2. GET /api/students
   ↓
   查询所有学生
   ↓
   Student 转 StudentResponse
   ↓
   返回列表

3. IllegalArgumentException
   ↓
   返回 400 + 错误信息

4. 非法 JSON
   ↓
   返回 400 + JSON 格式错误信息
相关推荐
勿忘,瞬间1 小时前
Mybatis高阶
java·数据库·mybatis
不甘先生1 小时前
Go 中 type、方法与指针接收者:从 str_name.Name() 看懂 Go 的类型系统
开发语言·后端·golang
quantdash_cc2 小时前
Python 股票 K 线数据质量校验:字段、缺失值、重复行和价格异常
开发语言·python·数据分析·量化交易·股票数据·quantdash
Zenova EdgeOS2 小时前
工业网关重试机制:从固定间隔到指数退避的工程实战
开发语言·网络·php
嘻哈baby2 小时前
Go 函数中的参数为什么不支持默认值?
java·开发语言·jvm
慧都小项2 小时前
MyEclipse 2026:当 Agent、MCP 与 Java 26 进入 Eclipse 工作流
java·eclipse·springboot·copilot·myeclipse
一晌小贪欢2 小时前
python-第29天:Python面向对象之多态与抽象类
开发语言·python·数据可视化·面向对象·python办公·python多态
Chester_19993 小时前
CSP202312C.树上搜索
开发语言·数据结构·c++·蓝桥杯·stl
Figo_Cheung3 小时前
Figo共振网络宇宙演化论(RNC) :从原初对称破缺到全息大和谐的演化路径研究
开发语言·php·量子计算