Introduction
One of the defining characteristics of modern software architecture is communication.
Unlike traditional monolithic applications, today’s enterprise systems consist of dozens or even hundreds of independent microservices.
Consider a simple Order Management System.
API Gateway
│
┌────────────────┼────────────────┐
▼ ▼ ▼
Customer Service Payment Service Inventory Service
│ │ │
└────────────────┼────────────────┘
▼
Notification Service
Every service communicates over HTTP.
A single request may trigger multiple REST API calls before a response is returned to the client.
For many years, Java developers relied on:
HttpURLConnection- Apache HttpClient
- OkHttp
- Jersey Client
- Spring RestTemplate
Although these libraries worked well, the Java platform itself lacked a modern, standard HTTP client.
Java 11 addressed this gap by introducing the Standard HTTP Client API.
The new client provides:
- A clean and modern API
- HTTP/2 support
- Asynchronous programming
- WebSocket support
- Improved performance
- Better integration with
CompletableFuture
Today, it is the recommended HTTP client for modern Java applications that do not depend on framework-specific alternatives.
Learning Objectives
By the end of this article, you will be able to:
- Understand why Java introduced a new HTTP client.
- Build synchronous and asynchronous REST clients.
- Configure headers, authentication, and timeouts.
- Upload and download files.
- Integrate with JSON.
- Use HTTP/2.
- Apply enterprise best practices.
- Compare the Java HTTP Client with Spring’s
RestTemplateandWebClient.
Before Java 11
For years, developers used HttpURLConnection.
Example:
URL url = new URL(
"https://api.example.com/customers");
HttpURLConnection connection =
(HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
Simple requests required many lines of code.
Handling:
- Timeouts
- Authentication
- Redirects
- Error handling
- Response parsing
quickly became cumbersome.
As applications evolved toward microservices, these limitations became more apparent.
Java 11 Solution
The new API revolves around three core classes:
HttpClient
↓
HttpRequest
↓
HttpResponse
This separation of responsibilities makes the API easier to understand and extend.
Creating an HttpClient
HttpClient client = HttpClient.newHttpClient();
This creates a client with sensible defaults.
For production systems, additional configuration is often desirable.
Custom Client Configuration
HttpClient client =
HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.followRedirects(
HttpClient.Redirect.NORMAL)
.version(HttpClient.Version.HTTP_2)
.build();
This client:
- Uses HTTP/2 where supported.
- Follows redirects.
- Applies a connection timeout.
Building an HTTP Request
HttpRequest request =
HttpRequest.newBuilder()
.uri(
URI.create(
"https://api.example.com/customers"))
.GET()
.build();
Requests are immutable, making them thread-safe and reusable.
Sending a Synchronous Request
HttpResponse<String> response =
client.send(
request,
HttpResponse.BodyHandlers.ofString());
Retrieve the response body:
System.out.println(response.body());
Status code:
response.statusCode();
Headers:
response.headers();
Sending an Asynchronous Request
One of the biggest advantages of the Java 11 client is seamless integration with CompletableFuture.
CompletableFuture<HttpResponse<String>> future =
client.sendAsync(
request,
HttpResponse.BodyHandlers.ofString());
This allows multiple remote services to be called concurrently.
Processing the Response
future.thenApply(HttpResponse::body)
.thenAccept(System.out::println);
No blocking.
No explicit thread management.
HTTP POST Request
HttpRequest request =
HttpRequest.newBuilder()
.uri(
URI.create(
"https://api.example.com/orders"))
.header(
"Content-Type",
"application/json")
.POST(
HttpRequest.BodyPublishers.ofString(json))
.build();
The API provides body publishers for common payload types.
HTTP PUT Request
.PUT(
HttpRequest.BodyPublishers.ofString(json))
HTTP DELETE Request
.DELETE()
Adding Headers
.header("Authorization", bearerToken)
.header("Accept", "application/json")
.header("X-Correlation-Id", correlationId)
Enterprise APIs commonly include:
- Authorization tokens
- Correlation IDs
- Tenant identifiers
- Locale information
Timeout Configuration
HttpRequest request =
HttpRequest.newBuilder()
.timeout(Duration.ofSeconds(5))
Always configure sensible timeouts.
Waiting indefinitely for another microservice is rarely acceptable.
File Download
client.send(
request,
HttpResponse.BodyHandlers.ofFile(path));
Useful for:
- Reports
- Images
- Documents
- Data exports
File Upload
HttpRequest.BodyPublishers.ofFile(path)
Suitable for sending binary files.
For multipart uploads, additional handling or libraries may be required depending on the API.
JSON Integration
The HTTP Client does not perform JSON serialization.
Typically:
Object
↓
Jackson
↓
JSON
↓
HTTP Client
Response:
JSON
↓
Jackson
↓
Java Object
This separation keeps the HTTP client lightweight.
Authentication
Basic Authentication:
Authorization
↓
Basic Base64(username:password)
Bearer Tokens:
Authorization
↓
Bearer JWT
The client allows any required headers to be added.
HTTP/2
Unlike HttpURLConnection, the Java 11 client supports HTTP/2.
Benefits include:
- Multiplexing multiple requests over a single connection.
- Reduced latency.
- Improved throughput.
- Better resource utilization.
When both client and server support HTTP/2, the benefits are realized automatically.
WebSocket Support
Java 11 also introduced a standard WebSocket client.
Example:
HttpClient.newHttpClient()
.newWebSocketBuilder()
Useful for:
- Live notifications
- Trading systems
- Dashboards
- Chat applications
Enterprise Example
Suppose an Order Service calls:
Customer Service
Inventory Service
Payment Service
Instead of sequential execution:
Customer
↓
Inventory
↓
Payment
Combine the Java 11 HTTP Client with CompletableFuture:
Customer Inventory Payment
│ │ │
└─────────┼─────────┘
▼
Order Summary
This significantly reduces overall response time.
Java HTTP Client vs RestTemplate vs WebClient
| Feature | Java HTTP Client | RestTemplate | WebClient |
|---|---|---|---|
| Standard JDK API | ✅ | ❌ | ❌ |
| HTTP/2 | ✅ | ❌ | ✅ |
| Async Support | ✅ | Limited | ✅ |
| Reactive | ❌ | ❌ | ✅ |
| Spring Integration | Moderate | Excellent | Excellent |
| External Dependency | None | Spring | Spring |
Which should you use?
- Plain Java application: Java HTTP Client.
- Spring Boot (blocking): Existing
RestTemplatecode can continue to work, though it is in maintenance mode. - Spring Boot (reactive):
WebClientis generally the preferred choice.
Common Mistakes
No Timeout
Never call external services without connection and request timeouts.
Blocking Immediately
Calling:
future.join();
immediately after sendAsync() removes most of the benefits of asynchronous execution.
Creating a New HttpClient for Every Request
HttpClient is designed to be reused.
Creating a single configured instance is generally more efficient.
Ignoring HTTP Status Codes
Always inspect the response status before assuming success.
A successful network call is not necessarily a successful business operation.
Best Practices
✔ Reuse HttpClient instances.
✔ Configure timeouts.
✔ Prefer HTTP/2 when available.
✔ Combine with CompletableFuture for parallel service calls.
✔ Use Jackson for JSON serialization.
✔ Include correlation IDs in outbound requests.
✔ Handle non-success HTTP status codes explicitly.
Interview Questions
Why did Java introduce a new HTTP Client?
To provide a modern, standard HTTP API with HTTP/2, asynchronous programming, WebSocket support, and improved usability.
Is the Java HTTP Client thread-safe?
Yes. A single configured instance can be shared across multiple requests.
What is the benefit of sendAsync()?
It enables non-blocking HTTP requests that integrate naturally with CompletableFuture.
Should Spring Boot applications always use the Java HTTP Client?
Not necessarily. Spring applications often use WebClient (particularly in reactive applications) or continue using existing RestTemplate code where appropriate.
Does the Java HTTP Client perform JSON serialization?
No. JSON serialization and deserialization are typically handled by libraries such as Jackson.
Summary
The Java 11 HTTP Client modernized HTTP communication in the JDK by providing a clean, thread-safe, and feature-rich API. With support for HTTP/2, asynchronous requests, WebSockets, and seamless integration with CompletableFuture, it became an excellent choice for modern Java applications.
For enterprise developers, understanding when to use the standard HTTP Client—and how it compares to framework-specific alternatives such as RestTemplate and WebClient—is an important part of designing efficient, maintainable microservices.
Coming Up Next
Part 28 – Java 11 String API Enhancements – isBlank(), strip(), lines(), repeat(), indent(), and Modern Text Processing
We’ll explore how Java 11 simplified everyday string handling, compare the new APIs with pre-Java 11 approaches, discuss Unicode-aware whitespace handling, and demonstrate practical enterprise use cases in validation, parsing, logging, and REST applications.