Java/后端

SpringBoot 接口注解

记录了SpringBoot中的一些接口注解,后续还会有更新

Spring MVC 参数接收与 Controller

一、Controller 路径前缀

1. 全局前缀

application.yml 中配置:

server:
  servlet:
    context-path: /api

所有接口都会自动添加 /api

实际请求:/api/users

2. Controller 前缀

@RestController
@RequestMapping("/users")
public class UserController {
}

类中的接口都会以 /users 开头。

如果同时配置了:

server:
  servlet:
    context-path: /api

最终路径是:

/api/users

不要在 Controller 中再次写 /api,否则会变成:

/api/api/users

二、@RestController

@RestController
public class UserController {
}

@RestController 等价于:

@Controller
@ResponseBody

它表示:

  1. 当前类是 Spring MVC 控制器;
  2. 方法返回值直接写入 HTTP 响应体;
  3. 返回 Java 对象时,通常由 Jackson 自动转换为 JSON。

例如:

@GetMapping("/users/1")
public User getUser() {
    return new User(1, "张三");
}

响应结果:

{
  "id": 1,
  "name": "张三"
}

三、HTTP 方法映射

注解HTTP 方法常见用途
@GetMappingGET查询资源
@PostMappingPOST新增资源
@PutMappingPUT全量修改
@PatchMappingPATCH局部修改
@DeleteMappingDELETE删除资源

示例:

@GetMapping("/users")
public List<User> list() {
    return userService.findAll();
}

@PostMapping("/users")
public User add(@RequestBody User user) {
    return userService.add(user);
}

Spring 启动时会根据 Controller 注解生成路由表:

HTTP 方法路径Controller 方法
GET/userslist()
GET/users/{id}getById()
POST/usersadd()
PUT/users/{id}update()
DELETE/users/{id}delete()

请求进入 Spring 后,Spring 会同时匹配:

HTTP 方法 + 请求路径

常见结果:

  • 路径不存在:404 Not Found
  • 路径存在但 HTTP 方法不匹配:405 Method Not Allowed

注解主要负责路由匹配和请求分发,真正的业务逻辑仍然需要自己编写。


四、Spring MVC 参数接收方式

常见的请求参数接收方式包括:

  • @PathVariable
  • @RequestParam
  • @RequestBody
  • @RequestHeader
  • @CookieValue
  • @RequestPart
  • @ModelAttribute

可以按照“数据从哪里来”进行记忆。


1. @PathVariable

@PathVariable 用于接收 URL 路径中的参数。

示例

@RestController
@RequestMapping("/users")
public class UserController {

    @GetMapping("/{id}")
    public User getById(@PathVariable int id) {
        return userService.findById(id);
    }
}

请求地址:

GET /users/18

18 会被传递给方法参数 id

如果配置了全局前缀:

server:
  servlet:
    context-path: /api

实际请求地址为:

GET /api/users/18

参数名不同

@GetMapping("/{id}")
public User getById(@PathVariable("id") int userId) {
    return userService.findById(userId);
}

这里:

URL 占位符:id
Java 参数名:userId

必须通过 @PathVariable("id") 明确指定对应关系。

常见用途

GET /users/18
GET /orders/1001
PUT /books/5
DELETE /products/8

通常用于表示具体资源的 ID。


2. @RequestParam

@RequestParam 用于接收 URL 查询参数,也就是 ? 后面的参数。

示例

@GetMapping("/users")
public User getById(@RequestParam int id) {
    return userService.findById(id);
}

请求地址:

GET /users?id=18

参数 id=18 会传递给方法参数 id

可选参数

@GetMapping("/users")
public List<User> list(
        @RequestParam(required = false) String name,
        @RequestParam(defaultValue = "1") int page,
        @RequestParam(defaultValue = "10") int size
) {
    return userService.search(name, page, size);
}

请求示例:

GET /users?name=张三&page=1&size=10

常用属性:

@RequestParam(required = false)
@RequestParam(defaultValue = "1")
@RequestParam("userName")

如果参数是必填的,但请求中没有传递,通常会返回 400 Bad Request

参数名不同

@GetMapping("/users")
public User get(
        @RequestParam("id") int userId
) {
    return userService.findById(userId);
}

URL 中的参数名是 id,Java 方法参数名是 userId

常见用途

GET /users?name=张三
GET /users?page=1&size=10
GET /products?category=book
GET /orders?status=paid

通常用于:

  • 条件查询;
  • 分页;
  • 排序;
  • 筛选;
  • 搜索。

3. @RequestBody

@RequestBody 用于接收 HTTP 请求体中的数据,通常接收 JSON,并将 JSON 转换为 Java 对象。

示例

@PostMapping("/users")
public User add(@RequestBody User user) {
    return userService.add(user);
}

请求:

POST /users
Content-Type: application/json

请求体:

{
  "name": "张三",
  "age": 18
}

JSON 字段会映射到 User 对象的属性:

public class User {
    private String name;
    private Integer age;

    // getter、setter
}

请求头要求

通常需要:

Content-Type: application/json

否则 Spring 可能无法正确选择 JSON 转换器。

可以使用:

  • Postman;
  • Apifox;
  • curl;
  • 前端 fetch
  • Axios。

常见搭配

新增:

@PostMapping("/users")
public User add(@RequestBody User user) {
    return userService.add(user);
}

全量修改:

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

局部修改:

@PatchMapping("/users/{id}")
public User patch(
        @PathVariable Long id,
        @RequestBody UserPatchRequest request
) {
    return userService.patch(id, request);
}

注意事项

一个 Controller 方法通常只能有一个 @RequestBody

// 不推荐,也通常无法正常处理
public void method(
        @RequestBody User user,
        @RequestBody Address address
) {
}

如果请求体包含多个对象,应封装成一个请求 DTO:

public class CreateOrderRequest {
    private User user;
    private Address address;
}

然后:

@PostMapping("/orders")
public Order create(@RequestBody CreateOrderRequest request) {
    return orderService.create(request);
}

@RequestBody 技术上不只适用于 POST,也可以用于 PUT、PATCH 等请求。虽然 GET 请求可能存在请求体,但很多客户端、代理和服务器不会稳定处理,因此通常不建议在 GET 中使用请求体。


4. @RequestHeader

用于接收请求头中的参数。

@GetMapping("/profile")
public User profile(
        @RequestHeader("Authorization") String authorization
) {
    return userService.getProfile(authorization);
}

请求头:

Authorization: Bearer token-value

也可以设置为可选:

@RequestHeader(
    value = "X-Trace-Id",
    required = false
) String traceId

常见用途:

  • Authorization
  • User-Agent
  • Content-Type
  • Accept
  • 自定义追踪 ID

5. @CookieValue

用于获取 Cookie。

@GetMapping("/session")
public String session(
        @CookieValue(value = "SESSION", required = false)
        String sessionId
) {
    return sessionId;
}

如果 Cookie 不存在且没有设置 required = false,可能返回 400 Bad Request


6. @RequestPart

用于接收 multipart/form-data 请求,常见于文件上传。

@PostMapping("/avatar")
public String upload(
        @RequestPart("file") MultipartFile file
) {
    return fileService.save(file);
}

请求类型:

Content-Type: multipart/form-data

如果同时上传文件和 JSON:

@PostMapping(
        value = "/articles",
        consumes = MediaType.MULTIPART_FORM_DATA_VALUE
)
public Article create(
        @RequestPart("file") MultipartFile file,
        @RequestPart("article") Article article
) {
    return articleService.create(file, article);
}

7. @ModelAttribute

用于接收表单参数,并自动封装成对象。

@PostMapping("/users")
public String add(@ModelAttribute User user) {
    userService.add(user);
    return "success";
}

表单数据:

name=张三&age=18

@ModelAttribute 常用于传统表单提交。前后端分离项目中,如果传递 JSON,通常使用 @RequestBody


五、Controller 返回响应

1. 直接返回对象

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

Spring 会将返回的 Java 对象转换为 JSON。


2. 使用 ResponseEntity

ResponseEntity 可以同时控制:

  • HTTP 状态码;
  • 响应头;
  • 响应体。
@GetMapping("/{id}")
public ResponseEntity<User> getById(
        @PathVariable Long id
) {
    User user = userService.findById(id);

    if (user == null) {
        return ResponseEntity.notFound().build();
    }

    return ResponseEntity.ok(user);
}

常见写法:

ResponseEntity.ok(body)
ResponseEntity.status(HttpStatus.CREATED).body(body)
ResponseEntity.notFound().build()
ResponseEntity.noContent().build()
ResponseEntity.badRequest().build()

3. @ResponseStatus

可以直接指定方法成功时的状态码:

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

新增成功通常使用:

201 Created

删除成功且没有响应体时可以使用:

204 No Content

六、参数校验

可以使用 @Valid@Validated 配合校验注解。

@PostMapping("/users")
public User add(@Valid @RequestBody User user) {
    return userService.add(user);
}

实体类:

public class User {

    @NotBlank
    private String name;

    @Min(1)
    private Integer age;
}

常见校验注解:

@NotNull
@NotBlank
@NotEmpty
@Min
@Max
@Size
@Email
@Pattern

@NotBlank 适合字符串,@NotNull 适合判断对象是否为空。


七、跨域

开发前后端分离项目时,浏览器可能出现跨域问题。

局部配置:

@CrossOrigin
@RestController
@RequestMapping("/users")
public class UserController {
}

也可以配置具体来源:

@CrossOrigin(
        origins = "http://localhost:5173"
)

生产项目通常更推荐进行全局 CORS 配置,而不是每个 Controller 都添加 @CrossOrigin


八、完整 Controller 示例

@RestController
@RequestMapping("/books")
public class BookController {

    private final BookService bookService;

    public BookController(BookService bookService) {
        this.bookService = bookService;
    }

    // 查询列表
    // GET /books?author=张三&page=1&size=10
    @GetMapping
    public List<Book> list(
            @RequestParam(required = false) String author,
            @RequestParam(defaultValue = "1") int page,
            @RequestParam(defaultValue = "10") int size
    ) {
        return bookService.findAll(author, page, size);
    }

    // 查询单个
    // GET /books/1
    @GetMapping("/{id}")
    public ResponseEntity<Book> getById(
            @PathVariable Long id
    ) {
        Book book = bookService.findById(id);

        if (book == null) {
            return ResponseEntity.notFound().build();
        }

        return ResponseEntity.ok(book);
    }

    // 新增
    // POST /books
    // Body: {"name":"Java","author":"张三"}
    @PostMapping
    public ResponseEntity<Book> add(
            @Valid @RequestBody Book book
    ) {
        Book savedBook = bookService.add(book);

        return ResponseEntity
                .status(HttpStatus.CREATED)
                .body(savedBook);
    }

    // 全量修改
    // PUT /books/1
    @PutMapping("/{id}")
    public ResponseEntity<Book> update(
            @PathVariable Long id,
            @Valid @RequestBody Book book
    ) {
        book.setId(id);

        Book updatedBook = bookService.update(book);

        if (updatedBook == null) {
            return ResponseEntity.notFound().build();
        }

        return ResponseEntity.ok(updatedBook);
    }

    // 局部修改
    // PATCH /books/1
    @PatchMapping("/{id}")
    public ResponseEntity<Book> patch(
            @PathVariable Long id,
            @RequestBody BookPatchRequest request
    ) {
        Book updatedBook = bookService.patch(id, request);

        if (updatedBook == null) {
            return ResponseEntity.notFound().build();
        }

        return ResponseEntity.ok(updatedBook);
    }

    // 删除
    // DELETE /books/1
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> delete(
            @PathVariable Long id
    ) {
        boolean removed = bookService.deleteById(id);

        if (!removed) {
            return ResponseEntity.notFound().build();
        }

        return ResponseEntity.noContent().build();
    }
}

九、RESTful 风格

RESTful 不是某个 Spring 注解,而是一种接口设计风格。

核心特征:

  1. URL 使用名词表示资源;
  2. HTTP 方法表示操作;
  3. 使用状态码表达处理结果;
  4. 使用路径表示资源层级;
  5. 请求尽量保持无状态。

示例:

GET     /books       查询书籍列表
GET     /books/1     查询 ID 为 1 的书
POST    /books       新增书籍
PUT     /books/1     全量修改书籍
PATCH   /books/1     局部修改书籍
DELETE  /books/1     删除书籍

不推荐:

GET /getBooks
POST /deleteBook
GET /updateBook

更推荐:

GET /books
DELETE /books/1
PUT /books/1

需要注意,使用了 @GetMapping 并不自动保证方法内部一定是查询逻辑。RESTful 是开发者遵守的接口设计约定,Spring 只负责根据注解匹配和分发请求。


十、三种核心参数注解对比

注解数据来源典型 URL/请求常见用途
@PathVariableURL 路径/users/18查询、修改、删除指定资源
@RequestParamURL 查询参数/users?id=18搜索、过滤、分页
@RequestBodyHTTP 请求体JSON Body新增、修改、复杂查询条件

记忆方式:

/users/18

使用:

@PathVariable
/users?id=18

使用:

@RequestParam
{
  "name": "张三",
  "age": 18
}

使用:

@RequestBody

常见搭配:

查询单个:GET + @PathVariable
查询列表:GET + @RequestParam
新增:POST + @RequestBody
全量修改:PUT + @PathVariable + @RequestBody
局部修改:PATCH + @PathVariable + @RequestBody
删除:DELETE + @PathVariable