Compare commits

..

1 Commits

Author SHA1 Message Date
Stéphane Nicoll c997d4018d Release v7.0.7 2026-04-17 08:32:10 +02:00
272 changed files with 1032 additions and 3287 deletions
+1 -1
View File
@@ -74,7 +74,7 @@ configure([rootProject] + javaProjects) { project ->
//"https://jakarta.ee/specifications/platform/11/apidocs/",
"https://docs.hibernate.org/orm/7.2/javadocs/",
"https://www.quartz-scheduler.org/api/2.3.0/",
"https://hc.apache.org/httpcomponents-client-5.6.x/5.6/httpclient5/apidocs/",
"https://hc.apache.org/httpcomponents-client-5.6.x/current/httpclient5/apidocs/",
"https://projectreactor.io/docs/core/release/api/",
"https://projectreactor.io/docs/test/release/api/",
"https://junit.org/junit4/javadoc/4.13.2/",
@@ -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("13.4.2");
checkstyle.setToolVersion("13.4.0");
checkstyle.getConfigDirectory().set(project.getRootProject().file("src/checkstyle"));
String version = SpringJavaFormatPlugin.class.getPackage().getImplementationVersion();
DependencySet checkstyleDependencies = project.getConfigurations().getByName("checkstyle").getDependencies();
-4
View File
@@ -20,10 +20,6 @@ antora {
]
}
node {
version = '24.15.0'
}
tasks.named("generateAntoraYml") {
asciidocAttributes = project.provider( {
return ["spring-version": project.version ]
-2
View File
@@ -198,7 +198,6 @@
*** xref:web/webmvc/mvc-uri-building.adoc[]
*** xref:web/webmvc/mvc-ann-async.adoc[]
*** xref:web/webmvc/mvc-range.adoc[]
*** xref:web/webmvc/mvc-data-binding.adoc[]
*** xref:web/webmvc-cors.adoc[]
*** xref:web/webmvc-versioning.adoc[]
*** xref:web/webmvc/mvc-ann-rest-exceptions.adoc[]
@@ -296,7 +295,6 @@
*** xref:web/webflux-functional.adoc[]
*** xref:web/webflux/uri-building.adoc[]
*** xref:web/webflux/range.adoc[]
*** xref:web/webflux/data-binding.adoc[]
*** xref:web/webflux-cors.adoc[]
*** xref:web/webflux-versioning.adoc[]
*** xref:web/webflux/ann-rest-exceptions.adoc[]
@@ -74,11 +74,6 @@ expressions used in XML bean definitions, `@Value`, etc.
| The mode to use when compiling expressions for the
xref:core/expressions/evaluation.adoc#expressions-compiler-configuration[Spring Expression Language].
| `spring.expression.maxOperations`
| The default maximum number of operations permitted during
xref:core/expressions/evaluation.adoc#expressions-parser-configuration[Spring Expression Language]
expression evaluation.
| `spring.getenv.ignore`
| Instructs Spring to ignore operating system environment variables if a Spring
`Environment` property -- for example, a placeholder in a configuration String -- isn't
@@ -610,19 +610,6 @@ Kotlin::
See the {spring-framework-api}/context/annotation/Conditional.html[`@Conditional`]
javadoc for more detail.
[NOTE]
====
A `@Conditional` annotation declared on an enclosing `@Configuration` class is only
applied to the registration of a nested `@Configuration` class if the nested class is
reached through the parser's recursion from its enclosing class, or via `@Import`. If a
nested class is discovered independently of its enclosing class — for example, via
`@ComponentScan` or by directly registering it against the application context — it is
processed using only its own `@Conditional` annotations. Thus, if you wish to ensure that
the same `@Conditional` annotations apply in such scenarios, you must redeclare the
relevant annotations on the nested class, or extract them into a composed annotation
which you apply to both the enclosing class and the nested class.
====
[[beans-java-combining]]
== Combining Java and XML Configuration
@@ -395,16 +395,6 @@ set a JVM system property or Spring property named `spring.context.expression.ma
to the maximum expression length needed by your application (see
xref:appendix.adoc#appendix-spring-properties[Supported Spring Properties]).
Similarly, the number of operations performed during the evaluation of a SpEL expression
cannot exceed 10,000 by default; however, the `maxOperations` value is configurable. If
you create a `SpelExpressionParser` programmatically (the recommend approach), you can
specify a custom `maxOperations` value when creating the `SpelParserConfiguration` that
you provide to the `SpelExpressionParser`. If you are not able to configure an explicit
value for `maxOperations` via `SpelParserConfiguration`, you can set a JVM system
property or Spring property named `spring.expression.maxOperations` to the maximum number
of operations required by your application (see
xref:appendix.adoc#appendix-spring-properties[Supported Spring Properties]).
[[expressions-spel-compilation]]
== SpEL Compilation
@@ -22,7 +22,7 @@ where all the underlying resources have to participate in the service-level tran
NOTE: By default, a participating transaction joins the characteristics of the outer scope,
silently ignoring the local isolation level, timeout value, or read-only flag (if any).
Consider switching the `validateExistingTransaction` flag to `true` on your transaction
Consider switching the `validateExistingTransactions` flag to `true` on your transaction
manager if you want isolation level declarations to be rejected when participating in
an existing transaction with a different isolation level. This non-lenient mode also
rejects read-only mismatches (that is, an inner read-write transaction that tries to participate
@@ -1179,7 +1179,7 @@ built-in decorators to suppress 404 exceptions and return a `ResponseEntity` wit
[source,java,indent=0,subs="verbatim,quotes"]
----
// For RestClient
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(restClientAdapter)
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(restCqlientAdapter)
.exchangeAdapterDecorator(NotFoundRestClientAdapterDecorator::new)
.build();
@@ -3,7 +3,7 @@
In the previous sections, we have seen how to use MockMvc in conjunction with the raw
HtmlUnit APIs. In this section, we use additional abstractions within the Selenium
https://www.selenium.dev/documentation/webdriver/[WebDriver] to make things even easier.
https://docs.seleniumhq.org/projects/webdriver/[WebDriver] to make things even easier.
[[mockmvc-server-htmlunit-webdriver-why]]
== Why WebDriver and MockMvc?
@@ -12,8 +12,8 @@ We can already use HtmlUnit and MockMvc, so why would we want to use WebDriver?
Selenium WebDriver provides a very elegant API that lets us easily organize our code. To
better show how it works, we explore an example in this section.
NOTE: Despite being a part of https://www.selenium.dev/documentation/[Selenium],
WebDriver does not require a Selenium Server to run your tests.
NOTE: Despite being a part of https://docs.seleniumhq.org/[Selenium], WebDriver does not
require a Selenium Server to run your tests.
Suppose we need to ensure that a message is created properly. The tests involve finding
the HTML form input elements, filling them out, and making various assertions.
@@ -308,7 +308,7 @@ interested. These are of type `WebElement`. WebDriver's
https://github.com/SeleniumHQ/selenium/wiki/PageFactory[`PageFactory`] lets us remove a
lot of code from the HtmlUnit version of `CreateMessagePage` by automatically resolving
each `WebElement`. The
https://www.selenium.dev/selenium/docs/api/java/org/openqa/selenium/support/PageFactory.html#initElements-org.openqa.selenium.WebDriver-java.lang.Class-[`PageFactory#initElements(WebDriver,Class<T>)`]
https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/PageFactory.html#initElements-org.openqa.selenium.WebDriver-java.lang.Class-[`PageFactory#initElements(WebDriver,Class<T>)`]
method automatically resolves each `WebElement` by using the field name and looking it up
by the `id` or `name` of the element within the HTML page.
<3> We can use the
@@ -352,7 +352,7 @@ interested. These are of type `WebElement`. WebDriver's
https://github.com/SeleniumHQ/selenium/wiki/PageFactory[`PageFactory`] lets us remove a
lot of code from the HtmlUnit version of `CreateMessagePage` by automatically resolving
each `WebElement`. The
https://www.selenium.dev/selenium/docs/api/java/org/openqa/selenium/support/PageFactory.html#initElements-org.openqa.selenium.WebDriver-java.lang.Class-[`PageFactory#initElements(WebDriver,Class<T>)`]
https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/PageFactory.html#initElements-org.openqa.selenium.WebDriver-java.lang.Class-[`PageFactory#initElements(WebDriver,Class<T>)`]
method automatically resolves each `WebElement` by using the field name and looking it up
by the `id` or `name` of the element within the HTML page.
<3> We can use the
@@ -107,4 +107,7 @@ Kotlin::
[[webflux-ann-initbinder-model-design]]
NOTE: For more guidance on model design, please see xref:web/webflux/data-binding.adoc[Data Binding].
== Model Design
[.small]#xref:web/webmvc/mvc-controller/ann-initbinder.adoc#mvc-ann-initbinder-model-design[See equivalent in the Servlet stack]#
include::partial$web/web-data-binding-model-design.adoc[]
@@ -1,36 +0,0 @@
[[webflux-data-binding]]
= Data Binding
:page-section-summary-toc: 1
[.small]#xref:web/webmvc/mvc-data-binding.adoc[See equivalent in the Servlet stack]#
Data binding is a mechanism that binds string parameters onto an object graph with type conversion.
It is a core mechanism of the Spring Framework that helps with application configuration.
In web applications it makes it easy to access query parameters and form data through richly typed objects rather than through maps of string values.
To learn more about the data binding mechanism, including constructor and setter binding, property name syntax, type conversion,
and more, see xref:core/validation/data-binding.adoc[Data binding] in the Core Technologies section.
For annotated controllers, data binding applies to a
xref:web/webflux/controller/ann-methods/modelattrib-method-args.adoc[@ModelAttribute] method argument.
For functional endpoints, use the `bind` method of xref:web/webflux-functional.adoc#webflux-fn-request[ServerRequest].
TIP: For browser applications with annotated controllers, you can use
xref:web/webflux/controller/ann-modelattrib-methods.adoc[@ModelAttribute methods]
to initialize additional model attributes for use in rendered views.
Each request uses a separate `WebDataBinder` instance.
For annotated controllers, this instance can be customized through
xref:web/webflux/controller/ann-initbinder.adoc[@InitBinder methods] within a controller, or
across controllers through xref:web/webmvc/mvc-controller/ann-advice.adoc[Controller Advice].
For functional endpoints, use overloaded `ServerRequest.bind` methods.
[[webflux-data-binding-design]]
== Model Design
[.small]#xref:web/webmvc/mvc-data-binding.adoc#mvc-data-binding-design[See equivalent in the Servlet stack]#
include::partial$web/web-data-binding-model-design.adoc[]
@@ -107,4 +107,7 @@ Kotlin::
[[mvc-ann-initbinder-model-design]]
NOTE: For more guidance on model design, please see xref:web/webmvc/mvc-data-binding.adoc[Data Binding].
== Model Design
[.small]#xref:web/webflux/controller/ann-initbinder.adoc#webflux-ann-initbinder-model-design[See equivalent in the Reactive stack]#
include::partial$web/web-data-binding-model-design.adoc[]
@@ -1,34 +0,0 @@
[[mvc-data-binding]]
= Data Binding
:page-section-summary-toc: 1
[.small]#xref:web/webflux/data-binding.adoc[See equivalent in the Reactive stack]#
Data binding is a mechanism that binds string parameters onto an object graph with type conversion.
It is a core mechanism of the Spring Framework that helps with application configuration.
In web applications it makes it easy to access query parameters and form data through richly typed objects rather than through maps of string values.
To learn more about the data binding mechanism, including constructor and setter binding, property name syntax, type conversion,
and more, see xref:core/validation/data-binding.adoc[Data binding] in the Core Technologies section.
For annotated controllers, data binding applies to a
xref:web/webmvc/mvc-controller/ann-methods/modelattrib-method-args.adoc[@ModelAttribute] method argument.
For functional endpoints, use the `bind` method of xref:web/webmvc-functional.adoc#webmvc-fn-request[ServerRequest].
TIP: For browser applications with annotated controllers, you can use
xref:web/webmvc/mvc-controller/ann-modelattrib-methods.adoc[@ModelAttribute methods]
to initialize additional model attributes for use in rendered views.
Each request uses a separate `WebDataBinder` instance.
For annotated controllers, this instance can be customized through
xref:web/webmvc/mvc-controller/ann-initbinder.adoc[@InitBinder methods] within a controller, or
across controllers through xref:web/webmvc/mvc-controller/ann-advice.adoc[Controller Advice].
For functional endpoints, use overloaded `ServerRequest.bind` methods.
[[mvc-data-binding-design]]
== Model Design
[.small]#xref:web/webflux/data-binding.adoc#webflux-data-binding-design[See equivalent in the Reactive stack]#
include::partial$web/web-data-binding-model-design.adoc[]
@@ -1,53 +1,91 @@
Data binding involves binding untrusted input onto application objects.
For security reasons, it's crucial to ensure that input is properly constrained to expected fields only.
This section provides guidance for safe binding.
xref:core/validation/data-binding.adoc[Data binding] for web requests involves
binding request parameters to a model object. By default, request parameters can be bound
to any public property of the model object, which means malicious clients can provide
extra values for properties that exist in the model object graph, but are not expected to
be set. This is why model object design requires careful consideration.
First, prefer **immutable object design** for web binding purposes.
It is safe because a constructor naturally constrains binding to expected inputs.
You can use a Java record or a class with a primary constructor, and either can have further nested objects.
See xref:core/validation/data-binding.adoc#data-binding-constructor-binding[Constructor Binding] for details.
TIP: The model object, and its nested object graph is also sometimes referred to as a
_command object_, _form-backing object_, or _POJO_ (Plain Old Java Object).
Another option for safe binding is to use **dedicated objects** designed for the expected input.
Such objects, even if mutable, are safe because they constrain binding to the expected inputs.
A good practice is to use a _dedicated model object_ rather than exposing your domain
model such as JPA or Hibernate entities for web data binding. For example, on a form to
change an email address, create a `ChangeEmailForm` model object that declares only
the properties required for the input:
Domain objects such as JPA or Hibernate entities are generally not safe for web binding
as they likely contain more properties than the expected inputs.
For such cases, it's crucial to declare the properties to expose for binding.
[source,java,indent=0,subs="verbatim,quotes"]
----
public class ChangeEmailForm {
private String oldEmailAddress;
private String newEmailAddress;
public void setOldEmailAddress(String oldEmailAddress) {
this.oldEmailAddress = oldEmailAddress;
}
public String getOldEmailAddress() {
return this.oldEmailAddress;
}
public void setNewEmailAddress(String newEmailAddress) {
this.newEmailAddress = newEmailAddress;
}
public String getNewEmailAddress() {
return this.newEmailAddress;
}
}
----
Another good practice is to apply
xref:core/validation/data-binding.adoc#data-binding-constructor-binding[constructor binding],
which uses only the request parameters it needs for constructor arguments, and any other
input is ignored. This is in contrast to property binding which by default binds every
request parameter for which there is a matching property.
If neither a dedicated model object nor constructor binding is sufficient, and you must
use property binding, we strongly recommend registering `allowedFields` patterns (case
sensitive) on `WebDataBinder` in order to prevent unexpected properties from being set.
For example:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Controller
public class PersonController {
public class ChangeEmailController {
@InitBinder
void initBinder(WebDataBinder binder) {
// See Javadoc for supported pattern syntax
binder.setAllowedFields("firstName", "lastName", "*Address");
binder.setAllowedFields("oldEmailAddress", "newEmailAddress");
}
// @RequestMapping methods, etc.
}
----
NOTE: It is also possible to configure `disallowedFields`, but that's fragile, and
due to be https://github.com/spring-projects/spring-framework/issues/36802[deprecated] in Spring Framework 7.1.
It is easy to overlook fields or introduce additional fields over time that should also be excluded.
You can also register `disallowedFields` patterns (case insensitive). However,
"allowed" configuration is preferred over "disallowed" as it is more explicit and less
prone to mistakes.
By default, `DataBinder` applies both constructor and setter binding.
This is fine with immutable objects and dedicated objects, but for domain objects, you must
remember to set `allowedFields`. To ensure data binding is only used in declarative style where
expected inputs are explicitly declared, you can set `declarativeBinding` on `DataBinder`.
That applies constructor binding always, and setter binding conditionally if `allowedFields` is set.
The following shows how to set this flag globally, or
you can also narrow it through attributes on `ControllerAdvice`:
By default, constructor and property binding are both used. If you want to use
constructor binding only, you can set the `declarativeBinding` flag on `WebDataBinder`
through an `@InitBinder` method either locally within a controller or globally through an
`@ControllerAdvice`. Turning this flag on ensures that only constructor binding is used
and that property binding is not used unless `allowedFields` patterns are configured.
For example:
[source,java,indent=0,subs="verbatim,quotes"]
----
@ControllerAdvice
public class ControllerConfig {
@Controller
public class MyController {
@InitBinder
void initBinder(WebDataBinder binder) {
binder.setDeclarativeBinding(true);
}
// @RequestMapping methods, etc.
}
----
+5 -5
View File
@@ -1,11 +1,11 @@
{
"dependencies": {
"antora": "3.2.0-alpha.12",
"antora": "3.2.0-alpha.11",
"@antora/atlas-extension": "1.0.0-alpha.5",
"@antora/collector-extension": "1.0.3",
"@antora/collector-extension": "1.0.2",
"@asciidoctor/tabs": "1.0.0-beta.6",
"@springio/antora-extensions": "1.14.12",
"fast-xml-parser": "5.7.0",
"@springio/asciidoctor-extensions": "1.0.0-alpha.18"
"@springio/antora-extensions": "1.14.7",
"fast-xml-parser": "5.3.8",
"@springio/asciidoctor-extensions": "1.0.0-alpha.17"
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
+13 -12
View File
@@ -8,15 +8,15 @@ javaPlatform {
dependencies {
api(platform("com.fasterxml.jackson:jackson-bom:2.20.2"))
api(platform("io.micrometer:micrometer-bom:1.16.6"))
api(platform("io.netty:netty-bom:4.2.15.Final"))
api(platform("io.projectreactor:reactor-bom:2025.0.6"))
api(platform("io.micrometer:micrometer-bom:1.16.5"))
api(platform("io.netty:netty-bom:4.2.12.Final"))
api(platform("io.projectreactor:reactor-bom:2025.0.5"))
api(platform("io.rsocket:rsocket-bom:1.1.5"))
api(platform("org.apache.groovy:groovy-bom:5.0.6"))
api(platform("org.apache.logging.log4j:log4j-bom:2.26.0"))
api(platform("org.apache.groovy:groovy-bom:5.0.5"))
api(platform("org.apache.logging.log4j:log4j-bom:2.25.4"))
api(platform("org.assertj:assertj-bom:3.27.7"))
api(platform("org.eclipse.jetty:jetty-bom:12.1.9"))
api(platform("org.eclipse.jetty.ee11:jetty-ee11-bom:12.1.9"))
api(platform("org.eclipse.jetty:jetty-bom:12.1.7"))
api(platform("org.eclipse.jetty.ee11:jetty-ee11-bom:12.1.7"))
api(platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.10.2"))
api(platform("org.jetbrains.kotlinx:kotlinx-serialization-bom:1.9.0"))
api(platform("org.junit:junit-bom:6.0.3"))
@@ -96,10 +96,10 @@ dependencies {
api("org.apache.httpcomponents.client5:httpclient5:5.6")
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.4.2")
api("org.apache.poi:poi-ooxml:5.5.1")
api("org.apache.tomcat.embed:tomcat-embed-core:11.0.22")
api("org.apache.tomcat.embed:tomcat-embed-websocket:11.0.22")
api("org.apache.tomcat:tomcat-util:11.0.22")
api("org.apache.tomcat:tomcat-websocket:11.0.22")
api("org.apache.tomcat.embed:tomcat-embed-core:11.0.20")
api("org.apache.tomcat.embed:tomcat-embed-websocket:11.0.20")
api("org.apache.tomcat:tomcat-util:11.0.20")
api("org.apache.tomcat:tomcat-websocket:11.0.20")
api("org.aspectj:aspectjrt:1.9.25")
api("org.aspectj:aspectjtools:1.9.25")
api("org.aspectj:aspectjweaver:1.9.25")
@@ -120,7 +120,7 @@ dependencies {
api("org.glassfish:jakarta.el:4.0.2")
api("org.graalvm.sdk:graal-sdk:22.3.1")
api("org.hamcrest:hamcrest:3.0")
api("org.hibernate.orm:hibernate-core:7.2.17.Final")
api("org.hibernate.orm:hibernate-core:7.2.11.Final")
api("org.hibernate.validator:hibernate-validator:9.1.0.Final")
api("org.hsqldb:hsqldb:2.7.4")
api("org.htmlunit:htmlunit:4.21.0")
@@ -138,6 +138,7 @@ dependencies {
api("org.seleniumhq.selenium:selenium-java:4.41.0")
api("org.skyscreamer:jsonassert:1.5.3")
api("org.testng:testng:7.12.0")
api("org.webjars:underscorejs:1.8.3")
api("org.webjars:webjars-locator-lite:1.1.0")
api("org.xmlunit:xmlunit-assertj:2.10.4")
api("org.xmlunit:xmlunit-matchers:2.10.4")
+1 -1
View File
@@ -1,4 +1,4 @@
version=7.0.8
version=7.0.7
org.gradle.caching=true
org.gradle.jvmargs=-Xmx2048m
Binary file not shown.
+1 -3
View File
@@ -1,9 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
networkTimeout=10000
retries=0
retryBackOffMs=500
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
+1 -1
View File
@@ -57,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/2d6327017519d23b96af35865dc997fcb544fb40/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
Vendored
+21 -10
View File
@@ -23,8 +23,8 @@
@rem
@rem ##########################################################################
@rem Set local scope for the variables, and ensure extensions are enabled
setlocal EnableExtensions
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@@ -51,7 +51,7 @@ echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
"%COMSPEC%" /c exit 1
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
@@ -65,7 +65,7 @@ echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
"%COMSPEC%" /c exit 1
goto fail
:execute
@rem Setup the command line
@@ -73,10 +73,21 @@ echo location of your Java installation. 1>&2
@rem Execute Gradle
@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
@rem which allows us to clear the local environment before executing the java command
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:exitWithErrorLevel
@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
"%COMSPEC%" /c exit %ERRORLEVEL%
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
@@ -163,7 +163,7 @@ public class MethodInvocationProceedingJoinPoint implements ProceedingJoinPoint,
@Override
public String toString() {
return "execution(" + getSignature() + ")";
return "execution(" + getSignature().toString() + ")";
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* Copyright 2002-2014 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* Copyright 2002-2017 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* Copyright 2002-2014 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* Copyright 2002-2013 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* Copyright 2002-2014 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* Copyright 2002-2016 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* Copyright 2002-2015 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* Copyright 2002-2015 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* Copyright 2002-2013 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.
@@ -956,8 +956,8 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA
actualName = propertyName.substring(0, keyStart);
}
String key = propertyName.substring(keyStart + PROPERTY_KEY_PREFIX.length(), keyEnd);
if (key.length() > 1 && ((key.startsWith("'") && key.endsWith("'")) ||
(key.startsWith("\"") && key.endsWith("\"")))) {
if (key.length() > 1 && (key.startsWith("'") && key.endsWith("'")) ||
(key.startsWith("\"") && key.endsWith("\""))) {
key = key.substring(1, key.length() - 1);
}
keys.add(key);
@@ -63,14 +63,12 @@ public interface ConfigurablePropertyAccessor extends PropertyAccessor, Property
* <p>If {@code true}, a {@code null} path location will be populated
* with a default object value and traversed instead of resulting in a
* {@link NullValueInNestedPathException}.
* <p>Default is {@code false} on a plain accessor.
* @since 4.1
* <p>Default is {@code false} on a plain PropertyAccessor instance.
*/
void setAutoGrowNestedPaths(boolean autoGrowNestedPaths);
/**
* Return whether "auto-growing" of nested paths has been activated.
* @since 4.1
*/
boolean isAutoGrowNestedPaths();
@@ -154,8 +154,7 @@ public abstract class PropertyAccessorUtils {
PropertyAccessor.PROPERTY_KEY_SUFFIX, keyStart + PropertyAccessor.PROPERTY_KEY_PREFIX.length());
if (keyEnd != -1) {
String key = sb.substring(keyStart + PropertyAccessor.PROPERTY_KEY_PREFIX.length(), keyEnd);
if (key.length() > 1 && ((key.startsWith("'") && key.endsWith("'")) ||
(key.startsWith("\"") && key.endsWith("\"")))) {
if ((key.startsWith("'") && key.endsWith("'")) || (key.startsWith("\"") && key.endsWith("\""))) {
sb.delete(keyStart + 1, keyStart + 2);
sb.delete(keyEnd - 2, keyEnd - 1);
keyEnd = keyEnd - 2;
@@ -32,7 +32,6 @@ public class AotBeanProcessingException extends AotProcessingException {
private final RootBeanDefinition beanDefinition;
/**
* Create an instance with the {@link RegisteredBean} that fails to be
* processed, a detail message, and an optional root cause.
@@ -66,7 +65,6 @@ public class AotBeanProcessingException extends AotProcessingException {
return sb.toString();
}
/**
* Return the bean definition of the bean that failed to be processed.
*/
@@ -249,7 +249,7 @@ public abstract class YamlProcessor {
}
else {
// It has to be a map key in this case
result.put("[" + key + "]", value);
result.put("[" + key.toString() + "]", value);
}
});
return result;
@@ -606,7 +606,9 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
if (ex instanceof BeanCreationException bce && beanName.equals(bce.getBeanName())) {
throw bce;
}
throw new BeanCreationException(mbd.getResourceDescription(), beanName, ex.getMessage(), ex);
else {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, ex.getMessage(), ex);
}
}
if (earlySingletonExposure) {
@@ -1244,8 +1246,8 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
instance = obtainInstanceFromSupplier(supplier, beanName, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException bce && beanName.equals(bce.getBeanName())) {
throw bce;
if (ex instanceof BeansException beansException) {
throw beansException;
}
throw new BeanCreationException(beanName, "Instantiation of supplied bean failed", ex);
}
@@ -1154,19 +1154,12 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
if (mbd.isBackgroundInit()) {
Executor executor = getBootstrapExecutor();
if (executor != null) {
// Force initialization of depends-on beans in mainline thread.
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
getBean(dep);
}
}
// Force initialization of factory reference in mainline thread.
String factoryBeanName = mbd.getFactoryBeanName();
if (factoryBeanName != null) {
getBean(factoryBeanName);
}
// Instantiate current bean in background thread.
CompletableFuture<?> future = CompletableFuture.runAsync(
() -> instantiateSingletonInBackgroundThread(beanName), executor);
addSingletonFactory(beanName, () -> {
@@ -1413,8 +1413,6 @@ abstract class AbstractPropertyAccessorTests {
assertThat(accessor.getPropertyValue("map[key5[foo]].name")).isEqualTo("name8");
assertThat(accessor.getPropertyValue("map['key5[foo]'].name")).isEqualTo("name8");
assertThat(accessor.getPropertyValue("map[\"key5[foo]\"].name")).isEqualTo("name8");
assertThat(accessor.getPropertyValue("map['].name")).isEqualTo("name9");
assertThat(accessor.getPropertyValue("map[\"].name")).isEqualTo("name9");
assertThat(accessor.getPropertyValue("iterableMap[key1].name")).isEqualTo("nameC");
assertThat(accessor.getPropertyValue("iterableMap[key2][0].name")).isEqualTo("nameA");
assertThat(accessor.getPropertyValue("iterableMap[key2][1].name")).isEqualTo("nameB");
@@ -79,10 +79,6 @@ class PropertyAccessorUtilsTests {
assertThat(PropertyAccessorUtils.canonicalPropertyName("map[key1].name")).isEqualTo("map[key1].name");
assertThat(PropertyAccessorUtils.canonicalPropertyName("map['key1'].name")).isEqualTo("map[key1].name");
assertThat(PropertyAccessorUtils.canonicalPropertyName("map[\"key1\"].name")).isEqualTo("map[key1].name");
assertThat(PropertyAccessorUtils.canonicalPropertyName("map['key1]")).isEqualTo("map['key1]");
assertThat(PropertyAccessorUtils.canonicalPropertyName("map[\"key1]")).isEqualTo("map[\"key1]");
assertThat(PropertyAccessorUtils.canonicalPropertyName("map[']")).isEqualTo("map[']");
assertThat(PropertyAccessorUtils.canonicalPropertyName("map[\"]")).isEqualTo("map[\"]");
}
@Test
@@ -76,7 +76,6 @@ public class IndexedTestBean {
TestBean tb6 = new TestBean("name6", 0);
TestBean tb7 = new TestBean("name7", 0);
TestBean tb8 = new TestBean("name8", 0);
TestBean tb9 = new TestBean("name9", 0);
TestBean tbA = new TestBean("nameA", 0);
TestBean tbB = new TestBean("nameB", 0);
TestBean tbC = new TestBean("nameC", 0);
@@ -105,8 +104,6 @@ public class IndexedTestBean {
list.add(tbY);
this.map.put("key4", list);
this.map.put("key5[foo]", tb8);
this.map.put("'", tb9);
this.map.put("\"", tb9);
this.myTestBeans = new MyTestBeans(tbZ);
}
@@ -63,7 +63,7 @@ class TypeHelper {
if (type instanceof DeclaredType declaredType) {
Element enclosingElement = declaredType.asElement().getEnclosingElement();
if (enclosingElement instanceof TypeElement) {
return getQualifiedName(enclosingElement) + "$" + declaredType.asElement().getSimpleName();
return getQualifiedName(enclosingElement) + "$" + declaredType.asElement().getSimpleName().toString();
}
else {
return getQualifiedName(declaredType.asElement());
@@ -198,13 +198,13 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria
private void validateCacheOperation(AnnotatedElement ae, CacheOperation operation) {
if (StringUtils.hasText(operation.getKey()) && StringUtils.hasText(operation.getKeyGenerator())) {
throw new IllegalStateException("Invalid cache annotation configuration on '" +
ae + "'. Both 'key' and 'keyGenerator' attributes have been set. " +
ae.toString() + "'. Both 'key' and 'keyGenerator' attributes have been set. " +
"These attributes are mutually exclusive: either set the SpEL expression used to" +
"compute the key at runtime or set the name of the KeyGenerator bean to use.");
}
if (StringUtils.hasText(operation.getCacheManager()) && StringUtils.hasText(operation.getCacheResolver())) {
throw new IllegalStateException("Invalid cache annotation configuration on '" +
ae + "'. Both 'cacheManager' and 'cacheResolver' attributes have been set. " +
ae.toString() + "'. Both 'cacheManager' and 'cacheResolver' attributes have been set. " +
"These attributes are mutually exclusive: the cache manager is used to configure a" +
"default cache resolver if none is set. If a cache resolver is set, the cache manager" +
"won't be used.");
@@ -222,7 +222,7 @@ class CacheAdviceParser extends AbstractSingleBeanDefinitionParser {
if (StringUtils.hasText(builder.getKey()) && StringUtils.hasText(builder.getKeyGenerator())) {
throw new IllegalStateException("Invalid cache advice configuration on '" +
element + "'. Both 'key' and 'keyGenerator' attributes have been set. " +
element.toString() + "'. Both 'key' and 'keyGenerator' attributes have been set. " +
"These attributes are mutually exclusive: either set the SpEL expression used to" +
"compute the key at runtime or set the name of the KeyGenerator bean to use.");
}
@@ -84,7 +84,7 @@ public abstract class AbstractValueAdaptingCache implements Cache {
* @return the value to return to the user
*/
protected @Nullable Object fromStoreValue(@Nullable Object storeValue) {
if (this.allowNullValues && storeValue instanceof NullValue) {
if (this.allowNullValues && storeValue == NullValue.INSTANCE) {
return null;
}
return storeValue;
@@ -783,13 +783,16 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
});
GeneratedMethod generateMethod = generatedClass.getMethods().add("apply", method -> {
method.addJavadoc("Apply resource autowiring.");
method.addModifiers(javax.lang.model.element.Modifier.PUBLIC, javax.lang.model.element.Modifier.STATIC);
method.addModifiers(javax.lang.model.element.Modifier.PUBLIC,
javax.lang.model.element.Modifier.STATIC);
method.addParameter(RegisteredBean.class, REGISTERED_BEAN_PARAMETER);
method.addParameter(this.target, INSTANCE_PARAMETER);
method.returns(this.target);
method.addCode(generateMethodCode(generatedClass.getName(), generationContext.getRuntimeHints()));
method.addCode(generateMethodCode(generatedClass.getName(),
generationContext.getRuntimeHints()));
});
beanRegistrationCode.addInstancePostProcessor(generateMethod.toMethodReference());
registerHints(generationContext.getRuntimeHints());
}
@@ -341,18 +341,6 @@ import org.springframework.stereotype.Component;
* with the {@code @Profile} annotation to provide two options of the same bean to the
* enclosing {@code @Configuration} class.
*
* <p>A {@link Conditional @Conditional} annotation declared on an enclosing
* {@code @Configuration} class is only applied to the registration of a nested
* {@code @Configuration} class if the nested class is reached through the parser's
* recursion from its enclosing class, or via {@link Import @Import}. If a nested
* class is discovered independently of its enclosing class &mdash; for example,
* via {@link ComponentScan @ComponentScan} or by directly registering it against
* the application context &mdash; it is processed using only its own
* {@code @Conditional} annotations. Thus, if you wish to ensure that the same
* {@code @Conditional} annotations apply in such scenarios, you must redeclare
* the relevant annotations on the nested class, or extract them into a composed
* annotation which you apply to both the enclosing class and the nested class.
*
* <h2>Configuring lazy initialization</h2>
*
* <p>By default, {@code @Bean} methods will be <em>eagerly instantiated</em> at container
@@ -260,11 +260,9 @@ class ConfigurationClassParser {
return;
}
else if (configClass.isScanned()) {
if (existingClass.isImported()) {
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
// An implicitly scanned bean definition should not override an explicit import.
return;
@@ -191,7 +191,7 @@ public class InstrumentationLoadTimeWeaver implements LoadTimeWeaver {
@Override
public String toString() {
return "FilteringClassFileTransformer for: " + this.targetTransformer;
return "FilteringClassFileTransformer for: " + this.targetTransformer.toString();
}
}
@@ -177,11 +177,7 @@ final class BitsCronField extends CronField {
int next = nextSetBit(current);
if (next == -1) {
temporal = type().rollForward(temporal);
next = nextSetBit(type().get(temporal));
if (next == -1) {
temporal = type().rollForward(temporal);
next = nextSetBit(0);
}
next = nextSetBit(0);
}
if (next == current) {
return temporal;
@@ -195,12 +191,7 @@ final class BitsCronField extends CronField {
next = nextSetBit(current);
if (next == -1) {
temporal = type().rollForward(temporal);
next = nextSetBit(type().get(temporal));
if (next == -1) {
temporal = type().rollForward(temporal);
next = nextSetBit(0);
}
current = type().get(temporal);
next = nextSetBit(0);
}
}
if (count >= CronExpression.MAX_ATTEMPTS) {
@@ -19,7 +19,6 @@ package org.springframework.scheduling.support;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Objects;
import java.util.TimeZone;
import org.jspecify.annotations.Nullable;
@@ -146,13 +145,12 @@ public class CronTrigger implements Trigger {
@Override
public boolean equals(@Nullable Object other) {
return (this == other || (other instanceof CronTrigger that &&
this.expression.equals(that.expression) &&
Objects.equals(this.zoneId, that.zoneId)));
this.expression.equals(that.expression)));
}
@Override
public int hashCode() {
return Objects.hash(this.expression, this.zoneId);
return this.expression.hashCode();
}
@Override
@@ -273,9 +273,8 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
* Specify the limit for array and collection auto-growing.
* <p>Default is 256, preventing OutOfMemoryErrors in case of large indexes.
* Raise this limit if your auto-growing needs are unusually high.
* <p>Used for setter injection via {@link #bind(PropertyValues)};
* not applicable to field injection, and not to constructor binding
* via {@link #construct} either.
* <p>Used for setter/field injection via {@link #bind(PropertyValues)}, and not
* applicable to constructor binding via {@link #construct}.
* @see #initBeanPropertyAccess()
* @see org.springframework.beans.BeanWrapper#setAutoGrowCollectionLimit
*/
@@ -197,16 +197,6 @@ class BackgroundBootstrapTests {
}
}
@Test
@Timeout(10)
@EnabledForTestGroups(LONG_RUNNING)
void bootstrapWithCustomExecutorAndLazyConfig() {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(CustomExecutorLazyBeanConfig.class);
assertThat(ctx.getBeanFactory().containsSingleton("testBean1")).isTrue();
assertThat(ctx.getBeanFactory().containsSingleton("testBean2")).isTrue();
ctx.close();
}
@Configuration(proxyBeanMethods = false)
static class UnmanagedThreadBeanConfig {
@@ -552,36 +542,4 @@ class BackgroundBootstrapTests {
}
}
@Configuration(proxyBeanMethods = false)
static class CustomExecutorLazyBeanConfig {
@Bean
public ThreadPoolTaskExecutor bootstrapExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setThreadNamePrefix("Custom-");
executor.setCorePoolSize(2);
executor.initialize();
return executor;
}
@Configuration(proxyBeanMethods = false)
@Lazy
static class LazyBeanConfig {
@Bean(bootstrap = BACKGROUND)
public TestBean testBean1() throws InterruptedException {
Thread.sleep(6000);
return new TestBean();
}
@Bean(bootstrap = BACKGROUND)
@Lazy
public TestBean testBean2() throws InterruptedException {
Thread.sleep(6000);
return new TestBean();
}
}
}
}
@@ -26,7 +26,6 @@ import example.scannable.CustomComponent;
import example.scannable.CustomStereotype;
import example.scannable.DefaultNamedComponent;
import example.scannable.FooService;
import example.scannable.FooServiceImpl;
import example.scannable.MessageBean;
import example.scannable.ScopedProxyTestBean;
import example.scannable_implicitbasepackage.ComponentScanAnnotatedConfigWithImplicitBasePackage;
@@ -44,7 +43,6 @@ import org.springframework.beans.factory.annotation.CustomAutowireConfigurer;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
@@ -86,17 +84,6 @@ class ComponentScanAnnotationIntegrationTests {
assertContextContainsBean(ctx, "fooServiceImpl");
}
@Test
void controlScanWithExplicitRegistration() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.registerBeanDefinition("myFooService", new RootBeanDefinition(FooServiceImpl.class));
ctx.scan(example.scannable.PackageMarker.class.getPackage().getName());
ctx.refresh();
assertContextContainsBean(ctx, "myFooService");
assertContextContainsBean(ctx, "fooServiceImpl");
}
@Test
void viaContextRegistration() {
ApplicationContext ctx = new AnnotationConfigApplicationContext(ComponentScanAnnotatedConfig.class);
@@ -289,7 +289,7 @@ class AutowiredConfigurationTests {
@Bean
public TestBean testBean(Color color, List<Color> colors) {
return new TestBean(color + "-" + colors.get(0));
return new TestBean(color.toString() + "-" + colors.get(0).toString());
}
}
@@ -303,7 +303,7 @@ class AutowiredConfigurationTests {
return new TestBean("");
}
else {
return new TestBean(color.get() + "-" + colors.get().get(0));
return new TestBean(color.get() + "-" + colors.get().get(0).toString());
}
}
}
@@ -30,7 +30,6 @@ import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanCurrentlyInCreationException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
@@ -42,15 +41,12 @@ import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostP
import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionOverrideException;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.testfixture.beans.factory.CircularBeanRegistrar;
import org.springframework.context.testfixture.beans.factory.ImportAwareBeanRegistrar;
import org.springframework.context.testfixture.beans.factory.OverridingBeanRegistrar;
import org.springframework.context.testfixture.beans.factory.SampleBeanRegistrar;
import org.springframework.core.DecoratingProxy;
import org.springframework.core.env.ConfigurableEnvironment;
@@ -641,22 +637,6 @@ class GenericApplicationContextTests {
assertThat(context.getBean(SampleBeanRegistrar.Bar.class).foo()).isEqualTo(context.getBean(SampleBeanRegistrar.Foo.class));
}
@Test
void beanRegistrarWithCircularReference() {
GenericApplicationContext context = new GenericApplicationContext();
context.register(new CircularBeanRegistrar());
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(context::refresh)
.withRootCauseInstanceOf(BeanCurrentlyInCreationException.class);
}
@Test
void beanRegistrarWithDefinitionOverride() {
GenericApplicationContext context = new GenericApplicationContext();
context.setAllowBeanDefinitionOverriding(false);
assertThatExceptionOfType(BeanDefinitionOverrideException.class).isThrownBy(
() -> context.register(new OverridingBeanRegistrar()));
}
@Test
void importAwareBeanRegistrar() {
GenericApplicationContext context = new GenericApplicationContext();
@@ -16,7 +16,6 @@
package org.springframework.scheduling.support;
import java.time.ZonedDateTime;
import java.util.Arrays;
import org.assertj.core.api.Condition;
@@ -30,7 +29,6 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
*
* @author Arjen Poutsma
* @author Sam Brannen
* @author Brian Clozel
*/
class BitsCronFieldTests {
@@ -114,24 +112,6 @@ class BitsCronFieldTests {
.has(clear(0)).has(setRange(1, 7));
}
@Test
void nextOrSameWithMidnightGap() {
BitsCronField field = BitsCronField.parseHours("0-23/2");
ZonedDateTime last = ZonedDateTime.parse("2025-04-24T23:00:00+02:00[Africa/Cairo]");
ZonedDateTime expected = ZonedDateTime.parse("2025-04-25T02:00:00+03:00[Africa/Cairo]");
ZonedDateTime actual = field.nextOrSame(last);
assertThat(actual).isEqualTo(expected);
}
@Test
void nextOrSameWithGapAfterRollForward() {
BitsCronField field = BitsCronField.parseHours("0,2");
ZonedDateTime last = ZonedDateTime.parse("2026-03-08T01:00:00-05:00[America/New_York]");
ZonedDateTime expected = ZonedDateTime.parse("2026-03-09T00:00:00-04:00[America/New_York]");
ZonedDateTime actual = field.nextOrSame(last);
assertThat(actual).isEqualTo(expected);
}
private static Condition<BitsCronField> set(int... indices) {
return new Condition<>(String.format("set bits %s", Arrays.toString(indices))) {
@@ -1367,14 +1367,6 @@ class CronExpressionTests {
actual = cronExpression.next(last);
assertThat(actual).isNotNull();
assertThat(actual).isEqualTo(expected);
cronExpression = CronExpression.parse("0 0 */2 * * ?");
last = ZonedDateTime.parse("2025-04-24T22:00:00+02:00[Africa/Cairo]");
expected = ZonedDateTime.parse("2025-04-25T02:00:00+03:00[Africa/Cairo]");
actual = cronExpression.next(last);
assertThat(actual).isNotNull();
assertThat(actual).isEqualTo(expected);
}
@Test
@@ -16,7 +16,6 @@
package org.springframework.scheduling.support;
import java.time.ZoneId;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
@@ -747,20 +746,6 @@ class CronTriggerTests {
assertThat(nextExecutionTime).isEqualTo(this.calendar.getTime());
}
@Test
void equalsAndHashCodeConsiderZoneId() {
String expression = "0 0 9 * * *";
CronTrigger amsterdam1 = new CronTrigger(expression, ZoneId.of("Europe/Amsterdam"));
CronTrigger amsterdam2 = new CronTrigger(expression, ZoneId.of("Europe/Amsterdam"));
CronTrigger newYork = new CronTrigger(expression, ZoneId.of("America/New_York"));
assertThat(amsterdam1)
.isEqualTo(amsterdam2)
.hasSameHashCodeAs(amsterdam2)
.isNotEqualTo(newYork)
.doesNotHaveSameHashCodeAs(newYork);
}
private static void roundup(Calendar calendar) {
calendar.add(Calendar.SECOND, 1);
@@ -1,59 +0,0 @@
/*
* Copyright 2002-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* 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.context.testfixture.beans.factory;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.BeanRegistrar;
import org.springframework.beans.factory.BeanRegistry;
import org.springframework.core.env.Environment;
public class CircularBeanRegistrar implements BeanRegistrar {
@Override
public void register(BeanRegistry registry, Environment env) {
registry.registerBean("foo", Foo.class, spec -> spec.supplier(context -> new Foo(context.bean(Bar.class))));
registry.registerAlias("foo", "fooAlias");
registry.registerBean("bar", Bar.class, spec -> spec
.prototype()
.lazyInit()
.description("Custom description")
.supplier(context -> new Bar(context.bean(Foo.class))));
if (env.matchesProfiles("baz")) {
registry.registerBean(Baz.class, spec -> spec
.supplier(context -> new Baz("Hello World!")));
}
registry.registerBean(Init.class);
}
public record Foo(Bar bar) {}
public record Bar(Foo foo) {}
public record Baz(String message) {}
public static class Init {
public boolean initialized = false;
@PostConstruct
public void postConstruct() {
initialized = true;
}
}
}
@@ -1,33 +0,0 @@
/*
* Copyright 2002-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* 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.context.testfixture.beans.factory;
import org.springframework.beans.factory.BeanRegistrar;
import org.springframework.beans.factory.BeanRegistry;
import org.springframework.core.env.Environment;
public class OverridingBeanRegistrar implements BeanRegistrar {
@Override
public void register(BeanRegistry registry, Environment env) {
registry.registerBean("foo", Foo.class);
registry.registerBean("foo", Foo.class);
}
public record Foo() {}
}
@@ -24,9 +24,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import org.junit.jupiter.api.Test;
import org.springframework.beans.BeanUtils;
import org.springframework.cache.Cache;
import org.springframework.cache.support.NullValue;
import static org.assertj.core.api.Assertions.assertThat;
@@ -74,12 +72,6 @@ public abstract class AbstractCacheTests<T extends Cache> {
assertThat(cache.get(key).get()).isNull();
assertThat(cache.get(key, String.class)).isNull();
assertThat(cache.get(key, Object.class)).isNull();
cache.put(key, BeanUtils.instantiateClass(NullValue.class));
assertThat(cache.get(key)).isNotNull();
assertThat(cache.get(key).get()).isNull();
assertThat(cache.get(key, String.class)).isNull();
assertThat(cache.get(key, Object.class)).isNull();
}
@Test
@@ -36,9 +36,10 @@ public abstract class AbstractValueAdaptingCacheTests<T extends AbstractValueAda
protected void cachePutNullValueAllowNullFalse() {
T cache = getCache(false);
String key = createRandomKey();
assertThatIllegalArgumentException().isThrownBy(() -> cache.put(key, null))
.withMessageContaining(CACHE_NAME_NO_NULL)
.withMessageContaining("is configured to not allow null values but null was provided");
assertThatIllegalArgumentException().isThrownBy(() ->
cache.put(key, null))
.withMessageContaining(CACHE_NAME_NO_NULL)
.withMessageContaining("is configured to not allow null values but null was provided");
}
}
@@ -145,7 +145,7 @@ implements CallbackGenerator
private static void superHelper(CodeEmitter e, MethodInfo method, Context context)
{
if (TypeUtils.isAbstract(method.getModifiers())) {
e.throw_exception(ABSTRACT_METHOD_ERROR, method + " is abstract" );
e.throw_exception(ABSTRACT_METHOD_ERROR, method.toString() + " is abstract" );
} else {
e.load_this();
context.emitLoadArgsAndInvoke(e, method);
@@ -134,10 +134,9 @@ public abstract class CoroutinesUtils {
Object arg = args[index];
if (!(parameter.isOptional() && arg == null)) {
KType type = parameter.getType();
if (!(type.isMarkedNullable() && arg == null) &&
if (!type.isMarkedNullable() &&
type.getClassifier() instanceof KClass<?> kClass &&
KotlinDetector.isInlineClass(JvmClassMappingKt.getJavaClass(kClass)) &&
!JvmClassMappingKt.getJavaClass(kClass).isInstance(arg)) {
KotlinDetector.isInlineClass(JvmClassMappingKt.getJavaClass(kClass))) {
arg = box(kClass, arg);
}
argMap.put(parameter, arg);
@@ -167,10 +166,9 @@ public abstract class CoroutinesUtils {
private static Object box(KClass<?> kClass, @Nullable Object arg) {
KFunction<?> constructor = Objects.requireNonNull(KClasses.getPrimaryConstructor(kClass));
KType type = constructor.getParameters().get(0).getType();
if (!(type.isMarkedNullable() && arg == null) &&
if (!type.isMarkedNullable() &&
type.getClassifier() instanceof KClass<?> parameterClass &&
KotlinDetector.isInlineClass(JvmClassMappingKt.getJavaClass(parameterClass)) &&
!JvmClassMappingKt.getJavaClass(parameterClass).isInstance(arg)) {
KotlinDetector.isInlineClass(JvmClassMappingKt.getJavaClass(parameterClass))) {
arg = box(parameterClass, arg);
}
if (!KCallablesJvm.isAccessible(constructor)) {
@@ -117,8 +117,8 @@ public class MethodParameter {
* return type; 0 for the first method parameter; 1 for the second method
* parameter, etc.
* @param nestingLevel the nesting level of the target type
* (1 for the top-level type; in case of a List of Lists, 2 would indicate
* the nested List, whereas 3 would indicate the element of the nested List)
* (typically 1; for example, in case of a List of Lists, 1 would indicate the
* nested List, whereas 2 would indicate the element of the nested List)
*/
public MethodParameter(Method method, int parameterIndex, int nestingLevel) {
Assert.notNull(method, "Method must not be null");
@@ -142,8 +142,8 @@ public class MethodParameter {
* @param constructor the Constructor to specify a parameter for
* @param parameterIndex the index of the parameter
* @param nestingLevel the nesting level of the target type
* (1 for the top-level type; in case of a List of Lists, 2 would indicate
* the nested List, whereas 3 would indicate the element of the nested List)
* (typically 1; for example, in case of a List of Lists, 1 would indicate the
* nested List, whereas 2 would indicate the element of the nested List)
*/
public MethodParameter(Constructor<?> constructor, int parameterIndex, int nestingLevel) {
Assert.notNull(constructor, "Constructor must not be null");
@@ -292,8 +292,8 @@ public class MethodParameter {
/**
* Return the nesting level of the target type
* (1 for the top-level type; in case of a List of Lists, 2 would indicate
* the nested List, whereas 3 would indicate the element of the nested List).
* (typically 1; for example, in case of a List of Lists, 1 would indicate the
* nested List, whereas 2 would indicate the element of the nested List).
*/
public int getNestingLevel() {
return this.nestingLevel;
@@ -71,8 +71,6 @@ public class ReactiveAdapterRegistry {
private static final boolean MUTINY_PRESENT;
private static final boolean CONTEXT_PROPAGATION_PRESENT;
static {
ClassLoader classLoader = ReactiveAdapterRegistry.class.getClassLoader();
REACTIVE_STREAMS_PRESENT = ClassUtils.isPresent("org.reactivestreams.Publisher", classLoader);
@@ -80,7 +78,6 @@ public class ReactiveAdapterRegistry {
RXJAVA_3_PRESENT = ClassUtils.isPresent("io.reactivex.rxjava3.core.Flowable", classLoader);
COROUTINES_REACTOR_PRESENT = ClassUtils.isPresent("kotlinx.coroutines.reactor.MonoKt", classLoader);
MUTINY_PRESENT = ClassUtils.isPresent("io.smallrye.mutiny.Multi", classLoader);
CONTEXT_PROPAGATION_PRESENT = ClassUtils.isPresent("io.micrometer.context.ContextSnapshotFactory", classLoader);
}
private final List<ReactiveAdapter> adapters = new ArrayList<>();
@@ -359,9 +356,7 @@ public class ReactiveAdapterRegistry {
registry.registerReactiveType(
ReactiveTypeDescriptor.multiValue(kotlinx.coroutines.flow.Flow.class, kotlinx.coroutines.flow.FlowKt::emptyFlow),
CONTEXT_PROPAGATION_PRESENT ?
source -> kotlinx.coroutines.reactor.ReactorFlowKt.asFlux((kotlinx.coroutines.flow.Flow<?>) source, new PropagationContextElement()) :
source -> kotlinx.coroutines.reactor.ReactorFlowKt.asFlux((kotlinx.coroutines.flow.Flow<?>) source),
source -> kotlinx.coroutines.reactor.ReactorFlowKt.asFlux((kotlinx.coroutines.flow.Flow<?>) source),
kotlinx.coroutines.reactive.ReactiveFlowKt::asFlow);
}
}
@@ -1533,6 +1533,9 @@ public class ResolvableType implements Serializable {
return new ResolvableType(type, null, typeProvider, variableResolver);
}
// Purge empty entries on access since we don't have a clean-up thread or the like.
cache.purgeUnreferencedEntries();
// Check the cache - we may have a ResolvableType which has been resolved before...
ResolvableType resultType = new ResolvableType(type, typeProvider, variableResolver);
ResolvableType cachedType = cache.get(resultType);
@@ -44,7 +44,6 @@ import org.jspecify.annotations.Nullable;
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#STRICT_LOCKING_PROPERTY_NAME
* @see org.springframework.core.env.AbstractEnvironment#IGNORE_GETENV_PROPERTY_NAME
* @see org.springframework.expression.spel.SpelParserConfiguration#SPRING_EXPRESSION_COMPILER_MODE_PROPERTY_NAME
* @see org.springframework.expression.spel.SpelParserConfiguration#SPRING_EXPRESSION_MAX_OPERATIONS_PROPERTY_NAME
* @see org.springframework.jdbc.core.StatementCreatorUtils#IGNORE_GETPARAMETERTYPE_PROPERTY_NAME
* @see org.springframework.jndi.JndiLocatorDelegate#IGNORE_JNDI_PROPERTY_NAME
* @see org.springframework.objenesis.SpringObjenesis#IGNORE_OBJENESIS_PROPERTY_NAME
@@ -38,27 +38,13 @@ public final class SpringVersion {
/**
* Return the "major.minor.patch" version string of the present Spring codebase,
* Return the full version string of the present Spring codebase,
* or {@code null} if it cannot be determined.
* @see Package#getImplementationVersion()
*/
public static @Nullable String getVersion() {
Package pkg = SpringVersion.class.getPackage();
String version = (pkg != null ? pkg.getImplementationVersion() : null);
if (version != null) {
int idx = version.indexOf('.'); // after major
if (idx != -1) {
idx = version.indexOf('.', idx + 1); // after minor
if (idx != -1) {
idx = version.indexOf('.', idx + 1); // after patch
if (idx != -1) {
// Ignore anything beyond "major.minor.patch"
version = version.substring(0, idx);
}
}
}
}
return version;
return (pkg != null ? pkg.getImplementationVersion() : null);
}
}
@@ -1138,11 +1138,9 @@ public abstract class DataBufferUtils {
protected void hookOnNext(DataBuffer dataBuffer) {
try {
try (DataBuffer.ByteBufferIterator iterator = dataBuffer.readableByteBuffers()) {
while (iterator.hasNext()) {
ByteBuffer byteBuffer = iterator.next();
while (byteBuffer.hasRemaining()) {
this.channel.write(byteBuffer);
}
ByteBuffer byteBuffer = iterator.next();
while (byteBuffer.hasRemaining()) {
this.channel.write(byteBuffer);
}
}
this.sink.next(dataBuffer);
@@ -1215,11 +1213,6 @@ public abstract class DataBufferUtils {
failed(ex, attachment);
}
}
else {
iterator.close();
this.sink.next(dataBuffer);
request(1);
}
}
@Override
@@ -1243,6 +1236,7 @@ public abstract class DataBufferUtils {
@Override
public void completed(Integer written, Attachment attachment) {
DataBuffer.ByteBufferIterator iterator = attachment.iterator();
iterator.close();
long pos = this.position.addAndGet(written);
ByteBuffer byteBuffer = attachment.byteBuffer();
@@ -1252,11 +1246,9 @@ public abstract class DataBufferUtils {
}
else if (iterator.hasNext()) {
ByteBuffer next = iterator.next();
Attachment nextAttachment = new Attachment(next, attachment.dataBuffer(), iterator);
this.channel.write(next, pos, nextAttachment, this);
this.channel.write(next, pos, attachment, this);
}
else {
iterator.close();
this.sink.next(attachment.dataBuffer());
this.writing.set(false);
@@ -59,17 +59,6 @@ public class DefaultDeserializer implements Deserializer<Object> {
}
/**
* Return the {@link ClassLoader} to use for deserialization, or {@code null}
* to use the "latest user-defined ClassLoader".
* @since 6.2.19
* @see ConfigurableObjectInputStream#ConfigurableObjectInputStream(InputStream, ClassLoader)
*/
public @Nullable ClassLoader getClassLoader() {
return this.classLoader;
}
/**
* Read from the supplied {@code InputStream} and deserialize the contents
* into an object.

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