Skip to content

路由


两种定义方式

方式一:注解路由(推荐,最常用)

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 开启:

yaml
logging:
  level:
    org.springframework.web: debug

启动时控制台会打印所有映射的路由。


HTTP 方法映射

注解HTTP 方法对应操作
@GetMappingGET查询
@PostMappingPOST创建
@PutMappingPUT全量更新
@PatchMappingPATCH部分更新
@DeleteMappingDELETE删除
@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') 会忽略非数字

面向 PHP 开发者的 Spring Boot 文档