Spring 6: HTTP Interfaces, RestClient, i18n ProblemDetail, Virtual Threads
This guide walks through Spring 6’s new capabilities—including Java 17 baseline, Jakarta namespace migration, HTTP interface proxies with @HttpExchange, WebClient integration via JDK HttpClient, internationalized ProblemDetail handling, the RestClient API, and executing asynchronous tasks on virtual threads—all demonstrated with concise code examples.
Environment: Spring 3.2.0 (Spring 6.1.1) + JDK 21
Spring 6 Baseline
The whole framework codebase targets Java 17 source level.
Servlet, JPA and other namespaces moved from javax to jakarta.
Runtime compatible with Jakarta EE 9 and Jakarta EE 10 APIs.
Compatible with latest web servers: Tomcat 10.1, Jetty 11, Undertow 2.3.
Early compatibility with virtual threads (preview in JDK 19).
1. HTTP Remote Interface Calls
Define HTTP interfaces using @HttpExchange and generate proxy beans to invoke remote endpoints.
@HttpExchange("/demos")
public interface RemoteService {
@GetExchange("/format")
Map<String, Object> format(@RequestParam Map<String, String> params) throws Exception;
}Configure the proxy bean:
@Configuration
public class RemoteInterfaceConfig {
@Bean
public RemoteService remoteService(WebClient.Builder remoteClient) {
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(
WebClientAdapter.forClient(remoteClient.build())).build();
return factory.createClient(RemoteService.class);
}
}Use the service:
@Resource
private RemoteService remoteService;
@GetMapping("/index")
public Object index(@RequestParam Map<String, String> params) throws Exception {
return remoteService.format(params);
}2. HttpClient
JDK 11 introduced HttpClient. In Spring it can be combined with WebClient:
@Bean
public WebClient webClient(WebClient.Builder builder) {
HttpClient httpClient = HttpClient.newBuilder()
.followRedirects(Redirect.NORMAL)
.connectTimeout(Duration.ofSeconds(20))
.build();
ClientHttpConnector connector = new JdkClientHttpConnector(httpClient);
return WebClient.builder().clientConnector(connector).build();
}3. ProblemDetail Internationalization
ProblemDetail exposes type, title, and detail. ResponseEntityExceptionHandler resolves these fields via MessageSource. Custom ResponseEntityExceptionHandler or ProblemDetailsExceptionHandler bean is required.
Example controller throwing ErrorResponseException:
@GetMapping("/{id}")
public Object index(@PathVariable("id") Long id) {
if (id == 66) {
throw new ErrorResponseException(HttpStatusCode.valueOf(500));
}
return id;
}Internationalization file (properties):
problemDetail.org.springframework.web.ErrorResponseException=这里是异常详细信息
problemDetail.title.org.springframework.web.ErrorResponseException=发生异常Handler implementation (excerpt):
@ExceptionHandler({ErrorResponseException.class})
@Nullable
public final ResponseEntity<Object> handleException(Exception ex, WebRequest request) throws Exception {
// ...
else if (ex instanceof ErrorResponseException subEx) {
return handleErrorResponseException(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);
}
// ...
}
protected ResponseEntity<Object> handleErrorResponseException(
ErrorResponseException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
return handleExceptionInternal(ex, null, headers, status, request);
}
protected ResponseEntity<Object> handleExceptionInternal(
Exception ex, @Nullable Object body, HttpHeaders headers, HttpStatusCode statusCode, WebRequest request) {
if (body == null && ex instanceof ErrorResponse errorResponse) {
body = errorResponse.updateAndGetBody(this.messageSource, LocaleContextHolder.getLocale());
}
return createResponseEntity(body, headers, statusCode, request);
}
default ProblemDetail updateAndGetBody(@Nullable MessageSource messageSource, Locale locale) {
if (messageSource != null) {
Object[] arguments = getDetailMessageArguments(messageSource, locale);
String detail = messageSource.getMessage(getDetailMessageCode(), arguments, null, locale);
if (detail != null) {
getBody().setDetail(detail);
}
String title = messageSource.getMessage(getTitleMessageCode(), null, null, locale);
if (title != null) {
getBody().setTitle(title);
}
}
return getBody();
}4. RestClient
Spring 6.1 adds RestClient, a synchronous HTTP client sharing infrastructure with RestTemplate but offering a WebClient‑like API.
RestClient customClient = RestClient.builder()
.baseUrl("http://localhost:8088")
.defaultUriVariables(Map.of("id", "888"))
.defaultHeader("x-api-token", "aabbcc")
.requestInterceptor(new ClientHttpRequestInterceptor() {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException {
System.out.println("拦截器...");
return execution.execute(request, body);
}
})
.requestInitializer(new ClientHttpRequestInitializer() {
@Override
public void initialize(ClientHttpRequest request) {
System.out.println("初始化器...");
request.getHeaders().add("x-version", "1.0.0");
}
})
.build();
Users users = customClient.get()
.uri("/demos/users/{id}")
.retrieve()
.onStatus(HttpStatusCode::isError, (request, response) -> {
throw new RuntimeException(response.getStatusCode().toString() + "请求错误");
})
.body(Users.class);
System.out.println(users);5. Virtual Thread Asynchronous Tasks
Methods annotated with @Async can run on virtual threads.
@Bean
VirtualThreadTaskExecutor taskExecutor() {
return new VirtualThreadTaskExecutor("pack-vm-");
}
// or
@Bean
SimpleAsyncTaskScheduler taskScheduler() {
SimpleAsyncTaskScheduler scheduler = new SimpleAsyncTaskScheduler();
scheduler.setThreadNamePrefix("pack-vm-");
scheduler.setVirtualThreads(true);
return scheduler;
}Test method:
@Async
public void task() {
System.out.println(Thread.currentThread().getName() + " - 执行异步任务");
}For more on virtual threads see the linked article.
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.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
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.
