diff --git a/framework-docs/modules/ROOT/pages/web/webmvc/mvc-ann-async.adoc b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-ann-async.adoc index 7c67abdfcf..3f71ba4852 100644 --- a/framework-docs/modules/ROOT/pages/web/webmvc/mvc-ann-async.adoc +++ b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-ann-async.adoc @@ -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 handle() { + return new WebAsyncTask(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 { + 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). diff --git a/spring-context/src/main/java/org/springframework/validation/ValidationUtils.java b/spring-context/src/main/java/org/springframework/validation/ValidationUtils.java index 263558009c..7744ba60ab 100644 --- a/spring-context/src/main/java/org/springframework/validation/ValidationUtils.java +++ b/spring-context/src/main/java/org/springframework/validation/ValidationUtils.java @@ -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); } } diff --git a/spring-core/src/test/java/org/springframework/core/annotation/AnnotationBackCompatibilityTests.java b/spring-core/src/test/java/org/springframework/core/annotation/AnnotationBackCompatibilityTests.java index 9ca47d7f63..434bfe7c93 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/AnnotationBackCompatibilityTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/AnnotationBackCompatibilityTests.java @@ -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 mergedAnnotation = MergedAnnotations.from(source).get(TestAnnotation.class); diff --git a/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java b/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java index 65022cb36b..dc053a54b9 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java @@ -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"); diff --git a/spring-core/src/test/java/org/springframework/core/convert/converter/ConverterTests.java b/spring-core/src/test/java/org/springframework/core/convert/converter/ConverterTests.java index 65d519f540..f72f2069cd 100644 --- a/spring-core/src/test/java/org/springframework/core/convert/converter/ConverterTests.java +++ b/spring-core/src/test/java/org/springframework/core/convert/converter/ConverterTests.java @@ -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 length = String::length; assertThat(length.andThen(this.moduloTwo).convert("example")).isEqualTo(1); assertThat(length.andThen(this.addOne).convert("example")).isEqualTo(8); diff --git a/spring-core/src/test/java/org/springframework/util/AntPathMatcherTests.java b/spring-core/src/test/java/org/springframework/util/AntPathMatcherTests.java index e1352e5eec..6de3602cbf 100644 --- a/spring-core/src/test/java/org/springframework/util/AntPathMatcherTests.java +++ b/spring-core/src/test/java/org/springframework/util/AntPathMatcherTests.java @@ -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); diff --git a/spring-core/src/test/kotlin/org/springframework/core/BridgeMethodResolverKotlinTests.kt b/spring-core/src/test/kotlin/org/springframework/core/BridgeMethodResolverKotlinTests.kt index bb94dd3867..f1c8eabc1e 100644 --- a/spring-core/src/test/kotlin/org/springframework/core/BridgeMethodResolverKotlinTests.kt +++ b/spring-core/src/test/kotlin/org/springframework/core/BridgeMethodResolverKotlinTests.kt @@ -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 { diff --git a/spring-test/src/main/java/org/springframework/mock/http/client/MockClientHttpRequest.java b/spring-test/src/main/java/org/springframework/mock/http/client/MockClientHttpRequest.java index 42222f4493..5c2c8c3229 100644 --- a/spring-test/src/main/java/org/springframework/mock/http/client/MockClientHttpRequest.java +++ b/spring-test/src/main/java/org/springframework/mock/http/client/MockClientHttpRequest.java @@ -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) { diff --git a/spring-web/src/test/java/org/springframework/http/converter/json/SpringHandlerInstantiatorTests.java b/spring-web/src/test/java/org/springframework/http/converter/json/SpringHandlerInstantiatorTests.java index 1c8fda3586..e3709e617f 100644 --- a/spring-web/src/test/java/org/springframework/http/converter/json/SpringHandlerInstantiatorTests.java +++ b/spring-web/src/test/java/org/springframework/http/converter/json/SpringHandlerInstantiatorTests.java @@ -104,7 +104,7 @@ class SpringHandlerInstantiatorTests { } @Test - void applicationContextAwaretypeResolverBuilder() throws JsonProcessingException { + void applicationContextAwareTypeResolverBuilder() throws JsonProcessingException { this.objectMapper.writeValueAsString(new Group()); assertThat(CustomTypeResolverBuilder.isAutowiredFiledInitialized).isTrue(); }