mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
92 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0f19fa5d8e | |||
| 208d52d852 | |||
| 18c3b637e4 | |||
| 34c69bfc67 | |||
| 37d7af42ac | |||
| cc986cd2e8 | |||
| 0141725638 | |||
| 0c2ba4e38e | |||
| c6a9aa59a3 | |||
| ad949a7450 | |||
| 86b2617c7f | |||
| 760376c318 | |||
| 5b6abe4c13 | |||
| 7a839e988a | |||
| 46859d6391 | |||
| 573e74b8bd | |||
| 6c231804a0 | |||
| 7c3913050a | |||
| ec488282a8 | |||
| 911cdb2ad0 | |||
| 0f83c483bb | |||
| c9050607bc | |||
| 5e82ee6bd7 | |||
| 387677eae8 | |||
| 4a314867d7 | |||
| 020f556841 | |||
| 6ea3b5a0e8 | |||
| d764087dbf | |||
| 9ab43b138a | |||
| 09ae080b99 | |||
| 70a1b2fae3 | |||
| 90ddb40d7a | |||
| 143985e862 | |||
| 4bd280b87e | |||
| 5877a38fa1 | |||
| 94d29bac9f | |||
| a6f3f18d89 | |||
| 8334cb1abb | |||
| 2d88f18bf1 | |||
| 15a6641677 | |||
| 63c8e7cb5d | |||
| c5ecc50bfe | |||
| 0fd94f1b9f | |||
| 2472126475 | |||
| f3b50c9a58 | |||
| 0a6e666857 | |||
| 403362fe3c | |||
| 534f123fe8 | |||
| e421104cf3 | |||
| adfeba23e0 | |||
| 7789e12575 | |||
| 30d793cefe | |||
| 78cc5df748 | |||
| 5ffaea7a43 | |||
| 1633ad24f3 | |||
| 5faf2ed7f4 | |||
| d2733cea36 | |||
| 108caea385 | |||
| 7e9ac120ac | |||
| c64dae3623 | |||
| 559ea6c480 | |||
| 34315fc20f | |||
| 3bb4795d43 | |||
| aae2952a32 | |||
| f62251aebd | |||
| f92f9c1d5b | |||
| 2b38c00656 | |||
| 06721ba6c0 | |||
| b6a5402d88 | |||
| aff9ac72ec | |||
| f895d762cd | |||
| 634d1dd20b | |||
| 725b02a66d | |||
| 5a0bd9e5d4 | |||
| 6dd73ae3e4 | |||
| 4606337180 | |||
| f45a08d4f5 | |||
| d62ce2969a | |||
| ed3fd12210 | |||
| 6f596e2e84 | |||
| 6786e1c3e5 | |||
| 93134fd4d1 | |||
| d0ceefedc6 | |||
| e230ea537c | |||
| 94eb6006e8 | |||
| 2576702cda | |||
| 2099e046d3 | |||
| 2ee7a8e77a | |||
| 3c40e5e501 | |||
| 9e45178202 | |||
| d82e70e345 | |||
| 1109892134 |
+2
-1
@@ -42,7 +42,8 @@ spring-*/src/main/java/META-INF/MANIFEST.MF
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea
|
||||
.idea/*
|
||||
!.idea/icon.svg
|
||||
out
|
||||
test-output
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
Generated
+52
@@ -0,0 +1,52 @@
|
||||
<?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>
|
||||
|
After Width: | Height: | Size: 4.8 KiB |
+2
-2
@@ -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 "3.1.0" apply false
|
||||
id "net.ltgt.errorprone" version "4.1.0" apply false
|
||||
}
|
||||
|
||||
ext {
|
||||
@@ -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.11.4/api/",
|
||||
// "https://junit.org/junit5/docs/5.12.1/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.2");
|
||||
checkstyle.setToolVersion("10.21.4");
|
||||
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/**");
|
||||
"**/.classpath", "**/.project", "**/.gradle/**", "**/node_modules/**", "buildSrc/build/**");
|
||||
List<String> buildFolders = List.of("bin", "build", "out");
|
||||
project.allprojects(subproject -> {
|
||||
Path rootPath = project.getRootDir().toPath();
|
||||
|
||||
@@ -45,6 +45,7 @@ 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"))
|
||||
@@ -66,9 +67,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 `@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 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:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@@ -477,6 +477,13 @@ 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
|
||||
|
||||
@@ -857,7 +857,7 @@ Start by creating the interface with `@HttpExchange` methods:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
interface RepositoryService {
|
||||
public interface RepositoryService {
|
||||
|
||||
@GetExchange("/repos/{owner}/{repo}")
|
||||
Repository getRepository(@PathVariable String owner, @PathVariable String repo);
|
||||
@@ -908,7 +908,7 @@ For `RestTemplate`:
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@HttpExchange(url = "/repos/{owner}/{repo}", accept = "application/vnd.github.v3+json")
|
||||
interface RepositoryService {
|
||||
public interface RepositoryService {
|
||||
|
||||
@GetExchange
|
||||
Repository getRepository(@PathVariable String owner, @PathVariable String repo);
|
||||
|
||||
@@ -7,16 +7,17 @@ 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
|
||||
@@ -161,15 +162,16 @@ 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,8 +47,7 @@ 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])
|
||||
or alternative Servlet API mock objects (such as http://www.mockobjects.com[MockObjects]).
|
||||
convenient to use than dynamic mock objects (such as https://easymock.org/[EasyMock]).
|
||||
|
||||
TIP: Since Spring Framework 6.0, the mock objects in `org.springframework.mock.web` are
|
||||
based on the Servlet 6.0 API.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
[[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
|
||||
@@ -224,9 +225,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://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://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://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,6 +1,7 @@
|
||||
[[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,6 +130,7 @@ 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://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://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://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[See equivalent in the Reactive stack]#
|
||||
[.small]#xref:web/webflux-view.adoc#webflux-view-script-integrate[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 WebFlux exceptions and for any
|
||||
To enable RFC 9457 responses for Spring MVC 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://hdiv.org/[HDIV] is another web security framework that integrates with Spring MVC.
|
||||
https://github.com/hdiv/hdiv[HDIV] is another web security framework that integrates with Spring MVC.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"@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
-1
@@ -29,7 +29,7 @@ import org.springframework.web.service.invoker.HttpServiceProxyFactory;
|
||||
public class CustomHttpServiceArgumentResolver {
|
||||
|
||||
// tag::httpinterface[]
|
||||
interface RepositoryService {
|
||||
public interface RepositoryService {
|
||||
|
||||
@GetExchange("/repos/search")
|
||||
List<Repository> searchRepository(Search search);
|
||||
|
||||
@@ -7,31 +7,31 @@ javaPlatform {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(platform("com.fasterxml.jackson:jackson-bom:2.18.2"))
|
||||
api(platform("io.micrometer:micrometer-bom:1.14.4"))
|
||||
api(platform("io.netty:netty-bom:4.1.118.Final"))
|
||||
api(platform("com.fasterxml.jackson:jackson-bom:2.18.3"))
|
||||
api(platform("io.micrometer:micrometer-bom:1.14.5"))
|
||||
api(platform("io.netty:netty-bom:4.1.119.Final"))
|
||||
api(platform("io.netty:netty5-bom:5.0.0.Alpha5"))
|
||||
api(platform("io.projectreactor:reactor-bom:2024.0.3"))
|
||||
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.24"))
|
||||
api(platform("org.apache.groovy:groovy-bom:4.0.26"))
|
||||
api(platform("org.apache.logging.log4j:log4j-bom:2.21.1"))
|
||||
api(platform("org.assertj:assertj-bom:3.27.3"))
|
||||
api(platform("org.eclipse.jetty:jetty-bom:12.0.16"))
|
||||
api(platform("org.eclipse.jetty.ee10:jetty-ee10-bom:12.0.16"))
|
||||
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.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.11.4"))
|
||||
api(platform("org.mockito:mockito-bom:5.15.2"))
|
||||
api(platform("org.junit:junit-bom:5.12.1"))
|
||||
api(platform("org.mockito:mockito-bom:5.16.1"))
|
||||
|
||||
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.1.8")
|
||||
api("com.github.ben-manes.caffeine:caffeine:3.2.0")
|
||||
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.11.0")
|
||||
api("com.google.protobuf:protobuf-java-util:4.29.3")
|
||||
api("com.google.code.gson:gson:2.12.1")
|
||||
api("com.google.protobuf:protobuf-java-util:4.30.0")
|
||||
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.1.0")
|
||||
api("com.thoughtworks.qdox:qdox:2.2.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.1")
|
||||
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.3.1")
|
||||
api("org.apache.httpcomponents.client5:httpclient5:5.4.2")
|
||||
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.3.3")
|
||||
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.2.2")
|
||||
api("org.awaitility:awaitility:4.3.0")
|
||||
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.4.0")
|
||||
api("org.eclipse.jetty:jetty-reactive-httpclient:4.0.8")
|
||||
api("org.easymock:easymock:5.5.0")
|
||||
api("org.eclipse.jetty:jetty-reactive-httpclient:4.0.9")
|
||||
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.33")
|
||||
api("org.freemarker:freemarker:2.3.34")
|
||||
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.6.0")
|
||||
api("org.htmlunit:htmlunit:4.10.0")
|
||||
api("org.javamoney:moneta:1.4.4")
|
||||
api("org.jruby:jruby:9.4.9.0")
|
||||
api("org.jruby:jruby:9.4.12.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.3")
|
||||
api("org.python:jython-standalone:2.7.4")
|
||||
api("org.quartz-scheduler:quartz:2.3.2")
|
||||
api("org.seleniumhq.selenium:htmlunit3-driver:4.26.0")
|
||||
api("org.seleniumhq.selenium:selenium-java:4.26.0")
|
||||
api("org.seleniumhq.selenium:htmlunit3-driver:4.29.0")
|
||||
api("org.seleniumhq.selenium:selenium-java:4.29.0")
|
||||
api("org.skyscreamer:jsonassert:1.5.3")
|
||||
api("org.slf4j:slf4j-api:2.0.16")
|
||||
api("org.slf4j:slf4j-api:2.0.17")
|
||||
api("org.testng:testng:7.11.0")
|
||||
api("org.webjars:underscorejs:1.8.3")
|
||||
api("org.webjars:webjars-locator-core:0.55")
|
||||
api("org.webjars:webjars-locator-lite:1.0.0")
|
||||
api("org.webjars:webjars-locator-core:0.59")
|
||||
api("org.webjars:webjars-locator-lite:1.1.0")
|
||||
api("org.xmlunit:xmlunit-assertj:2.10.0")
|
||||
api("org.xmlunit:xmlunit-matchers:2.10.0")
|
||||
api("org.yaml:snakeyaml:2.3")
|
||||
api("org.yaml:snakeyaml:2.4")
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
version=6.2.3-SNAPSHOT
|
||||
version=6.2.5
|
||||
|
||||
org.gradle.caching=true
|
||||
org.gradle.jvmargs=-Xmx2048m
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+1
-1
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -205,7 +205,7 @@ fi
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# * DEFAULT_JVM_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.
|
||||
|
||||
+11
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -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,7 +373,8 @@ 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.");
|
||||
"' is ambiguous: there are " + this.numberOfRemainingUnboundArguments + " candidates. " +
|
||||
"Consider compiling with -parameters in order to make declared parameter names available.");
|
||||
}
|
||||
|
||||
// We're all set... find the unbound parameter, and bind it.
|
||||
@@ -485,8 +486,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<>();
|
||||
@@ -535,8 +536,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<>();
|
||||
@@ -741,7 +742,9 @@ 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-2023 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -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-2023 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -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-2023 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -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
|
||||
|
||||
+31
-34
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -203,7 +203,6 @@ 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);
|
||||
@@ -301,7 +300,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;
|
||||
@@ -320,7 +319,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();
|
||||
@@ -329,7 +328,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();
|
||||
@@ -343,20 +342,19 @@ 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();
|
||||
@@ -370,8 +368,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);
|
||||
}
|
||||
|
||||
@@ -379,7 +377,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;
|
||||
@@ -393,9 +391,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);
|
||||
@@ -426,7 +424,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);
|
||||
@@ -439,12 +437,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
|
||||
@@ -452,7 +450,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("");
|
||||
@@ -466,7 +464,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();
|
||||
@@ -475,7 +473,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");
|
||||
}
|
||||
|
||||
@@ -487,7 +485,6 @@ 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
|
||||
}
|
||||
@@ -812,20 +809,20 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
invocations.add("before");
|
||||
}
|
||||
|
||||
@AfterReturning("echo()")
|
||||
void afterReturning() {
|
||||
invocations.add("after returning");
|
||||
}
|
||||
|
||||
@AfterThrowing("echo()")
|
||||
void afterThrowing() {
|
||||
invocations.add("after throwing");
|
||||
}
|
||||
|
||||
@After("echo()")
|
||||
void after() {
|
||||
invocations.add("after");
|
||||
}
|
||||
|
||||
@AfterReturning(pointcut = "this(target) && execution(* echo(*))", returning = "returnValue")
|
||||
void afterReturning(JoinPoint joinPoint, Echo target, Object returnValue) {
|
||||
invocations.add("after returning");
|
||||
}
|
||||
|
||||
@AfterThrowing(pointcut = "this(target) && execution(* echo(*))", throwing = "exception")
|
||||
void afterThrowing(JoinPoint joinPoint, Echo target, Throwable exception) {
|
||||
invocations.add("after throwing");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -967,7 +964,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
-2
@@ -27,13 +27,12 @@ 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.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
|
||||
/**
|
||||
* Tests for {@link AsyncExecutionInterceptor}.
|
||||
*
|
||||
|
||||
@@ -46,6 +46,21 @@ 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
|
||||
@@ -188,7 +203,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 #getIfAvailable()
|
||||
* @see #getIfUnique()
|
||||
*/
|
||||
default void ifUnique(Consumer<T> dependencyConsumer) throws BeansException {
|
||||
T dependency = getIfUnique();
|
||||
@@ -259,7 +274,7 @@ public interface ObjectProvider<T> extends ObjectFactory<T>, Iterable<T> {
|
||||
* @see #orderedStream(Predicate)
|
||||
*/
|
||||
default Stream<T> stream(Predicate<Class<?>> customFilter) {
|
||||
return stream().filter(obj -> customFilter.test(obj.getClass()));
|
||||
return stream(customFilter, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -273,6 +288,44 @@ public interface ObjectProvider<T> extends ObjectFactory<T>, Iterable<T> {
|
||||
* @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-2022 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -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()));
|
||||
}
|
||||
|
||||
+4
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -234,8 +234,7 @@ 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));
|
||||
@@ -325,8 +324,7 @@ 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);
|
||||
@@ -443,6 +441,7 @@ public class InstanceSupplierCodeGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
record ConstructorDescriptor(String beanName, Constructor<?> constructor, Class<?> publicType) {
|
||||
|
||||
Class<?> actualType() {
|
||||
|
||||
+20
@@ -35,6 +35,7 @@ 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;
|
||||
@@ -279,6 +280,25 @@ abstract class AutowireUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
||||
+43
-13
@@ -487,14 +487,14 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Stream<T> stream() {
|
||||
return Arrays.stream(getBeanNamesForTypedStream(requiredType, allowEagerInit))
|
||||
return Arrays.stream(beanNamesForStream(requiredType, true, allowEagerInit))
|
||||
.map(name -> (T) getBean(name))
|
||||
.filter(bean -> !(bean instanceof NullBean));
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Stream<T> orderedStream() {
|
||||
String[] beanNames = getBeanNamesForTypedStream(requiredType, allowEagerInit);
|
||||
String[] beanNames = beanNamesForStream(requiredType, true, allowEagerInit);
|
||||
if (beanNames.length == 0) {
|
||||
return Stream.empty();
|
||||
}
|
||||
@@ -510,16 +510,16 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Stream<T> stream(Predicate<Class<?>> customFilter) {
|
||||
return Arrays.stream(getBeanNamesForTypedStream(requiredType, allowEagerInit))
|
||||
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) {
|
||||
String[] beanNames = getBeanNamesForTypedStream(requiredType, allowEagerInit);
|
||||
public Stream<T> orderedStream(Predicate<Class<?>> customFilter, boolean includeNonSingletons) {
|
||||
String[] beanNames = beanNamesForStream(requiredType, includeNonSingletons, allowEagerInit);
|
||||
if (beanNames.length == 0) {
|
||||
return Stream.empty();
|
||||
}
|
||||
@@ -559,8 +559,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
return null;
|
||||
}
|
||||
|
||||
private String[] getBeanNamesForTypedStream(ResolvableType requiredType, boolean allowEagerInit) {
|
||||
return BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this, requiredType, true, allowEagerInit);
|
||||
private String[] beanNamesForStream(ResolvableType requiredType, boolean includeNonSingletons, boolean allowEagerInit) {
|
||||
return BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this, requiredType, includeNonSingletons, allowEagerInit);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1497,6 +1497,9 @@ 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) {
|
||||
@@ -1967,7 +1970,12 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
if (priorityCandidate != null) {
|
||||
return priorityCandidate;
|
||||
}
|
||||
// Step 4: pick directly registered dependency
|
||||
// Step 4: pick unique default-candidate
|
||||
String defaultCandidate = determineDefaultCandidate(candidates);
|
||||
if (defaultCandidate != null) {
|
||||
return defaultCandidate;
|
||||
}
|
||||
// Step 5: pick directly registered dependency
|
||||
for (Map.Entry<String, Object> entry : candidates.entrySet()) {
|
||||
String candidateName = entry.getKey();
|
||||
Object beanInstance = entry.getValue();
|
||||
@@ -2128,6 +2136,28 @@ 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.
|
||||
@@ -2514,8 +2544,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Object> stream(Predicate<Class<?>> customFilter) {
|
||||
return Arrays.stream(getBeanNamesForTypedStream(this.descriptor.getResolvableType(), true))
|
||||
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))
|
||||
@@ -2523,8 +2553,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Object> orderedStream(Predicate<Class<?>> customFilter) {
|
||||
String[] beanNames = getBeanNamesForTypedStream(this.descriptor.getResolvableType(), true);
|
||||
public Stream<Object> orderedStream(Predicate<Class<?>> customFilter, boolean includeNonSingletons) {
|
||||
String[] beanNames = beanNamesForStream(this.descriptor.getResolvableType(), includeNonSingletons, true);
|
||||
if (beanNames.length == 0) {
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
+25
-15
@@ -253,7 +253,6 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
|
||||
Boolean lockFlag = isCurrentThreadAllowedToHoldSingletonLock();
|
||||
boolean acquireLock = !Boolean.FALSE.equals(lockFlag);
|
||||
boolean locked = (acquireLock && this.singletonLock.tryLock());
|
||||
boolean lenient = false;
|
||||
try {
|
||||
Object singletonObject = this.singletonObjects.get(beanName);
|
||||
if (singletonObject == null) {
|
||||
@@ -268,7 +267,6 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
|
||||
Thread.currentThread().getName() + "\" while other thread holds " +
|
||||
"singleton lock for other beans " + this.singletonsCurrentlyInCreation);
|
||||
}
|
||||
lenient = true;
|
||||
this.lenientCreationLock.lock();
|
||||
try {
|
||||
this.singletonsInLenientCreation.add(beanName);
|
||||
@@ -329,7 +327,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
|
||||
// Try late locking for waiting on specific bean to be finished.
|
||||
this.singletonLock.lock();
|
||||
locked = true;
|
||||
// Singleton object should have appeared in the meantime.
|
||||
// Lock-created singleton object should have appeared in the meantime.
|
||||
singletonObject = this.singletonObjects.get(beanName);
|
||||
if (singletonObject != null) {
|
||||
return singletonObject;
|
||||
@@ -343,8 +341,12 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
|
||||
this.suppressedExceptions = new LinkedHashSet<>();
|
||||
}
|
||||
try {
|
||||
singletonObject = singletonFactory.getObject();
|
||||
newSingleton = true;
|
||||
// Leniently created singleton object could have appeared in the meantime.
|
||||
singletonObject = this.singletonObjects.get(beanName);
|
||||
if (singletonObject == null) {
|
||||
singletonObject = singletonFactory.getObject();
|
||||
newSingleton = true;
|
||||
}
|
||||
}
|
||||
catch (IllegalStateException ex) {
|
||||
// Has the singleton object implicitly appeared in the meantime ->
|
||||
@@ -368,8 +370,18 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
|
||||
}
|
||||
afterSingletonCreation(beanName);
|
||||
}
|
||||
|
||||
if (newSingleton) {
|
||||
addSingleton(beanName, singletonObject);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return singletonObject;
|
||||
@@ -378,15 +390,13 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
|
||||
if (locked) {
|
||||
this.singletonLock.unlock();
|
||||
}
|
||||
if (lenient) {
|
||||
this.lenientCreationLock.lock();
|
||||
try {
|
||||
this.singletonsInLenientCreation.remove(beanName);
|
||||
this.lenientCreationFinished.signalAll();
|
||||
}
|
||||
finally {
|
||||
this.lenientCreationLock.unlock();
|
||||
}
|
||||
this.lenientCreationLock.lock();
|
||||
try {
|
||||
this.singletonsInLenientCreation.remove(beanName);
|
||||
this.lenientCreationFinished.signalAll();
|
||||
}
|
||||
finally {
|
||||
this.lenientCreationLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+36
-2
@@ -67,10 +67,44 @@ public class SimpleAutowireCandidateResolver implements AutowireCandidateResolve
|
||||
* @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)) {
|
||||
for (String beanName : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(lbf, type,
|
||||
includeNonSingletons, allowEagerInit)) {
|
||||
if (AutowireUtils.isAutowireCandidate(lbf, beanName)) {
|
||||
candidates.put(beanName, lbf.getBean(beanName, type));
|
||||
Object beanInstance = lbf.getBean(beanName);
|
||||
if (!(beanInstance instanceof NullBean)) {
|
||||
candidates.put(beanName, (T) beanInstance);
|
||||
}
|
||||
}
|
||||
}
|
||||
return candidates;
|
||||
|
||||
+2
-2
@@ -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()) {
|
||||
|
||||
+99
-10
@@ -113,6 +113,7 @@ import static org.mockito.Mockito.verify;
|
||||
* @author Chris Beams
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
* @author Yanming Zhou
|
||||
*/
|
||||
class DefaultListableBeanFactoryTests {
|
||||
|
||||
@@ -1518,34 +1519,34 @@ class DefaultListableBeanFactoryTests {
|
||||
bd2.setBeanClass(DerivedTestBean.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(ObjectProvider.UNFILTERED).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 orderFromAttributeOverrideAnnotation() {
|
||||
void orderFromAttributeOverridesAnnotation() {
|
||||
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
|
||||
@@ -1658,12 +1659,94 @@ 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() {
|
||||
@@ -1904,7 +1987,6 @@ 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);
|
||||
@@ -1945,6 +2027,10 @@ class DefaultListableBeanFactoryTests {
|
||||
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
|
||||
@@ -1999,6 +2085,9 @@ class DefaultListableBeanFactoryTests {
|
||||
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
|
||||
|
||||
+39
-3
@@ -1614,6 +1614,10 @@ class AutowiredAnnotationBeanPostProcessorTests {
|
||||
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
|
||||
@@ -1648,6 +1652,12 @@ class AutowiredAnnotationBeanPostProcessorTests {
|
||||
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();
|
||||
assertThat(testBeans).hasSize(1);
|
||||
assertThat(testBeans).contains(bf.getBean("testBean", TestBean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1675,6 +1685,10 @@ class AutowiredAnnotationBeanPostProcessorTests {
|
||||
assertThat(testBeans).isEmpty();
|
||||
testBeans = bean.allTestBeansInOrder();
|
||||
assertThat(testBeans).isEmpty();
|
||||
testBeans = bean.allSingletonBeans();
|
||||
assertThat(testBeans).isEmpty();
|
||||
testBeans = bean.allSingletonBeansInOrder();
|
||||
assertThat(testBeans).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1698,6 +1712,8 @@ class AutowiredAnnotationBeanPostProcessorTests {
|
||||
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);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1728,6 +1744,8 @@ class AutowiredAnnotationBeanPostProcessorTests {
|
||||
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);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1739,7 +1757,7 @@ class AutowiredAnnotationBeanPostProcessorTests {
|
||||
bf.registerBeanDefinition("testBean1", tb1);
|
||||
RootBeanDefinition tb2 = new RootBeanDefinition(TestBeanFactory.class);
|
||||
tb2.setFactoryMethodName("newTestBean2");
|
||||
tb2.setLazyInit(true);
|
||||
tb2.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||
bf.registerBeanDefinition("testBean2", tb2);
|
||||
|
||||
ObjectProviderInjectionBean bean = bf.getBean("annotatedBean", ObjectProviderInjectionBean.class);
|
||||
@@ -1747,6 +1765,7 @@ class AutowiredAnnotationBeanPostProcessorTests {
|
||||
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
|
||||
@@ -1757,6 +1776,7 @@ class AutowiredAnnotationBeanPostProcessorTests {
|
||||
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();
|
||||
@@ -1770,6 +1790,10 @@ class AutowiredAnnotationBeanPostProcessorTests {
|
||||
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));
|
||||
@@ -1785,11 +1809,15 @@ class AutowiredAnnotationBeanPostProcessorTests {
|
||||
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(candidates);
|
||||
assertThat(candidates).containsExactlyEntriesOf(typeMatches);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -2366,7 +2394,7 @@ class AutowiredAnnotationBeanPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
@SuppressWarnings("unchecked")
|
||||
void genericsBasedConstructorInjectionWithNonTypedTarget() {
|
||||
RootBeanDefinition bd = new RootBeanDefinition(RepositoryConstructorInjectionBean.class);
|
||||
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||
@@ -3389,6 +3417,14 @@ class AutowiredAnnotationBeanPostProcessorTests {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+2
-2
@@ -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];
|
||||
|
||||
+4
-4
@@ -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
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+10
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -50,10 +50,15 @@ class DefaultSingletonBeanRegistryTests {
|
||||
assertThat(beanRegistry.getSingleton("tb2")).isSameAs(tb2);
|
||||
assertThat(tb2Flag.get()).isTrue();
|
||||
|
||||
assertThat(beanRegistry.getSingleton("tb")).isSameAs(tb);
|
||||
assertThat(beanRegistry.getSingleton("tb2")).isSameAs(tb2);
|
||||
assertThat(beanRegistry.getSingletonCount()).isEqualTo(2);
|
||||
assertThat(beanRegistry.getSingletonNames()).containsExactly("tb", "tb2");
|
||||
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");
|
||||
|
||||
beanRegistry.destroySingletons();
|
||||
assertThat(beanRegistry.getSingletonCount()).isZero();
|
||||
|
||||
+2
-2
@@ -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) {
|
||||
|
||||
+2
-2
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -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]));
|
||||
}
|
||||
|
||||
+3
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,7 +17,6 @@
|
||||
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;
|
||||
@@ -150,9 +149,9 @@ class JCacheKeyGeneratorTests {
|
||||
|
||||
@Override
|
||||
public Object generate(Object target, Method method, Object... params) {
|
||||
assertThat(Arrays.equals(expectedParams, params)).as("Unexpected parameters: expected: "
|
||||
+ Arrays.toString(this.expectedParams) + " but got: " + Arrays.toString(params)).isTrue();
|
||||
assertThat(params).as("Unexpected parameters").isEqualTo(expectedParams);
|
||||
return new SimpleKey(params);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -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;
|
||||
|
||||
+2
-2
@@ -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;
|
||||
}
|
||||
|
||||
+13
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -203,6 +203,15 @@ 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);
|
||||
}
|
||||
@@ -223,8 +232,9 @@ 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 declares proxyBeanMethods=false
|
||||
if (attributes != null && (Boolean) attributes.get("proxyBeanMethods") && this.metadata.isFinal()) {
|
||||
// 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()) {
|
||||
problemReporter.error(new FinalConfigurationProblem());
|
||||
}
|
||||
|
||||
|
||||
+5
-7
@@ -112,12 +112,11 @@ class ConfigurationClassEnhancer {
|
||||
|
||||
try {
|
||||
// Use original ClassLoader if config class not locally loaded in overriding class loader
|
||||
if (classLoader instanceof SmartClassLoader smartClassLoader &&
|
||||
classLoader != configClass.getClassLoader()) {
|
||||
boolean classLoaderMismatch = (classLoader != null && classLoader != configClass.getClassLoader());
|
||||
if (classLoaderMismatch && classLoader instanceof SmartClassLoader smartClassLoader) {
|
||||
classLoader = smartClassLoader.getOriginalClassLoader();
|
||||
}
|
||||
Enhancer enhancer = newEnhancer(configClass, classLoader);
|
||||
boolean classLoaderMismatch = (classLoader != null && classLoader != configClass.getClassLoader());
|
||||
Class<?> enhancedClass = createClass(enhancer, classLoaderMismatch);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format("Successfully enhanced %s; enhanced class name is: %s",
|
||||
@@ -168,9 +167,9 @@ class ConfigurationClassEnhancer {
|
||||
try {
|
||||
subclass = enhancer.createClass();
|
||||
}
|
||||
catch (CodeGenerationException ex) {
|
||||
catch (Throwable ex) {
|
||||
if (!fallback) {
|
||||
throw ex;
|
||||
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
|
||||
@@ -188,8 +187,7 @@ 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, for example,
|
||||
* have already been enhanced.
|
||||
* through checking to see if candidate classes are already assignable to it.
|
||||
* <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
|
||||
|
||||
+27
-14
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -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,14 +167,22 @@ class ConfigurationClassParser {
|
||||
for (BeanDefinitionHolder holder : configCandidates) {
|
||||
BeanDefinition bd = holder.getBeanDefinition();
|
||||
try {
|
||||
ConfigurationClass configClass;
|
||||
if (bd instanceof AnnotatedBeanDefinition annotatedBeanDef) {
|
||||
parse(annotatedBeanDef, holder.getBeanName());
|
||||
configClass = parse(annotatedBeanDef, holder.getBeanName());
|
||||
}
|
||||
else if (bd instanceof AbstractBeanDefinition abstractBeanDef && abstractBeanDef.hasBeanClass()) {
|
||||
parse(abstractBeanDef.getBeanClass(), holder.getBeanName());
|
||||
configClass = parse(abstractBeanDef.getBeanClass(), holder.getBeanName());
|
||||
}
|
||||
else {
|
||||
parse(bd.getBeanClassName(), holder.getBeanName());
|
||||
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);
|
||||
}
|
||||
}
|
||||
catch (BeanDefinitionStoreException ex) {
|
||||
@@ -189,20 +197,25 @@ class ConfigurationClassParser {
|
||||
this.deferredImportSelectorHandler.process();
|
||||
}
|
||||
|
||||
private void parse(AnnotatedBeanDefinition beanDef, String beanName) {
|
||||
processConfigurationClass(
|
||||
new ConfigurationClass(beanDef.getMetadata(), beanName, (beanDef instanceof ScannedGenericBeanDefinition)),
|
||||
DEFAULT_EXCLUSION_FILTER);
|
||||
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(Class<?> clazz, String beanName) {
|
||||
processConfigurationClass(new ConfigurationClass(clazz, beanName), DEFAULT_EXCLUSION_FILTER);
|
||||
private ConfigurationClass parse(Class<?> clazz, String beanName) {
|
||||
ConfigurationClass configClass = new ConfigurationClass(clazz, beanName);
|
||||
processConfigurationClass(configClass, DEFAULT_EXCLUSION_FILTER);
|
||||
return configClass;
|
||||
}
|
||||
|
||||
final void parse(@Nullable String className, String beanName) throws IOException {
|
||||
final ConfigurationClass 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);
|
||||
processConfigurationClass(new ConfigurationClass(reader, beanName), DEFAULT_EXCLUSION_FILTER);
|
||||
ConfigurationClass configClass = new ConfigurationClass(reader, beanName);
|
||||
processConfigurationClass(configClass, DEFAULT_EXCLUSION_FILTER);
|
||||
return configClass;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -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())) {
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -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) {
|
||||
|
||||
+2
-2
@@ -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)
|
||||
|
||||
+2
-2
@@ -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 {
|
||||
|
||||
+3
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,6 +18,7 @@ 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;
|
||||
@@ -53,6 +54,7 @@ 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) {
|
||||
|
||||
+3
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,6 +18,7 @@ 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;
|
||||
@@ -41,6 +42,7 @@ public class SchedulerBeanDefinitionParser extends AbstractSingleBeanDefinitionP
|
||||
if (StringUtils.hasText(poolSize)) {
|
||||
builder.addPropertyValue("poolSize", poolSize);
|
||||
}
|
||||
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -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
-1
@@ -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 (DefaultKeyGenerator) is not sufficient.
|
||||
explicitly if the default strategy (SimpleKeyGenerator) is not sufficient.
|
||||
]]></xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
|
||||
+2
-2
@@ -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)) {
|
||||
|
||||
+38
@@ -56,6 +56,16 @@ class BackgroundBootstrapTests {
|
||||
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)
|
||||
@@ -138,6 +148,34 @@ class BackgroundBootstrapTests {
|
||||
}
|
||||
|
||||
|
||||
@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)
|
||||
static class CustomExecutorBeanConfig {
|
||||
|
||||
|
||||
+22
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -67,6 +67,7 @@ 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;
|
||||
@@ -104,6 +105,7 @@ 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);
|
||||
@@ -118,6 +120,7 @@ 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);
|
||||
@@ -132,6 +135,7 @@ 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);
|
||||
@@ -143,6 +147,7 @@ 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);
|
||||
@@ -154,6 +159,7 @@ 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);
|
||||
@@ -167,6 +173,7 @@ 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);
|
||||
@@ -174,6 +181,15 @@ 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"));
|
||||
@@ -1166,7 +1182,7 @@ class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class StaticSingletonBeanConfig {
|
||||
static final class StaticSingletonBeanConfig {
|
||||
|
||||
@Bean
|
||||
public static Foo foo() {
|
||||
@@ -1179,6 +1195,10 @@ class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static final class EmptyConfig {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Order(2)
|
||||
static class OverridingSingletonBeanConfig {
|
||||
|
||||
+7
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -37,16 +37,18 @@ class InvalidConfigurationClassDefinitionTests {
|
||||
@Test
|
||||
void configurationClassesMayNotBeFinal() {
|
||||
@Configuration
|
||||
final class Config { }
|
||||
final class Config {
|
||||
@Bean String dummy() { return "dummy"; }
|
||||
}
|
||||
|
||||
BeanDefinition configBeanDef = rootBeanDefinition(Config.class).getBeanDefinition();
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerBeanDefinition("config", configBeanDef);
|
||||
|
||||
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||
assertThatExceptionOfType(BeanDefinitionParsingException.class).isThrownBy(() ->
|
||||
pp.postProcessBeanFactory(beanFactory))
|
||||
.withMessageContaining("Remove the final modifier");
|
||||
assertThatExceptionOfType(BeanDefinitionParsingException.class)
|
||||
.isThrownBy(() -> pp.postProcessBeanFactory(beanFactory))
|
||||
.withMessageContaining("Remove the final modifier");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+16
-20
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -277,9 +277,9 @@ public class GroovyScriptFactoryTests {
|
||||
|
||||
@Test
|
||||
void testScriptCompilationException() {
|
||||
assertThatExceptionOfType(NestedRuntimeException.class).isThrownBy(() ->
|
||||
new ClassPathXmlApplicationContext("org/springframework/scripting/groovy/groovyBrokenContext.xml"))
|
||||
.matches(ex -> ex.contains(ScriptCompilationException.class));
|
||||
assertThatExceptionOfType(NestedRuntimeException.class)
|
||||
.isThrownBy(() -> new ClassPathXmlApplicationContext("org/springframework/scripting/groovy/groovyBrokenContext.xml"))
|
||||
.matches(ex -> ex.contains(ScriptCompilationException.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -288,11 +288,10 @@ public class GroovyScriptFactoryTests {
|
||||
String badScript = "class Foo { public Foo(String foo) {}}";
|
||||
given(script.getScriptAsString()).willReturn(badScript);
|
||||
given(script.suggestedClassName()).willReturn("someName");
|
||||
GroovyScriptFactory factory = new GroovyScriptFactory(ScriptFactoryPostProcessor.INLINE_SCRIPT_PREFIX
|
||||
+ badScript);
|
||||
assertThatExceptionOfType(ScriptCompilationException.class).isThrownBy(() ->
|
||||
factory.getScriptedObject(script))
|
||||
.matches(ex -> ex.contains(NoSuchMethodException.class));
|
||||
GroovyScriptFactory factory = new GroovyScriptFactory(ScriptFactoryPostProcessor.INLINE_SCRIPT_PREFIX + badScript);
|
||||
assertThatExceptionOfType(ScriptCompilationException.class)
|
||||
.isThrownBy(() -> factory.getScriptedObject(script))
|
||||
.matches(ex -> ex.contains(NoSuchMethodException.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -327,27 +326,24 @@ public class GroovyScriptFactoryTests {
|
||||
|
||||
@Test
|
||||
void testCtorWithNullScriptSourceLocator() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
new GroovyScriptFactory(null));
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new GroovyScriptFactory(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCtorWithEmptyScriptSourceLocator() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
new GroovyScriptFactory(""));
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new GroovyScriptFactory(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCtorWithWhitespacedScriptSourceLocator() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
new GroovyScriptFactory("\n "));
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new GroovyScriptFactory("\n "));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWithInlineScriptWithLeadingWhitespace() {
|
||||
assertThatExceptionOfType(BeanCreationException.class).as("'inline:' prefix was preceded by whitespace").isThrownBy(() ->
|
||||
new ClassPathXmlApplicationContext("lwspBadGroovyContext.xml", getClass()))
|
||||
.matches(ex -> ex.contains(FileNotFoundException.class));
|
||||
assertThatExceptionOfType(BeanCreationException.class).as("'inline:' prefix was preceded by whitespace")
|
||||
.isThrownBy(() -> new ClassPathXmlApplicationContext("lwspBadGroovyContext.xml", getClass()))
|
||||
.matches(ex -> ex.contains(FileNotFoundException.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -364,8 +360,8 @@ public class GroovyScriptFactoryTests {
|
||||
@Test
|
||||
void testGetScriptedObjectDoesChokeOnNullScriptSourceBeingPassedIn() {
|
||||
GroovyScriptFactory factory = new GroovyScriptFactory("a script source locator (doesn't matter here)");
|
||||
assertThatNullPointerException().as("NullPointerException as per contract ('null' ScriptSource supplied)").isThrownBy(() ->
|
||||
factory.getScriptedObject(null));
|
||||
assertThatNullPointerException().as("NullPointerException as per contract ('null' ScriptSource supplied)")
|
||||
.isThrownBy(() -> factory.getScriptedObject(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+8
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.context.testfixture.context.annotation;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@@ -31,4 +32,10 @@ public class ValueCglibConfiguration {
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public String dummy() {
|
||||
return "dummy";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,11 +2,10 @@ description = "Spring Core Test"
|
||||
|
||||
dependencies {
|
||||
api(project(":spring-core"))
|
||||
api("org.assertj:assertj-core")
|
||||
api("org.junit.jupiter:junit-jupiter-api")
|
||||
compileOnly("org.junit.jupiter:junit-jupiter")
|
||||
compileOnly("org.junit.platform:junit-platform-engine")
|
||||
compileOnly("org.junit.platform:junit-platform-launcher")
|
||||
optional("org.assertj:assertj-core")
|
||||
optional("org.junit.jupiter:junit-jupiter-api")
|
||||
compileOnly("org.junit.jupiter:junit-jupiter-params") // Used in CompileWithForkedClassLoaderExtension Javadoc
|
||||
compileOnly("org.junit.platform:junit-platform-launcher") // Used in CompileWithForkedClassLoaderExtension
|
||||
implementation("com.thoughtworks.qdox:qdox")
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -338,8 +338,8 @@ public abstract class InstrumentedBridgeMethods {
|
||||
Object result = null;
|
||||
boolean accessibilityChanged = false;
|
||||
try {
|
||||
if (!Modifier.isPublic(method.getModifiers())
|
||||
|| !Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
|
||||
if (!Modifier.isPublic(method.getModifiers()) ||
|
||||
!Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
|
||||
method.setAccessible(true);
|
||||
accessibilityChanged = true;
|
||||
}
|
||||
|
||||
+2
-2
@@ -78,8 +78,8 @@ class InvocationsRecorderClassVisitor extends ClassVisitor implements Opcodes {
|
||||
static {
|
||||
for (InstrumentedMethod method : InstrumentedMethod.values()) {
|
||||
MethodReference methodReference = method.methodReference();
|
||||
instrumentedMethods.add(methodReference.getClassName().replace('.', '/')
|
||||
+ "#" + methodReference.getMethodName());
|
||||
instrumentedMethods.add(methodReference.getClassName().replace('.', '/') +
|
||||
"#" + methodReference.getMethodName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -108,8 +108,8 @@ public class RuntimeHintsInvocationsAssert extends AbstractAssert<RuntimeHintsIn
|
||||
|
||||
private String formatStackTrace(Stream<StackWalker.StackFrame> stackTraceElements) {
|
||||
return stackTraceElements
|
||||
.map(f -> f.getClassName() + "#" + f.getMethodName()
|
||||
+ ", Line " + f.getLineNumber()).collect(Collectors.joining(System.lineSeparator()));
|
||||
.map(f -> f.getClassName() + "#" + f.getMethodName() + ", Line " +
|
||||
f.getLineNumber()).collect(Collectors.joining(System.lineSeparator()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+2
-2
@@ -122,8 +122,8 @@ public class MockSpringFactoriesLoader extends SpringFactoriesLoader {
|
||||
public <T> void addInstance(String factoryType, T... factoryInstance) {
|
||||
List<String> implementations = this.factories.computeIfAbsent(factoryType, key -> new ArrayList<>());
|
||||
for (T factoryImplementation : factoryInstance) {
|
||||
String reference = "!" + factoryType + ":" + factoryImplementation.getClass().getName()
|
||||
+ this.sequence.getAndIncrement();
|
||||
String reference = "!" + factoryType + ":" + factoryImplementation.getClass().getName() +
|
||||
this.sequence.getAndIncrement();
|
||||
implementations.add(reference);
|
||||
this.implementations.put(reference, factoryImplementation);
|
||||
}
|
||||
|
||||
+2
-2
@@ -299,8 +299,8 @@ public final class TestCompiler {
|
||||
}
|
||||
|
||||
private DynamicClassLoader compile() {
|
||||
ClassLoader classLoaderToUse = (this.classLoader != null ? this.classLoader
|
||||
: Thread.currentThread().getContextClassLoader());
|
||||
ClassLoader classLoaderToUse = (this.classLoader != null ? this.classLoader :
|
||||
Thread.currentThread().getContextClassLoader());
|
||||
List<DynamicJavaFileObject> compilationUnits = this.sourceFiles.stream().map(
|
||||
DynamicJavaFileObject::new).toList();
|
||||
StandardJavaFileManager standardFileManager = this.compiler.getStandardFileManager(
|
||||
|
||||
+2
-2
@@ -102,8 +102,8 @@ public class DefaultMethodReference implements MethodReference {
|
||||
TypeName argumentType = argumentTypes[i];
|
||||
CodeBlock argumentCode = argumentCodeGenerator.generateCode(argumentType);
|
||||
if (argumentCode == null) {
|
||||
throw new IllegalArgumentException("Could not generate code for " + this
|
||||
+ ": parameter " + i + " of type " + argumentType + " is not supported");
|
||||
throw new IllegalArgumentException("Could not generate code for " + this +
|
||||
": parameter " + i + " of type " + argumentType + " is not supported");
|
||||
}
|
||||
arguments.add(argumentCode);
|
||||
}
|
||||
|
||||
@@ -192,9 +192,9 @@ public interface GeneratedFiles {
|
||||
|
||||
private static void validatePackage(String packageName, String className) {
|
||||
if (!StringUtils.hasLength(packageName)) {
|
||||
throw new IllegalArgumentException("Could not add '" + className + "', "
|
||||
+ "processing classes in the default package is not supported. "
|
||||
+ "Did you forget to add a package statement?");
|
||||
throw new IllegalArgumentException("Could not add '" + className + "', " +
|
||||
"processing classes in the default package is not supported. " +
|
||||
"Did you forget to add a package statement?");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -240,8 +240,8 @@ public abstract class ValueCodeGeneratorDelegates {
|
||||
if (escaped != null) {
|
||||
return escaped;
|
||||
}
|
||||
return (!Character.isISOControl(ch)) ? Character.toString(ch)
|
||||
: String.format("\\u%04x", (int) ch);
|
||||
return (!Character.isISOControl(ch)) ? Character.toString(ch) :
|
||||
String.format("\\u%04x", (int) ch);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -108,8 +108,8 @@ public class BindingReflectionHintsRegistrar {
|
||||
registerPropertyHints(hints, seen, method, 0);
|
||||
}
|
||||
else if ((methodName.startsWith("get") && method.getParameterCount() == 0 && method.getReturnType() != void.class) ||
|
||||
(methodName.startsWith("is") && method.getParameterCount() == 0
|
||||
&& ClassUtils.resolvePrimitiveIfNecessary(method.getReturnType()) == Boolean.class)) {
|
||||
(methodName.startsWith("is") && method.getParameterCount() == 0 &&
|
||||
ClassUtils.resolvePrimitiveIfNecessary(method.getReturnType()) == Boolean.class)) {
|
||||
registerPropertyHints(hints, seen, method, -1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -27,6 +27,7 @@ import java.util.stream.Stream;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Gather the need for resources available at runtime.
|
||||
@@ -50,14 +51,14 @@ public class ResourceHints {
|
||||
this.resourceBundleHints = new LinkedHashSet<>();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the resources that should be made available at runtime.
|
||||
* @return a stream of {@link ResourcePatternHints}
|
||||
*/
|
||||
public Stream<ResourcePatternHints> resourcePatternHints() {
|
||||
Stream<ResourcePatternHints> patterns = this.resourcePatternHints.stream();
|
||||
return (this.types.isEmpty() ? patterns
|
||||
: Stream.concat(Stream.of(typesPatternResourceHint()), patterns));
|
||||
return (this.types.isEmpty() ? patterns : Stream.concat(Stream.of(typesPatternResourceHint()), patterns));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,18 +71,18 @@ public class ResourceHints {
|
||||
|
||||
/**
|
||||
* Register a pattern if the given {@code location} is available on the
|
||||
* classpath. This delegates to {@link ClassLoader#getResource(String)}
|
||||
* which validates directories as well. The location is not included in
|
||||
* the hint.
|
||||
* @param classLoader the classloader to use
|
||||
* classpath. This delegates to {@link ClassLoader#getResource(String)} which
|
||||
* validates directories as well. The location is not included in the hint.
|
||||
* @param classLoader the ClassLoader to use, or {@code null} for the default
|
||||
* @param location a '/'-separated path name that should exist
|
||||
* @param resourceHint a builder to customize the resource pattern
|
||||
* @return {@code this}, to facilitate method chaining
|
||||
*/
|
||||
public ResourceHints registerPatternIfPresent(@Nullable ClassLoader classLoader, String location,
|
||||
Consumer<ResourcePatternHints.Builder> resourceHint) {
|
||||
ClassLoader classLoaderToUse = (classLoader != null ? classLoader : getClass().getClassLoader());
|
||||
if (classLoaderToUse.getResource(location) != null) {
|
||||
|
||||
ClassLoader classLoaderToUse = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
|
||||
if (classLoaderToUse != null && classLoaderToUse.getResource(location) != null) {
|
||||
registerPattern(resourceHint);
|
||||
}
|
||||
return this;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -39,8 +39,7 @@ public interface RuntimeHintsRegistrar {
|
||||
/**
|
||||
* Contribute hints to the given {@link RuntimeHints} instance.
|
||||
* @param hints the hints contributed so far for the deployment unit
|
||||
* @param classLoader the classloader, or {@code null} if even the system
|
||||
* ClassLoader is not accessible
|
||||
* @param classLoader the ClassLoader to use, or {@code null} for the default
|
||||
*/
|
||||
void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader);
|
||||
|
||||
|
||||
+15
-13
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,6 +23,7 @@ import java.util.List;
|
||||
import org.springframework.aot.hint.ResourceHints;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
|
||||
/**
|
||||
@@ -66,19 +67,21 @@ public class FilePatternResourceHintsRegistrar {
|
||||
|
||||
@Deprecated(since = "6.0.12", forRemoval = true)
|
||||
public void registerHints(ResourceHints hints, @Nullable ClassLoader classLoader) {
|
||||
ClassLoader classLoaderToUse = (classLoader != null ? classLoader : getClass().getClassLoader());
|
||||
List<String> includes = new ArrayList<>();
|
||||
for (String location : this.classpathLocations) {
|
||||
if (classLoaderToUse.getResource(location) != null) {
|
||||
for (String filePrefix : this.filePrefixes) {
|
||||
for (String fileExtension : this.fileExtensions) {
|
||||
includes.add(location + filePrefix + "*" + fileExtension);
|
||||
ClassLoader classLoaderToUse = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
|
||||
if (classLoaderToUse != null) {
|
||||
List<String> includes = new ArrayList<>();
|
||||
for (String location : this.classpathLocations) {
|
||||
if (classLoaderToUse.getResource(location) != null) {
|
||||
for (String filePrefix : this.filePrefixes) {
|
||||
for (String fileExtension : this.fileExtensions) {
|
||||
includes.add(location + filePrefix + "*" + fileExtension);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!includes.isEmpty()) {
|
||||
hints.registerPattern(hint -> hint.includes(includes.toArray(String[]::new)));
|
||||
if (!includes.isEmpty()) {
|
||||
hints.registerPattern(hint -> hint.includes(includes.toArray(String[]::new)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,8 +249,7 @@ public class FilePatternResourceHintsRegistrar {
|
||||
* classpath location that resolves against the {@code ClassLoader}, files
|
||||
* with the configured file prefixes and extensions are registered.
|
||||
* @param hints the hints contributed so far for the deployment unit
|
||||
* @param classLoader the classloader, or {@code null} if even the system
|
||||
* ClassLoader isn't accessible
|
||||
* @param classLoader the ClassLoader to use, or {@code null} for the default
|
||||
*/
|
||||
public void registerHints(ResourceHints hints, @Nullable ClassLoader classLoader) {
|
||||
build().registerHints(hints, classLoader);
|
||||
|
||||
+6
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -48,10 +48,11 @@ class SpringFactoriesLoaderRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
|
||||
ClassLoader classLoaderToUse = (classLoader != null ? classLoader :
|
||||
SpringFactoriesLoaderRuntimeHints.class.getClassLoader());
|
||||
for (String resourceLocation : RESOURCE_LOCATIONS) {
|
||||
registerHints(hints, classLoaderToUse, resourceLocation);
|
||||
ClassLoader classLoaderToUse = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
|
||||
if (classLoaderToUse != null) {
|
||||
for (String resourceLocation : RESOURCE_LOCATIONS) {
|
||||
registerHints(hints, classLoaderToUse, resourceLocation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -195,7 +195,7 @@ public class ClassReader {
|
||||
this.b = classFileBuffer;
|
||||
// Check the class' major_version. This field is after the magic and minor_version fields, which
|
||||
// use 4 and 2 bytes respectively.
|
||||
if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V24) {
|
||||
if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V25) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unsupported class file major version " + readShort(classFileOffset + 6));
|
||||
}
|
||||
|
||||
@@ -594,7 +594,7 @@ public abstract class MethodVisitor {
|
||||
* Visits a LOOKUPSWITCH instruction.
|
||||
*
|
||||
* @param dflt beginning of the default handler block.
|
||||
* @param keys the values of the keys.
|
||||
* @param keys the values of the keys. Keys must be sorted in increasing order.
|
||||
* @param labels beginnings of the handler blocks. {@code labels[i]} is the beginning of the
|
||||
* handler block for the {@code keys[i]} key.
|
||||
*/
|
||||
|
||||
@@ -289,6 +289,7 @@ public interface Opcodes {
|
||||
int V22 = 0 << 16 | 66;
|
||||
int V23 = 0 << 16 | 67;
|
||||
int V24 = 0 << 16 | 68;
|
||||
int V25 = 0 << 16 | 69;
|
||||
|
||||
/**
|
||||
* Version flag indicating that the class is using 'preview' features.
|
||||
|
||||
+9
-5
@@ -123,13 +123,17 @@ abstract public class AbstractClassGenerator<T> implements ClassGenerator {
|
||||
}
|
||||
|
||||
public Object get(AbstractClassGenerator gen, boolean useCache) {
|
||||
if (!useCache) {
|
||||
return gen.generate(ClassLoaderData.this);
|
||||
}
|
||||
else {
|
||||
// SPRING PATCH BEGIN
|
||||
Object value = null;
|
||||
if (useCache) {
|
||||
Object cachedValue = generatedClasses.get(gen);
|
||||
return gen.unwrapCachedValue(cachedValue);
|
||||
value = gen.unwrapCachedValue(cachedValue);
|
||||
}
|
||||
if (value == null) { // fallback when cached WeakReference returns null
|
||||
value = gen.generate(ClassLoaderData.this);
|
||||
}
|
||||
return value;
|
||||
// SPRING PATCH END
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -159,6 +159,9 @@ public final class GenericTypeResolver {
|
||||
if (genericType instanceof TypeVariable<?> typeVariable) {
|
||||
ResolvableType resolvedTypeVariable = resolveVariable(
|
||||
typeVariable, ResolvableType.forClass(contextClass));
|
||||
if (resolvedTypeVariable == ResolvableType.NONE) {
|
||||
resolvedTypeVariable = ResolvableType.forVariableBounds(typeVariable);
|
||||
}
|
||||
if (resolvedTypeVariable != ResolvableType.NONE) {
|
||||
Class<?> resolved = resolvedTypeVariable.resolve();
|
||||
if (resolved != null) {
|
||||
@@ -176,6 +179,9 @@ public final class GenericTypeResolver {
|
||||
Type typeArgument = typeArguments[i];
|
||||
if (typeArgument instanceof TypeVariable<?> typeVariable) {
|
||||
ResolvableType resolvedTypeArgument = resolveVariable(typeVariable, contextType);
|
||||
if (resolvedTypeArgument == ResolvableType.NONE) {
|
||||
resolvedTypeArgument = ResolvableType.forVariableBounds(typeVariable);
|
||||
}
|
||||
if (resolvedTypeArgument != ResolvableType.NONE) {
|
||||
generics[i] = resolvedTypeArgument;
|
||||
}
|
||||
@@ -229,7 +235,7 @@ public final class GenericTypeResolver {
|
||||
return resolvedType;
|
||||
}
|
||||
}
|
||||
return ResolvableType.forVariableBounds(typeVariable);
|
||||
return ResolvableType.NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -343,7 +343,7 @@ public class ResolvableType implements Serializable {
|
||||
return otherBounds.isAssignableFrom(this, matchedBefore);
|
||||
}
|
||||
else if (!strict) {
|
||||
return (matchedBefore != null ? otherBounds.equalsType(this) :
|
||||
return (matchedBefore != null ? otherBounds.equalsType(this, matchedBefore) :
|
||||
otherBounds.isAssignableTo(this, matchedBefore));
|
||||
}
|
||||
else {
|
||||
@@ -1794,11 +1794,13 @@ public class ResolvableType implements Serializable {
|
||||
* Return {@code true} if these bounds are equal to the specified type.
|
||||
* @param type the type to test against
|
||||
* @return {@code true} if these bounds are equal to the type
|
||||
* @since 6.2.3
|
||||
* @since 6.2.4
|
||||
*/
|
||||
public boolean equalsType(ResolvableType type) {
|
||||
public boolean equalsType(ResolvableType type, @Nullable Map<Type, Type> matchedBefore) {
|
||||
for (ResolvableType bound : this.bounds) {
|
||||
if (!type.equalsType(bound)) {
|
||||
if (this.kind == Kind.UPPER && bound.hasUnresolvableGenerics() ?
|
||||
!type.isAssignableFrom(bound, true, matchedBefore, false) :
|
||||
!type.equalsType(bound)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+8
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -36,8 +36,12 @@ import org.springframework.util.MultiValueMap;
|
||||
*
|
||||
* <p>{@code AnnotatedElementUtils} defines the public API for Spring's
|
||||
* meta-annotation programming model with support for <em>annotation attribute
|
||||
* overrides</em>. If you do not need support for annotation attribute
|
||||
* overrides, consider using {@link AnnotationUtils} instead.
|
||||
* overrides</em> and {@link AliasFor @AliasFor}. Note, however, that
|
||||
* {@code AnnotatedElementUtils} is effectively a facade for the
|
||||
* {@link MergedAnnotations} API. For fine-grained support consider using the
|
||||
* {@code MergedAnnotations} API directly. If you do not need support for
|
||||
* annotation attribute overrides, {@code @AliasFor}, or merged annotations,
|
||||
* consider using {@link AnnotationUtils} instead.
|
||||
*
|
||||
* <p>Note that the features of this class are not provided by the JDK's
|
||||
* introspection facilities themselves.
|
||||
@@ -87,6 +91,7 @@ import org.springframework.util.MultiValueMap;
|
||||
* @since 4.0
|
||||
* @see AliasFor
|
||||
* @see AnnotationAttributes
|
||||
* @see MergedAnnotations
|
||||
* @see AnnotationUtils
|
||||
* @see BridgeMethodResolver
|
||||
*/
|
||||
|
||||
+19
-19
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -60,7 +60,7 @@ import org.springframework.util.StringUtils;
|
||||
* <h3>Terminology</h3>
|
||||
* The terms <em>directly present</em>, <em>indirectly present</em>, and
|
||||
* <em>present</em> have the same meanings as defined in the class-level
|
||||
* javadoc for {@link AnnotatedElement} (in Java 8).
|
||||
* javadoc for {@link AnnotatedElement}.
|
||||
*
|
||||
* <p>An annotation is <em>meta-present</em> on an element if the annotation
|
||||
* is declared as a meta-annotation on some other annotation which is
|
||||
@@ -73,7 +73,8 @@ import org.springframework.util.StringUtils;
|
||||
* provide support for finding annotations used as meta-annotations. Consult the
|
||||
* javadoc for each method in this class for details. For fine-grained support for
|
||||
* meta-annotations with <em>attribute overrides</em> in <em>composed annotations</em>,
|
||||
* consider using {@link AnnotatedElementUtils}'s more specific methods instead.
|
||||
* consider using the {@link MergedAnnotations} API directly or the more specific
|
||||
* methods in {@link AnnotatedElementUtils} instead.
|
||||
*
|
||||
* <h3>Attribute Aliases</h3>
|
||||
* <p>All public methods in this class that return annotations, arrays of
|
||||
@@ -97,6 +98,7 @@ import org.springframework.util.StringUtils;
|
||||
* @since 2.0
|
||||
* @see AliasFor
|
||||
* @see AnnotationAttributes
|
||||
* @see MergedAnnotations
|
||||
* @see AnnotatedElementUtils
|
||||
* @see BridgeMethodResolver
|
||||
* @see java.lang.reflect.AnnotatedElement#getAnnotations()
|
||||
@@ -307,11 +309,11 @@ public abstract class AnnotationUtils {
|
||||
* {@code annotationType} from the supplied {@link AnnotatedElement}, where
|
||||
* such annotations are either <em>present</em>, <em>indirectly present</em>,
|
||||
* or <em>meta-present</em> on the element.
|
||||
* <p>This method mimics the functionality of Java 8's
|
||||
* <p>This method mimics the functionality of
|
||||
* {@link java.lang.reflect.AnnotatedElement#getAnnotationsByType(Class)}
|
||||
* with support for automatic detection of a <em>container annotation</em>
|
||||
* declared via @{@link java.lang.annotation.Repeatable} (when running on
|
||||
* Java 8 or higher) and with additional support for meta-annotations.
|
||||
* declared via {@link java.lang.annotation.Repeatable @Repeatable} and with
|
||||
* additional support for meta-annotations.
|
||||
* <p>Handles both single annotations and annotations nested within a
|
||||
* <em>container annotation</em>.
|
||||
* <p>Correctly handles <em>bridge methods</em> generated by the
|
||||
@@ -342,7 +344,7 @@ public abstract class AnnotationUtils {
|
||||
* {@code annotationType} from the supplied {@link AnnotatedElement}, where
|
||||
* such annotations are either <em>present</em>, <em>indirectly present</em>,
|
||||
* or <em>meta-present</em> on the element.
|
||||
* <p>This method mimics the functionality of Java 8's
|
||||
* <p>This method mimics the functionality
|
||||
* {@link java.lang.reflect.AnnotatedElement#getAnnotationsByType(Class)}
|
||||
* with additional support for meta-annotations.
|
||||
* <p>Handles both single annotations and annotations nested within a
|
||||
@@ -353,10 +355,9 @@ public abstract class AnnotationUtils {
|
||||
* <em>present</em> on the supplied element.
|
||||
* @param annotatedElement the element to look for annotations on
|
||||
* @param annotationType the annotation type to look for
|
||||
* @param containerAnnotationType the type of the container that holds
|
||||
* the annotations; may be {@code null} if a container is not supported
|
||||
* or if it should be looked up via @{@link java.lang.annotation.Repeatable}
|
||||
* when running on Java 8 or higher
|
||||
* @param containerAnnotationType the type of the container that holds the
|
||||
* annotations; may be {@code null} if a container is not supported or if it
|
||||
* should be looked up via {@link java.lang.annotation.Repeatable @Repeatable}
|
||||
* @return the annotations found or an empty set (never {@code null})
|
||||
* @since 4.2
|
||||
* @see #getRepeatableAnnotations(AnnotatedElement, Class)
|
||||
@@ -388,11 +389,11 @@ public abstract class AnnotationUtils {
|
||||
* of {@code annotationType} from the supplied {@link AnnotatedElement},
|
||||
* where such annotations are either <em>directly present</em>,
|
||||
* <em>indirectly present</em>, or <em>meta-present</em> on the element.
|
||||
* <p>This method mimics the functionality of Java 8's
|
||||
* <p>This method mimics the functionality of
|
||||
* {@link java.lang.reflect.AnnotatedElement#getDeclaredAnnotationsByType(Class)}
|
||||
* with support for automatic detection of a <em>container annotation</em>
|
||||
* declared via @{@link java.lang.annotation.Repeatable} (when running on
|
||||
* Java 8 or higher) and with additional support for meta-annotations.
|
||||
* declared via {@link java.lang.annotation.Repeatable @Repeatable} and with
|
||||
* additional support for meta-annotations.
|
||||
* <p>Handles both single annotations and annotations nested within a
|
||||
* <em>container annotation</em>.
|
||||
* <p>Correctly handles <em>bridge methods</em> generated by the
|
||||
@@ -424,7 +425,7 @@ public abstract class AnnotationUtils {
|
||||
* of {@code annotationType} from the supplied {@link AnnotatedElement},
|
||||
* where such annotations are either <em>directly present</em>,
|
||||
* <em>indirectly present</em>, or <em>meta-present</em> on the element.
|
||||
* <p>This method mimics the functionality of Java 8's
|
||||
* <p>This method mimics the functionality of
|
||||
* {@link java.lang.reflect.AnnotatedElement#getDeclaredAnnotationsByType(Class)}
|
||||
* with additional support for meta-annotations.
|
||||
* <p>Handles both single annotations and annotations nested within a
|
||||
@@ -435,10 +436,9 @@ public abstract class AnnotationUtils {
|
||||
* <em>present</em> on the supplied element.
|
||||
* @param annotatedElement the element to look for annotations on
|
||||
* @param annotationType the annotation type to look for
|
||||
* @param containerAnnotationType the type of the container that holds
|
||||
* the annotations; may be {@code null} if a container is not supported
|
||||
* or if it should be looked up via @{@link java.lang.annotation.Repeatable}
|
||||
* when running on Java 8 or higher
|
||||
* @param containerAnnotationType the type of the container that holds the
|
||||
* annotations; may be {@code null} if a container is not supported or if it
|
||||
* should be looked up via {@link java.lang.annotation.Repeatable @Repeatable}
|
||||
* @return the annotations found or an empty set (never {@code null})
|
||||
* @since 4.2
|
||||
* @see #getRepeatableAnnotations(AnnotatedElement, Class)
|
||||
|
||||
+6
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -37,7 +37,7 @@ import org.springframework.util.Assert;
|
||||
* <ul>
|
||||
* <li>Explicit and Implicit {@link AliasFor @AliasFor} declarations on one or
|
||||
* more attributes within the annotation</li>
|
||||
* <li>Explicit {@link AliasFor @AliasFor} declarations for a meta-annotation</li>
|
||||
* <li>Explicit {@code @AliasFor} declarations for a meta-annotation</li>
|
||||
* <li>Convention based attribute aliases for a meta-annotation</li>
|
||||
* <li>From a meta-annotation declaration</li>
|
||||
* </ul>
|
||||
@@ -140,6 +140,9 @@ import org.springframework.util.Assert;
|
||||
* @see MergedAnnotationCollectors
|
||||
* @see MergedAnnotationPredicates
|
||||
* @see MergedAnnotationSelectors
|
||||
* @see AliasFor
|
||||
* @see AnnotationUtils
|
||||
* @see AnnotatedElementUtils
|
||||
*/
|
||||
public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>> {
|
||||
|
||||
@@ -560,6 +563,7 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
|
||||
* @see #withRepeatableContainers(RepeatableContainers)
|
||||
* @see #withAnnotationFilter(AnnotationFilter)
|
||||
* @see #from(AnnotatedElement)
|
||||
* @see org.springframework.test.context.TestContextAnnotationUtils#searchEnclosingClass(Class)
|
||||
*/
|
||||
public Search withEnclosingClasses(Predicate<Class<?>> searchEnclosingClass) {
|
||||
Assert.notNull(searchEnclosingClass, "Predicate must not be null");
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -38,7 +38,7 @@ import org.springframework.util.ClassUtils;
|
||||
* applying the mapping and mirroring rules of an {@link AnnotationTypeMapping}.
|
||||
*
|
||||
* <p>Root attribute values are extracted from a source object using a supplied
|
||||
* {@code BiFunction}. This allows various different annotation models to be
|
||||
* {@link ValueExtractor}. This allows various different annotation models to be
|
||||
* supported by the same class. For example, the attributes source might be an
|
||||
* actual {@link Annotation} instance where methods on the annotation instance
|
||||
* are {@linkplain AnnotationUtils#invokeAnnotationMethod(Method, Object) invoked}
|
||||
@@ -375,8 +375,8 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
||||
private Object getRequiredValue(int attributeIndex, String attributeName) {
|
||||
Object value = getValue(attributeIndex, Object.class);
|
||||
if (value == null) {
|
||||
throw new NoSuchElementException("No element at attribute index "
|
||||
+ attributeIndex + " for name " + attributeName);
|
||||
throw new NoSuchElementException("No element at attribute index " +
|
||||
attributeIndex + " for name " + attributeName);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -71,8 +71,8 @@ public class ResourceRegionEncoder extends AbstractEncoder<ResourceRegion> {
|
||||
|
||||
@Override
|
||||
public boolean canEncode(ResolvableType elementType, @Nullable MimeType mimeType) {
|
||||
return super.canEncode(elementType, mimeType)
|
||||
&& ResourceRegion.class.isAssignableFrom(elementType.toClass());
|
||||
return super.canEncode(elementType, mimeType) &&
|
||||
ResourceRegion.class.isAssignableFrom(elementType.toClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+15
@@ -348,6 +348,13 @@ public class GenericConversionService implements ConfigurableConversionService {
|
||||
conditionalConverter.matches(sourceType, targetType);
|
||||
}
|
||||
|
||||
public boolean matchesFallback(TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
return (this.typeInfo.getTargetType() == targetType.getObjectType() &&
|
||||
this.targetType.hasUnresolvableGenerics() &&
|
||||
(!(this.converter instanceof ConditionalConverter conditionalConverter) ||
|
||||
conditionalConverter.matches(sourceType, targetType)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
@@ -622,12 +629,20 @@ public class GenericConversionService implements ConfigurableConversionService {
|
||||
|
||||
@Nullable
|
||||
public GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
// Look for proper match among all converters (taking full generics into account)
|
||||
for (GenericConverter converter : this.converters) {
|
||||
if (!(converter instanceof ConditionalGenericConverter genericConverter) ||
|
||||
genericConverter.matches(sourceType, targetType)) {
|
||||
return converter;
|
||||
}
|
||||
}
|
||||
// Fallback to pre-6.2.3 behavior: accept Class match for unresolvable generics
|
||||
for (GenericConverter converter : this.converters) {
|
||||
if (converter instanceof ConverterAdapter converterAdapter &&
|
||||
converterAdapter.matchesFallback(sourceType, targetType)) {
|
||||
return converter;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
+11
-3
@@ -56,6 +56,7 @@ public abstract class AbstractFileResolvingResource extends AbstractResource {
|
||||
// Try a URL connection content-length header
|
||||
URLConnection con = url.openConnection();
|
||||
customizeConnection(con);
|
||||
|
||||
HttpURLConnection httpCon = (con instanceof HttpURLConnection huc ? huc : null);
|
||||
if (httpCon != null) {
|
||||
httpCon.setRequestMethod("HEAD");
|
||||
@@ -81,9 +82,16 @@ public abstract class AbstractFileResolvingResource extends AbstractResource {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (con.getContentLengthLong() > 0) {
|
||||
|
||||
if (con instanceof JarURLConnection jarCon) {
|
||||
// For JarURLConnection, do not check content-length but rather the
|
||||
// existence of the entry (or the jar root in case of no entryName).
|
||||
return (jarCon.getEntryName() == null || jarCon.getJarEntry() != null);
|
||||
}
|
||||
else if (con.getContentLengthLong() > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (httpCon != null) {
|
||||
// No HTTP OK status, and no content-length header: give up
|
||||
httpCon.disconnect();
|
||||
@@ -343,8 +351,8 @@ public abstract class AbstractFileResolvingResource extends AbstractResource {
|
||||
*/
|
||||
protected void customizeConnection(URLConnection con) throws IOException {
|
||||
ResourceUtils.useCachesIfNecessary(con);
|
||||
if (con instanceof HttpURLConnection httpConn) {
|
||||
customizeConnection(httpConn);
|
||||
if (con instanceof HttpURLConnection httpCon) {
|
||||
customizeConnection(httpCon);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -234,8 +234,8 @@ public class UrlResource extends AbstractFileResolvingResource {
|
||||
}
|
||||
catch (IOException ex) {
|
||||
// Close the HTTP connection (if applicable).
|
||||
if (con instanceof HttpURLConnection httpConn) {
|
||||
httpConn.disconnect();
|
||||
if (con instanceof HttpURLConnection httpCon) {
|
||||
httpCon.disconnect();
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
|
||||
+5
-1
@@ -816,17 +816,21 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
// Clean root entry path to match jar entries format without "!" separators
|
||||
rootEntryPath = rootEntryPath.replace(ResourceUtils.JAR_URL_SEPARATOR, "/");
|
||||
// Search sorted entries from first entry with rootEntryPath prefix
|
||||
boolean rootEntryPathFound = false;
|
||||
for (String entryPath : entriesCache.tailSet(rootEntryPath, false)) {
|
||||
if (!entryPath.startsWith(rootEntryPath)) {
|
||||
// We are beyond the potential matches in the current TreeSet.
|
||||
break;
|
||||
}
|
||||
rootEntryPathFound = true;
|
||||
String relativePath = entryPath.substring(rootEntryPath.length());
|
||||
if (getPathMatcher().match(subPattern, relativePath)) {
|
||||
result.add(rootDirResource.createRelative(relativePath));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
if (rootEntryPathFound) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -472,8 +472,8 @@ public class SpringFactoriesLoader {
|
||||
|
||||
private static <T> void makeAccessible(Constructor<T> constructor,
|
||||
KFunction<T> kotlinConstructor) {
|
||||
if ((!Modifier.isPublic(constructor.getModifiers())
|
||||
|| !Modifier.isPublic(constructor.getDeclaringClass().getModifiers()))) {
|
||||
if ((!Modifier.isPublic(constructor.getModifiers()) ||
|
||||
!Modifier.isPublic(constructor.getDeclaringClass().getModifiers()))) {
|
||||
KCallablesJvm.setAccessible(kotlinConstructor, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -69,7 +69,13 @@ public class TaskRejectedException extends RejectedExecutionException {
|
||||
|
||||
private static String executorDescription(Executor executor) {
|
||||
if (executor instanceof ExecutorService executorService) {
|
||||
return "ExecutorService in " + (executorService.isShutdown() ? "shutdown" : "active") + " state";
|
||||
try {
|
||||
return "ExecutorService in " + (executorService.isShutdown() ? "shutdown" : "active") + " state";
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// UnsupportedOperationException/IllegalStateException from ManagedExecutorService.isShutdown()
|
||||
// Falling back to toString() below.
|
||||
}
|
||||
}
|
||||
return executor.toString();
|
||||
}
|
||||
|
||||
@@ -275,8 +275,8 @@ public class AntPathMatcher implements PathMatcher {
|
||||
if (!matchStrings(pattDir, pathDirs[pathIdxEnd], uriTemplateVariables)) {
|
||||
return false;
|
||||
}
|
||||
if (pattIdxEnd == (pattDirs.length - 1)
|
||||
&& pattern.endsWith(this.pathSeparator) != path.endsWith(this.pathSeparator)) {
|
||||
if (pattIdxEnd == (pattDirs.length - 1) &&
|
||||
pattern.endsWith(this.pathSeparator) != path.endsWith(this.pathSeparator)) {
|
||||
return false;
|
||||
}
|
||||
pattIdxEnd--;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user