Merge branch '6.2.x'
Build and Deploy Snapshot / Build and Deploy Snapshot (push) Waiting to run Details
Build and Deploy Snapshot / Verify (push) Blocked by required conditions Details
Deploy Docs / Dispatch docs deployment (push) Waiting to run Details

This commit is contained in:
rstoyanchev 2025-06-03 19:11:52 +01:00
commit b699b65b40
9 changed files with 64 additions and 21 deletions

View File

@ -4,11 +4,13 @@
Spring MVC has an extensive integration with Servlet asynchronous request
xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-processing[processing]:
* xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-deferredresult[`DeferredResult`] and xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-callable[`Callable`]
return values in controller methods provide basic support for a single asynchronous
return value.
* xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-deferredresult[`DeferredResult`],
xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-callable[`Callable`], and
xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-webasynctask[`WebAsyncTask`] return values
in controller methods provide support for a single asynchronous return value.
* Controllers can xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-http-streaming[stream] multiple values, including
xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-sse[SSE] and xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-output-stream[raw data].
xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-sse[SSE] and
xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-output-stream[raw data].
* Controllers can use reactive clients and return
xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-reactive-types[reactive types] for response handling.
@ -96,6 +98,47 @@ xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-configuration-spring-mvc[config
[[mvc-ann-async-webasynctask]]
== `WebAsyncTask`
`WebAsyncTask` is comparable to using xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-callable[Callable]
but allows customizing additional settings such a request timeout value, and the
`AsyncTaskExecutor` to execute the `java.util.concurrent.Callable` with instead
of the defaults set up globally for Spring MVC. Below is an example of using `WebAsyncTask`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
@GetMapping("/callable")
WebAsyncTask<String> handle() {
return new WebAsyncTask<String>(20000L,()->{
Thread.sleep(10000); //simulate long-running task
return "asynchronous request completed";
});
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@GetMapping("/callable")
fun handle(): WebAsyncTask<String> {
return WebAsyncTask(20000L) {
Thread.sleep(10000) // simulate long-running task
"asynchronous request completed"
}
}
----
======
[[mvc-ann-async-processing]]
== Processing
@ -390,7 +433,7 @@ reactive types from the controller method.
Reactive return values are handled as follows:
* A single-value promise is adapted to, similar to using `DeferredResult`. Examples
include `Mono` (Reactor) or `Single` (RxJava).
include `CompletionStage` (JDK), Mono` (Reactor), and `Single` (RxJava).
* A multi-value stream with a streaming media type (such as `application/x-ndjson`
or `text/event-stream`) is adapted to, similar to using `ResponseBodyEmitter` or
`SseEmitter`. Examples include `Flux` (Reactor) or `Observable` (RxJava).

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -250,7 +250,7 @@ public abstract class ValidationUtils {
Assert.notNull(errors, "Errors object must not be null");
Object value = errors.getFieldValue(field);
if (value == null ||!StringUtils.hasText(value.toString())) {
if (value == null || !StringUtils.hasText(value.toString())) {
errors.rejectValue(field, errorCode, errorArgs, defaultMessage);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -32,7 +32,7 @@ import static org.assertj.core.api.Assertions.assertThat;
class AnnotationBackCompatibilityTests {
@Test
void multiplRoutesToMetaAnnotation() {
void multipleRoutesToMetaAnnotation() {
Class<?> source = WithMetaMetaTestAnnotation1AndMetaTestAnnotation2.class;
// Merged annotation chooses lowest depth
MergedAnnotation<TestAnnotation> mergedAnnotation = MergedAnnotations.from(source).get(TestAnnotation.class);

View File

@ -984,7 +984,7 @@ class MergedAnnotationsTests {
}
@Test
void getDirectFromClassgetDirectFromClassMetaMetaAnnotatedClass() {
void getDirectFromClassGetDirectFromClassMetaMetaAnnotatedClass() {
MergedAnnotation<?> annotation = MergedAnnotations.from(
MetaMetaAnnotatedClass.class, SearchStrategy.TYPE_HIERARCHY).get(Component.class);
assertThat(annotation.getString("value")).isEqualTo("meta2");

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -46,7 +46,7 @@ class ConverterTests {
}
@Test
void andThenCanConvertfromDifferentSourceType() {
void andThenCanConvertFromDifferentSourceType() {
Converter<String, Integer> length = String::length;
assertThat(length.andThen(this.moduloTwo).convert("example")).isEqualTo(1);
assertThat(length.andThen(this.addOne).convert("example")).isEqualTo(8);

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -491,8 +491,8 @@ class AntPathMatcherTests {
assertThat(comparator.compare("/hotels/{hotel}/bookings/{booking}", "/hotels/{hotel}/booking")).isEqualTo(1);
// SPR-10550
assertThat(comparator.compare("/hotels/{hotel}/bookings/{booking}/cutomers/{customer}", "/**")).isEqualTo(-1);
assertThat(comparator.compare("/**", "/hotels/{hotel}/bookings/{booking}/cutomers/{customer}")).isEqualTo(1);
assertThat(comparator.compare("/hotels/{hotel}/bookings/{booking}/customers/{customer}", "/**")).isEqualTo(-1);
assertThat(comparator.compare("/**", "/hotels/{hotel}/bookings/{booking}/customers/{customer}")).isEqualTo(1);
assertThat(comparator.compare("/**", "/**")).isEqualTo(0);
assertThat(comparator.compare("/hotels/{hotel}", "/hotels/*")).isEqualTo(-1);
@ -505,8 +505,8 @@ class AntPathMatcherTests {
assertThat(comparator.compare("/hotels/{hotel}", "/hotels/{hotel}.*")).isEqualTo(2);
// SPR-6741
assertThat(comparator.compare("/hotels/{hotel}/bookings/{booking}/cutomers/{customer}", "/hotels/**")).isEqualTo(-1);
assertThat(comparator.compare("/hotels/**", "/hotels/{hotel}/bookings/{booking}/cutomers/{customer}")).isEqualTo(1);
assertThat(comparator.compare("/hotels/{hotel}/bookings/{booking}/customers/{customer}", "/hotels/**")).isEqualTo(-1);
assertThat(comparator.compare("/hotels/**", "/hotels/{hotel}/bookings/{booking}/customers/{customer}")).isEqualTo(1);
assertThat(comparator.compare("/hotels/foo/bar/**", "/hotels/{hotel}")).isEqualTo(1);
assertThat(comparator.compare("/hotels/{hotel}", "/hotels/foo/bar/**")).isEqualTo(-1);

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -22,7 +22,7 @@ import org.junit.jupiter.api.Test
/**
* Kotlin tests for [BridgeMethodResolver].
*
* @author Sebastien Deleuzes
* @author Sebastien Deleuze
*/
class BridgeMethodResolverKotlinTests {

View File

@ -104,7 +104,7 @@ public class MockClientHttpRequest extends MockHttpOutputMessage implements Clie
/**
* Set the {@link ClientHttpResponse} to be used as the result of executing
* the this request.
* this request.
* @see #execute()
*/
public void setResponse(ClientHttpResponse clientHttpResponse) {

View File

@ -104,7 +104,7 @@ class SpringHandlerInstantiatorTests {
}
@Test
void applicationContextAwaretypeResolverBuilder() throws JsonProcessingException {
void applicationContextAwareTypeResolverBuilder() throws JsonProcessingException {
this.objectMapper.writeValueAsString(new Group());
assertThat(CustomTypeResolverBuilder.isAutowiredFiledInitialized).isTrue();
}