Spring Cloud Gateway 2.x vs 4.x vs WebFlux — Complete Comparison
A full side-by-side comparison of Spring Cloud Gateway 2.x vs 4.x vs Spring WebFlux Gateway. Covers YAML config, filter implementation, performance, and selection criteria with production code.
Understanding the Version System
Spring Cloud Gateway versions are tied to Spring Cloud release trains.
| Spring Boot | Spring Cloud | Gateway Version | Java |
|---|---|---|---|
| 2.6.x | 2021.0.x (Jubilee) | 3.1.x | 8+ |
| 2.7.x | 2021.0.x (Jubilee) | 3.1.x | 8+ |
| 3.0.x | 2022.0.x (Kilburn) | 4.0.x | 17+ |
| 3.1.x | 2022.0.x (Kilburn) | 4.0.x | 17+ |
| 3.2.x | 2023.0.x (Leyton) | 4.1.x | 21+ |
| 3.3.x | 2023.0.x (Leyton) | 4.1.x | 21+ |
Convention: “2.x” = Boot 2.x + Gateway 3.x; “4.x” = Boot 3.x + Gateway 4.x.
This post uses 2.x = Gateway 3.1 (Boot 2.7) and 4.x = Gateway 4.1 (Boot 3.3).
1. Dependencies and BOM Changes
2.x (Boot 2.7 + Gateway 3.1)
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.18</version>
</parent>
<properties>
<spring-cloud.version>2021.0.9</spring-cloud.version>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<!-- Selects 3.1.x automatically -->
</dependency>
<!-- Hystrix circuit breaker (deprecated) -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
</dependencies>
4.x (Boot 3.3 + Gateway 4.1)
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.5</version>
</parent>
<properties>
<spring-cloud.version>2023.0.3</spring-cloud.version>
<java.version>21</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<!-- Selects 4.1.x automatically -->
</dependency>
<!-- Hystrix removed entirely → use Resilience4j -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
</dependency>
</dependencies>
Key breaking changes:
- Minimum Java: 11 → 17 (21 recommended)
- Hystrix → Resilience4j (Hystrix completely removed)
javax.*→jakarta.*package rename
2. YAML Configuration Comparison
Basic Routing (Unchanged)
# ─── Both 2.x and 4.x ───
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://USER-SERVICE
predicates:
- Path=/api/users/**
filters:
- StripPrefix=1
# Basic routing syntax is identical — backwards compatible
Circuit Breaker Filter
# ─────────── 2.x — Hystrix ───────────
filters:
- name: Hystrix
args:
name: userServiceFallback
fallbackUri: forward:/fallback/user
hystrix:
command:
userServiceFallback:
execution:
isolation:
thread:
timeoutInMilliseconds: 3000
# ─────────── 4.x — Resilience4j ───────────
filters:
- name: CircuitBreaker
args:
name: userServiceCB
fallbackUri: forward:/fallback/user
statusCodes: # 4.x new: specific status codes treated as failures
- 500
- 503
resilience4j:
circuitbreaker:
instances:
userServiceCB:
slidingWindowSize: 10
failureRateThreshold: 50
waitDurationInOpenState: 10s
permittedNumberOfCallsInHalfOpenState: 3
timelimiter:
instances:
userServiceCB:
timeoutDuration: 3s
Rate Limiting
# ─────────── 2.x ───────────
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
# requestedTokens not available in 2.x
key-resolver: "#{@ipKeyResolver}"
# ─────────── 4.x ───────────
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
redis-rate-limiter.requestedTokens: 1 # 4.x new: tokens per request
key-resolver: "#{@userKeyResolver}"
CORS Configuration
# ─────────── 2.x ───────────
spring:
cloud:
gateway:
globalcors:
corsConfigurations: # camelCase key
'[/**]':
allowedOrigins: ["https://testforge.kr"]
allowedMethods: ["GET", "POST"]
# ─────────── 4.x ───────────
spring:
cloud:
gateway:
globalcors:
cors-configurations: # kebab-case key (changed)
'[/**]':
allowedOriginPatterns: # Replaces allowedOrigins (supports wildcards)
- "https://testforge.kr"
- "https://*.testforge.kr"
allowedMethods: ["GET", "POST", "PUT", "DELETE"]
3. Java Code Changes
GlobalFilter — javax → jakarta
// ─── 2.x ───
import javax.annotation.PostConstruct;
import org.springframework.cloud.gateway.filter.GlobalFilter;
// ─── 4.x ───
import jakarta.annotation.PostConstruct; // Package renamed
import org.springframework.cloud.gateway.filter.GlobalFilter;
Circuit Breaker Implementation
// ─── 2.x — Hystrix ───
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
@HystrixCommand(fallbackMethod = "fallback")
public String callService() {
return restTemplate.getForObject("http://service/api", String.class);
}
// ─── 4.x — Resilience4j ───
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
@CircuitBreaker(name = "serviceCircuitBreaker", fallbackMethod = "fallback")
public Mono<String> callService() {
return webClient.get().uri("/api").retrieve().bodyToMono(String.class);
}
public Mono<String> fallback(Exception e) {
return Mono.just("Service temporarily unavailable");
}
4. Performance Differences
| Aspect | 2.x (Gateway 3.1) | 4.x (Gateway 4.1) |
|---|---|---|
| Java version | 8/11 | 17/21 |
| Virtual Threads | Not supported | Supported (Java 21) |
| GC | G1GC default | ZGC / G1GC |
| Memory footprint | Standard | Reduced (GraalVM native image support) |
| HTTP/2 upstream | Supported | Enhanced |
| Observability | Micrometer | Micrometer + OTLP |
5. Migration from 2.x to 4.x — Checklist
Dependencies
-
spring-boot-starter-parent:2.7.x→3.3.x -
spring-cloud.version:2021.0.x→2023.0.x - Remove
spring-cloud-starter-netflix-hystrix - Add
spring-cloud-starter-circuitbreaker-reactor-resilience4j
Code
- Replace all
javax.*imports withjakarta.* - Replace
@HystrixCommandwith@CircuitBreaker(Resilience4j) - Update
HystrixCommandconfig →resilience4jYAML - Update Hystrix dashboard dependencies if used
YAML
-
corsConfigurations→cors-configurations -
allowedOrigins→allowedOriginPatterns(if using wildcards) -
Hystrixfilter →CircuitBreakerfilter -
hystrix.command.*→resilience4j.circuitbreaker.instances.*
6. When to Use What
| Situation | Recommendation |
|---|---|
| New project (Java 17+) | 4.x (Gateway 4.1) |
| Spring Boot 2.7 still in use | 2.x (Gateway 3.1) |
| Cloud-native, GraalVM native image | 4.x |
| Team not yet migrated to Jakarta EE | Keep 2.x, plan migration |
| Java 21 virtual threads | 4.x |
Spring Boot 2.7 reached end of support in November 2023.
If you’re still on 2.x, plan your migration to 4.x now.
Key Takeaway
The biggest breaking changes from 2.x to 4.x are:
- Hystrix → Resilience4j: Configuration structure is completely different
- javax → jakarta: Every import that used
javaxmust be updated - CORS key name:
corsConfigurations→cors-configurations
All three can be addressed systematically. Start with dependency and import changes, then migrate Hystrix configuration to Resilience4j — the logic itself is nearly identical, just expressed differently.