DZone
Java Zone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
  • Refcardz
  • Trend Reports
  • Webinars
  • Zones
  • |
    • Agile
    • AI
    • Big Data
    • Cloud
    • Database
    • DevOps
    • Integration
    • IoT
    • Java
    • Microservices
    • Open Source
    • Performance
    • Security
    • Web Dev
DZone > Java Zone > RestTemplate vs. WebClient

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.

Hinanaaz Sanadi user avatar by
Hinanaaz Sanadi
·
Jun. 16, 22 · Java Zone · Tutorial
Like (18)
Save
Tweet
39.93K Views

Join the DZone community and get the full member experience.

Join For Free

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

  • RestTemplate
  • WebClient, Spring 5's reactive alternative

What Are 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
 
/* Main application class */
public class RestTemplateTest {
	 public static void main(String[] args) {
	        ThreadExecutor threadEx = new ThreadExecutor();
	        for (int i = 0; i < 4; i++) {
	            System.out.println("\n"+ threadEx.getData());
	        }
	    }
}

/* Thread executor class */
public class ThreadExecutor {
	 private ExecutorService executor = Executors.newFixedThreadPool(10);
	    private RestTemplate restTemplate = new RestTemplate();

	    public String getData() {
	    	System.out.println("\nRestTemplate obj details - "+restTemplate.toString() + "\n");
	        Future<String> future = executor.submit(new Task(restTemplate));
	        String response = null;

	        try {
	            response = future.get(100, TimeUnit.MILLISECONDS);
	        } catch (TimeoutException e) {
	            e.printStackTrace();
	        } catch (InterruptedException e) {
	            e.printStackTrace();
	        } catch (ExecutionException e) {
	            e.printStackTrace();
	        }

	        return response;
	    }
}

/* Task class, calls /person api */
class Task implements Callable<String> {

    private RestTemplate restTemplate;

    public Task(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    public String call() throws Exception {

        String url = "http://localhost:8000/person/1";
        String response = restTemplate.getForObject(url, String.class);

        return response;
    }
}

/* Controller */
@RestController
class PersonRestController {

	@GetMapping("/person/{id}")
	public Person findPersonById(@PathVariable("id") Long id) {
		return new Person(1L, "Heena", "Khan");
	}
  
 	@GetMapping("/person")
	public List<Person> getAllPersonList() {
		List<Person> personList = new ArrayList<Person>();
		personList.add(new Person(10L, "Heena", "Khan"));
		personList.add(new Person(20L, "Kiara", "Khan"));
		personList.add(new Person(30L, "Ayaan", "Shaikh"));
      
        return personList;
	}
}

/* Entity class */
class Person {

	Person(Long id, String name, String surname) {
		this.id = id;
		this.name = name;
		this.surname = surname;
	}

	private Long id;

	private String name;

	private String surname;

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getSurname() {
		return surname;
	}

	public void setSurname(String surname) {
		this.surname = surname;
	}
}

OUTPUT :

RestTemplate obj details - org.springframework.web.client.RestTemplate@124c278f

22:39:04.127 [pool-1-thread-1] DEBUG org.springframework.web.client.RestTemplate - HTTP GET http://localhost:8000/person/1
22:39:04.136 [pool-1-thread-1] DEBUG org.springframework.web.client.RestTemplate - Accept=[text/plain, application/json, application/*+json, */*]
22:39:04.156 [pool-1-thread-1] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK
22:39:04.157 [pool-1-thread-1] DEBUG org.springframework.web.client.RestTemplate - Reading to [java.lang.String] as "application/json"

{"id":1,"name":"Heena","surname":"Khan"}

RestTemplate obj details - org.springframework.web.client.RestTemplate@124c278f

22:39:04.160 [pool-1-thread-2] DEBUG org.springframework.web.client.RestTemplate - HTTP GET http://localhost:8000/person/1
22:39:04.161 [pool-1-thread-2] DEBUG org.springframework.web.client.RestTemplate - Accept=[text/plain, application/json, application/*+json, */*]
22:39:04.165 [pool-1-thread-2] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK
22:39:04.165 [pool-1-thread-2] DEBUG org.springframework.web.client.RestTemplate - Reading to [java.lang.String] as "application/json"

{"id":1,"name":"Heena","surname":"Khan"}

RestTemplate obj details - org.springframework.web.client.RestTemplate@124c278f

22:39:04.166 [pool-1-thread-3] DEBUG org.springframework.web.client.RestTemplate - HTTP GET http://localhost:8000/person/1
22:39:04.166 [pool-1-thread-3] DEBUG org.springframework.web.client.RestTemplate - Accept=[text/plain, application/json, application/*+json, */*]
22:39:04.167 [pool-1-thread-3] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK
22:39:04.167 [pool-1-thread-3] DEBUG org.springframework.web.client.RestTemplate - Reading to [java.lang.String] as "application/json"

{"id":1,"name":"Heena","surname":"Khan"}

RestTemplate obj details - org.springframework.web.client.RestTemplate@124c278f

22:39:04.168 [pool-1-thread-4] DEBUG org.springframework.web.client.RestTemplate - HTTP GET http://localhost:8000/person/1
22:39:04.168 [pool-1-thread-4] DEBUG org.springframework.web.client.RestTemplate - Accept=[text/plain, application/json, application/*+json, */*]
22:39:04.170 [pool-1-thread-4] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK
22:39:04.171 [pool-1-thread-4] DEBUG org.springframework.web.client.RestTemplate - Reading to [java.lang.String] as "application/json"

{"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.

resttemplate Spring Framework Requests

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Data Mesh — Graduating Your Data to Next Level
  • 6 Reasons Cybersecurity Awareness Training is Important
  • Benchmarking PostgreSQL Workloads on Kubernetes
  • Documenting the Architecture of Your Projects With the C4 Model

Comments

Java Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • MVB Program
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends:

DZone.com is powered by 

AnswerHub logo