Deprecate RestTemplate and related types

As announced in "the state of HTTP clients in Spring" blog post
(https://spring.io/blog/2025/09/30/the-state-of-http-clients-in-spring),
the deprecation timeline for `RestTemplate` was announced last November
and docs were updated accordingly.

This commit `@Deprecate` `RestTemplate` and related types for removal to
send a stronger signal to our community.
The actual removal is scheduled for Spring Framework 8.0 (not yet
scheduled).

Closes gh-36574
This commit is contained in:
Brian Clozel
2026-03-31 12:26:11 +02:00
parent 7473cd5fbc
commit 83c2afb643
35 changed files with 91 additions and 10 deletions
@@ -152,7 +152,10 @@ public final class MockRestServiceServer {
* Return a builder for a {@code MockRestServiceServer} that should be used
* to reply to the given {@code RestTemplate}.
* @since 4.3
* @deprecated as of 7.1 in favor of {@link #bindTo(RestClient.Builder)}.
*/
@Deprecated(since = "7.1", forRemoval = true)
@SuppressWarnings("removal")
public static MockRestServiceServerBuilder bindTo(RestTemplate restTemplate) {
return new RestTemplateMockRestServiceServerBuilder(restTemplate);
}
@@ -161,7 +164,10 @@ public final class MockRestServiceServer {
* Return a builder for a {@code MockRestServiceServer} that should be used
* to reply to the {@code RestTemplate} for the given {@code RestGatewaySupport}.
* @since 4.3
* @deprecated as of 7.1 in favor of {@link #bindTo(RestClient.Builder)}.
*/
@Deprecated(since = "7.1", forRemoval = true)
@SuppressWarnings("removal")
public static MockRestServiceServerBuilder bindTo(RestGatewaySupport restGatewaySupport) {
Assert.notNull(restGatewaySupport, "'restGatewaySupport' must not be null");
return new RestTemplateMockRestServiceServerBuilder(restGatewaySupport.getRestTemplate());
@@ -182,7 +188,10 @@ public final class MockRestServiceServer {
* A shortcut for {@code bindTo(restTemplate).build()}.
* @param restTemplate the RestTemplate to set up for mock testing
* @return the mock server
* @deprecated as of 7.1 in favor of {@link #bindTo(RestClient.Builder)}.
*/
@Deprecated(since = "7.1", forRemoval = true)
@SuppressWarnings("removal")
public static MockRestServiceServer createServer(RestTemplate restTemplate) {
return bindTo(restTemplate).build();
}
@@ -191,7 +200,10 @@ public final class MockRestServiceServer {
* A shortcut for {@code bindTo(restGateway).build()}.
* @param restGateway the REST gateway to set up for mock testing
* @return the mock server
* @deprecated as of 7.1 in favor of {@link #bindTo(RestClient.Builder)}.
*/
@Deprecated(since = "7.1", forRemoval = true)
@SuppressWarnings("removal")
public static MockRestServiceServer createServer(RestGatewaySupport restGateway) {
return bindTo(restGateway).build();
}
@@ -300,7 +312,7 @@ public final class MockRestServiceServer {
}
}
@SuppressWarnings("removal")
private static class RestTemplateMockRestServiceServerBuilder extends AbstractMockRestServiceServerBuilder {
private final RestTemplate restTemplate;
@@ -47,7 +47,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
* @author Juergen Hoeller
*/
@SpringJUnitWebConfig
@SuppressWarnings("deprecation")
@SuppressWarnings({"deprecation", "removal"})
class MockMvcClientHttpRequestFactoryTests {
private final RestTemplate template;
@@ -50,7 +50,10 @@ import org.springframework.util.Assert;
* @since 3.0
* @see ClientHttpRequestFactory
* @see org.springframework.web.client.RestTemplate
* @deprecated since 7.1 with no replacement.
*/
@Deprecated(since = "7.1", forRemoval = true)
@SuppressWarnings("removal")
public abstract class HttpAccessor {
/** Logger available to subclasses. */
@@ -42,7 +42,10 @@ import org.springframework.util.CollectionUtils;
* @see ClientHttpRequestInterceptor
* @see InterceptingClientHttpRequestFactory
* @see org.springframework.web.client.RestTemplate
* @deprecated since 7.1 with no replacement.
*/
@Deprecated(since = "7.1", forRemoval = true)
@SuppressWarnings("removal")
public abstract class InterceptingHttpAccessor extends HttpAccessor {
private final List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
@@ -58,7 +58,10 @@ import org.springframework.util.ObjectUtils;
* @author Juergen Hoeller
* @since 3.0
* @see RestTemplate#setErrorHandler
* @deprecated as of 7.1 in favor of {@link RestClient.ResponseSpec.ErrorHandler}
*/
@Deprecated(since = "7.1", forRemoval = true)
@SuppressWarnings("removal")
public class DefaultResponseErrorHandler implements ResponseErrorHandler {
private @Nullable List<HttpMessageConverter<?>> messageConverters;
@@ -814,6 +814,7 @@ final class DefaultRestClient implements RestClient {
}
@Override
@SuppressWarnings("removal")
public ResponseSpec onStatus(ResponseErrorHandler errorHandler) {
Assert.notNull(errorHandler, "ResponseErrorHandler must not be null");
@@ -148,6 +148,7 @@ final class DefaultRestClientBuilder implements RestClient.Builder {
this.observationConvention = other.observationConvention;
}
@SuppressWarnings("removal")
public DefaultRestClientBuilder(RestTemplate restTemplate) {
Assert.notNull(restTemplate, "RestTemplate must not be null");
@@ -167,6 +168,7 @@ final class DefaultRestClientBuilder implements RestClient.Builder {
this.observationConvention = restTemplate.getObservationConvention();
}
@SuppressWarnings("removal")
private static @Nullable UriBuilderFactory getUriBuilderFactory(RestTemplate restTemplate) {
UriTemplateHandler uriTemplateHandler = restTemplate.getUriTemplateHandler();
if (uriTemplateHandler instanceof DefaultUriBuilderFactory builderFactory) {
@@ -199,6 +201,7 @@ final class DefaultRestClientBuilder implements RestClient.Builder {
factory.shouldParsePath());
}
@SuppressWarnings("removal")
private static ClientHttpRequestFactory getRequestFactory(RestTemplate restTemplate) {
ClientHttpRequestFactory requestFactory = restTemplate.getRequestFactory();
if (requestFactory instanceof InterceptingClientHttpRequestFactory interceptingClientHttpRequestFactory) {
@@ -297,6 +300,7 @@ final class DefaultRestClientBuilder implements RestClient.Builder {
}
@Override
@SuppressWarnings("removal")
public RestClient.Builder defaultStatusHandler(ResponseErrorHandler errorHandler) {
return defaultStatusHandlerInternal(StatusHandler.fromErrorHandler(errorHandler));
}
@@ -60,7 +60,10 @@ import org.springframework.util.CollectionUtils;
* @author Arjen Poutsma
* @since 5.0
* @see RestTemplate#setErrorHandler(ResponseErrorHandler)
* @deprecated as of 7.1 in favor of {@link RestClient.ResponseSpec.ErrorHandler}
*/
@Deprecated(since = "7.1", forRemoval = true)
@SuppressWarnings("removal")
public class ExtractingResponseErrorHandler extends DefaultResponseErrorHandler {
private List<HttpMessageConverter<?>> messageConverters = Collections.emptyList();
@@ -43,7 +43,10 @@ import org.springframework.util.FileCopyUtils;
* @since 3.0
* @param <T> the data type
* @see RestTemplate
* @deprecated as of 7.1 with no replacement.
*/
@Deprecated(since = "7.1", forRemoval = true)
@SuppressWarnings("removal")
public class HttpMessageConverterExtractor<T> implements ResponseExtractor<T> {
private final Type responseType;
@@ -35,7 +35,10 @@ import org.springframework.web.client.RestClient.ResponseSpec.ErrorHandler;
*
* @author Stephane Nicoll
* @since 6.1.7
* @deprecated as of 7.1 in favor of {@link RestClient.ResponseSpec.ErrorHandler}
*/
@Deprecated(since = "7.1", forRemoval = true)
@SuppressWarnings("removal")
public final class NoOpResponseErrorHandler implements ResponseErrorHandler {
@Override
@@ -18,6 +18,7 @@ package org.springframework.web.client;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.function.Consumer;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.client.ClientHttpRequest;
@@ -37,8 +38,11 @@ import org.springframework.http.client.ClientHttpRequest;
* @author Arjen Poutsma
* @since 3.0
* @see RestTemplate#execute
* @deprecated as of 7.1 in favor of {@link RestClient.RequestBodySpec#httpRequest(Consumer)}.
*/
@FunctionalInterface
@Deprecated(since = "7.1", forRemoval = true)
@SuppressWarnings("removal")
public interface RequestCallback {
/**
@@ -31,7 +31,9 @@ import org.springframework.http.client.ClientHttpResponse;
*
* @author Arjen Poutsma
* @since 3.0
* @deprecated as of 7.1 in favor of {@link RestClient.ResponseSpec.ErrorHandler}
*/
@Deprecated(since = "7.1", forRemoval = true)
public interface ResponseErrorHandler {
/**
@@ -37,8 +37,11 @@ import org.springframework.http.client.ClientHttpResponse;
* @since 3.0
* @param <T> the data type
* @see RestTemplate#execute
* @deprecated as of 7.1 in favor of {@link RestClient.RequestBodySpec#exchange(RestClient.RequestHeadersSpec.ExchangeFunction)}.
*/
@FunctionalInterface
@Deprecated(since = "7.1", forRemoval = true)
@SuppressWarnings("removal")
public interface ResponseExtractor<T> {
/**
@@ -189,6 +189,7 @@ public interface RestClient {
* @return a {@code RestClient} initialized with the {@code restTemplate}'s
* configuration
*/
@SuppressWarnings("removal")
static RestClient create(RestTemplate restTemplate) {
return new DefaultRestClientBuilder(restTemplate).build();
}
@@ -218,6 +219,7 @@ public interface RestClient {
* @return a {@code RestClient} builder initialized with {@code restTemplate}'s
* configuration
*/
@SuppressWarnings("removal")
static RestClient.Builder builder(RestTemplate restTemplate) {
return new DefaultRestClientBuilder(restTemplate);
}
@@ -386,7 +388,10 @@ public interface RestClient {
* @param errorHandler the error handler to configure, internally adapted
* and integrated into the {@link ResponseSpec.ErrorHandler} chain.
* @return this builder
* @deprecated as of 7.1 in favor of {@link #defaultStatusHandler(Predicate, ResponseSpec.ErrorHandler)}
*/
@Deprecated(since = "7.1", forRemoval = true)
@SuppressWarnings("removal")
Builder defaultStatusHandler(ResponseErrorHandler errorHandler);
/**
@@ -1012,7 +1017,10 @@ public interface RestClient {
* {@link RestClientException}.
* @param errorHandler the error handler
* @return this builder
* @deprecated as of 7.1 in favor of {@link #onStatus(Predicate, ErrorHandler)}
*/
@Deprecated(since = "7.1", forRemoval = true)
@SuppressWarnings("removal")
ResponseSpec onStatus(ResponseErrorHandler errorHandler);
/**
@@ -39,7 +39,10 @@ import org.springframework.http.ResponseEntity;
* @author Juergen Hoeller
* @since 3.0
* @see RestTemplate
* @deprecated as of 7.1, in favor of {@link RestClient}.
*/
@Deprecated(since = "7.1", forRemoval = true)
@SuppressWarnings("removal")
public interface RestOperations {
// GET
@@ -100,7 +100,10 @@ import org.springframework.web.util.UriTemplateHandler;
* @see RequestCallback
* @see ResponseExtractor
* @see ResponseErrorHandler
* @deprecated as of 7.1, in favor of {@link RestClient}.
*/
@Deprecated(since = "7.1", forRemoval = true)
@SuppressWarnings("removal")
public class RestTemplate extends InterceptingHttpAccessor implements RestOperations {
private static final ClientRequestObservationConvention DEFAULT_OBSERVATION_CONVENTION = new DefaultClientRequestObservationConvention();
@@ -90,7 +90,10 @@ final class StatusHandler {
/**
* Create a StatusHandler from a {@link ResponseErrorHandler}.
* @deprecated as of 7.1 in favor of {@link #of(Predicate, RestClient.ResponseSpec.ErrorHandler)}
*/
@Deprecated(since = "7.1", forRemoval = true)
@SuppressWarnings("removal")
public static StatusHandler fromErrorHandler(ResponseErrorHandler errorHandler) {
Assert.notNull(errorHandler, "ResponseErrorHandler must not be null");
@@ -160,7 +163,7 @@ final class StatusHandler {
return preface + bodyText;
}
@SuppressWarnings("NullAway")
@SuppressWarnings({"NullAway", "removal"})
private static Function<ResolvableType, ? extends @Nullable Object> initBodyConvertFunction(
ClientHttpResponse response, byte[] body, List<HttpMessageConverter<?>> messageConverters) {
@@ -32,7 +32,10 @@ import org.springframework.web.client.RestTemplate;
* @since 3.0
* @see #setRestTemplate
* @see org.springframework.web.client.RestTemplate
* @deprecated as of 7.1, in favor of {@link org.springframework.web.client.RestClient}.
*/
@Deprecated(since = "7.1", forRemoval = true)
@SuppressWarnings("removal")
public class RestGatewaySupport {
/** Logger available to subclasses. */
@@ -45,7 +45,10 @@ import org.springframework.web.util.UriBuilderFactory;
* @author Olga Maciaszek-Sharma
* @author Brian Clozel
* @since 6.1
* @deprecated as of 7.1 in favor of {@link RestClientAdapter}.
*/
@Deprecated(since = "7.1", forRemoval = true)
@SuppressWarnings("removal")
public final class RestTemplateAdapter implements HttpExchangeAdapter {
private final RestTemplate restTemplate;
@@ -26,7 +26,6 @@ import org.jspecify.annotations.Nullable;
*
* @author Rossen Stoyanchev
* @since 4.2
* @see org.springframework.web.client.RestTemplate#setUriTemplateHandler(UriTemplateHandler)
*/
public interface UriTemplateHandler {
@@ -14,6 +14,8 @@
* limitations under the License.
*/
@file:Suppress("DEPRECATION", "REMOVAL")
package org.springframework.web.client
import org.springframework.core.ParameterizedTypeReference
@@ -35,6 +35,7 @@ import static org.assertj.core.api.Assertions.assertThat;
*
* @author Brian Clozel
*/
@SuppressWarnings("removal")
class InterceptingHttpAccessorTests {
@Test
@@ -57,6 +57,7 @@ import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY;
* Tests for {@link DefaultResponseErrorHandler} handling of specific
* HTTP status codes.
*/
@SuppressWarnings("removal")
class DefaultResponseErrorHandlerHttpStatusTests {
private final DefaultResponseErrorHandler handler = new DefaultResponseErrorHandler();
@@ -44,6 +44,7 @@ import static org.mockito.Mockito.mock;
* @author Juergen Hoeller
* @author Denys Ivano
*/
@SuppressWarnings("removal")
class DefaultResponseErrorHandlerTests {
private final DefaultResponseErrorHandler handler = new DefaultResponseErrorHandler();
@@ -44,7 +44,7 @@ import static org.mockito.Mockito.mock;
*
* @author Arjen Poutsma
*/
@SuppressWarnings("ALL")
@SuppressWarnings("removal")
class ExtractingResponseErrorHandlerTests {
private ExtractingResponseErrorHandler errorHandler;
@@ -52,6 +52,7 @@ import static org.mockito.Mockito.mock;
* @author Brian Clozel
* @author Sam Brannen
*/
@SuppressWarnings("removal")
class HttpMessageConverterExtractorTests {
private final HttpMessageConverter<String> converter = mock();
@@ -49,7 +49,7 @@ import static org.assertj.core.api.Assertions.fail;
*/
class RestClientBuilderTests {
@SuppressWarnings("unchecked")
@SuppressWarnings({"unchecked", "removal"})
@Test
void createFromRestTemplate() {
JettyClientHttpRequestFactory requestFactory = new JettyClientHttpRequestFactory();
@@ -90,6 +90,7 @@ class RestClientBuilderTests {
}
@Test
@SuppressWarnings("removal")
void defaultUriBuilderFactory() {
RestTemplate restTemplate = new RestTemplate();
@@ -82,6 +82,7 @@ import static org.springframework.http.MediaType.MULTIPART_MIXED;
* @author Brian Clozel
* @author Sam Brannen
*/
@SuppressWarnings("removal")
class RestTemplateIntegrationTests extends AbstractMockWebServerTests {
@Retention(RetentionPolicy.RUNTIME)
@@ -55,6 +55,7 @@ import static org.springframework.http.HttpMethod.GET;
* Tests for the client HTTP observations with {@link RestTemplate}.
* @author Brian Clozel
*/
@SuppressWarnings("removal")
class RestTemplateObservationTests {
@@ -83,7 +83,7 @@ import static org.springframework.http.MediaType.parseMediaType;
* @author Brian Clozel
* @author Sam Brannen
*/
@SuppressWarnings("unchecked")
@SuppressWarnings({"unchecked", "removal"})
class RestTemplateTests {
private final ClientHttpRequestFactory requestFactory = mock();
@@ -80,7 +80,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
* @author Rossen Stoyanchev
* @author Brian Clozel
*/
@SuppressWarnings("JUnitMalformedDeclaration")
@SuppressWarnings({"JUnitMalformedDeclaration", "removal"})
class RestClientAdapterTests {
private final MockWebServer anotherServer = new MockWebServer();
@@ -34,6 +34,7 @@ import kotlin.reflect.jvm.kotlinFunction
*
* @author Sebastien Deleuze
*/
@Suppress("REMOVAL", "DEPRECATION")
class RestOperationsExtensionsTests {
val template = mockk<RestOperations>()
@@ -28,7 +28,6 @@ import org.springframework.http.ResponseEntity
import org.springframework.util.LinkedMultiValueMap
import org.springframework.util.MultiValueMap
import org.springframework.web.bind.annotation.*
import org.springframework.web.client.RestTemplate
import org.springframework.web.multipart.MultipartFile
import org.springframework.web.service.annotation.GetExchange
import org.springframework.web.service.annotation.PostExchange
@@ -46,6 +45,7 @@ import java.util.*
*
* @author Olga Maciaszek-Sharma
*/
@Suppress("REMOVAL", "DEPRECATION")
class KotlinRestTemplateHttpServiceProxyTests {
private lateinit var server: MockWebServer
@@ -65,7 +65,7 @@ class KotlinRestTemplateHttpServiceProxyTests {
}
private fun initTestService(): TestService {
val restTemplate = RestTemplate()
val restTemplate = org.springframework.web.client.RestTemplate()
restTemplate.uriTemplateHandler = DefaultUriBuilderFactory(server.url("/").toString())
return HttpServiceProxyFactory.builder()
.exchangeAdapter(RestTemplateAdapter.create(restTemplate))
@@ -53,7 +53,10 @@ import org.springframework.web.socket.sockjs.frame.SockJsFrame;
*
* @author Rossen Stoyanchev
* @since 4.1
* @deprecated as of 7.1 in favor of {@link RestClientXhrTransport}.
*/
@Deprecated(since = "7.1", forRemoval = true)
@SuppressWarnings("removal")
public class RestTemplateXhrTransport extends AbstractXhrTransport {
private final RestOperations restTemplate;
@@ -67,6 +67,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
*
* @author Rossen Stoyanchev
*/
@SuppressWarnings("removal")
class RestTemplateXhrTransportTests {
private static final JacksonJsonSockJsMessageCodec CODEC = new JacksonJsonSockJsMessageCodec();
@@ -201,6 +202,7 @@ class RestTemplateXhrTransportTests {
}
@SuppressWarnings("removal")
private static class TestRestTemplate extends RestTemplate {
private Queue<ClientHttpResponse> responses = new LinkedBlockingDeque<>();