Compare commits

..

1 Commits

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

Before

Width:  |  Height:  |  Size: 4.8 KiB

+3 -3
View File
@@ -9,7 +9,7 @@ plugins {
id 'io.github.goooler.shadow' version '8.1.8' apply false
id 'me.champeau.jmh' version '0.7.2' apply false
id 'me.champeau.mrjar' version '0.1.1'
id "net.ltgt.errorprone" version "4.1.0" apply false
id "net.ltgt.errorprone" version "3.1.0" apply false
}
ext {
@@ -91,7 +91,7 @@ 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://eclipse.dev/aspectj/doc/released/aspectj5rt-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/",
@@ -102,7 +102,7 @@ configure([rootProject] + javaProjects) { project ->
// TODO Uncomment link to JUnit 5 docs once we execute Gradle with Java 18+.
// See https://github.com/spring-projects/spring-framework/issues/27497
//
// "https://junit.org/junit5/docs/5.12.1/api/",
// "https://junit.org/junit5/docs/5.11.4/api/",
"https://www.reactive-streams.org/reactive-streams-1.0.3-javadoc/",
//"https://javadoc.io/static/io.rsocket/rsocket-core/1.1.1/",
"https://r2dbc.io/spec/1.0.0.RELEASE/api/",
@@ -50,7 +50,7 @@ public class CheckstyleConventions {
project.getPlugins().apply(CheckstylePlugin.class);
project.getTasks().withType(Checkstyle.class).forEach(checkstyle -> checkstyle.getMaxHeapSize().set("1g"));
CheckstyleExtension checkstyle = project.getExtensions().getByType(CheckstyleExtension.class);
checkstyle.setToolVersion("10.21.4");
checkstyle.setToolVersion("10.21.1");
checkstyle.getConfigDirectory().set(project.getRootProject().file("src/checkstyle"));
String version = SpringJavaFormatPlugin.class.getPackage().getImplementationVersion();
DependencySet checkstyleDependencies = project.getConfigurations().getByName("checkstyle").getDependencies();
@@ -64,7 +64,7 @@ public class CheckstyleConventions {
NoHttpExtension noHttp = project.getExtensions().getByType(NoHttpExtension.class);
noHttp.setAllowlistFile(project.file("src/nohttp/allowlist.lines"));
noHttp.getSource().exclude("**/test-output/**", "**/.settings/**",
"**/.classpath", "**/.project", "**/.gradle/**", "**/node_modules/**", "buildSrc/build/**");
"**/.classpath", "**/.project", "**/.gradle/**", "**/node_modules/**");
List<String> buildFolders = List.of("bin", "build", "out");
project.allprojects(subproject -> {
Path rootPath = project.getRootDir().toPath();
@@ -27,7 +27,6 @@ import org.gradle.api.attributes.Usage;
import org.gradle.api.attributes.java.TargetJvmVersion;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.jvm.JvmTestSuite;
import org.gradle.api.tasks.TaskProvider;
import org.gradle.api.tasks.testing.Test;
import org.gradle.testing.base.TestingExtension;
@@ -53,7 +52,7 @@ public class RuntimeHintsAgentPlugin implements Plugin<Project> {
TestingExtension testing = project.getExtensions().getByType(TestingExtension.class);
JvmTestSuite jvmTestSuite = (JvmTestSuite) testing.getSuites().getByName("test");
RuntimeHintsAgentExtension agentExtension = createRuntimeHintsAgentExtension(project);
TaskProvider<Test> agentTest = project.getTasks().register(RUNTIMEHINTS_TEST_TASK, Test.class, test -> {
Test agentTest = project.getTasks().create(RUNTIMEHINTS_TEST_TASK, Test.class, test -> {
test.useJUnitPlatform(options -> {
options.includeTags("RuntimeHintsTests");
});
@@ -64,7 +63,7 @@ public class RuntimeHintsAgentPlugin implements Plugin<Project> {
test.setClasspath(jvmTestSuite.getSources().getRuntimeClasspath());
test.getJvmArgumentProviders().add(createRuntimeHintsAgentArgumentProvider(project, agentExtension));
});
project.getTasks().named("check", task -> task.dependsOn(agentTest));
project.getTasks().getByName("check", task -> task.dependsOn(agentTest));
project.getDependencies().add(CONFIGURATION_NAME, project.project(":spring-core-test"));
});
}
+2 -3
View File
@@ -45,7 +45,6 @@ dependencies {
api(project(":spring-aspects"))
api(project(":spring-context"))
api(project(":spring-context-support"))
api(project(":spring-core-test"))
api(project(":spring-jdbc"))
api(project(":spring-jms"))
api(project(":spring-test"))
@@ -67,9 +66,9 @@ dependencies {
api("org.apache.activemq:activemq-ra:6.1.2")
api("org.apache.commons:commons-dbcp2:2.11.0")
api("org.aspectj:aspectjweaver")
api("org.assertj:assertj-core")
api("org.eclipse.jetty.websocket:jetty-websocket-jetty-api")
api("org.jetbrains.kotlin:kotlin-stdlib")
api("org.junit.jupiter:junit-jupiter-api")
implementation(project(":spring-core-test"))
implementation("org.assertj:assertj-core")
}
@@ -444,9 +444,9 @@ through Java 8's `java.util.Optional`, as the following example shows:
}
----
You can also use a parameter-level `@Nullable` annotation (of any kind in any package --
for example, `javax.annotation.Nullable` from JSR-305) or just leverage Kotlin built-in
null-safety support:
You can also use a `@Nullable` annotation (of any kind in any package -- for example,
`javax.annotation.Nullable` from JSR-305) or just leverage Kotlin built-in null-safety
support:
[tabs]
======
@@ -477,13 +477,6 @@ Kotlin::
----
======
[NOTE]
====
A type-level `@Nullable` annotation such as from JSpecify is not supported in Spring
Framework 6.2 yet. You need to upgrade to Spring Framework 7.0 where the framework
detects type-level annotations and consistently declares JSpecify in its own codebase.
====
You can also use `@Autowired` for interfaces that are well-known resolvable
dependencies: `BeanFactory`, `ApplicationContext`, `Environment`, `ResourceLoader`,
`ApplicationEventPublisher`, and `MessageSource`. These interfaces and their extended
@@ -57,9 +57,9 @@ The following table describes these properties:
In addition to bean definitions that contain information on how to create a specific
bean, the `ApplicationContext` implementations also permit the registration of existing
objects that are created outside the container (by users). This is done by accessing the
ApplicationContext's `BeanFactory` through the `getAutowireCapableBeanFactory()` method,
which returns the `DefaultListableBeanFactory` implementation. `DefaultListableBeanFactory`
supports this registration through the `registerSingleton(..)` and `registerBeanDefinition(..)`
ApplicationContext's `BeanFactory` through the `getBeanFactory()` method, which returns
the `DefaultListableBeanFactory` implementation. `DefaultListableBeanFactory` supports
this registration through the `registerSingleton(..)` and `registerBeanDefinition(..)`
methods. However, typical applications work solely with beans defined through regular
bean definition metadata.
@@ -120,6 +120,8 @@ dynamically generate a subclass that overrides the method.
subclasses cannot be `final`, and the method to be overridden cannot be `final`, either.
* Unit-testing a class that has an `abstract` method requires you to subclass the class
yourself and to supply a stub implementation of the `abstract` method.
* Concrete methods are also necessary for component scanning, which requires concrete
classes to pick up.
* A further key limitation is that lookup methods do not work with factory methods and
in particular not with `@Bean` methods in configuration classes, since, in that case,
the container is not in charge of creating the instance and therefore cannot create
@@ -291,6 +293,11 @@ Kotlin::
----
======
Note that you should typically declare such annotated lookup methods with a concrete
stub implementation, in order for them to be compatible with Spring's component
scanning rules where abstract classes get ignored by default. This limitation does not
apply to explicitly registered or explicitly imported bean classes.
[TIP]
====
Another way of accessing differently scoped target beans is an `ObjectFactory`/
+1 -1
View File
@@ -7,7 +7,7 @@ xref:overview.adoc[Overview] :: History, Design Philosophy, Feedback,
Getting Started.
xref:core.adoc[Core] :: IoC Container, Events, Resources, i18n,
Validation, Data Binding, Type Conversion, SpEL, AOP, AOT.
xref:testing.adoc[Testing] :: Mock Objects, TestContext Framework,
<<testing.adoc#testing, Testing>> :: Mock Objects, TestContext Framework,
Spring MVC Test, WebTestClient.
xref:data-access.adoc[Data Access] :: Transactions, DAO Support,
JDBC, R2DBC, O/R Mapping, XML Marshalling.
@@ -115,15 +115,11 @@ Finally, the body can be set to a callback function that writes to an `OutputStr
==== Retrieving the response
Once the request has been set up, it can be sent by chaining method calls after `retrieve()`.
For example, the response body can be accessed by using `retrieve().body(Class)` or `retrieve().body(ParameterizedTypeReference)` for parameterized types like lists.
Once the request has been set up, the HTTP response is accessed by invoking `retrieve()`.
The response body can be accessed by using `body(Class)` or `body(ParameterizedTypeReference)` for parameterized types like lists.
The `body` method converts the response contents into various types for instance, bytes can be converted into a `String`, JSON can be converted into objects using Jackson, and so on (see <<rest-message-conversion>>).
The response can also be converted into a `ResponseEntity`, giving access to the response headers as well as the body, with `retrieve().toEntity(Class)`
NOTE: Calling `retrieve()` by itself is a no-op and returns a `ResponseSpec`.
Applications must invoke a terminal operation on the `ResponseSpec` to have any side effect.
If consuming the response has no interest for your use case, you can use `retrieve().toBodilessEntity()`.
The response can also be converted into a `ResponseEntity`, giving access to the response headers as well as the body.
This sample shows how `RestClient` can be used to perform a simple `GET` request.
@@ -857,7 +853,7 @@ Start by creating the interface with `@HttpExchange` methods:
[source,java,indent=0,subs="verbatim,quotes"]
----
public interface RepositoryService {
interface RepositoryService {
@GetExchange("/repos/{owner}/{repo}")
Repository getRepository(@PathVariable String owner, @PathVariable String repo);
@@ -908,7 +904,7 @@ For `RestTemplate`:
[source,java,indent=0,subs="verbatim,quotes"]
----
@HttpExchange(url = "/repos/{owner}/{repo}", accept = "application/vnd.github.v3+json")
public interface RepositoryService {
interface RepositoryService {
@GetExchange
Repository getRepository(@PathVariable String owner, @PathVariable String repo);
@@ -992,27 +988,6 @@ parameter annotation) is set to `false`, or the parameter is marked optional as
[[rest-http-interface.custom-resolver]]
=== Custom argument resolver
For more complex cases, HTTP interfaces do not support `RequestEntity` types as method parameters.
This would take over the entire HTTP request and not improve the semantics of the interface.
Instead of adding many method parameters, developers can combine them into a custom type
and configure a dedicated `HttpServiceArgumentResolver` implementation.
In the following HTTP interface, we are using a custom `Search` type as a parameter:
include-code::./CustomHttpServiceArgumentResolver[tag=httpinterface,indent=0]
We can implement our own `HttpServiceArgumentResolver` that supports our custom `Search` type
and writes its data in the outgoing HTTP request.
include-code::./CustomHttpServiceArgumentResolver[tag=argumentresolver,indent=0]
Finally, we can use this argument resolver during the setup and use our HTTP interface.
include-code::./CustomHttpServiceArgumentResolver[tag=usage,indent=0]
[[rest-http-interface-return-values]]
=== Return Values
@@ -1,62 +1,34 @@
[[spring-testing-annotation-beanoverriding-mockitobean]]
= `@MockitoBean` and `@MockitoSpyBean`
{spring-framework-api}/test/context/bean/override/mockito/MockitoBean.html[`@MockitoBean`] and
{spring-framework-api}/test/context/bean/override/mockito/MockitoSpyBean.html[`@MockitoSpyBean`]
can be used in test classes to override a bean in the test's `ApplicationContext` with a
Mockito _mock_ or _spy_, respectively. In the latter case, an early instance of the
original bean is captured and wrapped by the spy.
`@MockitoBean` and `@MockitoSpyBean` are used on non-static fields in test classes to
override beans in the test's `ApplicationContext` with a Mockito _mock_ or _spy_,
respectively. In the latter case, an early instance of the original bean is captured and
wrapped by the spy.
The annotations can be applied in the following ways.
* On a non-static field in a test class or any of its superclasses.
* On a non-static field in an enclosing class for a `@Nested` test class or in any class
in the type hierarchy or enclosing class hierarchy above the `@Nested` test class.
* At the type level on a test class or any superclass or implemented interface in the
type hierarchy above the test class.
* At the type level on an enclosing class for a `@Nested` test class or on any class or
interface in the type hierarchy or enclosing class hierarchy above the `@Nested` test
class.
When `@MockitoBean` or `@MockitoSpyBean` is declared on a field, the bean to mock or spy
is inferred from the type of the annotated field. If multiple candidates exist in the
`ApplicationContext`, a `@Qualifier` annotation can be declared on the field to help
disambiguate. In the absence of a `@Qualifier` annotation, the name of the annotated
field will be used as a _fallback qualifier_. Alternatively, you can explicitly specify a
bean name to mock or spy by setting the `value` or `name` attribute in the annotation.
When `@MockitoBean` or `@MockitoSpyBean` is declared at the type level, the type of bean
(or beans) to mock or spy must be supplied via the `types` attribute in the annotation
for example, `@MockitoBean(types = {OrderService.class, UserService.class})`. If multiple
candidates exist in the `ApplicationContext`, you can explicitly specify a bean name to
mock or spy by setting the `name` attribute. Note, however, that the `types` attribute
must contain a single type if an explicit bean `name` is configured for example,
`@MockitoBean(name = "ps1", types = PrintingService.class)`.
To support reuse of mock configuration, `@MockitoBean` and `@MockitoSpyBean` may be used
as meta-annotations to create custom _composed annotations_ for example, to define
common mock or spy configuration in a single annotation that can be reused across a test
suite. `@MockitoBean` and `@MockitoSpyBean` can also be used as repeatable annotations at
the type level — for example, to mock or spy several beans by name.
By default, the annotated field's type is used to search for candidate beans to override.
If multiple candidates match, `@Qualifier` can be provided to narrow the candidate to
override. Alternatively, a candidate whose bean name matches the name of the field will
match.
[WARNING]
====
Qualifiers, including the name of a field, are used to determine if a separate
Qualifiers, including the name of the field, are used to determine if a separate
`ApplicationContext` needs to be created. If you are using this feature to mock or spy
the same bean in several test classes, make sure to name the fields consistently to avoid
the same bean in several test classes, make sure to name the field consistently to avoid
creating unnecessary contexts.
====
Each annotation also defines Mockito-specific attributes to fine-tune the mocking behavior.
The `@MockitoBean` annotation uses the `REPLACE_OR_CREATE`
xref:testing/testcontext-framework/bean-overriding.adoc#testcontext-bean-overriding-strategy[strategy for bean overrides].
If a corresponding bean does not exist, a new bean will be created. However, you can
switch to the `REPLACE` strategy by setting the `enforceOverride` attribute to `true`
for example, `@MockitoBean(enforceOverride = true)`.
xref:testing/testcontext-framework/bean-overriding.adoc#testcontext-bean-overriding-custom[strategy for test bean overriding].
If no existing bean matches, a new bean is created on the fly. However, you can switch to
the `REPLACE` strategy by setting the `enforceOverride` attribute to `true`. See the
following section for an example.
The `@MockitoSpyBean` annotation uses the `WRAP`
xref:testing/testcontext-framework/bean-overriding.adoc#testcontext-bean-overriding-strategy[strategy],
xref:testing/testcontext-framework/bean-overriding.adoc#testcontext-bean-overriding-custom[strategy],
and the original instance is wrapped in a Mockito spy. This strategy requires that
exactly one candidate bean exists.
@@ -84,8 +56,15 @@ or `private` depending on the needs or coding practices of the project.
[[spring-testing-annotation-beanoverriding-mockitobean-examples]]
== `@MockitoBean` Examples
The following example shows how to use the default behavior of the `@MockitoBean`
annotation.
When using `@MockitoBean`, a new bean will be created if a corresponding bean does not
exist. However, if you would like for the test to fail when a corresponding bean does not
exist, you can set the `enforceOverride` attribute to `true` for example,
`@MockitoBean(enforceOverride = true)`.
To use a by-name override rather than a by-type override, specify the `name` (or `value`)
attribute of the annotation.
The following example shows how to use the default behavior of the `@MockitoBean` annotation:
[tabs]
======
@@ -102,7 +81,7 @@ Java::
// tests...
}
----
<1> Replace the bean with type `CustomService` with a Mockito mock.
<1> Replace the bean with type `CustomService` with a Mockito `mock`.
======
In the example above, we are creating a mock for `CustomService`. If more than one bean
@@ -111,8 +90,7 @@ will fail, and you will need to provide a qualifier of some sort to identify whi
`CustomService` beans you want to override. If no such bean exists, a bean will be
created with an auto-generated bean name.
The following example uses a by-name lookup, rather than a by-type lookup. If no bean
named `service` exists, one is created.
The following example uses a by-name lookup, rather than a by-type lookup:
[tabs]
======
@@ -130,9 +108,32 @@ Java::
}
----
<1> Replace the bean named `service` with a Mockito mock.
<1> Replace the bean named `service` with a Mockito `mock`.
======
If no bean named `service` exists, one is created.
`@MockitoBean` can also be used at the type level:
- on a test class or any superclass or implemented interface in the type hierarchy above
the test class
- on an enclosing class for a `@Nested` test class or on any class or interface in the
type hierarchy or enclosing class hierarchy above the `@Nested` test class
When `@MockitoBean` is declared at the type level, the type of bean (or beans) to mock
must be supplied via the `types` attribute for example,
`@MockitoBean(types = {OrderService.class, UserService.class})`. If multiple candidates
exist in the application context, you can explicitly specify a bean name to mock by
setting the `name` attribute. Note, however, that the `types` attribute must contain a
single type if an explicit bean `name` is configured for example,
`@MockitoBean(name = "ps1", types = PrintingService.class)`.
To support reuse of mock configuration, `@MockitoBean` may be used as a meta-annotation
to create custom _composed annotations_ — for example, to define common mock
configuration in a single annotation that can be reused across a test suite.
`@MockitoBean` can also be used as a repeatable annotation at the type level — for
example, to mock several beans by name.
The following `@SharedMocks` annotation registers two mocks by-type and one mock by-name.
[tabs]
@@ -190,7 +191,7 @@ APIs.
== `@MockitoSpyBean` Examples
The following example shows how to use the default behavior of the `@MockitoSpyBean`
annotation.
annotation:
[tabs]
======
@@ -207,7 +208,7 @@ Java::
// tests...
}
----
<1> Wrap the bean with type `CustomService` with a Mockito spy.
<1> Wrap the bean with type `CustomService` with a Mockito `spy`.
======
In the example above, we are wrapping the bean with type `CustomService`. If more than
@@ -215,7 +216,7 @@ one bean of that type exists, the bean named `customService` is considered. Othe
the test will fail, and you will need to provide a qualifier of some sort to identify
which of the `CustomService` beans you want to spy.
The following example uses a by-name lookup, rather than a by-type lookup.
The following example uses a by-name lookup, rather than a by-type lookup:
[tabs]
======
@@ -232,58 +233,5 @@ Java::
// tests...
}
----
<1> Wrap the bean named `service` with a Mockito spy.
<1> Wrap the bean named `service` with a Mockito `spy`.
======
The following `@SharedSpies` annotation registers two spies by-type and one spy by-name.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@MockitoSpyBean(types = {OrderService.class, UserService.class}) // <1>
@MockitoSpyBean(name = "ps1", types = PrintingService.class) // <2>
public @interface SharedSpies {
}
----
<1> Register `OrderService` and `UserService` spies by-type.
<2> Register `PrintingService` spy by-name.
======
The following demonstrates how `@SharedSpies` can be used on a test class.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
@SpringJUnitConfig(TestConfig.class)
@SharedSpies // <1>
class BeanOverrideTests {
@Autowired OrderService orderService; // <2>
@Autowired UserService userService; // <2>
@Autowired PrintingService ps1; // <2>
// Inject other components that rely on the spies.
@Test
void testThatDependsOnMocks() {
// ...
}
}
----
<1> Register common spies via the custom `@SharedSpies` annotation.
<2> Optionally inject spies to _stub_ or _verify_ them.
======
TIP: The spies can also be injected into `@Configuration` classes or other test-related
components in the `ApplicationContext` in order to configure them with Mockito's stubbing
APIs.
@@ -1,9 +1,8 @@
[[spring-testing-annotation-beanoverriding-testbean]]
= `@TestBean`
{spring-framework-api}/test/context/bean/override/convention/TestBean.html[`@TestBean`]
is used on a non-static field in a test class to override a specific bean in the test's
`ApplicationContext` with an instance provided by a factory method.
`@TestBean` is used on a non-static field in a test class to override a specific bean in
the test's `ApplicationContext` with an instance provided by a factory method.
The associated factory method name is derived from the annotated field's name, or the
bean name if specified. The factory method must be `static`, accept no arguments, and
@@ -2,8 +2,8 @@
= Bean Overriding in Tests
Bean overriding in tests refers to the ability to override specific beans in the
`ApplicationContext` for a test class, by annotating the test class or one or more
non-static fields in the test class.
`ApplicationContext` for a test class, by annotating one or more non-static fields in the
test class.
NOTE: This feature is intended as a less risky alternative to the practice of registering
a bean via `@Bean` with the `DefaultListableBeanFactory`
@@ -42,16 +42,15 @@ The `spring-test` module registers implementations of the latter two
{spring-framework-code}/spring-test/src/main/resources/META-INF/spring.factories[`META-INF/spring.factories`
properties file].
The bean overriding infrastructure searches for annotations on test classes as well as
annotations on non-static fields in test classes that are meta-annotated with
`@BeanOverride` and instantiates the corresponding `BeanOverrideProcessor` which is
responsible for creating an appropriate `BeanOverrideHandler`.
The bean overriding infrastructure searches in test classes for any non-static field that
is meta-annotated with `@BeanOverride` and instantiates the corresponding
`BeanOverrideProcessor` which is responsible for creating an appropriate
`BeanOverrideHandler`.
The internal `BeanOverrideBeanFactoryPostProcessor` then uses bean override handlers to
alter the test's `ApplicationContext` by creating, replacing, or wrapping beans as
defined by the corresponding `BeanOverrideStrategy`:
[[testcontext-bean-overriding-strategy]]
`REPLACE`::
Replaces the bean. Throws an exception if a corresponding bean does not exist.
`REPLACE_OR_CREATE`::
@@ -7,17 +7,16 @@ by default, exactly in the following order:
* `ServletTestExecutionListener`: Configures Servlet API mocks for a
`WebApplicationContext`.
* `DirtiesContextBeforeModesTestExecutionListener`: Handles the `@DirtiesContext`
annotation for "before" modes.
annotation for "`before`" modes.
* `ApplicationEventsTestExecutionListener`: Provides support for
xref:testing/testcontext-framework/application-events.adoc[`ApplicationEvents`].
* `BeanOverrideTestExecutionListener`: Provides support for
xref:testing/testcontext-framework/bean-overriding.adoc[].
* `BeanOverrideTestExecutionListener`: Provides support for xref:testing/testcontext-framework/bean-overriding.adoc[] .
* `DependencyInjectionTestExecutionListener`: Provides dependency injection for the test
instance.
* `MicrometerObservationRegistryTestExecutionListener`: Provides support for
Micrometer's `ObservationRegistry`.
* `DirtiesContextTestExecutionListener`: Handles the `@DirtiesContext` annotation for
"after" modes.
"`after`" modes.
* `CommonCachesTestExecutionListener`: Clears resource caches in the test's
`ApplicationContext` if necessary.
* `TransactionalTestExecutionListener`: Provides transactional test execution with
@@ -162,16 +161,15 @@ change from release to release -- for example, `SqlScriptsTestExecutionListener`
introduced in Spring Framework 4.1, and `DirtiesContextBeforeModesTestExecutionListener`
was introduced in Spring Framework 4.2. Furthermore, third-party frameworks like Spring
Boot and Spring Security register their own default `TestExecutionListener`
implementations by using the aforementioned
xref:testing/testcontext-framework/tel-config.adoc#testcontext-tel-config-automatic-discovery[automatic discovery mechanism].
implementations by using the aforementioned xref:testing/testcontext-framework/tel-config.adoc#testcontext-tel-config-automatic-discovery[automatic discovery mechanism]
.
To avoid having to be aware of and re-declare all default listeners, you can set the
`mergeMode` attribute of `@TestExecutionListeners` to `MergeMode.MERGE_WITH_DEFAULTS`.
`MERGE_WITH_DEFAULTS` indicates that locally declared listeners should be merged with the
default listeners. The merging algorithm ensures that duplicates are removed from the
list and that the resulting set of merged listeners is sorted according to the semantics
of `AnnotationAwareOrderComparator`, as described in
xref:testing/testcontext-framework/tel-config.adoc#testcontext-tel-config-ordering[Ordering `TestExecutionListener` Implementations].
of `AnnotationAwareOrderComparator`, as described in xref:testing/testcontext-framework/tel-config.adoc#testcontext-tel-config-ordering[Ordering `TestExecutionListener` Implementations].
If a listener implements `Ordered` or is annotated with `@Order`, it can influence the
position in which it is merged with the defaults. Otherwise, locally declared listeners
are appended to the list of default listeners when merged.
@@ -47,7 +47,8 @@ out-of-container tests for code that depends on environment-specific properties.
The `org.springframework.mock.web` package contains a comprehensive set of Servlet API
mock objects that are useful for testing web contexts, controllers, and filters. These
mock objects are targeted at usage with Spring's Web MVC framework and are generally more
convenient to use than dynamic mock objects (such as https://easymock.org/[EasyMock]).
convenient to use than dynamic mock objects (such as https://easymock.org/[EasyMock])
or alternative Servlet API mock objects (such as http://www.mockobjects.com[MockObjects]).
TIP: Since Spring Framework 6.0, the mock objects in `org.springframework.mock.web` are
based on the Servlet 6.0 API.
@@ -1,6 +1,5 @@
[[webflux-view]]
= View Technologies
[.small]#xref:web/webmvc-view.adoc[See equivalent in the Servlet stack]#
The rendering of views in Spring WebFlux is pluggable. Whether you decide to
@@ -225,9 +224,9 @@ The following table shows the templating libraries that we have tested on differ
|Scripting Library |Scripting Engine
|https://handlebarsjs.com/[Handlebars] |https://openjdk.java.net/projects/nashorn/[Nashorn]
|https://mustache.github.io/[Mustache] |https://openjdk.java.net/projects/nashorn/[Nashorn]
|https://react.dev/[React] |https://openjdk.java.net/projects/nashorn/[Nashorn]
|https://ejs.co/[EJS] |https://openjdk.java.net/projects/nashorn/[Nashorn]
|https://docs.ruby-lang.org/en/master/ERB.html[ERB] |https://www.jruby.org[JRuby]
|https://facebook.github.io/react/[React] |https://openjdk.java.net/projects/nashorn/[Nashorn]
|https://www.embeddedjs.com/[EJS] |https://openjdk.java.net/projects/nashorn/[Nashorn]
|https://www.stuartellis.name/articles/erb/[ERB] |https://www.jruby.org[JRuby]
|https://docs.python.org/2/library/string.html#template-strings[String templates] |https://www.jython.org/[Jython]
|https://github.com/sdeleuze/kotlin-script-templating[Kotlin Script templating] |{kotlin-site}[Kotlin]
|===
@@ -57,7 +57,7 @@ locale and language specific resource bundles.
For further custom handling of method validation errors, you can extend
`ResponseEntityExceptionHandler` or use an `@ExceptionHandler` method in a controller
or in a `@ControllerAdvice`, and handle `HandlerMethodValidationException` directly.
The exception contains a list of ``ParameterValidationResult``s that group validation errors
The exception contains a list of``ParameterValidationResult``s that group validation errors
by method parameter. You can either iterate over those, or provide a visitor with callback
methods by controller method parameter type:
@@ -1,7 +1,6 @@
[[mvc-view]]
= View Technologies
:page-section-summary-toc: 1
[.small]#xref:web/webflux-view.adoc[See equivalent in the Reactive stack]#
The rendering of views in Spring MVC is pluggable. Whether you decide to use
@@ -130,7 +130,6 @@ the `Configuration` object.
[[mvc-view-freemarker-forms]]
== Form Handling
[.small]#xref:web/webflux-view.adoc#webflux-view-freemarker-forms[See equivalent in the Reactive stack]#
Spring provides a tag library for use in JSPs that contains, among others, a
`<spring:bind/>` element. This element primarily lets forms display values from
@@ -13,9 +13,9 @@ templating libraries on different script engines:
|Scripting Library |Scripting Engine
|https://handlebarsjs.com/[Handlebars] |https://openjdk.java.net/projects/nashorn/[Nashorn]
|https://mustache.github.io/[Mustache] |https://openjdk.java.net/projects/nashorn/[Nashorn]
|https://react.dev/[React] |https://openjdk.java.net/projects/nashorn/[Nashorn]
|https://ejs.co/[EJS] |https://openjdk.java.net/projects/nashorn/[Nashorn]
|https://docs.ruby-lang.org/en/master/ERB.html[ERB] |https://www.jruby.org[JRuby]
|https://facebook.github.io/react/[React] |https://openjdk.java.net/projects/nashorn/[Nashorn]
|https://www.embeddedjs.com/[EJS] |https://openjdk.java.net/projects/nashorn/[Nashorn]
|https://www.stuartellis.name/articles/erb/[ERB] |https://www.jruby.org[JRuby]
|https://docs.python.org/2/library/string.html#template-strings[String templates] |https://www.jython.org/[Jython]
|https://github.com/sdeleuze/kotlin-script-templating[Kotlin Script templating] |{kotlin-site}[Kotlin]
|===
@@ -47,7 +47,7 @@ through https://www.webjars.org/[WebJars].
[[mvc-view-script-integrate]]
== Script Templates
[.small]#xref:web/webflux-view.adoc#webflux-view-script-integrate[See equivalent in the Reactive stack]#
[.small]#xref:web/webflux-view.adoc#webflux-view-script[See equivalent in the Reactive stack]#
You can declare a `ScriptTemplateConfigurer` bean to specify the script engine to use,
the script files to load, what function to call to render templates, and so on.
@@ -37,7 +37,7 @@ already set.
"application/problem+json" over "application/json" when rendering a `ProblemDetail`,
and also falls back on it if no compatible media type is found.
To enable RFC 9457 responses for Spring MVC exceptions and for any
To enable RFC 9457 responses for Spring WebFlux exceptions and for any
`ErrorResponseException`, extend `ResponseEntityExceptionHandler` and declare it as an
xref:web/webmvc/mvc-controller/ann-advice.adoc[@ControllerAdvice] in Spring configuration. The handler
has an `@ExceptionHandler` method that handles any `ErrorResponse` exception, which
@@ -57,7 +57,7 @@ locale and language specific resource bundles.
For further custom handling of method validation errors, you can extend
`ResponseEntityExceptionHandler` or use an `@ExceptionHandler` method in a controller
or in a `@ControllerAdvice`, and handle `HandlerMethodValidationException` directly.
The exception contains a list of ``ParameterValidationResult``s that group validation errors
The exception contains a list of``ParameterValidationResult``s that group validation errors
by method parameter. You can either iterate over those, or provide a visitor with callback
methods by controller method parameter type:
@@ -13,7 +13,7 @@ reference documentation, including:
* {docs-spring-security}/features/exploits/csrf.html#csrf-protection[CSRF protection]
* {docs-spring-security}/features/exploits/headers.html[Security Response Headers]
https://github.com/hdiv/hdiv[HDIV] is another web security framework that integrates with Spring MVC.
https://hdiv.org/[HDIV] is another web security framework that integrates with Spring MVC.
@@ -19,6 +19,6 @@ from where they are handled according to their destination prefix. As the channe
a `ThreadPoolExecutor`, messages are processed in different threads, and the resulting sequence
of handling may not match the exact order in which they were received.
To enable ordered receiving, set the `setPreserveReceiveOrder` flag as follows:
To enable ordered publishing, set the `setPreserveReceiveOrder` flag as follows:
include-code::./ReceiveOrderWebSocketConfiguration[tag=snippet,indent=0]
-1
View File
@@ -5,7 +5,6 @@
"@antora/collector-extension": "1.0.0-alpha.3",
"@asciidoctor/tabs": "1.0.0-beta.6",
"@springio/antora-extensions": "1.14.2",
"fast-xml-parser": "4.5.2",
"@springio/asciidoctor-extensions": "1.0.0-alpha.10"
}
}
@@ -1,105 +0,0 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.docs.integration.resthttpinterface.customresolver;
import java.util.List;
import org.springframework.core.MethodParameter;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.support.RestClientAdapter;
import org.springframework.web.service.annotation.GetExchange;
import org.springframework.web.service.invoker.HttpRequestValues;
import org.springframework.web.service.invoker.HttpServiceArgumentResolver;
import org.springframework.web.service.invoker.HttpServiceProxyFactory;
public class CustomHttpServiceArgumentResolver {
// tag::httpinterface[]
public interface RepositoryService {
@GetExchange("/repos/search")
List<Repository> searchRepository(Search search);
}
// end::httpinterface[]
class Sample {
void sample() {
// tag::usage[]
RestClient restClient = RestClient.builder().baseUrl("https://api.github.com/").build();
RestClientAdapter adapter = RestClientAdapter.create(restClient);
HttpServiceProxyFactory factory = HttpServiceProxyFactory
.builderFor(adapter)
.customArgumentResolver(new SearchQueryArgumentResolver())
.build();
RepositoryService repositoryService = factory.createClient(RepositoryService.class);
Search search = Search.create()
.owner("spring-projects")
.language("java")
.query("rest")
.build();
List<Repository> repositories = repositoryService.searchRepository(search);
// end::usage[]
}
}
// tag::argumentresolver[]
static class SearchQueryArgumentResolver implements HttpServiceArgumentResolver {
@Override
public boolean resolve(Object argument, MethodParameter parameter, HttpRequestValues.Builder requestValues) {
if (parameter.getParameterType().equals(Search.class)) {
Search search = (Search) argument;
requestValues.addRequestParameter("owner", search.owner());
requestValues.addRequestParameter("language", search.language());
requestValues.addRequestParameter("query", search.query());
return true;
}
return false;
}
}
// end::argumentresolver[]
record Search (String query, String owner, String language) {
static Builder create() {
return new Builder();
}
static class Builder {
Builder query(String query) { return this;}
Builder owner(String owner) { return this;}
Builder language(String language) { return this;}
Search build() {
return new Search(null, null, null);
}
}
}
record Repository(String name) {
}
}
@@ -1,78 +0,0 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.docs.integration.resthttpinterface.customresolver
import org.springframework.core.MethodParameter
import org.springframework.web.client.RestClient
import org.springframework.web.client.support.RestClientAdapter
import org.springframework.web.service.annotation.GetExchange
import org.springframework.web.service.invoker.HttpRequestValues
import org.springframework.web.service.invoker.HttpServiceArgumentResolver
import org.springframework.web.service.invoker.HttpServiceProxyFactory
class CustomHttpServiceArgumentResolver {
// tag::httpinterface[]
interface RepositoryService {
@GetExchange("/repos/search")
fun searchRepository(search: Search): List<Repository>
}
// end::httpinterface[]
class Sample {
fun sample() {
// tag::usage[]
val restClient = RestClient.builder().baseUrl("https://api.github.com/").build()
val adapter = RestClientAdapter.create(restClient)
val factory = HttpServiceProxyFactory
.builderFor(adapter)
.customArgumentResolver(SearchQueryArgumentResolver())
.build()
val repositoryService = factory.createClient<RepositoryService>(RepositoryService::class.java)
val search = Search(owner = "spring-projects", language = "java", query = "rest")
val repositories = repositoryService.searchRepository(search)
// end::usage[]
repositories.size
}
}
// tag::argumentresolver[]
class SearchQueryArgumentResolver : HttpServiceArgumentResolver {
override fun resolve(
argument: Any?,
parameter: MethodParameter,
requestValues: HttpRequestValues.Builder
): Boolean {
if (parameter.getParameterType() == Search::class.java) {
val search = argument as Search
requestValues.addRequestParameter("owner", search.owner)
.addRequestParameter("language", search.language)
.addRequestParameter("query", search.query)
return true
}
return false
}
}
// end::argumentresolver[]
data class Search(val query: String, val owner: String, val language: String)
data class Repository(val name: String)
}
+31 -31
View File
@@ -7,31 +7,31 @@ javaPlatform {
}
dependencies {
api(platform("com.fasterxml.jackson:jackson-bom:2.18.3"))
api(platform("io.micrometer:micrometer-bom:1.14.5"))
api(platform("io.netty:netty-bom:4.1.119.Final"))
api(platform("com.fasterxml.jackson:jackson-bom:2.18.2"))
api(platform("io.micrometer:micrometer-bom:1.14.3"))
api(platform("io.netty:netty-bom:4.1.117.Final"))
api(platform("io.netty:netty5-bom:5.0.0.Alpha5"))
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.26"))
api(platform("io.projectreactor:reactor-bom:2024.0.2"))
api(platform("io.rsocket:rsocket-bom:1.1.4"))
api(platform("org.apache.groovy:groovy-bom:4.0.24"))
api(platform("org.apache.logging.log4j:log4j-bom:2.21.1"))
api(platform("org.assertj:assertj-bom:3.27.3"))
api(platform("org.eclipse.jetty:jetty-bom:12.0.17"))
api(platform("org.eclipse.jetty.ee10:jetty-ee10-bom:12.0.17"))
api(platform("org.assertj:assertj-bom:3.27.2"))
api(platform("org.eclipse.jetty:jetty-bom:12.0.16"))
api(platform("org.eclipse.jetty.ee10:jetty-ee10-bom:12.0.16"))
api(platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.8.1"))
api(platform("org.jetbrains.kotlinx:kotlinx-serialization-bom:1.6.3"))
api(platform("org.junit:junit-bom:5.12.1"))
api(platform("org.mockito:mockito-bom:5.16.1"))
api(platform("org.junit:junit-bom:5.11.4"))
api(platform("org.mockito:mockito-bom:5.15.2"))
constraints {
api("com.fasterxml:aalto-xml:1.3.2")
api("com.fasterxml.woodstox:woodstox-core:6.7.0")
api("com.github.ben-manes.caffeine:caffeine:3.2.0")
api("com.github.ben-manes.caffeine:caffeine:3.1.8")
api("com.github.librepdf:openpdf:1.3.43")
api("com.google.code.findbugs:findbugs:3.0.1")
api("com.google.code.findbugs:jsr305:3.0.2")
api("com.google.code.gson:gson:2.12.1")
api("com.google.protobuf:protobuf-java-util:4.30.0")
api("com.google.code.gson:gson:2.11.0")
api("com.google.protobuf:protobuf-java-util:4.29.3")
api("com.h2database:h2:2.3.232")
api("com.jayway.jsonpath:json-path:2.9.0")
api("com.oracle.database.jdbc:ojdbc11:21.9.0.0")
@@ -43,7 +43,7 @@ dependencies {
api("com.sun.xml.bind:jaxb-core:3.0.2")
api("com.sun.xml.bind:jaxb-impl:3.0.2")
api("com.sun.xml.bind:jaxb-xjc:3.0.2")
api("com.thoughtworks.qdox:qdox:2.2.0")
api("com.thoughtworks.qdox:qdox:2.1.0")
api("com.thoughtworks.xstream:xstream:1.4.21")
api("commons-io:commons-io:2.15.0")
api("de.bechte.junit:junit-hierarchicalcontextrunner:4.12.2")
@@ -100,8 +100,8 @@ dependencies {
api("org.apache.derby:derby:10.16.1.1")
api("org.apache.derby:derbyclient:10.16.1.1")
api("org.apache.derby:derbytools:10.16.1.1")
api("org.apache.httpcomponents.client5:httpclient5:5.4.2")
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.3.3")
api("org.apache.httpcomponents.client5:httpclient5:5.4.1")
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.3.1")
api("org.apache.poi:poi-ooxml:5.2.5")
api("org.apache.tomcat.embed:tomcat-embed-core:10.1.28")
api("org.apache.tomcat.embed:tomcat-embed-websocket:10.1.28")
@@ -110,18 +110,18 @@ dependencies {
api("org.aspectj:aspectjrt:1.9.22.1")
api("org.aspectj:aspectjtools:1.9.22.1")
api("org.aspectj:aspectjweaver:1.9.22.1")
api("org.awaitility:awaitility:4.3.0")
api("org.awaitility:awaitility:4.2.2")
api("org.bouncycastle:bcpkix-jdk18on:1.72")
api("org.codehaus.jettison:jettison:1.5.4")
api("org.crac:crac:1.4.0")
api("org.dom4j:dom4j:2.1.4")
api("org.easymock:easymock:5.5.0")
api("org.eclipse.jetty:jetty-reactive-httpclient:4.0.9")
api("org.easymock:easymock:5.4.0")
api("org.eclipse.jetty:jetty-reactive-httpclient:4.0.8")
api("org.eclipse.persistence:org.eclipse.persistence.jpa:3.0.4")
api("org.eclipse:yasson:2.0.4")
api("org.ehcache:ehcache:3.10.8")
api("org.ehcache:jcache:1.0.1")
api("org.freemarker:freemarker:2.3.34")
api("org.freemarker:freemarker:2.3.33")
api("org.glassfish.external:opendmk_jmxremote_optional_jar:1.0-b01-ea")
api("org.glassfish:jakarta.el:4.0.2")
api("org.glassfish.tyrus:tyrus-container-servlet:2.1.3")
@@ -130,24 +130,24 @@ dependencies {
api("org.hibernate:hibernate-core-jakarta:5.6.15.Final")
api("org.hibernate:hibernate-validator:7.0.5.Final")
api("org.hsqldb:hsqldb:2.7.4")
api("org.htmlunit:htmlunit:4.10.0")
api("org.htmlunit:htmlunit:4.6.0")
api("org.javamoney:moneta:1.4.4")
api("org.jruby:jruby:9.4.12.0")
api("org.jruby:jruby:9.4.9.0")
api("org.junit.support:testng-engine:1.0.5")
api("org.mozilla:rhino:1.7.15")
api("org.ogce:xpp3:1.1.6")
api("org.python:jython-standalone:2.7.4")
api("org.python:jython-standalone:2.7.3")
api("org.quartz-scheduler:quartz:2.3.2")
api("org.seleniumhq.selenium:htmlunit3-driver:4.29.0")
api("org.seleniumhq.selenium:selenium-java:4.29.0")
api("org.seleniumhq.selenium:htmlunit3-driver:4.26.0")
api("org.seleniumhq.selenium:selenium-java:4.26.0")
api("org.skyscreamer:jsonassert:1.5.3")
api("org.slf4j:slf4j-api:2.0.17")
api("org.testng:testng:7.11.0")
api("org.slf4j:slf4j-api:2.0.16")
api("org.testng:testng:7.10.2")
api("org.webjars:underscorejs:1.8.3")
api("org.webjars:webjars-locator-core:0.59")
api("org.webjars:webjars-locator-lite:1.1.0")
api("org.webjars:webjars-locator-core:0.55")
api("org.webjars:webjars-locator-lite:1.0.0")
api("org.xmlunit:xmlunit-assertj:2.10.0")
api("org.xmlunit:xmlunit-matchers:2.10.0")
api("org.yaml:snakeyaml:2.4")
api("org.yaml:snakeyaml:2.3")
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
version=6.2.5
version=6.2.2
org.gradle.caching=true
org.gradle.jvmargs=-Xmx2048m
Binary file not shown.
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Vendored
+1 -1
View File
@@ -205,7 +205,7 @@ fi
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
+1 -1
View File
@@ -1,5 +1,5 @@
plugins {
id "com.gradle.develocity" version "3.19"
id "com.gradle.develocity" version "3.17.2"
id "io.spring.ge.conventions" version "0.0.17"
id "org.gradle.toolchains.foojay-resolver-convention" version "0.7.0"
}
@@ -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.
@@ -276,18 +276,14 @@ public abstract class AbstractAspectJAdvice implements Advice, AspectJPrecedence
}
if (this.aspectJAdviceMethod.getParameterCount() == this.argumentNames.length + 1) {
// May need to add implicit join point arg name...
for (int i = 0; i < this.aspectJAdviceMethod.getParameterCount(); i++) {
Class<?> argType = this.aspectJAdviceMethod.getParameterTypes()[i];
if (argType == JoinPoint.class ||
argType == ProceedingJoinPoint.class ||
argType == JoinPoint.StaticPart.class) {
String[] oldNames = this.argumentNames;
this.argumentNames = new String[oldNames.length + 1];
System.arraycopy(oldNames, 0, this.argumentNames, 0, i);
this.argumentNames[i] = "THIS_JOIN_POINT";
System.arraycopy(oldNames, i, this.argumentNames, i + 1, oldNames.length - i);
break;
}
Class<?> firstArgType = this.aspectJAdviceMethod.getParameterTypes()[0];
if (firstArgType == JoinPoint.class ||
firstArgType == ProceedingJoinPoint.class ||
firstArgType == JoinPoint.StaticPart.class) {
String[] oldNames = this.argumentNames;
this.argumentNames = new String[oldNames.length + 1];
this.argumentNames[0] = "THIS_JOIN_POINT";
System.arraycopy(oldNames, 0, this.argumentNames, 1, oldNames.length);
}
}
}
@@ -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.
@@ -241,7 +241,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
try {
int algorithmicStep = STEP_JOIN_POINT_BINDING;
while (this.numberOfRemainingUnboundArguments > 0 && algorithmicStep < STEP_FINISHED) {
while ((this.numberOfRemainingUnboundArguments > 0) && algorithmicStep < STEP_FINISHED) {
switch (algorithmicStep++) {
case STEP_JOIN_POINT_BINDING -> {
if (!maybeBindThisJoinPoint()) {
@@ -373,8 +373,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
if (this.returningName != null) {
if (this.numberOfRemainingUnboundArguments > 1) {
throw new AmbiguousBindingException("Binding of returning parameter '" + this.returningName +
"' is ambiguous: there are " + this.numberOfRemainingUnboundArguments + " candidates. " +
"Consider compiling with -parameters in order to make declared parameter names available.");
"' is ambiguous: there are " + this.numberOfRemainingUnboundArguments + " candidates.");
}
// We're all set... find the unbound parameter, and bind it.
@@ -486,8 +485,8 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
*/
private void maybeBindThisOrTargetOrArgsFromPointcutExpression() {
if (this.numberOfRemainingUnboundArguments > 1) {
throw new AmbiguousBindingException("Still " + this.numberOfRemainingUnboundArguments +
" unbound args at this()/target()/args() binding stage, with no way to determine between them");
throw new AmbiguousBindingException("Still " + this.numberOfRemainingUnboundArguments
+ " unbound args at this()/target()/args() binding stage, with no way to determine between them");
}
List<String> varNames = new ArrayList<>();
@@ -536,8 +535,8 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
private void maybeBindReferencePointcutParameter() {
if (this.numberOfRemainingUnboundArguments > 1) {
throw new AmbiguousBindingException("Still " + this.numberOfRemainingUnboundArguments +
" unbound args at reference pointcut binding stage, with no way to determine between them");
throw new AmbiguousBindingException("Still " + this.numberOfRemainingUnboundArguments
+ " unbound args at reference pointcut binding stage, with no way to determine between them");
}
List<String> varNames = new ArrayList<>();
@@ -742,9 +741,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
* Simple record to hold the extracted text from a pointcut body, together
* with the number of tokens consumed in extracting it.
*/
private record PointcutBody(int numTokensConsumed, @Nullable String text) {
}
private record PointcutBody(int numTokensConsumed, @Nullable String text) {}
/**
* Thrown in response to an ambiguous binding being detected when
@@ -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.
@@ -46,8 +46,8 @@ public class AspectEntry implements ParseState.Entry {
@Override
public String toString() {
return "Aspect: " + (StringUtils.hasLength(this.id) ? "id='" + this.id + "'" :
"ref='" + this.ref + "'");
return "Aspect: " + (StringUtils.hasLength(this.id) ? "id='" + this.id + "'"
: "ref='" + this.ref + "'");
}
}
@@ -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.
@@ -125,7 +125,7 @@ public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport imple
return null;
};
return doSubmit(task, executor, userMethod.getReturnType());
return doSubmit(task, executor, invocation.getMethod().getReturnType());
}
/**
@@ -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.
@@ -198,8 +198,8 @@ public abstract class ClassFilters {
@Override
public boolean equals(Object other) {
return (this == other || (other instanceof NegateClassFilter that &&
this.original.equals(that.original)));
return (this == other || (other instanceof NegateClassFilter that
&& this.original.equals(that.original)));
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -378,8 +378,8 @@ public abstract class MethodMatchers {
@Override
public boolean equals(Object other) {
return (this == other || (other instanceof NegateMethodMatcher that &&
this.original.equals(that.original)));
return (this == other || (other instanceof NegateMethodMatcher that
&& this.original.equals(that.original)));
}
@Override
@@ -1,135 +0,0 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.aop.aspectj;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.function.Consumer;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.assertj.core.api.InstanceOfAssertFactories;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link AbstractAspectJAdvice}.
*
* @author Joshua Chen
* @author Stephane Nicoll
*/
class AbstractAspectJAdviceTests {
@Test
void setArgumentNamesFromStringArray_withoutJoinPointParameter() {
AbstractAspectJAdvice advice = getAspectJAdvice("methodWithNoJoinPoint");
assertThat(advice).satisfies(hasArgumentNames("arg1", "arg2"));
}
@Test
void setArgumentNamesFromStringArray_withJoinPointAsFirstParameter() {
AbstractAspectJAdvice advice = getAspectJAdvice("methodWithJoinPointAsFirstParameter");
assertThat(advice).satisfies(hasArgumentNames("THIS_JOIN_POINT", "arg1", "arg2"));
}
@Test
void setArgumentNamesFromStringArray_withJoinPointAsLastParameter() {
AbstractAspectJAdvice advice = getAspectJAdvice("methodWithJoinPointAsLastParameter");
assertThat(advice).satisfies(hasArgumentNames("arg1", "arg2", "THIS_JOIN_POINT"));
}
@Test
void setArgumentNamesFromStringArray_withJoinPointAsMiddleParameter() {
AbstractAspectJAdvice advice = getAspectJAdvice("methodWithJoinPointAsMiddleParameter");
assertThat(advice).satisfies(hasArgumentNames("arg1", "THIS_JOIN_POINT", "arg2"));
}
@Test
void setArgumentNamesFromStringArray_withProceedingJoinPoint() {
AbstractAspectJAdvice advice = getAspectJAdvice("methodWithProceedingJoinPoint");
assertThat(advice).satisfies(hasArgumentNames("THIS_JOIN_POINT", "arg1", "arg2"));
}
@Test
void setArgumentNamesFromStringArray_withStaticPart() {
AbstractAspectJAdvice advice = getAspectJAdvice("methodWithStaticPart");
assertThat(advice).satisfies(hasArgumentNames("THIS_JOIN_POINT", "arg1", "arg2"));
}
private Consumer<AbstractAspectJAdvice> hasArgumentNames(String... argumentNames) {
return advice -> assertThat(advice).extracting("argumentNames")
.asInstanceOf(InstanceOfAssertFactories.array(String[].class))
.containsExactly(argumentNames);
}
private AbstractAspectJAdvice getAspectJAdvice(final String methodName) {
AbstractAspectJAdvice advice = new TestAspectJAdvice(getMethod(methodName),
mock(AspectJExpressionPointcut.class), mock(AspectInstanceFactory.class));
advice.setArgumentNamesFromStringArray("arg1", "arg2");
return advice;
}
private Method getMethod(final String methodName) {
return Arrays.stream(Sample.class.getDeclaredMethods())
.filter(method -> method.getName().equals(methodName)).findFirst()
.orElseThrow();
}
@SuppressWarnings("serial")
public static class TestAspectJAdvice extends AbstractAspectJAdvice {
public TestAspectJAdvice(Method aspectJAdviceMethod, AspectJExpressionPointcut pointcut,
AspectInstanceFactory aspectInstanceFactory) {
super(aspectJAdviceMethod, pointcut, aspectInstanceFactory);
}
@Override
public boolean isBeforeAdvice() {
return false;
}
@Override
public boolean isAfterAdvice() {
return false;
}
}
@SuppressWarnings("unused")
static class Sample {
void methodWithNoJoinPoint(String arg1, String arg2) {
}
void methodWithJoinPointAsFirstParameter(JoinPoint joinPoint, String arg1, String arg2) {
}
void methodWithJoinPointAsLastParameter(String arg1, String arg2, JoinPoint joinPoint) {
}
void methodWithJoinPointAsMiddleParameter(String arg1, JoinPoint joinPoint, String arg2) {
}
void methodWithProceedingJoinPoint(ProceedingJoinPoint joinPoint, String arg1, String arg2) {
}
void methodWithStaticPart(JoinPoint.StaticPart staticPart, String arg1, String arg2) {
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -203,6 +203,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
itb.getSpouse();
assertThat(maaif.isMaterialized()).isTrue();
assertThat(imapa.getDeclaredPointcut().getMethodMatcher().matches(TestBean.class.getMethod("getAge"), null)).isTrue();
assertThat(itb.getAge()).as("Around advice must apply").isEqualTo(0);
@@ -300,7 +301,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
void bindingWithMultipleArgsDifferentlyOrdered() {
ManyValuedArgs target = new ManyValuedArgs();
ManyValuedArgs mva = createProxy(target, ManyValuedArgs.class,
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new ManyValuedArgs(), "someBean")));
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new ManyValuedArgs(), "someBean")));
String a = "a";
int b = 12;
@@ -319,7 +320,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
NotLockable notLockableTarget = new NotLockable();
assertThat(notLockableTarget).isNotInstanceOf(Lockable.class);
NotLockable notLockable1 = createProxy(notLockableTarget, NotLockable.class,
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new MakeLockable(), "someBean")));
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new MakeLockable(), "someBean")));
assertThat(notLockable1).isInstanceOf(Lockable.class);
Lockable lockable = (Lockable) notLockable1;
assertThat(lockable.locked()).isFalse();
@@ -328,7 +329,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
NotLockable notLockable2Target = new NotLockable();
NotLockable notLockable2 = createProxy(notLockable2Target, NotLockable.class,
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new MakeLockable(), "someBean")));
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new MakeLockable(), "someBean")));
assertThat(notLockable2).isInstanceOf(Lockable.class);
Lockable lockable2 = (Lockable) notLockable2;
assertThat(lockable2.locked()).isFalse();
@@ -342,19 +343,20 @@ abstract class AbstractAspectJAdvisorFactoryTests {
void introductionAdvisorExcludedFromTargetImplementingInterface() {
assertThat(AopUtils.findAdvisorsThatCanApply(
getAdvisorFactory().getAdvisors(
aspectInstanceFactory(new MakeLockable(), "someBean")),
aspectInstanceFactory(new MakeLockable(), "someBean")),
CannotBeUnlocked.class)).isEmpty();
assertThat(AopUtils.findAdvisorsThatCanApply(getAdvisorFactory().getAdvisors(
aspectInstanceFactory(new MakeLockable(),"someBean")), NotLockable.class)).hasSize(2);
aspectInstanceFactory(new MakeLockable(),"someBean")), NotLockable.class)).hasSize(2);
}
@Test
void introductionOnTargetImplementingInterface() {
CannotBeUnlocked target = new CannotBeUnlocked();
Lockable proxy = createProxy(target, CannotBeUnlocked.class,
// Ensure that we exclude
AopUtils.findAdvisorsThatCanApply(
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new MakeLockable(), "someBean")),
CannotBeUnlocked.class));
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new MakeLockable(), "someBean")),
CannotBeUnlocked.class));
assertThat(proxy).isInstanceOf(Lockable.class);
Lockable lockable = proxy;
assertThat(lockable.locked()).as("Already locked").isTrue();
@@ -368,8 +370,8 @@ abstract class AbstractAspectJAdvisorFactoryTests {
ArrayList<Object> target = new ArrayList<>();
List<?> proxy = createProxy(target, List.class,
AopUtils.findAdvisorsThatCanApply(
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new MakeLockable(), "someBean")),
List.class));
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new MakeLockable(), "someBean")),
List.class));
assertThat(proxy).as("Type pattern must have excluded mixin").isNotInstanceOf(Lockable.class);
}
@@ -377,7 +379,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
void introductionBasedOnAnnotationMatch() { // gh-9980
AnnotatedTarget target = new AnnotatedTargetImpl();
List<Advisor> advisors = getAdvisorFactory().getAdvisors(
aspectInstanceFactory(new MakeAnnotatedTypeModifiable(), "someBean"));
aspectInstanceFactory(new MakeAnnotatedTypeModifiable(), "someBean"));
Object proxy = createProxy(target, AnnotatedTarget.class, advisors);
assertThat(proxy).isInstanceOf(Lockable.class);
Lockable lockable = (Lockable) proxy;
@@ -391,9 +393,9 @@ abstract class AbstractAspectJAdvisorFactoryTests {
TestBean target = new TestBean();
List<Advisor> advisors = getAdvisorFactory().getAdvisors(
aspectInstanceFactory(new MakeITestBeanModifiable(), "someBean"));
aspectInstanceFactory(new MakeITestBeanModifiable(), "someBean"));
advisors.addAll(getAdvisorFactory().getAdvisors(
aspectInstanceFactory(new MakeLockable(), "someBean")));
aspectInstanceFactory(new MakeLockable(), "someBean")));
Modifiable modifiable = (Modifiable) createProxy(target, ITestBean.class, advisors);
assertThat(modifiable).isInstanceOf(Modifiable.class);
@@ -424,7 +426,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
TestBean target = new TestBean();
UnsupportedOperationException expectedException = new UnsupportedOperationException();
List<Advisor> advisors = getAdvisorFactory().getAdvisors(
aspectInstanceFactory(new ExceptionThrowingAspect(expectedException), "someBean"));
aspectInstanceFactory(new ExceptionThrowingAspect(expectedException), "someBean"));
assertThat(advisors).as("One advice method was found").hasSize(1);
ITestBean itb = createProxy(target, ITestBean.class, advisors);
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(itb::getAge);
@@ -437,12 +439,12 @@ abstract class AbstractAspectJAdvisorFactoryTests {
TestBean target = new TestBean();
RemoteException expectedException = new RemoteException();
List<Advisor> advisors = getAdvisorFactory().getAdvisors(
aspectInstanceFactory(new ExceptionThrowingAspect(expectedException), "someBean"));
aspectInstanceFactory(new ExceptionThrowingAspect(expectedException), "someBean"));
assertThat(advisors).as("One advice method was found").hasSize(1);
ITestBean itb = createProxy(target, ITestBean.class, advisors);
assertThatExceptionOfType(UndeclaredThrowableException.class)
.isThrownBy(itb::getAge)
.withCause(expectedException);
.isThrownBy(itb::getAge)
.withCause(expectedException);
}
@Test
@@ -450,7 +452,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
TestBean target = new TestBean();
TwoAdviceAspect twoAdviceAspect = new TwoAdviceAspect();
List<Advisor> advisors = getAdvisorFactory().getAdvisors(
aspectInstanceFactory(twoAdviceAspect, "someBean"));
aspectInstanceFactory(twoAdviceAspect, "someBean"));
assertThat(advisors).as("Two advice methods found").hasSize(2);
ITestBean itb = createProxy(target, ITestBean.class, advisors);
itb.setName("");
@@ -464,7 +466,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
void afterAdviceTypes() throws Exception {
InvocationTrackingAspect aspect = new InvocationTrackingAspect();
List<Advisor> advisors = getAdvisorFactory().getAdvisors(
aspectInstanceFactory(aspect, "exceptionHandlingAspect"));
aspectInstanceFactory(aspect, "exceptionHandlingAspect"));
Echo echo = createProxy(new Echo(), Echo.class, advisors);
assertThat(aspect.invocations).isEmpty();
@@ -473,7 +475,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
aspect.invocations.clear();
assertThatExceptionOfType(FileNotFoundException.class)
.isThrownBy(() -> echo.echo(new FileNotFoundException()));
.isThrownBy(() -> echo.echo(new FileNotFoundException()));
assertThat(aspect.invocations).containsExactly("around - start", "before", "after throwing", "after", "around - end");
}
@@ -485,6 +487,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
assertThat(Modifier.isAbstract(aspect.getClass().getSuperclass().getModifiers())).isFalse();
List<Advisor> advisors = getAdvisorFactory().getAdvisors(aspectInstanceFactory(aspect, "incrementingAspect"));
ITestBean proxy = createProxy(new TestBean("Jane", 42), ITestBean.class, advisors);
assertThat(proxy.getAge()).isEqualTo(86); // (42 + 1) * 2
}
@@ -809,20 +812,20 @@ abstract class AbstractAspectJAdvisorFactoryTests {
invocations.add("before");
}
@After("echo()")
void after() {
invocations.add("after");
}
@AfterReturning(pointcut = "this(target) && execution(* echo(*))", returning = "returnValue")
void afterReturning(JoinPoint joinPoint, Echo target, Object returnValue) {
@AfterReturning("echo()")
void afterReturning() {
invocations.add("after returning");
}
@AfterThrowing(pointcut = "this(target) && execution(* echo(*))", throwing = "exception")
void afterThrowing(JoinPoint joinPoint, Echo target, Throwable exception) {
@AfterThrowing("echo()")
void afterThrowing() {
invocations.add("after throwing");
}
@After("echo()")
void after() {
invocations.add("after");
}
}
@@ -964,7 +967,7 @@ abstract class AbstractMakeModifiable {
class MakeITestBeanModifiable extends AbstractMakeModifiable {
@DeclareParents(value = "org.springframework.beans.testfixture.beans.ITestBean+",
defaultImpl = ModifiableImpl.class)
defaultImpl=ModifiableImpl.class)
static MutableModifiable mixin;
}
@@ -1,72 +0,0 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.aop.interceptor;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import org.aopalliance.intercept.MethodInvocation;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.core.task.AsyncTaskExecutor;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link AsyncExecutionInterceptor}.
*
* @author Bao Ngo
* @since 7.0
*/
class AsyncExecutionInterceptorTests {
@Test
@SuppressWarnings("unchecked")
void invokeOnInterfaceWithGeneric() throws Throwable {
AsyncExecutionInterceptor interceptor = spy(new AsyncExecutionInterceptor(null));
FutureRunner impl = new FutureRunner();
MethodInvocation mi = mock();
given(mi.getThis()).willReturn(impl);
given(mi.getMethod()).willReturn(GenericRunner.class.getMethod("run"));
interceptor.invoke(mi);
ArgumentCaptor<Class<?>> classArgumentCaptor = ArgumentCaptor.forClass(Class.class);
verify(interceptor).doSubmit(any(Callable.class), any(AsyncTaskExecutor.class), classArgumentCaptor.capture());
assertThat(classArgumentCaptor.getValue()).isEqualTo(Future.class);
}
interface GenericRunner<O> {
O run();
}
static class FutureRunner implements GenericRunner<Future<Void>> {
@Override
public Future<Void> run() {
return CompletableFuture.runAsync(() -> {
});
}
}
}
@@ -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.
@@ -658,14 +658,6 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA
growCollectionIfNecessary(list, index, indexedPropertyName.toString(), ph, i + 1);
value = list.get(index);
}
else if (value instanceof Map map) {
Class<?> mapKeyType = ph.getResolvableType().getNested(i + 1).asMap().resolveGeneric(0);
// IMPORTANT: Do not pass full property name in here - property editors
// must not kick in for map keys but rather only for map values.
TypeDescriptor typeDescriptor = TypeDescriptor.valueOf(mapKeyType);
Object convertedMapKey = convertIfNecessary(null, null, key, mapKeyType, typeDescriptor);
value = map.get(convertedMapKey);
}
else if (value instanceof Iterable iterable) {
// Apply index to Iterator in case of a Set/Collection/Iterable.
int index = Integer.parseInt(key);
@@ -693,6 +685,14 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA
currIndex + ", accessed using property path '" + propertyName + "'");
}
}
else if (value instanceof Map map) {
Class<?> mapKeyType = ph.getResolvableType().getNested(i + 1).asMap().resolveGeneric(0);
// IMPORTANT: Do not pass full property name in here - property editors
// must not kick in for map keys but rather only for map values.
TypeDescriptor typeDescriptor = TypeDescriptor.valueOf(mapKeyType);
Object convertedMapKey = convertIfNecessary(null, null, key, mapKeyType, typeDescriptor);
value = map.get(convertedMapKey);
}
else {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Property referenced in indexed property path '" + propertyName +
@@ -904,7 +904,16 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA
private Object newValue(Class<?> type, @Nullable TypeDescriptor desc, String name) {
try {
if (type.isArray()) {
return createArray(type);
Class<?> componentType = type.componentType();
// TODO - only handles 2-dimensional arrays
if (componentType.isArray()) {
Object array = Array.newInstance(componentType, 1);
Array.set(array, 0, Array.newInstance(componentType.componentType(), 0));
return array;
}
else {
return Array.newInstance(componentType, 0);
}
}
else if (Collection.class.isAssignableFrom(type)) {
TypeDescriptor elementDesc = (desc != null ? desc.getElementTypeDescriptor() : null);
@@ -928,24 +937,6 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA
}
}
/**
* Create the array for the given array type.
* @param arrayType the desired type of the target array
* @return a new array instance
*/
private static Object createArray(Class<?> arrayType) {
Assert.notNull(arrayType, "Array type must not be null");
Class<?> componentType = arrayType.componentType();
if (componentType.isArray()) {
Object array = Array.newInstance(componentType, 1);
Array.set(array, 0, createArray(componentType));
return array;
}
else {
return Array.newInstance(componentType, 0);
}
}
/**
* Parse the given property name into the corresponding property name tokens.
* @param propertyName the property name to parse
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -45,18 +45,4 @@ public interface PropertyEditorRegistrar {
*/
void registerCustomEditors(PropertyEditorRegistry registry);
/**
* Indicate whether this registrar exclusively overrides default editors
* rather than registering custom editors, intended to be applied lazily.
* <p>This has an impact on registrar handling in a bean factory: see
* {@link org.springframework.beans.factory.config.ConfigurableBeanFactory#addPropertyEditorRegistrar}.
* @since 6.2.3
* @see PropertyEditorRegistry#registerCustomEditor
* @see PropertyEditorRegistrySupport#overrideDefaultEditor
* @see PropertyEditorRegistrySupport#setDefaultEditorRegistrar
*/
default boolean overridesDefaultEditors() {
return false;
}
}
@@ -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.
@@ -99,9 +99,6 @@ public class PropertyEditorRegistrySupport implements PropertyEditorRegistry {
private boolean configValueEditorsActive = false;
@Nullable
private PropertyEditorRegistrar defaultEditorRegistrar;
@Nullable
private Map<Class<?>, PropertyEditor> defaultEditors;
@@ -158,19 +155,6 @@ public class PropertyEditorRegistrySupport implements PropertyEditorRegistry {
this.configValueEditorsActive = true;
}
/**
* Set a registrar for default editors, as a lazy way of overriding default editors.
* <p>This is expected to be a collaborator with {@link PropertyEditorRegistrySupport},
* downcasting the given {@link PropertyEditorRegistry} accordingly and calling
* {@link #overrideDefaultEditor} for registering additional default editors on it.
* @param registrar the registrar to call when default editors are actually needed
* @since 6.2.3
* @see #overrideDefaultEditor
*/
public void setDefaultEditorRegistrar(PropertyEditorRegistrar registrar) {
this.defaultEditorRegistrar = registrar;
}
/**
* Override the default editor for the specified type with the given property editor.
* <p>Note that this is different from registering a custom editor in that the editor
@@ -200,9 +184,6 @@ public class PropertyEditorRegistrySupport implements PropertyEditorRegistry {
if (!this.defaultEditorsActive) {
return null;
}
if (this.overriddenDefaultEditors == null && this.defaultEditorRegistrar != null) {
this.defaultEditorRegistrar.registerCustomEditors(this);
}
if (this.overriddenDefaultEditors != null) {
PropertyEditor editor = this.overriddenDefaultEditors.get(requiredType);
if (editor != null) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,14 +39,10 @@ import org.springframework.util.StringUtils;
* (which the methods defined on the ListableBeanFactory interface don't,
* in contrast to the methods defined on the BeanFactory interface).
*
* <p><b>NOTE:</b> It is generally preferable to use {@link ObjectProvider#stream()}
* via {@link BeanFactory#getBeanProvider} instead of this utility class.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Chris Beams
* @since 04.07.2003
* @see BeanFactory#getBeanProvider
*/
public abstract class BeanFactoryUtils {
@@ -312,7 +308,7 @@ public abstract class BeanFactoryUtils {
* 'replacing' beans by explicitly choosing the same bean name in a child factory;
* the bean in the ancestor factory won't be visible then, not even for by-type lookups.
* @param lbf the bean factory
* @param type the type of bean to match
* @param type type of bean to match
* @return the Map of matching bean instances, or an empty Map if none
* @throws BeansException if a bean could not be created
* @see ListableBeanFactory#getBeansOfType(Class)
@@ -351,7 +347,7 @@ public abstract class BeanFactoryUtils {
* 'replacing' beans by explicitly choosing the same bean name in a child factory;
* the bean in the ancestor factory won't be visible then, not even for by-type lookups.
* @param lbf the bean factory
* @param type the type of bean to match
* @param type type of bean to match
* @param includeNonSingletons whether to include prototype or scoped beans too
* or just singletons (also applies to FactoryBeans)
* @param allowEagerInit whether to initialize <i>lazy-init singletons</i> and
@@ -399,7 +395,7 @@ public abstract class BeanFactoryUtils {
* 'replacing' beans by explicitly choosing the same bean name in a child factory;
* the bean in the ancestor factory won't be visible then, not even for by-type lookups.
* @param lbf the bean factory
* @param type the type of bean to match
* @param type type of bean to match
* @return the matching bean instance
* @throws NoSuchBeanDefinitionException if no bean of the given type was found
* @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found
@@ -429,7 +425,7 @@ public abstract class BeanFactoryUtils {
* 'replacing' beans by explicitly choosing the same bean name in a child factory;
* the bean in the ancestor factory won't be visible then, not even for by-type lookups.
* @param lbf the bean factory
* @param type the type of bean to match
* @param type type of bean to match
* @param includeNonSingletons whether to include prototype or scoped beans too
* or just singletons (also applies to FactoryBeans)
* @param allowEagerInit whether to initialize <i>lazy-init singletons</i> and
@@ -461,7 +457,7 @@ public abstract class BeanFactoryUtils {
* <p>This version of {@code beanOfType} automatically includes
* prototypes and FactoryBeans.
* @param lbf the bean factory
* @param type the type of bean to match
* @param type type of bean to match
* @return the matching bean instance
* @throws NoSuchBeanDefinitionException if no bean of the given type was found
* @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found
@@ -485,7 +481,7 @@ public abstract class BeanFactoryUtils {
* only raw FactoryBeans will be checked (which doesn't require initialization
* of each FactoryBean).
* @param lbf the bean factory
* @param type the type of bean to match
* @param type type of bean to match
* @param includeNonSingletons whether to include prototype or scoped beans too
* or just singletons (also applies to FactoryBeans)
* @param allowEagerInit whether to initialize <i>lazy-init singletons</i> and
@@ -533,7 +529,7 @@ public abstract class BeanFactoryUtils {
/**
* Extract a unique bean for the given type from the given Map of matching beans.
* @param type the type of bean to match
* @param type type of bean to match
* @param matchingBeans all matching beans found
* @return the unique bean instance
* @throws NoSuchBeanDefinitionException if no bean of the given type was found
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,7 +18,6 @@ package org.springframework.beans.factory;
import java.util.Iterator;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;
@@ -46,21 +45,6 @@ import org.springframework.lang.Nullable;
* Alternatively, you may implement the specific methods that your callers expect,
* for example, just {@link #getObject()} or {@link #getIfAvailable()}.
*
* <p>Note that {@link #getObject()} never returns {@code null} - it will throw a
* {@link NoSuchBeanDefinitionException} instead -, whereas {@link #getIfAvailable()}
* will return {@code null} if no matching bean is present at all. However, both
* methods will throw a {@link NoUniqueBeanDefinitionException} if more than one
* matching bean is found without a clear unique winner (see below). Last but not
* least, {@link #getIfUnique()} will return {@code null} both when no matching bean
* is found and when more than one matching bean is found without a unique winner.
*
* <p>Uniqueness is generally up to the container's candidate resolution algorithm
* but always honors the "primary" flag (with only one of the candidate beans marked
* as primary) and the "fallback" flag (with only one of the candidate beans not
* marked as fallback). The default-candidate flag is consistently taken into
* account as well, even for non-annotation-based injection points, with a single
* default candidate winning in case of no clear primary/fallback indication.
*
* @author Juergen Hoeller
* @since 4.3
* @param <T> the object type
@@ -69,18 +53,6 @@ import org.springframework.lang.Nullable;
*/
public interface ObjectProvider<T> extends ObjectFactory<T>, Iterable<T> {
/**
* A predicate for unfiltered type matches, including non-default candidates
* but still excluding non-autowire candidates when used on injection points.
* @since 6.2.3
* @see #stream(Predicate)
* @see #orderedStream(Predicate)
* @see org.springframework.beans.factory.config.BeanDefinition#isAutowireCandidate()
* @see org.springframework.beans.factory.support.AbstractBeanDefinition#isDefaultCandidate()
*/
Predicate<Class<?>> UNFILTERED = (clazz -> true);
@Override
default T getObject() throws BeansException {
Iterator<T> it = iterator();
@@ -203,7 +175,7 @@ public interface ObjectProvider<T> extends ObjectFactory<T>, Iterable<T> {
* if unique (not called otherwise)
* @throws BeansException in case of creation errors
* @since 5.0
* @see #getIfUnique()
* @see #getIfAvailable()
*/
default void ifUnique(Consumer<T> dependencyConsumer) throws BeansException {
T dependency = getIfUnique();
@@ -226,10 +198,6 @@ public interface ObjectProvider<T> extends ObjectFactory<T>, Iterable<T> {
/**
* Return a sequential {@link Stream} over all matching object instances,
* without specific ordering guarantees (but typically in registration order).
* <p>Note: The result may be filtered by default according to qualifiers on the
* injection point versus target beans and the general autowire candidate status
* of matching beans. For custom filtering against type-matching candidates, use
* {@link #stream(Predicate)} instead (potentially with {@link #UNFILTERED}).
* @since 5.1
* @see #iterator()
* @see #orderedStream()
@@ -251,10 +219,6 @@ public interface ObjectProvider<T> extends ObjectFactory<T>, Iterable<T> {
* {@link #stream()} method. You may override this to apply an
* {@link org.springframework.core.annotation.AnnotationAwareOrderComparator}
* if necessary.
* <p>Note: The result may be filtered by default according to qualifiers on the
* injection point versus target beans and the general autowire candidate status
* of matching beans. For custom filtering against type-matching candidates, use
* {@link #stream(Predicate)} instead (potentially with {@link #UNFILTERED}).
* @since 5.1
* @see #stream()
* @see org.springframework.core.OrderComparator
@@ -263,70 +227,4 @@ public interface ObjectProvider<T> extends ObjectFactory<T>, Iterable<T> {
return stream().sorted(OrderComparator.INSTANCE);
}
/**
* Return a custom-filtered {@link Stream} over all matching object instances,
* without specific ordering guarantees (but typically in registration order).
* @param customFilter a custom type filter for selecting beans among the raw
* bean type matches (or {@link #UNFILTERED} for all raw type matches without
* any default filtering)
* @since 6.2.3
* @see #stream()
* @see #orderedStream(Predicate)
*/
default Stream<T> stream(Predicate<Class<?>> customFilter) {
return stream(customFilter, true);
}
/**
* Return a custom-filtered {@link Stream} over all matching object instances,
* pre-ordered according to the factory's common order comparator.
* @param customFilter a custom type filter for selecting beans among the raw
* bean type matches (or {@link #UNFILTERED} for all raw type matches without
* any default filtering)
* @since 6.2.3
* @see #orderedStream()
* @see #stream(Predicate)
*/
default Stream<T> orderedStream(Predicate<Class<?>> customFilter) {
return orderedStream(customFilter, true);
}
/**
* Return a custom-filtered {@link Stream} over all matching object instances,
* without specific ordering guarantees (but typically in registration order).
* @param customFilter a custom type filter for selecting beans among the raw
* bean type matches (or {@link #UNFILTERED} for all raw type matches without
* any default filtering)
* @param includeNonSingletons whether to include prototype or scoped beans too
* or just singletons (also applies to FactoryBeans)
* @since 6.2.5
* @see #stream(Predicate)
* @see #orderedStream(Predicate, boolean)
*/
default Stream<T> stream(Predicate<Class<?>> customFilter, boolean includeNonSingletons) {
if (!includeNonSingletons) {
throw new UnsupportedOperationException("Only supports includeNonSingletons=true by default");
}
return stream().filter(obj -> customFilter.test(obj.getClass()));
}
/**
* Return a custom-filtered {@link Stream} over all matching object instances,
* pre-ordered according to the factory's common order comparator.
* @param customFilter a custom type filter for selecting beans among the raw
* bean type matches (or {@link #UNFILTERED} for all raw type matches without
* any default filtering)
* @param includeNonSingletons whether to include prototype or scoped beans too
* or just singletons (also applies to FactoryBeans)
* @since 6.2.5
* @see #orderedStream()
* @see #stream(Predicate)
*/
default Stream<T> orderedStream(Predicate<Class<?>> customFilter, boolean includeNonSingletons) {
if (!includeNonSingletons) {
throw new UnsupportedOperationException("Only supports includeNonSingletons=true by default");
}
return orderedStream().filter(obj -> customFilter.test(obj.getClass()));
}
}
@@ -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.
@@ -212,9 +212,9 @@ public final class AotServices<T> implements Iterable<T> {
}
private <T> Map<String, T> loadBeans(Class<T> type) {
return (this.beanFactory != null ?
BeanFactoryUtils.beansOfTypeIncludingAncestors(this.beanFactory, type, true, false) :
Collections.emptyMap());
return (this.beanFactory != null) ? BeanFactoryUtils
.beansOfTypeIncludingAncestors(this.beanFactory, type, true, false)
: Collections.emptyMap();
}
}
@@ -69,8 +69,8 @@ class BeanDefinitionMethodGeneratorFactory {
this.excludeFilters = loader.load(BeanRegistrationExcludeFilter.class);
for (BeanRegistrationExcludeFilter excludeFilter : this.excludeFilters) {
if (this.excludeFilters.getSource(excludeFilter) == Source.BEAN_FACTORY) {
Assert.state(excludeFilter instanceof BeanRegistrationAotProcessor ||
excludeFilter instanceof BeanFactoryInitializationAotProcessor,
Assert.state(excludeFilter instanceof BeanRegistrationAotProcessor
|| excludeFilter instanceof BeanFactoryInitializationAotProcessor,
() -> "BeanRegistrationExcludeFilter bean of type %s must also implement an AOT processor interface"
.formatted(excludeFilter.getClass().getName()));
}
@@ -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.
@@ -234,7 +234,8 @@ public class InstanceSupplierCodeGenerator {
CodeBlock arguments = hasArguments ?
new AutowiredArgumentsCodeGenerator(actualType, constructor)
.generateCode(constructor.getParameterTypes(), (onInnerClass ? 1 : 0)) : NO_ARGS;
.generateCode(constructor.getParameterTypes(), (onInnerClass ? 1 : 0))
: NO_ARGS;
CodeBlock newInstance = generateNewInstanceCodeForConstructor(actualType, arguments);
code.add(generateWithGeneratorCode(hasArguments, newInstance));
@@ -324,7 +325,8 @@ public class InstanceSupplierCodeGenerator {
boolean hasArguments = factoryMethod.getParameterCount() > 0;
CodeBlock arguments = hasArguments ?
new AutowiredArgumentsCodeGenerator(ClassUtils.getUserClass(targetClass), factoryMethod)
.generateCode(factoryMethod.getParameterTypes()) : NO_ARGS;
.generateCode(factoryMethod.getParameterTypes())
: NO_ARGS;
CodeBlock newInstance = generateNewInstanceCodeForMethod(
factoryBeanName, ClassUtils.getUserClass(targetClass), factoryMethodName, arguments);
@@ -441,7 +443,6 @@ public class InstanceSupplierCodeGenerator {
}
}
record ConstructorDescriptor(String beanName, Constructor<?> constructor, Class<?> publicType) {
Class<?> actualType() {
@@ -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.
@@ -183,11 +183,7 @@ public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, Single
* on the given registry, fresh for each bean creation attempt. This avoids
* the need for synchronization on custom editors; hence, it is generally
* preferable to use this method instead of {@link #registerCustomEditor}.
* <p>If the given registrar implements
* {@link PropertyEditorRegistrar#overridesDefaultEditors()} to return {@code true},
* it will be applied lazily (only when default editors are actually needed).
* @param registrar the PropertyEditorRegistrar to register
* @see PropertyEditorRegistrar#overridesDefaultEditors()
*/
void addPropertyEditorRegistrar(PropertyEditorRegistrar registrar);
@@ -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.
@@ -137,9 +137,6 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
@Nullable
private ConversionService conversionService;
/** Default PropertyEditorRegistrars to apply to the beans of this factory. */
private final Set<PropertyEditorRegistrar> defaultEditorRegistrars = new LinkedHashSet<>(4);
/** Custom PropertyEditorRegistrars to apply to the beans of this factory. */
private final Set<PropertyEditorRegistrar> propertyEditorRegistrars = new LinkedHashSet<>(4);
@@ -886,12 +883,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
@Override
public void addPropertyEditorRegistrar(PropertyEditorRegistrar registrar) {
Assert.notNull(registrar, "PropertyEditorRegistrar must not be null");
if (registrar.overridesDefaultEditors()) {
this.defaultEditorRegistrars.add(registrar);
}
else {
this.propertyEditorRegistrars.add(registrar);
}
this.propertyEditorRegistrars.add(registrar);
}
/**
@@ -1122,7 +1114,6 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
setBeanExpressionResolver(otherFactory.getBeanExpressionResolver());
setConversionService(otherFactory.getConversionService());
if (otherFactory instanceof AbstractBeanFactory otherAbstractFactory) {
this.defaultEditorRegistrars.addAll(otherAbstractFactory.defaultEditorRegistrars);
this.propertyEditorRegistrars.addAll(otherAbstractFactory.propertyEditorRegistrars);
this.customEditors.putAll(otherAbstractFactory.customEditors);
this.typeConverter = otherAbstractFactory.typeConverter;
@@ -1322,18 +1313,29 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
protected void registerCustomEditors(PropertyEditorRegistry registry) {
if (registry instanceof PropertyEditorRegistrySupport registrySupport) {
registrySupport.useConfigValueEditors();
if (!this.defaultEditorRegistrars.isEmpty()) {
// Optimization: lazy overriding of default editors only when needed
registrySupport.setDefaultEditorRegistrar(new BeanFactoryDefaultEditorRegistrar());
}
}
else if (!this.defaultEditorRegistrars.isEmpty()) {
// Fallback: proactive overriding of default editors
applyEditorRegistrars(registry, this.defaultEditorRegistrars);
}
if (!this.propertyEditorRegistrars.isEmpty()) {
applyEditorRegistrars(registry, this.propertyEditorRegistrars);
for (PropertyEditorRegistrar registrar : this.propertyEditorRegistrars) {
try {
registrar.registerCustomEditors(registry);
}
catch (BeanCreationException ex) {
Throwable rootCause = ex.getMostSpecificCause();
if (rootCause instanceof BeanCurrentlyInCreationException bce) {
String bceBeanName = bce.getBeanName();
if (bceBeanName != null && isCurrentlyInCreation(bceBeanName)) {
if (logger.isDebugEnabled()) {
logger.debug("PropertyEditorRegistrar [" + registrar.getClass().getName() +
"] failed because it tried to obtain currently created bean '" +
ex.getBeanName() + "': " + ex.getMessage());
}
onSuppressedException(ex);
continue;
}
}
throw ex;
}
}
}
if (!this.customEditors.isEmpty()) {
this.customEditors.forEach((requiredType, editorClass) ->
@@ -1341,29 +1343,6 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
}
}
private void applyEditorRegistrars(PropertyEditorRegistry registry, Set<PropertyEditorRegistrar> registrars) {
for (PropertyEditorRegistrar registrar : registrars) {
try {
registrar.registerCustomEditors(registry);
}
catch (BeanCreationException ex) {
Throwable rootCause = ex.getMostSpecificCause();
if (rootCause instanceof BeanCurrentlyInCreationException bce) {
String bceBeanName = bce.getBeanName();
if (bceBeanName != null && isCurrentlyInCreation(bceBeanName)) {
if (logger.isDebugEnabled()) {
logger.debug("PropertyEditorRegistrar [" + registrar.getClass().getName() +
"] failed because it tried to obtain currently created bean '" +
ex.getBeanName() + "': " + ex.getMessage());
}
onSuppressedException(ex);
return;
}
}
throw ex;
}
}
}
/**
* Return a merged RootBeanDefinition, traversing the parent bean definition
@@ -2116,20 +2095,4 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
final List<MergedBeanDefinitionPostProcessor> mergedDefinition = new ArrayList<>();
}
/**
* {@link PropertyEditorRegistrar} that delegates to the bean factory's
* default registrars, adding exception handling for circular reference
* scenarios where an editor tries to refer back to the currently created bean.
*
* @since 6.2.3
*/
class BeanFactoryDefaultEditorRegistrar implements PropertyEditorRegistrar {
@Override
public void registerCustomEditors(PropertyEditorRegistry registry) {
applyEditorRegistrars(registry, defaultEditorRegistrars);
}
}
}
@@ -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.
@@ -33,10 +33,7 @@ import java.util.Comparator;
import java.util.Set;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -262,43 +259,6 @@ abstract class AutowireUtils {
return method.getReturnType();
}
/**
* Check the autowire-candidate status for the specified bean.
* @param beanFactory the bean factory
* @param beanName the name of the bean to check
* @return whether the specified bean qualifies as an autowire candidate
* @since 6.2.3
* @see org.springframework.beans.factory.config.BeanDefinition#isAutowireCandidate()
*/
public static boolean isAutowireCandidate(ConfigurableBeanFactory beanFactory, String beanName) {
try {
return beanFactory.getMergedBeanDefinition(beanName).isAutowireCandidate();
}
catch (NoSuchBeanDefinitionException ex) {
// A manually registered singleton instance not backed by a BeanDefinition.
return true;
}
}
/**
* Check the default-candidate status for the specified bean.
* @param beanFactory the bean factory
* @param beanName the name of the bean to check
* @return whether the specified bean qualifies as a default candidate
* @since 6.2.4
* @see AbstractBeanDefinition#isDefaultCandidate()
*/
public static boolean isDefaultCandidate(ConfigurableBeanFactory beanFactory, String beanName) {
try {
BeanDefinition mbd = beanFactory.getMergedBeanDefinition(beanName);
return (!(mbd instanceof AbstractBeanDefinition abd) || abd.isDefaultCandidate());
}
catch (NoSuchBeanDefinitionException ex) {
// A manually registered singleton instance not backed by a BeanDefinition.
return true;
}
}
/**
* Reflective {@link InvocationHandler} for lazy access to the current target object.
@@ -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.
@@ -487,14 +487,14 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
@SuppressWarnings("unchecked")
@Override
public Stream<T> stream() {
return Arrays.stream(beanNamesForStream(requiredType, true, allowEagerInit))
return Arrays.stream(getBeanNamesForTypedStream(requiredType, allowEagerInit))
.map(name -> (T) getBean(name))
.filter(bean -> !(bean instanceof NullBean));
}
@SuppressWarnings("unchecked")
@Override
public Stream<T> orderedStream() {
String[] beanNames = beanNamesForStream(requiredType, true, allowEagerInit);
String[] beanNames = getBeanNamesForTypedStream(requiredType, allowEagerInit);
if (beanNames.length == 0) {
return Stream.empty();
}
@@ -508,32 +508,6 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
Stream<T> stream = matchingBeans.values().stream();
return stream.sorted(adaptOrderComparator(matchingBeans));
}
@SuppressWarnings("unchecked")
@Override
public Stream<T> stream(Predicate<Class<?>> customFilter, boolean includeNonSingletons) {
return Arrays.stream(beanNamesForStream(requiredType, includeNonSingletons, allowEagerInit))
.filter(name -> customFilter.test(getType(name)))
.map(name -> (T) getBean(name))
.filter(bean -> !(bean instanceof NullBean));
}
@SuppressWarnings("unchecked")
@Override
public Stream<T> orderedStream(Predicate<Class<?>> customFilter, boolean includeNonSingletons) {
String[] beanNames = beanNamesForStream(requiredType, includeNonSingletons, allowEagerInit);
if (beanNames.length == 0) {
return Stream.empty();
}
Map<String, T> matchingBeans = CollectionUtils.newLinkedHashMap(beanNames.length);
for (String beanName : beanNames) {
if (customFilter.test(getType(beanName))) {
Object beanInstance = getBean(beanName);
if (!(beanInstance instanceof NullBean)) {
matchingBeans.put(beanName, (T) beanInstance);
}
}
}
return matchingBeans.values().stream().sorted(adaptOrderComparator(matchingBeans));
}
};
}
@@ -559,8 +533,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
return null;
}
private String[] beanNamesForStream(ResolvableType requiredType, boolean includeNonSingletons, boolean allowEagerInit) {
return BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this, requiredType, includeNonSingletons, allowEagerInit);
private String[] getBeanNamesForTypedStream(ResolvableType requiredType, boolean allowEagerInit) {
return BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this, requiredType, true, allowEagerInit);
}
@Override
@@ -1115,15 +1089,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
"without bootstrap executor configured - falling back to mainline initialization");
}
}
if (!mbd.isLazyInit()) {
try {
instantiateSingleton(beanName);
}
catch (BeanCurrentlyInCreationException ex) {
logger.info("Bean '" + beanName + "' marked for pre-instantiation (not lazy-init) " +
"but currently initialized by other thread - skipping it in mainline thread");
}
instantiateSingleton(beanName);
}
return null;
}
@@ -1497,9 +1464,6 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
if (candidateName == null) {
candidateName = determineHighestPriorityCandidate(candidates, requiredType.toClass());
}
if (candidateName == null) {
candidateName = determineDefaultCandidate(candidates);
}
if (candidateName != null) {
Object beanInstance = candidates.get(candidateName);
if (beanInstance == null) {
@@ -1921,8 +1885,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
candidates.put(candidateName, beanInstance);
}
}
else if (containsSingleton(candidateName) ||
(descriptor instanceof StreamDependencyDescriptor streamDescriptor && streamDescriptor.isOrdered())) {
else if (containsSingleton(candidateName) || (descriptor instanceof StreamDependencyDescriptor streamDescriptor &&
streamDescriptor.isOrdered())) {
Object beanInstance = descriptor.resolveCandidate(candidateName, requiredType, this);
candidates.put(candidateName, (beanInstance instanceof NullBean ? null : beanInstance));
}
@@ -1970,12 +1934,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
if (priorityCandidate != null) {
return priorityCandidate;
}
// Step 4: pick unique default-candidate
String defaultCandidate = determineDefaultCandidate(candidates);
if (defaultCandidate != null) {
return defaultCandidate;
}
// Step 5: pick directly registered dependency
// Step 4: pick directly registered dependency
for (Map.Entry<String, Object> entry : candidates.entrySet()) {
String candidateName = entry.getKey();
Object beanInstance = entry.getValue();
@@ -2136,28 +2095,6 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
return null;
}
/**
* Return a unique "default-candidate" among remaining non-default candidates.
* @param candidates a Map of candidate names and candidate instances
* (or candidate classes if not created yet) that match the required type
* @return the name of the default candidate, or {@code null} if none found
* @since 6.2.4
* @see AbstractBeanDefinition#isDefaultCandidate()
*/
@Nullable
private String determineDefaultCandidate(Map<String, Object> candidates) {
String defaultBeanName = null;
for (String candidateBeanName : candidates.keySet()) {
if (AutowireUtils.isDefaultCandidate(this, candidateBeanName)) {
if (defaultBeanName != null) {
return null;
}
defaultBeanName = candidateBeanName;
}
}
return defaultBeanName;
}
/**
* Determine whether the given candidate name matches the bean name or the aliases
* stored in this bean definition.
@@ -2542,34 +2479,6 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
Object result = doResolveDependency(descriptorToUse, this.beanName, null, null);
return (result instanceof Stream stream ? stream : Stream.of(result));
}
@Override
public Stream<Object> stream(Predicate<Class<?>> customFilter, boolean includeNonSingletons) {
return Arrays.stream(beanNamesForStream(this.descriptor.getResolvableType(), includeNonSingletons, true))
.filter(name -> AutowireUtils.isAutowireCandidate(DefaultListableBeanFactory.this, name))
.filter(name -> customFilter.test(getType(name)))
.map(name -> getBean(name))
.filter(bean -> !(bean instanceof NullBean));
}
@Override
public Stream<Object> orderedStream(Predicate<Class<?>> customFilter, boolean includeNonSingletons) {
String[] beanNames = beanNamesForStream(this.descriptor.getResolvableType(), includeNonSingletons, true);
if (beanNames.length == 0) {
return Stream.empty();
}
Map<String, Object> matchingBeans = CollectionUtils.newLinkedHashMap(beanNames.length);
for (String beanName : beanNames) {
if (AutowireUtils.isAutowireCandidate(DefaultListableBeanFactory.this, beanName) &&
customFilter.test(getType(beanName))) {
Object beanInstance = getBean(beanName);
if (!(beanInstance instanceof NullBean)) {
matchingBeans.put(beanName, beanInstance);
}
}
}
return matchingBeans.values().stream().sorted(adaptOrderComparator(matchingBeans));
}
}
@@ -24,7 +24,6 @@ import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
@@ -101,15 +100,6 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
/** Names of beans currently excluded from in creation checks. */
private final Set<String> inCreationCheckExclusions = ConcurrentHashMap.newKeySet(16);
/** Specific lock for lenient creation tracking. */
private final Lock lenientCreationLock = new ReentrantLock();
/** Specific lock condition for lenient creation tracking. */
private final Condition lenientCreationFinished = this.lenientCreationLock.newCondition();
/** Names of beans that are currently in lenient creation. */
private final Set<String> singletonsInLenientCreation = new HashSet<>();
/** Flag that indicates whether we're currently within destroySingletons. */
private volatile boolean singletonsCurrentlyInDestruction = false;
@@ -267,13 +257,6 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
Thread.currentThread().getName() + "\" while other thread holds " +
"singleton lock for other beans " + this.singletonsCurrentlyInCreation);
}
this.lenientCreationLock.lock();
try {
this.singletonsInLenientCreation.add(beanName);
}
finally {
this.lenientCreationLock.unlock();
}
}
else {
// No specific locking indication (outside a coordinated bootstrap) and
@@ -301,33 +284,13 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
beforeSingletonCreation(beanName);
}
catch (BeanCurrentlyInCreationException ex) {
this.lenientCreationLock.lock();
try {
while ((singletonObject = this.singletonObjects.get(beanName)) == null) {
if (!this.singletonsInLenientCreation.contains(beanName)) {
break;
}
try {
this.lenientCreationFinished.await();
}
catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
}
}
finally {
this.lenientCreationLock.unlock();
}
if (singletonObject != null) {
return singletonObject;
}
if (locked) {
throw ex;
}
// Try late locking for waiting on specific bean to be finished.
this.singletonLock.lock();
locked = true;
// Lock-created singleton object should have appeared in the meantime.
// Singleton object should have appeared in the meantime.
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject != null) {
return singletonObject;
@@ -341,12 +304,8 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
// Leniently created singleton object could have appeared in the meantime.
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
// Has the singleton object implicitly appeared in the meantime ->
@@ -370,18 +329,8 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
}
afterSingletonCreation(beanName);
}
if (newSingleton) {
try {
addSingleton(beanName, singletonObject);
}
catch (IllegalStateException ex) {
// Leniently accept same instance if implicitly appeared.
Object object = this.singletonObjects.get(beanName);
if (singletonObject != object) {
throw ex;
}
}
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
@@ -390,14 +339,6 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
if (locked) {
this.singletonLock.unlock();
}
this.lenientCreationLock.lock();
try {
this.singletonsInLenientCreation.remove(beanName);
this.lenientCreationFinished.signalAll();
}
finally {
this.lenientCreationLock.unlock();
}
}
}
@@ -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,13 +16,9 @@
package org.springframework.beans.factory.support;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.lang.Nullable;
/**
* {@link AutowireCandidateResolver} implementation to use when no annotation
@@ -40,6 +36,46 @@ public class SimpleAutowireCandidateResolver implements AutowireCandidateResolve
*/
public static final SimpleAutowireCandidateResolver INSTANCE = new SimpleAutowireCandidateResolver();
@Override
public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
return bdHolder.getBeanDefinition().isAutowireCandidate();
}
@Override
public boolean isRequired(DependencyDescriptor descriptor) {
return descriptor.isRequired();
}
@Override
public boolean hasQualifier(DependencyDescriptor descriptor) {
return false;
}
@Override
@Nullable
public String getSuggestedName(DependencyDescriptor descriptor) {
return null;
}
@Override
@Nullable
public Object getSuggestedValue(DependencyDescriptor descriptor) {
return null;
}
@Override
@Nullable
public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {
return null;
}
@Override
@Nullable
public Class<?> getLazyResolutionProxyClass(DependencyDescriptor descriptor, @Nullable String beanName) {
return null;
}
/**
* This implementation returns {@code this} as-is.
* @see #INSTANCE
@@ -49,65 +85,4 @@ public class SimpleAutowireCandidateResolver implements AutowireCandidateResolve
return this;
}
/**
* Resolve a map of all beans of the given type, also picking up beans defined in
* ancestor bean factories, with the specific condition that each bean actually
* has autowire candidate status. This matches simple injection point resolution
* as implemented by this {@link AutowireCandidateResolver} strategy, including
* beans which are not marked as default candidates but excluding beans which
* are not even marked as autowire candidates.
* @param lbf the bean factory
* @param type the type of bean to match
* @return the Map of matching bean instances, or an empty Map if none
* @throws BeansException if a bean could not be created
* @since 6.2.3
* @see BeanFactoryUtils#beansOfTypeIncludingAncestors(ListableBeanFactory, Class)
* @see org.springframework.beans.factory.config.BeanDefinition#isAutowireCandidate()
* @see AbstractBeanDefinition#isDefaultCandidate()
*/
public static <T> Map<String, T> resolveAutowireCandidates(ConfigurableListableBeanFactory lbf, Class<T> type) {
return resolveAutowireCandidates(lbf, type, true, true);
}
/**
* Resolve a map of all beans of the given type, also picking up beans defined in
* ancestor bean factories, with the specific condition that each bean actually
* has autowire candidate status. This matches simple injection point resolution
* as implemented by this {@link AutowireCandidateResolver} strategy, including
* beans which are not marked as default candidates but excluding beans which
* are not even marked as autowire candidates.
* @param lbf the bean factory
* @param type the type of bean to match
* @param includeNonSingletons whether to include prototype or scoped beans too
* or just singletons (also applies to FactoryBeans)
* @param allowEagerInit whether to initialize <i>lazy-init singletons</i> and
* <i>objects created by FactoryBeans</i> (or by factory methods with a
* "factory-bean" reference) for the type check. Note that FactoryBeans need to be
* eagerly initialized to determine their type: So be aware that passing in "true"
* for this flag will initialize FactoryBeans and "factory-bean" references.
* @return the Map of matching bean instances, or an empty Map if none
* @throws BeansException if a bean could not be created
* @since 6.2.5
* @see BeanFactoryUtils#beansOfTypeIncludingAncestors(ListableBeanFactory, Class, boolean, boolean)
* @see org.springframework.beans.factory.config.BeanDefinition#isAutowireCandidate()
* @see AbstractBeanDefinition#isDefaultCandidate()
*/
@SuppressWarnings("unchecked")
public static <T> Map<String, T> resolveAutowireCandidates(ConfigurableListableBeanFactory lbf, Class<T> type,
boolean includeNonSingletons, boolean allowEagerInit) {
Map<String, T> candidates = new LinkedHashMap<>();
for (String beanName : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(lbf, type,
includeNonSingletons, allowEagerInit)) {
if (AutowireUtils.isAutowireCandidate(lbf, beanName)) {
Object beanInstance = lbf.getBean(beanName);
if (!(beanInstance instanceof NullBean)) {
candidates.put(beanName, (T) beanInstance);
}
}
}
return candidates;
}
}
@@ -66,8 +66,8 @@ public abstract class AbstractBeanDefinitionParser implements BeanDefinitionPars
String id = resolveId(element, definition, parserContext);
if (!StringUtils.hasText(id)) {
parserContext.getReaderContext().error(
"Id is required for element '" + parserContext.getDelegate().getLocalName(element) +
"' when used as a top-level tag", element);
"Id is required for element '" + parserContext.getDelegate().getLocalName(element)
+ "' when used as a top-level tag", element);
}
String[] aliases = null;
if (shouldParseNameAsAliases()) {
@@ -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.
@@ -135,12 +135,4 @@ public class ResourceEditorRegistrar implements PropertyEditorRegistrar {
}
}
/**
* Indicate the use of {@link PropertyEditorRegistrySupport#overrideDefaultEditor} above.
*/
@Override
public boolean overridesDefaultEditors() {
return true;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -139,7 +139,6 @@ abstract class AbstractPropertyAccessorTests {
assertThat(accessor.isReadableProperty("list")).isTrue();
assertThat(accessor.isReadableProperty("set")).isTrue();
assertThat(accessor.isReadableProperty("map")).isTrue();
assertThat(accessor.isReadableProperty("iterableMap")).isTrue();
assertThat(accessor.isReadableProperty("myTestBeans")).isTrue();
assertThat(accessor.isReadableProperty("xxx")).isFalse();
@@ -147,7 +146,6 @@ abstract class AbstractPropertyAccessorTests {
assertThat(accessor.isWritableProperty("list")).isTrue();
assertThat(accessor.isWritableProperty("set")).isTrue();
assertThat(accessor.isWritableProperty("map")).isTrue();
assertThat(accessor.isWritableProperty("iterableMap")).isTrue();
assertThat(accessor.isWritableProperty("myTestBeans")).isTrue();
assertThat(accessor.isWritableProperty("xxx")).isFalse();
@@ -163,14 +161,6 @@ abstract class AbstractPropertyAccessorTests {
assertThat(accessor.isReadableProperty("map[key4][0].name")).isTrue();
assertThat(accessor.isReadableProperty("map[key4][1]")).isTrue();
assertThat(accessor.isReadableProperty("map[key4][1].name")).isTrue();
assertThat(accessor.isReadableProperty("map[key999]")).isTrue();
assertThat(accessor.isReadableProperty("iterableMap[key1]")).isTrue();
assertThat(accessor.isReadableProperty("iterableMap[key1].name")).isTrue();
assertThat(accessor.isReadableProperty("iterableMap[key2][0]")).isTrue();
assertThat(accessor.isReadableProperty("iterableMap[key2][0].name")).isTrue();
assertThat(accessor.isReadableProperty("iterableMap[key2][1]")).isTrue();
assertThat(accessor.isReadableProperty("iterableMap[key2][1].name")).isTrue();
assertThat(accessor.isReadableProperty("iterableMap[key999]")).isTrue();
assertThat(accessor.isReadableProperty("myTestBeans[0]")).isTrue();
assertThat(accessor.isReadableProperty("myTestBeans[1]")).isFalse();
assertThat(accessor.isReadableProperty("array[key1]")).isFalse();
@@ -187,14 +177,6 @@ abstract class AbstractPropertyAccessorTests {
assertThat(accessor.isWritableProperty("map[key4][0].name")).isTrue();
assertThat(accessor.isWritableProperty("map[key4][1]")).isTrue();
assertThat(accessor.isWritableProperty("map[key4][1].name")).isTrue();
assertThat(accessor.isWritableProperty("map[key999]")).isTrue();
assertThat(accessor.isWritableProperty("iterableMap[key1]")).isTrue();
assertThat(accessor.isWritableProperty("iterableMap[key1].name")).isTrue();
assertThat(accessor.isWritableProperty("iterableMap[key2][0]")).isTrue();
assertThat(accessor.isWritableProperty("iterableMap[key2][0].name")).isTrue();
assertThat(accessor.isWritableProperty("iterableMap[key2][1]")).isTrue();
assertThat(accessor.isWritableProperty("iterableMap[key2][1].name")).isTrue();
assertThat(accessor.isWritableProperty("iterableMap[key999]")).isTrue();
assertThat(accessor.isReadableProperty("myTestBeans[0]")).isTrue();
assertThat(accessor.isReadableProperty("myTestBeans[1]")).isFalse();
assertThat(accessor.isWritableProperty("array[key1]")).isFalse();
@@ -1413,9 +1395,6 @@ abstract class AbstractPropertyAccessorTests {
assertThat(accessor.getPropertyValue("map[key5[foo]].name")).isEqualTo("name8");
assertThat(accessor.getPropertyValue("map['key5[foo]'].name")).isEqualTo("name8");
assertThat(accessor.getPropertyValue("map[\"key5[foo]\"].name")).isEqualTo("name8");
assertThat(accessor.getPropertyValue("iterableMap[key1].name")).isEqualTo("nameC");
assertThat(accessor.getPropertyValue("iterableMap[key2][0].name")).isEqualTo("nameA");
assertThat(accessor.getPropertyValue("iterableMap[key2][1].name")).isEqualTo("nameB");
assertThat(accessor.getPropertyValue("myTestBeans[0].name")).isEqualTo("nameZ");
MutablePropertyValues pvs = new MutablePropertyValues();
@@ -1430,9 +1409,6 @@ abstract class AbstractPropertyAccessorTests {
pvs.add("map[key4][0].name", "nameA");
pvs.add("map[key4][1].name", "nameB");
pvs.add("map[key5[foo]].name", "name10");
pvs.add("iterableMap[key1].name", "newName1");
pvs.add("iterableMap[key2][0].name", "newName2A");
pvs.add("iterableMap[key2][1].name", "newName2B");
pvs.add("myTestBeans[0].name", "nameZZ");
accessor.setPropertyValues(pvs);
assertThat(tb0.getName()).isEqualTo("name5");
@@ -1452,9 +1428,6 @@ abstract class AbstractPropertyAccessorTests {
assertThat(accessor.getPropertyValue("map[key4][0].name")).isEqualTo("nameA");
assertThat(accessor.getPropertyValue("map[key4][1].name")).isEqualTo("nameB");
assertThat(accessor.getPropertyValue("map[key5[foo]].name")).isEqualTo("name10");
assertThat(accessor.getPropertyValue("iterableMap[key1].name")).isEqualTo("newName1");
assertThat(accessor.getPropertyValue("iterableMap[key2][0].name")).isEqualTo("newName2A");
assertThat(accessor.getPropertyValue("iterableMap[key2][1].name")).isEqualTo("newName2B");
assertThat(accessor.getPropertyValue("myTestBeans[0].name")).isEqualTo("nameZZ");
}
@@ -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.
@@ -103,27 +103,6 @@ class BeanWrapperAutoGrowingTests {
assertThat(bean.getThreeDimensionalArray()[1][2][3]).isInstanceOf(Bean.class);
}
@Test
void getPropertyValueAutoGrow3dArrayList() {
assertThat(wrapper.getPropertyValue("threeDimensionalArrayList[1][2][3][4]")).isNotNull();
assertThat(bean.getThreeDimensionalArrayList()).hasSize(2);
assertThat(bean.getThreeDimensionalArrayList().get(1)).hasNumberOfRows(3);
assertThat(bean.getThreeDimensionalArrayList().get(1)[2]).hasNumberOfRows(4);
assertThat(bean.getThreeDimensionalArrayList().get(1)[2][3]).hasSize(5);
assertThat(bean.getThreeDimensionalArrayList().get(1)[2][3][4]).isInstanceOf(Bean.class);
}
@Test
void getPropertyValueAutoGrow3dArrayListForDefault3dArray() {
assertThat(wrapper.getPropertyValue("threeDimensionalArrayList[0]")).isNotNull();
assertThat(bean.getThreeDimensionalArrayList()).hasSize(1);
// Default 3-dimensional array should be [[[]]]
assertThat(bean.getThreeDimensionalArrayList().get(0)).hasNumberOfRows(1);
assertThat(bean.getThreeDimensionalArrayList().get(0)[0]).hasNumberOfRows(1);
assertThat(bean.getThreeDimensionalArrayList().get(0)[0][0]).isEmpty();
}
@Test
void setPropertyValueAutoGrow2dArray() {
Bean newBean = new Bean();
@@ -144,16 +123,6 @@ class BeanWrapperAutoGrowingTests {
.extracting(Bean::getProp).isEqualTo("enigma");
}
@Test
void setPropertyValueAutoGrow3dArrayList() {
Bean newBean = new Bean();
newBean.setProp("enigma");
wrapper.setPropertyValue("threeDimensionalArrayList[0][1][2][3]", newBean);
assertThat(bean.getThreeDimensionalArrayList().get(0)[1][2][3])
.isInstanceOf(Bean.class)
.extracting(Bean::getProp).isEqualTo("enigma");
}
@Test
void getPropertyValueAutoGrowList() {
assertThat(wrapper.getPropertyValue("list[0]")).isNotNull();
@@ -246,8 +215,6 @@ class BeanWrapperAutoGrowingTests {
private Bean[][][] threeDimensionalArray;
private List<Bean[][][]> threeDimensionalArrayList;
private List<Bean> list;
private List<List<Bean>> nestedList;
@@ -302,14 +269,6 @@ class BeanWrapperAutoGrowingTests {
this.threeDimensionalArray = threeDimensionalArray;
}
public List<Bean[][][]> getThreeDimensionalArrayList() {
return threeDimensionalArrayList;
}
public void setThreeDimensionalArrayList(List<Bean[][][]> threeDimensionalArrayList) {
this.threeDimensionalArrayList = threeDimensionalArrayList;
}
public List<Bean> getList() {
return list;
}
@@ -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.
@@ -113,7 +113,6 @@ import static org.mockito.Mockito.verify;
* @author Chris Beams
* @author Phillip Webb
* @author Stephane Nicoll
* @author Yanming Zhou
*/
class DefaultListableBeanFactoryTests {
@@ -1516,37 +1515,31 @@ class DefaultListableBeanFactoryTests {
bd1.setAttribute(AbstractBeanDefinition.ORDER_ATTRIBUTE, Ordered.LOWEST_PRECEDENCE);
lbf.registerBeanDefinition("bean1", bd1);
GenericBeanDefinition bd2 = new GenericBeanDefinition();
bd2.setBeanClass(DerivedTestBean.class);
bd2.setBeanClass(TestBean.class);
bd2.setPropertyValues(new MutablePropertyValues(List.of(new PropertyValue("name", "highest"))));
bd2.setAttribute(AbstractBeanDefinition.ORDER_ATTRIBUTE, Ordered.HIGHEST_PRECEDENCE);
bd2.setScope(BeanDefinition.SCOPE_PROTOTYPE);
lbf.registerBeanDefinition("bean2", bd2);
assertThat(lbf.getBeanProvider(TestBean.class).orderedStream().map(TestBean::getName))
.containsExactly("highest", "lowest");
assertThat(lbf.getBeanProvider(TestBean.class).orderedStream(clazz -> !DerivedTestBean.class.isAssignableFrom(clazz))
.map(TestBean::getName)).containsExactly("lowest");
assertThat(lbf.getBeanProvider(TestBean.class).orderedStream(ObjectProvider.UNFILTERED).map(TestBean::getName))
.containsExactly("highest", "lowest");
assertThat(lbf.getBeanProvider(TestBean.class).orderedStream(ObjectProvider.UNFILTERED, false).map(TestBean::getName))
.containsExactly("lowest");
}
@Test
void orderFromAttributeOverridesAnnotation() {
void orderFromAttributeOverrideAnnotation() {
lbf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
RootBeanDefinition rbd1 = new RootBeanDefinition(LowestPrecedenceTestBeanFactoryBean.class);
rbd1.setAttribute(AbstractBeanDefinition.ORDER_ATTRIBUTE, Ordered.HIGHEST_PRECEDENCE);
lbf.registerBeanDefinition("lowestPrecedenceFactory", rbd1);
RootBeanDefinition rbd2 = new RootBeanDefinition(HighestPrecedenceTestBeanFactoryBean.class);
rbd2.setAttribute(AbstractBeanDefinition.ORDER_ATTRIBUTE, Ordered.LOWEST_PRECEDENCE);
rbd2.setScope(BeanDefinition.SCOPE_PROTOTYPE);
lbf.registerBeanDefinition("highestPrecedenceFactory", rbd2);
GenericBeanDefinition bd1 = new GenericBeanDefinition();
bd1.setFactoryBeanName("highestPrecedenceFactory");
lbf.registerBeanDefinition("bean1", bd1);
GenericBeanDefinition bd2 = new GenericBeanDefinition();
bd2.setFactoryBeanName("lowestPrecedenceFactory");
lbf.registerBeanDefinition("bean2", bd2);
assertThat(lbf.getBeanProvider(TestBean.class).orderedStream().map(TestBean::getName))
.containsExactly("fromLowestPrecedenceTestBeanFactoryBean", "fromHighestPrecedenceTestBeanFactoryBean");
assertThat(lbf.getBeanProvider(TestBean.class).orderedStream(ObjectProvider.UNFILTERED).map(TestBean::getName))
.containsExactly("fromLowestPrecedenceTestBeanFactoryBean", "fromHighestPrecedenceTestBeanFactoryBean");
assertThat(lbf.getBeanProvider(TestBean.class).orderedStream(ObjectProvider.UNFILTERED, false).map(TestBean::getName))
.containsExactly("fromLowestPrecedenceTestBeanFactoryBean");
}
@Test
@@ -1659,94 +1652,12 @@ class DefaultListableBeanFactoryTests {
bd2.setPrimary(true);
lbf.registerBeanDefinition("bd1", bd1);
lbf.registerBeanDefinition("bd2", bd2);
lbf.registerSingleton("bd3", new TestBean());
TestBean bean = lbf.getBean(TestBean.class);
assertThat(bean.getBeanName()).isEqualTo("bd2");
assertThat(lbf.containsSingleton("bd1")).isFalse();
}
@Test
void getBeanByTypeWithUniqueNonFallbackDefinition() {
RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class);
bd1.setLazyInit(true);
bd1.setFallback(true);
RootBeanDefinition bd2 = new RootBeanDefinition(TestBean.class);
bd2.setFallback(true);
RootBeanDefinition bd3 = new RootBeanDefinition(TestBean.class);
lbf.registerBeanDefinition("bd1", bd1);
lbf.registerBeanDefinition("bd2", bd2);
lbf.registerBeanDefinition("bd3", bd3);
TestBean bean = lbf.getBean(TestBean.class);
assertThat(bean.getBeanName()).isEqualTo("bd3");
assertThat(lbf.containsSingleton("bd1")).isFalse();
}
@Test
void getBeanByTypeWithPrimaryAndUniqueNonFallbackDefinition() {
RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class);
bd1.setLazyInit(true);
bd1.setFallback(true);
RootBeanDefinition bd2 = new RootBeanDefinition(TestBean.class);
bd2.setPrimary(true);
bd2.setFallback(true);
RootBeanDefinition bd3 = new RootBeanDefinition(TestBean.class);
lbf.registerBeanDefinition("bd1", bd1);
lbf.registerBeanDefinition("bd2", bd2);
lbf.registerBeanDefinition("bd3", bd3);
TestBean bean = lbf.getBean(TestBean.class);
assertThat(bean.getBeanName()).isEqualTo("bd2");
assertThat(lbf.containsSingleton("bd1")).isFalse();
}
@Test
void getBeanByTypeWithUniqueNonFallbackAndUniqueNonDefaultDefinition() {
RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class);
bd1.setLazyInit(true);
bd1.setFallback(true);
RootBeanDefinition bd2 = new RootBeanDefinition(TestBean.class);
bd2.setFallback(true);
bd2.setDefaultCandidate(false);
RootBeanDefinition bd3 = new RootBeanDefinition(TestBean.class);
bd3.setDefaultCandidate(false);
lbf.registerBeanDefinition("bd1", bd1);
lbf.registerBeanDefinition("bd2", bd2);
lbf.registerBeanDefinition("bd3", bd3);
TestBean bean = lbf.getBean(TestBean.class);
assertThat(bean.getBeanName()).isEqualTo("bd3");
assertThat(lbf.containsSingleton("bd1")).isFalse();
}
@Test
void getBeanByTypeWithUniqueNonDefaultDefinition() {
RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class);
bd1.setDefaultCandidate(false);
bd1.setLazyInit(true);
RootBeanDefinition bd2 = new RootBeanDefinition(TestBean.class);
lbf.registerBeanDefinition("bd1", bd1);
lbf.registerBeanDefinition("bd2", bd2);
TestBean bean = lbf.getBean(TestBean.class);
assertThat(bean.getBeanName()).isEqualTo("bd2");
assertThat(lbf.containsSingleton("bd1")).isFalse();
}
@Test
void getBeanByTypeWithUniqueNonDefaultSingleton() {
RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class);
bd1.setDefaultCandidate(false);
bd1.setLazyInit(true);
lbf.registerBeanDefinition("bd1", bd1);
lbf.registerSingleton("bd2", new TestBean());
TestBean bean = lbf.getBean(TestBean.class);
assertThat(bean.getBeanName()).isNull();
assertThat(lbf.containsSingleton("bd1")).isFalse();
}
@Test
@SuppressWarnings("rawtypes")
void getFactoryBeanByTypeWithPrimary() {
@@ -1987,6 +1898,7 @@ class DefaultListableBeanFactoryTests {
void getBeanByTypeInstanceWithAmbiguity() {
RootBeanDefinition bd1 = createConstructorDependencyBeanDefinition(99);
RootBeanDefinition bd2 = new RootBeanDefinition(ConstructorDependency.class);
bd2.setScope(BeanDefinition.SCOPE_PROTOTYPE);
bd2.getConstructorArgumentValues().addGenericArgumentValue("43");
lbf.registerBeanDefinition("bd1", bd1);
lbf.registerBeanDefinition("bd2", bd2);
@@ -2022,15 +1934,6 @@ class DefaultListableBeanFactoryTests {
assertThat(resolved).hasSize(2);
assertThat(resolved).contains(lbf.getBean("bd1"));
assertThat(resolved).contains(lbf.getBean("bd2"));
resolved = provider.stream(ObjectProvider.UNFILTERED).collect(Collectors.toSet());
assertThat(resolved).hasSize(2);
assertThat(resolved).contains(lbf.getBean("bd1"));
assertThat(resolved).contains(lbf.getBean("bd2"));
resolved = provider.stream(ObjectProvider.UNFILTERED, false).collect(Collectors.toSet());
assertThat(resolved).hasSize(1);
assertThat(resolved).contains(lbf.getBean("bd2"));
}
@Test
@@ -2080,14 +1983,6 @@ class DefaultListableBeanFactoryTests {
assertThat(resolved).hasSize(2);
assertThat(resolved).contains(lbf.getBean("bd1"));
assertThat(resolved).contains(lbf.getBean("bd2"));
resolved = provider.stream(ObjectProvider.UNFILTERED).collect(Collectors.toSet());
assertThat(resolved).hasSize(2);
assertThat(resolved).contains(lbf.getBean("bd1"));
assertThat(resolved).contains(lbf.getBean("bd2"));
resolved = provider.stream(ObjectProvider.UNFILTERED, false).collect(Collectors.toSet());
assertThat(resolved).isEmpty();
}
@Test
@@ -2483,20 +2378,11 @@ class DefaultListableBeanFactoryTests {
parentBf.registerBeanDefinition("highPriorityTestBean", bd2);
ObjectProvider<TestBean> testBeanProvider = lbf.getBeanProvider(ResolvableType.forClass(TestBean.class));
assertThat(testBeanProvider.orderedStream()).containsExactly(
List<TestBean> resolved = testBeanProvider.orderedStream().toList();
assertThat(resolved).containsExactly(
lbf.getBean("highPriorityTestBean", TestBean.class),
lbf.getBean("lowPriorityTestBean", TestBean.class),
lbf.getBean("plainTestBean", TestBean.class));
assertThat(testBeanProvider.orderedStream(clazz -> clazz != TestBean.class).toList()).containsExactly(
lbf.getBean("highPriorityTestBean", TestBean.class),
lbf.getBean("lowPriorityTestBean", TestBean.class));
assertThat(testBeanProvider.stream()).containsExactly(
lbf.getBean("plainTestBean", TestBean.class),
lbf.getBean("lowPriorityTestBean", TestBean.class),
lbf.getBean("highPriorityTestBean", TestBean.class));
assertThat(testBeanProvider.orderedStream(clazz -> clazz != TestBean.class).toList()).containsExactly(
lbf.getBean("lowPriorityTestBean", TestBean.class),
lbf.getBean("highPriorityTestBean", TestBean.class));
}
@Test
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -48,7 +48,6 @@ import org.mockito.Mockito;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
@@ -65,8 +64,6 @@ import org.springframework.beans.factory.support.AutowireCandidateQualifier;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.support.SimpleAutowireCandidateResolver;
import org.springframework.beans.testfixture.beans.DerivedTestBean;
import org.springframework.beans.testfixture.beans.ITestBean;
import org.springframework.beans.testfixture.beans.IndexedTestBean;
import org.springframework.beans.testfixture.beans.NestedTestBean;
@@ -1608,16 +1605,8 @@ class AutowiredAnnotationBeanPostProcessorTests {
assertThat(testBeans).containsExactly(bf.getBean("testBean1", TestBean.class), bf.getBean("testBean2", TestBean.class));
testBeans = bean.streamTestBeans();
assertThat(testBeans).containsExactly(bf.getBean("testBean1", TestBean.class), bf.getBean("testBean2", TestBean.class));
testBeans = bean.streamTestBeansInOrder();
testBeans = bean.sortedTestBeans();
assertThat(testBeans).containsExactly(bf.getBean("testBean1", TestBean.class), bf.getBean("testBean2", TestBean.class));
testBeans = bean.allTestBeans();
assertThat(testBeans).containsExactly(bf.getBean("testBean1", TestBean.class), bf.getBean("testBean2", TestBean.class));
testBeans = bean.allTestBeansInOrder();
assertThat(testBeans).containsExactly(bf.getBean("testBean1", TestBean.class), bf.getBean("testBean2", TestBean.class));
testBeans = bean.allSingletonBeans();
assertThat(testBeans).isEmpty();
testBeans = bean.allSingletonBeansInOrder();
assertThat(testBeans).isEmpty();
}
@Test
@@ -1643,19 +1632,7 @@ class AutowiredAnnotationBeanPostProcessorTests {
testBeans = bean.streamTestBeans();
assertThat(testBeans).hasSize(1);
assertThat(testBeans).contains(bf.getBean("testBean", TestBean.class));
testBeans = bean.streamTestBeansInOrder();
assertThat(testBeans).hasSize(1);
assertThat(testBeans).contains(bf.getBean("testBean", TestBean.class));
testBeans = bean.allTestBeans();
assertThat(testBeans).hasSize(1);
assertThat(testBeans).contains(bf.getBean("testBean", TestBean.class));
testBeans = bean.allTestBeansInOrder();
assertThat(testBeans).hasSize(1);
assertThat(testBeans).contains(bf.getBean("testBean", TestBean.class));
testBeans = bean.allSingletonBeans();
assertThat(testBeans).hasSize(1);
assertThat(testBeans).contains(bf.getBean("testBean", TestBean.class));
testBeans = bean.allSingletonBeansInOrder();
testBeans = bean.sortedTestBeans();
assertThat(testBeans).hasSize(1);
assertThat(testBeans).contains(bf.getBean("testBean", TestBean.class));
}
@@ -1679,15 +1656,7 @@ class AutowiredAnnotationBeanPostProcessorTests {
assertThat(testBeans).isEmpty();
testBeans = bean.streamTestBeans();
assertThat(testBeans).isEmpty();
testBeans = bean.streamTestBeansInOrder();
assertThat(testBeans).isEmpty();
testBeans = bean.allTestBeans();
assertThat(testBeans).isEmpty();
testBeans = bean.allTestBeansInOrder();
assertThat(testBeans).isEmpty();
testBeans = bean.allSingletonBeans();
assertThat(testBeans).isEmpty();
testBeans = bean.allSingletonBeansInOrder();
testBeans = bean.sortedTestBeans();
assertThat(testBeans).isEmpty();
}
@@ -1709,11 +1678,7 @@ class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.iterateTestBeans()).containsExactly(testBean1, testBean2);
assertThat(bean.forEachTestBeans()).containsExactly(testBean1, testBean2);
assertThat(bean.streamTestBeans()).containsExactly(testBean1, testBean2);
assertThat(bean.streamTestBeansInOrder()).containsExactly(testBean1, testBean2);
assertThat(bean.allTestBeans()).containsExactly(testBean1, testBean2);
assertThat(bean.allTestBeansInOrder()).containsExactly(testBean1, testBean2);
assertThat(bean.allSingletonBeans()).containsExactly(testBean1, testBean2);
assertThat(bean.allSingletonBeansInOrder()).containsExactly(testBean1, testBean2);
assertThat(bean.sortedTestBeans()).containsExactly(testBean1, testBean2);
}
@Test
@@ -1741,11 +1706,7 @@ class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.iterateTestBeans()).containsExactly(testBean1, testBean2);
assertThat(bean.forEachTestBeans()).containsExactly(testBean1, testBean2);
assertThat(bean.streamTestBeans()).containsExactly(testBean1, testBean2);
assertThat(bean.streamTestBeansInOrder()).containsExactly(testBean2, testBean1);
assertThat(bean.allTestBeans()).containsExactly(testBean1, testBean2);
assertThat(bean.allTestBeansInOrder()).containsExactly(testBean2, testBean1);
assertThat(bean.allSingletonBeans()).containsExactly(testBean1, testBean2);
assertThat(bean.allSingletonBeansInOrder()).containsExactly(testBean2, testBean1);
assertThat(bean.sortedTestBeans()).containsExactly(testBean2, testBean1);
}
@Test
@@ -1757,67 +1718,12 @@ class AutowiredAnnotationBeanPostProcessorTests {
bf.registerBeanDefinition("testBean1", tb1);
RootBeanDefinition tb2 = new RootBeanDefinition(TestBeanFactory.class);
tb2.setFactoryMethodName("newTestBean2");
tb2.setScope(BeanDefinition.SCOPE_PROTOTYPE);
tb2.setLazyInit(true);
bf.registerBeanDefinition("testBean2", tb2);
ObjectProviderInjectionBean bean = bf.getBean("annotatedBean", ObjectProviderInjectionBean.class);
assertThat(bean.streamTestBeansInOrder()).containsExactly(bf.getBean("testBean2", TestBean.class),
assertThat(bean.sortedTestBeans()).containsExactly(bf.getBean("testBean2", TestBean.class),
bf.getBean("testBean1", TestBean.class));
assertThat(bean.allTestBeansInOrder()).containsExactly(bf.getBean("testBean2", TestBean.class),
bf.getBean("testBean1", TestBean.class));
assertThat(bean.allSingletonBeansInOrder()).containsExactly(bf.getBean("testBean1", TestBean.class));
}
@Test
void objectProviderInjectionWithNonCandidatesInStream() {
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectProviderInjectionBean.class));
RootBeanDefinition tb1 = new RootBeanDefinition(TestBeanFactory.class);
tb1.setFactoryMethodName("newTestBean1");
bf.registerBeanDefinition("testBean1", tb1);
RootBeanDefinition tb2 = new RootBeanDefinition(TestBeanFactory.class);
tb2.setFactoryMethodName("newTestBean2");
tb2.setScope(BeanDefinition.SCOPE_PROTOTYPE);
bf.registerBeanDefinition("testBean2", tb2);
DefaultListableBeanFactory parent = new DefaultListableBeanFactory();
RootBeanDefinition tb3 = new RootBeanDefinition(TestBean.class);
tb3.setAutowireCandidate(false);
tb3.setLazyInit(true);
parent.registerBeanDefinition("testBean3", tb3);
RootBeanDefinition tb4 = new RootBeanDefinition(DerivedTestBean.class);
tb4.setDefaultCandidate(false);
tb4.setLazyInit(true);
parent.registerBeanDefinition("testBean4", tb4);
bf.setParentBeanFactory(parent);
RootBeanDefinition tb5 = new RootBeanDefinition(NullFactoryMethods.class);
tb5.setFactoryMethodName("createTestBean");
bf.registerBeanDefinition("testBean5", tb5);
ObjectProviderInjectionBean bean = bf.getBean("annotatedBean", ObjectProviderInjectionBean.class);
assertThat(bean.streamTestBeans()).containsExactly(bf.getBean("testBean1", TestBean.class),
bf.getBean("testBean2", TestBean.class));
assertThat(bean.streamTestBeansInOrder()).containsExactly(bf.getBean("testBean2", TestBean.class),
bf.getBean("testBean1", TestBean.class));
assertThat(bf.containsSingleton("testBean3")).isFalse();
assertThat(bean.plainTestBeans()).containsExactly(bf.getBean("testBean1", TestBean.class),
bf.getBean("testBean2", TestBean.class));
assertThat(bean.plainTestBeansInOrder()).containsExactly(bf.getBean("testBean2", TestBean.class),
bf.getBean("testBean1", TestBean.class));
assertThat(bf.containsSingleton("testBean4")).isFalse();
assertThat(bean.allTestBeans()).containsExactly(bf.getBean("testBean1", TestBean.class),
bf.getBean("testBean2", TestBean.class), bf.getBean("testBean4", TestBean.class));
assertThat(bean.allTestBeansInOrder()).containsExactly(bf.getBean("testBean2", TestBean.class),
bf.getBean("testBean1", TestBean.class), bf.getBean("testBean4", TestBean.class));
assertThat(bean.allSingletonBeans()).containsExactly(bf.getBean("testBean1", TestBean.class),
bf.getBean("testBean4", TestBean.class));
assertThat(bean.allSingletonBeansInOrder()).containsExactly(bf.getBean("testBean1", TestBean.class),
bf.getBean("testBean4", TestBean.class));
Map<String, TestBean> typeMatches = BeanFactoryUtils.beansOfTypeIncludingAncestors(bf, TestBean.class);
assertThat(typeMatches.remove("testBean3")).isNotNull();
Map<String, TestBean> candidates = SimpleAutowireCandidateResolver.resolveAutowireCandidates(bf, TestBean.class);
assertThat(candidates).containsExactlyEntriesOf(typeMatches);
}
@Test
@@ -2394,7 +2300,7 @@ class AutowiredAnnotationBeanPostProcessorTests {
}
@Test
@SuppressWarnings("unchecked")
@SuppressWarnings({ "rawtypes", "unchecked" })
void genericsBasedConstructorInjectionWithNonTypedTarget() {
RootBeanDefinition bd = new RootBeanDefinition(RepositoryConstructorInjectionBean.class);
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
@@ -3398,33 +3304,9 @@ class AutowiredAnnotationBeanPostProcessorTests {
return this.testBean.stream().toList();
}
public List<TestBean> streamTestBeansInOrder() {
public List<TestBean> sortedTestBeans() {
return this.testBean.orderedStream().toList();
}
public List<TestBean> plainTestBeans() {
return this.testBean.stream(clazz -> !DerivedTestBean.class.isAssignableFrom(clazz)).toList();
}
public List<TestBean> plainTestBeansInOrder() {
return this.testBean.orderedStream(clazz -> !DerivedTestBean.class.isAssignableFrom(clazz)).toList();
}
public List<TestBean> allTestBeans() {
return this.testBean.stream(ObjectProvider.UNFILTERED).toList();
}
public List<TestBean> allTestBeansInOrder() {
return this.testBean.orderedStream(ObjectProvider.UNFILTERED).toList();
}
public List<TestBean> allSingletonBeans() {
return this.testBean.stream(ObjectProvider.UNFILTERED, false).toList();
}
public List<TestBean> allSingletonBeansInOrder() {
return this.testBean.orderedStream(ObjectProvider.UNFILTERED, false).toList();
}
}
@@ -71,8 +71,8 @@ class ParameterResolutionTests {
}
private void assertAutowirableParameters(Executable executable) {
int startIndex = (executable instanceof Constructor) &&
ClassUtils.isInnerClass(executable.getDeclaringClass()) ? 1 : 0;
int startIndex = (executable instanceof Constructor)
&& ClassUtils.isInnerClass(executable.getDeclaringClass()) ? 1 : 0;
Parameter[] parameters = executable.getParameters();
for (int parameterIndex = startIndex; parameterIndex < parameters.length; parameterIndex++) {
Parameter parameter = parameters[parameterIndex];
@@ -103,8 +103,8 @@ class BeanInstanceSupplierTests {
RegisteredBean registerBean = source.registerBean(this.beanFactory);
assertThatIllegalArgumentException()
.isThrownBy(() -> resolver.get(registerBean)).withMessage(
"Constructor with parameter types [java.io.InputStream] cannot be found on " +
SingleArgConstructor.class.getName());
"Constructor with parameter types [java.io.InputStream] cannot be found on "
+ SingleArgConstructor.class.getName());
}
@Test
@@ -151,8 +151,8 @@ class BeanInstanceSupplierTests {
RegisteredBean registerBean = source.registerBean(this.beanFactory);
assertThatIllegalArgumentException()
.isThrownBy(() -> resolver.get(registerBean)).withMessage(
"Factory method 'single' with parameter types [java.io.InputStream] declared on class " +
SingleArgFactory.class.getName() + " cannot be found");
"Factory method 'single' with parameter types [java.io.InputStream] declared on class "
+ SingleArgFactory.class.getName() + " cannot be found");
}
@Test
@@ -86,8 +86,8 @@ class DefaultBeanRegistrationCodeFragmentsTests {
BeanRegistrationCodeFragments codeFragments = createInstance(registeredBean);
assertThatExceptionOfType(AotBeanProcessingException.class)
.isThrownBy(() -> codeFragments.getTarget(registeredBean))
.withMessageContaining("Error processing bean with name 'testBean' defined in my test resource: " +
"instance supplier is not supported");
.withMessageContaining("Error processing bean with name 'testBean' defined in my test resource: "
+ "instance supplier is not supported");
}
@Test
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -50,15 +50,10 @@ class DefaultSingletonBeanRegistryTests {
assertThat(beanRegistry.getSingleton("tb2")).isSameAs(tb2);
assertThat(tb2Flag.get()).isTrue();
TestBean tb3 = (TestBean) beanRegistry.getSingleton("tb3", () -> {
TestBean newTb = new TestBean();
beanRegistry.registerSingleton("tb3", newTb);
return newTb;
});
assertThat(beanRegistry.getSingleton("tb3")).isSameAs(tb3);
assertThat(beanRegistry.getSingletonCount()).isEqualTo(3);
assertThat(beanRegistry.getSingletonNames()).containsExactly("tb", "tb2", "tb3");
assertThat(beanRegistry.getSingleton("tb")).isSameAs(tb);
assertThat(beanRegistry.getSingleton("tb2")).isSameAs(tb2);
assertThat(beanRegistry.getSingletonCount()).isEqualTo(2);
assertThat(beanRegistry.getSingletonNames()).containsExactly("tb", "tb2");
beanRegistry.destroySingletons();
assertThat(beanRegistry.getSingletonCount()).isZero();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-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.
@@ -21,7 +21,6 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -50,8 +49,6 @@ public class IndexedTestBean {
private SortedMap sortedMap;
private IterableMap iterableMap;
private MyTestBeans myTestBeans;
@@ -76,9 +73,6 @@ public class IndexedTestBean {
TestBean tb6 = new TestBean("name6", 0);
TestBean tb7 = new TestBean("name7", 0);
TestBean tb8 = new TestBean("name8", 0);
TestBean tbA = new TestBean("nameA", 0);
TestBean tbB = new TestBean("nameB", 0);
TestBean tbC = new TestBean("nameC", 0);
TestBean tbX = new TestBean("nameX", 0);
TestBean tbY = new TestBean("nameY", 0);
TestBean tbZ = new TestBean("nameZ", 0);
@@ -94,12 +88,6 @@ public class IndexedTestBean {
this.map.put("key2", tb5);
this.map.put("key.3", tb5);
List list = new ArrayList();
list.add(tbA);
list.add(tbB);
this.iterableMap = new IterableMap<>();
this.iterableMap.put("key1", tbC);
this.iterableMap.put("key2", list);
list = new ArrayList();
list.add(tbX);
list.add(tbY);
this.map.put("key4", list);
@@ -164,14 +152,6 @@ public class IndexedTestBean {
this.sortedMap = sortedMap;
}
public IterableMap getIterableMap() {
return this.iterableMap;
}
public void setIterableMap(IterableMap iterableMap) {
this.iterableMap = iterableMap;
}
public MyTestBeans getMyTestBeans() {
return myTestBeans;
}
@@ -181,15 +161,6 @@ public class IndexedTestBean {
}
@SuppressWarnings("serial")
public static class IterableMap<K,V> extends LinkedHashMap<K,V> implements Iterable<V> {
@Override
public Iterator<V> iterator() {
return values().iterator();
}
}
public static class MyTestBeans implements Iterable<TestBean> {
private final Collection<TestBean> testBeans;
@@ -93,8 +93,8 @@ class MetadataCollector {
private boolean shouldBeMerged(ItemMetadata itemMetadata) {
String sourceType = itemMetadata.getType();
return (sourceType != null && !deletedInCurrentBuild(sourceType) &&
!processedInCurrentBuild(sourceType));
return (sourceType != null && !deletedInCurrentBuild(sourceType)
&& !processedInCurrentBuild(sourceType));
}
private boolean deletedInCurrentBuild(String sourceType) {
@@ -42,8 +42,8 @@ class Metadata {
ItemMetadata itemMetadata = metadata.getItems().stream()
.filter(item -> item.getType().equals(type))
.findFirst().orElse(null);
return (itemMetadata != null && itemMetadata.getStereotypes().size() == stereotypes.size() &&
itemMetadata.getStereotypes().containsAll(stereotypes));
return itemMetadata != null && itemMetadata.getStereotypes().size() == stereotypes.size()
&& itemMetadata.getStereotypes().containsAll(stereotypes);
}, "Candidates with type %s and stereotypes %s", type, stereotypes);
}
@@ -79,8 +79,8 @@ abstract class AbstractJCacheKeyOperation<A extends Annotation> extends Abstract
for (CacheParameterDetail keyParameterDetail : this.keyParameterDetails) {
int parameterPosition = keyParameterDetail.getParameterPosition();
if (parameterPosition >= values.length) {
throw new IllegalStateException("Values mismatch, key parameter at position " +
parameterPosition + " cannot be matched against " + values.length + " value(s)");
throw new IllegalStateException("Values mismatch, key parameter at position "
+ parameterPosition + " cannot be matched against " + values.length + " value(s)");
}
result.add(keyParameterDetail.toCacheInvocationParameter(values[parameterPosition]));
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
package org.springframework.cache.jcache.interceptor;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicLong;
import javax.cache.annotation.CacheDefaults;
@@ -149,9 +150,9 @@ class JCacheKeyGeneratorTests {
@Override
public Object generate(Object target, Method method, Object... params) {
assertThat(params).as("Unexpected parameters").isEqualTo(expectedParams);
assertThat(Arrays.equals(expectedParams, params)).as("Unexpected parameters: expected: "
+ Arrays.toString(this.expectedParams) + " but got: " + Arrays.toString(params)).isTrue();
return new SimpleKey(params);
}
}
}
@@ -288,8 +288,8 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
}
}
catch (NoSuchBeanDefinitionException ex) {
throw new NoSuchBeanDefinitionException(CacheManager.class, "no CacheResolver specified - " +
"register a CacheManager bean or remove the @EnableCaching annotation from your configuration.");
throw new NoSuchBeanDefinitionException(CacheManager.class, "no CacheResolver specified - "
+ "register a CacheManager bean or remove the @EnableCaching annotation from your configuration.");
}
}
this.initialized = true;
@@ -85,8 +85,8 @@ public class NameMatchCacheOperationSource implements CacheOperationSource, Seri
// Look for most specific name match.
String bestNameMatch = null;
for (String mappedName : this.nameMap.keySet()) {
if (isMatch(methodName, mappedName) &&
(bestNameMatch == null || bestNameMatch.length() <= mappedName.length())) {
if (isMatch(methodName, mappedName)
&& (bestNameMatch == null || bestNameMatch.length() <= mappedName.length())) {
ops = this.nameMap.get(mappedName);
bestNameMatch = mappedName;
}
@@ -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.
@@ -17,7 +17,6 @@
package org.springframework.context.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
@@ -34,7 +33,6 @@ import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.core.annotation.AliasFor;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.MergedAnnotation;
import org.springframework.core.annotation.MergedAnnotation.Adapt;
@@ -43,7 +41,6 @@ import org.springframework.core.type.AnnotationMetadata;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
@@ -150,26 +147,16 @@ public class AnnotationBeanNameGenerator implements BeanNameGenerator {
Set<String> metaAnnotationTypes = this.metaAnnotationTypesCache.computeIfAbsent(annotationType,
key -> getMetaAnnotationTypes(mergedAnnotation));
if (isStereotypeWithNameValue(annotationType, metaAnnotationTypes, attributes)) {
Object value = attributes.get(MergedAnnotation.VALUE);
Object value = attributes.get("value");
if (value instanceof String currentName && !currentName.isBlank()) {
if (conventionBasedStereotypeCheckCache.add(annotationType) &&
metaAnnotationTypes.contains(COMPONENT_ANNOTATION_CLASSNAME) && logger.isWarnEnabled()) {
if (hasExplicitlyAliasedValueAttribute(mergedAnnotation.getType())) {
logger.warn("""
Although the 'value' attribute in @%s declares @AliasFor for an attribute \
other than @Component's 'value' attribute, the value is still used as the \
@Component name based on convention. As of Spring Framework 7.0, such a \
'value' attribute will no longer be used as the @Component name."""
.formatted(annotationType));
}
else {
logger.warn("""
Support for convention-based @Component names is deprecated and will \
be removed in a future version of the framework. Please annotate the \
'value' attribute in @%s with @AliasFor(annotation=Component.class) \
to declare an explicit alias for @Component's 'value' attribute."""
.formatted(annotationType));
}
logger.warn("""
Support for convention-based stereotype names is deprecated and will \
be removed in a future version of the framework. Please annotate the \
'value' attribute in @%s with @AliasFor(annotation=Component.class) \
to declare an explicit alias for @Component's 'value' attribute."""
.formatted(annotationType));
}
if (beanName != null && !currentName.equals(beanName)) {
throw new IllegalStateException("Stereotype annotations suggest inconsistent " +
@@ -237,7 +224,7 @@ public class AnnotationBeanNameGenerator implements BeanNameGenerator {
annotationType.equals("jakarta.inject.Named") ||
annotationType.equals("javax.inject.Named");
return (isStereotype && attributes.containsKey(MergedAnnotation.VALUE));
return (isStereotype && attributes.containsKey("value"));
}
/**
@@ -268,14 +255,4 @@ public class AnnotationBeanNameGenerator implements BeanNameGenerator {
return StringUtils.uncapitalizeAsProperty(shortClassName);
}
/**
* Determine if the supplied annotation type declares a {@code value()} attribute
* with an explicit alias configured via {@link AliasFor @AliasFor}.
* @since 6.2.3
*/
private static boolean hasExplicitlyAliasedValueAttribute(Class<? extends Annotation> annotationType) {
Method valueAttribute = ReflectionUtils.findMethod(annotationType, MergedAnnotation.VALUE);
return (valueAttribute != null && valueAttribute.isAnnotationPresent(AliasFor.class));
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -565,10 +565,9 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
}
/**
* Determine whether the given bean definition qualifies as a candidate component.
* <p>The default implementation checks whether the class is not dependent on an
* enclosing class as well as whether the class is either concrete (and therefore
* not an interface) or has {@link Lookup @Lookup} methods.
* Determine whether the given bean definition qualifies as candidate.
* <p>The default implementation checks whether the class is not an interface
* and not dependent on an enclosing class.
* <p>Can be overridden in subclasses.
* @param beanDefinition the bean definition to check
* @return whether the bean definition qualifies as a candidate component
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -203,15 +203,6 @@ final class ConfigurationClass {
return this.beanMethods;
}
boolean hasNonStaticBeanMethods() {
for (BeanMethod beanMethod : this.beanMethods) {
if (!beanMethod.getMetadata().isStatic()) {
return true;
}
}
return false;
}
void addImportedResource(String importedResource, Class<? extends BeanDefinitionReader> readerClass) {
this.importedResources.put(importedResource, readerClass);
}
@@ -232,9 +223,8 @@ final class ConfigurationClass {
void validate(ProblemReporter problemReporter) {
Map<String, Object> attributes = this.metadata.getAnnotationAttributes(Configuration.class.getName());
// A configuration class may not be final (CGLIB limitation) unless it does not have to proxy bean methods
if (attributes != null && (Boolean) attributes.get("proxyBeanMethods") && hasNonStaticBeanMethods() &&
this.metadata.isFinal()) {
// A configuration class may not be final (CGLIB limitation) unless it declares proxyBeanMethods=false
if (attributes != null && (Boolean) attributes.get("proxyBeanMethods") && this.metadata.isFinal()) {
problemReporter.error(new FinalConfigurationProblem());
}
@@ -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.
@@ -109,15 +109,8 @@ class ConfigurationClassEnhancer {
}
return configClass;
}
try {
// Use original ClassLoader if config class not locally loaded in overriding class loader
boolean classLoaderMismatch = (classLoader != null && classLoader != configClass.getClassLoader());
if (classLoaderMismatch && classLoader instanceof SmartClassLoader smartClassLoader) {
classLoader = smartClassLoader.getOriginalClassLoader();
}
Enhancer enhancer = newEnhancer(configClass, classLoader);
Class<?> enhancedClass = createClass(enhancer, classLoaderMismatch);
Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader));
if (logger.isTraceEnabled()) {
logger.trace(String.format("Successfully enhanced %s; enhanced class name is: %s",
configClass.getName(), enhancedClass.getName()));
@@ -136,9 +129,6 @@ class ConfigurationClassEnhancer {
*/
private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
Enhancer enhancer = new Enhancer();
if (classLoader != null) {
enhancer.setClassLoader(classLoader);
}
enhancer.setSuperclass(configSuperClass);
enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
enhancer.setUseFactory(false);
@@ -162,21 +152,8 @@ class ConfigurationClassEnhancer {
* Uses enhancer to generate a subclass of superclass,
* ensuring that callbacks are registered for the new subclass.
*/
private Class<?> createClass(Enhancer enhancer, boolean fallback) {
Class<?> subclass;
try {
subclass = enhancer.createClass();
}
catch (Throwable ex) {
if (!fallback) {
throw (ex instanceof CodeGenerationException cgex ? cgex : new CodeGenerationException(ex));
}
// Possibly a package-visible @Bean method declaration not accessible
// in the given ClassLoader -> retry with original ClassLoader
enhancer.setClassLoader(null);
subclass = enhancer.createClass();
}
private Class<?> createClass(Enhancer enhancer) {
Class<?> subclass = enhancer.createClass();
// Registering callbacks statically (as opposed to thread-local)
// is critical for usage in an OSGi environment (SPR-5932)...
Enhancer.registerStaticCallbacks(subclass, CALLBACKS);
@@ -187,7 +164,8 @@ class ConfigurationClassEnhancer {
/**
* Marker interface to be implemented by all @Configuration CGLIB subclasses.
* Facilitates idempotent behavior for {@link ConfigurationClassEnhancer#enhance}
* through checking to see if candidate classes are already assignable to it.
* through checking to see if candidate classes are already assignable to it, for example,
* have already been enhanced.
* <p>Also extends {@link BeanFactoryAware}, as all enhanced {@code @Configuration}
* classes require access to the {@link BeanFactory} that created them.
* <p>Note that this interface is intended for framework-internal use only, however
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -105,8 +105,8 @@ class ConfigurationClassParser {
(className.startsWith("java.lang.annotation.") || className.startsWith("org.springframework.stereotype."));
private static final Predicate<Condition> REGISTER_BEAN_CONDITION_FILTER = condition ->
(condition instanceof ConfigurationCondition configurationCondition &&
ConfigurationPhase.REGISTER_BEAN.equals(configurationCondition.getConfigurationPhase()));
(condition instanceof ConfigurationCondition configurationCondition
&& ConfigurationPhase.REGISTER_BEAN.equals(configurationCondition.getConfigurationPhase()));
private static final Comparator<DeferredImportSelectorHolder> DEFERRED_IMPORT_COMPARATOR =
(o1, o2) -> AnnotationAwareOrderComparator.INSTANCE.compare(o1.getImportSelector(), o2.getImportSelector());
@@ -167,22 +167,14 @@ class ConfigurationClassParser {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
ConfigurationClass configClass;
if (bd instanceof AnnotatedBeanDefinition annotatedBeanDef) {
configClass = parse(annotatedBeanDef, holder.getBeanName());
parse(annotatedBeanDef, holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition abstractBeanDef && abstractBeanDef.hasBeanClass()) {
configClass = parse(abstractBeanDef.getBeanClass(), holder.getBeanName());
parse(abstractBeanDef.getBeanClass(), holder.getBeanName());
}
else {
configClass = parse(bd.getBeanClassName(), holder.getBeanName());
}
// Downgrade to lite (no enhancement) in case of no instance-level @Bean methods.
if (!configClass.hasNonStaticBeanMethods() && ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(
bd.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE))) {
bd.setAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE,
ConfigurationClassUtils.CONFIGURATION_CLASS_LITE);
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
@@ -197,25 +189,20 @@ class ConfigurationClassParser {
this.deferredImportSelectorHandler.process();
}
private ConfigurationClass parse(AnnotatedBeanDefinition beanDef, String beanName) {
ConfigurationClass configClass = new ConfigurationClass(
beanDef.getMetadata(), beanName, (beanDef instanceof ScannedGenericBeanDefinition));
processConfigurationClass(configClass, DEFAULT_EXCLUSION_FILTER);
return configClass;
private void parse(AnnotatedBeanDefinition beanDef, String beanName) {
processConfigurationClass(
new ConfigurationClass(beanDef.getMetadata(), beanName, (beanDef instanceof ScannedGenericBeanDefinition)),
DEFAULT_EXCLUSION_FILTER);
}
private ConfigurationClass parse(Class<?> clazz, String beanName) {
ConfigurationClass configClass = new ConfigurationClass(clazz, beanName);
processConfigurationClass(configClass, DEFAULT_EXCLUSION_FILTER);
return configClass;
private void parse(Class<?> clazz, String beanName) {
processConfigurationClass(new ConfigurationClass(clazz, beanName), DEFAULT_EXCLUSION_FILTER);
}
final ConfigurationClass parse(@Nullable String className, String beanName) throws IOException {
final void parse(@Nullable String className, String beanName) throws IOException {
Assert.notNull(className, "No bean class name for configuration class bean definition");
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
ConfigurationClass configClass = new ConfigurationClass(reader, beanName);
processConfigurationClass(configClass, DEFAULT_EXCLUSION_FILTER);
return configClass;
processConfigurationClass(new ConfigurationClass(reader, beanName), DEFAULT_EXCLUSION_FILTER);
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-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.
@@ -150,12 +150,12 @@ class ApplicationContextInitializationCodeGenerator implements BeanFactoryInitia
@Nullable
private CodeBlock apply(ClassName className) {
String name = className.canonicalName();
if (name.equals(DefaultListableBeanFactory.class.getName()) ||
name.equals(ConfigurableListableBeanFactory.class.getName())) {
if (name.equals(DefaultListableBeanFactory.class.getName())
|| name.equals(ConfigurableListableBeanFactory.class.getName())) {
return CodeBlock.of(BEAN_FACTORY_VARIABLE);
}
else if (name.equals(ConfigurableEnvironment.class.getName()) ||
name.equals(Environment.class.getName())) {
else if (name.equals(ConfigurableEnvironment.class.getName())
|| name.equals(Environment.class.getName())) {
return CodeBlock.of("$L.getEnvironment()", APPLICATION_CONTEXT_VARIABLE);
}
else if (name.equals(ResourceLoader.class.getName())) {
@@ -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.
@@ -201,8 +201,8 @@ public class StandardBeanExpressionResolver implements BeanExpressionResolver {
try {
int maxLength = Integer.parseInt(value.trim());
Assert.isTrue(maxLength > 0, () -> "Value [" + maxLength + "] for system property [" +
MAX_SPEL_EXPRESSION_LENGTH_PROPERTY_NAME + "] must be positive");
Assert.isTrue(maxLength > 0, () -> "Value [" + maxLength + "] for system property ["
+ MAX_SPEL_EXPRESSION_LENGTH_PROPERTY_NAME + "] must be positive");
return maxLength;
}
catch (NumberFormatException ex) {
@@ -493,8 +493,8 @@ final class PostProcessorRegistrationDelegate {
private void postProcessValue(List<MergedBeanDefinitionPostProcessor> postProcessors,
BeanDefinitionValueResolver valueResolver, @Nullable Object value) {
if (value instanceof BeanDefinitionHolder bdh &&
bdh.getBeanDefinition() instanceof AbstractBeanDefinition innerBd) {
if (value instanceof BeanDefinitionHolder bdh
&& bdh.getBeanDefinition() instanceof AbstractBeanDefinition innerBd) {
Class<?> innerBeanType = resolveBeanType(innerBd);
resolveInnerBeanDefinition(valueResolver, innerBd, (innerBeanName, innerBeanDefinition)
@@ -143,8 +143,8 @@ public abstract class DurationFormatterUtils {
private static final Pattern ISO_8601_PATTERN = Pattern.compile("^[+-]?[pP].*$");
private static final Pattern SIMPLE_PATTERN = Pattern.compile("^([+-]?\\d+)([a-zA-Z]{0,2})$");
private static final Pattern COMPOSITE_PATTERN = Pattern.compile("^([+-]?)\\(?\\s?(\\d+d)?\\s?(\\d+h)?\\s?(\\d+m)?" +
"\\s?(\\d+s)?\\s?(\\d+ms)?\\s?(\\d+us)?\\s?(\\d+ns)?\\)?$");
private static final Pattern COMPOSITE_PATTERN = Pattern.compile("^([+-]?)\\(?\\s?(\\d+d)?\\s?(\\d+h)?\\s?(\\d+m)?"
+ "\\s?(\\d+s)?\\s?(\\d+ms)?\\s?(\\d+us)?\\s?(\\d+ns)?\\)?$");
private static Duration parseIso8601(String value) {
try {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,7 +18,6 @@ package org.springframework.scheduling.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
@@ -54,7 +53,6 @@ public class ExecutorBeanDefinitionParser extends AbstractSingleBeanDefinitionPa
if (StringUtils.hasText(poolSize)) {
builder.addPropertyValue("poolSize", poolSize);
}
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
}
private void configureRejectionPolicy(Element element, BeanDefinitionBuilder builder) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,7 +18,6 @@ package org.springframework.scheduling.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.util.StringUtils;
@@ -42,7 +41,6 @@ public class SchedulerBeanDefinitionParser extends AbstractSingleBeanDefinitionP
if (StringUtils.hasText(poolSize)) {
builder.addPropertyValue("poolSize", poolSize);
}
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
}
}
@@ -48,8 +48,8 @@ public class DefaultScheduledTaskObservationConvention implements ScheduledTaskO
@Override
public String getContextualName(ScheduledTaskObservationContext context) {
return "task " + StringUtils.uncapitalize(context.getTargetClass().getSimpleName()) +
"." + context.getMethod().getName();
return "task " + StringUtils.uncapitalize(context.getTargetClass().getSimpleName())
+ "." + context.getMethod().getName();
}
@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.
@@ -317,16 +317,8 @@ final class QuartzCronField extends CronField {
private static TemporalAdjuster dayOfWeekInMonth(int ordinal, DayOfWeek dayOfWeek) {
TemporalAdjuster adjuster = TemporalAdjusters.dayOfWeekInMonth(ordinal, dayOfWeek);
return temporal -> {
// TemporalAdjusters can overflow to a different month
// in this case, attempt the same adjustment with the next/previous month
for (int i = 0; i < 12; i++) {
Temporal result = adjuster.adjustInto(temporal);
if (result.get(ChronoField.MONTH_OF_YEAR) == temporal.get(ChronoField.MONTH_OF_YEAR)) {
return rollbackToMidnight(temporal, result);
}
temporal = result;
}
return null;
Temporal result = adjuster.adjustInto(temporal);
return rollbackToMidnight(temporal, result);
};
}
@@ -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.
@@ -142,10 +142,6 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
*/
protected static final Log logger = LogFactory.getLog(DataBinder.class);
/** Internal constant for constructor binding via "[]". */
private static final int NO_INDEX = -1;
@Nullable
private Object target;
@@ -968,7 +964,7 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
value = createMap(paramPath, paramType, resolvableType, valueResolver);
}
else if (paramType.isArray()) {
value = createArray(paramPath, paramType, resolvableType, valueResolver);
value = createArray(paramPath, resolvableType, valueResolver);
}
}
@@ -985,9 +981,11 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
}
}
catch (TypeMismatchException ex) {
ex.initPropertyName(paramPath);
args[i] = null;
failedParamNames.add(paramPath);
handleTypeMismatchException(ex, paramPath, paramType, value);
getBindingResult().recordFieldValue(paramPath, paramType, value);
getBindingErrorProcessor().processPropertyAccessException(ex, getBindingResult());
}
}
}
@@ -1050,8 +1048,9 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
return false;
}
@SuppressWarnings("unchecked")
@Nullable
private List<?> createList(
private <V> List<V> createList(
String paramPath, Class<?> paramType, ResolvableType type, ValueResolver valueResolver) {
ResolvableType elementType = type.getNested(2);
@@ -1059,23 +1058,18 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
if (indexes == null) {
return null;
}
int lastIndex = Math.max(indexes.last(), 0);
int size = (lastIndex < this.autoGrowCollectionLimit ? lastIndex + 1 : 0);
List<?> list = (List<?>) CollectionFactory.createCollection(paramType, size);
int size = (indexes.last() < this.autoGrowCollectionLimit ? indexes.last() + 1 : 0);
List<V> list = (List<V>) CollectionFactory.createCollection(paramType, size);
for (int i = 0; i < size; i++) {
list.add(null);
}
for (int index : indexes) {
String indexedPath = paramPath + "[" + (index != NO_INDEX ? index : "") + "]";
list.set(Math.max(index, 0),
createIndexedValue(paramPath, paramType, elementType, indexedPath, valueResolver));
list.set(index, (V) createObject(elementType, paramPath + "[" + index + "].", valueResolver));
}
return list;
}
@SuppressWarnings("unchecked")
@Nullable
private <V> Map<String, V> createMap(
String paramPath, Class<?> paramType, ResolvableType type, ValueResolver valueResolver) {
@@ -1086,44 +1080,34 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
if (!name.startsWith(paramPath + "[")) {
continue;
}
int startIdx = paramPath.length() + 1;
int endIdx = name.indexOf(']', startIdx);
String nestedPath = ((name.length() > endIdx + 1) ? name.substring(0, endIdx + 2) : "");
boolean quoted = (endIdx - startIdx > 2 && name.charAt(startIdx) == '\'' && name.charAt(endIdx - 1) == '\'');
String key = (quoted ? name.substring(startIdx + 1, endIdx - 1) : name.substring(startIdx, endIdx));
if (map == null) {
map = CollectionFactory.createMap(paramType, 16);
}
String indexedPath = name.substring(0, endIdx + 1);
map.put(key, createIndexedValue(paramPath, paramType, elementType, indexedPath, valueResolver));
if (!map.containsKey(key)) {
map.put(key, (V) createObject(elementType, nestedPath, valueResolver));
}
}
return map;
}
@SuppressWarnings("unchecked")
@Nullable
private <V> V[] createArray(
String paramPath, Class<?> paramType, ResolvableType type, ValueResolver valueResolver) {
private <V> V[] createArray(String paramPath, ResolvableType type, ValueResolver valueResolver) {
ResolvableType elementType = type.getNested(2);
SortedSet<Integer> indexes = getIndexes(paramPath, valueResolver);
if (indexes == null) {
return null;
}
int lastIndex = Math.max(indexes.last(), 0);
int size = (lastIndex < this.autoGrowCollectionLimit ? lastIndex + 1: 0);
int size = (indexes.last() < this.autoGrowCollectionLimit ? indexes.last() + 1: 0);
V[] array = (V[]) Array.newInstance(elementType.resolve(), size);
for (int index : indexes) {
String indexedPath = paramPath + "[" + (index != NO_INDEX ? index : "") + "]";
array[Math.max(index, 0)] =
createIndexedValue(paramPath, paramType, elementType, indexedPath, valueResolver);
array[index] = (V) createObject(elementType, paramPath + "[" + index + "].", valueResolver);
}
return array;
}
@@ -1132,18 +1116,9 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
SortedSet<Integer> indexes = null;
for (String name : valueResolver.getNames()) {
if (name.startsWith(paramPath + "[")) {
int index;
if (paramPath.length() + 2 == name.length()) {
if (!name.endsWith("[]")) {
continue;
}
index = NO_INDEX;
}
else {
int endIndex = name.indexOf(']', paramPath.length() + 2);
String indexValue = name.substring(paramPath.length() + 1, endIndex);
index = Integer.parseInt(indexValue);
}
int endIndex = name.indexOf(']', paramPath.length() + 1);
String rawIndex = name.substring(paramPath.length() + 1, endIndex);
int index = Integer.parseInt(rawIndex);
indexes = (indexes != null ? indexes : new TreeSet<>());
indexes.add(index);
}
@@ -1151,50 +1126,6 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
return indexes;
}
@SuppressWarnings("unchecked")
@Nullable
private <V> V createIndexedValue(
String paramPath, Class<?> containerType, ResolvableType elementType,
String indexedPath, ValueResolver valueResolver) {
Object value = null;
Class<?> elementClass = elementType.resolve(Object.class);
if (List.class.isAssignableFrom(elementClass)) {
value = createList(indexedPath, elementClass, elementType, valueResolver);
}
else if (Map.class.isAssignableFrom(elementClass)) {
value = createMap(indexedPath, elementClass, elementType, valueResolver);
}
else if (elementClass.isArray()) {
value = createArray(indexedPath, elementClass, elementType, valueResolver);
}
else {
Object rawValue = valueResolver.resolveValue(indexedPath, elementClass);
if (rawValue != null) {
try {
value = convertIfNecessary(rawValue, elementClass);
}
catch (TypeMismatchException ex) {
handleTypeMismatchException(ex, paramPath, containerType, rawValue);
}
}
else {
value = createObject(elementType, indexedPath + ".", valueResolver);
}
}
return (V) value;
}
private void handleTypeMismatchException(
TypeMismatchException ex, String paramPath, Class<?> paramType, @Nullable Object value) {
ex.initPropertyName(paramPath);
getBindingResult().recordFieldValue(paramPath, paramType, value);
getBindingErrorProcessor().processPropertyAccessException(ex, getBindingResult());
}
private void validateConstructorArgument(
Class<?> constructorClass, String nestedPath, String name, @Nullable Object value) {
@@ -1481,9 +1412,6 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
/**
* Return the names of all property values.
* <p>Useful for proactive checks whether there are property values nested
* further below the path for a constructor arg. If not then the
* constructor arg can be considered missing and not to be instantiated.
* @since 6.1.2
*/
Set<String> getNames();
@@ -75,7 +75,7 @@
The bean name of the KeyGenerator that is to be used to retrieve the backing caches.
This attribute is not required, and only needs to be specified
explicitly if the default strategy (SimpleKeyGenerator) is not sufficient.
explicitly if the default strategy (DefaultKeyGenerator) is not sufficient.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
@@ -106,8 +106,8 @@ class AspectAndAdvicePrecedenceTests {
private void checkAdvice(String whatJustHappened) {
//System.out.println("[" + adviceInvocationNumber + "] " + whatJustHappened + " ==> " + EXPECTED[adviceInvocationNumber]);
if (adviceInvocationNumber > (EXPECTED.length - 1)) {
throw new AssertionError("Too many advice invocations, expecting " + EXPECTED.length +
" but had " + adviceInvocationNumber);
throw new AssertionError("Too many advice invocations, expecting " + EXPECTED.length
+ " but had " + adviceInvocationNumber);
}
String expecting = EXPECTED[adviceInvocationNumber++];
if (!whatJustHappened.equals(expecting)) {
@@ -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.
@@ -422,7 +422,6 @@ class CglibProxyTests extends AbstractAopProxyTests {
}
@Test // SPR-13328
@SuppressWarnings("unchecked")
void varargsWithEnumArray() {
ProxyFactory proxyFactory = new ProxyFactory(new MyBean());
MyBean proxy = (MyBean) proxyFactory.getProxy();
@@ -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,7 +141,6 @@ class JdkDynamicProxyTests extends AbstractAopProxyTests {
}
@Test // SPR-13328
@SuppressWarnings("unchecked")
void varargsWithEnumArray() {
ProxyFactory proxyFactory = new ProxyFactory(new VarargTestBean());
VarargTestInterface proxy = (VarargTestInterface) proxyFactory.getProxy();
@@ -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.
@@ -168,25 +168,6 @@ class AnnotationBeanNameGeneratorTests {
assertGeneratedName(RestControllerAdviceClass.class, "myRestControllerAdvice");
}
@Test // gh-34317
void generateBeanNameFromStereotypeAnnotationWithStringValueAsExplicitAliasForMetaAnnotationOtherThanComponent() {
// As of Spring Framework 6.2, "enigma" is incorrectly used as the @Component name.
// As of Spring Framework 7.0, the generated name will be "annotationBeanNameGeneratorTests.StereotypeWithoutExplicitName".
assertGeneratedName(StereotypeWithoutExplicitName.class, "enigma");
}
@Test // gh-34317
void generateBeanNameFromStereotypeAnnotationWithStringValueAndExplicitAliasForComponentNameWithBlankName() {
// As of Spring Framework 6.2, "enigma" is incorrectly used as the @Component name.
// As of Spring Framework 7.0, the generated name will be "annotationBeanNameGeneratorTests.StereotypeWithGeneratedName".
assertGeneratedName(StereotypeWithGeneratedName.class, "enigma");
}
@Test // gh-34317
void generateBeanNameFromStereotypeAnnotationWithStringValueAndExplicitAliasForComponentName() {
assertGeneratedName(StereotypeWithExplicitName.class, "explicitName");
}
private void assertGeneratedName(Class<?> clazz, String expectedName) {
BeanDefinition bd = annotatedBeanDef(clazz);
@@ -229,7 +210,7 @@ class AnnotationBeanNameGeneratorTests {
@Retention(RetentionPolicy.RUNTIME)
@Component
@interface ConventionBasedComponent1 {
// This is intentionally convention-based. Please do not add @AliasFor.
// This intentionally convention-based. Please do not add @AliasFor.
// See gh-31093.
String value() default "";
}
@@ -237,7 +218,7 @@ class AnnotationBeanNameGeneratorTests {
@Retention(RetentionPolicy.RUNTIME)
@Component
@interface ConventionBasedComponent2 {
// This is intentionally convention-based. Please do not add @AliasFor.
// This intentionally convention-based. Please do not add @AliasFor.
// See gh-31093.
String value() default "";
}
@@ -279,7 +260,7 @@ class AnnotationBeanNameGeneratorTests {
@Target(ElementType.TYPE)
@Controller
@interface TestRestController {
// This is intentionally convention-based. Please do not add @AliasFor.
// This intentionally convention-based. Please do not add @AliasFor.
// See gh-31093.
String value() default "";
}
@@ -338,6 +319,7 @@ class AnnotationBeanNameGeneratorTests {
String[] basePackages() default {};
}
@TestControllerAdvice(basePackages = "com.example", name = "myControllerAdvice")
static class ControllerAdviceClass {
}
@@ -346,56 +328,4 @@ class AnnotationBeanNameGeneratorTests {
static class RestControllerAdviceClass {
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
@interface MetaAnnotationWithStringAttribute {
String attribute() default "";
}
/**
* Custom stereotype annotation which has a {@code String value} attribute that
* is explicitly declared as an alias for an attribute in a meta-annotation
* other than {@link Component @Component}.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Component
@MetaAnnotationWithStringAttribute
@interface MyStereotype {
@AliasFor(annotation = MetaAnnotationWithStringAttribute.class, attribute = "attribute")
String value() default "";
}
@MyStereotype("enigma")
static class StereotypeWithoutExplicitName {
}
/**
* Custom stereotype annotation which is identical to {@link MyStereotype @MyStereotype}
* except that it has a {@link #name} attribute that is an explicit alias for
* {@link Component#value}.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Component
@MetaAnnotationWithStringAttribute
@interface MyNamedStereotype {
@AliasFor(annotation = MetaAnnotationWithStringAttribute.class, attribute = "attribute")
String value() default "";
@AliasFor(annotation = Component.class, attribute = "value")
String name() default "";
}
@MyNamedStereotype(value = "enigma", name ="explicitName")
static class StereotypeWithExplicitName {
}
@MyNamedStereotype(value = "enigma")
static class StereotypeWithGeneratedName {
}
}
@@ -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.
@@ -19,7 +19,6 @@ package org.springframework.context.annotation;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.testfixture.beans.TestBean;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.testfixture.EnabledForTestGroups;
@@ -34,38 +33,6 @@ import static org.springframework.core.testfixture.TestGroup.LONG_RUNNING;
*/
class BackgroundBootstrapTests {
@Test
@Timeout(5)
@EnabledForTestGroups(LONG_RUNNING)
void bootstrapWithUnmanagedThread() {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(UnmanagedThreadBeanConfig.class);
ctx.getBean("testBean1", TestBean.class);
ctx.getBean("testBean2", TestBean.class);
ctx.close();
}
@Test
@Timeout(5)
@EnabledForTestGroups(LONG_RUNNING)
void bootstrapWithUnmanagedThreads() {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(UnmanagedThreadsBeanConfig.class);
ctx.getBean("testBean1", TestBean.class);
ctx.getBean("testBean2", TestBean.class);
ctx.getBean("testBean3", TestBean.class);
ctx.getBean("testBean4", TestBean.class);
ctx.close();
}
@Test
@Timeout(5)
@EnabledForTestGroups(LONG_RUNNING)
void bootstrapWithCircularReference() {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(CircularReferenceBeanConfig.class);
ctx.getBean("testBean1", TestBean.class);
ctx.getBean("testBean2", TestBean.class);
ctx.close();
}
@Test
@Timeout(5)
@EnabledForTestGroups(LONG_RUNNING)
@@ -74,109 +41,11 @@ class BackgroundBootstrapTests {
ctx.getBean("testBean1", TestBean.class);
ctx.getBean("testBean2", TestBean.class);
ctx.getBean("testBean3", TestBean.class);
ctx.getBean("testBean4", TestBean.class);
ctx.close();
}
@Configuration(proxyBeanMethods = false)
static class UnmanagedThreadBeanConfig {
@Bean
public TestBean testBean1(ObjectProvider<TestBean> testBean2) {
new Thread(testBean2::getObject).start();
try {
Thread.sleep(1000);
}
catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
return new TestBean();
}
@Bean
public TestBean testBean2() {
try {
Thread.sleep(2000);
}
catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
return new TestBean();
}
}
@Configuration(proxyBeanMethods = false)
static class UnmanagedThreadsBeanConfig {
@Bean
public TestBean testBean1(ObjectProvider<TestBean> testBean3, ObjectProvider<TestBean> testBean4) {
new Thread(testBean3::getObject).start();
new Thread(testBean4::getObject).start();
new Thread(testBean3::getObject).start();
new Thread(testBean4::getObject).start();
try {
Thread.sleep(1000);
}
catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
return new TestBean();
}
@Bean
public TestBean testBean2(TestBean testBean4) {
return new TestBean(testBean4);
}
@Bean
public TestBean testBean3(TestBean testBean4) {
return new TestBean(testBean4);
}
@Bean
public TestBean testBean4() {
try {
Thread.sleep(2000);
}
catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
return new TestBean();
}
}
@Configuration(proxyBeanMethods = false)
static class CircularReferenceBeanConfig {
@Bean
public TestBean testBean1(ObjectProvider<TestBean> testBean2) {
new Thread(testBean2::getObject).start();
try {
Thread.sleep(1000);
}
catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
return new TestBean();
}
@Bean
public TestBean testBean2(TestBean testBean1) {
try {
Thread.sleep(2000);
}
catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
return new TestBean();
}
}
@Configuration(proxyBeanMethods = false)
@Configuration
static class CustomExecutorBeanConfig {
@Bean
@@ -189,7 +58,7 @@ class BackgroundBootstrapTests {
}
@Bean(bootstrap = BACKGROUND) @DependsOn("testBean3")
public TestBean testBean1(TestBean testBean3) throws InterruptedException {
public TestBean testBean1(TestBean testBean3) throws InterruptedException{
Thread.sleep(3000);
return new TestBean();
}
@@ -206,8 +75,8 @@ class BackgroundBootstrapTests {
}
@Bean
public TestBean testBean4(@Lazy TestBean testBean1, @Lazy TestBean testBean2, @Lazy TestBean testBean3) {
return new TestBean();
public String dependent(@Lazy TestBean testBean1, @Lazy TestBean testBean2, @Lazy TestBean testBean3) {
return "";
}
}
@@ -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,16 +18,11 @@ package org.springframework.context.annotation;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.ProtectionDomain;
import java.security.SecureClassLoader;
import org.junit.jupiter.api.Test;
import org.springframework.core.OverridingClassLoader;
import org.springframework.core.SmartClassLoader;
import org.springframework.lang.Nullable;
import org.springframework.util.StreamUtils;
import static org.assertj.core.api.Assertions.assertThat;
@@ -41,108 +36,19 @@ class ConfigurationClassEnhancerTests {
@Test
void enhanceReloadedClass() throws Exception {
ConfigurationClassEnhancer configurationClassEnhancer = new ConfigurationClassEnhancer();
ClassLoader parentClassLoader = getClass().getClassLoader();
ClassLoader classLoader = new CustomSmartClassLoader(parentClassLoader);
CustomClassLoader classLoader = new CustomClassLoader(parentClassLoader);
Class<?> myClass = parentClassLoader.loadClass(MyConfig.class.getName());
Class<?> enhancedClass = configurationClassEnhancer.enhance(myClass, parentClassLoader);
assertThat(myClass).isAssignableFrom(enhancedClass);
myClass = classLoader.loadClass(MyConfig.class.getName());
enhancedClass = configurationClassEnhancer.enhance(myClass, classLoader);
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader);
assertThat(myClass).isAssignableFrom(enhancedClass);
}
@Test
void withPublicClass() {
ConfigurationClassEnhancer configurationClassEnhancer = new ConfigurationClassEnhancer();
ClassLoader classLoader = new URLClassLoader(new URL[0], getClass().getClassLoader());
Class<?> enhancedClass = configurationClassEnhancer.enhance(MyConfigWithPublicClass.class, classLoader);
assertThat(MyConfigWithPublicClass.class).isAssignableFrom(enhancedClass);
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader);
classLoader = new OverridingClassLoader(getClass().getClassLoader());
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithPublicClass.class, classLoader);
assertThat(MyConfigWithPublicClass.class).isAssignableFrom(enhancedClass);
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
classLoader = new CustomSmartClassLoader(getClass().getClassLoader());
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithPublicClass.class, classLoader);
assertThat(MyConfigWithPublicClass.class).isAssignableFrom(enhancedClass);
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
classLoader = new BasicSmartClassLoader(getClass().getClassLoader());
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithPublicClass.class, classLoader);
assertThat(MyConfigWithPublicClass.class).isAssignableFrom(enhancedClass);
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
}
@Test
void withNonPublicClass() {
ConfigurationClassEnhancer configurationClassEnhancer = new ConfigurationClassEnhancer();
ClassLoader classLoader = new URLClassLoader(new URL[0], getClass().getClassLoader());
Class<?> enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicClass.class, classLoader);
assertThat(MyConfigWithNonPublicClass.class).isAssignableFrom(enhancedClass);
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
classLoader = new OverridingClassLoader(getClass().getClassLoader());
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicClass.class, classLoader);
assertThat(MyConfigWithNonPublicClass.class).isAssignableFrom(enhancedClass);
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
classLoader = new CustomSmartClassLoader(getClass().getClassLoader());
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicClass.class, classLoader);
assertThat(MyConfigWithNonPublicClass.class).isAssignableFrom(enhancedClass);
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
classLoader = new BasicSmartClassLoader(getClass().getClassLoader());
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicClass.class, classLoader);
assertThat(MyConfigWithNonPublicClass.class).isAssignableFrom(enhancedClass);
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
}
@Test
void withNonPublicMethod() {
ConfigurationClassEnhancer configurationClassEnhancer = new ConfigurationClassEnhancer();
ClassLoader classLoader = new URLClassLoader(new URL[0], getClass().getClassLoader());
Class<?> enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicMethod.class, classLoader);
assertThat(MyConfigWithNonPublicMethod.class).isAssignableFrom(enhancedClass);
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader);
classLoader = new OverridingClassLoader(getClass().getClassLoader());
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicMethod.class, classLoader);
assertThat(MyConfigWithNonPublicMethod.class).isAssignableFrom(enhancedClass);
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
classLoader = new CustomSmartClassLoader(getClass().getClassLoader());
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicMethod.class, classLoader);
assertThat(MyConfigWithNonPublicMethod.class).isAssignableFrom(enhancedClass);
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
classLoader = new BasicSmartClassLoader(getClass().getClassLoader());
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicMethod.class, classLoader);
assertThat(MyConfigWithNonPublicMethod.class).isAssignableFrom(enhancedClass);
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
configurationClassEnhancer.enhance(myClass, parentClassLoader);
Class<?> myReloadedClass = classLoader.loadClass(MyConfig.class.getName());
Class<?> enhancedReloadedClass = configurationClassEnhancer.enhance(myReloadedClass, classLoader);
assertThat(enhancedReloadedClass.getClassLoader()).isEqualTo(classLoader);
}
@Configuration
static class MyConfig {
@Bean
String myBean() {
return "bean";
}
}
@Configuration
public static class MyConfigWithPublicClass {
@Bean
public String myBean() {
return "bean";
@@ -150,29 +56,9 @@ class ConfigurationClassEnhancerTests {
}
@Configuration
static class MyConfigWithNonPublicClass {
static class CustomClassLoader extends SecureClassLoader implements SmartClassLoader {
@Bean
public String myBean() {
return "bean";
}
}
@Configuration
public static class MyConfigWithNonPublicMethod {
@Bean
String myBean() {
return "bean";
}
}
static class CustomSmartClassLoader extends SecureClassLoader implements SmartClassLoader {
CustomSmartClassLoader(ClassLoader parent) {
CustomClassLoader(ClassLoader parent) {
super(parent);
}
@@ -196,29 +82,6 @@ class ConfigurationClassEnhancerTests {
public boolean isClassReloadable(Class<?> clazz) {
return clazz.getName().contains("MyConfig");
}
@Override
public ClassLoader getOriginalClassLoader() {
return getParent();
}
@Override
public Class<?> publicDefineClass(String name, byte[] b, @Nullable ProtectionDomain protectionDomain) {
return defineClass(name, b, 0, b.length, protectionDomain);
}
}
static class BasicSmartClassLoader extends SecureClassLoader implements SmartClassLoader {
BasicSmartClassLoader(ClassLoader parent) {
super(parent);
}
@Override
public Class<?> publicDefineClass(String name, byte[] b, @Nullable ProtectionDomain protectionDomain) {
return defineClass(name, b, 0, b.length, protectionDomain);
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -67,7 +67,6 @@ import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.SyncTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@@ -105,7 +104,6 @@ class ConfigurationClassPostProcessorTests {
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
pp.postProcessBeanFactory(beanFactory);
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).hasBeanClass()).isTrue();
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).getBeanClass().getName()).contains(ClassUtils.CGLIB_CLASS_SEPARATOR);
Foo foo = beanFactory.getBean("foo", Foo.class);
Bar bar = beanFactory.getBean("bar", Bar.class);
assertThat(bar.foo).isSameAs(foo);
@@ -120,7 +118,6 @@ class ConfigurationClassPostProcessorTests {
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
pp.postProcessBeanFactory(beanFactory);
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).hasBeanClass()).isTrue();
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).getBeanClass().getName()).contains(ClassUtils.CGLIB_CLASS_SEPARATOR);
Foo foo = beanFactory.getBean("foo", Foo.class);
Bar bar = beanFactory.getBean("bar", Bar.class);
assertThat(bar.foo).isSameAs(foo);
@@ -135,7 +132,6 @@ class ConfigurationClassPostProcessorTests {
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
pp.postProcessBeanFactory(beanFactory);
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).hasBeanClass()).isTrue();
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).getBeanClass().getName()).doesNotContain(ClassUtils.CGLIB_CLASS_SEPARATOR);
Foo foo = beanFactory.getBean("foo", Foo.class);
Bar bar = beanFactory.getBean("bar", Bar.class);
assertThat(bar.foo).isNotSameAs(foo);
@@ -147,7 +143,6 @@ class ConfigurationClassPostProcessorTests {
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
pp.postProcessBeanFactory(beanFactory);
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).hasBeanClass()).isTrue();
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).getBeanClass().getName()).doesNotContain(ClassUtils.CGLIB_CLASS_SEPARATOR);
Foo foo = beanFactory.getBean("foo", Foo.class);
Bar bar = beanFactory.getBean("bar", Bar.class);
assertThat(bar.foo).isNotSameAs(foo);
@@ -159,7 +154,6 @@ class ConfigurationClassPostProcessorTests {
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
pp.postProcessBeanFactory(beanFactory);
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).hasBeanClass()).isTrue();
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).getBeanClass().getName()).doesNotContain(ClassUtils.CGLIB_CLASS_SEPARATOR);
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("foo")).hasBeanClass()).isTrue();
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("bar")).hasBeanClass()).isTrue();
Foo foo = beanFactory.getBean("foo", Foo.class);
@@ -173,7 +167,6 @@ class ConfigurationClassPostProcessorTests {
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
pp.postProcessBeanFactory(beanFactory);
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).hasBeanClass()).isTrue();
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).getBeanClass().getName()).doesNotContain(ClassUtils.CGLIB_CLASS_SEPARATOR);
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("foo")).hasBeanClass()).isTrue();
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("bar")).hasBeanClass()).isTrue();
Foo foo = beanFactory.getBean("foo", Foo.class);
@@ -181,15 +174,6 @@ class ConfigurationClassPostProcessorTests {
assertThat(bar.foo).isNotSameAs(foo);
}
@Test
void enhancementIsNotPresentWithEmptyConfig() {
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(EmptyConfig.class));
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
pp.postProcessBeanFactory(beanFactory);
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).hasBeanClass()).isTrue();
assertThat(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).getBeanClass().getName()).doesNotContain(ClassUtils.CGLIB_CLASS_SEPARATOR);
}
@Test
void configurationIntrospectionOfInnerClassesWorksWithDotNameSyntax() {
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(getClass().getName() + ".SingletonBeanConfig"));
@@ -1182,7 +1166,7 @@ class ConfigurationClassPostProcessorTests {
}
@Configuration
static final class StaticSingletonBeanConfig {
static class StaticSingletonBeanConfig {
@Bean
public static Foo foo() {
@@ -1195,10 +1179,6 @@ class ConfigurationClassPostProcessorTests {
}
}
@Configuration
static final class EmptyConfig {
}
@Configuration
@Order(2)
static class OverridingSingletonBeanConfig {

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