Compare commits

..

1 Commits

Author SHA1 Message Date
Brian Clozel 90f9c0929b Release v6.2.6 2025-04-17 09:18:15 +02:00
374 changed files with 3587 additions and 5697 deletions
+3 -2
View File
@@ -91,17 +91,18 @@ configure([rootProject] + javaProjects) { project ->
"https://docs.oracle.com/en/java/javase/17/docs/api/",
"https://jakarta.ee/specifications/platform/9/apidocs/",
"https://docs.jboss.org/hibernate/orm/5.6/javadocs/",
"https://eclipse.dev/aspectj/doc/latest/runtime-api/",
"https://www.quartz-scheduler.org/api/2.3.0/",
"https://fasterxml.github.io/jackson-core/javadoc/2.14/",
"https://fasterxml.github.io/jackson-databind/javadoc/2.14/",
"https://fasterxml.github.io/jackson-dataformat-xml/javadoc/2.14/",
"https://hc.apache.org/httpcomponents-client-5.5.x/current/httpclient5/apidocs/",
"https://hc.apache.org/httpcomponents-client-5.4.x/current/httpclient5/apidocs/",
"https://projectreactor.io/docs/test/release/api/",
"https://junit.org/junit4/javadoc/4.13.2/",
// TODO Uncomment link to JUnit 5 docs once we execute Gradle with Java 18+.
// See https://github.com/spring-projects/spring-framework/issues/27497
//
// "https://junit.org/junit5/docs/5.13.1/api/",
// "https://junit.org/junit5/docs/5.12.2/api/",
"https://www.reactive-streams.org/reactive-streams-1.0.3-javadoc/",
//"https://javadoc.io/static/io.rsocket/rsocket-core/1.1.1/",
"https://r2dbc.io/spec/1.0.0.RELEASE/api/",
+1 -1
View File
@@ -1,2 +1,2 @@
org.gradle.caching=true
javaFormatVersion=0.0.43
javaFormatVersion=0.0.42
@@ -50,7 +50,7 @@ public class CheckstyleConventions {
project.getPlugins().apply(CheckstylePlugin.class);
project.getTasks().withType(Checkstyle.class).forEach(checkstyle -> checkstyle.getMaxHeapSize().set("1g"));
CheckstyleExtension checkstyle = project.getExtensions().getByType(CheckstyleExtension.class);
checkstyle.setToolVersion("10.25.0");
checkstyle.setToolVersion("10.23.0");
checkstyle.getConfigDirectory().set(project.getRootProject().file("src/checkstyle"));
String version = SpringJavaFormatPlugin.class.getPackage().getImplementationVersion();
DependencySet checkstyleDependencies = project.getConfigurations().getByName("checkstyle").getDependencies();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,8 +21,6 @@ import java.util.Map;
import org.gradle.api.Project;
import org.gradle.api.plugins.JavaBasePlugin;
import org.gradle.api.tasks.testing.Test;
import org.gradle.api.tasks.testing.TestFrameworkOptions;
import org.gradle.api.tasks.testing.junitplatform.JUnitPlatformOptions;
import org.gradle.testretry.TestRetryPlugin;
import org.gradle.testretry.TestRetryTaskExtension;
@@ -36,7 +34,6 @@ import org.gradle.testretry.TestRetryTaskExtension;
*
* @author Brian Clozel
* @author Andy Wilkinson
* @author Sam Brannen
*/
class TestConventions {
@@ -53,12 +50,7 @@ class TestConventions {
}
private void configureTests(Project project, Test test) {
TestFrameworkOptions existingOptions = test.getOptions();
test.useJUnitPlatform(options -> {
if (existingOptions instanceof JUnitPlatformOptions junitPlatformOptions) {
options.copyFrom(junitPlatformOptions);
}
});
test.useJUnitPlatform();
test.include("**/*Tests.class", "**/*Test.class");
test.setSystemProperties(Map.of(
"java.awt.headless", "true",
+1 -2
View File
@@ -1,6 +1,6 @@
plugins {
id 'java-platform'
id 'io.freefair.aggregate-javadoc' version '8.13.1'
id 'io.freefair.aggregate-javadoc' version '8.3'
}
description = "Spring Framework API Docs"
@@ -21,7 +21,6 @@ dependencies {
javadoc {
title = "${rootProject.description} ${version} API"
failOnError = true
options {
encoding = "UTF-8"
memberLevel = JavadocMemberLevel.PROTECTED
@@ -103,14 +103,6 @@ for details.
{spring-framework-api}++/objenesis/SpringObjenesis.html#IGNORE_OBJENESIS_PROPERTY_NAME++[`SpringObjenesis`]
for details.
| `spring.placeholder.escapeCharacter.default`
| The default escape character for property placeholder support. If not set, `'\'` will
be used. Can be set to a custom escape character or an empty string to disable support
for an escape character. The default escape character be explicitly overridden in
`PropertySourcesPlaceholderConfigurer` and subclasses of `AbstractPropertyResolver`. See
{spring-framework-api}++/core/env/AbstractPropertyResolver.html#DEFAULT_PLACEHOLDER_ESCAPE_CHARACTER_PROPERTY_NAME++[`AbstractPropertyResolver`]
for details.
| `spring.test.aot.processing.failOnError`
| A boolean flag that controls whether errors encountered during AOT processing in the
_Spring TestContext Framework_ should result in an exception that fails the overall process.
@@ -101,11 +101,8 @@ NOTE: When configuring a `PropertySourcesPlaceholderConfigurer` using JavaConfig
Using the above configuration ensures Spring initialization failure if any `${}`
placeholder could not be resolved. It is also possible to use methods like
`setPlaceholderPrefix()`, `setPlaceholderSuffix()`, `setValueSeparator()`, or
`setEscapeCharacter()` to customize the placeholder syntax. In addition, the default
escape character can be changed or disabled globally by setting the
`spring.placeholder.escapeCharacter.default` property via a JVM system property (or via
the xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism).
`setPlaceholderPrefix`, `setPlaceholderSuffix`, `setValueSeparator`, or
`setEscapeCharacter` to customize placeholders.
NOTE: Spring Boot configures by default a `PropertySourcesPlaceholderConfigurer` bean that
will get properties from `application.properties` and `application.yml` files.
@@ -314,7 +314,7 @@ Thus, marking it for lazy initialization will be ignored, and the
[[beans-factory-placeholderconfigurer]]
=== Example: Property Placeholder Substitution with `PropertySourcesPlaceholderConfigurer`
=== Example: The Class Name Substitution `PropertySourcesPlaceholderConfigurer`
You can use the `PropertySourcesPlaceholderConfigurer` to externalize property values
from a bean definition in a separate file by using the standard Java `Properties` format.
@@ -341,8 +341,8 @@ with placeholder values is defined:
The example shows properties configured from an external `Properties` file. At runtime,
a `PropertySourcesPlaceholderConfigurer` is applied to the metadata that replaces some
properties of the `DataSource`. The values to replace are specified as placeholders of the
form pass:q[`${property-name}`], which follows the Ant, log4j, and JSP EL style.
properties of the DataSource. The values to replace are specified as placeholders of the
form pass:q[`${property-name}`], which follows the Ant and log4j and JSP EL style.
The actual values come from another file in the standard Java `Properties` format:
@@ -355,15 +355,11 @@ jdbc.password=root
----
Therefore, the `${jdbc.username}` string is replaced at runtime with the value, 'sa', and
the same applies for other placeholder values that match keys in the properties file. The
`PropertySourcesPlaceholderConfigurer` checks for placeholders in most properties and
attributes of a bean definition. Furthermore, you can customize the placeholder prefix,
suffix, default value separator, and escape character. In addition, the default escape
character can be changed or disabled globally by setting the
`spring.placeholder.escapeCharacter.default` property via a JVM system property (or via
the xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism).
the same applies for other placeholder values that match keys in the properties file.
The `PropertySourcesPlaceholderConfigurer` checks for placeholders in most properties and
attributes of a bean definition. Furthermore, you can customize the placeholder prefix and suffix.
With the `context` namespace, you can configure property placeholders
With the `context` namespace introduced in Spring 2.5, you can configure property placeholders
with a dedicated configuration element. You can provide one or more locations as a
comma-separated list in the `location` attribute, as the following example shows:
@@ -190,7 +190,7 @@ NOTE: If you use Spring Boot, you should probably use
instead of `@Value` annotations.
As an alternative, you can customize the property placeholder prefix by declaring the
following `PropertySourcesPlaceholderConfigurer` bean:
following configuration beans:
[source,kotlin,indent=0]
----
@@ -200,10 +200,8 @@ following `PropertySourcesPlaceholderConfigurer` bean:
}
----
You can support components (such as Spring Boot actuators or `@LocalServerPort`) that use
the standard `${...}` syntax alongside components that use the custom `%{...}` syntax by
declaring multiple `PropertySourcesPlaceholderConfigurer` beans, as the following example
shows:
You can customize existing code (such as Spring Boot actuators or `@LocalServerPort`)
that uses the `${...}` syntax, with configuration beans, as the following example shows:
[source,kotlin,indent=0]
----
@@ -217,9 +215,6 @@ shows:
fun defaultPropertyConfigurer() = PropertySourcesPlaceholderConfigurer()
----
In addition, the default escape character can be changed or disabled globally by setting
the `spring.placeholder.escapeCharacter.default` property via a JVM system property (or
via the xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism).
[[checked-exceptions]]
@@ -2,9 +2,8 @@
= Spring JUnit 4 Testing Annotations
The following annotations are supported only when used in conjunction with the
xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit4-runner[SpringRunner],
xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit4-rules[Spring's JUnit 4 rules], or
xref:testing/testcontext-framework/support-classes.adoc#testcontext-support-classes-junit4[Spring's JUnit 4 support classes]:
xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit4-runner[SpringRunner], xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit4-rules[Spring's JUnit 4 rules]
, or xref:testing/testcontext-framework/support-classes.adoc#testcontext-support-classes-junit4[Spring's JUnit 4 support classes]:
* xref:testing/annotations/integration-junit4.adoc#integration-testing-annotations-junit4-ifprofilevalue[`@IfProfileValue`]
* xref:testing/annotations/integration-junit4.adoc#integration-testing-annotations-junit4-profilevaluesourceconfiguration[`@ProfileValueSourceConfiguration`]
@@ -166,7 +166,7 @@ following sections to make this pattern much easier to implement.
== MockMvc and WebDriver Setup
To use Selenium WebDriver with `MockMvc`, make sure that your project includes a test
dependency on `org.seleniumhq.selenium:htmlunit3-driver`.
dependency on `org.seleniumhq.selenium:selenium-htmlunit3-driver`.
We can easily create a Selenium WebDriver that integrates with MockMvc by using the
`MockMvcHtmlUnitDriverBuilder` as the following example shows:
@@ -1,26 +1,10 @@
[[spring-mvc-test-client]]
= Testing Client Applications
To test code that uses the `RestClient` or `RestTemplate`, you can use a mock web server, such as
https://github.com/square/okhttp#mockwebserver[OkHttp MockWebServer] or
https://wiremock.org/[WireMock]. Mock web servers accept requests over HTTP like a regular
server, and that means you can test with the same HTTP client that is also configured in
the same way as in production, which is important because there are often subtle
differences in the way different clients handle network I/O. Another advantage of mock
web servers is the ability to simulate specific network issues and conditions at the
transport level, in combination with the client used in production.
In addition to dedicated mock web servers, historically the Spring Framework has provided
a built-in option to test `RestClient` or `RestTemplate` through `MockRestServiceServer`.
This relies on configuring the client under test with a custom `ClientHttpRequestFactory`
backed by the mock server that is in turn set up to expect requests and send "`stub`"
responses so that you can focus on testing the code in isolation, without running a server.
TIP: `MockRestServiceServer` predates the existence of mock web servers. At present, we
recommend using mock web servers for more complete testing of the transport layer and
network conditions.
The following example shows an example of using `MockRestServiceServer`:
You can use client-side tests to test code that internally uses the `RestTemplate`. The
idea is to declare expected requests and to provide "`stub`" responses so that you can
focus on testing the code in isolation (that is, without running a server). The following
example shows how to do so:
[tabs]
======
@@ -250,7 +250,7 @@ Java::
@SqlGroup({
@Sql(scripts = "/test-schema.sql", config = @SqlConfig(commentPrefix = "`")),
@Sql("/test-user-data.sql")
})
)}
void userTest() {
// run code that uses the test schema and test data
}
@@ -1,9 +1,166 @@
[[testcontext-support-classes]]
= TestContext Framework Support Classes
This section describes the various classes that support the Spring TestContext Framework
in JUnit and TestNG.
This section describes the various classes that support the Spring TestContext Framework.
[[testcontext-junit4-runner]]
== Spring JUnit 4 Runner
The Spring TestContext Framework offers full integration with JUnit 4 through a custom
runner (supported on JUnit 4.12 or higher). By annotating test classes with
`@RunWith(SpringJUnit4ClassRunner.class)` or the shorter `@RunWith(SpringRunner.class)`
variant, developers can implement standard JUnit 4-based unit and integration tests and
simultaneously reap the benefits of the TestContext framework, such as support for
loading application contexts, dependency injection of test instances, transactional test
method execution, and so on. If you want to use the Spring TestContext Framework with an
alternative runner (such as JUnit 4's `Parameterized` runner) or third-party runners
(such as the `MockitoJUnitRunner`), you can, optionally, use
xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit4-rules[Spring's support for JUnit rules] instead.
The following code listing shows the minimal requirements for configuring a test class to
run with the custom Spring `Runner`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
@RunWith(SpringRunner.class)
@TestExecutionListeners({})
public class SimpleTest {
@Test
public void testMethod() {
// test logic...
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@RunWith(SpringRunner::class)
@TestExecutionListeners
class SimpleTest {
@Test
fun testMethod() {
// test logic...
}
}
----
======
In the preceding example, `@TestExecutionListeners` is configured with an empty list, to
disable the default listeners, which otherwise would require an `ApplicationContext` to
be configured through `@ContextConfiguration`.
[[testcontext-junit4-rules]]
== Spring JUnit 4 Rules
The `org.springframework.test.context.junit4.rules` package provides the following JUnit
4 rules (supported on JUnit 4.12 or higher):
* `SpringClassRule`
* `SpringMethodRule`
`SpringClassRule` is a JUnit `TestRule` that supports class-level features of the Spring
TestContext Framework, whereas `SpringMethodRule` is a JUnit `MethodRule` that supports
instance-level and method-level features of the Spring TestContext Framework.
In contrast to the `SpringRunner`, Spring's rule-based JUnit support has the advantage of
being independent of any `org.junit.runner.Runner` implementation and can, therefore, be
combined with existing alternative runners (such as JUnit 4's `Parameterized`) or
third-party runners (such as the `MockitoJUnitRunner`).
To support the full functionality of the TestContext framework, you must combine a
`SpringClassRule` with a `SpringMethodRule`. The following example shows the proper way
to declare these rules in an integration test:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
// Optionally specify a non-Spring Runner via @RunWith(...)
@ContextConfiguration
public class IntegrationTest {
@ClassRule
public static final SpringClassRule springClassRule = new SpringClassRule();
@Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();
@Test
public void testMethod() {
// test logic...
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
// Optionally specify a non-Spring Runner via @RunWith(...)
@ContextConfiguration
class IntegrationTest {
@Rule
val springMethodRule = SpringMethodRule()
@Test
fun testMethod() {
// test logic...
}
companion object {
@ClassRule
val springClassRule = SpringClassRule()
}
}
----
======
[[testcontext-support-classes-junit4]]
== JUnit 4 Support Classes
The `org.springframework.test.context.junit4` package provides the following support
classes for JUnit 4-based test cases (supported on JUnit 4.12 or higher):
* `AbstractJUnit4SpringContextTests`
* `AbstractTransactionalJUnit4SpringContextTests`
`AbstractJUnit4SpringContextTests` is an abstract base test class that integrates the
Spring TestContext Framework with explicit `ApplicationContext` testing support in a
JUnit 4 environment. When you extend `AbstractJUnit4SpringContextTests`, you can access a
`protected` `applicationContext` instance variable that you can use to perform explicit
bean lookups or to test the state of the context as a whole.
`AbstractTransactionalJUnit4SpringContextTests` is an abstract transactional extension of
`AbstractJUnit4SpringContextTests` that adds some convenience functionality for JDBC
access. This class expects a `javax.sql.DataSource` bean and a
`PlatformTransactionManager` bean to be defined in the `ApplicationContext`. When you
extend `AbstractTransactionalJUnit4SpringContextTests`, you can access a `protected`
`jdbcTemplate` instance variable that you can use to run SQL statements to query the
database. You can use such queries to confirm database state both before and after
running database-related application code, and Spring ensures that such queries run in
the scope of the same transaction as the application code. When used in conjunction with
an ORM tool, be sure to avoid xref:testing/testcontext-framework/tx.adoc#testcontext-tx-false-positives[false positives].
As mentioned in xref:testing/support-jdbc.adoc[JDBC Testing Support],
`AbstractTransactionalJUnit4SpringContextTests` also provides convenience methods that
delegate to methods in `JdbcTestUtils` by using the aforementioned `jdbcTemplate`.
Furthermore, `AbstractTransactionalJUnit4SpringContextTests` provides an
`executeSqlScript(..)` method for running SQL scripts against the configured `DataSource`.
TIP: These classes are a convenience for extension. If you do not want your test classes
to be tied to a Spring-specific class hierarchy, you can configure your own custom test
classes by using `@RunWith(SpringRunner.class)` or xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit4-rules[Spring's JUnit rules]
.
[[testcontext-junit-jupiter-extension]]
== SpringExtension for JUnit Jupiter
@@ -20,17 +177,14 @@ following features above and beyond the feature set that Spring supports for JUn
TestNG:
* Dependency injection for test constructors, test methods, and test lifecycle callback
methods. See xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit-jupiter-di[Dependency
Injection with the `SpringExtension`] for further details.
methods. See xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit-jupiter-di[Dependency Injection with the `SpringExtension`] for further details.
* Powerful support for link:https://junit.org/junit5/docs/current/user-guide/#extensions-conditions[conditional
test execution] based on SpEL expressions, environment variables, system properties,
and so on. See the documentation for `@EnabledIf` and `@DisabledIf` in
xref:testing/annotations/integration-junit-jupiter.adoc[Spring JUnit Jupiter Testing Annotations]
for further details and examples.
xref:testing/annotations/integration-junit-jupiter.adoc[Spring JUnit Jupiter Testing Annotations] for further details and examples.
* Custom composed annotations that combine annotations from Spring and JUnit Jupiter. See
the `@TransactionalDevTestConfig` and `@TransactionalIntegrationTest` examples in
xref:testing/annotations/integration-meta.adoc[Meta-Annotation Support for Testing] for
further details.
xref:testing/annotations/integration-meta.adoc[Meta-Annotation Support for Testing] for further details.
The following code listing shows how to configure a test class to use the
`SpringExtension` in conjunction with `@ContextConfiguration`:
@@ -153,8 +307,7 @@ Kotlin::
======
See the documentation for `@SpringJUnitConfig` and `@SpringJUnitWebConfig` in
xref:testing/annotations/integration-junit-jupiter.adoc[Spring JUnit Jupiter Testing Annotations]
for further details.
xref:testing/annotations/integration-junit-jupiter.adoc[Spring JUnit Jupiter Testing Annotations] for further details.
[[testcontext-junit-jupiter-di]]
=== Dependency Injection with the `SpringExtension`
@@ -165,9 +318,10 @@ extension API from JUnit Jupiter, which lets Spring provide dependency injection
constructors, test methods, and test lifecycle callback methods.
Specifically, the `SpringExtension` can inject dependencies from the test's
`ApplicationContext` into test constructors and methods that are annotated with Spring's
`@BeforeTransaction` and `@AfterTransaction` or JUnit's `@BeforeAll`, `@AfterAll`,
`@BeforeEach`, `@AfterEach`, `@Test`, `@RepeatedTest`, `@ParameterizedTest`, and others.
`ApplicationContext` into test constructors and methods that are annotated with
Spring's `@BeforeTransaction` and `@AfterTransaction` or JUnit's `@BeforeAll`,
`@AfterAll`, `@BeforeEach`, `@AfterEach`, `@Test`, `@RepeatedTest`, `@ParameterizedTest`,
and others.
[[testcontext-junit-jupiter-di-constructor]]
@@ -187,9 +341,8 @@ autowirable if one of the following conditions is met (in order of precedence).
attribute set to `ALL`.
* The default _test constructor autowire mode_ has been changed to `ALL`.
See xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-testconstructor[`@TestConstructor`]
for details on the use of `@TestConstructor` and how to change the global _test
constructor autowire mode_.
See xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-testconstructor[`@TestConstructor`] for details on the use of
`@TestConstructor` and how to change the global _test constructor autowire mode_.
WARNING: If the constructor for a test class is considered to be _autowirable_, Spring
assumes the responsibility for resolving arguments for all parameters in the constructor.
@@ -254,9 +407,8 @@ Kotlin::
Note that this feature lets test dependencies be `final` and therefore immutable.
If the `spring.test.constructor.autowire.mode` property is to `all` (see
xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-testconstructor[`@TestConstructor`]),
we can omit the declaration of `@Autowired` on the constructor in the previous example,
resulting in the following.
xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-testconstructor[`@TestConstructor`]), we can omit the declaration of
`@Autowired` on the constructor in the previous example, resulting in the following.
[tabs]
======
@@ -401,19 +553,17 @@ honor `@NestedTestConfiguration` semantics.
In order to allow development teams to change the default to `OVERRIDE` for example,
for compatibility with Spring Framework 5.0 through 5.2 the default mode can be changed
globally via a JVM system property or a `spring.properties` file in the root of the
classpath. See the
xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-nestedtestconfiguration["Changing the default enclosing configuration inheritance mode"]
note for details.
classpath. See the xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-nestedtestconfiguration["Changing the default enclosing configuration inheritance mode"]
note for details.
Although the following "Hello World" example is very simplistic, it shows how to declare
common configuration on a top-level class that is inherited by its `@Nested` test
classes. In this particular example, only the `TestConfig` configuration class is
inherited. Each nested test class provides its own set of active profiles, resulting in a
distinct `ApplicationContext` for each nested test class (see
xref:testing/testcontext-framework/ctx-management/caching.adoc[Context Caching] for details).
Consult the list of
xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-nestedtestconfiguration[supported annotations]
to see which annotations can be inherited in `@Nested` test classes.
xref:testing/testcontext-framework/ctx-management/caching.adoc[Context Caching] for details). Consult the list of
xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-nestedtestconfiguration[supported annotations] to see
which annotations can be inherited in `@Nested` test classes.
[tabs]
======
@@ -476,174 +626,8 @@ Kotlin::
----
======
[[testcontext-junit4-support]]
== JUnit 4 Support
[[testcontext-junit4-runner]]
=== Spring JUnit 4 Runner
The Spring TestContext Framework offers full integration with JUnit 4 through a custom
runner (supported on JUnit 4.12 or higher). By annotating test classes with
`@RunWith(SpringJUnit4ClassRunner.class)` or the shorter `@RunWith(SpringRunner.class)`
variant, developers can implement standard JUnit 4-based unit and integration tests and
simultaneously reap the benefits of the TestContext framework, such as support for
loading application contexts, dependency injection of test instances, transactional test
method execution, and so on. If you want to use the Spring TestContext Framework with an
alternative runner (such as JUnit 4's `Parameterized` runner) or third-party runners
(such as the `MockitoJUnitRunner`), you can, optionally, use
xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit4-rules[Spring's support for JUnit rules]
instead.
The following code listing shows the minimal requirements for configuring a test class to
run with the custom Spring `Runner`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
@RunWith(SpringRunner.class)
@TestExecutionListeners({})
public class SimpleTest {
@Test
public void testMethod() {
// test logic...
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@RunWith(SpringRunner::class)
@TestExecutionListeners
class SimpleTest {
@Test
fun testMethod() {
// test logic...
}
}
----
======
In the preceding example, `@TestExecutionListeners` is configured with an empty list, to
disable the default listeners, which otherwise would require an `ApplicationContext` to
be configured through `@ContextConfiguration`.
[[testcontext-junit4-rules]]
=== Spring JUnit 4 Rules
The `org.springframework.test.context.junit4.rules` package provides the following JUnit
4 rules (supported on JUnit 4.12 or higher):
* `SpringClassRule`
* `SpringMethodRule`
`SpringClassRule` is a JUnit `TestRule` that supports class-level features of the Spring
TestContext Framework, whereas `SpringMethodRule` is a JUnit `MethodRule` that supports
instance-level and method-level features of the Spring TestContext Framework.
In contrast to the `SpringRunner`, Spring's rule-based JUnit support has the advantage of
being independent of any `org.junit.runner.Runner` implementation and can, therefore, be
combined with existing alternative runners (such as JUnit 4's `Parameterized`) or
third-party runners (such as the `MockitoJUnitRunner`).
To support the full functionality of the TestContext framework, you must combine a
`SpringClassRule` with a `SpringMethodRule`. The following example shows the proper way
to declare these rules in an integration test:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
// Optionally specify a non-Spring Runner via @RunWith(...)
@ContextConfiguration
public class IntegrationTest {
@ClassRule
public static final SpringClassRule springClassRule = new SpringClassRule();
@Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();
@Test
public void testMethod() {
// test logic...
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
// Optionally specify a non-Spring Runner via @RunWith(...)
@ContextConfiguration
class IntegrationTest {
@Rule
val springMethodRule = SpringMethodRule()
@Test
fun testMethod() {
// test logic...
}
companion object {
@ClassRule
val springClassRule = SpringClassRule()
}
}
----
======
[[testcontext-support-classes-junit4]]
=== JUnit 4 Base Classes
The `org.springframework.test.context.junit4` package provides the following support
classes for JUnit 4-based test cases (supported on JUnit 4.12 or higher):
* `AbstractJUnit4SpringContextTests`
* `AbstractTransactionalJUnit4SpringContextTests`
`AbstractJUnit4SpringContextTests` is an abstract base test class that integrates the
Spring TestContext Framework with explicit `ApplicationContext` testing support in a
JUnit 4 environment. When you extend `AbstractJUnit4SpringContextTests`, you can access a
`protected` `applicationContext` instance variable that you can use to perform explicit
bean lookups or to test the state of the context as a whole.
`AbstractTransactionalJUnit4SpringContextTests` is an abstract transactional extension of
`AbstractJUnit4SpringContextTests` that adds some convenience functionality for JDBC
access. This class expects a `javax.sql.DataSource` bean and a
`PlatformTransactionManager` bean to be defined in the `ApplicationContext`. When you
extend `AbstractTransactionalJUnit4SpringContextTests`, you can access a `protected`
`jdbcTemplate` instance variable that you can use to run SQL statements to query the
database. You can use such queries to confirm database state both before and after
running database-related application code, and Spring ensures that such queries run in
the scope of the same transaction as the application code. When used in conjunction with
an ORM tool, be sure to avoid
xref:testing/testcontext-framework/tx.adoc#testcontext-tx-false-positives[false positives].
As mentioned in xref:testing/support-jdbc.adoc[JDBC Testing Support],
`AbstractTransactionalJUnit4SpringContextTests` also provides convenience methods that
delegate to methods in `JdbcTestUtils` by using the aforementioned `jdbcTemplate`.
Furthermore, `AbstractTransactionalJUnit4SpringContextTests` provides an
`executeSqlScript(..)` method for running SQL scripts against the configured `DataSource`.
TIP: These classes are a convenience for extension. If you do not want your test classes
to be tied to a Spring-specific class hierarchy, you can configure your own custom test
classes by using `@RunWith(SpringRunner.class)` or
xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit4-rules[Spring's JUnit rules].
[[testcontext-support-classes-testng]]
== TestNG Support
== TestNG Support Classes
The `org.springframework.test.context.testng` package provides the following support
classes for TestNG based test cases:
@@ -666,8 +650,7 @@ extend `AbstractTransactionalTestNGSpringContextTests`, you can access a `protec
database. You can use such queries to confirm database state both before and after
running database-related application code, and Spring ensures that such queries run in
the scope of the same transaction as the application code. When used in conjunction with
an ORM tool, be sure to avoid
xref:testing/testcontext-framework/tx.adoc#testcontext-tx-false-positives[false positives].
an ORM tool, be sure to avoid xref:testing/testcontext-framework/tx.adoc#testcontext-tx-false-positives[false positives].
As mentioned in xref:testing/support-jdbc.adoc[JDBC Testing Support],
`AbstractTransactionalTestNGSpringContextTests` also provides convenience methods that
delegate to methods in `JdbcTestUtils` by using the aforementioned `jdbcTemplate`.
@@ -2,16 +2,9 @@
= Testing
:page-section-summary-toc: 1
To test code that uses the `WebClient`, you can use a mock web server, such as
https://github.com/square/okhttp#mockwebserver[OkHttp MockWebServer] or
https://wiremock.org/[WireMock]. Mock web servers accept requests over HTTP like a regular
server, and that means you can test with the same HTTP client that is also configured in
the same way as in production, which is important because there are often subtle
differences in the way different clients handle network I/O. Another advantage of mock
web servers is the ability to simulate specific network issues and conditions at the
transport level, in combination with the client used in production.
For example use of MockWebServer, see
To test code that uses the `WebClient`, you can use a mock web server, such as the
https://github.com/square/okhttp#mockwebserver[OkHttp MockWebServer]. To see an example
of its use, check out
{spring-framework-code}/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java[`WebClientIntegrationTests`]
in the Spring Framework test suite or the
https://github.com/square/okhttp/tree/master/samples/static-server[`static-server`]
@@ -19,7 +19,7 @@ Java::
private String name;
private FilePart file;
private MultipartFile file;
// ...
@@ -42,7 +42,7 @@ Kotlin::
----
class MyForm(
val name: String,
val file: FilePart)
val file: MultipartFile)
@Controller
class FileUploadController {
@@ -34,7 +34,11 @@ Controllers can then return a `Flux<List<B>>`; Reactor provides a dedicated oper
| `HttpHeaders`
| For returning a response with headers and no body.
| `ErrorResponse`, `ProblemDetail`
| `ErrorResponse`
| To render an RFC 9457 error response with details in the body,
see xref:web/webflux/ann-rest-exceptions.adoc[Error Responses]
| `ProblemDetail`
| To render an RFC 9457 error response with details in the body,
see xref:web/webflux/ann-rest-exceptions.adoc[Error Responses]
@@ -234,8 +234,8 @@ Kotlin::
--
URI path patterns can also have embedded `${...}` placeholders that are resolved on startup
by using `PropertySourcesPlaceholderConfigurer` against local, system, environment, and
other property sources. You can use this, for example, to parameterize a base URL based on
through `PropertySourcesPlaceholderConfigurer` against local, system, environment, and
other property sources. You can use this to, for example, parameterize a base URL based on
some external configuration.
NOTE: Spring WebFlux uses `PathPattern` and the `PathPatternParser` for URI path matching support.
@@ -4,13 +4,11 @@
Spring MVC has an extensive integration with Servlet asynchronous request
xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-processing[processing]:
* xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-deferredresult[`DeferredResult`],
xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-callable[`Callable`], and
xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-webasynctask[`WebAsyncTask`] return values
in controller methods provide support for a single asynchronous return value.
* xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-deferredresult[`DeferredResult`] and xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-callable[`Callable`]
return values in controller methods provide basic support for a single asynchronous
return value.
* Controllers can xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-http-streaming[stream] multiple values, including
xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-sse[SSE] and
xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-output-stream[raw data].
xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-sse[SSE] and xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-output-stream[raw data].
* Controllers can use reactive clients and return
xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-reactive-types[reactive types] for response handling.
@@ -98,47 +96,6 @@ xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-configuration-spring-mvc[config
[[mvc-ann-async-webasynctask]]
== `WebAsyncTask`
`WebAsyncTask` is comparable to using xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-callable[Callable]
but allows customizing additional settings such a request timeout value, and the
`AsyncTaskExecutor` to execute the `java.util.concurrent.Callable` with instead
of the defaults set up globally for Spring MVC. Below is an example of using `WebAsyncTask`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
@GetMapping("/callable")
WebAsyncTask<String> handle() {
return new WebAsyncTask<String>(20000L,()->{
Thread.sleep(10000); //simulate long-running task
return "asynchronous request completed";
});
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@GetMapping("/callable")
fun handle(): WebAsyncTask<String> {
return WebAsyncTask(20000L) {
Thread.sleep(10000) // simulate long-running task
"asynchronous request completed"
}
}
----
======
[[mvc-ann-async-processing]]
== Processing
@@ -433,7 +390,7 @@ reactive types from the controller method.
Reactive return values are handled as follows:
* A single-value promise is adapted to, similar to using `DeferredResult`. Examples
include `CompletionStage` (JDK), Mono` (Reactor), and `Single` (RxJava).
include `Mono` (Reactor) or `Single` (RxJava).
* A multi-value stream with a streaming media type (such as `application/x-ndjson`
or `text/event-stream`) is adapted to, similar to using `ResponseBodyEmitter` or
`SseEmitter`. Examples include `Flux` (Reactor) or `Observable` (RxJava).
@@ -10,20 +10,18 @@ to any controller. Moreover, as of 5.3, `@ExceptionHandler` methods in `@Control
can be used to handle exceptions from any `@Controller` or any other handler.
`@ControllerAdvice` is meta-annotated with `@Component` and therefore can be registered as
a Spring bean through xref:core/beans/java/instantiating-container.adoc#beans-java-instantiating-container-scan[component scanning].
`@RestControllerAdvice` is a shortcut annotation that combines `@ControllerAdvice`
with `@ResponseBody`, in effect simply an `@ControllerAdvice` whose exception handler
methods render to the response body.
a Spring bean through xref:core/beans/java/instantiating-container.adoc#beans-java-instantiating-container-scan[component scanning]
. `@RestControllerAdvice` is meta-annotated with `@ControllerAdvice`
and `@ResponseBody`, and that means `@ExceptionHandler` methods will have their return
value rendered via response body message conversion, rather than via HTML views.
On startup, `RequestMappingHandlerMapping` and `ExceptionHandlerExceptionResolver` detect
controller advice beans and apply them at runtime. Global `@ExceptionHandler` methods,
from an `@ControllerAdvice`, are applied _after_ local ones, from the `@Controller`.
By contrast, global `@ModelAttribute` and `@InitBinder` methods are applied _before_ local ones.
By default, both `@ControllerAdvice` and `@RestControllerAdvice` apply to any controller,
including `@Controller` and `@RestController`. Use attributes of the annotation to narrow
the set of controllers and handlers that they apply to. For example:
The `@ControllerAdvice` annotation has attributes that let you narrow the set of controllers
and handlers that they apply to. For example:
[tabs]
======
@@ -177,9 +177,13 @@ the content negotiation during the error handling phase will decide which conten
be converted through `HttpMessageConverter` instances and written to the response.
See xref:web/webmvc/mvc-controller/ann-methods/responseentity.adoc[ResponseEntity].
| `ErrorResponse`, `ProblemDetail`
| `ErrorResponse`
| To render an RFC 9457 error response with details in the body,
see xref:web/webmvc/mvc-ann-rest-exceptions.adoc[Error Responses]
see xref:web/webmvc/mvc-ann-rest-exceptions.adoc[Error Responses]
| `ProblemDetail`
| To render an RFC 9457 error response with details in the body,
see xref:web/webmvc/mvc-ann-rest-exceptions.adoc[Error Responses]
| `String`
| A view name to be resolved with `ViewResolver` implementations and used together with the
@@ -243,7 +243,7 @@ Kotlin::
======
If there is no `BindingResult` parameter after the `@ModelAttribute`, then
a `MethodArgumentNotValidException` is raised with the validation errors. However, if method
`MethodArgumentNotValueException` is raised with the validation errors. However, if method
validation applies because other parameters have `@jakarta.validation.Constraint` annotations,
then `HandlerMethodValidationException` is raised instead. For more details, see the section
xref:web/webmvc/mvc-controller/ann-validation.adoc[Validation].
@@ -22,7 +22,11 @@ supported for all return values.
| `HttpHeaders`
| For returning a response with headers and no body.
| `ErrorResponse`, `ProblemDetail`
| `ErrorResponse`
| To render an RFC 9457 error response with details in the body,
see xref:web/webmvc/mvc-ann-rest-exceptions.adoc[Error Responses]
| `ProblemDetail`
| To render an RFC 9457 error response with details in the body,
see xref:web/webmvc/mvc-ann-rest-exceptions.adoc[Error Responses]
@@ -38,7 +38,7 @@ to inform the server that the original port was `443`.
==== X-Forwarded-Proto
While not standard, https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto[`X-Forwarded-Proto: (https|http)`]
is a de-facto standard header that is used to communicate the original protocol (for example, https / http)
is a de-facto standard header that is used to communicate the original protocol (for example, https / https)
to a downstream server. For example, if a request of `https://example.com/resource` is sent to
a proxy which forwards the request to `http://localhost:8080/resource`, then a header of
`X-Forwarded-Proto: https` can be sent to inform the server that the original protocol was `https`.
@@ -119,4 +119,4 @@ https://example.com/api/app1/{path} -> http://localhost:8080/app1/{path}
In this case, the proxy has a prefix of `/api/app1` and the server has a prefix of
`/app1`. The proxy can send `X-Forwarded-Prefix: /api/app1` to have the original prefix
`/api/app1` override the server prefix `/app1`.
`/api/app1` override the server prefix `/app1`.
+11 -10
View File
@@ -7,21 +7,21 @@ javaPlatform {
}
dependencies {
api(platform("com.fasterxml.jackson:jackson-bom:2.18.4"))
api(platform("io.micrometer:micrometer-bom:1.14.8"))
api(platform("io.netty:netty-bom:4.1.121.Final"))
api(platform("com.fasterxml.jackson:jackson-bom:2.18.3"))
api(platform("io.micrometer:micrometer-bom:1.14.5"))
api(platform("io.netty:netty-bom:4.1.119.Final"))
api(platform("io.netty:netty5-bom:5.0.0.Alpha5"))
api(platform("io.projectreactor:reactor-bom:2024.0.7"))
api(platform("io.projectreactor:reactor-bom:2024.0.4"))
api(platform("io.rsocket:rsocket-bom:1.1.5"))
api(platform("org.apache.groovy:groovy-bom:4.0.27"))
api(platform("org.apache.groovy:groovy-bom:4.0.26"))
api(platform("org.apache.logging.log4j:log4j-bom:2.21.1"))
api(platform("org.assertj:assertj-bom:3.27.3"))
api(platform("org.eclipse.jetty:jetty-bom:12.0.21"))
api(platform("org.eclipse.jetty.ee10:jetty-ee10-bom:12.0.21"))
api(platform("org.eclipse.jetty:jetty-bom:12.0.18"))
api(platform("org.eclipse.jetty.ee10:jetty-ee10-bom:12.0.18"))
api(platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.8.1"))
api(platform("org.jetbrains.kotlinx:kotlinx-serialization-bom:1.6.3"))
api(platform("org.junit:junit-bom:5.13.1"))
api(platform("org.mockito:mockito-bom:5.18.0"))
api(platform("org.junit:junit-bom:5.12.2"))
api(platform("org.mockito:mockito-bom:5.17.0"))
constraints {
api("com.fasterxml:aalto-xml:1.3.2")
@@ -47,6 +47,7 @@ dependencies {
api("com.thoughtworks.xstream:xstream:1.4.21")
api("commons-io:commons-io:2.15.0")
api("de.bechte.junit:junit-hierarchicalcontextrunner:4.12.2")
api("io.micrometer:context-propagation:1.1.1")
api("io.mockk:mockk:1.13.4")
api("io.projectreactor.netty:reactor-netty5-http:2.0.0-M3")
api("io.projectreactor.tools:blockhound:1.0.8.RELEASE")
@@ -99,7 +100,7 @@ dependencies {
api("org.apache.derby:derby:10.16.1.1")
api("org.apache.derby:derbyclient:10.16.1.1")
api("org.apache.derby:derbytools:10.16.1.1")
api("org.apache.httpcomponents.client5:httpclient5:5.5")
api("org.apache.httpcomponents.client5:httpclient5:5.4.3")
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.3.4")
api("org.apache.poi:poi-ooxml:5.2.5")
api("org.apache.tomcat.embed:tomcat-embed-core:10.1.28")
+1 -1
View File
@@ -1,4 +1,4 @@
version=6.2.8
version=6.2.6
org.gradle.caching=true
org.gradle.jvmargs=-Xmx2048m
+1 -1
View File
@@ -69,7 +69,7 @@ normalization {
javadoc {
description = "Generates project-level javadoc for use in -javadoc jar"
failOnError = true
options {
encoding = "UTF-8"
memberLevel = JavadocMemberLevel.PROTECTED
Binary file not shown.
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Vendored
+2 -2
View File
@@ -114,7 +114,7 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH="\\\"\\\""
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
@@ -213,7 +213,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
Vendored
+2 -2
View File
@@ -70,11 +70,11 @@ goto fail
:execute
@rem Setup the command line
set CLASSPATH=
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
+1
View File
@@ -1,6 +1,7 @@
plugins {
id "com.gradle.develocity" version "3.19"
id "io.spring.ge.conventions" version "0.0.17"
id "org.gradle.toolchains.foojay-resolver-convention" version "0.7.0"
}
include "spring-aop"
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -112,12 +112,12 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
private BeanFactory beanFactory;
@Nullable
private transient volatile ClassLoader pointcutClassLoader;
private transient ClassLoader pointcutClassLoader;
@Nullable
private transient volatile PointcutExpression pointcutExpression;
private transient PointcutExpression pointcutExpression;
private transient volatile boolean pointcutParsingFailed;
private transient boolean pointcutParsingFailed = false;
/**
@@ -197,14 +197,11 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
* Lazily build the underlying AspectJ pointcut expression.
*/
private PointcutExpression obtainPointcutExpression() {
PointcutExpression pointcutExpression = this.pointcutExpression;
if (pointcutExpression == null) {
ClassLoader pointcutClassLoader = determinePointcutClassLoader();
pointcutExpression = buildPointcutExpression(pointcutClassLoader);
this.pointcutClassLoader = pointcutClassLoader;
this.pointcutExpression = pointcutExpression;
if (this.pointcutExpression == null) {
this.pointcutClassLoader = determinePointcutClassLoader();
this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader);
}
return pointcutExpression;
return this.pointcutExpression;
}
/**
@@ -470,24 +467,40 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
}
private ShadowMatch getShadowMatch(Method targetMethod, Method originalMethod) {
ShadowMatchKey key = new ShadowMatchKey(this, targetMethod);
ShadowMatch shadowMatch = ShadowMatchUtils.getShadowMatch(key);
ShadowMatch shadowMatch = ShadowMatchUtils.getShadowMatch(this, targetMethod);
if (shadowMatch == null) {
PointcutExpression pointcutExpression = obtainPointcutExpression();
synchronized (pointcutExpression) {
shadowMatch = ShadowMatchUtils.getShadowMatch(key);
if (shadowMatch != null) {
return shadowMatch;
}
PointcutExpression fallbackExpression = null;
Method methodToMatch = targetMethod;
PointcutExpression fallbackExpression = null;
Method methodToMatch = targetMethod;
try {
try {
shadowMatch = obtainPointcutExpression().matchesMethodExecution(methodToMatch);
}
catch (ReflectionWorldException ex) {
// Failed to introspect target method, probably because it has been loaded
// in a special ClassLoader. Let's try the declaring ClassLoader instead...
try {
shadowMatch = pointcutExpression.matchesMethodExecution(methodToMatch);
fallbackExpression = getFallbackPointcutExpression(methodToMatch.getDeclaringClass());
if (fallbackExpression != null) {
shadowMatch = fallbackExpression.matchesMethodExecution(methodToMatch);
}
}
catch (ReflectionWorldException ex2) {
fallbackExpression = null;
}
}
if (targetMethod != originalMethod && (shadowMatch == null ||
(Proxy.isProxyClass(targetMethod.getDeclaringClass()) &&
(shadowMatch.neverMatches() || containsAnnotationPointcut())))) {
// Fall back to the plain original method in case of no resolvable match or a
// negative match on a proxy class (which doesn't carry any annotations on its
// redeclared methods), as well as for annotation pointcuts.
methodToMatch = originalMethod;
try {
shadowMatch = obtainPointcutExpression().matchesMethodExecution(methodToMatch);
}
catch (ReflectionWorldException ex) {
// Failed to introspect target method, probably because it has been loaded
// in a special ClassLoader. Let's try the declaring ClassLoader instead...
// Could neither introspect the target class nor the proxy class ->
// let's try the original method's declaring class before we give up...
try {
fallbackExpression = getFallbackPointcutExpression(methodToMatch.getDeclaringClass());
if (fallbackExpression != null) {
@@ -498,45 +511,21 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
fallbackExpression = null;
}
}
if (targetMethod != originalMethod && (shadowMatch == null ||
(Proxy.isProxyClass(targetMethod.getDeclaringClass()) &&
(shadowMatch.neverMatches() || containsAnnotationPointcut())))) {
// Fall back to the plain original method in case of no resolvable match or a
// negative match on a proxy class (which doesn't carry any annotations on its
// redeclared methods), as well as for annotation pointcuts.
methodToMatch = originalMethod;
try {
shadowMatch = pointcutExpression.matchesMethodExecution(methodToMatch);
}
catch (ReflectionWorldException ex) {
// Could neither introspect the target class nor the proxy class ->
// let's try the original method's declaring class before we give up...
try {
fallbackExpression = getFallbackPointcutExpression(methodToMatch.getDeclaringClass());
if (fallbackExpression != null) {
shadowMatch = fallbackExpression.matchesMethodExecution(methodToMatch);
}
}
catch (ReflectionWorldException ex2) {
fallbackExpression = null;
}
}
}
}
catch (Throwable ex) {
// Possibly AspectJ 1.8.10 encountering an invalid signature
logger.debug("PointcutExpression matching rejected target method", ex);
fallbackExpression = null;
}
if (shadowMatch == null) {
shadowMatch = new ShadowMatchImpl(org.aspectj.util.FuzzyBoolean.NO, null, null, null);
}
else if (shadowMatch.maybeMatches() && fallbackExpression != null) {
shadowMatch = new DefensiveShadowMatch(shadowMatch,
fallbackExpression.matchesMethodExecution(methodToMatch));
}
shadowMatch = ShadowMatchUtils.setShadowMatch(key, shadowMatch);
}
catch (Throwable ex) {
// Possibly AspectJ 1.8.10 encountering an invalid signature
logger.debug("PointcutExpression matching rejected target method", ex);
fallbackExpression = null;
}
if (shadowMatch == null) {
shadowMatch = new ShadowMatchImpl(org.aspectj.util.FuzzyBoolean.NO, null, null, null);
}
else if (shadowMatch.maybeMatches() && fallbackExpression != null) {
shadowMatch = new DefensiveShadowMatch(shadowMatch,
fallbackExpression.matchesMethodExecution(methodToMatch));
}
shadowMatch = ShadowMatchUtils.setShadowMatch(this, targetMethod, shadowMatch);
}
return shadowMatch;
}
@@ -731,8 +720,4 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
}
}
private record ShadowMatchKey(AspectJExpressionPointcut expression, Method method) {
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,48 +16,24 @@
package org.springframework.aop.aspectj;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.aspectj.weaver.tools.ShadowMatch;
import org.springframework.aop.support.ExpressionPointcut;
import org.springframework.lang.Nullable;
/**
* Internal {@link ShadowMatch} utilities.
*
* @author Stephane Nicoll
* @author Juergen Hoeller
* @since 6.2
*/
public abstract class ShadowMatchUtils {
private static final Map<Object, ShadowMatch> shadowMatchCache = new ConcurrentHashMap<>(256);
/**
* Find a {@link ShadowMatch} for the specified key.
* @param key the key to use
* @return the {@code ShadowMatch} to use for the specified key,
* or {@code null} if none found
*/
@Nullable
static ShadowMatch getShadowMatch(Object key) {
return shadowMatchCache.get(key);
}
/**
* Associate the {@link ShadowMatch} with the specified key.
* If an entry already exists, the given {@code shadowMatch} is ignored.
* @param key the key to use
* @param shadowMatch the shadow match to use for this key
* if none already exists
* @return the shadow match to use for the specified key
*/
static ShadowMatch setShadowMatch(Object key, ShadowMatch shadowMatch) {
ShadowMatch existing = shadowMatchCache.putIfAbsent(key, shadowMatch);
return (existing != null ? existing : shadowMatch);
}
private static final Map<Key, ShadowMatch> shadowMatchCache = new ConcurrentHashMap<>(256);
/**
* Clear the cache of computed {@link ShadowMatch} instances.
@@ -66,4 +42,34 @@ public abstract class ShadowMatchUtils {
shadowMatchCache.clear();
}
/**
* Return the {@link ShadowMatch} for the specified {@link ExpressionPointcut}
* and {@link Method} or {@code null} if none is found.
* @param expression the expression
* @param method the method
* @return the {@code ShadowMatch} to use for the specified expression and method
*/
@Nullable
static ShadowMatch getShadowMatch(ExpressionPointcut expression, Method method) {
return shadowMatchCache.get(new Key(expression, method));
}
/**
* Associate the {@link ShadowMatch} to the specified {@link ExpressionPointcut}
* and method. If an entry already exists, the given {@code shadowMatch} is
* ignored.
* @param expression the expression
* @param method the method
* @param shadowMatch the shadow match to use for this expression and method
* if none already exists
* @return the shadow match to use for the specified expression and method
*/
static ShadowMatch setShadowMatch(ExpressionPointcut expression, Method method, ShadowMatch shadowMatch) {
ShadowMatch existing = shadowMatchCache.putIfAbsent(new Key(expression, method), shadowMatch);
return (existing != null ? existing : shadowMatch);
}
private record Key(ExpressionPointcut expression, Method method) {}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,7 +24,7 @@ import java.util.Map;
import org.springframework.lang.Nullable;
/**
* Abstract superclass for counting advice, etc.
* Abstract superclass for counting advices etc.
*
* @author Rod Johnson
* @author Chris Beams
@@ -62,7 +62,7 @@ public class MethodCounter implements Serializable {
*/
@Override
public boolean equals(@Nullable Object other) {
return (other != null && getClass() == other.getClass());
return (other != null && other.getClass() == this.getClass());
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,7 +33,6 @@ import java.util.Map;
import java.util.Set;
import kotlin.jvm.JvmClassMappingKt;
import kotlin.jvm.internal.DefaultConstructorMarker;
import kotlin.reflect.KClass;
import kotlin.reflect.KFunction;
import kotlin.reflect.KParameter;
@@ -670,9 +669,7 @@ public abstract class BeanUtils {
ConstructorProperties cp = ctor.getAnnotation(ConstructorProperties.class);
String[] paramNames = (cp != null ? cp.value() : parameterNameDiscoverer.getParameterNames(ctor));
Assert.state(paramNames != null, () -> "Cannot resolve parameter names for constructor " + ctor);
int parameterCount = (KotlinDetector.isKotlinReflectPresent() && KotlinDelegate.hasDefaultConstructorMarker(ctor) ?
ctor.getParameterCount() - 1 : ctor.getParameterCount());
Assert.state(paramNames.length == parameterCount,
Assert.state(paramNames.length == ctor.getParameterCount(),
() -> "Invalid number of parameter names: " + paramNames.length + " for constructor " + ctor);
return paramNames;
}
@@ -942,11 +939,6 @@ public abstract class BeanUtils {
}
return kotlinConstructor.callBy(argParameters);
}
public static boolean hasDefaultConstructorMarker(Constructor<?> ctor) {
int parameterCount = ctor.getParameterCount();
return parameterCount > 0 && ctor.getParameters()[parameterCount -1].getType() == DefaultConstructorMarker.class;
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -68,10 +68,10 @@ public class AutowiredArgumentsCodeGenerator {
for (int i = startIndex; i < parameterTypes.length; i++) {
code.add(i > startIndex ? ", " : "");
if (!ambiguous) {
code.add("$L.get($L)", variableName, i);
code.add("$L.get($L)", variableName, i - startIndex);
}
else {
code.add("$L.get($L, $T.class)", variableName, i, parameterTypes[i]);
code.add("$L.get($L, $T.class)", variableName, i - startIndex, parameterTypes[i]);
}
}
return code.build();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,10 +20,8 @@ import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.core.env.AbstractPropertyResolver;
import org.springframework.lang.Nullable;
import org.springframework.util.StringValueResolver;
import org.springframework.util.SystemPropertyUtils;
/**
* Abstract base class for property resource configurers that resolve placeholders
@@ -39,16 +37,16 @@ import org.springframework.util.SystemPropertyUtils;
*
* <pre class="code">
* &lt;bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"&gt;
* &lt;property name="driverClassName" value="${jdbc.driver}" /&gt;
* &lt;property name="url" value="jdbc:${jdbc.dbname}" /&gt;
* &lt;property name="driverClassName" value="${driver}" /&gt;
* &lt;property name="url" value="jdbc:${dbname}" /&gt;
* &lt;/bean&gt;
* </pre>
*
* Example properties file:
*
* <pre class="code">
* jdbc.driver=com.mysql.jdbc.Driver
* jdbc.dbname=mysql:mydb</pre>
* driver=com.mysql.jdbc.Driver
* dbname=mysql:mydb</pre>
*
* Annotated bean definitions may take advantage of property replacement using
* the {@link org.springframework.beans.factory.annotation.Value @Value} annotation:
@@ -81,12 +79,11 @@ import org.springframework.util.SystemPropertyUtils;
* <p>Example XML property with default value:
*
* <pre class="code">
* &lt;property name="url" value="jdbc:${jdbc.dbname:defaultdb}" /&gt;
* &lt;property name="url" value="jdbc:${dbname:defaultdb}" /&gt;
* </pre>
*
* @author Chris Beams
* @author Juergen Hoeller
* @author Sam Brannen
* @since 3.1
* @see PropertyPlaceholderConfigurer
* @see org.springframework.context.support.PropertySourcesPlaceholderConfigurer
@@ -95,21 +92,16 @@ public abstract class PlaceholderConfigurerSupport extends PropertyResourceConfi
implements BeanNameAware, BeanFactoryAware {
/** Default placeholder prefix: {@value}. */
public static final String DEFAULT_PLACEHOLDER_PREFIX = SystemPropertyUtils.PLACEHOLDER_PREFIX;
public static final String DEFAULT_PLACEHOLDER_PREFIX = "${";
/** Default placeholder suffix: {@value}. */
public static final String DEFAULT_PLACEHOLDER_SUFFIX = SystemPropertyUtils.PLACEHOLDER_SUFFIX;
public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";
/** Default value separator: {@value}. */
public static final String DEFAULT_VALUE_SEPARATOR = SystemPropertyUtils.VALUE_SEPARATOR;
/**
* Default escape character: {@code '\'}.
* @since 6.2
* @see AbstractPropertyResolver#getDefaultEscapeCharacter()
*/
public static final Character DEFAULT_ESCAPE_CHARACTER = SystemPropertyUtils.ESCAPE_CHARACTER;
public static final String DEFAULT_VALUE_SEPARATOR = ":";
/** Default escape character: {@code '\'}. */
public static final Character DEFAULT_ESCAPE_CHARACTER = '\\';
/** Defaults to {@value #DEFAULT_PLACEHOLDER_PREFIX}. */
protected String placeholderPrefix = DEFAULT_PLACEHOLDER_PREFIX;
@@ -121,11 +113,9 @@ public abstract class PlaceholderConfigurerSupport extends PropertyResourceConfi
@Nullable
protected String valueSeparator = DEFAULT_VALUE_SEPARATOR;
/**
* The default is determined by {@link AbstractPropertyResolver#getDefaultEscapeCharacter()}.
*/
/** Defaults to {@link #DEFAULT_ESCAPE_CHARACTER}. */
@Nullable
protected Character escapeCharacter = AbstractPropertyResolver.getDefaultEscapeCharacter();
protected Character escapeCharacter = DEFAULT_ESCAPE_CHARACTER;
protected boolean trimValues = false;
@@ -143,7 +133,7 @@ public abstract class PlaceholderConfigurerSupport extends PropertyResourceConfi
/**
* Set the prefix that a placeholder string starts with.
* <p>The default is {@value #DEFAULT_PLACEHOLDER_PREFIX}.
* The default is {@value #DEFAULT_PLACEHOLDER_PREFIX}.
*/
public void setPlaceholderPrefix(String placeholderPrefix) {
this.placeholderPrefix = placeholderPrefix;
@@ -151,32 +141,31 @@ public abstract class PlaceholderConfigurerSupport extends PropertyResourceConfi
/**
* Set the suffix that a placeholder string ends with.
* <p>The default is {@value #DEFAULT_PLACEHOLDER_SUFFIX}.
* The default is {@value #DEFAULT_PLACEHOLDER_SUFFIX}.
*/
public void setPlaceholderSuffix(String placeholderSuffix) {
this.placeholderSuffix = placeholderSuffix;
}
/**
* Specify the separating character between the placeholder variable and the
* associated default value, or {@code null} if no such special character
* should be processed as a value separator.
* <p>The default is {@value #DEFAULT_VALUE_SEPARATOR}.
* Specify the separating character between the placeholder variable
* and the associated default value, or {@code null} if no such
* special character should be processed as a value separator.
* The default is {@value #DEFAULT_VALUE_SEPARATOR}.
*/
public void setValueSeparator(@Nullable String valueSeparator) {
this.valueSeparator = valueSeparator;
}
/**
* Set the escape character to use to ignore the
* {@linkplain #setPlaceholderPrefix(String) placeholder prefix} and the
* {@linkplain #setValueSeparator(String) value separator}, or {@code null}
* if no escaping should take place.
* <p>The default is determined by {@link AbstractPropertyResolver#getDefaultEscapeCharacter()}.
* Specify the escape character to use to ignore placeholder prefix
* or value separator, or {@code null} if no escaping should take
* place.
* <p>Default is {@link #DEFAULT_ESCAPE_CHARACTER}.
* @since 6.2
*/
public void setEscapeCharacter(@Nullable Character escapeCharacter) {
this.escapeCharacter = escapeCharacter;
public void setEscapeCharacter(@Nullable Character escsEscapeCharacter) {
this.escapeCharacter = escsEscapeCharacter;
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,14 +35,12 @@ import org.springframework.beans.factory.BeanInitializationException;
*
* Example properties file:
*
* <pre class="code">
* dataSource.driverClassName=com.mysql.jdbc.Driver
* <pre class="code">dataSource.driverClassName=com.mysql.jdbc.Driver
* dataSource.url=jdbc:mysql:mydb</pre>
*
* <p>In contrast to {@link PropertyPlaceholderConfigurer}, the original definition
* can have default values or no values at all for such bean properties. If an
* overriding properties file does not have an entry for a certain bean property,
* the default context definition is used.
* In contrast to PropertyPlaceholderConfigurer, the original definition can have default
* values or no values at all for such bean properties. If an overriding properties file does
* not have an entry for a certain bean property, the default context definition is used.
*
* <p>Note that the context definition <i>is not</i> aware of being overridden;
* so this is not immediately obvious when looking at the XML definition file.
@@ -997,17 +997,9 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
*/
@Nullable
private FactoryBean<?> getSingletonFactoryBeanForTypeCheck(String beanName, RootBeanDefinition mbd) {
Boolean lockFlag = isCurrentThreadAllowedToHoldSingletonLock();
if (lockFlag == null) {
this.singletonLock.lock();
}
else {
boolean locked = (lockFlag && this.singletonLock.tryLock());
if (!locked) {
// Avoid shortcut FactoryBean instance but allow for subsequent type-based resolution.
resolveBeanClass(mbd, beanName);
return null;
}
boolean locked = this.singletonLock.tryLock();
if (!locked) {
return null;
}
try {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2021 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.
@@ -29,6 +29,10 @@ import org.springframework.lang.Nullable;
* load and register methods for bean definitions, specific to
* their bean definition format.
*
* <p>Note that a bean definition reader does not have to implement
* this interface. It only serves as a suggestion for bean definition
* readers that want to follow standard naming conventions.
*
* @author Juergen Hoeller
* @since 1.1
* @see org.springframework.core.io.Resource
@@ -1066,9 +1066,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
@Nullable
protected Boolean isCurrentThreadAllowedToHoldSingletonLock() {
String mainThreadPrefix = this.mainThreadPrefix;
if (mainThreadPrefix != null) {
// We only differentiate in the preInstantiateSingletons phase, using
// the volatile mainThreadPrefix field as an indicator for that phase.
if (this.mainThreadPrefix != null) {
// We only differentiate in the preInstantiateSingletons phase.
PreInstantiation preInstantiation = this.preInstantiationThread.get();
if (preInstantiation != null) {
@@ -1088,7 +1087,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
}
else if (this.strictLocking == null) {
// No explicit locking configuration -> infer appropriate locking.
if (!getThreadNamePrefix().equals(mainThreadPrefix)) {
if (mainThreadPrefix != null && !getThreadNamePrefix().equals(mainThreadPrefix)) {
// An unmanaged thread (assumed to be application-internal) with lenient locking,
// and not part of the same thread pool that provided the main bootstrap thread
// (excluding scenarios where we are hit by multiple external bootstrap threads).
@@ -1679,12 +1678,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
if (autowiredBeanNames != null) {
autowiredBeanNames.add(dependencyName);
}
boolean preExisting = containsSingleton(dependencyName);
Object dependencyBean = getBean(dependencyName);
if (preExisting && dependencyBean instanceof NullBean) {
// for backwards compatibility with addCandidateEntry in the regular code path
dependencyBean = null;
}
return resolveInstance(dependencyBean, descriptor, type, dependencyName);
}
}
@@ -1765,6 +1759,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
throw new BeanNotOfRequiredTypeException(name, type, candidate.getClass());
}
return result;
}
@Nullable
@@ -2249,7 +2244,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
* i.e. whether the candidate points back to the original bean or to a factory method
* on the original bean.
*/
@Contract("null, _ -> false; _, null -> false;")
@Contract("null, _ -> false;_, null -> false;")
private boolean isSelfReference(@Nullable String beanName, @Nullable String candidateName) {
return (beanName != null && candidateName != null &&
(beanName.equals(candidateName) || (containsBeanDefinition(candidateName) &&
@@ -271,15 +271,13 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
// Fallback as of 6.2: process given singleton bean outside of singleton lock.
// Thread-safe exposure is still guaranteed, there is just a risk of collisions
// when triggering creation of other beans as dependencies of the current bean.
if (logger.isInfoEnabled()) {
logger.info("Obtaining singleton bean '" + beanName + "' in thread \"" +
Thread.currentThread().getName() + "\" while other thread holds " +
"singleton lock for other beans " + this.singletonsCurrentlyInCreation);
}
this.lenientCreationLock.lock();
try {
if (logger.isInfoEnabled()) {
Set<String> lockedBeans = new HashSet<>(this.singletonsCurrentlyInCreation);
lockedBeans.removeAll(this.singletonsInLenientCreation);
logger.info("Obtaining singleton bean '" + beanName + "' in thread \"" +
currentThread.getName() + "\" while other thread holds singleton " +
"lock for other beans " + lockedBeans);
}
this.singletonsInLenientCreation.add(beanName);
}
finally {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -118,15 +118,7 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg
*/
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
if (factory.isSingleton() && containsSingleton(beanName)) {
Boolean lockFlag = isCurrentThreadAllowedToHoldSingletonLock();
boolean locked;
if (lockFlag == null) {
this.singletonLock.lock();
locked = true;
}
else {
locked = (lockFlag && this.singletonLock.tryLock());
}
this.singletonLock.lock();
try {
Object object = this.factoryBeanObjectCache.get(beanName);
if (object == null) {
@@ -139,13 +131,11 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg
}
else {
if (shouldPostProcess) {
if (locked) {
if (isSingletonCurrentlyInCreation(beanName)) {
// Temporarily return non-post-processed object, not storing it yet
return object;
}
beforeSingletonCreation(beanName);
if (isSingletonCurrentlyInCreation(beanName)) {
// Temporarily return non-post-processed object, not storing it yet
return object;
}
beforeSingletonCreation(beanName);
try {
object = postProcessObjectFromFactoryBean(object, beanName);
}
@@ -154,9 +144,7 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg
"Post-processing of FactoryBean's singleton object failed", ex);
}
finally {
if (locked) {
afterSingletonCreation(beanName);
}
afterSingletonCreation(beanName);
}
}
if (containsSingleton(beanName)) {
@@ -167,9 +155,7 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg
return object;
}
finally {
if (locked) {
this.singletonLock.unlock();
}
this.singletonLock.unlock();
}
}
else {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,14 +33,13 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Chris Beams
*/
class FactoryBeanLookupTests {
private final BeanFactory beanFactory = new DefaultListableBeanFactory();
private BeanFactory beanFactory;
@BeforeEach
void setUp() {
beanFactory = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader((BeanDefinitionRegistry) beanFactory).loadBeanDefinitions(
new ClassPathResource("FactoryBeanLookupTests-context.xml", getClass()));
new ClassPathResource("FactoryBeanLookupTests-context.xml", this.getClass()));
}
@Test
@@ -72,7 +71,6 @@ class FactoryBeanLookupTests {
Foo foo = beanFactory.getBean("fooFactory", Foo.class);
assertThat(foo).isNotNull();
}
}
class FooFactoryBean extends AbstractFactoryBean<Foo> {
@@ -960,34 +960,11 @@ class AutowiredAnnotationBeanPostProcessorTests {
@Test
void constructorResourceInjectionWithNoCandidatesAndNoFallback() {
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ConstructorWithoutFallbackBean.class));
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
.isThrownBy(() -> bf.getBean("annotatedBean"))
.satisfies(methodParameterDeclaredOn(ConstructorWithoutFallbackBean.class));
}
@Test
void constructorResourceInjectionWithCandidateAndNoFallback() {
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ConstructorWithoutFallbackBean.class));
RootBeanDefinition tb = new RootBeanDefinition(NullFactoryMethods.class);
tb.setFactoryMethodName("createTestBean");
bf.registerBeanDefinition("testBean", tb);
bf.getBean("testBean");
assertThat(bf.getBean("annotatedBean", ConstructorWithoutFallbackBean.class).getTestBean3()).isNull();
}
@Test
void constructorResourceInjectionWithNameMatchingCandidateAndNoFallback() {
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ConstructorWithoutFallbackBean.class));
RootBeanDefinition tb = new RootBeanDefinition(NullFactoryMethods.class);
tb.setFactoryMethodName("createTestBean");
bf.registerBeanDefinition("testBean3", tb);
bf.getBean("testBean3");
assertThat(bf.getBean("annotatedBean", ConstructorWithoutFallbackBean.class).getTestBean3()).isNull();
}
@Test
void constructorResourceInjectionWithSometimesNullBeanEarly() {
RootBeanDefinition bd = new RootBeanDefinition(ConstructorWithNullableArgument.class);
@@ -1216,7 +1193,6 @@ class AutowiredAnnotationBeanPostProcessorTests {
@Test
void singleConstructorInjectionWithMissingDependency() {
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SingleConstructorOptionalCollectionBean.class));
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
.isThrownBy(() -> bf.getBean("annotatedBean"));
}
@@ -1227,7 +1203,6 @@ class AutowiredAnnotationBeanPostProcessorTests {
RootBeanDefinition tb = new RootBeanDefinition(NullFactoryMethods.class);
tb.setFactoryMethodName("createTestBean");
bf.registerBeanDefinition("testBean", tb);
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
.isThrownBy(() -> bf.getBean("annotatedBean"));
}
@@ -2453,7 +2428,7 @@ class AutowiredAnnotationBeanPostProcessorTests {
}
@Test
@SuppressWarnings({ "unchecked", "rawtypes" })
@SuppressWarnings("unchecked")
void genericsBasedConstructorInjectionWithNonTypedTarget() {
RootBeanDefinition bd = new RootBeanDefinition(RepositoryConstructorInjectionBean.class);
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
@@ -3085,6 +3060,7 @@ class AutowiredAnnotationBeanPostProcessorTests {
protected ITestBean testBean3;
@Autowired(required = false)
public ConstructorWithoutFallbackBean(ITestBean testBean3) {
this.testBean3 = testBean3;
}
@@ -3099,6 +3075,7 @@ class AutowiredAnnotationBeanPostProcessorTests {
protected ITestBean testBean3;
@Autowired(required = false)
public ConstructorWithNullableArgument(@Nullable ITestBean testBean3) {
this.testBean3 = testBean3;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -67,7 +67,7 @@ class AutowiredArgumentsCodeGeneratorTests {
AutowiredArgumentsCodeGenerator generator = new AutowiredArgumentsCodeGenerator(
Outer.Nested.class, constructor);
assertThat(generator.generateCode(constructor.getParameterTypes(), 1))
.hasToString("args.get(1), args.get(2)");
.hasToString("args.get(0), args.get(1)");
}
@Test
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,7 +36,6 @@ import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;
import org.junit.jupiter.params.provider.ArgumentsSource;
import org.junit.jupiter.params.support.AnnotationConsumer;
import org.junit.jupiter.params.support.ParameterDeclarations;
import org.springframework.beans.factory.BeanCurrentlyInCreationException;
import org.springframework.beans.factory.ObjectProvider;
@@ -692,7 +691,7 @@ class BeanInstanceSupplierTests {
}
@Override
public Stream<? extends Arguments> provideArguments(ParameterDeclarations parameters, ExtensionContext context) {
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
return this.source.provideArguments(context);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -133,13 +133,11 @@ class InstanceSupplierCodeGeneratorTests {
@Test
void generateWhenHasConstructorWithInnerClassAndParameter() {
BeanDefinition beanDefinition = new RootBeanDefinition(EnvironmentAwareComponent.class);
StandardEnvironment environment = new StandardEnvironment();
this.beanFactory.registerSingleton("configuration", new InnerComponentConfiguration());
this.beanFactory.registerSingleton("environment", environment);
this.beanFactory.registerSingleton("environment", new StandardEnvironment());
compile(beanDefinition, (instanceSupplier, compiled) -> {
Object bean = getBean(beanDefinition, instanceSupplier);
assertThat(bean).isInstanceOf(EnvironmentAwareComponent.class);
assertThat(bean).hasFieldOrPropertyWithValue("environment", environment);
assertThat(compiled.getSourceFile()).contains(
"getBeanFactory().getBean(InnerComponentConfiguration.class).new EnvironmentAwareComponent(");
});
@@ -164,13 +162,11 @@ class InstanceSupplierCodeGeneratorTests {
@Test
void generateWhenHasNonPublicConstructorWithInnerClassAndParameter() {
BeanDefinition beanDefinition = new RootBeanDefinition(EnvironmentAwareComponentWithoutPublicConstructor.class);
StandardEnvironment environment = new StandardEnvironment();
this.beanFactory.registerSingleton("configuration", new InnerComponentConfiguration());
this.beanFactory.registerSingleton("environment", environment);
this.beanFactory.registerSingleton("environment", new StandardEnvironment());
compile(beanDefinition, (instanceSupplier, compiled) -> {
Object bean = getBean(beanDefinition, instanceSupplier);
assertThat(bean).isInstanceOf(EnvironmentAwareComponentWithoutPublicConstructor.class);
assertThat(bean).hasFieldOrPropertyWithValue("environment", environment);
assertThat(compiled.getSourceFile()).doesNotContain(
"getBeanFactory().getBean(InnerComponentConfiguration.class)");
});
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -84,7 +84,7 @@ class PropertyPlaceholderConfigurerTests {
.getBeanDefinition());
PropertyPlaceholderConfigurer pc = new PropertyPlaceholderConfigurer();
Resource resource = new ClassPathResource("PropertyPlaceholderConfigurerTests.properties", getClass());
Resource resource = new ClassPathResource("PropertyPlaceholderConfigurerTests.properties", this.getClass());
pc.setLocation(resource);
pc.postProcessBeanFactory(bf);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -46,7 +46,7 @@ class DuplicateBeanIdTests {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(bf);
assertThatException().as("duplicate ids in same nesting level").isThrownBy(() ->
reader.loadBeanDefinitions(new ClassPathResource("DuplicateBeanIdTests-sameLevel-context.xml", getClass())));
reader.loadBeanDefinitions(new ClassPathResource("DuplicateBeanIdTests-sameLevel-context.xml", this.getClass())));
}
@Test
@@ -54,7 +54,7 @@ class DuplicateBeanIdTests {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.setAllowBeanDefinitionOverriding(true);
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(bf);
reader.loadBeanDefinitions(new ClassPathResource("DuplicateBeanIdTests-multiLevel-context.xml", getClass()));
reader.loadBeanDefinitions(new ClassPathResource("DuplicateBeanIdTests-multiLevel-context.xml", this.getClass()));
TestBean testBean = bf.getBean(TestBean.class); // there should be only one
assertThat(testBean.getName()).isEqualTo("nested");
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,7 +36,7 @@ class NestedBeansElementAttributeRecursionTests {
void defaultLazyInit() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(
new ClassPathResource("NestedBeansElementAttributeRecursionTests-lazy-context.xml", getClass()));
new ClassPathResource("NestedBeansElementAttributeRecursionTests-lazy-context.xml", this.getClass()));
assertLazyInits(bf);
}
@@ -47,7 +47,7 @@ class NestedBeansElementAttributeRecursionTests {
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(bf);
xmlBeanDefinitionReader.setValidating(false);
xmlBeanDefinitionReader.loadBeanDefinitions(
new ClassPathResource("NestedBeansElementAttributeRecursionTests-lazy-context.xml", getClass()));
new ClassPathResource("NestedBeansElementAttributeRecursionTests-lazy-context.xml", this.getClass()));
assertLazyInits(bf);
}
@@ -70,7 +70,7 @@ class NestedBeansElementAttributeRecursionTests {
void defaultMerge() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(
new ClassPathResource("NestedBeansElementAttributeRecursionTests-merge-context.xml", getClass()));
new ClassPathResource("NestedBeansElementAttributeRecursionTests-merge-context.xml", this.getClass()));
assertMerge(bf);
}
@@ -81,7 +81,7 @@ class NestedBeansElementAttributeRecursionTests {
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(bf);
xmlBeanDefinitionReader.setValidating(false);
xmlBeanDefinitionReader.loadBeanDefinitions(
new ClassPathResource("NestedBeansElementAttributeRecursionTests-merge-context.xml", getClass()));
new ClassPathResource("NestedBeansElementAttributeRecursionTests-merge-context.xml", this.getClass()));
assertMerge(bf);
}
@@ -109,7 +109,7 @@ class NestedBeansElementAttributeRecursionTests {
void defaultAutowireCandidates() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(
new ClassPathResource("NestedBeansElementAttributeRecursionTests-autowire-candidates-context.xml", getClass()));
new ClassPathResource("NestedBeansElementAttributeRecursionTests-autowire-candidates-context.xml", this.getClass()));
assertAutowireCandidates(bf);
}
@@ -120,7 +120,7 @@ class NestedBeansElementAttributeRecursionTests {
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(bf);
xmlBeanDefinitionReader.setValidating(false);
xmlBeanDefinitionReader.loadBeanDefinitions(
new ClassPathResource("NestedBeansElementAttributeRecursionTests-autowire-candidates-context.xml", getClass()));
new ClassPathResource("NestedBeansElementAttributeRecursionTests-autowire-candidates-context.xml", this.getClass()));
assertAutowireCandidates(bf);
}
@@ -149,7 +149,7 @@ class NestedBeansElementAttributeRecursionTests {
void initMethod() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(
new ClassPathResource("NestedBeansElementAttributeRecursionTests-init-destroy-context.xml", getClass()));
new ClassPathResource("NestedBeansElementAttributeRecursionTests-init-destroy-context.xml", this.getClass()));
InitDestroyBean beanA = bf.getBean("beanA", InitDestroyBean.class);
InitDestroyBean beanB = bf.getBean("beanB", InitDestroyBean.class);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,14 +26,15 @@ import org.springframework.core.io.Resource;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for new nested beans element support in Spring XML
*
* @author Chris Beams
*/
class NestedBeansElementTests {
private final Resource XML = new ClassPathResource("NestedBeansElementTests-context.xml", getClass());
private final Resource XML =
new ClassPathResource("NestedBeansElementTests-context.xml", this.getClass());
@Test
void getBean_withoutActiveProfile() {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -93,6 +93,7 @@ class BeanUtilsKotlinTests {
@Test
fun `Instantiate value class`() {
val constructor = BeanUtils.findPrimaryConstructor(ValueClass::class.java)!!
assertThat(constructor).isNotNull()
val value = "Hello value class!"
val instance = BeanUtils.instantiateClass(constructor, value)
assertThat(instance).isEqualTo(ValueClass(value))
@@ -101,6 +102,7 @@ class BeanUtilsKotlinTests {
@Test
fun `Instantiate value class with multiple constructors`() {
val constructor = BeanUtils.findPrimaryConstructor(ValueClassWithMultipleConstructors::class.java)!!
assertThat(constructor).isNotNull()
val value = "Hello value class!"
val instance = BeanUtils.instantiateClass(constructor, value)
assertThat(instance).isEqualTo(ValueClassWithMultipleConstructors(value))
@@ -109,6 +111,7 @@ class BeanUtilsKotlinTests {
@Test
fun `Instantiate class with value class parameter`() {
val constructor = BeanUtils.findPrimaryConstructor(ConstructorWithValueClass::class.java)!!
assertThat(constructor).isNotNull()
val value = ValueClass("Hello value class!")
val instance = BeanUtils.instantiateClass(constructor, value)
assertThat(instance).isEqualTo(ConstructorWithValueClass(value))
@@ -117,6 +120,7 @@ class BeanUtilsKotlinTests {
@Test
fun `Instantiate class with nullable value class parameter`() {
val constructor = BeanUtils.findPrimaryConstructor(ConstructorWithNullableValueClass::class.java)!!
assertThat(constructor).isNotNull()
val value = ValueClass("Hello value class!")
var instance = BeanUtils.instantiateClass(constructor, value)
assertThat(instance).isEqualTo(ConstructorWithNullableValueClass(value))
@@ -127,6 +131,7 @@ class BeanUtilsKotlinTests {
@Test
fun `Instantiate primitive value class`() {
val constructor = BeanUtils.findPrimaryConstructor(PrimitiveValueClass::class.java)!!
assertThat(constructor).isNotNull()
val value = 0
val instance = BeanUtils.instantiateClass(constructor, value)
assertThat(instance).isEqualTo(PrimitiveValueClass(value))
@@ -135,6 +140,7 @@ class BeanUtilsKotlinTests {
@Test
fun `Instantiate class with primitive value class parameter`() {
val constructor = BeanUtils.findPrimaryConstructor(ConstructorWithPrimitiveValueClass::class.java)!!
assertThat(constructor).isNotNull()
val value = PrimitiveValueClass(0)
val instance = BeanUtils.instantiateClass(constructor, value)
assertThat(instance).isEqualTo(ConstructorWithPrimitiveValueClass(value))
@@ -143,6 +149,7 @@ class BeanUtilsKotlinTests {
@Test
fun `Instantiate class with nullable primitive value class parameter`() {
val constructor = BeanUtils.findPrimaryConstructor(ConstructorWithNullablePrimitiveValueClass::class.java)!!
assertThat(constructor).isNotNull()
val value = PrimitiveValueClass(0)
var instance = BeanUtils.instantiateClass(constructor, value)
assertThat(instance).isEqualTo(ConstructorWithNullablePrimitiveValueClass(value))
@@ -150,48 +157,6 @@ class BeanUtilsKotlinTests {
assertThat(instance).isEqualTo(ConstructorWithNullablePrimitiveValueClass(null))
}
@Test
fun `Get parameter names with Foo`() {
val ctor = BeanUtils.findPrimaryConstructor(Foo::class.java)!!
val names = BeanUtils.getParameterNames(ctor)
assertThat(names).containsExactly("param1", "param2")
}
@Test
fun `Get parameter names filters out DefaultConstructorMarker with ConstructorWithValueClass`() {
val ctor = BeanUtils.findPrimaryConstructor(ConstructorWithValueClass::class.java)!!
val names = BeanUtils.getParameterNames(ctor)
assertThat(names).containsExactly("value")
}
@Test
fun `getParameterNames filters out DefaultConstructorMarker with ConstructorWithNullableValueClass`() {
val ctor = BeanUtils.findPrimaryConstructor(ConstructorWithNullableValueClass::class.java)!!
val names = BeanUtils.getParameterNames(ctor)
assertThat(names).containsExactly("value")
}
@Test
fun `getParameterNames filters out DefaultConstructorMarker with ConstructorWithPrimitiveValueClass`() {
val ctor = BeanUtils.findPrimaryConstructor(ConstructorWithPrimitiveValueClass::class.java)!!
val names = BeanUtils.getParameterNames(ctor)
assertThat(names).containsExactly("value")
}
@Test
fun `getParameterNames filters out DefaultConstructorMarker with ConstructorWithNullablePrimitiveValueClass`() {
val ctor = BeanUtils.findPrimaryConstructor(ConstructorWithNullablePrimitiveValueClass::class.java)!!
val names = BeanUtils.getParameterNames(ctor)
assertThat(names).containsExactly("value")
}
@Test
fun `getParameterNames with ClassWithZeroParameterCtor`() {
val ctor = BeanUtils.findPrimaryConstructor(ClassWithZeroParameterCtor::class.java)!!
val names = BeanUtils.getParameterNames(ctor)
assertThat(names).isEmpty()
}
class Foo(val param1: String, val param2: Int)
@@ -251,6 +216,4 @@ class BeanUtilsKotlinTests {
data class ConstructorWithNullablePrimitiveValueClass(val value: PrimitiveValueClass?)
class ClassWithZeroParameterCtor()
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,10 +28,7 @@ public class InnerComponentConfiguration {
public class EnvironmentAwareComponent {
final Environment environment;
public EnvironmentAwareComponent(Environment environment) {
this.environment = environment;
}
}
@@ -43,10 +40,7 @@ public class InnerComponentConfiguration {
public class EnvironmentAwareComponentWithoutPublicConstructor {
final Environment environment;
EnvironmentAwareComponentWithoutPublicConstructor(Environment environment) {
this.environment = environment;
}
}
-7
View File
@@ -59,10 +59,3 @@ dependencies {
testRuntimeOnly("org.javamoney:moneta")
testRuntimeOnly("org.junit.vintage:junit-vintage-engine") // for @Inject TCK
}
test {
description = "Runs JUnit Jupiter tests and the @Inject TCK via JUnit Vintage."
useJUnitPlatform {
includeEngines "junit-jupiter", "junit-vintage"
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,7 +16,6 @@
package org.springframework.context.annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
@@ -28,7 +27,6 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
@@ -288,8 +286,8 @@ class ConfigurationClassBeanDefinitionReader {
}
if (logger.isTraceEnabled()) {
logger.trace("Registering bean definition for @Bean method %s.%s()"
.formatted(configClass.getMetadata().getClassName(), beanName));
logger.trace(String.format("Registering bean definition for @Bean method %s.%s()",
configClass.getMetadata().getClassName(), beanName));
}
this.registry.registerBeanDefinition(beanName, beanDefToRegister);
}
@@ -346,8 +344,9 @@ class ConfigurationClassBeanDefinitionReader {
"@Bean definition illegally overridden by existing bean definition: " + existingBeanDef);
}
if (logger.isDebugEnabled()) {
logger.debug("Skipping bean definition for %s: a definition for bean '%s' already exists. " +
"This top-level bean definition is considered as an override.".formatted(beanMethod, beanName));
logger.debug(String.format("Skipping bean definition for %s: a definition for bean '%s' " +
"already exists. This top-level bean definition is considered as an override.",
beanMethod, beanName));
}
return true;
}
@@ -373,11 +372,9 @@ class ConfigurationClassBeanDefinitionReader {
BeanDefinitionReader reader = readerInstanceCache.get(readerClass);
if (reader == null) {
try {
Constructor<? extends BeanDefinitionReader> constructor =
readerClass.getDeclaredConstructor(BeanDefinitionRegistry.class);
// Instantiate the specified BeanDefinitionReader
reader = BeanUtils.instantiateClass(constructor, this.registry);
// Delegate the current ResourceLoader and Environment to it if possible
reader = readerClass.getConstructor(BeanDefinitionRegistry.class).newInstance(this.registry);
// Delegate the current ResourceLoader to it if possible
if (reader instanceof AbstractBeanDefinitionReader abdr) {
abdr.setResourceLoader(this.resourceLoader);
abdr.setEnvironment(this.environment);
@@ -386,9 +383,11 @@ class ConfigurationClassBeanDefinitionReader {
}
catch (Throwable ex) {
throw new IllegalStateException(
"Could not instantiate BeanDefinitionReader class [" + readerClass.getName() + "]", ex);
"Could not instantiate BeanDefinitionReader class [" + readerClass.getName() + "]");
}
}
// TODO SPR-6310: qualify relative path locations as done in AbstractContextLoader.modifyLocations
reader.loadBeanDefinitions(resource);
});
}
@@ -16,7 +16,6 @@
package org.springframework.context.annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@@ -140,20 +139,14 @@ class ConfigurationClassEnhancer {
}
/**
* Checks whether the given config class relies on package visibility, either for
* the class and any of its constructors or for any of its {@code @Bean} methods.
* Checks whether the given config class relies on package visibility,
* either for the class itself or for any of its {@code @Bean} methods.
*/
private boolean reliesOnPackageVisibility(Class<?> configSuperClass) {
int mod = configSuperClass.getModifiers();
if (!Modifier.isPublic(mod) && !Modifier.isProtected(mod)) {
return true;
}
for (Constructor<?> ctor : configSuperClass.getDeclaredConstructors()) {
mod = ctor.getModifiers();
if (!Modifier.isPublic(mod) && !Modifier.isProtected(mod)) {
return true;
}
}
for (Method method : ReflectionUtils.getDeclaredMethods(configSuperClass)) {
if (BeanAnnotationHelper.isBeanAnnotated(method)) {
mod = method.getModifiers();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 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.
@@ -29,15 +29,14 @@ import org.springframework.core.annotation.AliasFor;
* Indicates one or more resources containing bean definitions to import.
*
* <p>Like {@link Import @Import}, this annotation provides functionality similar to
* the {@code <import/>} element in Spring XML configuration. It is typically used
* when designing {@link Configuration @Configuration} classes to be bootstrapped by
* an {@link AnnotationConfigApplicationContext}, but where some XML functionality
* such as namespaces is still necessary.
* the {@code <import/>} element in Spring XML. It is typically used when designing
* {@link Configuration @Configuration} classes to be bootstrapped by an
* {@link AnnotationConfigApplicationContext}, but where some XML functionality such
* as namespaces is still necessary.
*
* <p>By default, arguments to the {@link #locations() locations} or {@link #value() value}
* attribute will be processed using a
* <p>By default, arguments to the {@link #value} attribute will be processed using a
* {@link org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader GroovyBeanDefinitionReader}
* for resource locations ending in {@code ".groovy"}; otherwise, an
* if ending in {@code ".groovy"}; otherwise, an
* {@link org.springframework.beans.factory.xml.XmlBeanDefinitionReader XmlBeanDefinitionReader}
* will be used to parse Spring {@code <beans/>} XML files. Optionally, the {@link #reader}
* attribute may be declared, allowing the user to choose a custom {@link BeanDefinitionReader}
@@ -78,19 +77,12 @@ public @interface ImportResource {
/**
* {@link BeanDefinitionReader} implementation to use when processing
* resources specified via the {@link #locations() locations} or
* {@link #value() value} attribute.
* <p>The configured {@code BeanDefinitionReader} type must declare a
* constructor that accepts a single
* {@link org.springframework.beans.factory.support.BeanDefinitionRegistry
* BeanDefinitionRegistry} argument.
* resources specified via the {@link #value} attribute.
* <p>By default, the reader will be adapted to the resource path specified:
* {@code ".groovy"} files will be processed with a
* {@link org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader
* GroovyBeanDefinitionReader}; whereas, all other resources will be processed
* with an {@link org.springframework.beans.factory.xml.XmlBeanDefinitionReader
* XmlBeanDefinitionReader}.
* @see #locations
* {@link org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader GroovyBeanDefinitionReader};
* whereas, all other resources will be processed with an
* {@link org.springframework.beans.factory.xml.XmlBeanDefinitionReader XmlBeanDefinitionReader}.
* @see #value
*/
Class<? extends BeanDefinitionReader> reader() default BeanDefinitionReader.class;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -50,7 +50,6 @@ public class ApplicationContextAotGenerator {
*/
public ClassName processAheadOfTime(GenericApplicationContext applicationContext,
GenerationContext generationContext) {
return withCglibClassHandler(new CglibClassHandler(generationContext), () -> {
applicationContext.refreshForAotProcessing(generationContext.getRuntimeHints());
ApplicationContextInitializationCodeGenerator codeGenerator =
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -80,9 +80,8 @@ public abstract class ContextAotProcessor extends AbstractAotProcessor<ClassName
@Override
protected ClassName doProcess() {
deleteExistingOutput();
try (GenericApplicationContext applicationContext = prepareApplicationContext(getApplicationClass())) {
return performAotProcessing(applicationContext);
}
GenericApplicationContext applicationContext = prepareApplicationContext(getApplicationClass());
return performAotProcessing(applicationContext);
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -100,7 +100,6 @@ public class ReflectiveProcessorAotContributionBuilder {
return (!this.classes.isEmpty() ? new AotContribution(this.classes) : null);
}
private static class AotContribution implements BeanFactoryInitializationAotContribution {
private final Class<?>[] classes;
@@ -114,8 +113,8 @@ public class ReflectiveProcessorAotContributionBuilder {
RuntimeHints runtimeHints = generationContext.getRuntimeHints();
registrar.registerRuntimeHints(runtimeHints, this.classes);
}
}
}
private static class ReflectiveClassPathScanner extends ClassPathScanningCandidateComponentProvider {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,12 +24,12 @@ import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.PlaceholderConfigurerSupport;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.ConfigurablePropertyResolver;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertyResolver;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.PropertySources;
import org.springframework.core.env.PropertySourcesPropertyResolver;
@@ -49,7 +49,7 @@ import org.springframework.util.StringValueResolver;
* XSD documentation for complete details.
*
* <p>Any local properties (for example, those added via {@link #setProperties}, {@link #setLocations}
* et al.) are added as a single {@link PropertySource}. Search precedence of local properties is
* et al.) are added as a {@code PropertySource}. Search precedence of local properties is
* based on the value of the {@link #setLocalOverride localOverride} property, which is by
* default {@code false} meaning that local properties are to be searched last, after all
* environment property sources.
@@ -101,9 +101,8 @@ public class PropertySourcesPlaceholderConfigurer extends PlaceholderConfigurerS
}
/**
* {@inheritDoc}
* <p>{@code PropertySources} from the given {@link Environment} will be searched
* when replacing ${...} placeholders.
* {@code PropertySources} from the given {@link Environment}
* will be searched when replacing ${...} placeholders.
* @see #setPropertySources
* @see #postProcessBeanFactory
*/
@@ -133,11 +132,28 @@ public class PropertySourcesPlaceholderConfigurer extends PlaceholderConfigurerS
if (this.propertySources == null) {
this.propertySources = new MutablePropertySources();
if (this.environment != null) {
PropertySource<?> environmentPropertySource =
(this.environment instanceof ConfigurableEnvironment configurableEnvironment ?
new ConfigurableEnvironmentPropertySource(configurableEnvironment) :
new FallbackEnvironmentPropertySource(this.environment));
this.propertySources.addLast(environmentPropertySource);
PropertyResolver propertyResolver = this.environment;
// If the ignoreUnresolvablePlaceholders flag is set to true, we have to create a
// local PropertyResolver to enforce that setting, since the Environment is most
// likely not configured with ignoreUnresolvablePlaceholders set to true.
// See https://github.com/spring-projects/spring-framework/issues/27947
if (this.ignoreUnresolvablePlaceholders &&
(this.environment instanceof ConfigurableEnvironment configurableEnvironment)) {
PropertySourcesPropertyResolver resolver =
new PropertySourcesPropertyResolver(configurableEnvironment.getPropertySources());
resolver.setIgnoreUnresolvableNestedPlaceholders(true);
propertyResolver = resolver;
}
PropertyResolver propertyResolverToUse = propertyResolver;
this.propertySources.addLast(
new PropertySource<>(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, this.environment) {
@Override
@Nullable
public String getProperty(String key) {
return propertyResolverToUse.getProperty(key);
}
}
);
}
try {
PropertySource<?> localPropertySource =
@@ -160,7 +176,6 @@ public class PropertySourcesPlaceholderConfigurer extends PlaceholderConfigurerS
/**
* Create a {@link ConfigurablePropertyResolver} for the specified property sources.
* <p>The default implementation creates a {@link PropertySourcesPropertyResolver}.
* @param propertySources the property sources to use
* @since 6.0.12
*/
@@ -173,7 +188,7 @@ public class PropertySourcesPlaceholderConfigurer extends PlaceholderConfigurerS
* placeholders with values from the given properties.
*/
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
ConfigurablePropertyResolver propertyResolver) throws BeansException {
final ConfigurablePropertyResolver propertyResolver) throws BeansException {
propertyResolver.setPlaceholderPrefix(this.placeholderPrefix);
propertyResolver.setPlaceholderSuffix(this.placeholderSuffix);
@@ -219,94 +234,4 @@ public class PropertySourcesPlaceholderConfigurer extends PlaceholderConfigurerS
return this.appliedPropertySources;
}
/**
* Custom {@link PropertySource} that delegates to the
* {@link ConfigurableEnvironment#getPropertySources() PropertySources} in a
* {@link ConfigurableEnvironment}.
* @since 6.2.7
*/
private static class ConfigurableEnvironmentPropertySource extends PropertySource<ConfigurableEnvironment> {
ConfigurableEnvironmentPropertySource(ConfigurableEnvironment environment) {
super(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, environment);
}
@Override
public boolean containsProperty(String name) {
for (PropertySource<?> propertySource : super.source.getPropertySources()) {
if (propertySource.containsProperty(name)) {
return true;
}
}
return false;
}
@Override
@Nullable
// Declare String as covariant return type, since a String is actually required.
public String getProperty(String name) {
for (PropertySource<?> propertySource : super.source.getPropertySources()) {
Object candidate = propertySource.getProperty(name);
if (candidate != null) {
return convertToString(candidate);
}
}
return null;
}
/**
* Convert the supplied value to a {@link String} using the {@link ConversionService}
* from the {@link Environment}.
* <p>This is a modified version of
* {@link org.springframework.core.env.AbstractPropertyResolver#convertValueIfNecessary(Object, Class)}.
* @param value the value to convert
* @return the converted value, or the original value if no conversion is necessary
* @since 6.2.8
*/
@Nullable
private String convertToString(Object value) {
if (value instanceof String string) {
return string;
}
return super.source.getConversionService().convert(value, String.class);
}
@Override
public String toString() {
return "ConfigurableEnvironmentPropertySource {propertySources=" + super.source.getPropertySources() + "}";
}
}
/**
* Fallback {@link PropertySource} that delegates to a raw {@link Environment}.
* <p>Should never apply in a regular scenario, since the {@code Environment}
* in an {@code ApplicationContext} should always be a {@link ConfigurableEnvironment}.
* @since 6.2.7
*/
private static class FallbackEnvironmentPropertySource extends PropertySource<Environment> {
FallbackEnvironmentPropertySource(Environment environment) {
super(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, environment);
}
@Override
public boolean containsProperty(String name) {
return super.source.containsProperty(name);
}
@Override
@Nullable
// Declare String as covariant return type, since a String is actually required.
public String getProperty(String name) {
return super.source.getProperty(name);
}
@Override
public String toString() {
return "FallbackEnvironmentPropertySource {environment=" + super.source + "}";
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -146,12 +146,6 @@ import org.springframework.core.Ordered;
* compile-time weaving or load-time weaving applying the aspect to the affected classes.
* There is no proxy involved in such a scenario; local calls will be intercepted as well.
*
* <p><b>Note: {@code @EnableAsync} applies to its local application context only,
* allowing for selective activation at different levels.</b> Please redeclare
* {@code @EnableAsync} in each individual context, for example, the common root web
* application context and any separate {@code DispatcherServlet} application contexts,
* if you need to apply its behavior at multiple levels.
*
* @author Chris Beams
* @author Juergen Hoeller
* @author Stephane Nicoll
@@ -27,6 +27,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
@@ -549,13 +550,14 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
* <p>Mark fields as disallowed, for example to avoid unwanted
* modifications by malicious users when binding HTTP request parameters.
* <p>Supports {@code "xxx*"}, {@code "*xxx"}, {@code "*xxx*"}, and
* {@code "xxx*yyy"} matches (with an arbitrary number of pattern parts),
* as well as direct equality.
* <p>The default implementation of this method stores disallowed field
* patterns in {@linkplain PropertyAccessorUtils#canonicalPropertyName(String)
* canonical} form, and subsequently pattern matching in {@link #isAllowed}
* is case-insensitive. Subclasses that override this method must therefore
* take this transformation into account.
* {@code "xxx*yyy"} matches (with an arbitrary number of pattern parts), as
* well as direct equality.
* <p>The default implementation of this method stores disallowed field patterns
* in {@linkplain PropertyAccessorUtils#canonicalPropertyName(String) canonical}
* form and also transforms disallowed field patterns to
* {@linkplain String#toLowerCase() lowercase} to support case-insensitive
* pattern matching in {@link #isAllowed}. Subclasses which override this
* method must therefore take both of these transformations into account.
* <p>More sophisticated matching can be implemented by overriding the
* {@link #isAllowed} method.
* <p>Alternatively, specify a list of <i>allowed</i> field patterns.
@@ -573,7 +575,8 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
else {
String[] fieldPatterns = new String[disallowedFields.length];
for (int i = 0; i < fieldPatterns.length; i++) {
fieldPatterns[i] = PropertyAccessorUtils.canonicalPropertyName(disallowedFields[i]);
String field = PropertyAccessorUtils.canonicalPropertyName(disallowedFields[i]);
fieldPatterns[i] = field.toLowerCase(Locale.ROOT);
}
this.disallowedFields = fieldPatterns;
}
@@ -1299,9 +1302,9 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
* Determine if the given field is allowed for binding.
* <p>Invoked for each passed-in property value.
* <p>Checks for {@code "xxx*"}, {@code "*xxx"}, {@code "*xxx*"}, and
* {@code "xxx*yyy"} matches (with an arbitrary number of pattern parts),
* as well as direct equality, in the configured lists of allowed field
* patterns and disallowed field patterns.
* {@code "xxx*yyy"} matches (with an arbitrary number of pattern parts), as
* well as direct equality, in the configured lists of allowed field patterns
* and disallowed field patterns.
* <p>Matching against allowed field patterns is case-sensitive; whereas,
* matching against disallowed field patterns is case-insensitive.
* <p>A field matching a disallowed pattern will not be accepted even if it
@@ -1317,13 +1320,8 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
protected boolean isAllowed(String field) {
String[] allowed = getAllowedFields();
String[] disallowed = getDisallowedFields();
if (!ObjectUtils.isEmpty(allowed) && !PatternMatchUtils.simpleMatch(allowed, field)) {
return false;
}
if (!ObjectUtils.isEmpty(disallowed)) {
return !PatternMatchUtils.simpleMatchIgnoreCase(disallowed, field);
}
return true;
return ((ObjectUtils.isEmpty(allowed) || PatternMatchUtils.simpleMatch(allowed, field)) &&
(ObjectUtils.isEmpty(disallowed) || !PatternMatchUtils.simpleMatch(disallowed, field.toLowerCase(Locale.ROOT))));
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -250,7 +250,7 @@ public abstract class ValidationUtils {
Assert.notNull(errors, "Errors object must not be null");
Object value = errors.getFieldValue(field);
if (value == null || !StringUtils.hasText(value.toString())) {
if (value == null ||!StringUtils.hasText(value.toString())) {
errors.rejectValue(field, errorCode, errorArgs, defaultMessage);
}
}
@@ -0,0 +1,11 @@
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE rmi PUBLIC "-//BEA Systems, Inc.//RMI Runtime DTD 1.0//EN" "rmi.dtd">
<!--
- Special WebLogic deployment descriptor for Spring's RMI invoker.
- Only applied by WebLogic Server, ignored on other platforms.
-->
<rmi name="org.springframework.remoting.rmi.RmiInvocationWrapper">
<cluster clusterable="true"/>
<method name="getTargetInterfaceName" idempotent="true"/>
</rmi>
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -130,7 +130,7 @@ class SimpleKeyGeneratorTests {
private Object generateKey(Object[] arguments) {
Method method = ReflectionUtils.findMethod(getClass(), "generateKey", Object[].class);
Method method = ReflectionUtils.findMethod(this.getClass(), "generateKey", Object[].class);
return this.generator.generate(this, method, arguments);
}
@@ -24,7 +24,6 @@ import org.junit.jupiter.api.Timeout;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanCurrentlyInCreationException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.UnsatisfiedDependencyException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
@@ -244,24 +243,14 @@ class BackgroundBootstrapTests {
}
@Bean
public FactoryBean<TestBean> testBean4() {
public TestBean testBean4() {
try {
Thread.sleep(2000);
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
TestBean testBean = new TestBean();
return new FactoryBean<>() {
@Override
public TestBean getObject() {
return testBean;
}
@Override
public Class<?> getObjectType() {
return testBean.getClass();
}
};
return new TestBean();
}
}
@@ -104,31 +104,6 @@ class ConfigurationClassEnhancerTests {
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
}
@Test
void withNonPublicConstructor() {
ConfigurationClassEnhancer configurationClassEnhancer = new ConfigurationClassEnhancer();
ClassLoader classLoader = new URLClassLoader(new URL[0], getClass().getClassLoader());
Class<?> enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicConstructor.class, classLoader);
assertThat(MyConfigWithNonPublicConstructor.class).isAssignableFrom(enhancedClass);
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
classLoader = new OverridingClassLoader(getClass().getClassLoader());
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicConstructor.class, classLoader);
assertThat(MyConfigWithNonPublicConstructor.class).isAssignableFrom(enhancedClass);
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
classLoader = new CustomSmartClassLoader(getClass().getClassLoader());
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicConstructor.class, classLoader);
assertThat(MyConfigWithNonPublicConstructor.class).isAssignableFrom(enhancedClass);
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
classLoader = new BasicSmartClassLoader(getClass().getClassLoader());
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicConstructor.class, classLoader);
assertThat(MyConfigWithNonPublicConstructor.class).isAssignableFrom(enhancedClass);
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
}
@Test
void withNonPublicMethod() {
ConfigurationClassEnhancer configurationClassEnhancer = new ConfigurationClassEnhancer();
@@ -185,19 +160,6 @@ class ConfigurationClassEnhancerTests {
}
@Configuration
public static class MyConfigWithNonPublicConstructor {
MyConfigWithNonPublicConstructor() {
}
@Bean
public String myBean() {
return "bean";
}
}
@Configuration
public static class MyConfigWithNonPublicMethod {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,8 @@
package org.springframework.context.annotation.configuration;
import java.util.Collections;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.junit.jupiter.api.Test;
@@ -23,18 +25,18 @@ import org.junit.jupiter.api.Test;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.testfixture.beans.TestBean;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import org.springframework.core.testfixture.env.MockPropertySource;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for {@link ImportResource @ImportResource} support.
* Integration tests for {@link ImportResource} support.
*
* @author Chris Beams
* @author Juergen Hoeller
@@ -43,88 +45,81 @@ import static org.assertj.core.api.Assertions.assertThat;
class ImportResourceTests {
@Test
void importResource() {
try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ImportXmlConfig.class)) {
assertThat(ctx.containsBean("javaDeclaredBean")).as("did not contain java-declared bean").isTrue();
assertThat(ctx.containsBean("xmlDeclaredBean")).as("did not contain xml-declared bean").isTrue();
TestBean tb = ctx.getBean("javaDeclaredBean", TestBean.class);
assertThat(tb.getName()).isEqualTo("myName");
}
void importXml() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ImportXmlConfig.class);
assertThat(ctx.containsBean("javaDeclaredBean")).as("did not contain java-declared bean").isTrue();
assertThat(ctx.containsBean("xmlDeclaredBean")).as("did not contain xml-declared bean").isTrue();
TestBean tb = ctx.getBean("javaDeclaredBean", TestBean.class);
assertThat(tb.getName()).isEqualTo("myName");
ctx.close();
}
@Test
void importResourceIsInheritedFromSuperclassDeclarations() {
try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(FirstLevelSubConfig.class)) {
assertThat(ctx.containsBean("xmlDeclaredBean")).isTrue();
}
void importXmlIsInheritedFromSuperclassDeclarations() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(FirstLevelSubConfig.class);
assertThat(ctx.containsBean("xmlDeclaredBean")).isTrue();
ctx.close();
}
@Test
void importResourceIsMergedFromSuperclassDeclarations() {
try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SecondLevelSubConfig.class)) {
assertThat(ctx.containsBean("secondLevelXmlDeclaredBean")).as("failed to pick up second-level-declared XML bean").isTrue();
assertThat(ctx.containsBean("xmlDeclaredBean")).as("failed to pick up parent-declared XML bean").isTrue();
}
void importXmlIsMergedFromSuperclassDeclarations() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SecondLevelSubConfig.class);
assertThat(ctx.containsBean("secondLevelXmlDeclaredBean")).as("failed to pick up second-level-declared XML bean").isTrue();
assertThat(ctx.containsBean("xmlDeclaredBean")).as("failed to pick up parent-declared XML bean").isTrue();
ctx.close();
}
@Test
void importResourceWithNamespaceConfig() {
try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ImportXmlWithAopNamespaceConfig.class)) {
Object bean = ctx.getBean("proxiedXmlBean");
assertThat(AopUtils.isAopProxy(bean)).isTrue();
}
void importXmlWithNamespaceConfig() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ImportXmlWithAopNamespaceConfig.class);
Object bean = ctx.getBean("proxiedXmlBean");
assertThat(AopUtils.isAopProxy(bean)).isTrue();
ctx.close();
}
@Test
void importResourceWithOtherConfigurationClass() {
try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ImportXmlWithConfigurationClass.class)) {
assertThat(ctx.containsBean("javaDeclaredBean")).as("did not contain java-declared bean").isTrue();
assertThat(ctx.containsBean("xmlDeclaredBean")).as("did not contain xml-declared bean").isTrue();
TestBean tb = ctx.getBean("javaDeclaredBean", TestBean.class);
assertThat(tb.getName()).isEqualTo("myName");
}
void importXmlWithOtherConfigurationClass() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ImportXmlWithConfigurationClass.class);
assertThat(ctx.containsBean("javaDeclaredBean")).as("did not contain java-declared bean").isTrue();
assertThat(ctx.containsBean("xmlDeclaredBean")).as("did not contain xml-declared bean").isTrue();
TestBean tb = ctx.getBean("javaDeclaredBean", TestBean.class);
assertThat(tb.getName()).isEqualTo("myName");
ctx.close();
}
@Test
void importWithPlaceholder() {
try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext()) {
ctx.getEnvironment().getPropertySources().addFirst(new MockPropertySource("test").withProperty("test", "springframework"));
ctx.register(ImportXmlConfig.class);
ctx.refresh();
assertThat(ctx.containsBean("xmlDeclaredBean")).as("did not contain xml-declared bean").isTrue();
}
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
PropertySource<?> propertySource = new MapPropertySource("test",
Collections.<String, Object> singletonMap("test", "springframework"));
ctx.getEnvironment().getPropertySources().addFirst(propertySource);
ctx.register(ImportXmlConfig.class);
ctx.refresh();
assertThat(ctx.containsBean("xmlDeclaredBean")).as("did not contain xml-declared bean").isTrue();
ctx.close();
}
@Test
void importResourceWithAutowiredConfig() {
try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ImportXmlAutowiredConfig.class)) {
String name = ctx.getBean("xmlBeanName", String.class);
assertThat(name).isEqualTo("xml.declared");
}
void importXmlWithAutowiredConfig() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ImportXmlAutowiredConfig.class);
String name = ctx.getBean("xmlBeanName", String.class);
assertThat(name).isEqualTo("xml.declared");
ctx.close();
}
@Test
void importNonXmlResource() {
try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ImportNonXmlResourceConfig.class)) {
assertThat(ctx.containsBean("propertiesDeclaredBean")).isTrue();
}
}
@Test
void importResourceWithPrivateReader() {
try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ImportWithPrivateReaderConfig.class)) {
assertThat(ctx.containsBean("propertiesDeclaredBean")).isTrue();
}
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ImportNonXmlResourceConfig.class);
assertThat(ctx.containsBean("propertiesDeclaredBean")).isTrue();
ctx.close();
}
@Configuration
@ImportResource("classpath:org/springframework/context/annotation/configuration/ImportXmlConfig-context.xml")
static class ImportXmlConfig {
@Value("${name}")
private String name;
@Bean public TestBean javaDeclaredBean() {
return new TestBean(this.name);
}
@@ -151,7 +146,6 @@ class ImportResourceTests {
@Aspect
static class AnAspect {
@Before("execution(* org.springframework.beans.testfixture.beans.TestBean.*(..))")
public void advice() { }
}
@@ -164,37 +158,18 @@ class ImportResourceTests {
@Configuration
@ImportResource("classpath:org/springframework/context/annotation/configuration/ImportXmlConfig-context.xml")
static class ImportXmlAutowiredConfig {
@Autowired TestBean xmlDeclaredBean;
@Autowired
TestBean xmlDeclaredBean;
@Bean
public String xmlBeanName() {
@Bean public String xmlBeanName() {
return xmlDeclaredBean.getName();
}
}
@SuppressWarnings("deprecation")
@Configuration
@ImportResource(locations = "org/springframework/context/annotation/configuration/ImportNonXmlResourceConfig.properties",
@ImportResource(locations = "classpath:org/springframework/context/annotation/configuration/ImportNonXmlResourceConfig-context.properties",
reader = org.springframework.beans.factory.support.PropertiesBeanDefinitionReader.class)
static class ImportNonXmlResourceConfig {
}
@SuppressWarnings("deprecation")
@Configuration
@ImportResource(locations = "org/springframework/context/annotation/configuration/ImportNonXmlResourceConfig.properties",
reader = PrivatePropertiesBeanDefinitionReader.class)
static class ImportWithPrivateReaderConfig {
}
@SuppressWarnings("deprecation")
private static class PrivatePropertiesBeanDefinitionReader
extends org.springframework.beans.factory.support.PropertiesBeanDefinitionReader {
PrivatePropertiesBeanDefinitionReader(BeanDefinitionRegistry registry) {
super(registry);
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,8 +38,7 @@ import org.springframework.context.support.GenericApplicationContext;
* @author Juergen Hoeller
* @since 3.0
*/
// WARNING: This class MUST be public, since it is based on JUnit 3.
public class SpringAtInjectTckTests {
class SpringAtInjectTckTests {
@SuppressWarnings("unchecked")
public static Test suite() {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -45,9 +45,8 @@ class ContextAotProcessorTests {
void processGeneratesAssets(@TempDir Path directory) {
GenericApplicationContext context = new AnnotationConfigApplicationContext();
context.registerBean(SampleApplication.class);
DemoContextAotProcessor processor = new DemoContextAotProcessor(SampleApplication.class, directory);
ContextAotProcessor processor = new DemoContextAotProcessor(SampleApplication.class, directory);
ClassName className = processor.process();
assertThat(processor.context.isClosed()).isTrue();
assertThat(className).isEqualTo(ClassName.get(SampleApplication.class.getPackageName(),
"ContextAotProcessorTests_SampleApplication__ApplicationContextInitializer"));
assertThat(directory).satisfies(hasGeneratedAssetsForSampleApplication());
@@ -62,10 +61,9 @@ class ContextAotProcessorTests {
Path existingSourceOutput = createExisting(sourceOutput);
Path existingResourceOutput = createExisting(resourceOutput);
Path existingClassOutput = createExisting(classOutput);
DemoContextAotProcessor processor = new DemoContextAotProcessor(SampleApplication.class,
ContextAotProcessor processor = new DemoContextAotProcessor(SampleApplication.class,
sourceOutput, resourceOutput, classOutput);
processor.process();
assertThat(processor.context.isClosed()).isTrue();
assertThat(existingSourceOutput).doesNotExist();
assertThat(existingResourceOutput).doesNotExist();
assertThat(existingClassOutput).doesNotExist();
@@ -75,14 +73,13 @@ class ContextAotProcessorTests {
void processWithEmptyNativeImageArgumentsDoesNotCreateNativeImageProperties(@TempDir Path directory) {
GenericApplicationContext context = new AnnotationConfigApplicationContext();
context.registerBean(SampleApplication.class);
DemoContextAotProcessor processor = new DemoContextAotProcessor(SampleApplication.class, directory) {
ContextAotProcessor processor = new DemoContextAotProcessor(SampleApplication.class, directory) {
@Override
protected List<String> getDefaultNativeImageArguments(String application) {
return Collections.emptyList();
}
};
processor.process();
assertThat(processor.context.isClosed()).isTrue();
assertThat(directory.resolve("resource/META-INF/native-image/com.example/example/native-image.properties"))
.doesNotExist();
context.close();
@@ -121,8 +118,6 @@ class ContextAotProcessorTests {
private static class DemoContextAotProcessor extends ContextAotProcessor {
AnnotationConfigApplicationContext context;
DemoContextAotProcessor(Class<?> application, Path rootPath) {
this(application, rootPath.resolve("source"), rootPath.resolve("resource"), rootPath.resolve("class"));
}
@@ -146,11 +141,10 @@ class ContextAotProcessorTests {
protected GenericApplicationContext prepareApplicationContext(Class<?> application) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(application);
this.context = context;
return context;
}
}
}
@Configuration(proxyBeanMethods = false)
static class SampleApplication {
@@ -159,6 +153,7 @@ class ContextAotProcessorTests {
public String testBean() {
return "Hello";
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,36 +16,20 @@
package org.springframework.context.support;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Properties;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.testfixture.beans.TestBean;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.core.SpringProperties;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.env.AbstractPropertyResolver;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.StandardEnvironment;
@@ -54,15 +38,12 @@ import org.springframework.core.io.Resource;
import org.springframework.core.testfixture.env.MockPropertySource;
import org.springframework.mock.env.MockEnvironment;
import org.springframework.util.PlaceholderResolutionException;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.genericBeanDefinition;
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.rootBeanDefinition;
import static org.springframework.core.env.AbstractPropertyResolver.DEFAULT_PLACEHOLDER_ESCAPE_CHARACTER_PROPERTY_NAME;
/**
* Tests for {@link PropertySourcesPlaceholderConfigurer}.
@@ -92,75 +73,6 @@ class PropertySourcesPlaceholderConfigurerTests {
assertThat(ppc.getAppliedPropertySources()).isNotNull();
}
/**
* Ensure that a {@link Converter} registered in the {@link ConversionService}
* used by the {@code Environment} is applied during placeholder resolution
* against a {@link PropertySource} registered in the {@code Environment}.
*/
@Test // gh-34936
void replacementFromEnvironmentPropertiesWithConversion() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.registerBeanDefinition("testBean",
genericBeanDefinition(TestBean.class)
.addPropertyValue("name", "${my.name}")
.getBeanDefinition());
record Point(int x, int y) {
}
Converter<Point, String> pointToStringConverter =
point -> "(%d,%d)".formatted(point.x, point.y);
DefaultConversionService conversionService = new DefaultConversionService();
conversionService.addConverter(Point.class, String.class, pointToStringConverter);
MockEnvironment env = new MockEnvironment();
env.setConversionService(conversionService);
env.setProperty("my.name", new Point(4,5));
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();
ppc.setEnvironment(env);
ppc.postProcessBeanFactory(bf);
assertThat(bf.getBean(TestBean.class).getName()).isEqualTo("(4,5)");
}
/**
* Ensure that a {@link PropertySource} added to the {@code Environment} after context
* refresh (i.e., after {@link PropertySourcesPlaceholderConfigurer#postProcessBeanFactory()}
* has been invoked) can still contribute properties in late-binding scenarios.
*/
@Test // gh-34861
void replacementFromEnvironmentPropertiesWithLateBinding() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
MutablePropertySources propertySources = context.getEnvironment().getPropertySources();
propertySources.addFirst(new MockPropertySource("early properties").withProperty("foo", "bar"));
context.register(PropertySourcesPlaceholderConfigurer.class);
context.register(PrototypeBean.class);
context.refresh();
// Verify that placeholder resolution works for early binding.
PrototypeBean prototypeBean = context.getBean(PrototypeBean.class);
assertThat(prototypeBean.getName()).isEqualTo("bar");
assertThat(prototypeBean.isJedi()).isFalse();
// Add new PropertySource after context refresh.
propertySources.addFirst(new MockPropertySource("late properties").withProperty("jedi", "true"));
// Verify that placeholder resolution works for late binding: isJedi() switches to true.
prototypeBean = context.getBean(PrototypeBean.class);
assertThat(prototypeBean.getName()).isEqualTo("bar");
assertThat(prototypeBean.isJedi()).isTrue();
// Add yet another PropertySource after context refresh.
propertySources.addFirst(new MockPropertySource("even later properties").withProperty("foo", "enigma"));
// Verify that placeholder resolution works for even later binding: getName() switches to enigma.
prototypeBean = context.getBean(PrototypeBean.class);
assertThat(prototypeBean.getName()).isEqualTo("enigma");
assertThat(prototypeBean.isJedi()).isTrue();
}
@Test
void localPropertiesViaResource() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
@@ -170,35 +82,20 @@ class PropertySourcesPlaceholderConfigurerTests {
.getBeanDefinition());
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();
Resource resource = new ClassPathResource("PropertySourcesPlaceholderConfigurerTests.properties", getClass());
Resource resource = new ClassPathResource("PropertySourcesPlaceholderConfigurerTests.properties", this.getClass());
ppc.setLocation(resource);
ppc.postProcessBeanFactory(bf);
assertThat(bf.getBean(TestBean.class).getName()).isEqualTo("foo");
}
@ParameterizedTest
@ValueSource(booleans = {true, false})
void localPropertiesOverride(boolean override) {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.registerBeanDefinition("testBean",
genericBeanDefinition(TestBean.class)
.addPropertyValue("name", "${foo}")
.getBeanDefinition());
@Test
void localPropertiesOverrideFalse() {
localPropertiesOverride(false);
}
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();
ppc.setLocalOverride(override);
ppc.setProperties(new Properties() {{
setProperty("foo", "local");
}});
ppc.setEnvironment(new MockEnvironment().withProperty("foo", "enclosing"));
ppc.postProcessBeanFactory(bf);
if (override) {
assertThat(bf.getBean(TestBean.class).getName()).isEqualTo("local");
}
else {
assertThat(bf.getBean(TestBean.class).getName()).isEqualTo("enclosing");
}
@Test
void localPropertiesOverrideTrue() {
localPropertiesOverride(true);
}
@Test
@@ -384,58 +281,28 @@ class PropertySourcesPlaceholderConfigurerTests {
assertThat(bf.getBean(TestBean.class).getName()).isEqualTo("bar");
}
@Test // gh-34861
void withEnumerableAndNonEnumerablePropertySourcesInTheEnvironmentAndLocalProperties() {
@SuppressWarnings("serial")
private void localPropertiesOverride(boolean override) {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.registerBeanDefinition("testBean",
genericBeanDefinition(TestBean.class)
.addPropertyValue("name", "${foo:bogus}")
.addPropertyValue("jedi", "${local:false}")
.addPropertyValue("name", "${foo}")
.getBeanDefinition());
// 1) MockPropertySource is an EnumerablePropertySource.
MockPropertySource mockPropertySource = new MockPropertySource("mockPropertySource")
.withProperty("foo", "${bar}");
// 2) PropertySource is not an EnumerablePropertySource.
PropertySource<?> rawPropertySource = new PropertySource<>("rawPropertySource", new Object()) {
@Override
public Object getProperty(String key) {
return ("bar".equals(key) ? "quux" : null);
}
};
MockEnvironment env = new MockEnvironment();
env.getPropertySources().addFirst(mockPropertySource);
env.getPropertySources().addLast(rawPropertySource);
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();
ppc.setEnvironment(env);
// 3) Local properties are stored in a PropertiesPropertySource which is an EnumerablePropertySource.
ppc.setLocalOverride(override);
ppc.setProperties(new Properties() {{
setProperty("local", "true");
setProperty("foo", "local");
}});
ppc.setEnvironment(new MockEnvironment().withProperty("foo", "enclosing"));
ppc.postProcessBeanFactory(bf);
// Verify all properties can be resolved via the Environment.
assertThat(env.getProperty("foo")).isEqualTo("quux");
assertThat(env.getProperty("bar")).isEqualTo("quux");
// Verify that placeholder resolution works.
TestBean testBean = bf.getBean(TestBean.class);
assertThat(testBean.getName()).isEqualTo("quux");
assertThat(testBean.isJedi()).isTrue();
// Verify that the presence of a non-EnumerablePropertySource does not prevent
// accessing EnumerablePropertySources via getAppliedPropertySources().
List<String> propertyNames = new ArrayList<>();
for (PropertySource<?> propertySource : ppc.getAppliedPropertySources()) {
if (propertySource instanceof EnumerablePropertySource<?> enumerablePropertySource) {
Collections.addAll(propertyNames, enumerablePropertySource.getPropertyNames());
}
if (override) {
assertThat(bf.getBean(TestBean.class).getName()).isEqualTo("local");
}
else {
assertThat(bf.getBean(TestBean.class).getName()).isEqualTo("enclosing");
}
// Should not contain "foo" or "bar" from the Environment.
assertThat(propertyNames).containsOnly("local");
}
@Test
@@ -565,252 +432,6 @@ class PropertySourcesPlaceholderConfigurerTests {
}
/**
* Tests that use the escape character (or disable it) with nested placeholder
* resolution.
*/
@Nested
class EscapedNestedPlaceholdersTests {
@Test // gh-34861
void singleEscapeWithDefaultEscapeCharacter() {
MockEnvironment env = new MockEnvironment()
.withProperty("user.home", "admin")
.withProperty("my.property", "\\DOMAIN\\${user.home}");
DefaultListableBeanFactory bf = createBeanFactory();
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();
ppc.setEnvironment(env);
ppc.postProcessBeanFactory(bf);
// \DOMAIN\${user.home} resolves to \DOMAIN${user.home} instead of \DOMAIN\admin
assertThat(bf.getBean(TestBean.class).getName()).isEqualTo("\\DOMAIN${user.home}");
}
@Test // gh-34861
void singleEscapeWithCustomEscapeCharacter() {
MockEnvironment env = new MockEnvironment()
.withProperty("user.home", "admin\\~${nested}")
.withProperty("my.property", "DOMAIN\\${user.home}\\~${enigma}");
DefaultListableBeanFactory bf = createBeanFactory();
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();
ppc.setEnvironment(env);
// Set custom escape character.
ppc.setEscapeCharacter('~');
ppc.postProcessBeanFactory(bf);
assertThat(bf.getBean(TestBean.class).getName()).isEqualTo("DOMAIN\\admin\\${nested}\\${enigma}");
}
@Test // gh-34861
void singleEscapeWithEscapeCharacterDisabled() {
MockEnvironment env = new MockEnvironment()
.withProperty("user.home", "admin\\")
.withProperty("my.property", "\\DOMAIN\\${user.home}");
DefaultListableBeanFactory bf = createBeanFactory();
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();
ppc.setEnvironment(env);
// Disable escape character.
ppc.setEscapeCharacter(null);
ppc.postProcessBeanFactory(bf);
// \DOMAIN\${user.home} resolves to \DOMAIN\admin
assertThat(bf.getBean(TestBean.class).getName()).isEqualTo("\\DOMAIN\\admin\\");
}
@Test // gh-34861
void tripleEscapeWithDefaultEscapeCharacter() {
MockEnvironment env = new MockEnvironment()
.withProperty("user.home", "admin\\\\\\")
.withProperty("my.property", "DOMAIN\\\\\\${user.home}#${user.home}");
DefaultListableBeanFactory bf = createBeanFactory();
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();
ppc.setEnvironment(env);
ppc.postProcessBeanFactory(bf);
assertThat(bf.getBean(TestBean.class).getName()).isEqualTo("DOMAIN\\\\${user.home}#admin\\\\\\");
}
@Test // gh-34861
void tripleEscapeWithCustomEscapeCharacter() {
MockEnvironment env = new MockEnvironment()
.withProperty("user.home", "admin\\~${enigma}")
.withProperty("my.property", "DOMAIN~~~${user.home}#${user.home}");
DefaultListableBeanFactory bf = createBeanFactory();
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();
ppc.setEnvironment(env);
// Set custom escape character.
ppc.setEscapeCharacter('~');
ppc.postProcessBeanFactory(bf);
assertThat(bf.getBean(TestBean.class).getName()).isEqualTo("DOMAIN~~${user.home}#admin\\${enigma}");
}
@Test // gh-34861
void singleEscapeWithDefaultEscapeCharacterAndIgnoreUnresolvablePlaceholders() {
MockEnvironment env = new MockEnvironment()
.withProperty("user.home", "${enigma}")
.withProperty("my.property", "\\${DOMAIN}${user.home}");
DefaultListableBeanFactory bf = createBeanFactory();
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();
ppc.setEnvironment(env);
ppc.setIgnoreUnresolvablePlaceholders(true);
ppc.postProcessBeanFactory(bf);
assertThat(bf.getBean(TestBean.class).getName()).isEqualTo("${DOMAIN}${enigma}");
}
@Test // gh-34861
void singleEscapeWithCustomEscapeCharacterAndIgnoreUnresolvablePlaceholders() {
MockEnvironment env = new MockEnvironment()
.withProperty("user.home", "${enigma}")
.withProperty("my.property", "~${DOMAIN}\\${user.home}");
DefaultListableBeanFactory bf = createBeanFactory();
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();
ppc.setEnvironment(env);
// Set custom escape character.
ppc.setEscapeCharacter('~');
ppc.setIgnoreUnresolvablePlaceholders(true);
ppc.postProcessBeanFactory(bf);
assertThat(bf.getBean(TestBean.class).getName()).isEqualTo("${DOMAIN}\\${enigma}");
}
@Test // gh-34861
void tripleEscapeWithDefaultEscapeCharacterAndIgnoreUnresolvablePlaceholders() {
MockEnvironment env = new MockEnvironment()
.withProperty("user.home", "${enigma}")
.withProperty("my.property", "X:\\\\\\${DOMAIN}${user.home}");
DefaultListableBeanFactory bf = createBeanFactory();
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();
ppc.setEnvironment(env);
ppc.setIgnoreUnresolvablePlaceholders(true);
ppc.postProcessBeanFactory(bf);
assertThat(bf.getBean(TestBean.class).getName()).isEqualTo("X:\\\\${DOMAIN}${enigma}");
}
private static DefaultListableBeanFactory createBeanFactory() {
BeanDefinition beanDefinition = genericBeanDefinition(TestBean.class)
.addPropertyValue("name", "${my.property}")
.getBeanDefinition();
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.registerBeanDefinition("testBean",beanDefinition);
return bf;
}
}
/**
* Tests that globally set the default escape character (or disable it) and
* rely on nested placeholder resolution.
*/
@Nested
class GlobalDefaultEscapeCharacterTests {
private static final Field defaultEscapeCharacterField =
ReflectionUtils.findField(AbstractPropertyResolver.class, "defaultEscapeCharacter");
static {
ReflectionUtils.makeAccessible(defaultEscapeCharacterField);
}
@BeforeEach
void resetStateBeforeEachTest() {
resetState();
}
@AfterAll
static void resetState() {
ReflectionUtils.setField(defaultEscapeCharacterField, null, Character.MIN_VALUE);
setSpringProperty(null);
}
@Test // gh-34865
void defaultEscapeCharacterSetToXyz() {
setSpringProperty("XYZ");
assertThatIllegalArgumentException()
.isThrownBy(PropertySourcesPlaceholderConfigurer::new)
.withMessage("Value [XYZ] for property [%s] must be a single character or an empty string",
DEFAULT_PLACEHOLDER_ESCAPE_CHARACTER_PROPERTY_NAME);
}
@Test // gh-34865
void defaultEscapeCharacterDisabled() {
setSpringProperty("");
MockEnvironment env = new MockEnvironment()
.withProperty("user.home", "admin")
.withProperty("my.property", "\\DOMAIN\\${user.home}");
DefaultListableBeanFactory bf = createBeanFactory();
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();
ppc.setEnvironment(env);
ppc.postProcessBeanFactory(bf);
assertThat(bf.getBean(TestBean.class).getName()).isEqualTo("\\DOMAIN\\admin");
}
@Test // gh-34865
void defaultEscapeCharacterSetToBackslash() {
setSpringProperty("\\");
MockEnvironment env = new MockEnvironment()
.withProperty("user.home", "admin")
.withProperty("my.property", "\\DOMAIN\\${user.home}");
DefaultListableBeanFactory bf = createBeanFactory();
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();
ppc.setEnvironment(env);
ppc.postProcessBeanFactory(bf);
// \DOMAIN\${user.home} resolves to \DOMAIN${user.home} instead of \DOMAIN\admin
assertThat(bf.getBean(TestBean.class).getName()).isEqualTo("\\DOMAIN${user.home}");
}
@Test // gh-34865
void defaultEscapeCharacterSetToTilde() {
setSpringProperty("~");
MockEnvironment env = new MockEnvironment()
.withProperty("user.home", "admin\\~${nested}")
.withProperty("my.property", "DOMAIN\\${user.home}\\~${enigma}");
DefaultListableBeanFactory bf = createBeanFactory();
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();
ppc.setEnvironment(env);
ppc.postProcessBeanFactory(bf);
assertThat(bf.getBean(TestBean.class).getName()).isEqualTo("DOMAIN\\admin\\${nested}\\${enigma}");
}
private static void setSpringProperty(String value) {
SpringProperties.setProperty(DEFAULT_PLACEHOLDER_ESCAPE_CHARACTER_PROPERTY_NAME, value);
}
private static DefaultListableBeanFactory createBeanFactory() {
BeanDefinition beanDefinition = genericBeanDefinition(TestBean.class)
.addPropertyValue("name", "${my.property}")
.getBeanDefinition();
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.registerBeanDefinition("testBean",beanDefinition);
return bf;
}
}
private static class OptionalTestBean {
private Optional<String> name;
@@ -851,23 +472,4 @@ class PropertySourcesPlaceholderConfigurerTests {
}
}
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
static class PrototypeBean {
@Value("${foo:bogus}")
private String name;
@Value("${jedi:false}")
private boolean jedi;
public String getName() {
return this.name;
}
public boolean isJedi() {
return this.jedi;
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,7 +32,6 @@ import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;
import org.junit.jupiter.params.provider.ArgumentsSource;
import org.junit.jupiter.params.support.ParameterDeclarations;
import static java.time.Instant.MAX;
import static java.time.Instant.MIN;
@@ -92,7 +91,7 @@ class InstantFormatterTests {
private static final Random random = new Random();
@Override
public final Stream<Arguments> provideArguments(ParameterDeclarations parameters, ExtensionContext context) {
public final Stream<Arguments> provideArguments(ExtensionContext context) {
return provideArguments().map(Arguments::of).limit(DATA_SET_SIZE);
}
@@ -138,7 +137,7 @@ class InstantFormatterTests {
private static final Random random = new Random();
@Override
public Stream<Arguments> provideArguments(ParameterDeclarations parameters, ExtensionContext context) {
public Stream<Arguments> provideArguments(ExtensionContext context) {
return random.longs(DATA_SET_SIZE, Long.MIN_VALUE, Long.MAX_VALUE)
.mapToObj(Instant::ofEpochMilli)
.map(instant -> instant.truncatedTo(ChronoUnit.MILLIS))
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,7 +27,7 @@ import org.springframework.core.testfixture.env.MockPropertySource;
* @author Chris Beams
* @author Sam Brannen
* @since 3.2
* @see MockPropertySource
* @see org.springframework.core.testfixture.env.MockPropertySource
*/
public class MockEnvironment extends AbstractEnvironment {
@@ -44,23 +44,19 @@ public class MockEnvironment extends AbstractEnvironment {
/**
* Set a property on the underlying {@link MockPropertySource} for this environment.
* @since 6.2.8
* @see MockPropertySource#setProperty(String, Object)
*/
public void setProperty(String name, Object value) {
this.propertySource.setProperty(name, value);
public void setProperty(String key, String value) {
this.propertySource.setProperty(key, value);
}
/**
* Convenient synonym for {@link #setProperty(String, Object)} that returns
* the current instance.
* <p>Useful for method chaining and fluent-style use.
* Convenient synonym for {@link #setProperty} that returns the current instance.
* Useful for method chaining and fluent-style use.
* @return this {@link MockEnvironment} instance
* @since 6.2.8
* @see MockPropertySource#withProperty(String, Object)
* @see MockPropertySource#withProperty
*/
public MockEnvironment withProperty(String name, Object value) {
setProperty(name, value);
public MockEnvironment withProperty(String key, String value) {
setProperty(key, value);
return this;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -81,7 +81,7 @@ public abstract class AbstractTypeReference implements TypeReference {
@Override
public int compareTo(TypeReference other) {
return getCanonicalName().compareToIgnoreCase(other.getCanonicalName());
return this.getCanonicalName().compareToIgnoreCase(other.getCanonicalName());
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -150,8 +150,7 @@ public class BindingReflectionHintsRegistrar {
String companionClassName = clazz.getCanonicalName() + KOTLIN_COMPANION_SUFFIX;
if (ClassUtils.isPresent(companionClassName, null)) {
Class<?> companionClass = ClassUtils.resolveClassName(companionClassName, null);
Method serializerMethod = ClassUtils.getMethodIfAvailable(companionClass, "serializer",
(Class<?>[]) null);
Method serializerMethod = ClassUtils.getMethodIfAvailable(companionClass, "serializer");
if (serializerMethod != null) {
hints.registerMethod(serializerMethod, ExecutableMode.INVOKE);
}
@@ -463,21 +463,10 @@ public class ReflectUtils {
c = lookup.defineClass(b);
}
catch (LinkageError | IllegalArgumentException ex) {
if (ex instanceof LinkageError) {
// Could be a ClassLoader mismatch with the class pre-existing in a
// parent ClassLoader -> try loadClass before giving up completely.
try {
c = contextClass.getClassLoader().loadClass(className);
}
catch (ClassNotFoundException cnfe) {
}
}
if (c == null) {
// in case of plain LinkageError (class already defined)
// or IllegalArgumentException (class in different package):
// fall through to traditional ClassLoader.defineClass below
t = ex;
}
// in case of plain LinkageError (class already defined)
// or IllegalArgumentException (class in different package):
// fall through to traditional ClassLoader.defineClass below
t = ex;
}
catch (Throwable ex) {
throw new CodeGenerationException(ex);
@@ -26,20 +26,18 @@ import org.springframework.lang.Nullable;
/**
* Static holder for local Spring properties, i.e. defined at the Spring library level.
*
* <p>Reads a {@code spring.properties} file from the root of the classpath and
* also allows for programmatically setting properties via {@link #setProperty}.
* When retrieving properties, local entries are checked first, with JVM-level
* system properties checked next as a fallback via {@link System#getProperty}.
* <p>Reads a {@code spring.properties} file from the root of the Spring library classpath,
* and also allows for programmatically setting properties through {@link #setProperty}.
* When checking a property, local entries are being checked first, then falling back
* to JVM-level system properties through a {@link System#getProperty} check.
*
* <p>This is an alternative way to set Spring-related system properties such as
* {@code spring.getenv.ignore} and {@code spring.beaninfo.ignore}, in particular
* for scenarios where JVM system properties are locked on the target platform
* (for example, WebSphere). See {@link #setFlag} for a convenient way to locally
* set such flags to {@code "true"}.
* "spring.getenv.ignore" and "spring.beaninfo.ignore", in particular for scenarios
* where JVM system properties are locked on the target platform (for example, WebSphere).
* See {@link #setFlag} for a convenient way to locally set such flags to "true".
*
* @author Juergen Hoeller
* @since 3.2.7
* @see org.springframework.aot.AotDetector#AOT_ENABLED
* @see org.springframework.beans.StandardBeanInfoFactory#IGNORE_BEANINFO_PROPERTY_NAME
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#STRICT_LOCKING_PROPERTY_NAME
* @see org.springframework.core.env.AbstractEnvironment#IGNORE_GETENV_PROPERTY_NAME
@@ -361,12 +361,16 @@ abstract class AnnotationsScanner {
}
private static boolean hasSameParameterTypes(Method rootMethod, Method candidateMethod) {
if (candidateMethod.getParameterCount() != rootMethod.getParameterCount()) {
return false;
}
Class<?>[] rootParameterTypes = rootMethod.getParameterTypes();
Class<?>[] candidateParameterTypes = candidateMethod.getParameterTypes();
if (Arrays.equals(candidateParameterTypes, rootParameterTypes)) {
return true;
}
return hasSameGenericTypeParameters(rootMethod, candidateMethod, rootParameterTypes);
return hasSameGenericTypeParameters(rootMethod, candidateMethod,
rootParameterTypes);
}
private static boolean hasSameGenericTypeParameters(
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -42,7 +42,7 @@ import org.springframework.util.StringUtils;
* add by default. {@code AbstractEnvironment} adds none. Subclasses should contribute
* property sources through the protected {@link #customizePropertySources(MutablePropertySources)}
* hook, while clients should customize using {@link ConfigurableEnvironment#getPropertySources()}
* and work against the {@link MutablePropertySources} API.
* and working against the {@link MutablePropertySources} API.
* See {@link ConfigurableEnvironment} javadoc for usage examples.
*
* @author Chris Beams
@@ -66,7 +66,7 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
public static final String IGNORE_GETENV_PROPERTY_NAME = "spring.getenv.ignore";
/**
* Name of the property to specify active profiles: {@value}.
* Name of the property to set to specify active profiles: {@value}.
* <p>The value may be comma delimited.
* <p>Note that certain shell environments such as Bash disallow the use of the period
* character in variable names. Assuming that Spring's {@link SystemEnvironmentPropertySource}
@@ -77,7 +77,7 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
public static final String ACTIVE_PROFILES_PROPERTY_NAME = "spring.profiles.active";
/**
* Name of the property to specify profiles that are active by default: {@value}.
* Name of the property to set to specify profiles that are active by default: {@value}.
* <p>The value may be comma delimited.
* <p>Note that certain shell environments such as Bash disallow the use of the period
* character in variable names. Assuming that Spring's {@link SystemEnvironmentPropertySource}
@@ -141,7 +141,7 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
/**
* Factory method used to create the {@link ConfigurablePropertyResolver}
* used by this {@code Environment}.
* instance used by the Environment.
* @since 5.3.4
* @see #getPropertyResolver()
*/
@@ -150,7 +150,8 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
}
/**
* Return the {@link ConfigurablePropertyResolver} used by the {@code Environment}.
* Return the {@link ConfigurablePropertyResolver} being used by the
* {@link Environment}.
* @since 5.3.4
* @see #createPropertyResolver(MutablePropertySources)
*/
@@ -319,6 +320,7 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
}
}
@Override
public String[] getDefaultProfiles() {
return StringUtils.toStringArray(doGetDefaultProfiles());
@@ -326,7 +328,7 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
/**
* Return the set of default profiles explicitly set via
* {@link #setDefaultProfiles(String...)}, or if the current set of default profiles
* {@link #setDefaultProfiles(String...)} or if the current set of default profiles
* consists only of {@linkplain #getReservedDefaultProfiles() reserved default
* profiles}, then check for the presence of {@link #doGetActiveProfilesProperty()}
* and assign its value (if any) to the set of default profiles.
@@ -418,7 +420,7 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
* active or default profiles.
* <p>Subclasses may override to impose further restrictions on profile syntax.
* @throws IllegalArgumentException if the profile is null, empty, whitespace-only or
* begins with the profile NOT operator (!)
* begins with the profile NOT operator (!).
* @see #acceptsProfiles
* @see #addActiveProfile
* @see #setDefaultProfiles
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,7 +23,6 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.SpringProperties;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.ConfigurableConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
@@ -38,52 +37,10 @@ import org.springframework.util.SystemPropertyUtils;
*
* @author Chris Beams
* @author Juergen Hoeller
* @author Sam Brannen
* @since 3.1
*/
public abstract class AbstractPropertyResolver implements ConfigurablePropertyResolver {
/**
* JVM system property used to change the <em>default</em> escape character
* for property placeholder support: {@value}.
* <p>To configure a custom escape character, supply a string containing a
* single character (other than {@link Character#MIN_VALUE}). For example,
* supplying the following JVM system property via the command line sets the
* default escape character to {@code '@'}.
* <pre style="code">-Dspring.placeholder.escapeCharacter.default=@</pre>
* <p>To disable escape character support, set the value to an empty string
* &mdash; for example, by supplying the following JVM system property via
* the command line.
* <pre style="code">-Dspring.placeholder.escapeCharacter.default=</pre>
* <p>If the property is not set, {@code '\'} will be used as the default
* escape character.
* <p>May alternatively be configured via a
* {@link org.springframework.core.SpringProperties spring.properties} file
* in the root of the classpath.
* @since 6.2.7
* @see #getDefaultEscapeCharacter()
*/
public static final String DEFAULT_PLACEHOLDER_ESCAPE_CHARACTER_PROPERTY_NAME =
"spring.placeholder.escapeCharacter.default";
/**
* Since {@code null} is a valid value for {@link #defaultEscapeCharacter},
* this constant provides a way to represent an undefined (or not yet set)
* value. Consequently, {@link #getDefaultEscapeCharacter()} prevents the use
* of {@link Character#MIN_VALUE} as the actual escape character.
* @since 6.2.7
*/
static final Character UNDEFINED_ESCAPE_CHARACTER = Character.MIN_VALUE;
/**
* Cached value for the default escape character.
* @since 6.2.7
*/
@Nullable
static volatile Character defaultEscapeCharacter = UNDEFINED_ESCAPE_CHARACTER;
protected final Log logger = LogFactory.getLog(getClass());
@Nullable
@@ -105,7 +62,7 @@ public abstract class AbstractPropertyResolver implements ConfigurablePropertyRe
private String valueSeparator = SystemPropertyUtils.VALUE_SEPARATOR;
@Nullable
private Character escapeCharacter = getDefaultEscapeCharacter();
private Character escapeCharacter = SystemPropertyUtils.ESCAPE_CHARACTER;
private final Set<String> requiredProperties = new LinkedHashSet<>();
@@ -134,9 +91,9 @@ public abstract class AbstractPropertyResolver implements ConfigurablePropertyRe
}
/**
* {@inheritDoc}
* <p>The default is <code>"${"</code>.
* @see SystemPropertyUtils#PLACEHOLDER_PREFIX
* Set the prefix that placeholders replaced by this resolver must begin with.
* <p>The default is "${".
* @see org.springframework.util.SystemPropertyUtils#PLACEHOLDER_PREFIX
*/
@Override
public void setPlaceholderPrefix(String placeholderPrefix) {
@@ -145,9 +102,9 @@ public abstract class AbstractPropertyResolver implements ConfigurablePropertyRe
}
/**
* {@inheritDoc}
* <p>The default is <code>"}"</code>.
* @see SystemPropertyUtils#PLACEHOLDER_SUFFIX
* Set the suffix that placeholders replaced by this resolver must end with.
* <p>The default is "}".
* @see org.springframework.util.SystemPropertyUtils#PLACEHOLDER_SUFFIX
*/
@Override
public void setPlaceholderSuffix(String placeholderSuffix) {
@@ -156,9 +113,11 @@ public abstract class AbstractPropertyResolver implements ConfigurablePropertyRe
}
/**
* {@inheritDoc}
* <p>The default is {@code ":"}.
* @see SystemPropertyUtils#VALUE_SEPARATOR
* Specify the separating character between the placeholders replaced by this
* resolver and their associated default value, or {@code null} if no such
* special character should be processed as a value separator.
* <p>The default is ":".
* @see org.springframework.util.SystemPropertyUtils#VALUE_SEPARATOR
*/
@Override
public void setValueSeparator(@Nullable String valueSeparator) {
@@ -166,9 +125,12 @@ public abstract class AbstractPropertyResolver implements ConfigurablePropertyRe
}
/**
* {@inheritDoc}
* <p>The default is determined by {@link #getDefaultEscapeCharacter()}.
* Specify the escape character to use to ignore placeholder prefix
* or value separator, or {@code null} if no escaping should take
* place.
* <p>The default is "\".
* @since 6.2
* @see org.springframework.util.SystemPropertyUtils#ESCAPE_CHARACTER
*/
@Override
public void setEscapeCharacter(@Nullable Character escapeCharacter) {
@@ -198,7 +160,7 @@ public abstract class AbstractPropertyResolver implements ConfigurablePropertyRe
public void validateRequiredProperties() {
MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();
for (String key : this.requiredProperties) {
if (getProperty(key) == null) {
if (this.getProperty(key) == null) {
ex.addMissingRequiredProperty(key);
}
}
@@ -329,60 +291,4 @@ public abstract class AbstractPropertyResolver implements ConfigurablePropertyRe
@Nullable
protected abstract String getPropertyAsRawString(String key);
/**
* Get the default {@linkplain #setEscapeCharacter(Character) escape character}
* to use when parsing strings for property placeholder resolution.
* <p>This method attempts to retrieve the default escape character configured
* via the {@value #DEFAULT_PLACEHOLDER_ESCAPE_CHARACTER_PROPERTY_NAME} JVM system
* property or Spring property.
* <p>Falls back to {@code '\'} if the property has not been set.
* @return the configured default escape character, {@code null} if escape character
* support has been disabled, or {@code '\'} if the property has not been set
* @throws IllegalArgumentException if the property is configured with an
* invalid value, such as {@link Character#MIN_VALUE} or a string containing
* more than one character
* @since 6.2.7
* @see #DEFAULT_PLACEHOLDER_ESCAPE_CHARACTER_PROPERTY_NAME
* @see SystemPropertyUtils#ESCAPE_CHARACTER
* @see SpringProperties
*/
@Nullable
public static Character getDefaultEscapeCharacter() throws IllegalArgumentException {
Character escapeCharacter = defaultEscapeCharacter;
if (UNDEFINED_ESCAPE_CHARACTER.equals(escapeCharacter)) {
String value = SpringProperties.getProperty(DEFAULT_PLACEHOLDER_ESCAPE_CHARACTER_PROPERTY_NAME);
if (value != null) {
if (value.isEmpty()) {
// Disable escape character support by default.
escapeCharacter = null;
}
else if (value.length() == 1) {
try {
// Use custom default escape character.
escapeCharacter = value.charAt(0);
}
catch (Exception ex) {
throw new IllegalArgumentException("Failed to process value [%s] for property [%s]: %s"
.formatted(value, DEFAULT_PLACEHOLDER_ESCAPE_CHARACTER_PROPERTY_NAME, ex.getMessage()), ex);
}
Assert.isTrue(!escapeCharacter.equals(Character.MIN_VALUE),
() -> "Value for property [%s] must not be Character.MIN_VALUE"
.formatted(DEFAULT_PLACEHOLDER_ESCAPE_CHARACTER_PROPERTY_NAME));
}
else {
throw new IllegalArgumentException(
"Value [%s] for property [%s] must be a single character or an empty string"
.formatted(value, DEFAULT_PLACEHOLDER_ESCAPE_CHARACTER_PROPERTY_NAME));
}
}
else {
// Use standard default value for the escape character.
escapeCharacter = SystemPropertyUtils.ESCAPE_CHARACTER;
}
defaultEscapeCharacter = escapeCharacter;
}
return escapeCharacter;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,10 +34,7 @@ import org.springframework.util.StringUtils;
*
* <p>As of Spring 4.1.2, this class extends {@link EnumerablePropertySource} instead
* of plain {@link PropertySource}, exposing {@link #getPropertyNames()} based on the
* accumulated property names from all contained sources - and failing with an
* {@code IllegalStateException} against any non-{@code EnumerablePropertySource}.
* <b>When used through the {@code EnumerablePropertySource} contract, all contained
* sources are expected to be of type {@code EnumerablePropertySource} as well.</b>
* accumulated property names from all contained sources (as far as possible).
*
* @author Chris Beams
* @author Juergen Hoeller
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -137,13 +137,13 @@ public interface ConfigurableEnvironment extends Environment, ConfigurableProper
Map<String, Object> getSystemEnvironment();
/**
* Append the given parent environment's active profiles, default profiles, and
* Append the given parent environment's active profiles, default profiles and
* property sources to this (child) environment's respective collections of each.
* <p>For any identically-named {@code PropertySource} instance existing in both
* parent and child, the child instance is to be preserved and the parent instance
* discarded. This has the effect of allowing overriding of property sources by the
* child as well as avoiding redundant searches through common property source types
* &mdash; for example, system environment and system properties.
* child as well as avoiding redundant searches through common property source types,
* for example, system environment and system properties.
* <p>Active and default profile names are also filtered for duplicates, to avoid
* confusion and redundant storage.
* <p>The parent environment remains unmodified in any case. Note that any changes to
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -69,23 +69,21 @@ public interface ConfigurablePropertyResolver extends PropertyResolver {
void setPlaceholderSuffix(String placeholderSuffix);
/**
* Set the separating character to be honored between placeholders replaced by
* this resolver and their associated default values, or {@code null} if no such
* Specify the separating character between the placeholders replaced by this
* resolver and their associated default value, or {@code null} if no such
* special character should be processed as a value separator.
*/
void setValueSeparator(@Nullable String valueSeparator);
/**
* Set the escape character to use to ignore the
* {@linkplain #setPlaceholderPrefix(String) placeholder prefix} and the
* {@linkplain #setValueSeparator(String) value separator}, or {@code null}
* if no escaping should take place.
* Specify the escape character to use to ignore placeholder prefix or
* value separator, or {@code null} if no escaping should take place.
* @since 6.2
*/
void setEscapeCharacter(@Nullable Character escapeCharacter);
/**
* Specify whether to throw an exception when encountering an unresolvable placeholder
* Set whether to throw an exception when encountering an unresolvable placeholder
* nested within the value of a given property. A {@code false} value indicates strict
* resolution, i.e. that an exception will be thrown. A {@code true} value indicates
* that unresolvable nested placeholders should be passed through in their unresolved
@@ -108,7 +106,7 @@ public interface ConfigurablePropertyResolver extends PropertyResolver {
* {@link #setRequiredProperties} is present and resolves to a
* non-{@code null} value.
* @throws MissingRequiredPropertiesException if any of the required
* properties are not resolvable
* properties are not resolvable.
*/
void validateRequiredProperties() throws MissingRequiredPropertiesException;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,13 +30,13 @@ import org.springframework.lang.Nullable;
public interface PropertyResolver {
/**
* Determine whether the given property key is available for resolution
* &mdash; for example, if the value for the given key is not {@code null}.
* Return whether the given property key is available for resolution,
* i.e. if the value for the given key is not {@code null}.
*/
boolean containsProperty(String key);
/**
* Resolve the property value associated with the given key,
* Return the property value associated with the given key,
* or {@code null} if the key cannot be resolved.
* @param key the property name to resolve
* @see #getProperty(String, String)
@@ -47,7 +47,7 @@ public interface PropertyResolver {
String getProperty(String key);
/**
* Resolve the property value associated with the given key, or
* Return the property value associated with the given key, or
* {@code defaultValue} if the key cannot be resolved.
* @param key the property name to resolve
* @param defaultValue the default value to return if no value is found
@@ -57,7 +57,7 @@ public interface PropertyResolver {
String getProperty(String key, String defaultValue);
/**
* Resolve the property value associated with the given key,
* Return the property value associated with the given key,
* or {@code null} if the key cannot be resolved.
* @param key the property name to resolve
* @param targetType the expected type of the property value
@@ -67,7 +67,7 @@ public interface PropertyResolver {
<T> T getProperty(String key, Class<T> targetType);
/**
* Resolve the property value associated with the given key,
* Return the property value associated with the given key,
* or {@code defaultValue} if the key cannot be resolved.
* @param key the property name to resolve
* @param targetType the expected type of the property value
@@ -77,14 +77,14 @@ public interface PropertyResolver {
<T> T getProperty(String key, Class<T> targetType, T defaultValue);
/**
* Resolve the property value associated with the given key (never {@code null}).
* Return the property value associated with the given key (never {@code null}).
* @throws IllegalStateException if the key cannot be resolved
* @see #getRequiredProperty(String, Class)
*/
String getRequiredProperty(String key) throws IllegalStateException;
/**
* Resolve the property value associated with the given key, converted to the given
* Return the property value associated with the given key, converted to the given
* targetType (never {@code null}).
* @throws IllegalStateException if the given key cannot be resolved
*/
@@ -29,7 +29,6 @@ import java.nio.channels.ReadableByteChannel;
import java.nio.file.NoSuchFileException;
import java.nio.file.StandardOpenOption;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import org.springframework.util.ResourceUtils;
@@ -87,17 +86,7 @@ public abstract class AbstractFileResolvingResource extends AbstractResource {
if (con instanceof JarURLConnection jarCon) {
// For JarURLConnection, do not check content-length but rather the
// existence of the entry (or the jar root in case of no entryName).
// getJarFile() called for enforced presence check of the jar file,
// throwing a NoSuchFileException otherwise (turned to false below).
JarFile jarFile = jarCon.getJarFile();
try {
return (jarCon.getEntryName() == null || jarCon.getJarEntry() != null);
}
finally {
if (!jarCon.getUseCaches()) {
jarFile.close();
}
}
return (jarCon.getEntryName() == null || jarCon.getJarEntry() != null);
}
else if (con.getContentLengthLong() > 0) {
return true;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,8 +18,6 @@ package org.springframework.core.io.buffer;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Objects;
import org.springframework.util.Assert;
@@ -105,44 +103,10 @@ final class DataBufferInputStream extends InputStream {
this.closed = true;
}
@Override
public byte[] readNBytes(int len) throws IOException {
if (len < 0) {
throw new IllegalArgumentException("len < 0");
}
checkClosed();
int size = Math.min(available(), len);
byte[] out = new byte[size];
this.dataBuffer.read(out);
return out;
}
@Override
public long skip(long n) throws IOException {
checkClosed();
if (n <= 0) {
return 0L;
}
int skipped = Math.min(available(), n > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) n);
this.dataBuffer.readPosition(Math.min(this.end, this.dataBuffer.readPosition() + skipped));
return skipped;
}
@Override
public long transferTo(OutputStream out) throws IOException {
Objects.requireNonNull(out, "out");
checkClosed();
if (available() == 0) {
return 0L;
}
byte[] buf = readAllBytes();
out.write(buf);
return buf.length;
}
private void checkClosed() throws IOException {
if (this.closed) {
throw new IOException("DataBufferInputStream is closed");
}
}
}
@@ -36,7 +36,6 @@ import java.nio.file.FileSystemNotFoundException;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitOption;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.util.Collections;
import java.util.Enumeration;
@@ -875,9 +874,9 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
rootEntryPath = (jarEntry != null ? jarEntry.getName() : "");
closeJarFile = !jarCon.getUseCaches();
}
catch (ZipException | FileNotFoundException | NoSuchFileException ex) {
catch (ZipException | FileNotFoundException ex) {
// Happens in case of a non-jar file or in case of a cached root directory
// without the specific subdirectory present, respectively.
// without specific subdirectory present, respectively.
return Collections.emptySet();
}
}
@@ -1276,7 +1275,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
}
/**
* Return an alternative form of the resource, i.e. with or without a leading slash.
* Return a alternative form of the resource, i.e. with or without a leading slash.
* @param path the file path (with or without a leading slash)
* @return the alternative form or {@code null}
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -141,8 +141,8 @@ public abstract class PropertiesLoaderSupport {
/**
* Return a merged {@link Properties} instance containing both the
* loaded properties and properties set on this component.
* Return a merged Properties instance containing both the
* loaded properties and properties set on this FactoryBean.
*/
protected Properties mergeProperties() throws IOException {
Properties result = new Properties();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,8 @@ package org.springframework.lang;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
@@ -75,6 +77,7 @@ import java.lang.annotation.Target;
* NullAway custom contract annotations</a>
*/
@Documented
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface Contract {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,25 +37,13 @@ public abstract class PatternMatchUtils {
* @return whether the String matches the given pattern
*/
public static boolean simpleMatch(@Nullable String pattern, @Nullable String str) {
return simpleMatch(pattern, str, false);
}
/**
* Variant of {@link #simpleMatch(String, String)} that ignores upper/lower case.
* @since 6.1.20
*/
public static boolean simpleMatchIgnoreCase(@Nullable String pattern, @Nullable String str) {
return simpleMatch(pattern, str, true);
}
private static boolean simpleMatch(@Nullable String pattern, @Nullable String str, boolean ignoreCase) {
if (pattern == null || str == null) {
return false;
}
int firstIndex = pattern.indexOf('*');
if (firstIndex == -1) {
return (ignoreCase ? pattern.equalsIgnoreCase(str) : pattern.equals(str));
return pattern.equals(str);
}
if (firstIndex == 0) {
@@ -64,43 +52,25 @@ public abstract class PatternMatchUtils {
}
int nextIndex = pattern.indexOf('*', 1);
if (nextIndex == -1) {
String part = pattern.substring(1);
return (ignoreCase ? StringUtils.endsWithIgnoreCase(str, part) : str.endsWith(part));
return str.endsWith(pattern.substring(1));
}
String part = pattern.substring(1, nextIndex);
if (part.isEmpty()) {
return simpleMatch(pattern.substring(nextIndex), str, ignoreCase);
return simpleMatch(pattern.substring(nextIndex), str);
}
int partIndex = indexOf(str, part, 0, ignoreCase);
int partIndex = str.indexOf(part);
while (partIndex != -1) {
if (simpleMatch(pattern.substring(nextIndex), str.substring(partIndex + part.length()), ignoreCase)) {
if (simpleMatch(pattern.substring(nextIndex), str.substring(partIndex + part.length()))) {
return true;
}
partIndex = indexOf(str, part, partIndex + 1, ignoreCase);
partIndex = str.indexOf(part, partIndex + 1);
}
return false;
}
return (str.length() >= firstIndex &&
checkStartsWith(pattern, str, firstIndex, ignoreCase) &&
simpleMatch(pattern.substring(firstIndex), str.substring(firstIndex), ignoreCase));
}
private static boolean checkStartsWith(String pattern, String str, int index, boolean ignoreCase) {
String part = str.substring(0, index);
return (ignoreCase ? StringUtils.startsWithIgnoreCase(pattern, part) : pattern.startsWith(part));
}
private static int indexOf(String str, String otherStr, int startIndex, boolean ignoreCase) {
if (!ignoreCase) {
return str.indexOf(otherStr, startIndex);
}
for (int i = startIndex; i <= (str.length() - otherStr.length()); i++) {
if (str.regionMatches(true, i, otherStr, 0, otherStr.length())) {
return i;
}
}
return -1;
pattern.startsWith(str.substring(0, firstIndex)) &&
simpleMatch(pattern.substring(firstIndex), str.substring(firstIndex)));
}
/**
@@ -124,19 +94,4 @@ public abstract class PatternMatchUtils {
return false;
}
/**
* Variant of {@link #simpleMatch(String[], String)} that ignores upper/lower case.
* @since 6.1.20
*/
public static boolean simpleMatchIgnoreCase(@Nullable String[] patterns, @Nullable String str) {
if (patterns != null) {
for (String pattern : patterns) {
if (simpleMatch(pattern, str, true)) {
return true;
}
}
}
return false;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,14 +25,13 @@ import java.io.InputStreamReader;
import java.io.OutputStream;
import java.nio.charset.Charset;
import org.springframework.lang.Contract;
import org.springframework.lang.Nullable;
/**
* Simple utility methods for dealing with streams.
*
* <p>The copy methods of this class are similar to those defined in
* {@link FileCopyUtils} except that all affected streams are left open when done.
* All copy methods use a block size of {@value #BUFFER_SIZE} bytes.
* Simple utility methods for dealing with streams. The copy methods of this class are
* similar to those defined in {@link FileCopyUtils} except that all affected streams are
* left open when done. All copy methods use a block size of 8192 bytes.
*
* <p>Mainly for use within the framework, but also useful for application code.
*
@@ -191,14 +190,14 @@ public abstract class StreamUtils {
}
/**
* Drain the remaining content of the given {@link InputStream}.
* <p>Leaves the {@code InputStream} open when done.
* @param in the {@code InputStream} to drain
* @return the number of bytes read, or {@code 0} if the supplied
* {@code InputStream} is {@code null} or empty
* Drain the remaining content of the given InputStream.
* <p>Leaves the InputStream open when done.
* @param in the InputStream to drain
* @return the number of bytes read
* @throws IOException in case of I/O errors
* @since 4.3
*/
@Contract("null -> fail")
public static int drain(@Nullable InputStream in) throws IOException {
if (in == null) {
return 0;
@@ -240,7 +239,7 @@ public abstract class StreamUtils {
}
private static final class NonClosingInputStream extends FilterInputStream {
private static class NonClosingInputStream extends FilterInputStream {
public NonClosingInputStream(InputStream in) {
super(in);
@@ -249,30 +248,10 @@ public abstract class StreamUtils {
@Override
public void close() throws IOException {
}
@Override
public byte[] readAllBytes() throws IOException {
return in.readAllBytes();
}
@Override
public byte[] readNBytes(int len) throws IOException {
return in.readNBytes(len);
}
@Override
public int readNBytes(byte[] b, int off, int len) throws IOException {
return in.readNBytes(b, off, len);
}
@Override
public long transferTo(OutputStream out) throws IOException {
return in.transferTo(out);
}
}
private static final class NonClosingOutputStream extends FilterOutputStream {
private static class NonClosingOutputStream extends FilterOutputStream {
public NonClosingOutputStream(OutputStream out) {
super(out);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,19 +35,16 @@ import org.springframework.lang.Nullable;
*/
public abstract class SystemPropertyUtils {
/** Prefix for property placeholders: {@value}. */
/** Prefix for system property placeholders: {@value}. */
public static final String PLACEHOLDER_PREFIX = "${";
/** Suffix for property placeholders: {@value}. */
/** Suffix for system property placeholders: {@value}. */
public static final String PLACEHOLDER_SUFFIX = "}";
/** Value separator for property placeholders: {@value}. */
/** Value separator for system property placeholders: {@value}. */
public static final String VALUE_SEPARATOR = ":";
/**
* Escape character for property placeholders: {@code '\'}.
* @since 6.2
*/
/** Default escape character: {@code '\'}. */
public static final Character ESCAPE_CHARACTER = '\\';
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -85,7 +85,6 @@ public class ExponentialBackOff implements BackOff {
*/
public static final int DEFAULT_MAX_ATTEMPTS = Integer.MAX_VALUE;
private long initialInterval = DEFAULT_INITIAL_INTERVAL;
private double multiplier = DEFAULT_MULTIPLIER;
@@ -205,7 +204,6 @@ public class ExponentialBackOff implements BackOff {
return this.maxAttempts;
}
@Override
public BackOffExecution start() {
return new ExponentialBackOffExecution();
@@ -227,7 +225,6 @@ public class ExponentialBackOff implements BackOff {
.toString();
}
private class ExponentialBackOffExecution implements BackOffExecution {
private long currentInterval = -1;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,7 +35,6 @@ public class FixedBackOff implements BackOff {
*/
public static final long UNLIMITED_ATTEMPTS = Long.MAX_VALUE;
private long interval = DEFAULT_INTERVAL;
private long maxAttempts = UNLIMITED_ATTEMPTS;
@@ -87,7 +86,6 @@ public class FixedBackOff implements BackOff {
return this.maxAttempts;
}
@Override
public BackOffExecution start() {
return new FixedBackOffExecution();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,7 +32,7 @@ import static org.assertj.core.api.Assertions.assertThat;
class AnnotationBackCompatibilityTests {
@Test
void multipleRoutesToMetaAnnotation() {
void multiplRoutesToMetaAnnotation() {
Class<?> source = WithMetaMetaTestAnnotation1AndMetaTestAnnotation2.class;
// Merged annotation chooses lowest depth
MergedAnnotation<TestAnnotation> mergedAnnotation = MergedAnnotations.from(source).get(TestAnnotation.class);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -983,7 +983,7 @@ class MergedAnnotationsTests {
}
@Test
void getDirectFromClassWithMetaMetaAnnotatedClass() {
void getDirectFromClassgetDirectFromClassMetaMetaAnnotatedClass() {
MergedAnnotation<?> annotation = MergedAnnotations.from(
MetaMetaAnnotatedClass.class, SearchStrategy.TYPE_HIERARCHY).get(Component.class);
assertThat(annotation.getString("value")).isEqualTo("meta2");

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