控制器
两种控制器
@RestController(最常用,返回 JSON)
java
@RestController // 所有方法自动 JSON 序列化
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
// 构造器注入(推荐方式)
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping
public List<User> index() { // 自动转 JSON
return userService.findAll();
}
@GetMapping("/{id}")
public User show(@PathVariable Long id) {
return userService.findById(id);
}
}
@RestController=@Controller+@ResponseBody,表示该类所有方法的返回值都自动序列化为 JSON。绝大多数 API 项目用这个。
@Controller(返回模板页面)
java
@Controller // 返回视图模板
public class HomeController {
@GetMapping("/")
public String home(Model model) {
model.addAttribute("title", "Welcome");
return "home"; // 渲染 templates/home.html
}
}依赖注入进控制器
java
@RestController
public class UserController {
// 方式一:构造器注入 ✅ 推荐
private final UserService userService;
private final Logger log = LoggerFactory.getLogger(getClass());
public UserController(UserService userService) {
this.userService = userService;
}
// 方式二:字段注入(不推荐,但常见)
@Autowired
private UserRepository userRepository;
}⚠️ 为什么推荐构造器注入?
- 构造器注入的对象可以用
final修饰,一旦赋值不可变- 构造器注入在 Spring 启动时就能发现循环依赖,而不是运行时才报错
- 字段注入 (
@Autowired) 在单元测试中难以 Mock
方法返回值处理
java
@RestController
public class UserController {
// 1. 直接返回对象 → 自动 JSON
@GetMapping("/{id}")
public User show(@PathVariable Long id) {
return userService.findById(id);
}
// 2. 返回 ResponseEntity → 完全控制状态码和头
@PostMapping
public ResponseEntity<User> create(@RequestBody User user) {
User saved = userService.save(user);
return ResponseEntity
.created(URI.create("/api/users/" + saved.getId())) // 201
.body(saved);
}
// 3. 返回 void → 204 No Content
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void delete(@PathVariable Long id) {
userService.delete(id);
}
}⚠️ 注意事项
1. 控制器默认是单例(Singleton)
java
@RestController
public class UserController {
// 所有请求共用这一个实例!不能在这里放请求级别的状态
private int counter = 0; // ❌ 并发环境下会出错
}和 PHP 不同,PHP-FPM 每个请求都是独立进程,控制器在每个请求中重新构造。Java 的控制器默认是单例 Bean,无状态是基本要求。如果需要状态,用
@Scope("request")或@SessionAttributes。
2. 不需要继承任何基类
java
// 不需要像 PHP 那样 class UserController extends Controller
@RestController // 只需要这个注解即可
public class UserController { }Spring 通过注解来判断类的角色,不依赖类继承。这也是为什么你看到的 Spring 代码很少有 extends。
3. 方法参数按需声明
java
@GetMapping("/{id}")
public User show(@PathVariable Long id, // 从路径取
@RequestParam int page, // 从查询参数取
HttpServletRequest req, // 直接注入 Servlet 原对象
Model model) { } // 注入模型(@Controller 场景用)Spring 自动匹配方法参数:检查参数类型和注解,从请求中提取对应值。你不需要像 PHP 那样手动
$request->input('id')。
4. 没有验证回调
php
// PHP: 控制器中可以写 validate、authorize 等逻辑
public function store(Request $request) {
$validated = $request->validate([...]);
$this->authorize('create', Post::class);
}java
// Java: 验证和授权通过注解处理,控制器中只保留业务逻辑
@PostMapping
public Post create(@Valid @RequestBody Post post) { // 验证在参数上
// 业务逻辑,没有 validate() 调用
}