控制器
Laravel 控制器是「接收请求、组织业务、返回响应」的枢纽;Spring Boot 控制器概念完全一致,区别在于:方法签名声明即绑定(参数由框架填充),返回值即响应体(由转换器序列化)。
两种控制器注解
| 注解 | 返回值处理 | 使用场景 |
|---|---|---|
@Controller | 返回值走「视图解析」,或需配 @ResponseBody | 服务端渲染页面(Thymeleaf) |
@RestController | 返回值直接序列化为 JSON | 99% 的后端接口场景 |
java
@RestController // = @Controller + @ResponseBody
public class PostController { ... }只做 API 就用 @RestController
@RestController 等价于「@Controller + 每个方法都带 @ResponseBody」。写 JSON 接口用它最省事。返回值如何变成 JSON、如何定制状态码,见 响应。
一个完整控制器
java
package com.example.demo.post;
import jakarta.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/posts")
public class PostController {
private final PostService service;
public PostController(PostService service) { // 构造器注入业务服务
this.service = service;
}
@GetMapping
public List<Post> index() {
return service.list();
}
@GetMapping("/{id}")
public Post show(@PathVariable Long id) {
return service.get(id);
}
@PostMapping
public ResponseEntity<Post> store(@Valid @RequestBody PostDto dto) {
return ResponseEntity.status(HttpStatus.CREATED).body(service.create(dto));
}
@PutMapping("/{id}")
public Post update(@PathVariable Long id, @RequestBody PostDto dto) {
return service.update(id, dto);
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> destroy(@PathVariable Long id) {
service.delete(id);
return ResponseEntity.noContent().build();
}
}对照 Laravel:
| Laravel | Spring Boot |
|---|---|
public function index() | public List<Post> index() |
public function store(Request $request) | public Post store(@RequestBody PostDto dto) |
return response()->json($posts, 201) | return ResponseEntity.status(201).body(...) |
return response()->noContent() | return ResponseEntity.noContent().build() |
控制器里尽量不写业务逻辑
Laravel 最佳实践是「薄控制器 + 服务层」;Spring 社区同样强调。但要注意:Java 里把逻辑抽到 Service 不只是为了整洁,更是为了让「事务注解」和「依赖注入」生效。
关键:Spring 的魔法只对容器管理的 Bean 生效
@Transactional、@Cacheable 等注解靠代理实现。PostController 是 Bean,注入的 PostService 也是 Bean,所以 service.create(dto) 里的事务注解才有效。如果你在控制器里直接 new PostService(...),注解全部失效。永远通过注入拿依赖,不要自己 new Bean。
控制器方法可以返回哪些类型
| 返回类型 | 结果 |
|---|---|
String | 纯文本 / 视图名(取决于是否 @RestController) |
POJO / List<POJO> | 自动序列化为 JSON |
Map / List | 直接当 JSON 用(临时结构) |
ResponseEntity<T> | 完整控制状态码 + 头 + 体 |
void | 配合 HttpServletResponse 手动写 |
java
@GetMapping("/stats")
public Map<String, Object> stats() {
return Map.of("posts", 100, "users", 50);
}TIP
临时返回 Map 很方便,但对外 API 建议用 DTO(PostDto),字段名、可空性可控,还能加 @JsonProperty 定制输出名。见 JPA 序列化与 DTO。
处理依赖:构造器注入
java
@RestController
public class PostController {
private final PostService service;
public PostController(PostService service) {
this.service = service;
}
}Spring 自动把 PostService 单例注入进来。等价于 Laravel 控制器构造函数里 app(PostService::class)。
服务层(Service)对应 Laravel 的哪一层?
| Laravel | Spring Boot |
|---|---|
app/Services/PostService.php | @Service public class PostService |
控制器 -> PostService | 控制器注入 PostService |
java
@Service
public class PostService {
private final PostRepository repository;
public PostService(PostRepository repository) {
this.repository = repository;
}
@Transactional
public Post create(PostDto dto) {
return repository.save(dto.toEntity());
}
}常用姿势速查
| 需求 | 写法 |
|---|---|
| 注入 Service | 构造器注入(推荐) |
| 返回 201 | ResponseEntity.status(HttpStatus.CREATED) |
| 返回 204 | ResponseEntity.noContent().build() |
| 返回 404 | throw new NotFoundException(...),交给全局异常处理器(见 异常处理) |
| 拿请求体 | @RequestBody PostDto dto |
| 拿路径参数 | @PathVariable Long id |
| 拿查询参数 | @RequestParam String keyword |
| 校验请求体 | @Valid @RequestBody PostDto dto |
下一节,把「参数绑定」彻底讲透:请求(参数绑定)。