GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • java.evolved
  • ←
  • →

This view

  • Original page
  • New address

GeezerVison is showing javaevolved.dev — Spring Framework 7 API Versioning | java.evolved

Home / Enterprise / Spring Framework 7 API Versioning
Enterprise Intermediate

Spring Framework 7 API Versioning

  • 🐛 Report a code issue
  • 🌐 Report a translation issue
  • 💡 Suggest a new pattern

Replace duplicated version-prefixed controllers with Spring Framework 7's native API versioning support.

Enterprise (Jakarta EE) Spring Framework REST
Code Comparison
✕ Spring Boot 2/3
// Version 1 controller
@RestController
@RequestMapping("/api/v1/products")
public class ProductControllerV1 {
    @GetMapping("/{id}")
    public ProductDtoV1 getProduct(
            @PathVariable Long id) {
        return service.getV1(id);
    }
}

// Version 2 — duplicated structure
@RestController
@RequestMapping("/api/v2/products")
public class ProductControllerV2 {
    @GetMapping("/{id}")
    public ProductDtoV2 getProduct(
            @PathVariable Long id) {
        return service.getV2(id);
    }
}
✓ Spring Framework 7+
// Configure versioning once
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void configureApiVersioning(
            ApiVersionConfigurer config) {
        config.useRequestHeader("X-API-Version");
    }
}

// Single controller, version per method
@RestController
@RequestMapping("/api/products")
public class ProductController {
    @GetMapping(value = "/{id}", version = "1")
    public ProductDtoV1 getV1(@PathVariable Long id) {
        return service.getV1(id);
    }

    @GetMapping(value = "/{id}", version = "2")
    public ProductDtoV2 getV2(@PathVariable Long id) {
        return service.getV2(id);
    }
}
Share 𝕏
See a problem with this code? Let us know.
Why the modern way wins
🗂️

No controller duplication

All versions live in one controller class; only the individual handler methods carry a version attribute.

⚙️

Centralised version strategy

Switch from header to URL or query-param versioning in a single configureApiVersioning call.

📈

Incremental evolution

Add a new version to one method without touching unrelated endpoints or creating new controller files.

Old Approach
Manual URL Path Versioning
Modern Approach
Native API Versioning
Since JDK
17
Difficulty
Intermediate
JDK Support
Spring Framework 7 API Versioning
Available

Available since Spring Framework 7.0 (requires Java 17+)

How it works

Before Spring Framework 7, API versioning required separate controller classes per version (e.g., /api/v1/products, /api/v2/products), duplicating request mappings and scattering version logic across many files. Spring Framework 7 introduces native versioning through a new version attribute on @RequestMapping and related annotations, plus a configureApiVersioning hook in WebMvcConfigurer. The version can be resolved from a request header, a URL path segment, or a query parameter — all controlled in one place.

Related Documentation
Spring Framework 7.0 — API Versioning ↗ Spring Framework 7.0 Migration Guide ↗
Proof
View proof source ↗
Related patterns
Enterprise Intermediate

SOAP Web Services vs Jakarta REST

Java EE
@WebService
public class UserWebService {
    @WebMethod
    public UserResponse getUser(
            @WebParam(name = "id") String id) {
        User user = findUser(id);
        UserResponse res = new UserResponse();
        res.setId(user.getId());
        res.setName(user.getName());
        return res;
    }
}
Jakarta EE 8+
@Path("/users")
@Produces(MediaType.APPLICATION_JSON)
public class UserResource {
    @Inject
    UserService userService;

    @GET
    @Path("/{id}")
    public User getUser(@PathParam("id") String id) {
        return userService.findById(id);
    }
}
Hover to see modern ➜
JDK 11+ →
Enterprise Intermediate

Servlet versus JAX-RS

Java EE
@WebServlet("/users")
public class UserServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req,
                         HttpServletResponse res)
            throws ServletException, IOException {
        String id = req.getParameter("id");
        res.setContentType("application/json");
        res.getWriter().write("{\"id\":\"" + id + "\"}");
    }
}
Jakarta EE 8+
@Path("/users")
public class UserResource {
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response getUser(
            @QueryParam("id") String id) {
        return Response.ok(new User(id)).build();
    }
}
Hover to see modern ➜
JDK 11+ →
Enterprise Intermediate

EJB versus CDI

Java EE
@Stateless
public class OrderEJB {
    @EJB
    private InventoryEJB inventory;

    public void placeOrder(Order order) {
        // container-managed transaction
        inventory.reserve(order.getItem());
    }
}
Jakarta EE 8+
@ApplicationScoped
public class OrderService {
    @Inject
    private InventoryService inventory;

    @Transactional
    public void placeOrder(Order order) {
        inventory.reserve(order.getItem());
    }
}
Hover to see modern ➜
JDK 11+ →
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.