Merge remote-tracking branch 'upstream/main' into issue-33355

This commit is contained in:
Réda Housni Alaoui 2025-06-30 15:03:14 +02:00
commit c14ca9d199
8762 changed files with 35752 additions and 15356 deletions

View File

@ -19,7 +19,7 @@ inputs:
java-version:
description: 'Java version to compile and test with'
required: false
default: '17'
default: '24'
publish:
description: 'Whether to publish artifacts ready for deployment to Artifactory'
required: false

View File

@ -19,7 +19,7 @@ inputs:
java-version:
description: 'Java version to use for the build'
required: false
default: '17'
default: '24'
runs:
using: composite
steps:
@ -31,7 +31,7 @@ runs:
${{ inputs.java-early-access == 'true' && format('{0}-ea', inputs.java-version) || inputs.java-version }}
${{ inputs.java-toolchain == 'true' && '17' || '' }}
- name: Set Up Gradle
uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
uses: gradle/actions/setup-gradle@ac638b010cf58a27ee6c972d7336334ccaf61c96 # v4.4.1
with:
cache-read-only: false
develocity-access-key: ${{ inputs.develocity-access-key }}

View File

@ -20,7 +20,7 @@ jobs:
toolchain: false
- version: 21
toolchain: true
- version: 23
- version: 24
toolchain: true
exclude:
- os:

View File

@ -46,7 +46,7 @@ jobs:
distribution: 'liberica'
java-version: 17
- name: Set Up Gradle
uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
uses: gradle/actions/setup-gradle@ac638b010cf58a27ee6c972d7336334ccaf61c96 # v4.4.1
with:
cache-read-only: false
- name: Configure Gradle Properties

View File

@ -6,7 +6,7 @@ plugins {
id 'com.github.bjornvester.xjc' version '1.8.2' apply false
id 'io.github.goooler.shadow' version '8.1.8' apply false
id 'me.champeau.jmh' version '0.7.2' apply false
id "net.ltgt.errorprone" version "4.1.0" apply false
id "io.spring.nullability" version "0.0.1" apply false
}
ext {
@ -57,17 +57,13 @@ configure([rootProject] + javaProjects) { project ->
apply from: "${rootDir}/gradle/ide.gradle"
dependencies {
testImplementation("org.junit.jupiter:junit-jupiter-api")
testImplementation("org.junit.jupiter:junit-jupiter-params")
testImplementation("org.junit.platform:junit-platform-suite-api")
testImplementation("org.junit.jupiter:junit-jupiter")
testImplementation("org.junit.platform:junit-platform-suite")
testImplementation("org.mockito:mockito-core")
testImplementation("org.mockito:mockito-junit-jupiter")
testImplementation("io.mockk:mockk")
testImplementation("org.assertj:assertj-core")
// Pull in the latest JUnit 5 Launcher API to ensure proper support in IDEs.
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
testRuntimeOnly("org.junit.platform:junit-platform-suite-engine")
testRuntimeOnly("org.apache.logging.log4j:log4j-core")
}
@ -75,12 +71,11 @@ configure([rootProject] + javaProjects) { project ->
"https://docs.oracle.com/en/java/javase/17/docs/api/",
"https://jakarta.ee/specifications/platform/11/apidocs/",
"https://docs.jboss.org/hibernate/orm/5.6/javadocs/",
"https://eclipse.dev/aspectj/doc/latest/runtime-api/",
"https://www.quartz-scheduler.org/api/2.3.0/",
"https://hc.apache.org/httpcomponents-client-5.4.x/current/httpclient5/apidocs/",
"https://hc.apache.org/httpcomponents-client-5.5.x/current/httpclient5/apidocs/",
"https://projectreactor.io/docs/test/release/api/",
"https://junit.org/junit4/javadoc/4.13.2/",
"https://junit.org/junit5/docs/5.12.2/api/",
"https://docs.junit.org/5.13.2/api/",
"https://www.reactive-streams.org/reactive-streams-1.0.3-javadoc/",
//"https://javadoc.io/static/io.rsocket/rsocket-core/1.1.1/",
"https://r2dbc.io/spec/1.0.0.RELEASE/api/",
@ -88,7 +83,9 @@ configure([rootProject] + javaProjects) { project ->
// but since 6.0 JSR 250 annotations such as @Resource and @PostConstruct have been replaced by their
// JakartaEE equivalents in the jakarta.annotation package.
//"https://www.javadoc.io/doc/com.google.code.findbugs/jsr305/3.0.2/",
"https://jspecify.dev/docs/api/"
"https://jspecify.dev/docs/api/",
"https://www.javadoc.io/doc/tools.jackson.core/jackson-databind/3.0.0-rc4/"
] as String[]
}

View File

@ -6,7 +6,7 @@
<module name="io.spring.javaformat.checkstyle.check.SpringHeaderCheck">
<property name="fileExtensions" value="java"/>
<property name="headerType" value="apache2"/>
<property name="headerCopyrightPattern" value="20\d\d-20\d\d"/>
<property name="headerCopyrightPattern" value="20\d\d-present"/>
<property name="packageInfoHeaderType" value="none"/>
</module>
<module name="com.puppycrawl.tools.checkstyle.checks.NewlineAtEndOfFileCheck"/>

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-present 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.
@ -50,7 +50,7 @@ public class CheckstyleConventions {
project.getPlugins().apply(CheckstylePlugin.class);
project.getTasks().withType(Checkstyle.class).forEach(checkstyle -> checkstyle.getMaxHeapSize().set("1g"));
CheckstyleExtension checkstyle = project.getExtensions().getByType(CheckstyleExtension.class);
checkstyle.setToolVersion("10.23.0");
checkstyle.setToolVersion("10.26.0");
checkstyle.getConfigDirectory().set(project.getRootProject().file("src/checkstyle"));
String version = SpringJavaFormatPlugin.class.getPackage().getImplementationVersion();
DependencySet checkstyleDependencies = project.getConfigurations().getByName("checkstyle").getDependencies();

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-present 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.
@ -26,7 +26,6 @@ import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.tasks.compile.JavaCompile;
import org.gradle.jvm.toolchain.JavaLanguageVersion;
import org.gradle.jvm.toolchain.JvmVendorSpec;
/**
* {@link Plugin} that applies conventions for compiling Java sources in Spring Framework.
@ -86,7 +85,6 @@ public class JavaConventions {
*/
private static void applyToolchainConventions(Project project) {
project.getExtensions().getByType(JavaPluginExtension.class).toolchain(toolchain -> {
toolchain.getVendor().set(JvmVendorSpec.BELLSOFT);
toolchain.getLanguageVersion().set(DEFAULT_LANGUAGE_VERSION);
});
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-present 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.
@ -34,8 +34,8 @@ public class KotlinConventions {
private void configure(KotlinCompile compile) {
compile.compilerOptions(options -> {
options.getApiVersion().set(KotlinVersion.KOTLIN_2_1);
options.getLanguageVersion().set(KotlinVersion.KOTLIN_2_1);
options.getApiVersion().set(KotlinVersion.KOTLIN_2_2);
options.getLanguageVersion().set(KotlinVersion.KOTLIN_2_2);
options.getJvmTarget().set(JvmTarget.JVM_17);
options.getJavaParameters().set(true);
options.getAllWarningsAsErrors().set(true);
@ -43,7 +43,8 @@ public class KotlinConventions {
"-Xsuppress-version-warnings",
"-Xjsr305=strict", // For dependencies using JSR 305
"-opt-in=kotlin.RequiresOptIn",
"-Xjdk-release=17" // Needed due to https://youtrack.jetbrains.com/issue/KT-49746
"-Xjdk-release=17", // Needed due to https://youtrack.jetbrains.com/issue/KT-49746
"-Xannotation-default-target=param-property" // Upcoming default, see https://youtrack.jetbrains.com/issue/KT-73255
);
});
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-present 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.
@ -62,7 +62,8 @@ class TestConventions {
test.include("**/*Tests.class", "**/*Test.class");
test.setSystemProperties(Map.of(
"java.awt.headless", "true",
"io.netty.leakDetection.level", "paranoid"
"io.netty.leakDetection.level", "paranoid",
"junit.platform.discovery.issue.severity.critical", "INFO"
));
if (project.hasProperty("testGroups")) {
test.systemProperty("testGroups", project.getProperties().get("testGroups"));

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-present 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.

View File

@ -46,6 +46,7 @@ repositories {
// To avoid a redeclaration error with Kotlin compiler
tasks.named('compileKotlin', KotlinCompilationTask.class) {
javaSources.from = []
compilerOptions.freeCompilerArgs = [ "-Xannotation-default-target=param-property" ] // Upcoming default, see https://youtrack.jetbrains.com/issue/KT-73255
}
dependencies {
@ -63,6 +64,7 @@ dependencies {
implementation("com.fasterxml.jackson.core:jackson-databind")
implementation("com.fasterxml.jackson.module:jackson-module-parameter-names")
implementation("com.github.ben-manes.caffeine:caffeine")
implementation("com.mchange:c3p0:0.9.5.5")
implementation("com.oracle.database.jdbc:ojdbc11")
implementation("io.projectreactor.netty:reactor-netty-http")

View File

@ -197,6 +197,7 @@
*** xref:web/webmvc/mvc-uri-building.adoc[]
*** xref:web/webmvc/mvc-ann-async.adoc[]
*** xref:web/webmvc-cors.adoc[]
*** xref:web/webmvc-versioning.adoc[]
*** xref:web/webmvc/mvc-ann-rest-exceptions.adoc[]
*** xref:web/webmvc/mvc-security.adoc[]
*** xref:web/webmvc/mvc-caching.adoc[]
@ -225,6 +226,7 @@
**** xref:web/webmvc/mvc-config/static-resources.adoc[]
**** xref:web/webmvc/mvc-config/default-servlet-handler.adoc[]
**** xref:web/webmvc/mvc-config/path-matching.adoc[]
**** xref:web/webmvc/mvc-config/api-version.adoc[]
**** xref:web/webmvc/mvc-config/advanced-java.adoc[]
**** xref:web/webmvc/mvc-config/advanced-xml.adoc[]
*** xref:web/webmvc/mvc-http2.adoc[]
@ -292,6 +294,7 @@
*** xref:web/webflux-functional.adoc[]
*** xref:web/webflux/uri-building.adoc[]
*** xref:web/webflux-cors.adoc[]
*** xref:web/webflux-versioning.adoc[]
*** xref:web/webflux/ann-rest-exceptions.adoc[]
*** xref:web/webflux/security.adoc[]
*** xref:web/webflux/caching.adoc[]

View File

@ -103,6 +103,14 @@ for details.
{spring-framework-api}++/objenesis/SpringObjenesis.html#IGNORE_OBJENESIS_PROPERTY_NAME++[`SpringObjenesis`]
for details.
| `spring.placeholder.escapeCharacter.default`
| The default escape character for property placeholder support. If not set, `'\'` will
be used. Can be set to a custom escape character or an empty string to disable support
for an escape character. The default escape character be explicitly overridden in
`PropertySourcesPlaceholderConfigurer` and subclasses of `AbstractPropertyResolver`. See
{spring-framework-api}++/core/env/AbstractPropertyResolver.html#DEFAULT_PLACEHOLDER_ESCAPE_CHARACTER_PROPERTY_NAME++[`AbstractPropertyResolver`]
for details.
| `spring.test.aot.processing.failOnError`
| A boolean flag that controls whether errors encountered during AOT processing in the
_Spring TestContext Framework_ should result in an exception that fails the overall process.

View File

@ -101,8 +101,11 @@ NOTE: When configuring a `PropertySourcesPlaceholderConfigurer` using JavaConfig
Using the above configuration ensures Spring initialization failure if any `${}`
placeholder could not be resolved. It is also possible to use methods like
`setPlaceholderPrefix`, `setPlaceholderSuffix`, `setValueSeparator`, or
`setEscapeCharacter` to customize placeholders.
`setPlaceholderPrefix()`, `setPlaceholderSuffix()`, `setValueSeparator()`, or
`setEscapeCharacter()` to customize the placeholder syntax. In addition, the default
escape character can be changed or disabled globally by setting the
`spring.placeholder.escapeCharacter.default` property via a JVM system property (or via
the xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism).
NOTE: Spring Boot configures by default a `PropertySourcesPlaceholderConfigurer` bean that
will get properties from `application.properties` and `application.yml` files.

View File

@ -314,7 +314,7 @@ Thus, marking it for lazy initialization will be ignored, and the
[[beans-factory-placeholderconfigurer]]
=== Example: The Class Name Substitution `PropertySourcesPlaceholderConfigurer`
=== Example: Property Placeholder Substitution with `PropertySourcesPlaceholderConfigurer`
You can use the `PropertySourcesPlaceholderConfigurer` to externalize property values
from a bean definition in a separate file by using the standard Java `Properties` format.
@ -341,8 +341,8 @@ with placeholder values is defined:
The example shows properties configured from an external `Properties` file. At runtime,
a `PropertySourcesPlaceholderConfigurer` is applied to the metadata that replaces some
properties of the DataSource. The values to replace are specified as placeholders of the
form pass:q[`${property-name}`], which follows the Ant and log4j and JSP EL style.
properties of the `DataSource`. The values to replace are specified as placeholders of the
form pass:q[`${property-name}`], which follows the Ant, log4j, and JSP EL style.
The actual values come from another file in the standard Java `Properties` format:
@ -355,11 +355,15 @@ jdbc.password=root
----
Therefore, the `${jdbc.username}` string is replaced at runtime with the value, 'sa', and
the same applies for other placeholder values that match keys in the properties file.
The `PropertySourcesPlaceholderConfigurer` checks for placeholders in most properties and
attributes of a bean definition. Furthermore, you can customize the placeholder prefix and suffix.
the same applies for other placeholder values that match keys in the properties file. The
`PropertySourcesPlaceholderConfigurer` checks for placeholders in most properties and
attributes of a bean definition. Furthermore, you can customize the placeholder prefix,
suffix, default value separator, and escape character. In addition, the default escape
character can be changed or disabled globally by setting the
`spring.placeholder.escapeCharacter.default` property via a JVM system property (or via
the xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism).
With the `context` namespace introduced in Spring 2.5, you can configure property placeholders
With the `context` namespace, you can configure property placeholders
with a dedicated configuration element. You can provide one or more locations as a
comma-separated list in the `location` attribute, as the following example shows:

View File

@ -1,56 +1,56 @@
[[null-safety]]
= Null-safety
Although Java does not let you express null-safety with its type system, the Spring Framework codebase is annotated with
https://jspecify.dev/docs/start-here/[JSpecify] annotations to declare the nullness of APIs, fields and related type
usages. Reading the https://jspecify.dev/docs/user-guide/[JSpecify user guide] is highly recommended in order to get
familiar with those annotations and semantics.
Although Java does not let you express nullness markers with its type system yet, the Spring Framework codebase is
annotated with https://jspecify.dev/docs/start-here/[JSpecify] annotations to declare the nullability of its APIs,
fields, and related type usages. Reading the https://jspecify.dev/docs/user-guide/[JSpecify user guide] is highly
recommended in order to get familiar with those annotations and semantics.
The primary goal of this explicit null-safety arrangement is to prevent `NullPointerException` to be thrown at runtime via
build time checks and to turn explicit nullness into a way to express the possible absence of value. It is useful in
both Java by leveraging some tooling (https://github.com/uber/NullAway[NullAway] or IDEs supporting null-safety
annotations such as IntelliJ IDEA or Eclipse) and Kotlin where JSpecify annotations are automatically translated to
The primary goal of this null-safety arrangement is to prevent a `NullPointerException` from being thrown at runtime via build
time checks and to use explicit nullability as a way to express the possible absence of value. It is useful in both
Java by leveraging some tooling (https://github.com/uber/NullAway[NullAway] or IDEs supporting JSpecify annotations
such as IntelliJ IDEA) and Kotlin where JSpecify annotations are automatically translated to
{kotlin-docs}/null-safety.html[Kotlin's null safety].
The {spring-framework-api}/core/Nullness.html[`Nullness` Spring API] can be used at runtime to detect the nullness of a
type usage, a field, a method return type or a parameter. It provides full support for JSpecify annotations,
Kotlin null safety, Java primitive types, as well as a pragmatic check on any `@Nullable` annotation (regardless of the
type usage, a field, a method return type, or a parameter. It provides full support for JSpecify annotations,
Kotlin null safety, and Java primitive types, as well as a pragmatic check on any `@Nullable` annotation (regardless of the
package).
[[null-safety-libraries]]
== Annotating libraries with JSpecify annotations
As of Spring Framework 7, the Spring Framework codebase leverages JSpecify annotations to expose null-safe APIs and
to check the consistency of those null-safety declarations with https://github.com/uber/NullAway[NullAway] as part of
its build. It is recommended for each library depending on Spring Framework (Spring portfolio projects), as
well as other libraries related to the Spring ecosystem (Reactor, Micrometer and Spring community projects), to do the
to check the consistency of those nullability declarations with https://github.com/uber/NullAway[NullAway] as part of
its build. It is recommended for each library depending on Spring Framework and Spring portfolio projects, as
well as other libraries related to the Spring ecosystem (Reactor, Micrometer, and Spring community projects), to do the
same.
[[null-safety-applications]]
== Leveraging JSpecify annotations in Spring applications
Developing applications with IDEs supporting null-safety annotations, such as IntelliJ IDEA or Eclipse, will provide
warnings in Java and errors in Kotlin when the null-safety contracts are not honored, allowing Spring application
developers to refine their null handling to prevent `NullPointerException` to be thrown at runtime.
Developing applications with IDEs that support nullness annotations will provide warnings in Java and errors in Kotlin
when the nullability contracts are not honored, allowing Spring application developers to refine their null handling to
prevent a `NullPointerException` from being thrown at runtime.
Optionally, Spring application developers can annotate their codebase and use https://github.com/uber/NullAway[NullAway]
to enforce null-safety during build time at application level.
Optionally, Spring application developers can annotate their codebase and use build plugins like
https://github.com/uber/NullAway[NullAway] to enforce null-safety at the application level during build time.
[[null-safety-guidelines]]
== Guidelines
The purpose of this section is to share some guidelines proposed for specifying explicitly the nullness of Spring-related
libraries or applications.
The purpose of this section is to share some proposed guidelines for explicitly specifying the nullability of
Spring-related libraries or applications.
[[null-safety-guidelines-jpecify]]
[[null-safety-guidelines-jspecify]]
=== JSpecify
The key points to understand is that by default, the nullness of types is unknown in Java, and that non-null type
usages are by far more frequent than nullable ones. In order to keep codebases readable, we typically want to define
that by default, type usages are non-null unless marked as nullable for a specific scope. This is exactly the purpose of
https://jspecify.dev/docs/api/org/jspecify/annotations/NullMarked.html[`@NullMarked`] that is typically set with Spring
at package level via a `package-info.java` file, for example:
The key points to understand are that the nullness of types is unknown in Java by default and that non-null type
usage is by far more frequent than nullable usage. In order to keep codebases readable, we typically want to define
by default that type usage is non-null unless marked as nullable for a specific scope. This is exactly the purpose of
https://jspecify.dev/docs/api/org/jspecify/annotations/NullMarked.html[`@NullMarked`] which is typically set in Spring
projects at the package level via a `package-info.java` file, for example:
[source,java,subs="verbatim,quotes",chomp="-packages",fold="none"]
----
@ -60,9 +60,9 @@ package org.springframework.core;
import org.jspecify.annotations.NullMarked;
----
In the various Java files belonging to the package, nullable type usages are defined explicitly with
In the various Java files belonging to the package, nullable type usage is defined explicitly with
https://jspecify.dev/docs/api/org/jspecify/annotations/Nullable.html[`@Nullable`]. It is recommended that this
annotation is specified just before the related type.
annotation is specified just before the related type on the same line.
For example, for a field:
@ -71,7 +71,7 @@ For example, for a field:
private @Nullable String fileEncoding;
----
Or for method parameters and return value:
Or for method parameters and method return types:
[source,java,subs="verbatim,quotes"]
----
@ -81,20 +81,23 @@ public static @Nullable String buildMessage(@Nullable String message,
}
----
When overriding a method, nullness annotations are not inherited from the superclass method. That means those
nullness annotations should be repeated if you just want to override the implementation and keep the same API
nullness.
[NOTE]
====
When overriding a method, JSpecify annotations are not inherited from the original
method. That means the JSpecify annotations should be copied to the overriding method if
you want to override the implementation and keep the same nullability semantics.
====
With arrays and varargs, you need to be able to differentiate the nullness of the elements from the nullness of
the array itself. Pay attention to the syntax
https://docs.oracle.com/javase/specs/jls/se17/html/jls-9.html#jls-9.7.4[defined by the Java specification] which may be
initially surprising:
- `@Nullable Object[] array` means individual elements can be null but the array itself can't.
- `Object @Nullable [] array` means individual elements can't be null but the array itself can.
- `@Nullable Object[] array` means individual elements can be null but the array itself cannot.
- `Object @Nullable [] array` means individual elements cannot be null but the array itself can.
- `@Nullable Object @Nullable [] array` means both individual elements and the array can be null.
The Java specifications also enforces that annotations defined with `@Target(ElementType.TYPE_USE)` like JSpecify
The Java specification also enforces that annotations defined with `@Target(ElementType.TYPE_USE)` like JSpecify
`@Nullable` should be specified after the last `.` with inner or fully qualified types:
- `Cache.@Nullable ValueWrapper`
@ -111,15 +114,15 @@ typical use cases.
The recommended configuration is:
- `NullAway:OnlyNullMarked=true` in order to perform nullness checks only for packages annotated with `@NullMarked`.
- `NullAway:OnlyNullMarked=true` in order to perform nullability checks only for packages annotated with `@NullMarked`.
- `NullAway:CustomContractAnnotations=org.springframework.lang.Contract` which makes NullAway aware of the
{spring-framework-api}/lang/Contract.html[@Contract] annotation in the `org.springframework.lang` package which
can be used to express complementary semantics to avoid non-relevant null-safety warnings in your codebase.
can be used to express complementary semantics to avoid irrelevant warnings in your codebase.
A good example of `@Contract` benefits is
{spring-framework-api}/util/Assert.html#notNull(java.lang.Object,java.lang.String)[`Assert#notnull`] which is annotated
with `@Contract("null, _ -> fail")`. With the configuration above, NullAway will understand that after a successful
invocation, the value passed as a parameter is not null.
A good example of the benefits of a `@Contract` declaration can be seen with
{spring-framework-api}/util/Assert.html#notNull(java.lang.Object,java.lang.String)[`Assert.notNull()`] which is annotated
with `@Contract("null, _ -> fail")`. With that contract declaration, NullAway will understand that the value passed as a
parameter cannot be null after a successful invocation of `Assert.notNull()`.
Optionally, it is possible to set `NullAway:JSpecifyMode=true` to enable
https://github.com/uber/NullAway/wiki/JSpecify-Support[checks on the full JSpecify semantics], including annotations on
@ -127,26 +130,26 @@ generic types. Be aware that this mode is
https://github.com/uber/NullAway/issues?q=is%3Aissue+is%3Aopen+label%3Ajspecify[still under development] and requires
using JDK 22 or later (typically combined with the `--release` Java compiler flag to configure the
expected baseline). It is recommended to enable the JSpecify mode only as a second step, after making sure the codebase
generates no warning with the recommended configuration mentioned above.
generates no warning with the recommended configuration mentioned previously in this section.
==== Warnings suppression
There are a few valid use cases where NullAway will wrongly detect nullness problems. In such case, it is recommended
There are a few valid use cases where NullAway will incorrectly detect nullability problems. In such case, it is recommended
to suppress related warnings and to document the reason:
- `@SuppressWarnings("NullAway.Init")` at field, constructor or class level can be used to avoid unnecessary warnings
due to the lazy initialization of fields, for example due to a class implementing
- `@SuppressWarnings("NullAway.Init")` at field, constructor, or class level can be used to avoid unnecessary warnings
due to the lazy initialization of fields for example, due to a class implementing
{spring-framework-api}/beans/factory/InitializingBean.html[`InitializingBean`].
- `@SuppressWarnings("NullAway") // Dataflow analysis limitation` can be used when NullAway dataflow analysis is not
able to detect that the path involving a nullness problem will never happen.
able to detect that the path involving a nullability problem will never happen.
- `@SuppressWarnings("NullAway") // Lambda` can be used when NullAway does not take into account assertions performed
outside of a lambda for the code path within the lambda.
- `@SuppressWarnings("NullAway") // Reflection` can be used for some reflection operations that are known returning
non-null values even if that can't be expressed by the API.
- `@SuppressWarnings("NullAway") // Well-known map keys` can be used when `Map#get` invocations are done with keys known
to be present and non-null related values inserted previously.
- `@SuppressWarnings("NullAway") // Overridden method does not define nullness` can be used when the super class does
not define nullness (typically when the super class is coming from a dependency).
- `@SuppressWarnings("NullAway") // Reflection` can be used for some reflection operations that are known to return
non-null values even if that cannot be expressed by the API.
- `@SuppressWarnings("NullAway") // Well-known map keys` can be used when `Map#get` invocations are performed with keys that are known
to be present and when non-null related values have been inserted previously.
- `@SuppressWarnings("NullAway") // Overridden method does not define nullability` can be used when the superclass does
not define nullability (typically when the superclass comes from a dependency).
[[null-safety-migrating]]
@ -155,30 +158,30 @@ not define nullness (typically when the super class is coming from a dependency)
Spring null-safety annotations {spring-framework-api}/lang/Nullable.html[`@Nullable`],
{spring-framework-api}/lang/NonNull.html[`@NonNull`],
{spring-framework-api}/lang/NonNullApi.html[`@NonNullApi`], and
{spring-framework-api}/lang/NonNullFields.html[`@NonNullFields`] in the `org.springframework.lang` package have been
introduced in Spring Framework 5 when JSpecify did not exist and the best option was to leverage JSR 305 (a dormant
but widespread JSR) meta-annotations. They are deprecated as of Spring Framework 7 in favor of
{spring-framework-api}/lang/NonNullFields.html[`@NonNullFields`] in the `org.springframework.lang` package were
introduced in Spring Framework 5 when JSpecify did not exist, and the best option at that time was to leverage
meta-annotations from JSR 305 (a dormant but widespread JSR). They are deprecated as of Spring Framework 7 in favor of
https://jspecify.dev/docs/start-here/[JSpecify] annotations, which provide significant enhancements such as properly
defined specifications, a canonical dependency with no split-package issue, better tooling, better Kotlin integration
and the capability to specify the nullness more precisely for more use cases.
defined specifications, a canonical dependency with no split-package issues, better tooling, better Kotlin integration,
and the capability to specify nullability more precisely for more use cases.
A key difference is that Spring null-safety annotations, following JSR 305 semantics, apply to fields,
parameters and return values while JSpecify annotations apply to type usages. This subtle difference
is in practice pretty significant, as it allows for example to differentiate the nullness of elements from the
nullness of arrays/varargs as well as defining the nullness of generic types.
A key difference is that Spring's deprecated null-safety annotations, which follow JSR 305 semantics, apply to fields,
parameters, and return values; while JSpecify annotations apply to type usage. This subtle difference
is in practice pretty significant, as it allows developers to differentiate between the nullness of elements and the
nullness of arrays/varargs as well as to define the nullness of generic types.
That means array and varargs null-safety declarations have to be updated to keep the same semantic. For example
That means array and varargs null-safety declarations have to be updated to keep the same semantics. For example
`@Nullable Object[] array` with Spring annotations needs to be changed to `Object @Nullable [] array` with JSpecify
annotations. Same for varargs.
annotations. The same applies to varargs.
It is also recommended to move field and return value annotations closer to the type, for example:
It is also recommended to move field and return value annotations closer to the type and on the same line, for example:
- For fields, instead of `@Nullable private String field` with Spring annotations, use `private @Nullable String field`
with JSpecify annotations.
- For return values, instead of `@Nullable public String method()` with Spring annotations, use
- For method return types, instead of `@Nullable public String method()` with Spring annotations, use
`public @Nullable String method()` with JSpecify annotations.
Also, with JSpecify, you don't need to specify `@NonNull` when overriding a type usage annotated with `@Nullable` in the
Also, with JSpecify, you do not need to specify `@NonNull` when overriding a type usage annotated with `@Nullable` in the
super method to "undo" the nullable declaration in null-marked code. Just declare it unannotated and the null-marked
defaults (a type usage is considered non-null unless explicitly annotated as nullable) will apply.

View File

@ -1,7 +1,7 @@
[[orm-hibernate]]
= Hibernate
We start with a coverage of https://hibernate.org/[Hibernate 5] in a Spring environment,
We start with a coverage of https://hibernate.org/[Hibernate] in a Spring environment,
using it to demonstrate the approach that Spring takes towards integrating OR mappers.
This section covers many issues in detail and shows different variations of DAO
implementations and transaction demarcation. Most of these patterns can be directly
@ -10,13 +10,12 @@ cover the other ORM technologies and show brief examples.
[NOTE]
====
As of Spring Framework 6.0, Spring requires Hibernate ORM 5.5+ for Spring's
As of Spring Framework 7.0, Spring requires Hibernate ORM 7.0 for Spring's
`HibernateJpaVendorAdapter` as well as for a native Hibernate `SessionFactory` setup.
We recommend Hibernate ORM 5.6 as the last feature branch in that Hibernate generation.
Hibernate ORM 6.x is only supported as a JPA provider (`HibernateJpaVendorAdapter`).
Plain `SessionFactory` setup with the `orm.hibernate5` package is not supported anymore.
We recommend Hibernate ORM 6.1/6.2 with JPA-style setup for new development projects.
The `org.springframework.orm.jpa.hibernate` package supersedes the former `orm.hibernate5`:
now for use with Hibernate ORM 7.0, tightly integrated with `HibernateJpaVendorAdapter`
as well as supporting Hibernate's native `SessionFactory.getCurrentSession()` style.
====
@ -43,7 +42,7 @@ JDBC `DataSource` and a Hibernate `SessionFactory` on top of it:
<property name="password" value=""/>
</bean>
<bean id="mySessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<bean id="mySessionFactory" class="org.springframework.orm.jpa.hibernate.LocalSessionFactoryBean">
<property name="dataSource" ref="myDataSource"/>
<property name="mappingResources">
<list>
@ -271,7 +270,7 @@ processing at runtime. The following example shows how to do so:
<!-- SessionFactory, DataSource, etc. omitted -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate5.HibernateTransactionManager">
class="org.springframework.orm.jpa.hibernate.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
@ -301,7 +300,7 @@ and an example for a business method implementation:
----
<beans>
<bean id="myTxManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<bean id="myTxManager" class="org.springframework.orm.jpa.hibernate.HibernateTransactionManager">
<property name="sessionFactory" ref="mySessionFactory"/>
</bean>

View File

@ -212,7 +212,7 @@ example declares `sessionFactory` and `txManager` beans:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<bean id="sessionFactory" class="org.springframework.orm.jpa.hibernate.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mappingResources">
<list>
@ -226,7 +226,7 @@ example declares `sessionFactory` and `txManager` beans:
</property>
</bean>
<bean id="txManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<bean id="txManager" class="org.springframework.orm.jpa.hibernate.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
----
@ -238,7 +238,7 @@ transaction coordinator and possibly also its connection release mode configurat
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<bean id="sessionFactory" class="org.springframework.orm.jpa.hibernate.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mappingResources">
<list>
@ -262,7 +262,7 @@ for enforcing the same defaults:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<bean id="sessionFactory" class="org.springframework.orm.jpa.hibernate.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mappingResources">
<list>

View File

@ -3,26 +3,34 @@
The Spring Framework provides the following choices for making calls to REST endpoints:
* xref:integration/rest-clients.adoc#rest-restclient[`RestClient`] - synchronous client with a fluent API.
* xref:integration/rest-clients.adoc#rest-webclient[`WebClient`] - non-blocking, reactive client with fluent API.
* xref:integration/rest-clients.adoc#rest-resttemplate[`RestTemplate`] - synchronous client with template method API.
* xref:integration/rest-clients.adoc#rest-http-interface[HTTP Interface] - annotated interface with generated, dynamic proxy implementation.
* xref:integration/rest-clients.adoc#rest-restclient[`RestClient`] -- synchronous client with a fluent API
* xref:integration/rest-clients.adoc#rest-webclient[`WebClient`] -- non-blocking, reactive client with fluent API
* xref:integration/rest-clients.adoc#rest-resttemplate[`RestTemplate`] -- synchronous client with template method API
* xref:integration/rest-clients.adoc#rest-http-interface[HTTP Interface Clients] -- annotated interface backed by generated proxy
[[rest-restclient]]
== `RestClient`
The `RestClient` is a synchronous HTTP client that offers a modern, fluent API.
It offers an abstraction over HTTP libraries that allows for convenient conversion from a Java object to an HTTP request, and the creation of objects from an HTTP response.
`RestClient` is a synchronous HTTP client that provides a fluent API to perform requests.
It serves as an abstraction over HTTP libraries, and handles conversion of HTTP request and response content to and from higher level Java objects.
=== Creating a `RestClient`
=== Create a `RestClient`
The `RestClient` is created using one of the static `create` methods.
You can also use `builder()` to get a builder with further options, such as specifying which HTTP library to use (see <<rest-request-factories>>) and which message converters to use (see <<rest-message-conversion>>), setting a default URI, default path variables, default request headers, or `uriBuilderFactory`, or registering interceptors and initializers.
`RestClient` has static `create` shortcut methods.
It also exposes a `builder()` with further options:
Once created (or built), the `RestClient` can be used safely by multiple threads.
- select the HTTP library to use, see <<rest-request-factories>>
- configure message converters, see <<rest-message-conversion>>
- set a baseUrl
- set default request headers, cookies, path variables, API version
- configure an `ApiVersionInserter`
- register interceptors
- register request initializers
The following sample shows how to create a default `RestClient`, and how to build a custom one.
Once created, a `RestClient` is safe to use in multiple threads.
The below shows how to create or build a `RestClient`:
[tabs]
======
@ -39,6 +47,8 @@ Java::
.defaultUriVariables(Map.of("variable", "foo"))
.defaultHeader("My-Header", "Foo")
.defaultCookie("My-Cookie", "Bar")
.defaultVersion("1.2")
.apiVersionInserter(ApiVersionInserter.fromHeader("API-Version").build())
.requestInterceptor(myCustomInterceptor)
.requestInitializer(myCustomInitializer)
.build();
@ -57,23 +67,25 @@ Kotlin::
.defaultUriVariables(mapOf("variable" to "foo"))
.defaultHeader("My-Header", "Foo")
.defaultCookie("My-Cookie", "Bar")
.defaultVersion("1.2")
.apiVersionInserter(ApiVersionInserter.fromHeader("API-Version").build())
.requestInterceptor(myCustomInterceptor)
.requestInitializer(myCustomInitializer)
.build()
----
======
=== Using the `RestClient`
=== Use the `RestClient`
When making an HTTP request with the `RestClient`, the first thing to specify is which HTTP method to use.
This can be done with `method(HttpMethod)` or with the convenience methods `get()`, `head()`, `post()`, and so on.
To perform an HTTP request, first specify the HTTP method to use.
Use the convenience methods like `get()`, `head()`, `post()`, and others, or `method(HttpMethod)`.
==== Request URL
Next, the request URI can be specified with the `uri` methods.
This step is optional and can be skipped if the `RestClient` is configured with a default URI.
Next, specify the request URI with the `uri` methods.
This is optional, and you can skip this step if you configured a baseUrl through the builder.
The URL is typically specified as a `String`, with optional URI template variables.
The following example configures a GET request to `https://example.com/orders/42`:
The following shows how to perform a request:
[tabs]
======
@ -108,6 +120,7 @@ For more details on working with and encoding URIs, see xref:web/webmvc/mvc-uri-
If necessary, the HTTP request can be manipulated by adding request headers with `header(String, String)`, `headers(Consumer<HttpHeaders>`, or with the convenience methods `accept(MediaType...)`, `acceptCharset(Charset...)` and so on.
For HTTP requests that can contain a body (`POST`, `PUT`, and `PATCH`), additional methods are available: `contentType(MediaType)`, and `contentLength(long)`.
You can set an API version for the request if the client is configured with `ApiVersionInserter`.
The request body itself can be set by `body(Object)`, which internally uses <<rest-message-conversion>>.
Alternatively, the request body can be set using a `ParameterizedTypeReference`, allowing you to use generics.
@ -844,15 +857,17 @@ It can be used to migrate from the latter to the former.
[[rest-http-interface]]
== HTTP Interface
== HTTP Interface Clients
The Spring Framework lets you define an HTTP service as a Java interface with
`@HttpExchange` methods. You can pass such an interface to `HttpServiceProxyFactory`
to create a proxy which performs requests through an HTTP client such as `RestClient`
or `WebClient`. You can also implement the interface from an `@Controller` for server
request handling.
You can define an HTTP Service as a Java interface with `@HttpExchange` methods, and use
`HttpServiceProxyFactory` to create a client proxy from it for remote access over HTTP via
`RestClient`, `WebClient`, or `RestTemplate`. On the server side, an `@Controller` class
can implement the same interface to handle requests with
xref:web/webmvc/mvc-controller/ann-requestmapping.adoc#mvc-ann-httpexchange-annotation[@HttpExchange]
controller methods.
Start by creating the interface with `@HttpExchange` methods:
First, create the Java interface:
[source,java,indent=0,subs="verbatim,quotes"]
----
@ -866,43 +881,7 @@ Start by creating the interface with `@HttpExchange` methods:
}
----
Now you can create a proxy that performs requests when methods are called.
For `RestClient`:
[source,java,indent=0,subs="verbatim,quotes"]
----
RestClient restClient = RestClient.builder().baseUrl("https://api.github.com/").build();
RestClientAdapter adapter = RestClientAdapter.create(restClient);
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build();
RepositoryService service = factory.createClient(RepositoryService.class);
----
For `WebClient`:
[source,java,indent=0,subs="verbatim,quotes"]
----
WebClient webClient = WebClient.builder().baseUrl("https://api.github.com/").build();
WebClientAdapter adapter = WebClientAdapter.create(webClient);
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build();
RepositoryService service = factory.createClient(RepositoryService.class);
----
For `RestTemplate`:
[source,java,indent=0,subs="verbatim,quotes"]
----
RestTemplate restTemplate = new RestTemplate();
restTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory("https://api.github.com/"));
RestTemplateAdapter adapter = RestTemplateAdapter.create(restTemplate);
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build();
RepositoryService service = factory.createClient(RepositoryService.class);
----
`@HttpExchange` is supported at the type level where it applies to all methods:
Optionally, use `@HttpExchange` at the type level to declare common attributes for all methods:
[source,java,indent=0,subs="verbatim,quotes"]
----
@ -920,15 +899,46 @@ For `RestTemplate`:
----
Next, configure the client and create the `HttpServiceProxyFactory`:
[source,java,indent=0,subs="verbatim,quotes"]
----
// Using RestClient...
RestClient restClient = RestClient.create("...");
RestClientAdapter adapter = RestClientAdapter.create(restClient);
// or WebClient...
WebClient webClient = WebClient.create("...");
WebClientAdapter adapter = WebClientAdapter.create(webClient);
// or RestTemplate...
RestTemplate restTemplate = new RestTemplate();
RestTemplateAdapter adapter = RestTemplateAdapter.create(restTemplate);
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build();
----
Now, you're ready to create client proxies:
[source,java,indent=0,subs="verbatim,quotes"]
----
RepositoryService service = factory.createClient(RepositoryService.class);
// Use service methods for remote calls...
----
[[rest-http-interface-method-parameters]]
=== Method Parameters
Annotated, HTTP exchange methods support flexible method signatures with the following
method parameters:
`@HttpExchange` methods support flexible method signatures with the following inputs:
[cols="1,2", options="header"]
|===
| Method argument | Description
| Method parameter | Description
| `URI`
| Dynamically set the URL for the request, overriding the annotation's `url` attribute.
@ -989,29 +999,33 @@ Method parameters cannot be `null` unless the `required` attribute (where availa
parameter annotation) is set to `false`, or the parameter is marked optional as determined by
{spring-framework-api}/core/MethodParameter.html#isOptional()[`MethodParameter#isOptional`].
`RestClientAdapter` provides additional support for a method parameter of type
`StreamingHttpOutputMessage.Body` that allows sending the request body by writing to an
`OutputStream`.
[[rest-http-interface.custom-resolver]]
=== Custom argument resolver
=== Custom Arguments
For more complex cases, HTTP interfaces do not support `RequestEntity` types as method parameters.
This would take over the entire HTTP request and not improve the semantics of the interface.
Instead of adding many method parameters, developers can combine them into a custom type
and configure a dedicated `HttpServiceArgumentResolver` implementation.
In the following HTTP interface, we are using a custom `Search` type as a parameter:
You can configure a custom `HttpServiceArgumentResolver`. The example interface below
uses a custom `Search` method parameter type:
include-code::./CustomHttpServiceArgumentResolver[tag=httpinterface,indent=0]
We can implement our own `HttpServiceArgumentResolver` that supports our custom `Search` type
and writes its data in the outgoing HTTP request.
A custom argument resolver could be implemented like this:
include-code::./CustomHttpServiceArgumentResolver[tag=argumentresolver,indent=0]
Finally, we can use this argument resolver during the setup and use our HTTP interface.
To configure the custom argument resolver:
include-code::./CustomHttpServiceArgumentResolver[tag=usage,indent=0]
TIP: By default, `RequestEntity` is not supported as a method parameter, instead encouraging
the use of more fine-grained method parameters for individual parts of the request.
[[rest-http-interface-return-values]]
=== Return Values
@ -1084,65 +1098,180 @@ depends on how the underlying HTTP client is configured. You can set a `blockTim
value on the adapter level as well, but we recommend relying on timeout settings of the
underlying HTTP client, which operates at a lower level and provides more control.
`RestClientAdapter` provides supports additional support for a return value of type
`InputStream` or `ResponseEntity<InputStream>` that provides access to the raw response
body content.
[[rest-http-interface-exceptions]]
=== Error Handling
To customize error response handling, you need to configure the underlying HTTP client.
For `RestClient`:
By default, `RestClient` raises `RestClientException` for 4xx and 5xx HTTP status codes.
To customize this, register a response status handler that applies to all responses
performed through the client:
To customize error handling for HTTP Service client proxies, you can configure the
underlying client as needed. By default, clients raise an exception for 4xx and 5xx HTTP
status codes. To customize this, register a response status handler that applies to all
responses performed through the client as follows:
[source,java,indent=0,subs="verbatim,quotes"]
----
// For RestClient
RestClient restClient = RestClient.builder()
.defaultStatusHandler(HttpStatusCode::isError, (request, response) -> ...)
.build();
RestClientAdapter adapter = RestClientAdapter.create(restClient);
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build();
----
For more details and options, such as suppressing error status codes, see the Javadoc of
`defaultStatusHandler` in `RestClient.Builder`.
For `WebClient`:
By default, `WebClient` raises `WebClientResponseException` for 4xx and 5xx HTTP status codes.
To customize this, register a response status handler that applies to all responses
performed through the client:
[source,java,indent=0,subs="verbatim,quotes"]
----
// or for WebClient...
WebClient webClient = WebClient.builder()
.defaultStatusHandler(HttpStatusCode::isError, resp -> ...)
.build();
WebClientAdapter adapter = WebClientAdapter.create(webClient);
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(adapter).build();
----
For more details and options, such as suppressing error status codes, see the Javadoc of
`defaultStatusHandler` in `WebClient.Builder`.
For `RestTemplate`:
By default, `RestTemplate` raises `RestClientException` for 4xx and 5xx HTTP status codes.
To customize this, register an error handler that applies to all responses
performed through the client:
[source,java,indent=0,subs="verbatim,quotes"]
----
// or for RestTemplate...
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(myErrorHandler);
RestTemplateAdapter adapter = RestTemplateAdapter.create(restTemplate);
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build();
----
For more details and options, see the Javadoc of `setErrorHandler` in `RestTemplate` and
the `ResponseErrorHandler` hierarchy.
For more details and options such as suppressing error status codes, see the reference
documentation for each client, as well as the Javadoc of `defaultStatusHandler` in
`RestClient.Builder` or `WebClient.Builder`, and the `setErrorHandler` of `RestTemplate`.
[[rest-http-interface-group-config]]
=== HTTP Interface Groups
It's trivial to create client proxies with `HttpServiceProxyFactory`, but to have them
declared as beans leads to repetitive configuration. You may also have multiple
target hosts, and therefore multiple clients to configure, and even more client proxy
beans to create.
To make it easier to work with interface clients at scale the Spring Framework provides
dedicated configuration support. It lets applications focus on identifying HTTP Services
by group, and customizing the client for each group, while the framework transparently
creates a registry of client proxies, and declares each proxy as a bean.
An HTTP Service group is simply a set of interfaces that share the same client setup and
`HttpServiceProxyFactory` instance to create proxies. Typically, that means one group per
host, but you can have more than one group for the same target host in case the
underlying client needs to be configured differently.
One way to declare HTTP Service groups is via `@ImportHttpServices` annotations in
`@Configuration` classes as shown below:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@ImportHttpServices(group = "echo", types = {EchoServiceA.class, EchoServiceB.class}) // <1>
@ImportHttpServices(group = "greeting", basePackageClasses = GreetServiceA.class) // <2>
public class ClientConfig {
}
----
<1> Manually list interfaces for group "echo"
<2> Detect interfaces for group "greeting" under a base package
It is also possible to declare groups programmatically by creating an HTTP Service
registrar and then importing it:
[source,java,indent=0,subs="verbatim,quotes"]
----
public class MyHttpServiceRegistrar extends AbstractHttpServiceRegistrar { // <1>
@Override
protected void registerHttpServices(GroupRegistry registry, AnnotationMetadata metadata) {
registry.forGroup("echo").register(EchoServiceA.class, EchoServiceB.class); // <2>
registry.forGroup("greeting").detectInBasePackages(GreetServiceA.class); // <3>
}
}
@Configuration
@Import(MyHttpServiceRegistrar.class) // <4>
public class ClientConfig {
}
----
<1> Create extension class of `AbstractHttpServiceRegistrar`
<2> Manually list interfaces for group "echo"
<3> Detect interfaces for group "greeting" under a base package
<4> Import the registrar
TIP: You can mix and match `@ImportHttpService` annotations with programmatic registrars,
and you can spread the imports across multiple configuration classes. All imports
contribute collaboratively the same, shared `HttpServiceProxyRegistry` instance.
Once HTTP Service groups are declared, add an `HttpServiceGroupConfigurer` bean to
customize the client for each group. For example:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@ImportHttpServices(group = "echo", types = {EchoServiceA.class, EchoServiceB.class})
@ImportHttpServices(group = "greeting", basePackageClasses = GreetServiceA.class)
public class ClientConfig {
@Bean
public RestClientHttpServiceGroupConfigurer groupConfigurer() {
return groups -> {
// configure client for group "echo"
groups.filterByName("echo").forEachClient((group, clientBuilder) -> ...);
// configure the clients for all groups
groups.forEachClient((group, clientBuilder) -> ...);
// configure client and proxy factory for each group
groups.forEachGroup((group, clientBuilder, factoryBuilder) -> ...);
};
}
}
----
TIP: Spring Boot uses an `HttpServiceGroupConfigurer` to add support for client properties
by HTTP Service group, Spring Security to add OAuth support, and Spring Cloud to add load
balancing.
As a result of the above, each client proxy is available as a bean that you can
conveniently autowire by type:
[source,java,indent=0,subs="verbatim,quotes"]
----
@RestController
public class EchoController {
private final EchoService echoService;
public EchoController(EchoService echoService) {
this.echoService = echoService;
}
// ...
}
----
However, if there are multiple client proxies of the same type, e.g. the same interface
in multiple groups, then there is no unique bean of that type, and you cannot autowire by
type only. For such cases, you can work directly with the `HttpServiceProxyRegistry` that
holds all proxies, and obtain the ones you need by group:
[source,java,indent=0,subs="verbatim,quotes"]
----
@RestController
public class EchoController {
private final EchoService echoService1;
private final EchoService echoService2;
public EchoController(HttpServiceProxyRegistry registry) {
this.echoService1 = registry.getClient("echo1", EchoService.class); // <1>
this.echoService2 = registry.getClient("echo2", EchoService.class); // <2>
}
// ...
}
----
<1> Access the `EchoService` client proxy for group "echo1"
<2> Access the `EchoService` client proxy for group "echo2"

View File

@ -2,7 +2,7 @@
= Requirements
:page-section-summary-toc: 1
Spring Framework supports Kotlin 2.1+ and requires
Spring Framework supports Kotlin 2.2+ and requires
https://search.maven.org/artifact/org.jetbrains.kotlin/kotlin-stdlib[`kotlin-stdlib`]
and https://search.maven.org/artifact/org.jetbrains.kotlin/kotlin-reflect[`kotlin-reflect`]
to be present on the classpath. They are provided by default if you bootstrap a Kotlin project on

View File

@ -190,7 +190,7 @@ NOTE: If you use Spring Boot, you should probably use
instead of `@Value` annotations.
As an alternative, you can customize the property placeholder prefix by declaring the
following configuration beans:
following `PropertySourcesPlaceholderConfigurer` bean:
[source,kotlin,indent=0]
----
@ -200,8 +200,10 @@ following configuration beans:
}
----
You can customize existing code (such as Spring Boot actuators or `@LocalServerPort`)
that uses the `${...}` syntax, with configuration beans, as the following example shows:
You can support components (such as Spring Boot actuators or `@LocalServerPort`) that use
the standard `${...}` syntax alongside components that use the custom `%{...}` syntax by
declaring multiple `PropertySourcesPlaceholderConfigurer` beans, as the following example
shows:
[source,kotlin,indent=0]
----
@ -215,6 +217,9 @@ that uses the `${...}` syntax, with configuration beans, as the following exampl
fun defaultPropertyConfigurer() = PropertySourcesPlaceholderConfigurer()
----
In addition, the default escape character can be changed or disabled globally by setting
the `spring.placeholder.escapeCharacter.default` property via a JVM system property (or
via the xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism).
[[checked-exceptions]]
@ -319,7 +324,7 @@ progresses.
== Testing
This section addresses testing with the combination of Kotlin and Spring Framework.
The recommended testing framework is https://junit.org/junit5/[JUnit 5] along with
The recommended testing framework is https://junit.org/junit5/[JUnit] along with
https://mockk.io/[Mockk] for mocking.
NOTE: If you are using Spring Boot, see
@ -330,7 +335,7 @@ NOTE: If you are using Spring Boot, see
=== Constructor injection
As described in the xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit-jupiter-di[dedicated section],
JUnit Jupiter (JUnit 5) allows constructor injection of beans which is pretty useful with Kotlin
JUnit Jupiter allows constructor injection of beans which is pretty useful with Kotlin
in order to use `val` instead of `lateinit var`. You can use
{spring-framework-api}/test/context/TestConstructor.html[`@TestConstructor(autowireMode = AutowireMode.ALL)`]
to enable autowiring for all parameters.
@ -355,7 +360,7 @@ file with a `spring.test.constructor.autowire.mode = all` property.
=== `PER_CLASS` Lifecycle
Kotlin lets you specify meaningful test function names between backticks (```).
With JUnit Jupiter (JUnit 5), Kotlin test classes can use the `@TestInstance(TestInstance.Lifecycle.PER_CLASS)`
With JUnit Jupiter, Kotlin test classes can use the `@TestInstance(TestInstance.Lifecycle.PER_CLASS)`
annotation to enable single instantiation of test classes, which allows the use of `@BeforeAll`
and `@AfterAll` annotations on non-static methods, which is a good fit for Kotlin.
@ -399,8 +404,8 @@ class IntegrationTests {
[[specification-like-tests]]
=== Specification-like Tests
You can create specification-like tests with JUnit 5 and Kotlin.
The following example shows how to do so:
You can create specification-like tests with Kotlin and JUnit Jupiter's `@Nested` test
class support. The following example shows how to do so:
[source,kotlin,indent=0]
----

View File

@ -2,8 +2,8 @@
= Spring JUnit Jupiter Testing Annotations
The following annotations are supported when used in conjunction with the
xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit-jupiter-extension[`SpringExtension`] and JUnit Jupiter
(that is, the programming model in JUnit 5):
xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit-jupiter-extension[`SpringExtension`]
and JUnit Jupiter (that is, the programming model in JUnit):
* xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-junit-jupiter-springjunitconfig[`@SpringJUnitConfig`]
* xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-junit-jupiter-springjunitwebconfig[`@SpringJUnitWebConfig`]

View File

@ -140,8 +140,8 @@ Kotlin::
======
If we write tests that use JUnit Jupiter, we can reduce code duplication even further,
since annotations in JUnit 5 can also be used as meta-annotations. Consider the following
example:
since annotations in JUnit Jupiter can also be used as meta-annotations. Consider the
following example:
[tabs]
======

View File

@ -10,7 +10,8 @@ Resource locations are typically XML configuration files or Groovy scripts locat
classpath, while component classes are typically `@Configuration` classes. However,
resource locations can also refer to files and scripts in the file system, and component
classes can be `@Component` classes, `@Service` classes, and so on. See
xref:testing/testcontext-framework/ctx-management/javaconfig.adoc#testcontext-ctx-management-javaconfig-component-classes[Component Classes] for further details.
xref:testing/testcontext-framework/ctx-management/javaconfig.adoc#testcontext-ctx-management-javaconfig-component-classes[Component Classes]
for further details.
The following example shows a `@ContextConfiguration` annotation that refers to an XML
file:
@ -137,6 +138,6 @@ configuration classes as well as context initializers that are declared by super
or enclosing classes.
See xref:testing/testcontext-framework/ctx-management.adoc[Context Management],
xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit-jupiter-nested-test-configuration[`@Nested` test class configuration], and the `@ContextConfiguration`
xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit-jupiter-nested-test-configuration[`@Nested` test class configuration],
and the {spring-framework-api}/test/context/ContextConfiguration.html[`@ContextConfiguration`]
javadocs for further details.

View File

@ -166,7 +166,7 @@ following sections to make this pattern much easier to implement.
== MockMvc and WebDriver Setup
To use Selenium WebDriver with `MockMvc`, make sure that your project includes a test
dependency on `org.seleniumhq.selenium:selenium-htmlunit3-driver`.
dependency on `org.seleniumhq.selenium:htmlunit3-driver`.
We can easily create a Selenium WebDriver that integrates with MockMvc by using the
`MockMvcHtmlUnitDriverBuilder` as the following example shows:

View File

@ -1,10 +1,26 @@
[[spring-mvc-test-client]]
= Testing Client Applications
You can use client-side tests to test code that internally uses the `RestTemplate`. The
idea is to declare expected requests and to provide "`stub`" responses so that you can
focus on testing the code in isolation (that is, without running a server). The following
example shows how to do so:
To test code that uses the `RestClient` or `RestTemplate`, you can use a mock web server, such as
https://github.com/square/okhttp#mockwebserver[OkHttp MockWebServer] or
https://wiremock.org/[WireMock]. Mock web servers accept requests over HTTP like a regular
server, and that means you can test with the same HTTP client that is also configured in
the same way as in production, which is important because there are often subtle
differences in the way different clients handle network I/O. Another advantage of mock
web servers is the ability to simulate specific network issues and conditions at the
transport level, in combination with the client used in production.
In addition to dedicated mock web servers, historically the Spring Framework has provided
a built-in option to test `RestClient` or `RestTemplate` through `MockRestServiceServer`.
This relies on configuring the client under test with a custom `ClientHttpRequestFactory`
backed by the mock server that is in turn set up to expect requests and send "`stub`"
responses so that you can focus on testing the code in isolation, without running a server.
TIP: `MockRestServiceServer` predates the existence of mock web servers. At present, we
recommend using mock web servers for more complete testing of the transport layer and
network conditions.
The following example shows an example of using `MockRestServiceServer`:
[tabs]
======

View File

@ -9,11 +9,11 @@ deal of importance on convention over configuration, with reasonable defaults th
can override through annotation-based configuration.
In addition to generic testing infrastructure, the TestContext framework provides
explicit support for JUnit 4, JUnit Jupiter (AKA JUnit 5), and TestNG. For JUnit 4 and
TestNG, Spring provides `abstract` support classes. Furthermore, Spring provides a custom
JUnit `Runner` and custom JUnit `Rules` for JUnit 4 and a custom `Extension` for JUnit
Jupiter that let you write so-called POJO test classes. POJO test classes are not
required to extend a particular class hierarchy, such as the `abstract` support classes.
explicit support for JUnit Jupiter, JUnit 4, and TestNG. For JUnit 4 and TestNG, Spring
provides `abstract` support classes. Furthermore, Spring provides a custom JUnit `Runner`
and custom JUnit `Rules` for JUnit 4 and a custom `Extension` for JUnit Jupiter that let
you write so-called POJO test classes. POJO test classes are not required to extend a
particular class hierarchy, such as the `abstract` support classes.
The following section provides an overview of the internals of the TestContext framework.
If you are interested only in using the framework and are not interested in extending it

View File

@ -250,7 +250,7 @@ Java::
@SqlGroup({
@Sql(scripts = "/test-schema.sql", config = @SqlConfig(commentPrefix = "`")),
@Sql("/test-user-data.sql")
)}
})
void userTest() {
// run code that uses the test schema and test data
}

View File

@ -172,7 +172,7 @@ shows this configuration:
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<bean id="sessionFactory" class="org.springframework.orm.jpa.hibernate.LocalSessionFactoryBean">
<!-- configuration elided for brevity -->
</bean>

View File

@ -9,7 +9,7 @@ in JUnit and TestNG.
== SpringExtension for JUnit Jupiter
The Spring TestContext Framework offers full integration with the JUnit Jupiter testing
framework, introduced in JUnit 5. By annotating test classes with
framework, originally introduced in JUnit 5. By annotating test classes with
`@ExtendWith(SpringExtension.class)`, you can implement standard JUnit Jupiter-based unit
and integration tests and simultaneously reap the benefits of the TestContext framework,
such as support for loading application contexts, dependency injection of test instances,
@ -72,8 +72,8 @@ Kotlin::
----
======
Since you can also use annotations in JUnit 5 as meta-annotations, Spring provides the
`@SpringJUnitConfig` and `@SpringJUnitWebConfig` composed annotations to simplify the
Since you can also use annotations in JUnit Jupiter as meta-annotations, Spring provides
the `@SpringJUnitConfig` and `@SpringJUnitWebConfig` composed annotations to simplify the
configuration of the test `ApplicationContext` and JUnit Jupiter.
The following example uses `@SpringJUnitConfig` to reduce the amount of configuration
@ -485,7 +485,8 @@ Kotlin::
[WARNING]
====
JUnit 4 support is deprecated since Spring Framework 7.0 in favor of the
JUnit 4 is officially in maintenance mode, and JUnit 4 support in Spring is deprecated
since Spring Framework 7.0 in favor of the
xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit-jupiter-extension[`SpringExtension`]
and JUnit Jupiter.
====
@ -547,7 +548,8 @@ be configured through `@ContextConfiguration`.
[WARNING]
====
JUnit 4 support is deprecated since Spring Framework 7.0 in favor of the
JUnit 4 is officially in maintenance mode, and JUnit 4 support in Spring is deprecated
since Spring Framework 7.0 in favor of the
xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit-jupiter-extension[`SpringExtension`]
and JUnit Jupiter.
====
@ -623,9 +625,10 @@ Kotlin::
[WARNING]
====
JUnit 4 support is deprecated since Spring Framework 7.0 in favor of the
JUnit 4 is officially in maintenance mode, and JUnit 4 support in Spring is deprecated
since Spring Framework 7.0 in favor of the
xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit-jupiter-extension[`SpringExtension`]
for JUnit Jupiter.
and JUnit Jupiter.
====
The `org.springframework.test.context.junit4` package provides the following support

View File

@ -265,6 +265,7 @@ Java::
client = WebTestClient.bindToController(new TestController())
.configureClient()
.baseUrl("/test")
.apiVersionInserter(ApiVersionInserter.fromHeader("API-Version").build())
.build();
----
@ -275,6 +276,7 @@ Kotlin::
client = WebTestClient.bindToController(TestController())
.configureClient()
.baseUrl("/test")
.apiVersionInserter(ApiVersionInserter.fromHeader("API-Version").build())
.build()
----
======

View File

@ -0,0 +1,102 @@
[[webflux-versioning]]
= API Versioning
:page-section-summary-toc: 1
[.small]#xref:web/webmvc-versioning.adoc[See equivalent in the Servlet stack]#
Spring WebFlux supports API versioning. This section provides an overview of the support
and underlying strategies.
Please, see also related content in:
- Configure xref:web/webflux/config.adoc#webflux-config-api-version[API versioning]
in the WebFlux Config
- xref:web/webflux/controller/ann-requestmapping.adoc#webflux-ann-requestmapping-version[Map requests]
to annotated controller methods with an API version
Client support for API versioning is available also in `RestClient`, `WebClient`, and
xref:integration/rest-clients.adoc#rest-http-interface[HTTP Service] clients, as well as
for testing in `WebTestClient`.
[[webflux-versioning-strategy]]
== ApiVersionStrategy
[.small]#xref:web/webmvc-versioning.adoc#mvc-versioning-strategy[See equivalent in the Servlet stack]#
This is the central strategy for API versioning that holds all configured preferences
related to versioning. It does the following:
- Resolves versions from the requests via xref:#webflux-versioning-resolver[ApiVersionResolver]
- Parses raw version values into `Comparable<?>` with xref:#webflux-versioning-parser[ApiVersionParser]
- xref:#webflux-versioning-validation[Validates] request versions
`ApiVersionStrategy` helps to map requests to `@RequestMapping` controller methods,
and is initialized by the WebFlux config. Typically, applications do not interact
directly with it.
[[webflux-versioning-resolver]]
== ApiVersionResolver
[.small]#xref:web/webmvc-versioning.adoc#mvc-versioning-resolver[See equivalent in the Servlet stack]#
This strategy resolves the API version from a request. The WebFlux config provides built-in
options to resolve from a header, a request parameter, or from the URL path.
You can also use a custom `ApiVersionResolver`.
[[webflux-versioning-parser]]
== ApiVersionParser
[.small]#xref:web/webmvc-versioning.adoc#mvc-versioning-parser[See equivalent in the Servlet stack]#
This strategy helps to parse raw version values into `Comparable<?>`, which helps to
compare, sort, and select versions. By default, the built-in `SemanticApiVersionParser`
parses a version into `major`, `minor`, and `patWebFluxch` integer values. Minor and patch
values are set to 0 if not present.
[[webflux-versioning-validation]]
== Validation
[.small]#xref:web/webmvc-versioning.adoc#mvc-versioning-validation[See equivalent in the Servlet stack]#
If a request version is not supported, `InvalidApiVersionException` is raised resulting
in a 400 response. By default, the list of supported versions is initialized from declared
versions in annotated controller mappings, but you can turn that off through a flag in the
WebFlux config, and use only the versions configured explicitly in the config.
By default, a version is required when API versioning is enabled, and
`MissingApiVersionException` is raised resulting in a 400 response if not present.
You can make it optional in which case the most recent version is used.
You can also specify a default version to use.
[[webflux-versioning-deprecation-handler]]
== ApiVersionDeprecationHandler
[.small]#xref:web/webmvc-versioning.adoc#mvc-versioning-deprecation-handler[See equivalent in the Reactive stack]#
This strategy can be configured to send hints and information about deprecated versions to
clients via response headers. The built-in `StandardApiVersionDeprecationHandler`
can set the "Deprecation" "Sunset" headers and "Link" headers as defined in
https://datatracker.ietf.org/doc/html/rfc9745[RFC 9745] and
https://datatracker.ietf.org/doc/html/rfc8594[RFC 8594]. You can also configure a custom
handler for different headers.
[[webflux-versioning-mapping]]
== Request Mapping
[.small]#xref:web/webmvc-versioning.adoc#mvc-versioning-mapping[See equivalent in the Servlet stack]#
`ApiVersionStrategy` supports the mapping of requests to annotated controller methods.
See xref:web/webflux/controller/ann-requestmapping.adoc#webflux-ann-requestmapping-version[API Versions]
for more details.

View File

@ -1,7 +1,7 @@
[[webflux-client-builder]]
= Configuration
The simplest way to create a `WebClient` is through one of the static factory methods:
The simplest way to create `WebClient` is through one of the static factory methods:
* `WebClient.create()`
* `WebClient.create(String baseUrl)`
@ -12,10 +12,12 @@ You can also use `WebClient.builder()` with further options:
* `defaultUriVariables`: default values to use when expanding URI templates.
* `defaultHeader`: Headers for every request.
* `defaultCookie`: Cookies for every request.
* `defaultApiVersion`: API version for every request.
* `defaultRequest`: `Consumer` to customize every request.
* `filter`: Client filter for every request.
* `exchangeStrategies`: HTTP message reader/writer customizations.
* `clientConnector`: HTTP client library settings.
* `apiVersionInserter`: to insert API version values in the request
* `observationRegistry`: the registry to use for enabling xref:integration/observability.adoc#http-client.webclient[Observability support].
* `observationConvention`: xref:integration/observability.adoc#config[an optional, custom convention to extract metadata] for recorded observations.

View File

@ -2,9 +2,16 @@
= Testing
:page-section-summary-toc: 1
To test code that uses the `WebClient`, you can use a mock web server, such as the
https://github.com/square/okhttp#mockwebserver[OkHttp MockWebServer]. To see an example
of its use, check out
To test code that uses the `WebClient`, you can use a mock web server, such as
https://github.com/square/okhttp#mockwebserver[OkHttp MockWebServer] or
https://wiremock.org/[WireMock]. Mock web servers accept requests over HTTP like a regular
server, and that means you can test with the same HTTP client that is also configured in
the same way as in production, which is important because there are often subtle
differences in the way different clients handle network I/O. Another advantage of mock
web servers is the ability to simulate specific network issues and conditions at the
transport level, in combination with the client used in production.
For example use of MockWebServer, see
{spring-framework-code}/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java[`WebClientIntegrationTests`]
in the Spring Framework test suite or the
https://github.com/square/okhttp/tree/master/samples/static-server[`static-server`]

View File

@ -686,6 +686,74 @@ reliance on it.
[[webflux-config-api-version]]
== API Version
[.small]#xref:web/webmvc/mvc-config/api-version.adoc[See equivalent in the Servlet stack]#
To enable API versioning, use the `ApiVersionConfigurer` callback of `WebFluxConfigurer`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim"]
----
@Configuration
public class WebConfiguration implements WebFluxConfigurer {
@Override
public void configureApiVersioning(ApiVersionConfigurer configurer) {
configurer.useRequestHeader("X-API-Version");
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
----
@Configuration
class WebConfiguration : WebMvcConfigurer {
override fun configureApiVersioning(configurer: ApiVersionConfigurer) {
configurer.useRequestHeader("X-API-Version")
}
}
----
======
You can resolve the version through one of the built-in options listed below, or
alternatively use a custom `ApiVersionResolver`:
- Request header
- Request parameter
- Path segment
- Media type parameter
TIP: When using a path segment, consider configuring a shared path prefix externally
in xref:web/webmvc/mvc-config/path-matching.adoc[Path Matching] options.
By default, the version is parsed with `SemanticVersionParser`, but you can also configure
a custom xref:web/webflux-versioning.adoc#webflux-versioning-parser[ApiVersionParser].
Supported versions are transparently detected from versions declared in request mappings
for convenience, but you can turn that off through a flag in the WebFlux config, and
consider only the versions configured explicitly in the config as supported.
Requests with a version that is not supported are rejected with
`InvalidApiVersionException` resulting in a 400 response.
You can set an `ApiVersionDeprecationHandler` to send information about deprecated
versions to clients. The built-in standard handler can set "Deprecation", "Sunset", and
"Link" headers based on https://datatracker.ietf.org/doc/html/rfc9745[RFC 9745] and
https://datatracker.ietf.org/doc/html/rfc8594[RFC 8594].
Once API versioning is configured, you can begin to map requests to
xref:web/webflux/controller/ann-requestmapping.adoc#webflux-ann-requestmapping-version[controller methods]
according to the request version.
[[webflux-config-blocking-execution]]
== Blocking Execution

View File

@ -19,7 +19,7 @@ Java::
private String name;
private MultipartFile file;
private FilePart file;
// ...
@ -42,7 +42,7 @@ Kotlin::
----
class MyForm(
val name: String,
val file: MultipartFile)
val file: FilePart)
@Controller
class FileUploadController {

View File

@ -34,11 +34,7 @@ Controllers can then return a `Flux<List<B>>`; Reactor provides a dedicated oper
| `HttpHeaders`
| For returning a response with headers and no body.
| `ErrorResponse`
| To render an RFC 9457 error response with details in the body,
see xref:web/webflux/ann-rest-exceptions.adoc[Error Responses]
| `ProblemDetail`
| `ErrorResponse`, `ProblemDetail`
| To render an RFC 9457 error response with details in the body,
see xref:web/webflux/ann-rest-exceptions.adoc[Error Responses]

View File

@ -234,8 +234,8 @@ Kotlin::
--
URI path patterns can also have embedded `${...}` placeholders that are resolved on startup
through `PropertySourcesPlaceholderConfigurer` against local, system, environment, and
other property sources. You can use this to, for example, parameterize a base URL based on
by using `PropertySourcesPlaceholderConfigurer` against local, system, environment, and
other property sources. You can use this, for example, to parameterize a base URL based on
some external configuration.
NOTE: Spring WebFlux uses `PathPattern` and the `PathPatternParser` for URI path matching support.
@ -408,6 +408,86 @@ Kotlin::
======
[[webflux-ann-requestmapping-version]]
== API Version
[.small]#xref:web/webmvc/mvc-controller/ann-requestmapping.adoc#mvc-ann-requestmapping-version[See equivalent in the Servlet stack]#
There is no standard way to specify an API version, so when you enable API versioning
in the xref:web/webflux/config.adoc#webflux-config-api-version[WebFlux Config] you need
to specify how to resolve the version. The WebFlux Config creates an
xref:web/webflux-versioning.adoc#webflux-versioning-strategy[ApiVersionStrategy] that in turn
is used to map requests.
Once API versioning is enabled, you can begin to map requests with versions.
The `@RequestMapping` `version` attribute supports the following:
- No value -- matches any version
- Fixed version ("1.2") -- matches the given version only
- Baseline version ("1.2+") -- matches the given version and above
If multiple controller methods have a version less than or equal to the request version,
the highest of those, and closest to the request version, is the one considered,
in effect superseding the rest.
To illustrate this, consider the following mappings:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
@RestController
@RequestMapping("/account/{id}")
public class AccountController {
@GetMapping // <1>
public Account getAccount() {
}
@GetMapping(version = "1.1") // <2>
public Account getAccount1_1() {
}
@GetMapping(version = "1.2+") // <3>
public Account getAccount1_2() {
}
@GetMapping(version = "1.5") // <4>
public Account getAccount1_5() {
}
}
----
<1> match any version
<2> match version 1.1
<3> match version 1.2 and above
<4> match version 1.5
======
For request with version `"1.3"`:
- (1) matches as it matches any version
- (2) does not match
- (3) matches as it matches 1.2 and above, and is *chosen* as the highest match
- (4) is higher and does not match
For request with version `"1.5"`:
- (1) matches as it matches any version
- (2) does not match
- (3) matches as it matches 1.2 and above
- (4) matches and is *chosen* as the highest match
A request with version `"1.6"` does not have a match. (1) and (3) do match, but are
superseded by (4), which allows only a strict match, and therefore does not match.
In this scenario, a `NotAcceptableApiVersionException` results in a 400 response.
NOTE: The above assumes the request version is a
xref:web/webmvc/mvc-config/api-version.adoc["supported" version], or otherwise it
would fail xref:web/webflux-versioning.adoc#webflux-versioning-validation[Validation].
[[webflux-ann-requestmapping-head-options]]
== HTTP HEAD, OPTIONS

View File

@ -81,7 +81,7 @@ The following table describes server dependencies (also see
|jetty-server, jetty-servlet
|===
The code snippets below show using the `HttpHandler` adapters with each server API:
The code snippets below show using the `HttpHandler` adapters with each server API.
*Reactor Netty*
[tabs]
@ -176,17 +176,16 @@ Java::
[source,java,indent=0,subs="verbatim,quotes"]
----
HttpHandler handler = ...
Servlet servlet = new JettyHttpHandlerAdapter(handler);
JettyCoreHttpHandlerAdapter adapter = new JettyCoreHttpHandlerAdapter(handler);
Server server = new Server();
ServletContextHandler contextHandler = new ServletContextHandler(server, "");
contextHandler.addServlet(new ServletHolder(servlet), "/");
contextHandler.start();
server.setHandler(adapter);
ServerConnector connector = new ServerConnector(server);
connector.setHost(host);
connector.setPort(port);
server.addConnector(connector);
server.start();
----
@ -195,27 +194,27 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
val handler: HttpHandler = ...
val servlet = JettyHttpHandlerAdapter(handler)
val adapter = JettyCoreHttpHandlerAdapter(handler)
val server = Server()
val contextHandler = ServletContextHandler(server, "")
contextHandler.addServlet(ServletHolder(servlet), "/")
contextHandler.start();
server.setHandler(adapter)
val connector = ServerConnector(server)
connector.host = host
connector.port = port
server.addConnector(connector)
server.start()
----
======
*Servlet Container*
TIP: In Spring Framework 6.2, `JettyHttpHandlerAdapter` was deprecated in favor of
`JettyCoreHttpHandlerAdapter`, which integrates directly with Jetty 12 APIs
without a Servlet layer.
To deploy as a WAR to any Servlet container, you can extend and include
{spring-framework-api}/web/server/adapter/AbstractReactiveWebInitializer.html[`AbstractReactiveWebInitializer`]
in the WAR. That class wraps an `HttpHandler` with `ServletHttpHandlerAdapter` and registers
that as a `Servlet`.
To deploy as a WAR to a Servlet container instead, use
{spring-framework-api}/web/server/adapter/AbstractReactiveWebInitializer.html[`AbstractReactiveWebInitializer`],
to adapt `HttpHandler` to a `Servlet` via `ServletHttpHandlerAdapter`.
@ -800,4 +799,3 @@ Kotlin::
.build()
----
======

View File

@ -846,7 +846,7 @@ processing lifecycle and also (potentially) run side by side with annotated cont
any are declared. It is also how functional endpoints are enabled by the Spring Boot Web
starter.
The following example shows a WebFlux Java configuration:
The following example shows a WebMvc Java configuration:
[tabs]
======

View File

@ -0,0 +1,102 @@
[[mvc-versioning]]
= API Versioning
:page-section-summary-toc: 1
[.small]#xref:web/webflux-versioning.adoc[See equivalent in the Reactive stack]#
Spring MVC supports API versioning. This section provides an overview of the support
and underlying strategies.
Please, see also related content in:
- Configure xref:web/webmvc/mvc-config/api-version.adoc[API versioning] in the MVC Config
- xref:web/webmvc/mvc-controller/ann-requestmapping.adoc#mvc-ann-requestmapping-version[Map requests]
to annotated controller methods with an API version
Client support for API versioning is available also in `RestClient`, `WebClient`, and
xref:integration/rest-clients.adoc#rest-http-interface[HTTP Service] clients, as well as
for testing in MockMvc and `WebTestClient`.
[[mvc-versioning-strategy]]
== ApiVersionStrategy
[.small]#xref:web/webflux-versioning.adoc#webflux-versioning-strategy[See equivalent in the Reactive stack]#
This is the central strategy for API versioning that holds all configured preferences
related to versioning. It does the following:
- Resolves versions from the requests via xref:#mvc-versioning-resolver[ApiVersionResolver]
- Parses raw version values into `Comparable<?>` with an xref:#mvc-versioning-parser[ApiVersionParser]
- xref:#mvc-versioning-validation[Validates] request versions
- Sends deprecation hints in the responses
`ApiVersionStrategy` helps to map requests to `@RequestMapping` controller methods,
and is initialized by the MVC config. Typically, applications do not interact
directly with it.
[[mvc-versioning-resolver]]
== ApiVersionResolver
[.small]#xref:web/webflux-versioning.adoc#webflux-versioning-resolver[See equivalent in the Reactive stack]#
This strategy resolves the API version from a request. The MVC config provides built-in
options to resolve from a header, from a request parameter, or from the URL path.
You can also use a custom `ApiVersionResolver`.
[[mvc-versioning-parser]]
== ApiVersionParser
[.small]#xref:web/webflux-versioning.adoc#webflux-versioning-parser[See equivalent in the Reactive stack]#
This strategy helps to parse raw version values into `Comparable<?>`, which helps to
compare, sort, and select versions. By default, the built-in `SemanticApiVersionParser`
parses a version into `major`, `minor`, and `patch` integer values. Minor and patch
values are set to 0 if not present.
[[mvc-versioning-validation]]
== Validation
[.small]#xref:web/webflux-versioning.adoc#webflux-versioning-validation[See equivalent in the Reactive stack]#
If a request version is not supported, `InvalidApiVersionException` is raised resulting
in a 400 response. By default, the list of supported versions is initialized from declared
versions in annotated controller mappings, but you can turn that off through a flag in the
MVC config, and use only the versions configured explicitly in the config.
By default, a version is required when API versioning is enabled, and
`MissingApiVersionException` is raised resulting in a 400 response if not present.
You can make it optional in which case the most recent version is used.
You can also specify a default version to use.
[[mvc-versioning-deprecation-handler]]
== ApiVersionDeprecationHandler
[.small]#xref:web/webflux-versioning.adoc#webflux-versioning-deprecation-handler[See equivalent in the Reactive stack]#
This strategy can be configured to send hints and information about deprecated versions to
clients via response headers. The built-in `StandardApiVersionDeprecationHandler`
can set the "Deprecation" "Sunset" headers and "Link" headers as defined in
https://datatracker.ietf.org/doc/html/rfc9745[RFC 9745] and
https://datatracker.ietf.org/doc/html/rfc8594[RFC 8594]. You can also configure a custom
handler for different headers.
[[mvc-versioning-mapping]]
== Request Mapping
[.small]#xref:web/webflux-versioning.adoc#webflux-versioning-mapping[See equivalent in the Reactive stack]#
`ApiVersionStrategy` supports the mapping of requests to annotated controller methods.
See xref:web/webmvc/mvc-controller/ann-requestmapping.adoc#mvc-ann-requestmapping-version[API Version]
for more details.

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

@ -0,0 +1,37 @@
[[mvc-config-api-version]]
= API Version
[.small]#xref:web/webflux/config.adoc#webflux-config-api-version[See equivalent in the Reactive stack]#
To enable API versioning, use the `ApiVersionConfigurer` callback of `WebMvcConfigurer`:
include-code::./WebConfiguration[tag=snippet,indent=0]
You can resolve the version through one of the built-in options listed below, or
alternatively use a custom `ApiVersionResolver`:
- Request header
- Request parameter
- Path segment
- Media type parameter
TIP: When using a path segment, consider configuring a shared path prefix externally
in xref:web/webmvc/mvc-config/path-matching.adoc[Path Matching] options.
By default, the version is parsed with `SemanticVersionParser`, but you can also configure
a custom xref:web/webmvc-versioning.adoc#mvc-versioning-parser[ApiVersionParser].
Supported versions are transparently detected from versions declared in request mappings
for convenience, but you can turn that off through a flag in the MVC config, and
consider only the versions configured explicitly in the config as supported.
Requests with a version that is not supported are rejected with
`InvalidApiVersionException` resulting in a 400 response.
You can set an `ApiVersionDeprecationHandler` to send information about deprecated
versions to clients. The built-in standard handler can set "Deprecation", "Sunset", and
"Link" headers based on https://datatracker.ietf.org/doc/html/rfc9745[RFC 9745] and
https://datatracker.ietf.org/doc/html/rfc8594[RFC 8594].
Once API versioning is configured, you can begin to map requests to
xref:web/webmvc/mvc-controller/ann-requestmapping.adoc#mvc-ann-requestmapping-version[controller methods]
according to the request version.

View File

@ -10,18 +10,20 @@ to any controller. Moreover, as of 5.3, `@ExceptionHandler` methods in `@Control
can be used to handle exceptions from any `@Controller` or any other handler.
`@ControllerAdvice` is meta-annotated with `@Component` and therefore can be registered as
a Spring bean through xref:core/beans/java/instantiating-container.adoc#beans-java-instantiating-container-scan[component scanning]
. `@RestControllerAdvice` is meta-annotated with `@ControllerAdvice`
and `@ResponseBody`, and that means `@ExceptionHandler` methods will have their return
value rendered via response body message conversion, rather than via HTML views.
a Spring bean through xref:core/beans/java/instantiating-container.adoc#beans-java-instantiating-container-scan[component scanning].
`@RestControllerAdvice` is a shortcut annotation that combines `@ControllerAdvice`
with `@ResponseBody`, in effect simply an `@ControllerAdvice` whose exception handler
methods render to the response body.
On startup, `RequestMappingHandlerMapping` and `ExceptionHandlerExceptionResolver` detect
controller advice beans and apply them at runtime. Global `@ExceptionHandler` methods,
from an `@ControllerAdvice`, are applied _after_ local ones, from the `@Controller`.
By contrast, global `@ModelAttribute` and `@InitBinder` methods are applied _before_ local ones.
The `@ControllerAdvice` annotation has attributes that let you narrow the set of controllers
and handlers that they apply to. For example:
By default, both `@ControllerAdvice` and `@RestControllerAdvice` apply to any controller,
including `@Controller` and `@RestController`. Use attributes of the annotation to narrow
the set of controllers and handlers that they apply to. For example:
[tabs]
======

View File

@ -177,13 +177,9 @@ the content negotiation during the error handling phase will decide which conten
be converted through `HttpMessageConverter` instances and written to the response.
See xref:web/webmvc/mvc-controller/ann-methods/responseentity.adoc[ResponseEntity].
| `ErrorResponse`
| `ErrorResponse`, `ProblemDetail`
| To render an RFC 9457 error response with details in the body,
see xref:web/webmvc/mvc-ann-rest-exceptions.adoc[Error Responses]
| `ProblemDetail`
| To render an RFC 9457 error response with details in the body,
see xref:web/webmvc/mvc-ann-rest-exceptions.adoc[Error Responses]
see xref:web/webmvc/mvc-ann-rest-exceptions.adoc[Error Responses]
| `String`
| A view name to be resolved with `ViewResolver` implementations and used together with the

View File

@ -243,7 +243,7 @@ Kotlin::
======
If there is no `BindingResult` parameter after the `@ModelAttribute`, then
`MethodArgumentNotValueException` is raised with the validation errors. However, if method
a `MethodArgumentNotValidException` is raised with the validation errors. However, if method
validation applies because other parameters have `@jakarta.validation.Constraint` annotations,
then `HandlerMethodValidationException` is raised instead. For more details, see the section
xref:web/webmvc/mvc-controller/ann-validation.adoc[Validation].

View File

@ -22,11 +22,7 @@ supported for all return values.
| `HttpHeaders`
| For returning a response with headers and no body.
| `ErrorResponse`
| To render an RFC 9457 error response with details in the body,
see xref:web/webmvc/mvc-ann-rest-exceptions.adoc[Error Responses]
| `ProblemDetail`
| `ErrorResponse`, `ProblemDetail`
| To render an RFC 9457 error response with details in the body,
see xref:web/webmvc/mvc-ann-rest-exceptions.adoc[Error Responses]

View File

@ -429,6 +429,87 @@ xref:web/webmvc/mvc-controller/ann-requestmapping.adoc#mvc-ann-requestmapping-co
instead.
[[mvc-ann-requestmapping-version]]
== API Version
[.small]#xref:web/webflux/controller/ann-requestmapping.adoc#webflux-ann-requestmapping-version[See equivalent in the Reactive stack]#
There is no standard way to specify an API version, so when you enable API versioning
in the xref:web/webmvc/mvc-config/api-version.adoc[MVC Config] you need
to specify how to resolve the version. The MVC Config creates an
xref:web/webmvc-versioning.adoc#mvc-versioning-strategy[ApiVersionStrategy] that in turn
is used to map requests.
Once API versioning is enabled, you can begin to map requests with versions.
The `@RequestMapping` `version` attribute supports the following:
- No value -- matches any version
- Fixed version ("1.2") -- matches the given version only
- Baseline version ("1.2+") -- matches the given version and above
If multiple controller methods have a version less than or equal to the request version,
the highest of those, and closest to the request version, is the one considered,
in effect superseding the rest.
To illustrate this, consider the following mappings:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
@RestController
@RequestMapping("/account/{id}")
public class AccountController {
@GetMapping // <1>
public Account getAccount() {
}
@GetMapping(version = "1.1") // <2>
public Account getAccount1_1() {
}
@GetMapping(version = "1.2+") // <3>
public Account getAccount1_2() {
}
@GetMapping(version = "1.5") // <4>
public Account getAccount1_5() {
}
}
----
<1> match any version
<2> match version 1.1
<3> match version 1.2 and above
<4> match version 1.5
======
For request with version `"1.3"`:
- (1) matches as it matches any version
- (2) does not match
- (3) matches as it matches 1.2 and above, and is *chosen* as the highest match
- (4) is higher and does not match
For request with version `"1.5"`:
- (1) matches as it matches any version
- (2) does not match
- (3) matches as it matches 1.2 and above
- (4) matches and is *chosen* as the highest match
A request with version `"1.6"` does not have a match. (1) and (3) do match, but are
superseded by (4), which allows only a strict match, and therefore does not match.
In this scenario, a `NotAcceptableApiVersionException` results in a 400 response.
NOTE: The above assumes the request version is a
xref:web/webmvc/mvc-config/api-version.adoc["supported" version], or otherwise it
would fail xref:web/webmvc-versioning.adoc#mvc-versioning-validation[Validation].
[[mvc-ann-requestmapping-head-options]]
== HTTP HEAD, OPTIONS
[.small]#xref:web/webflux/controller/ann-requestmapping.adoc#webflux-ann-requestmapping-head-options[See equivalent in the Reactive stack]#

View File

@ -38,7 +38,7 @@ to inform the server that the original port was `443`.
==== X-Forwarded-Proto
While not standard, https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto[`X-Forwarded-Proto: (https|http)`]
is a de-facto standard header that is used to communicate the original protocol (for example, https / https)
is a de-facto standard header that is used to communicate the original protocol (for example, https / http)
to a downstream server. For example, if a request of `https://example.com/resource` is sent to
a proxy which forwards the request to `http://localhost:8080/resource`, then a header of
`X-Forwarded-Proto: https` can be sent to inform the server that the original protocol was `https`.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present 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.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present 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.

Some files were not shown because too many files have changed in this diff Show More