Backend Development 6 min read

Implement Multi-Version REST APIs in SpringBoot with a Single Annotation

Learn how to implement multi-version REST APIs in SpringBoot 2.7.16 using a single custom annotation, custom RequestCondition, and HandlerMapping, enabling seamless version control, client compatibility, and flexible business scenarios with clear code examples and testing screenshots.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Implement Multi-Version REST APIs in SpringBoot with a Single Annotation

1. Introduction

Multiple versions of a Rest API are needed when the system upgrades functionality, when different client versions must be supported, or when diverse business scenarios require distinct API contracts.

2. Practical Example

2.1 Desired Effect

<code>@RestController
@RequestMapping("/users")
public class UserController {

  @GetMapping("")
  @ApiVersion("v1")
  public Object v1() {
    return "User v1";
  }

  @GetMapping("")
  @ApiVersion("v2")
  public Object v2() {
    return "User v2";
  }
}
</code>

Without additional configuration, Spring cannot differentiate the two methods and will raise an error at startup.

2.2 Custom Annotation

<code>@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiVersion {
    // specific version number
    String value();
}
</code>

The annotation marks a method with its API version.

2.3 Custom RequestCondition

<code>public class ApiVersionCondition implements RequestCondition<ApiVersionCondition> {
    private final String apiVersion;
    public ApiVersionCondition(String apiVersion) { this.apiVersion = apiVersion; }
    @Override
    public ApiVersionCondition combine(ApiVersionCondition other) { return this; }
    @Override
    public ApiVersionCondition getMatchingCondition(HttpServletRequest request) {
        String requestedVersion = request.getHeader("X-API-Version");
        if (apiVersion.equals(requestedVersion)) { return this; }
        return null;
    }
    @Override
    public int compareTo(ApiVersionCondition other, HttpServletRequest request) {
        return this.apiVersion.compareTo(other.apiVersion);
    }
}
</code>

This condition lets Spring select the method whose @ApiVersion matches the request header.

2.4 Custom HandlerMapping

<code>public class PackVersionRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
    @Override
    protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
        RequestMappingInfo mappingInfo = super.getMappingForMethod(method, handlerType);
        if (mappingInfo != null) {
            ApiVersion apiVersionAnnotation = method.getAnnotation(ApiVersion.class);
            if (apiVersionAnnotation != null) {
                String apiVersion = apiVersionAnnotation.value();
                ApiVersionCondition apiVersionCondition = new ApiVersionCondition(apiVersion);
                mappingInfo = mappingInfo.addCustomCondition(apiVersionCondition);
            }
        }
        return mappingInfo;
    }
    @Override
    protected boolean isHandler(Class<?> beanType) {
        return super.isHandler(beanType);
    }
}
</code>

Register the custom mapping:

<code>@Component
public class PackWebMvcRegistrations implements WebMvcRegistrations {
    @Override
    public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
        return new PackVersionRequestMappingHandlerMapping();
    }
}
</code>

2.5 Testing

When the request header does not contain X-API-Version , the endpoint returns 404.

Adding the header with value v1 or v2 invokes the corresponding method.

Based on the X-API-Version header value, the appropriate versioned API is called.

Conclusion

Multi-version Rest API interfaces are essential for handling system upgrades, client version differences, and varied business scenarios. By using a custom annotation and handler mapping, developers can implement version control easily, improving flexibility and scalability.

BackendJavaSpringBootREST APIAnnotationVersioning
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.