mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
132 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4c7f939e01 | |||
| ea208dc304 | |||
| c8c95e360f | |||
| 9fb36a5dcb | |||
| 4caf6bc5b8 | |||
| ee3e1591de | |||
| e81c788274 | |||
| 09f23a578b | |||
| 5288504ceb | |||
| 8b6a54c45f | |||
| f3f3063091 | |||
| c1f3e37acd | |||
| 427a96befc | |||
| 77951dc622 | |||
| 43dd22ba31 | |||
| dfe437ac88 | |||
| 9c775d2643 | |||
| 3b50b6ef94 | |||
| 9a31f3b5a5 | |||
| b11d118376 | |||
| 0931769822 | |||
| f7b7e29fd7 | |||
| 08a63a4180 | |||
| 4e3b834310 | |||
| fb67f97a41 | |||
| cafb5cfbbe | |||
| 9d2c6f80b8 | |||
| d9330bcac7 | |||
| 1fb179b057 | |||
| 5c9f364352 | |||
| 55f9581743 | |||
| 190397d5e0 | |||
| 94097fb112 | |||
| e4ab2aa775 | |||
| 40bf550d56 | |||
| f3bdce455e | |||
| 402246df28 | |||
| 88a68ddbd2 | |||
| 7d178c6771 | |||
| ba776d7201 | |||
| aba5f421fc | |||
| 2bac1629e6 | |||
| 9412d782ce | |||
| a0bd13ceb1 | |||
| 46a0821205 | |||
| 2c531344c6 | |||
| 6610ee360e | |||
| afde96c540 | |||
| da799bc519 | |||
| 755968fd2c | |||
| eea00628f9 | |||
| 8d745462b4 | |||
| ed6c25fb6e | |||
| 8460a2d285 | |||
| c3d186b4d0 | |||
| f8926d6a66 | |||
| 19b21b15c1 | |||
| 547425275c | |||
| 658194c155 | |||
| 769f73ebf1 | |||
| 80809bbc14 | |||
| 6d323d710a | |||
| f2fd2f1226 | |||
| 072ebb6ffc | |||
| 08e7f7efa4 | |||
| b1fafbf7e1 | |||
| a3647a8c5e | |||
| 5c8d9cd0b2 | |||
| 40d5196243 | |||
| ec2c9b5d0e | |||
| 36539bdaa9 | |||
| 67ba7dd1da | |||
| 7b3fcf2647 | |||
| fd76c33589 | |||
| 1a7a6f421f | |||
| 9ac1feceb5 | |||
| 7029042e44 | |||
| 1a5661d426 | |||
| b208c63414 | |||
| 814c003b43 | |||
| 5c34e1d11a | |||
| d57775bbb2 | |||
| b598ad3f33 | |||
| b44ef70bc3 | |||
| d1b3107398 | |||
| 629c560316 | |||
| 7bf07ef393 | |||
| ca602ef874 | |||
| e9bf5f5569 | |||
| 9a6f636e17 | |||
| 7686f5467e | |||
| 429c477f6a | |||
| 5187281b50 | |||
| 24a4487050 | |||
| 8fba4a448a | |||
| 8f286de773 | |||
| ee100cfba7 | |||
| aaf90f5a96 | |||
| 70be04ba81 | |||
| b976ee3f67 | |||
| b11ff96652 | |||
| 667e4e7531 | |||
| 5fd9fab0ce | |||
| 95a8646309 | |||
| 5434edd726 | |||
| 5f13ea95fb | |||
| 74bb42b78f | |||
| 55717adf88 | |||
| 72835f10b9 | |||
| 125ebd029c | |||
| d8d4fa0e24 | |||
| bfd3b3ad88 | |||
| 0909161ba1 | |||
| 4910c217dc | |||
| f262046ef9 | |||
| d50d4a9092 | |||
| b06305e64f | |||
| c749a14326 | |||
| 3817936ca5 | |||
| 9bd2be80b9 | |||
| c6e9cd0c62 | |||
| e2a5cfb459 | |||
| b484ab116f | |||
| c668473186 | |||
| 38595c6a99 | |||
| d756c2b128 | |||
| 3be4322f71 | |||
| 328e444db3 | |||
| 94bdd4380f | |||
| 3f9d479583 | |||
| 338922f03d | |||
| fad857e06a |
@@ -0,0 +1,33 @@
|
||||
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
|
||||
@@ -18,11 +18,13 @@ jobs:
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-java@v3
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '17'
|
||||
distribution: 'liberica'
|
||||
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
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
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) }}
|
||||
@@ -0,0 +1,78 @@
|
||||
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) }}
|
||||
@@ -1,12 +1,14 @@
|
||||
name: Deploy Docs
|
||||
on:
|
||||
push:
|
||||
branches-ignore: [ gh-pages ]
|
||||
tags: '**'
|
||||
branches:
|
||||
- 'main'
|
||||
- '*.x'
|
||||
- '!gh-pages'
|
||||
tags:
|
||||
- 'v*'
|
||||
repository_dispatch:
|
||||
types: request-build-reference # legacy
|
||||
schedule:
|
||||
- cron: '0 10 * * *' # Once per day at 10am UTC
|
||||
workflow_dispatch:
|
||||
permissions:
|
||||
actions: write
|
||||
@@ -15,8 +17,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository_owner == 'spring-projects'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: docs-build
|
||||
fetch-depth: 1
|
||||
|
||||
@@ -9,5 +9,5 @@ jobs:
|
||||
name: "Validation"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: gradle/wrapper-validation-action@v1
|
||||
- uses: actions/checkout@v4
|
||||
- uses: gradle/wrapper-validation-action@v2
|
||||
|
||||
@@ -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.8.1-librca
|
||||
java=17.0.11-librca
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# <img src="framework-docs/src/docs/spring-framework.png" width="80" height="80"> Spring Framework [](https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-6.0.x?groups=Build") [](https://ge.spring.io/scans?search.rootProjectNames=spring)
|
||||
# <img src="framework-docs/src/docs/spring-framework.png" width="80" height="80"> Spring Framework [](https://github.com/spring-projects/spring-framework/actions/workflows/build-and-deploy-snapshot.yml?query=branch%3A6.0.x) [](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".
|
||||
|
||||
|
||||
+3
-4
@@ -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.10'
|
||||
id 'org.jetbrains.dokka' version '1.8.20'
|
||||
id 'org.unbroken-dome.xjc' version '2.0.0' apply false
|
||||
id 'com.github.ben-manes.versions' version '0.49.0'
|
||||
id 'com.github.ben-manes.versions' version '0.51.0'
|
||||
id 'com.github.johnrengelman.shadow' version '8.1.1' apply false
|
||||
id 'de.undercouch.download' version '5.4.0'
|
||||
id 'me.champeau.jmh' version '0.7.1' apply false
|
||||
id 'me.champeau.jmh' version '0.7.2' apply false
|
||||
}
|
||||
|
||||
ext {
|
||||
@@ -113,7 +113,6 @@ 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",
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
== 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].
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM ubuntu:jammy-20231211.1
|
||||
FROM ubuntu:jammy-20240125
|
||||
|
||||
ADD setup.sh /setup.sh
|
||||
ADD get-jdk-url.sh /get-jdk-url.sh
|
||||
@@ -6,6 +6,5 @@ RUN ./setup.sh
|
||||
|
||||
ENV JAVA_HOME /opt/openjdk/java17
|
||||
ENV JDK17 /opt/openjdk/java17
|
||||
ENV JDK20 /opt/openjdk/java20
|
||||
|
||||
ENV PATH $JAVA_HOME/bin:$PATH
|
||||
|
||||
@@ -3,10 +3,7 @@ set -e
|
||||
|
||||
case "$1" in
|
||||
java17)
|
||||
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.9+11/bellsoft-jdk17.0.9+11-linux-amd64.tar.gz"
|
||||
;;
|
||||
java20)
|
||||
echo "https://github.com/bell-sw/Liberica/releases/download/20.0.1+10/bellsoft-jdk20.0.1+10-linux-amd64.tar.gz"
|
||||
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.10%2B13/bellsoft-jdk17.0.10+13-linux-amd64.tar.gz"
|
||||
;;
|
||||
*)
|
||||
echo $"Unknown java version"
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ curl https://raw.githubusercontent.com/spring-io/concourse-java-scripts/v0.0.4/c
|
||||
|
||||
mkdir -p /opt/openjdk
|
||||
pushd /opt/openjdk > /dev/null
|
||||
for jdk in java17 java20
|
||||
for jdk in java17
|
||||
do
|
||||
JDK_URL=$( /get-jdk-url.sh $jdk )
|
||||
mkdir $jdk
|
||||
|
||||
@@ -8,4 +8,3 @@ milestone: "6.0.x"
|
||||
build-name: "spring-framework"
|
||||
pipeline-name: "spring-framework"
|
||||
concourse-url: "https://ci.spring.io"
|
||||
task-timeout: 1h00m
|
||||
|
||||
+15
-123
@@ -5,9 +5,7 @@ anchors:
|
||||
password: ((github-ci-release-token))
|
||||
branch: ((branch))
|
||||
gradle-enterprise-task-params: &gradle-enterprise-task-params
|
||||
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))
|
||||
DEVELOCITY_ACCESS_KEY: ((gradle_enterprise_secret_access_key))
|
||||
sonatype-task-params: &sonatype-task-params
|
||||
SONATYPE_USERNAME: ((sonatype-username))
|
||||
SONATYPE_PASSWORD: ((sonatype-password))
|
||||
@@ -23,14 +21,6 @@ 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
|
||||
@@ -45,7 +35,7 @@ resource_types:
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: concourse/registry-image-resource
|
||||
tag: 1.5.0
|
||||
tag: 1.8.0
|
||||
- name: artifactory-resource
|
||||
type: registry-image
|
||||
source:
|
||||
@@ -57,19 +47,13 @@ resource_types:
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: concourse/github-release-resource
|
||||
tag: 1.5.5
|
||||
tag: 1.8.0
|
||||
- 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
|
||||
@@ -90,13 +74,6 @@ resources:
|
||||
<<: *docker-resource-source
|
||||
repository: ((docker-hub-organization))/spring-framework-ci
|
||||
tag: ((milestone))
|
||||
- name: every-morning
|
||||
type: time
|
||||
icon: alarm
|
||||
source:
|
||||
start: 8:00 AM
|
||||
stop: 9:00 AM
|
||||
location: Europe/Vienna
|
||||
- name: artifactory-repo
|
||||
type: artifactory-resource
|
||||
icon: package-variant
|
||||
@@ -105,27 +82,6 @@ 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
|
||||
@@ -160,37 +116,23 @@ jobs:
|
||||
- put: ci-image
|
||||
params:
|
||||
image: ci-image/image.tar
|
||||
- name: build
|
||||
- name: stage-milestone
|
||||
serial: true
|
||||
public: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
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" }
|
||||
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
|
||||
signing_key: ((signing-key))
|
||||
signing_passphrase: ((signing-passphrase))
|
||||
repo: libs-snapshot-local
|
||||
repo: libs-staging-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}"
|
||||
@@ -215,55 +157,9 @@ jobs:
|
||||
- "/**/framework-docs-*-schema.zip"
|
||||
properties:
|
||||
"zip.type": "schema"
|
||||
get_params:
|
||||
threads: 8
|
||||
- name: jdk20-build
|
||||
serial: true
|
||||
public: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
- get: every-morning
|
||||
trigger: true
|
||||
- put: repo-status-jdk20-build
|
||||
params: { state: "pending", commit: "git-repo" }
|
||||
- do:
|
||||
- task: check-project
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/check-project.yml
|
||||
privileged: true
|
||||
timeout: ((task-timeout))
|
||||
params:
|
||||
TEST_TOOLCHAIN: 20
|
||||
<<: *build-project-task-params
|
||||
on_failure:
|
||||
do:
|
||||
- put: repo-status-jdk20-build
|
||||
params: { state: "failure", commit: "git-repo" }
|
||||
- put: slack-alert
|
||||
params:
|
||||
<<: *slack-fail-params
|
||||
- put: repo-status-jdk20-build
|
||||
params: { state: "success", commit: "git-repo" }
|
||||
- name: stage-milestone
|
||||
serial: true
|
||||
plan:
|
||||
- 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
|
||||
- put: git-repo
|
||||
params:
|
||||
repository: stage-git-repo
|
||||
- name: promote-milestone
|
||||
serial: true
|
||||
plan:
|
||||
@@ -305,7 +201,6 @@ jobs:
|
||||
- put: artifactory-repo
|
||||
params:
|
||||
<<: *artifactory-params
|
||||
repo: libs-staging-local
|
||||
- put: git-repo
|
||||
params:
|
||||
repository: stage-git-repo
|
||||
@@ -350,7 +245,6 @@ jobs:
|
||||
- put: artifactory-repo
|
||||
params:
|
||||
<<: *artifactory-params
|
||||
repo: libs-staging-local
|
||||
- put: git-repo
|
||||
params:
|
||||
repository: stage-git-repo
|
||||
@@ -395,8 +289,6 @@ 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"
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,19 +0,0 @@
|
||||
---
|
||||
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
|
||||
@@ -1,22 +0,0 @@
|
||||
---
|
||||
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
|
||||
@@ -1,24 +0,0 @@
|
||||
---
|
||||
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
|
||||
@@ -4,7 +4,7 @@ image_resource:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: springio/concourse-release-scripts
|
||||
tag: '0.4.0-SNAPSHOT'
|
||||
tag: '0.4.0'
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
inputs:
|
||||
|
||||
@@ -39,8 +39,8 @@
|
||||
** xref:core/resources.adoc[]
|
||||
** xref:core/validation.adoc[]
|
||||
*** xref:core/validation/validator.adoc[]
|
||||
*** xref:core/validation/conversion.adoc[]
|
||||
*** xref:core/validation/beans-beans.adoc[]
|
||||
*** xref:core/validation/conversion.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:web/webflux-webclient/client-builder.adoc#webflux-client-builder-reactor-resources[Resources])
|
||||
xref:core/resources.adoc[Resources])
|
||||
which provides a convenient mechanism for reading an InputStream from locations defined
|
||||
in a URI syntax. In particular, `Resource` paths are used to construct applications contexts,
|
||||
as described in xref:core/resources.adoc#resources-app-ctx[Application Contexts and Resource Paths].
|
||||
|
||||
+7
-5
@@ -4,7 +4,7 @@
|
||||
Projection lets a collection drive the evaluation of a sub-expression, and the result is
|
||||
a new collection. The syntax for projection is `.![projectionExpression]`. For example,
|
||||
suppose we have a list of inventors but want the list of cities where they were born.
|
||||
Effectively, we want to evaluate 'placeOfBirth.city' for every entry in the inventor
|
||||
Effectively, we want to evaluate `placeOfBirth.city` for every entry in the inventor
|
||||
list. The following example uses projection to do so:
|
||||
|
||||
[tabs]
|
||||
@@ -13,16 +13,18 @@ Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
// returns ['Smiljan', 'Idvor' ]
|
||||
List placesOfBirth = (List)parser.parseExpression("members.![placeOfBirth.city]");
|
||||
// evaluates to ["SmilJan", "Idvor"]
|
||||
List placesOfBirth = parser.parseExpression("members.![placeOfBirth.city]")
|
||||
.getValue(societyContext, List.class);
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
// returns ['Smiljan', 'Idvor' ]
|
||||
val placesOfBirth = parser.parseExpression("members.![placeOfBirth.city]") as List<*>
|
||||
// evaluates to ["SmilJan", "Idvor"]
|
||||
val placesOfBirth = parser.parseExpression("members.![placeOfBirth.city]")
|
||||
.getValue(societyContext) as List<*>
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
+12
-11
@@ -28,13 +28,14 @@ Kotlin::
|
||||
======
|
||||
|
||||
Selection is supported for arrays and anything that implements `java.lang.Iterable` or
|
||||
`java.util.Map`. For a list or array, the selection criteria is evaluated against each
|
||||
individual element. Against a map, the selection criteria is evaluated against each map
|
||||
entry (objects of the Java type `Map.Entry`). Each map entry has its `key` and `value`
|
||||
accessible as properties for use in the selection.
|
||||
`java.util.Map`. For an array or `Iterable`, the selection expression is evaluated
|
||||
against each individual element. Against a map, the selection expression is evaluated
|
||||
against each map entry (objects of the Java type `Map.Entry`). Each map entry has its
|
||||
`key` and `value` accessible as properties for use in the selection.
|
||||
|
||||
The following expression returns a new map that consists of those elements of the
|
||||
original map where the entry's value is less than 27:
|
||||
Given a `Map` stored in a variable named `#map`, the following expression returns a new
|
||||
map that consists of those elements of the original map where the entry's value is less
|
||||
than 27:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@@ -42,21 +43,21 @@ Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
Map newMap = parser.parseExpression("map.?[value<27]").getValue();
|
||||
Map newMap = parser.parseExpression("#map.?[value < 27]").getValue(Map.class);
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
val newMap = parser.parseExpression("map.?[value<27]").getValue()
|
||||
val newMap = parser.parseExpression("#map.?[value < 27]").getValue() as Map
|
||||
----
|
||||
======
|
||||
|
||||
In addition to returning all the selected elements, you can retrieve only the first or
|
||||
the last element. To obtain the first element matching the selection, the syntax is
|
||||
`.^[selectionExpression]`. To obtain the last matching selection, the syntax is
|
||||
`.$[selectionExpression]`.
|
||||
the last element. To obtain the first element matching the selection expression, the
|
||||
syntax is `.^[selectionExpression]`. To obtain the last element matching the selection
|
||||
expression, the syntax is `.$[selectionExpression]`.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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]
|
||||
======
|
||||
|
||||
+1
-1
@@ -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 [official Vavr documentation](https://www.vavr.io/vavr-docs/#_try).
|
||||
refer to the https://docs.vavr.io/#_try[official Vavr documentation].
|
||||
|
||||
Here's an example of how to use Vavr's Try with a transactional method:
|
||||
[tabs]
|
||||
|
||||
+1
-1
@@ -152,7 +152,7 @@ Kotlin::
|
||||
@Autowired
|
||||
lateinit var accountService: AccountService
|
||||
|
||||
lateinit mockMvc: MockMvc
|
||||
lateinit var mockMvc: MockMvc
|
||||
|
||||
@BeforeEach
|
||||
fun setup(wac: WebApplicationContext) {
|
||||
|
||||
+1
-1
@@ -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.
|
||||
|
||||
+10
-33
@@ -1,38 +1,15 @@
|
||||
[[spring-mvc-test-vs-streaming-response]]
|
||||
= Streaming Responses
|
||||
|
||||
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].
|
||||
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].
|
||||
|
||||
`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,14 +52,10 @@ The following example shows how to achieve the same configuration in XML:
|
||||
</mvc:interceptors>
|
||||
----
|
||||
|
||||
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.
|
||||
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: The XML config declares interceptors as `MappedInterceptor` beans, and those are in
|
||||
turn detected by any `HandlerMapping` bean, including those from other frameworks.
|
||||
|
||||
+18
-23
@@ -1,34 +1,29 @@
|
||||
[[mvc-handlermapping-interceptor]]
|
||||
= Interception
|
||||
|
||||
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:
|
||||
All `HandlerMapping` implementations support handler interception which is useful when
|
||||
you want to apply functionality across requests. A `HandlerInterceptor` can implement the
|
||||
following:
|
||||
|
||||
* `preHandle(..)`: Before the actual handler is run
|
||||
* `postHandle(..)`: After the handler is run
|
||||
* `afterCompletion(..)`: After the complete request has finished
|
||||
* `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.
|
||||
|
||||
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.
|
||||
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`.
|
||||
|
||||
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.
|
||||
|
||||
`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`.
|
||||
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -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.104.Final"))
|
||||
api(platform("io.netty:netty-bom:4.1.109.Final"))
|
||||
api(platform("io.netty:netty5-bom:5.0.0.Alpha5"))
|
||||
api(platform("io.projectreactor:reactor-bom:2022.0.15"))
|
||||
api(platform("io.projectreactor:reactor-bom:2022.0.19"))
|
||||
api(platform("io.rsocket:rsocket-bom:1.1.3"))
|
||||
api(platform("org.apache.groovy:groovy-bom:4.0.17"))
|
||||
api(platform("org.apache.groovy:groovy-bom:4.0.21"))
|
||||
api(platform("org.apache.logging.log4j:log4j-bom:2.21.1"))
|
||||
api(platform("org.eclipse.jetty:jetty-bom:11.0.19"))
|
||||
api(platform("org.eclipse.jetty:jetty-bom:11.0.20"))
|
||||
api(platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.6.4"))
|
||||
api(platform("org.jetbrains.kotlinx:kotlinx-serialization-bom:1.4.0"))
|
||||
api(platform("org.junit:junit-bom:5.9.3"))
|
||||
api(platform("org.mockito:mockito-bom:5.8.0"))
|
||||
api(platform("org.mockito:mockito-bom:5.12.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.36")
|
||||
api("com.github.librepdf:openpdf:1.3.43")
|
||||
api("com.google.code.findbugs:findbugs:3.0.1")
|
||||
api("com.google.code.findbugs:jsr305:3.0.2")
|
||||
api("com.google.code.gson:gson:2.10.1")
|
||||
@@ -53,10 +53,10 @@ dependencies {
|
||||
api("io.r2dbc:r2dbc-spi-test:1.0.0.RELEASE")
|
||||
api("io.r2dbc:r2dbc-spi:1.0.0.RELEASE")
|
||||
api("io.reactivex.rxjava3:rxjava:3.1.8")
|
||||
api("io.smallrye.reactive:mutiny:1.9.0")
|
||||
api("io.undertow:undertow-core:2.3.10.Final")
|
||||
api("io.undertow:undertow-servlet:2.3.10.Final")
|
||||
api("io.undertow:undertow-websockets-jsr:2.3.10.Final")
|
||||
api("io.smallrye.reactive:mutiny:1.10.0")
|
||||
api("io.undertow:undertow-core:2.3.13.Final")
|
||||
api("io.undertow:undertow-servlet:2.3.13.Final")
|
||||
api("io.undertow:undertow-websockets-jsr:2.3.13.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.20.1")
|
||||
api("org.aspectj:aspectjtools:1.9.20.1")
|
||||
api("org.aspectj:aspectjweaver:1.9.20.1")
|
||||
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.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.10")
|
||||
api("org.eclipse.jetty:jetty-reactive-httpclient:3.0.12")
|
||||
api("org.eclipse.persistence:org.eclipse.persistence.jpa:3.0.4")
|
||||
api("org.eclipse:yasson:2.0.4")
|
||||
api("org.ehcache:ehcache:3.10.8")
|
||||
@@ -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.5.0")
|
||||
api("org.jruby:jruby:9.4.6.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.11")
|
||||
api("org.slf4j:slf4j-api:2.0.12")
|
||||
api("org.testng:testng:7.8.0")
|
||||
api("org.webjars:underscorejs:1.8.3")
|
||||
api("org.webjars:webjars-locator-core:0.55")
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
version=6.0.16-SNAPSHOT
|
||||
version=6.0.20
|
||||
|
||||
org.gradle.caching=true
|
||||
org.gradle.jvmargs=-Xmx2048m
|
||||
|
||||
@@ -6,6 +6,7 @@ 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/"))
|
||||
@@ -21,6 +22,7 @@ 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/"))
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+1
-1
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
Vendored
+10
-10
@@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
@@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
|
||||
+3
-3
@@ -7,8 +7,8 @@ pluginManagement {
|
||||
}
|
||||
|
||||
plugins {
|
||||
id "com.gradle.enterprise" version "3.12.6"
|
||||
id "io.spring.ge.conventions" version "0.0.13"
|
||||
id "com.gradle.develocity" version "3.17.2"
|
||||
id "io.spring.ge.conventions" version "0.0.17"
|
||||
}
|
||||
|
||||
include "spring-aop"
|
||||
@@ -45,7 +45,7 @@ rootProject.children.each {project ->
|
||||
}
|
||||
|
||||
settings.gradle.projectsLoaded {
|
||||
gradleEnterprise {
|
||||
develocity {
|
||||
buildScan {
|
||||
File buildDir = settings.gradle.rootProject
|
||||
.getLayout().getBuildDirectory().getAsFile().get()
|
||||
|
||||
+14
-9
@@ -169,25 +169,30 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
|
||||
@Override
|
||||
public ClassFilter getClassFilter() {
|
||||
obtainPointcutExpression();
|
||||
checkExpression();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodMatcher getMethodMatcher() {
|
||||
obtainPointcutExpression();
|
||||
checkExpression();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check whether this pointcut is ready to match,
|
||||
* lazily building the underlying AspectJ pointcut expression.
|
||||
* Check whether this pointcut is ready to match.
|
||||
*/
|
||||
private PointcutExpression obtainPointcutExpression() {
|
||||
private void checkExpression() {
|
||||
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);
|
||||
@@ -264,10 +269,9 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
|
||||
@Override
|
||||
public boolean matches(Class<?> targetClass) {
|
||||
PointcutExpression pointcutExpression = obtainPointcutExpression();
|
||||
try {
|
||||
try {
|
||||
return pointcutExpression.couldMatchJoinPointsInType(targetClass);
|
||||
return obtainPointcutExpression().couldMatchJoinPointsInType(targetClass);
|
||||
}
|
||||
catch (ReflectionWorldException ex) {
|
||||
logger.debug("PointcutExpression matching rejected target class - trying fallback expression", ex);
|
||||
@@ -278,6 +282,9 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IllegalArgumentException | IllegalStateException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
logger.debug("PointcutExpression matching rejected target class", ex);
|
||||
}
|
||||
@@ -286,7 +293,6 @@ 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
|
||||
@@ -324,7 +330,6 @@ 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,
|
||||
|
||||
+2
-30
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,7 +18,6 @@ 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;
|
||||
@@ -56,8 +55,6 @@ 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};
|
||||
|
||||
@@ -68,37 +65,11 @@ 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);
|
||||
@@ -115,6 +86,7 @@ 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...).
|
||||
|
||||
+11
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -124,10 +124,16 @@ public class AspectMetadata implements Serializable {
|
||||
* Extract contents from String of form {@code pertarget(contents)}.
|
||||
*/
|
||||
private String findPerClause(Class<?> aspectClass) {
|
||||
String str = aspectClass.getAnnotation(Aspect.class).value();
|
||||
int beginIndex = str.indexOf('(') + 1;
|
||||
int endIndex = str.length() - 1;
|
||||
return str.substring(beginIndex, endIndex);
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -368,8 +368,7 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
private void validateIntroductionAdvisor(IntroductionAdvisor advisor) {
|
||||
advisor.validateInterfaces();
|
||||
// If the advisor passed validation, we can make the change.
|
||||
Class<?>[] ifcs = advisor.getInterfaces();
|
||||
for (Class<?> ifc : ifcs) {
|
||||
for (Class<?> ifc : advisor.getInterfaces()) {
|
||||
addInterface(ifc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -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.
|
||||
* May be {@code null} or may not even implement the method.
|
||||
* @param targetClass the target class for the current invocation
|
||||
* (can be {@code null} or may not even implement the method)
|
||||
* @return the specific target method, or the original method if the
|
||||
* {@code targetClass} doesn't implement it or is {@code null}
|
||||
* {@code targetClass} does not implement it
|
||||
* @see org.springframework.util.ClassUtils#getMostSpecificMethod
|
||||
*/
|
||||
public static Method getMostSpecificMethod(Method method, @Nullable Class<?> targetClass) {
|
||||
|
||||
+15
-18
@@ -23,8 +23,6 @@ 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;
|
||||
@@ -41,7 +39,6 @@ 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;
|
||||
|
||||
@@ -174,25 +171,25 @@ public class AspectJExpressionPointcutTests {
|
||||
@Test
|
||||
public void testFriendlyErrorOnNoLocationClassMatching() {
|
||||
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
pc.matches(ITestBean.class))
|
||||
.withMessageContaining("expression");
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> pc.getClassFilter().matches(ITestBean.class))
|
||||
.withMessageContaining("expression");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFriendlyErrorOnNoLocation2ArgMatching() {
|
||||
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
pc.matches(getAge, ITestBean.class))
|
||||
.withMessageContaining("expression");
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> pc.getMethodMatcher().matches(getAge, ITestBean.class))
|
||||
.withMessageContaining("expression");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFriendlyErrorOnNoLocation3ArgMatching() {
|
||||
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
pc.matches(getAge, ITestBean.class, (Object[]) null))
|
||||
.withMessageContaining("expression");
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> pc.getMethodMatcher().matches(getAge, ITestBean.class, (Object[]) null))
|
||||
.withMessageContaining("expression");
|
||||
}
|
||||
|
||||
|
||||
@@ -209,8 +206,10 @@ 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();
|
||||
}
|
||||
@@ -245,7 +244,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); // call to getClassFilter forces resolution
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> getPointcut(expression).getClassFilter().matches(Object.class));
|
||||
}
|
||||
|
||||
private TestBean getAdvisedProxy(String pointcutExpression, CallCountingInterceptor interceptor) {
|
||||
@@ -275,9 +274,7 @@ public class AspectJExpressionPointcutTests {
|
||||
@Test
|
||||
public void testWithUnsupportedPointcutPrimitive() {
|
||||
String expression = "call(int org.springframework.beans.testfixture.beans.TestBean.getAge())";
|
||||
assertThatExceptionOfType(UnsupportedPointcutPrimitiveException.class)
|
||||
.isThrownBy(() -> getPointcut(expression).getClassFilter()) // call to getClassFilter forces resolution...
|
||||
.satisfies(ex -> assertThat(ex.getUnsupportedPrimitive()).isEqualTo(PointcutPrimitive.CALL));
|
||||
assertThat(getPointcut(expression).getClassFilter().matches(Object.class)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,29 +17,35 @@
|
||||
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
|
||||
*/
|
||||
public class AopUtilsTests {
|
||||
class AopUtilsTests {
|
||||
|
||||
@Test
|
||||
public void testPointcutCanNeverApply() {
|
||||
void testPointcutCanNeverApply() {
|
||||
class TestPointcut extends StaticMethodMatcherPointcut {
|
||||
@Override
|
||||
public boolean matches(Method method, @Nullable Class<?> clazzy) {
|
||||
@@ -52,13 +58,13 @@ public class AopUtilsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPointcutAlwaysApplies() {
|
||||
void testPointcutAlwaysApplies() {
|
||||
assertThat(AopUtils.canApply(new DefaultPointcutAdvisor(new NopInterceptor()), Object.class)).isTrue();
|
||||
assertThat(AopUtils.canApply(new DefaultPointcutAdvisor(new NopInterceptor()), TestBean.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPointcutAppliesToOneMethodOnObject() {
|
||||
void testPointcutAppliesToOneMethodOnObject() {
|
||||
class TestPointcut extends StaticMethodMatcherPointcut {
|
||||
@Override
|
||||
public boolean matches(Method method, @Nullable Class<?> clazz) {
|
||||
@@ -78,7 +84,7 @@ public class AopUtilsTests {
|
||||
* that's subverted the singleton construction limitation.
|
||||
*/
|
||||
@Test
|
||||
public void testCanonicalFrameworkClassesStillCanonicalOnDeserialization() throws Exception {
|
||||
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);
|
||||
@@ -88,4 +94,45 @@ public 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) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -292,7 +292,7 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa
|
||||
if (actualValue != null) {
|
||||
actualValue = typeConverter.convertIfNecessary(actualValue, expectedValue.getClass());
|
||||
}
|
||||
if (!expectedValue.equals(actualValue)) {
|
||||
if (!ObjectUtils.nullSafeEquals(expectedValue, actualValue)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -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, checks the FactoryBean's {@code getObjectType} method
|
||||
* declared as a raw type, it 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} a
|
||||
* full creation of the FactoryBean is used as fallback (through delegation to the
|
||||
* superclass's implementation).
|
||||
* 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).
|
||||
* <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.
|
||||
|
||||
+6
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -161,6 +161,9 @@ 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);
|
||||
|
||||
@@ -171,8 +174,6 @@ 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.
|
||||
@@ -1052,7 +1053,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;
|
||||
}
|
||||
|
||||
@@ -1648,7 +1649,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} if set on the bean definition
|
||||
* <p>If no {@link FactoryBean#OBJECT_TYPE_ATTRIBUTE} is 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
|
||||
|
||||
+4
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -609,13 +609,10 @@ 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,
|
||||
|
||||
+6
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -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> dependencies;
|
||||
Set<String> dependentBeanNames;
|
||||
synchronized (this.dependentBeanMap) {
|
||||
// Within full synchronization in order to guarantee a disconnected Set
|
||||
dependencies = this.dependentBeanMap.remove(beanName);
|
||||
dependentBeanNames = this.dependentBeanMap.remove(beanName);
|
||||
}
|
||||
if (dependencies != null) {
|
||||
if (dependentBeanNames != null) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Retrieved dependent beans for bean '" + beanName + "': " + dependencies);
|
||||
logger.trace("Retrieved dependent beans for bean '" + beanName + "': " + dependentBeanNames);
|
||||
}
|
||||
for (String dependentBeanName : dependencies) {
|
||||
for (String dependentBeanName : dependentBeanNames) {
|
||||
destroySingleton(dependentBeanName);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -261,7 +261,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||
if (destroyMethod != null) {
|
||||
return destroyMethod;
|
||||
}
|
||||
for (Class<?> beanInterface : beanClass.getInterfaces()) {
|
||||
for (Class<?> beanInterface : ClassUtils.getAllInterfacesForClass(beanClass)) {
|
||||
destroyMethod = findDestroyMethod(beanInterface, methodName);
|
||||
if (destroyMethod != null) {
|
||||
return destroyMethod;
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -108,7 +108,8 @@ 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)) {
|
||||
if (typeToBeMatched != null && !FactoryBean.class.isAssignableFrom(typeToBeMatched) &&
|
||||
!typeToBeMatched.isAssignableFrom(resolvedClass)) {
|
||||
targetType = targetType.getGeneric();
|
||||
if (descriptor.fallbackMatchAllowed()) {
|
||||
// Matching the Class-based type determination for FactoryBean
|
||||
|
||||
+436
-431
File diff suppressed because it is too large
Load Diff
+12
-13
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,12 +29,10 @@ import org.springframework.core.MethodClassKey;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Abstract implementation of {@link JCacheOperationSource} that caches attributes
|
||||
* Abstract implementation of {@link JCacheOperationSource} that caches operations
|
||||
* 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
|
||||
@@ -43,24 +41,25 @@ import org.springframework.lang.Nullable;
|
||||
public abstract class AbstractFallbackJCacheOperationSource implements JCacheOperationSource {
|
||||
|
||||
/**
|
||||
* Canonical value held in cache to indicate no caching attribute was
|
||||
* found for this method and we don't need to look again.
|
||||
* Canonical value held in cache to indicate no cache operation was
|
||||
* found for this method, and we don't need to look again.
|
||||
*/
|
||||
private static final Object NULL_CACHING_ATTRIBUTE = new Object();
|
||||
private static final Object NULL_CACHING_MARKER = new Object();
|
||||
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final Map<MethodClassKey, Object> cache = new ConcurrentHashMap<>(1024);
|
||||
private final Map<MethodClassKey, Object> operationCache = new ConcurrentHashMap<>(1024);
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public JCacheOperation<?> getCacheOperation(Method method, @Nullable Class<?> targetClass) {
|
||||
MethodClassKey cacheKey = new MethodClassKey(method, targetClass);
|
||||
Object cached = this.cache.get(cacheKey);
|
||||
Object cached = this.operationCache.get(cacheKey);
|
||||
|
||||
if (cached != null) {
|
||||
return (cached != NULL_CACHING_ATTRIBUTE ? (JCacheOperation<?>) cached : null);
|
||||
return (cached != NULL_CACHING_MARKER ? (JCacheOperation<?>) cached : null);
|
||||
}
|
||||
else {
|
||||
JCacheOperation<?> operation = computeCacheOperation(method, targetClass);
|
||||
@@ -68,10 +67,10 @@ public abstract class AbstractFallbackJCacheOperationSource implements JCacheOpe
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Adding cacheable method '" + method.getName() + "' with operation: " + operation);
|
||||
}
|
||||
this.cache.put(cacheKey, operation);
|
||||
this.operationCache.put(cacheKey, operation);
|
||||
}
|
||||
else {
|
||||
this.cache.put(cacheKey, NULL_CACHING_ATTRIBUTE);
|
||||
this.operationCache.put(cacheKey, NULL_CACHING_MARKER);
|
||||
}
|
||||
return operation;
|
||||
}
|
||||
@@ -84,7 +83,7 @@ public abstract class AbstractFallbackJCacheOperationSource implements JCacheOpe
|
||||
return null;
|
||||
}
|
||||
|
||||
// The method may be on an interface, but we need attributes from the target class.
|
||||
// The method may be on an interface, but we need metadata from the target class.
|
||||
// If the target class is null, the method will be unchanged.
|
||||
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
|
||||
|
||||
|
||||
+3
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -212,10 +212,8 @@ 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) {
|
||||
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -46,6 +46,7 @@ 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);
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -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 (may be {@code null}, in which case
|
||||
* @param targetClass the target class (can 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
|
||||
*/
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -24,7 +24,7 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* A Pointcut that matches if the underlying {@link JCacheOperationSource}
|
||||
* A {@code Pointcut} that matches if the underlying {@link JCacheOperationSource}
|
||||
* has an operation for a given method.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
|
||||
+6
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,11 +22,12 @@ 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 entries are registered.
|
||||
* reflection hints are registered.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Stephane Nicoll
|
||||
@@ -36,11 +37,11 @@ class SchedulerFactoryBeanRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
private static final String SCHEDULER_FACTORY_CLASS_NAME = "org.quartz.impl.StdSchedulerFactory";
|
||||
|
||||
private final ReflectiveRuntimeHintsRegistrar reflectiveRegistrar = new ReflectiveRuntimeHintsRegistrar();
|
||||
private static final ReflectiveRuntimeHintsRegistrar registrar = new ReflectiveRuntimeHintsRegistrar();
|
||||
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
||||
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
|
||||
if (!ClassUtils.isPresent(SCHEDULER_FACTORY_CLASS_NAME, classLoader)) {
|
||||
return;
|
||||
}
|
||||
@@ -48,7 +49,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);
|
||||
this.reflectiveRegistrar.registerRuntimeHints(hints, LocalTaskExecutorThreadPool.class);
|
||||
registrar.registerRuntimeHints(hints, LocalTaskExecutorThreadPool.class);
|
||||
}
|
||||
|
||||
private void typeHint(Builder typeHint) {
|
||||
|
||||
+24
-28
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -32,20 +32,16 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Abstract implementation of {@link CacheOperation} that caches attributes
|
||||
* Abstract implementation of {@link CacheOperationSource} that caches operations
|
||||
* 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 caching attribute if none is
|
||||
* associated with the target method. Any caching attribute associated with
|
||||
* the target method completely overrides a class caching attribute.
|
||||
* <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.
|
||||
* 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
|
||||
@@ -53,10 +49,10 @@ import org.springframework.util.ClassUtils;
|
||||
public abstract class AbstractFallbackCacheOperationSource implements CacheOperationSource {
|
||||
|
||||
/**
|
||||
* Canonical value held in cache to indicate no caching attribute was
|
||||
* found for this method and we don't need to look again.
|
||||
* Canonical value held in cache to indicate no cache operation was
|
||||
* found for this method, and we don't need to look again.
|
||||
*/
|
||||
private static final Collection<CacheOperation> NULL_CACHING_ATTRIBUTE = Collections.emptyList();
|
||||
private static final Collection<CacheOperation> NULL_CACHING_MARKER = Collections.emptyList();
|
||||
|
||||
|
||||
/**
|
||||
@@ -71,14 +67,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>> attributeCache = new ConcurrentHashMap<>(1024);
|
||||
private final Map<Object, Collection<CacheOperation>> operationCache = new ConcurrentHashMap<>(1024);
|
||||
|
||||
|
||||
/**
|
||||
* Determine the caching attribute for this method invocation.
|
||||
* <p>Defaults to the class's caching attribute if no method attribute is found.
|
||||
* Determine the cache operations for this method invocation.
|
||||
* <p>Defaults to class-declared metadata if no method-level metadata is found.
|
||||
* @param method the method for the current invocation (never {@code null})
|
||||
* @param targetClass the target class for this invocation (may be {@code null})
|
||||
* @param targetClass the target class for this invocation (can be {@code null})
|
||||
* @return {@link CacheOperation} for this method, or {@code null} if the method
|
||||
* is not cacheable
|
||||
*/
|
||||
@@ -90,21 +86,21 @@ public abstract class AbstractFallbackCacheOperationSource implements CacheOpera
|
||||
}
|
||||
|
||||
Object cacheKey = getCacheKey(method, targetClass);
|
||||
Collection<CacheOperation> cached = this.attributeCache.get(cacheKey);
|
||||
Collection<CacheOperation> cached = this.operationCache.get(cacheKey);
|
||||
|
||||
if (cached != null) {
|
||||
return (cached != NULL_CACHING_ATTRIBUTE ? cached : null);
|
||||
return (cached != NULL_CACHING_MARKER ? cached : null);
|
||||
}
|
||||
else {
|
||||
Collection<CacheOperation> cacheOps = computeCacheOperations(method, targetClass);
|
||||
if (cacheOps != null) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Adding cacheable method '" + method.getName() + "' with attribute: " + cacheOps);
|
||||
logger.trace("Adding cacheable method '" + method.getName() + "' with operations: " + cacheOps);
|
||||
}
|
||||
this.attributeCache.put(cacheKey, cacheOps);
|
||||
this.operationCache.put(cacheKey, cacheOps);
|
||||
}
|
||||
else {
|
||||
this.attributeCache.put(cacheKey, NULL_CACHING_ATTRIBUTE);
|
||||
this.operationCache.put(cacheKey, NULL_CACHING_MARKER);
|
||||
}
|
||||
return cacheOps;
|
||||
}
|
||||
@@ -129,7 +125,7 @@ public abstract class AbstractFallbackCacheOperationSource implements CacheOpera
|
||||
return null;
|
||||
}
|
||||
|
||||
// The method may be on an interface, but we need attributes from the target class.
|
||||
// The method may be on an interface, but we need metadata from the target class.
|
||||
// If the target class is null, the method will be unchanged.
|
||||
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
|
||||
|
||||
@@ -163,19 +159,19 @@ public abstract class AbstractFallbackCacheOperationSource implements CacheOpera
|
||||
|
||||
|
||||
/**
|
||||
* Subclasses need to implement this to return the caching attribute for the
|
||||
* Subclasses need to implement this to return the cache operations for the
|
||||
* given class, if any.
|
||||
* @param clazz the class to retrieve the attribute for
|
||||
* @return all caching attribute associated with this class, or {@code null} if none
|
||||
* @param clazz the class to retrieve the cache operations for
|
||||
* @return all cache operations 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 caching attribute for the
|
||||
* Subclasses need to implement this to return the cache operations for the
|
||||
* given method, if any.
|
||||
* @param method the method to retrieve the attribute for
|
||||
* @return all caching attribute associated with this method, or {@code null} if none
|
||||
* @param method the method to retrieve the cache operations for
|
||||
* @return all cache operations associated with this method, or {@code null} if none
|
||||
*/
|
||||
@Nullable
|
||||
protected abstract Collection<CacheOperation> findCacheOperations(Method method);
|
||||
|
||||
Vendored
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -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 (may be {@code null}, in which case
|
||||
* @param targetClass the target class (can 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
|
||||
*/
|
||||
|
||||
spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationSourcePointcut.java
Vendored
+7
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -28,7 +28,7 @@ import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* A {@code Pointcut} that matches if the underlying {@link CacheOperationSource}
|
||||
* has an attribute for a given method.
|
||||
* has an operation for a given method.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Juergen Hoeller
|
||||
@@ -36,7 +36,7 @@ import org.springframework.util.ObjectUtils;
|
||||
* @since 3.1
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
|
||||
final class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
|
||||
|
||||
@Nullable
|
||||
private CacheOperationSource cacheOperationSource;
|
||||
@@ -78,7 +78,7 @@ class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implement
|
||||
* {@link ClassFilter} that delegates to {@link CacheOperationSource#isCandidateClass}
|
||||
* for filtering classes whose methods are not worth searching to begin with.
|
||||
*/
|
||||
private class CacheOperationSourceClassFilter implements ClassFilter {
|
||||
private final class CacheOperationSourceClassFilter implements ClassFilter {
|
||||
|
||||
@Override
|
||||
public boolean matches(Class<?> clazz) {
|
||||
@@ -88,6 +88,7 @@ class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implement
|
||||
return (cacheOperationSource == null || cacheOperationSource.isCandidateClass(clazz));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private CacheOperationSource getCacheOperationSource() {
|
||||
return cacheOperationSource;
|
||||
}
|
||||
@@ -95,7 +96,7 @@ class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implement
|
||||
@Override
|
||||
public boolean equals(@Nullable Object other) {
|
||||
return (this == other || (other instanceof CacheOperationSourceClassFilter that &&
|
||||
ObjectUtils.nullSafeEquals(cacheOperationSource, that.getCacheOperationSource())));
|
||||
ObjectUtils.nullSafeEquals(getCacheOperationSource(), that.getCacheOperationSource())));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -105,9 +106,8 @@ class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implement
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return CacheOperationSourceClassFilter.class.getName() + ": " + cacheOperationSource;
|
||||
return CacheOperationSourceClassFilter.class.getName() + ": " + getCacheOperationSource();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -121,7 +121,7 @@ public interface SmartLifecycle extends Lifecycle, Phased {
|
||||
/**
|
||||
* Return the phase that this lifecycle object is supposed to run in.
|
||||
* <p>The default implementation returns {@link #DEFAULT_PHASE} in order to
|
||||
* let {@code stop()} callbacks execute after regular {@code Lifecycle}
|
||||
* let {@code stop()} callbacks execute before regular {@code Lifecycle}
|
||||
* implementations.
|
||||
* @see #isAutoStartup()
|
||||
* @see #start()
|
||||
|
||||
+12
-15
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -73,7 +73,6 @@ 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");
|
||||
@@ -87,10 +86,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 or {@code null}
|
||||
* @param importedBy the configuration class importing this one
|
||||
* @since 3.1.1
|
||||
*/
|
||||
ConfigurationClass(MetadataReader metadataReader, @Nullable ConfigurationClass importedBy) {
|
||||
ConfigurationClass(MetadataReader metadataReader, ConfigurationClass importedBy) {
|
||||
this.metadata = metadataReader.getAnnotationMetadata();
|
||||
this.resource = metadataReader.getResource();
|
||||
this.importedBy.add(importedBy);
|
||||
@@ -100,7 +99,6 @@ 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");
|
||||
@@ -114,10 +112,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 (or {@code null})
|
||||
* @param importedBy the configuration class importing this one
|
||||
* @since 3.1.1
|
||||
*/
|
||||
ConfigurationClass(Class<?> clazz, @Nullable ConfigurationClass importedBy) {
|
||||
ConfigurationClass(Class<?> clazz, ConfigurationClass importedBy) {
|
||||
this.metadata = AnnotationMetadata.introspect(clazz);
|
||||
this.resource = new DescriptiveResource(clazz.getName());
|
||||
this.importedBy.add(importedBy);
|
||||
@@ -127,7 +125,6 @@ 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");
|
||||
@@ -149,12 +146,12 @@ final class ConfigurationClass {
|
||||
return ClassUtils.getShortName(getMetadata().getClassName());
|
||||
}
|
||||
|
||||
void setBeanName(String beanName) {
|
||||
void setBeanName(@Nullable String beanName) {
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getBeanName() {
|
||||
String getBeanName() {
|
||||
return this.beanName;
|
||||
}
|
||||
|
||||
@@ -164,7 +161,7 @@ final class ConfigurationClass {
|
||||
* @since 3.1.1
|
||||
* @see #getImportedBy()
|
||||
*/
|
||||
public boolean isImported() {
|
||||
boolean isImported() {
|
||||
return !this.importedBy.isEmpty();
|
||||
}
|
||||
|
||||
@@ -198,6 +195,10 @@ 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);
|
||||
}
|
||||
@@ -206,10 +207,6 @@ 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());
|
||||
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -225,7 +225,6 @@ class ConfigurationClassEnhancer {
|
||||
};
|
||||
return new TransformingClassGenerator(cg, transformer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -334,6 +333,7 @@ class ConfigurationClassEnhancer {
|
||||
return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Object resolveBeanReference(Method beanMethod, Object[] beanMethodArgs,
|
||||
ConfigurableBeanFactory beanFactory, String beanName) {
|
||||
|
||||
|
||||
+13
-12
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -386,11 +386,11 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
});
|
||||
|
||||
// Detect any custom bean name generation strategy supplied through the enclosing application context
|
||||
SingletonBeanRegistry sbr = null;
|
||||
if (registry instanceof SingletonBeanRegistry _sbr) {
|
||||
sbr = _sbr;
|
||||
SingletonBeanRegistry singletonRegistry = null;
|
||||
if (registry instanceof SingletonBeanRegistry sbr) {
|
||||
singletonRegistry = sbr;
|
||||
if (!this.localBeanNameGeneratorSet) {
|
||||
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
|
||||
BeanNameGenerator generator = (BeanNameGenerator) singletonRegistry.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 (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
|
||||
sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
|
||||
if (singletonRegistry != null && !singletonRegistry.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
|
||||
singletonRegistry.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
|
||||
}
|
||||
|
||||
// Store the PropertySourceDescriptors to contribute them Ahead-of-time if necessary
|
||||
@@ -550,6 +550,7 @@ 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.
|
||||
@@ -645,9 +646,9 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
}
|
||||
return mappings;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static class PropertySourcesAotContribution implements BeanFactoryInitializationAotContribution {
|
||||
|
||||
private static final String ENVIRONMENT_VARIABLE = "environment";
|
||||
@@ -743,15 +744,14 @@ 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,6 +759,7 @@ 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));
|
||||
@@ -771,6 +772,7 @@ 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);
|
||||
@@ -788,7 +790,6 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
}
|
||||
return userExecutable;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+7
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -39,7 +39,8 @@ 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) {
|
||||
@@ -49,7 +50,9 @@ class ReflectiveProcessorBeanFactoryInitializationAotProcessor implements BeanFa
|
||||
return new ReflectiveProcessorBeanFactoryInitializationAotContribution(beanTypes);
|
||||
}
|
||||
|
||||
private static class ReflectiveProcessorBeanFactoryInitializationAotContribution implements BeanFactoryInitializationAotContribution {
|
||||
|
||||
private static class ReflectiveProcessorBeanFactoryInitializationAotContribution
|
||||
implements BeanFactoryInitializationAotContribution {
|
||||
|
||||
private final Class<?>[] types;
|
||||
|
||||
@@ -60,9 +63,8 @@ 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+22
-19
@@ -135,17 +135,6 @@ 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.
|
||||
@@ -166,6 +155,17 @@ 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,8 +796,9 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the MessageSource.
|
||||
* Use parent's if none defined in this context.
|
||||
* Initialize the {@link MessageSource}.
|
||||
* <p>Uses parent's {@code MessageSource} if none defined in this context.
|
||||
* @see #MESSAGE_SOURCE_BEAN_NAME
|
||||
*/
|
||||
protected void initMessageSource() {
|
||||
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
|
||||
@@ -827,8 +828,9 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the ApplicationEventMulticaster.
|
||||
* Uses SimpleApplicationEventMulticaster if none defined in the context.
|
||||
* Initialize the {@link ApplicationEventMulticaster}.
|
||||
* <p>Uses {@link SimpleApplicationEventMulticaster} if none defined in the context.
|
||||
* @see #APPLICATION_EVENT_MULTICASTER_BEAN_NAME
|
||||
* @see org.springframework.context.event.SimpleApplicationEventMulticaster
|
||||
*/
|
||||
protected void initApplicationEventMulticaster() {
|
||||
@@ -851,15 +853,16 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the LifecycleProcessor.
|
||||
* Uses DefaultLifecycleProcessor if none defined in the context.
|
||||
* Initialize the {@link LifecycleProcessor}.
|
||||
* <p>Uses {@link DefaultLifecycleProcessor} if none defined in the context.
|
||||
* @since 3.0
|
||||
* @see #LIFECYCLE_PROCESSOR_BEAN_NAME
|
||||
* @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 + "]");
|
||||
}
|
||||
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -126,6 +126,7 @@ public abstract class AbstractRefreshableApplicationContext extends AbstractAppl
|
||||
try {
|
||||
DefaultListableBeanFactory beanFactory = createBeanFactory();
|
||||
beanFactory.setSerializationId(getId());
|
||||
beanFactory.setApplicationStartup(getApplicationStartup());
|
||||
customizeBeanFactory(beanFactory);
|
||||
loadBeanDefinitions(beanFactory);
|
||||
this.beanFactory = beanFactory;
|
||||
|
||||
+8
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -45,7 +45,11 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Default implementation of the {@link LifecycleProcessor} strategy.
|
||||
* Spring's default implementation of the {@link LifecycleProcessor} strategy.
|
||||
*
|
||||
* <p>Provides interaction with {@link Lifecycle} and {@link SmartLifecycle} beans in
|
||||
* groups for specific phases, on startup/shutdown as well as for explicit start/stop
|
||||
* interactions on a {@link org.springframework.context.ConfigurableApplicationContext}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Juergen Hoeller
|
||||
@@ -314,6 +318,8 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
|
||||
/**
|
||||
* Helper class for maintaining a group of Lifecycle beans that should be started
|
||||
* and stopped together based on their 'phase' value (or the default value of 0).
|
||||
* The group is expected to be created in an ad-hoc fashion and group members are
|
||||
* expected to always have the same 'phase' value.
|
||||
*/
|
||||
private class LifecycleGroup {
|
||||
|
||||
|
||||
+32
-10
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,6 +18,7 @@ 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;
|
||||
@@ -423,16 +424,37 @@ 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()) {
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -27,6 +27,7 @@ import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
@@ -400,7 +401,7 @@ public class ReloadableResourceBundleMessageSource extends AbstractResourceBased
|
||||
|
||||
/**
|
||||
* Refresh the PropertiesHolder for the given bundle filename.
|
||||
* The holder can be {@code null} if not cached before, or a timed-out cache entry
|
||||
* <p>The holder can be {@code null} if not cached before, or a timed-out cache entry
|
||||
* (potentially getting re-validated against the current last-modified timestamp).
|
||||
* @param filename the bundle filename (basename + Locale)
|
||||
* @param propHolder the current PropertiesHolder for the bundle
|
||||
@@ -561,7 +562,7 @@ public class ReloadableResourceBundleMessageSource extends AbstractResourceBased
|
||||
|
||||
private volatile long refreshTimestamp = -2;
|
||||
|
||||
private final ReentrantLock refreshLock = new ReentrantLock();
|
||||
private final Lock refreshLock = new ReentrantLock();
|
||||
|
||||
/** Cache to hold already generated MessageFormats per message code. */
|
||||
private final ConcurrentMap<String, Map<Locale, MessageFormat>> cachedMessageFormats =
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -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;
|
||||
|
||||
+5
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -134,11 +134,6 @@ 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) {
|
||||
@@ -179,11 +174,10 @@ public class ConcurrentTaskExecutor implements AsyncListenableTaskExecutor, Sche
|
||||
}
|
||||
|
||||
|
||||
private TaskExecutorAdapter getAdaptedExecutor(Executor concurrentExecutor) {
|
||||
if (managedExecutorServiceClass != null && managedExecutorServiceClass.isInstance(concurrentExecutor)) {
|
||||
return new ManagedTaskExecutorAdapter(concurrentExecutor);
|
||||
}
|
||||
TaskExecutorAdapter adapter = new TaskExecutorAdapter(concurrentExecutor);
|
||||
private TaskExecutorAdapter getAdaptedExecutor(Executor originalExecutor) {
|
||||
TaskExecutorAdapter adapter =
|
||||
(managedExecutorServiceClass != null && managedExecutorServiceClass.isInstance(originalExecutor) ?
|
||||
new ManagedTaskExecutorAdapter(originalExecutor) : new TaskExecutorAdapter(originalExecutor));
|
||||
if (this.taskDecorator != null) {
|
||||
adapter.setTaskDecorator(this.taskDecorator);
|
||||
}
|
||||
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -183,6 +183,7 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
+6
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -73,7 +73,7 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac
|
||||
|
||||
/**
|
||||
* Set the ThreadFactory to use for the ExecutorService's thread pool.
|
||||
* Default is the underlying ExecutorService's default thread factory.
|
||||
* The default is the underlying ExecutorService's default thread factory.
|
||||
* <p>In a Jakarta EE or other managed environment with JSR-236 support,
|
||||
* consider specifying a JNDI-located ManagedThreadFactory: by default,
|
||||
* to be found at "java:comp/DefaultManagedThreadFactory".
|
||||
@@ -108,7 +108,7 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac
|
||||
/**
|
||||
* Set whether to wait for scheduled tasks to complete on shutdown,
|
||||
* not interrupting running tasks and executing all tasks in the queue.
|
||||
* <p>Default is {@code false}, shutting down immediately through interrupting
|
||||
* <p>The default is {@code false}, shutting down immediately through interrupting
|
||||
* ongoing tasks and clearing the queue. Switch this flag to {@code true} if
|
||||
* you prefer fully completed tasks at the expense of a longer shutdown phase.
|
||||
* <p>Note that Spring's container shutdown continues while ongoing tasks
|
||||
@@ -119,6 +119,8 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac
|
||||
* property instead of or in addition to this property.
|
||||
* @see java.util.concurrent.ExecutorService#shutdown()
|
||||
* @see java.util.concurrent.ExecutorService#shutdownNow()
|
||||
* @see #shutdown()
|
||||
* @see #setAwaitTerminationSeconds
|
||||
*/
|
||||
public void setWaitForTasksToCompleteOnShutdown(boolean waitForJobsToCompleteOnShutdown) {
|
||||
this.waitForTasksToCompleteOnShutdown = waitForJobsToCompleteOnShutdown;
|
||||
@@ -237,7 +239,7 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel the given remaining task which never commended execution,
|
||||
* Cancel the given remaining task which never commenced execution,
|
||||
* as returned from {@link ExecutorService#shutdownNow()}.
|
||||
* @param task the task to cancel (typically a {@link RunnableFuture})
|
||||
* @since 5.0.5
|
||||
|
||||
+5
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -46,8 +46,9 @@ import org.springframework.util.concurrent.ListenableFuture;
|
||||
import org.springframework.util.concurrent.ListenableFutureTask;
|
||||
|
||||
/**
|
||||
* Implementation of Spring's {@link TaskScheduler} interface, wrapping
|
||||
* a native {@link java.util.concurrent.ScheduledThreadPoolExecutor}.
|
||||
* 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.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Mark Fisher
|
||||
@@ -158,6 +159,7 @@ public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport
|
||||
* @see Clock#systemDefaultZone()
|
||||
*/
|
||||
public void setClock(Clock clock) {
|
||||
Assert.notNull(clock, "Clock must not be null");
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
|
||||
+18
-27
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,17 +29,15 @@ import org.springframework.util.StringUtils;
|
||||
* Created using the {@code parse*} methods.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Juergen Hoeller
|
||||
* @since 5.3
|
||||
*/
|
||||
final class BitsCronField extends CronField {
|
||||
|
||||
public static final BitsCronField ZERO_NANOS = forZeroNanos();
|
||||
|
||||
private static final long MASK = 0xFFFFFFFFFFFFFFFFL;
|
||||
|
||||
|
||||
@Nullable
|
||||
private static BitsCronField zeroNanos = null;
|
||||
|
||||
|
||||
// we store at most 60 bits, for seconds and minutes, so a 64-bit long suffices
|
||||
private long bits;
|
||||
|
||||
@@ -48,16 +46,14 @@ final class BitsCronField extends CronField {
|
||||
super(type);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a {@code BitsCronField} enabled for 0 nanoseconds.
|
||||
*/
|
||||
public static BitsCronField zeroNanos() {
|
||||
if (zeroNanos == null) {
|
||||
BitsCronField field = new BitsCronField(Type.NANO);
|
||||
field.setBit(0);
|
||||
zeroNanos = field;
|
||||
}
|
||||
return zeroNanos;
|
||||
private static BitsCronField forZeroNanos() {
|
||||
BitsCronField field = new BitsCronField(Type.NANO);
|
||||
field.setBit(0);
|
||||
return field;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,7 +104,6 @@ final class BitsCronField extends CronField {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private static BitsCronField parseDate(String value, BitsCronField.Type type) {
|
||||
if (value.equals("?")) {
|
||||
value = "*";
|
||||
@@ -174,6 +169,7 @@ final class BitsCronField extends CronField {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <T extends Temporal & Comparable<? super T>> T nextOrSame(T temporal) {
|
||||
@@ -217,7 +213,6 @@ final class BitsCronField extends CronField {
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void setBits(ValueRange range) {
|
||||
@@ -247,7 +242,14 @@ final class BitsCronField extends CronField {
|
||||
}
|
||||
|
||||
private void clearBit(int index) {
|
||||
this.bits &= ~(1L << index);
|
||||
this.bits &= ~(1L << index);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return (this == other || (other instanceof BitsCronField that &&
|
||||
type() == that.type() && this.bits == that.bits));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -255,17 +257,6 @@ final class BitsCronField extends CronField {
|
||||
return Long.hashCode(this.bits);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof BitsCronField other)) {
|
||||
return false;
|
||||
}
|
||||
return type() == other.type() && this.bits == other.bits;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder(type().toString());
|
||||
|
||||
+8
-3
@@ -29,9 +29,14 @@ import org.springframework.util.StringUtils;
|
||||
* <a href="https://www.manpagez.com/man/5/crontab/">crontab expression</a>
|
||||
* that can calculate the next time it matches.
|
||||
*
|
||||
* <p>{@code CronExpression} instances are created through
|
||||
* {@link #parse(String)}; the next match is determined with
|
||||
* {@link #next(Temporal)}.
|
||||
* <p>{@code CronExpression} instances are created through {@link #parse(String)};
|
||||
* the next match is determined with {@link #next(Temporal)}.
|
||||
*
|
||||
* <p>Supports a Quartz day-of-month/week field with an L/# expression. Follows
|
||||
* common cron conventions in every other respect, including 0-6 for SUN-SAT
|
||||
* (plus 7 for SUN as well). Note that Quartz deviates from the day-of-week
|
||||
* convention in cron through 1-7 for SUN-SAT whereas Spring strictly follows
|
||||
* cron even in combination with the optional Quartz-specific L/# expressions.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 5.3
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,17 +29,24 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Single field in a cron pattern. Created using the {@code parse*} methods,
|
||||
* main and only entry point is {@link #nextOrSame(Temporal)}.
|
||||
* the main and only entry point is {@link #nextOrSame(Temporal)}.
|
||||
*
|
||||
* <p>Supports a Quartz day-of-month/week field with an L/# expression. Follows
|
||||
* common cron conventions in every other respect, including 0-6 for SUN-SAT
|
||||
* (plus 7 for SUN as well). Note that Quartz deviates from the day-of-week
|
||||
* convention in cron through 1-7 for SUN-SAT whereas Spring strictly follows
|
||||
* cron even in combination with the optional Quartz-specific L/# expressions.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 5.3
|
||||
*/
|
||||
abstract class CronField {
|
||||
|
||||
private static final String[] MONTHS = new String[]{"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP",
|
||||
"OCT", "NOV", "DEC"};
|
||||
private static final String[] MONTHS = new String[]
|
||||
{"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"};
|
||||
|
||||
private static final String[] DAYS = new String[]{"MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"};
|
||||
private static final String[] DAYS = new String[]
|
||||
{"MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"};
|
||||
|
||||
private final Type type;
|
||||
|
||||
@@ -48,11 +55,12 @@ abstract class CronField {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a {@code CronField} enabled for 0 nanoseconds.
|
||||
*/
|
||||
public static CronField zeroNanos() {
|
||||
return BitsCronField.zeroNanos();
|
||||
return BitsCronField.ZERO_NANOS;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -169,6 +177,7 @@ abstract class CronField {
|
||||
* day-of-month, month, day-of-week.
|
||||
*/
|
||||
protected enum Type {
|
||||
|
||||
NANO(ChronoField.NANO_OF_SECOND, ChronoUnit.SECONDS),
|
||||
SECOND(ChronoField.SECOND_OF_MINUTE, ChronoUnit.MINUTES, ChronoField.NANO_OF_SECOND),
|
||||
MINUTE(ChronoField.MINUTE_OF_HOUR, ChronoUnit.HOURS, ChronoField.SECOND_OF_MINUTE, ChronoField.NANO_OF_SECOND),
|
||||
@@ -177,21 +186,18 @@ abstract class CronField {
|
||||
MONTH(ChronoField.MONTH_OF_YEAR, ChronoUnit.YEARS, ChronoField.DAY_OF_MONTH, ChronoField.HOUR_OF_DAY, ChronoField.MINUTE_OF_HOUR, ChronoField.SECOND_OF_MINUTE, ChronoField.NANO_OF_SECOND),
|
||||
DAY_OF_WEEK(ChronoField.DAY_OF_WEEK, ChronoUnit.WEEKS, ChronoField.HOUR_OF_DAY, ChronoField.MINUTE_OF_HOUR, ChronoField.SECOND_OF_MINUTE, ChronoField.NANO_OF_SECOND);
|
||||
|
||||
|
||||
private final ChronoField field;
|
||||
|
||||
private final ChronoUnit higherOrder;
|
||||
|
||||
private final ChronoField[] lowerOrders;
|
||||
|
||||
|
||||
Type(ChronoField field, ChronoUnit higherOrder, ChronoField... lowerOrders) {
|
||||
this.field = field;
|
||||
this.higherOrder = higherOrder;
|
||||
this.lowerOrders = lowerOrders;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the value of this type for the given temporal.
|
||||
* @return the value of this type
|
||||
|
||||
+8
-2
@@ -27,8 +27,14 @@ import org.springframework.scheduling.TriggerContext;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link Trigger} implementation for cron expressions.
|
||||
* Wraps a {@link CronExpression}.
|
||||
* {@link Trigger} implementation for cron expressions. Wraps a
|
||||
* {@link CronExpression} which parses according to common crontab conventions.
|
||||
*
|
||||
* <p>Supports a Quartz day-of-month/week field with an L/# expression. Follows
|
||||
* common cron conventions in every other respect, including 0-6 for SUN-SAT
|
||||
* (plus 7 for SUN as well). Note that Quartz deviates from the day-of-week
|
||||
* convention in cron through 1-7 for SUN-SAT whereas Spring strictly follows
|
||||
* cron even in combination with the optional Quartz-specific L/# expressions.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Arjen Poutsma
|
||||
|
||||
+31
-34
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -30,10 +30,15 @@ import org.springframework.util.Assert;
|
||||
/**
|
||||
* Extension of {@link CronField} for
|
||||
* <a href="https://www.quartz-scheduler.org">Quartz</a>-specific fields.
|
||||
*
|
||||
* <p>Created using the {@code parse*} methods, uses a {@link TemporalAdjuster}
|
||||
* Created using the {@code parse*} methods, uses a {@link TemporalAdjuster}
|
||||
* internally.
|
||||
*
|
||||
* <p>Supports a Quartz day-of-month/week field with an L/# expression. Follows
|
||||
* common cron conventions in every other respect, including 0-6 for SUN-SAT
|
||||
* (plus 7 for SUN as well). Note that Quartz deviates from the day-of-week
|
||||
* convention in cron through 1-7 for SUN-SAT whereas Spring strictly follows
|
||||
* cron even in combination with the optional Quartz-specific L/# expressions.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 5.3
|
||||
*/
|
||||
@@ -61,8 +66,9 @@ final class QuartzCronField extends CronField {
|
||||
this.rollForwardType = rollForwardType;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the given value is a Quartz day-of-month field.
|
||||
* Determine whether the given value is a Quartz day-of-month field.
|
||||
*/
|
||||
public static boolean isQuartzDaysOfMonthField(String value) {
|
||||
return value.contains("L") || value.contains("W");
|
||||
@@ -80,14 +86,14 @@ final class QuartzCronField extends CronField {
|
||||
if (idx != 0) {
|
||||
throw new IllegalArgumentException("Unrecognized characters before 'L' in '" + value + "'");
|
||||
}
|
||||
else if (value.length() == 2 && value.charAt(1) == 'W') { // "LW"
|
||||
else if (value.length() == 2 && value.charAt(1) == 'W') { // "LW"
|
||||
adjuster = lastWeekdayOfMonth();
|
||||
}
|
||||
else {
|
||||
if (value.length() == 1) { // "L"
|
||||
if (value.length() == 1) { // "L"
|
||||
adjuster = lastDayOfMonth();
|
||||
}
|
||||
else { // "L-[0-9]+"
|
||||
else { // "L-[0-9]+"
|
||||
int offset = Integer.parseInt(value, idx + 1, value.length(), 10);
|
||||
if (offset >= 0) {
|
||||
throw new IllegalArgumentException("Offset '" + offset + " should be < 0 '" + value + "'");
|
||||
@@ -105,7 +111,7 @@ final class QuartzCronField extends CronField {
|
||||
else if (idx != value.length() - 1) {
|
||||
throw new IllegalArgumentException("Unrecognized characters after 'W' in '" + value + "'");
|
||||
}
|
||||
else { // "[0-9]+W"
|
||||
else { // "[0-9]+W"
|
||||
int dayOfMonth = Integer.parseInt(value, 0, idx, 10);
|
||||
dayOfMonth = Type.DAY_OF_MONTH.checkValidValue(dayOfMonth);
|
||||
TemporalAdjuster adjuster = weekdayNearestTo(dayOfMonth);
|
||||
@@ -116,7 +122,7 @@ final class QuartzCronField extends CronField {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given value is a Quartz day-of-week field.
|
||||
* Determine whether the given value is a Quartz day-of-week field.
|
||||
*/
|
||||
public static boolean isQuartzDaysOfWeekField(String value) {
|
||||
return value.contains("L") || value.contains("#");
|
||||
@@ -138,7 +144,7 @@ final class QuartzCronField extends CronField {
|
||||
if (idx == 0) {
|
||||
throw new IllegalArgumentException("No day-of-week before 'L' in '" + value + "'");
|
||||
}
|
||||
else { // "[0-7]L"
|
||||
else { // "[0-7]L"
|
||||
DayOfWeek dayOfWeek = parseDayOfWeek(value.substring(0, idx));
|
||||
adjuster = lastInMonth(dayOfWeek);
|
||||
}
|
||||
@@ -160,7 +166,6 @@ final class QuartzCronField extends CronField {
|
||||
throw new IllegalArgumentException("Ordinal '" + ordinal + "' in '" + value +
|
||||
"' must be positive number ");
|
||||
}
|
||||
|
||||
TemporalAdjuster adjuster = dayOfWeekInMonth(ordinal, dayOfWeek);
|
||||
return new QuartzCronField(Type.DAY_OF_WEEK, Type.DAY_OF_MONTH, adjuster, value);
|
||||
}
|
||||
@@ -170,14 +175,13 @@ final class QuartzCronField extends CronField {
|
||||
private static DayOfWeek parseDayOfWeek(String value) {
|
||||
int dayOfWeek = Integer.parseInt(value);
|
||||
if (dayOfWeek == 0) {
|
||||
dayOfWeek = 7; // cron is 0 based; java.time 1 based
|
||||
dayOfWeek = 7; // cron is 0 based; java.time 1 based
|
||||
}
|
||||
try {
|
||||
return DayOfWeek.of(dayOfWeek);
|
||||
}
|
||||
catch (DateTimeException ex) {
|
||||
String msg = ex.getMessage() + " '" + value + "'";
|
||||
throw new IllegalArgumentException(msg, ex);
|
||||
throw new IllegalArgumentException(ex.getMessage() + " '" + value + "'", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,10 +220,10 @@ final class QuartzCronField extends CronField {
|
||||
Temporal lastDom = adjuster.adjustInto(temporal);
|
||||
Temporal result;
|
||||
int dow = lastDom.get(ChronoField.DAY_OF_WEEK);
|
||||
if (dow == 6) { // Saturday
|
||||
if (dow == 6) { // Saturday
|
||||
result = lastDom.minus(1, ChronoUnit.DAYS);
|
||||
}
|
||||
else if (dow == 7) { // Sunday
|
||||
else if (dow == 7) { // Sunday
|
||||
result = lastDom.minus(2, ChronoUnit.DAYS);
|
||||
}
|
||||
else {
|
||||
@@ -256,10 +260,10 @@ final class QuartzCronField extends CronField {
|
||||
int current = Type.DAY_OF_MONTH.get(temporal);
|
||||
DayOfWeek dayOfWeek = DayOfWeek.from(temporal);
|
||||
|
||||
if ((current == dayOfMonth && isWeekday(dayOfWeek)) || // dayOfMonth is a weekday
|
||||
(dayOfWeek == DayOfWeek.FRIDAY && current == dayOfMonth - 1) || // dayOfMonth is a Saturday, so Friday before
|
||||
(dayOfWeek == DayOfWeek.MONDAY && current == dayOfMonth + 1) || // dayOfMonth is a Sunday, so Monday after
|
||||
(dayOfWeek == DayOfWeek.MONDAY && dayOfMonth == 1 && current == 3)) { // dayOfMonth is Saturday 1st, so Monday 3rd
|
||||
if ((current == dayOfMonth && isWeekday(dayOfWeek)) || // dayOfMonth is a weekday
|
||||
(dayOfWeek == DayOfWeek.FRIDAY && current == dayOfMonth - 1) || // dayOfMonth is a Saturday, so Friday before
|
||||
(dayOfWeek == DayOfWeek.MONDAY && current == dayOfMonth + 1) || // dayOfMonth is a Sunday, so Monday after
|
||||
(dayOfWeek == DayOfWeek.MONDAY && dayOfMonth == 1 && current == 3)) { // dayOfMonth is Saturday 1st, so Monday 3rd
|
||||
return temporal;
|
||||
}
|
||||
int count = 0;
|
||||
@@ -332,6 +336,7 @@ final class QuartzCronField extends CronField {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public <T extends Temporal & Comparable<? super T>> T nextOrSame(T temporal) {
|
||||
T result = adjust(temporal);
|
||||
@@ -348,7 +353,6 @@ final class QuartzCronField extends CronField {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T extends Temporal & Comparable<? super T>> T adjust(T temporal) {
|
||||
@@ -356,27 +360,20 @@ final class QuartzCronField extends CronField {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object other) {
|
||||
return (this == other || (other instanceof QuartzCronField that &&
|
||||
type() == that.type() && this.value.equals(that.value)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.value.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof QuartzCronField other)) {
|
||||
return false;
|
||||
}
|
||||
return type() == other.type() &&
|
||||
this.value.equals(other.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return type() + " '" + this.value + "'";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -39,7 +39,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
*/
|
||||
public class ScopedProxyTests {
|
||||
class ScopedProxyTests {
|
||||
|
||||
private static final Class<?> CLASS = ScopedProxyTests.class;
|
||||
private static final String CLASSNAME = CLASS.getSimpleName();
|
||||
@@ -51,27 +51,24 @@ public class ScopedProxyTests {
|
||||
|
||||
|
||||
@Test // SPR-2108
|
||||
public void testProxyAssignable() throws Exception {
|
||||
void testProxyAssignable() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(MAP_CONTEXT);
|
||||
Object baseMap = bf.getBean("singletonMap");
|
||||
boolean condition = baseMap instanceof Map;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(baseMap instanceof Map).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleProxy() throws Exception {
|
||||
void testSimpleProxy() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(MAP_CONTEXT);
|
||||
Object simpleMap = bf.getBean("simpleMap");
|
||||
boolean condition1 = simpleMap instanceof Map;
|
||||
assertThat(condition1).isTrue();
|
||||
boolean condition = simpleMap instanceof HashMap;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(simpleMap instanceof Map).isTrue();
|
||||
assertThat(simpleMap instanceof HashMap).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScopedOverride() throws Exception {
|
||||
void testScopedOverride() {
|
||||
GenericApplicationContext ctx = new GenericApplicationContext();
|
||||
new XmlBeanDefinitionReader(ctx).loadBeanDefinitions(OVERRIDE_CONTEXT);
|
||||
SimpleMapScope scope = new SimpleMapScope();
|
||||
@@ -87,7 +84,7 @@ public class ScopedProxyTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJdkScopedProxy() throws Exception {
|
||||
void testJdkScopedProxy() throws Exception {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(TESTBEAN_CONTEXT);
|
||||
bf.setSerializationId("X");
|
||||
@@ -97,8 +94,7 @@ public class ScopedProxyTests {
|
||||
ITestBean bean = (ITestBean) bf.getBean("testBean");
|
||||
assertThat(bean).isNotNull();
|
||||
assertThat(AopUtils.isJdkDynamicProxy(bean)).isTrue();
|
||||
boolean condition1 = bean instanceof ScopedObject;
|
||||
assertThat(condition1).isTrue();
|
||||
assertThat(bean instanceof ScopedObject).isTrue();
|
||||
ScopedObject scoped = (ScopedObject) bean;
|
||||
assertThat(scoped.getTargetObject().getClass()).isEqualTo(TestBean.class);
|
||||
bean.setAge(101);
|
||||
@@ -110,8 +106,7 @@ public class ScopedProxyTests {
|
||||
assertThat(deserialized).isNotNull();
|
||||
assertThat(AopUtils.isJdkDynamicProxy(deserialized)).isTrue();
|
||||
assertThat(bean.getAge()).isEqualTo(101);
|
||||
boolean condition = deserialized instanceof ScopedObject;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(deserialized instanceof ScopedObject).isTrue();
|
||||
ScopedObject scopedDeserialized = (ScopedObject) deserialized;
|
||||
assertThat(scopedDeserialized.getTargetObject().getClass()).isEqualTo(TestBean.class);
|
||||
|
||||
@@ -119,7 +114,7 @@ public class ScopedProxyTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCglibScopedProxy() throws Exception {
|
||||
void testCglibScopedProxy() throws Exception {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(LIST_CONTEXT);
|
||||
bf.setSerializationId("Y");
|
||||
@@ -128,8 +123,7 @@ public class ScopedProxyTests {
|
||||
|
||||
TestBean tb = (TestBean) bf.getBean("testBean");
|
||||
assertThat(AopUtils.isCglibProxy(tb.getFriends())).isTrue();
|
||||
boolean condition1 = tb.getFriends() instanceof ScopedObject;
|
||||
assertThat(condition1).isTrue();
|
||||
assertThat(tb.getFriends() instanceof ScopedObject).isTrue();
|
||||
ScopedObject scoped = (ScopedObject) tb.getFriends();
|
||||
assertThat(scoped.getTargetObject().getClass()).isEqualTo(ArrayList.class);
|
||||
tb.getFriends().add("myFriend");
|
||||
@@ -137,12 +131,11 @@ public class ScopedProxyTests {
|
||||
assertThat(scope.getMap().containsKey("scopedTarget.scopedList")).isTrue();
|
||||
assertThat(scope.getMap().get("scopedTarget.scopedList").getClass()).isEqualTo(ArrayList.class);
|
||||
|
||||
ArrayList<?> deserialized = (ArrayList<?>) SerializationTestUtils.serializeAndDeserialize(tb.getFriends());
|
||||
ArrayList<Object> deserialized = (ArrayList<Object>) SerializationTestUtils.serializeAndDeserialize(tb.getFriends());
|
||||
assertThat(deserialized).isNotNull();
|
||||
assertThat(AopUtils.isCglibProxy(deserialized)).isTrue();
|
||||
assertThat(deserialized.contains("myFriend")).isTrue();
|
||||
boolean condition = deserialized instanceof ScopedObject;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(deserialized).contains("myFriend");
|
||||
assertThat(deserialized instanceof ScopedObject).isTrue();
|
||||
ScopedObject scopedDeserialized = (ScopedObject) deserialized;
|
||||
assertThat(scopedDeserialized.getTargetObject().getClass()).isEqualTo(ArrayList.class);
|
||||
|
||||
|
||||
+61
-52
@@ -63,12 +63,12 @@ public class QualifierAnnotationAutowireContextTests {
|
||||
new RootBeanDefinition(QualifiedFieldTestBean.class));
|
||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
|
||||
context::refresh)
|
||||
.satisfies(ex -> {
|
||||
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
|
||||
assertThat(ex.getBeanName()).isEqualTo("autowired");
|
||||
});
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(context::refresh)
|
||||
.satisfies(ex -> {
|
||||
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
|
||||
assertThat(ex.getBeanName()).isEqualTo("autowired");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -81,12 +81,13 @@ public class QualifierAnnotationAutowireContextTests {
|
||||
context.registerBeanDefinition("autowired",
|
||||
new RootBeanDefinition(QualifiedMethodParameterTestBean.class));
|
||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
|
||||
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
|
||||
context::refresh)
|
||||
.satisfies(ex -> {
|
||||
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
|
||||
assertThat(ex.getBeanName()).isEqualTo("autowired");
|
||||
});
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(context::refresh)
|
||||
.satisfies(ex -> {
|
||||
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
|
||||
assertThat(ex.getBeanName()).isEqualTo("autowired");
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@@ -100,9 +101,10 @@ public class QualifierAnnotationAutowireContextTests {
|
||||
context.registerBeanDefinition("autowired",
|
||||
new RootBeanDefinition(QualifiedConstructorArgumentTestBean.class));
|
||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
|
||||
assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(
|
||||
context::refresh)
|
||||
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("autowired"));
|
||||
|
||||
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
|
||||
.isThrownBy(context::refresh)
|
||||
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("autowired"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -205,12 +207,13 @@ public class QualifierAnnotationAutowireContextTests {
|
||||
context.registerBeanDefinition("autowired",
|
||||
new RootBeanDefinition(QualifiedFieldTestBean.class));
|
||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
|
||||
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
|
||||
context::refresh)
|
||||
.satisfies(ex -> {
|
||||
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
|
||||
assertThat(ex.getBeanName()).isEqualTo("autowired");
|
||||
});
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(context::refresh)
|
||||
.satisfies(ex -> {
|
||||
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
|
||||
assertThat(ex.getBeanName()).isEqualTo("autowired");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -227,12 +230,13 @@ public class QualifierAnnotationAutowireContextTests {
|
||||
context.registerBeanDefinition("autowired",
|
||||
new RootBeanDefinition(QualifiedMethodParameterTestBean.class));
|
||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
|
||||
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
|
||||
context::refresh)
|
||||
.satisfies(ex -> {
|
||||
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
|
||||
assertThat(ex.getBeanName()).isEqualTo("autowired");
|
||||
});
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(context::refresh)
|
||||
.satisfies(ex -> {
|
||||
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
|
||||
assertThat(ex.getBeanName()).isEqualTo("autowired");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -249,9 +253,10 @@ public class QualifierAnnotationAutowireContextTests {
|
||||
context.registerBeanDefinition("autowired",
|
||||
new RootBeanDefinition(QualifiedConstructorArgumentTestBean.class));
|
||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
|
||||
assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(
|
||||
context::refresh)
|
||||
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("autowired"));
|
||||
|
||||
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
|
||||
.isThrownBy(context::refresh)
|
||||
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("autowired"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -374,12 +379,13 @@ public class QualifierAnnotationAutowireContextTests {
|
||||
context.registerBeanDefinition("autowired",
|
||||
new RootBeanDefinition(QualifiedFieldWithDefaultValueTestBean.class));
|
||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
|
||||
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
|
||||
context::refresh)
|
||||
.satisfies(ex -> {
|
||||
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
|
||||
assertThat(ex.getBeanName()).isEqualTo("autowired");
|
||||
});
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(context::refresh)
|
||||
.satisfies(ex -> {
|
||||
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
|
||||
assertThat(ex.getBeanName()).isEqualTo("autowired");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -451,12 +457,13 @@ public class QualifierAnnotationAutowireContextTests {
|
||||
context.registerBeanDefinition("autowired",
|
||||
new RootBeanDefinition(QualifiedFieldWithMultipleAttributesTestBean.class));
|
||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
|
||||
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
|
||||
context::refresh)
|
||||
.satisfies(ex -> {
|
||||
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
|
||||
assertThat(ex.getBeanName()).isEqualTo("autowired");
|
||||
});
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(context::refresh)
|
||||
.satisfies(ex -> {
|
||||
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
|
||||
assertThat(ex.getBeanName()).isEqualTo("autowired");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -507,12 +514,13 @@ public class QualifierAnnotationAutowireContextTests {
|
||||
context.registerBeanDefinition("autowired",
|
||||
new RootBeanDefinition(QualifiedFieldWithMultipleAttributesTestBean.class));
|
||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
|
||||
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
|
||||
context::refresh)
|
||||
.satisfies(ex -> {
|
||||
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
|
||||
assertThat(ex.getBeanName()).isEqualTo("autowired");
|
||||
});
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(context::refresh)
|
||||
.satisfies(ex -> {
|
||||
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
|
||||
assertThat(ex.getBeanName()).isEqualTo("autowired");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -574,9 +582,10 @@ public class QualifierAnnotationAutowireContextTests {
|
||||
context.registerBeanDefinition("autowired",
|
||||
new RootBeanDefinition(QualifiedConstructorArgumentWithBaseQualifierNonDefaultValueTestBean.class));
|
||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
|
||||
assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(
|
||||
context::refresh)
|
||||
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("autowired"));
|
||||
|
||||
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
|
||||
.isThrownBy(context::refresh)
|
||||
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("autowired"));
|
||||
}
|
||||
|
||||
|
||||
@@ -752,7 +761,7 @@ public class QualifierAnnotationAutowireContextTests {
|
||||
@Qualifier
|
||||
@interface TestQualifierWithMultipleAttributes {
|
||||
|
||||
String value() default "default";
|
||||
String[] value() default "default";
|
||||
|
||||
int number();
|
||||
}
|
||||
|
||||
+8
-6
@@ -58,9 +58,10 @@ public class QualifierAnnotationTests {
|
||||
BeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
|
||||
reader.loadBeanDefinitions(CONFIG_LOCATION);
|
||||
context.registerSingleton("testBean", NonQualifiedTestBean.class);
|
||||
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
|
||||
context::refresh)
|
||||
.withMessageContaining("found 6");
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(context::refresh)
|
||||
.withMessageContaining("found 6");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -191,9 +192,10 @@ public class QualifierAnnotationTests {
|
||||
BeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
|
||||
reader.loadBeanDefinitions(CONFIG_LOCATION);
|
||||
context.registerSingleton("testBean", QualifiedByAttributesTestBean.class);
|
||||
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
|
||||
context::refresh)
|
||||
.withMessageContaining("found 6");
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(context::refresh)
|
||||
.withMessageContaining("found 6");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+20
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -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().iterator().next()).isSameAs(context.getBean(BeanB.class));
|
||||
assertThat(context.getBeansOfType(BeanC.class).values().iterator().next()).isSameAs(context.getBean(BeanC.class));
|
||||
assertThat(context.getBeansOfType(BeanB.class).values()).singleElement().isSameAs(context.getBean(BeanB.class));
|
||||
assertThat(context.getBeansOfType(BeanC.class).values()).singleElement().isSameAs(context.getBean(BeanC.class));
|
||||
|
||||
assertThatExceptionOfType(NoSuchBeanDefinitionException.class)
|
||||
.isThrownBy(() -> context.getBeanFactory().resolveNamedBean(BeanA.class));
|
||||
@@ -409,15 +409,17 @@ class AnnotationConfigApplicationContextTests {
|
||||
bd2.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class, ResolvableType.forClassWithGenerics(GenericHolder.class, Integer.class)));
|
||||
bd2.setLazyInit(true);
|
||||
context.registerBeanDefinition("fb2", bd2);
|
||||
context.registerBeanDefinition("ip", new RootBeanDefinition(FactoryBeanInjectionPoints.class));
|
||||
RootBeanDefinition bd3 = new RootBeanDefinition(FactoryBeanInjectionPoints.class);
|
||||
bd3.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
|
||||
context.registerBeanDefinition("ip", bd3);
|
||||
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
|
||||
@@ -425,7 +427,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, Object.class)));
|
||||
bd1.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class, ResolvableType.forClassWithGenerics(GenericHolder.class, String.class)));
|
||||
bd1.setLazyInit(true);
|
||||
context.registerBeanDefinition("fb1", bd1);
|
||||
RootBeanDefinition bd2 = new RootBeanDefinition();
|
||||
@@ -433,13 +435,17 @@ class AnnotationConfigApplicationContextTests {
|
||||
bd2.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class, ResolvableType.forClassWithGenerics(GenericHolder.class, Integer.class)));
|
||||
bd2.setLazyInit(true);
|
||||
context.registerBeanDefinition("fb2", bd2);
|
||||
context.registerBeanDefinition("ip", new RootBeanDefinition(FactoryResultInjectionPoint.class));
|
||||
RootBeanDefinition bd3 = new RootBeanDefinition(FactoryBeanInjectionPoints.class);
|
||||
bd3.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
|
||||
context.registerBeanDefinition("ip", bd3);
|
||||
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.getBean("ip", FactoryResultInjectionPoint.class).factoryResult).isSameAs(context.getBean("fb1"));
|
||||
assertThat(context.getBeanNamesForType(FactoryBean.class)).hasSize(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -453,14 +459,17 @@ class AnnotationConfigApplicationContextTests {
|
||||
bd2.setBeanClass(UntypedFactoryBean.class);
|
||||
bd2.setTargetType(ResolvableType.forClassWithGenerics(GenericHolder.class, Integer.class));
|
||||
context.registerBeanDefinition("fb2", bd2);
|
||||
context.registerBeanDefinition("ip", new RootBeanDefinition(FactoryResultInjectionPoint.class));
|
||||
RootBeanDefinition bd3 = new RootBeanDefinition(FactoryResultInjectionPoint.class);
|
||||
bd3.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
|
||||
context.registerBeanDefinition("ip", bd3);
|
||||
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
|
||||
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -268,6 +268,7 @@ 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())
|
||||
|
||||
+10
-10
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -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.get(0)).isEqualTo(freshContext);
|
||||
assertThat(bean.instances.get(1)).isInstanceOfSatisfying(AnnotationMetadata.class, metadata ->
|
||||
assertThat(bean.instances).element(0).isEqualTo(freshContext);
|
||||
assertThat(bean.instances).element(1).isInstanceOfSatisfying(AnnotationMetadata.class, metadata ->
|
||||
assertThat(metadata.getClassName()).isEqualTo(TestAwareCallbackConfiguration.class.getName()));
|
||||
freshContext.close();
|
||||
});
|
||||
@@ -236,13 +236,14 @@ class ConfigurationClassPostProcessorAotContributionTests {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
public void afterPropertiesSet() {
|
||||
Assert.notNull(this.metadata, "Metadata was not injected");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
class PropertySourceTests {
|
||||
|
||||
@@ -362,9 +363,9 @@ class ConfigurationClassPostProcessorAotContributionTests {
|
||||
static class PropertySourceWithCustomFactoryConfiguration {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
class ConfigurationClassProxyTests {
|
||||
|
||||
@@ -384,15 +385,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) {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
@@ -410,8 +410,8 @@ class ConfigurationClassPostProcessorAotContributionTests {
|
||||
.containsExactly(entry(key.getName(), value.getName()));
|
||||
}
|
||||
|
||||
static class CustomPropertySourcesFactory extends DefaultPropertySourceFactory {
|
||||
|
||||
static class CustomPropertySourcesFactory extends DefaultPropertySourceFactory {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+97
-68
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -35,6 +35,8 @@ 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;
|
||||
@@ -321,8 +323,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
|
||||
@@ -372,11 +374,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
|
||||
@@ -429,12 +431,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
|
||||
@@ -944,8 +946,8 @@ class ConfigurationClassPostProcessorTests {
|
||||
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(ConcreteConfig.class));
|
||||
beanFactory.registerBeanDefinition("serviceBeanProvider", new RootBeanDefinition(ServiceBeanProvider.class));
|
||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
||||
beanFactory.preInstantiateSingletons();
|
||||
|
||||
beanFactory.preInstantiateSingletons();
|
||||
beanFactory.getBean(ServiceBean.class);
|
||||
}
|
||||
|
||||
@@ -958,8 +960,8 @@ class ConfigurationClassPostProcessorTests {
|
||||
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(ConcreteConfigWithDefaultMethods.class));
|
||||
beanFactory.registerBeanDefinition("serviceBeanProvider", new RootBeanDefinition(ServiceBeanProvider.class));
|
||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
||||
beanFactory.preInstantiateSingletons();
|
||||
|
||||
beanFactory.preInstantiateSingletons();
|
||||
beanFactory.getBean(ServiceBean.class);
|
||||
}
|
||||
|
||||
@@ -972,11 +974,25 @@ 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.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();
|
||||
@@ -985,16 +1001,17 @@ 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
|
||||
@@ -1048,9 +1065,7 @@ class ConfigurationClassPostProcessorTests {
|
||||
void testCollectionArgumentOnBeanMethod() {
|
||||
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(CollectionArgumentConfiguration.class, TestBean.class);
|
||||
CollectionArgumentConfiguration bean = ctx.getBean(CollectionArgumentConfiguration.class);
|
||||
assertThat(bean.testBeans).isNotNull();
|
||||
assertThat(bean.testBeans).hasSize(1);
|
||||
assertThat(bean.testBeans.get(0)).isSameAs(ctx.getBean(TestBean.class));
|
||||
assertThat(bean.testBeans).containsExactly(ctx.getBean(TestBean.class));
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@@ -1058,8 +1073,7 @@ class ConfigurationClassPostProcessorTests {
|
||||
void testEmptyCollectionArgumentOnBeanMethod() {
|
||||
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(CollectionArgumentConfiguration.class);
|
||||
CollectionArgumentConfiguration bean = ctx.getBean(CollectionArgumentConfiguration.class);
|
||||
assertThat(bean.testBeans).isNotNull();
|
||||
assertThat(bean.testBeans.isEmpty()).isTrue();
|
||||
assertThat(bean.testBeans).isEmpty();
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@@ -1067,9 +1081,7 @@ class ConfigurationClassPostProcessorTests {
|
||||
void testMapArgumentOnBeanMethod() {
|
||||
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(MapArgumentConfiguration.class, DummyRunnable.class);
|
||||
MapArgumentConfiguration bean = ctx.getBean(MapArgumentConfiguration.class);
|
||||
assertThat(bean.testBeans).isNotNull();
|
||||
assertThat(bean.testBeans).hasSize(1);
|
||||
assertThat(bean.testBeans.values().iterator().next()).isSameAs(ctx.getBean(Runnable.class));
|
||||
assertThat(bean.testBeans).hasSize(1).containsValue(ctx.getBean(Runnable.class));
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@@ -1077,8 +1089,7 @@ class ConfigurationClassPostProcessorTests {
|
||||
void testEmptyMapArgumentOnBeanMethod() {
|
||||
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(MapArgumentConfiguration.class);
|
||||
MapArgumentConfiguration bean = ctx.getBean(MapArgumentConfiguration.class);
|
||||
assertThat(bean.testBeans).isNotNull();
|
||||
assertThat(bean.testBeans.isEmpty()).isTrue();
|
||||
assertThat(bean.testBeans).isEmpty();
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@@ -1086,9 +1097,7 @@ class ConfigurationClassPostProcessorTests {
|
||||
void testCollectionInjectionFromSameConfigurationClass() {
|
||||
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(CollectionInjectionConfiguration.class);
|
||||
CollectionInjectionConfiguration bean = ctx.getBean(CollectionInjectionConfiguration.class);
|
||||
assertThat(bean.testBeans).isNotNull();
|
||||
assertThat(bean.testBeans).hasSize(1);
|
||||
assertThat(bean.testBeans.get(0)).isSameAs(ctx.getBean(TestBean.class));
|
||||
assertThat(bean.testBeans).containsExactly(ctx.getBean(TestBean.class));
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@@ -1096,25 +1105,21 @@ class ConfigurationClassPostProcessorTests {
|
||||
void testMapInjectionFromSameConfigurationClass() {
|
||||
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(MapInjectionConfiguration.class);
|
||||
MapInjectionConfiguration bean = ctx.getBean(MapInjectionConfiguration.class);
|
||||
assertThat(bean.testBeans).isNotNull();
|
||||
assertThat(bean.testBeans).hasSize(1);
|
||||
assertThat(bean.testBeans.get("testBean")).isSameAs(ctx.getBean(Runnable.class));
|
||||
assertThat(bean.testBeans).containsOnly(Map.entry("testBean", ctx.getBean(Runnable.class)));
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBeanLookupFromSameConfigurationClass() {
|
||||
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(BeanLookupConfiguration.class);
|
||||
BeanLookupConfiguration bean = ctx.getBean(BeanLookupConfiguration.class);
|
||||
assertThat(bean.getTestBean()).isNotNull();
|
||||
assertThat(bean.getTestBean()).isSameAs(ctx.getBean(TestBean.class));
|
||||
assertThat(ctx.getBean(BeanLookupConfiguration.class).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
|
||||
@@ -1131,11 +1136,11 @@ class ConfigurationClassPostProcessorTests {
|
||||
@Order(1)
|
||||
static class SingletonBeanConfig {
|
||||
|
||||
public @Bean Foo foo() {
|
||||
@Bean public Foo foo() {
|
||||
return new Foo();
|
||||
}
|
||||
|
||||
public @Bean Bar bar() {
|
||||
@Bean public Bar bar() {
|
||||
return new Bar(foo());
|
||||
}
|
||||
}
|
||||
@@ -1143,11 +1148,11 @@ class ConfigurationClassPostProcessorTests {
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class NonEnhancedSingletonBeanConfig {
|
||||
|
||||
public @Bean Foo foo() {
|
||||
@Bean public Foo foo() {
|
||||
return new Foo();
|
||||
}
|
||||
|
||||
public @Bean Bar bar() {
|
||||
@Bean public Bar bar() {
|
||||
return new Bar(foo());
|
||||
}
|
||||
}
|
||||
@@ -1155,11 +1160,13 @@ class ConfigurationClassPostProcessorTests {
|
||||
@Configuration
|
||||
static class StaticSingletonBeanConfig {
|
||||
|
||||
public static @Bean Foo foo() {
|
||||
@Bean
|
||||
public static Foo foo() {
|
||||
return new Foo();
|
||||
}
|
||||
|
||||
public static @Bean Bar bar() {
|
||||
@Bean
|
||||
public static Bar bar() {
|
||||
return new Bar(foo());
|
||||
}
|
||||
}
|
||||
@@ -1168,11 +1175,11 @@ class ConfigurationClassPostProcessorTests {
|
||||
@Order(2)
|
||||
static class OverridingSingletonBeanConfig {
|
||||
|
||||
public @Bean ExtendedFoo foo() {
|
||||
@Bean public ExtendedFoo foo() {
|
||||
return new ExtendedFoo();
|
||||
}
|
||||
|
||||
public @Bean Bar bar() {
|
||||
@Bean public Bar bar() {
|
||||
return new Bar(foo());
|
||||
}
|
||||
}
|
||||
@@ -1180,7 +1187,7 @@ class ConfigurationClassPostProcessorTests {
|
||||
@Configuration
|
||||
static class OverridingAgainSingletonBeanConfig {
|
||||
|
||||
public @Bean ExtendedAgainFoo foo() {
|
||||
@Bean public ExtendedAgainFoo foo() {
|
||||
return new ExtendedAgainFoo();
|
||||
}
|
||||
}
|
||||
@@ -1188,7 +1195,7 @@ class ConfigurationClassPostProcessorTests {
|
||||
@Configuration
|
||||
static class InvalidOverridingSingletonBeanConfig {
|
||||
|
||||
public @Bean Foo foo() {
|
||||
@Bean public Foo foo() {
|
||||
return new Foo();
|
||||
}
|
||||
}
|
||||
@@ -1200,11 +1207,11 @@ class ConfigurationClassPostProcessorTests {
|
||||
@Order(1)
|
||||
static class SingletonBeanConfig {
|
||||
|
||||
public @Bean Foo foo() {
|
||||
@Bean public Foo foo() {
|
||||
return new Foo();
|
||||
}
|
||||
|
||||
public @Bean Bar bar() {
|
||||
@Bean public Bar bar() {
|
||||
return new Bar(foo());
|
||||
}
|
||||
}
|
||||
@@ -1213,11 +1220,11 @@ class ConfigurationClassPostProcessorTests {
|
||||
@Order(2)
|
||||
static class OverridingSingletonBeanConfig {
|
||||
|
||||
public @Bean ExtendedFoo foo() {
|
||||
@Bean public ExtendedFoo foo() {
|
||||
return new ExtendedFoo();
|
||||
}
|
||||
|
||||
public @Bean Bar bar() {
|
||||
@Bean public Bar bar() {
|
||||
return new Bar(foo());
|
||||
}
|
||||
}
|
||||
@@ -1233,11 +1240,11 @@ class ConfigurationClassPostProcessorTests {
|
||||
public SingletonBeanConfig(ConfigWithOrderedInnerClasses other) {
|
||||
}
|
||||
|
||||
public @Bean Foo foo() {
|
||||
@Bean public Foo foo() {
|
||||
return new Foo();
|
||||
}
|
||||
|
||||
public @Bean Bar bar() {
|
||||
@Bean public Bar bar() {
|
||||
return new Bar(foo());
|
||||
}
|
||||
}
|
||||
@@ -1250,11 +1257,11 @@ class ConfigurationClassPostProcessorTests {
|
||||
other.getObject();
|
||||
}
|
||||
|
||||
public @Bean ExtendedFoo foo() {
|
||||
@Bean public ExtendedFoo foo() {
|
||||
return new ExtendedFoo();
|
||||
}
|
||||
|
||||
public @Bean Bar bar() {
|
||||
@Bean public Bar bar() {
|
||||
return new Bar(foo());
|
||||
}
|
||||
}
|
||||
@@ -1281,7 +1288,7 @@ class ConfigurationClassPostProcessorTests {
|
||||
@Configuration
|
||||
static class UnloadedConfig {
|
||||
|
||||
public @Bean Foo foo() {
|
||||
@Bean public Foo foo() {
|
||||
return new Foo();
|
||||
}
|
||||
}
|
||||
@@ -1289,7 +1296,7 @@ class ConfigurationClassPostProcessorTests {
|
||||
@Configuration
|
||||
static class LoadedConfig {
|
||||
|
||||
public @Bean Bar bar() {
|
||||
@Bean public Bar bar() {
|
||||
return new Bar(new Foo());
|
||||
}
|
||||
}
|
||||
@@ -1598,7 +1605,7 @@ class ConfigurationClassPostProcessorTests {
|
||||
public static class WildcardWithGenericExtendsConfiguration {
|
||||
|
||||
@Bean
|
||||
public Repository<? extends Object> genericRepo() {
|
||||
public Repository<?> genericRepo() {
|
||||
return new Repository<String>();
|
||||
}
|
||||
|
||||
@@ -1707,7 +1714,7 @@ class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static abstract class AbstractConfig {
|
||||
public abstract static class AbstractConfig {
|
||||
|
||||
@Bean
|
||||
public ServiceBean serviceBean() {
|
||||
@@ -1758,7 +1765,6 @@ class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
public interface DefaultMethodsConfig extends BaseDefaultMethods {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@@ -1779,6 +1785,29 @@ 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 {
|
||||
|
||||
@@ -1891,7 +1920,7 @@ class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
}
|
||||
|
||||
static abstract class FooFactory {
|
||||
abstract static class FooFactory {
|
||||
|
||||
abstract DependingFoo createFoo(BarArgument bar);
|
||||
}
|
||||
@@ -2010,7 +2039,7 @@ class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static abstract class BeanLookupConfiguration {
|
||||
abstract static class BeanLookupConfiguration {
|
||||
|
||||
@Bean
|
||||
public TestBean thing() {
|
||||
|
||||
+58
-40
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,7 +29,9 @@ 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;
|
||||
@@ -39,51 +41,62 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* {@link FactoryBean FactoryBeans} defined in the configuration.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
|
||||
class ConfigurationWithFactoryBeanEarlyDeductionTests {
|
||||
|
||||
@Test
|
||||
public void preFreezeDirect() {
|
||||
void preFreezeDirect() {
|
||||
assertPreFreeze(DirectConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postFreezeDirect() {
|
||||
void postFreezeDirect() {
|
||||
assertPostFreeze(DirectConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preFreezeGenericMethod() {
|
||||
void preFreezeGenericMethod() {
|
||||
assertPreFreeze(GenericMethodConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postFreezeGenericMethod() {
|
||||
void postFreezeGenericMethod() {
|
||||
assertPostFreeze(GenericMethodConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preFreezeGenericClass() {
|
||||
void preFreezeGenericClass() {
|
||||
assertPreFreeze(GenericClassConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postFreezeGenericClass() {
|
||||
void postFreezeGenericClass() {
|
||||
assertPostFreeze(GenericClassConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preFreezeAttribute() {
|
||||
void preFreezeAttribute() {
|
||||
assertPreFreeze(AttributeClassConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postFreezeAttribute() {
|
||||
void postFreezeAttribute() {
|
||||
assertPostFreeze(AttributeClassConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preFreezeUnresolvedGenericFactoryBean() {
|
||||
void preFreezeTargetType() {
|
||||
assertPreFreeze(TargetTypeConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void postFreezeTargetType() {
|
||||
assertPostFreeze(TargetTypeConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
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
|
||||
@@ -105,14 +118,13 @@ public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
@@ -132,41 +144,38 @@ public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
|
||||
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 {
|
||||
this.names = beanFactory.getBeanNamesForType(MyBean.class, true, false);
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||
ResolvableType typeToMatch = ResolvableType.forClassWithGenerics(MyBean.class, String.class);
|
||||
this.names = beanFactory.getBeanNamesForType(typeToMatch, true, false);
|
||||
}
|
||||
|
||||
public String[] getNames() {
|
||||
return this.names;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class DirectConfiguration {
|
||||
|
||||
@Bean
|
||||
MyBean myBean() {
|
||||
return new MyBean();
|
||||
MyBean<String> myBean() {
|
||||
return new MyBean<>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class GenericMethodConfiguration {
|
||||
|
||||
@Bean
|
||||
FactoryBean<MyBean> myBean() {
|
||||
return new TestFactoryBean<>(new MyBean());
|
||||
FactoryBean<MyBean<String>> myBean() {
|
||||
return new TestFactoryBean<>(new MyBean<>());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@@ -176,13 +185,11 @@ public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
|
||||
MyFactoryBean myBean() {
|
||||
return new MyFactoryBean();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(AttributeClassRegistrar.class)
|
||||
static class AttributeClassConfiguration {
|
||||
|
||||
}
|
||||
|
||||
static class AttributeClassRegistrar implements ImportBeanDefinitionRegistrar {
|
||||
@@ -191,16 +198,32 @@ public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
|
||||
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
|
||||
BeanDefinition definition = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
RawWithAbstractObjectTypeFactoryBean.class).getBeanDefinition();
|
||||
definition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, MyBean.class);
|
||||
definition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE,
|
||||
ResolvableType.forClassWithGenerics(MyBean.class, String.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 extends AbstractMyBean {
|
||||
static class MyBean<T> extends AbstractMyBean {
|
||||
}
|
||||
|
||||
static class TestFactoryBean<T> implements FactoryBean<T> {
|
||||
@@ -212,7 +235,7 @@ public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getObject() throws Exception {
|
||||
public T getObject() {
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
@@ -220,31 +243,26 @@ public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
|
||||
public Class<?> getObjectType() {
|
||||
return this.instance.getClass();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class MyFactoryBean extends TestFactoryBean<MyBean> {
|
||||
static class MyFactoryBean extends TestFactoryBean<MyBean<String>> {
|
||||
|
||||
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 {
|
||||
return object;
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return MyBean.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+13
-13
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -204,7 +204,7 @@ class ConfigurationClassProcessingTests {
|
||||
BeanFactory factory = initBeanFactory(ConfigWithNullReference.class);
|
||||
|
||||
TestBean foo = factory.getBean("foo", TestBean.class);
|
||||
assertThat(factory.getBean("bar").equals(null)).isTrue();
|
||||
assertThat(factory.getBean("bar")).isEqualTo(null);
|
||||
assertThat(foo.getSpouse()).isNull();
|
||||
}
|
||||
|
||||
@@ -426,7 +426,7 @@ class ConfigurationClassProcessingTests {
|
||||
@Configuration
|
||||
static class ConfigWithFinalBean {
|
||||
|
||||
public final @Bean TestBean testBean() {
|
||||
@Bean public final TestBean testBean() {
|
||||
return new TestBean();
|
||||
}
|
||||
}
|
||||
@@ -435,7 +435,7 @@ class ConfigurationClassProcessingTests {
|
||||
@Configuration
|
||||
static class SimplestPossibleConfig {
|
||||
|
||||
public @Bean String stringBean() {
|
||||
@Bean public String stringBean() {
|
||||
return "foo";
|
||||
}
|
||||
}
|
||||
@@ -444,11 +444,11 @@ class ConfigurationClassProcessingTests {
|
||||
@Configuration
|
||||
static class ConfigWithNonSpecificReturnTypes {
|
||||
|
||||
public @Bean Object stringBean() {
|
||||
@Bean public Object stringBean() {
|
||||
return "foo";
|
||||
}
|
||||
|
||||
public @Bean FactoryBean<?> factoryBean() {
|
||||
@Bean public FactoryBean<?> factoryBean() {
|
||||
ListFactoryBean fb = new ListFactoryBean();
|
||||
fb.setSourceList(Arrays.asList("element1", "element2"));
|
||||
return fb;
|
||||
@@ -459,13 +459,13 @@ class ConfigurationClassProcessingTests {
|
||||
@Configuration
|
||||
static class ConfigWithPrototypeBean {
|
||||
|
||||
public @Bean TestBean foo() {
|
||||
@Bean public TestBean foo() {
|
||||
TestBean foo = new SpousyTestBean("foo");
|
||||
foo.setSpouse(bar());
|
||||
return foo;
|
||||
}
|
||||
|
||||
public @Bean TestBean bar() {
|
||||
@Bean public 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 testBean) {
|
||||
return new NestedTestBean(testBean.getSpouse().getName());
|
||||
public NestedTestBean nestedTestBean(TestBean spouse) {
|
||||
return new NestedTestBean(spouse.getSpouse().getName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -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 RunnableFuture) {
|
||||
((RunnableFuture<?>) task).cancel(true);
|
||||
if (task instanceof Future) {
|
||||
((Future<?>) task).cancel(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -101,7 +101,7 @@ class ThreadPoolTaskSchedulerTests extends AbstractSchedulingTaskExecutorTests {
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("deprecation")
|
||||
void scheduleOneTimeFailingTaskWithoutErrorHandler() throws Exception {
|
||||
void scheduleOneTimeFailingTaskWithoutErrorHandler() {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+14
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -35,27 +35,32 @@ class BitsCronFieldTests {
|
||||
@Test
|
||||
void parse() {
|
||||
assertThat(BitsCronField.parseSeconds("42")).has(clearRange(0, 41)).has(set(42)).has(clearRange(43, 59));
|
||||
assertThat(BitsCronField.parseSeconds("0-4,8-12")).has(setRange(0, 4)).has(clearRange(5,7)).has(setRange(8, 12)).has(clearRange(13,59));
|
||||
assertThat(BitsCronField.parseSeconds("57/2")).has(clearRange(0, 56)).has(set(57)).has(clear(58)).has(set(59));
|
||||
assertThat(BitsCronField.parseSeconds("0-4,8-12")).has(setRange(0, 4)).has(clearRange(5,7))
|
||||
.has(setRange(8, 12)).has(clearRange(13,59));
|
||||
assertThat(BitsCronField.parseSeconds("57/2")).has(clearRange(0, 56)).has(set(57))
|
||||
.has(clear(58)).has(set(59));
|
||||
|
||||
assertThat(BitsCronField.parseMinutes("30")).has(set(30)).has(clearRange(1, 29)).has(clearRange(31, 59));
|
||||
|
||||
assertThat(BitsCronField.parseHours("23")).has(set(23)).has(clearRange(0, 23));
|
||||
assertThat(BitsCronField.parseHours("0-23/2")).has(set(0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22)).has(clear(1,3,5,7,9,11,13,15,17,19,21,23));
|
||||
assertThat(BitsCronField.parseHours("0-23/2")).has(set(0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22))
|
||||
.has(clear(1,3,5,7,9,11,13,15,17,19,21,23));
|
||||
|
||||
assertThat(BitsCronField.parseDaysOfMonth("1")).has(set(1)).has(clearRange(2, 31));
|
||||
|
||||
assertThat(BitsCronField.parseMonth("1")).has(set(1)).has(clearRange(2, 12));
|
||||
|
||||
assertThat(BitsCronField.parseDaysOfWeek("0")).has(set(7, 7)).has(clearRange(0, 6));
|
||||
|
||||
assertThat(BitsCronField.parseDaysOfWeek("7-5")).has(clear(0)).has(setRange(1, 5)).has(clear(6)).has(set(7));
|
||||
assertThat(BitsCronField.parseDaysOfWeek("7-5")).has(clear(0)).has(setRange(1, 5))
|
||||
.has(clear(6)).has(set(7));
|
||||
}
|
||||
|
||||
@Test
|
||||
void parseLists() {
|
||||
assertThat(BitsCronField.parseSeconds("15,30")).has(set(15, 30)).has(clearRange(1, 15)).has(clearRange(31, 59));
|
||||
assertThat(BitsCronField.parseMinutes("1,2,5,9")).has(set(1, 2, 5, 9)).has(clear(0)).has(clearRange(3, 4)).has(clearRange(6, 8)).has(clearRange(10, 59));
|
||||
assertThat(BitsCronField.parseSeconds("15,30")).has(set(15, 30)).has(clearRange(1, 15))
|
||||
.has(clearRange(31, 59));
|
||||
assertThat(BitsCronField.parseMinutes("1,2,5,9")).has(set(1, 2, 5, 9)).has(clear(0))
|
||||
.has(clearRange(3, 4)).has(clearRange(6, 8)).has(clearRange(10, 59));
|
||||
assertThat(BitsCronField.parseHours("1,2,3")).has(set(1, 2, 3)).has(clearRange(4, 23));
|
||||
assertThat(BitsCronField.parseDaysOfMonth("1,2,3")).has(set(1, 2, 3)).has(clearRange(4, 31));
|
||||
assertThat(BitsCronField.parseMonth("1,2,3")).has(set(1, 2, 3)).has(clearRange(4, 12));
|
||||
@@ -107,6 +112,7 @@ class BitsCronFieldTests {
|
||||
.has(clear(0)).has(setRange(1, 7));
|
||||
}
|
||||
|
||||
|
||||
private static Condition<BitsCronField> set(int... indices) {
|
||||
return new Condition<>(String.format("set bits %s", Arrays.toString(indices))) {
|
||||
@Override
|
||||
|
||||
+42
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -28,6 +28,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
* Unit tests for {@link QuartzCronField}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
class QuartzCronFieldTests {
|
||||
|
||||
@@ -71,6 +72,46 @@ class QuartzCronFieldTests {
|
||||
assertThat(field.nextOrSame(last)).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
void dayOfWeek_0(){
|
||||
// third Sunday (0) of the month
|
||||
QuartzCronField field = QuartzCronField.parseDaysOfWeek("0#3");
|
||||
|
||||
LocalDate last = LocalDate.of(2024, 1, 1);
|
||||
LocalDate expected = LocalDate.of(2024, 1, 21);
|
||||
assertThat(field.nextOrSame(last)).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
void dayOfWeek_1(){
|
||||
// third Monday (1) of the month
|
||||
QuartzCronField field = QuartzCronField.parseDaysOfWeek("1#3");
|
||||
|
||||
LocalDate last = LocalDate.of(2024, 1, 1);
|
||||
LocalDate expected = LocalDate.of(2024, 1, 15);
|
||||
assertThat(field.nextOrSame(last)).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
void dayOfWeek_2(){
|
||||
// third Tuesday (2) of the month
|
||||
QuartzCronField field = QuartzCronField.parseDaysOfWeek("2#3");
|
||||
|
||||
LocalDate last = LocalDate.of(2024, 1, 1);
|
||||
LocalDate expected = LocalDate.of(2024, 1, 16);
|
||||
assertThat(field.nextOrSame(last)).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
void dayOfWeek_7() {
|
||||
// third Sunday (7 as alternative to 0) of the month
|
||||
QuartzCronField field = QuartzCronField.parseDaysOfWeek("7#3");
|
||||
|
||||
LocalDate last = LocalDate.of(2024, 1, 1);
|
||||
LocalDate expected = LocalDate.of(2024, 1, 21);
|
||||
assertThat(field.nextOrSame(last)).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
void invalidValues() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> QuartzCronField.parseDaysOfMonth(""));
|
||||
|
||||
@@ -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");
|
||||
|
||||
+1
-2
@@ -78,8 +78,7 @@ public class MethodValidationTests {
|
||||
ac.close();
|
||||
}
|
||||
|
||||
@Test // gh-29782
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test // gh-29782
|
||||
public void testMethodValidationPostProcessorForInterfaceOnlyProxy() {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
context.register(MethodValidationPostProcessor.class);
|
||||
|
||||
+8
-2
@@ -195,9 +195,15 @@ public class BindingReflectionHintsRegistrar {
|
||||
}
|
||||
|
||||
private void registerHintsForClassAttributes(ReflectionHints hints, MergedAnnotation<Annotation> annotation) {
|
||||
annotation.getRoot().asMap().values().forEach(value -> {
|
||||
annotation.getRoot().asMap().forEach((key,value) -> {
|
||||
if (value instanceof Class<?> classValue && value != Void.class) {
|
||||
hints.registerType(classValue, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
|
||||
if (key.equals("builder")) {
|
||||
hints.registerType(classValue, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
|
||||
MemberCategory.INVOKE_DECLARED_METHODS);
|
||||
}
|
||||
else {
|
||||
hints.registerType(classValue, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user