mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
100 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f8bad939c3 | |||
| ee100cfba7 | |||
| aaf90f5a96 | |||
| 70be04ba81 | |||
| b976ee3f67 | |||
| b11ff96652 | |||
| 667e4e7531 | |||
| 5fd9fab0ce | |||
| 95a8646309 | |||
| 5434edd726 | |||
| 5f13ea95fb | |||
| 74bb42b78f | |||
| 55717adf88 | |||
| 72835f10b9 | |||
| 125ebd029c | |||
| d8d4fa0e24 | |||
| bfd3b3ad88 | |||
| 0909161ba1 | |||
| 4910c217dc | |||
| f262046ef9 | |||
| d50d4a9092 | |||
| b06305e64f | |||
| c749a14326 | |||
| 3817936ca5 | |||
| 9bd2be80b9 | |||
| c6e9cd0c62 | |||
| e2a5cfb459 | |||
| b484ab116f | |||
| c668473186 | |||
| 38595c6a99 | |||
| d756c2b128 | |||
| 3be4322f71 | |||
| 328e444db3 | |||
| 94bdd4380f | |||
| 3f9d479583 | |||
| 338922f03d | |||
| fad857e06a | |||
| b8395a2321 | |||
| 6df8be8be3 | |||
| 4a599d0b22 | |||
| a1463c2bf2 | |||
| 23eff5c650 | |||
| c44bb29aa5 | |||
| 0c22866b72 | |||
| 0c6957e395 | |||
| 8d51fc0444 | |||
| bad01011da | |||
| 867a199507 | |||
| 1b4a4ac51f | |||
| dd0d26b4ba | |||
| d074f660a1 | |||
| 1b8baffbd7 | |||
| 56c63c779f | |||
| 198cf063fd | |||
| b1b6b544a2 | |||
| 9d13ea290f | |||
| c9163b77df | |||
| 033c8df53f | |||
| b272f4e615 | |||
| bafff5079f | |||
| 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 |
@@ -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>
|
||||
+2
-3
@@ -75,7 +75,7 @@ configure([rootProject] + javaProjects) { project ->
|
||||
}
|
||||
|
||||
checkstyle {
|
||||
toolVersion = "10.12.5"
|
||||
toolVersion = "10.12.7"
|
||||
configDirectory.set(rootProject.file("src/checkstyle"))
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ configure([rootProject] + javaProjects) { project ->
|
||||
// JSR-305 only used for non-required meta-annotations
|
||||
compileOnly("com.google.code.findbugs:jsr305")
|
||||
testCompileOnly("com.google.code.findbugs:jsr305")
|
||||
checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:0.0.39")
|
||||
checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:0.0.41")
|
||||
}
|
||||
|
||||
ext.javadocLinks = [
|
||||
@@ -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,141 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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) {
|
||||
String buildDirectoryPath = project.getRootProject()
|
||||
.getLayout().getBuildDirectory().getAsFile().get().getAbsolutePath();
|
||||
Path outDir = Paths.get(buildDirectoryPath, "reports", "api-diff",
|
||||
baseLineVersion + "_to_" + project.getRootProject().getVersion());
|
||||
return project.file(outDir.resolve(project.getName() + ".html").toString());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM ubuntu:jammy-20231004
|
||||
FROM ubuntu:jammy-20240111
|
||||
|
||||
ADD setup.sh /setup.sh
|
||||
ADD get-jdk-url.sh /get-jdk-url.sh
|
||||
@@ -6,6 +6,5 @@ RUN ./setup.sh
|
||||
|
||||
ENV JAVA_HOME /opt/openjdk/java17
|
||||
ENV JDK17 /opt/openjdk/java17
|
||||
ENV JDK20 /opt/openjdk/java20
|
||||
|
||||
ENV PATH $JAVA_HOME/bin:$PATH
|
||||
|
||||
@@ -3,10 +3,7 @@ set -e
|
||||
|
||||
case "$1" in
|
||||
java17)
|
||||
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"
|
||||
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.10%2B13/bellsoft-jdk17.0.10+13-linux-amd64.tar.gz"
|
||||
;;
|
||||
*)
|
||||
echo $"Unknown java version"
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ curl https://raw.githubusercontent.com/spring-io/concourse-java-scripts/v0.0.4/c
|
||||
|
||||
mkdir -p /opt/openjdk
|
||||
pushd /opt/openjdk > /dev/null
|
||||
for jdk in java17 java20
|
||||
for jdk in java17
|
||||
do
|
||||
JDK_URL=$( /get-jdk-url.sh $jdk )
|
||||
mkdir $jdk
|
||||
|
||||
+1
-44
@@ -90,13 +90,6 @@ resources:
|
||||
<<: *docker-resource-source
|
||||
repository: ((docker-hub-organization))/spring-framework-ci
|
||||
tag: ((milestone))
|
||||
- name: every-morning
|
||||
type: time
|
||||
icon: alarm
|
||||
source:
|
||||
start: 8:00 AM
|
||||
stop: 9:00 AM
|
||||
location: Europe/Vienna
|
||||
- name: artifactory-repo
|
||||
type: artifactory-resource
|
||||
icon: package-variant
|
||||
@@ -113,14 +106,6 @@ resources:
|
||||
access_token: ((github-ci-status-token))
|
||||
branch: ((branch))
|
||||
context: build
|
||||
- name: repo-status-jdk20-build
|
||||
type: github-status-resource
|
||||
icon: eye-check-outline
|
||||
source:
|
||||
repository: ((github-repo-name))
|
||||
access_token: ((github-ci-status-token))
|
||||
branch: ((branch))
|
||||
context: jdk20-build
|
||||
- name: slack-alert
|
||||
type: slack-notification
|
||||
icon: slack
|
||||
@@ -217,34 +202,6 @@ jobs:
|
||||
"zip.type": "schema"
|
||||
get_params:
|
||||
threads: 8
|
||||
- name: jdk20-build
|
||||
serial: true
|
||||
public: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
- get: every-morning
|
||||
trigger: true
|
||||
- put: repo-status-jdk20-build
|
||||
params: { state: "pending", commit: "git-repo" }
|
||||
- do:
|
||||
- task: check-project
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/check-project.yml
|
||||
privileged: true
|
||||
timeout: ((task-timeout))
|
||||
params:
|
||||
TEST_TOOLCHAIN: 20
|
||||
<<: *build-project-task-params
|
||||
on_failure:
|
||||
do:
|
||||
- put: repo-status-jdk20-build
|
||||
params: { state: "failure", commit: "git-repo" }
|
||||
- put: slack-alert
|
||||
params:
|
||||
<<: *slack-fail-params
|
||||
- put: repo-status-jdk20-build
|
||||
params: { state: "success", commit: "git-repo" }
|
||||
- name: stage-milestone
|
||||
serial: true
|
||||
plan:
|
||||
@@ -396,7 +353,7 @@ jobs:
|
||||
|
||||
groups:
|
||||
- name: "builds"
|
||||
jobs: ["build", "jdk20-build"]
|
||||
jobs: ["build"]
|
||||
- name: "releases"
|
||||
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release"]
|
||||
- name: "ci-images"
|
||||
|
||||
@@ -4,6 +4,6 @@ set -e
|
||||
source $(dirname $0)/common.sh
|
||||
|
||||
pushd git-repo > /dev/null
|
||||
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK17,JDK20 \
|
||||
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK17 \
|
||||
-PmainToolchain=${MAIN_TOOLCHAIN} -PtestToolchain=${TEST_TOOLCHAIN} --no-daemon --max-workers=4 check antora
|
||||
popd > /dev/null
|
||||
|
||||
@@ -168,7 +168,7 @@ Kotlin::
|
||||
======
|
||||
|
||||
NOTE: Pooling stateless service objects is not usually necessary. We do not believe it should
|
||||
be the default choice, as most stateless objects are naturally thread safe, and instance
|
||||
be the default choice, as most stateless objects are naturally thread-safe, and instance
|
||||
pooling is problematic if resources are cached.
|
||||
|
||||
Simpler pooling is available by using auto-proxying. You can set the `TargetSource` implementations
|
||||
|
||||
@@ -141,7 +141,7 @@ Kotlin::
|
||||
====
|
||||
After you learn about Spring's IoC container, you may want to know more about Spring's
|
||||
`Resource` abstraction (as described in
|
||||
xref:web/webflux-webclient/client-builder.adoc#webflux-client-builder-reactor-resources[Resources])
|
||||
xref:core/resources.adoc[Resources])
|
||||
which provides a convenient mechanism for reading an InputStream from locations defined
|
||||
in a URI syntax. In particular, `Resource` paths are used to construct applications contexts,
|
||||
as described in xref:core/resources.adoc#resources-app-ctx[Application Contexts and Resource Paths].
|
||||
|
||||
@@ -324,7 +324,6 @@ Kotlin::
|
||||
|
||||
|
||||
|
||||
|
||||
[[beans-factory-scopes-application]]
|
||||
=== Application Scope
|
||||
|
||||
@@ -374,7 +373,6 @@ Kotlin::
|
||||
|
||||
|
||||
|
||||
|
||||
[[beans-factory-scopes-websocket]]
|
||||
=== WebSocket Scope
|
||||
|
||||
@@ -384,7 +382,6 @@ xref:web/websocket/stomp/scope.adoc[WebSocket scope] for more details.
|
||||
|
||||
|
||||
|
||||
|
||||
[[beans-factory-scopes-other-injection]]
|
||||
=== Scoped Beans as Dependencies
|
||||
|
||||
|
||||
+7
-5
@@ -4,7 +4,7 @@
|
||||
Projection lets a collection drive the evaluation of a sub-expression, and the result is
|
||||
a new collection. The syntax for projection is `.![projectionExpression]`. For example,
|
||||
suppose we have a list of inventors but want the list of cities where they were born.
|
||||
Effectively, we want to evaluate 'placeOfBirth.city' for every entry in the inventor
|
||||
Effectively, we want to evaluate `placeOfBirth.city` for every entry in the inventor
|
||||
list. The following example uses projection to do so:
|
||||
|
||||
[tabs]
|
||||
@@ -13,16 +13,18 @@ Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
// returns ['Smiljan', 'Idvor' ]
|
||||
List placesOfBirth = (List)parser.parseExpression("members.![placeOfBirth.city]");
|
||||
// evaluates to ["SmilJan", "Idvor"]
|
||||
List placesOfBirth = parser.parseExpression("members.![placeOfBirth.city]")
|
||||
.getValue(societyContext, List.class);
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
// returns ['Smiljan', 'Idvor' ]
|
||||
val placesOfBirth = parser.parseExpression("members.![placeOfBirth.city]") as List<*>
|
||||
// evaluates to ["SmilJan", "Idvor"]
|
||||
val placesOfBirth = parser.parseExpression("members.![placeOfBirth.city]")
|
||||
.getValue(societyContext) as List<*>
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
+12
-11
@@ -28,13 +28,14 @@ Kotlin::
|
||||
======
|
||||
|
||||
Selection is supported for arrays and anything that implements `java.lang.Iterable` or
|
||||
`java.util.Map`. For a list or array, the selection criteria is evaluated against each
|
||||
individual element. Against a map, the selection criteria is evaluated against each map
|
||||
entry (objects of the Java type `Map.Entry`). Each map entry has its `key` and `value`
|
||||
accessible as properties for use in the selection.
|
||||
`java.util.Map`. For an array or `Iterable`, the selection expression is evaluated
|
||||
against each individual element. Against a map, the selection expression is evaluated
|
||||
against each map entry (objects of the Java type `Map.Entry`). Each map entry has its
|
||||
`key` and `value` accessible as properties for use in the selection.
|
||||
|
||||
The following expression returns a new map that consists of those elements of the
|
||||
original map where the entry's value is less than 27:
|
||||
Given a `Map` stored in a variable named `#map`, the following expression returns a new
|
||||
map that consists of those elements of the original map where the entry's value is less
|
||||
than 27:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@@ -42,21 +43,21 @@ Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
Map newMap = parser.parseExpression("map.?[value<27]").getValue();
|
||||
Map newMap = parser.parseExpression("#map.?[value < 27]").getValue(Map.class);
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
val newMap = parser.parseExpression("map.?[value<27]").getValue()
|
||||
val newMap = parser.parseExpression("#map.?[value < 27]").getValue() as Map
|
||||
----
|
||||
======
|
||||
|
||||
In addition to returning all the selected elements, you can retrieve only the first or
|
||||
the last element. To obtain the first element matching the selection, the syntax is
|
||||
`.^[selectionExpression]`. To obtain the last matching selection, the syntax is
|
||||
`.$[selectionExpression]`.
|
||||
the last element. To obtain the first element matching the selection expression, the
|
||||
syntax is `.^[selectionExpression]`. To obtain the last element matching the selection
|
||||
expression, the syntax is `.$[selectionExpression]`.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ email when someone places an order:
|
||||
|
||||
// Call the collaborators to persist the order...
|
||||
|
||||
// Create a thread safe "copy" of the template message and customize it
|
||||
// Create a thread-safe "copy" of the template message and customize it
|
||||
SimpleMailMessage msg = new SimpleMailMessage(this.templateMessage);
|
||||
msg.setTo(order.getCustomer().getEmailAddress());
|
||||
msg.setText(
|
||||
|
||||
+1
-1
@@ -152,7 +152,7 @@ Kotlin::
|
||||
@Autowired
|
||||
lateinit var accountService: AccountService
|
||||
|
||||
lateinit mockMvc: MockMvc
|
||||
lateinit var mockMvc: MockMvc
|
||||
|
||||
@BeforeEach
|
||||
fun setup(wac: WebApplicationContext) {
|
||||
|
||||
+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.
|
||||
|
||||
@@ -70,8 +70,9 @@ it does the same, but it also compares the computed value against the `If-None-M
|
||||
request header and, if the two are equal, returns a 304 (NOT_MODIFIED).
|
||||
|
||||
This strategy saves network bandwidth but not CPU, as the full response must be computed for each request.
|
||||
State-changing HTTP methods and other HTTP conditional request headers such as `If-Match` and `If-Unmodified-Since` are outside the scope of this filter.
|
||||
Other strategies at the controller level can avoid the computation and have a broader support for HTTP conditional requests.
|
||||
State-changing HTTP methods and other HTTP conditional request headers such as `If-Match` and
|
||||
`If-Unmodified-Since` are outside the scope of this filter. Other strategies at the controller level
|
||||
can avoid the computation and have a broader support for HTTP conditional requests.
|
||||
See xref:web/webmvc/mvc-caching.adoc[HTTP Caching].
|
||||
|
||||
This filter has a `writeWeakETag` parameter that configures the filter to write weak ETags
|
||||
|
||||
+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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
@@ -9,23 +9,23 @@ javaPlatform {
|
||||
dependencies {
|
||||
api(platform("com.fasterxml.jackson:jackson-bom:2.14.3"))
|
||||
api(platform("io.micrometer:micrometer-bom:1.10.13"))
|
||||
api(platform("io.netty:netty-bom:4.1.101.Final"))
|
||||
api(platform("io.netty:netty-bom:4.1.107.Final"))
|
||||
api(platform("io.netty:netty5-bom:5.0.0.Alpha5"))
|
||||
api(platform("io.projectreactor:reactor-bom:2022.0.13"))
|
||||
api(platform("io.projectreactor:reactor-bom:2022.0.16"))
|
||||
api(platform("io.rsocket:rsocket-bom:1.1.3"))
|
||||
api(platform("org.apache.groovy:groovy-bom:4.0.15"))
|
||||
api(platform("org.apache.groovy:groovy-bom:4.0.18"))
|
||||
api(platform("org.apache.logging.log4j:log4j-bom:2.21.1"))
|
||||
api(platform("org.eclipse.jetty:jetty-bom:11.0.18"))
|
||||
api(platform("org.eclipse.jetty:jetty-bom:11.0.20"))
|
||||
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.7.0"))
|
||||
api(platform("org.mockito:mockito-bom:5.10.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.33")
|
||||
api("com.github.librepdf:openpdf:1.3.40")
|
||||
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")
|
||||
@@ -53,10 +53,10 @@ dependencies {
|
||||
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.8")
|
||||
api("io.smallrye.reactive:mutiny:1.9.0")
|
||||
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.smallrye.reactive:mutiny:1.10.0")
|
||||
api("io.undertow:undertow-core:2.3.11.Final")
|
||||
api("io.undertow:undertow-servlet:2.3.11.Final")
|
||||
api("io.undertow:undertow-websockets-jsr:2.3.11.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")
|
||||
@@ -96,9 +96,9 @@ dependencies {
|
||||
api("org.apache.derby:derby:10.16.1.1")
|
||||
api("org.apache.derby:derbyclient:10.16.1.1")
|
||||
api("org.apache.derby:derbytools:10.16.1.1")
|
||||
api("org.apache.httpcomponents.client5:httpclient5:5.2.1")
|
||||
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.2.3")
|
||||
api("org.apache.poi:poi-ooxml:5.2.4")
|
||||
api("org.apache.httpcomponents.client5:httpclient5:5.2.3")
|
||||
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.2.4")
|
||||
api("org.apache.poi:poi-ooxml:5.2.5")
|
||||
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")
|
||||
@@ -111,7 +111,7 @@ dependencies {
|
||||
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.10")
|
||||
api("org.eclipse.jetty:jetty-reactive-httpclient:3.0.12")
|
||||
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")
|
||||
@@ -135,7 +135,7 @@ 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.9")
|
||||
api("org.slf4j:slf4j-api:2.0.12")
|
||||
api("org.testng:testng:7.8.0")
|
||||
api("org.webjars:underscorejs:1.8.3")
|
||||
api("org.webjars:webjars-locator-core:0.55")
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
version=6.0.14-SNAPSHOT
|
||||
version=6.0.17
|
||||
|
||||
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.4-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
Vendored
+10
-10
@@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
@@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
|
||||
+6
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -333,12 +333,12 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
Object targetObject = null;
|
||||
Object thisObject = null;
|
||||
try {
|
||||
MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
|
||||
targetObject = mi.getThis();
|
||||
if (!(mi instanceof ProxyMethodInvocation _pmi)) {
|
||||
throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
|
||||
MethodInvocation curr = ExposeInvocationInterceptor.currentInvocation();
|
||||
targetObject = curr.getThis();
|
||||
if (!(curr instanceof ProxyMethodInvocation currPmi)) {
|
||||
throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + curr);
|
||||
}
|
||||
pmi = _pmi;
|
||||
pmi = currPmi;
|
||||
thisObject = pmi.getProxy();
|
||||
}
|
||||
catch (IllegalStateException ex) {
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -293,7 +293,7 @@ final class InstantiationModelAwarePointcutAdvisorImpl
|
||||
@Override
|
||||
public boolean matches(Method method, Class<?> targetClass, Object... args) {
|
||||
// This can match only on declared pointcut.
|
||||
return (isAspectMaterialized() && this.declaredPointcut.matches(method, targetClass));
|
||||
return (isAspectMaterialized() && this.declaredPointcut.matches(method, targetClass, args));
|
||||
}
|
||||
|
||||
private boolean isAspectMaterialized() {
|
||||
|
||||
@@ -102,6 +102,12 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
*/
|
||||
private List<Advisor> advisors = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* List of minimal {@link AdvisorKeyEntry} instances,
|
||||
* to be assigned to the {@link #advisors} field on reduction.
|
||||
* @since 6.0.10
|
||||
* @see #reduceToAdvisorKey
|
||||
*/
|
||||
private List<Advisor> advisorKey = this.advisors;
|
||||
|
||||
|
||||
@@ -557,18 +563,6 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Serialization support
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
|
||||
// Rely on default serialization; just initialize state after deserialization.
|
||||
ois.defaultReadObject();
|
||||
|
||||
// Initialize transient fields.
|
||||
this.methodCache = new ConcurrentHashMap<>(32);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toProxyConfigString() {
|
||||
return toString();
|
||||
@@ -590,6 +584,19 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Serialization support
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
|
||||
// Rely on default serialization; just initialize state after deserialization.
|
||||
ois.defaultReadObject();
|
||||
|
||||
// Initialize transient fields.
|
||||
this.methodCache = new ConcurrentHashMap<>(32);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simple wrapper class around a Method. Used as the key when
|
||||
* caching methods, for efficient equals and hashCode comparisons.
|
||||
@@ -639,7 +646,7 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
* @see #getConfigurationOnlyCopy()
|
||||
* @see #getAdvisorKey()
|
||||
*/
|
||||
private static class AdvisorKeyEntry implements Advisor {
|
||||
private static final class AdvisorKeyEntry implements Advisor {
|
||||
|
||||
private final Class<?> adviceType;
|
||||
|
||||
@@ -649,7 +656,6 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
@Nullable
|
||||
private final String methodMatcherKey;
|
||||
|
||||
|
||||
public AdvisorKeyEntry(Advisor advisor) {
|
||||
this.adviceType = advisor.getAdvice().getClass();
|
||||
if (advisor instanceof PointcutAdvisor pointcutAdvisor) {
|
||||
|
||||
+2
-1
@@ -58,6 +58,7 @@ import org.springframework.util.function.SingletonSupplier;
|
||||
* @author Juergen Hoeller
|
||||
* @author Stephane Nicoll
|
||||
* @author He Bo
|
||||
* @author Sebastien Deleuze
|
||||
* @since 3.1.2
|
||||
*/
|
||||
public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
|
||||
@@ -290,7 +291,7 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
|
||||
else if (Future.class.isAssignableFrom(returnType)) {
|
||||
return executor.submit(task);
|
||||
}
|
||||
else if (void.class == returnType) {
|
||||
else if (void.class == returnType || "kotlin.Unit".equals(returnType.getName())) {
|
||||
executor.submit(task);
|
||||
return null;
|
||||
}
|
||||
|
||||
+23
-25
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,7 +23,6 @@ import java.util.Map;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.aspectj.weaver.tools.PointcutExpression;
|
||||
import org.aspectj.weaver.tools.PointcutPrimitive;
|
||||
import org.aspectj.weaver.tools.UnsupportedPointcutPrimitiveException;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -65,7 +64,7 @@ public class AspectJExpressionPointcutTests {
|
||||
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() throws NoSuchMethodException {
|
||||
public void setup() throws NoSuchMethodException {
|
||||
getAge = TestBean.class.getMethod("getAge");
|
||||
setAge = TestBean.class.getMethod("setAge", int.class);
|
||||
setSomeNumber = TestBean.class.getMethod("setSomeNumber", Number.class);
|
||||
@@ -246,14 +245,13 @@ public class AspectJExpressionPointcutTests {
|
||||
@Test
|
||||
public void testInvalidExpression() {
|
||||
String expression = "execution(void org.springframework.beans.testfixture.beans.TestBean.setSomeNumber(Number) && args(Double)";
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
getPointcut(expression)::getClassFilter); // call to getClassFilter forces resolution
|
||||
assertThatIllegalArgumentException().isThrownBy(getPointcut(expression)::getClassFilter); // call to getClassFilter forces resolution
|
||||
}
|
||||
|
||||
private TestBean getAdvisedProxy(String pointcutExpression, CallCountingInterceptor interceptor) {
|
||||
TestBean target = new TestBean();
|
||||
|
||||
Pointcut pointcut = getPointcut(pointcutExpression);
|
||||
AspectJExpressionPointcut pointcut = getPointcut(pointcutExpression);
|
||||
|
||||
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
|
||||
advisor.setAdvice(interceptor);
|
||||
@@ -277,40 +275,31 @@ public class AspectJExpressionPointcutTests {
|
||||
@Test
|
||||
public void testWithUnsupportedPointcutPrimitive() {
|
||||
String expression = "call(int org.springframework.beans.testfixture.beans.TestBean.getAge())";
|
||||
assertThatExceptionOfType(UnsupportedPointcutPrimitiveException.class).isThrownBy(() ->
|
||||
getPointcut(expression).getClassFilter()) // call to getClassFilter forces resolution...
|
||||
.satisfies(ex -> assertThat(ex.getUnsupportedPrimitive()).isEqualTo(PointcutPrimitive.CALL));
|
||||
assertThatExceptionOfType(UnsupportedPointcutPrimitiveException.class)
|
||||
.isThrownBy(() -> getPointcut(expression).getClassFilter()) // call to getClassFilter forces resolution...
|
||||
.satisfies(ex -> assertThat(ex.getUnsupportedPrimitive()).isEqualTo(PointcutPrimitive.CALL));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAndSubstitution() {
|
||||
Pointcut pc = getPointcut("execution(* *(..)) and args(String)");
|
||||
PointcutExpression expr = ((AspectJExpressionPointcut) pc).getPointcutExpression();
|
||||
assertThat(expr.getPointcutExpression()).isEqualTo("execution(* *(..)) && args(String)");
|
||||
AspectJExpressionPointcut pc = getPointcut("execution(* *(..)) and args(String)");
|
||||
String expr = pc.getPointcutExpression().getPointcutExpression();
|
||||
assertThat(expr).isEqualTo("execution(* *(..)) && args(String)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleAndSubstitutions() {
|
||||
Pointcut pc = getPointcut("execution(* *(..)) and args(String) and this(Object)");
|
||||
PointcutExpression expr = ((AspectJExpressionPointcut) pc).getPointcutExpression();
|
||||
assertThat(expr.getPointcutExpression()).isEqualTo("execution(* *(..)) && args(String) && this(Object)");
|
||||
AspectJExpressionPointcut pc = getPointcut("execution(* *(..)) and args(String) and this(Object)");
|
||||
String expr = pc.getPointcutExpression().getPointcutExpression();
|
||||
assertThat(expr).isEqualTo("execution(* *(..)) && args(String) && this(Object)");
|
||||
}
|
||||
|
||||
private Pointcut getPointcut(String expression) {
|
||||
private AspectJExpressionPointcut getPointcut(String expression) {
|
||||
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
|
||||
pointcut.setExpression(expression);
|
||||
return pointcut;
|
||||
}
|
||||
|
||||
|
||||
public static class OtherIOther implements IOther {
|
||||
|
||||
@Override
|
||||
public void absquatulate() {
|
||||
// Empty
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatchGenericArgument() {
|
||||
String expression = "execution(* set*(java.util.List<org.springframework.beans.testfixture.beans.TestBean>) )";
|
||||
@@ -505,6 +494,15 @@ public class AspectJExpressionPointcutTests {
|
||||
}
|
||||
|
||||
|
||||
public static class OtherIOther implements IOther {
|
||||
|
||||
@Override
|
||||
public void absquatulate() {
|
||||
// Empty
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class HasGeneric {
|
||||
|
||||
public void setFriends(List<TestBean> friends) {
|
||||
|
||||
+40
-17
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -42,26 +42,38 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
*/
|
||||
class ArgumentBindingTests {
|
||||
|
||||
@Test
|
||||
void bindingInPointcutUsedByAdvice() {
|
||||
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new TestBean());
|
||||
proxyFactory.addAspect(NamedPointcutWithArgs.class);
|
||||
|
||||
ITestBean proxiedTestBean = proxyFactory.getProxy();
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> proxiedTestBean.setName("enigma"))
|
||||
.withMessage("enigma");
|
||||
}
|
||||
|
||||
@Test
|
||||
void annotationArgumentNameBinding() {
|
||||
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new TransactionalBean());
|
||||
proxyFactory.addAspect(PointcutWithAnnotationArgument.class);
|
||||
|
||||
ITransactionalBean proxiedTestBean = proxyFactory.getProxy();
|
||||
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(proxiedTestBean::doInTransaction)
|
||||
.withMessage("Invoked with @Transactional");
|
||||
.isThrownBy(proxiedTestBean::doInTransaction)
|
||||
.withMessage("Invoked with @Transactional");
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindingInPointcutUsedByAdvice() {
|
||||
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new TestBean());
|
||||
proxyFactory.addAspect(NamedPointcutWithArgs.class);
|
||||
ITestBean proxiedTestBean = proxyFactory.getProxy();
|
||||
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> proxiedTestBean.setName("enigma"))
|
||||
.withMessage("enigma");
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindingWithDynamicAdvice() {
|
||||
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new TestBean());
|
||||
proxyFactory.addAspect(DynamicPointcutWithArgs.class);
|
||||
ITestBean proxiedTestBean = proxyFactory.getProxy();
|
||||
|
||||
proxiedTestBean.applyName(1);
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> proxiedTestBean.applyName("enigma"))
|
||||
.withMessage("enigma");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -94,6 +106,7 @@ class ArgumentBindingTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Mimics Spring's @Transactional annotation without actually introducing the dependency.
|
||||
*/
|
||||
@@ -101,16 +114,17 @@ class ArgumentBindingTests {
|
||||
@interface Transactional {
|
||||
}
|
||||
|
||||
|
||||
@Aspect
|
||||
static class PointcutWithAnnotationArgument {
|
||||
|
||||
@Around(value = "execution(* org.springframework..*.*(..)) && @annotation(transactional)")
|
||||
@Around("execution(* org.springframework..*.*(..)) && @annotation(transactional)")
|
||||
public Object around(ProceedingJoinPoint pjp, Transactional transactional) throws Throwable {
|
||||
throw new IllegalStateException("Invoked with @Transactional");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Aspect
|
||||
static class NamedPointcutWithArgs {
|
||||
|
||||
@@ -121,7 +135,16 @@ class ArgumentBindingTests {
|
||||
public Object doAround(ProceedingJoinPoint pjp, String aString) throws Throwable {
|
||||
throw new IllegalArgumentException(aString);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Aspect("pertarget(execution(* *(..)))")
|
||||
static class DynamicPointcutWithArgs {
|
||||
|
||||
@Around("execution(* *(..)) && args(java.lang.String)")
|
||||
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
|
||||
throw new IllegalArgumentException(String.valueOf(pjp.getArgs()[0]));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+17
-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);
|
||||
}
|
||||
@@ -1011,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,
|
||||
@@ -1026,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()));
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -292,7 +292,7 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa
|
||||
if (actualValue != null) {
|
||||
actualValue = typeConverter.convertIfNecessary(actualValue, expectedValue.getClass());
|
||||
}
|
||||
if (!expectedValue.equals(actualValue)) {
|
||||
if (!ObjectUtils.nullSafeEquals(expectedValue, actualValue)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+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;
|
||||
}
|
||||
|
||||
|
||||
+12
-14
@@ -92,10 +92,10 @@ import org.springframework.util.function.ThrowingSupplier;
|
||||
* Supports autowiring constructors, properties by name, and properties by type.
|
||||
*
|
||||
* <p>The main template method to be implemented by subclasses is
|
||||
* {@link #resolveDependency(DependencyDescriptor, String, Set, TypeConverter)},
|
||||
* used for autowiring by type. In case of a factory which is capable of searching
|
||||
* its bean definitions, matching beans will typically be implemented through such
|
||||
* a search. For other factory styles, simplified matching algorithms can be implemented.
|
||||
* {@link #resolveDependency(DependencyDescriptor, String, Set, TypeConverter)}, used for
|
||||
* autowiring. In case of a {@link org.springframework.beans.factory.ListableBeanFactory}
|
||||
* which is capable of searching its bean definitions, matching beans will typically be
|
||||
* implemented through such a search. Otherwise, simplified matching can be implemented.
|
||||
*
|
||||
* <p>Note that this class does <i>not</i> assume or implement bean definition
|
||||
* registry capabilities. See {@link DefaultListableBeanFactory} for an implementation
|
||||
@@ -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 {
|
||||
@@ -652,7 +650,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||
// Apply SmartInstantiationAwareBeanPostProcessors to predict the
|
||||
// eventual type after a before-instantiation shortcut.
|
||||
if (targetType != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
|
||||
boolean matchingOnlyFactoryBean = typesToMatch.length == 1 && typesToMatch[0] == FactoryBean.class;
|
||||
boolean matchingOnlyFactoryBean = (typesToMatch.length == 1 && typesToMatch[0] == FactoryBean.class);
|
||||
for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
|
||||
Class<?> predicted = bp.predictBeanType(targetType, beanName);
|
||||
if (predicted != null &&
|
||||
|
||||
+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
|
||||
|
||||
+1
-5
@@ -120,11 +120,7 @@ class ConstructorResolver {
|
||||
|
||||
/**
|
||||
* "autowire constructor" (with constructor arguments by type) behavior.
|
||||
* Also applied if explicit constructor argument values are specified,
|
||||
* matching all remaining arguments with beans from the bean factory.
|
||||
* <p>This corresponds to constructor injection: In this mode, a Spring
|
||||
* bean factory is able to host components that expect constructor-based
|
||||
* dependency resolution.
|
||||
* Also applied if explicit constructor argument values are specified.
|
||||
* @param beanName the name of the bean
|
||||
* @param mbd the merged bean definition for the bean
|
||||
* @param chosenCtors chosen candidate constructors (or {@code null} if none)
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -261,7 +261,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||
if (destroyMethod != null) {
|
||||
return destroyMethod;
|
||||
}
|
||||
for (Class<?> beanInterface : beanClass.getInterfaces()) {
|
||||
for (Class<?> beanInterface : ClassUtils.getAllInterfacesForClass(beanClass)) {
|
||||
destroyMethod = findDestroyMethod(beanInterface, methodName);
|
||||
if (destroyMethod != null) {
|
||||
return destroyMethod;
|
||||
|
||||
+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
|
||||
|
||||
+5
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -33,6 +33,10 @@ public interface ITestBean extends AgeHolder {
|
||||
|
||||
void setName(String name);
|
||||
|
||||
default void applyName(Object name) {
|
||||
setName(String.valueOf(name));
|
||||
}
|
||||
|
||||
ITestBean getSpouse();
|
||||
|
||||
void setSpouse(ITestBean spouse);
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -236,7 +236,7 @@ public class CaffeineCacheManager implements CacheManager {
|
||||
* Build a common {@link CaffeineCache} instance for the specified cache name,
|
||||
* using the common Caffeine configuration specified on this cache manager.
|
||||
* <p>Delegates to {@link #adaptCaffeineCache} as the adaptation method to
|
||||
* Spring's cache abstraction (allowing for centralized decoration etc),
|
||||
* Spring's cache abstraction (allowing for centralized decoration etc.),
|
||||
* passing in a freshly built native Caffeine Cache instance.
|
||||
* @param name the name of the cache
|
||||
* @return the Spring CaffeineCache adapter (or a decorator thereof)
|
||||
|
||||
+1
@@ -121,6 +121,7 @@ public class SimpleMailMessageTests {
|
||||
assertThat(message2.hashCode()).isEqualTo(message1.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public final void testEqualsObject() {
|
||||
SimpleMailMessage message1;
|
||||
SimpleMailMessage message2;
|
||||
|
||||
+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,
|
||||
|
||||
Vendored
+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.
|
||||
@@ -22,7 +22,7 @@ import org.springframework.lang.Nullable;
|
||||
* Abstract the invocation of a cache operation.
|
||||
*
|
||||
* <p>Does not provide a way to transmit checked exceptions but
|
||||
* provide a special exception that should be used to wrap any
|
||||
* provides a special exception that should be used to wrap any
|
||||
* exception that was thrown by the underlying invocation.
|
||||
* Callers are expected to handle this issue type specifically.
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -121,7 +121,7 @@ public interface SmartLifecycle extends Lifecycle, Phased {
|
||||
/**
|
||||
* Return the phase that this lifecycle object is supposed to run in.
|
||||
* <p>The default implementation returns {@link #DEFAULT_PHASE} in order to
|
||||
* let {@code stop()} callbacks execute after regular {@code Lifecycle}
|
||||
* let {@code stop()} callbacks execute before regular {@code Lifecycle}
|
||||
* implementations.
|
||||
* @see #isAutoStartup()
|
||||
* @see #start()
|
||||
|
||||
+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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -387,8 +387,8 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
|
||||
return;
|
||||
}
|
||||
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
|
||||
if (ejbAnnotationType != null && bridgedMethod.isAnnotationPresent(ejbAnnotationType)) {
|
||||
if (ejbAnnotationType != null && bridgedMethod.isAnnotationPresent(ejbAnnotationType)) {
|
||||
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
|
||||
if (Modifier.isStatic(method.getModifiers())) {
|
||||
throw new IllegalStateException("@EJB annotation is not supported on static methods");
|
||||
}
|
||||
@@ -398,7 +398,9 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
|
||||
currElements.add(new EjbRefElement(method, bridgedMethod, pd));
|
||||
}
|
||||
else if (jakartaResourceType != null && bridgedMethod.isAnnotationPresent(jakartaResourceType)) {
|
||||
}
|
||||
else if (jakartaResourceType != null && bridgedMethod.isAnnotationPresent(jakartaResourceType)) {
|
||||
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
|
||||
if (Modifier.isStatic(method.getModifiers())) {
|
||||
throw new IllegalStateException("@Resource annotation is not supported on static methods");
|
||||
}
|
||||
@@ -411,7 +413,9 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
currElements.add(new ResourceElement(method, bridgedMethod, pd));
|
||||
}
|
||||
}
|
||||
else if (javaxResourceType != null && bridgedMethod.isAnnotationPresent(javaxResourceType)) {
|
||||
}
|
||||
else if (javaxResourceType != null && bridgedMethod.isAnnotationPresent(javaxResourceType)) {
|
||||
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
|
||||
if (Modifier.isStatic(method.getModifiers())) {
|
||||
throw new IllegalStateException("@Resource annotation is not supported on static methods");
|
||||
}
|
||||
|
||||
+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
|
||||
|
||||
+2
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -203,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. */
|
||||
@@ -595,7 +595,6 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
|
||||
// Invoke factory processors registered as beans in the context.
|
||||
invokeBeanFactoryPostProcessors(beanFactory);
|
||||
|
||||
// Register bean processors that intercept bean creation.
|
||||
registerBeanPostProcessors(beanFactory);
|
||||
beanPostProcess.end();
|
||||
|
||||
+8
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -45,7 +45,11 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Default implementation of the {@link LifecycleProcessor} strategy.
|
||||
* Spring's default implementation of the {@link LifecycleProcessor} strategy.
|
||||
*
|
||||
* <p>Provides interaction with {@link Lifecycle} and {@link SmartLifecycle} beans in
|
||||
* groups for specific phases, on startup/shutdown as well as for explicit start/stop
|
||||
* interactions on a {@link org.springframework.context.ConfigurableApplicationContext}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Juergen Hoeller
|
||||
@@ -314,6 +318,8 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
|
||||
/**
|
||||
* Helper class for maintaining a group of Lifecycle beans that should be started
|
||||
* and stopped together based on their 'phase' value (or the default value of 0).
|
||||
* The group is expected to be created in an ad-hoc fashion and group members are
|
||||
* expected to always have the same 'phase' value.
|
||||
*/
|
||||
private class LifecycleGroup {
|
||||
|
||||
|
||||
+4
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -27,6 +27,7 @@ import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
@@ -400,7 +401,7 @@ public class ReloadableResourceBundleMessageSource extends AbstractResourceBased
|
||||
|
||||
/**
|
||||
* Refresh the PropertiesHolder for the given bundle filename.
|
||||
* The holder can be {@code null} if not cached before, or a timed-out cache entry
|
||||
* <p>The holder can be {@code null} if not cached before, or a timed-out cache entry
|
||||
* (potentially getting re-validated against the current last-modified timestamp).
|
||||
* @param filename the bundle filename (basename + Locale)
|
||||
* @param propHolder the current PropertiesHolder for the bundle
|
||||
@@ -561,7 +562,7 @@ public class ReloadableResourceBundleMessageSource extends AbstractResourceBased
|
||||
|
||||
private volatile long refreshTimestamp = -2;
|
||||
|
||||
private final ReentrantLock refreshLock = new ReentrantLock();
|
||||
private final Lock refreshLock = new ReentrantLock();
|
||||
|
||||
/** Cache to hold already generated MessageFormats per message code. */
|
||||
private final ConcurrentMap<String, Map<Locale, MessageFormat>> cachedMessageFormats =
|
||||
|
||||
+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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ public interface TaskScheduler {
|
||||
* wrapping a cron expression
|
||||
* @return a {@link ScheduledFuture} representing pending completion of the task,
|
||||
* or {@code null} if the given Trigger object never fires (i.e. returns
|
||||
* {@code null} from {@link Trigger#nextExecutionTime})
|
||||
* {@code null} from {@link Trigger#nextExecution})
|
||||
* @throws org.springframework.core.task.TaskRejectedException if the given task was not accepted
|
||||
* for internal reasons (e.g. a pool overload handling policy or a pool shutdown in progress)
|
||||
* @see org.springframework.scheduling.support.CronTrigger
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -102,9 +102,9 @@ public @interface Scheduled {
|
||||
|
||||
/**
|
||||
* A time zone for which the cron expression will be resolved. By default, this
|
||||
* attribute is the empty String (i.e. the server's local time zone will be used).
|
||||
* attribute is the empty String (i.e. the scheduler's time zone will be used).
|
||||
* @return a zone id accepted by {@link java.util.TimeZone#getTimeZone(String)},
|
||||
* or an empty String to indicate the server's default time zone
|
||||
* or an empty String to indicate the scheduler's default time zone
|
||||
* @since 4.0
|
||||
* @see org.springframework.scheduling.support.CronTrigger#CronTrigger(String, java.util.TimeZone)
|
||||
* @see java.util.TimeZone
|
||||
|
||||
+5
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,7 +26,6 @@ import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -430,14 +429,14 @@ public class ScheduledAnnotationBeanPostProcessor
|
||||
Assert.isTrue(initialDelay.isNegative(), "'initialDelay' not supported for cron triggers");
|
||||
processedSchedule = true;
|
||||
if (!Scheduled.CRON_DISABLED.equals(cron)) {
|
||||
TimeZone timeZone;
|
||||
CronTrigger trigger;
|
||||
if (StringUtils.hasText(zone)) {
|
||||
timeZone = StringUtils.parseTimeZoneString(zone);
|
||||
trigger = new CronTrigger(cron, StringUtils.parseTimeZoneString(zone));
|
||||
}
|
||||
else {
|
||||
timeZone = TimeZone.getDefault();
|
||||
trigger = new CronTrigger(cron);
|
||||
}
|
||||
tasks.add(this.registrar.scheduleCronTask(new CronTask(runnable, new CronTrigger(cron, timeZone))));
|
||||
tasks.add(this.registrar.scheduleCronTask(new CronTask(runnable, trigger)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -73,7 +73,7 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac
|
||||
|
||||
/**
|
||||
* Set the ThreadFactory to use for the ExecutorService's thread pool.
|
||||
* Default is the underlying ExecutorService's default thread factory.
|
||||
* The default is the underlying ExecutorService's default thread factory.
|
||||
* <p>In a Jakarta EE or other managed environment with JSR-236 support,
|
||||
* consider specifying a JNDI-located ManagedThreadFactory: by default,
|
||||
* to be found at "java:comp/DefaultManagedThreadFactory".
|
||||
@@ -108,7 +108,7 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac
|
||||
/**
|
||||
* Set whether to wait for scheduled tasks to complete on shutdown,
|
||||
* not interrupting running tasks and executing all tasks in the queue.
|
||||
* <p>Default is {@code false}, shutting down immediately through interrupting
|
||||
* <p>The default is {@code false}, shutting down immediately through interrupting
|
||||
* ongoing tasks and clearing the queue. Switch this flag to {@code true} if
|
||||
* you prefer fully completed tasks at the expense of a longer shutdown phase.
|
||||
* <p>Note that Spring's container shutdown continues while ongoing tasks
|
||||
@@ -119,6 +119,8 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac
|
||||
* property instead of or in addition to this property.
|
||||
* @see java.util.concurrent.ExecutorService#shutdown()
|
||||
* @see java.util.concurrent.ExecutorService#shutdownNow()
|
||||
* @see #shutdown()
|
||||
* @see #setAwaitTerminationSeconds
|
||||
*/
|
||||
public void setWaitForTasksToCompleteOnShutdown(boolean waitForJobsToCompleteOnShutdown) {
|
||||
this.waitForTasksToCompleteOnShutdown = waitForJobsToCompleteOnShutdown;
|
||||
@@ -237,7 +239,7 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel the given remaining task which never commended execution,
|
||||
* Cancel the given remaining task which never commenced execution,
|
||||
* as returned from {@link ExecutorService#shutdownNow()}.
|
||||
* @param task the task to cancel (typically a {@link RunnableFuture})
|
||||
* @since 5.0.5
|
||||
|
||||
+18
-27
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,17 +29,15 @@ import org.springframework.util.StringUtils;
|
||||
* Created using the {@code parse*} methods.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Juergen Hoeller
|
||||
* @since 5.3
|
||||
*/
|
||||
final class BitsCronField extends CronField {
|
||||
|
||||
public static final BitsCronField ZERO_NANOS = forZeroNanos();
|
||||
|
||||
private static final long MASK = 0xFFFFFFFFFFFFFFFFL;
|
||||
|
||||
|
||||
@Nullable
|
||||
private static BitsCronField zeroNanos = null;
|
||||
|
||||
|
||||
// we store at most 60 bits, for seconds and minutes, so a 64-bit long suffices
|
||||
private long bits;
|
||||
|
||||
@@ -48,16 +46,14 @@ final class BitsCronField extends CronField {
|
||||
super(type);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a {@code BitsCronField} enabled for 0 nanoseconds.
|
||||
*/
|
||||
public static BitsCronField zeroNanos() {
|
||||
if (zeroNanos == null) {
|
||||
BitsCronField field = new BitsCronField(Type.NANO);
|
||||
field.setBit(0);
|
||||
zeroNanos = field;
|
||||
}
|
||||
return zeroNanos;
|
||||
private static BitsCronField forZeroNanos() {
|
||||
BitsCronField field = new BitsCronField(Type.NANO);
|
||||
field.setBit(0);
|
||||
return field;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,7 +104,6 @@ final class BitsCronField extends CronField {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private static BitsCronField parseDate(String value, BitsCronField.Type type) {
|
||||
if (value.equals("?")) {
|
||||
value = "*";
|
||||
@@ -174,6 +169,7 @@ final class BitsCronField extends CronField {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <T extends Temporal & Comparable<? super T>> T nextOrSame(T temporal) {
|
||||
@@ -217,7 +213,6 @@ final class BitsCronField extends CronField {
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void setBits(ValueRange range) {
|
||||
@@ -247,7 +242,14 @@ final class BitsCronField extends CronField {
|
||||
}
|
||||
|
||||
private void clearBit(int index) {
|
||||
this.bits &= ~(1L << index);
|
||||
this.bits &= ~(1L << index);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return (this == other || (other instanceof BitsCronField that &&
|
||||
type() == that.type() && this.bits == that.bits));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -255,17 +257,6 @@ final class BitsCronField extends CronField {
|
||||
return Long.hashCode(this.bits);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof BitsCronField other)) {
|
||||
return false;
|
||||
}
|
||||
return type() == other.type() && this.bits == other.bits;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder(type().toString());
|
||||
|
||||
+10
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,9 +29,14 @@ import org.springframework.util.StringUtils;
|
||||
* <a href="https://www.manpagez.com/man/5/crontab/">crontab expression</a>
|
||||
* that can calculate the next time it matches.
|
||||
*
|
||||
* <p>{@code CronExpression} instances are created through
|
||||
* {@link #parse(String)}; the next match is determined with
|
||||
* {@link #next(Temporal)}.
|
||||
* <p>{@code CronExpression} instances are created through {@link #parse(String)};
|
||||
* the next match is determined with {@link #next(Temporal)}.
|
||||
*
|
||||
* <p>Supports a Quartz day-of-month/week field with an L/# expression. Follows
|
||||
* common cron conventions in every other respect, including 0-6 for SUN-SAT
|
||||
* (plus 7 for SUN as well). Note that Quartz deviates from the day-of-week
|
||||
* convention in cron through 1-7 for SUN-SAT whereas Spring strictly follows
|
||||
* cron even in combination with the optional Quartz-specific L/# expressions.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 5.3
|
||||
@@ -168,7 +173,7 @@ public final class CronExpression {
|
||||
* the cron format
|
||||
*/
|
||||
public static CronExpression parse(String expression) {
|
||||
Assert.hasLength(expression, "Expression string must not be empty");
|
||||
Assert.hasLength(expression, "Expression must not be empty");
|
||||
|
||||
expression = resolveMacros(expression);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,17 +29,24 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Single field in a cron pattern. Created using the {@code parse*} methods,
|
||||
* main and only entry point is {@link #nextOrSame(Temporal)}.
|
||||
* the main and only entry point is {@link #nextOrSame(Temporal)}.
|
||||
*
|
||||
* <p>Supports a Quartz day-of-month/week field with an L/# expression. Follows
|
||||
* common cron conventions in every other respect, including 0-6 for SUN-SAT
|
||||
* (plus 7 for SUN as well). Note that Quartz deviates from the day-of-week
|
||||
* convention in cron through 1-7 for SUN-SAT whereas Spring strictly follows
|
||||
* cron even in combination with the optional Quartz-specific L/# expressions.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 5.3
|
||||
*/
|
||||
abstract class CronField {
|
||||
|
||||
private static final String[] MONTHS = new String[]{"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP",
|
||||
"OCT", "NOV", "DEC"};
|
||||
private static final String[] MONTHS = new String[]
|
||||
{"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"};
|
||||
|
||||
private static final String[] DAYS = new String[]{"MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"};
|
||||
private static final String[] DAYS = new String[]
|
||||
{"MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"};
|
||||
|
||||
private final Type type;
|
||||
|
||||
@@ -48,11 +55,12 @@ abstract class CronField {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a {@code CronField} enabled for 0 nanoseconds.
|
||||
*/
|
||||
public static CronField zeroNanos() {
|
||||
return BitsCronField.zeroNanos();
|
||||
return BitsCronField.ZERO_NANOS;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -169,6 +177,7 @@ abstract class CronField {
|
||||
* day-of-month, month, day-of-week.
|
||||
*/
|
||||
protected enum Type {
|
||||
|
||||
NANO(ChronoField.NANO_OF_SECOND, ChronoUnit.SECONDS),
|
||||
SECOND(ChronoField.SECOND_OF_MINUTE, ChronoUnit.MINUTES, ChronoField.NANO_OF_SECOND),
|
||||
MINUTE(ChronoField.MINUTE_OF_HOUR, ChronoUnit.HOURS, ChronoField.SECOND_OF_MINUTE, ChronoField.NANO_OF_SECOND),
|
||||
@@ -177,21 +186,18 @@ abstract class CronField {
|
||||
MONTH(ChronoField.MONTH_OF_YEAR, ChronoUnit.YEARS, ChronoField.DAY_OF_MONTH, ChronoField.HOUR_OF_DAY, ChronoField.MINUTE_OF_HOUR, ChronoField.SECOND_OF_MINUTE, ChronoField.NANO_OF_SECOND),
|
||||
DAY_OF_WEEK(ChronoField.DAY_OF_WEEK, ChronoUnit.WEEKS, ChronoField.HOUR_OF_DAY, ChronoField.MINUTE_OF_HOUR, ChronoField.SECOND_OF_MINUTE, ChronoField.NANO_OF_SECOND);
|
||||
|
||||
|
||||
private final ChronoField field;
|
||||
|
||||
private final ChronoUnit higherOrder;
|
||||
|
||||
private final ChronoField[] lowerOrders;
|
||||
|
||||
|
||||
Type(ChronoField field, ChronoUnit higherOrder, ChronoField... lowerOrders) {
|
||||
this.field = field;
|
||||
this.higherOrder = higherOrder;
|
||||
this.lowerOrders = lowerOrders;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the value of this type for the given temporal.
|
||||
* @return the value of this type
|
||||
|
||||
+25
-16
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -27,8 +27,14 @@ import org.springframework.scheduling.TriggerContext;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link Trigger} implementation for cron expressions.
|
||||
* Wraps a {@link CronExpression}.
|
||||
* {@link Trigger} implementation for cron expressions. Wraps a
|
||||
* {@link CronExpression} which parses according to common crontab conventions.
|
||||
*
|
||||
* <p>Supports a Quartz day-of-month/week field with an L/# expression. Follows
|
||||
* common cron conventions in every other respect, including 0-6 for SUN-SAT
|
||||
* (plus 7 for SUN as well). Note that Quartz deviates from the day-of-week
|
||||
* convention in cron through 1-7 for SUN-SAT whereas Spring strictly follows
|
||||
* cron even in combination with the optional Quartz-specific L/# expressions.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Arjen Poutsma
|
||||
@@ -39,6 +45,7 @@ public class CronTrigger implements Trigger {
|
||||
|
||||
private final CronExpression expression;
|
||||
|
||||
@Nullable
|
||||
private final ZoneId zoneId;
|
||||
|
||||
|
||||
@@ -48,7 +55,8 @@ public class CronTrigger implements Trigger {
|
||||
* expression conventions
|
||||
*/
|
||||
public CronTrigger(String expression) {
|
||||
this(expression, ZoneId.systemDefault());
|
||||
this.expression = CronExpression.parse(expression);
|
||||
this.zoneId = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,7 +66,9 @@ public class CronTrigger implements Trigger {
|
||||
* @param timeZone a time zone in which the trigger times will be generated
|
||||
*/
|
||||
public CronTrigger(String expression, TimeZone timeZone) {
|
||||
this(expression, timeZone.toZoneId());
|
||||
this.expression = CronExpression.parse(expression);
|
||||
Assert.notNull(timeZone, "TimeZone must not be null");
|
||||
this.zoneId = timeZone.toZoneId();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,10 +80,8 @@ public class CronTrigger implements Trigger {
|
||||
* @see CronExpression#parse(String)
|
||||
*/
|
||||
public CronTrigger(String expression, ZoneId zoneId) {
|
||||
Assert.hasLength(expression, "Expression must not be empty");
|
||||
Assert.notNull(zoneId, "ZoneId must not be null");
|
||||
|
||||
this.expression = CronExpression.parse(expression);
|
||||
Assert.notNull(zoneId, "ZoneId must not be null");
|
||||
this.zoneId = zoneId;
|
||||
}
|
||||
|
||||
@@ -94,22 +102,23 @@ public class CronTrigger implements Trigger {
|
||||
*/
|
||||
@Override
|
||||
public Instant nextExecution(TriggerContext triggerContext) {
|
||||
Instant instant = triggerContext.lastCompletion();
|
||||
if (instant != null) {
|
||||
Instant timestamp = triggerContext.lastCompletion();
|
||||
if (timestamp != null) {
|
||||
Instant scheduled = triggerContext.lastScheduledExecution();
|
||||
if (scheduled != null && instant.isBefore(scheduled)) {
|
||||
if (scheduled != null && timestamp.isBefore(scheduled)) {
|
||||
// Previous task apparently executed too early...
|
||||
// Let's simply use the last calculated execution time then,
|
||||
// in order to prevent accidental re-fires in the same second.
|
||||
instant = scheduled;
|
||||
timestamp = scheduled;
|
||||
}
|
||||
}
|
||||
else {
|
||||
instant = triggerContext.getClock().instant();
|
||||
timestamp = triggerContext.getClock().instant();
|
||||
}
|
||||
ZonedDateTime dateTime = ZonedDateTime.ofInstant(instant, this.zoneId);
|
||||
ZonedDateTime next = this.expression.next(dateTime);
|
||||
return (next != null ? next.toInstant() : null);
|
||||
ZoneId zone = (this.zoneId != null ? this.zoneId : triggerContext.getClock().getZone());
|
||||
ZonedDateTime zonedTimestamp = ZonedDateTime.ofInstant(timestamp, zone);
|
||||
ZonedDateTime nextTimestamp = this.expression.next(zonedTimestamp);
|
||||
return (nextTimestamp != null ? nextTimestamp.toInstant() : null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+31
-34
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -30,10 +30,15 @@ import org.springframework.util.Assert;
|
||||
/**
|
||||
* Extension of {@link CronField} for
|
||||
* <a href="https://www.quartz-scheduler.org">Quartz</a>-specific fields.
|
||||
*
|
||||
* <p>Created using the {@code parse*} methods, uses a {@link TemporalAdjuster}
|
||||
* Created using the {@code parse*} methods, uses a {@link TemporalAdjuster}
|
||||
* internally.
|
||||
*
|
||||
* <p>Supports a Quartz day-of-month/week field with an L/# expression. Follows
|
||||
* common cron conventions in every other respect, including 0-6 for SUN-SAT
|
||||
* (plus 7 for SUN as well). Note that Quartz deviates from the day-of-week
|
||||
* convention in cron through 1-7 for SUN-SAT whereas Spring strictly follows
|
||||
* cron even in combination with the optional Quartz-specific L/# expressions.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 5.3
|
||||
*/
|
||||
@@ -61,8 +66,9 @@ final class QuartzCronField extends CronField {
|
||||
this.rollForwardType = rollForwardType;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the given value is a Quartz day-of-month field.
|
||||
* Determine whether the given value is a Quartz day-of-month field.
|
||||
*/
|
||||
public static boolean isQuartzDaysOfMonthField(String value) {
|
||||
return value.contains("L") || value.contains("W");
|
||||
@@ -80,14 +86,14 @@ final class QuartzCronField extends CronField {
|
||||
if (idx != 0) {
|
||||
throw new IllegalArgumentException("Unrecognized characters before 'L' in '" + value + "'");
|
||||
}
|
||||
else if (value.length() == 2 && value.charAt(1) == 'W') { // "LW"
|
||||
else if (value.length() == 2 && value.charAt(1) == 'W') { // "LW"
|
||||
adjuster = lastWeekdayOfMonth();
|
||||
}
|
||||
else {
|
||||
if (value.length() == 1) { // "L"
|
||||
if (value.length() == 1) { // "L"
|
||||
adjuster = lastDayOfMonth();
|
||||
}
|
||||
else { // "L-[0-9]+"
|
||||
else { // "L-[0-9]+"
|
||||
int offset = Integer.parseInt(value, idx + 1, value.length(), 10);
|
||||
if (offset >= 0) {
|
||||
throw new IllegalArgumentException("Offset '" + offset + " should be < 0 '" + value + "'");
|
||||
@@ -105,7 +111,7 @@ final class QuartzCronField extends CronField {
|
||||
else if (idx != value.length() - 1) {
|
||||
throw new IllegalArgumentException("Unrecognized characters after 'W' in '" + value + "'");
|
||||
}
|
||||
else { // "[0-9]+W"
|
||||
else { // "[0-9]+W"
|
||||
int dayOfMonth = Integer.parseInt(value, 0, idx, 10);
|
||||
dayOfMonth = Type.DAY_OF_MONTH.checkValidValue(dayOfMonth);
|
||||
TemporalAdjuster adjuster = weekdayNearestTo(dayOfMonth);
|
||||
@@ -116,7 +122,7 @@ final class QuartzCronField extends CronField {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given value is a Quartz day-of-week field.
|
||||
* Determine whether the given value is a Quartz day-of-week field.
|
||||
*/
|
||||
public static boolean isQuartzDaysOfWeekField(String value) {
|
||||
return value.contains("L") || value.contains("#");
|
||||
@@ -138,7 +144,7 @@ final class QuartzCronField extends CronField {
|
||||
if (idx == 0) {
|
||||
throw new IllegalArgumentException("No day-of-week before 'L' in '" + value + "'");
|
||||
}
|
||||
else { // "[0-7]L"
|
||||
else { // "[0-7]L"
|
||||
DayOfWeek dayOfWeek = parseDayOfWeek(value.substring(0, idx));
|
||||
adjuster = lastInMonth(dayOfWeek);
|
||||
}
|
||||
@@ -160,7 +166,6 @@ final class QuartzCronField extends CronField {
|
||||
throw new IllegalArgumentException("Ordinal '" + ordinal + "' in '" + value +
|
||||
"' must be positive number ");
|
||||
}
|
||||
|
||||
TemporalAdjuster adjuster = dayOfWeekInMonth(ordinal, dayOfWeek);
|
||||
return new QuartzCronField(Type.DAY_OF_WEEK, Type.DAY_OF_MONTH, adjuster, value);
|
||||
}
|
||||
@@ -170,14 +175,13 @@ final class QuartzCronField extends CronField {
|
||||
private static DayOfWeek parseDayOfWeek(String value) {
|
||||
int dayOfWeek = Integer.parseInt(value);
|
||||
if (dayOfWeek == 0) {
|
||||
dayOfWeek = 7; // cron is 0 based; java.time 1 based
|
||||
dayOfWeek = 7; // cron is 0 based; java.time 1 based
|
||||
}
|
||||
try {
|
||||
return DayOfWeek.of(dayOfWeek);
|
||||
}
|
||||
catch (DateTimeException ex) {
|
||||
String msg = ex.getMessage() + " '" + value + "'";
|
||||
throw new IllegalArgumentException(msg, ex);
|
||||
throw new IllegalArgumentException(ex.getMessage() + " '" + value + "'", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,10 +220,10 @@ final class QuartzCronField extends CronField {
|
||||
Temporal lastDom = adjuster.adjustInto(temporal);
|
||||
Temporal result;
|
||||
int dow = lastDom.get(ChronoField.DAY_OF_WEEK);
|
||||
if (dow == 6) { // Saturday
|
||||
if (dow == 6) { // Saturday
|
||||
result = lastDom.minus(1, ChronoUnit.DAYS);
|
||||
}
|
||||
else if (dow == 7) { // Sunday
|
||||
else if (dow == 7) { // Sunday
|
||||
result = lastDom.minus(2, ChronoUnit.DAYS);
|
||||
}
|
||||
else {
|
||||
@@ -256,10 +260,10 @@ final class QuartzCronField extends CronField {
|
||||
int current = Type.DAY_OF_MONTH.get(temporal);
|
||||
DayOfWeek dayOfWeek = DayOfWeek.from(temporal);
|
||||
|
||||
if ((current == dayOfMonth && isWeekday(dayOfWeek)) || // dayOfMonth is a weekday
|
||||
(dayOfWeek == DayOfWeek.FRIDAY && current == dayOfMonth - 1) || // dayOfMonth is a Saturday, so Friday before
|
||||
(dayOfWeek == DayOfWeek.MONDAY && current == dayOfMonth + 1) || // dayOfMonth is a Sunday, so Monday after
|
||||
(dayOfWeek == DayOfWeek.MONDAY && dayOfMonth == 1 && current == 3)) { // dayOfMonth is Saturday 1st, so Monday 3rd
|
||||
if ((current == dayOfMonth && isWeekday(dayOfWeek)) || // dayOfMonth is a weekday
|
||||
(dayOfWeek == DayOfWeek.FRIDAY && current == dayOfMonth - 1) || // dayOfMonth is a Saturday, so Friday before
|
||||
(dayOfWeek == DayOfWeek.MONDAY && current == dayOfMonth + 1) || // dayOfMonth is a Sunday, so Monday after
|
||||
(dayOfWeek == DayOfWeek.MONDAY && dayOfMonth == 1 && current == 3)) { // dayOfMonth is Saturday 1st, so Monday 3rd
|
||||
return temporal;
|
||||
}
|
||||
int count = 0;
|
||||
@@ -332,6 +336,7 @@ final class QuartzCronField extends CronField {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public <T extends Temporal & Comparable<? super T>> T nextOrSame(T temporal) {
|
||||
T result = adjust(temporal);
|
||||
@@ -348,7 +353,6 @@ final class QuartzCronField extends CronField {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T extends Temporal & Comparable<? super T>> T adjust(T temporal) {
|
||||
@@ -356,27 +360,20 @@ final class QuartzCronField extends CronField {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object other) {
|
||||
return (this == other || (other instanceof QuartzCronField that &&
|
||||
type() == that.type() && this.value.equals(that.value)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.value.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof QuartzCronField other)) {
|
||||
return false;
|
||||
}
|
||||
return type() == other.type() &&
|
||||
this.value.equals(other.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return type() + " '" + this.value + "'";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
+11
-29
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -220,16 +220,16 @@ class AspectJAutoProxyCreatorTests {
|
||||
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);
|
||||
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(testBean2.getClass().getInterfaces()).containsExactlyInAnyOrder(
|
||||
Factory.class, SpringProxy.class, Advised.class);
|
||||
}
|
||||
|
||||
assertThat(testBean1.getClass()).isSameAs(testBean2.getClass());
|
||||
@@ -344,8 +344,8 @@ class AspectJAutoProxyCreatorTests {
|
||||
Supplier<String> supplier = context.getBean(Supplier.class);
|
||||
assertThat(AopUtils.isAopProxy(supplier)).as("AOP proxy").isTrue();
|
||||
assertThat(AopUtils.isJdkDynamicProxy(supplier)).as("JDK Dynamic proxy").isTrue();
|
||||
assertThat(supplier.getClass().getInterfaces())
|
||||
.containsExactlyInAnyOrder(Supplier.class, SpringProxy.class, Advised.class, DecoratingProxy.class);
|
||||
assertThat(supplier.getClass().getInterfaces()).containsExactlyInAnyOrder(
|
||||
Supplier.class, SpringProxy.class, Advised.class, DecoratingProxy.class);
|
||||
assertThat(supplier.get()).isEqualTo("advised: lambda");
|
||||
}
|
||||
}
|
||||
@@ -357,26 +357,14 @@ class AspectJAutoProxyCreatorTests {
|
||||
MessageGenerator messageGenerator = context.getBean(MessageGenerator.class);
|
||||
assertThat(AopUtils.isAopProxy(messageGenerator)).as("AOP proxy").isTrue();
|
||||
assertThat(AopUtils.isJdkDynamicProxy(messageGenerator)).as("JDK Dynamic proxy").isTrue();
|
||||
assertThat(messageGenerator.getClass().getInterfaces())
|
||||
.containsExactlyInAnyOrder(MessageGenerator.class, Mixin.class, SpringProxy.class, Advised.class, DecoratingProxy.class);
|
||||
assertThat(messageGenerator.getClass().getInterfaces()).containsExactlyInAnyOrder(
|
||||
MessageGenerator.class, Mixin.class, SpringProxy.class, Advised.class, DecoratingProxy.class);
|
||||
assertThat(messageGenerator.generateMessage()).isEqualTo("mixin: lambda");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link ClassPathXmlApplicationContext} for the file ending in <var>fileSuffix</var>.
|
||||
*/
|
||||
private ClassPathXmlApplicationContext newContext(String fileSuffix) {
|
||||
return new ClassPathXmlApplicationContext(qName(fileSuffix), getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the relatively qualified name for <var>fileSuffix</var>.
|
||||
* e.g. for a fileSuffix='foo.xml', this method will return
|
||||
* 'AspectJAutoProxyCreatorTests-foo.xml'
|
||||
*/
|
||||
private String qName(String fileSuffix) {
|
||||
return String.format("%s-%s", getClass().getSimpleName(), fileSuffix);
|
||||
return new ClassPathXmlApplicationContext(getClass().getSimpleName() + "-" + fileSuffix, getClass());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -416,7 +404,6 @@ class DummyAspectWithParameter {
|
||||
public Object test(ProceedingJoinPoint pjp, int age) throws Throwable {
|
||||
return pjp.proceed();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class DummyFactoryBean implements FactoryBean<Object> {
|
||||
@@ -435,7 +422,6 @@ class DummyFactoryBean implements FactoryBean<Object> {
|
||||
public boolean isSingleton() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Aspect
|
||||
@@ -591,7 +577,6 @@ class UnreliableBean {
|
||||
}
|
||||
return this.calls;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@@ -607,7 +592,6 @@ class TestBeanAdvisor extends StaticMethodMatcherPointcutAdvisor {
|
||||
public boolean matches(Method method, @Nullable Class<?> targetClass) {
|
||||
return ITestBean.class.isAssignableFrom(targetClass);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
abstract class AbstractProxyTargetClassConfig {
|
||||
@@ -661,6 +645,7 @@ class PerTargetProxyTargetClassTrueConfig {
|
||||
|
||||
@FunctionalInterface
|
||||
interface MessageGenerator {
|
||||
|
||||
String generateMessage();
|
||||
}
|
||||
|
||||
@@ -678,7 +663,6 @@ class MixinIntroductionInterceptor implements IntroductionInterceptor {
|
||||
public boolean implementsInterface(Class<?> intf) {
|
||||
return Mixin.class.isAssignableFrom(intf);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@@ -708,7 +692,6 @@ class MixinAdvisor extends AbstractPointcutAdvisor implements IntroductionAdviso
|
||||
public void validateInterfaces() {
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
abstract class AbstractMixinConfig {
|
||||
@@ -722,7 +705,6 @@ abstract class AbstractMixinConfig {
|
||||
MixinAdvisor mixinAdvisor() {
|
||||
return new MixinAdvisor();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
|
||||
+13
-14
@@ -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() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -39,7 +39,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
*/
|
||||
public class ScopedProxyTests {
|
||||
class ScopedProxyTests {
|
||||
|
||||
private static final Class<?> CLASS = ScopedProxyTests.class;
|
||||
private static final String CLASSNAME = CLASS.getSimpleName();
|
||||
@@ -51,27 +51,24 @@ public class ScopedProxyTests {
|
||||
|
||||
|
||||
@Test // SPR-2108
|
||||
public void testProxyAssignable() throws Exception {
|
||||
void testProxyAssignable() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(MAP_CONTEXT);
|
||||
Object baseMap = bf.getBean("singletonMap");
|
||||
boolean condition = baseMap instanceof Map;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(baseMap instanceof Map).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleProxy() throws Exception {
|
||||
void testSimpleProxy() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(MAP_CONTEXT);
|
||||
Object simpleMap = bf.getBean("simpleMap");
|
||||
boolean condition1 = simpleMap instanceof Map;
|
||||
assertThat(condition1).isTrue();
|
||||
boolean condition = simpleMap instanceof HashMap;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(simpleMap instanceof Map).isTrue();
|
||||
assertThat(simpleMap instanceof HashMap).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScopedOverride() throws Exception {
|
||||
void testScopedOverride() {
|
||||
GenericApplicationContext ctx = new GenericApplicationContext();
|
||||
new XmlBeanDefinitionReader(ctx).loadBeanDefinitions(OVERRIDE_CONTEXT);
|
||||
SimpleMapScope scope = new SimpleMapScope();
|
||||
@@ -87,7 +84,7 @@ public class ScopedProxyTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJdkScopedProxy() throws Exception {
|
||||
void testJdkScopedProxy() throws Exception {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(TESTBEAN_CONTEXT);
|
||||
bf.setSerializationId("X");
|
||||
@@ -97,8 +94,7 @@ public class ScopedProxyTests {
|
||||
ITestBean bean = (ITestBean) bf.getBean("testBean");
|
||||
assertThat(bean).isNotNull();
|
||||
assertThat(AopUtils.isJdkDynamicProxy(bean)).isTrue();
|
||||
boolean condition1 = bean instanceof ScopedObject;
|
||||
assertThat(condition1).isTrue();
|
||||
assertThat(bean instanceof ScopedObject).isTrue();
|
||||
ScopedObject scoped = (ScopedObject) bean;
|
||||
assertThat(scoped.getTargetObject().getClass()).isEqualTo(TestBean.class);
|
||||
bean.setAge(101);
|
||||
@@ -110,8 +106,7 @@ public class ScopedProxyTests {
|
||||
assertThat(deserialized).isNotNull();
|
||||
assertThat(AopUtils.isJdkDynamicProxy(deserialized)).isTrue();
|
||||
assertThat(bean.getAge()).isEqualTo(101);
|
||||
boolean condition = deserialized instanceof ScopedObject;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(deserialized instanceof ScopedObject).isTrue();
|
||||
ScopedObject scopedDeserialized = (ScopedObject) deserialized;
|
||||
assertThat(scopedDeserialized.getTargetObject().getClass()).isEqualTo(TestBean.class);
|
||||
|
||||
@@ -119,7 +114,7 @@ public class ScopedProxyTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCglibScopedProxy() throws Exception {
|
||||
void testCglibScopedProxy() throws Exception {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(LIST_CONTEXT);
|
||||
bf.setSerializationId("Y");
|
||||
@@ -128,8 +123,7 @@ public class ScopedProxyTests {
|
||||
|
||||
TestBean tb = (TestBean) bf.getBean("testBean");
|
||||
assertThat(AopUtils.isCglibProxy(tb.getFriends())).isTrue();
|
||||
boolean condition1 = tb.getFriends() instanceof ScopedObject;
|
||||
assertThat(condition1).isTrue();
|
||||
assertThat(tb.getFriends() instanceof ScopedObject).isTrue();
|
||||
ScopedObject scoped = (ScopedObject) tb.getFriends();
|
||||
assertThat(scoped.getTargetObject().getClass()).isEqualTo(ArrayList.class);
|
||||
tb.getFriends().add("myFriend");
|
||||
@@ -137,12 +131,11 @@ public class ScopedProxyTests {
|
||||
assertThat(scope.getMap().containsKey("scopedTarget.scopedList")).isTrue();
|
||||
assertThat(scope.getMap().get("scopedTarget.scopedList").getClass()).isEqualTo(ArrayList.class);
|
||||
|
||||
ArrayList<?> deserialized = (ArrayList<?>) SerializationTestUtils.serializeAndDeserialize(tb.getFriends());
|
||||
ArrayList<Object> deserialized = (ArrayList<Object>) SerializationTestUtils.serializeAndDeserialize(tb.getFriends());
|
||||
assertThat(deserialized).isNotNull();
|
||||
assertThat(AopUtils.isCglibProxy(deserialized)).isTrue();
|
||||
assertThat(deserialized.contains("myFriend")).isTrue();
|
||||
boolean condition = deserialized instanceof ScopedObject;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(deserialized).contains("myFriend");
|
||||
assertThat(deserialized instanceof ScopedObject).isTrue();
|
||||
ScopedObject scopedDeserialized = (ScopedObject) deserialized;
|
||||
assertThat(scopedDeserialized.getTargetObject().getClass()).isEqualTo(ArrayList.class);
|
||||
|
||||
|
||||
+61
-52
@@ -63,12 +63,12 @@ public class QualifierAnnotationAutowireContextTests {
|
||||
new RootBeanDefinition(QualifiedFieldTestBean.class));
|
||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
|
||||
context::refresh)
|
||||
.satisfies(ex -> {
|
||||
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
|
||||
assertThat(ex.getBeanName()).isEqualTo("autowired");
|
||||
});
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(context::refresh)
|
||||
.satisfies(ex -> {
|
||||
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
|
||||
assertThat(ex.getBeanName()).isEqualTo("autowired");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -81,12 +81,13 @@ public class QualifierAnnotationAutowireContextTests {
|
||||
context.registerBeanDefinition("autowired",
|
||||
new RootBeanDefinition(QualifiedMethodParameterTestBean.class));
|
||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
|
||||
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
|
||||
context::refresh)
|
||||
.satisfies(ex -> {
|
||||
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
|
||||
assertThat(ex.getBeanName()).isEqualTo("autowired");
|
||||
});
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(context::refresh)
|
||||
.satisfies(ex -> {
|
||||
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
|
||||
assertThat(ex.getBeanName()).isEqualTo("autowired");
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@@ -100,9 +101,10 @@ public class QualifierAnnotationAutowireContextTests {
|
||||
context.registerBeanDefinition("autowired",
|
||||
new RootBeanDefinition(QualifiedConstructorArgumentTestBean.class));
|
||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
|
||||
assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(
|
||||
context::refresh)
|
||||
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("autowired"));
|
||||
|
||||
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
|
||||
.isThrownBy(context::refresh)
|
||||
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("autowired"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -205,12 +207,13 @@ public class QualifierAnnotationAutowireContextTests {
|
||||
context.registerBeanDefinition("autowired",
|
||||
new RootBeanDefinition(QualifiedFieldTestBean.class));
|
||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
|
||||
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
|
||||
context::refresh)
|
||||
.satisfies(ex -> {
|
||||
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
|
||||
assertThat(ex.getBeanName()).isEqualTo("autowired");
|
||||
});
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(context::refresh)
|
||||
.satisfies(ex -> {
|
||||
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
|
||||
assertThat(ex.getBeanName()).isEqualTo("autowired");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -227,12 +230,13 @@ public class QualifierAnnotationAutowireContextTests {
|
||||
context.registerBeanDefinition("autowired",
|
||||
new RootBeanDefinition(QualifiedMethodParameterTestBean.class));
|
||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
|
||||
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
|
||||
context::refresh)
|
||||
.satisfies(ex -> {
|
||||
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
|
||||
assertThat(ex.getBeanName()).isEqualTo("autowired");
|
||||
});
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(context::refresh)
|
||||
.satisfies(ex -> {
|
||||
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
|
||||
assertThat(ex.getBeanName()).isEqualTo("autowired");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -249,9 +253,10 @@ public class QualifierAnnotationAutowireContextTests {
|
||||
context.registerBeanDefinition("autowired",
|
||||
new RootBeanDefinition(QualifiedConstructorArgumentTestBean.class));
|
||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
|
||||
assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(
|
||||
context::refresh)
|
||||
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("autowired"));
|
||||
|
||||
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
|
||||
.isThrownBy(context::refresh)
|
||||
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("autowired"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -374,12 +379,13 @@ public class QualifierAnnotationAutowireContextTests {
|
||||
context.registerBeanDefinition("autowired",
|
||||
new RootBeanDefinition(QualifiedFieldWithDefaultValueTestBean.class));
|
||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
|
||||
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
|
||||
context::refresh)
|
||||
.satisfies(ex -> {
|
||||
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
|
||||
assertThat(ex.getBeanName()).isEqualTo("autowired");
|
||||
});
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(context::refresh)
|
||||
.satisfies(ex -> {
|
||||
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
|
||||
assertThat(ex.getBeanName()).isEqualTo("autowired");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -451,12 +457,13 @@ public class QualifierAnnotationAutowireContextTests {
|
||||
context.registerBeanDefinition("autowired",
|
||||
new RootBeanDefinition(QualifiedFieldWithMultipleAttributesTestBean.class));
|
||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
|
||||
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
|
||||
context::refresh)
|
||||
.satisfies(ex -> {
|
||||
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
|
||||
assertThat(ex.getBeanName()).isEqualTo("autowired");
|
||||
});
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(context::refresh)
|
||||
.satisfies(ex -> {
|
||||
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
|
||||
assertThat(ex.getBeanName()).isEqualTo("autowired");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -507,12 +514,13 @@ public class QualifierAnnotationAutowireContextTests {
|
||||
context.registerBeanDefinition("autowired",
|
||||
new RootBeanDefinition(QualifiedFieldWithMultipleAttributesTestBean.class));
|
||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
|
||||
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
|
||||
context::refresh)
|
||||
.satisfies(ex -> {
|
||||
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
|
||||
assertThat(ex.getBeanName()).isEqualTo("autowired");
|
||||
});
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(context::refresh)
|
||||
.satisfies(ex -> {
|
||||
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
|
||||
assertThat(ex.getBeanName()).isEqualTo("autowired");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -574,9 +582,10 @@ public class QualifierAnnotationAutowireContextTests {
|
||||
context.registerBeanDefinition("autowired",
|
||||
new RootBeanDefinition(QualifiedConstructorArgumentWithBaseQualifierNonDefaultValueTestBean.class));
|
||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
|
||||
assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(
|
||||
context::refresh)
|
||||
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("autowired"));
|
||||
|
||||
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
|
||||
.isThrownBy(context::refresh)
|
||||
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("autowired"));
|
||||
}
|
||||
|
||||
|
||||
@@ -752,7 +761,7 @@ public class QualifierAnnotationAutowireContextTests {
|
||||
@Qualifier
|
||||
@interface TestQualifierWithMultipleAttributes {
|
||||
|
||||
String value() default "default";
|
||||
String[] value() default "default";
|
||||
|
||||
int number();
|
||||
}
|
||||
|
||||
+8
-6
@@ -58,9 +58,10 @@ public class QualifierAnnotationTests {
|
||||
BeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
|
||||
reader.loadBeanDefinitions(CONFIG_LOCATION);
|
||||
context.registerSingleton("testBean", NonQualifiedTestBean.class);
|
||||
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
|
||||
context::refresh)
|
||||
.withMessageContaining("found 6");
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(context::refresh)
|
||||
.withMessageContaining("found 6");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -191,9 +192,10 @@ public class QualifierAnnotationTests {
|
||||
BeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
|
||||
reader.loadBeanDefinitions(CONFIG_LOCATION);
|
||||
context.registerSingleton("testBean", QualifiedByAttributesTestBean.class);
|
||||
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
|
||||
context::refresh)
|
||||
.withMessageContaining("found 6");
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(context::refresh)
|
||||
.withMessageContaining("found 6");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+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
+1
-1
@@ -54,7 +54,7 @@ class EnableCachingIntegrationTests {
|
||||
|
||||
|
||||
@AfterEach
|
||||
public void closeContext() {
|
||||
void closeContext() {
|
||||
if (this.context != null) {
|
||||
this.context.close();
|
||||
}
|
||||
|
||||
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"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
+6
-6
@@ -130,8 +130,8 @@ class PropertySourceAnnotationTests {
|
||||
@Test
|
||||
void withUnresolvablePlaceholder() {
|
||||
assertThatExceptionOfType(BeanDefinitionStoreException.class)
|
||||
.isThrownBy(() -> new AnnotationConfigApplicationContext(ConfigWithUnresolvablePlaceholder.class))
|
||||
.withCauseInstanceOf(IllegalArgumentException.class);
|
||||
.isThrownBy(() -> new AnnotationConfigApplicationContext(ConfigWithUnresolvablePlaceholder.class))
|
||||
.withCauseInstanceOf(IllegalArgumentException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -162,8 +162,8 @@ class PropertySourceAnnotationTests {
|
||||
@Test
|
||||
void withEmptyResourceLocations() {
|
||||
assertThatExceptionOfType(BeanDefinitionStoreException.class)
|
||||
.isThrownBy(() -> new AnnotationConfigApplicationContext(ConfigWithEmptyResourceLocations.class))
|
||||
.withCauseInstanceOf(IllegalArgumentException.class);
|
||||
.isThrownBy(() -> new AnnotationConfigApplicationContext(ConfigWithEmptyResourceLocations.class))
|
||||
.withCauseInstanceOf(IllegalArgumentException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -245,8 +245,8 @@ class PropertySourceAnnotationTests {
|
||||
@Test
|
||||
void withMissingPropertySource() {
|
||||
assertThatExceptionOfType(BeanDefinitionStoreException.class)
|
||||
.isThrownBy(() -> new AnnotationConfigApplicationContext(ConfigWithMissingPropertySource.class))
|
||||
.withCauseInstanceOf(FileNotFoundException.class);
|
||||
.isThrownBy(() -> new AnnotationConfigApplicationContext(ConfigWithMissingPropertySource.class))
|
||||
.withCauseInstanceOf(FileNotFoundException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+11
-11
@@ -126,16 +126,16 @@ class GenericApplicationContextTests {
|
||||
|
||||
assertThat(context.getBean(String.class)).isSameAs(context.getBean("testBean"));
|
||||
assertThat(context.getAutowireCapableBeanFactory().getBean(String.class))
|
||||
.isSameAs(context.getAutowireCapableBeanFactory().getBean("testBean"));
|
||||
.isSameAs(context.getAutowireCapableBeanFactory().getBean("testBean"));
|
||||
|
||||
context.close();
|
||||
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> context.getBean(String.class));
|
||||
.isThrownBy(() -> context.getBean(String.class));
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> context.getAutowireCapableBeanFactory().getBean(String.class));
|
||||
.isThrownBy(() -> context.getAutowireCapableBeanFactory().getBean(String.class));
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> context.getAutowireCapableBeanFactory().getBean("testBean"));
|
||||
.isThrownBy(() -> context.getAutowireCapableBeanFactory().getBean("testBean"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -236,9 +236,9 @@ class GenericApplicationContextTests {
|
||||
assertThat(context.getBeanNamesForType(BeanB.class)).containsExactly("b");
|
||||
assertThat(context.getBeanNamesForType(BeanC.class)).containsExactly("c");
|
||||
assertThat(context.getBeansOfType(BeanA.class)).isEmpty();
|
||||
assertThat(context.getBeansOfType(BeanB.class).values().iterator().next())
|
||||
assertThat(context.getBeansOfType(BeanB.class).values()).element(0)
|
||||
.isSameAs(context.getBean(BeanB.class));
|
||||
assertThat(context.getBeansOfType(BeanC.class).values().iterator().next())
|
||||
assertThat(context.getBeansOfType(BeanC.class).values()).element(0)
|
||||
.isSameAs(context.getBean(BeanC.class));
|
||||
}
|
||||
|
||||
@@ -281,8 +281,8 @@ class GenericApplicationContextTests {
|
||||
// java.nio.file.InvalidPathException: Illegal char <:> at index 4: ping:foo
|
||||
if (resourceLoader instanceof FileSystemResourceLoader && OS.WINDOWS.isCurrentOs()) {
|
||||
assertThatExceptionOfType(InvalidPathException.class)
|
||||
.isThrownBy(() -> context.getResource(pingLocation))
|
||||
.withMessageContaining(pingLocation);
|
||||
.isThrownBy(() -> context.getResource(pingLocation))
|
||||
.withMessageContaining(pingLocation);
|
||||
}
|
||||
else {
|
||||
resource = context.getResource(pingLocation);
|
||||
@@ -297,8 +297,8 @@ class GenericApplicationContextTests {
|
||||
assertThat(resource).isInstanceOf(FileUrlResource.class);
|
||||
resource = context.getResource(pingLocation);
|
||||
assertThat(resource).asInstanceOf(type(ByteArrayResource.class))
|
||||
.extracting(bar -> new String(bar.getByteArray(), UTF_8))
|
||||
.isEqualTo("pong:foo");
|
||||
.extracting(bar -> new String(bar.getByteArray(), UTF_8))
|
||||
.isEqualTo("pong:foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -536,7 +536,7 @@ class GenericApplicationContextTests {
|
||||
}
|
||||
}
|
||||
|
||||
static class BeanB implements ApplicationContextAware {
|
||||
static class BeanB implements ApplicationContextAware {
|
||||
|
||||
ApplicationContext applicationContext;
|
||||
|
||||
|
||||
+35
-1
@@ -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.
|
||||
@@ -16,9 +16,12 @@
|
||||
|
||||
package org.springframework.ejb.config;
|
||||
|
||||
import javax.naming.NoInitialContextException;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
@@ -29,6 +32,7 @@ import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.jndi.JndiObjectFactoryBean;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* @author Rob Harrop
|
||||
@@ -93,6 +97,10 @@ public class JeeNamespaceHandlerTests {
|
||||
BeanDefinition beanDefinition = this.beanFactory.getMergedBeanDefinition("simpleLocalEjb");
|
||||
assertThat(beanDefinition.getBeanClassName()).isEqualTo(JndiObjectFactoryBean.class.getName());
|
||||
assertPropertyValue(beanDefinition, "jndiName", "ejb/MyLocalBean");
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(() -> this.beanFactory.getBean("simpleLocalEjb"))
|
||||
.withCauseInstanceOf(NoInitialContextException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -100,6 +108,32 @@ public class JeeNamespaceHandlerTests {
|
||||
BeanDefinition beanDefinition = this.beanFactory.getMergedBeanDefinition("simpleRemoteEjb");
|
||||
assertThat(beanDefinition.getBeanClassName()).isEqualTo(JndiObjectFactoryBean.class.getName());
|
||||
assertPropertyValue(beanDefinition, "jndiName", "ejb/MyRemoteBean");
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(() -> this.beanFactory.getBean("simpleRemoteEjb"))
|
||||
.withCauseInstanceOf(NoInitialContextException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComplexLocalSlsb() {
|
||||
BeanDefinition beanDefinition = this.beanFactory.getMergedBeanDefinition("complexLocalEjb");
|
||||
assertThat(beanDefinition.getBeanClassName()).isEqualTo(JndiObjectFactoryBean.class.getName());
|
||||
assertPropertyValue(beanDefinition, "jndiName", "ejb/MyLocalBean");
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(() -> this.beanFactory.getBean("complexLocalEjb"))
|
||||
.withCauseInstanceOf(NoInitialContextException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComplexRemoteSlsb() {
|
||||
BeanDefinition beanDefinition = this.beanFactory.getMergedBeanDefinition("complexRemoteEjb");
|
||||
assertThat(beanDefinition.getBeanClassName()).isEqualTo(JndiObjectFactoryBean.class.getName());
|
||||
assertPropertyValue(beanDefinition, "jndiName", "ejb/MyRemoteBean");
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(() -> this.beanFactory.getBean("complexRemoteEjb"))
|
||||
.withCauseInstanceOf(NoInitialContextException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.jmx.export.naming;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.management.MalformedObjectNameException;
|
||||
import javax.management.ObjectName;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.entry;
|
||||
|
||||
|
||||
/**
|
||||
* Tests for {@link MetadataNamingStrategy}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class MetadataNamingStrategyTests {
|
||||
|
||||
private static final TestBean TEST_BEAN = new TestBean();
|
||||
|
||||
private final MetadataNamingStrategy strategy;
|
||||
|
||||
MetadataNamingStrategyTests() {
|
||||
this.strategy = new MetadataNamingStrategy();
|
||||
this.strategy.setDefaultDomain("com.example");
|
||||
this.strategy.setAttributeSource(new AnnotationJmxAttributeSource());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getObjectNameWhenBeanNameIsSimple() throws MalformedObjectNameException {
|
||||
ObjectName name = this.strategy.getObjectName(TEST_BEAN, "myBean");
|
||||
assertThat(name.getDomain()).isEqualTo("com.example");
|
||||
assertThat(name).satisfies(hasDefaultProperties(TEST_BEAN, "myBean"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getObjectNameWhenBeanNameIsValidObjectName() throws MalformedObjectNameException {
|
||||
ObjectName name = this.strategy.getObjectName(TEST_BEAN, "com.another:name=myBean");
|
||||
assertThat(name.getDomain()).isEqualTo("com.another");
|
||||
assertThat(name.getKeyPropertyList()).containsOnly(entry("name", "myBean"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getObjectNameWhenBeanNamContainsComma() throws MalformedObjectNameException {
|
||||
ObjectName name = this.strategy.getObjectName(TEST_BEAN, "myBean,");
|
||||
assertThat(name).satisfies(hasDefaultProperties(TEST_BEAN, "\"myBean,\""));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getObjectNameWhenBeanNamContainsEquals() throws MalformedObjectNameException {
|
||||
ObjectName name = this.strategy.getObjectName(TEST_BEAN, "my=Bean");
|
||||
assertThat(name).satisfies(hasDefaultProperties(TEST_BEAN, "\"my=Bean\""));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getObjectNameWhenBeanNamContainsColon() throws MalformedObjectNameException {
|
||||
ObjectName name = this.strategy.getObjectName(TEST_BEAN, "my:Bean");
|
||||
assertThat(name).satisfies(hasDefaultProperties(TEST_BEAN, "\"my:Bean\""));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getObjectNameWhenBeanNamContainsQuote() throws MalformedObjectNameException {
|
||||
ObjectName name = this.strategy.getObjectName(TEST_BEAN, "\"myBean\"");
|
||||
assertThat(name).satisfies(hasDefaultProperties(TEST_BEAN, "\"\\\"myBean\\\"\""));
|
||||
}
|
||||
|
||||
private Consumer<ObjectName> hasDefaultProperties(Object instance, String expectedName) {
|
||||
return objectName -> assertThat(objectName.getKeyPropertyList()).containsOnly(
|
||||
entry("type", ClassUtils.getShortName(instance.getClass())),
|
||||
entry("name", expectedName));
|
||||
}
|
||||
|
||||
static class TestBean {}
|
||||
|
||||
}
|
||||
+14
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -35,27 +35,32 @@ class BitsCronFieldTests {
|
||||
@Test
|
||||
void parse() {
|
||||
assertThat(BitsCronField.parseSeconds("42")).has(clearRange(0, 41)).has(set(42)).has(clearRange(43, 59));
|
||||
assertThat(BitsCronField.parseSeconds("0-4,8-12")).has(setRange(0, 4)).has(clearRange(5,7)).has(setRange(8, 12)).has(clearRange(13,59));
|
||||
assertThat(BitsCronField.parseSeconds("57/2")).has(clearRange(0, 56)).has(set(57)).has(clear(58)).has(set(59));
|
||||
assertThat(BitsCronField.parseSeconds("0-4,8-12")).has(setRange(0, 4)).has(clearRange(5,7))
|
||||
.has(setRange(8, 12)).has(clearRange(13,59));
|
||||
assertThat(BitsCronField.parseSeconds("57/2")).has(clearRange(0, 56)).has(set(57))
|
||||
.has(clear(58)).has(set(59));
|
||||
|
||||
assertThat(BitsCronField.parseMinutes("30")).has(set(30)).has(clearRange(1, 29)).has(clearRange(31, 59));
|
||||
|
||||
assertThat(BitsCronField.parseHours("23")).has(set(23)).has(clearRange(0, 23));
|
||||
assertThat(BitsCronField.parseHours("0-23/2")).has(set(0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22)).has(clear(1,3,5,7,9,11,13,15,17,19,21,23));
|
||||
assertThat(BitsCronField.parseHours("0-23/2")).has(set(0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22))
|
||||
.has(clear(1,3,5,7,9,11,13,15,17,19,21,23));
|
||||
|
||||
assertThat(BitsCronField.parseDaysOfMonth("1")).has(set(1)).has(clearRange(2, 31));
|
||||
|
||||
assertThat(BitsCronField.parseMonth("1")).has(set(1)).has(clearRange(2, 12));
|
||||
|
||||
assertThat(BitsCronField.parseDaysOfWeek("0")).has(set(7, 7)).has(clearRange(0, 6));
|
||||
|
||||
assertThat(BitsCronField.parseDaysOfWeek("7-5")).has(clear(0)).has(setRange(1, 5)).has(clear(6)).has(set(7));
|
||||
assertThat(BitsCronField.parseDaysOfWeek("7-5")).has(clear(0)).has(setRange(1, 5))
|
||||
.has(clear(6)).has(set(7));
|
||||
}
|
||||
|
||||
@Test
|
||||
void parseLists() {
|
||||
assertThat(BitsCronField.parseSeconds("15,30")).has(set(15, 30)).has(clearRange(1, 15)).has(clearRange(31, 59));
|
||||
assertThat(BitsCronField.parseMinutes("1,2,5,9")).has(set(1, 2, 5, 9)).has(clear(0)).has(clearRange(3, 4)).has(clearRange(6, 8)).has(clearRange(10, 59));
|
||||
assertThat(BitsCronField.parseSeconds("15,30")).has(set(15, 30)).has(clearRange(1, 15))
|
||||
.has(clearRange(31, 59));
|
||||
assertThat(BitsCronField.parseMinutes("1,2,5,9")).has(set(1, 2, 5, 9)).has(clear(0))
|
||||
.has(clearRange(3, 4)).has(clearRange(6, 8)).has(clearRange(10, 59));
|
||||
assertThat(BitsCronField.parseHours("1,2,3")).has(set(1, 2, 3)).has(clearRange(4, 23));
|
||||
assertThat(BitsCronField.parseDaysOfMonth("1,2,3")).has(set(1, 2, 3)).has(clearRange(4, 31));
|
||||
assertThat(BitsCronField.parseMonth("1,2,3")).has(set(1, 2, 3)).has(clearRange(4, 12));
|
||||
@@ -107,6 +112,7 @@ class BitsCronFieldTests {
|
||||
.has(clear(0)).has(setRange(1, 7));
|
||||
}
|
||||
|
||||
|
||||
private static Condition<BitsCronField> set(int... indices) {
|
||||
return new Condition<>(String.format("set bits %s", Arrays.toString(indices))) {
|
||||
@Override
|
||||
|
||||
+3
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -848,6 +848,7 @@ class CronTriggerTests {
|
||||
assertThat(nextExecutionTime).isEqualTo(this.calendar.getTime());
|
||||
}
|
||||
|
||||
|
||||
private static void roundup(Calendar calendar) {
|
||||
calendar.add(Calendar.SECOND, 1);
|
||||
calendar.set(Calendar.MILLISECOND, 0);
|
||||
@@ -861,9 +862,7 @@ class CronTriggerTests {
|
||||
}
|
||||
|
||||
private static TriggerContext getTriggerContext(Date lastCompletionTime) {
|
||||
SimpleTriggerContext context = new SimpleTriggerContext();
|
||||
context.update(null, null, lastCompletionTime);
|
||||
return context;
|
||||
return new SimpleTriggerContext(null, null, lastCompletionTime);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+42
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -28,6 +28,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
* Unit tests for {@link QuartzCronField}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
class QuartzCronFieldTests {
|
||||
|
||||
@@ -71,6 +72,46 @@ class QuartzCronFieldTests {
|
||||
assertThat(field.nextOrSame(last)).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
void dayOfWeek_0(){
|
||||
// third Sunday (0) of the month
|
||||
QuartzCronField field = QuartzCronField.parseDaysOfWeek("0#3");
|
||||
|
||||
LocalDate last = LocalDate.of(2024, 1, 1);
|
||||
LocalDate expected = LocalDate.of(2024, 1, 21);
|
||||
assertThat(field.nextOrSame(last)).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
void dayOfWeek_1(){
|
||||
// third Monday (1) of the month
|
||||
QuartzCronField field = QuartzCronField.parseDaysOfWeek("1#3");
|
||||
|
||||
LocalDate last = LocalDate.of(2024, 1, 1);
|
||||
LocalDate expected = LocalDate.of(2024, 1, 15);
|
||||
assertThat(field.nextOrSame(last)).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
void dayOfWeek_2(){
|
||||
// third Tuesday (2) of the month
|
||||
QuartzCronField field = QuartzCronField.parseDaysOfWeek("2#3");
|
||||
|
||||
LocalDate last = LocalDate.of(2024, 1, 1);
|
||||
LocalDate expected = LocalDate.of(2024, 1, 16);
|
||||
assertThat(field.nextOrSame(last)).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
void dayOfWeek_7() {
|
||||
// third Sunday (7 as alternative to 0) of the month
|
||||
QuartzCronField field = QuartzCronField.parseDaysOfWeek("7#3");
|
||||
|
||||
LocalDate last = LocalDate.of(2024, 1, 1);
|
||||
LocalDate expected = LocalDate.of(2024, 1, 21);
|
||||
assertThat(field.nextOrSame(last)).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
void invalidValues() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> QuartzCronField.parseDaysOfMonth(""));
|
||||
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.scheduling.annotation
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.mockito.BDDMockito.given
|
||||
import org.mockito.Mockito
|
||||
|
||||
/**
|
||||
* Kotlin tests for [AnnotationAsyncExecutionInterceptor].
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
class AnnotationAsyncExecutionInterceptorKotlinTests {
|
||||
|
||||
@Test
|
||||
fun nullableUnitReturnValue() {
|
||||
val interceptor = AnnotationAsyncExecutionInterceptor(null)
|
||||
|
||||
class C { @Async fun nullableUnit(): Unit? = null }
|
||||
val invocation = Mockito.mock<MethodInvocation>()
|
||||
given(invocation.method).willReturn(C::class.java.getDeclaredMethod("nullableUnit"))
|
||||
|
||||
Assertions.assertThat(interceptor.invoke(invocation)).isNull()
|
||||
}
|
||||
|
||||
}
|
||||
+12
-21
@@ -3,9 +3,6 @@
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
|
||||
|
||||
<!--
|
||||
Not yet in use: illustration of possible approach
|
||||
-->
|
||||
<bean id="overrideOneMethod" class="org.springframework.beans.factory.xml.OverrideOneMethod">
|
||||
|
||||
<lookup-method name="getPrototypeDependency" bean="jenny"/>
|
||||
@@ -27,39 +24,34 @@
|
||||
|
||||
<lookup-method name="protectedOverrideSingleton" bean="david"/>
|
||||
|
||||
<!--
|
||||
This method is not overloaded, so we don't need to specify any arg types
|
||||
-->
|
||||
<!-- This method is not overloaded, so we don't need to specify any arg types -->
|
||||
<replaced-method name="doSomething" replacer="doSomethingReplacer"/>
|
||||
|
||||
</bean>
|
||||
|
||||
<bean id="replaceVoidMethod" parent="someParent"
|
||||
class="org.springframework.beans.factory.xml.OverrideOneMethodSubclass">
|
||||
<bean id="replaceVoidMethod" parent="someParent" class="org.springframework.beans.factory.xml.OverrideOneMethodSubclass"/>
|
||||
|
||||
<bean id="replaceEchoMethod" class="org.springframework.beans.factory.xml.EchoService">
|
||||
<!-- This method is not overloaded, so we don't need to specify any arg types -->
|
||||
<replaced-method name="echo" replacer="reverseArrayReplacer" />
|
||||
</bean>
|
||||
|
||||
<bean id="reverseReplacer"
|
||||
class="org.springframework.beans.factory.xml.ReverseMethodReplacer"/>
|
||||
<bean id="reverseReplacer" class="org.springframework.beans.factory.xml.ReverseMethodReplacer"/>
|
||||
|
||||
<bean id="fixedReplacer"
|
||||
class="org.springframework.beans.factory.xml.FixedMethodReplacer"/>
|
||||
<bean id="reverseArrayReplacer" class="org.springframework.beans.factory.xml.ReverseArrayMethodReplacer"/>
|
||||
|
||||
<bean id="doSomethingReplacer"
|
||||
class="org.springframework.beans.factory.xml.XmlBeanFactoryTests$DoSomethingReplacer"/>
|
||||
<bean id="fixedReplacer" class="org.springframework.beans.factory.xml.FixedMethodReplacer"/>
|
||||
|
||||
<bean id="serializableReplacer"
|
||||
class="org.springframework.beans.factory.xml.SerializableMethodReplacerCandidate">
|
||||
<bean id="doSomethingReplacer" class="org.springframework.beans.factory.xml.XmlBeanFactoryTests$DoSomethingReplacer"/>
|
||||
|
||||
<bean id="serializableReplacer" class="org.springframework.beans.factory.xml.SerializableMethodReplacerCandidate">
|
||||
<!-- Arbitrary method replacer -->
|
||||
<replaced-method name="replaceMe" replacer="reverseReplacer">
|
||||
<arg-type>String</arg-type>
|
||||
</replaced-method>
|
||||
|
||||
</bean>
|
||||
|
||||
<bean id="jenny" class="org.springframework.beans.testfixture.beans.TestBean"
|
||||
scope="prototype">
|
||||
<bean id="jenny" class="org.springframework.beans.testfixture.beans.TestBean" scope="prototype">
|
||||
<property name="name"><value>Jenny</value></property>
|
||||
<property name="age"><value>30</value></property>
|
||||
<property name="spouse">
|
||||
@@ -68,8 +60,7 @@
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="david" class="org.springframework.beans.testfixture.beans.TestBean"
|
||||
scope="singleton">
|
||||
<bean id="david" class="org.springframework.beans.testfixture.beans.TestBean" scope="singleton">
|
||||
<description>
|
||||
Simple bean, without any collections.
|
||||
</description>
|
||||
|
||||
+32
-5
@@ -40,14 +40,41 @@
|
||||
</util:properties>
|
||||
|
||||
<!-- Local EJB Tests -->
|
||||
<jee:local-slsb id="simpleLocalEjb" jndi-name="ejb/MyLocalBean"/>
|
||||
<jee:local-slsb id="simpleLocalEjb" jndi-name="ejb/MyLocalBean"
|
||||
business-interface="org.springframework.beans.testfixture.beans.ITestBean"/>
|
||||
|
||||
<jee:local-slsb id="complexLocalEjb"
|
||||
jndi-name="ejb/MyLocalBean"
|
||||
business-interface="org.springframework.beans.testfixture.beans.ITestBean"
|
||||
cache-home="true"
|
||||
lookup-home-on-startup="true"
|
||||
resource-ref="true">
|
||||
<jee:environment>foo=bar</jee:environment>
|
||||
</jee:local-slsb>
|
||||
|
||||
<!-- Remote EJB Tests -->
|
||||
<jee:remote-slsb id="simpleRemoteEjb" jndi-name="ejb/MyRemoteBean"/>
|
||||
<jee:remote-slsb id="simpleRemoteEjb" jndi-name="ejb/MyRemoteBean"
|
||||
business-interface="org.springframework.beans.testfixture.beans.ITestBean"/>
|
||||
|
||||
<!-- Lazy beans Tests-->
|
||||
<jee:remote-slsb id="complexRemoteEjb"
|
||||
jndi-name="ejb/MyRemoteBean"
|
||||
business-interface="org.springframework.beans.testfixture.beans.ITestBean"
|
||||
cache-home="true"
|
||||
lookup-home-on-startup="true"
|
||||
resource-ref="true"
|
||||
home-interface="org.springframework.beans.testfixture.beans.ITestBean"
|
||||
refresh-home-on-connect-failure="true"
|
||||
cache-session-bean="true">
|
||||
<jee:environment>foo=bar</jee:environment>
|
||||
</jee:remote-slsb>
|
||||
|
||||
<!-- Lazy Lookup Tests-->
|
||||
<jee:jndi-lookup id="lazyDataSource" jndi-name="jdbc/MyDataSource" lazy-init="true"/>
|
||||
<jee:local-slsb id="lazyLocalBean" jndi-name="ejb/MyLocalBean" lazy-init="true"/>
|
||||
<jee:remote-slsb id="lazyRemoteBean" jndi-name="ejb/MyRemoteBean" lazy-init="true"/>
|
||||
|
||||
<jee:local-slsb id="lazyLocalBean" jndi-name="ejb/MyLocalBean"
|
||||
business-interface="org.springframework.beans.testfixture.beans.ITestBean" lazy-init="true"/>
|
||||
|
||||
<jee:remote-slsb id="lazyRemoteBean" jndi-name="ejb/MyRemoteBean"
|
||||
business-interface="org.springframework.beans.testfixture.beans.ITestBean" lazy-init="true"/>
|
||||
|
||||
</beans>
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.springframework.core.io.InputStreamSource;
|
||||
import org.springframework.javapoet.JavaFile;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.function.ThrowingConsumer;
|
||||
|
||||
/**
|
||||
@@ -43,6 +44,7 @@ public interface GeneratedFiles {
|
||||
* @param javaFile the java file to add
|
||||
*/
|
||||
default void addSourceFile(JavaFile javaFile) {
|
||||
validatePackage(javaFile.packageName, javaFile.typeSpec.name);
|
||||
String className = javaFile.packageName + "." + javaFile.typeSpec.name;
|
||||
addSourceFile(className, javaFile::writeTo);
|
||||
}
|
||||
@@ -161,11 +163,20 @@ public interface GeneratedFiles {
|
||||
|
||||
private static String getClassNamePath(String className) {
|
||||
Assert.hasLength(className, "'className' must not be empty");
|
||||
validatePackage(ClassUtils.getPackageName(className), className);
|
||||
Assert.isTrue(isJavaIdentifier(className),
|
||||
"'className' must be a valid identifier, got '" + className + "'");
|
||||
return ClassUtils.convertClassNameToResourcePath(className) + ".java";
|
||||
}
|
||||
|
||||
private static void validatePackage(String packageName, String className) {
|
||||
if (!StringUtils.hasLength(packageName)) {
|
||||
throw new IllegalArgumentException("Could not add '" + className + "', "
|
||||
+ "processing classes in the default package is not supported. "
|
||||
+ "Did you forget to add a package statement?");
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isJavaIdentifier(String className) {
|
||||
char[] chars = className.toCharArray();
|
||||
for (int i = 0; i < chars.length; i++) {
|
||||
|
||||
+8
-2
@@ -195,9 +195,15 @@ public class BindingReflectionHintsRegistrar {
|
||||
}
|
||||
|
||||
private void registerHintsForClassAttributes(ReflectionHints hints, MergedAnnotation<Annotation> annotation) {
|
||||
annotation.getRoot().asMap().values().forEach(value -> {
|
||||
annotation.getRoot().asMap().forEach((key,value) -> {
|
||||
if (value instanceof Class<?> classValue && value != Void.class) {
|
||||
hints.registerType(classValue, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
|
||||
if (key.equals("builder")) {
|
||||
hints.registerType(classValue, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
|
||||
MemberCategory.INVOKE_DECLARED_METHODS);
|
||||
}
|
||||
else {
|
||||
hints.registerType(classValue, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -159,7 +159,7 @@ final class SerializableTypeWrapper {
|
||||
|
||||
/**
|
||||
* Return the source of the type, or {@code null} if not known.
|
||||
* <p>The default implementations returns {@code null}.
|
||||
* <p>The default implementation returns {@code null}.
|
||||
*/
|
||||
@Nullable
|
||||
default Object getSource() {
|
||||
@@ -214,7 +214,12 @@ final class SerializableTypeWrapper {
|
||||
return result;
|
||||
}
|
||||
|
||||
return ReflectionUtils.invokeMethod(method, this.provider.getType(), args);
|
||||
Type type = this.provider.getType();
|
||||
if (type instanceof TypeVariable<?> tv && method.getName().equals("getName")) {
|
||||
// Avoid reflection for common comparison of type variables
|
||||
return tv.getName();
|
||||
}
|
||||
return ReflectionUtils.invokeMethod(method, type, args);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+27
-12
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -757,7 +757,7 @@ public abstract class AnnotationUtils {
|
||||
* Google App Engine's late arrival of {@code TypeNotPresentExceptionProxy} for
|
||||
* {@code Class} values (instead of early {@code Class.getAnnotations() failure}).
|
||||
* <p>This method not failing indicates that {@link #getAnnotationAttributes(Annotation)}
|
||||
* won't failure either (when attempted later on).
|
||||
* won't fail either (when attempted later on).
|
||||
* @param annotation the annotation to validate
|
||||
* @throws IllegalStateException if a declared {@code Class} attribute could not be read
|
||||
* @since 4.3.15
|
||||
@@ -1056,8 +1056,7 @@ public abstract class AnnotationUtils {
|
||||
return null;
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
rethrowAnnotationConfigurationException(ex);
|
||||
handleIntrospectionFailure(annotation.getClass(), ex);
|
||||
handleValueRetrievalFailure(annotation, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1073,14 +1072,18 @@ public abstract class AnnotationUtils {
|
||||
* @return the value returned from the method invocation
|
||||
* @since 5.3.24
|
||||
*/
|
||||
static Object invokeAnnotationMethod(Method method, Object annotation) {
|
||||
@Nullable
|
||||
static Object invokeAnnotationMethod(Method method, @Nullable Object annotation) {
|
||||
if (annotation == null) {
|
||||
return null;
|
||||
}
|
||||
if (Proxy.isProxyClass(annotation.getClass())) {
|
||||
try {
|
||||
InvocationHandler handler = Proxy.getInvocationHandler(annotation);
|
||||
return handler.invoke(annotation, method, null);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// ignore and fall back to reflection below
|
||||
// Ignore and fall back to reflection below
|
||||
}
|
||||
}
|
||||
return ReflectionUtils.invokeMethod(method, annotation);
|
||||
@@ -1114,20 +1117,32 @@ public abstract class AnnotationUtils {
|
||||
* @see #rethrowAnnotationConfigurationException
|
||||
* @see IntrospectionFailureLogger
|
||||
*/
|
||||
static void handleIntrospectionFailure(@Nullable AnnotatedElement element, Throwable ex) {
|
||||
static void handleIntrospectionFailure(AnnotatedElement element, Throwable ex) {
|
||||
rethrowAnnotationConfigurationException(ex);
|
||||
IntrospectionFailureLogger logger = IntrospectionFailureLogger.INFO;
|
||||
boolean meta = false;
|
||||
if (element instanceof Class<?> clazz && Annotation.class.isAssignableFrom(clazz)) {
|
||||
// Meta-annotation or (default) value lookup on an annotation type
|
||||
// Meta-annotation introspection failure
|
||||
logger = IntrospectionFailureLogger.DEBUG;
|
||||
meta = true;
|
||||
}
|
||||
if (logger.isEnabled()) {
|
||||
String message = meta ?
|
||||
"Failed to meta-introspect annotation " :
|
||||
"Failed to introspect annotations on ";
|
||||
logger.log(message + element + ": " + ex);
|
||||
logger.log("Failed to " + (meta ? "meta-introspect annotation " : "introspect annotations on ") +
|
||||
element + ": " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the supplied value retrieval exception.
|
||||
* @param annotation the annotation instance from which to retrieve the value
|
||||
* @param ex the exception that we encountered
|
||||
* @see #handleIntrospectionFailure
|
||||
*/
|
||||
private static void handleValueRetrievalFailure(Annotation annotation, Throwable ex) {
|
||||
rethrowAnnotationConfigurationException(ex);
|
||||
IntrospectionFailureLogger logger = IntrospectionFailureLogger.INFO;
|
||||
if (logger.isEnabled()) {
|
||||
logger.log("Failed to retrieve value from " + annotation + ": " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -336,11 +336,10 @@ abstract class AnnotationsScanner {
|
||||
|
||||
Method[] methods = baseTypeMethodsCache.get(baseType);
|
||||
if (methods == null) {
|
||||
boolean isInterface = baseType.isInterface();
|
||||
methods = isInterface ? baseType.getMethods() : ReflectionUtils.getDeclaredMethods(baseType);
|
||||
methods = ReflectionUtils.getDeclaredMethods(baseType);
|
||||
int cleared = 0;
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
if ((!isInterface && Modifier.isPrivate(methods[i].getModifiers())) ||
|
||||
if (Modifier.isPrivate(methods[i].getModifiers()) ||
|
||||
hasPlainJavaAnnotationsOnly(methods[i]) ||
|
||||
getDeclaredAnnotations(methods[i], false).length == 0) {
|
||||
methods[i] = null;
|
||||
@@ -454,7 +453,7 @@ abstract class AnnotationsScanner {
|
||||
for (int i = 0; i < annotations.length; i++) {
|
||||
Annotation annotation = annotations[i];
|
||||
if (isIgnorable(annotation.annotationType()) ||
|
||||
!AttributeMethods.forAnnotationType(annotation.annotationType()).isValid(annotation)) {
|
||||
!AttributeMethods.forAnnotationType(annotation.annotationType()).canLoad(annotation)) {
|
||||
annotations[i] = null;
|
||||
}
|
||||
else {
|
||||
|
||||
+19
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -45,7 +45,7 @@ final class AttributeMethods {
|
||||
if (m1 != null && m2 != null) {
|
||||
return m1.getName().compareTo(m2.getName());
|
||||
}
|
||||
return m1 != null ? -1 : 1;
|
||||
return (m1 != null ? -1 : 1);
|
||||
};
|
||||
|
||||
|
||||
@@ -87,18 +87,26 @@ final class AttributeMethods {
|
||||
/**
|
||||
* Determine if values from the given annotation can be safely accessed without
|
||||
* causing any {@link TypeNotPresentException TypeNotPresentExceptions}.
|
||||
* <p>This method is designed to cover Google App Engine's late arrival of such
|
||||
* exceptions for {@code Class} values (instead of the more typical early
|
||||
* {@code Class.getAnnotations() failure} on a regular JVM).
|
||||
* @param annotation the annotation to check
|
||||
* @return {@code true} if all values are present
|
||||
* @see #validate(Annotation)
|
||||
*/
|
||||
boolean isValid(Annotation annotation) {
|
||||
boolean canLoad(Annotation annotation) {
|
||||
assertAnnotation(annotation);
|
||||
for (int i = 0; i < size(); i++) {
|
||||
if (canThrowTypeNotPresentException(i)) {
|
||||
try {
|
||||
AnnotationUtils.invokeAnnotationMethod(get(i), annotation);
|
||||
}
|
||||
catch (IllegalStateException ex) {
|
||||
// Plain invocation failure to expose -> leave up to attribute retrieval
|
||||
// (if any) where such invocation failure will be logged eventually.
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// TypeNotPresentException etc. -> annotation type not actually loadable.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -108,13 +116,13 @@ final class AttributeMethods {
|
||||
|
||||
/**
|
||||
* Check if values from the given annotation can be safely accessed without causing
|
||||
* any {@link TypeNotPresentException TypeNotPresentExceptions}. In particular,
|
||||
* this method is designed to cover Google App Engine's late arrival of such
|
||||
* any {@link TypeNotPresentException TypeNotPresentExceptions}.
|
||||
* <p>This method is designed to cover Google App Engine's late arrival of such
|
||||
* exceptions for {@code Class} values (instead of the more typical early
|
||||
* {@code Class.getAnnotations() failure}).
|
||||
* {@code Class.getAnnotations() failure} on a regular JVM).
|
||||
* @param annotation the annotation to validate
|
||||
* @throws IllegalStateException if a declared {@code Class} attribute could not be read
|
||||
* @see #isValid(Annotation)
|
||||
* @see #canLoad(Annotation)
|
||||
*/
|
||||
void validate(Annotation annotation) {
|
||||
assertAnnotation(annotation);
|
||||
@@ -123,6 +131,9 @@ final class AttributeMethods {
|
||||
try {
|
||||
AnnotationUtils.invokeAnnotationMethod(get(i), annotation);
|
||||
}
|
||||
catch (IllegalStateException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new IllegalStateException("Could not obtain annotation attribute value for " +
|
||||
get(i).getName() + " declared on " + annotation.annotationType(), ex);
|
||||
@@ -147,7 +158,7 @@ final class AttributeMethods {
|
||||
@Nullable
|
||||
Method get(String name) {
|
||||
int index = indexOf(name);
|
||||
return index != -1 ? this.attributeMethods[index] : null;
|
||||
return (index != -1 ? this.attributeMethods[index] : null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -475,7 +475,7 @@ public class TypeDescriptor implements Serializable {
|
||||
ObjectUtils.nullSafeEquals(getMapValueTypeDescriptor(), otherDesc.getMapValueTypeDescriptor()));
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
return Arrays.equals(getResolvableType().getGenerics(), otherDesc.getResolvableType().getGenerics());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -152,8 +152,7 @@ public interface Resource extends InputStreamSource {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the contents of this resource as a string, using the specified
|
||||
* charset.
|
||||
* Return the contents of this resource as a string, using the specified charset.
|
||||
* @param charset the charset to use for decoding
|
||||
* @return the contents of this resource as a {@code String}
|
||||
* @throws java.io.FileNotFoundException if the resource cannot be resolved as
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user