mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
167 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f32e749dc0 | |||
| ccaecab500 | |||
| 67e03105b5 | |||
| 76bc9cf325 | |||
| db52c77cca | |||
| 1e742aae34 | |||
| 20dd585c93 | |||
| 707eb701dc | |||
| 2c97996796 | |||
| 3a068b807b | |||
| f54b19ff90 | |||
| 494d2ab727 | |||
| 627d9cf8be | |||
| dd3a67c7ab | |||
| 85cc229063 | |||
| fa95f12be0 | |||
| 035cc72fc8 | |||
| 29a39b617e | |||
| a1471a9266 | |||
| 21793b4f93 | |||
| 2d255c4d5e | |||
| 10391586d1 | |||
| edadc79835 | |||
| 3783d31c09 | |||
| 87730f76b1 | |||
| 6fae3e150e | |||
| ad44e8ab0a | |||
| aadf96ba92 | |||
| 2baf064d04 | |||
| 6121e2f526 | |||
| 8e33805d29 | |||
| 1f19bb2311 | |||
| 2784410cc6 | |||
| f4ac323409 | |||
| 6db00e63c7 | |||
| ed613e767a | |||
| 65781046cf | |||
| 0ee36095e7 | |||
| 0fc38117df | |||
| 86b8a70ce6 | |||
| 4bf759d872 | |||
| 770cbd2fb5 | |||
| e5f04e5ddf | |||
| c18784678d | |||
| 51cdff591c | |||
| d93114df9a | |||
| 5ac4c3bd76 | |||
| 05c3ffb2fb | |||
| 19e8ed130c | |||
| 4464251754 | |||
| 8868fe2ea5 | |||
| c373f496f3 | |||
| eb1883bdc4 | |||
| df6f66110f | |||
| ac235a0c43 | |||
| a4e3af5cbe | |||
| e71117dcdf | |||
| d5874ab99e | |||
| 99327b7db1 | |||
| 3e06441d97 | |||
| 01f2925048 | |||
| 44a37000ec | |||
| 53fe5fafed | |||
| b9bd98fc5b | |||
| 7714110940 | |||
| 75d1278bde | |||
| 620f558547 | |||
| 1e78cc35e5 | |||
| 11fdb5ba17 | |||
| 9957bb6918 | |||
| 6a7a0bddb7 | |||
| 5c012bbb0c | |||
| 5df6e8825d | |||
| 654e822676 | |||
| e943058b18 | |||
| cafb38ad1d | |||
| e778d2e908 | |||
| c5bcfc7682 | |||
| 5752e03d97 | |||
| dc26d3b0ec | |||
| 7f94c64b72 | |||
| 50e55d5219 | |||
| fcd4ba2f1f | |||
| e73107341c | |||
| f8a33cd66e | |||
| f088d4a05b | |||
| 84c28995fb | |||
| 925fa0272b | |||
| 09aa59f9e7 | |||
| 7874a59771 | |||
| 4dab35205d | |||
| 5c6b9be3a1 | |||
| 33fba8ea0c | |||
| 9aa707ec4b | |||
| ea30c8fb5b | |||
| 6bdf7ad36a | |||
| d2108d2db6 | |||
| f9be717602 | |||
| bb446a3905 | |||
| f3dce4bb9a | |||
| 70cb96c1d8 | |||
| 6ec264252b | |||
| 187b4e5ea6 | |||
| 3bc607df53 | |||
| 69c92f9ac7 | |||
| 875eeabb6f | |||
| 7a60e2024b | |||
| da95542d8f | |||
| 6669ab1ae7 | |||
| d05ac097dd | |||
| 35103b0cd1 | |||
| 3b50f992fe | |||
| 4bd54dffbc | |||
| 86650d1a39 | |||
| 87424cd605 | |||
| 80e82cd43f | |||
| 66ce8c9a25 | |||
| e9fcb21d55 | |||
| ee9dff30c5 | |||
| 2158410853 | |||
| e0a55c2caa | |||
| 0b96da4b6d | |||
| 387a16bd4e | |||
| 8b5d993e61 | |||
| 5459304a4b | |||
| 00d4830e5c | |||
| 3c2e21a578 | |||
| 5f053401e2 | |||
| 63770fb074 | |||
| 9df4bce043 | |||
| 19fd8159b2 | |||
| 147abc91a5 | |||
| e12eb9436d | |||
| 23162bb306 | |||
| a2c5fed494 | |||
| 957b6b2caf | |||
| 35f458fa5f | |||
| 8e83f93bcb | |||
| 407113945d | |||
| 4cf5c7796d | |||
| 847e8a2b23 | |||
| 9aab4a60f5 | |||
| 1fe2216c59 | |||
| 5ff9e6955c | |||
| 0cb4043aac | |||
| 6030e62766 | |||
| 867b9f61b2 | |||
| 6300fb37ad | |||
| 0d22569422 | |||
| 06658c3c71 | |||
| a37abd5e54 | |||
| 18456dec52 | |||
| d17c75a7ef | |||
| f547b6ad2a | |||
| e1bd13d3d6 | |||
| 08237da4b4 | |||
| f7496a393d | |||
| 0f92ba1663 | |||
| 865fa33927 | |||
| 9120f87897 | |||
| edd1e9134f | |||
| fef3cf8e58 | |||
| ce0923b946 | |||
| 227049824c | |||
| 8f130316d2 | |||
| 54c4f1b226 | |||
| 062c6241e1 |
@@ -1,36 +0,0 @@
|
||||
Juergen Hoeller <jhoeller@vmware.com>
|
||||
Juergen Hoeller <jhoeller@vmware.com> <jhoeller@pivotal.io>
|
||||
Juergen Hoeller <jhoeller@vmware.com> <jhoeller@gopivotal.com>
|
||||
Rossen Stoyanchev <rstoyanchev@vmware.com>
|
||||
Rossen Stoyanchev <rstoyanchev@vmware.com> <rstoyanchev@pivotal.io>
|
||||
Rossen Stoyanchev <rstoyanchev@vmware.com> <rstoyanchev@gopivotal.com>
|
||||
Phillip Webb <pwebb@vmware.com>
|
||||
Phillip Webb <pwebb@vmware.com> <pwebb@pivotal.io>
|
||||
Phillip Webb <pwebb@vmware.com> <pwebb@gopivotal.com>
|
||||
Chris Beams <cbeams@vmware.com>
|
||||
Chris Beams <cbeams@vmware.com> <cbeams@pivotal.io>
|
||||
Chris Beams <cbeams@vmware.com> <cbeams@gopivotal.com>
|
||||
Arjen Poutsma <poutsmaa@vmware.com>
|
||||
Arjen Poutsma <poutsmaa@vmware.com> <apoutsma@pivotal.io>
|
||||
Arjen Poutsma <poutsmaa@vmware.com> <apoutsma@gopivotal.com>
|
||||
Arjen Poutsma <poutsmaa@vmware.com> <poutsma@mac.com>
|
||||
Arjen Poutsma <poutsmaa@vmware.com> <apoutsma@vmware.com>
|
||||
Oliver Drotbohm <odrotbohm@vmware.com>
|
||||
Oliver Drotbohm <odrotbohm@vmware.com> <ogierke@vmware.com>
|
||||
Oliver Drotbohm <odrotbohm@vmware.com> <ogierke@pivotal.io>
|
||||
Oliver Drotbohm <odrotbohm@vmware.com> <ogierke@gopivotal.com>
|
||||
Dave Syer <dsyer@vmware.com>
|
||||
Dave Syer <dsyer@vmware.com> <dsyer@pivotal.io>
|
||||
Dave Syer <dsyer@vmware.com> <dsyer@gopivotal.com>
|
||||
Dave Syer <dsyer@vmware.com> <david_syer@hotmail.com>
|
||||
Andy Clement <aclement@vmware.com>
|
||||
Andy Clement <aclement@vmware.com> <aclement@pivotal.io>
|
||||
Andy Clement <aclement@vmware.com> <aclement@gopivotal.com>
|
||||
Andy Clement <aclement@vmware.com> <andrew.clement@gmail.com>
|
||||
Sam Brannen <sbrannen@vmware.com>
|
||||
Sam Brannen <sbrannen@vmware.com> <sbrannen@pivotal.io>
|
||||
Sam Brannen <sbrannen@vmware.com> <sam@sambrannen.com>
|
||||
Simon Basle <sbasle@vmware.com>
|
||||
Simon Baslé <sbasle@vmware.com>
|
||||
<dmitry.katsubo@gmail.com> <dmitry.katsubo@gmai.com>
|
||||
Nick Williams <nicholas@nicholaswilliams.net>
|
||||
@@ -1,3 +1,3 @@
|
||||
# Enable auto-env through the sdkman_auto_env config
|
||||
# Add key=value pairs of SDKs to use below
|
||||
java=17.0.7-librca
|
||||
java=17.0.8.1-librca
|
||||
|
||||
+2
-3
@@ -5,7 +5,7 @@ plugins {
|
||||
id 'org.jetbrains.kotlin.plugin.serialization' version "${kotlinVersion}" apply false
|
||||
id 'org.jetbrains.dokka' version '1.8.10'
|
||||
id 'org.unbroken-dome.xjc' version '2.0.0' apply false
|
||||
id 'com.github.ben-manes.versions' version '0.46.0'
|
||||
id 'com.github.ben-manes.versions' version '0.49.0'
|
||||
id 'com.github.johnrengelman.shadow' version '8.1.1' apply false
|
||||
id 'de.undercouch.download' version '5.4.0'
|
||||
id 'me.champeau.jmh' version '0.7.1' apply false
|
||||
@@ -75,7 +75,7 @@ configure([rootProject] + javaProjects) { project ->
|
||||
}
|
||||
|
||||
checkstyle {
|
||||
toolVersion = "10.12.3"
|
||||
toolVersion = "10.12.5"
|
||||
configDirectory.set(rootProject.file("src/checkstyle"))
|
||||
}
|
||||
|
||||
@@ -146,7 +146,6 @@ configure(rootProject) {
|
||||
description = "Spring Framework"
|
||||
|
||||
apply plugin: "io.spring.nohttp"
|
||||
apply plugin: 'org.springframework.build.api-diff'
|
||||
|
||||
nohttp {
|
||||
source.exclude "**/test-output/**"
|
||||
|
||||
@@ -22,21 +22,6 @@ but doesn't affect the classpath of dependent projects.
|
||||
This plugin does not provide a `provided` configuration, as the native `compileOnly` and `testCompileOnly`
|
||||
configurations are preferred.
|
||||
|
||||
### API Diff
|
||||
|
||||
This plugin uses the [Gradle JApiCmp](https://github.com/melix/japicmp-gradle-plugin) plugin
|
||||
to generate API Diff reports for each Spring Framework module. This plugin is applied once on the root
|
||||
project and creates tasks in each framework module. Unlike previous versions of this part of the build,
|
||||
there is no need for checking out a specific tag. The plugin will fetch the JARs we want to compare the
|
||||
current working version with. You can generate the reports for all modules or a single module:
|
||||
|
||||
```
|
||||
./gradlew apiDiff -PbaselineVersion=5.1.0.RELEASE
|
||||
./gradlew :spring-core:apiDiff -PbaselineVersion=5.1.0.RELEASE
|
||||
```
|
||||
|
||||
The reports are located under `build/reports/api-diff/$OLDVERSION_to_$NEWVERSION/`.
|
||||
|
||||
|
||||
### RuntimeHints Java Agent
|
||||
|
||||
|
||||
@@ -19,16 +19,11 @@ ext {
|
||||
dependencies {
|
||||
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}")
|
||||
implementation("org.jetbrains.kotlin:kotlin-compiler-embeddable:${kotlinVersion}")
|
||||
implementation "me.champeau.gradle:japicmp-gradle-plugin:0.3.0"
|
||||
implementation "org.gradle:test-retry-gradle-plugin:1.4.1"
|
||||
implementation "org.gradle:test-retry-gradle-plugin:1.5.6"
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
plugins {
|
||||
apiDiffPlugin {
|
||||
id = "org.springframework.build.api-diff"
|
||||
implementationClass = "org.springframework.build.api.ApiDiffPlugin"
|
||||
}
|
||||
conventionsPlugin {
|
||||
id = "org.springframework.build.conventions"
|
||||
implementationClass = "org.springframework.build.ConventionsPlugin"
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.build.api;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import me.champeau.gradle.japicmp.JapicmpPlugin;
|
||||
import me.champeau.gradle.japicmp.JapicmpTask;
|
||||
import org.gradle.api.GradleException;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.artifacts.Dependency;
|
||||
import org.gradle.api.plugins.JavaBasePlugin;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin;
|
||||
import org.gradle.api.tasks.TaskProvider;
|
||||
import org.gradle.jvm.tasks.Jar;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* {@link Plugin} that applies the {@code "japicmp-gradle-plugin"}
|
||||
* and create tasks for all subprojects named {@code "spring-*"}, diffing the public API one by one
|
||||
* and creating the reports in {@code "build/reports/api-diff/$OLDVERSION_to_$NEWVERSION/"}.
|
||||
* <p>{@code "./gradlew apiDiff -PbaselineVersion=5.1.0.RELEASE"} will output the
|
||||
* reports for the API diff between the baseline version and the current one for all modules.
|
||||
* You can limit the report to a single module with
|
||||
* {@code "./gradlew :spring-core:apiDiff -PbaselineVersion=5.1.0.RELEASE"}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
public class ApiDiffPlugin implements Plugin<Project> {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ApiDiffPlugin.class);
|
||||
|
||||
public static final String TASK_NAME = "apiDiff";
|
||||
|
||||
private static final String BASELINE_VERSION_PROPERTY = "baselineVersion";
|
||||
|
||||
private static final List<String> PACKAGE_INCLUDES = Collections.singletonList("org.springframework.*");
|
||||
|
||||
private static final URI SPRING_MILESTONE_REPOSITORY = URI.create("https://repo.spring.io/milestone");
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
if (project.hasProperty(BASELINE_VERSION_PROPERTY) && project.equals(project.getRootProject())) {
|
||||
project.getPluginManager().apply(JapicmpPlugin.class);
|
||||
project.getPlugins().withType(JapicmpPlugin.class,
|
||||
plugin -> applyApiDiffConventions(project));
|
||||
}
|
||||
}
|
||||
|
||||
private void applyApiDiffConventions(Project project) {
|
||||
String baselineVersion = project.property(BASELINE_VERSION_PROPERTY).toString();
|
||||
project.subprojects(subProject -> {
|
||||
if (subProject.getName().startsWith("spring-")) {
|
||||
createApiDiffTask(baselineVersion, subProject);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void createApiDiffTask(String baselineVersion, Project project) {
|
||||
if (isProjectEligible(project)) {
|
||||
// Add Spring Milestone repository for generating diffs against previous milestones
|
||||
project.getRootProject()
|
||||
.getRepositories()
|
||||
.maven(mavenArtifactRepository -> mavenArtifactRepository.setUrl(SPRING_MILESTONE_REPOSITORY));
|
||||
JapicmpTask apiDiff = project.getTasks().create(TASK_NAME, JapicmpTask.class);
|
||||
apiDiff.setDescription("Generates an API diff report with japicmp");
|
||||
apiDiff.setGroup(JavaBasePlugin.DOCUMENTATION_GROUP);
|
||||
|
||||
apiDiff.setOldClasspath(createBaselineConfiguration(baselineVersion, project));
|
||||
TaskProvider<Jar> jar = project.getTasks().withType(Jar.class).named("jar");
|
||||
apiDiff.setNewArchives(project.getLayout().files(jar.get().getArchiveFile().get().getAsFile()));
|
||||
apiDiff.setNewClasspath(getRuntimeClassPath(project));
|
||||
apiDiff.setPackageIncludes(PACKAGE_INCLUDES);
|
||||
apiDiff.setOnlyModified(true);
|
||||
apiDiff.setIgnoreMissingClasses(true);
|
||||
// Ignore Kotlin metadata annotations since they contain
|
||||
// illegal HTML characters and fail the report generation
|
||||
apiDiff.setAnnotationExcludes(Collections.singletonList("@kotlin.Metadata"));
|
||||
|
||||
apiDiff.setHtmlOutputFile(getOutputFile(baselineVersion, project));
|
||||
|
||||
apiDiff.dependsOn(project.getTasks().getByName("jar"));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isProjectEligible(Project project) {
|
||||
return project.getPlugins().hasPlugin(JavaPlugin.class)
|
||||
&& project.getPlugins().hasPlugin(MavenPublishPlugin.class);
|
||||
}
|
||||
|
||||
private Configuration createBaselineConfiguration(String baselineVersion, Project project) {
|
||||
String baseline = String.join(":",
|
||||
project.getGroup().toString(), project.getName(), baselineVersion);
|
||||
Dependency baselineDependency = project.getDependencies().create(baseline + "@jar");
|
||||
Configuration baselineConfiguration = project.getRootProject().getConfigurations().detachedConfiguration(baselineDependency);
|
||||
try {
|
||||
// eagerly resolve the baseline configuration to check whether this is a new Spring module
|
||||
baselineConfiguration.resolve();
|
||||
return baselineConfiguration;
|
||||
}
|
||||
catch (GradleException exception) {
|
||||
logger.warn("Could not resolve {} - assuming this is a new Spring module.", baseline);
|
||||
}
|
||||
return project.getRootProject().getConfigurations().detachedConfiguration();
|
||||
}
|
||||
|
||||
private Configuration getRuntimeClassPath(Project project) {
|
||||
return project.getConfigurations().getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME);
|
||||
}
|
||||
|
||||
private File getOutputFile(String baseLineVersion, Project project) {
|
||||
Path outDir = Paths.get(project.getRootProject().getBuildDir().getAbsolutePath(),
|
||||
"reports", "api-diff",
|
||||
baseLineVersion + "_to_" + project.getRootProject().getVersion());
|
||||
return project.file(outDir.resolve(project.getName() + ".html").toString());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM ubuntu:jammy-20230816
|
||||
FROM ubuntu:jammy-20231004
|
||||
|
||||
ADD setup.sh /setup.sh
|
||||
ADD get-jdk-url.sh /get-jdk-url.sh
|
||||
|
||||
@@ -3,7 +3,7 @@ set -e
|
||||
|
||||
case "$1" in
|
||||
java17)
|
||||
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.7+7/bellsoft-jdk17.0.7+7-linux-amd64.tar.gz"
|
||||
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.9+11/bellsoft-jdk17.0.9+11-linux-amd64.tar.gz"
|
||||
;;
|
||||
java20)
|
||||
echo "https://github.com/bell-sw/Liberica/releases/download/20.0.1+10/bellsoft-jdk20.0.1+10-linux-amd64.tar.gz"
|
||||
|
||||
@@ -4,7 +4,7 @@ image_resource:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: springio/concourse-release-scripts
|
||||
tag: '0.3.4'
|
||||
tag: '0.4.0-SNAPSHOT'
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
inputs:
|
||||
|
||||
@@ -6,8 +6,8 @@ it until later.
|
||||
|
||||
By default, there is a single instance of each aspect within the application
|
||||
context. AspectJ calls this the singleton instantiation model. It is possible to define
|
||||
aspects with alternate lifecycles. Spring supports AspectJ's `perthis` and `pertarget`
|
||||
instantiation models; `percflow`, `percflowbelow`, and `pertypewithin` are not currently
|
||||
aspects with alternate lifecycles. Spring supports AspectJ's `perthis`, `pertarget`, and
|
||||
`pertypewithin` instantiation models; `percflow` and `percflowbelow` are not currently
|
||||
supported.
|
||||
|
||||
You can declare a `perthis` aspect by specifying a `perthis` clause in the `@Aspect`
|
||||
|
||||
@@ -565,6 +565,17 @@ is a convenience mechanism that sets up a xref:core/beans/factory-extension.adoc
|
||||
for you. If you need more control over the specific
|
||||
`PropertySourcesPlaceholderConfigurer` setup, you can explicitly define it as a bean yourself.
|
||||
|
||||
[WARNING]
|
||||
=====
|
||||
Only one such element should be defined for a given application with the properties
|
||||
that it needs. Several property placeholders can be configured as long as they have distinct
|
||||
placeholder syntax (`${...}`).
|
||||
|
||||
If you need to modularize the source of properties used for the replacement, you should
|
||||
not create multiple properties placeholders. Rather, each module should contribute a
|
||||
`PropertySource` to the `Environment`. Alternatively, you can create your own
|
||||
`PropertySourcesPlaceholderConfigurer` bean that gathers the properties to use.
|
||||
=====
|
||||
|
||||
[[xsd-schemas-context-ac]]
|
||||
=== Using `<annotation-config/>`
|
||||
|
||||
@@ -562,8 +562,9 @@ If no profile is active, the `dataSource` is created. You can see this
|
||||
as a way to provide a default definition for one or more beans. If any
|
||||
profile is enabled, the default profile does not apply.
|
||||
|
||||
You can change the name of the default profile by using `setDefaultProfiles()` on
|
||||
the `Environment` or, declaratively, by using the `spring.profiles.default` property.
|
||||
The name of the default profile is `default`. You can change the name of
|
||||
the default profile by using `setDefaultProfiles()` on the `Environment` or,
|
||||
declaratively, by using the `spring.profiles.default` property.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -372,6 +372,17 @@ The `PropertySourcesPlaceholderConfigurer` not only looks for properties in the
|
||||
file you specify. By default, if it cannot find a property in the specified properties files,
|
||||
it checks against Spring `Environment` properties and regular Java `System` properties.
|
||||
|
||||
[WARNING]
|
||||
=====
|
||||
Only one such element should be defined for a given application with the properties
|
||||
that it needs. Several property placeholders can be configured as long as they have distinct
|
||||
placeholder syntax (`${...}`).
|
||||
|
||||
If you need to modularize the source of properties used for the replacement, you should
|
||||
not create multiple properties placeholders. Rather, you should create your own
|
||||
`PropertySourcesPlaceholderConfigurer` bean that gathers the properties to use.
|
||||
=====
|
||||
|
||||
[TIP]
|
||||
=====
|
||||
You can use the `PropertySourcesPlaceholderConfigurer` to substitute class names, which
|
||||
|
||||
@@ -514,8 +514,8 @@ the `<aop:scoped-proxy/>` element, a CGLIB-based class proxy is created.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
CGLIB proxies intercept only public method calls! Do not call non-public methods
|
||||
on such a proxy. They are not delegated to the actual scoped target object.
|
||||
CGLIB proxies do not intercept private methods. Attempting to call a private method
|
||||
on such a proxy will not delegate to the actual scoped target object.
|
||||
====
|
||||
|
||||
Alternatively, you can configure the Spring container to create standard JDK
|
||||
|
||||
+7
-2
@@ -113,7 +113,8 @@ issue, because no compiler is involved, and you can declare
|
||||
When using `@Configuration` classes, the Java compiler places constraints on
|
||||
the configuration model, in that references to other beans must be valid Java syntax.
|
||||
|
||||
Fortunately, solving this problem is simple. As xref:core/beans/java/bean-annotation.adoc#beans-java-dependencies[we already discussed],
|
||||
Fortunately, solving this problem is simple. As
|
||||
xref:core/beans/java/bean-annotation.adoc#beans-java-dependencies[we already discussed],
|
||||
a `@Bean` method can have an arbitrary number of parameters that describe the bean
|
||||
dependencies. Consider the following more real-world scenario with several `@Configuration`
|
||||
classes, each depending on beans declared in the others:
|
||||
@@ -204,7 +205,6 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
There is another way to achieve the same result. Remember that `@Configuration` classes are
|
||||
ultimately only another bean in the container: This means that they can take advantage of
|
||||
`@Autowired` and `@Value` injection and other features the same as any other bean.
|
||||
@@ -216,6 +216,11 @@ classes are processed quite early during the initialization of the context, and
|
||||
to be injected this way may lead to unexpected early initialization. Whenever possible, resort to
|
||||
parameter-based injection, as in the preceding example.
|
||||
|
||||
Avoid access to locally defined beans within a `@PostConstruct` method on the same configuration
|
||||
class. This effectively leads to a circular reference since non-static `@Bean` methods semantically
|
||||
require a fully initialized configuration class instance to be called on. With circular references
|
||||
disallowed (e.g. in Spring Boot 2.6+), this may trigger a `BeanCurrentlyInCreationException`.
|
||||
|
||||
Also, be particularly careful with `BeanPostProcessor` and `BeanFactoryPostProcessor` definitions
|
||||
through `@Bean`. Those should usually be declared as `static @Bean` methods, not triggering the
|
||||
instantiation of their containing configuration class. Otherwise, `@Autowired` and `@Value` may not
|
||||
|
||||
@@ -268,6 +268,8 @@ Java::
|
||||
@Service
|
||||
public class MyService {
|
||||
|
||||
private final ConversionService conversionService;
|
||||
|
||||
public MyService(ConversionService conversionService) {
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
+24
-27
@@ -76,7 +76,7 @@ Kotlin::
|
||||
|
||||
Used at the class level as above, the annotation indicates a default for all methods of
|
||||
the declaring class (as well as its subclasses). Alternatively, each method can be
|
||||
annotated individually. See xref:data-access/transaction/declarative/annotations.adoc#transaction-declarative-annotations-method-visibility[null] for
|
||||
annotated individually. See xref:data-access/transaction/declarative/annotations.adoc#transaction-declarative-annotations-method-visibility[method visibility] for
|
||||
further details on which methods Spring considers transactional. Note that a class-level
|
||||
annotation does not apply to ancestor classes up the class hierarchy; in such a scenario,
|
||||
inherited methods need to be locally redeclared in order to participate in a
|
||||
@@ -229,30 +229,27 @@ in the testing chapter for examples.
|
||||
====
|
||||
|
||||
You can apply the `@Transactional` annotation to an interface definition, a method
|
||||
on an interface, a class definition, or a method on a class. However, the
|
||||
mere presence of the `@Transactional` annotation is not enough to activate the
|
||||
transactional behavior. The `@Transactional` annotation is merely metadata that can
|
||||
be consumed by some runtime infrastructure that is `@Transactional`-aware and that
|
||||
can use the metadata to configure the appropriate beans with transactional behavior.
|
||||
In the preceding example, the `<tx:annotation-driven/>` element switches on the
|
||||
transactional behavior.
|
||||
on an interface, a class definition, or a method on a class. However, the mere presence
|
||||
of the `@Transactional` annotation is not enough to activate the transactional behavior.
|
||||
The `@Transactional` annotation is merely metadata that can be consumed by corresponding
|
||||
runtime infrastructure which uses that metadata to configure the appropriate beans with
|
||||
transactional behavior. In the preceding example, the `<tx:annotation-driven/>` element
|
||||
switches on actual transaction management at runtime.
|
||||
|
||||
TIP: The Spring team recommends that you annotate only concrete classes (and methods of
|
||||
concrete classes) with the `@Transactional` annotation, as opposed to annotating interfaces.
|
||||
You certainly can place the `@Transactional` annotation on an interface (or an interface
|
||||
method), but this works only as you would expect it to if you use interface-based
|
||||
proxies. The fact that Java annotations are not inherited from interfaces means that,
|
||||
if you use class-based proxies (`proxy-target-class="true"`) or the weaving-based
|
||||
aspect (`mode="aspectj"`), the transaction settings are not recognized by the proxying
|
||||
and weaving infrastructure, and the object is not wrapped in a transactional proxy.
|
||||
TIP: The Spring team recommends that you annotate methods of concrete classes with the
|
||||
`@Transactional` annotation, rather than relying on annotated methods in interfaces,
|
||||
even if the latter does work for interface-based and target-class proxies as of 5.0.
|
||||
Since Java annotations are not inherited from interfaces, interface-declared annotations
|
||||
are still not recognized by the weaving infrastructure when using AspectJ mode, so the
|
||||
aspect does not get applied. As a consequence, your transaction annotations may be
|
||||
silently ignored: Your code might appear to "work" until you test a rollback scenario.
|
||||
|
||||
NOTE: In proxy mode (which is the default), only external method calls coming in through
|
||||
the proxy are intercepted. This means that self-invocation (in effect, a method within
|
||||
the target object calling another method of the target object) does not lead to an actual
|
||||
transaction at runtime even if the invoked method is marked with `@Transactional`. Also,
|
||||
the proxy must be fully initialized to provide the expected behavior, so you should not
|
||||
rely on this feature in your initialization code -- for example, in a `@PostConstruct`
|
||||
method.
|
||||
rely on this feature in your initialization code -- e.g. in a `@PostConstruct` method.
|
||||
|
||||
Consider using AspectJ mode (see the `mode` attribute in the following table) if you
|
||||
expect self-invocations to be wrapped with transactions as well. In this case, there is
|
||||
@@ -277,20 +274,20 @@ is modified) to support `@Transactional` runtime behavior on any kind of method.
|
||||
framework (following proxy semantics, as discussed earlier, applying to method calls
|
||||
coming in through the proxy only). The alternative mode (`aspectj`) instead weaves the
|
||||
affected classes with Spring's AspectJ transaction aspect, modifying the target class
|
||||
byte code to apply to any kind of method call. AspectJ weaving requires
|
||||
`spring-aspects.jar` in the classpath as well as having load-time weaving (or compile-time
|
||||
weaving) enabled. (See xref:core/aop/using-aspectj.adoc#aop-aj-ltw-spring[Spring configuration]
|
||||
for details on how to set up load-time weaving.)
|
||||
byte code to apply to any kind of method call. AspectJ weaving requires `spring-aspects.jar`
|
||||
in the classpath as well as having load-time weaving (or compile-time weaving) enabled.
|
||||
(See xref:core/aop/using-aspectj.adoc#aop-aj-ltw-spring[Spring configuration] for details
|
||||
on how to set up load-time weaving.)
|
||||
|
||||
| `proxy-target-class`
|
||||
| `proxyTargetClass`
|
||||
| `false`
|
||||
| Applies to `proxy` mode only. Controls what type of transactional proxies are created
|
||||
for classes annotated with the `@Transactional` annotation. If the
|
||||
`proxy-target-class` attribute is set to `true`, class-based proxies are created.
|
||||
If `proxy-target-class` is `false` or if the attribute is omitted, then standard JDK
|
||||
interface-based proxies are created. (See xref:core/aop/proxying.adoc[Proxying Mechanisms]
|
||||
for a detailed examination of the different proxy types.)
|
||||
for classes annotated with the `@Transactional` annotation. If the `proxy-target-class`
|
||||
attribute is set to `true`, class-based proxies are created. If `proxy-target-class` is
|
||||
`false` or if the attribute is omitted, then standard JDK interface-based proxies are
|
||||
created. (See xref:core/aop/proxying.adoc[Proxying Mechanisms] for a detailed examination
|
||||
of the different proxy types.)
|
||||
|
||||
| `order`
|
||||
| `order`
|
||||
|
||||
@@ -108,7 +108,7 @@ By default, the following `KeyValues` are created:
|
||||
|===
|
||||
|Name | Description
|
||||
|`exception` _(required)_|Name of the exception thrown during the exchange, or `KeyValue#NONE_VALUE`} if no exception happened.
|
||||
|`method` _(required)_|Name of HTTP request method or `"none"` if the request was not received properly.
|
||||
|`method` _(required)_|Name of HTTP request method or `"none"` if not a well-known method.
|
||||
|`outcome` _(required)_|Outcome of the HTTP server exchange.
|
||||
|`status` _(required)_|HTTP response raw status code, or `"UNKNOWN"` if no response was created.
|
||||
|`uri` _(required)_|URI pattern for the matching handler if available, falling back to `REDIRECTION` for 3xx responses, `NOT_FOUND` for 404 responses, `root` for requests with no path info, and `UNKNOWN` for all other requests.
|
||||
@@ -141,7 +141,7 @@ By default, the following `KeyValues` are created:
|
||||
|===
|
||||
|Name | Description
|
||||
|`exception` _(required)_|Name of the exception thrown during the exchange, or `"none"` if no exception happened.
|
||||
|`method` _(required)_|Name of HTTP request method or `"none"` if the request was not received properly.
|
||||
|`method` _(required)_|Name of HTTP request method or `"none"` if not a well-known method.
|
||||
|`outcome` _(required)_|Outcome of the HTTP server exchange.
|
||||
|`status` _(required)_|HTTP response raw status code, or `"UNKNOWN"` if no response was created.
|
||||
|`uri` _(required)_|URI pattern for the matching handler if available, falling back to `REDIRECTION` for 3xx responses, `NOT_FOUND` for 404 responses, `root` for requests with no path info, and `UNKNOWN` for all other requests.
|
||||
@@ -174,7 +174,7 @@ Instrumentation uses the `org.springframework.http.client.observation.ClientRequ
|
||||
[cols="a,a"]
|
||||
|===
|
||||
|Name | Description
|
||||
|`method` _(required)_|Name of HTTP request method or `"none"` if the request could not be created.
|
||||
|`method` _(required)_|Name of HTTP request method or `"none"` if not a well-known method.
|
||||
|`uri` _(required)_|URI template used for HTTP request, or `"none"` if none was provided. Only the path part of the URI is considered.
|
||||
|`client.name` _(required)_|Client name derived from the request URI host.
|
||||
|`status` _(required)_|HTTP response raw status code, or `"IO_ERROR"` in case of `IOException`, or `"CLIENT_ERROR"` if no response was received.
|
||||
@@ -203,7 +203,7 @@ Instrumentation uses the `org.springframework.web.reactive.function.client.Clien
|
||||
[cols="a,a"]
|
||||
|===
|
||||
|Name | Description
|
||||
|`method` _(required)_|Name of HTTP request method or `"none"` if the request could not be created.
|
||||
|`method` _(required)_|Name of HTTP request method or `"none"` if not a well-known method.
|
||||
|`uri` _(required)_|URI template used for HTTP request, or `"none"` if none was provided. Only the path part of the URI is considered.
|
||||
|`client.name` _(required)_|Client name derived from the request URI host.
|
||||
|`status` _(required)_|HTTP response raw status code, or `"IO_ERROR"` in case of `IOException`, or `"CLIENT_ERROR"` if no response was received.
|
||||
|
||||
@@ -97,11 +97,7 @@ preparation and response extraction through callback interfaces.
|
||||
|
||||
The default constructor uses `java.net.HttpURLConnection` to perform requests. You can
|
||||
switch to a different HTTP library with an implementation of `ClientHttpRequestFactory`.
|
||||
There is built-in support for the following:
|
||||
|
||||
* Apache HttpComponents
|
||||
* Netty
|
||||
* OkHttp
|
||||
Currently, there is also built-in support for Apache HttpComponents and OkHttp.
|
||||
|
||||
For example, to switch to Apache HttpComponents, you can use the following:
|
||||
|
||||
|
||||
@@ -236,6 +236,42 @@ be matched, not only the `GET` method.
|
||||
|
||||
|
||||
|
||||
[[declaration-site-variance]]
|
||||
== Declaration-site variance
|
||||
|
||||
Dealing with generic types in Spring applications written in Kotlin may require, for some use cases, to understand
|
||||
Kotlin https://kotlinlang.org/docs/generics.html#declaration-site-variance[declaration-site variance]
|
||||
which allows to define the variance when declaring a type, which is not possible in Java which supports only use-site
|
||||
variance.
|
||||
|
||||
For example, declaring `List<Foo>` in Kotlin is conceptually equivalent to `java.util.List<? extends Foo>` because
|
||||
`kotlin.collections.List` is declared as
|
||||
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/[`interface List<out E> : kotlin.collections.Collection<E>`].
|
||||
|
||||
This needs to be taken in account by using the `out` Kotlin keyword on generic types when using Java classes,
|
||||
for example when writing a `org.springframework.core.convert.converter.Converter` from a Kotlin type to a Java type.
|
||||
|
||||
[source,kotlin,indent=0]
|
||||
----
|
||||
class ListOfFooConverter : Converter<List<Foo>, CustomJavaList<out Foo>> {
|
||||
// ...
|
||||
}
|
||||
----
|
||||
|
||||
When converting any kind of objects, star projection with `*` can be used instead of `out Any`.
|
||||
[source,kotlin,indent=0]
|
||||
----
|
||||
class ListOfAnyConverter : Converter<List<*>, CustomJavaList<*>> {
|
||||
// ...
|
||||
}
|
||||
----
|
||||
|
||||
NOTE: Spring Framework does not leverage yet declaration-site variance type information for injecting beans,
|
||||
subscribe to https://github.com/spring-projects/spring-framework/issues/22313[spring-framework#22313] to track related
|
||||
progresses.
|
||||
|
||||
|
||||
|
||||
[[testing]]
|
||||
== Testing
|
||||
|
||||
|
||||
+4
-1
@@ -166,4 +166,7 @@ By default, any argument that is not a simple value type (as determined by
|
||||
and is not resolved by any other argument resolver is treated as if it were annotated
|
||||
with `@ModelAttribute`.
|
||||
|
||||
|
||||
WARNING: When compiling to a native image with GraalVM, the implicit `@ModelAttribute`
|
||||
support described above does not allow proper ahead-of-time inference of related data
|
||||
binding reflection hints. As a consequence, it is recommended to explicitly annotate
|
||||
method parameters with `@ModelAttribute` for use in a GraalVM native image.
|
||||
|
||||
+4
-1
@@ -216,4 +216,7 @@ By default, any argument that is not a simple value type (as determined by
|
||||
and is not resolved by any other argument resolver is treated as if it were annotated
|
||||
with `@ModelAttribute`.
|
||||
|
||||
|
||||
WARNING: When compiling to a native image with GraalVM, the implicit `@ModelAttribute`
|
||||
support described above does not allow proper ahead-of-time inference of related data
|
||||
binding reflection hints. As a consequence, it is recommended to explicitly annotate
|
||||
method parameters with `@ModelAttribute` for use in a GraalVM native image.
|
||||
|
||||
@@ -220,7 +220,7 @@ one of the following depending on whether use of parsed `PathPattern` is enabled
|
||||
* {api-spring-framework}/web/util/pattern/PathPattern.html#SPECIFICITY_COMPARATOR[`PathPattern.SPECIFICITY_COMPARATOR`]
|
||||
* {api-spring-framework}/util/AntPathMatcher.html#getPatternComparator-java.lang.String-[`AntPathMatcher.getPatternComparator(String path)`]
|
||||
|
||||
Both help to sort patterns with more specific ones on top. A pattern is less specific if
|
||||
Both help to sort patterns with more specific ones on top. A pattern is more specific if
|
||||
it has a lower count of URI variables (counted as 1), single wildcards (counted as 1),
|
||||
and double wildcards (counted as 2). Given an equal score, the longer pattern is chosen.
|
||||
Given the same score and length, the pattern with more URI variables than wildcards is
|
||||
|
||||
@@ -229,34 +229,27 @@ Java initialization API. The following example shows how to do so:
|
||||
|
||||
|
||||
[[websocket-server-runtime-configuration]]
|
||||
== Server Configuration
|
||||
== Configuring the Server
|
||||
[.small]#xref:web/webflux-websocket.adoc#webflux-websocket-server-config[See equivalent in the Reactive stack]#
|
||||
|
||||
Each underlying WebSocket engine exposes configuration properties that control
|
||||
runtime characteristics, such as the size of message buffer sizes, idle timeout,
|
||||
and others.
|
||||
You can configure of the underlying WebSocket server such as input message buffer size,
|
||||
idle timeout, and more.
|
||||
|
||||
For Tomcat, WildFly, and GlassFish, you can add a `ServletServerContainerFactoryBean` to your
|
||||
WebSocket Java config, as the following example shows:
|
||||
For Jakarta WebSocket servers, you can add a `ServletServerContainerFactoryBean` to your
|
||||
Java configuration. For example:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Configuration
|
||||
@EnableWebSocket
|
||||
public class WebSocketConfig implements WebSocketConfigurer {
|
||||
|
||||
@Bean
|
||||
public ServletServerContainerFactoryBean createWebSocketContainer() {
|
||||
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
|
||||
container.setMaxTextMessageBufferSize(8192);
|
||||
container.setMaxBinaryMessageBufferSize(8192);
|
||||
return container;
|
||||
}
|
||||
|
||||
}
|
||||
@Bean
|
||||
public ServletServerContainerFactoryBean createWebSocketContainer() {
|
||||
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
|
||||
container.setMaxTextMessageBufferSize(8192);
|
||||
container.setMaxBinaryMessageBufferSize(8192);
|
||||
return container;
|
||||
}
|
||||
----
|
||||
|
||||
The following example shows the XML configuration equivalent of the preceding example:
|
||||
Or to your XML configuration:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes,attributes"]
|
||||
----
|
||||
@@ -277,12 +270,11 @@ The following example shows the XML configuration equivalent of the preceding ex
|
||||
</beans>
|
||||
----
|
||||
|
||||
NOTE: For client-side WebSocket configuration, you should use `WebSocketContainerFactoryBean`
|
||||
(XML) or `ContainerProvider.getWebSocketContainer()` (Java configuration).
|
||||
NOTE: For client Jakarta WebSocket configuration, use
|
||||
ContainerProvider.getWebSocketContainer() in Java configuration, or
|
||||
`WebSocketContainerFactoryBean` in XML.
|
||||
|
||||
For Jetty, you need to supply a pre-configured Jetty `WebSocketServerFactory` and plug
|
||||
that into Spring's `DefaultHandshakeHandler` through your WebSocket Java config.
|
||||
The following example shows how to do so:
|
||||
For Jetty, you can supply a `Consumer` callback to configure the WebSocket server:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@@ -298,11 +290,9 @@ The following example shows how to do so:
|
||||
|
||||
@Bean
|
||||
public DefaultHandshakeHandler handshakeHandler() {
|
||||
|
||||
WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
|
||||
policy.setInputBufferSize(8192);
|
||||
policy.setIdleTimeout(600000);
|
||||
|
||||
return new DefaultHandshakeHandler(
|
||||
new JettyRequestUpgradeStrategy(new WebSocketServerFactory(policy)));
|
||||
}
|
||||
@@ -349,6 +339,10 @@ The following example shows the XML configuration equivalent of the preceding ex
|
||||
</beans>
|
||||
----
|
||||
|
||||
TIP: When using STOMP over WebSocket, you will also need to configure
|
||||
xref:web/websocket/stomp/server-config.adoc[STOMP WebSocket transport]
|
||||
properties.
|
||||
|
||||
|
||||
|
||||
[[websocket-server-allowed-origins]]
|
||||
|
||||
+3
-3
@@ -103,9 +103,9 @@ You can also use the WebSocket transport configuration shown earlier to configur
|
||||
maximum allowed size for incoming STOMP messages. In theory, a WebSocket
|
||||
message can be almost unlimited in size. In practice, WebSocket servers impose
|
||||
limits -- for example, 8K on Tomcat and 64K on Jetty. For this reason, STOMP clients
|
||||
(such as the JavaScript https://github.com/JSteunou/webstomp-client[webstomp-client]
|
||||
and others) split larger STOMP messages at 16K boundaries and send them as multiple
|
||||
WebSocket messages, which requires the server to buffer and re-assemble.
|
||||
such as https://github.com/stomp-js/stompjs[`stomp-js/stompjs`] and others split larger
|
||||
STOMP messages at 16K boundaries and send them as multiple WebSocket messages,
|
||||
which requires the server to buffer and re-assemble.
|
||||
|
||||
Spring's STOMP-over-WebSocket support does this ,so applications can configure the
|
||||
maximum size for STOMP messages irrespective of WebSocket server-specific message
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
[[websocket-stomp-server-config]]
|
||||
= WebSocket Server
|
||||
= WebSocket Transport
|
||||
|
||||
To configure the underlying WebSocket server, the information in
|
||||
xref:web/websocket/server.adoc#websocket-server-runtime-configuration[Server Configuration] applies. For Jetty, however you need to set
|
||||
the `HandshakeHandler` and `WebSocketPolicy` through the `StompEndpointRegistry`:
|
||||
This section explains how to configure the underlying WebSocket server transport.
|
||||
|
||||
For Jakarta WebSocket servers, add a `ServletServerContainerFactoryBean` to your
|
||||
configuration. For examples, see
|
||||
xref:web/websocket/server.adoc#websocket-server-runtime-configuration[Configuring the Server]
|
||||
under the WebSocket section.
|
||||
|
||||
For Jetty WebSocket servers, customize the `JettyRequestUpgradeStrategy` as follows:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@@ -29,5 +34,20 @@ the `HandshakeHandler` and `WebSocketPolicy` through the `StompEndpointRegistry`
|
||||
}
|
||||
----
|
||||
|
||||
In addition to WebSocket server properties, there are also STOMP WebSocket transport properties
|
||||
to customize as follows:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Configuration
|
||||
@EnableWebSocketMessageBroker
|
||||
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
|
||||
|
||||
@Override
|
||||
public void configureWebSocketTransport(WebSocketTransportRegistration registry) {
|
||||
registry.setMessageSizeLimit(4 * 8192);
|
||||
registry.setTimeToFirstMessage(30000);
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
@@ -8,30 +8,30 @@ javaPlatform {
|
||||
|
||||
dependencies {
|
||||
api(platform("com.fasterxml.jackson:jackson-bom:2.14.3"))
|
||||
api(platform("io.micrometer:micrometer-bom:1.10.11"))
|
||||
api(platform("io.netty:netty-bom:4.1.97.Final"))
|
||||
api(platform("io.micrometer:micrometer-bom:1.10.13"))
|
||||
api(platform("io.netty:netty-bom:4.1.101.Final"))
|
||||
api(platform("io.netty:netty5-bom:5.0.0.Alpha5"))
|
||||
api(platform("io.projectreactor:reactor-bom:2022.0.11"))
|
||||
api(platform("io.projectreactor:reactor-bom:2022.0.14"))
|
||||
api(platform("io.rsocket:rsocket-bom:1.1.3"))
|
||||
api(platform("org.apache.groovy:groovy-bom:4.0.14"))
|
||||
api(platform("org.apache.logging.log4j:log4j-bom:2.20.0"))
|
||||
api(platform("org.eclipse.jetty:jetty-bom:11.0.16"))
|
||||
api(platform("org.apache.groovy:groovy-bom:4.0.16"))
|
||||
api(platform("org.apache.logging.log4j:log4j-bom:2.21.1"))
|
||||
api(platform("org.eclipse.jetty:jetty-bom:11.0.18"))
|
||||
api(platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.6.4"))
|
||||
api(platform("org.jetbrains.kotlinx:kotlinx-serialization-bom:1.4.0"))
|
||||
api(platform("org.junit:junit-bom:5.9.3"))
|
||||
api(platform("org.mockito:mockito-bom:5.5.0"))
|
||||
api(platform("org.mockito:mockito-bom:5.8.0"))
|
||||
|
||||
constraints {
|
||||
api("com.fasterxml:aalto-xml:1.3.2")
|
||||
api("com.fasterxml.woodstox:woodstox-core:6.5.1")
|
||||
api("com.github.ben-manes.caffeine:caffeine:3.1.8")
|
||||
api("com.github.librepdf:openpdf:1.3.30")
|
||||
api("com.github.librepdf:openpdf:1.3.33")
|
||||
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.10.1")
|
||||
api("com.google.protobuf:protobuf-java-util:3.23.2")
|
||||
api("com.googlecode.protobuf-java-format:protobuf-java-format:1.4")
|
||||
api("com.h2database:h2:2.2.220")
|
||||
api("com.h2database:h2:2.2.224")
|
||||
api("com.jayway.jsonpath:json-path:2.8.0")
|
||||
api("com.rometools:rome:1.19.0")
|
||||
api("com.squareup.okhttp3:mockwebserver:3.14.9")
|
||||
@@ -43,20 +43,20 @@ dependencies {
|
||||
api("com.sun.xml.bind:jaxb-xjc:3.0.2")
|
||||
api("com.thoughtworks.qdox:qdox:2.0.3")
|
||||
api("com.thoughtworks.xstream:xstream:1.4.20")
|
||||
api("commons-io:commons-io:2.11.0")
|
||||
api("commons-io:commons-io:2.15.0")
|
||||
api("de.bechte.junit:junit-hierarchicalcontextrunner:4.12.2")
|
||||
api("io.micrometer:context-propagation:1.0.5")
|
||||
api("io.micrometer:context-propagation:1.0.6")
|
||||
api("io.mockk:mockk:1.13.4")
|
||||
api("io.projectreactor.netty:reactor-netty5-http:2.0.0-M3")
|
||||
api("io.projectreactor.tools:blockhound:1.0.8.RELEASE")
|
||||
api("io.r2dbc:r2dbc-h2:1.0.0.RELEASE")
|
||||
api("io.r2dbc:r2dbc-spi-test:1.0.0.RELEASE")
|
||||
api("io.r2dbc:r2dbc-spi:1.0.0.RELEASE")
|
||||
api("io.reactivex.rxjava3:rxjava:3.1.6")
|
||||
api("io.reactivex.rxjava3:rxjava:3.1.8")
|
||||
api("io.smallrye.reactive:mutiny:1.9.0")
|
||||
api("io.undertow:undertow-core:2.3.8.Final")
|
||||
api("io.undertow:undertow-servlet:2.3.8.Final")
|
||||
api("io.undertow:undertow-websockets-jsr:2.3.8.Final")
|
||||
api("io.undertow:undertow-core:2.3.10.Final")
|
||||
api("io.undertow:undertow-servlet:2.3.10.Final")
|
||||
api("io.undertow:undertow-websockets-jsr:2.3.10.Final")
|
||||
api("io.vavr:vavr:0.10.4")
|
||||
api("jakarta.activation:jakarta.activation-api:2.0.1")
|
||||
api("jakarta.annotation:jakarta.annotation-api:2.0.0")
|
||||
@@ -89,46 +89,44 @@ dependencies {
|
||||
api("net.sf.jopt-simple:jopt-simple:5.0.4")
|
||||
api("net.sourceforge.htmlunit:htmlunit:2.70.0")
|
||||
api("org.apache-extras.beanshell:bsh:2.0b6")
|
||||
api("org.apache.activemq:activemq-broker:5.17.4")
|
||||
api("org.apache.activemq:activemq-kahadb-store:5.17.4")
|
||||
api("org.apache.activemq:activemq-stomp:5.17.4")
|
||||
api("org.apache.activemq:activemq-broker:5.17.6")
|
||||
api("org.apache.activemq:activemq-kahadb-store:5.17.6")
|
||||
api("org.apache.activemq:activemq-stomp:5.17.6")
|
||||
api("org.apache.commons:commons-pool2:2.9.0")
|
||||
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.2.1")
|
||||
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.2.2")
|
||||
api("org.apache.poi:poi-ooxml:5.2.3")
|
||||
api("org.apache.tomcat.embed:tomcat-embed-core:10.1.13")
|
||||
api("org.apache.tomcat.embed:tomcat-embed-websocket:10.1.13")
|
||||
api("org.apache.tomcat:tomcat-util:10.1.13")
|
||||
api("org.apache.tomcat:tomcat-websocket:10.1.13")
|
||||
api("org.aspectj:aspectjrt:1.9.20")
|
||||
api("org.aspectj:aspectjtools:1.9.20")
|
||||
api("org.aspectj:aspectjweaver:1.9.20")
|
||||
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.2.3")
|
||||
api("org.apache.poi:poi-ooxml:5.2.4")
|
||||
api("org.apache.tomcat.embed:tomcat-embed-core:10.1.15")
|
||||
api("org.apache.tomcat.embed:tomcat-embed-websocket:10.1.15")
|
||||
api("org.apache.tomcat:tomcat-util:10.1.15")
|
||||
api("org.apache.tomcat:tomcat-websocket:10.1.15")
|
||||
api("org.aspectj:aspectjrt:1.9.20.1")
|
||||
api("org.aspectj:aspectjtools:1.9.20.1")
|
||||
api("org.aspectj:aspectjweaver:1.9.20.1")
|
||||
api("org.assertj:assertj-core:3.24.2")
|
||||
api("org.awaitility:awaitility:4.2.0")
|
||||
api("org.bouncycastle:bcpkix-jdk18on:1.72")
|
||||
api("org.codehaus.jettison:jettison:1.5.4")
|
||||
api("org.dom4j:dom4j:2.1.4")
|
||||
api("org.eclipse.jetty:jetty-reactive-httpclient:3.0.8")
|
||||
api("org.eclipse.persistence:org.eclipse.persistence.jpa:3.0.3")
|
||||
api("org.eclipse.jetty:jetty-reactive-httpclient:3.0.10")
|
||||
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.32")
|
||||
// Substitute for "javax.management:jmxremote_optional:1.0.1_04" which
|
||||
// is not available on Maven Central
|
||||
api("org.glassfish.external:opendmk_jmxremote_optional_jar:1.0-b01-ea")
|
||||
api("org.glassfish.tyrus:tyrus-container-servlet:2.1.3")
|
||||
api("org.glassfish:jakarta.el:4.0.2")
|
||||
api("org.glassfish.tyrus:tyrus-container-servlet:2.1.3")
|
||||
api("org.graalvm.sdk:graal-sdk:22.3.1")
|
||||
api("org.hamcrest:hamcrest:2.2")
|
||||
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.2")
|
||||
api("org.javamoney:moneta:1.4.2")
|
||||
api("org.jruby:jruby:9.4.3.0")
|
||||
api("org.jruby:jruby:9.4.5.0")
|
||||
api("org.junit.support:testng-engine:1.0.4")
|
||||
api("org.mozilla:rhino:1.7.14")
|
||||
api("org.ogce:xpp3:1.1.6")
|
||||
@@ -137,10 +135,10 @@ dependencies {
|
||||
api("org.seleniumhq.selenium:htmlunit-driver:2.70.0")
|
||||
api("org.seleniumhq.selenium:selenium-java:3.141.59")
|
||||
api("org.skyscreamer:jsonassert:1.5.1")
|
||||
api("org.slf4j:slf4j-api:2.0.7")
|
||||
api("org.slf4j:slf4j-api:2.0.9")
|
||||
api("org.testng:testng:7.8.0")
|
||||
api("org.webjars:underscorejs:1.8.3")
|
||||
api("org.webjars:webjars-locator-core:0.53")
|
||||
api("org.webjars:webjars-locator-core:0.55")
|
||||
api("org.xmlunit:xmlunit-assertj:2.9.1")
|
||||
api("org.xmlunit:xmlunit-matchers:2.9.1")
|
||||
api("org.yaml:snakeyaml:1.33")
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
version=6.0.12
|
||||
version=6.0.15
|
||||
|
||||
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.3-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -145,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
@@ -153,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
@@ -202,11 +202,11 @@ fi
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
|
||||
+2
-1
@@ -47,7 +47,8 @@ rootProject.children.each {project ->
|
||||
settings.gradle.projectsLoaded {
|
||||
gradleEnterprise {
|
||||
buildScan {
|
||||
File buildDir = settings.gradle.rootProject.getBuildDir()
|
||||
File buildDir = settings.gradle.rootProject
|
||||
.getLayout().getBuildDirectory().getAsFile().get()
|
||||
buildDir.mkdirs()
|
||||
new File(buildDir, "build-scan-uri.txt").text = "(build scan not generated)"
|
||||
buildScanPublished { scan ->
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,18 +17,23 @@
|
||||
package org.springframework.aop;
|
||||
|
||||
/**
|
||||
* Filter that restricts matching of a pointcut or introduction to
|
||||
* a given set of target classes.
|
||||
* Filter that restricts matching of a pointcut or introduction to a given set
|
||||
* of target classes.
|
||||
*
|
||||
* <p>Can be used as part of a {@link Pointcut} or for the entire
|
||||
* targeting of an {@link IntroductionAdvisor}.
|
||||
* <p>Can be used as part of a {@link Pointcut} or for the entire targeting of
|
||||
* an {@link IntroductionAdvisor}.
|
||||
*
|
||||
* <p>Concrete implementations of this interface typically should provide proper
|
||||
* implementations of {@link Object#equals(Object)} and {@link Object#hashCode()}
|
||||
* in order to allow the filter to be used in caching scenarios — for
|
||||
* example, in proxies generated by CGLIB.
|
||||
* <p><strong>WARNING</strong>: Concrete implementations of this interface must
|
||||
* provide proper implementations of {@link Object#equals(Object)},
|
||||
* {@link Object#hashCode()}, and {@link Object#toString()} in order to allow the
|
||||
* filter to be used in caching scenarios — for example, in proxies generated
|
||||
* by CGLIB. As of Spring Framework 6.0.13, the {@code toString()} implementation
|
||||
* must generate a unique string representation that aligns with the logic used
|
||||
* to implement {@code equals()}. See concrete implementations of this interface
|
||||
* within the framework for examples.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Sam Brannen
|
||||
* @see Pointcut
|
||||
* @see MethodMatcher
|
||||
*/
|
||||
@@ -44,7 +49,7 @@ public interface ClassFilter {
|
||||
|
||||
|
||||
/**
|
||||
* Canonical instance of a ClassFilter that matches all classes.
|
||||
* Canonical instance of a {@code ClassFilter} that matches all classes.
|
||||
*/
|
||||
ClassFilter TRUE = TrueClassFilter.INSTANCE;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,31 +21,37 @@ import java.lang.reflect.Method;
|
||||
/**
|
||||
* Part of a {@link Pointcut}: Checks whether the target method is eligible for advice.
|
||||
*
|
||||
* <p>A MethodMatcher may be evaluated <b>statically</b> or at <b>runtime</b> (dynamically).
|
||||
* Static matching involves method and (possibly) method attributes. Dynamic matching
|
||||
* also makes arguments for a particular call available, and any effects of running
|
||||
* previous advice applying to the joinpoint.
|
||||
* <p>A {@code MethodMatcher} may be evaluated <b>statically</b> or at <b>runtime</b>
|
||||
* (dynamically). Static matching involves a method and (possibly) method attributes.
|
||||
* Dynamic matching also makes arguments for a particular call available, and any
|
||||
* effects of running previous advice applying to the joinpoint.
|
||||
*
|
||||
* <p>If an implementation returns {@code false} from its {@link #isRuntime()}
|
||||
* method, evaluation can be performed statically, and the result will be the same
|
||||
* for all invocations of this method, whatever their arguments. This means that
|
||||
* if the {@link #isRuntime()} method returns {@code false}, the 3-arg
|
||||
* {@link #matches(java.lang.reflect.Method, Class, Object[])} method will never be invoked.
|
||||
* {@link #matches(Method, Class, Object[])} method will never be invoked.
|
||||
*
|
||||
* <p>If an implementation returns {@code true} from its 2-arg
|
||||
* {@link #matches(java.lang.reflect.Method, Class)} method and its {@link #isRuntime()} method
|
||||
* returns {@code true}, the 3-arg {@link #matches(java.lang.reflect.Method, Class, Object[])}
|
||||
* method will be invoked <i>immediately before each potential execution of the related advice</i>,
|
||||
* to decide whether the advice should run. All previous advice, such as earlier interceptors
|
||||
* in an interceptor chain, will have run, so any state changes they have produced in
|
||||
* parameters or ThreadLocal state will be available at the time of evaluation.
|
||||
* {@link #matches(Method, Class)} method and its {@link #isRuntime()} method
|
||||
* returns {@code true}, the 3-arg {@link #matches(Method, Class, Object[])}
|
||||
* method will be invoked <i>immediately before each potential execution of the
|
||||
* related advice</i> to decide whether the advice should run. All previous advice,
|
||||
* such as earlier interceptors in an interceptor chain, will have run, so any
|
||||
* state changes they have produced in parameters or {@code ThreadLocal} state will
|
||||
* be available at the time of evaluation.
|
||||
*
|
||||
* <p>Concrete implementations of this interface typically should provide proper
|
||||
* implementations of {@link Object#equals(Object)} and {@link Object#hashCode()}
|
||||
* in order to allow the matcher to be used in caching scenarios — for
|
||||
* example, in proxies generated by CGLIB.
|
||||
* <p><strong>WARNING</strong>: Concrete implementations of this interface must
|
||||
* provide proper implementations of {@link Object#equals(Object)},
|
||||
* {@link Object#hashCode()}, and {@link Object#toString()} in order to allow the
|
||||
* matcher to be used in caching scenarios — for example, in proxies generated
|
||||
* by CGLIB. As of Spring Framework 6.0.13, the {@code toString()} implementation
|
||||
* must generate a unique string representation that aligns with the logic used
|
||||
* to implement {@code equals()}. See concrete implementations of this interface
|
||||
* within the framework for examples.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Sam Brannen
|
||||
* @since 11.11.2003
|
||||
* @see Pointcut
|
||||
* @see ClassFilter
|
||||
@@ -53,11 +59,10 @@ import java.lang.reflect.Method;
|
||||
public interface MethodMatcher {
|
||||
|
||||
/**
|
||||
* Perform static checking whether the given method matches.
|
||||
* <p>If this returns {@code false} or if the {@link #isRuntime()}
|
||||
* method returns {@code false}, no runtime check (i.e. no
|
||||
* {@link #matches(java.lang.reflect.Method, Class, Object[])} call)
|
||||
* will be made.
|
||||
* Perform static checking to determine whether the given method matches.
|
||||
* <p>If this method returns {@code false} or if {@link #isRuntime()}
|
||||
* returns {@code false}, no runtime check (i.e. no
|
||||
* {@link #matches(Method, Class, Object[])} call) will be made.
|
||||
* @param method the candidate method
|
||||
* @param targetClass the target class
|
||||
* @return whether this method matches statically
|
||||
@@ -65,36 +70,35 @@ public interface MethodMatcher {
|
||||
boolean matches(Method method, Class<?> targetClass);
|
||||
|
||||
/**
|
||||
* Is this MethodMatcher dynamic, that is, must a final call be made on the
|
||||
* {@link #matches(java.lang.reflect.Method, Class, Object[])} method at
|
||||
* runtime even if the 2-arg matches method returns {@code true}?
|
||||
* Is this {@code MethodMatcher} dynamic, that is, must a final check be made
|
||||
* via the {@link #matches(Method, Class, Object[])} method at runtime even
|
||||
* if {@link #matches(Method, Class)} returns {@code true}?
|
||||
* <p>Can be invoked when an AOP proxy is created, and need not be invoked
|
||||
* again before each method invocation,
|
||||
* @return whether a runtime match via the 3-arg
|
||||
* {@link #matches(java.lang.reflect.Method, Class, Object[])} method
|
||||
* again before each method invocation.
|
||||
* @return whether a runtime match via {@link #matches(Method, Class, Object[])}
|
||||
* is required if static matching passed
|
||||
*/
|
||||
boolean isRuntime();
|
||||
|
||||
/**
|
||||
* Check whether there a runtime (dynamic) match for this method,
|
||||
* which must have matched statically.
|
||||
* <p>This method is invoked only if the 2-arg matches method returns
|
||||
* {@code true} for the given method and target class, and if the
|
||||
* {@link #isRuntime()} method returns {@code true}. Invoked
|
||||
* immediately before potential running of the advice, after any
|
||||
* Check whether there is a runtime (dynamic) match for this method, which
|
||||
* must have matched statically.
|
||||
* <p>This method is invoked only if {@link #matches(Method, Class)} returns
|
||||
* {@code true} for the given method and target class, and if
|
||||
* {@link #isRuntime()} returns {@code true}.
|
||||
* <p>Invoked immediately before potential running of the advice, after any
|
||||
* advice earlier in the advice chain has run.
|
||||
* @param method the candidate method
|
||||
* @param targetClass the target class
|
||||
* @param args arguments to the method
|
||||
* @return whether there's a runtime match
|
||||
* @see MethodMatcher#matches(Method, Class)
|
||||
* @see #matches(Method, Class)
|
||||
*/
|
||||
boolean matches(Method method, Class<?> targetClass, Object... args);
|
||||
|
||||
|
||||
/**
|
||||
* Canonical instance that matches all methods.
|
||||
* Canonical instance of a {@code MethodMatcher} that matches all methods.
|
||||
*/
|
||||
MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
|
||||
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* AspectJPointcutAdvisor that adapts an {@link AbstractAspectJAdvice}
|
||||
* AspectJ {@link PointcutAdvisor} that adapts an {@link AbstractAspectJAdvice}
|
||||
* to the {@link org.springframework.aop.PointcutAdvisor} interface.
|
||||
*
|
||||
* @author Adrian Colyer
|
||||
|
||||
+6
-6
@@ -40,11 +40,12 @@ import org.springframework.util.ClassUtils;
|
||||
*/
|
||||
class AspectJBeanFactoryInitializationAotProcessor implements BeanFactoryInitializationAotProcessor {
|
||||
|
||||
private static final boolean aspectJPresent = ClassUtils.isPresent(
|
||||
"org.aspectj.lang.annotation.Pointcut", AspectJBeanFactoryInitializationAotProcessor.class.getClassLoader());
|
||||
private static final boolean aspectJPresent = ClassUtils.isPresent("org.aspectj.lang.annotation.Pointcut",
|
||||
AspectJBeanFactoryInitializationAotProcessor.class.getClassLoader());
|
||||
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
@Nullable
|
||||
public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) {
|
||||
if (aspectJPresent) {
|
||||
return AspectDelegate.processAheadOfTime(beanFactory);
|
||||
@@ -52,6 +53,7 @@ class AspectJBeanFactoryInitializationAotProcessor implements BeanFactoryInitial
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inner class to avoid a hard dependency on AspectJ at runtime.
|
||||
*/
|
||||
@@ -61,9 +63,8 @@ class AspectJBeanFactoryInitializationAotProcessor implements BeanFactoryInitial
|
||||
private static AspectContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) {
|
||||
BeanFactoryAspectJAdvisorsBuilder builder = new BeanFactoryAspectJAdvisorsBuilder(beanFactory);
|
||||
List<Advisor> advisors = builder.buildAspectJAdvisors();
|
||||
return advisors.isEmpty() ? null : new AspectContribution(advisors);
|
||||
return (advisors.isEmpty() ? null : new AspectContribution(advisors));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -84,7 +85,6 @@ class AspectJBeanFactoryInitializationAotProcessor implements BeanFactoryInitial
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+23
@@ -32,6 +32,7 @@ import org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactory.
|
||||
import org.springframework.aop.support.DynamicMethodMatcherPointcut;
|
||||
import org.springframework.aop.support.Pointcuts;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Internal implementation of AspectJPointcutAdvisor.
|
||||
@@ -40,6 +41,7 @@ import org.springframework.lang.Nullable;
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @since 2.0
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@@ -297,6 +299,27 @@ final class InstantiationModelAwarePointcutAdvisorImpl
|
||||
private boolean isAspectMaterialized() {
|
||||
return (this.aspectInstanceFactory == null || this.aspectInstanceFactory.isMaterialized());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object other) {
|
||||
// For equivalence, we only need to compare the preInstantiationPointcut fields since
|
||||
// they include the declaredPointcut fields. In addition, we should not compare the
|
||||
// aspectInstanceFactory fields since LazySingletonAspectInstanceFactoryDecorator does
|
||||
// not implement equals().
|
||||
return (this == other || (other instanceof PerTargetInstantiationModelPointcut that &&
|
||||
ObjectUtils.nullSafeEquals(this.preInstantiationPointcut, that.preInstantiationPointcut)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return ObjectUtils.nullSafeHashCode(this.declaredPointcut.getExpression());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return PerTargetInstantiationModelPointcut.class.getName() + ": " + this.declaredPointcut.getExpression();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -48,7 +48,8 @@ import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Base class for AOP proxy configuration managers.
|
||||
* These are not themselves AOP proxies, but subclasses of this class are
|
||||
*
|
||||
* <p>These are not themselves AOP proxies, but subclasses of this class are
|
||||
* normally factories from which AOP proxy instances are obtained directly.
|
||||
*
|
||||
* <p>This class frees subclasses of the housekeeping of Advices
|
||||
@@ -56,10 +57,12 @@ import org.springframework.util.ObjectUtils;
|
||||
* methods, which are provided by subclasses.
|
||||
*
|
||||
* <p>This class is serializable; subclasses need not be.
|
||||
* This class is used to hold snapshots of proxies.
|
||||
*
|
||||
* <p>This class is used to hold snapshots of proxies.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @see org.springframework.aop.framework.AopProxy
|
||||
*/
|
||||
public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
@@ -111,7 +114,7 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a AdvisedSupport instance with the given parameters.
|
||||
* Create an {@code AdvisedSupport} instance with the given parameters.
|
||||
* @param interfaces the proxied interfaces
|
||||
*/
|
||||
public AdvisedSupport(Class<?>... interfaces) {
|
||||
@@ -131,7 +134,7 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
|
||||
/**
|
||||
* Set the given object as target.
|
||||
* Will create a SingletonTargetSource for the object.
|
||||
* <p>Will create a SingletonTargetSource for the object.
|
||||
* @see #setTargetSource
|
||||
* @see org.springframework.aop.target.SingletonTargetSource
|
||||
*/
|
||||
@@ -506,9 +509,9 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the AOP configuration from the given AdvisedSupport object,
|
||||
* but allow substitution of a fresh TargetSource and a given interceptor chain.
|
||||
* @param other the AdvisedSupport object to take proxy configuration from
|
||||
* Copy the AOP configuration from the given {@link AdvisedSupport} object,
|
||||
* but allow substitution of a fresh {@link TargetSource} and a given interceptor chain.
|
||||
* @param other the {@code AdvisedSupport} object to take proxy configuration from
|
||||
* @param targetSource the new TargetSource
|
||||
* @param advisors the Advisors for the chain
|
||||
*/
|
||||
@@ -528,8 +531,8 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a configuration-only copy of this AdvisedSupport,
|
||||
* replacing the TargetSource.
|
||||
* Build a configuration-only copy of this {@link AdvisedSupport},
|
||||
* replacing the {@link TargetSource}.
|
||||
*/
|
||||
AdvisedSupport getConfigurationOnlyCopy() {
|
||||
AdvisedSupport copy = new AdvisedSupport(this.advisorChainFactory, this.methodCache);
|
||||
@@ -604,8 +607,7 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object other) {
|
||||
return (this == other || (other instanceof MethodCacheKey methodCacheKey &&
|
||||
this.method == methodCacheKey.method));
|
||||
return (this == other || (other instanceof MethodCacheKey that && this.method == that.method));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -630,7 +632,7 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
|
||||
|
||||
/**
|
||||
* Stub for an Advisor instance that is just needed for key purposes,
|
||||
* Stub for an {@link Advisor} instance that is just needed for key purposes,
|
||||
* allowing for efficient equals and hashCode comparisons against the
|
||||
* advice class and the pointcut.
|
||||
* @since 6.0.10
|
||||
@@ -642,17 +644,22 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
private final Class<?> adviceType;
|
||||
|
||||
@Nullable
|
||||
private String classFilterKey;
|
||||
private final String classFilterKey;
|
||||
|
||||
@Nullable
|
||||
private String methodMatcherKey;
|
||||
private final String methodMatcherKey;
|
||||
|
||||
|
||||
public AdvisorKeyEntry(Advisor advisor) {
|
||||
this.adviceType = advisor.getAdvice().getClass();
|
||||
if (advisor instanceof PointcutAdvisor pointcutAdvisor) {
|
||||
Pointcut pointcut = pointcutAdvisor.getPointcut();
|
||||
this.classFilterKey = ObjectUtils.identityToString(pointcut.getClassFilter());
|
||||
this.methodMatcherKey = ObjectUtils.identityToString(pointcut.getMethodMatcher());
|
||||
this.classFilterKey = pointcut.getClassFilter().toString();
|
||||
this.methodMatcherKey = pointcut.getMethodMatcher().toString();
|
||||
}
|
||||
else {
|
||||
this.classFilterKey = null;
|
||||
this.methodMatcherKey = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -663,10 +670,10 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return (this == other || (other instanceof AdvisorKeyEntry otherEntry &&
|
||||
this.adviceType == otherEntry.adviceType &&
|
||||
ObjectUtils.nullSafeEquals(this.classFilterKey, otherEntry.classFilterKey) &&
|
||||
ObjectUtils.nullSafeEquals(this.methodMatcherKey, otherEntry.methodMatcherKey)));
|
||||
return (this == other || (other instanceof AdvisorKeyEntry that &&
|
||||
this.adviceType == that.adviceType &&
|
||||
ObjectUtils.nullSafeEquals(this.classFilterKey, that.classFilterKey) &&
|
||||
ObjectUtils.nullSafeEquals(this.methodMatcherKey, that.methodMatcherKey)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -265,18 +265,32 @@ public class ProxyFactoryBean extends ProxyCreatorSupport
|
||||
* Return the type of the proxy. Will check the singleton instance if
|
||||
* already created, else fall back to the proxy interface (in case of just
|
||||
* a single one), the target bean type, or the TargetSource's target class.
|
||||
* @see org.springframework.aop.TargetSource#getTargetClass
|
||||
* @see org.springframework.aop.framework.AopProxy#getProxyClass
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public Class<?> getObjectType() {
|
||||
synchronized (this) {
|
||||
if (this.singletonInstance != null) {
|
||||
return this.singletonInstance.getClass();
|
||||
}
|
||||
}
|
||||
// This might be incomplete since it potentially misses introduced interfaces
|
||||
// from Advisors that will be lazily retrieved via setInterceptorNames.
|
||||
return createAopProxy().getProxyClass(this.proxyClassLoader);
|
||||
try {
|
||||
// This might be incomplete since it potentially misses introduced interfaces
|
||||
// from Advisors that will be lazily retrieved via setInterceptorNames.
|
||||
return createAopProxy().getProxyClass(this.proxyClassLoader);
|
||||
}
|
||||
catch (AopConfigException ex) {
|
||||
if (getTargetClass() == null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Failed to determine early proxy class: " + ex.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -123,7 +123,6 @@ public abstract class ClassFilters {
|
||||
public String toString() {
|
||||
return getClass().getName() + ": " + Arrays.toString(this.filters);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -164,7 +163,6 @@ public abstract class ClassFilters {
|
||||
public String toString() {
|
||||
return getClass().getName() + ": " + Arrays.toString(this.filters);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
-6
@@ -40,10 +40,6 @@ import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import test.aop.DefaultLockable;
|
||||
import test.aop.Lockable;
|
||||
import test.aop.PerTargetAspect;
|
||||
import test.aop.TwoAdviceAspect;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.aop.framework.Advised;
|
||||
@@ -51,6 +47,10 @@ import org.springframework.aop.framework.AopConfigException;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.aop.testfixture.aspectj.PerTargetAspect;
|
||||
import org.springframework.aop.testfixture.aspectj.TwoAdviceAspect;
|
||||
import org.springframework.aop.testfixture.mixin.DefaultLockable;
|
||||
import org.springframework.aop.testfixture.mixin.Lockable;
|
||||
import org.springframework.beans.testfixture.beans.ITestBean;
|
||||
import org.springframework.beans.testfixture.beans.TestBean;
|
||||
import org.springframework.core.OrderComparator;
|
||||
@@ -459,10 +459,10 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
assertThat(advisors).as("Two advice methods found").hasSize(2);
|
||||
ITestBean itb = createProxy(target, ITestBean.class, advisors);
|
||||
itb.setName("");
|
||||
assertThat(itb.getAge()).isEqualTo(0);
|
||||
assertThat(itb.age()).isEqualTo(0);
|
||||
int newAge = 32;
|
||||
itb.setAge(newAge);
|
||||
assertThat(itb.getAge()).isEqualTo(1);
|
||||
assertThat(itb.age()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,13 +17,13 @@
|
||||
package org.springframework.aop.aspectj.annotation;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import test.aop.PerTargetAspect;
|
||||
|
||||
import org.springframework.aop.Pointcut;
|
||||
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
|
||||
import org.springframework.aop.aspectj.AspectJExpressionPointcutTests;
|
||||
import org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactoryTests.ExceptionThrowingAspect;
|
||||
import org.springframework.aop.framework.AopConfigException;
|
||||
import org.springframework.aop.testfixture.aspectj.PerTargetAspect;
|
||||
import org.springframework.beans.testfixture.beans.TestBean;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,11 +18,11 @@ package org.springframework.aop.aspectj.annotation;
|
||||
|
||||
import org.aspectj.lang.reflect.PerClauseKind;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import test.aop.PerTargetAspect;
|
||||
|
||||
import org.springframework.aop.Pointcut;
|
||||
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
|
||||
import org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactoryTests.ExceptionThrowingAspect;
|
||||
import org.springframework.aop.testfixture.aspectj.PerTargetAspect;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -24,8 +24,8 @@ import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import test.aop.PerThisAspect;
|
||||
|
||||
import org.springframework.aop.testfixture.aspectj.PerThisAspect;
|
||||
import org.springframework.core.testfixture.io.SerializationTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2005 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package test.aop;
|
||||
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
|
||||
@Aspect("perthis(execution(* getAge()))")
|
||||
public class PerThisAspect {
|
||||
|
||||
private int invocations = 0;
|
||||
|
||||
public int getInvocations() {
|
||||
return this.invocations;
|
||||
}
|
||||
|
||||
@Around("execution(* getAge())")
|
||||
public int changeAge(ProceedingJoinPoint pjp) throws Throwable {
|
||||
return invocations++;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package test.aop;
|
||||
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Before;
|
||||
|
||||
@Aspect
|
||||
public class TwoAdviceAspect {
|
||||
private int totalCalls;
|
||||
|
||||
@Around("execution(* getAge())")
|
||||
public int returnCallCount(ProceedingJoinPoint pjp) throws Exception {
|
||||
return totalCalls;
|
||||
}
|
||||
|
||||
@Before("execution(* setAge(int)) && args(newAge)")
|
||||
public void countSet(int newAge) throws Exception {
|
||||
++totalCalls;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package test.aspect;
|
||||
package org.springframework.aop.testfixture.aspectj;
|
||||
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package test.aop;
|
||||
package org.springframework.aop.testfixture.aspectj;
|
||||
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
+3
-3
@@ -14,13 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package test.aspect;
|
||||
package org.springframework.aop.testfixture.aspectj;
|
||||
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
|
||||
@Aspect("perthis(test.aspect.CommonPointcuts.getAgeExecution())")
|
||||
@Aspect("perthis(org.springframework.aop.testfixture.aspectj.CommonPointcuts.getAgeExecution())")
|
||||
public class PerThisAspect {
|
||||
|
||||
private int invocations = 0;
|
||||
@@ -29,7 +29,7 @@ public class PerThisAspect {
|
||||
return this.invocations;
|
||||
}
|
||||
|
||||
@Around("test.aspect.CommonPointcuts.getAgeExecution()")
|
||||
@Around("org.springframework.aop.testfixture.aspectj.CommonPointcuts.getAgeExecution()")
|
||||
public int changeAge(ProceedingJoinPoint pjp) {
|
||||
return this.invocations++;
|
||||
}
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package test.aspect;
|
||||
package org.springframework.aop.testfixture.aspectj;
|
||||
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
+2
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,8 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package test.mixin;
|
||||
|
||||
package org.springframework.aop.testfixture.mixin;
|
||||
|
||||
/**
|
||||
* Simple implementation of Lockable interface for use in mixins.
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package test.mixin;
|
||||
package org.springframework.aop.testfixture.mixin;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package test.mixin;
|
||||
package org.springframework.aop.testfixture.mixin;
|
||||
|
||||
import org.springframework.aop.support.DefaultIntroductionAdvisor;
|
||||
|
||||
+3
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,14 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package test.mixin;
|
||||
|
||||
package org.springframework.aop.testfixture.mixin;
|
||||
|
||||
/**
|
||||
* Simple interface to use for mixins
|
||||
* Simple interface to use for mixins.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
*
|
||||
*/
|
||||
public interface Lockable {
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,12 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package test.mixin;
|
||||
|
||||
package org.springframework.aop.testfixture.mixin;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class LockedException extends RuntimeException {
|
||||
|
||||
public LockedException() {
|
||||
}
|
||||
|
||||
}
|
||||
+3
-1
@@ -1000,18 +1000,20 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA
|
||||
*/
|
||||
protected abstract static class PropertyHandler {
|
||||
|
||||
@Nullable
|
||||
private final Class<?> propertyType;
|
||||
|
||||
private final boolean readable;
|
||||
|
||||
private final boolean writable;
|
||||
|
||||
public PropertyHandler(Class<?> propertyType, boolean readable, boolean writable) {
|
||||
public PropertyHandler(@Nullable Class<?> propertyType, boolean readable, boolean writable) {
|
||||
this.propertyType = propertyType;
|
||||
this.readable = readable;
|
||||
this.writable = writable;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Class<?> getPropertyType() {
|
||||
return this.propertyType;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Type;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.time.temporal.Temporal;
|
||||
@@ -615,8 +616,8 @@ public abstract class BeanUtils {
|
||||
* @return a corresponding MethodParameter object
|
||||
*/
|
||||
public static MethodParameter getWriteMethodParameter(PropertyDescriptor pd) {
|
||||
if (pd instanceof GenericTypeAwarePropertyDescriptor typeAwarePd) {
|
||||
return new MethodParameter(typeAwarePd.getWriteMethodParameter());
|
||||
if (pd instanceof GenericTypeAwarePropertyDescriptor gpd) {
|
||||
return new MethodParameter(gpd.getWriteMethodParameter());
|
||||
}
|
||||
else {
|
||||
Method writeMethod = pd.getWriteMethod();
|
||||
@@ -787,38 +788,28 @@ public abstract class BeanUtils {
|
||||
if (editable != null) {
|
||||
if (!editable.isInstance(target)) {
|
||||
throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
|
||||
"] not assignable to Editable class [" + editable.getName() + "]");
|
||||
"] not assignable to editable class [" + editable.getName() + "]");
|
||||
}
|
||||
actualEditable = editable;
|
||||
}
|
||||
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
|
||||
Set<String> ignoredProps = (ignoreProperties != null ? new HashSet<>(Arrays.asList(ignoreProperties)) : null);
|
||||
CachedIntrospectionResults sourceResults = (actualEditable != source.getClass() ?
|
||||
CachedIntrospectionResults.forClass(source.getClass()) : null);
|
||||
|
||||
for (PropertyDescriptor targetPd : targetPds) {
|
||||
Method writeMethod = targetPd.getWriteMethod();
|
||||
if (writeMethod != null && (ignoredProps == null || !ignoredProps.contains(targetPd.getName()))) {
|
||||
PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
|
||||
PropertyDescriptor sourcePd = (sourceResults != null ?
|
||||
sourceResults.getPropertyDescriptor(targetPd.getName()) : targetPd);
|
||||
if (sourcePd != null) {
|
||||
Method readMethod = sourcePd.getReadMethod();
|
||||
if (readMethod != null) {
|
||||
ResolvableType sourceResolvableType = ResolvableType.forMethodReturnType(readMethod);
|
||||
ResolvableType targetResolvableType = ResolvableType.forMethodParameter(writeMethod, 0);
|
||||
|
||||
// Ignore generic types in assignable check if either ResolvableType has unresolvable generics.
|
||||
boolean isAssignable =
|
||||
(sourceResolvableType.hasUnresolvableGenerics() || targetResolvableType.hasUnresolvableGenerics() ?
|
||||
ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType()) :
|
||||
targetResolvableType.isAssignableFrom(sourceResolvableType));
|
||||
|
||||
if (isAssignable) {
|
||||
if (isAssignable(writeMethod, readMethod)) {
|
||||
try {
|
||||
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
|
||||
readMethod.setAccessible(true);
|
||||
}
|
||||
ReflectionUtils.makeAccessible(readMethod);
|
||||
Object value = readMethod.invoke(source);
|
||||
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
|
||||
writeMethod.setAccessible(true);
|
||||
}
|
||||
ReflectionUtils.makeAccessible(writeMethod);
|
||||
writeMethod.invoke(target, value);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
@@ -832,6 +823,24 @@ public abstract class BeanUtils {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isAssignable(Method writeMethod, Method readMethod) {
|
||||
Type paramType = writeMethod.getGenericParameterTypes()[0];
|
||||
if (paramType instanceof Class<?> clazz) {
|
||||
return ClassUtils.isAssignable(clazz, readMethod.getReturnType());
|
||||
}
|
||||
else if (paramType.equals(readMethod.getGenericReturnType())) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
ResolvableType sourceType = ResolvableType.forMethodReturnType(readMethod);
|
||||
ResolvableType targetType = ResolvableType.forMethodParameter(writeMethod, 0);
|
||||
// Ignore generic types in assignable check if either ResolvableType has unresolvable generics.
|
||||
return (sourceType.hasUnresolvableGenerics() || targetType.hasUnresolvableGenerics() ?
|
||||
ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType()) :
|
||||
targetType.isAssignableFrom(sourceType));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inner class to avoid a hard dependency on Kotlin at runtime.
|
||||
@@ -896,7 +905,6 @@ public abstract class BeanUtils {
|
||||
}
|
||||
return kotlinConstructor.callBy(argParameters);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -129,7 +129,6 @@ public class DirectFieldAccessor extends AbstractNestablePropertyAccessor {
|
||||
ReflectionUtils.makeAccessible(this.field);
|
||||
return this.field.get(getWrappedInstance());
|
||||
}
|
||||
|
||||
catch (IllegalAccessException ex) {
|
||||
throw new InvalidPropertyException(getWrappedClass(),
|
||||
this.field.getName(), "Field is not accessible", ex);
|
||||
|
||||
+21
-12
@@ -159,6 +159,9 @@ import org.springframework.util.StringUtils;
|
||||
public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor,
|
||||
MergedBeanDefinitionPostProcessor, BeanRegistrationAotProcessor, PriorityOrdered, BeanFactoryAware {
|
||||
|
||||
private static final Constructor<?>[] EMPTY_CONSTRUCTOR_ARRAY = new Constructor<?>[0];
|
||||
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final Set<Class<? extends Annotation>> autowiredAnnotationTypes = new LinkedHashSet<>(4);
|
||||
@@ -193,9 +196,10 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
this.autowiredAnnotationTypes.add(Autowired.class);
|
||||
this.autowiredAnnotationTypes.add(Value.class);
|
||||
|
||||
ClassLoader classLoader = AutowiredAnnotationBeanPostProcessor.class.getClassLoader();
|
||||
try {
|
||||
this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
|
||||
ClassUtils.forName("jakarta.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
|
||||
ClassUtils.forName("jakarta.inject.Inject", classLoader));
|
||||
logger.trace("'jakarta.inject.Inject' annotation found and supported for autowiring");
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
@@ -204,7 +208,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
|
||||
try {
|
||||
this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
|
||||
ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
|
||||
ClassUtils.forName("javax.inject.Inject", classLoader));
|
||||
logger.trace("'javax.inject.Inject' annotation found and supported for autowiring");
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
@@ -285,9 +289,16 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
|
||||
@Override
|
||||
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
|
||||
// Register externally managed config members on bean definition.
|
||||
findInjectionMetadata(beanName, beanType, beanDefinition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetBeanDefinition(String beanName) {
|
||||
this.lookupMethodsChecked.remove(beanName);
|
||||
this.injectionMetadataCache.remove(beanName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
|
||||
@@ -323,12 +334,6 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
return metadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetBeanDefinition(String beanName) {
|
||||
this.lookupMethodsChecked.remove(beanName);
|
||||
this.injectionMetadataCache.remove(beanName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> determineBeanType(Class<?> beanClass, String beanName) throws BeanCreationException {
|
||||
checkLookupMethods(beanClass, beanName);
|
||||
@@ -428,7 +433,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
"default constructor to fall back to: " + candidates.get(0));
|
||||
}
|
||||
}
|
||||
candidateConstructors = candidates.toArray(new Constructor<?>[0]);
|
||||
candidateConstructors = candidates.toArray(EMPTY_CONSTRUCTOR_ARRAY);
|
||||
}
|
||||
else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) {
|
||||
candidateConstructors = new Constructor<?>[] {rawCandidates[0]};
|
||||
@@ -441,7 +446,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
candidateConstructors = new Constructor<?>[] {primaryConstructor};
|
||||
}
|
||||
else {
|
||||
candidateConstructors = new Constructor<?>[0];
|
||||
candidateConstructors = EMPTY_CONSTRUCTOR_ARRAY;
|
||||
}
|
||||
this.candidateConstructorsCache.put(beanClass, candidateConstructors);
|
||||
}
|
||||
@@ -582,6 +587,10 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
return;
|
||||
}
|
||||
if (method.getParameterCount() == 0) {
|
||||
if (method.getDeclaringClass().isRecord()) {
|
||||
// Annotations on the compact constructor arguments made available on accessors, ignoring.
|
||||
return;
|
||||
}
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Autowired annotation should only be used on methods with parameters: " +
|
||||
method);
|
||||
@@ -1007,7 +1016,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
hints.reflection().registerField(field);
|
||||
CodeBlock resolver = CodeBlock.of("$T.$L($S)",
|
||||
AutowiredFieldValueResolver.class,
|
||||
(!required) ? "forField" : "forRequiredField", field.getName());
|
||||
(!required ? "forField" : "forRequiredField"), field.getName());
|
||||
AccessControl accessControl = AccessControl.forMember(field);
|
||||
if (!accessControl.isAccessibleFrom(targetClassName)) {
|
||||
return CodeBlock.of("$L.resolveAndSet($L, $L)", resolver,
|
||||
@@ -1022,7 +1031,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
|
||||
CodeBlock.Builder code = CodeBlock.builder();
|
||||
code.add("$T.$L", AutowiredMethodArgumentsResolver.class,
|
||||
(!required) ? "forMethod" : "forRequiredMethod");
|
||||
(!required ? "forMethod" : "forRequiredMethod"));
|
||||
code.add("($S", method.getName());
|
||||
if (method.getParameterCount() > 0) {
|
||||
code.add(", $L", generateParameterTypesCode(method.getParameterTypes()));
|
||||
|
||||
+4
-6
@@ -191,16 +191,14 @@ public final class AutowiredFieldValueResolver extends AutowiredElementResolver
|
||||
return value;
|
||||
}
|
||||
catch (BeansException ex) {
|
||||
throw new UnsatisfiedDependencyException(null, beanName,
|
||||
new InjectionPoint(field), ex);
|
||||
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
|
||||
}
|
||||
}
|
||||
|
||||
private Field getField(RegisteredBean registeredBean) {
|
||||
Field field = ReflectionUtils.findField(registeredBean.getBeanClass(),
|
||||
this.fieldName);
|
||||
Assert.notNull(field, () -> "No field '" + this.fieldName + "' found on "
|
||||
+ registeredBean.getBeanClass().getName());
|
||||
Field field = ReflectionUtils.findField(registeredBean.getBeanClass(), this.fieldName);
|
||||
Assert.notNull(field, () -> "No field '" + this.fieldName + "' found on " +
|
||||
registeredBean.getBeanClass().getName());
|
||||
return field;
|
||||
}
|
||||
|
||||
|
||||
+2
-4
@@ -69,6 +69,7 @@ class BeanDefinitionPropertyValueCodeGenerator {
|
||||
|
||||
BeanDefinitionPropertyValueCodeGenerator(GeneratedMethods generatedMethods,
|
||||
@Nullable BiFunction<Object, ResolvableType, CodeBlock> customValueGenerator) {
|
||||
|
||||
this.generatedMethods = generatedMethods;
|
||||
this.delegates = new ArrayList<>();
|
||||
if (customValueGenerator != null) {
|
||||
@@ -143,7 +144,6 @@ class BeanDefinitionPropertyValueCodeGenerator {
|
||||
|
||||
@Nullable
|
||||
CodeBlock generateCode(Object value, ResolvableType type);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -163,7 +163,6 @@ class BeanDefinitionPropertyValueCodeGenerator {
|
||||
'\\', "\\\\"
|
||||
);
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public CodeBlock generateCode(Object value, ResolvableType type) {
|
||||
@@ -231,7 +230,6 @@ class BeanDefinitionPropertyValueCodeGenerator {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -321,8 +319,8 @@ class BeanDefinitionPropertyValueCodeGenerator {
|
||||
this.emptyResult = emptyResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
@Nullable
|
||||
public CodeBlock generateCode(Object value, ResolvableType type) {
|
||||
if (this.collectionType.isInstance(value)) {
|
||||
|
||||
+14
-13
@@ -117,7 +117,8 @@ class DefaultBeanRegistrationCodeFragments implements BeanRegistrationCodeFragme
|
||||
Class<?> beanClass = (mergedBeanDefinition.hasBeanClass()
|
||||
? ClassUtils.getUserClass(mergedBeanDefinition.getBeanClass()) : null);
|
||||
CodeBlock beanClassCode = generateBeanClassCode(
|
||||
beanRegistrationCode.getClassName().packageName(), beanClass);
|
||||
beanRegistrationCode.getClassName().packageName(),
|
||||
(beanClass != null ? beanClass : beanType.toClass()));
|
||||
code.addStatement("$T $L = new $T($L)", RootBeanDefinition.class,
|
||||
BEAN_DEFINITION_VARIABLE, RootBeanDefinition.class, beanClassCode);
|
||||
if (targetTypeNecessary(beanType, beanClass)) {
|
||||
@@ -127,16 +128,13 @@ class DefaultBeanRegistrationCodeFragments implements BeanRegistrationCodeFragme
|
||||
return code.build();
|
||||
}
|
||||
|
||||
private CodeBlock generateBeanClassCode(String targetPackage, @Nullable Class<?> beanClass) {
|
||||
if (beanClass != null) {
|
||||
if (Modifier.isPublic(beanClass.getModifiers()) || targetPackage.equals(beanClass.getPackageName())) {
|
||||
return CodeBlock.of("$T.class", beanClass);
|
||||
}
|
||||
else {
|
||||
return CodeBlock.of("$S", beanClass.getName());
|
||||
}
|
||||
private CodeBlock generateBeanClassCode(String targetPackage, Class<?> beanClass) {
|
||||
if (Modifier.isPublic(beanClass.getModifiers()) || targetPackage.equals(beanClass.getPackageName())) {
|
||||
return CodeBlock.of("$T.class", beanClass);
|
||||
}
|
||||
else {
|
||||
return CodeBlock.of("$S", beanClass.getName());
|
||||
}
|
||||
return CodeBlock.of("");
|
||||
}
|
||||
|
||||
private CodeBlock generateBeanTypeCode(ResolvableType beanType) {
|
||||
@@ -147,11 +145,14 @@ class DefaultBeanRegistrationCodeFragments implements BeanRegistrationCodeFragme
|
||||
}
|
||||
|
||||
private boolean targetTypeNecessary(ResolvableType beanType, @Nullable Class<?> beanClass) {
|
||||
if (beanType.hasGenerics() || beanClass == null) {
|
||||
if (beanType.hasGenerics()) {
|
||||
return true;
|
||||
}
|
||||
return (!beanType.toClass().equals(beanClass)
|
||||
|| this.registeredBean.getMergedBeanDefinition().getFactoryMethodName() != null);
|
||||
if (beanClass != null
|
||||
&& this.registeredBean.getMergedBeanDefinition().getFactoryMethodName() != null) {
|
||||
return true;
|
||||
}
|
||||
return (beanClass != null && !beanType.toClass().equals(beanClass));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+7
-9
@@ -493,15 +493,13 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
|
||||
mbdToUse = new RootBeanDefinition(mbd);
|
||||
mbdToUse.setBeanClass(resolvedClass);
|
||||
}
|
||||
|
||||
// Prepare method overrides.
|
||||
try {
|
||||
mbdToUse.prepareMethodOverrides();
|
||||
}
|
||||
catch (BeanDefinitionValidationException ex) {
|
||||
throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
|
||||
beanName, "Validation of method overrides failed", ex);
|
||||
try {
|
||||
mbdToUse.prepareMethodOverrides();
|
||||
}
|
||||
catch (BeanDefinitionValidationException ex) {
|
||||
throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
|
||||
beanName, "Validation of method overrides failed", ex);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
+10
-2
@@ -731,7 +731,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
aliases.add(fullBeanName);
|
||||
}
|
||||
String[] retrievedAliases = super.getAliases(beanName);
|
||||
String prefix = factoryPrefix ? FACTORY_BEAN_PREFIX : "";
|
||||
String prefix = (factoryPrefix ? FACTORY_BEAN_PREFIX : "");
|
||||
for (String retrievedAlias : retrievedAliases) {
|
||||
String alias = prefix + retrievedAlias;
|
||||
if (!alias.equals(name)) {
|
||||
@@ -1497,7 +1497,11 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
if (mbd.hasBeanClass()) {
|
||||
return mbd.getBeanClass();
|
||||
}
|
||||
return doResolveBeanClass(mbd, typesToMatch);
|
||||
Class<?> beanClass = doResolveBeanClass(mbd, typesToMatch);
|
||||
if (mbd.hasBeanClass()) {
|
||||
mbd.prepareMethodOverrides();
|
||||
}
|
||||
return beanClass;
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
throw new CannotLoadBeanClassException(mbd.getResourceDescription(), beanName, mbd.getBeanClassName(), ex);
|
||||
@@ -1505,6 +1509,10 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
catch (LinkageError err) {
|
||||
throw new CannotLoadBeanClassException(mbd.getResourceDescription(), beanName, mbd.getBeanClassName(), err);
|
||||
}
|
||||
catch (BeanDefinitionValidationException ex) {
|
||||
throw new BeanDefinitionStoreException(mbd.getResourceDescription(),
|
||||
beanName, "Validation of method overrides failed", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
+3
-3
@@ -71,7 +71,7 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy {
|
||||
synchronized (bd.constructorArgumentLock) {
|
||||
constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
|
||||
if (constructorToUse == null) {
|
||||
final Class<?> clazz = bd.getBeanClass();
|
||||
Class<?> clazz = bd.getBeanClass();
|
||||
if (clazz.isInterface()) {
|
||||
throw new BeanInstantiationException(clazz, "Specified class is an interface");
|
||||
}
|
||||
@@ -104,7 +104,7 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy {
|
||||
|
||||
@Override
|
||||
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
|
||||
final Constructor<?> ctor, Object... args) {
|
||||
Constructor<?> ctor, Object... args) {
|
||||
|
||||
if (!bd.hasMethodOverrides()) {
|
||||
return BeanUtils.instantiateClass(ctor, args);
|
||||
@@ -128,7 +128,7 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy {
|
||||
|
||||
@Override
|
||||
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
|
||||
@Nullable Object factoryBean, final Method factoryMethod, Object... args) {
|
||||
@Nullable Object factoryBean, Method factoryMethod, Object... args) {
|
||||
|
||||
try {
|
||||
ReflectionUtils.makeAccessible(factoryMethod);
|
||||
|
||||
+7
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -78,8 +78,10 @@ public class PathEditor extends PropertyEditorSupport {
|
||||
if (nioPathCandidate && !text.startsWith("/")) {
|
||||
try {
|
||||
URI uri = ResourceUtils.toURI(text);
|
||||
if (uri.getScheme() != null) {
|
||||
nioPathCandidate = false;
|
||||
String scheme = uri.getScheme();
|
||||
if (scheme != null) {
|
||||
// No NIO candidate except for "C:" style drive letters
|
||||
nioPathCandidate = (scheme.length() == 1);
|
||||
// Let's try NIO file system providers via Paths.get(URI)
|
||||
setValue(Paths.get(uri).normalize());
|
||||
return;
|
||||
@@ -109,7 +111,8 @@ public class PathEditor extends PropertyEditorSupport {
|
||||
setValue(resource.getFile().toPath());
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalArgumentException("Failed to retrieve file for " + resource, ex);
|
||||
throw new IllegalArgumentException(
|
||||
"Could not retrieve file for " + resource + ": " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+39
-1
@@ -2102,7 +2102,7 @@ class DefaultListableBeanFactoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void beanProviderWithParentBeanFactoryReuseOrder() {
|
||||
void beanProviderWithParentBeanFactoryDetectsOrder() {
|
||||
DefaultListableBeanFactory parentBf = new DefaultListableBeanFactory();
|
||||
parentBf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
|
||||
parentBf.registerBeanDefinition("regular", new RootBeanDefinition(TestBean.class));
|
||||
@@ -2110,10 +2110,36 @@ class DefaultListableBeanFactoryTests {
|
||||
lbf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
|
||||
lbf.setParentBeanFactory(parentBf);
|
||||
lbf.registerBeanDefinition("low", new RootBeanDefinition(LowPriorityTestBean.class));
|
||||
|
||||
Stream<Class<?>> orderedTypes = lbf.getBeanProvider(TestBean.class).orderedStream().map(Object::getClass);
|
||||
assertThat(orderedTypes).containsExactly(HighPriorityTestBean.class, LowPriorityTestBean.class, TestBean.class);
|
||||
}
|
||||
|
||||
@Test // gh-28374
|
||||
void beanProviderWithParentBeanFactoryAndMixedOrder() {
|
||||
DefaultListableBeanFactory parentBf = new DefaultListableBeanFactory();
|
||||
parentBf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
|
||||
lbf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
|
||||
lbf.setParentBeanFactory(parentBf);
|
||||
|
||||
lbf.registerSingleton("plainTestBean", new TestBean());
|
||||
|
||||
RootBeanDefinition bd1 = new RootBeanDefinition(PriorityTestBeanFactory.class);
|
||||
bd1.setFactoryMethodName("lowPriorityTestBean");
|
||||
lbf.registerBeanDefinition("lowPriorityTestBean", bd1);
|
||||
|
||||
RootBeanDefinition bd2 = new RootBeanDefinition(PriorityTestBeanFactory.class);
|
||||
bd2.setFactoryMethodName("highPriorityTestBean");
|
||||
parentBf.registerBeanDefinition("highPriorityTestBean", bd2);
|
||||
|
||||
ObjectProvider<TestBean> testBeanProvider = lbf.getBeanProvider(ResolvableType.forClass(TestBean.class));
|
||||
List<TestBean> resolved = testBeanProvider.orderedStream().toList();
|
||||
assertThat(resolved.size()).isEqualTo(3);
|
||||
assertThat(resolved.get(0)).isSameAs(lbf.getBean("highPriorityTestBean"));
|
||||
assertThat(resolved.get(1)).isSameAs(lbf.getBean("lowPriorityTestBean"));
|
||||
assertThat(resolved.get(2)).isSameAs(lbf.getBean("plainTestBean"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void autowireExistingBeanByName() {
|
||||
RootBeanDefinition bd = new RootBeanDefinition(TestBean.class);
|
||||
@@ -3287,6 +3313,18 @@ class DefaultListableBeanFactoryTests {
|
||||
}
|
||||
|
||||
|
||||
private static class PriorityTestBeanFactory {
|
||||
|
||||
public static LowPriorityTestBean lowPriorityTestBean() {
|
||||
return new LowPriorityTestBean();
|
||||
}
|
||||
|
||||
public static HighPriorityTestBean highPriorityTestBean() {
|
||||
return new HighPriorityTestBean();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class NullTestBeanFactoryBean<T> implements FactoryBean<TestBean> {
|
||||
|
||||
@Override
|
||||
|
||||
+109
-23
@@ -47,6 +47,7 @@ import org.springframework.beans.testfixture.beans.TestBean;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.InnerBeanConfiguration;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.MockBeanRegistrationsCode;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.SimpleBean;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.SimpleBeanConfiguration;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.TestHierarchy;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.TestHierarchy.Implementation;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.TestHierarchy.One;
|
||||
@@ -92,29 +93,8 @@ class BeanDefinitionMethodGeneratorTests {
|
||||
this.beanRegistrationsCode = new MockBeanRegistrationsCode(this.generationContext);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void generateBeanDefinitionMethodWithOnlyTargetTypeDoesNotSetBeanClass() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition();
|
||||
beanDefinition.setTargetType(TestBean.class);
|
||||
RegisteredBean registeredBean = registerBean(beanDefinition);
|
||||
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
|
||||
this.methodGeneratorFactory, registeredBean, null,
|
||||
Collections.emptyList());
|
||||
MethodReference method = generator.generateBeanDefinitionMethod(
|
||||
this.generationContext, this.beanRegistrationsCode);
|
||||
compile(method, (actual, compiled) -> {
|
||||
SourceFile sourceFile = compiled.getSourceFile(".*BeanDefinitions");
|
||||
assertThat(sourceFile).contains("Get the bean definition for 'testBean'");
|
||||
assertThat(sourceFile).contains("new RootBeanDefinition()");
|
||||
assertThat(sourceFile).contains("setTargetType(TestBean.class)");
|
||||
assertThat(sourceFile).contains("setInstanceSupplier(TestBean::new)");
|
||||
assertThat(actual).isInstanceOf(RootBeanDefinition.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateBeanDefinitionMethodSpecifiesBeanClassIfSet() {
|
||||
void generateWithBeanClassSetsOnlyBeanClass() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(TestBean.class);
|
||||
RegisteredBean registeredBean = registerBean(beanDefinition);
|
||||
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
|
||||
@@ -133,7 +113,91 @@ class BeanDefinitionMethodGeneratorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateBeanDefinitionMethodSpecifiesBeanClassAndTargetTypIfDifferent() {
|
||||
void generateWithTargetTypeWithNoGenericSetsOnlyBeanClass() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition();
|
||||
beanDefinition.setTargetType(TestBean.class);
|
||||
RegisteredBean registeredBean = registerBean(beanDefinition);
|
||||
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
|
||||
this.methodGeneratorFactory, registeredBean, null,
|
||||
Collections.emptyList());
|
||||
MethodReference method = generator.generateBeanDefinitionMethod(
|
||||
this.generationContext, this.beanRegistrationsCode);
|
||||
compile(method, (actual, compiled) -> {
|
||||
SourceFile sourceFile = compiled.getSourceFile(".*BeanDefinitions");
|
||||
assertThat(sourceFile).contains("Get the bean definition for 'testBean'");
|
||||
assertThat(sourceFile).contains("new RootBeanDefinition(TestBean.class)");
|
||||
assertThat(sourceFile).contains("setInstanceSupplier(TestBean::new)");
|
||||
assertThat(actual).isInstanceOf(RootBeanDefinition.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWithTargetTypeUsingGenericsSetsBothBeanClassAndTargetType() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition();
|
||||
beanDefinition.setTargetType(ResolvableType.forClassWithGenerics(GenericBean.class, Integer.class));
|
||||
RegisteredBean registeredBean = registerBean(beanDefinition);
|
||||
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
|
||||
this.methodGeneratorFactory, registeredBean, null,
|
||||
Collections.emptyList());
|
||||
MethodReference method = generator.generateBeanDefinitionMethod(
|
||||
this.generationContext, this.beanRegistrationsCode);
|
||||
compile(method, (actual, compiled) -> {
|
||||
assertThat(actual.getResolvableType().resolve()).isEqualTo(GenericBean.class);
|
||||
SourceFile sourceFile = compiled.getSourceFile(".*BeanDefinitions");
|
||||
assertThat(sourceFile).contains("Get the bean definition for 'testBean'");
|
||||
assertThat(sourceFile).contains("new RootBeanDefinition(GenericBean.class)");
|
||||
assertThat(sourceFile).contains(
|
||||
"setTargetType(ResolvableType.forClassWithGenerics(GenericBean.class, Integer.class))");
|
||||
assertThat(sourceFile).contains("setInstanceSupplier(GenericBean::new)");
|
||||
assertThat(actual).isInstanceOf(RootBeanDefinition.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWithBeanClassAndFactoryMethodNameSetsTargetTypeAndBeanClass() {
|
||||
this.beanFactory.registerSingleton("factory", new SimpleBeanConfiguration());
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(SimpleBean.class);
|
||||
beanDefinition.setFactoryBeanName("factory");
|
||||
beanDefinition.setFactoryMethodName("simpleBean");
|
||||
RegisteredBean registeredBean = registerBean(beanDefinition);
|
||||
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
|
||||
this.methodGeneratorFactory, registeredBean, null,
|
||||
Collections.emptyList());
|
||||
MethodReference method = generator.generateBeanDefinitionMethod(
|
||||
this.generationContext, this.beanRegistrationsCode);
|
||||
compile(method, (actual, compiled) -> {
|
||||
SourceFile sourceFile = compiled.getSourceFile(".*BeanDefinitions");
|
||||
assertThat(sourceFile).contains("Get the bean definition for 'testBean'");
|
||||
assertThat(sourceFile).contains("new RootBeanDefinition(SimpleBean.class)");
|
||||
assertThat(sourceFile).contains("setTargetType(SimpleBean.class)");
|
||||
assertThat(actual).isInstanceOf(RootBeanDefinition.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWithTargetTypeAndFactoryMethodNameSetsOnlyBeanClass() {
|
||||
this.beanFactory.registerSingleton("factory", new SimpleBeanConfiguration());
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition();
|
||||
beanDefinition.setTargetType(SimpleBean.class);
|
||||
beanDefinition.setFactoryBeanName("factory");
|
||||
beanDefinition.setFactoryMethodName("simpleBean");
|
||||
RegisteredBean registeredBean = registerBean(beanDefinition);
|
||||
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
|
||||
this.methodGeneratorFactory, registeredBean, null,
|
||||
Collections.emptyList());
|
||||
MethodReference method = generator.generateBeanDefinitionMethod(
|
||||
this.generationContext, this.beanRegistrationsCode);
|
||||
compile(method, (actual, compiled) -> {
|
||||
SourceFile sourceFile = compiled.getSourceFile(".*BeanDefinitions");
|
||||
assertThat(sourceFile).contains("Get the bean definition for 'testBean'");
|
||||
assertThat(sourceFile).contains("new RootBeanDefinition(SimpleBean.class)");
|
||||
assertThat(sourceFile).doesNotContain("setTargetType(");
|
||||
assertThat(actual).isInstanceOf(RootBeanDefinition.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWithBeanClassAndTargetTypeDifferentSetsBoth() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(One.class);
|
||||
beanDefinition.setTargetType(Implementation.class);
|
||||
beanDefinition.setResolvedFactoryMethod(ReflectionUtils.findMethod(TestHierarchy.class, "oneBean"));
|
||||
@@ -152,6 +216,28 @@ class BeanDefinitionMethodGeneratorTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWithBeanClassAndTargetTypWithGenericSetsBoth() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(Integer.class);
|
||||
beanDefinition.setTargetType(ResolvableType.forClassWithGenerics(GenericBean.class, Integer.class));
|
||||
RegisteredBean registeredBean = registerBean(beanDefinition);
|
||||
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
|
||||
this.methodGeneratorFactory, registeredBean, null,
|
||||
Collections.emptyList());
|
||||
MethodReference method = generator.generateBeanDefinitionMethod(
|
||||
this.generationContext, this.beanRegistrationsCode);
|
||||
compile(method, (actual, compiled) -> {
|
||||
assertThat(actual.getResolvableType().resolve()).isEqualTo(GenericBean.class);
|
||||
SourceFile sourceFile = compiled.getSourceFile(".*BeanDefinitions");
|
||||
assertThat(sourceFile).contains("Get the bean definition for 'testBean'");
|
||||
assertThat(sourceFile).contains("new RootBeanDefinition(Integer.class)");
|
||||
assertThat(sourceFile).contains(
|
||||
"setTargetType(ResolvableType.forClassWithGenerics(GenericBean.class, Integer.class))");
|
||||
assertThat(sourceFile).contains("setInstanceSupplier(GenericBean::new)");
|
||||
assertThat(actual).isInstanceOf(RootBeanDefinition.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateBeanDefinitionMethodUSeBeanClassNameIfNotReachable() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(PackagePrivateTestBean.class);
|
||||
|
||||
@@ -6,12 +6,12 @@ dependencies {
|
||||
api(project(":spring-core"))
|
||||
optional(project(":spring-jdbc")) // for Quartz support
|
||||
optional(project(":spring-tx")) // for Quartz support
|
||||
optional("com.github.ben-manes.caffeine:caffeine")
|
||||
optional("jakarta.activation:jakarta.activation-api")
|
||||
optional("jakarta.mail:jakarta.mail-api")
|
||||
optional("javax.cache:cache-api")
|
||||
optional("com.github.ben-manes.caffeine:caffeine")
|
||||
optional("org.quartz-scheduler:quartz")
|
||||
optional("org.freemarker:freemarker")
|
||||
optional("org.quartz-scheduler:quartz")
|
||||
testFixturesApi("org.junit.jupiter:junit-jupiter-api")
|
||||
testFixturesImplementation("org.assertj:assertj-core")
|
||||
testFixturesImplementation("org.mockito:mockito-core")
|
||||
@@ -20,10 +20,10 @@ dependencies {
|
||||
testImplementation(testFixtures(project(":spring-context")))
|
||||
testImplementation(testFixtures(project(":spring-core")))
|
||||
testImplementation(testFixtures(project(":spring-tx")))
|
||||
testImplementation("org.hsqldb:hsqldb")
|
||||
testImplementation("jakarta.annotation:jakarta.annotation-api")
|
||||
testRuntimeOnly("org.ehcache:jcache")
|
||||
testRuntimeOnly("org.ehcache:ehcache")
|
||||
testRuntimeOnly("org.glassfish:jakarta.el")
|
||||
testImplementation("org.hsqldb:hsqldb")
|
||||
testRuntimeOnly("com.sun.mail:jakarta.mail")
|
||||
testRuntimeOnly("org.ehcache:ehcache")
|
||||
testRuntimeOnly("org.ehcache:jcache")
|
||||
testRuntimeOnly("org.glassfish:jakarta.el")
|
||||
}
|
||||
|
||||
+6
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -32,6 +32,7 @@ import java.lang.annotation.Target;
|
||||
* @author Stephane Nicoll
|
||||
* @author Sam Brannen
|
||||
* @since 4.1
|
||||
* @see Cacheable
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@@ -42,8 +43,10 @@ public @interface CacheConfig {
|
||||
* Names of the default caches to consider for caching operations defined
|
||||
* in the annotated class.
|
||||
* <p>If none is set at the operation level, these are used instead of the default.
|
||||
* <p>May be used to determine the target cache (or caches), matching the
|
||||
* qualifier value or the bean names of a specific bean definition.
|
||||
* <p>Names may be used to determine the target cache(s), to be resolved via the
|
||||
* configured {@link #cacheResolver()} which typically delegates to
|
||||
* {@link org.springframework.cache.CacheManager#getCache}.
|
||||
* For further details see {@link Cacheable#cacheNames()}.
|
||||
*/
|
||||
String[] cacheNames() default {};
|
||||
|
||||
|
||||
+6
-2
@@ -70,8 +70,12 @@ public @interface Cacheable {
|
||||
|
||||
/**
|
||||
* Names of the caches in which method invocation results are stored.
|
||||
* <p>Names may be used to determine the target cache (or caches), matching
|
||||
* the qualifier value or bean name of a specific bean definition.
|
||||
* <p>Names may be used to determine the target cache(s), to be resolved via the
|
||||
* configured {@link #cacheResolver()} which typically delegates to
|
||||
* {@link org.springframework.cache.CacheManager#getCache}.
|
||||
* <p>This will usually be a single cache name. If multiple names are specified,
|
||||
* they will be consulted for a cache hit in the order of definition, and they
|
||||
* will all receive a put/evict request for the same newly cached value.
|
||||
* @since 4.2
|
||||
* @see #value
|
||||
* @see CacheConfig#cacheNames
|
||||
|
||||
+3
-3
@@ -547,10 +547,10 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect the {@link CachePutRequest} for all {@link CacheOperation} using
|
||||
* the specified result value.
|
||||
* Collect a {@link CachePutRequest} for every {@link CacheOperation}
|
||||
* using the specified result value.
|
||||
* @param contexts the contexts to handle
|
||||
* @param result the result value (never {@code null})
|
||||
* @param result the result value
|
||||
* @param putRequests the collection to update
|
||||
*/
|
||||
private void collectPutRequests(Collection<CacheOperationContext> contexts,
|
||||
|
||||
spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationSourcePointcut.java
Vendored
+23
-1
@@ -27,11 +27,12 @@ import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* A Pointcut that matches if the underlying {@link CacheOperationSource}
|
||||
* A {@code Pointcut} that matches if the underlying {@link CacheOperationSource}
|
||||
* has an attribute for a given method.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @since 3.1
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@@ -86,6 +87,27 @@ class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implement
|
||||
}
|
||||
return (cacheOperationSource == null || cacheOperationSource.isCandidateClass(clazz));
|
||||
}
|
||||
|
||||
private CacheOperationSource getCacheOperationSource() {
|
||||
return cacheOperationSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object other) {
|
||||
return (this == other || (other instanceof CacheOperationSourceClassFilter that &&
|
||||
ObjectUtils.nullSafeEquals(cacheOperationSource, that.getCacheOperationSource())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return CacheOperationSourceClassFilter.class.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return CacheOperationSourceClassFilter.class.getName() + ": " + cacheOperationSource;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+11
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -62,11 +62,13 @@ import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* A component provider that provides candidate components from a base package. Can
|
||||
* use {@link CandidateComponentsIndex the index} if it is available of scans the
|
||||
* classpath otherwise. Candidate components are identified by applying exclude and
|
||||
* include filters. {@link AnnotationTypeFilter}, {@link AssignableTypeFilter} include
|
||||
* filters on an annotation/superclass that are annotated with {@link Indexed} are
|
||||
* A component provider that scans for candidate components starting from a
|
||||
* specified base package. Can use the {@linkplain CandidateComponentsIndex component
|
||||
* index}, if it is available, and scans the classpath otherwise.
|
||||
*
|
||||
* <p>Candidate components are identified by applying exclude and include filters.
|
||||
* {@link AnnotationTypeFilter} and {@link AssignableTypeFilter} include filters
|
||||
* for an annotation/target-type that is annotated with {@link Indexed} are
|
||||
* supported: if any other include filter is specified, the index is ignored and
|
||||
* classpath scanning is used instead.
|
||||
*
|
||||
@@ -201,7 +203,6 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
|
||||
* {@link Controller @Controller} stereotype annotations.
|
||||
* <p>Also supports Jakarta EE's {@link jakarta.annotation.ManagedBean} and
|
||||
* JSR-330's {@link jakarta.inject.Named} annotations, if available.
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void registerDefaultFilters() {
|
||||
@@ -305,7 +306,7 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
|
||||
|
||||
|
||||
/**
|
||||
* Scan the class path for candidate components.
|
||||
* Scan the component index or class path for candidate components.
|
||||
* @param basePackage the package to check for annotated classes
|
||||
* @return a corresponding Set of autodetected bean definitions
|
||||
*/
|
||||
@@ -319,7 +320,7 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the index can be used by this instance.
|
||||
* Determine if the component index can be used by this instance.
|
||||
* @return {@code true} if the index is available and the configuration of this
|
||||
* instance is supported by it, {@code false} otherwise
|
||||
* @since 5.0
|
||||
@@ -460,8 +461,7 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new BeanDefinitionStoreException(
|
||||
"Failed to read candidate component class: " + resource, ex);
|
||||
throw new BeanDefinitionStoreException("Failed to read candidate component class: " + resource, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-6
@@ -445,7 +445,7 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
* @see #getResource
|
||||
* @see Lazy
|
||||
*/
|
||||
protected Object buildLazyResourceProxy(final LookupElement element, final @Nullable String requestingBeanName) {
|
||||
protected Object buildLazyResourceProxy(LookupElement element, @Nullable String requestingBeanName) {
|
||||
TargetSource ts = new TargetSource() {
|
||||
@Override
|
||||
public Class<?> getTargetClass() {
|
||||
@@ -525,16 +525,16 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
String name = element.name;
|
||||
|
||||
if (factory instanceof AutowireCapableBeanFactory autowireCapableBeanFactory) {
|
||||
DependencyDescriptor descriptor = element.getDependencyDescriptor();
|
||||
if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {
|
||||
autowiredBeanNames = new LinkedHashSet<>();
|
||||
resource = autowireCapableBeanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);
|
||||
resource = autowireCapableBeanFactory.resolveDependency(
|
||||
element.getDependencyDescriptor(), requestingBeanName, autowiredBeanNames, null);
|
||||
if (resource == null) {
|
||||
throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");
|
||||
}
|
||||
}
|
||||
else {
|
||||
resource = autowireCapableBeanFactory.resolveBeanByName(name, descriptor);
|
||||
resource = autowireCapableBeanFactory.resolveBeanByName(name, element.getDependencyDescriptor());
|
||||
autowiredBeanNames = Collections.singleton(name);
|
||||
}
|
||||
}
|
||||
@@ -661,8 +661,6 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Class representing injection information about an annotated field
|
||||
* or setter method, supporting the @Resource annotation.
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -104,8 +104,8 @@ import org.springframework.stereotype.Component;
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>{@code @Configuration} classes may not only be bootstrapped using
|
||||
* component scanning, but may also themselves <em>configure</em> component scanning using
|
||||
* <p>{@code @Configuration} classes may not only be bootstrapped using component
|
||||
* scanning, but may also themselves <em>configure</em> component scanning using
|
||||
* the {@link ComponentScan @ComponentScan} annotation:
|
||||
*
|
||||
* <pre class="code">
|
||||
|
||||
+1
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -44,7 +44,6 @@ import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
* public MyService myService() {
|
||||
* return new MyService();
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>If the configuration class above is processed, {@code MyHints} will be
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -38,6 +38,14 @@ import org.springframework.core.env.Profiles;
|
||||
* for web applications. Profiles may also be activated declaratively in
|
||||
* integration tests via the {@code @ActiveProfiles} annotation.
|
||||
*
|
||||
* <p>If no profile is active using one of those options, a default profile is
|
||||
* enabled as a fallback. The name of the default profile is
|
||||
* {@value AbstractEnvironment#RESERVED_DEFAULT_PROFILE_NAME}. This can be changed
|
||||
* via {@link ConfigurableEnvironment#setDefaultProfiles} or declaratively by
|
||||
* setting the {@link AbstractEnvironment#DEFAULT_PROFILES_PROPERTY_NAME
|
||||
* spring.profiles.default} property as a JVM system property, as an environment
|
||||
* variable, or as a Servlet context parameter in {@code web.xml} for web applications.
|
||||
*
|
||||
* <p>The {@code @Profile} annotation may be used in any of the following ways:
|
||||
* <ul>
|
||||
* <li>as a type-level annotation on any class directly or indirectly annotated with
|
||||
|
||||
+20
-12
@@ -136,25 +136,33 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
implements ConfigurableApplicationContext {
|
||||
|
||||
/**
|
||||
* Name of the MessageSource bean in the factory.
|
||||
* If none is supplied, message resolution is delegated to the parent.
|
||||
* @see MessageSource
|
||||
*/
|
||||
public static final String MESSAGE_SOURCE_BEAN_NAME = "messageSource";
|
||||
|
||||
/**
|
||||
* Name of the LifecycleProcessor bean in the factory.
|
||||
* If none is supplied, a DefaultLifecycleProcessor is used.
|
||||
* The name of the {@link LifecycleProcessor} bean in the context.
|
||||
* If none is supplied, a {@link DefaultLifecycleProcessor} is used.
|
||||
* @since 3.0
|
||||
* @see org.springframework.context.LifecycleProcessor
|
||||
* @see org.springframework.context.support.DefaultLifecycleProcessor
|
||||
* @see #start()
|
||||
* @see #stop()
|
||||
*/
|
||||
public static final String LIFECYCLE_PROCESSOR_BEAN_NAME = "lifecycleProcessor";
|
||||
|
||||
/**
|
||||
* Name of the ApplicationEventMulticaster bean in the factory.
|
||||
* If none is supplied, a default SimpleApplicationEventMulticaster is used.
|
||||
* The name of the {@link MessageSource} bean in the context.
|
||||
* If none is supplied, message resolution is delegated to the parent.
|
||||
* @see org.springframework.context.MessageSource
|
||||
* @see org.springframework.context.support.ResourceBundleMessageSource
|
||||
* @see org.springframework.context.support.ReloadableResourceBundleMessageSource
|
||||
* @see #getMessage(MessageSourceResolvable, Locale)
|
||||
*/
|
||||
public static final String MESSAGE_SOURCE_BEAN_NAME = "messageSource";
|
||||
|
||||
/**
|
||||
* The name of the {@link ApplicationEventMulticaster} bean in the context.
|
||||
* If none is supplied, a {@link SimpleApplicationEventMulticaster} is used.
|
||||
* @see org.springframework.context.event.ApplicationEventMulticaster
|
||||
* @see org.springframework.context.event.SimpleApplicationEventMulticaster
|
||||
* @see #publishEvent(ApplicationEvent)
|
||||
* @see #addApplicationListener(ApplicationListener)
|
||||
*/
|
||||
public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
|
||||
|
||||
@@ -195,7 +203,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
/** Flag that indicates whether this context has been closed already. */
|
||||
private final AtomicBoolean closed = new AtomicBoolean();
|
||||
|
||||
/** Synchronization monitor for the "refresh" and "destroy". */
|
||||
/** Synchronization monitor for "refresh" and "close". */
|
||||
private final Object startupShutdownMonitor = new Object();
|
||||
|
||||
/** Reference to the JVM shutdown hook, if registered. */
|
||||
|
||||
+2
-1
@@ -55,7 +55,7 @@ class ContextTypeMatchClassLoader extends DecoratingClassLoader implements Smart
|
||||
// override classes that have not been loaded yet. If not accessible, we will
|
||||
// always override requested classes, even when the classes have been loaded
|
||||
// by the parent ClassLoader already and cannot be transformed anymore anyway.
|
||||
Method method = null;
|
||||
Method method;
|
||||
try {
|
||||
method = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
|
||||
ReflectionUtils.makeAccessible(method);
|
||||
@@ -63,6 +63,7 @@ class ContextTypeMatchClassLoader extends DecoratingClassLoader implements Smart
|
||||
catch (Throwable ex) {
|
||||
// Typically a JDK 9+ InaccessibleObjectException...
|
||||
// Avoid through JVM startup with --add-opens=java.base/java.lang=ALL-UNNAMED
|
||||
method = null;
|
||||
LogFactory.getLog(ContextTypeMatchClassLoader.class).debug(
|
||||
"ClassLoader.findLoadedClass not accessible -> will always override requested class", ex);
|
||||
}
|
||||
|
||||
+9
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,12 +18,13 @@ package org.springframework.ejb.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.jndi.JndiObjectFactoryBean;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser}
|
||||
* implementation for parsing '{@code local-slsb}' tags and
|
||||
* creating plain {@link JndiObjectFactoryBean} definitions.
|
||||
* creating plain {@link JndiObjectFactoryBean} definitions on 6.0.
|
||||
*
|
||||
* @author Rob Harrop
|
||||
* @author Juergen Hoeller
|
||||
@@ -36,4 +37,10 @@ class LocalStatelessSessionBeanDefinitionParser extends AbstractJndiLocatingBean
|
||||
return JndiObjectFactoryBean.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEligibleAttribute(String attributeName) {
|
||||
return (super.isEligibleAttribute(attributeName) &&
|
||||
BeanUtils.getPropertyDescriptor(JndiObjectFactoryBean.class, extractPropertyName(attributeName)) != null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+9
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,12 +18,13 @@ package org.springframework.ejb.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.jndi.JndiObjectFactoryBean;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser}
|
||||
* implementation for parsing '{@code remote-slsb}' tags and
|
||||
* creating plain {@link JndiObjectFactoryBean} definitions.
|
||||
* creating plain {@link JndiObjectFactoryBean} definitions as of 6.0.
|
||||
*
|
||||
* @author Rob Harrop
|
||||
* @author Juergen Hoeller
|
||||
@@ -36,4 +37,10 @@ class RemoteStatelessSessionBeanDefinitionParser extends AbstractJndiLocatingBea
|
||||
return JndiObjectFactoryBean.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEligibleAttribute(String attributeName) {
|
||||
return (super.isEligibleAttribute(attributeName) &&
|
||||
BeanUtils.getPropertyDescriptor(JndiObjectFactoryBean.class, extractPropertyName(attributeName)) != null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+18
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -50,6 +50,9 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public class MetadataNamingStrategy implements ObjectNamingStrategy, InitializingBean {
|
||||
|
||||
private static final char[] QUOTABLE_CHARS = new char[] {',', '=', ':', '"'};
|
||||
|
||||
|
||||
/**
|
||||
* The {@code JmxAttributeSource} implementation to use for reading metadata.
|
||||
*/
|
||||
@@ -132,10 +135,23 @@ public class MetadataNamingStrategy implements ObjectNamingStrategy, Initializin
|
||||
}
|
||||
Hashtable<String, String> properties = new Hashtable<>();
|
||||
properties.put("type", ClassUtils.getShortName(managedClass));
|
||||
properties.put("name", beanKey);
|
||||
properties.put("name", quoteIfNecessary(beanKey));
|
||||
return ObjectNameManager.getInstance(domain, properties);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String quoteIfNecessary(String value) {
|
||||
return shouldQuote(value) ? ObjectName.quote(value) : value;
|
||||
}
|
||||
|
||||
private static boolean shouldQuote(String value) {
|
||||
for (char quotableChar : QUOTABLE_CHARS) {
|
||||
if (value.indexOf(quotableChar) != -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="local-slsb" type="jndiLocatingType">
|
||||
<xsd:element name="local-slsb" type="ejbType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.jndi.JndiObjectFactoryBean"><![CDATA[
|
||||
Exposes a reference to a local EJB Stateless SessionBean.
|
||||
@@ -103,15 +103,56 @@
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="remote-slsb" type="jndiLocatingType">
|
||||
<xsd:element name="remote-slsb">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.jndi.JndiObjectFactoryBean"><![CDATA[
|
||||
Exposes a reference to a remote EJB Stateless SessionBean.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="ejbType">
|
||||
<xsd:attribute name="home-interface" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:java.lang.Class"><![CDATA[
|
||||
The home interface that will be narrowed to before performing the
|
||||
parameterless SLSB create() call that returns the actual SLSB proxy.
|
||||
NOTE: Effectively ignored as of 6.0 in favor of plain JNDI lookups.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="refresh-home-on-connect-failure" type="xsd:boolean" default="false">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Controls whether to refresh the EJB home on connect failure.
|
||||
NOTE: Effectively ignored as of 6.0 in favor of plain JNDI lookups.
|
||||
|
||||
Can be turned on to allow for hot restart of the EJB server.
|
||||
If a cached EJB home throws an RMI exception that indicates a
|
||||
remote connect failure, a fresh home will be fetched and the
|
||||
invocation will be retried.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="cache-session-bean" type="xsd:boolean" default="false">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Controls whether to cache the actual session bean object.
|
||||
NOTE: Effectively ignored as of 6.0 in favor of plain JNDI lookups.
|
||||
|
||||
Off by default for standard EJB compliance. Turn this flag
|
||||
on to optimize session bean access for servers that are
|
||||
known to allow for caching the actual session bean object.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:complexType name="jndiLocatingType">
|
||||
<!-- base types -->
|
||||
<xsd:complexType name="jndiLocatingType" abstract="true">
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="beans:identifiedType">
|
||||
<xsd:sequence>
|
||||
@@ -183,6 +224,40 @@
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="ejbType">
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="jndiLocatingType">
|
||||
<xsd:attribute name="lookup-home-on-startup" type="xsd:boolean" default="true">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Controls whether the lookup of the EJB home object is performed
|
||||
immediately on startup (if true, the default), or on first access
|
||||
(if false).
|
||||
NOTE: Effectively ignored as of 6.0 in favor of plain JNDI lookups.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="cache-home" type="xsd:boolean" default="true">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Controls whether the EJB home object is cached once it has been located.
|
||||
On by default; turn this flag off to always reobtain fresh home objects.
|
||||
NOTE: Effectively ignored as of 6.0 in favor of plain JNDI lookups.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="business-interface" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:java.lang.Class"><![CDATA[
|
||||
The business interface of the EJB being proxied.
|
||||
NOTE: Effectively ignored as of 6.0 in favor of plain JNDI lookups.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:simpleType name="environmentRefType">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,9 +19,9 @@ package org.springframework.aop.aspectj;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import test.mixin.Lockable;
|
||||
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.aop.testfixture.mixin.Lockable;
|
||||
import org.springframework.beans.testfixture.beans.ITestBean;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
|
||||
|
||||
+45
-28
@@ -46,23 +46,25 @@ import org.springframework.aop.framework.ProxyConfig;
|
||||
import org.springframework.aop.support.AbstractPointcutAdvisor;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
|
||||
import org.springframework.aop.testfixture.aspectj.PerTargetAspect;
|
||||
import org.springframework.beans.PropertyValue;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.testfixture.beans.ITestBean;
|
||||
import org.springframework.beans.testfixture.beans.TestBean;
|
||||
import org.springframework.cglib.proxy.Factory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.core.DecoratingProxy;
|
||||
import org.springframework.core.NestedRuntimeException;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
@@ -208,6 +210,31 @@ class AspectJAutoProxyCreatorTests {
|
||||
assertThat(adrian1.getAge()).isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test // gh-31238
|
||||
void cglibProxyClassIsCachedAcrossApplicationContextsForPerTargetAspect() {
|
||||
Class<?> configClass = PerTargetProxyTargetClassTrueConfig.class;
|
||||
TestBean testBean1;
|
||||
TestBean testBean2;
|
||||
|
||||
// Round #1
|
||||
try (ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(configClass)) {
|
||||
testBean1 = context.getBean(TestBean.class);
|
||||
assertThat(AopUtils.isCglibProxy(testBean1)).as("CGLIB proxy").isTrue();
|
||||
assertThat(testBean1.getClass().getInterfaces())
|
||||
.containsExactlyInAnyOrder(Factory.class, SpringProxy.class, Advised.class);
|
||||
}
|
||||
|
||||
// Round #2
|
||||
try (ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(configClass)) {
|
||||
testBean2 = context.getBean(TestBean.class);
|
||||
assertThat(AopUtils.isCglibProxy(testBean2)).as("CGLIB proxy").isTrue();
|
||||
assertThat(testBean2.getClass().getInterfaces())
|
||||
.containsExactlyInAnyOrder(Factory.class, SpringProxy.class, Advised.class);
|
||||
}
|
||||
|
||||
assertThat(testBean1.getClass()).isSameAs(testBean2.getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
void twoAdviceAspect() {
|
||||
ClassPathXmlApplicationContext bf = newContext("twoAdviceAspect.xml");
|
||||
@@ -354,33 +381,6 @@ class AspectJAutoProxyCreatorTests {
|
||||
|
||||
}
|
||||
|
||||
@Aspect("pertarget(execution(* *.getSpouse()))")
|
||||
class PerTargetAspect implements Ordered {
|
||||
|
||||
public int count;
|
||||
|
||||
private int order = Ordered.LOWEST_PRECEDENCE;
|
||||
|
||||
@Around("execution(int *.getAge())")
|
||||
public int returnCountAsAge() {
|
||||
return count++;
|
||||
}
|
||||
|
||||
@Before("execution(void *.set*(int))")
|
||||
public void countSetter() {
|
||||
++count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
}
|
||||
|
||||
@Aspect
|
||||
class AdviceUsingThisJoinPoint {
|
||||
|
||||
@@ -642,6 +642,23 @@ class ProxyTargetClassFalseConfig extends AbstractProxyTargetClassConfig {
|
||||
class ProxyTargetClassTrueConfig extends AbstractProxyTargetClassConfig {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAspectJAutoProxy(proxyTargetClass = true)
|
||||
class PerTargetProxyTargetClassTrueConfig {
|
||||
|
||||
@Bean
|
||||
@Scope("prototype")
|
||||
TestBean testBean() {
|
||||
return new TestBean("Jane", 34);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Scope("prototype")
|
||||
PerTargetAspect perTargetAspect() {
|
||||
return new PerTargetAspect();
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface MessageGenerator {
|
||||
String generateMessage();
|
||||
|
||||
+17
-18
@@ -34,10 +34,6 @@ import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import test.mixin.LockMixin;
|
||||
import test.mixin.LockMixinAdvisor;
|
||||
import test.mixin.Lockable;
|
||||
import test.mixin.LockedException;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.aop.AfterReturningAdvice;
|
||||
@@ -64,6 +60,10 @@ import org.springframework.aop.testfixture.advice.MyThrowsHandler;
|
||||
import org.springframework.aop.testfixture.interceptor.NopInterceptor;
|
||||
import org.springframework.aop.testfixture.interceptor.SerializableNopInterceptor;
|
||||
import org.springframework.aop.testfixture.interceptor.TimestampIntroductionInterceptor;
|
||||
import org.springframework.aop.testfixture.mixin.LockMixin;
|
||||
import org.springframework.aop.testfixture.mixin.LockMixinAdvisor;
|
||||
import org.springframework.aop.testfixture.mixin.Lockable;
|
||||
import org.springframework.aop.testfixture.mixin.LockedException;
|
||||
import org.springframework.beans.testfixture.beans.IOther;
|
||||
import org.springframework.beans.testfixture.beans.ITestBean;
|
||||
import org.springframework.beans.testfixture.beans.Person;
|
||||
@@ -1086,8 +1086,8 @@ public abstract class AbstractAopProxyTests {
|
||||
// NameReverter saved it back
|
||||
assertThat(it.getName()).isEqualTo(name1);
|
||||
assertThat(saver.names).hasSize(2);
|
||||
assertThat(saver.names.get(0)).isEqualTo(name2);
|
||||
assertThat(saver.names.get(1)).isEqualTo(name1);
|
||||
assertThat(saver.names).element(0).isEqualTo(name2);
|
||||
assertThat(saver.names).element(1).isEqualTo(name1);
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@@ -1178,7 +1178,7 @@ public abstract class AbstractAopProxyTests {
|
||||
assertThat(i2).isEqualTo(i1);
|
||||
assertThat(proxyB).isEqualTo(proxyA);
|
||||
assertThat(proxyB.hashCode()).isEqualTo(proxyA.hashCode());
|
||||
assertThat(proxyA.equals(a)).isFalse();
|
||||
assertThat(proxyA).isNotEqualTo(a);
|
||||
|
||||
// Equality checks were handled by the proxy
|
||||
assertThat(i1.getCount()).isEqualTo(0);
|
||||
@@ -1187,7 +1187,7 @@ public abstract class AbstractAopProxyTests {
|
||||
// and won't think it's equal to B's NopInterceptor
|
||||
proxyA.absquatulate();
|
||||
assertThat(i1.getCount()).isEqualTo(1);
|
||||
assertThat(proxyA.equals(proxyB)).isFalse();
|
||||
assertThat(proxyA).isNotEqualTo(proxyB);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1874,6 +1874,14 @@ public abstract class AbstractAopProxyTests {
|
||||
return target.getClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.springframework.aop.TargetSource#isStatic()
|
||||
*/
|
||||
@Override
|
||||
public boolean isStatic() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.springframework.aop.TargetSource#getTarget()
|
||||
*/
|
||||
@@ -1903,19 +1911,10 @@ public abstract class AbstractAopProxyTests {
|
||||
throw new RuntimeException("Expectation failed: " + gets + " gets and " + releases + " releases");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.springframework.aop.TargetSource#isStatic()
|
||||
*/
|
||||
@Override
|
||||
public boolean isStatic() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static abstract class ExposedInvocationTestBean extends TestBean {
|
||||
abstract static class ExposedInvocationTestBean extends TestBean {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
|
||||
@@ -21,7 +21,6 @@ import java.io.Serializable;
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import test.mixin.LockMixinAdvisor;
|
||||
|
||||
import org.springframework.aop.ClassFilter;
|
||||
import org.springframework.aop.MethodMatcher;
|
||||
@@ -31,6 +30,7 @@ import org.springframework.aop.support.DefaultPointcutAdvisor;
|
||||
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
|
||||
import org.springframework.aop.testfixture.advice.CountingBeforeAdvice;
|
||||
import org.springframework.aop.testfixture.interceptor.NopInterceptor;
|
||||
import org.springframework.aop.testfixture.mixin.LockMixinAdvisor;
|
||||
import org.springframework.beans.testfixture.beans.ITestBean;
|
||||
import org.springframework.beans.testfixture.beans.TestBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
+28
-22
@@ -27,8 +27,6 @@ import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import test.mixin.Lockable;
|
||||
import test.mixin.LockedException;
|
||||
|
||||
import org.springframework.aop.ClassFilter;
|
||||
import org.springframework.aop.IntroductionAdvisor;
|
||||
@@ -42,6 +40,8 @@ import org.springframework.aop.testfixture.advice.CountingBeforeAdvice;
|
||||
import org.springframework.aop.testfixture.advice.MyThrowsHandler;
|
||||
import org.springframework.aop.testfixture.interceptor.NopInterceptor;
|
||||
import org.springframework.aop.testfixture.interceptor.TimestampIntroductionInterceptor;
|
||||
import org.springframework.aop.testfixture.mixin.Lockable;
|
||||
import org.springframework.aop.testfixture.mixin.LockedException;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
@@ -139,22 +139,24 @@ public class ProxyFactoryBeanTests {
|
||||
private void testDoubleTargetSourceIsRejected(String name) {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(new ClassPathResource(DBL_TARGETSOURCE_CONTEXT, CLASS));
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class).as("Should not allow TargetSource to be specified in interceptorNames as well as targetSource property")
|
||||
.isThrownBy(() -> bf.getBean(name))
|
||||
.havingCause()
|
||||
.isInstanceOf(AopConfigException.class)
|
||||
.withMessageContaining("TargetSource");
|
||||
.isThrownBy(() -> bf.getBean(name))
|
||||
.havingCause()
|
||||
.isInstanceOf(AopConfigException.class)
|
||||
.withMessageContaining("TargetSource");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTargetSourceNotAtEndOfInterceptorNamesIsRejected() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(new ClassPathResource(NOTLAST_TARGETSOURCE_CONTEXT, CLASS));
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class).as("TargetSource or non-advised object must be last in interceptorNames")
|
||||
.isThrownBy(() -> bf.getBean("targetSourceNotLast"))
|
||||
.havingCause()
|
||||
.isInstanceOf(AopConfigException.class)
|
||||
.withMessageContaining("interceptorNames");
|
||||
.isThrownBy(() -> bf.getBean("targetSourceNotLast"))
|
||||
.havingCause()
|
||||
.isInstanceOf(AopConfigException.class)
|
||||
.withMessageContaining("interceptorNames");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -171,7 +173,7 @@ public class ProxyFactoryBeanTests {
|
||||
assertThat(cba.getCalls()).isEqualTo(1);
|
||||
|
||||
ProxyFactoryBean pfb = (ProxyFactoryBean) bf.getBean("&directTarget");
|
||||
assertThat(TestBean.class.isAssignableFrom(pfb.getObjectType())).as("Has correct object type").isTrue();
|
||||
assertThat(pfb.getObjectType()).isAssignableTo(TestBean.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -181,7 +183,7 @@ public class ProxyFactoryBeanTests {
|
||||
ITestBean tb = (ITestBean) bf.getBean("viaTargetSource");
|
||||
assertThat(tb.getName()).isEqualTo("Adam");
|
||||
ProxyFactoryBean pfb = (ProxyFactoryBean) bf.getBean("&viaTargetSource");
|
||||
assertThat(TestBean.class.isAssignableFrom(pfb.getObjectType())).as("Has correct object type").isTrue();
|
||||
assertThat(pfb.getObjectType()).isAssignableTo(TestBean.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -190,11 +192,15 @@ public class ProxyFactoryBeanTests {
|
||||
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(new ClassPathResource(TARGETSOURCE_CONTEXT, CLASS));
|
||||
|
||||
ITestBean tb = (ITestBean) bf.getBean("noTarget");
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() ->
|
||||
tb.getName())
|
||||
.withMessage("getName");
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(tb::getName).withMessage("getName");
|
||||
FactoryBean<?> pfb = (ProxyFactoryBean) bf.getBean("&noTarget");
|
||||
assertThat(ITestBean.class.isAssignableFrom(pfb.getObjectType())).as("Has correct object type").isTrue();
|
||||
assertThat(pfb.getObjectType()).isAssignableTo(ITestBean.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetObjectTypeOnUninitializedFactoryBean() {
|
||||
ProxyFactoryBean pfb = new ProxyFactoryBean();
|
||||
assertThat(pfb.getObjectType()).isNull();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -227,12 +233,12 @@ public class ProxyFactoryBeanTests {
|
||||
|
||||
@Test
|
||||
public void testPrototypeInstancesAreNotEqual() {
|
||||
assertThat(ITestBean.class.isAssignableFrom(factory.getType("prototype"))).as("Has correct object type").isTrue();
|
||||
assertThat(factory.getType("prototype")).isAssignableTo(ITestBean.class);
|
||||
ITestBean test2 = (ITestBean) factory.getBean("prototype");
|
||||
ITestBean test2_1 = (ITestBean) factory.getBean("prototype");
|
||||
assertThat(test2).as("Prototype instances !=").isNotSameAs(test2_1);
|
||||
assertThat(test2).as("Prototype instances equal").isEqualTo(test2_1);
|
||||
assertThat(ITestBean.class.isAssignableFrom(factory.getType("prototype"))).as("Has correct object type").isTrue();
|
||||
assertThat(factory.getType("prototype")).isAssignableTo(ITestBean.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -291,13 +297,13 @@ public class ProxyFactoryBeanTests {
|
||||
@Test
|
||||
public void testCanGetFactoryReferenceAndManipulate() {
|
||||
ProxyFactoryBean config = (ProxyFactoryBean) factory.getBean("&test1");
|
||||
assertThat(ITestBean.class.isAssignableFrom(config.getObjectType())).as("Has correct object type").isTrue();
|
||||
assertThat(ITestBean.class.isAssignableFrom(factory.getType("test1"))).as("Has correct object type").isTrue();
|
||||
assertThat(config.getObjectType()).isAssignableTo(ITestBean.class);
|
||||
assertThat(factory.getType("test1")).isAssignableTo(ITestBean.class);
|
||||
// Trigger lazy initialization.
|
||||
config.getObject();
|
||||
assertThat(config.getAdvisors().length).as("Have one advisors").isEqualTo(1);
|
||||
assertThat(ITestBean.class.isAssignableFrom(config.getObjectType())).as("Has correct object type").isTrue();
|
||||
assertThat(ITestBean.class.isAssignableFrom(factory.getType("test1"))).as("Has correct object type").isTrue();
|
||||
assertThat(config.getObjectType()).isAssignableTo(ITestBean.class);
|
||||
assertThat(factory.getType("test1")).isAssignableTo(ITestBean.class);
|
||||
|
||||
ITestBean tb = (ITestBean) factory.getBean("test1");
|
||||
// no exception
|
||||
|
||||
+1
-1
@@ -19,7 +19,6 @@ package org.springframework.aop.framework.autoproxy;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import test.mixin.Lockable;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.aop.framework.Advised;
|
||||
@@ -32,6 +31,7 @@ import org.springframework.aop.target.PrototypeTargetSource;
|
||||
import org.springframework.aop.target.ThreadLocalTargetSource;
|
||||
import org.springframework.aop.testfixture.advice.CountingBeforeAdvice;
|
||||
import org.springframework.aop.testfixture.interceptor.NopInterceptor;
|
||||
import org.springframework.aop.testfixture.mixin.Lockable;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.testfixture.beans.CountingTestBean;
|
||||
import org.springframework.beans.testfixture.beans.ITestBean;
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,13 +17,13 @@
|
||||
package org.springframework.aop.framework.autoproxy;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import test.mixin.Lockable;
|
||||
import test.mixin.LockedException;
|
||||
|
||||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.aop.testfixture.advice.CountingBeforeAdvice;
|
||||
import org.springframework.aop.testfixture.interceptor.NopInterceptor;
|
||||
import org.springframework.aop.testfixture.mixin.Lockable;
|
||||
import org.springframework.aop.testfixture.mixin.LockedException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.testfixture.beans.ITestBean;
|
||||
|
||||
+33
@@ -22,8 +22,13 @@ import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.StringWriter;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -56,6 +61,8 @@ import org.springframework.beans.testfixture.beans.ITestBean;
|
||||
import org.springframework.beans.testfixture.beans.IndexedTestBean;
|
||||
import org.springframework.beans.testfixture.beans.TestBean;
|
||||
import org.springframework.beans.testfixture.beans.factory.DummyFactory;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.UrlResource;
|
||||
@@ -1336,6 +1343,15 @@ class XmlBeanFactoryTests {
|
||||
assertThat(dos.lastArg).isEqualTo(s2);
|
||||
}
|
||||
|
||||
@Test // gh-31826
|
||||
void replaceNonOverloadedInterfaceMethodWithoutSpecifyingExplicitArgTypes() {
|
||||
try (ConfigurableApplicationContext context =
|
||||
new ClassPathXmlApplicationContext(DELEGATION_OVERRIDES_CONTEXT.getPath())) {
|
||||
EchoService echoService = context.getBean(EchoService.class);
|
||||
assertThat(echoService.echo("foo", "bar")).containsExactly("bar", "foo");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void lookupOverrideOneMethodWithConstructorInjection() {
|
||||
DefaultListableBeanFactory xbf = new DefaultListableBeanFactory();
|
||||
@@ -1891,3 +1907,20 @@ class XmlBeanFactoryTests {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface EchoService {
|
||||
|
||||
String[] echo(Object... objects);
|
||||
}
|
||||
|
||||
class ReverseArrayMethodReplacer implements MethodReplacer {
|
||||
|
||||
@Override
|
||||
public Object reimplement(Object obj, Method method, Object[] args) {
|
||||
List<String> list = Arrays.stream((Object[]) args[0])
|
||||
.map(Object::toString)
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
Collections.reverse(list);
|
||||
return list.toArray(String[]::new);
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+23
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,6 +21,7 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
@@ -45,6 +46,7 @@ import static org.springframework.context.testfixture.cache.CacheTestUtils.asser
|
||||
* Tests that represent real use cases with advanced configuration.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
class EnableCachingIntegrationTests {
|
||||
|
||||
@@ -52,7 +54,7 @@ class EnableCachingIntegrationTests {
|
||||
|
||||
|
||||
@AfterEach
|
||||
public void closeContext() {
|
||||
void closeContext() {
|
||||
if (this.context != null) {
|
||||
this.context.close();
|
||||
}
|
||||
@@ -83,6 +85,25 @@ class EnableCachingIntegrationTests {
|
||||
assertCacheHit(key, value, cache);
|
||||
}
|
||||
|
||||
@Test // gh-31238
|
||||
public void cglibProxyClassIsCachedAcrossApplicationContexts() {
|
||||
ConfigurableApplicationContext ctx;
|
||||
|
||||
// Round #1
|
||||
ctx = new AnnotationConfigApplicationContext(FooConfigCglib.class);
|
||||
FooService service1 = ctx.getBean(FooService.class);
|
||||
assertThat(AopUtils.isCglibProxy(service1)).as("FooService #1 is not a CGLIB proxy").isTrue();
|
||||
ctx.close();
|
||||
|
||||
// Round #2
|
||||
ctx = new AnnotationConfigApplicationContext(FooConfigCglib.class);
|
||||
FooService service2 = ctx.getBean(FooService.class);
|
||||
assertThat(AopUtils.isCglibProxy(service2)).as("FooService #2 is not a CGLIB proxy").isTrue();
|
||||
ctx.close();
|
||||
|
||||
assertThat(service1.getClass()).isSameAs(service2.getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
void barServiceWithCacheableInterfaceCglib() {
|
||||
this.context = new AnnotationConfigApplicationContext(BarConfigCglib.class);
|
||||
|
||||
Vendored
+16
-13
@@ -60,6 +60,7 @@ class CacheErrorHandlerTests {
|
||||
|
||||
private SimpleService simpleService;
|
||||
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
this.context = new AnnotationConfigApplicationContext(Config.class);
|
||||
@@ -69,11 +70,13 @@ class CacheErrorHandlerTests {
|
||||
this.simpleService = context.getBean(SimpleService.class);
|
||||
}
|
||||
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
void closeContext() {
|
||||
this.context.close();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void getFail() {
|
||||
UnsupportedOperationException exception = new UnsupportedOperationException("Test exception on get");
|
||||
@@ -107,9 +110,9 @@ class CacheErrorHandlerTests {
|
||||
|
||||
this.cacheInterceptor.setErrorHandler(new SimpleCacheErrorHandler());
|
||||
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() ->
|
||||
this.simpleService.get(0L))
|
||||
.withMessage("Test exception on get");
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class)
|
||||
.isThrownBy(() -> this.simpleService.get(0L))
|
||||
.withMessage("Test exception on get");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -128,9 +131,9 @@ class CacheErrorHandlerTests {
|
||||
|
||||
this.cacheInterceptor.setErrorHandler(new SimpleCacheErrorHandler());
|
||||
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() ->
|
||||
this.simpleService.put(0L))
|
||||
.withMessage("Test exception on put");
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class)
|
||||
.isThrownBy(() -> this.simpleService.put(0L))
|
||||
.withMessage("Test exception on put");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -149,9 +152,9 @@ class CacheErrorHandlerTests {
|
||||
|
||||
this.cacheInterceptor.setErrorHandler(new SimpleCacheErrorHandler());
|
||||
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() ->
|
||||
this.simpleService.evict(0L))
|
||||
.withMessage("Test exception on evict");
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class)
|
||||
.isThrownBy(() -> this.simpleService.evict(0L))
|
||||
.withMessage("Test exception on evict");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -170,9 +173,9 @@ class CacheErrorHandlerTests {
|
||||
|
||||
this.cacheInterceptor.setErrorHandler(new SimpleCacheErrorHandler());
|
||||
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() ->
|
||||
this.simpleService.clear())
|
||||
.withMessage("Test exception on clear");
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class)
|
||||
.isThrownBy(() -> this.simpleService.clear())
|
||||
.withMessage("Test exception on clear");
|
||||
}
|
||||
|
||||
|
||||
|
||||
Vendored
+5
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -51,6 +51,7 @@ public class CachePutEvaluationTests {
|
||||
|
||||
private SimpleService service;
|
||||
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
this.context = new AnnotationConfigApplicationContext(Config.class);
|
||||
@@ -59,12 +60,11 @@ public class CachePutEvaluationTests {
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void close() {
|
||||
if (this.context != null) {
|
||||
this.context.close();
|
||||
}
|
||||
public void closeContext() {
|
||||
this.context.close();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void mutualGetPutExclusion() {
|
||||
String key = "1";
|
||||
|
||||
+9
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -72,7 +72,7 @@ class CacheResolverCustomizationTests {
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
void closeContext() {
|
||||
this.context.close();
|
||||
}
|
||||
|
||||
@@ -142,16 +142,17 @@ class CacheResolverCustomizationTests {
|
||||
@Test
|
||||
void noCacheResolved() {
|
||||
Method method = ReflectionUtils.findMethod(SimpleService.class, "noCacheResolved", Object.class);
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
this.simpleService.noCacheResolved(new Object()))
|
||||
.withMessageContaining(method.toString());
|
||||
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> this.simpleService.noCacheResolved(new Object()))
|
||||
.withMessageContaining(method.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void unknownCacheResolver() {
|
||||
assertThatExceptionOfType(NoSuchBeanDefinitionException.class).isThrownBy(() ->
|
||||
this.simpleService.unknownCacheResolver(new Object()))
|
||||
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("unknownCacheResolver"));
|
||||
assertThatExceptionOfType(NoSuchBeanDefinitionException.class)
|
||||
.isThrownBy(() -> this.simpleService.unknownCacheResolver(new Object()))
|
||||
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("unknownCacheResolver"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
+19
@@ -543,6 +543,25 @@ public class CommonAnnotationBeanPostProcessorTests {
|
||||
assertThat(tb.getName()).isEqualTo("notLazyAnymore");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLazyResolutionWithFallbackTypeMatch() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
|
||||
CommonAnnotationBeanPostProcessor bpp = new CommonAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(LazyResourceCglibInjectionBean.class));
|
||||
bf.registerBeanDefinition("tb", new RootBeanDefinition(TestBean.class));
|
||||
|
||||
LazyResourceCglibInjectionBean bean = (LazyResourceCglibInjectionBean) bf.getBean("annotatedBean");
|
||||
assertThat(bf.containsSingleton("tb")).isFalse();
|
||||
bean.testBean.setName("notLazyAnymore");
|
||||
assertThat(bf.containsSingleton("tb")).isTrue();
|
||||
TestBean tb = (TestBean) bf.getBean("tb");
|
||||
assertThat(tb.getName()).isEqualTo("notLazyAnymore");
|
||||
}
|
||||
|
||||
|
||||
public static class AnnotatedInitDestroyBean {
|
||||
|
||||
|
||||
+2
-2
@@ -359,8 +359,8 @@ class ApplicationContextAotGeneratorTests {
|
||||
applicationContext.registerBean(CglibConfiguration.class);
|
||||
TestGenerationContext context = processAheadOfTime(applicationContext);
|
||||
isRegisteredCglibClass(context, CglibConfiguration.class.getName() + "$$SpringCGLIB$$0");
|
||||
isRegisteredCglibClass(context, CglibConfiguration.class.getName() + "$$SpringCGLIB$$1");
|
||||
isRegisteredCglibClass(context, CglibConfiguration.class.getName() + "$$SpringCGLIB$$2");
|
||||
isRegisteredCglibClass(context, CglibConfiguration.class.getName() + "$$SpringCGLIB$$FastClass$$0");
|
||||
isRegisteredCglibClass(context, CglibConfiguration.class.getName() + "$$SpringCGLIB$$FastClass$$1");
|
||||
}
|
||||
|
||||
private void isRegisteredCglibClass(TestGenerationContext context, String cglibClassName) throws IOException {
|
||||
|
||||
+16
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -36,6 +36,10 @@ public abstract class AbstractApplicationEventListenerTests {
|
||||
}
|
||||
}
|
||||
|
||||
protected <T> GenericTestEvent<T> createGenericTestEvent(T payload) {
|
||||
return new GenericTestEvent<>(this, payload);
|
||||
}
|
||||
|
||||
|
||||
protected static class GenericTestEvent<T> extends ApplicationEvent {
|
||||
|
||||
@@ -51,6 +55,7 @@ public abstract class AbstractApplicationEventListenerTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected static class SmartGenericTestEvent<T> extends GenericTestEvent<T> implements ResolvableTypeProvider {
|
||||
|
||||
private final ResolvableType resolvableType;
|
||||
@@ -67,6 +72,7 @@ public abstract class AbstractApplicationEventListenerTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected static class StringEvent extends GenericTestEvent<String> {
|
||||
|
||||
public StringEvent(Object source, String payload) {
|
||||
@@ -74,6 +80,7 @@ public abstract class AbstractApplicationEventListenerTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected static class LongEvent extends GenericTestEvent<Long> {
|
||||
|
||||
public LongEvent(Object source, Long payload) {
|
||||
@@ -81,31 +88,31 @@ public abstract class AbstractApplicationEventListenerTests {
|
||||
}
|
||||
}
|
||||
|
||||
protected <T> GenericTestEvent<T> createGenericTestEvent(T payload) {
|
||||
return new GenericTestEvent<>(this, payload);
|
||||
}
|
||||
|
||||
|
||||
static class GenericEventListener implements ApplicationListener<GenericTestEvent<?>> {
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(GenericTestEvent<?> event) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class ObjectEventListener implements ApplicationListener<GenericTestEvent<Object>> {
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(GenericTestEvent<Object> event) {
|
||||
}
|
||||
}
|
||||
|
||||
static class UpperBoundEventListener
|
||||
implements ApplicationListener<GenericTestEvent<? extends RuntimeException>> {
|
||||
|
||||
static class UpperBoundEventListener implements ApplicationListener<GenericTestEvent<? extends RuntimeException>> {
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(GenericTestEvent<? extends RuntimeException> event) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class StringEventListener implements ApplicationListener<GenericTestEvent<String>> {
|
||||
|
||||
@Override
|
||||
@@ -113,6 +120,7 @@ public abstract class AbstractApplicationEventListenerTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
static class RawApplicationListener implements ApplicationListener {
|
||||
|
||||
@@ -121,10 +129,10 @@ public abstract class AbstractApplicationEventListenerTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class TestEvents {
|
||||
|
||||
public GenericTestEvent<?> wildcardEvent;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-1
@@ -104,7 +104,8 @@ public class GenericApplicationListenerAdapterTests extends AbstractApplicationE
|
||||
|
||||
@Test
|
||||
public void genericListenerStrictTypeSubClass() {
|
||||
supportsEventType(false, ObjectEventListener.class, ResolvableType.forClassWithGenerics(GenericTestEvent.class, Long.class));
|
||||
supportsEventType(false, ObjectEventListener.class,
|
||||
ResolvableType.forClassWithGenerics(GenericTestEvent.class, Long.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user