sync/blocking 하는 restTemplate deprecated 하고 async/non-blocking 하는 webClient 로 완벽이전 하려고 했으나 중간다리가 필요함
아래의 discussion 에서 restClient 가 왜 나왔는지 discussion 이 있어서 정리해봄.
Reference: https://github.com/spring-projects/spring-framework/issues/29552
- Spring 5.0 나오면서 async/non-blocking 풀 장착한 NIO 리엑터 기반의 WebClient 가 나옴. 그리고 Spring 6.1 에 restTemplate 에서 webClient 로 완벽이전 할려고함.
- 이것의 전제가 Java Project Loom 가상 스레드 기반의 async/non-blocking 풀 지원임. 그런데 이게 어떤 구조로 병렬성을 지원할 지 모름. Spring 팀에서 보기에 Project Loom 이 정착할려면 꽤 많은 시간이 소모될 것으로 보인다고 판단. DB async 도 지원해야되고 따라서 restTemplate 를 deprecated 시키기엔 아직 이르다고 판단함. + async/non-blocking 을 사용할 필요가 없는 경우가 있는데 webClient 는 이를 강요하는 듯한 인터페이스임.
- 그래서 restTemplate 의 쉬운 blocking 인터페이스를 default 로 계속 사용하되, WebClient 를 간접 사용할 수 있게끔 하려고 함. 이것이 restClient 임. fully async/non-blocking 과 sync/blocking 의 중간 연결다리 느낌.
RestClient 의 Rest prefix 는 RESTful API 를 지칭하는 것이 아니라, restTemplate 의 계보를 잇는다는 느낌으로 rest prefix 를 붙였다고 함.
사실 restTemplate 또한 restful API 를 지칭하는게 아니긴함. 그냥 이름이 그렇게 지어진 것일 뿐. poutsma 뿐만 아니라 다른 사람들도 이 잘못 지어진 이름에 신경이 쓰인다고 함. 아마 되돌아가면 rest 라는 prefix 를 없애버리지 않을까 생각함.
interceptor, exception, error handler, retry, … 등등 restTemplate 에서 사용하던 기능들을 restClient 에서도 사용할 수 있음. 그리고 되게 쉽게 restTemplate 에서 restClient 로 마이그레이션 할 수 있음.
@Bean(name = ["paymentRestTemplate"])
fun paymentRestTemplate(): RestTemplate {
val requestFactory = SimpleClientHttpRequestFactory()
requestFactory.setConnectTimeout(3 * 1000)
requestFactory.setReadTimeout(30 * 1000)
return RestTemplateBuilder()
.setConnectTimeout(Duration.ofMillis(3 * 1000))
.setReadTimeout(Duration.ofMillis(30 * 1000))
.requestFactory { _ ->
BufferingClientHttpRequestFactory(requestFactory)
}
.interceptors(consoleLogApiCallInterceptor)
.build()
}
@Bean(name = ["paymentRestClient"])
fun paymentRestClient(): RestClient {
val requestFactory = SimpleClientHttpRequestFactory()
requestFactory.setConnectTimeout(3 * 1000)
requestFactory.setReadTimeout(30 * 1000)
return RestClient.builder()
.requestFactory(requestFactory)
.requestInterceptor(restClientConsoleLogApiCallInterceptor)
.messageConverters { converters ->
converters.removeIf { it is MappingJackson2HttpMessageConverter }
converters.add(customJacksonMessageConverter)
}
.build()
}
보면 converter, interceptor, requestFactory 등등 restTemplate 에서 사용하던 것들을 restClient 에서도 사용할 수 있음. 그리고 restClient 는 restTemplate 를 상속받아서 만들어졌기 때문에 restTemplate 에서 사용하던 것들을 그대로 사용할 수 있음.
With RestClient we are introducing a HTTP client that offers an API similar to WebClient, and that uses the message converters, request factories, interceptors, and other underlying components of RestTemplate.
reference : https://spring.io/blog/2023/07/13/new-in-spring-6-1-restclient
진짜 위의 래퍼에 얘네가 말한대로 restTemplate 의 request factories, interceptor, etc. 그대로 쓸 수 있으면서도 webClient 와 비슷한 async/non-blocking 간접체험 가능. 거기에 spring-boot-starter-webflux 는 필요없도록 구성했다고 함.
Spring wanted to deprecate the sync/blocking RestTemplate and fully move to the async/non-blocking WebClient, but needed a middle bridge
I found a discussion about why RestClient appeared, so I summarized it.
Reference: https://github.com/spring-projects/spring-framework/issues/29552
- When Spring 5.0 was released, WebClient appeared with a full async/non-blocking NIO reactor-based stack. Then in Spring 6.1, the team tried to fully move from RestTemplate to WebClient.
- The premise behind this was full async/non-blocking support based on Java Project Loom virtual threads. But it was unclear how this would support parallelism structurally. The Spring team judged that Project Loom would take quite a lot of time to settle. DB async also needed support, so they decided it was still too early to deprecate RestTemplate. Also, there are cases where async/non-blocking is not needed, but WebClient feels like an interface that forces it.
- So they wanted to keep using RestTemplate’s simple blocking interface as the default, while making it possible to use WebClient indirectly. This is RestClient. It feels like a bridge between fully async/non-blocking and sync/blocking.
The Rest prefix in RestClient does not mean RESTful API. It was added to inherit the lineage of RestTemplate.
In fact, RestTemplate also does not really refer to RESTful API. It is just named that way. Not only poutsma, but other people also seem bothered by this poorly chosen name. If they could go back, I think they would probably remove the rest prefix.
Features used in RestTemplate such as interceptors, exceptions, error handlers, retries, and so on can also be used in RestClient. It is also very easy to migrate from RestTemplate to RestClient.
@Bean(name = ["paymentRestTemplate"])
fun paymentRestTemplate(): RestTemplate {
val requestFactory = SimpleClientHttpRequestFactory()
requestFactory.setConnectTimeout(3 * 1000)
requestFactory.setReadTimeout(30 * 1000)
return RestTemplateBuilder()
.setConnectTimeout(Duration.ofMillis(3 * 1000))
.setReadTimeout(Duration.ofMillis(30 * 1000))
.requestFactory { _ ->
BufferingClientHttpRequestFactory(requestFactory)
}
.interceptors(consoleLogApiCallInterceptor)
.build()
}
@Bean(name = ["paymentRestClient"])
fun paymentRestClient(): RestClient {
val requestFactory = SimpleClientHttpRequestFactory()
requestFactory.setConnectTimeout(3 * 1000)
requestFactory.setReadTimeout(30 * 1000)
return RestClient.builder()
.requestFactory(requestFactory)
.requestInterceptor(restClientConsoleLogApiCallInterceptor)
.messageConverters { converters ->
converters.removeIf { it is MappingJackson2HttpMessageConverter }
converters.add(customJacksonMessageConverter)
}
.build()
}
As you can see, converters, interceptors, requestFactory, and other things used in RestTemplate can also be used in RestClient. Since RestClient was made by inheriting from the RestTemplate lineage, the things used in RestTemplate can be used almost as-is.
With RestClient we are introducing a HTTP client that offers an API similar to WebClient, and that uses the message converters, request factories, interceptors, and other underlying components of RestTemplate.
reference: https://spring.io/blog/2023/07/13/new-in-spring-6-1-restclient
In the wrapper above, as they said, request factories, interceptors, and so on from RestTemplate can be used as-is, while also indirectly experiencing something similar to WebClient’s async/non-blocking API. They also configured it so
spring-boot-starter-webfluxis not required.