{{announcement.body}}
{{announcement.title}}

RestTemplate vs. WebClient

DZone 's Guide to

RestTemplate vs. WebClient

Take a look at this comparison for Spring framework's two web client implementation, RestTemplate and WebClient, based on a couple criteria.

· Java Zone ·
Free Resource

In this tutorial, we will compare two of Spring framework's provided web client implementations:

  • RestTemplate
  • WebClient, Spring 5's reactive alternative

What is RestTemplate and WebClient?

RestTemplate is the central class within the Spring framework for executing synchronous HTTP requests on the client side.

WebClient is a reactive client for performing HTTP requests with Reactive Streams back pressure.

How Do RestTemplate and WebClient Differ from Each Other?

Thread Safety

RestTemplate and WebClient both are thread safe, but each one implements it differently.

RestTemplate

RestTemplate is thread-safe once constructed. Objects of the RestTemplate class do not change any of their state information to process HTTP: the class is an instance of the Strategy design pattern. With no state information, there is no possibility of different threads corrupting or racing state information if they share a RestTemplate object. This is why it is possible for threads to share these objects. Let's see this with following example.

Java
 




xxxxxxxxxx
1
172


1
/* Main application class */
2
public class RestTemplateTest {
3
     public static void main(String[] args) {
4
5
            ThreadExecutor threadEx = new ThreadExecutor();
6
7
            for (int i = 0; i < 4; i++) {
8
                System.out.println("\n"+ threadEx.getData());
9
            }
10
        }public class ThreadExecutor {
11
     private ExecutorService executor = Executors.newFixedThreadPool(10);
12
        private RestTemplate restTemplate = new RestTemplate();
13
14
        public String getData() {
15
            System.out.println("\nRestTemplate obj details - "+restTemplate.toString() + "\n");
16
            Future<String> future = executor.submit(new Task(restTemplate));
17
            String response = null;
18
19
            try {
20
                response = future.get(100, TimeUnit.MILLISECONDS);
21
            } catch (TimeoutException e) {
22
                e.printStackTrace();
23
            } catch (InterruptedException e) {
24
                e.printStackTrace();
25
            } catch (ExecutionException e) {
26
                e.printStackTrace();
27
            }
28
29
            return response;
30
        }
31
}
32
}
33
34
/* Thread executor class */
35
public class ThreadExecutor {
36
     private ExecutorService executor = Executors.newFixedThreadPool(10);
37
        private RestTemplate restTemplate = new RestTemplate();
38
39
        public String getData() {
40
            System.out.println("\nRestTemplate obj details - "+restTemplate.toString() + "\n");
41
            Future<String> future = executor.submit(new Task(restTemplate));
42
            String response = null;
43
44
            try {
45
                response = future.get(100, TimeUnit.MILLISECONDS);
46
            } catch (TimeoutException e) {
47
                e.printStackTrace();
48
            } catch (InterruptedException e) {
49
                e.printStackTrace();
50
            } catch (ExecutionException e) {
51
                e.printStackTrace();
52
            }
53
54
            return response;
55
        }
56
}
57
58
/* Task class, calls /person api */
59
class Task implements Callable<String> {
60
61
    private RestTemplate restTemplate;
62
63
    public Task(RestTemplate restTemplate) {
64
        this.restTemplate = restTemplate;
65
    }
66
67
    public String call() throws Exception {
68
69
        String url = "http://localhost:8000/person/1";
70
        String response = restTemplate.getForObject(url, String.class);
71
72
        return response;
73
    }
74
}
75
76
/* Controller */
77
@RestController
78
class PersonRestController {
79
80
    @GetMapping("/person/{id}")
81
    public Person findPersonById(@PathVariable("id") Long id) {
82
        return new Person(1L, "Heena", "Khan");
83
    }
84
  
85
    @GetMapping("/person")
86
    public List<Person> getAllPersonList() {
87
        List<Person> personList = new ArrayList<Person>();
88
        personList.add(new Person(10L, "Heena", "Khan"));
89
        personList.add(new Person(20L, "Kiara", "Khan"));
90
        personList.add(new Person(30L, "Ayaan", "Shaikh"));
91
      
92
        return personList;
93
    }
94
}
95
96
/* Entity class */
97
class Person {
98
99
    Person(Long id, String name, String surname) {
100
        this.id = id;
101
        this.name = name;
102
        this.surname = surname;
103
    }
104
105
    private Long id;
106
107
    private String name;
108
109
    private String surname;
110
111
    public Long getId() {
112
        return id;
113
    }
114
115
    public void setId(Long id) {
116
        this.id = id;
117
    }
118
119
    public String getName() {
120
        return name;
121
    }
122
123
    public void setName(String name) {
124
        this.name = name;
125
    }
126
127
    public String getSurname() {
128
        return surname;
129
    }
130
131
    public void setSurname(String surname) {
132
        this.surname = surname;
133
    }
134
}
135
136
OUTPUT :
137
138
RestTemplate obj details - org.springframework.web.client.RestTemplate@124c278f
139
140
22:39:04.127 [pool-1-thread-1] DEBUG org.springframework.web.client.RestTemplate - HTTP GET http://localhost:8000/person/1
141
22:39:04.136 [pool-1-thread-1] DEBUG org.springframework.web.client.RestTemplate - Accept=[text/plain, application/json, application/*+json, */*]
142
22:39:04.156 [pool-1-thread-1] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK
143
22:39:04.157 [pool-1-thread-1] DEBUG org.springframework.web.client.RestTemplate - Reading to [java.lang.String] as "application/json"
144
145
{"id":1,"name":"Heena","surname":"Khan"}
146
147
RestTemplate obj details - org.springframework.web.client.RestTemplate@124c278f
148
149
22:39:04.160 [pool-1-thread-2] DEBUG org.springframework.web.client.RestTemplate - HTTP GET http://localhost:8000/person/1
150
22:39:04.161 [pool-1-thread-2] DEBUG org.springframework.web.client.RestTemplate - Accept=[text/plain, application/json, application/*+json, */*]
151
22:39:04.165 [pool-1-thread-2] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK
152
22:39:04.165 [pool-1-thread-2] DEBUG org.springframework.web.client.RestTemplate - Reading to [java.lang.String] as "application/json"
153
154
{"id":1,"name":"Heena","surname":"Khan"}
155
156
RestTemplate obj details - org.springframework.web.client.RestTemplate@124c278f
157
158
22:39:04.166 [pool-1-thread-3] DEBUG org.springframework.web.client.RestTemplate - HTTP GET http://localhost:8000/person/1
159
22:39:04.166 [pool-1-thread-3] DEBUG org.springframework.web.client.RestTemplate - Accept=[text/plain, application/json, application/*+json, */*]
160
22:39:04.167 [pool-1-thread-3] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK
161
22:39:04.167 [pool-1-thread-3] DEBUG org.springframework.web.client.RestTemplate - Reading to [java.lang.String] as "application/json"
162
163
{"id":1,"name":"Heena","surname":"Khan"}
164
165
RestTemplate obj details - org.springframework.web.client.RestTemplate@124c278f
166
167
22:39:04.168 [pool-1-thread-4] DEBUG org.springframework.web.client.RestTemplate - HTTP GET http://localhost:8000/person/1
168
22:39:04.168 [pool-1-thread-4] DEBUG org.springframework.web.client.RestTemplate - Accept=[text/plain, application/json, application/*+json, */*]
169
22:39:04.170 [pool-1-thread-4] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK
170
22:39:04.171 [pool-1-thread-4] DEBUG org.springframework.web.client.RestTemplate - Reading to [java.lang.String] as "application/json"
171
172
{"id":1,"name":"Heena","surname":"Khan"}


If we observe the above output, all  /person/{id} API call are using same RestTemplate object.

Accessing a third-party REST service inside a Spring application revolves around the use of the Spring RestTemplate class. The RestTemplate class is designed on the same principles as the many other Spring *Template classes (e.g., JdbcTemplate, JmsTemplate ).

Supported methods are:

  • getForObject(url, classType) – retrieve a representation by doing a GET on the URL. The response (if any) is unmarshalled to given class type and returned.
  • getForEntity(url, responseType) – retrieve a representation as ResponseEntity by doing a GET on the URL.
  • exchange(requestEntity, responseType) – execute the specified request and return the response as ResponseEntity.
  • execute(url, httpMethod, requestCallback, responseExtractor) – execute the httpMethod to the given URI template, preparing the request with the RequestCallback, and reading the response with a ResponseExtractor.

WebClient

WebClient is thread-safe because it is immutable. WebClient is meant to be used in a reactive environment, where nothing is tied to a particular thread. Here we see how to create a WebClient instance, and use it to build and execute an HTTP request. Spring WebClient is a non-blocking, reactive client to perform HTTP requests, a part of Spring Webflux framework.

The create(baseUrl) method is most commonly used to create WebClient instance because it is handy and we can provide the default base URL for subsequence requests later.

Java
 




xxxxxxxxxx
1


 
1
WebClient webClient = WebClient.create("http://localhost:8080");



We can use builder() if we want to set the default headers and cookies.

Java
 




xxxxxxxxxx
1


 
1
WebClient webClient = WebClient.builder()  
2
    .baseUrl("http://localhost:8080")
3
    .defaultHeader(HttpHeaders.USER_AGENT, "Hello World")
4
    .defaultCookie("cookie name", "cookie value")
5
    .build();



Build an HTTP request with WebClient. The following gives you an example on building a GET request.

Java
 




xxxxxxxxxx
1
15


1
public void buildAnHttpRequest() {  
2
    WebClient webClient = WebClient.create("http://localhost:8080");
3
 
          
4
/* Mono is a stream which returns zero items or a single item */
5
    Mono<String> result = webClient.get()
6
        .uri(uriBuilder -> uriBuilder
7
            .path("/hello")
8
            .queryParam("name", "Heena")
9
            .build())
10
        .retrieve()
11
        .bodyToMono(String.class);
12
 
          
13
    assertThat(result.block())
14
        .isEqualTo("Hello, Heena!");
15
}


Blocking vs. Non-Blocking client

RestTemplate

RestTemplate is synchronous in nature, using a Thread-per-Request method. Until each request is completed and response is sent back to user or service erred out, the thread will be blocked out for that user. Below is an example of synchronous call.

Java
 




xxxxxxxxxx
1
30


1
public class BlockingMechanism {
2
3
    public static void main(String[] args) {
4
          System.out.println("Starting BLOCKING mechanism...");
5
            final String uri = "http://localhost:8000/person";
6
7
            RestTemplate restTemplate = new RestTemplate();
8
            ResponseEntity<List<Person>> response = restTemplate.exchange(
9
              uri, HttpMethod.GET, null,
10
              new ParameterizedTypeReference<List<Person>>(){});
11
            
12
            List<Person> result = response.getBody();
13
            result.forEach(person -> System.out.println(person.toString()));
14
            System.out.println("Exiting BLOCKING mechanism...");
15
16
    }
17
18
}
19
20
OUTPUT :
21
Starting BLOCKING mechanism...
22
12:26:14.725 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP GET http://localhost:8000/person
23
12:26:14.761 [main] DEBUG org.springframework.web.client.RestTemplate - Accept=[application/json, application/json]
24
12:26:15.064 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK
25
12:26:15.077 [main] DEBUG org.springframework.web.client.RestTemplate - Reading to [java.util.List<hello.Person>]
26
Person [id=10, name=Heena, surname=Khan]
27
Person [id=20, name=Kiara, surname=Khan]
28
Person [id=30, name=Ayaan, surname=Shaikh]
29
30
Exiting BLOCKING mechanism...



WebClient

WebClient works on the concept of asynchronous and non-blocking strategy.

Java
 




xxxxxxxxxx
1
54


1
 public class NonBlockingMechanism {
2
 
          
3
    public static void main(String[] args) {
4
         System.out.println("Starting NON-BLOCKING mechanism...");
5
            Flux<Person> PersonFlux = WebClient.create()
6
              .get()
7
              .uri("http://localhost:8000/person")
8
              .retrieve()
9
              .bodyToFlux(Person.class);
10
 
          
11
            PersonFlux.subscribe(Person -> System.out.println(Person.toString()));
12
            System.out.println("Exiting NON-BLOCKING mechanism...");
13
            System.out.println(PersonFlux.blockFirst().toString());
14
    }
15
 
          
16
}
17
 
          
18
OUTPUT :
19
Starting NON-BLOCKING mechanism...
20
12:36:13.082 [main] DEBUG io.netty.util.internal.logging.InternalLoggerFactory - Using SLF4J as the default logging framework
21
12:36:13.104 [main] DEBUG io.netty.util.internal.PlatformDependent - Platform: Windows
22
12:36:13.107 [main] DEBUG io.netty.util.internal.PlatformDependent0 - -Dio.netty.noUnsafe: false
23
12:36:13.108 [main] DEBUG io.netty.util.internal.PlatformDependent0 - Java version: 13
24
12:36:13.109 [main] DEBUG io.netty.util.internal.PlatformDependent0 - sun.misc.Unsafe.theUnsafe: available
25
12:36:13.110 [main] DEBUG io.netty.util.internal.PlatformDependent0 - sun.misc.Unsafe.copyMemory: available
26
12:36:13.110 [main] DEBUG io.netty.util.internal.PlatformDependent0 - java.nio.Buffer.address: available
27
12:36:13.113 [main] DEBUG io.netty.util.internal.PlatformDependent0 - direct buffer constructor: unavailable
28
...
29
Exiting NON-BLOCKING mechanism...
30
12:36:14.621 [main] DEBUG org.springframework.web.reactive.function.client.ExchangeFunctions - [15d49048] HTTP GET http://localhost:8000/person
31
12:36:14.631 [reactor-http-nio-2] DEBUG reactor.netty.resources.PooledConnectionProvider - [id: 0x31fc4f5b] Created a new pooled channel, now 2 active connections and 0 inactive connections
32
12:36:14.631 [reactor-http-nio-1] DEBUG reactor.netty.resources.PooledConnectionProvider - [id: 0x80cf000e] Created a new pooled channel, now 2 active connections and 0 inactive connections
33
 
          
34
12:36:14.778 [reactor-http-nio-2] DEBUG org.springframework.web.reactive.function.client.ExchangeFunctions - [15d49048] Response 200 OK
35
12:36:14.876 [reactor-http-nio-1] DEBUG reactor.netty.channel.FluxReceive - [id: 0x80cf000e, L:/127.0.0.1:63237 - R:localhost/127.0.0.1:8000] Subscribing inbound receiver [pending: 0, cancelled:false, inboundDone: false]
36
12:36:14.876 [reactor-http-nio-2] DEBUG reactor.netty.channel.FluxReceive - [id: 0x31fc4f5b, L:/127.0.0.1:63238 - R:localhost/127.0.0.1:8000] Subscribing inbound receiver [pending: 0, cancelled:false, inboundDone: false]
37
12:36:14.891 [reactor-http-nio-1] DEBUG org.springframework.http.codec.json.Jackson2JsonDecoder - [15d49048] Decoded [Person [id=10, name=Heena, surname=naaz]]
38
12:36:14.891 [reactor-http-nio-2] DEBUG org.springframework.http.codec.json.Jackson2JsonDecoder - [15d49048] Decoded [Person [id=10, name=Heena, surname=naaz]]
39
Person [id=10, name=Heena, surname=naaz]
40
12:36:14.892 [reactor-http-nio-1] DEBUG org.springframework.http.codec.json.Jackson2JsonDecoder - [15d49048] Decoded [Person [id=20, name=Kiara, surname=khan]]
41
Person [id=20, name=Kiara, surname=khan]
42
12:36:14.892 [reactor-http-nio-2] DEBUG org.springframework.web.reactive.function.client.ExchangeFunctions - [15d49048] Cancel signal (to close connection)
43
12:36:14.892 [reactor-http-nio-1] DEBUG org.springframework.http.codec.json.Jackson2JsonDecoder - [15d49048] Decoded [Person [id=30, name=Ayaan, surname=Shaikh]]
44
Person [id=30, name=Ayaan, surname=Shaikh]
45
12:36:14.892 [reactor-http-nio-1] DEBUG reactor.netty.http.client.HttpClientOperations - [id: 0x80cf000e, L:/127.0.0.1:63237 - R:localhost/127.0.0.1:8000] Received last HTTP packet
46
12:36:14.893 [reactor-http-nio-1] DEBUG reactor.netty.resources.PooledConnectionProvider - [id: 0x80cf000e, L:/127.0.0.1:63237 - R:localhost/127.0.0.1:8000] onStateChange(GET{uri=/person, connection=PooledConnection{channel=[id: 0x80cf000e, L:/127.0.0.1:63237 - R:localhost/127.0.0.1:8000]}}, [disconnecting])
47
12:36:14.893 [reactor-http-nio-1] DEBUG reactor.netty.resources.PooledConnectionProvider - [id: 0x80cf000e, L:/127.0.0.1:63237 - R:localhost/127.0.0.1:8000] Releasing channel
48
12:36:14.896 [reactor-http-nio-1] DEBUG reactor.netty.resources.PooledConnectionProvider - [id: 0x80cf000e, L:/127.0.0.1:63237 - R:localhost/127.0.0.1:8000] Channel cleaned, now 0 active connections and 1 inactive connections
49
12:36:14.900 [reactor-http-nio-2] DEBUG reactor.netty.resources.PooledConnectionProvider - [id: 0x31fc4f5b, L:/127.0.0.1:63238 ! R:localhost/127.0.0.1:8000] Channel closed, now 0 active connections and 1 inactive connections
50
Person [id=10, name=Heena, surname=naaz]
51
12:36:14.901 [reactor-http-nio-2] DEBUG reactor.netty.http.client.HttpClientOperations - [id: 0x31fc4f5b, L:/127.0.0.1:63238 ! R:localhost/127.0.0.1:8000] Received last HTTP packet
52
12:36:14.901 [reactor-http-nio-2] DEBUG reactor.netty.resources.PooledConnectionProvider - [id: 0x31fc4f5b, L:/127.0.0.1:63238 ! R:localhost/127.0.0.1:8000] onStateChange(GET{uri=/person, connection=PooledConnection{channel=[id: 0x31fc4f5b, L:/127.0.0.1:63238 ! R:localhost/127.0.0.1:8000]}}, [disconnecting])
53
12:36:14.901 [reactor-http-nio-2] DEBUG reactor.netty.resources.PooledConnectionProvider - [id: 0x31fc4f5b, L:/127.0.0.1:63238 ! R:localhost/127.0.0.1:8000] Releasing channel


In the above output,

  • ExchangeFunctions - Represents a function that exchanges a request for a (delayed) ClientResponse.
  • PooledConnectionProvider - Will produce Connection.
  • FluxReceive - Subscribes inbound receiver.

Here each request will be treated as a task. all requests (which are waiting for response) are getting added in a queue, mean while the serving thread will be unblocked to serve other requests. When the response is received for the waiting task, WebClient will activate that task by assigning thread to it and resume the rest execution.

Configuration

RestTemplate

To make use of RestTemplate ,we need the following spring-web artifact that contains RestTemplate class.

HTML
 




x


 
1
<dependency>
2
<groupId>org.springframework.boot</groupId>
3
<artifactId>spring-boot-starter-web</artifactId>
4
</dependency>


WebClient

To make use of WebClient, we need the following dependency in your Spring Boot project.

HTML
 




xxxxxxxxxx
1


 
1
<dependency>
2
<groupId>org.springframework.boot</groupId>
3
<artifactId>spring-boot-starter-webflux</artifactId>
4
</dependency>



RestTemplate is getting deprecated and will not be supported in future versions of Spring.
We should start using WebClient. It not only supports Asynchronous it also supports Synchronous calls so we call use WebClient as a replacement for RestTemplate.

Topics:
api ,blocking client ,comparison ,java ,resttemplate ,spring boot ,webclient

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}