Compare commits

..

1 Commits

Author SHA1 Message Date
Stéphane Nicoll debd312d50 Release v6.2.3 2025-02-13 13:39:03 +01:00
115 changed files with 359 additions and 1680 deletions
+1 -2
View File
@@ -42,8 +42,7 @@ spring-*/src/main/java/META-INF/MANIFEST.MF
*.iml
*.ipr
*.iws
.idea/*
!.idea/icon.svg
.idea
out
test-output
atlassian-ide-plugin.xml
-52
View File
@@ -1,52 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 510 510" style="enable-background:new 0 0 510 510;" xml:space="preserve">
<style type="text/css">
.st0{fill:#6DB33F;}
</style>
<title>icon-framework</title>
<g id="Layer_2_1_">
<g id="Layer_1-2">
<path class="st0" d="M479.2,162.5c-5.6-34.3-20.5-64.4-43.9-87.8S382,36.5,347.6,30.9C320.1,11.2,288.5,0,255,0
s-65.1,11.2-92.6,30.9C128,36.5,98,51.3,74.7,74.7C50.9,98.4,36.5,128.7,31,162.2C11.2,189.8,0,221.4,0,255s11.2,65.2,31,92.8
c5.5,33.5,19.9,63.8,43.7,87.5c23.3,23.3,53.3,38.2,87.7,43.8C190,498.8,221.5,510,255,510s65-11.2,92.6-30.9
c34.4-5.6,64.4-20.5,87.7-43.8c23.8-23.8,38.1-54.1,43.7-87.5c19.8-27.6,31-59.2,31-92.8S498.8,190,479.2,162.5z M473,224.2
c-0.8,6-1.9,11.9-3.2,17.9c-7.2-22.4-17.1-43.8-29.4-63.8c-5.5-22.8-13.6-44.9-24.4-65.8c22.9,14.8,42.2,32.6,56.7,52.6
C475.7,184.6,475.8,204.6,473,224.2z M444.7,199.6c9.3,17.7,16.6,36.3,21.9,55.6c-5.3,19.3-12.7,37.9-22,55.6
C450.9,274,451,236.4,444.7,199.6L444.7,199.6z M175.4,341.1c6.5-4.5,13.7-7.8,21.3-9.8c32.2-7.5,46.7-9.9,66.3-18.4
c36.8-15.9,74.8-53.3,84.6-94c-16.1,38.8-59.5,70.2-97.9,81.6c-26.2,7.8-79.8,13.6-80.1,13.6l-1.4-1.2
c-30.6-17.4-27-85.8,31.8-104.1c25.7-8,55.9-0.7,83.5-5.4c29.4-5,64.4-24.5,79.9-52c12.3,46.4,26.1,118.5-9.7,160.2
C309.4,363,211.8,318,175.4,341.1z M310.8,444.6c-17.7,9.4-36.5,16.8-55.8,22.1c-19.4-5.3-38.1-12.7-55.8-22.1
C236.1,450.9,273.9,450.9,310.8,444.6z M117.6,392.4c13.1,13.1,27.3,25.1,42.3,35.8c-19-5.9-37.2-13.8-54.5-23.7
c-9.9-17.3-17.8-35.7-23.7-54.7C92.4,365.1,104.5,379.3,117.6,392.4z M81.7,160.1c5.9-19,13.9-37.4,23.7-54.7
c17.3-9.8,35.5-17.8,54.5-23.7c-15.1,10.8-29.2,22.7-42.3,35.8C104.5,130.7,92.4,144.9,81.7,160.1z M149.1,346.4
c-8.5,0.2-15.5-6.5-15.7-15c-0.2-8.5,6.5-15.5,15-15.7c8.5-0.2,15.5,6.5,15.7,15c0,0,0,0.1,0,0.1
C164.2,339.3,157.5,346.2,149.1,346.4L149.1,346.4z M199.2,65.4c17.7-9.4,36.5-16.8,55.8-22.1c19.4,5.3,38.1,12.7,55.8,22.1
C273.9,59.1,236.1,59.1,199.2,65.4L199.2,65.4z M392.4,392.4c13.1-13.1,25.1-27.3,35.9-42.4c-5.9,19-13.9,37.3-23.7,54.5
c-17.3,9.8-35.5,17.7-54.5,23.7C365.2,417.5,379.3,405.5,392.4,392.4L392.4,392.4z M392.4,117.6c-13.1-13.1-27.2-25.1-42.3-35.8
c19,5.9,37.2,13.9,54.5,23.7c9.8,17.3,17.8,35.6,23.7,54.7C417.6,144.9,405.5,130.7,392.4,117.6L392.4,117.6z M430.5,79.5
c19.4,19.3,32.6,43.6,39.3,71.1c-16.2-19.2-36.6-36.2-60.4-50c-13.8-23.8-30.8-44.2-50-60.4C387.1,46.9,411.2,60.1,430.5,79.5
L430.5,79.5z M397.6,94c-20.8-10.7-42.8-18.8-65.6-24.2c-20-12.4-41.5-22.4-63.9-29.6c15.5-3.4,31.3-5.1,47.2-5.2
c10,0,19.9,0.8,29.7,2.3C365.1,51.8,382.8,71.1,397.6,94z M255,6.8c27.6,0,53.8,8,77.4,22.2c-5.6-0.5-11.3-0.7-17.1-0.7
c-20.4,0.1-40.6,2.8-60.3,8.1c-19.7-5.3-39.9-8-60.3-8.1c-5.8,0-11.5,0.2-17.1,0.7C201.2,14.7,227.4,6.8,255,6.8z M165,37.3
c9.8-1.5,19.8-2.3,29.7-2.3c15.9,0.1,31.7,1.8,47.2,5.2C219.5,47.4,198,57.3,178,69.8c-22.7,5.4-44.8,13.6-65.6,24.2
C127.2,71.1,144.9,51.8,165,37.3z M79.5,79.5c19.3-19.3,43.5-32.6,71.1-39.3c-19.2,16.2-36.2,36.6-50,60.4
c-23.7,13.8-44,30.6-60.2,49.8C47,123.4,59.8,99.1,79.5,79.5z M37.4,164.9c14.6-20,33.8-37.7,56.6-52.4
c-10.7,20.9-18.8,43-24.3,65.8c-12.3,20-22.2,41.4-29.4,63.8C34.6,215.4,33.5,189.3,37.4,164.9z M65.4,310.4
c-9.3-17.6-16.6-36.2-21.9-55.4c5.3-19.2,12.6-37.8,21.9-55.4C59.1,236.3,59.1,273.7,65.4,310.4L65.4,310.4z M29.2,332.8
c-14.4-23.7-22.4-50-22.4-77.8s8.1-54.1,22.4-77.8c-2.1,25,0.4,51.3,7.4,77.8C29.5,281.5,27,307.8,29.2,332.8z M40.3,268
c7.2,22.3,17.1,43.7,29.4,63.7c5.5,22.8,13.6,44.9,24.3,65.8c-22.8-14.7-42-32.5-56.6-52.4C33.5,320.7,34.6,294.6,40.3,268z
M79.5,430.5c-19.6-19.6-32.5-43.9-39.1-70.9c16.1,19.1,36.5,36,60.2,49.8c13.8,23.7,30.8,44.2,50,60.4
C122.9,463.1,98.8,449.9,79.5,430.5z M112.5,416c20.8,10.7,42.8,18.8,65.5,24.2c20,12.4,41.5,22.4,63.9,29.6
c-15.5,3.4-31.3,5.1-47.2,5.1c-10,0-19.9-0.8-29.7-2.3C144.9,458.1,127.2,438.9,112.5,416L112.5,416z M255,503.2
c-27.6,0-53.8-8-77.4-22.2c5.6,0.5,11.3,0.7,17.1,0.7c20.4-0.1,40.6-2.8,60.3-8.1c19.7,5.3,39.9,8,60.3,8.1
c5.8,0,11.5-0.2,17.1-0.7C308.8,495.3,282.6,503.2,255,503.2z M345,472.7c-9.8,1.5-19.8,2.3-29.7,2.3c-15.9-0.1-31.7-1.8-47.2-5.1
c22.4-7.2,43.9-17.2,63.9-29.6c22.7-5.4,44.8-13.6,65.6-24.2C382.8,438.9,365.1,458.1,345,472.7z M430.5,430.5
c-19.3,19.3-43.5,32.6-71.1,39.3c19.2-16.2,36.2-36.6,50-60.4c23.7-13.8,44-30.6,60.2-49.8C463.1,386.6,450.1,410.9,430.5,430.5
L430.5,430.5z M472.6,345.1c-14.6,20-33.8,37.7-56.6,52.4c10.7-20.8,18.8-42.8,24.3-65.6c12.3-20,22.2-41.3,29.5-63.7
C475.4,294.7,476.5,320.8,472.6,345.1L472.6,345.1z M473.5,255.3c2.6-9.9,4.7-20,6.1-30.1c2.2-15.7,2.7-31.7,1.4-47.5
c14.2,23.6,22.2,49.8,22.2,77.4s-8.1,54.1-22.4,77.8C482.9,307.9,480.5,281.6,473.5,255.3L473.5,255.3z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.8 KiB

+2 -2
View File
@@ -9,7 +9,7 @@ plugins {
id 'io.github.goooler.shadow' version '8.1.8' apply false
id 'me.champeau.jmh' version '0.7.2' apply false
id 'me.champeau.mrjar' version '0.1.1'
id "net.ltgt.errorprone" version "4.1.0" apply false
id "net.ltgt.errorprone" version "3.1.0" apply false
}
ext {
@@ -102,7 +102,7 @@ configure([rootProject] + javaProjects) { project ->
// TODO Uncomment link to JUnit 5 docs once we execute Gradle with Java 18+.
// See https://github.com/spring-projects/spring-framework/issues/27497
//
// "https://junit.org/junit5/docs/5.12.0/api/",
// "https://junit.org/junit5/docs/5.11.4/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/",
@@ -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.21.4");
checkstyle.setToolVersion("10.21.2");
checkstyle.getConfigDirectory().set(project.getRootProject().file("src/checkstyle"));
String version = SpringJavaFormatPlugin.class.getPackage().getImplementationVersion();
DependencySet checkstyleDependencies = project.getConfigurations().getByName("checkstyle").getDependencies();
@@ -64,7 +64,7 @@ public class CheckstyleConventions {
NoHttpExtension noHttp = project.getExtensions().getByType(NoHttpExtension.class);
noHttp.setAllowlistFile(project.file("src/nohttp/allowlist.lines"));
noHttp.getSource().exclude("**/test-output/**", "**/.settings/**",
"**/.classpath", "**/.project", "**/.gradle/**", "**/node_modules/**", "buildSrc/build/**");
"**/.classpath", "**/.project", "**/.gradle/**", "**/node_modules/**");
List<String> buildFolders = List.of("bin", "build", "out");
project.allprojects(subproject -> {
Path rootPath = project.getRootDir().toPath();
@@ -444,9 +444,9 @@ through Java 8's `java.util.Optional`, as the following example shows:
}
----
You can also use a parameter-level `@Nullable` annotation (of any kind in any package --
for example, `javax.annotation.Nullable` from JSR-305) or just leverage Kotlin built-in
null-safety support:
You can also use a `@Nullable` annotation (of any kind in any package -- for example,
`javax.annotation.Nullable` from JSR-305) or just leverage Kotlin built-in null-safety
support:
[tabs]
======
@@ -477,13 +477,6 @@ Kotlin::
----
======
[NOTE]
====
A type-level `@Nullable` annotation such as from JSpecify is not supported in Spring
Framework 6.2 yet. You need to upgrade to Spring Framework 7.0 where the framework
detects type-level annotations and consistently declares JSpecify in its own codebase.
====
You can also use `@Autowired` for interfaces that are well-known resolvable
dependencies: `BeanFactory`, `ApplicationContext`, `Environment`, `ResourceLoader`,
`ApplicationEventPublisher`, and `MessageSource`. These interfaces and their extended
@@ -857,7 +857,7 @@ Start by creating the interface with `@HttpExchange` methods:
[source,java,indent=0,subs="verbatim,quotes"]
----
public interface RepositoryService {
interface RepositoryService {
@GetExchange("/repos/{owner}/{repo}")
Repository getRepository(@PathVariable String owner, @PathVariable String repo);
@@ -908,7 +908,7 @@ For `RestTemplate`:
[source,java,indent=0,subs="verbatim,quotes"]
----
@HttpExchange(url = "/repos/{owner}/{repo}", accept = "application/vnd.github.v3+json")
public interface RepositoryService {
interface RepositoryService {
@GetExchange
Repository getRepository(@PathVariable String owner, @PathVariable String repo);
@@ -7,17 +7,16 @@ by default, exactly in the following order:
* `ServletTestExecutionListener`: Configures Servlet API mocks for a
`WebApplicationContext`.
* `DirtiesContextBeforeModesTestExecutionListener`: Handles the `@DirtiesContext`
annotation for "before" modes.
annotation for "`before`" modes.
* `ApplicationEventsTestExecutionListener`: Provides support for
xref:testing/testcontext-framework/application-events.adoc[`ApplicationEvents`].
* `BeanOverrideTestExecutionListener`: Provides support for
xref:testing/testcontext-framework/bean-overriding.adoc[].
* `BeanOverrideTestExecutionListener`: Provides support for xref:testing/testcontext-framework/bean-overriding.adoc[] .
* `DependencyInjectionTestExecutionListener`: Provides dependency injection for the test
instance.
* `MicrometerObservationRegistryTestExecutionListener`: Provides support for
Micrometer's `ObservationRegistry`.
* `DirtiesContextTestExecutionListener`: Handles the `@DirtiesContext` annotation for
"after" modes.
"`after`" modes.
* `CommonCachesTestExecutionListener`: Clears resource caches in the test's
`ApplicationContext` if necessary.
* `TransactionalTestExecutionListener`: Provides transactional test execution with
@@ -162,16 +161,15 @@ change from release to release -- for example, `SqlScriptsTestExecutionListener`
introduced in Spring Framework 4.1, and `DirtiesContextBeforeModesTestExecutionListener`
was introduced in Spring Framework 4.2. Furthermore, third-party frameworks like Spring
Boot and Spring Security register their own default `TestExecutionListener`
implementations by using the aforementioned
xref:testing/testcontext-framework/tel-config.adoc#testcontext-tel-config-automatic-discovery[automatic discovery mechanism].
implementations by using the aforementioned xref:testing/testcontext-framework/tel-config.adoc#testcontext-tel-config-automatic-discovery[automatic discovery mechanism]
.
To avoid having to be aware of and re-declare all default listeners, you can set the
`mergeMode` attribute of `@TestExecutionListeners` to `MergeMode.MERGE_WITH_DEFAULTS`.
`MERGE_WITH_DEFAULTS` indicates that locally declared listeners should be merged with the
default listeners. The merging algorithm ensures that duplicates are removed from the
list and that the resulting set of merged listeners is sorted according to the semantics
of `AnnotationAwareOrderComparator`, as described in
xref:testing/testcontext-framework/tel-config.adoc#testcontext-tel-config-ordering[Ordering `TestExecutionListener` Implementations].
of `AnnotationAwareOrderComparator`, as described in xref:testing/testcontext-framework/tel-config.adoc#testcontext-tel-config-ordering[Ordering `TestExecutionListener` Implementations].
If a listener implements `Ordered` or is annotated with `@Order`, it can influence the
position in which it is merged with the defaults. Otherwise, locally declared listeners
are appended to the list of default listeners when merged.
@@ -1,6 +1,5 @@
[[webflux-view]]
= View Technologies
[.small]#xref:web/webmvc-view.adoc[See equivalent in the Servlet stack]#
The rendering of views in Spring WebFlux is pluggable. Whether you decide to
@@ -1,7 +1,6 @@
[[mvc-view]]
= View Technologies
:page-section-summary-toc: 1
[.small]#xref:web/webflux-view.adoc[See equivalent in the Reactive stack]#
The rendering of views in Spring MVC is pluggable. Whether you decide to use
@@ -130,7 +130,6 @@ the `Configuration` object.
[[mvc-view-freemarker-forms]]
== Form Handling
[.small]#xref:web/webflux-view.adoc#webflux-view-freemarker-forms[See equivalent in the Reactive stack]#
Spring provides a tag library for use in JSPs that contains, among others, a
`<spring:bind/>` element. This element primarily lets forms display values from
@@ -47,7 +47,7 @@ through https://www.webjars.org/[WebJars].
[[mvc-view-script-integrate]]
== Script Templates
[.small]#xref:web/webflux-view.adoc#webflux-view-script-integrate[See equivalent in the Reactive stack]#
[.small]#xref:web/webflux-view.adoc#webflux-view-script[See equivalent in the Reactive stack]#
You can declare a `ScriptTemplateConfigurer` bean to specify the script engine to use,
the script files to load, what function to call to render templates, and so on.
@@ -37,7 +37,7 @@ already set.
"application/problem+json" over "application/json" when rendering a `ProblemDetail`,
and also falls back on it if no compatible media type is found.
To enable RFC 9457 responses for Spring MVC exceptions and for any
To enable RFC 9457 responses for Spring WebFlux exceptions and for any
`ErrorResponseException`, extend `ResponseEntityExceptionHandler` and declare it as an
xref:web/webmvc/mvc-controller/ann-advice.adoc[@ControllerAdvice] in Spring configuration. The handler
has an `@ExceptionHandler` method that handles any `ErrorResponse` exception, which
-1
View File
@@ -5,7 +5,6 @@
"@antora/collector-extension": "1.0.0-alpha.3",
"@asciidoctor/tabs": "1.0.0-beta.6",
"@springio/antora-extensions": "1.14.2",
"fast-xml-parser": "4.5.2",
"@springio/asciidoctor-extensions": "1.0.0-alpha.10"
}
}
@@ -29,7 +29,7 @@ import org.springframework.web.service.invoker.HttpServiceProxyFactory;
public class CustomHttpServiceArgumentResolver {
// tag::httpinterface[]
public interface RepositoryService {
interface RepositoryService {
@GetExchange("/repos/search")
List<Repository> searchRepository(Search search);
+28 -28
View File
@@ -7,31 +7,31 @@ javaPlatform {
}
dependencies {
api(platform("com.fasterxml.jackson:jackson-bom:2.18.3"))
api(platform("io.micrometer:micrometer-bom:1.14.5"))
api(platform("io.netty:netty-bom:4.1.119.Final"))
api(platform("com.fasterxml.jackson:jackson-bom:2.18.2"))
api(platform("io.micrometer:micrometer-bom:1.14.4"))
api(platform("io.netty:netty-bom:4.1.118.Final"))
api(platform("io.netty:netty5-bom:5.0.0.Alpha5"))
api(platform("io.projectreactor:reactor-bom:2024.0.4"))
api(platform("io.projectreactor:reactor-bom:2024.0.3"))
api(platform("io.rsocket:rsocket-bom:1.1.5"))
api(platform("org.apache.groovy:groovy-bom:4.0.26"))
api(platform("org.apache.groovy:groovy-bom:4.0.24"))
api(platform("org.apache.logging.log4j:log4j-bom:2.21.1"))
api(platform("org.assertj:assertj-bom:3.27.3"))
api(platform("org.eclipse.jetty:jetty-bom:12.0.17"))
api(platform("org.eclipse.jetty.ee10:jetty-ee10-bom:12.0.17"))
api(platform("org.eclipse.jetty:jetty-bom:12.0.16"))
api(platform("org.eclipse.jetty.ee10:jetty-ee10-bom:12.0.16"))
api(platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.8.1"))
api(platform("org.jetbrains.kotlinx:kotlinx-serialization-bom:1.6.3"))
api(platform("org.junit:junit-bom:5.12.0"))
api(platform("org.mockito:mockito-bom:5.16.0"))
api(platform("org.junit:junit-bom:5.11.4"))
api(platform("org.mockito:mockito-bom:5.15.2"))
constraints {
api("com.fasterxml:aalto-xml:1.3.2")
api("com.fasterxml.woodstox:woodstox-core:6.7.0")
api("com.github.ben-manes.caffeine:caffeine:3.2.0")
api("com.github.ben-manes.caffeine:caffeine:3.1.8")
api("com.github.librepdf:openpdf:1.3.43")
api("com.google.code.findbugs:findbugs:3.0.1")
api("com.google.code.findbugs:jsr305:3.0.2")
api("com.google.code.gson:gson:2.12.1")
api("com.google.protobuf:protobuf-java-util:4.30.0")
api("com.google.code.gson:gson:2.11.0")
api("com.google.protobuf:protobuf-java-util:4.29.3")
api("com.h2database:h2:2.3.232")
api("com.jayway.jsonpath:json-path:2.9.0")
api("com.oracle.database.jdbc:ojdbc11:21.9.0.0")
@@ -43,7 +43,7 @@ dependencies {
api("com.sun.xml.bind:jaxb-core:3.0.2")
api("com.sun.xml.bind:jaxb-impl:3.0.2")
api("com.sun.xml.bind:jaxb-xjc:3.0.2")
api("com.thoughtworks.qdox:qdox:2.2.0")
api("com.thoughtworks.qdox:qdox:2.1.0")
api("com.thoughtworks.xstream:xstream:1.4.21")
api("commons-io:commons-io:2.15.0")
api("de.bechte.junit:junit-hierarchicalcontextrunner:4.12.2")
@@ -100,8 +100,8 @@ dependencies {
api("org.apache.derby:derby:10.16.1.1")
api("org.apache.derby:derbyclient:10.16.1.1")
api("org.apache.derby:derbytools:10.16.1.1")
api("org.apache.httpcomponents.client5:httpclient5:5.4.2")
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.3.3")
api("org.apache.httpcomponents.client5:httpclient5:5.4.1")
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.3.1")
api("org.apache.poi:poi-ooxml:5.2.5")
api("org.apache.tomcat.embed:tomcat-embed-core:10.1.28")
api("org.apache.tomcat.embed:tomcat-embed-websocket:10.1.28")
@@ -110,18 +110,18 @@ dependencies {
api("org.aspectj:aspectjrt:1.9.22.1")
api("org.aspectj:aspectjtools:1.9.22.1")
api("org.aspectj:aspectjweaver:1.9.22.1")
api("org.awaitility:awaitility:4.3.0")
api("org.awaitility:awaitility:4.2.2")
api("org.bouncycastle:bcpkix-jdk18on:1.72")
api("org.codehaus.jettison:jettison:1.5.4")
api("org.crac:crac:1.4.0")
api("org.dom4j:dom4j:2.1.4")
api("org.easymock:easymock:5.5.0")
api("org.eclipse.jetty:jetty-reactive-httpclient:4.0.9")
api("org.easymock:easymock:5.4.0")
api("org.eclipse.jetty:jetty-reactive-httpclient:4.0.8")
api("org.eclipse.persistence:org.eclipse.persistence.jpa:3.0.4")
api("org.eclipse:yasson:2.0.4")
api("org.ehcache:ehcache:3.10.8")
api("org.ehcache:jcache:1.0.1")
api("org.freemarker:freemarker:2.3.34")
api("org.freemarker:freemarker:2.3.33")
api("org.glassfish.external:opendmk_jmxremote_optional_jar:1.0-b01-ea")
api("org.glassfish:jakarta.el:4.0.2")
api("org.glassfish.tyrus:tyrus-container-servlet:2.1.3")
@@ -130,24 +130,24 @@ dependencies {
api("org.hibernate:hibernate-core-jakarta:5.6.15.Final")
api("org.hibernate:hibernate-validator:7.0.5.Final")
api("org.hsqldb:hsqldb:2.7.4")
api("org.htmlunit:htmlunit:4.10.0")
api("org.htmlunit:htmlunit:4.6.0")
api("org.javamoney:moneta:1.4.4")
api("org.jruby:jruby:9.4.12.0")
api("org.jruby:jruby:9.4.9.0")
api("org.junit.support:testng-engine:1.0.5")
api("org.mozilla:rhino:1.7.15")
api("org.ogce:xpp3:1.1.6")
api("org.python:jython-standalone:2.7.4")
api("org.python:jython-standalone:2.7.3")
api("org.quartz-scheduler:quartz:2.3.2")
api("org.seleniumhq.selenium:htmlunit3-driver:4.29.0")
api("org.seleniumhq.selenium:selenium-java:4.29.0")
api("org.seleniumhq.selenium:htmlunit3-driver:4.26.0")
api("org.seleniumhq.selenium:selenium-java:4.26.0")
api("org.skyscreamer:jsonassert:1.5.3")
api("org.slf4j:slf4j-api:2.0.17")
api("org.slf4j:slf4j-api:2.0.16")
api("org.testng:testng:7.11.0")
api("org.webjars:underscorejs:1.8.3")
api("org.webjars:webjars-locator-core:0.59")
api("org.webjars:webjars-locator-lite:1.1.0")
api("org.webjars:webjars-locator-core:0.55")
api("org.webjars:webjars-locator-lite:1.0.0")
api("org.xmlunit:xmlunit-assertj:2.10.0")
api("org.xmlunit:xmlunit-matchers:2.10.0")
api("org.yaml:snakeyaml:2.4")
api("org.yaml:snakeyaml:2.3")
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
version=6.2.4
version=6.2.3
org.gradle.caching=true
org.gradle.jvmargs=-Xmx2048m
Binary file not shown.
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Vendored
+1 -1
View File
@@ -205,7 +205,7 @@ fi
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
@@ -46,21 +46,6 @@ import org.springframework.lang.Nullable;
* Alternatively, you may implement the specific methods that your callers expect,
* for example, just {@link #getObject()} or {@link #getIfAvailable()}.
*
* <p>Note that {@link #getObject()} never returns {@code null} - it will throw a
* {@link NoSuchBeanDefinitionException} instead -, whereas {@link #getIfAvailable()}
* will return {@code null} if no matching bean is present at all. However, both
* methods will throw a {@link NoUniqueBeanDefinitionException} if more than one
* matching bean is found without a clear unique winner (see below). Last but not
* least, {@link #getIfUnique()} will return {@code null} both when no matching bean
* is found and when more than one matching bean is found without a unique winner.
*
* <p>Uniqueness is generally up to the container's candidate resolution algorithm
* but always honors the "primary" flag (with only one of the candidate beans marked
* as primary) and the "fallback" flag (with only one of the candidate beans not
* marked as fallback). The default-candidate flag is consistently taken into
* account as well, even for non-annotation-based injection points, with a single
* default candidate winning in case of no clear primary/fallback indication.
*
* @author Juergen Hoeller
* @since 4.3
* @param <T> the object type
@@ -203,7 +188,7 @@ public interface ObjectProvider<T> extends ObjectFactory<T>, Iterable<T> {
* if unique (not called otherwise)
* @throws BeansException in case of creation errors
* @since 5.0
* @see #getIfUnique()
* @see #getIfAvailable()
*/
default void ifUnique(Consumer<T> dependencyConsumer) throws BeansException {
T dependency = getIfUnique();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 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.
@@ -443,7 +443,6 @@ public class InstanceSupplierCodeGenerator {
}
}
record ConstructorDescriptor(String beanName, Constructor<?> constructor, Class<?> publicType) {
Class<?> actualType() {
@@ -35,7 +35,6 @@ import java.util.Set;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.lang.Nullable;
@@ -280,25 +279,6 @@ abstract class AutowireUtils {
}
}
/**
* Check the default-candidate status for the specified bean.
* @param beanFactory the bean factory
* @param beanName the name of the bean to check
* @return whether the specified bean qualifies as a default candidate
* @since 6.2.4
* @see AbstractBeanDefinition#isDefaultCandidate()
*/
public static boolean isDefaultCandidate(ConfigurableBeanFactory beanFactory, String beanName) {
try {
BeanDefinition mbd = beanFactory.getMergedBeanDefinition(beanName);
return (!(mbd instanceof AbstractBeanDefinition abd) || abd.isDefaultCandidate());
}
catch (NoSuchBeanDefinitionException ex) {
// A manually registered singleton instance not backed by a BeanDefinition.
return true;
}
}
/**
* Reflective {@link InvocationHandler} for lazy access to the current target object.
@@ -1497,9 +1497,6 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
if (candidateName == null) {
candidateName = determineHighestPriorityCandidate(candidates, requiredType.toClass());
}
if (candidateName == null) {
candidateName = determineDefaultCandidate(candidates);
}
if (candidateName != null) {
Object beanInstance = candidates.get(candidateName);
if (beanInstance == null) {
@@ -1970,12 +1967,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
if (priorityCandidate != null) {
return priorityCandidate;
}
// Step 4: pick unique default-candidate
String defaultCandidate = determineDefaultCandidate(candidates);
if (defaultCandidate != null) {
return defaultCandidate;
}
// Step 5: pick directly registered dependency
// Step 4: pick directly registered dependency
for (Map.Entry<String, Object> entry : candidates.entrySet()) {
String candidateName = entry.getKey();
Object beanInstance = entry.getValue();
@@ -2136,28 +2128,6 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
return null;
}
/**
* Return a unique "default-candidate" among remaining non-default candidates.
* @param candidates a Map of candidate names and candidate instances
* (or candidate classes if not created yet) that match the required type
* @return the name of the default candidate, or {@code null} if none found
* @since 6.2.4
* @see AbstractBeanDefinition#isDefaultCandidate()
*/
@Nullable
private String determineDefaultCandidate(Map<String, Object> candidates) {
String defaultBeanName = null;
for (String candidateBeanName : candidates.keySet()) {
if (AutowireUtils.isDefaultCandidate(this, candidateBeanName)) {
if (defaultBeanName != null) {
return null;
}
defaultBeanName = candidateBeanName;
}
}
return defaultBeanName;
}
/**
* Determine whether the given candidate name matches the bean name or the aliases
* stored in this bean definition.
@@ -253,6 +253,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
Boolean lockFlag = isCurrentThreadAllowedToHoldSingletonLock();
boolean acquireLock = !Boolean.FALSE.equals(lockFlag);
boolean locked = (acquireLock && this.singletonLock.tryLock());
boolean lenient = false;
try {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
@@ -267,6 +268,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
Thread.currentThread().getName() + "\" while other thread holds " +
"singleton lock for other beans " + this.singletonsCurrentlyInCreation);
}
lenient = true;
this.lenientCreationLock.lock();
try {
this.singletonsInLenientCreation.add(beanName);
@@ -327,7 +329,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
// Try late locking for waiting on specific bean to be finished.
this.singletonLock.lock();
locked = true;
// Lock-created singleton object should have appeared in the meantime.
// Singleton object should have appeared in the meantime.
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject != null) {
return singletonObject;
@@ -341,12 +343,8 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
// Leniently created singleton object could have appeared in the meantime.
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
// Has the singleton object implicitly appeared in the meantime ->
@@ -370,18 +368,8 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
}
afterSingletonCreation(beanName);
}
if (newSingleton) {
try {
addSingleton(beanName, singletonObject);
}
catch (IllegalStateException ex) {
// Leniently accept same instance if implicitly appeared.
Object object = this.singletonObjects.get(beanName);
if (singletonObject != object) {
throw ex;
}
}
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
@@ -390,13 +378,15 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
if (locked) {
this.singletonLock.unlock();
}
this.lenientCreationLock.lock();
try {
this.singletonsInLenientCreation.remove(beanName);
this.lenientCreationFinished.signalAll();
}
finally {
this.lenientCreationLock.unlock();
if (lenient) {
this.lenientCreationLock.lock();
try {
this.singletonsInLenientCreation.remove(beanName);
this.lenientCreationFinished.signalAll();
}
finally {
this.lenientCreationLock.unlock();
}
}
}
}
@@ -66,15 +66,11 @@ public class SimpleAutowireCandidateResolver implements AutowireCandidateResolve
* @see org.springframework.beans.factory.config.BeanDefinition#isAutowireCandidate()
* @see AbstractBeanDefinition#isDefaultCandidate()
*/
@SuppressWarnings("unchecked")
public static <T> Map<String, T> resolveAutowireCandidates(ConfigurableListableBeanFactory lbf, Class<T> type) {
Map<String, T> candidates = new LinkedHashMap<>();
for (String beanName : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(lbf, type)) {
if (AutowireUtils.isAutowireCandidate(lbf, beanName)) {
Object beanInstance = lbf.getBean(beanName);
if (!(beanInstance instanceof NullBean)) {
candidates.put(beanName, (T) beanInstance);
}
candidates.put(beanName, lbf.getBean(beanName, type));
}
}
return candidates;
@@ -113,7 +113,6 @@ import static org.mockito.Mockito.verify;
* @author Chris Beams
* @author Phillip Webb
* @author Stephane Nicoll
* @author Yanming Zhou
*/
class DefaultListableBeanFactoryTests {
@@ -1659,94 +1658,12 @@ class DefaultListableBeanFactoryTests {
bd2.setPrimary(true);
lbf.registerBeanDefinition("bd1", bd1);
lbf.registerBeanDefinition("bd2", bd2);
lbf.registerSingleton("bd3", new TestBean());
TestBean bean = lbf.getBean(TestBean.class);
assertThat(bean.getBeanName()).isEqualTo("bd2");
assertThat(lbf.containsSingleton("bd1")).isFalse();
}
@Test
void getBeanByTypeWithUniqueNonFallbackDefinition() {
RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class);
bd1.setLazyInit(true);
bd1.setFallback(true);
RootBeanDefinition bd2 = new RootBeanDefinition(TestBean.class);
bd2.setFallback(true);
RootBeanDefinition bd3 = new RootBeanDefinition(TestBean.class);
lbf.registerBeanDefinition("bd1", bd1);
lbf.registerBeanDefinition("bd2", bd2);
lbf.registerBeanDefinition("bd3", bd3);
TestBean bean = lbf.getBean(TestBean.class);
assertThat(bean.getBeanName()).isEqualTo("bd3");
assertThat(lbf.containsSingleton("bd1")).isFalse();
}
@Test
void getBeanByTypeWithPrimaryAndUniqueNonFallbackDefinition() {
RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class);
bd1.setLazyInit(true);
bd1.setFallback(true);
RootBeanDefinition bd2 = new RootBeanDefinition(TestBean.class);
bd2.setPrimary(true);
bd2.setFallback(true);
RootBeanDefinition bd3 = new RootBeanDefinition(TestBean.class);
lbf.registerBeanDefinition("bd1", bd1);
lbf.registerBeanDefinition("bd2", bd2);
lbf.registerBeanDefinition("bd3", bd3);
TestBean bean = lbf.getBean(TestBean.class);
assertThat(bean.getBeanName()).isEqualTo("bd2");
assertThat(lbf.containsSingleton("bd1")).isFalse();
}
@Test
void getBeanByTypeWithUniqueNonFallbackAndUniqueNonDefaultDefinition() {
RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class);
bd1.setLazyInit(true);
bd1.setFallback(true);
RootBeanDefinition bd2 = new RootBeanDefinition(TestBean.class);
bd2.setFallback(true);
bd2.setDefaultCandidate(false);
RootBeanDefinition bd3 = new RootBeanDefinition(TestBean.class);
bd3.setDefaultCandidate(false);
lbf.registerBeanDefinition("bd1", bd1);
lbf.registerBeanDefinition("bd2", bd2);
lbf.registerBeanDefinition("bd3", bd3);
TestBean bean = lbf.getBean(TestBean.class);
assertThat(bean.getBeanName()).isEqualTo("bd3");
assertThat(lbf.containsSingleton("bd1")).isFalse();
}
@Test
void getBeanByTypeWithUniqueNonDefaultDefinition() {
RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class);
bd1.setDefaultCandidate(false);
bd1.setLazyInit(true);
RootBeanDefinition bd2 = new RootBeanDefinition(TestBean.class);
lbf.registerBeanDefinition("bd1", bd1);
lbf.registerBeanDefinition("bd2", bd2);
TestBean bean = lbf.getBean(TestBean.class);
assertThat(bean.getBeanName()).isEqualTo("bd2");
assertThat(lbf.containsSingleton("bd1")).isFalse();
}
@Test
void getBeanByTypeWithUniqueNonDefaultSingleton() {
RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class);
bd1.setDefaultCandidate(false);
bd1.setLazyInit(true);
lbf.registerBeanDefinition("bd1", bd1);
lbf.registerSingleton("bd2", new TestBean());
TestBean bean = lbf.getBean(TestBean.class);
assertThat(bean.getBeanName()).isNull();
assertThat(lbf.containsSingleton("bd1")).isFalse();
}
@Test
@SuppressWarnings("rawtypes")
void getFactoryBeanByTypeWithPrimary() {
@@ -1770,10 +1770,6 @@ class AutowiredAnnotationBeanPostProcessorTests {
parent.registerBeanDefinition("testBean4", tb4);
bf.setParentBeanFactory(parent);
RootBeanDefinition tb5 = new RootBeanDefinition(NullFactoryMethods.class);
tb5.setFactoryMethodName("createTestBean");
bf.registerBeanDefinition("testBean5", tb5);
ObjectProviderInjectionBean bean = bf.getBean("annotatedBean", ObjectProviderInjectionBean.class);
assertThat(bean.streamTestBeans()).containsExactly(bf.getBean("testBean1", TestBean.class),
bf.getBean("testBean2", TestBean.class));
@@ -1793,7 +1789,7 @@ class AutowiredAnnotationBeanPostProcessorTests {
Map<String, TestBean> typeMatches = BeanFactoryUtils.beansOfTypeIncludingAncestors(bf, TestBean.class);
assertThat(typeMatches.remove("testBean3")).isNotNull();
Map<String, TestBean> candidates = SimpleAutowireCandidateResolver.resolveAutowireCandidates(bf, TestBean.class);
assertThat(candidates).containsExactlyEntriesOf(typeMatches);
assertThat(candidates).containsExactlyEntriesOf(candidates);
}
@Test
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 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,15 +50,10 @@ class DefaultSingletonBeanRegistryTests {
assertThat(beanRegistry.getSingleton("tb2")).isSameAs(tb2);
assertThat(tb2Flag.get()).isTrue();
TestBean tb3 = (TestBean) beanRegistry.getSingleton("tb3", () -> {
TestBean newTb = new TestBean();
beanRegistry.registerSingleton("tb3", newTb);
return newTb;
});
assertThat(beanRegistry.getSingleton("tb3")).isSameAs(tb3);
assertThat(beanRegistry.getSingletonCount()).isEqualTo(3);
assertThat(beanRegistry.getSingletonNames()).containsExactly("tb", "tb2", "tb3");
assertThat(beanRegistry.getSingleton("tb")).isSameAs(tb);
assertThat(beanRegistry.getSingleton("tb2")).isSameAs(tb2);
assertThat(beanRegistry.getSingletonCount()).isEqualTo(2);
assertThat(beanRegistry.getSingletonNames()).containsExactly("tb", "tb2");
beanRegistry.destroySingletons();
assertThat(beanRegistry.getSingletonCount()).isZero();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 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.
@@ -203,15 +203,6 @@ final class ConfigurationClass {
return this.beanMethods;
}
boolean hasNonStaticBeanMethods() {
for (BeanMethod beanMethod : this.beanMethods) {
if (!beanMethod.getMetadata().isStatic()) {
return true;
}
}
return false;
}
void addImportedResource(String importedResource, Class<? extends BeanDefinitionReader> readerClass) {
this.importedResources.put(importedResource, readerClass);
}
@@ -232,9 +223,8 @@ final class ConfigurationClass {
void validate(ProblemReporter problemReporter) {
Map<String, Object> attributes = this.metadata.getAnnotationAttributes(Configuration.class.getName());
// A configuration class may not be final (CGLIB limitation) unless it does not have to proxy bean methods
if (attributes != null && (Boolean) attributes.get("proxyBeanMethods") && hasNonStaticBeanMethods() &&
this.metadata.isFinal()) {
// A configuration class may not be final (CGLIB limitation) unless it declares proxyBeanMethods=false
if (attributes != null && (Boolean) attributes.get("proxyBeanMethods") && this.metadata.isFinal()) {
problemReporter.error(new FinalConfigurationProblem());
}
@@ -112,11 +112,12 @@ class ConfigurationClassEnhancer {
try {
// Use original ClassLoader if config class not locally loaded in overriding class loader
boolean classLoaderMismatch = (classLoader != null && classLoader != configClass.getClassLoader());
if (classLoaderMismatch && classLoader instanceof SmartClassLoader smartClassLoader) {
if (classLoader instanceof SmartClassLoader smartClassLoader &&
classLoader != configClass.getClassLoader()) {
classLoader = smartClassLoader.getOriginalClassLoader();
}
Enhancer enhancer = newEnhancer(configClass, classLoader);
boolean classLoaderMismatch = (classLoader != null && classLoader != configClass.getClassLoader());
Class<?> enhancedClass = createClass(enhancer, classLoaderMismatch);
if (logger.isTraceEnabled()) {
logger.trace(String.format("Successfully enhanced %s; enhanced class name is: %s",
@@ -167,9 +168,9 @@ class ConfigurationClassEnhancer {
try {
subclass = enhancer.createClass();
}
catch (Throwable ex) {
catch (CodeGenerationException ex) {
if (!fallback) {
throw (ex instanceof CodeGenerationException cgex ? cgex : new CodeGenerationException(ex));
throw ex;
}
// Possibly a package-visible @Bean method declaration not accessible
// in the given ClassLoader -> retry with original ClassLoader
@@ -187,7 +188,8 @@ class ConfigurationClassEnhancer {
/**
* Marker interface to be implemented by all @Configuration CGLIB subclasses.
* Facilitates idempotent behavior for {@link ConfigurationClassEnhancer#enhance}
* through checking to see if candidate classes are already assignable to it.
* through checking to see if candidate classes are already assignable to it, for example,
* have already been enhanced.
* <p>Also extends {@link BeanFactoryAware}, as all enhanced {@code @Configuration}
* classes require access to the {@link BeanFactory} that created them.
* <p>Note that this interface is intended for framework-internal use only, however
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 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.
@@ -167,22 +167,14 @@ class ConfigurationClassParser {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
ConfigurationClass configClass;
if (bd instanceof AnnotatedBeanDefinition annotatedBeanDef) {
configClass = parse(annotatedBeanDef, holder.getBeanName());
parse(annotatedBeanDef, holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition abstractBeanDef && abstractBeanDef.hasBeanClass()) {
configClass = parse(abstractBeanDef.getBeanClass(), holder.getBeanName());
parse(abstractBeanDef.getBeanClass(), holder.getBeanName());
}
else {
configClass = parse(bd.getBeanClassName(), holder.getBeanName());
}
// Downgrade to lite (no enhancement) in case of no instance-level @Bean methods.
if (!configClass.hasNonStaticBeanMethods() && ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(
bd.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE))) {
bd.setAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE,
ConfigurationClassUtils.CONFIGURATION_CLASS_LITE);
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
@@ -197,25 +189,20 @@ class ConfigurationClassParser {
this.deferredImportSelectorHandler.process();
}
private ConfigurationClass parse(AnnotatedBeanDefinition beanDef, String beanName) {
ConfigurationClass configClass = new ConfigurationClass(
beanDef.getMetadata(), beanName, (beanDef instanceof ScannedGenericBeanDefinition));
processConfigurationClass(configClass, DEFAULT_EXCLUSION_FILTER);
return configClass;
private void parse(AnnotatedBeanDefinition beanDef, String beanName) {
processConfigurationClass(
new ConfigurationClass(beanDef.getMetadata(), beanName, (beanDef instanceof ScannedGenericBeanDefinition)),
DEFAULT_EXCLUSION_FILTER);
}
private ConfigurationClass parse(Class<?> clazz, String beanName) {
ConfigurationClass configClass = new ConfigurationClass(clazz, beanName);
processConfigurationClass(configClass, DEFAULT_EXCLUSION_FILTER);
return configClass;
private void parse(Class<?> clazz, String beanName) {
processConfigurationClass(new ConfigurationClass(clazz, beanName), DEFAULT_EXCLUSION_FILTER);
}
final ConfigurationClass parse(@Nullable String className, String beanName) throws IOException {
final void parse(@Nullable String className, String beanName) throws IOException {
Assert.notNull(className, "No bean class name for configuration class bean definition");
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
ConfigurationClass configClass = new ConfigurationClass(reader, beanName);
processConfigurationClass(configClass, DEFAULT_EXCLUSION_FILTER);
return configClass;
processConfigurationClass(new ConfigurationClass(reader, beanName), DEFAULT_EXCLUSION_FILTER);
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 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.
@@ -18,7 +18,6 @@ package org.springframework.scheduling.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
@@ -54,7 +53,6 @@ public class ExecutorBeanDefinitionParser extends AbstractSingleBeanDefinitionPa
if (StringUtils.hasText(poolSize)) {
builder.addPropertyValue("poolSize", poolSize);
}
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
}
private void configureRejectionPolicy(Element element, BeanDefinitionBuilder builder) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2012 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.
@@ -18,7 +18,6 @@ package org.springframework.scheduling.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.util.StringUtils;
@@ -42,7 +41,6 @@ public class SchedulerBeanDefinitionParser extends AbstractSingleBeanDefinitionP
if (StringUtils.hasText(poolSize)) {
builder.addPropertyValue("poolSize", poolSize);
}
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
}
}
@@ -75,7 +75,7 @@
The bean name of the KeyGenerator that is to be used to retrieve the backing caches.
This attribute is not required, and only needs to be specified
explicitly if the default strategy (SimpleKeyGenerator) is not sufficient.
explicitly if the default strategy (DefaultKeyGenerator) is not sufficient.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
@@ -56,16 +56,6 @@ class BackgroundBootstrapTests {
ctx.close();
}
@Test
@Timeout(5)
@EnabledForTestGroups(LONG_RUNNING)
void bootstrapWithCircularReference() {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(CircularReferenceBeanConfig.class);
ctx.getBean("testBean1", TestBean.class);
ctx.getBean("testBean2", TestBean.class);
ctx.close();
}
@Test
@Timeout(5)
@EnabledForTestGroups(LONG_RUNNING)
@@ -148,34 +138,6 @@ class BackgroundBootstrapTests {
}
@Configuration(proxyBeanMethods = false)
static class CircularReferenceBeanConfig {
@Bean
public TestBean testBean1(ObjectProvider<TestBean> testBean2) {
new Thread(testBean2::getObject).start();
try {
Thread.sleep(1000);
}
catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
return new TestBean();
}
@Bean
public TestBean testBean2(TestBean testBean1) {
try {
Thread.sleep(2000);
}
catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
return new TestBean();
}
}
@Configuration(proxyBeanMethods = false)
static class CustomExecutorBeanConfig {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 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.
@@ -67,7 +67,6 @@ import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.SyncTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@@ -105,7 +104,6 @@ class ConfigurationClassPostProcessorTests {
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
pp.postProcessBeanFactory(beanFactory);
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).hasBeanClass()).isTrue();
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).getBeanClass().getName()).contains(ClassUtils.CGLIB_CLASS_SEPARATOR);
Foo foo = beanFactory.getBean("foo", Foo.class);
Bar bar = beanFactory.getBean("bar", Bar.class);
assertThat(bar.foo).isSameAs(foo);
@@ -120,7 +118,6 @@ class ConfigurationClassPostProcessorTests {
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
pp.postProcessBeanFactory(beanFactory);
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).hasBeanClass()).isTrue();
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).getBeanClass().getName()).contains(ClassUtils.CGLIB_CLASS_SEPARATOR);
Foo foo = beanFactory.getBean("foo", Foo.class);
Bar bar = beanFactory.getBean("bar", Bar.class);
assertThat(bar.foo).isSameAs(foo);
@@ -135,7 +132,6 @@ class ConfigurationClassPostProcessorTests {
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
pp.postProcessBeanFactory(beanFactory);
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).hasBeanClass()).isTrue();
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).getBeanClass().getName()).doesNotContain(ClassUtils.CGLIB_CLASS_SEPARATOR);
Foo foo = beanFactory.getBean("foo", Foo.class);
Bar bar = beanFactory.getBean("bar", Bar.class);
assertThat(bar.foo).isNotSameAs(foo);
@@ -147,7 +143,6 @@ class ConfigurationClassPostProcessorTests {
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
pp.postProcessBeanFactory(beanFactory);
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).hasBeanClass()).isTrue();
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).getBeanClass().getName()).doesNotContain(ClassUtils.CGLIB_CLASS_SEPARATOR);
Foo foo = beanFactory.getBean("foo", Foo.class);
Bar bar = beanFactory.getBean("bar", Bar.class);
assertThat(bar.foo).isNotSameAs(foo);
@@ -159,7 +154,6 @@ class ConfigurationClassPostProcessorTests {
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
pp.postProcessBeanFactory(beanFactory);
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).hasBeanClass()).isTrue();
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).getBeanClass().getName()).doesNotContain(ClassUtils.CGLIB_CLASS_SEPARATOR);
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("foo")).hasBeanClass()).isTrue();
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("bar")).hasBeanClass()).isTrue();
Foo foo = beanFactory.getBean("foo", Foo.class);
@@ -173,7 +167,6 @@ class ConfigurationClassPostProcessorTests {
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
pp.postProcessBeanFactory(beanFactory);
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).hasBeanClass()).isTrue();
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).getBeanClass().getName()).doesNotContain(ClassUtils.CGLIB_CLASS_SEPARATOR);
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("foo")).hasBeanClass()).isTrue();
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("bar")).hasBeanClass()).isTrue();
Foo foo = beanFactory.getBean("foo", Foo.class);
@@ -181,15 +174,6 @@ class ConfigurationClassPostProcessorTests {
assertThat(bar.foo).isNotSameAs(foo);
}
@Test
void enhancementIsNotPresentWithEmptyConfig() {
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(EmptyConfig.class));
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
pp.postProcessBeanFactory(beanFactory);
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).hasBeanClass()).isTrue();
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).getBeanClass().getName()).doesNotContain(ClassUtils.CGLIB_CLASS_SEPARATOR);
}
@Test
void configurationIntrospectionOfInnerClassesWorksWithDotNameSyntax() {
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(getClass().getName() + ".SingletonBeanConfig"));
@@ -1182,7 +1166,7 @@ class ConfigurationClassPostProcessorTests {
}
@Configuration
static final class StaticSingletonBeanConfig {
static class StaticSingletonBeanConfig {
@Bean
public static Foo foo() {
@@ -1195,10 +1179,6 @@ class ConfigurationClassPostProcessorTests {
}
}
@Configuration
static final class EmptyConfig {
}
@Configuration
@Order(2)
static class OverridingSingletonBeanConfig {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 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.
@@ -37,18 +37,16 @@ class InvalidConfigurationClassDefinitionTests {
@Test
void configurationClassesMayNotBeFinal() {
@Configuration
final class Config {
@Bean String dummy() { return "dummy"; }
}
final class Config { }
BeanDefinition configBeanDef = rootBeanDefinition(Config.class).getBeanDefinition();
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerBeanDefinition("config", configBeanDef);
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
assertThatExceptionOfType(BeanDefinitionParsingException.class)
.isThrownBy(() -> pp.postProcessBeanFactory(beanFactory))
.withMessageContaining("Remove the final modifier");
assertThatExceptionOfType(BeanDefinitionParsingException.class).isThrownBy(() ->
pp.postProcessBeanFactory(beanFactory))
.withMessageContaining("Remove the final modifier");
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 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.
@@ -17,7 +17,6 @@
package org.springframework.context.testfixture.context.annotation;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@@ -32,10 +31,4 @@ public class ValueCglibConfiguration {
public String getName() {
return this.name;
}
@Bean
public String dummy() {
return "dummy";
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2023 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.
@@ -27,7 +27,6 @@ import java.util.stream.Stream;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
/**
* Gather the need for resources available at runtime.
@@ -51,14 +50,14 @@ public class ResourceHints {
this.resourceBundleHints = new LinkedHashSet<>();
}
/**
* Return the resources that should be made available at runtime.
* @return a stream of {@link ResourcePatternHints}
*/
public Stream<ResourcePatternHints> resourcePatternHints() {
Stream<ResourcePatternHints> patterns = this.resourcePatternHints.stream();
return (this.types.isEmpty() ? patterns : Stream.concat(Stream.of(typesPatternResourceHint()), patterns));
return (this.types.isEmpty() ? patterns
: Stream.concat(Stream.of(typesPatternResourceHint()), patterns));
}
/**
@@ -71,18 +70,18 @@ public class ResourceHints {
/**
* Register a pattern if the given {@code location} is available on the
* classpath. This delegates to {@link ClassLoader#getResource(String)} which
* validates directories as well. The location is not included in the hint.
* @param classLoader the ClassLoader to use, or {@code null} for the default
* classpath. This delegates to {@link ClassLoader#getResource(String)}
* which validates directories as well. The location is not included in
* the hint.
* @param classLoader the classloader to use
* @param location a '/'-separated path name that should exist
* @param resourceHint a builder to customize the resource pattern
* @return {@code this}, to facilitate method chaining
*/
public ResourceHints registerPatternIfPresent(@Nullable ClassLoader classLoader, String location,
Consumer<ResourcePatternHints.Builder> resourceHint) {
ClassLoader classLoaderToUse = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
if (classLoaderToUse != null && classLoaderToUse.getResource(location) != null) {
ClassLoader classLoaderToUse = (classLoader != null ? classLoader : getClass().getClassLoader());
if (classLoaderToUse.getResource(location) != null) {
registerPattern(resourceHint);
}
return this;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 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.
@@ -39,7 +39,8 @@ public interface RuntimeHintsRegistrar {
/**
* Contribute hints to the given {@link RuntimeHints} instance.
* @param hints the hints contributed so far for the deployment unit
* @param classLoader the ClassLoader to use, or {@code null} for the default
* @param classLoader the classloader, or {@code null} if even the system
* ClassLoader is not accessible
*/
void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2023 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.
@@ -23,7 +23,6 @@ import java.util.List;
import org.springframework.aot.hint.ResourceHints;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ResourceUtils;
/**
@@ -67,21 +66,19 @@ public class FilePatternResourceHintsRegistrar {
@Deprecated(since = "6.0.12", forRemoval = true)
public void registerHints(ResourceHints hints, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
if (classLoaderToUse != null) {
List<String> includes = new ArrayList<>();
for (String location : this.classpathLocations) {
if (classLoaderToUse.getResource(location) != null) {
for (String filePrefix : this.filePrefixes) {
for (String fileExtension : this.fileExtensions) {
includes.add(location + filePrefix + "*" + fileExtension);
}
ClassLoader classLoaderToUse = (classLoader != null ? classLoader : getClass().getClassLoader());
List<String> includes = new ArrayList<>();
for (String location : this.classpathLocations) {
if (classLoaderToUse.getResource(location) != null) {
for (String filePrefix : this.filePrefixes) {
for (String fileExtension : this.fileExtensions) {
includes.add(location + filePrefix + "*" + fileExtension);
}
}
}
if (!includes.isEmpty()) {
hints.registerPattern(hint -> hint.includes(includes.toArray(String[]::new)));
}
}
if (!includes.isEmpty()) {
hints.registerPattern(hint -> hint.includes(includes.toArray(String[]::new)));
}
}
@@ -249,7 +246,8 @@ public class FilePatternResourceHintsRegistrar {
* classpath location that resolves against the {@code ClassLoader}, files
* with the configured file prefixes and extensions are registered.
* @param hints the hints contributed so far for the deployment unit
* @param classLoader the ClassLoader to use, or {@code null} for the default
* @param classLoader the classloader, or {@code null} if even the system
* ClassLoader isn't accessible
*/
public void registerHints(ResourceHints hints, @Nullable ClassLoader classLoader) {
build().registerHints(hints, classLoader);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 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.
@@ -48,11 +48,10 @@ class SpringFactoriesLoaderRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
if (classLoaderToUse != null) {
for (String resourceLocation : RESOURCE_LOCATIONS) {
registerHints(hints, classLoaderToUse, resourceLocation);
}
ClassLoader classLoaderToUse = (classLoader != null ? classLoader :
SpringFactoriesLoaderRuntimeHints.class.getClassLoader());
for (String resourceLocation : RESOURCE_LOCATIONS) {
registerHints(hints, classLoaderToUse, resourceLocation);
}
}
@@ -123,17 +123,13 @@ abstract public class AbstractClassGenerator<T> implements ClassGenerator {
}
public Object get(AbstractClassGenerator gen, boolean useCache) {
// SPRING PATCH BEGIN
Object value = null;
if (useCache) {
if (!useCache) {
return gen.generate(ClassLoaderData.this);
}
else {
Object cachedValue = generatedClasses.get(gen);
value = gen.unwrapCachedValue(cachedValue);
return gen.unwrapCachedValue(cachedValue);
}
if (value == null) { // fallback when cached WeakReference returns null
value = gen.generate(ClassLoaderData.this);
}
return value;
// SPRING PATCH END
}
}
@@ -159,9 +159,6 @@ public final class GenericTypeResolver {
if (genericType instanceof TypeVariable<?> typeVariable) {
ResolvableType resolvedTypeVariable = resolveVariable(
typeVariable, ResolvableType.forClass(contextClass));
if (resolvedTypeVariable == ResolvableType.NONE) {
resolvedTypeVariable = ResolvableType.forVariableBounds(typeVariable);
}
if (resolvedTypeVariable != ResolvableType.NONE) {
Class<?> resolved = resolvedTypeVariable.resolve();
if (resolved != null) {
@@ -179,9 +176,6 @@ public final class GenericTypeResolver {
Type typeArgument = typeArguments[i];
if (typeArgument instanceof TypeVariable<?> typeVariable) {
ResolvableType resolvedTypeArgument = resolveVariable(typeVariable, contextType);
if (resolvedTypeArgument == ResolvableType.NONE) {
resolvedTypeArgument = ResolvableType.forVariableBounds(typeVariable);
}
if (resolvedTypeArgument != ResolvableType.NONE) {
generics[i] = resolvedTypeArgument;
}
@@ -235,7 +229,7 @@ public final class GenericTypeResolver {
return resolvedType;
}
}
return ResolvableType.NONE;
return ResolvableType.forVariableBounds(typeVariable);
}
/**
@@ -343,7 +343,7 @@ public class ResolvableType implements Serializable {
return otherBounds.isAssignableFrom(this, matchedBefore);
}
else if (!strict) {
return (matchedBefore != null ? otherBounds.equalsType(this, matchedBefore) :
return (matchedBefore != null ? otherBounds.equalsType(this) :
otherBounds.isAssignableTo(this, matchedBefore));
}
else {
@@ -1794,13 +1794,11 @@ public class ResolvableType implements Serializable {
* Return {@code true} if these bounds are equal to the specified type.
* @param type the type to test against
* @return {@code true} if these bounds are equal to the type
* @since 6.2.4
* @since 6.2.3
*/
public boolean equalsType(ResolvableType type, @Nullable Map<Type, Type> matchedBefore) {
public boolean equalsType(ResolvableType type) {
for (ResolvableType bound : this.bounds) {
if (this.kind == Kind.UPPER && bound.hasUnresolvableGenerics() ?
!type.isAssignableFrom(bound, true, matchedBefore, false) :
!type.equalsType(bound)) {
if (!type.equalsType(bound)) {
return false;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2022 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.
@@ -36,12 +36,8 @@ import org.springframework.util.MultiValueMap;
*
* <p>{@code AnnotatedElementUtils} defines the public API for Spring's
* meta-annotation programming model with support for <em>annotation attribute
* overrides</em> and {@link AliasFor @AliasFor}. Note, however, that
* {@code AnnotatedElementUtils} is effectively a facade for the
* {@link MergedAnnotations} API. For fine-grained support consider using the
* {@code MergedAnnotations} API directly. If you do not need support for
* annotation attribute overrides, {@code @AliasFor}, or merged annotations,
* consider using {@link AnnotationUtils} instead.
* overrides</em>. If you do not need support for annotation attribute
* overrides, consider using {@link AnnotationUtils} instead.
*
* <p>Note that the features of this class are not provided by the JDK's
* introspection facilities themselves.
@@ -91,7 +87,6 @@ import org.springframework.util.MultiValueMap;
* @since 4.0
* @see AliasFor
* @see AnnotationAttributes
* @see MergedAnnotations
* @see AnnotationUtils
* @see BridgeMethodResolver
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 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.
@@ -60,7 +60,7 @@ import org.springframework.util.StringUtils;
* <h3>Terminology</h3>
* The terms <em>directly present</em>, <em>indirectly present</em>, and
* <em>present</em> have the same meanings as defined in the class-level
* javadoc for {@link AnnotatedElement}.
* javadoc for {@link AnnotatedElement} (in Java 8).
*
* <p>An annotation is <em>meta-present</em> on an element if the annotation
* is declared as a meta-annotation on some other annotation which is
@@ -73,8 +73,7 @@ import org.springframework.util.StringUtils;
* provide support for finding annotations used as meta-annotations. Consult the
* javadoc for each method in this class for details. For fine-grained support for
* meta-annotations with <em>attribute overrides</em> in <em>composed annotations</em>,
* consider using the {@link MergedAnnotations} API directly or the more specific
* methods in {@link AnnotatedElementUtils} instead.
* consider using {@link AnnotatedElementUtils}'s more specific methods instead.
*
* <h3>Attribute Aliases</h3>
* <p>All public methods in this class that return annotations, arrays of
@@ -98,7 +97,6 @@ import org.springframework.util.StringUtils;
* @since 2.0
* @see AliasFor
* @see AnnotationAttributes
* @see MergedAnnotations
* @see AnnotatedElementUtils
* @see BridgeMethodResolver
* @see java.lang.reflect.AnnotatedElement#getAnnotations()
@@ -309,11 +307,11 @@ public abstract class AnnotationUtils {
* {@code annotationType} from the supplied {@link AnnotatedElement}, where
* such annotations are either <em>present</em>, <em>indirectly present</em>,
* or <em>meta-present</em> on the element.
* <p>This method mimics the functionality of
* <p>This method mimics the functionality of Java 8's
* {@link java.lang.reflect.AnnotatedElement#getAnnotationsByType(Class)}
* with support for automatic detection of a <em>container annotation</em>
* declared via {@link java.lang.annotation.Repeatable @Repeatable} and with
* additional support for meta-annotations.
* declared via @{@link java.lang.annotation.Repeatable} (when running on
* Java 8 or higher) and with additional support for meta-annotations.
* <p>Handles both single annotations and annotations nested within a
* <em>container annotation</em>.
* <p>Correctly handles <em>bridge methods</em> generated by the
@@ -344,7 +342,7 @@ public abstract class AnnotationUtils {
* {@code annotationType} from the supplied {@link AnnotatedElement}, where
* such annotations are either <em>present</em>, <em>indirectly present</em>,
* or <em>meta-present</em> on the element.
* <p>This method mimics the functionality
* <p>This method mimics the functionality of Java 8's
* {@link java.lang.reflect.AnnotatedElement#getAnnotationsByType(Class)}
* with additional support for meta-annotations.
* <p>Handles both single annotations and annotations nested within a
@@ -355,9 +353,10 @@ public abstract class AnnotationUtils {
* <em>present</em> on the supplied element.
* @param annotatedElement the element to look for annotations on
* @param annotationType the annotation type to look for
* @param containerAnnotationType the type of the container that holds the
* annotations; may be {@code null} if a container is not supported or if it
* should be looked up via {@link java.lang.annotation.Repeatable @Repeatable}
* @param containerAnnotationType the type of the container that holds
* the annotations; may be {@code null} if a container is not supported
* or if it should be looked up via @{@link java.lang.annotation.Repeatable}
* when running on Java 8 or higher
* @return the annotations found or an empty set (never {@code null})
* @since 4.2
* @see #getRepeatableAnnotations(AnnotatedElement, Class)
@@ -389,11 +388,11 @@ public abstract class AnnotationUtils {
* of {@code annotationType} from the supplied {@link AnnotatedElement},
* where such annotations are either <em>directly present</em>,
* <em>indirectly present</em>, or <em>meta-present</em> on the element.
* <p>This method mimics the functionality of
* <p>This method mimics the functionality of Java 8's
* {@link java.lang.reflect.AnnotatedElement#getDeclaredAnnotationsByType(Class)}
* with support for automatic detection of a <em>container annotation</em>
* declared via {@link java.lang.annotation.Repeatable @Repeatable} and with
* additional support for meta-annotations.
* declared via @{@link java.lang.annotation.Repeatable} (when running on
* Java 8 or higher) and with additional support for meta-annotations.
* <p>Handles both single annotations and annotations nested within a
* <em>container annotation</em>.
* <p>Correctly handles <em>bridge methods</em> generated by the
@@ -425,7 +424,7 @@ public abstract class AnnotationUtils {
* of {@code annotationType} from the supplied {@link AnnotatedElement},
* where such annotations are either <em>directly present</em>,
* <em>indirectly present</em>, or <em>meta-present</em> on the element.
* <p>This method mimics the functionality of
* <p>This method mimics the functionality of Java 8's
* {@link java.lang.reflect.AnnotatedElement#getDeclaredAnnotationsByType(Class)}
* with additional support for meta-annotations.
* <p>Handles both single annotations and annotations nested within a
@@ -436,9 +435,10 @@ public abstract class AnnotationUtils {
* <em>present</em> on the supplied element.
* @param annotatedElement the element to look for annotations on
* @param annotationType the annotation type to look for
* @param containerAnnotationType the type of the container that holds the
* annotations; may be {@code null} if a container is not supported or if it
* should be looked up via {@link java.lang.annotation.Repeatable @Repeatable}
* @param containerAnnotationType the type of the container that holds
* the annotations; may be {@code null} if a container is not supported
* or if it should be looked up via @{@link java.lang.annotation.Repeatable}
* when running on Java 8 or higher
* @return the annotations found or an empty set (never {@code null})
* @since 4.2
* @see #getRepeatableAnnotations(AnnotatedElement, Class)
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2023 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.
@@ -37,7 +37,7 @@ import org.springframework.util.Assert;
* <ul>
* <li>Explicit and Implicit {@link AliasFor @AliasFor} declarations on one or
* more attributes within the annotation</li>
* <li>Explicit {@code @AliasFor} declarations for a meta-annotation</li>
* <li>Explicit {@link AliasFor @AliasFor} declarations for a meta-annotation</li>
* <li>Convention based attribute aliases for a meta-annotation</li>
* <li>From a meta-annotation declaration</li>
* </ul>
@@ -140,9 +140,6 @@ import org.springframework.util.Assert;
* @see MergedAnnotationCollectors
* @see MergedAnnotationPredicates
* @see MergedAnnotationSelectors
* @see AliasFor
* @see AnnotationUtils
* @see AnnotatedElementUtils
*/
public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>> {
@@ -563,7 +560,6 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
* @see #withRepeatableContainers(RepeatableContainers)
* @see #withAnnotationFilter(AnnotationFilter)
* @see #from(AnnotatedElement)
* @see org.springframework.test.context.TestContextAnnotationUtils#searchEnclosingClass(Class)
*/
public Search withEnclosingClasses(Predicate<Class<?>> searchEnclosingClass) {
Assert.notNull(searchEnclosingClass, "Predicate must not be null");
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 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.
@@ -38,7 +38,7 @@ import org.springframework.util.ClassUtils;
* applying the mapping and mirroring rules of an {@link AnnotationTypeMapping}.
*
* <p>Root attribute values are extracted from a source object using a supplied
* {@link ValueExtractor}. This allows various different annotation models to be
* {@code BiFunction}. This allows various different annotation models to be
* supported by the same class. For example, the attributes source might be an
* actual {@link Annotation} instance where methods on the annotation instance
* are {@linkplain AnnotationUtils#invokeAnnotationMethod(Method, Object) invoked}
@@ -348,13 +348,6 @@ public class GenericConversionService implements ConfigurableConversionService {
conditionalConverter.matches(sourceType, targetType);
}
public boolean matchesFallback(TypeDescriptor sourceType, TypeDescriptor targetType) {
return (this.typeInfo.getTargetType() == targetType.getObjectType() &&
this.targetType.hasUnresolvableGenerics() &&
(!(this.converter instanceof ConditionalConverter conditionalConverter) ||
conditionalConverter.matches(sourceType, targetType)));
}
@Override
@Nullable
public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
@@ -629,20 +622,12 @@ public class GenericConversionService implements ConfigurableConversionService {
@Nullable
public GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
// Look for proper match among all converters (taking full generics into account)
for (GenericConverter converter : this.converters) {
if (!(converter instanceof ConditionalGenericConverter genericConverter) ||
genericConverter.matches(sourceType, targetType)) {
return converter;
}
}
// Fallback to pre-6.2.3 behavior: accept Class match for unresolvable generics
for (GenericConverter converter : this.converters) {
if (converter instanceof ConverterAdapter converterAdapter &&
converterAdapter.matchesFallback(sourceType, targetType)) {
return converter;
}
}
return null;
}
@@ -81,10 +81,7 @@ public abstract class AbstractFileResolvingResource extends AbstractResource {
}
}
}
// Check content-length entry but not for JarURLConnection where
// this would open the jar file but effectively never close it ->
// for jar entries, always fall back to stream existence instead.
if (!(con instanceof JarURLConnection) && con.getContentLengthLong() > 0) {
if (con.getContentLengthLong() > 0) {
return true;
}
if (httpCon != null) {
@@ -816,21 +816,17 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
// Clean root entry path to match jar entries format without "!" separators
rootEntryPath = rootEntryPath.replace(ResourceUtils.JAR_URL_SEPARATOR, "/");
// Search sorted entries from first entry with rootEntryPath prefix
boolean rootEntryPathFound = false;
for (String entryPath : entriesCache.tailSet(rootEntryPath, false)) {
if (!entryPath.startsWith(rootEntryPath)) {
// We are beyond the potential matches in the current TreeSet.
break;
}
rootEntryPathFound = true;
String relativePath = entryPath.substring(rootEntryPath.length());
if (getPathMatcher().match(subPattern, relativePath)) {
result.add(rootDirResource.createRelative(relativePath));
}
}
if (rootEntryPathFound) {
return result;
}
return result;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2023 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.
@@ -69,13 +69,7 @@ public class TaskRejectedException extends RejectedExecutionException {
private static String executorDescription(Executor executor) {
if (executor instanceof ExecutorService executorService) {
try {
return "ExecutorService in " + (executorService.isShutdown() ? "shutdown" : "active") + " state";
}
catch (Exception ex) {
// UnsupportedOperationException/IllegalStateException from ManagedExecutorService.isShutdown()
// Falling back to toString() below.
}
return "ExecutorService in " + (executorService.isShutdown() ? "shutdown" : "active") + " state";
}
return executor.toString();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 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.
@@ -787,9 +787,9 @@ public abstract class StringUtils {
pathElements.addFirst(CURRENT_PATH);
}
String joined = collectionToDelimitedString(pathElements, FOLDER_SEPARATOR);
// Avoid String concatenation with empty prefix
return (prefix.isEmpty() ? joined : prefix + joined);
final String joined = collectionToDelimitedString(pathElements, FOLDER_SEPARATOR);
// avoid string concatenation with empty prefix
return prefix.isEmpty() ? joined : prefix + joined;
}
/**
@@ -243,13 +243,6 @@ class GenericTypeResolverTests {
assertThat(resolvedType.toString()).isEqualTo("java.util.List<E>");
}
@Test
void resolveTypeFromGenericDefaultMethod() {
Type type = method(InterfaceWithDefaultMethod.class, "get", InheritsDefaultMethod.AbstractType.class).getGenericParameterTypes()[0];
Type resolvedType = resolveType(type, InheritsDefaultMethod.class);
assertThat(resolvedType).isEqualTo(InheritsDefaultMethod.ConcreteType.class);
}
private static Method method(Class<?> target, String methodName, Class<?>... parameterTypes) {
Method method = findMethod(target, methodName, parameterTypes);
assertThat(method).describedAs(target.getName() + "#" + methodName).isNotNull();
@@ -461,20 +454,4 @@ class GenericTypeResolverTests {
}
}
public interface InterfaceWithDefaultMethod<T extends InterfaceWithDefaultMethod.AbstractType> {
default String get(T input) {
throw new UnsupportedOperationException();
}
interface AbstractType {
}
}
public static class InheritsDefaultMethod implements InterfaceWithDefaultMethod<InheritsDefaultMethod.ConcreteType> {
static class ConcreteType implements InterfaceWithDefaultMethod.AbstractType {
}
}
}
@@ -1527,12 +1527,6 @@ class ResolvableTypeTests {
assertThat(repository3.isAssignableFromResolvedPart(repository2)).isTrue();
}
@Test
void gh34541() throws Exception {
ResolvableType typeWithGenerics = ResolvableType.forField(getClass().getDeclaredField("paymentCreator"));
assertThat(typeWithGenerics.isAssignableFrom(PaymentCreator.class)).isTrue();
}
private ResolvableType testSerialization(ResolvableType type) throws Exception {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
@@ -1934,18 +1928,6 @@ class ResolvableTypeTests {
}
PaymentCreator<? extends Payment, PaymentCreatorParameter<? extends Payment>> paymentCreator;
static class PaymentCreator<T extends Payment, P extends PaymentCreatorParameter<T>> {
}
static class PaymentCreatorParameter<T extends Payment> {
}
abstract static class Payment {
}
private static class ResolvableTypeAssert extends AbstractAssert<ResolvableTypeAssert, ResolvableType>{
public ResolvableTypeAssert(ResolvableType actual) {
@@ -581,19 +581,6 @@ class GenericConversionServiceTests {
assertThat(bList).allMatch(e -> e instanceof BRaw);
}
@Test
@SuppressWarnings("unchecked")
void stringToListOfMapConverterWithFallbackMatch() {
conversionService.addConverter(new StringToListOfMapConverter());
List<Map<String, Object>> result = (List<Map<String, Object>>) conversionService.convert("foo",
TypeDescriptor.valueOf(String.class),
TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(Map.class))
);
assertThat("foo").isEqualTo(result.get(0).get("bar"));
}
@ExampleAnnotation(active = true)
public String annotatedString;
@@ -984,13 +971,4 @@ class GenericConversionServiceTests {
}
}
private static class StringToListOfMapConverter implements Converter<String, List<? extends Map<String, ?>>> {
@Override
public List<? extends Map<String, ?>> convert(String source) {
return List.of(Map.of("bar", source));
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2023 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.
@@ -439,11 +439,7 @@ public abstract class DataSourceUtils {
public static Connection getTargetConnection(Connection con) {
Connection conToUse = con;
while (conToUse instanceof ConnectionProxy connectionProxy) {
Connection targetCon = connectionProxy.getTargetConnection();
if (targetCon == conToUse) {
break;
}
conToUse = targetCon;
conToUse = connectionProxy.getTargetConnection();
}
return conToUse;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2023 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.
@@ -210,23 +210,13 @@ public class TransactionAwareDataSourceProxy extends DelegatingDataSource {
sb.append('[').append(this.target).append(']');
}
else {
sb.append("from DataSource [").append(this.targetDataSource).append(']');
sb.append(" from DataSource [").append(this.targetDataSource).append(']');
}
return sb.toString();
}
case "close" -> {
// Handle close method: only close if not within a transaction.
if (this.target != null) {
ConnectionHolder conHolder = (ConnectionHolder)
TransactionSynchronizationManager.getResource(this.targetDataSource);
if (conHolder != null && conHolder.hasConnection() && conHolder.getConnection() == this.target) {
// It's the transactional Connection: Don't close it.
conHolder.released();
}
else {
DataSourceUtils.doCloseConnection(this.target, this.targetDataSource);
}
}
DataSourceUtils.doReleaseConnection(this.target, this.targetDataSource);
this.closed = true;
return null;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2022 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.
@@ -63,7 +63,6 @@ public class SqlFunction<T> extends MappingSqlQuery<T> {
* @see #setSql
* @see #compile
*/
@SuppressWarnings("removal")
public SqlFunction() {
setRowsExpected(1);
}
@@ -74,7 +73,6 @@ public class SqlFunction<T> extends MappingSqlQuery<T> {
* @param ds the DataSource to obtain connections from
* @param sql the SQL to execute
*/
@SuppressWarnings("removal")
public SqlFunction(DataSource ds, String sql) {
setRowsExpected(1);
setDataSource(ds);
@@ -89,7 +87,6 @@ public class SqlFunction<T> extends MappingSqlQuery<T> {
* {@code java.sql.Types} class
* @see java.sql.Types
*/
@SuppressWarnings("removal")
public SqlFunction(DataSource ds, String sql, int[] types) {
setRowsExpected(1);
setDataSource(ds);
@@ -107,7 +104,6 @@ public class SqlFunction<T> extends MappingSqlQuery<T> {
* @see #setResultType(Class)
* @see java.sql.Types
*/
@SuppressWarnings("removal")
public SqlFunction(DataSource ds, String sql, int[] types, Class<T> resultType) {
setRowsExpected(1);
setDataSource(ds);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2018 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.
@@ -84,20 +84,14 @@ public abstract class SqlQuery<T> extends SqlOperation {
* Set the number of rows expected.
* <p>This can be used to ensure efficient storage of results. The
* default behavior is not to expect any specific number of rows.
* @deprecated since 6.2.4 with no replacement since the property has never
* had any affect on behavior; to be removed in 7.0
*/
@Deprecated(since = "6.2.4", forRemoval = true)
public void setRowsExpected(int rowsExpected) {
this.rowsExpected = rowsExpected;
}
/**
* Get the number of rows expected.
* @deprecated since 6.2.4 with no replacement since the property has never
* had any affect on behavior; to be removed in 7.0
*/
@Deprecated(since = "6.2.4", forRemoval = true)
public int getRowsExpected() {
return this.rowsExpected;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 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.
@@ -72,7 +72,7 @@ public class DataSourceTransactionManagerTests {
protected DataSource ds = mock();
protected ConnectionProxy con = mock();
protected Connection con = mock();
protected DataSourceTransactionManager tm;
@@ -81,7 +81,6 @@ public class DataSourceTransactionManagerTests {
void setup() throws Exception {
tm = createTransactionManager(ds);
given(ds.getConnection()).willReturn(con);
given(con.getTargetConnection()).willThrow(new UnsupportedOperationException());
}
protected DataSourceTransactionManager createTransactionManager(DataSource ds) {
@@ -1074,9 +1073,9 @@ public class DataSourceTransactionManagerTests {
Connection tCon = dsProxy.getConnection();
tCon.getWarnings();
tCon.clearWarnings();
assertThat(((ConnectionProxy) tCon).getTargetConnection()).isEqualTo(con);
assertThat(((ConnectionProxy) dsProxy.getConnection()).getTargetConnection()).isEqualTo(con);
// should be ignored
tCon.close();
dsProxy.getConnection().close();
}
catch (SQLException ex) {
throw new UncategorizedSQLException("", "", ex);
@@ -1110,9 +1109,9 @@ public class DataSourceTransactionManagerTests {
Connection tCon = dsProxy.getConnection();
assertThatExceptionOfType(SQLException.class).isThrownBy(tCon::getWarnings);
tCon.clearWarnings();
assertThat(((ConnectionProxy) tCon).getTargetConnection()).isEqualTo(con);
assertThat(((ConnectionProxy) dsProxy.getConnection()).getTargetConnection()).isEqualTo(con);
// should be ignored
tCon.close();
dsProxy.getConnection().close();
}
catch (SQLException ex) {
throw new UncategorizedSQLException("", "", ex);
@@ -1128,42 +1127,6 @@ public class DataSourceTransactionManagerTests {
verify(con).close();
}
@Test
void testTransactionAwareDataSourceProxyWithEarlyConnection() throws Exception {
given(ds.getConnection()).willReturn(mock(Connection.class), con);
given(con.getAutoCommit()).willReturn(true);
given(con.getWarnings()).willThrow(new SQLException());
TransactionAwareDataSourceProxy dsProxy = new TransactionAwareDataSourceProxy(ds);
dsProxy.setLazyTransactionalConnections(false);
Connection tCon = dsProxy.getConnection();
TransactionTemplate tt = new TransactionTemplate(tm);
assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse();
tt.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
// something transactional
assertThat(DataSourceUtils.getConnection(ds)).isEqualTo(con);
try {
// should close the early Connection obtained before the transaction
tCon.close();
}
catch (SQLException ex) {
throw new UncategorizedSQLException("", "", ex);
}
}
});
assertThat(TransactionSynchronizationManager.hasResource(ds)).isFalse();
InOrder ordered = inOrder(con);
ordered.verify(con).setAutoCommit(false);
ordered.verify(con).commit();
ordered.verify(con).setAutoCommit(true);
verify(con).close();
}
@Test
void testTransactionAwareDataSourceProxyWithSuspension() throws Exception {
given(con.getAutoCommit()).willReturn(true);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 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.
@@ -163,7 +163,6 @@ class SqlQueryTests {
}
@Test
@SuppressWarnings("removal")
void testStringQueryWithResults() throws Exception {
String[] dbResults = new String[] { "alpha", "beta", "charlie" };
given(resultSet.next()).willReturn(true, true, true, false);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 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.
@@ -178,8 +178,7 @@ public class MockCookie extends Cookie {
cookie.setComment(extractAttributeValue(attribute, setCookieHeader));
}
else if (!attribute.isEmpty()) {
String[] nameAndValue = extractOptionalAttributeNameAndValue(attribute, setCookieHeader);
cookie.setAttribute(nameAndValue[0], nameAndValue[1]);
cookie.setAttribute(attribute, extractOptionalAttributeValue(attribute, setCookieHeader));
}
}
return cookie;
@@ -192,9 +191,9 @@ public class MockCookie extends Cookie {
return nameAndValue[1];
}
private static String[] extractOptionalAttributeNameAndValue(String attribute, String header) {
private static String extractOptionalAttributeValue(String attribute, String header) {
String[] nameAndValue = attribute.split("=");
return (nameAndValue.length == 2 ? nameAndValue : new String[] {attribute, ""});
return nameAndValue.length == 2 ? nameAndValue[1] : "";
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 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.
@@ -686,12 +686,7 @@ public class MockHttpServletResponse implements HttpServletResponse {
@Override
public void setHeader(String name, @Nullable String value) {
if (value == null) {
this.headers.remove(name);
}
else {
setHeaderValue(name, value);
}
setHeaderValue(name, value);
}
@Override
@@ -743,17 +738,14 @@ public class MockHttpServletResponse implements HttpServletResponse {
}
else if (HttpHeaders.CONTENT_LANGUAGE.equalsIgnoreCase(name)) {
String contentLanguages = value.toString();
// only set the locale if we replace the header or if there was none before
if (replaceHeader || !this.headers.containsKey(HttpHeaders.CONTENT_LANGUAGE)) {
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_LANGUAGE, contentLanguages);
Locale language = headers.getContentLanguage();
this.locale = language != null ? language : Locale.getDefault();
doAddHeaderValue(HttpHeaders.CONTENT_LANGUAGE, contentLanguages, replaceHeader);
}
else {
doAddHeaderValue(HttpHeaders.CONTENT_LANGUAGE, contentLanguages, false);
}
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_LANGUAGE, contentLanguages);
Locale language = headers.getContentLanguage();
setLocale(language != null ? language : Locale.getDefault());
// Since setLocale() sets the Content-Language header to the given
// single Locale, we have to explicitly set the Content-Language header
// to the user-provided value.
doAddHeaderValue(HttpHeaders.CONTENT_LANGUAGE, contentLanguages, true);
return true;
}
else if (HttpHeaders.SET_COOKIE.equalsIgnoreCase(name)) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 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.
@@ -43,20 +43,19 @@ import org.springframework.util.ObjectUtils;
/**
* {@code TestContextAnnotationUtils} is a collection of utility methods that
* complements the standard support already available in {@link MergedAnnotations},
* {@link AnnotationUtils}, and {@link AnnotatedElementUtils}, while transparently
* honoring {@link NestedTestConfiguration @NestedTestConfiguration} semantics.
* complements the standard support already available in {@link AnnotationUtils}
* and {@link AnnotatedElementUtils}, while transparently honoring
* {@link NestedTestConfiguration @NestedTestConfiguration} semantics.
*
* <p>Mainly for internal use within the <em>Spring TestContext Framework</em>
* but also supported for third-party integrations with the TestContext framework.
*
* <p>Whereas {@code MergedAnnotations}, {@code AnnotationUtils} and
* {@code AnnotatedElementUtils} provide utilities for <em>getting</em> or
* <em>finding</em> annotations, {@code TestContextAnnotationUtils} goes a step
* further by providing support for determining the <em>root class</em> on which
* an annotation is declared, either directly or indirectly via a
* <em>composed annotation</em>. This additional information is encapsulated in
* an {@link AnnotationDescriptor}.
* <p>Whereas {@code AnnotationUtils} and {@code AnnotatedElementUtils} provide
* utilities for <em>getting</em> or <em>finding</em> annotations,
* {@code TestContextAnnotationUtils} goes a step further by providing support
* for determining the <em>root class</em> on which an annotation is declared,
* either directly or indirectly via a <em>composed annotation</em>. This
* additional information is encapsulated in an {@link AnnotationDescriptor}.
*
* <p>The additional information provided by an {@code AnnotationDescriptor} is
* required by the <em>Spring TestContext Framework</em> in order to be able to
@@ -68,11 +67,7 @@ import org.springframework.util.ObjectUtils;
* example, {@link ContextConfiguration#inheritLocations}.
*
* @author Sam Brannen
* @since 5.3
* @see MergedAnnotations
* @see MergedAnnotations.Search
* @see MergedAnnotations.Search#withEnclosingClasses(Predicate)
* @see #searchEnclosingClass(Class)
* @since 5.3, though originally since 4.0 as {@code org.springframework.test.util.MetaAnnotationUtils}
* @see AnnotationUtils
* @see AnnotatedElementUtils
* @see AnnotationDescriptor
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,9 +22,8 @@ package org.springframework.test.context;
* the listener is registered.
*
* <p>Note that not all testing frameworks support all lifecycle callbacks defined
* in this API. For example, the {@link #beforeTestExecution(TestContext)
* beforeTestExecution} and {@link #afterTestExecution(TestContext)
* afterTestExecution} callbacks are not supported in conjunction with JUnit 4 when
* in this API. For example, {@link #beforeTestExecution} and
* {@link #afterTestExecution} are not supported in conjunction with JUnit 4 when
* using the {@link org.springframework.test.context.junit4.rules.SpringMethodRule
* SpringMethodRule}.
*
@@ -42,35 +41,6 @@ package org.springframework.test.context;
* {@link org.springframework.core.annotation.Order @Order} annotation. See
* {@link TestContextBootstrapper#getTestExecutionListeners()} for details.
*
* <h3>Wrapping Behavior for Listeners</h3>
*
* <p>The {@link TestContextManager} guarantees <em>wrapping</em> behavior for
* multiple registered listeners that implement lifecycle callbacks such as
* {@link #beforeTestClass(TestContext) beforeTestClass},
* {@link #afterTestClass(TestContext) afterTestClass},
* {@link #beforeTestMethod(TestContext) beforeTestMethod},
* {@link #afterTestMethod(TestContext) afterTestMethod},
* {@link #beforeTestExecution(TestContext) beforeTestExecution}, and
* {@link #afterTestExecution(TestContext) afterTestExecution}. This means that,
* given two listeners {@code Listener1} and {@code Listener2} with {@code Listener1}
* registered before {@code Listener2}, any <em>before</em> callbacks implemented
* by {@code Listener1} are guaranteed to be invoked <strong>before</strong> any
* <em>before</em> callbacks implemented by {@code Listener2}. Similarly, given
* the same two listeners registered in the same order, any <em>after</em>
* callbacks implemented by {@code Listener1} are guaranteed to be invoked
* <strong>after</strong> any <em>after</em> callbacks implemented by
* {@code Listener2}. {@code Listener1} is therefore said to <em>wrap</em>
* {@code Listener2}.
*
* <p>For a concrete example, consider the relationship between the
* {@link org.springframework.test.context.transaction.TransactionalTestExecutionListener
* TransactionalTestExecutionListener} and the
* {@link org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener
* SqlScriptsTestExecutionListener}. The {@code SqlScriptsTestExecutionListener}
* is registered after the {@code TransactionalTestExecutionListener}, so that
* SQL scripts are executed within a transaction managed by the
* {@code TransactionalTestExecutionListener}.
*
* <h3>Registering TestExecutionListener Implementations</h3>
*
* <p>A {@code TestExecutionListener} can be registered explicitly for a test class,
@@ -130,8 +100,6 @@ public interface TestExecutionListener {
* the class.
* <p>This method should be called immediately before framework-specific
* <em>before class</em> lifecycle callbacks.
* <p>See the {@linkplain TestExecutionListener class-level documentation}
* for details on wrapping behavior for lifecycle callbacks.
* <p>The default implementation is <em>empty</em>. Can be overridden by
* concrete classes as necessary.
* @param testContext the test context for the test; never {@code null}
@@ -150,8 +118,6 @@ public interface TestExecutionListener {
* {@link org.springframework.test.context.junit4.rules.SpringMethodRule
* SpringMethodRule}). In any case, this method must be called prior to any
* framework-specific lifecycle callbacks.
* <p>See the {@linkplain TestExecutionListener class-level documentation}
* for details on wrapping behavior for listeners.
* <p>The default implementation is <em>empty</em>. Can be overridden by
* concrete classes as necessary.
* @param testContext the test context for the test; never {@code null}
@@ -171,8 +137,6 @@ public interface TestExecutionListener {
* this method might be something like {@code beforeTestSetUp} or
* {@code beforeEach}; however, it is unfortunately impossible to rename
* this method due to backward compatibility concerns.
* <p>See the {@linkplain TestExecutionListener class-level documentation}
* for details on wrapping behavior for lifecycle callbacks.
* <p>The default implementation is <em>empty</em>. Can be overridden by
* concrete classes as necessary.
* @param testContext the test context in which the test method will be
@@ -192,8 +156,6 @@ public interface TestExecutionListener {
* or logging purposes.
* <p>This method <strong>must</strong> be called after framework-specific
* <em>before</em> lifecycle callbacks.
* <p>See the {@linkplain TestExecutionListener class-level documentation}
* for details on wrapping behavior for lifecycle callbacks.
* <p>The default implementation is <em>empty</em>. Can be overridden by
* concrete classes as necessary.
* @param testContext the test context in which the test method will be
@@ -214,8 +176,6 @@ public interface TestExecutionListener {
* or logging purposes.
* <p>This method <strong>must</strong> be called before framework-specific
* <em>after</em> lifecycle callbacks.
* <p>See the {@linkplain TestExecutionListener class-level documentation}
* for details on wrapping behavior for lifecycle callbacks.
* <p>The default implementation is <em>empty</em>. Can be overridden by
* concrete classes as necessary.
* @param testContext the test context in which the test method will be
@@ -240,8 +200,6 @@ public interface TestExecutionListener {
* this method might be something like {@code afterTestTearDown} or
* {@code afterEach}; however, it is unfortunately impossible to rename
* this method due to backward compatibility concerns.
* <p>See the {@linkplain TestExecutionListener class-level documentation}
* for details on wrapping behavior for lifecycle callbacks.
* <p>The default implementation is <em>empty</em>. Can be overridden by
* concrete classes as necessary.
* @param testContext the test context in which the test method was
@@ -259,8 +217,6 @@ public interface TestExecutionListener {
* the class.
* <p>This method should be called immediately after framework-specific
* <em>after class</em> lifecycle callbacks.
* <p>See the {@linkplain TestExecutionListener class-level documentation}
* for details on wrapping behavior for lifecycle callbacks.
* <p>The default implementation is <em>empty</em>. Can be overridden by
* concrete classes as necessary.
* @param testContext the test context for the test; never {@code null}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2022 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.
@@ -16,7 +16,6 @@
package org.springframework.test.context.aot;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
@@ -26,9 +25,9 @@ import org.springframework.test.context.SmartContextLoader;
/**
* Strategy interface for loading an {@link ApplicationContext} for build-time
* {@linkplain #loadContextForAotProcessing(MergedContextConfiguration, RuntimeHints)
* AOT processing} as well as run-time {@linkplain #loadContextForAotRuntime
* AOT execution} for an integration test managed by the Spring TestContext Framework.
* {@linkplain #loadContextForAotProcessing AOT processing} as well as run-time
* {@linkplain #loadContextForAotRuntime AOT execution} for an integration test
* managed by the Spring TestContext Framework.
*
* <p>{@code AotContextLoader} is an extension of the {@link SmartContextLoader}
* SPI that allows a context loader to optionally provide ahead-of-time (AOT)
@@ -43,30 +42,6 @@ import org.springframework.test.context.SmartContextLoader;
*/
public interface AotContextLoader extends SmartContextLoader {
/**
* Load a new {@link ApplicationContext} for AOT build-time processing based
* on the supplied {@link MergedContextConfiguration}, configure the context,
* and return the context.
* <p>The default implementation of this method throws an
* {@link UnsupportedOperationException}. Note, however, that the framework
* invokes {@link #loadContextForAotProcessing(MergedContextConfiguration, RuntimeHints)}
* as of Spring Framework 6.2.4.
* @param mergedConfig the merged context configuration to use to load the
* application context
* @return a new {@code GenericApplicationContext}
* @throws ContextLoadException if context loading failed
* @see #loadContextForAotProcessing(MergedContextConfiguration, RuntimeHints)
* @see #loadContextForAotRuntime(MergedContextConfiguration, ApplicationContextInitializer)
* @deprecated as of Spring Framework 6.2.4, in favor of
* {@link #loadContextForAotProcessing(MergedContextConfiguration, RuntimeHints)};
* to be removed in Spring Framework 8.0
*/
@Deprecated(since = "6.2.4", forRemoval = true)
default ApplicationContext loadContextForAotProcessing(MergedContextConfiguration mergedConfig) throws Exception {
throw new UnsupportedOperationException(
"Invoke loadContextForAotProcessing(MergedContextConfiguration, RuntimeHints) instead");
}
/**
* Load a new {@link ApplicationContext} for AOT build-time processing based
* on the supplied {@link MergedContextConfiguration}, configure the context,
@@ -90,23 +65,13 @@ public interface AotContextLoader extends SmartContextLoader {
* throw new ContextLoadException(context, ex);
* }
* </pre>
* <p>For backward compatibility, the default implementation of this method
* delegates to {@link #loadContextForAotProcessing(MergedContextConfiguration)}.
* Note, however, that the framework only invokes this method as of Spring
* Framework 6.2.4.
* @param mergedConfig the merged context configuration to use to load the
* application context
* @param runtimeHints the runtime hints
* @return a new {@code GenericApplicationContext}
* @throws ContextLoadException if context loading failed
* @since 6.2.4
* @see #loadContextForAotRuntime(MergedContextConfiguration, ApplicationContextInitializer)
*/
default ApplicationContext loadContextForAotProcessing(MergedContextConfiguration mergedConfig,
RuntimeHints runtimeHints) throws Exception {
return loadContextForAotProcessing(mergedConfig);
}
ApplicationContext loadContextForAotProcessing(MergedContextConfiguration mergedConfig) throws Exception;
/**
* Load a new {@link ApplicationContext} for AOT run-time execution based on
@@ -133,7 +98,7 @@ public interface AotContextLoader extends SmartContextLoader {
* be applied to the context in order to recreate bean definitions
* @return a new {@code GenericApplicationContext}
* @throws ContextLoadException if context loading failed
* @see #loadContextForAotProcessing(MergedContextConfiguration, RuntimeHints)
* @see #loadContextForAotProcessing(MergedContextConfiguration)
*/
ApplicationContext loadContextForAotRuntime(MergedContextConfiguration mergedConfig,
ApplicationContextInitializer<ConfigurableApplicationContext> initializer) throws Exception;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 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.
@@ -331,7 +331,7 @@ public class TestContextAotGenerator {
* create {@link GenericApplicationContext GenericApplicationContexts}.
* @throws TestContextAotException if an error occurs while loading the application
* context or if one of the prerequisites is not met
* @see AotContextLoader#loadContextForAotProcessing(MergedContextConfiguration, RuntimeHints)
* @see AotContextLoader#loadContextForAotProcessing(MergedContextConfiguration)
*/
private GenericApplicationContext loadContextForAotProcessing(
MergedContextConfiguration mergedConfig) throws TestContextAotException {
@@ -345,7 +345,7 @@ public class TestContextAotGenerator {
if (contextLoader instanceof AotContextLoader aotContextLoader) {
try {
ApplicationContext context = aotContextLoader.loadContextForAotProcessing(mergedConfig, this.runtimeHints);
ApplicationContext context = aotContextLoader.loadContextForAotProcessing(mergedConfig);
if (context instanceof GenericApplicationContext gac) {
return gac;
}
@@ -88,7 +88,7 @@ public class MockitoResetTestExecutionListener extends AbstractTestExecutionList
*/
@Override
public int getOrder() {
return ORDER;
return Ordered.LOWEST_PRECEDENCE - 100;
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2023 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.
@@ -19,7 +19,6 @@ package org.springframework.test.context.support;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
@@ -226,13 +225,8 @@ public abstract class AbstractDelegatingSmartContextLoader implements AotContext
* {@code ApplicationContext} from the supplied merged context configuration
* @since 6.0
* @see AotContextLoader#loadContextForAotProcessing(MergedContextConfiguration)
* @deprecated as of Spring Framework 6.2.4, in favor of
* {@link #loadContextForAotProcessing(MergedContextConfiguration, RuntimeHints)};
* to be removed in Spring Framework 8.0
*/
@Deprecated(since = "6.2.4", forRemoval = true)
@Override
@SuppressWarnings("removal")
public final ApplicationContext loadContextForAotProcessing(MergedContextConfiguration mergedConfig) throws Exception {
AotContextLoader loader = getAotContextLoader(mergedConfig);
if (logger.isTraceEnabled()) {
@@ -242,33 +236,6 @@ public abstract class AbstractDelegatingSmartContextLoader implements AotContext
return loader.loadContextForAotProcessing(mergedConfig);
}
/**
* Delegates to an appropriate candidate {@code SmartContextLoader} to load
* an {@link ApplicationContext} for AOT processing.
* <p>Delegation is based on explicit knowledge of the implementations of the
* default loaders. See {@link #loadContext(MergedContextConfiguration)} for
* details.
* @param mergedConfig the merged context configuration to use to load the application context
* @param runtimeHints the runtime hints
* @return a new application context
* @throws IllegalArgumentException if the supplied merged configuration is {@code null}
* @throws IllegalStateException if neither candidate loader is capable of loading an
* {@code ApplicationContext} from the supplied merged context configuration
* @since 6.2.4
* @see AotContextLoader#loadContextForAotProcessing(MergedContextConfiguration, RuntimeHints)
*/
@Override
public final ApplicationContext loadContextForAotProcessing(MergedContextConfiguration mergedConfig,
RuntimeHints runtimeHints) throws Exception {
AotContextLoader loader = getAotContextLoader(mergedConfig);
if (logger.isTraceEnabled()) {
logger.trace("Delegating to %s to load context for AOT processing for %s"
.formatted(name(loader), mergedConfig));
}
return loader.loadContextForAotProcessing(mergedConfig, runtimeHints);
}
/**
* Delegates to an appropriate candidate {@code SmartContextLoader} to load
* an {@link ApplicationContext} for AOT run-time execution.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2022 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.
@@ -21,7 +21,6 @@ import java.util.Arrays;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
@@ -127,41 +126,10 @@ public abstract class AbstractGenericContextLoader extends AbstractContextLoader
* @throws Exception if context loading failed
* @since 6.0
* @see AotContextLoader#loadContextForAotProcessing(MergedContextConfiguration)
* @deprecated as of Spring Framework 6.2.4, in favor of
* {@link #loadContextForAotProcessing(MergedContextConfiguration, RuntimeHints)};
* to be removed in Spring Framework 8.0
*/
@Deprecated(since = "6.2.4", forRemoval = true)
@Override
@SuppressWarnings("removal")
public final GenericApplicationContext loadContextForAotProcessing(MergedContextConfiguration mergedConfig)
throws Exception {
return loadContext(mergedConfig, true);
}
/**
* Load a {@link GenericApplicationContext} for AOT build-time processing based
* on the supplied {@link MergedContextConfiguration}.
* <p>In contrast to {@link #loadContext(MergedContextConfiguration)}, this
* method does not
* {@linkplain org.springframework.context.ConfigurableApplicationContext#refresh()
* refresh} the {@code ApplicationContext} or
* {@linkplain org.springframework.context.ConfigurableApplicationContext#registerShutdownHook()
* register a JVM shutdown hook} for it. Otherwise, this method implements
* behavior identical to {@link #loadContext(MergedContextConfiguration)}.
* @param mergedConfig the merged context configuration to use to load the
* application context
* @param runtimeHints the runtime hints
* @return a new application context
* @throws Exception if context loading failed
* @since 6.2.4
* @see AotContextLoader#loadContextForAotProcessing(MergedContextConfiguration, RuntimeHints)
*/
@Override
public final GenericApplicationContext loadContextForAotProcessing(MergedContextConfiguration mergedConfig,
RuntimeHints runtimeHints) throws Exception {
return loadContext(mergedConfig, true);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2023 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.
@@ -20,7 +20,6 @@ import jakarta.servlet.ServletContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextInitializer;
@@ -42,9 +41,9 @@ import org.springframework.web.context.support.GenericWebApplicationContext;
* Abstract, generic extension of {@link AbstractContextLoader} that loads a
* {@link GenericWebApplicationContext}.
*
* <p>Instances of concrete subclasses are invoked via the
* <p>If instances of concrete subclasses are invoked via the
* {@link org.springframework.test.context.SmartContextLoader SmartContextLoader}
* SPI, and the context will be loaded from the {@link MergedContextConfiguration}
* SPI, the context will be loaded from the {@link MergedContextConfiguration}
* provided to {@link #loadContext(MergedContextConfiguration)}. In such cases, a
* {@code SmartContextLoader} will decide whether to load the context from
* <em>locations</em> or <em>annotated classes</em>. Note that {@code
@@ -59,6 +58,7 @@ import org.springframework.web.context.support.GenericWebApplicationContext;
* @author Phillip Webb
* @since 3.2
* @see #loadContext(MergedContextConfiguration)
* @see #loadContext(String...)
*/
public abstract class AbstractGenericWebContextLoader extends AbstractContextLoader implements AotContextLoader {
@@ -121,41 +121,10 @@ public abstract class AbstractGenericWebContextLoader extends AbstractContextLoa
* @throws Exception if context loading failed
* @since 6.0
* @see AotContextLoader#loadContextForAotProcessing(MergedContextConfiguration)
* @deprecated as of Spring Framework 6.2.4, in favor of
* {@link #loadContextForAotProcessing(MergedContextConfiguration, RuntimeHints)};
* to be removed in Spring Framework 8.0
*/
@Deprecated(since = "6.2.4", forRemoval = true)
@Override
@SuppressWarnings("removal")
public final GenericWebApplicationContext loadContextForAotProcessing(MergedContextConfiguration mergedConfig)
throws Exception {
return loadContext(mergedConfig, true);
}
/**
* Load a {@link GenericWebApplicationContext} for AOT build-time processing based
* on the supplied {@link MergedContextConfiguration}.
* <p>In contrast to {@link #loadContext(MergedContextConfiguration)}, this
* method does not
* {@linkplain org.springframework.context.ConfigurableApplicationContext#refresh()
* refresh} the {@code ApplicationContext} or
* {@linkplain org.springframework.context.ConfigurableApplicationContext#registerShutdownHook()
* register a JVM shutdown hook} for it. Otherwise, this method implements
* behavior identical to {@link #loadContext(MergedContextConfiguration)}.
* @param mergedConfig the merged context configuration to use to load the
* application context
* @param runtimeHints the runtime hints
* @return a new web application context
* @throws Exception if context loading failed
* @since 6.2.4
* @see AotContextLoader#loadContextForAotProcessing(MergedContextConfiguration, RuntimeHints)
*/
@Override
public final GenericWebApplicationContext loadContextForAotProcessing(MergedContextConfiguration mergedConfig,
RuntimeHints runtimeHints) throws Exception {
return loadContext(mergedConfig, true);
}
@@ -219,7 +188,7 @@ public abstract class AbstractGenericWebContextLoader extends AbstractContextLoa
* register a JVM shutdown hook for it
* @return a new web application context
* @see org.springframework.test.context.SmartContextLoader#loadContext(MergedContextConfiguration)
* @see org.springframework.test.context.aot.AotContextLoader#loadContextForAotProcessing(MergedContextConfiguration, RuntimeHints)
* @see org.springframework.test.context.aot.AotContextLoader#loadContextForAotProcessing(MergedContextConfiguration)
*/
private GenericWebApplicationContext loadContext(
MergedContextConfiguration mergedConfig, boolean forAotProcessing) throws Exception {
@@ -130,7 +130,6 @@ open class MockHttpServletRequestDsl(private val builder: AbstractMockHttpServle
var queryParams: MultiValueMap<String, String>? = null
/**
* @since 6.2.3
* @see [MockHttpServletRequestBuilder.formField]
*/
fun formField(name: String, vararg values: String) {
@@ -138,7 +137,6 @@ open class MockHttpServletRequestDsl(private val builder: AbstractMockHttpServle
}
/**
* @since 6.2.3
* @see [MockHttpServletRequestBuilder.formFields]
*/
var formFields: MultiValueMap<String, String>? = null
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2020 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.
@@ -71,7 +71,7 @@ class ContentResultMatchersDsl internal constructor (private val actions: Result
/**
* @see ContentResultMatchers.string
*/
fun string(matcher: Matcher<in String>) {
fun string(matcher: Matcher<String>) {
actions.andExpect(matchers.string(matcher))
}
@@ -99,14 +99,14 @@ class ContentResultMatchersDsl internal constructor (private val actions: Result
/**
* @see ContentResultMatchers.node
*/
fun node(matcher: Matcher<in Node>) {
fun node(matcher: Matcher<Node>) {
actions.andExpect(matchers.node(matcher))
}
/**
* @see ContentResultMatchers.source
*/
fun source(matcher: Matcher<in Source>) {
fun source(matcher: Matcher<Source>) {
actions.andExpect(matchers.source(matcher))
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 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.
@@ -68,8 +68,8 @@ class MockCookieTests {
assertCookie(cookie, "SESSION", "123");
}
@Test
@SuppressWarnings("removal")
@Test
void parseHeaderWithAttributes() {
MockCookie cookie = MockCookie.parse("SESSION=123; Domain=example.com; Max-Age=60; " +
"Expires=Tue, 8 Oct 2019 19:50:00 GMT; Path=/; Secure; HttpOnly; Partitioned; SameSite=Lax");
@@ -87,19 +87,6 @@ class MockCookieTests {
assertThat(cookie.getComment()).isNull();
}
@Test // gh-34575
void parseHeaderWithOptionalAttributes() {
MockCookie cookie = MockCookie.parse("SESSION=123; HttpOnly; Version=1; Partitioned; Secure");
assertCookie(cookie, "SESSION", "123");
assertThat(cookie.isHttpOnly()).isTrue();
assertThat(cookie.getSecure()).isTrue();
assertThat(cookie.isPartitioned()).isTrue();
assertThat(cookie.getAttribute("Partitioned")).isEmpty();
assertThat(cookie.getAttribute("Version")).isEqualTo("1");
assertThat(cookie.getAttribute("BOGUS")).isNull();
}
@ParameterizedTest
@ValueSource(strings = {"0", "bogus"})
void parseHeaderWithInvalidExpiresAttribute(String expiresValue) {
@@ -222,13 +209,10 @@ class MockCookieTests {
void setPartitioned() {
MockCookie cookie = new MockCookie("SESSION", "123");
assertThat(cookie.isPartitioned()).isFalse();
assertThat(cookie.getAttribute("Partitioned")).isNull();
cookie.setPartitioned(true);
assertThat(cookie.isPartitioned()).isTrue();
assertThat(cookie.getAttribute("Partitioned")).isEmpty();
cookie.setPartitioned(false);
assertThat(cookie.isPartitioned()).isFalse();
assertThat(cookie.getAttribute("Partitioned")).isNull();
}
}
@@ -88,18 +88,6 @@ class MockHttpServletResponseTests {
assertThat(response.containsHeader(headerName)).isFalse();
}
@ParameterizedTest
@ValueSource(strings = {
CONTENT_TYPE,
CONTENT_LANGUAGE,
"X-Test-Header"
})
void removeHeaderIfNullValue(String headerName) {
response.addHeader(headerName, "test");
response.setHeader(headerName, null);
assertThat(response.containsHeader(headerName)).isFalse();
}
@Test // gh-26493
void setLocaleWithNullValue() {
assertThat(response.getLocale()).isEqualTo(Locale.getDefault());
@@ -641,12 +629,4 @@ class MockHttpServletResponseTests {
assertThat(response.getContentAsString()).isEqualTo(content);
}
@Test // gh-34488
void shouldAddMultipleContentLanguage() {
response.addHeader("Content-Language", "en");
response.addHeader("Content-Language", "fr");
assertThat(response.getHeaders("Content-Language")).contains("en", "fr");
assertThat(response.getLocale()).isEqualTo(Locale.ENGLISH);
}
}
@@ -1,49 +0,0 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.aot;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.test.context.support.AbstractContextLoader;
/**
* @author Sam Brannen
* @since 6.2.4
*/
class AbstractAotContextLoader extends AbstractContextLoader implements AotContextLoader {
@Override
public final GenericApplicationContext loadContext(MergedContextConfiguration mergedConfig) {
return new StaticApplicationContext();
}
@Override
public final GenericApplicationContext loadContextForAotRuntime(MergedContextConfiguration mergedConfig,
ApplicationContextInitializer<ConfigurableApplicationContext> initializer) {
return loadContext(mergedConfig);
}
@Override
protected final String getResourceSuffix() {
throw new UnsupportedOperationException();
}
}
@@ -1,87 +0,0 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.aot;
import java.lang.reflect.Method;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.springframework.aot.generate.InMemoryGeneratedFiles;
import org.springframework.aot.hint.ExecutableMode;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.test.context.MergedContextConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.aot.hint.predicate.RuntimeHintsPredicates.reflection;
/**
* Tests for registering run-time hints within an {@link AotContextLoader}, tested
* via the {@link TestContextAotGenerator}.
*
* @author Sam Brannen
* @since 6.2.4
*/
class AotContextLoaderRuntimeHintsTests {
@Test // gh-34513
void aotContextLoaderCanRegisterRuntimeHints() {
RuntimeHints runtimeHints = new RuntimeHints();
TestContextAotGenerator generator = new TestContextAotGenerator(new InMemoryGeneratedFiles(), runtimeHints);
generator.processAheadOfTime(Stream.of(TestCase.class));
assertThat(reflection().onMethod(ConfigWithMain.class, "main").invoke()).accepts(runtimeHints);
}
@ContextConfiguration(classes = ConfigWithMain.class, loader = RuntimeHintsAwareAotContextLoader.class)
static class TestCase {
}
@Configuration(proxyBeanMethods = false)
static class ConfigWithMain {
public static void main(String[] args) {
// Mimics main() method for Spring Boot app
}
}
static class RuntimeHintsAwareAotContextLoader extends AbstractAotContextLoader {
@Override
public void processContextConfiguration(ContextConfigurationAttributes configAttributes) {
/* no-op */
}
@Override
public GenericApplicationContext loadContextForAotProcessing(MergedContextConfiguration mergedConfig,
RuntimeHints runtimeHints) throws Exception {
// Mimics SpringBootContextLoader
Method mainMethod = mergedConfig.getClasses()[0].getMethod("main", String[].class);
runtimeHints.reflection().registerMethod(mainMethod, ExecutableMode.INVOKE);
return loadContext(mergedConfig);
}
}
}
@@ -1,109 +0,0 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.aot;
import java.lang.reflect.Method;
import org.junit.jupiter.api.Test;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.test.context.MergedContextConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.never;
import static org.mockito.BDDMockito.spy;
import static org.mockito.BDDMockito.then;
/**
* Unit tests for {@link AotContextLoader}.
*
* @author Sam Brannen
* @since 6.2.4
*/
class AotContextLoaderTests {
/**
* Verifies that a legacy {@link AotContextLoader} which only overrides
* {@link AotContextLoader#loadContextForAotProcessing(MergedContextConfiguration)
* is still supported.
*/
@Test // gh-34513
@SuppressWarnings("removal")
void legacyAotContextLoader() throws Exception {
// Prerequisites
assertDeclaringClasses(LegacyAotContextLoader.class, LegacyAotContextLoader.class, AotContextLoader.class);
AotContextLoader loader = spy(new LegacyAotContextLoader());
MergedContextConfiguration mergedConfig = new MergedContextConfiguration(getClass(), null, null, null, loader);
loader.loadContextForAotProcessing(mergedConfig, new RuntimeHints());
then(loader).should().loadContextForAotProcessing(mergedConfig);
}
/**
* Verifies that a modern {@link AotContextLoader} which only overrides
* {@link AotContextLoader#loadContextForAotProcessing(MergedContextConfiguration, RuntimeHints)
* is supported.
*/
@Test // gh-34513
@SuppressWarnings("removal")
void runtimeHintsAwareAotContextLoader() throws Exception {
// Prerequisites
assertDeclaringClasses(RuntimeHintsAwareAotContextLoader.class, AotContextLoader.class, RuntimeHintsAwareAotContextLoader.class);
AotContextLoader loader = spy(new RuntimeHintsAwareAotContextLoader());
MergedContextConfiguration mergedConfig = new MergedContextConfiguration(getClass(), null, null, null, loader);
loader.loadContextForAotProcessing(mergedConfig, new RuntimeHints());
then(loader).should(never()).loadContextForAotProcessing(mergedConfig);
}
private static void assertDeclaringClasses(Class<? extends AotContextLoader> loaderClass,
Class<?> declaringClassForLegacyMethod, Class<?> declaringClassForNewMethod) throws Exception {
Method legacyMethod = loaderClass.getMethod("loadContextForAotProcessing", MergedContextConfiguration.class);
Method newMethod = loaderClass.getMethod("loadContextForAotProcessing", MergedContextConfiguration.class, RuntimeHints.class);
assertThat(legacyMethod.getDeclaringClass()).isEqualTo(declaringClassForLegacyMethod);
assertThat(newMethod.getDeclaringClass()).isEqualTo(declaringClassForNewMethod);
}
private static class LegacyAotContextLoader extends AbstractAotContextLoader {
@Override
@SuppressWarnings("removal")
public GenericApplicationContext loadContextForAotProcessing(MergedContextConfiguration mergedConfig) {
return loadContext(mergedConfig);
}
}
private static class RuntimeHintsAwareAotContextLoader extends AbstractAotContextLoader {
@Override
public GenericApplicationContext loadContextForAotProcessing(MergedContextConfiguration mergedConfig,
RuntimeHints runtimeHints) {
return loadContext(mergedConfig);
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2023 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.
@@ -38,7 +38,7 @@ import static org.assertj.core.api.Assertions.assertThatNoException;
* @author Sam Brannen
* @since 6.1
*/
class TestPropertySourceRuntimeHintsTests {
class TestPropertySourceRuntimeHintsTests extends AbstractAotTests {
private final RuntimeHints runtimeHints = new RuntimeHints();
@@ -93,7 +93,7 @@ class TestPropertySourceRuntimeHintsTests {
static class WildcardLocationPatternTestCase {
}
@Configuration(proxyBeanMethods = false)
@Configuration
static class Config {
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 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.
@@ -20,7 +20,6 @@ import java.util.Arrays;
import org.junit.jupiter.api.Test;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.test.context.MergedContextConfiguration;
@@ -80,7 +79,7 @@ class AnnotationConfigContextLoaderTests {
AnnotatedFooConfigInnerClassTestCase.class, EMPTY_STRING_ARRAY,
new Class<?>[] {AnnotatedFooConfigInnerClassTestCase.FooConfig.class},
EMPTY_STRING_ARRAY, contextLoader);
ConfigurableApplicationContext context = contextLoader.loadContextForAotProcessing(mergedConfig, new RuntimeHints());
ConfigurableApplicationContext context = contextLoader.loadContextForAotProcessing(mergedConfig);
assertThat(context.isActive()).as("ApplicationContext is active").isFalse();
assertThat(Arrays.stream(context.getBeanDefinitionNames())).anyMatch(name -> name.contains("FooConfig"));
context.close();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 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.
@@ -21,7 +21,6 @@ import java.util.Arrays;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
@@ -182,7 +181,7 @@ class DelegatingSmartContextLoaderTests {
private void assertApplicationContextLoadsForAotProcessing(MergedContextConfiguration mergedConfig,
String expectedBeanDefName) throws Exception {
ApplicationContext context = loader.loadContextForAotProcessing(mergedConfig, new RuntimeHints());
ApplicationContext context = loader.loadContextForAotProcessing(mergedConfig);
assertThat(context).isInstanceOf(ConfigurableApplicationContext.class);
ConfigurableApplicationContext cac = (ConfigurableApplicationContext) context;
assertThat(cac.isActive()).as("ApplicationContext is active").isFalse();
@@ -1,68 +0,0 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.web.servlet.result
import io.mockk.mockk
import org.hamcrest.text.CharSequenceLength.*
import org.junit.jupiter.api.Test
import org.springframework.mock.web.MockHttpServletRequest
import org.springframework.mock.web.MockHttpServletResponse
import org.springframework.test.web.servlet.*
import org.springframework.web.servlet.FlashMap
import org.springframework.web.servlet.ModelAndView
import java.nio.charset.StandardCharsets
/**
* Tests for [ContentResultMatchersDsl].
*
* @author Dmitry Sulman
*/
class ContentResultMatchersDslTests {
val mockMvc = mockk<MockMvc>()
@Test
fun `ContentResultMatchersDsl#string accepts Matcher parameterized with String supertype`() {
getStubResultActionsDsl("some string")
.andExpect { content { string(hasLength(11)) } }
}
private fun getStubResultActionsDsl(content: String): ResultActionsDsl {
val resultActions = object : ResultActions {
override fun andExpect(matcher: ResultMatcher): ResultActions {
matcher.match(getStubMvcResult(content))
return this
}
override fun andDo(handler: ResultHandler): ResultActions {
throw UnsupportedOperationException()
}
override fun andReturn(): MvcResult {
throw UnsupportedOperationException()
}
}
return ResultActionsDsl(resultActions, mockMvc)
}
private fun getStubMvcResult(content: String): StubMvcResult {
val response = MockHttpServletResponse()
response.outputStream.write(content.toByteArray(StandardCharsets.UTF_8))
return StubMvcResult(MockHttpServletRequest(), Any(), emptyArray(), Exception(), ModelAndView(), FlashMap(), response)
}
}
@@ -90,7 +90,6 @@ class HttpComponentsClientHttpResponse extends AbstractClientHttpResponse {
.maxAge(getMaxAgeSeconds(cookie))
.secure(cookie.isSecure())
.httpOnly(cookie.containsAttribute("httponly"))
.partitioned(cookie.containsAttribute("partitioned"))
.sameSite(cookie.getAttribute("samesite"))
.build());
}
@@ -148,7 +148,6 @@ class ReactorClientHttpResponse implements ClientHttpResponse {
.secure(cookie.isSecure())
.httpOnly(cookie.isHttpOnly())
.sameSite(getSameSite(cookie))
.partitioned(getPartitioned(cookie))
.build()));
return CollectionUtils.unmodifiableMultiValueMap(result);
}
@@ -161,13 +160,6 @@ class ReactorClientHttpResponse implements ClientHttpResponse {
return null;
}
private static boolean getPartitioned(Cookie cookie) {
if (cookie instanceof DefaultCookie defaultCookie) {
return defaultCookie.isPartitioned();
}
return false;
}
/**
* Called by {@link ReactorClientHttpConnector} when a cancellation is detected
* but the content has not been subscribed to. If the subscription never
@@ -100,7 +100,6 @@ final class MultipartParser extends BaseSubscriber<DataBuffer> {
return Flux.create(sink -> {
MultipartParser parser = new MultipartParser(sink, boundary, maxHeadersSize, headersCharset);
sink.onCancel(parser::onSinkCancel);
sink.onRequest(l -> parser.requestBuffer());
buffers.subscribe(parser);
});
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2022 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.
@@ -93,6 +93,8 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs, CodecConfigure
private static final boolean protobufPresent;
static final boolean synchronossMultipartPresent;
static final boolean nettyByteBufPresent;
static final boolean netty5BufferPresent;
@@ -110,6 +112,7 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs, CodecConfigure
jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader);
jaxb2Present = ClassUtils.isPresent("jakarta.xml.bind.Binder", classLoader);
protobufPresent = ClassUtils.isPresent("com.google.protobuf.Message", classLoader);
synchronossMultipartPresent = ClassUtils.isPresent("org.synchronoss.cloud.nio.multipart.NioMultipartParser", classLoader);
nettyByteBufPresent = ClassUtils.isPresent("io.netty.buffer.ByteBuf", classLoader);
netty5BufferPresent = ClassUtils.isPresent("io.netty5.buffer.Buffer", classLoader);
kotlinSerializationCborPresent = ClassUtils.isPresent("kotlinx.serialization.cbor.Cbor", classLoader);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 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.
@@ -43,7 +43,6 @@ public class JettyCoreHttpHandlerAdapter extends Handler.Abstract.NonBlocking {
this.httpHandler = httpHandler;
}
public void setDataBufferFactory(JettyDataBufferFactory dataBufferFactory) {
Assert.notNull(dataBufferFactory, "DataBufferFactory must not be null");
this.dataBufferFactory = dataBufferFactory;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 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.
@@ -61,7 +61,6 @@ class JettyCoreServerHttpRequest extends AbstractServerHttpRequest {
this.request = request;
}
@Override
protected MultiValueMap<String, HttpCookie> initCookies() {
List<org.eclipse.jetty.http.HttpCookie> httpCookies = Request.getCookies(this.request);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 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.
@@ -59,10 +59,10 @@ class JettyCoreServerHttpResponse extends AbstractServerHttpResponse implements
super(dataBufferFactory, new HttpHeaders(new JettyHeadersAdapter(response.getHeaders())));
this.response = response;
// Remove all existing cookies from the response and add them to the cookie map, to be added back later
for (ListIterator<HttpField> it = this.response.getHeaders().listIterator(); it.hasNext();) {
HttpField field = it.next();
if (field instanceof HttpCookieUtils.SetCookieHttpField setCookieHttpField) {
// remove all existing cookies from the response and add them to the cookie map, to be added back later
for (ListIterator<HttpField> i = this.response.getHeaders().listIterator(); i.hasNext(); ) {
HttpField f = i.next();
if (f instanceof HttpCookieUtils.SetCookieHttpField setCookieHttpField) {
HttpCookie httpCookie = setCookieHttpField.getHttpCookie();
ResponseCookie responseCookie = ResponseCookie.from(httpCookie.getName(), httpCookie.getValue())
.httpOnly(httpCookie.isHttpOnly())
@@ -72,8 +72,8 @@ class JettyCoreServerHttpResponse extends AbstractServerHttpResponse implements
.secure(httpCookie.isSecure())
.partitioned(httpCookie.isPartitioned())
.build();
addCookie(responseCookie);
it.remove();
this.addCookie(responseCookie);
i.remove();
}
}
}
@@ -94,9 +94,7 @@ class JettyCoreServerHttpResponse extends AbstractServerHttpResponse implements
@Override
protected void applyStatusCode() {
HttpStatusCode status = getStatusCode();
if (status != null){
this.response.setStatus(status.value());
}
this.response.setStatus(status == null ? 0 : status.value());
}
@Override
@@ -105,7 +103,7 @@ class JettyCoreServerHttpResponse extends AbstractServerHttpResponse implements
@Override
protected void applyCookies() {
getCookies().values().stream()
this.getCookies().values().stream()
.flatMap(List::stream)
.forEach(cookie -> Response.addCookie(this.response, new ResponseHttpCookie(cookie)));
}
@@ -167,10 +165,12 @@ class JettyCoreServerHttpResponse extends AbstractServerHttpResponse implements
private final ResponseCookie responseCookie;
ResponseHttpCookie(ResponseCookie responseCookie) {
this.responseCookie = responseCookie;
}
@Override
public String getName() {
return this.responseCookie.getName();
@@ -235,6 +235,7 @@ class JettyCoreServerHttpResponse extends AbstractServerHttpResponse implements
public Map<String, String> getAttributes() {
return Collections.emptyMap();
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 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.
@@ -44,19 +44,13 @@ import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.ResourceHttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.cbor.KotlinSerializationCborHttpMessageConverter;
import org.springframework.http.converter.cbor.MappingJackson2CborHttpMessageConverter;
import org.springframework.http.converter.feed.AtomFeedHttpMessageConverter;
import org.springframework.http.converter.feed.RssChannelHttpMessageConverter;
import org.springframework.http.converter.json.GsonHttpMessageConverter;
import org.springframework.http.converter.json.JsonbHttpMessageConverter;
import org.springframework.http.converter.json.KotlinSerializationJsonHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.protobuf.KotlinSerializationProtobufHttpMessageConverter;
import org.springframework.http.converter.smile.MappingJackson2SmileHttpMessageConverter;
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
import org.springframework.http.converter.yaml.MappingJackson2YamlHttpMessageConverter;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -90,13 +84,13 @@ final class DefaultRestClientBuilder implements RestClient.Builder {
// message factories
private static final boolean romePresent;
private static final boolean jaxb2Present;
private static final boolean jackson2Present;
private static final boolean jackson2XmlPresent;
private static final boolean gsonPresent;
private static final boolean jsonbPresent;
private static final boolean kotlinSerializationJsonPresent;
private static final boolean jackson2SmilePresent;
@@ -104,16 +98,6 @@ final class DefaultRestClientBuilder implements RestClient.Builder {
private static final boolean jackson2YamlPresent;
private static final boolean gsonPresent;
private static final boolean jsonbPresent;
private static final boolean kotlinSerializationCborPresent;
private static final boolean kotlinSerializationJsonPresent;
private static final boolean kotlinSerializationProtobufPresent;
static {
ClassLoader loader = DefaultRestClientBuilder.class.getClassLoader();
@@ -123,19 +107,14 @@ final class DefaultRestClientBuilder implements RestClient.Builder {
reactorNettyClientPresent = ClassUtils.isPresent("reactor.netty.http.client.HttpClient", loader);
jdkClientPresent = ClassUtils.isPresent("java.net.http.HttpClient", loader);
romePresent = ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", loader);
jaxb2Present = ClassUtils.isPresent("jakarta.xml.bind.Binder", loader);
jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", loader) &&
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", loader);
jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", loader);
gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", loader);
jsonbPresent = ClassUtils.isPresent("jakarta.json.bind.Jsonb", loader);
kotlinSerializationJsonPresent = ClassUtils.isPresent("kotlinx.serialization.json.Json", loader);
jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", loader);
jackson2CborPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory", loader);
jackson2YamlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.yaml.YAMLFactory", loader);
gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", loader);
jsonbPresent = ClassUtils.isPresent("jakarta.json.bind.Jsonb", loader);
kotlinSerializationCborPresent = ClassUtils.isPresent("kotlinx.serialization.cbor.Cbor", loader);
kotlinSerializationJsonPresent = ClassUtils.isPresent("kotlinx.serialization.json.Json", loader);
kotlinSerializationProtobufPresent = ClassUtils.isPresent("kotlinx.serialization.protobuf.ProtoBuf", loader);
}
@Nullable
@@ -444,28 +423,11 @@ final class DefaultRestClientBuilder implements RestClient.Builder {
private List<HttpMessageConverter<?>> initMessageConverters() {
if (this.messageConverters == null) {
this.messageConverters = new ArrayList<>();
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(new StringHttpMessageConverter());
this.messageConverters.add(new ResourceHttpMessageConverter(false));
this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
if (romePresent) {
this.messageConverters.add(new AtomFeedHttpMessageConverter());
this.messageConverters.add(new RssChannelHttpMessageConverter());
}
if (jackson2XmlPresent) {
this.messageConverters.add(new MappingJackson2XmlHttpMessageConverter());
}
else if (jaxb2Present) {
this.messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
}
if (kotlinSerializationProtobufPresent) {
this.messageConverters.add(new KotlinSerializationProtobufHttpMessageConverter());
}
if (kotlinSerializationJsonPresent) {
this.messageConverters.add(new KotlinSerializationJsonHttpMessageConverter());
}
@@ -478,18 +440,12 @@ final class DefaultRestClientBuilder implements RestClient.Builder {
else if (jsonbPresent) {
this.messageConverters.add(new JsonbHttpMessageConverter());
}
if (jackson2SmilePresent) {
this.messageConverters.add(new MappingJackson2SmileHttpMessageConverter());
}
if (jackson2CborPresent) {
this.messageConverters.add(new MappingJackson2CborHttpMessageConverter());
}
else if (kotlinSerializationCborPresent) {
this.messageConverters.add(new KotlinSerializationCborHttpMessageConverter());
}
if (jackson2YamlPresent) {
this.messageConverters.add(new MappingJackson2YamlHttpMessageConverter());
}
@@ -148,7 +148,6 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
static {
ClassLoader classLoader = RestTemplate.class.getClassLoader();
romePresent = ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", classLoader);
jaxb2Present = ClassUtils.isPresent("jakarta.xml.bind.Binder", classLoader);
jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
@@ -187,6 +186,7 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(new StringHttpMessageConverter());
this.messageConverters.add(new ResourceHttpMessageConverter(false));
this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
if (romePresent) {
@@ -208,7 +208,6 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
if (kotlinSerializationJsonPresent) {
this.messageConverters.add(new KotlinSerializationJsonHttpMessageConverter());
}
if (jackson2Present) {
this.messageConverters.add(new MappingJackson2HttpMessageConverter());
}
@@ -186,7 +186,6 @@ public class HandlerMethod extends AnnotatedMethod {
* validation annotations.
* <p>Subclasses can override this to ensure that a HandlerMethod is of the
* same type if re-created.
* @since 6.2.3
*/
protected HandlerMethod(HandlerMethod handlerMethod, @Nullable Object handler, boolean initValidateFlags) {
super(handlerMethod);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 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.
@@ -146,12 +146,7 @@ public class HandlerMethodValidationException extends ResponseStatusException im
}
RequestBody requestBody = param.getParameterAnnotation(RequestBody.class);
if (requestBody != null) {
if (result instanceof ParameterErrors errors) {
visitor.requestBody(requestBody, errors);
}
else {
visitor.requestBodyValidationResult(requestBody, result);
}
visitor.requestBody(requestBody, asErrors(result));
continue;
}
RequestHeader requestHeader = param.getParameterAnnotation(RequestHeader.class);
@@ -221,20 +216,6 @@ public class HandlerMethodValidationException extends ResponseStatusException im
*/
void requestBody(RequestBody requestBody, ParameterErrors errors);
/**
* An additional {@code @RequestBody} callback for validation failures
* for constraints on the method parameter. For example:
* <pre class="code">
* &#064;RequestBody List&lt;&#064;NotEmpty String&gt; ids
* </pre>
* Handle results for {@code @RequestBody} method parameters.
* @param requestBody the annotation declared on the parameter
* @param result the validation result
* @since 6.2.4
*/
default void requestBodyValidationResult(RequestBody requestBody, ParameterValidationResult result) {
}
/**
* Handle results for {@code @RequestHeader} method parameters.
* @param requestHeader the annotation declared on the parameter
@@ -493,7 +493,7 @@ public class HttpRequestValues {
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(uriTemplate);
for (Map.Entry<String, List<String>> entry : requestParams.entrySet()) {
String nameVar = "queryParam-" + entry.getKey().replace(":", "%3A"); // suppress treatment as regex
String nameVar = entry.getKey().replace(":", "%3A"); // suppress treatment as regex
uriVars.put(nameVar, entry.getKey());
for (int j = 0; j < entry.getValue().size(); j++) {
String valueVar = nameVar + "[" + j + "]";
@@ -164,13 +164,7 @@ public class ContentCachingResponseWrapper extends HttpServletResponseWrapper {
@Override
public void setHeader(String name, String value) {
if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name)) {
if (value != null) {
this.contentLength = toContentLengthInt(Long.parseLong(value));
}
else {
this.contentLength = null;
super.setHeader(name, null);
}
this.contentLength = toContentLengthInt(Long.parseLong(value));
}
else {
super.setHeader(name, value);
@@ -24,7 +24,6 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.NestedExceptionUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@@ -101,11 +100,7 @@ public class DisconnectedClientHelper {
* <li>IOException "Broken pipe" or "connection reset by peer"
* </ul>
*/
public static boolean isClientDisconnectedException(@Nullable Throwable ex) {
if (ex == null) {
return false;
}
public static boolean isClientDisconnectedException(Throwable ex) {
Throwable currentEx = ex;
Throwable lastEx = null;
while (currentEx != null && currentEx != lastEx) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 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.
@@ -41,7 +41,6 @@ import okhttp3.mockwebserver.RecordedRequest;
import okio.Buffer;
import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Named;
import org.junit.jupiter.api.Test;
@@ -202,25 +201,6 @@ class ClientHttpConnectorTests {
.verifyComplete();
}
@ParameterizedConnectorTest
void partitionedCookieSupport(ClientHttpConnector connector) {
Assumptions.assumeFalse(connector instanceof JettyClientHttpConnector, "Jetty client does not support partitioned cookies");
Assumptions.assumeFalse(connector instanceof JdkClientHttpConnector, "JDK client does not support partitioned cookies");
prepareResponse(response -> {
response.setResponseCode(200);
response.addHeader("Set-Cookie", "id=test; Partitioned;");
});
Mono<ClientHttpResponse> futureResponse =
connector.connect(HttpMethod.GET, this.server.url("/").uri(), ReactiveHttpOutputMessage::setComplete);
StepVerifier.create(futureResponse)
.assertNext(response -> {
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getCookies().getFirst("id").isPartitioned()).isTrue();
}
)
.verifyComplete();
}
@Test
void disableCookieWithHttpComponents() {
ClientHttpConnector connector = new HttpComponentsClientHttpConnector(
@@ -270,17 +270,6 @@ class ContentCachingResponseWrapperTests {
.withMessageContaining(overflow);
}
@Test
void setContentLengthNull() {
MockHttpServletResponse response = new MockHttpServletResponse();
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);
responseWrapper.setContentLength(1024);
responseWrapper.setHeader(CONTENT_LENGTH, null);
assertThat(response.getHeaderNames()).doesNotContain(CONTENT_LENGTH);
assertThat(responseWrapper.getHeader(CONTENT_LENGTH)).isNull();
}
private void assertHeader(HttpServletResponse response, String header, int value) {
assertHeader(response, header, Integer.toString(value));

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