mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 90f9c0929b |
+3
-2
@@ -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,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,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.
|
||||
|
||||
+2
-5
@@ -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
|
||||
}
|
||||
|
||||
+178
-195
@@ -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`]
|
||||
|
||||
+2
-2
@@ -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 {
|
||||
|
||||
+5
-1
@@ -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]
|
||||
======
|
||||
|
||||
+6
-2
@@ -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
|
||||
|
||||
+1
-1
@@ -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].
|
||||
|
||||
+5
-1
@@ -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`.
|
||||
@@ -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
@@ -1,4 +1,4 @@
|
||||
version=6.2.8
|
||||
version=6.2.6
|
||||
|
||||
org.gradle.caching=true
|
||||
org.gradle.jvmargs=-Xmx2048m
|
||||
|
||||
@@ -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
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+1
-1
@@ -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
|
||||
|
||||
@@ -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
@@ -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,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"
|
||||
|
||||
+50
-65
@@ -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) {}
|
||||
|
||||
}
|
||||
|
||||
+3
-3
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+3
-3
@@ -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();
|
||||
|
||||
+25
-36
@@ -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">
|
||||
* <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
|
||||
* <property name="driverClassName" value="${jdbc.driver}" />
|
||||
* <property name="url" value="jdbc:${jdbc.dbname}" />
|
||||
* <property name="driverClassName" value="${driver}" />
|
||||
* <property name="url" value="jdbc:${dbname}" />
|
||||
* </bean>
|
||||
* </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">
|
||||
* <property name="url" value="jdbc:${jdbc.dbname:defaultdb}" />
|
||||
* <property name="url" value="jdbc:${dbname:defaultdb}" />
|
||||
* </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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+5
-7
@@ -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.
|
||||
|
||||
+3
-11
@@ -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 {
|
||||
|
||||
+5
-1
@@ -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
|
||||
|
||||
+5
-10
@@ -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) &&
|
||||
|
||||
+5
-7
@@ -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 {
|
||||
|
||||
+8
-22
@@ -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 {
|
||||
|
||||
+4
-6
@@ -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> {
|
||||
|
||||
+3
-26
@@ -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;
|
||||
}
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+2
-3
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-7
@@ -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)");
|
||||
});
|
||||
|
||||
+2
-2
@@ -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);
|
||||
}
|
||||
|
||||
+3
-3
@@ -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");
|
||||
}
|
||||
|
||||
+8
-8
@@ -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);
|
||||
|
||||
+4
-3
@@ -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
-7
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
+11
-12
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
+2
-9
@@ -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();
|
||||
|
||||
+11
-19
@@ -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
-2
@@ -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 =
|
||||
|
||||
+3
-4
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+2
-3
@@ -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 {
|
||||
|
||||
|
||||
+28
-103
@@ -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
-7
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+11
@@ -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>
|
||||
Vendored
+2
-2
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+2
-13
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-38
@@ -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 {
|
||||
|
||||
|
||||
+53
-78
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-3
@@ -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() {
|
||||
|
||||
+6
-11
@@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+21
-419
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+3
-4
@@ -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))
|
||||
|
||||
+9
-13
@@ -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
|
||||
|
||||
+2
-3
@@ -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
|
||||
|
||||
+5
-1
@@ -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(
|
||||
|
||||
+10
-8
@@ -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
|
||||
|
||||
+19
-113
@@ -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
|
||||
* — 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-5
@@ -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
|
||||
|
||||
+4
-4
@@ -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
|
||||
* — 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
|
||||
|
||||
+7
-9
@@ -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;
|
||||
|
||||
|
||||
+9
-9
@@ -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
|
||||
* — 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
|
||||
*/
|
||||
|
||||
+1
-12
@@ -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;
|
||||
|
||||
+2
-38
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+3
-4
@@ -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}
|
||||
*/
|
||||
|
||||
+3
-3
@@ -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();
|
||||
|
||||
+2
-2
@@ -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);
|
||||
|
||||
+2
-2
@@ -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
Reference in New Issue
Block a user