2.7 KiB
Migrating from RestTemplate
RestTemplate is permitted only inside …httpclient.migration; RestTemplateBoundaryTest enforces
that. New retry, Dynamic Target, and HTTP/3 capabilities are deliberately unreachable from the
migration path — a caller that wants them moves to a Named Client Profile.
1. Audit before changing anything
RestTemplateInventory inventory = new RestTemplateInventoryScanner().scan(existingTemplate);
The inventory reports the request factory, message converters, interceptors, error handler, and URI template handler, plus findings:
| Code | Severity | Meaning |
|---|---|---|
SIMPLE_REQUEST_FACTORY |
blocking | no connection pool; unsupported in production |
NO_MESSAGE_CONVERTERS |
blocking | the template cannot encode or decode a body |
NO_INTERCEPTORS |
warning | confirm where correlation and timeouts are applied |
TIMEOUTS_NOT_INTROSPECTABLE |
informational | declare timeouts explicitly on the target profile |
2. Bridge without changing behaviour
RestClient client = new RestTemplateToRestClientAdapter().adaptChecked(existingTemplate);
adaptChecked refuses to migrate a template with a blocking finding. The bridge carries the
existing converters, interceptors, error handler, and URI handler across, so this step changes the
API and nothing else.
3. Move to a Named Client Profile
Turn the platform on with APP_HTTPCLIENT_ENABLED=true — it ships off, and while it is off none of
the settings below are bound — then declare the upstream as app.httpclient.clients[N] with its
name and an explicit base URL, transport, timeouts, pool, body limits, authentication, retry
policy, redirect policy, and TLS profile. Startup validation will tell you exactly which of those is
missing. See docs/httpclient/configuration-reference.md for the environment form.
4. Move to a typed client
@HttpClientProfile("payment")
@HttpExchange("/payments")
public interface PaymentClient {
@PostExchange
@HttpOperationPolicy(
name = "create-payment",
idempotency = OperationIdempotency.IDEMPOTENCY_KEY_REQUIRED,
retryPolicy = "payment-write")
PaymentResponse create(
@RequestHeader("Idempotency-Key") String idempotencyKey, @RequestBody PaymentRequest request);
}
The interface fails startup validation unless it declares a profile, gives every method a stable operation name and an explicit idempotency, supplies a key parameter when the operation requires one, keeps a single execution model, and does not enable retry on a non-idempotent write.
5. Retire the template
Once no production package references RestTemplate, RestTemplateBoundaryTest keeps it that way.