路由
两种定义方式
方式一:注解路由(推荐,最常用)
java
@RestController
@RequestMapping("/api/users") // 类比 Route::prefix('api/users')
public class UserController {
@GetMapping // Route::get('/api/users')
public List<User> index() { ... }
@GetMapping("/{id}") // Route::get('/api/users/{id}')
public User show(@PathVariable Long id) { ... }
@PostMapping // Route::post('/api/users')
public User create(@RequestBody User user) { ... }
@PutMapping("/{id}") // Route::put('/api/users/{id}')
public User update(@PathVariable Long id, @RequestBody User user) { ... }
@DeleteMapping("/{id}") // Route::delete('/api/users/{id}')
public void delete(@PathVariable Long id) { ... }
}方式二:Java Config 集中定义(类似 routes/web.php)
java
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("home");
registry.addRedirectViewController("/old", "/new");
}
}注解方式是绝对主流。Java Config 方式仅在无需控制器逻辑的静态路由场景使用。
参数绑定
java
@GetMapping("/{category}/{id}")
public Post show(
@PathVariable String category, // 路径参数
@PathVariable Long id,
@RequestParam(defaultValue = "10") int size, // 查询参数 ?size=20
@RequestHeader("User-Agent") String ua // 请求头
) { ... }PHP 中
$_GET['size'] ?? 10,Java 中@RequestParam(defaultValue = "10")。 原因:Java 要求在编译期就知道参数是否存在,不能像 PHP 那样运行时动态读取并默认 null。
路由分组
java
@RestController
@RequestMapping("/api") // 类比 Route::prefix('api')
public class ApiController {
@GetMapping("/posts") // GET /api/posts
public List<Post> list() { ... }
@PostMapping("/posts") // POST /api/posts
public Post create(@RequestBody Post post) { ... }
}嵌套分组:
java
@RestController
@RequestMapping("/api")
public class PostController {
@GetMapping("/posts") // GET /api/posts
public List<Post> index() { ... }
@GetMapping("/posts/{id}") // GET /api/posts/1
public Post show(@PathVariable Long id) { ... }
}
@RestController
@RequestMapping("/api")
public class UserController {
@GetMapping("/users") // GET /api/users
public List<User> index() { ... }
}⚠️ 和 PHP 的差异:PHP 可以一个文件写所有路由,路由定义和控制器分离。Java 的路由直接写在控制器类上,路由散落在各个控制器的注解中。想全局看有哪些路由?可以在 application.yml 开启:
yamllogging: level: org.springframework.web: debug启动时控制台会打印所有映射的路由。
HTTP 方法映射
| 注解 | HTTP 方法 | 对应操作 |
|---|---|---|
@GetMapping | GET | 查询 |
@PostMapping | POST | 创建 |
@PutMapping | PUT | 全量更新 |
@PatchMapping | PATCH | 部分更新 |
@DeleteMapping | DELETE | 删除 |
@RequestMapping(method = RequestMethod.POST) | 任意 | 通用(不常用) |
⚠️ 注意事项
1. 路由定义发散在多个类中
php
// PHP:routes/web.php 集中查看所有路由
Route::get('/users', [UserController::class, 'index']);
Route::post('/users', [UserController::class, 'store']);java
// Java:路由写在每个控制器类上,需要 IDE 的 "Find Usages" 或端点日志查看
@GetMapping("/users") // 写在 UserController.java 第 15 行原因:Java 注解是声明式编程的体现。Spring 团队认为路由属于控制器的元数据,应该和控制器代码放在一起。习惯后配合 IDE 的 Structure 面板反而更方便——一个控制器文件包含了该资源的所有端点信息。
2. 没有路由缓存
bash
# PHP:生产环境可以 php artisan route:cache
# Java:没有也不需要。每次启动都会扫描注解重建路由表,耗时在毫秒级3. @RequestMapping 不写 method 会匹配所有 HTTP 方法
java
@RequestMapping("/test") // 匹配 GET/POST/PUT/DELETE 所有方法明确指定方法是个好习惯。
4. 路径参数类型不匹配会返回 400
java
@GetMapping("/{id}")
public User show(@PathVariable Long id) { }
// 访问 /users/abc → 400 Bad Request
// PHP 中 /users/{id} 传 abc 也是 404,因为 Eloquent find('abc') 会忽略非数字