Backend Development 11 min read

Master Java 11 HttpClient: Sync & Async Requests with Full Code Guide

Learn how Java 11’s built‑in HttpClient simplifies both synchronous and asynchronous HTTP calls, covering client and request creation, setting URIs, methods, headers, timeouts, and various body types, with complete code examples and a comparison to alternative libraries.

Java Architecture Diary
Java Architecture Diary
Java Architecture Diary
Master Java 11 HttpClient: Sync & Async Requests with Full Code Guide

1. Introduction

Before Java 11, Java only provided the HttpURLConnection API, which was hard to use and performed poorly. Consequently, most developers relied on third‑party libraries such as Apache HttpClient, Jetty, OkHttp, and Spring's RestTemplate.

Unlike HttpURLConnection, the Java 11 HTTP Client offers both synchronous and asynchronous request mechanisms.

The API includes three core classes: HttpRequest (represents the request to be sent), HttpClient (sends synchronous or asynchronous requests), and HttpResponse (represents the final result). The following sections describe them in detail.

2. HttpClient Synchronous and Asynchronous

Javadoc synchronous example:

<code>HttpClient client = HttpClient.newBuilder()
        .version(Version.HTTP_1_1)
        .followRedirects(Redirect.NORMAL)
        .connectTimeout(Duration.ofSeconds(20))
        .proxy(ProxySelector.of(new InetSocketAddress("proxy.example.com", 80)))
        .authenticator(Authenticator.getDefault())
        .build();

HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://www.dreamlu.net"))
        .timeout(Duration.ofMinutes(2))
        .header("Content-Type", "application/json")
        .POST(BodyPublishers.ofFile(Paths.get("file.json")))
        .build();

HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());</code>

In this case the program sends the request and waits for the response, then prints the status code and response body.

Now an asynchronous example:

<code>HttpClient client = HttpClient.newBuilder()
    .build();

HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://www.dreamlu.net"))
        .timeout(Duration.ofMinutes(2))
        .header("Content-Type", "application/json")
        .POST(BodyPublishers.ofFile(Paths.get("file.json")))
        .build();

client.sendAsync(request, BodyHandlers.ofString())
        .thenApply(HttpResponse::body)
        .thenAccept(System.out::println);
</code>

The example uses the sendAsync() method instead of send() ; send() performs a synchronous request, while sendAsync() issues an asynchronous request to any HTTP server or RESTful web service.

Next we look at adding timeout, headers, cookies, and other parameters.

3. HttpRequest Details

1. HttpRequest

HttpRequest represents the request to be sent. It can be created via

HttpRequest.newBuilder()

. In JDK 16 a new

HttpRequest.newBuilder(HttpRequest request, BiPredicate filter)

method copies configuration from an existing request.

2. Setting URI

Setting a URI is the first step when creating a new request. You can use the builder constructor with a URI parameter or call

uri(URI)

on the builder.

<code>HttpRequest.newBuilder(new URI("https://www.dreamlu.net/get"))

HttpRequest.newBuilder()
        .uri(new URI("https://www.dreamlu.net/get"))
</code>

3. Setting HTTP Method

You can define the HTTP method using builder methods:

GET()

POST(BodyPublisher body)

PUT(BodyPublisher body)

DELETE()

Example of a simple GET request:

<code>HttpRequest request = HttpRequest.newBuilder()
  .uri(new URI("https://www.dreamlu.net/get"))
  .GET()
  .build();
</code>

4. Setting HTTP Version

The HttpClient API fully supports HTTP/2 and uses it by default. You can set the version with the builder’s

version()

method.

<code>HttpRequest request = HttpRequest.newBuilder()
  .uri(new URI("https://www.dreamlu.net/get"))
  .version(HttpClient.Version.HTTP_2)
  .GET()
  .build();
</code>

Note: If HTTP/2 is not supported, HttpClient automatically falls back to HTTP/1.1.

5. Setting Headers

Headers can be added easily via the builder’s

headers()

method or multiple

header()

calls.

<code>HttpRequest request = HttpRequest.newBuilder()
  .uri(new URI("https://www.dreamlu.net/get"))
  .headers("key1", "value1", "key2", "value2")
  .GET()
  .build();

HttpRequest request2 = HttpRequest.newBuilder()
  .uri(new URI("https://www.dreamlu.net/get"))
  .header("key1", "value1")
  .header("key2", "value2")
  .GET()
  .build();
</code>

6. Setting Timeout

Timeout defines how long to wait for a response; exceeding it throws

HttpTimeoutException

. By default it is infinite, but you can set a

Duration

value.

<code>HttpRequest request = HttpRequest.newBuilder()
  .uri(new URI("https://www.dreamlu.net/get"))
  .timeout(Duration.of(10, SECONDS))
  .GET()
  .build();
</code>

7. Setting Request Body

You can add a body using

POST

,

PUT

, or

DELETE

with

BodyPublishers

, or use

BodyPublishers.noBody()

for an empty request.

<code>HttpRequest request = HttpRequest.newBuilder()
  .uri(new URI("https://www.dreamlu.net/post"))
  .POST(HttpRequest.BodyPublishers.noBody())
  .build();
</code>

8. Sending String Body

Use

BodyPublishers.ofString()

(or the simple factory method

String()

) to send a string as the request body.

<code>HttpRequest request = HttpRequest.newBuilder()
  .uri(new URI("https://www.dreamlu.net/post"))
  .headers("Content-Type", "text/plain;charset=UTF-8")
  .POST(HttpRequest.BodyPublishers.ofString("Sample request body"))
  .build();
</code>

9. Sending InputStream Body

Pass an

InputStream

via a

Supplier

. Example uses a simple

ByteArrayInputStream

.

<code>byte[] sampleData = "Sample request body".getBytes();

HttpRequest request = HttpRequest.newBuilder()
  .uri(new URI("https://www.dreamlu.net/post"))
  .headers("Content-Type", "text/plain;charset=UTF-8")
  .POST(HttpRequest.BodyPublishers.ofInputStream(() -> new ByteArrayInputStream(sampleData)))
  .build();
</code>

10. Sending ByteArray

Use

BodyPublishers.ofByteArray()

to send a byte array.

<code>byte[] sampleData = "Sample request body".getBytes();

HttpRequest request = HttpRequest.newBuilder()
  .uri(new URI("https://www.dreamlu.net/post"))
  .headers("Content-Type", "text/plain;charset=UTF-8")
  .POST(HttpRequest.BodyPublishers.ofByteArray(sampleData))
  .build();
</code>

4. Conclusion

In terms of pure usability, the author finds Java 11 HttpClient less convenient than his open‑source

mica‑http

library (based on OkHttp). Example of a

mica‑http

request:

<code>// synchronous, returns null on exception
String html = HttpRequest.get("https://www.baidu.com")
    .connectTimeout(Duration.ofSeconds(1000))
    .query("test", "a")
    .query("name", "張三")
    .query("x", 1)
    .query("abd", Base64Util.encode("123&$#%"))
    .queryEncoded("abc", Base64Util.encode("123&$#%"))
    .execute()
    .onFailed((request, e) -> {
        e.printStackTrace();
    })
    .onSuccess(ResponseSpec::asString);
</code>

As Spring Boot 3.x moves projects to Java 17, the built‑in HttpClient introduced in JDK 11 will become the optimal choice for SDK and middleware development.

javaBackend DevelopmentasynchronoushttpHttpClientsynchronous
Java Architecture Diary
Written by

Java Architecture Diary

Committed to sharing original, high‑quality technical articles; no fluff or promotional content.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.