mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
93 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2c18c33ce0 | |||
| 83667f808c | |||
| 7a8917b137 | |||
| 7baa86536f | |||
| 12b44f2545 | |||
| e8f10244e3 | |||
| a1826b725c | |||
| 7add5243b9 | |||
| 9bec52b1ec | |||
| fe68e337b2 | |||
| 322eef2942 | |||
| ba0a9b8923 | |||
| 8a192eab1f | |||
| 45812cb9d0 | |||
| 95bd3f7c67 | |||
| 6ca66afc7b | |||
| 1b32d8d41d | |||
| b4a378186f | |||
| 220fcaa1e3 | |||
| 80534f9df6 | |||
| d8bc54d2e7 | |||
| 6467fca05b | |||
| cddc671a8c | |||
| 2e653246b5 | |||
| 86a68a77c4 | |||
| fe27ad24d5 | |||
| ecc847c493 | |||
| c2191e3ce2 | |||
| 175c1f9437 | |||
| 0b77c3df45 | |||
| 6985d00fce | |||
| 1e45ad8fa6 | |||
| 6fd45c118d | |||
| f9b75b02da | |||
| 84efa46e09 | |||
| b3dfba8719 | |||
| 2782a2701c | |||
| af2b96192d | |||
| 121c0ac285 | |||
| 3e585830d7 | |||
| b95caa8331 | |||
| c17939ed5c | |||
| f3bfe27445 | |||
| 7651d5841f | |||
| 6e122d3aaa | |||
| f7be796c1c | |||
| 1e843fd3ec | |||
| 4bb20ea8db | |||
| ac24766510 | |||
| d5e85bd95e | |||
| c3c96b9f32 | |||
| 4d98ff5dcf | |||
| 48262b8efe | |||
| ca32ac5e7f | |||
| 63ccc9d9cc | |||
| 8fe1de4595 | |||
| c048074436 | |||
| b7882d703c | |||
| ca8a1ea375 | |||
| cfb8dc6cc8 | |||
| 665c9ad7df | |||
| d3152c11c7 | |||
| 856e1d5dc8 | |||
| 987d6cca6d | |||
| ec21dc0f91 | |||
| 628261d5bb | |||
| 715d33f001 | |||
| bb5142164e | |||
| 25b8d64cd0 | |||
| d72da90d3a | |||
| bff9899905 | |||
| 41cd6879bd | |||
| 3a91d90c28 | |||
| 08c5280843 | |||
| cd5fee5347 | |||
| 916cb64581 | |||
| b3ef834ae6 | |||
| a9d344b3ed | |||
| 39a746d30f | |||
| 952198b2ee | |||
| 8d93670430 | |||
| 29a7402adf | |||
| 3dfd6838c0 | |||
| 32170e5847 | |||
| bfb88cfc1c | |||
| c4dfb24116 | |||
| 4fb0244b5a | |||
| 0725bf4941 | |||
| 8965d9bccf | |||
| af7a5716e8 | |||
| 83e7aa1131 | |||
| 865cf3c117 | |||
| be70e1c5a7 |
+1
-1
@@ -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/current/httpclient5/apidocs/",
|
||||
"https://hc.apache.org/httpcomponents-client-5.6.x/5.6/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.0");
|
||||
checkstyle.setToolVersion("13.4.2");
|
||||
checkstyle.getConfigDirectory().set(project.getRootProject().file("src/checkstyle"));
|
||||
String version = SpringJavaFormatPlugin.class.getPackage().getImplementationVersion();
|
||||
DependencySet checkstyleDependencies = project.getConfigurations().getByName("checkstyle").getDependencies();
|
||||
|
||||
@@ -20,6 +20,10 @@ antora {
|
||||
]
|
||||
}
|
||||
|
||||
node {
|
||||
version = '24.15.0'
|
||||
}
|
||||
|
||||
tasks.named("generateAntoraYml") {
|
||||
asciidocAttributes = project.provider( {
|
||||
return ["spring-version": project.version ]
|
||||
|
||||
@@ -198,6 +198,7 @@
|
||||
*** 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[]
|
||||
@@ -295,6 +296,7 @@
|
||||
*** 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,6 +74,11 @@ 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
|
||||
|
||||
+13
@@ -610,6 +610,19 @@ 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,6 +395,16 @@ 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
|
||||
|
||||
+1
-1
@@ -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 `validateExistingTransactions` flag to `true` on your transaction
|
||||
Consider switching the `validateExistingTransaction` 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(restCqlientAdapter)
|
||||
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(restClientAdapter)
|
||||
.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://docs.seleniumhq.org/projects/webdriver/[WebDriver] to make things even easier.
|
||||
https://www.selenium.dev/documentation/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://docs.seleniumhq.org/[Selenium], WebDriver does not
|
||||
require a Selenium Server to run your tests.
|
||||
NOTE: Despite being a part of https://www.selenium.dev/documentation/[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://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>)`]
|
||||
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>)`]
|
||||
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://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>)`]
|
||||
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>)`]
|
||||
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,7 +107,4 @@ Kotlin::
|
||||
|
||||
|
||||
[[webflux-ann-initbinder-model-design]]
|
||||
== 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[]
|
||||
NOTE: For more guidance on model design, please see xref:web/webflux/data-binding.adoc[Data Binding].
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
[[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,7 +107,4 @@ Kotlin::
|
||||
|
||||
|
||||
[[mvc-ann-initbinder-model-design]]
|
||||
== 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[]
|
||||
NOTE: For more guidance on model design, please see xref:web/webmvc/mvc-data-binding.adoc[Data Binding].
|
||||
@@ -0,0 +1,34 @@
|
||||
[[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,91 +1,53 @@
|
||||
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.
|
||||
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.
|
||||
|
||||
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).
|
||||
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.
|
||||
|
||||
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:
|
||||
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.
|
||||
|
||||
[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.
|
||||
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.
|
||||
For example:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Controller
|
||||
public class ChangeEmailController {
|
||||
public class PersonController {
|
||||
|
||||
@InitBinder
|
||||
void initBinder(WebDataBinder binder) {
|
||||
binder.setAllowedFields("oldEmailAddress", "newEmailAddress");
|
||||
// See Javadoc for supported pattern syntax
|
||||
binder.setAllowedFields("firstName", "lastName", "*Address");
|
||||
}
|
||||
|
||||
// @RequestMapping methods, etc.
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
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:
|
||||
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`:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Controller
|
||||
public class MyController {
|
||||
@ControllerAdvice
|
||||
public class ControllerConfig {
|
||||
|
||||
@InitBinder
|
||||
void initBinder(WebDataBinder binder) {
|
||||
binder.setDeclarativeBinding(true);
|
||||
}
|
||||
|
||||
// @RequestMapping methods, etc.
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"antora": "3.2.0-alpha.11",
|
||||
"antora": "3.2.0-alpha.12",
|
||||
"@antora/atlas-extension": "1.0.0-alpha.5",
|
||||
"@antora/collector-extension": "1.0.2",
|
||||
"@antora/collector-extension": "1.0.3",
|
||||
"@asciidoctor/tabs": "1.0.0-beta.6",
|
||||
"@springio/antora-extensions": "1.14.7",
|
||||
"fast-xml-parser": "5.3.8",
|
||||
"@springio/asciidoctor-extensions": "1.0.0-alpha.17"
|
||||
"@springio/antora-extensions": "1.14.12",
|
||||
"fast-xml-parser": "5.7.0",
|
||||
"@springio/asciidoctor-extensions": "1.0.0-alpha.18"
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -8,15 +8,15 @@ javaPlatform {
|
||||
|
||||
dependencies {
|
||||
api(platform("com.fasterxml.jackson:jackson-bom:2.20.2"))
|
||||
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.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.rsocket:rsocket-bom:1.1.5"))
|
||||
api(platform("org.apache.groovy:groovy-bom:5.0.5"))
|
||||
api(platform("org.apache.logging.log4j:log4j-bom:2.25.4"))
|
||||
api(platform("org.apache.groovy:groovy-bom:5.0.6"))
|
||||
api(platform("org.apache.logging.log4j:log4j-bom:2.26.0"))
|
||||
api(platform("org.assertj:assertj-bom:3.27.7"))
|
||||
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.eclipse.jetty:jetty-bom:12.1.9"))
|
||||
api(platform("org.eclipse.jetty.ee11:jetty-ee11-bom:12.1.9"))
|
||||
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.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.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.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.11.Final")
|
||||
api("org.hibernate.orm:hibernate-core:7.2.17.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,7 +138,6 @@ 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
@@ -1,4 +1,4 @@
|
||||
version=7.0.7-SNAPSHOT
|
||||
version=7.0.8-SNAPSHOT
|
||||
|
||||
org.gradle.caching=true
|
||||
org.gradle.jvmargs=-Xmx2048m
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+3
-1
@@ -1,7 +1,9 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip
|
||||
networkTimeout=10000
|
||||
retries=0
|
||||
retryBackOffMs=500
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/2d6327017519d23b96af35865dc997fcb544fb40/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/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
+10
-21
@@ -23,8 +23,8 @@
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
@rem Set local scope for the variables, and ensure extensions are enabled
|
||||
setlocal EnableExtensions
|
||||
|
||||
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
|
||||
|
||||
goto fail
|
||||
"%COMSPEC%" /c exit 1
|
||||
|
||||
: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
|
||||
|
||||
goto fail
|
||||
"%COMSPEC%" /c exit 1
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
@@ -73,21 +73,10 @@ goto fail
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
@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
|
||||
|
||||
: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
|
||||
:exitWithErrorLevel
|
||||
@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
|
||||
"%COMSPEC%" /c exit %ERRORLEVEL%
|
||||
|
||||
+1
-1
@@ -163,7 +163,7 @@ public class MethodInvocationProceedingJoinPoint implements ProceedingJoinPoint,
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "execution(" + getSignature().toString() + ")";
|
||||
return "execution(" + getSignature() + ")";
|
||||
}
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+2
-2
@@ -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);
|
||||
|
||||
+3
-1
@@ -63,12 +63,14 @@ 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 PropertyAccessor instance.
|
||||
* <p>Default is {@code false} on a plain accessor.
|
||||
* @since 4.1
|
||||
*/
|
||||
void setAutoGrowNestedPaths(boolean autoGrowNestedPaths);
|
||||
|
||||
/**
|
||||
* Return whether "auto-growing" of nested paths has been activated.
|
||||
* @since 4.1
|
||||
*/
|
||||
boolean isAutoGrowNestedPaths();
|
||||
|
||||
|
||||
@@ -154,7 +154,8 @@ 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.startsWith("'") && key.endsWith("'")) || (key.startsWith("\"") && key.endsWith("\""))) {
|
||||
if (key.length() > 1 && ((key.startsWith("'") && key.endsWith("'")) ||
|
||||
(key.startsWith("\"") && key.endsWith("\"")))) {
|
||||
sb.delete(keyStart + 1, keyStart + 2);
|
||||
sb.delete(keyEnd - 2, keyEnd - 1);
|
||||
keyEnd = keyEnd - 2;
|
||||
|
||||
+2
@@ -32,6 +32,7 @@ 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.
|
||||
@@ -65,6 +66,7 @@ public class AotBeanProcessingException extends AotProcessingException {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the bean definition of the bean that failed to be processed.
|
||||
*/
|
||||
|
||||
+1
-1
@@ -249,7 +249,7 @@ public abstract class YamlProcessor {
|
||||
}
|
||||
else {
|
||||
// It has to be a map key in this case
|
||||
result.put("[" + key.toString() + "]", value);
|
||||
result.put("[" + key + "]", value);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
|
||||
+3
-5
@@ -606,9 +606,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||
if (ex instanceof BeanCreationException bce && beanName.equals(bce.getBeanName())) {
|
||||
throw bce;
|
||||
}
|
||||
else {
|
||||
throw new BeanCreationException(mbd.getResourceDescription(), beanName, ex.getMessage(), ex);
|
||||
}
|
||||
throw new BeanCreationException(mbd.getResourceDescription(), beanName, ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
if (earlySingletonExposure) {
|
||||
@@ -1246,8 +1244,8 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||
instance = obtainInstanceFromSupplier(supplier, beanName, mbd);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
if (ex instanceof BeansException beansException) {
|
||||
throw beansException;
|
||||
if (ex instanceof BeanCreationException bce && beanName.equals(bce.getBeanName())) {
|
||||
throw bce;
|
||||
}
|
||||
throw new BeanCreationException(beanName, "Instantiation of supplied bean failed", ex);
|
||||
}
|
||||
|
||||
+7
@@ -1154,12 +1154,19 @@ 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, () -> {
|
||||
|
||||
+2
@@ -1413,6 +1413,8 @@ 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,6 +79,10 @@ 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
|
||||
|
||||
+3
@@ -76,6 +76,7 @@ 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);
|
||||
@@ -104,6 +105,8 @@ 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);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -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().toString();
|
||||
return getQualifiedName(enclosingElement) + "$" + declaredType.asElement().getSimpleName();
|
||||
}
|
||||
else {
|
||||
return getQualifiedName(declaredType.asElement());
|
||||
|
||||
Vendored
+2
-2
@@ -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.toString() + "'. Both 'key' and 'keyGenerator' attributes have been set. " +
|
||||
ae + "'. 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.toString() + "'. Both 'cacheManager' and 'cacheResolver' attributes have been set. " +
|
||||
ae + "'. 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.");
|
||||
|
||||
+1
-1
@@ -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.toString() + "'. Both 'key' and 'keyGenerator' attributes have been set. " +
|
||||
element + "'. 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.");
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -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 == NullValue.INSTANCE) {
|
||||
if (this.allowNullValues && storeValue instanceof NullValue) {
|
||||
return null;
|
||||
}
|
||||
return storeValue;
|
||||
|
||||
+2
-5
@@ -783,16 +783,13 @@ 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());
|
||||
}
|
||||
|
||||
|
||||
+12
@@ -341,6 +341,18 @@ 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 — for example,
|
||||
* via {@link ComponentScan @ComponentScan} or by directly registering it against
|
||||
* the application context — 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
|
||||
|
||||
+5
-3
@@ -260,9 +260,11 @@ class ConfigurationClassParser {
|
||||
return;
|
||||
}
|
||||
else if (configClass.isScanned()) {
|
||||
String beanName = configClass.getBeanName();
|
||||
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
|
||||
this.registry.removeBeanDefinition(beanName);
|
||||
if (existingClass.isImported()) {
|
||||
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;
|
||||
|
||||
+1
-1
@@ -191,7 +191,7 @@ public class InstrumentationLoadTimeWeaver implements LoadTimeWeaver {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "FilteringClassFileTransformer for: " + this.targetTransformer.toString();
|
||||
return "FilteringClassFileTransformer for: " + this.targetTransformer;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
-2
@@ -177,7 +177,11 @@ final class BitsCronField extends CronField {
|
||||
int next = nextSetBit(current);
|
||||
if (next == -1) {
|
||||
temporal = type().rollForward(temporal);
|
||||
next = nextSetBit(0);
|
||||
next = nextSetBit(type().get(temporal));
|
||||
if (next == -1) {
|
||||
temporal = type().rollForward(temporal);
|
||||
next = nextSetBit(0);
|
||||
}
|
||||
}
|
||||
if (next == current) {
|
||||
return temporal;
|
||||
@@ -191,7 +195,12 @@ final class BitsCronField extends CronField {
|
||||
next = nextSetBit(current);
|
||||
if (next == -1) {
|
||||
temporal = type().rollForward(temporal);
|
||||
next = nextSetBit(0);
|
||||
next = nextSetBit(type().get(temporal));
|
||||
if (next == -1) {
|
||||
temporal = type().rollForward(temporal);
|
||||
next = nextSetBit(0);
|
||||
}
|
||||
current = type().get(temporal);
|
||||
}
|
||||
}
|
||||
if (count >= CronExpression.MAX_ATTEMPTS) {
|
||||
|
||||
+4
-2
@@ -19,6 +19,7 @@ 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;
|
||||
@@ -145,12 +146,13 @@ public class CronTrigger implements Trigger {
|
||||
@Override
|
||||
public boolean equals(@Nullable Object other) {
|
||||
return (this == other || (other instanceof CronTrigger that &&
|
||||
this.expression.equals(that.expression)));
|
||||
this.expression.equals(that.expression) &&
|
||||
Objects.equals(this.zoneId, that.zoneId)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.expression.hashCode();
|
||||
return Objects.hash(this.expression, this.zoneId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -273,8 +273,9 @@ 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/field injection via {@link #bind(PropertyValues)}, and not
|
||||
* applicable to constructor binding via {@link #construct}.
|
||||
* <p>Used for setter injection via {@link #bind(PropertyValues)};
|
||||
* not applicable to field injection, and not to constructor binding
|
||||
* via {@link #construct} either.
|
||||
* @see #initBeanPropertyAccess()
|
||||
* @see org.springframework.beans.BeanWrapper#setAutoGrowCollectionLimit
|
||||
*/
|
||||
|
||||
+42
@@ -197,6 +197,16 @@ 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 {
|
||||
@@ -542,4 +552,36 @@ 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+13
@@ -26,6 +26,7 @@ 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;
|
||||
@@ -43,6 +44,7 @@ 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;
|
||||
@@ -84,6 +86,17 @@ 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);
|
||||
|
||||
+2
-2
@@ -289,7 +289,7 @@ class AutowiredConfigurationTests {
|
||||
|
||||
@Bean
|
||||
public TestBean testBean(Color color, List<Color> colors) {
|
||||
return new TestBean(color.toString() + "-" + colors.get(0).toString());
|
||||
return new TestBean(color + "-" + colors.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,7 +303,7 @@ class AutowiredConfigurationTests {
|
||||
return new TestBean("");
|
||||
}
|
||||
else {
|
||||
return new TestBean(color.get() + "-" + colors.get().get(0).toString());
|
||||
return new TestBean(color.get() + "-" + colors.get().get(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+20
@@ -30,6 +30,7 @@ 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;
|
||||
@@ -41,12 +42,15 @@ 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;
|
||||
@@ -637,6 +641,22 @@ 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();
|
||||
|
||||
+20
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.scheduling.support;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.assertj.core.api.Condition;
|
||||
@@ -29,6 +30,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Sam Brannen
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
class BitsCronFieldTests {
|
||||
|
||||
@@ -112,6 +114,24 @@ 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))) {
|
||||
|
||||
+8
@@ -1367,6 +1367,14 @@ 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
|
||||
|
||||
+15
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.scheduling.support;
|
||||
|
||||
import java.time.ZoneId;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
@@ -746,6 +747,20 @@ 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);
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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() {}
|
||||
|
||||
}
|
||||
+8
@@ -24,7 +24,9 @@ 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;
|
||||
|
||||
@@ -72,6 +74,12 @@ 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
|
||||
|
||||
+3
-4
@@ -36,10 +36,9 @@ 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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -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.toString() + " is abstract" );
|
||||
e.throw_exception(ABSTRACT_METHOD_ERROR, method + " is abstract" );
|
||||
} else {
|
||||
e.load_this();
|
||||
context.emitLoadArgsAndInvoke(e, method);
|
||||
|
||||
@@ -134,9 +134,10 @@ public abstract class CoroutinesUtils {
|
||||
Object arg = args[index];
|
||||
if (!(parameter.isOptional() && arg == null)) {
|
||||
KType type = parameter.getType();
|
||||
if (!type.isMarkedNullable() &&
|
||||
if (!(type.isMarkedNullable() && arg == null) &&
|
||||
type.getClassifier() instanceof KClass<?> kClass &&
|
||||
KotlinDetector.isInlineClass(JvmClassMappingKt.getJavaClass(kClass))) {
|
||||
KotlinDetector.isInlineClass(JvmClassMappingKt.getJavaClass(kClass)) &&
|
||||
!JvmClassMappingKt.getJavaClass(kClass).isInstance(arg)) {
|
||||
arg = box(kClass, arg);
|
||||
}
|
||||
argMap.put(parameter, arg);
|
||||
@@ -166,9 +167,10 @@ 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() &&
|
||||
if (!(type.isMarkedNullable() && arg == null) &&
|
||||
type.getClassifier() instanceof KClass<?> parameterClass &&
|
||||
KotlinDetector.isInlineClass(JvmClassMappingKt.getJavaClass(parameterClass))) {
|
||||
KotlinDetector.isInlineClass(JvmClassMappingKt.getJavaClass(parameterClass)) &&
|
||||
!JvmClassMappingKt.getJavaClass(parameterClass).isInstance(arg)) {
|
||||
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
|
||||
* (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)
|
||||
* (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)
|
||||
*/
|
||||
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
|
||||
* (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)
|
||||
* (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)
|
||||
*/
|
||||
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
|
||||
* (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).
|
||||
* (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).
|
||||
*/
|
||||
public int getNestingLevel() {
|
||||
return this.nestingLevel;
|
||||
|
||||
@@ -71,6 +71,8 @@ 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);
|
||||
@@ -78,6 +80,7 @@ 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<>();
|
||||
@@ -356,7 +359,9 @@ public class ReactiveAdapterRegistry {
|
||||
|
||||
registry.registerReactiveType(
|
||||
ReactiveTypeDescriptor.multiValue(kotlinx.coroutines.flow.Flow.class, kotlinx.coroutines.flow.FlowKt::emptyFlow),
|
||||
source -> kotlinx.coroutines.reactor.ReactorFlowKt.asFlux((kotlinx.coroutines.flow.Flow<?>) source),
|
||||
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),
|
||||
kotlinx.coroutines.reactive.ReactiveFlowKt::asFlow);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1533,9 +1533,6 @@ 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,6 +44,7 @@ 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,13 +38,27 @@ public final class SpringVersion {
|
||||
|
||||
|
||||
/**
|
||||
* Return the full version string of the present Spring codebase,
|
||||
* Return the "major.minor.patch" 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();
|
||||
return (pkg != null ? pkg.getImplementationVersion() : null);
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1138,9 +1138,11 @@ public abstract class DataBufferUtils {
|
||||
protected void hookOnNext(DataBuffer dataBuffer) {
|
||||
try {
|
||||
try (DataBuffer.ByteBufferIterator iterator = dataBuffer.readableByteBuffers()) {
|
||||
ByteBuffer byteBuffer = iterator.next();
|
||||
while (byteBuffer.hasRemaining()) {
|
||||
this.channel.write(byteBuffer);
|
||||
while (iterator.hasNext()) {
|
||||
ByteBuffer byteBuffer = iterator.next();
|
||||
while (byteBuffer.hasRemaining()) {
|
||||
this.channel.write(byteBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.sink.next(dataBuffer);
|
||||
@@ -1213,6 +1215,11 @@ public abstract class DataBufferUtils {
|
||||
failed(ex, attachment);
|
||||
}
|
||||
}
|
||||
else {
|
||||
iterator.close();
|
||||
this.sink.next(dataBuffer);
|
||||
request(1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1236,7 +1243,6 @@ 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();
|
||||
@@ -1246,9 +1252,11 @@ public abstract class DataBufferUtils {
|
||||
}
|
||||
else if (iterator.hasNext()) {
|
||||
ByteBuffer next = iterator.next();
|
||||
this.channel.write(next, pos, attachment, this);
|
||||
Attachment nextAttachment = new Attachment(next, attachment.dataBuffer(), iterator);
|
||||
this.channel.write(next, pos, nextAttachment, this);
|
||||
}
|
||||
else {
|
||||
iterator.close();
|
||||
this.sink.next(attachment.dataBuffer());
|
||||
this.writing.set(false);
|
||||
|
||||
|
||||
+11
@@ -59,6 +59,17 @@ 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
Reference in New Issue
Block a user