Compare commits

..

1 Commits

Author SHA1 Message Date
Spring Builds 66f4509c2c Release v6.0.16 2024-01-11 10:21:01 +00:00
264 changed files with 2592 additions and 4802 deletions
@@ -1,33 +0,0 @@
name: Send notification
description: Sends a Google Chat message as a notification of the job's outcome
inputs:
webhook-url:
description: 'Google Chat Webhook URL'
required: true
status:
description: 'Status of the job'
required: true
build-scan-url:
description: 'URL of the build scan to include in the notification'
run-name:
description: 'Name of the run to include in the notification'
default: ${{ format('{0} {1}', github.ref_name, github.job) }}
runs:
using: composite
steps:
- shell: bash
run: |
echo "BUILD_SCAN=${{ inputs.build-scan-url == '' && ' [build scan unavailable]' || format(' [<{0}|Build Scan>]', inputs.build-scan-url) }}" >> "$GITHUB_ENV"
echo "RUN_URL=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> "$GITHUB_ENV"
- shell: bash
if: ${{ inputs.status == 'success' }}
run: |
curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<${{ env.RUN_URL }}|${{ inputs.run-name }}> was successful ${{ env.BUILD_SCAN }}"}' || true
- shell: bash
if: ${{ inputs.status == 'failure' }}
run: |
curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<users/all> *<${{ env.RUN_URL }}|${{ inputs.run-name }}> failed* ${{ env.BUILD_SCAN }}"}' || true
- shell: bash
if: ${{ inputs.status == 'cancelled' }}
run: |
curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<${{ env.RUN_URL }}|${{ inputs.run-name }}> was cancelled"}' || true
+4 -6
View File
@@ -18,13 +18,11 @@ jobs:
pull-requests: write
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Set up Java
uses: actions/setup-java@v4
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
with:
distribution: 'liberica'
java-version: 17
distribution: 'temurin'
java-version: '17'
- name: Download BackportBot
run: wget https://github.com/spring-io/backport-bot/releases/download/latest/backport-bot-0.0.1-SNAPSHOT.jar
- name: Backport
@@ -1,63 +0,0 @@
name: Build and deploy snapshot
on:
push:
branches:
- 6.0.x
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
build-and-deploy-snapshot:
if: ${{ github.repository == 'spring-projects/spring-framework' }}
name: Build and deploy snapshot
runs-on: ubuntu-latest
steps:
- name: Set up Java
uses: actions/setup-java@v4
with:
distribution: 'liberica'
java-version: 17
- name: Check out code
uses: actions/checkout@v4
- name: Set up Gradle
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
with:
cache-read-only: false
- name: Configure Gradle properties
shell: bash
run: |
mkdir -p $HOME/.gradle
echo 'systemProp.user.name=spring-builds+github' >> $HOME/.gradle/gradle.properties
echo 'systemProp.org.gradle.internal.launcher.welcomeMessageEnabled=false' >> $HOME/.gradle/gradle.properties
echo 'org.gradle.daemon=false' >> $HOME/.gradle/gradle.properties
echo 'org.gradle.daemon=4' >> $HOME/.gradle/gradle.properties
- name: Build and publish
id: build
env:
CI: 'true'
GRADLE_ENTERPRISE_URL: 'https://ge.spring.io'
DEVELOCITY_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
run: ./gradlew -PdeploymentRepository=$(pwd)/deployment-repository build publishAllPublicationsToDeploymentRepository
- name: Deploy
uses: spring-io/artifactory-deploy-action@v0.0.1
with:
uri: 'https://repo.spring.io'
username: ${{ secrets.ARTIFACTORY_USERNAME }}
password: ${{ secrets.ARTIFACTORY_PASSWORD }}
build-name: ${{ format('spring-framework-{0}', github.ref_name)}}
repository: 'libs-snapshot-local'
folder: 'deployment-repository'
signing-key: ${{ secrets.GPG_PRIVATE_KEY }}
signing-passphrase: ${{ secrets.GPG_PASSPHRASE }}
artifact-properties: |
/**/framework-docs-*.zip::zip.name=spring-framework,zip.deployed=false
/**/framework-docs-*-docs.zip::zip.type=docs
/**/framework-docs-*-dist.zip::zip.type=dist
/**/framework-docs-*-schema.zip::zip.type=schema
- name: Send notification
uses: ./.github/actions/send-notification
if: always()
with:
webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
status: ${{ job.status }}
build-scan-url: ${{ steps.build.outputs.build-scan-url }}
run-name: ${{ format('{0} | Linux | Java 17', github.ref_name) }}
-78
View File
@@ -1,78 +0,0 @@
name: CI
on:
push:
branches:
- 6.0.x
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
ci:
if: ${{ github.repository == 'spring-projects/spring-framework' }}
strategy:
matrix:
os:
- id: ubuntu-latest
name: Linux
java:
- version: 17
toolchain: false
- version: 21
toolchain: true
exclude:
- os:
name: Linux
java:
version: 17
name: '${{ matrix.os.name}} | Java ${{ matrix.java.version}}'
runs-on: ${{ matrix.os.id }}
steps:
- name: Set up Java
uses: actions/setup-java@v4
with:
distribution: 'liberica'
java-version: |
${{ matrix.java.version }}
${{ matrix.java.toolchain && '17' || '' }}
- name: Prepare Windows runner
if: ${{ runner.os == 'Windows' }}
run: |
git config --global core.autocrlf true
git config --global core.longPaths true
Stop-Service -name Docker
- name: Check out code
uses: actions/checkout@v4
- name: Set up Gradle
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
with:
cache-read-only: false
- name: Configure Gradle properties
shell: bash
run: |
mkdir -p $HOME/.gradle
echo 'systemProp.user.name=spring-builds+github' >> $HOME/.gradle/gradle.properties
echo 'systemProp.org.gradle.internal.launcher.welcomeMessageEnabled=false' >> $HOME/.gradle/gradle.properties
echo 'org.gradle.daemon=false' >> $HOME/.gradle/gradle.properties
echo 'org.gradle.daemon=4' >> $HOME/.gradle/gradle.properties
- name: Configure toolchain properties
if: ${{ matrix.java.toolchain }}
shell: bash
run: |
echo toolchainVersion=${{ matrix.java.version }} >> $HOME/.gradle/gradle.properties
echo systemProp.org.gradle.java.installations.auto-detect=false >> $HOME/.gradle/gradle.properties
echo systemProp.org.gradle.java.installations.auto-download=false >> $HOME/.gradle/gradle.properties
echo systemProp.org.gradle.java.installations.paths=${{ format('$JAVA_HOME_{0}_X64', matrix.java.version) }} >> $HOME/.gradle/gradle.properties
- name: Build
id: build
env:
CI: 'true'
GRADLE_ENTERPRISE_URL: 'https://ge.spring.io'
DEVELOCITY_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
run: ./gradlew check antora
- name: Send notification
uses: ./.github/actions/send-notification
if: always()
with:
webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
status: ${{ job.status }}
build-scan-url: ${{ steps.build.outputs.build-scan-url }}
run-name: ${{ format('{0} | {1} | Java {2}', github.ref_name, matrix.os.name, matrix.java.version) }}
+6 -8
View File
@@ -1,14 +1,12 @@
name: Deploy Docs
on:
push:
branches:
- 'main'
- '*.x'
- '!gh-pages'
tags:
- 'v*'
branches-ignore: [ gh-pages ]
tags: '**'
repository_dispatch:
types: request-build-reference # legacy
schedule:
- cron: '0 10 * * *' # Once per day at 10am UTC
workflow_dispatch:
permissions:
actions: write
@@ -17,8 +15,8 @@ jobs:
runs-on: ubuntu-latest
if: github.repository_owner == 'spring-projects'
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Checkout
uses: actions/checkout@v3
with:
ref: docs-build
fetch-depth: 1
@@ -9,5 +9,5 @@ jobs:
name: "Validation"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: gradle/wrapper-validation-action@v2
- uses: actions/checkout@v3
- uses: gradle/wrapper-validation-action@v1
+1 -1
View File
@@ -1,3 +1,3 @@
# Enable auto-env through the sdkman_auto_env config
# Add key=value pairs of SDKs to use below
java=17.0.11-librca
java=17.0.8.1-librca
+1 -1
View File
@@ -1,4 +1,4 @@
# <img src="framework-docs/src/docs/spring-framework.png" width="80" height="80"> Spring Framework [![Build Status](https://github.com/spring-projects/spring-framework/actions/workflows/build-and-deploy-snapshot.yml/badge.svg?branch=6.0.x)](https://github.com/spring-projects/spring-framework/actions/workflows/build-and-deploy-snapshot.yml?query=branch%3A6.0.x) [![Revved up by Develocity](https://img.shields.io/badge/Revved%20up%20by-Develocity-06A0CE?logo=Gradle&labelColor=02303A)](https://ge.spring.io/scans?search.rootProjectNames=spring)
# <img src="framework-docs/src/docs/spring-framework.png" width="80" height="80"> Spring Framework [![Build Status](https://ci.spring.io/api/v1/teams/spring-framework/pipelines/spring-framework-6.0.x/jobs/build/badge)](https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-6.0.x?groups=Build") [![Revved up by Gradle Enterprise](https://img.shields.io/badge/Revved%20up%20by-Gradle%20Enterprise-06A0CE?logo=Gradle&labelColor=02303A)](https://ge.spring.io/scans?search.rootProjectNames=spring)
This is the home of the Spring Framework: the foundation for all [Spring projects](https://spring.io/projects). Collectively the Spring Framework and the family of Spring projects are often referred to simply as "Spring".
+4 -3
View File
@@ -3,12 +3,12 @@ plugins {
id 'io.freefair.aspectj' version '8.0.1' apply false
// kotlinVersion is managed in gradle.properties
id 'org.jetbrains.kotlin.plugin.serialization' version "${kotlinVersion}" apply false
id 'org.jetbrains.dokka' version '1.8.20'
id 'org.jetbrains.dokka' version '1.8.10'
id 'org.unbroken-dome.xjc' version '2.0.0' apply false
id 'com.github.ben-manes.versions' version '0.51.0'
id 'com.github.ben-manes.versions' version '0.49.0'
id 'com.github.johnrengelman.shadow' version '8.1.1' apply false
id 'de.undercouch.download' version '5.4.0'
id 'me.champeau.jmh' version '0.7.2' apply false
id 'me.champeau.jmh' version '0.7.1' apply false
}
ext {
@@ -113,6 +113,7 @@ configure([rootProject] + javaProjects) { project ->
"https://docs.oracle.com/en/java/javase/17/docs/api/",
"https://jakarta.ee/specifications/platform/9/apidocs/",
"https://docs.oracle.com/cd/E13222_01/wls/docs90/javadocs/", // CommonJ and weblogic.* packages
"https://www.ibm.com/docs/api/v1/content/SSEQTP_8.5.5/com.ibm.websphere.javadoc.doc/web/apidocs/", // com.ibm.*
"https://docs.jboss.org/jbossas/javadoc/4.0.5/connector/", // org.jboss.resource.*
"https://docs.jboss.org/hibernate/orm/5.6/javadocs/",
"https://eclipse.dev/aspectj/doc/released/aspectj5rt-api",
-2
View File
@@ -1,7 +1,5 @@
== Spring Framework Concourse pipeline
NOTE: CI is being migrated to GitHub Actions.
The Spring Framework uses https://concourse-ci.org/[Concourse] for its CI build and other automated tasks.
The Spring team has a dedicated Concourse instance available at https://ci.spring.io with a build pipeline
for https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-6.0.x[Spring Framework 6.0.x].
+2 -1
View File
@@ -1,4 +1,4 @@
FROM ubuntu:jammy-20240125
FROM ubuntu:jammy-20231211.1
ADD setup.sh /setup.sh
ADD get-jdk-url.sh /get-jdk-url.sh
@@ -6,5 +6,6 @@ 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
+4 -1
View File
@@ -3,7 +3,10 @@ set -e
case "$1" in
java17)
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.10%2B13/bellsoft-jdk17.0.10+13-linux-amd64.tar.gz"
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.9+11/bellsoft-jdk17.0.9+11-linux-amd64.tar.gz"
;;
java20)
echo "https://github.com/bell-sw/Liberica/releases/download/20.0.1+10/bellsoft-jdk20.0.1+10-linux-amd64.tar.gz"
;;
*)
echo $"Unknown java version"
+1 -1
View File
@@ -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
for jdk in java17 java20
do
JDK_URL=$( /get-jdk-url.sh $jdk )
mkdir $jdk
+1
View File
@@ -8,3 +8,4 @@ milestone: "6.0.x"
build-name: "spring-framework"
pipeline-name: "spring-framework"
concourse-url: "https://ci.spring.io"
task-timeout: 1h00m
+123 -15
View File
@@ -5,7 +5,9 @@ anchors:
password: ((github-ci-release-token))
branch: ((branch))
gradle-enterprise-task-params: &gradle-enterprise-task-params
DEVELOCITY_ACCESS_KEY: ((gradle_enterprise_secret_access_key))
GRADLE_ENTERPRISE_ACCESS_KEY: ((gradle_enterprise_secret_access_key))
GRADLE_ENTERPRISE_CACHE_USERNAME: ((gradle_enterprise_cache_user.username))
GRADLE_ENTERPRISE_CACHE_PASSWORD: ((gradle_enterprise_cache_user.password))
sonatype-task-params: &sonatype-task-params
SONATYPE_USERNAME: ((sonatype-username))
SONATYPE_PASSWORD: ((sonatype-password))
@@ -21,6 +23,14 @@ anchors:
docker-resource-source: &docker-resource-source
username: ((docker-hub-username))
password: ((docker-hub-password))
slack-fail-params: &slack-fail-params
text: >
:concourse-failed: <https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}|${BUILD_PIPELINE_NAME} ${BUILD_JOB_NAME} failed!>
[$TEXT_FILE_CONTENT]
text_file: git-repo/build/build-scan-uri.txt
silent: true
icon_emoji: ":concourse:"
username: concourse-ci
changelog-task-params: &changelog-task-params
name: generated-changelog/tag
tag: generated-changelog/tag
@@ -35,7 +45,7 @@ resource_types:
source:
<<: *docker-resource-source
repository: concourse/registry-image-resource
tag: 1.8.0
tag: 1.5.0
- name: artifactory-resource
type: registry-image
source:
@@ -47,13 +57,19 @@ resource_types:
source:
<<: *docker-resource-source
repository: concourse/github-release-resource
tag: 1.8.0
tag: 1.5.5
- name: github-status-resource
type: registry-image
source:
<<: *docker-resource-source
repository: dpb587/github-status-resource
tag: master
- name: slack-notification
type: registry-image
source:
<<: *docker-resource-source
repository: cfcommunity/slack-notification-resource
tag: latest
resources:
- name: git-repo
type: git
@@ -74,6 +90,13 @@ 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
@@ -82,6 +105,27 @@ resources:
username: ((artifactory-username))
password: ((artifactory-password))
build_name: ((build-name))
- name: repo-status-build
type: github-status-resource
icon: eye-check-outline
source:
repository: ((github-repo-name))
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
source:
url: ((slack-webhook-url))
- name: github-pre-release
type: github-release
icon: briefcase-download-outline
@@ -116,23 +160,37 @@ jobs:
- put: ci-image
params:
image: ci-image/image.tar
- name: stage-milestone
- name: build
serial: true
public: true
plan:
- get: ci-image
- get: git-repo
trigger: false
- task: stage
image: ci-image
file: git-repo/ci/tasks/stage-version.yml
params:
RELEASE_TYPE: M
<<: *gradle-enterprise-task-params
trigger: true
- put: repo-status-build
params: { state: "pending", commit: "git-repo" }
- do:
- task: build-project
image: ci-image
file: git-repo/ci/tasks/build-project.yml
privileged: true
timeout: ((task-timeout))
params:
<<: *build-project-task-params
on_failure:
do:
- put: repo-status-build
params: { state: "failure", commit: "git-repo" }
- put: slack-alert
params:
<<: *slack-fail-params
- put: repo-status-build
params: { state: "success", commit: "git-repo" }
- put: artifactory-repo
params: &artifactory-params
signing_key: ((signing-key))
signing_passphrase: ((signing-passphrase))
repo: libs-staging-local
repo: libs-snapshot-local
folder: distribution-repository
build_uri: "https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}"
build_number: "${BUILD_PIPELINE_NAME}-${BUILD_JOB_NAME}-${BUILD_NAME}"
@@ -157,9 +215,55 @@ jobs:
- "/**/framework-docs-*-schema.zip"
properties:
"zip.type": "schema"
- put: git-repo
params:
repository: stage-git-repo
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:
- get: ci-image
- get: git-repo
trigger: false
- task: stage
image: ci-image
file: git-repo/ci/tasks/stage-version.yml
params:
RELEASE_TYPE: M
<<: *gradle-enterprise-task-params
- put: artifactory-repo
params:
<<: *artifactory-params
repo: libs-staging-local
- put: git-repo
params:
repository: stage-git-repo
- name: promote-milestone
serial: true
plan:
@@ -201,6 +305,7 @@ jobs:
- put: artifactory-repo
params:
<<: *artifactory-params
repo: libs-staging-local
- put: git-repo
params:
repository: stage-git-repo
@@ -245,6 +350,7 @@ jobs:
- put: artifactory-repo
params:
<<: *artifactory-params
repo: libs-staging-local
- put: git-repo
params:
repository: stage-git-repo
@@ -289,6 +395,8 @@ jobs:
<<: *changelog-task-params
groups:
- name: "builds"
jobs: ["build", "jdk20-build"]
- name: "releases"
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release"]
- name: "ci-images"
+8
View File
@@ -0,0 +1,8 @@
#!/bin/bash
set -e
source $(dirname $0)/common.sh
pushd git-repo > /dev/null
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --max-workers=4 check
popd > /dev/null
+9
View File
@@ -0,0 +1,9 @@
#!/bin/bash
set -e
source $(dirname $0)/common.sh
repository=$(pwd)/distribution-repository
pushd git-repo > /dev/null
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --max-workers=4 -PdeploymentRepository=${repository} build publishAllPublicationsToDeploymentRepository
popd > /dev/null
+9
View File
@@ -0,0 +1,9 @@
#!/bin/bash
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 \
-PmainToolchain=${MAIN_TOOLCHAIN} -PtestToolchain=${TEST_TOOLCHAIN} --no-daemon --max-workers=4 check antora
popd > /dev/null
+19
View File
@@ -0,0 +1,19 @@
---
platform: linux
inputs:
- name: git-repo
caches:
- path: gradle
params:
BRANCH:
CI: true
GRADLE_ENTERPRISE_ACCESS_KEY:
GRADLE_ENTERPRISE_CACHE_USERNAME:
GRADLE_ENTERPRISE_CACHE_PASSWORD:
GRADLE_ENTERPRISE_URL: https://ge.spring.io
run:
path: bash
args:
- -ec
- |
${PWD}/git-repo/ci/scripts/build-pr.sh
+22
View File
@@ -0,0 +1,22 @@
---
platform: linux
inputs:
- name: git-repo
outputs:
- name: distribution-repository
- name: git-repo
caches:
- path: gradle
params:
BRANCH:
CI: true
GRADLE_ENTERPRISE_ACCESS_KEY:
GRADLE_ENTERPRISE_CACHE_USERNAME:
GRADLE_ENTERPRISE_CACHE_PASSWORD:
GRADLE_ENTERPRISE_URL: https://ge.spring.io
run:
path: bash
args:
- -ec
- |
${PWD}/git-repo/ci/scripts/build-project.sh
+24
View File
@@ -0,0 +1,24 @@
---
platform: linux
inputs:
- name: git-repo
outputs:
- name: distribution-repository
- name: git-repo
caches:
- path: gradle
params:
BRANCH:
CI: true
MAIN_TOOLCHAIN:
TEST_TOOLCHAIN:
GRADLE_ENTERPRISE_ACCESS_KEY:
GRADLE_ENTERPRISE_CACHE_USERNAME:
GRADLE_ENTERPRISE_CACHE_PASSWORD:
GRADLE_ENTERPRISE_URL: https://ge.spring.io
run:
path: bash
args:
- -ec
- |
${PWD}/git-repo/ci/scripts/check-project.sh
+1 -1
View File
@@ -4,7 +4,7 @@ image_resource:
type: registry-image
source:
repository: springio/concourse-release-scripts
tag: '0.4.0'
tag: '0.4.0-SNAPSHOT'
username: ((docker-hub-username))
password: ((docker-hub-password))
inputs:
+2 -2
View File
@@ -39,8 +39,8 @@
** xref:core/resources.adoc[]
** xref:core/validation.adoc[]
*** xref:core/validation/validator.adoc[]
*** xref:core/validation/beans-beans.adoc[]
*** xref:core/validation/conversion.adoc[]
*** xref:core/validation/beans-beans.adoc[]
*** xref:core/validation/convert.adoc[]
*** xref:core/validation/format.adoc[]
*** xref:core/validation/format-configuring-formatting-globaldatetimeformat.adoc[]
@@ -431,4 +431,4 @@
** xref:languages/groovy.adoc[]
** xref:languages/dynamic.adoc[]
* xref:appendix.adoc[]
* https://github.com/spring-projects/spring-framework/wiki[Wiki]
* https://github.com/spring-projects/spring-framework/wiki[Wiki]
@@ -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:core/resources.adoc[Resources])
xref:web/webflux-webclient/client-builder.adoc#webflux-client-builder-reactor-resources[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].
@@ -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,18 +13,16 @@ Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
// evaluates to ["SmilJan", "Idvor"]
List placesOfBirth = parser.parseExpression("members.![placeOfBirth.city]")
.getValue(societyContext, List.class);
// returns ['Smiljan', 'Idvor' ]
List placesOfBirth = (List)parser.parseExpression("members.![placeOfBirth.city]");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
// evaluates to ["SmilJan", "Idvor"]
val placesOfBirth = parser.parseExpression("members.![placeOfBirth.city]")
.getValue(societyContext) as List<*>
// returns ['Smiljan', 'Idvor' ]
val placesOfBirth = parser.parseExpression("members.![placeOfBirth.city]") as List<*>
----
======
@@ -28,14 +28,13 @@ Kotlin::
======
Selection is supported for arrays and anything that implements `java.lang.Iterable` or
`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.
`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.
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:
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]
======
@@ -43,21 +42,21 @@ Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
Map newMap = parser.parseExpression("#map.?[value < 27]").getValue(Map.class);
Map newMap = parser.parseExpression("map.?[value<27]").getValue();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val newMap = parser.parseExpression("#map.?[value < 27]").getValue() as Map
val newMap = parser.parseExpression("map.?[value<27]").getValue()
----
======
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 expression, the
syntax is `.^[selectionExpression]`. To obtain the last element matching the selection
expression, the syntax is `.$[selectionExpression]`.
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]`.
@@ -268,8 +268,8 @@ The actual JPA provider bootstrapping is handed off to the specified executor an
running in parallel, to the application bootstrap thread. The exposed `EntityManagerFactory`
proxy can be injected into other application components and is even able to respond to
`EntityManagerFactoryInfo` configuration inspection. However, once the actual JPA provider
is being accessed by other components (for example, calling `createEntityManager`), those
calls block until the background bootstrapping has completed. In particular, when you use
is being accessed by other components (for example, calling `createEntityManager`), those calls
block until the background bootstrapping has completed. In particular, when you use
Spring Data JPA, make sure to set up deferred bootstrapping for its repositories as well.
@@ -284,9 +284,9 @@ to a newly created `EntityManager` per operation, in effect making its usage thr
It is possible to write code against the plain JPA without any Spring dependencies, by
using an injected `EntityManagerFactory` or `EntityManager`. Spring can understand the
`@PersistenceUnit` and `@PersistenceContext` annotations both at the field and the method
level if a `PersistenceAnnotationBeanPostProcessor` is enabled. The following example
shows a plain JPA DAO implementation that uses the `@PersistenceUnit` annotation:
`@PersistenceUnit` and `@PersistenceContext` annotations both at the field and the method level
if a `PersistenceAnnotationBeanPostProcessor` is enabled. The following example shows a plain
JPA DAO implementation that uses the `@PersistenceUnit` annotation:
[tabs]
======
@@ -23,7 +23,7 @@ As of Spring Framework 5.2, the default configuration also provides support for
Vavr's `Try` method to trigger transaction rollbacks when it returns a 'Failure'.
This allows you to handle functional-style errors using Try and have the transaction
automatically rolled back in case of a failure. For more information on Vavr's Try,
refer to the https://docs.vavr.io/#_try[official Vavr documentation].
refer to the [official Vavr documentation](https://www.vavr.io/vavr-docs/#_try).
Here's an example of how to use Vavr's Try with a transactional method:
[tabs]
@@ -152,7 +152,7 @@ Kotlin::
@Autowired
lateinit var accountService: AccountService
lateinit var mockMvc: MockMvc
lateinit mockMvc: MockMvc
@BeforeEach
fun setup(wac: WebApplicationContext) {
@@ -1,7 +1,7 @@
[[spring-mvc-test-vs-end-to-end-integration-tests]]
= MockMvc vs End-to-End Tests
MockMvc is built on Servlet API mock implementations from the
MockMVc is built on Servlet API mock implementations from the
`spring-test` module and does not rely on a running container. Therefore, there are
some differences when compared to full end-to-end integration tests with an actual
client and a live server running.
@@ -1,15 +1,38 @@
[[spring-mvc-test-vs-streaming-response]]
= Streaming Responses
You can use `WebTestClient` to test xref:testing/webtestclient.adoc#webtestclient-stream[streaming responses]
such as Server-Sent Events. However, `MockMvcWebTestClient` doesn't support infinite
streams because there is no way to cancel the server stream from the client side.
To test infinite streams, you'll need to
xref:testing/webtestclient.adoc#webtestclient-server-config[bind to] a running server,
or when using Spring Boot,
{docs-spring-boot}/spring-boot-features.html#boot-features-testing-spring-boot-applications-testing-with-running-server[test with a running server].
The best way to test streaming responses such as Server-Sent Events is through the
<<WebTestClient>> which can be used as a test client to connect to a `MockMvc` instance
to perform tests on Spring MVC controllers without a running server. For example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
WebTestClient client = MockMvcWebTestClient.bindToController(new SseController()).build();
FluxExchangeResult<Person> exchangeResult = client.get()
.uri("/persons")
.exchange()
.expectStatus().isOk()
.expectHeader().contentType("text/event-stream")
.returnResult(Person.class);
// Use StepVerifier from Project Reactor to test the streaming response
StepVerifier.create(exchangeResult.getResponseBody())
.expectNext(new Person("N0"), new Person("N1"), new Person("N2"))
.expectNextCount(4)
.consumeNextWith(person -> assertThat(person.getName()).endsWith("7"))
.thenCancel()
.verify();
----
======
`WebTestClient` can also connect to a live server and perform full end-to-end integration
tests. This is also supported in Spring Boot where you can
{docs-spring-boot}/html/spring-boot-features.html#boot-features-testing-spring-boot-applications-testing-with-running-server[test a running server].
`MockMvcWebTestClient` does support asynchronous responses, and even streaming responses.
The limitation is that it can't influence the server to stop, and therefore the server
must finish writing the response on its own.
@@ -52,10 +52,14 @@ The following example shows how to achieve the same configuration in XML:
</mvc:interceptors>
----
WARNING: Interceptors are not ideally suited as a security layer due to the potential for
a mismatch with annotated controller path matching. Generally, we recommend using Spring
Security, or alternatively a similar approach integrated with the Servlet filter chain,
and applied as early as possible.
NOTE: Interceptors are not ideally suited as a security layer due to the potential
for a mismatch with annotated controller path matching, which can also match trailing
slashes and path extensions transparently, along with other path matching options. Many
of these options have been deprecated but the potential for a mismatch remains.
Generally, we recommend using Spring Security which includes a dedicated
https://docs.spring.io/spring-security/reference/servlet/integrations/mvc.html#mvc-requestmatcher[MvcRequestMatcher]
to align with Spring MVC path matching and also has a security firewall that blocks many
unwanted characters in URL paths.
NOTE: The XML config declares interceptors as `MappedInterceptor` beans, and those are in
turn detected by any `HandlerMapping` bean, including those from other frameworks.
@@ -1,29 +1,34 @@
[[mvc-handlermapping-interceptor]]
= Interception
All `HandlerMapping` implementations support handler interception which is useful when
you want to apply functionality across requests. A `HandlerInterceptor` can implement the
following:
All `HandlerMapping` implementations support handler interceptors that are useful when
you want to apply specific functionality to certain requests -- for example, checking for
a principal. Interceptors must implement `HandlerInterceptor` from the
`org.springframework.web.servlet` package with three methods that should provide enough
flexibility to do all kinds of pre-processing and post-processing:
* `preHandle(..)` -- callback before the actual handler is run that returns a boolean.
If the method returns `true`, execution continues; if it returns `false`, the rest of the
execution chain is bypassed and the handler is not called.
* `postHandle(..)` -- callback after the handler is run.
* `afterCompletion(..)` -- callback after the complete request has finished.
* `preHandle(..)`: Before the actual handler is run
* `postHandle(..)`: After the handler is run
* `afterCompletion(..)`: After the complete request has finished
NOTE: For `@ResponseBody` and `ResponseEntity` controller methods, the response is written
and committed within the `HandlerAdapter`, before `postHandle` is called. That means it is
too late to change the response, such as to add an extra header. You can implement
`ResponseBodyAdvice` and declare it as an
xref:web/webmvc/mvc-controller/ann-advice.adoc[Controller Advice] bean or configure it
directly on `RequestMappingHandlerAdapter`.
The `preHandle(..)` method returns a boolean value. You can use this method to break or
continue the processing of the execution chain. When this method returns `true`, the
handler execution chain continues. When it returns false, the `DispatcherServlet`
assumes the interceptor itself has taken care of requests (and, for example, rendered an
appropriate view) and does not continue executing the other interceptors and the actual
handler in the execution chain.
See xref:web/webmvc/mvc-config/interceptors.adoc[Interceptors] in the section on MVC configuration for examples of how to
configure interceptors. You can also register them directly by using setters on individual
`HandlerMapping` implementations.
WARNING: Interceptors are not ideally suited as a security layer due to the potential for
a mismatch with annotated controller path matching. Generally, we recommend using Spring
Security, or alternatively a similar approach integrated with the Servlet filter chain,
and applied as early as possible.
`postHandle` method is less useful with `@ResponseBody` and `ResponseEntity` methods for
which the response is written and committed within the `HandlerAdapter` and before
`postHandle`. That means it is too late to make any changes to the response, such as adding
an extra header. For such scenarios, you can implement `ResponseBodyAdvice` and either
declare it as an xref:web/webmvc/mvc-controller/ann-advice.adoc[Controller Advice] bean or configure it directly on
`RequestMappingHandlerAdapter`.
+16 -16
View File
@@ -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.109.Final"))
api(platform("io.netty:netty-bom:4.1.104.Final"))
api(platform("io.netty:netty5-bom:5.0.0.Alpha5"))
api(platform("io.projectreactor:reactor-bom:2022.0.19"))
api(platform("io.projectreactor:reactor-bom:2022.0.15"))
api(platform("io.rsocket:rsocket-bom:1.1.3"))
api(platform("org.apache.groovy:groovy-bom:4.0.21"))
api(platform("org.apache.groovy:groovy-bom:4.0.17"))
api(platform("org.apache.logging.log4j:log4j-bom:2.21.1"))
api(platform("org.eclipse.jetty:jetty-bom:11.0.20"))
api(platform("org.eclipse.jetty:jetty-bom:11.0.19"))
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.12.0"))
api(platform("org.mockito:mockito-bom:5.8.0"))
constraints {
api("com.fasterxml:aalto-xml:1.3.2")
api("com.fasterxml.woodstox:woodstox-core:6.5.1")
api("com.github.ben-manes.caffeine:caffeine:3.1.8")
api("com.github.librepdf:openpdf:1.3.43")
api("com.github.librepdf:openpdf:1.3.36")
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.10.0")
api("io.undertow:undertow-core:2.3.13.Final")
api("io.undertow:undertow-servlet:2.3.13.Final")
api("io.undertow:undertow-websockets-jsr:2.3.13.Final")
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.vavr:vavr:0.10.4")
api("jakarta.activation:jakarta.activation-api:2.0.1")
api("jakarta.annotation:jakarta.annotation-api:2.0.0")
@@ -103,15 +103,15 @@ dependencies {
api("org.apache.tomcat.embed:tomcat-embed-websocket:10.1.15")
api("org.apache.tomcat:tomcat-util:10.1.15")
api("org.apache.tomcat:tomcat-websocket:10.1.15")
api("org.aspectj:aspectjrt:1.9.22.1")
api("org.aspectj:aspectjtools:1.9.22.1")
api("org.aspectj:aspectjweaver:1.9.22.1")
api("org.aspectj:aspectjrt:1.9.20.1")
api("org.aspectj:aspectjtools:1.9.20.1")
api("org.aspectj:aspectjweaver:1.9.20.1")
api("org.assertj:assertj-core:3.24.2")
api("org.awaitility:awaitility:4.2.0")
api("org.bouncycastle:bcpkix-jdk18on:1.72")
api("org.codehaus.jettison:jettison:1.5.4")
api("org.dom4j:dom4j:2.1.4")
api("org.eclipse.jetty:jetty-reactive-httpclient:3.0.12")
api("org.eclipse.jetty:jetty-reactive-httpclient:3.0.10")
api("org.eclipse.persistence:org.eclipse.persistence.jpa:3.0.4")
api("org.eclipse:yasson:2.0.4")
api("org.ehcache:ehcache:3.10.8")
@@ -126,7 +126,7 @@ dependencies {
api("org.hibernate:hibernate-validator:7.0.5.Final")
api("org.hsqldb:hsqldb:2.7.2")
api("org.javamoney:moneta:1.4.2")
api("org.jruby:jruby:9.4.6.0")
api("org.jruby:jruby:9.4.5.0")
api("org.junit.support:testng-engine:1.0.4")
api("org.mozilla:rhino:1.7.14")
api("org.ogce:xpp3:1.1.6")
@@ -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.12")
api("org.slf4j:slf4j-api:2.0.11")
api("org.testng:testng:7.8.0")
api("org.webjars:underscorejs:1.8.3")
api("org.webjars:webjars-locator-core:0.55")
+1 -1
View File
@@ -1,4 +1,4 @@
version=6.0.20
version=6.0.16
org.gradle.caching=true
org.gradle.jvmargs=-Xmx2048m
-2
View File
@@ -6,7 +6,6 @@ tasks.findByName("dokkaHtmlPartial")?.configure {
classpath.from(sourceSets["main"].runtimeClasspath)
externalDocumentationLink {
url.set(new URL("https://docs.spring.io/spring-framework/docs/current/javadoc-api/"))
packageListUrl.set(new URL("https://docs.spring.io/spring-framework/docs/current/javadoc-api/element-list"))
}
externalDocumentationLink {
url.set(new URL("https://projectreactor.io/docs/core/release/api/"))
@@ -22,7 +21,6 @@ tasks.findByName("dokkaHtmlPartial")?.configure {
}
externalDocumentationLink {
url.set(new URL("https://javadoc.io/doc/jakarta.servlet/jakarta.servlet-api/latest/"))
packageListUrl.set(new URL("https://javadoc.io/doc/jakarta.servlet/jakarta.servlet-api/latest/element-list"))
}
externalDocumentationLink {
url.set(new URL("https://javadoc.io/static/io.rsocket/rsocket-core/1.1.1/"))
Binary file not shown.
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Vendored
+10 -10
View File
@@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
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
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.
goto fail
@@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
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
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.
goto fail
+3 -3
View File
@@ -7,8 +7,8 @@ pluginManagement {
}
plugins {
id "com.gradle.develocity" version "3.17.2"
id "io.spring.ge.conventions" version "0.0.17"
id "com.gradle.enterprise" version "3.12.6"
id "io.spring.ge.conventions" version "0.0.13"
}
include "spring-aop"
@@ -45,7 +45,7 @@ rootProject.children.each {project ->
}
settings.gradle.projectsLoaded {
develocity {
gradleEnterprise {
buildScan {
File buildDir = settings.gradle.rootProject
.getLayout().getBuildDirectory().getAsFile().get()
@@ -169,30 +169,25 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
@Override
public ClassFilter getClassFilter() {
checkExpression();
obtainPointcutExpression();
return this;
}
@Override
public MethodMatcher getMethodMatcher() {
checkExpression();
obtainPointcutExpression();
return this;
}
/**
* Check whether this pointcut is ready to match.
* Check whether this pointcut is ready to match,
* lazily building the underlying AspectJ pointcut expression.
*/
private void checkExpression() {
private PointcutExpression obtainPointcutExpression() {
if (getExpression() == null) {
throw new IllegalStateException("Must set property 'expression' before attempting to match");
}
}
/**
* Lazily build the underlying AspectJ pointcut expression.
*/
private PointcutExpression obtainPointcutExpression() {
if (this.pointcutExpression == null) {
this.pointcutClassLoader = determinePointcutClassLoader();
this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader);
@@ -269,9 +264,10 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
@Override
public boolean matches(Class<?> targetClass) {
PointcutExpression pointcutExpression = obtainPointcutExpression();
try {
try {
return obtainPointcutExpression().couldMatchJoinPointsInType(targetClass);
return pointcutExpression.couldMatchJoinPointsInType(targetClass);
}
catch (ReflectionWorldException ex) {
logger.debug("PointcutExpression matching rejected target class - trying fallback expression", ex);
@@ -282,9 +278,6 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
}
}
}
catch (IllegalArgumentException | IllegalStateException ex) {
throw ex;
}
catch (Throwable ex) {
logger.debug("PointcutExpression matching rejected target class", ex);
}
@@ -293,6 +286,7 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
@Override
public boolean matches(Method method, Class<?> targetClass, boolean hasIntroductions) {
obtainPointcutExpression();
ShadowMatch shadowMatch = getTargetShadowMatch(method, targetClass);
// Special handling for this, target, @this, @target, @annotation
@@ -330,6 +324,7 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
@Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
obtainPointcutExpression();
ShadowMatch shadowMatch = getTargetShadowMatch(method, targetClass);
// Bind Spring AOP proxy to AspectJ "this" and Spring AOP target to AspectJ target,
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 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,6 +18,7 @@ package org.springframework.aop.aspectj.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.StringTokenizer;
@@ -55,6 +56,8 @@ import org.springframework.lang.Nullable;
*/
public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFactory {
private static final String AJC_MAGIC = "ajc$";
private static final Class<?>[] ASPECTJ_ANNOTATION_CLASSES = new Class<?>[] {
Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class};
@@ -65,11 +68,37 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac
protected final ParameterNameDiscoverer parameterNameDiscoverer = new AspectJAnnotationParameterNameDiscoverer();
/**
* We consider something to be an AspectJ aspect suitable for use by the Spring AOP system
* if it has the @Aspect annotation, and was not compiled by ajc. The reason for this latter test
* is that aspects written in the code-style (AspectJ language) also have the annotation present
* when compiled by ajc with the -1.5 flag, yet they cannot be consumed by Spring AOP.
*/
@Override
public boolean isAspect(Class<?> clazz) {
return (hasAspectAnnotation(clazz) && !compiledByAjc(clazz));
}
private boolean hasAspectAnnotation(Class<?> clazz) {
return (AnnotationUtils.findAnnotation(clazz, Aspect.class) != null);
}
/**
* We need to detect this as "code-style" AspectJ aspects should not be
* interpreted by Spring AOP.
*/
private boolean compiledByAjc(Class<?> clazz) {
// The AJTypeSystem goes to great lengths to provide a uniform appearance between code-style and
// annotation-style aspects. Therefore there is no 'clean' way to tell them apart. Here we rely on
// an implementation detail of the AspectJ compiler.
for (Field field : clazz.getDeclaredFields()) {
if (field.getName().startsWith(AJC_MAGIC)) {
return true;
}
}
return false;
}
@Override
public void validate(Class<?> aspectClass) throws AopConfigException {
AjType<?> ajType = AjTypeSystem.getAjType(aspectClass);
@@ -86,7 +115,6 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac
}
}
/**
* Find and return the first AspectJ annotation on the given method
* (there <i>should</i> only be one anyway...).
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -124,16 +124,10 @@ public class AspectMetadata implements Serializable {
* Extract contents from String of form {@code pertarget(contents)}.
*/
private String findPerClause(Class<?> aspectClass) {
Aspect ann = aspectClass.getAnnotation(Aspect.class);
if (ann == null) {
return "";
}
String value = ann.value();
int beginIndex = value.indexOf('(');
if (beginIndex < 0) {
return "";
}
return value.substring(beginIndex + 1, value.length() - 1);
String str = aspectClass.getAnnotation(Aspect.class).value();
int beginIndex = str.indexOf('(') + 1;
int endIndex = str.length() - 1;
return str.substring(beginIndex, endIndex);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 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.
@@ -368,7 +368,8 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
private void validateIntroductionAdvisor(IntroductionAdvisor advisor) {
advisor.validateInterfaces();
// If the advisor passed validation, we can make the change.
for (Class<?> ifc : advisor.getInterfaces()) {
Class<?>[] ifcs = advisor.getInterfaces();
for (Class<?> ifc : ifcs) {
addInterface(ifc);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -186,10 +186,10 @@ public abstract class AopUtils {
* this method resolves bridge methods in order to retrieve attributes from
* the <i>original</i> method definition.
* @param method the method to be invoked, which may come from an interface
* @param targetClass the target class for the current invocation
* (can be {@code null} or may not even implement the method)
* @param targetClass the target class for the current invocation.
* May be {@code null} or may not even implement the method.
* @return the specific target method, or the original method if the
* {@code targetClass} does not implement it
* {@code targetClass} doesn't implement it or is {@code null}
* @see org.springframework.util.ClassUtils#getMostSpecificMethod
*/
public static Method getMostSpecificMethod(Method method, @Nullable Class<?> targetClass) {
@@ -23,6 +23,8 @@ import java.util.Map;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.aspectj.weaver.tools.PointcutPrimitive;
import org.aspectj.weaver.tools.UnsupportedPointcutPrimitiveException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import test.annotation.EmptySpringAnnotation;
@@ -39,6 +41,7 @@ import org.springframework.beans.testfixture.beans.TestBean;
import org.springframework.beans.testfixture.beans.subpkg.DeepBean;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
@@ -171,25 +174,25 @@ public class AspectJExpressionPointcutTests {
@Test
public void testFriendlyErrorOnNoLocationClassMatching() {
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
assertThatIllegalStateException()
.isThrownBy(() -> pc.getClassFilter().matches(ITestBean.class))
.withMessageContaining("expression");
assertThatIllegalStateException().isThrownBy(() ->
pc.matches(ITestBean.class))
.withMessageContaining("expression");
}
@Test
public void testFriendlyErrorOnNoLocation2ArgMatching() {
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
assertThatIllegalStateException()
.isThrownBy(() -> pc.getMethodMatcher().matches(getAge, ITestBean.class))
.withMessageContaining("expression");
assertThatIllegalStateException().isThrownBy(() ->
pc.matches(getAge, ITestBean.class))
.withMessageContaining("expression");
}
@Test
public void testFriendlyErrorOnNoLocation3ArgMatching() {
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
assertThatIllegalStateException()
.isThrownBy(() -> pc.getMethodMatcher().matches(getAge, ITestBean.class, (Object[]) null))
.withMessageContaining("expression");
assertThatIllegalStateException().isThrownBy(() ->
pc.matches(getAge, ITestBean.class, (Object[]) null))
.withMessageContaining("expression");
}
@@ -206,10 +209,8 @@ public class AspectJExpressionPointcutTests {
// not currently testable in a reliable fashion
//assertDoesNotMatchStringClass(classFilter);
assertThat(methodMatcher.matches(setSomeNumber, TestBean.class, 12D))
.as("Should match with setSomeNumber with Double input").isTrue();
assertThat(methodMatcher.matches(setSomeNumber, TestBean.class, 11))
.as("Should not match setSomeNumber with Integer input").isFalse();
assertThat(methodMatcher.matches(setSomeNumber, TestBean.class, 12D)).as("Should match with setSomeNumber with Double input").isTrue();
assertThat(methodMatcher.matches(setSomeNumber, TestBean.class, 11)).as("Should not match setSomeNumber with Integer input").isFalse();
assertThat(methodMatcher.matches(getAge, TestBean.class)).as("Should not match getAge").isFalse();
assertThat(methodMatcher.isRuntime()).as("Should be a runtime match").isTrue();
}
@@ -244,7 +245,7 @@ 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().matches(Object.class));
assertThatIllegalArgumentException().isThrownBy(getPointcut(expression)::getClassFilter); // call to getClassFilter forces resolution
}
private TestBean getAdvisedProxy(String pointcutExpression, CallCountingInterceptor interceptor) {
@@ -274,7 +275,9 @@ public class AspectJExpressionPointcutTests {
@Test
public void testWithUnsupportedPointcutPrimitive() {
String expression = "call(int org.springframework.beans.testfixture.beans.TestBean.getAge())";
assertThat(getPointcut(expression).getClassFilter().matches(Object.class)).isFalse();
assertThatExceptionOfType(UnsupportedPointcutPrimitiveException.class)
.isThrownBy(() -> getPointcut(expression).getClassFilter()) // call to getClassFilter forces resolution...
.satisfies(ex -> assertThat(ex.getUnsupportedPrimitive()).isEqualTo(PointcutPrimitive.CALL));
}
@Test
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,35 +17,29 @@
package org.springframework.aop.support;
import java.lang.reflect.Method;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
import org.springframework.aop.target.EmptyTargetSource;
import org.springframework.aop.testfixture.interceptor.NopInterceptor;
import org.springframework.beans.testfixture.beans.TestBean;
import org.springframework.core.ResolvableType;
import org.springframework.core.testfixture.io.SerializationTestUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Rod Johnson
* @author Chris Beams
* @author Sebastien Deleuze
* @author Juergen Hoeller
*/
class AopUtilsTests {
public class AopUtilsTests {
@Test
void testPointcutCanNeverApply() {
public void testPointcutCanNeverApply() {
class TestPointcut extends StaticMethodMatcherPointcut {
@Override
public boolean matches(Method method, @Nullable Class<?> clazzy) {
@@ -58,13 +52,13 @@ class AopUtilsTests {
}
@Test
void testPointcutAlwaysApplies() {
public void testPointcutAlwaysApplies() {
assertThat(AopUtils.canApply(new DefaultPointcutAdvisor(new NopInterceptor()), Object.class)).isTrue();
assertThat(AopUtils.canApply(new DefaultPointcutAdvisor(new NopInterceptor()), TestBean.class)).isTrue();
}
@Test
void testPointcutAppliesToOneMethodOnObject() {
public void testPointcutAppliesToOneMethodOnObject() {
class TestPointcut extends StaticMethodMatcherPointcut {
@Override
public boolean matches(Method method, @Nullable Class<?> clazz) {
@@ -84,7 +78,7 @@ class AopUtilsTests {
* that's subverted the singleton construction limitation.
*/
@Test
void testCanonicalFrameworkClassesStillCanonicalOnDeserialization() throws Exception {
public void testCanonicalFrameworkClassesStillCanonicalOnDeserialization() throws Exception {
assertThat(SerializationTestUtils.serializeAndDeserialize(MethodMatcher.TRUE)).isSameAs(MethodMatcher.TRUE);
assertThat(SerializationTestUtils.serializeAndDeserialize(ClassFilter.TRUE)).isSameAs(ClassFilter.TRUE);
assertThat(SerializationTestUtils.serializeAndDeserialize(Pointcut.TRUE)).isSameAs(Pointcut.TRUE);
@@ -94,45 +88,4 @@ class AopUtilsTests {
assertThat(SerializationTestUtils.serializeAndDeserialize(ExposeInvocationInterceptor.INSTANCE)).isSameAs(ExposeInvocationInterceptor.INSTANCE);
}
@Test
void testInvokeJoinpointUsingReflection() throws Throwable {
String name = "foo";
TestBean testBean = new TestBean(name);
Method method = ReflectionUtils.findMethod(TestBean.class, "getName");
Object result = AopUtils.invokeJoinpointUsingReflection(testBean, method, new Object[0]);
assertThat(result).isEqualTo(name);
}
@Test // gh-32365
void mostSpecificMethodBetweenJdkProxyAndTarget() throws Exception {
Class<?> proxyClass = new ProxyFactory(new WithInterface()).getProxyClass(getClass().getClassLoader());
Method specificMethod = AopUtils.getMostSpecificMethod(proxyClass.getMethod("handle", List.class), WithInterface.class);
assertThat(ResolvableType.forMethodParameter(specificMethod, 0).getGeneric().toClass()).isEqualTo(String.class);
}
@Test // gh-32365
void mostSpecificMethodBetweenCglibProxyAndTarget() throws Exception {
Class<?> proxyClass = new ProxyFactory(new WithoutInterface()).getProxyClass(getClass().getClassLoader());
Method specificMethod = AopUtils.getMostSpecificMethod(proxyClass.getMethod("handle", List.class), WithoutInterface.class);
assertThat(ResolvableType.forMethodParameter(specificMethod, 0).getGeneric().toClass()).isEqualTo(String.class);
}
interface ProxyInterface {
void handle(List<String> list);
}
static class WithInterface implements ProxyInterface {
public void handle(List<String> list) {
}
}
static class WithoutInterface {
public void handle(List<String> list) {
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 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.
@@ -292,7 +292,7 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa
if (actualValue != null) {
actualValue = typeConverter.convertIfNecessary(actualValue, expectedValue.getClass());
}
if (!ObjectUtils.nullSafeEquals(expectedValue, actualValue)) {
if (!expectedValue.equals(actualValue)) {
return false;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 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.
@@ -818,11 +818,11 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
/**
* This implementation attempts to query the FactoryBean's generic parameter metadata
* if present to determine the object type. If not present, i.e. the FactoryBean is
* declared as a raw type, it checks the FactoryBean's {@code getObjectType} method
* declared as a raw type, checks the FactoryBean's {@code getObjectType} method
* on a plain instance of the FactoryBean, without bean properties applied yet.
* If this doesn't return a type yet and {@code allowInit} is {@code true}, full
* creation of the FactoryBean is attempted as fallback (through delegation to the
* superclass implementation).
* If this doesn't return a type yet, and {@code allowInit} is {@code true} a
* full creation of the FactoryBean is used as fallback (through delegation to the
* superclass's implementation).
* <p>The shortcut check for a FactoryBean is only applied in case of a singleton
* FactoryBean. If the FactoryBean instance itself is not kept as singleton,
* it will be fully created to check the type of its exposed object.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 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.
@@ -161,9 +161,6 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
/** Map from scope identifier String to corresponding Scope. */
private final Map<String, Scope> scopes = new LinkedHashMap<>(8);
/** Application startup metrics. **/
private ApplicationStartup applicationStartup = ApplicationStartup.DEFAULT;
/** Map from bean name to merged RootBeanDefinition. */
private final Map<String, RootBeanDefinition> mergedBeanDefinitions = new ConcurrentHashMap<>(256);
@@ -174,6 +171,8 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
private final ThreadLocal<Object> prototypesCurrentlyInCreation =
new NamedThreadLocal<>("Prototype beans currently in creation");
/** Application startup metrics. **/
private ApplicationStartup applicationStartup = ApplicationStartup.DEFAULT;
/**
* Create a new AbstractBeanFactory.
@@ -1053,7 +1052,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
@Override
public void setApplicationStartup(ApplicationStartup applicationStartup) {
Assert.notNull(applicationStartup, "ApplicationStartup must not be null");
Assert.notNull(applicationStartup, "applicationStartup must not be null");
this.applicationStartup = applicationStartup;
}
@@ -1649,7 +1648,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
* already. The implementation is allowed to instantiate the target factory bean if
* {@code allowInit} is {@code true} and the type cannot be determined another way;
* otherwise it is restricted to introspecting signatures and related metadata.
* <p>If no {@link FactoryBean#OBJECT_TYPE_ATTRIBUTE} is set on the bean definition
* <p>If no {@link FactoryBean#OBJECT_TYPE_ATTRIBUTE} if set on the bean definition
* and {@code allowInit} is {@code true}, the default implementation will create
* the FactoryBean via {@code getBean} to call its {@code getObjectType} method.
* Subclasses are encouraged to optimize this, typically by inspecting the generic
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 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.
@@ -609,10 +609,13 @@ class ConstructorResolver {
String argDesc = StringUtils.collectionToCommaDelimitedString(argTypes);
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"No matching factory method found on class [" + factoryClass.getName() + "]: " +
(mbd.getFactoryBeanName() != null ? "factory bean '" + mbd.getFactoryBeanName() + "'; " : "") +
(mbd.getFactoryBeanName() != null ?
"factory bean '" + mbd.getFactoryBeanName() + "'; " : "") +
"factory method '" + mbd.getFactoryMethodName() + "(" + argDesc + ")'. " +
"Check that a method with the specified name " + (minNrOfArgs > 0 ? "and arguments " : "") +
"exists and that it is " + (isStatic ? "static" : "non-static") + ".");
"Check that a method with the specified name " +
(minNrOfArgs > 0 ? "and arguments " : "") +
"exists and that it is " +
(isStatic ? "static" : "non-static") + ".");
}
else if (void.class == factoryMethodToUse.getReturnType()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 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.
@@ -567,16 +567,16 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
*/
protected void destroyBean(String beanName, @Nullable DisposableBean bean) {
// Trigger destruction of dependent beans first...
Set<String> dependentBeanNames;
Set<String> dependencies;
synchronized (this.dependentBeanMap) {
// Within full synchronization in order to guarantee a disconnected Set
dependentBeanNames = this.dependentBeanMap.remove(beanName);
dependencies = this.dependentBeanMap.remove(beanName);
}
if (dependentBeanNames != null) {
if (dependencies != null) {
if (logger.isTraceEnabled()) {
logger.trace("Retrieved dependent beans for bean '" + beanName + "': " + dependentBeanNames);
logger.trace("Retrieved dependent beans for bean '" + beanName + "': " + dependencies);
}
for (String dependentBeanName : dependentBeanNames) {
for (String dependentBeanName : dependencies) {
destroySingleton(dependentBeanName);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 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.
@@ -261,7 +261,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
if (destroyMethod != null) {
return destroyMethod;
}
for (Class<?> beanInterface : ClassUtils.getAllInterfacesForClass(beanClass)) {
for (Class<?> beanInterface : beanClass.getInterfaces()) {
destroyMethod = findDestroyMethod(beanInterface, methodName);
if (destroyMethod != null) {
return destroyMethod;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 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.
@@ -108,8 +108,7 @@ public class GenericTypeAwareAutowireCandidateResolver extends SimpleAutowireCan
Class<?> resolvedClass = targetType.resolve();
if (resolvedClass != null && FactoryBean.class.isAssignableFrom(resolvedClass)) {
Class<?> typeToBeMatched = dependencyType.resolve();
if (typeToBeMatched != null && !FactoryBean.class.isAssignableFrom(typeToBeMatched) &&
!typeToBeMatched.isAssignableFrom(resolvedClass)) {
if (typeToBeMatched != null && !FactoryBean.class.isAssignableFrom(typeToBeMatched)) {
targetType = targetType.getGeneric();
if (descriptor.fallbackMatchAllowed()) {
// Matching the Class-based type determination for FactoryBean
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,10 +29,12 @@ import org.springframework.core.MethodClassKey;
import org.springframework.lang.Nullable;
/**
* Abstract implementation of {@link JCacheOperationSource} that caches operations
* Abstract implementation of {@link JCacheOperationSource} that caches attributes
* for methods and implements a fallback policy: 1. specific target method;
* 2. declaring method.
*
* <p>This implementation caches attributes by method after they are first used.
*
* @author Stephane Nicoll
* @author Juergen Hoeller
* @since 4.1
@@ -41,25 +43,24 @@ import org.springframework.lang.Nullable;
public abstract class AbstractFallbackJCacheOperationSource implements JCacheOperationSource {
/**
* Canonical value held in cache to indicate no cache operation was
* found for this method, and we don't need to look again.
* Canonical value held in cache to indicate no caching attribute was
* found for this method and we don't need to look again.
*/
private static final Object NULL_CACHING_MARKER = new Object();
private static final Object NULL_CACHING_ATTRIBUTE = new Object();
protected final Log logger = LogFactory.getLog(getClass());
private final Map<MethodClassKey, Object> operationCache = new ConcurrentHashMap<>(1024);
private final Map<MethodClassKey, Object> cache = new ConcurrentHashMap<>(1024);
@Override
@Nullable
public JCacheOperation<?> getCacheOperation(Method method, @Nullable Class<?> targetClass) {
MethodClassKey cacheKey = new MethodClassKey(method, targetClass);
Object cached = this.operationCache.get(cacheKey);
Object cached = this.cache.get(cacheKey);
if (cached != null) {
return (cached != NULL_CACHING_MARKER ? (JCacheOperation<?>) cached : null);
return (cached != NULL_CACHING_ATTRIBUTE ? (JCacheOperation<?>) cached : null);
}
else {
JCacheOperation<?> operation = computeCacheOperation(method, targetClass);
@@ -67,10 +68,10 @@ public abstract class AbstractFallbackJCacheOperationSource implements JCacheOpe
if (logger.isDebugEnabled()) {
logger.debug("Adding cacheable method '" + method.getName() + "' with operation: " + operation);
}
this.operationCache.put(cacheKey, operation);
this.cache.put(cacheKey, operation);
}
else {
this.operationCache.put(cacheKey, NULL_CACHING_MARKER);
this.cache.put(cacheKey, NULL_CACHING_ATTRIBUTE);
}
return operation;
}
@@ -83,7 +84,7 @@ public abstract class AbstractFallbackJCacheOperationSource implements JCacheOpe
return null;
}
// The method may be on an interface, but we need metadata from the target class.
// The method may be on an interface, but we need attributes from the target class.
// If the target class is null, the method will be unchanged.
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -212,8 +212,10 @@ public abstract class AnnotationJCacheOperationSource extends AbstractFallbackJC
for (Class<?> parameterType : parameterTypes) {
parameters.add(parameterType.getName());
}
return method.getDeclaringClass().getName() + '.' + method.getName() +
'(' + StringUtils.collectionToCommaDelimitedString(parameters) + ')';
return method.getDeclaringClass().getName()
+ '.' + method.getName()
+ '(' + StringUtils.collectionToCommaDelimitedString(parameters) + ')';
}
private int countNonNull(Object... instances) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -46,7 +46,6 @@ public class BeanFactoryJCacheOperationSourceAdvisor extends AbstractBeanFactory
* Set the cache operation attribute source which is used to find cache
* attributes. This should usually be identical to the source reference
* set on the cache interceptor itself.
* @see JCacheInterceptor#setCacheOperationSource
*/
public void setCacheOperationSource(JCacheOperationSource cacheOperationSource) {
this.pointcut.setCacheOperationSource(cacheOperationSource);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,7 +34,7 @@ public interface JCacheOperationSource {
* Return the cache operations for this method, or {@code null}
* if the method contains no <em>JSR-107</em> related metadata.
* @param method the method to introspect
* @param targetClass the target class (can be {@code null}, in which case
* @param targetClass the target class (may be {@code null}, in which case
* the declaring class of the method must be used)
* @return the cache operation for this method, or {@code null} if none found
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,7 +24,7 @@ import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
/**
* A {@code Pointcut} that matches if the underlying {@link JCacheOperationSource}
* A Pointcut that matches if the underlying {@link JCacheOperationSource}
* has an operation for a given method.
*
* @author Stephane Nicoll
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,12 +22,11 @@ import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.aot.hint.TypeHint.Builder;
import org.springframework.aot.hint.TypeReference;
import org.springframework.aot.hint.annotation.ReflectiveRuntimeHintsRegistrar;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
/**
* {@link RuntimeHintsRegistrar} implementation that makes sure {@link SchedulerFactoryBean}
* reflection hints are registered.
* reflection entries are registered.
*
* @author Sebastien Deleuze
* @author Stephane Nicoll
@@ -37,11 +36,11 @@ class SchedulerFactoryBeanRuntimeHints implements RuntimeHintsRegistrar {
private static final String SCHEDULER_FACTORY_CLASS_NAME = "org.quartz.impl.StdSchedulerFactory";
private static final ReflectiveRuntimeHintsRegistrar registrar = new ReflectiveRuntimeHintsRegistrar();
private final ReflectiveRuntimeHintsRegistrar reflectiveRegistrar = new ReflectiveRuntimeHintsRegistrar();
@Override
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
if (!ClassUtils.isPresent(SCHEDULER_FACTORY_CLASS_NAME, classLoader)) {
return;
}
@@ -49,7 +48,7 @@ class SchedulerFactoryBeanRuntimeHints implements RuntimeHintsRegistrar {
.registerType(TypeReference.of(SCHEDULER_FACTORY_CLASS_NAME), this::typeHint)
.registerTypes(TypeReference.listOf(ResourceLoaderClassLoadHelper.class,
LocalTaskExecutorThreadPool.class, LocalDataSourceJobStore.class), this::typeHint);
registrar.registerRuntimeHints(hints, LocalTaskExecutorThreadPool.class);
this.reflectiveRegistrar.registerRuntimeHints(hints, LocalTaskExecutorThreadPool.class);
}
private void typeHint(Builder typeHint) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,16 +32,20 @@ import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
/**
* Abstract implementation of {@link CacheOperationSource} that caches operations
* Abstract implementation of {@link CacheOperation} that caches attributes
* for methods and implements a fallback policy: 1. specific target method;
* 2. target class; 3. declaring method; 4. declaring class/interface.
*
* <p>Defaults to using the target class's declared cache operations if none are
* associated with the target method. Any cache operations associated with
* the target method completely override any class-level declarations.
* <p>Defaults to using the target class's caching attribute if none is
* associated with the target method. Any caching attribute associated with
* the target method completely overrides a class caching attribute.
* If none found on the target class, the interface that the invoked method
* has been called through (in case of a JDK proxy) will be checked.
*
* <p>This implementation caches attributes by method after they are first
* used. If it is ever desirable to allow dynamic changing of cacheable
* attributes (which is very unlikely), caching could be made configurable.
*
* @author Costin Leau
* @author Juergen Hoeller
* @since 3.1
@@ -49,10 +53,10 @@ import org.springframework.util.ClassUtils;
public abstract class AbstractFallbackCacheOperationSource implements CacheOperationSource {
/**
* Canonical value held in cache to indicate no cache operation was
* found for this method, and we don't need to look again.
* Canonical value held in cache to indicate no caching attribute was
* found for this method and we don't need to look again.
*/
private static final Collection<CacheOperation> NULL_CACHING_MARKER = Collections.emptyList();
private static final Collection<CacheOperation> NULL_CACHING_ATTRIBUTE = Collections.emptyList();
/**
@@ -67,14 +71,14 @@ public abstract class AbstractFallbackCacheOperationSource implements CacheOpera
* <p>As this base class is not marked Serializable, the cache will be recreated
* after serialization - provided that the concrete subclass is Serializable.
*/
private final Map<Object, Collection<CacheOperation>> operationCache = new ConcurrentHashMap<>(1024);
private final Map<Object, Collection<CacheOperation>> attributeCache = new ConcurrentHashMap<>(1024);
/**
* Determine the cache operations for this method invocation.
* <p>Defaults to class-declared metadata if no method-level metadata is found.
* Determine the caching attribute for this method invocation.
* <p>Defaults to the class's caching attribute if no method attribute is found.
* @param method the method for the current invocation (never {@code null})
* @param targetClass the target class for this invocation (can be {@code null})
* @param targetClass the target class for this invocation (may be {@code null})
* @return {@link CacheOperation} for this method, or {@code null} if the method
* is not cacheable
*/
@@ -86,21 +90,21 @@ public abstract class AbstractFallbackCacheOperationSource implements CacheOpera
}
Object cacheKey = getCacheKey(method, targetClass);
Collection<CacheOperation> cached = this.operationCache.get(cacheKey);
Collection<CacheOperation> cached = this.attributeCache.get(cacheKey);
if (cached != null) {
return (cached != NULL_CACHING_MARKER ? cached : null);
return (cached != NULL_CACHING_ATTRIBUTE ? cached : null);
}
else {
Collection<CacheOperation> cacheOps = computeCacheOperations(method, targetClass);
if (cacheOps != null) {
if (logger.isTraceEnabled()) {
logger.trace("Adding cacheable method '" + method.getName() + "' with operations: " + cacheOps);
logger.trace("Adding cacheable method '" + method.getName() + "' with attribute: " + cacheOps);
}
this.operationCache.put(cacheKey, cacheOps);
this.attributeCache.put(cacheKey, cacheOps);
}
else {
this.operationCache.put(cacheKey, NULL_CACHING_MARKER);
this.attributeCache.put(cacheKey, NULL_CACHING_ATTRIBUTE);
}
return cacheOps;
}
@@ -125,7 +129,7 @@ public abstract class AbstractFallbackCacheOperationSource implements CacheOpera
return null;
}
// The method may be on an interface, but we need metadata from the target class.
// The method may be on an interface, but we need attributes from the target class.
// If the target class is null, the method will be unchanged.
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
@@ -159,19 +163,19 @@ public abstract class AbstractFallbackCacheOperationSource implements CacheOpera
/**
* Subclasses need to implement this to return the cache operations for the
* Subclasses need to implement this to return the caching attribute for the
* given class, if any.
* @param clazz the class to retrieve the cache operations for
* @return all cache operations associated with this class, or {@code null} if none
* @param clazz the class to retrieve the attribute for
* @return all caching attribute associated with this class, or {@code null} if none
*/
@Nullable
protected abstract Collection<CacheOperation> findCacheOperations(Class<?> clazz);
/**
* Subclasses need to implement this to return the cache operations for the
* Subclasses need to implement this to return the caching attribute for the
* given method, if any.
* @param method the method to retrieve the cache operations for
* @return all cache operations associated with this method, or {@code null} if none
* @param method the method to retrieve the attribute for
* @return all caching attribute associated with this method, or {@code null} if none
*/
@Nullable
protected abstract Collection<CacheOperation> findCacheOperations(Method method);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -54,7 +54,7 @@ public interface CacheOperationSource {
* Return the collection of cache operations for this method,
* or {@code null} if the method contains no <em>cacheable</em> annotations.
* @param method the method to introspect
* @param targetClass the target class (can be {@code null}, in which case
* @param targetClass the target class (may be {@code null}, in which case
* the declaring class of the method must be used)
* @return all cache operations for this method, or {@code null} if none found
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 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.
@@ -28,7 +28,7 @@ import org.springframework.util.ObjectUtils;
/**
* A {@code Pointcut} that matches if the underlying {@link CacheOperationSource}
* has an operation for a given method.
* has an attribute for a given method.
*
* @author Costin Leau
* @author Juergen Hoeller
@@ -36,7 +36,7 @@ import org.springframework.util.ObjectUtils;
* @since 3.1
*/
@SuppressWarnings("serial")
final class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
@Nullable
private CacheOperationSource cacheOperationSource;
@@ -78,7 +78,7 @@ final class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut imp
* {@link ClassFilter} that delegates to {@link CacheOperationSource#isCandidateClass}
* for filtering classes whose methods are not worth searching to begin with.
*/
private final class CacheOperationSourceClassFilter implements ClassFilter {
private class CacheOperationSourceClassFilter implements ClassFilter {
@Override
public boolean matches(Class<?> clazz) {
@@ -88,7 +88,6 @@ final class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut imp
return (cacheOperationSource == null || cacheOperationSource.isCandidateClass(clazz));
}
@Nullable
private CacheOperationSource getCacheOperationSource() {
return cacheOperationSource;
}
@@ -96,7 +95,7 @@ final class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut imp
@Override
public boolean equals(@Nullable Object other) {
return (this == other || (other instanceof CacheOperationSourceClassFilter that &&
ObjectUtils.nullSafeEquals(getCacheOperationSource(), that.getCacheOperationSource())));
ObjectUtils.nullSafeEquals(cacheOperationSource, that.getCacheOperationSource())));
}
@Override
@@ -106,8 +105,9 @@ final class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut imp
@Override
public String toString() {
return CacheOperationSourceClassFilter.class.getName() + ": " + getCacheOperationSource();
return CacheOperationSourceClassFilter.class.getName() + ": " + cacheOperationSource;
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -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 before regular {@code Lifecycle}
* let {@code stop()} callbacks execute after regular {@code Lifecycle}
* implementations.
* @see #isAutoStartup()
* @see #start()
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 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.
@@ -73,6 +73,7 @@ final class ConfigurationClass {
* Create a new {@link ConfigurationClass} with the given name.
* @param metadataReader reader used to parse the underlying {@link Class}
* @param beanName must not be {@code null}
* @see ConfigurationClass#ConfigurationClass(Class, ConfigurationClass)
*/
ConfigurationClass(MetadataReader metadataReader, String beanName) {
Assert.notNull(beanName, "Bean name must not be null");
@@ -86,10 +87,10 @@ final class ConfigurationClass {
* using the {@link Import} annotation or automatically processed as a nested
* configuration class (if importedBy is not {@code null}).
* @param metadataReader reader used to parse the underlying {@link Class}
* @param importedBy the configuration class importing this one
* @param importedBy the configuration class importing this one or {@code null}
* @since 3.1.1
*/
ConfigurationClass(MetadataReader metadataReader, ConfigurationClass importedBy) {
ConfigurationClass(MetadataReader metadataReader, @Nullable ConfigurationClass importedBy) {
this.metadata = metadataReader.getAnnotationMetadata();
this.resource = metadataReader.getResource();
this.importedBy.add(importedBy);
@@ -99,6 +100,7 @@ final class ConfigurationClass {
* Create a new {@link ConfigurationClass} with the given name.
* @param clazz the underlying {@link Class} to represent
* @param beanName name of the {@code @Configuration} class bean
* @see ConfigurationClass#ConfigurationClass(Class, ConfigurationClass)
*/
ConfigurationClass(Class<?> clazz, String beanName) {
Assert.notNull(beanName, "Bean name must not be null");
@@ -112,10 +114,10 @@ final class ConfigurationClass {
* using the {@link Import} annotation or automatically processed as a nested
* configuration class (if imported is {@code true}).
* @param clazz the underlying {@link Class} to represent
* @param importedBy the configuration class importing this one
* @param importedBy the configuration class importing this one (or {@code null})
* @since 3.1.1
*/
ConfigurationClass(Class<?> clazz, ConfigurationClass importedBy) {
ConfigurationClass(Class<?> clazz, @Nullable ConfigurationClass importedBy) {
this.metadata = AnnotationMetadata.introspect(clazz);
this.resource = new DescriptiveResource(clazz.getName());
this.importedBy.add(importedBy);
@@ -125,6 +127,7 @@ final class ConfigurationClass {
* Create a new {@link ConfigurationClass} with the given name.
* @param metadata the metadata for the underlying class to represent
* @param beanName name of the {@code @Configuration} class bean
* @see ConfigurationClass#ConfigurationClass(Class, ConfigurationClass)
*/
ConfigurationClass(AnnotationMetadata metadata, String beanName) {
Assert.notNull(beanName, "Bean name must not be null");
@@ -146,12 +149,12 @@ final class ConfigurationClass {
return ClassUtils.getShortName(getMetadata().getClassName());
}
void setBeanName(@Nullable String beanName) {
void setBeanName(String beanName) {
this.beanName = beanName;
}
@Nullable
String getBeanName() {
public String getBeanName() {
return this.beanName;
}
@@ -161,7 +164,7 @@ final class ConfigurationClass {
* @since 3.1.1
* @see #getImportedBy()
*/
boolean isImported() {
public boolean isImported() {
return !this.importedBy.isEmpty();
}
@@ -195,10 +198,6 @@ final class ConfigurationClass {
this.importedResources.put(importedResource, readerClass);
}
Map<String, Class<? extends BeanDefinitionReader>> getImportedResources() {
return this.importedResources;
}
void addImportBeanDefinitionRegistrar(ImportBeanDefinitionRegistrar registrar, AnnotationMetadata importingClassMetadata) {
this.importBeanDefinitionRegistrars.put(registrar, importingClassMetadata);
}
@@ -207,6 +206,10 @@ final class ConfigurationClass {
return this.importBeanDefinitionRegistrars;
}
Map<String, Class<? extends BeanDefinitionReader>> getImportedResources() {
return this.importedResources;
}
void validate(ProblemReporter problemReporter) {
Map<String, Object> attributes = this.metadata.getAnnotationAttributes(Configuration.class.getName());
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -225,6 +225,7 @@ class ConfigurationClassEnhancer {
};
return new TransformingClassGenerator(cg, transformer);
}
}
@@ -333,7 +334,6 @@ class ConfigurationClassEnhancer {
return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
}
@Nullable
private Object resolveBeanReference(Method beanMethod, Object[] beanMethodArgs,
ConfigurableBeanFactory beanFactory, String beanName) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 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.
@@ -386,11 +386,11 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
});
// Detect any custom bean name generation strategy supplied through the enclosing application context
SingletonBeanRegistry singletonRegistry = null;
if (registry instanceof SingletonBeanRegistry sbr) {
singletonRegistry = sbr;
SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry _sbr) {
sbr = _sbr;
if (!this.localBeanNameGeneratorSet) {
BeanNameGenerator generator = (BeanNameGenerator) singletonRegistry.getSingleton(
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
if (generator != null) {
this.componentScanBeanNameGenerator = generator;
@@ -451,8 +451,8 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
while (!candidates.isEmpty());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
if (singletonRegistry != null && !singletonRegistry.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
singletonRegistry.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}
// Store the PropertySourceDescriptors to contribute them Ahead-of-time if necessary
@@ -550,7 +550,6 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
}
@Override
@Nullable
public PropertyValues postProcessProperties(@Nullable PropertyValues pvs, Object bean, String beanName) {
// Inject the BeanFactory before AutowiredAnnotationBeanPostProcessor's
// postProcessProperties method attempts to autowire other configuration beans.
@@ -646,8 +645,8 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
}
return mappings;
}
}
}
private static class PropertySourcesAotContribution implements BeanFactoryInitializationAotContribution {
@@ -744,14 +743,15 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
return nonNull.get();
}
}
}
}
private static class ConfigurationClassProxyBeanRegistrationCodeFragments extends BeanRegistrationCodeFragmentsDecorator {
private final Class<?> proxyClass;
public ConfigurationClassProxyBeanRegistrationCodeFragments(BeanRegistrationCodeFragments codeFragments, Class<?> proxyClass) {
public ConfigurationClassProxyBeanRegistrationCodeFragments(BeanRegistrationCodeFragments codeFragments,
Class<?> proxyClass) {
super(codeFragments);
this.proxyClass = proxyClass;
}
@@ -759,7 +759,6 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
@Override
public CodeBlock generateSetBeanDefinitionPropertiesCode(GenerationContext generationContext,
BeanRegistrationCode beanRegistrationCode, RootBeanDefinition beanDefinition, Predicate<String> attributeFilter) {
CodeBlock.Builder code = CodeBlock.builder();
code.add(super.generateSetBeanDefinitionPropertiesCode(generationContext,
beanRegistrationCode, beanDefinition, attributeFilter));
@@ -772,7 +771,6 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
public CodeBlock generateInstanceSupplierCode(GenerationContext generationContext,
BeanRegistrationCode beanRegistrationCode, Executable constructorOrFactoryMethod,
boolean allowDirectSupplierShortcut) {
Executable executableToUse = proxyExecutable(generationContext.getRuntimeHints(), constructorOrFactoryMethod);
return super.generateInstanceSupplierCode(generationContext, beanRegistrationCode,
executableToUse, allowDirectSupplierShortcut);
@@ -790,6 +788,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
}
return userExecutable;
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,8 +39,7 @@ import org.springframework.beans.factory.support.RegisteredBean;
*/
class ReflectiveProcessorBeanFactoryInitializationAotProcessor implements BeanFactoryInitializationAotProcessor {
private static final ReflectiveRuntimeHintsRegistrar registrar = new ReflectiveRuntimeHintsRegistrar();
private static final ReflectiveRuntimeHintsRegistrar REGISTRAR = new ReflectiveRuntimeHintsRegistrar();
@Override
public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) {
@@ -50,9 +49,7 @@ class ReflectiveProcessorBeanFactoryInitializationAotProcessor implements BeanFa
return new ReflectiveProcessorBeanFactoryInitializationAotContribution(beanTypes);
}
private static class ReflectiveProcessorBeanFactoryInitializationAotContribution
implements BeanFactoryInitializationAotContribution {
private static class ReflectiveProcessorBeanFactoryInitializationAotContribution implements BeanFactoryInitializationAotContribution {
private final Class<?>[] types;
@@ -63,8 +60,9 @@ class ReflectiveProcessorBeanFactoryInitializationAotProcessor implements BeanFa
@Override
public void applyTo(GenerationContext generationContext, BeanFactoryInitializationCode beanFactoryInitializationCode) {
RuntimeHints runtimeHints = generationContext.getRuntimeHints();
registrar.registerRuntimeHints(runtimeHints, this.types);
REGISTRAR.registerRuntimeHints(runtimeHints, this.types);
}
}
}
@@ -135,6 +135,17 @@ import org.springframework.util.ReflectionUtils;
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
/**
* The name of the {@link LifecycleProcessor} bean in the context.
* If none is supplied, a {@link DefaultLifecycleProcessor} is used.
* @since 3.0
* @see org.springframework.context.LifecycleProcessor
* @see org.springframework.context.support.DefaultLifecycleProcessor
* @see #start()
* @see #stop()
*/
public static final String LIFECYCLE_PROCESSOR_BEAN_NAME = "lifecycleProcessor";
/**
* The name of the {@link MessageSource} bean in the context.
* If none is supplied, message resolution is delegated to the parent.
@@ -155,17 +166,6 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
*/
public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
/**
* The name of the {@link LifecycleProcessor} bean in the context.
* If none is supplied, a {@link DefaultLifecycleProcessor} is used.
* @since 3.0
* @see org.springframework.context.LifecycleProcessor
* @see org.springframework.context.support.DefaultLifecycleProcessor
* @see #start()
* @see #stop()
*/
public static final String LIFECYCLE_PROCESSOR_BEAN_NAME = "lifecycleProcessor";
static {
// Eagerly load the ContextClosedEvent class to avoid weird classloader issues
@@ -796,9 +796,8 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
}
/**
* Initialize the {@link MessageSource}.
* <p>Uses parent's {@code MessageSource} if none defined in this context.
* @see #MESSAGE_SOURCE_BEAN_NAME
* Initialize the MessageSource.
* Use parent's if none defined in this context.
*/
protected void initMessageSource() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
@@ -828,9 +827,8 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
}
/**
* Initialize the {@link ApplicationEventMulticaster}.
* <p>Uses {@link SimpleApplicationEventMulticaster} if none defined in the context.
* @see #APPLICATION_EVENT_MULTICASTER_BEAN_NAME
* Initialize the ApplicationEventMulticaster.
* Uses SimpleApplicationEventMulticaster if none defined in the context.
* @see org.springframework.context.event.SimpleApplicationEventMulticaster
*/
protected void initApplicationEventMulticaster() {
@@ -853,16 +851,15 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
}
/**
* Initialize the {@link LifecycleProcessor}.
* <p>Uses {@link DefaultLifecycleProcessor} if none defined in the context.
* @since 3.0
* @see #LIFECYCLE_PROCESSOR_BEAN_NAME
* Initialize the LifecycleProcessor.
* Uses DefaultLifecycleProcessor if none defined in the context.
* @see org.springframework.context.support.DefaultLifecycleProcessor
*/
protected void initLifecycleProcessor() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(LIFECYCLE_PROCESSOR_BEAN_NAME)) {
this.lifecycleProcessor = beanFactory.getBean(LIFECYCLE_PROCESSOR_BEAN_NAME, LifecycleProcessor.class);
this.lifecycleProcessor =
beanFactory.getBean(LIFECYCLE_PROCESSOR_BEAN_NAME, LifecycleProcessor.class);
if (logger.isTraceEnabled()) {
logger.trace("Using LifecycleProcessor [" + this.lifecycleProcessor + "]");
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -126,7 +126,6 @@ public abstract class AbstractRefreshableApplicationContext extends AbstractAppl
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
beanFactory.setApplicationStartup(getApplicationStartup());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
this.beanFactory = beanFactory;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 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.
@@ -45,11 +45,7 @@ import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* 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}.
* Default implementation of the {@link LifecycleProcessor} strategy.
*
* @author Mark Fisher
* @author Juergen Hoeller
@@ -318,8 +314,6 @@ 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 {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 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,7 +18,6 @@ package org.springframework.context.support;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
@@ -424,37 +423,16 @@ public class GenericApplicationContext extends AbstractApplicationContext implem
PostProcessorRegistrationDelegate.loadBeanPostProcessors(
this.beanFactory, SmartInstantiationAwareBeanPostProcessor.class);
List<String> lazyBeans = new ArrayList<>();
// First round: non-lazy singleton beans in definition order,
// matching preInstantiateSingletons.
for (String beanName : this.beanFactory.getBeanDefinitionNames()) {
BeanDefinition bd = getBeanDefinition(beanName);
if (bd.isSingleton() && !bd.isLazyInit()) {
preDetermineBeanType(beanName, bpps, runtimeHints);
}
else {
lazyBeans.add(beanName);
}
}
// Second round: lazy singleton beans and scoped beans.
for (String beanName : lazyBeans) {
preDetermineBeanType(beanName, bpps, runtimeHints);
}
}
private void preDetermineBeanType(String beanName, List<SmartInstantiationAwareBeanPostProcessor> bpps,
RuntimeHints runtimeHints) {
Class<?> beanType = this.beanFactory.getType(beanName);
if (beanType != null) {
ClassHintUtils.registerProxyIfNecessary(beanType, runtimeHints);
for (SmartInstantiationAwareBeanPostProcessor bpp : bpps) {
Class<?> newBeanType = bpp.determineBeanType(beanType, beanName);
if (newBeanType != beanType) {
ClassHintUtils.registerProxyIfNecessary(newBeanType, runtimeHints);
beanType = newBeanType;
Class<?> beanType = this.beanFactory.getType(beanName);
if (beanType != null) {
ClassHintUtils.registerProxyIfNecessary(beanType, runtimeHints);
for (SmartInstantiationAwareBeanPostProcessor bpp : bpps) {
Class<?> newBeanType = bpp.determineBeanType(beanType, beanName);
if (newBeanType != beanType) {
ClassHintUtils.registerProxyIfNecessary(newBeanType, runtimeHints);
beanType = newBeanType;
}
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,7 +27,6 @@ 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;
@@ -401,7 +400,7 @@ public class ReloadableResourceBundleMessageSource extends AbstractResourceBased
/**
* Refresh the PropertiesHolder for the given bundle filename.
* <p>The holder can be {@code null} if not cached before, or a timed-out cache entry
* 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
@@ -562,7 +561,7 @@ public class ReloadableResourceBundleMessageSource extends AbstractResourceBased
private volatile long refreshTimestamp = -2;
private final Lock refreshLock = new ReentrantLock();
private final ReentrantLock refreshLock = new ReentrantLock();
/** Cache to hold already generated MessageFormats per message code. */
private final ConcurrentMap<String, Map<Locale, MessageFormat>> cachedMessageFormats =
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -98,8 +98,8 @@ public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAd
* applying the corresponding default if a supplier is not resolvable.
* @since 5.1
*/
public void configure(@Nullable Supplier<Executor> executor,
@Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {
public void configure(
@Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {
this.executor = executor;
this.exceptionHandler = exceptionHandler;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 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.
@@ -134,6 +134,11 @@ public class ConcurrentTaskExecutor implements AsyncListenableTaskExecutor, Sche
* execution callback (which may be a wrapper around the user-supplied task).
* <p>The primary use case is to set some execution context around the task's
* invocation, or to provide some monitoring/statistics for task execution.
* <p><b>NOTE:</b> Exception handling in {@code TaskDecorator} implementations
* is limited to plain {@code Runnable} execution via {@code execute} calls.
* In case of {@code #submit} calls, the exposed {@code Runnable} will be a
* {@code FutureTask} which does not propagate any exceptions; you might
* have to cast it and call {@code Future#get} to evaluate exceptions.
* @since 4.3
*/
public final void setTaskDecorator(TaskDecorator taskDecorator) {
@@ -174,10 +179,11 @@ public class ConcurrentTaskExecutor implements AsyncListenableTaskExecutor, Sche
}
private TaskExecutorAdapter getAdaptedExecutor(Executor originalExecutor) {
TaskExecutorAdapter adapter =
(managedExecutorServiceClass != null && managedExecutorServiceClass.isInstance(originalExecutor) ?
new ManagedTaskExecutorAdapter(originalExecutor) : new TaskExecutorAdapter(originalExecutor));
private TaskExecutorAdapter getAdaptedExecutor(Executor concurrentExecutor) {
if (managedExecutorServiceClass != null && managedExecutorServiceClass.isInstance(concurrentExecutor)) {
return new ManagedTaskExecutorAdapter(concurrentExecutor);
}
TaskExecutorAdapter adapter = new TaskExecutorAdapter(concurrentExecutor);
if (this.taskDecorator != null) {
adapter.setTaskDecorator(this.taskDecorator);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 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.
@@ -183,7 +183,6 @@ public class ConcurrentTaskScheduler extends ConcurrentTaskExecutor implements T
* @see Clock#systemDefaultZone()
*/
public void setClock(Clock clock) {
Assert.notNull(clock, "Clock must not be null");
this.clock = clock;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 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.
@@ -73,7 +73,7 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac
/**
* Set the ThreadFactory to use for the ExecutorService's thread pool.
* The default is the underlying ExecutorService's default thread factory.
* 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>The default is {@code false}, shutting down immediately through interrupting
* <p>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,8 +119,6 @@ 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;
@@ -239,7 +237,7 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac
}
/**
* Cancel the given remaining task which never commenced execution,
* Cancel the given remaining task which never commended execution,
* as returned from {@link ExecutorService#shutdownNow()}.
* @param task the task to cancel (typically a {@link RunnableFuture})
* @since 5.0.5
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -46,9 +46,8 @@ import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureTask;
/**
* A standard implementation of Spring's {@link TaskScheduler} interface, wrapping
* a native {@link java.util.concurrent.ScheduledThreadPoolExecutor} and providing
* all applicable configuration options for it.
* Implementation of Spring's {@link TaskScheduler} interface, wrapping
* a native {@link java.util.concurrent.ScheduledThreadPoolExecutor}.
*
* @author Juergen Hoeller
* @author Mark Fisher
@@ -159,7 +158,6 @@ public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport
* @see Clock#systemDefaultZone()
*/
public void setClock(Clock clock) {
Assert.notNull(clock, "Clock must not be null");
this.clock = clock;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 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.
@@ -29,15 +29,17 @@ 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;
@@ -46,14 +48,16 @@ final class BitsCronField extends CronField {
super(type);
}
/**
* Return a {@code BitsCronField} enabled for 0 nanoseconds.
*/
private static BitsCronField forZeroNanos() {
BitsCronField field = new BitsCronField(Type.NANO);
field.setBit(0);
return field;
public static BitsCronField zeroNanos() {
if (zeroNanos == null) {
BitsCronField field = new BitsCronField(Type.NANO);
field.setBit(0);
zeroNanos = field;
}
return zeroNanos;
}
/**
@@ -104,6 +108,7 @@ final class BitsCronField extends CronField {
return result;
}
private static BitsCronField parseDate(String value, BitsCronField.Type type) {
if (value.equals("?")) {
value = "*";
@@ -169,7 +174,6 @@ final class BitsCronField extends CronField {
}
}
@Nullable
@Override
public <T extends Temporal & Comparable<? super T>> T nextOrSame(T temporal) {
@@ -213,6 +217,7 @@ final class BitsCronField extends CronField {
else {
return -1;
}
}
private void setBits(ValueRange range) {
@@ -242,14 +247,7 @@ final class BitsCronField extends CronField {
}
private void clearBit(int 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));
this.bits &= ~(1L << index);
}
@Override
@@ -257,6 +255,17 @@ 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());
@@ -29,14 +29,9 @@ 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>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.
* <p>{@code CronExpression} instances are created through
* {@link #parse(String)}; the next match is determined with
* {@link #next(Temporal)}.
*
* @author Arjen Poutsma
* @since 5.3
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,24 +29,17 @@ import org.springframework.util.StringUtils;
/**
* Single field in a cron pattern. Created using the {@code parse*} methods,
* 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.
* main and only entry point is {@link #nextOrSame(Temporal)}.
*
* @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;
@@ -55,12 +48,11 @@ abstract class CronField {
this.type = type;
}
/**
* Return a {@code CronField} enabled for 0 nanoseconds.
*/
public static CronField zeroNanos() {
return BitsCronField.ZERO_NANOS;
return BitsCronField.zeroNanos();
}
/**
@@ -177,7 +169,6 @@ 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),
@@ -186,18 +177,21 @@ 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
@@ -27,14 +27,8 @@ import org.springframework.scheduling.TriggerContext;
import org.springframework.util.Assert;
/**
* {@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.
* {@link Trigger} implementation for cron expressions.
* Wraps a {@link CronExpression}.
*
* @author Juergen Hoeller
* @author Arjen Poutsma
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 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.
@@ -30,14 +30,9 @@ import org.springframework.util.Assert;
/**
* Extension of {@link CronField} for
* <a href="https://www.quartz-scheduler.org">Quartz</a>-specific fields.
* 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.
* <p>Created using the {@code parse*} methods, uses a {@link TemporalAdjuster}
* internally.
*
* @author Arjen Poutsma
* @since 5.3
@@ -66,9 +61,8 @@ final class QuartzCronField extends CronField {
this.rollForwardType = rollForwardType;
}
/**
* Determine whether the given value is a Quartz day-of-month field.
* Returns whether the given value is a Quartz day-of-month field.
*/
public static boolean isQuartzDaysOfMonthField(String value) {
return value.contains("L") || value.contains("W");
@@ -86,14 +80,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 + "'");
@@ -111,7 +105,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);
@@ -122,7 +116,7 @@ final class QuartzCronField extends CronField {
}
/**
* Determine whether the given value is a Quartz day-of-week field.
* Returns whether the given value is a Quartz day-of-week field.
*/
public static boolean isQuartzDaysOfWeekField(String value) {
return value.contains("L") || value.contains("#");
@@ -144,7 +138,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);
}
@@ -166,6 +160,7 @@ 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);
}
@@ -175,13 +170,14 @@ 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) {
throw new IllegalArgumentException(ex.getMessage() + " '" + value + "'", ex);
String msg = ex.getMessage() + " '" + value + "'";
throw new IllegalArgumentException(msg, ex);
}
}
@@ -220,10 +216,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 {
@@ -260,10 +256,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;
@@ -336,7 +332,6 @@ final class QuartzCronField extends CronField {
}
}
@Override
public <T extends Temporal & Comparable<? super T>> T nextOrSame(T temporal) {
T result = adjust(temporal);
@@ -353,6 +348,7 @@ final class QuartzCronField extends CronField {
return result;
}
@Nullable
@SuppressWarnings("unchecked")
private <T extends Temporal & Comparable<? super T>> T adjust(T temporal) {
@@ -360,20 +356,27 @@ 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 + "'";
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,7 +39,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Juergen Hoeller
* @author Chris Beams
*/
class ScopedProxyTests {
public class ScopedProxyTests {
private static final Class<?> CLASS = ScopedProxyTests.class;
private static final String CLASSNAME = CLASS.getSimpleName();
@@ -51,24 +51,27 @@ class ScopedProxyTests {
@Test // SPR-2108
void testProxyAssignable() {
public void testProxyAssignable() throws Exception {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(MAP_CONTEXT);
Object baseMap = bf.getBean("singletonMap");
assertThat(baseMap instanceof Map).isTrue();
boolean condition = baseMap instanceof Map;
assertThat(condition).isTrue();
}
@Test
void testSimpleProxy() {
public void testSimpleProxy() throws Exception {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(MAP_CONTEXT);
Object simpleMap = bf.getBean("simpleMap");
assertThat(simpleMap instanceof Map).isTrue();
assertThat(simpleMap instanceof HashMap).isTrue();
boolean condition1 = simpleMap instanceof Map;
assertThat(condition1).isTrue();
boolean condition = simpleMap instanceof HashMap;
assertThat(condition).isTrue();
}
@Test
void testScopedOverride() {
public void testScopedOverride() throws Exception {
GenericApplicationContext ctx = new GenericApplicationContext();
new XmlBeanDefinitionReader(ctx).loadBeanDefinitions(OVERRIDE_CONTEXT);
SimpleMapScope scope = new SimpleMapScope();
@@ -84,7 +87,7 @@ class ScopedProxyTests {
}
@Test
void testJdkScopedProxy() throws Exception {
public void testJdkScopedProxy() throws Exception {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(TESTBEAN_CONTEXT);
bf.setSerializationId("X");
@@ -94,7 +97,8 @@ class ScopedProxyTests {
ITestBean bean = (ITestBean) bf.getBean("testBean");
assertThat(bean).isNotNull();
assertThat(AopUtils.isJdkDynamicProxy(bean)).isTrue();
assertThat(bean instanceof ScopedObject).isTrue();
boolean condition1 = bean instanceof ScopedObject;
assertThat(condition1).isTrue();
ScopedObject scoped = (ScopedObject) bean;
assertThat(scoped.getTargetObject().getClass()).isEqualTo(TestBean.class);
bean.setAge(101);
@@ -106,7 +110,8 @@ class ScopedProxyTests {
assertThat(deserialized).isNotNull();
assertThat(AopUtils.isJdkDynamicProxy(deserialized)).isTrue();
assertThat(bean.getAge()).isEqualTo(101);
assertThat(deserialized instanceof ScopedObject).isTrue();
boolean condition = deserialized instanceof ScopedObject;
assertThat(condition).isTrue();
ScopedObject scopedDeserialized = (ScopedObject) deserialized;
assertThat(scopedDeserialized.getTargetObject().getClass()).isEqualTo(TestBean.class);
@@ -114,7 +119,7 @@ class ScopedProxyTests {
}
@Test
void testCglibScopedProxy() throws Exception {
public void testCglibScopedProxy() throws Exception {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(LIST_CONTEXT);
bf.setSerializationId("Y");
@@ -123,7 +128,8 @@ class ScopedProxyTests {
TestBean tb = (TestBean) bf.getBean("testBean");
assertThat(AopUtils.isCglibProxy(tb.getFriends())).isTrue();
assertThat(tb.getFriends() instanceof ScopedObject).isTrue();
boolean condition1 = tb.getFriends() instanceof ScopedObject;
assertThat(condition1).isTrue();
ScopedObject scoped = (ScopedObject) tb.getFriends();
assertThat(scoped.getTargetObject().getClass()).isEqualTo(ArrayList.class);
tb.getFriends().add("myFriend");
@@ -131,11 +137,12 @@ class ScopedProxyTests {
assertThat(scope.getMap().containsKey("scopedTarget.scopedList")).isTrue();
assertThat(scope.getMap().get("scopedTarget.scopedList").getClass()).isEqualTo(ArrayList.class);
ArrayList<Object> deserialized = (ArrayList<Object>) SerializationTestUtils.serializeAndDeserialize(tb.getFriends());
ArrayList<?> deserialized = (ArrayList<?>) SerializationTestUtils.serializeAndDeserialize(tb.getFriends());
assertThat(deserialized).isNotNull();
assertThat(AopUtils.isCglibProxy(deserialized)).isTrue();
assertThat(deserialized).contains("myFriend");
assertThat(deserialized instanceof ScopedObject).isTrue();
assertThat(deserialized.contains("myFriend")).isTrue();
boolean condition = deserialized instanceof ScopedObject;
assertThat(condition).isTrue();
ScopedObject scopedDeserialized = (ScopedObject) deserialized;
assertThat(scopedDeserialized.getTargetObject().getClass()).isEqualTo(ArrayList.class);
@@ -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,13 +81,12 @@ 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");
});
}
@@ -101,10 +100,9 @@ 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
@@ -207,13 +205,12 @@ 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
@@ -230,13 +227,12 @@ 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
@@ -253,10 +249,9 @@ 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
@@ -379,13 +374,12 @@ 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
@@ -457,13 +451,12 @@ 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
@@ -514,13 +507,12 @@ 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
@@ -582,10 +574,9 @@ 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"));
}
@@ -761,7 +752,7 @@ public class QualifierAnnotationAutowireContextTests {
@Qualifier
@interface TestQualifierWithMultipleAttributes {
String[] value() default "default";
String value() default "default";
int number();
}
@@ -58,10 +58,9 @@ 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
@@ -192,10 +191,9 @@ 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
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 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.
@@ -307,8 +307,8 @@ class AnnotationConfigApplicationContextTests {
assertThat(ObjectUtils.containsElement(context.getBeanNamesForType(BeanC.class), "c")).isTrue();
assertThat(context.getBeansOfType(BeanA.class)).isEmpty();
assertThat(context.getBeansOfType(BeanB.class).values()).singleElement().isSameAs(context.getBean(BeanB.class));
assertThat(context.getBeansOfType(BeanC.class).values()).singleElement().isSameAs(context.getBean(BeanC.class));
assertThat(context.getBeansOfType(BeanB.class).values().iterator().next()).isSameAs(context.getBean(BeanB.class));
assertThat(context.getBeansOfType(BeanC.class).values().iterator().next()).isSameAs(context.getBean(BeanC.class));
assertThatExceptionOfType(NoSuchBeanDefinitionException.class)
.isThrownBy(() -> context.getBeanFactory().resolveNamedBean(BeanA.class));
@@ -409,17 +409,15 @@ class AnnotationConfigApplicationContextTests {
bd2.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class, ResolvableType.forClassWithGenerics(GenericHolder.class, Integer.class)));
bd2.setLazyInit(true);
context.registerBeanDefinition("fb2", bd2);
RootBeanDefinition bd3 = new RootBeanDefinition(FactoryBeanInjectionPoints.class);
bd3.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
context.registerBeanDefinition("ip", bd3);
context.registerBeanDefinition("ip", new RootBeanDefinition(FactoryBeanInjectionPoints.class));
context.refresh();
assertThat(context.getBean("ip", FactoryBeanInjectionPoints.class).factoryBean).isSameAs(context.getBean("&fb1"));
assertThat(context.getBean("ip", FactoryBeanInjectionPoints.class).factoryResult).isSameAs(context.getBean("fb1"));
assertThat(context.getType("&fb1")).isEqualTo(GenericHolderFactoryBean.class);
assertThat(context.getType("fb1")).isEqualTo(GenericHolder.class);
assertThat(context.getBeanNamesForType(FactoryBean.class)).hasSize(2);
assertThat(context.getBeanNamesForType(GenericHolderFactoryBean.class)).hasSize(1);
assertThat(context.getBean("ip", FactoryBeanInjectionPoints.class).factoryBean).isSameAs(context.getBean("&fb1"));
assertThat(context.getBean("ip", FactoryBeanInjectionPoints.class).factoryResult).isSameAs(context.getBean("fb1"));
}
@Test
@@ -427,7 +425,7 @@ class AnnotationConfigApplicationContextTests {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
RootBeanDefinition bd1 = new RootBeanDefinition();
bd1.setBeanClass(GenericHolderFactoryBean.class);
bd1.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class, ResolvableType.forClassWithGenerics(GenericHolder.class, String.class)));
bd1.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class, ResolvableType.forClassWithGenerics(GenericHolder.class, Object.class)));
bd1.setLazyInit(true);
context.registerBeanDefinition("fb1", bd1);
RootBeanDefinition bd2 = new RootBeanDefinition();
@@ -435,17 +433,13 @@ class AnnotationConfigApplicationContextTests {
bd2.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class, ResolvableType.forClassWithGenerics(GenericHolder.class, Integer.class)));
bd2.setLazyInit(true);
context.registerBeanDefinition("fb2", bd2);
RootBeanDefinition bd3 = new RootBeanDefinition(FactoryBeanInjectionPoints.class);
bd3.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
context.registerBeanDefinition("ip", bd3);
context.registerBeanDefinition("ip", new RootBeanDefinition(FactoryResultInjectionPoint.class));
context.refresh();
assertThat(context.getBean("ip", FactoryResultInjectionPoint.class).factoryResult).isSameAs(context.getBean("fb1"));
assertThat(context.getBean("ip", FactoryResultInjectionPoint.class).factoryResult).isSameAs(context.getBean("fb1"));
assertThat(context.getType("&fb1")).isEqualTo(GenericHolderFactoryBean.class);
assertThat(context.getType("fb1")).isEqualTo(GenericHolder.class);
assertThat(context.getBeanNamesForType(FactoryBean.class)).hasSize(2);
assertThat(context.getBeanNamesForType(FactoryBean.class)).hasSize(2);
assertThat(context.getBean("ip", FactoryResultInjectionPoint.class).factoryResult).isSameAs(context.getBean("fb1"));
}
@Test
@@ -459,17 +453,14 @@ class AnnotationConfigApplicationContextTests {
bd2.setBeanClass(UntypedFactoryBean.class);
bd2.setTargetType(ResolvableType.forClassWithGenerics(GenericHolder.class, Integer.class));
context.registerBeanDefinition("fb2", bd2);
RootBeanDefinition bd3 = new RootBeanDefinition(FactoryResultInjectionPoint.class);
bd3.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
context.registerBeanDefinition("ip", bd3);
context.registerBeanDefinition("ip", new RootBeanDefinition(FactoryResultInjectionPoint.class));
context.refresh();
assertThat(context.getBean("ip", FactoryResultInjectionPoint.class).factoryResult).isSameAs(context.getBean("fb1"));
assertThat(context.getBean("ip", FactoryResultInjectionPoint.class).factoryResult).isSameAs(context.getBean("fb1"));
assertThat(context.getType("&fb1")).isEqualTo(GenericHolderFactoryBean.class);
assertThat(context.getType("fb1")).isEqualTo(GenericHolder.class);
assertThat(context.getBeanNamesForType(FactoryBean.class)).hasSize(2);
assertThat(context.getBeanNamesForType(GenericHolderFactoryBean.class)).hasSize(1);
assertThat(context.getBean("ip", FactoryResultInjectionPoint.class).factoryResult).isSameAs(context.getBean("fb1"));
}
@Test
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 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.
@@ -268,7 +268,6 @@ public class ClassPathBeanDefinitionScannerTests {
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
scanner.setIncludeAnnotationConfig(false);
scanner.scan("org.springframework.context.annotation2");
assertThatIllegalStateException().isThrownBy(() -> scanner.scan(BASE_PACKAGE))
.withMessageContaining("myNamedDao")
.withMessageContaining(NamedStubDao.class.getName())
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -99,8 +99,8 @@ class ConfigurationClassPostProcessorAotContributionTests {
initializer.accept(freshBeanFactory);
freshContext.refresh();
assertThat(freshBeanFactory.getBeanPostProcessors()).filteredOn(ImportAwareAotBeanPostProcessor.class::isInstance)
.singleElement().satisfies(postProcessor ->
assertPostProcessorEntry(postProcessor, ImportAwareConfiguration.class, ImportConfiguration.class));
.singleElement().satisfies(postProcessor -> assertPostProcessorEntry(postProcessor, ImportAwareConfiguration.class,
ImportConfiguration.class));
freshContext.close();
});
}
@@ -117,8 +117,8 @@ class ConfigurationClassPostProcessorAotContributionTests {
freshContext.refresh();
TestAwareCallbackBean bean = freshContext.getBean(TestAwareCallbackBean.class);
assertThat(bean.instances).hasSize(2);
assertThat(bean.instances).element(0).isEqualTo(freshContext);
assertThat(bean.instances).element(1).isInstanceOfSatisfying(AnnotationMetadata.class, metadata ->
assertThat(bean.instances.get(0)).isEqualTo(freshContext);
assertThat(bean.instances.get(1)).isInstanceOfSatisfying(AnnotationMetadata.class, metadata ->
assertThat(metadata.getClassName()).isEqualTo(TestAwareCallbackConfiguration.class.getName()));
freshContext.close();
});
@@ -236,14 +236,13 @@ class ConfigurationClassPostProcessorAotContributionTests {
}
@Override
public void afterPropertiesSet() {
public void afterPropertiesSet() throws Exception {
Assert.notNull(this.metadata, "Metadata was not injected");
}
}
}
@Nested
class PropertySourceTests {
@@ -363,8 +362,8 @@ class ConfigurationClassPostProcessorAotContributionTests {
static class PropertySourceWithCustomFactoryConfiguration {
}
}
}
@Nested
class ConfigurationClassProxyTests {
@@ -385,13 +384,14 @@ class ConfigurationClassPostProcessorAotContributionTests {
getRegisteredBean(CglibConfiguration.class))).isNotNull();
}
private RegisteredBean getRegisteredBean(Class<?> bean) {
this.beanFactory.registerBeanDefinition("test", new RootBeanDefinition(bean));
this.processor.postProcessBeanFactory(this.beanFactory);
return RegisteredBean.of(this.beanFactory, "test");
}
}
}
@Nullable
private BeanFactoryInitializationAotContribution getContribution(Class<?>... types) {
@@ -410,8 +410,8 @@ class ConfigurationClassPostProcessorAotContributionTests {
.containsExactly(entry(key.getName(), value.getName()));
}
static class CustomPropertySourcesFactory extends DefaultPropertySourceFactory {
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,8 +35,6 @@ import org.springframework.aop.support.AopUtils;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.ObjectProvider;
@@ -323,8 +321,8 @@ class ConfigurationClassPostProcessorTests {
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
pp.setEnvironment(new StandardEnvironment());
pp.postProcessBeanFactory(beanFactory);
assertThatExceptionOfType(NoSuchBeanDefinitionException.class)
.isThrownBy(() -> beanFactory.getBean(SimpleComponent.class));
assertThatExceptionOfType(NoSuchBeanDefinitionException.class).isThrownBy(() ->
beanFactory.getBean(SimpleComponent.class));
}
@Test
@@ -374,11 +372,11 @@ class ConfigurationClassPostProcessorTests {
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(SingletonBeanConfig.class));
beanFactory.setAllowBeanDefinitionOverriding(false);
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
assertThatExceptionOfType(BeanDefinitionStoreException.class)
.isThrownBy(() -> pp.postProcessBeanFactory(beanFactory))
.withMessageContaining("bar")
.withMessageContaining("SingletonBeanConfig")
.withMessageContaining(TestBean.class.getName());
assertThatExceptionOfType(BeanDefinitionStoreException.class).isThrownBy(() ->
pp.postProcessBeanFactory(beanFactory))
.withMessageContaining("bar")
.withMessageContaining("SingletonBeanConfig")
.withMessageContaining(TestBean.class.getName());
}
@Test // gh-25430
@@ -431,12 +429,12 @@ class ConfigurationClassPostProcessorTests {
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
pp.postProcessBeanFactory(beanFactory);
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(() -> beanFactory.getBean(Bar.class))
.withMessageContaining("OverridingSingletonBeanConfig.foo")
.withMessageContaining(ExtendedFoo.class.getName())
.withMessageContaining(Foo.class.getName())
.withMessageContaining("InvalidOverridingSingletonBeanConfig");
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() ->
beanFactory.getBean(Bar.class))
.withMessageContaining("OverridingSingletonBeanConfig.foo")
.withMessageContaining(ExtendedFoo.class.getName())
.withMessageContaining(Foo.class.getName())
.withMessageContaining("InvalidOverridingSingletonBeanConfig");
}
@Test // SPR-15384
@@ -946,8 +944,8 @@ class ConfigurationClassPostProcessorTests {
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(ConcreteConfig.class));
beanFactory.registerBeanDefinition("serviceBeanProvider", new RootBeanDefinition(ServiceBeanProvider.class));
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
beanFactory.preInstantiateSingletons();
beanFactory.getBean(ServiceBean.class);
}
@@ -960,8 +958,8 @@ class ConfigurationClassPostProcessorTests {
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(ConcreteConfigWithDefaultMethods.class));
beanFactory.registerBeanDefinition("serviceBeanProvider", new RootBeanDefinition(ServiceBeanProvider.class));
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
beanFactory.preInstantiateSingletons();
beanFactory.getBean(ServiceBean.class);
}
@@ -974,25 +972,11 @@ class ConfigurationClassPostProcessorTests {
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(ConcreteConfigWithDefaultMethods.class.getName()));
beanFactory.registerBeanDefinition("serviceBeanProvider", new RootBeanDefinition(ServiceBeanProvider.class.getName()));
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
beanFactory.preInstantiateSingletons();
beanFactory.getBean(ServiceBean.class);
}
@Test
void testConfigWithFailingInit() { // gh-23343
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(beanFactory);
beanFactory.addBeanPostProcessor(bpp);
beanFactory.addBeanPostProcessor(new CommonAnnotationBeanPostProcessor());
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(ConcreteConfigWithFailingInit.class));
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(beanFactory::preInstantiateSingletons);
assertThat(beanFactory.containsSingleton("configClass")).isFalse();
assertThat(beanFactory.containsSingleton("provider")).isFalse();
}
@Test
void testCircularDependency() {
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
@@ -1001,17 +985,16 @@ class ConfigurationClassPostProcessorTests {
beanFactory.registerBeanDefinition("configClass1", new RootBeanDefinition(A.class));
beanFactory.registerBeanDefinition("configClass2", new RootBeanDefinition(AStrich.class));
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(beanFactory::preInstantiateSingletons)
.withMessageContaining("Circular reference");
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
beanFactory::preInstantiateSingletons)
.withMessageContaining("Circular reference");
}
@Test
void testCircularDependencyWithApplicationContext() {
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(() -> new AnnotationConfigApplicationContext(A.class, AStrich.class))
.withMessageContaining("Circular reference");
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() ->
new AnnotationConfigApplicationContext(A.class, AStrich.class))
.withMessageContaining("Circular reference");
}
@Test
@@ -1065,7 +1048,9 @@ class ConfigurationClassPostProcessorTests {
void testCollectionArgumentOnBeanMethod() {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(CollectionArgumentConfiguration.class, TestBean.class);
CollectionArgumentConfiguration bean = ctx.getBean(CollectionArgumentConfiguration.class);
assertThat(bean.testBeans).containsExactly(ctx.getBean(TestBean.class));
assertThat(bean.testBeans).isNotNull();
assertThat(bean.testBeans).hasSize(1);
assertThat(bean.testBeans.get(0)).isSameAs(ctx.getBean(TestBean.class));
ctx.close();
}
@@ -1073,7 +1058,8 @@ class ConfigurationClassPostProcessorTests {
void testEmptyCollectionArgumentOnBeanMethod() {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(CollectionArgumentConfiguration.class);
CollectionArgumentConfiguration bean = ctx.getBean(CollectionArgumentConfiguration.class);
assertThat(bean.testBeans).isEmpty();
assertThat(bean.testBeans).isNotNull();
assertThat(bean.testBeans.isEmpty()).isTrue();
ctx.close();
}
@@ -1081,7 +1067,9 @@ class ConfigurationClassPostProcessorTests {
void testMapArgumentOnBeanMethod() {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(MapArgumentConfiguration.class, DummyRunnable.class);
MapArgumentConfiguration bean = ctx.getBean(MapArgumentConfiguration.class);
assertThat(bean.testBeans).hasSize(1).containsValue(ctx.getBean(Runnable.class));
assertThat(bean.testBeans).isNotNull();
assertThat(bean.testBeans).hasSize(1);
assertThat(bean.testBeans.values().iterator().next()).isSameAs(ctx.getBean(Runnable.class));
ctx.close();
}
@@ -1089,7 +1077,8 @@ class ConfigurationClassPostProcessorTests {
void testEmptyMapArgumentOnBeanMethod() {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(MapArgumentConfiguration.class);
MapArgumentConfiguration bean = ctx.getBean(MapArgumentConfiguration.class);
assertThat(bean.testBeans).isEmpty();
assertThat(bean.testBeans).isNotNull();
assertThat(bean.testBeans.isEmpty()).isTrue();
ctx.close();
}
@@ -1097,7 +1086,9 @@ class ConfigurationClassPostProcessorTests {
void testCollectionInjectionFromSameConfigurationClass() {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(CollectionInjectionConfiguration.class);
CollectionInjectionConfiguration bean = ctx.getBean(CollectionInjectionConfiguration.class);
assertThat(bean.testBeans).containsExactly(ctx.getBean(TestBean.class));
assertThat(bean.testBeans).isNotNull();
assertThat(bean.testBeans).hasSize(1);
assertThat(bean.testBeans.get(0)).isSameAs(ctx.getBean(TestBean.class));
ctx.close();
}
@@ -1105,21 +1096,25 @@ class ConfigurationClassPostProcessorTests {
void testMapInjectionFromSameConfigurationClass() {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(MapInjectionConfiguration.class);
MapInjectionConfiguration bean = ctx.getBean(MapInjectionConfiguration.class);
assertThat(bean.testBeans).containsOnly(Map.entry("testBean", ctx.getBean(Runnable.class)));
assertThat(bean.testBeans).isNotNull();
assertThat(bean.testBeans).hasSize(1);
assertThat(bean.testBeans.get("testBean")).isSameAs(ctx.getBean(Runnable.class));
ctx.close();
}
@Test
void testBeanLookupFromSameConfigurationClass() {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(BeanLookupConfiguration.class);
assertThat(ctx.getBean(BeanLookupConfiguration.class).getTestBean()).isSameAs(ctx.getBean(TestBean.class));
BeanLookupConfiguration bean = ctx.getBean(BeanLookupConfiguration.class);
assertThat(bean.getTestBean()).isNotNull();
assertThat(bean.getTestBean()).isSameAs(ctx.getBean(TestBean.class));
ctx.close();
}
@Test
void testNameClashBetweenConfigurationClassAndBean() {
assertThatExceptionOfType(BeanDefinitionStoreException.class)
.isThrownBy(() -> new AnnotationConfigApplicationContext(MyTestBean.class).getBean("myTestBean", TestBean.class));
.isThrownBy(() -> new AnnotationConfigApplicationContext(MyTestBean.class).getBean("myTestBean", TestBean.class));
}
@Test
@@ -1136,11 +1131,11 @@ class ConfigurationClassPostProcessorTests {
@Order(1)
static class SingletonBeanConfig {
@Bean public Foo foo() {
public @Bean Foo foo() {
return new Foo();
}
@Bean public Bar bar() {
public @Bean Bar bar() {
return new Bar(foo());
}
}
@@ -1148,11 +1143,11 @@ class ConfigurationClassPostProcessorTests {
@Configuration(proxyBeanMethods = false)
static class NonEnhancedSingletonBeanConfig {
@Bean public Foo foo() {
public @Bean Foo foo() {
return new Foo();
}
@Bean public Bar bar() {
public @Bean Bar bar() {
return new Bar(foo());
}
}
@@ -1160,13 +1155,11 @@ class ConfigurationClassPostProcessorTests {
@Configuration
static class StaticSingletonBeanConfig {
@Bean
public static Foo foo() {
public static @Bean Foo foo() {
return new Foo();
}
@Bean
public static Bar bar() {
public static @Bean Bar bar() {
return new Bar(foo());
}
}
@@ -1175,11 +1168,11 @@ class ConfigurationClassPostProcessorTests {
@Order(2)
static class OverridingSingletonBeanConfig {
@Bean public ExtendedFoo foo() {
public @Bean ExtendedFoo foo() {
return new ExtendedFoo();
}
@Bean public Bar bar() {
public @Bean Bar bar() {
return new Bar(foo());
}
}
@@ -1187,7 +1180,7 @@ class ConfigurationClassPostProcessorTests {
@Configuration
static class OverridingAgainSingletonBeanConfig {
@Bean public ExtendedAgainFoo foo() {
public @Bean ExtendedAgainFoo foo() {
return new ExtendedAgainFoo();
}
}
@@ -1195,7 +1188,7 @@ class ConfigurationClassPostProcessorTests {
@Configuration
static class InvalidOverridingSingletonBeanConfig {
@Bean public Foo foo() {
public @Bean Foo foo() {
return new Foo();
}
}
@@ -1207,11 +1200,11 @@ class ConfigurationClassPostProcessorTests {
@Order(1)
static class SingletonBeanConfig {
@Bean public Foo foo() {
public @Bean Foo foo() {
return new Foo();
}
@Bean public Bar bar() {
public @Bean Bar bar() {
return new Bar(foo());
}
}
@@ -1220,11 +1213,11 @@ class ConfigurationClassPostProcessorTests {
@Order(2)
static class OverridingSingletonBeanConfig {
@Bean public ExtendedFoo foo() {
public @Bean ExtendedFoo foo() {
return new ExtendedFoo();
}
@Bean public Bar bar() {
public @Bean Bar bar() {
return new Bar(foo());
}
}
@@ -1240,11 +1233,11 @@ class ConfigurationClassPostProcessorTests {
public SingletonBeanConfig(ConfigWithOrderedInnerClasses other) {
}
@Bean public Foo foo() {
public @Bean Foo foo() {
return new Foo();
}
@Bean public Bar bar() {
public @Bean Bar bar() {
return new Bar(foo());
}
}
@@ -1257,11 +1250,11 @@ class ConfigurationClassPostProcessorTests {
other.getObject();
}
@Bean public ExtendedFoo foo() {
public @Bean ExtendedFoo foo() {
return new ExtendedFoo();
}
@Bean public Bar bar() {
public @Bean Bar bar() {
return new Bar(foo());
}
}
@@ -1288,7 +1281,7 @@ class ConfigurationClassPostProcessorTests {
@Configuration
static class UnloadedConfig {
@Bean public Foo foo() {
public @Bean Foo foo() {
return new Foo();
}
}
@@ -1296,7 +1289,7 @@ class ConfigurationClassPostProcessorTests {
@Configuration
static class LoadedConfig {
@Bean public Bar bar() {
public @Bean Bar bar() {
return new Bar(new Foo());
}
}
@@ -1605,7 +1598,7 @@ class ConfigurationClassPostProcessorTests {
public static class WildcardWithGenericExtendsConfiguration {
@Bean
public Repository<?> genericRepo() {
public Repository<? extends Object> genericRepo() {
return new Repository<String>();
}
@@ -1714,7 +1707,7 @@ class ConfigurationClassPostProcessorTests {
}
@Configuration
public abstract static class AbstractConfig {
public static abstract class AbstractConfig {
@Bean
public ServiceBean serviceBean() {
@@ -1765,6 +1758,7 @@ class ConfigurationClassPostProcessorTests {
}
public interface DefaultMethodsConfig extends BaseDefaultMethods {
}
@Configuration
@@ -1785,29 +1779,6 @@ class ConfigurationClassPostProcessorTests {
}
}
@Configuration
public static class ConcreteConfigWithFailingInit implements DefaultMethodsConfig, BeanFactoryAware {
private BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Bean
@Override
public ServiceBeanProvider provider() {
return new ServiceBeanProvider();
}
@PostConstruct
public void validate() {
beanFactory.getBean("provider");
throw new IllegalStateException();
}
}
@Primary
public static class ServiceBeanProvider {
@@ -1920,7 +1891,7 @@ class ConfigurationClassPostProcessorTests {
}
}
abstract static class FooFactory {
static abstract class FooFactory {
abstract DependingFoo createFoo(BarArgument bar);
}
@@ -2039,7 +2010,7 @@ class ConfigurationClassPostProcessorTests {
}
@Configuration
abstract static class BeanLookupConfiguration {
static abstract class BeanLookupConfiguration {
@Bean
public TestBean thing() {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,9 +29,7 @@ import org.springframework.beans.factory.support.AbstractBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.ResolvableType;
import org.springframework.core.type.AnnotationMetadata;
import static org.assertj.core.api.Assertions.assertThat;
@@ -41,62 +39,51 @@ import static org.assertj.core.api.Assertions.assertThat;
* {@link FactoryBean FactoryBeans} defined in the configuration.
*
* @author Phillip Webb
* @author Juergen Hoeller
*/
class ConfigurationWithFactoryBeanEarlyDeductionTests {
public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
@Test
void preFreezeDirect() {
public void preFreezeDirect() {
assertPreFreeze(DirectConfiguration.class);
}
@Test
void postFreezeDirect() {
public void postFreezeDirect() {
assertPostFreeze(DirectConfiguration.class);
}
@Test
void preFreezeGenericMethod() {
public void preFreezeGenericMethod() {
assertPreFreeze(GenericMethodConfiguration.class);
}
@Test
void postFreezeGenericMethod() {
public void postFreezeGenericMethod() {
assertPostFreeze(GenericMethodConfiguration.class);
}
@Test
void preFreezeGenericClass() {
public void preFreezeGenericClass() {
assertPreFreeze(GenericClassConfiguration.class);
}
@Test
void postFreezeGenericClass() {
public void postFreezeGenericClass() {
assertPostFreeze(GenericClassConfiguration.class);
}
@Test
void preFreezeAttribute() {
public void preFreezeAttribute() {
assertPreFreeze(AttributeClassConfiguration.class);
}
@Test
void postFreezeAttribute() {
public void postFreezeAttribute() {
assertPostFreeze(AttributeClassConfiguration.class);
}
@Test
void preFreezeTargetType() {
assertPreFreeze(TargetTypeConfiguration.class);
}
@Test
void postFreezeTargetType() {
assertPostFreeze(TargetTypeConfiguration.class);
}
@Test
void preFreezeUnresolvedGenericFactoryBean() {
public void preFreezeUnresolvedGenericFactoryBean() {
// Covers the case where a @Configuration is picked up via component scanning
// and its bean definition only has a String bean class. In such cases
// beanDefinition.hasBeanClass() returns false so we need to actually
@@ -118,13 +105,14 @@ class ConfigurationWithFactoryBeanEarlyDeductionTests {
}
}
private void assertPostFreeze(Class<?> configurationClass) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(configurationClass);
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
configurationClass);
assertContainsMyBeanName(context);
}
private void assertPreFreeze(Class<?> configurationClass, BeanFactoryPostProcessor... postProcessors) {
private void assertPreFreeze(Class<?> configurationClass,
BeanFactoryPostProcessor... postProcessors) {
NameCollectingBeanFactoryPostProcessor postProcessor = new NameCollectingBeanFactoryPostProcessor();
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
try (context) {
@@ -144,38 +132,41 @@ class ConfigurationWithFactoryBeanEarlyDeductionTests {
assertThat(names).containsExactly("myBean");
}
private static class NameCollectingBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
private static class NameCollectingBeanFactoryPostProcessor
implements BeanFactoryPostProcessor {
private String[] names;
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
ResolvableType typeToMatch = ResolvableType.forClassWithGenerics(MyBean.class, String.class);
this.names = beanFactory.getBeanNamesForType(typeToMatch, true, false);
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
this.names = beanFactory.getBeanNamesForType(MyBean.class, true, false);
}
public String[] getNames() {
return this.names;
}
}
@Configuration
static class DirectConfiguration {
@Bean
MyBean<String> myBean() {
return new MyBean<>();
MyBean myBean() {
return new MyBean();
}
}
@Configuration
static class GenericMethodConfiguration {
@Bean
FactoryBean<MyBean<String>> myBean() {
return new TestFactoryBean<>(new MyBean<>());
FactoryBean<MyBean> myBean() {
return new TestFactoryBean<>(new MyBean());
}
}
@Configuration
@@ -185,11 +176,13 @@ class ConfigurationWithFactoryBeanEarlyDeductionTests {
MyFactoryBean myBean() {
return new MyFactoryBean();
}
}
@Configuration
@Import(AttributeClassRegistrar.class)
static class AttributeClassConfiguration {
}
static class AttributeClassRegistrar implements ImportBeanDefinitionRegistrar {
@@ -198,32 +191,16 @@ class ConfigurationWithFactoryBeanEarlyDeductionTests {
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
BeanDefinition definition = BeanDefinitionBuilder.genericBeanDefinition(
RawWithAbstractObjectTypeFactoryBean.class).getBeanDefinition();
definition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE,
ResolvableType.forClassWithGenerics(MyBean.class, String.class));
definition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, MyBean.class);
registry.registerBeanDefinition("myBean", definition);
}
}
@Configuration
@Import(TargetTypeRegistrar.class)
static class TargetTypeConfiguration {
}
static class TargetTypeRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
RootBeanDefinition definition = new RootBeanDefinition(RawWithAbstractObjectTypeFactoryBean.class);
definition.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class,
ResolvableType.forClassWithGenerics(MyBean.class, String.class)));
registry.registerBeanDefinition("myBean", definition);
}
}
abstract static class AbstractMyBean {
}
static class MyBean<T> extends AbstractMyBean {
static class MyBean extends AbstractMyBean {
}
static class TestFactoryBean<T> implements FactoryBean<T> {
@@ -235,7 +212,7 @@ class ConfigurationWithFactoryBeanEarlyDeductionTests {
}
@Override
public T getObject() {
public T getObject() throws Exception {
return this.instance;
}
@@ -243,26 +220,31 @@ class ConfigurationWithFactoryBeanEarlyDeductionTests {
public Class<?> getObjectType() {
return this.instance.getClass();
}
}
static class MyFactoryBean extends TestFactoryBean<MyBean<String>> {
static class MyFactoryBean extends TestFactoryBean<MyBean> {
public MyFactoryBean() {
super(new MyBean<>());
super(new MyBean());
}
}
static class RawWithAbstractObjectTypeFactoryBean implements FactoryBean<Object> {
private final Object object = new MyBean();
@Override
public Object getObject() throws Exception {
throw new IllegalStateException();
return object;
}
@Override
public Class<?> getObjectType() {
return MyBean.class;
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -204,7 +204,7 @@ class ConfigurationClassProcessingTests {
BeanFactory factory = initBeanFactory(ConfigWithNullReference.class);
TestBean foo = factory.getBean("foo", TestBean.class);
assertThat(factory.getBean("bar")).isEqualTo(null);
assertThat(factory.getBean("bar").equals(null)).isTrue();
assertThat(foo.getSpouse()).isNull();
}
@@ -426,7 +426,7 @@ class ConfigurationClassProcessingTests {
@Configuration
static class ConfigWithFinalBean {
@Bean public final TestBean testBean() {
public final @Bean TestBean testBean() {
return new TestBean();
}
}
@@ -435,7 +435,7 @@ class ConfigurationClassProcessingTests {
@Configuration
static class SimplestPossibleConfig {
@Bean public String stringBean() {
public @Bean String stringBean() {
return "foo";
}
}
@@ -444,11 +444,11 @@ class ConfigurationClassProcessingTests {
@Configuration
static class ConfigWithNonSpecificReturnTypes {
@Bean public Object stringBean() {
public @Bean Object stringBean() {
return "foo";
}
@Bean public FactoryBean<?> factoryBean() {
public @Bean FactoryBean<?> factoryBean() {
ListFactoryBean fb = new ListFactoryBean();
fb.setSourceList(Arrays.asList("element1", "element2"));
return fb;
@@ -459,13 +459,13 @@ class ConfigurationClassProcessingTests {
@Configuration
static class ConfigWithPrototypeBean {
@Bean public TestBean foo() {
public @Bean TestBean foo() {
TestBean foo = new SpousyTestBean("foo");
foo.setSpouse(bar());
return foo;
}
@Bean public TestBean bar() {
public @Bean TestBean bar() {
TestBean bar = new SpousyTestBean("bar");
bar.setSpouse(baz());
return bar;
@@ -605,15 +605,15 @@ class ConfigurationClassProcessingTests {
void register(GenericApplicationContext ctx) {
ctx.registerBean("spouse", TestBean.class,
() -> new TestBean("functional"));
Supplier<TestBean> testBeanSupplier =
() -> new TestBean(ctx.getBean("spouse", TestBean.class));
ctx.registerBean(TestBean.class, testBeanSupplier,
Supplier<TestBean> testBeanSupplier = () -> new TestBean(ctx.getBean("spouse", TestBean.class));
ctx.registerBean(TestBean.class,
testBeanSupplier,
bd -> bd.setPrimary(true));
}
@Bean
public NestedTestBean nestedTestBean(TestBean spouse) {
return new NestedTestBean(spouse.getSpouse().getName());
public NestedTestBean nestedTestBean(TestBean testBean) {
return new NestedTestBean(testBean.getSpouse().getName());
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,8 @@
package org.springframework.scheduling.concurrent;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@@ -52,8 +52,8 @@ class ConcurrentTaskExecutorTests extends AbstractSchedulingTaskExecutorTests {
@AfterEach
void shutdownExecutor() {
for (Runnable task : concurrentExecutor.shutdownNow()) {
if (task instanceof Future) {
((Future<?>) task).cancel(true);
if (task instanceof RunnableFuture) {
((RunnableFuture<?>) task).cancel(true);
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -101,7 +101,7 @@ class ThreadPoolTaskSchedulerTests extends AbstractSchedulingTaskExecutorTests {
@Test
@SuppressWarnings("deprecation")
void scheduleOneTimeFailingTaskWithoutErrorHandler() {
void scheduleOneTimeFailingTaskWithoutErrorHandler() throws Exception {
TestTask task = new TestTask(this.testName, 0);
Future<?> future = scheduler.schedule(task, new Date());
assertThatExceptionOfType(ExecutionException.class).isThrownBy(() -> future.get(1000, TimeUnit.MILLISECONDS));
@@ -147,7 +147,7 @@ class ThreadPoolTaskSchedulerTests extends AbstractSchedulingTaskExecutorTests {
catch (InterruptedException ex) {
throw new IllegalStateException(ex);
}
assertThat(latch.getCount()).as("latch did not count down").isEqualTo(0);
assertThat(latch.getCount()).as("latch did not count down,").isEqualTo(0);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,32 +35,27 @@ 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));
@@ -112,7 +107,6 @@ 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
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,7 +28,6 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
* Unit tests for {@link QuartzCronField}.
*
* @author Arjen Poutsma
* @author Juergen Hoeller
*/
class QuartzCronFieldTests {
@@ -72,46 +71,6 @@ 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(""));
@@ -1984,7 +1984,7 @@ class DataBinderTests {
.withMessageContaining("DataBinder is already initialized - call setAutoGrowCollectionLimit before other configuration methods");
}
@Test // SPR-15009
@Test // SPR-15009
void setCustomMessageCodesResolverBeforeInitializeBindingResultForBeanPropertyAccess() {
TestBean testBean = new TestBean();
DataBinder binder = new DataBinder(testBean, "testBean");
@@ -2001,7 +2001,7 @@ class DataBinderTests {
assertThat(((BeanWrapper) binder.getInternalBindingResult().getPropertyAccessor()).getAutoGrowCollectionLimit()).isEqualTo(512);
}
@Test // SPR-15009
@Test // SPR-15009
void setCustomMessageCodesResolverBeforeInitializeBindingResultForDirectFieldAccess() {
TestBean testBean = new TestBean();
DataBinder binder = new DataBinder(testBean, "testBean");
@@ -2055,7 +2055,7 @@ class DataBinderTests {
.withMessageContaining("DataBinder is already initialized with MessageCodesResolver");
}
@Test // gh-24347
@Test // gh-24347
void overrideBindingResultType() {
TestBean testBean = new TestBean();
DataBinder binder = new DataBinder(testBean, "testBean");
@@ -78,7 +78,8 @@ public class MethodValidationTests {
ac.close();
}
@Test // gh-29782
@Test // gh-29782
@SuppressWarnings("unchecked")
public void testMethodValidationPostProcessorForInterfaceOnlyProxy() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(MethodValidationPostProcessor.class);
@@ -195,15 +195,9 @@ public class BindingReflectionHintsRegistrar {
}
private void registerHintsForClassAttributes(ReflectionHints hints, MergedAnnotation<Annotation> annotation) {
annotation.getRoot().asMap().forEach((key,value) -> {
annotation.getRoot().asMap().values().forEach(value -> {
if (value instanceof Class<?> classValue && value != Void.class) {
if (key.equals("builder")) {
hints.registerType(classValue, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
MemberCategory.INVOKE_DECLARED_METHODS);
}
else {
hints.registerType(classValue, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
}
hints.registerType(classValue, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
}
});
}

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