Using Spring Boot 3 Declarative HTTP Client with HttpServiceProxyFactory
This article explains how Spring Boot 3 introduces a native declarative HTTP client via HttpServiceProxyFactory, showing required dependencies, supported annotations, interface definition, and complete code examples for building, configuring, and invoking remote HTTP services without third‑party libraries.
In modern Java development, remote HTTP calls are common, and frameworks such as OpenFeign, Retrofit, or Hutool HttpUtil have been widely used. Spring Boot 3 adds native support for declarative HTTP clients through HttpServiceProxyFactory, reducing the need for external libraries.
The declarative HTTP interface works like a regular Java interface annotated with @HttpExchange and related method‑level annotations ( @GetExchange, @PostExchange, @PutExchange, @DeleteExchange, @PatchExchange) to specify HTTP verbs and endpoints.
To enable this feature, add the Spring Web (or WebFlux for reactive support) starter dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- For reactive support -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>Define a HTTP service interface with @HttpExchange and method annotations, for example:
@HttpExchange(url = "/users", accept = "application/json", contentType = "application/json")
public interface UserClient {
@GetExchange("/")
Flux<User> getAll();
@GetExchange("/{id}")
Mono<User> getById(@PathVariable("id") Long id);
@PostExchange("/")
Mono<ResponseEntity<Void>> save(@RequestBody User user);
@PutExchange("/{id}")
Mono<ResponseEntity<Void>> update(@PathVariable Long id, @RequestBody User user);
@DeleteExchange("/{id}")
Mono<ResponseEntity<Void>> delete(@PathVariable Long id);
}Configure a WebClient and create the proxy factory:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.howtodoinjava.app.web.UserClient;
import lombok.SneakyThrows;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.support.WebClientAdapter;
import org.springframework.web.service.invoker.HttpServiceProxyFactory;
@Configuration
public class WebConfig {
@Bean
WebClient webClient(ObjectMapper objectMapper) {
return WebClient.builder()
.baseUrl("https://jsonplaceholder.typicode.com/")
.build();
}
@SneakyThrows
@Bean
UserClient userClient(WebClient webClient) {
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(WebClientAdapter.forClient(webClient)).build();
return factory.createClient(UserClient.class);
}
}Define a simple User entity class (omitting getters/setters for brevity) and inject UserClient to perform CRUD operations:
@Autowired
UserClient userClient;
// Get all users
userClient.getAll().subscribe(data -> log.info("User: {}", data));
// Get user by ID
userClient.getById(1L).subscribe(data -> log.info("User: {}", data));
// Create a new user
userClient.save(new User(null, "Lokesh", "lokesh", "[email protected]"))
.subscribe(data -> log.info("User: {}", data));
// Delete a user
userClient.delete(1L).subscribe(data -> log.info("User: {}", data));With this approach, developers can call remote HTTP services using a type‑safe, annotation‑driven interface without writing implementation code, leveraging Spring’s native support introduced in Spring Boot 3.
Additional resources include the Spring Boot 3 release notes, the GitHub repository containing the demo code, and the official Spring Framework documentation on declarative HTTP interfaces.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
