Spring Boot 3 多版本 API 控制器与动态 Bean 注册
在实际业务中,API 接口往往需要 迭代更新,但不能影响已有的调用方,因此 API 多版本管理 成为微服务架构的重要课题。本文结合 Spring Boot 3 的新特性,介绍一种 基于动态 Bean 注册与条件配置 的多版本 API 管理方案,并提供扩展优化思路。
一、实现目标
支持 多版本 API 控制器(如 /api/v1/users、/api/v2/users)通过 配置文件 动态激活某个版本支持 请求头选择版本(X-API-Version: v2)兼顾 灵活扩展(灰度升级、弃用策略、API 文档分组)
二、核心方案
1. 定义版本注解与条件
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ApiVersion {
String value();
}
public class ApiVersionCondition implements RequestCondition<ApiVersionCondition> {
private final String apiVersion;
public ApiVersionCondition(String apiVersion) {
this.apiVersion = apiVersion;
}
@Override
public ApiVersionCondition combine(ApiVersionCondition other) {
return new ApiVersionCondition(other.getApiVersion());
}
@Override
public ApiVersionCondition getMatchingCondition(HttpServletRequest request) {
String version = request.getHeader("X-API-Version");
return this.apiVersion.equals(version) ? this : null;
}
@Override
public int compareTo(ApiVersionCondition other, HttpServletRequest request) {
return other.getApiVersion().compareTo(this.apiVersion);
}
public String getApiVersion() {
return apiVersion;
}
}
2. 创建多版本控制器
// V1 控制器
@RestController
@ApiVersion("v1")
@RequestMapping("/api/v1")
public class UserControllerV1 {
@GetMapping("/users")
public ResponseEntity<List<User>> getUsers() {
// V1 实现
return ResponseEntity.ok(Collections.emptyList());
}
}
// V2 控制器
@RestController
@ApiVersion("v2")
@RequestMapping("/api/v2")
public class UserControllerV2 {
@GetMapping("/users")
public ResponseEntity<List<UserV2>> getUsers() {
// V2 实现
return ResponseEntity.ok(Collections.emptyList());
}
}
3. 动态 Bean 注册配置
方案一:手动注册(灵活但稍显复杂)
@Configuration
public class DynamicControllerRegistration implements BeanDefinitionRegistryPostProcessor {
private final Environment environment;
public DynamicControllerRegistration(Environment environment) {
this.environment = environment;
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
String activeVersion = environment.getProperty("api.version.active", "v1");
if ("v1".equals(activeVersion)) {
registerController(registry, UserControllerV1.class);
} else if ("v2".equals(activeVersion)) {
registerController(registry, UserControllerV2.class);
}
}
private void registerController(BeanDefinitionRegistry registry, Class<?> controllerClass) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(controllerClass);
registry.registerBeanDefinition(
StringUtils.uncapitalize(controllerClass.getSimpleName()),
builder.getBeanDefinition()
);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {}
}
方案二:条件注解(推荐,更简洁)
@RestController
@RequestMapping("/api/v1")
@ConditionalOnProperty(name = "api.version.active", havingValue = "v1")
public class UserControllerV1 { ... }
@RestController
@RequestMapping("/api/v2")
@ConditionalOnProperty(name = "api.version.active", havingValue = "v2")
public class UserControllerV2 { ... }
4. 请求拦截与异常处理
public class ApiVersionInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
if (handler instanceof HandlerMethod) {
ApiVersion apiVersion = handler.getBean().getClass().getAnnotation(ApiVersion.class);
if (apiVersion != null) {
String requestVersion = request.getHeader("X-API-Version");
if (!apiVersion.value().equals(requestVersion)) {
throw new ApiVersionException("API version mismatch");
}
}
}
return true;
}
}
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ApiVersionException.class)
public ResponseEntity<ErrorResponse> handleApiVersionException(ApiVersionException ex) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(new ErrorResponse("API_VERSION_MISMATCH", ex.getMessage()));
}
}
5. 配置文件
# application.properties
api.version.active=v2
三、优化与扩展
1. 运行时热切换
结合 Spring Cloud Config / Nacos 配置中心 + @RefreshScope,可实现 API 版本 动态切换,支持灰度发布。
2. API 路由器模式(集中式)
如果版本非常多,可以写一个路由器,统一分发:
@RestController
@RequestMapping("/api/users")
public class UserControllerRouter {
private final UserServiceV1 userServiceV1;
private final UserServiceV2 userServiceV2;
@GetMapping
public Object getUsers(@RequestHeader("X-API-Version") String version) {
if ("v1".equals(version)) return userServiceV1.getUsers();
if ("v2".equals(version)) return userServiceV2.getUsers();
throw new ApiVersionException("Unsupported version: " + version);
}
}
3. OpenAPI/Swagger 文档分组
使用 SpringDoc OpenAPI 配置分组:
/v1/api-docs/v2/api-docs
让调用方清晰查看不同版本的接口文档。
4. 弃用策略
在旧版本接口上标记:
@Deprecated响应头:
Deprecation: true
Sunset: 2025-12-31
Link: <https://yourapi.com/docs/v2>; rel="successor-version"
便于调用方提前迁移。
四、架构全景图

五、方案优势
灵活性:支持 Header + URI 两种版本选择方式动态控制:配置文件决定激活版本,可扩展热切换清晰分离:每个版本独立,易维护、易测试全生命周期管理:支持新增、灰度、弃用、下线
✅ 这套方案可以覆盖从 开发 → 部署 → 升级 → 下线 的完整 API 版本管理流程
© 版权声明
文章版权归作者所有,未经允许请勿转载。如内容涉嫌侵权,请在本页底部进入<联系我们>进行举报投诉!
THE END

















暂无评论内容