背景
最近我需要把几个独立的项目合并到一个项目中,每个项目都有自己的路径前缀。
最初的想法是在每个模块中定义一个路径前缀常量:
java
public static final String PREFIX = "/api/user";
@RestController
@RequestMapping(PREFIX + "/profile")
public class UserProfileController {
// ...
}
这种方式虽然能用,但总感觉不够优雅。既然 Spring 会扫描所有的 Controller,并将类和方法上的 @RequestMapping 进行合并,那必然有办法统一添加路径前缀。
功夫不负有心人,发现了 WebMvcConfigurer 这个宝藏。
解决方案
通过实现 WebMvcConfigurer 接口的 configurePathMatch 方法,可以优雅地为指定的 Controller 添加统一前缀。
基础用法
java
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
// 为所有 @RestController 添加 /api 前缀
configurer.addPathPrefix("/api",
c -> c.isAnnotationPresent(RestController.class));
}
}
这样,所有带有 @RestController 注解的类,其路径都会自动加上 /api 前缀。
按包名区分前缀
如果需要为不同模块添加不同的前缀,可以根据包名来区分:
java
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.addPathPrefix("/api/user",
c -> c.getPackageName().startsWith("com.example.user"));
configurer.addPathPrefix("/api/order",
c -> c.getPackageName().startsWith("com.example.order"));
configurer.addPathPrefix("/api/product",
c -> c.getPackageName().startsWith("com.example.product"));
}
}
自定义注解方式
更灵活的做法是定义自己的注解:
java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@RestController
public @interface UserApi {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@RestController
public @interface OrderApi {
}
然后在配置中使用:
java
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.addPathPrefix("/api/user",
c -> c.isAnnotationPresent(UserApi.class));
configurer.addPathPrefix("/api/order",
c -> c.isAnnotationPresent(OrderApi.class));
}
}
Controller 中使用:
java
@UserApi
@RequestMapping("/profile")
public class UserProfileController {
@GetMapping("/{id}")
public User getProfile(@PathVariable Long id) {
// 实际路径: GET /api/user/profile/{id}
return userService.findById(id);
}
}
原理简析
PathMatchConfigurer.addPathPrefix() 方法接收两个参数:
prefix: 要添加的路径前缀predicate: 一个Predicate<Class<?>>用于判断哪些 Controller 需要添加前缀
Spring MVC 在初始化时会遍历所有的 Controller,对于满足条件的 Controller,会将前缀与原有的 @RequestMapping 路径进行拼接。
注意事项
- Spring Boot 版本: 此方法在 Spring Boot 2.x 以上版本中均可使用
- 多个前缀匹配: 如果一个 Controller 同时满足多个前缀条件,会按配置顺序应用第一个匹配的前缀
- Swagger/OpenAPI: 使用此方式添加的前缀,Swagger 文档会自动识别,无需额外配置
总结
相比在每个 Controller 上手动拼接路径前缀,使用 WebMvcConfigurer.configurePathMatch() 的方式更加优雅:
- 前缀配置集中管理
- Controller 代码更简洁
- 模块划分更清晰
- 后期维护更方便