mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
248 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fb9a56f40c | |||
| 7678286fb3 | |||
| 510ff87721 | |||
| 7609727433 | |||
| a0ae96da69 | |||
| 1d2daa5d6e | |||
| 239594595c | |||
| 76c0017180 | |||
| 0628b479f1 | |||
| f6205d4207 | |||
| d21100fea0 | |||
| f7f1028428 | |||
| 1cb0c7c036 | |||
| 51d70dcf34 | |||
| 61f7087911 | |||
| 2ff8a00e9a | |||
| a653b85378 | |||
| 17650e0741 | |||
| 0c17d257fb | |||
| 297cbae299 | |||
| 274fba47f3 | |||
| 5dfec09edd | |||
| 5056e8cbfb | |||
| 4566e8685d | |||
| 1b84f970de | |||
| 41bc43b033 | |||
| 915d5bddea | |||
| dc86feaeb6 | |||
| ff412de247 | |||
| 5d572f6490 | |||
| b8b1f5b6be | |||
| 99e38ecf41 | |||
| ed0c2ff37f | |||
| e668e7767c | |||
| 30c75ffe8e | |||
| dd5fe68522 | |||
| 2aca714010 | |||
| 3b7c435134 | |||
| 6432b13a4c | |||
| 3478a702e5 | |||
| b31550fd85 | |||
| 701e9e410f | |||
| ce385d136d | |||
| e44f84e8ee | |||
| 464fa7ea0e | |||
| 6e1f583c06 | |||
| 57646a092e | |||
| 380f5d5ea4 | |||
| 1acc1c3a27 | |||
| 6ad75bdb4a | |||
| 8e3ad4a488 | |||
| 6f4cc40a52 | |||
| 452973fbbd | |||
| 7ec5c994c1 | |||
| 60035b5e39 | |||
| 43ecb0b94a | |||
| 0e5edc4474 | |||
| 70e2e89602 | |||
| a2af34f9b6 | |||
| c35e90c171 | |||
| 186d6e370d | |||
| 732642d8ad | |||
| d7649d088d | |||
| 7ac66eff4d | |||
| f22bdf4734 | |||
| a0f24cf485 | |||
| 562fa4e1ad | |||
| 9d10807f8b | |||
| fd240b3b86 | |||
| df1bec9e97 | |||
| cfec88bfa8 | |||
| dd9b6749d7 | |||
| 2b9cea618f | |||
| e16dd5f3e2 | |||
| c1db06af88 | |||
| b2bdc7de30 | |||
| 8f2bb4973d | |||
| a45154c875 | |||
| d54e101f00 | |||
| 9ef2b5d908 | |||
| 0ecbeef01d | |||
| 5105fdf8c0 | |||
| 687676ea47 | |||
| b17714bb7b | |||
| 61ac912f67 | |||
| 1077d5c5ea | |||
| b28a5f858c | |||
| e660859ec3 | |||
| a6ab308cf4 | |||
| c489234cc2 | |||
| af593588d3 | |||
| 75193b2e84 | |||
| f97e819c9e | |||
| f8e1ce3074 | |||
| d3ec9395e1 | |||
| de0cb53394 | |||
| 1bf5d8b9e5 | |||
| 15a306d8dd | |||
| 680939557c | |||
| 84f3ce1f91 | |||
| 2dc4fe0857 | |||
| cb28e2a701 | |||
| 0370aa6007 | |||
| 704650de6d | |||
| 8538a73f20 | |||
| c615a6667d | |||
| 45cfe70d30 | |||
| 288e255ce9 | |||
| 12159b98a2 | |||
| 7a8ec2a761 | |||
| ca4674df42 | |||
| 2ec32a4e2f | |||
| 3849ed3a0c | |||
| d62f17864c | |||
| 3c59c2a275 | |||
| 2b4c1e265c | |||
| cc0cbc15aa | |||
| f7bf2431fb | |||
| 40678bb981 | |||
| 75faf698af | |||
| 39c225c813 | |||
| 0c3d8d7a44 | |||
| ddcae04ad5 | |||
| 994bbec0c3 | |||
| afb378a59f | |||
| a4fc7d3c11 | |||
| 88c3a788f3 | |||
| d7ac89ecc9 | |||
| 9894174960 | |||
| 493f75e892 | |||
| df066d8190 | |||
| 5f7a6a0f38 | |||
| 4326c53222 | |||
| 3da7a35a91 | |||
| b9be40ccd2 | |||
| f7d4bd176e | |||
| 2b48254268 | |||
| bb46b31925 | |||
| 0c275107ea | |||
| 9931f442e4 | |||
| fc085e8663 | |||
| 9d7154901f | |||
| 24893d038f | |||
| b9482375b7 | |||
| ad61fb75da | |||
| c5aa7830bc | |||
| 14c8c9168c | |||
| 23eb0e3128 | |||
| 840bd574db | |||
| 6dea580145 | |||
| d1efc891db | |||
| c9849d6972 | |||
| 790abeda1c | |||
| 340b32a3cb | |||
| a7b7466274 | |||
| 3a8c0dbd8a | |||
| b387d9bf10 | |||
| 0f33f79c05 | |||
| ef65429823 | |||
| 0b4b313bae | |||
| 6879be7508 | |||
| 355fa258bd | |||
| 60337738d8 | |||
| 53319381d0 | |||
| e1e7fa489b | |||
| e1351a5cb5 | |||
| ac94d2bd54 | |||
| 16fd70ae35 | |||
| c1bf09952b | |||
| 9e7ee0cb8e | |||
| 1cd994d3a8 | |||
| 3d28c024c5 | |||
| 3444892aea | |||
| 14a7983eef | |||
| 0a4f290f0f | |||
| 6ea4d3794a | |||
| 85eec5d344 | |||
| 0961bc546a | |||
| f2df10c7fe | |||
| 03f1fabbdb | |||
| 2ada2b77f6 | |||
| d5380b3070 | |||
| a3907a64e5 | |||
| dd16e012ba | |||
| d4cd358c76 | |||
| da814e01c7 | |||
| 5d4c2846d9 | |||
| a3daee6ad8 | |||
| 5614e5bc18 | |||
| 6dde13f597 | |||
| c057da23ec | |||
| a7f07328ab | |||
| 45f747fae1 | |||
| 258bd3f73c | |||
| 97b95d9d01 | |||
| 69827a2f21 | |||
| ef699b6a9e | |||
| e440eb8365 | |||
| 02cbee560d | |||
| 14da1aca2f | |||
| ce97342fee | |||
| 2e51aa250e | |||
| ec2957afc8 | |||
| d3df45d8fe | |||
| 5375f62dc1 | |||
| c7bc40d3ba | |||
| 1071778aa9 | |||
| 40a9ae9d14 | |||
| e34a7baeb3 | |||
| 62eb9b391d | |||
| ea89bf2c91 | |||
| 99ae6e70bb | |||
| c2cc55eacc | |||
| 9cff2ace97 | |||
| 3c2590d339 | |||
| 4b55333b0e | |||
| c27acad616 | |||
| 70be9afdc6 | |||
| d4450a8702 | |||
| 210e47d65e | |||
| 06411831e8 | |||
| 8c7daa807a | |||
| 46d171a8fd | |||
| 259bd5250d | |||
| cef046218c | |||
| e4bd1344e2 | |||
| 9decbf2158 | |||
| 268b7a8931 | |||
| 1240fb6b9a | |||
| 7dae3afb4b | |||
| d9de36b5ee | |||
| 4e696db922 | |||
| 0adad10595 | |||
| 540d0d9345 | |||
| 572bbeeba3 | |||
| 77cd44dd20 | |||
| 65e7b4a279 | |||
| 7d95a24573 | |||
| 1dbe0ee6db | |||
| 0211016957 | |||
| 08fe123930 | |||
| 219448796f | |||
| 0956c144c9 | |||
| 964950a8b9 | |||
| 5afd94f90f | |||
| ec5f7644e4 | |||
| 0c8ef4c671 | |||
| a851b7309a |
@@ -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
|
||||
@@ -0,0 +1,34 @@
|
||||
name: Backport Bot
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [labeled]
|
||||
pull_request:
|
||||
types: [labeled]
|
||||
push:
|
||||
branches:
|
||||
- '*.x'
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
build:
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
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
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_EVENT: ${{ toJSON(github.event) }}
|
||||
run: java -jar backport-bot-0.0.1-SNAPSHOT.jar --github.accessToken="$GITHUB_TOKEN" --github.event_name "$GITHUB_EVENT_NAME" --github.event "$GITHUB_EVENT"
|
||||
@@ -0,0 +1,64 @@
|
||||
name: Build and deploy snapshot
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 5.3.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: 8
|
||||
- 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'
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GRADLE_ENTERPRISE_CACHE_USER }}
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }}
|
||||
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-api-*.zip::zip.name=spring-framework,zip.deployed=false
|
||||
/**/framework-api-*-docs.zip::zip.type=docs
|
||||
/**/framework-api-*-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 8', github.ref_name) }}
|
||||
@@ -0,0 +1,82 @@
|
||||
name: CI
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 5.3.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: 8
|
||||
toolchain: false
|
||||
- version: 17
|
||||
toolchain: true
|
||||
- version: 21
|
||||
toolchain: true
|
||||
exclude:
|
||||
- os:
|
||||
name: Linux
|
||||
java:
|
||||
version: 8
|
||||
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 && '8' || '' }}
|
||||
- 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'
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GRADLE_ENTERPRISE_CACHE_USER }}
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }}
|
||||
run: ./gradlew check
|
||||
- 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) }}
|
||||
@@ -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
|
||||
|
||||
+3
-1
@@ -46,4 +46,6 @@ atlassian-ide-plugin.xml
|
||||
.gradletasknamecache
|
||||
|
||||
# VS Code
|
||||
.vscode/
|
||||
.vscode/
|
||||
|
||||
cached-antora-playbook.yml
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# Enable auto-env through the sdkman_auto_env config
|
||||
# Add key=value pairs of SDKs to use below
|
||||
java=8.0.362-librca
|
||||
java=8.0.382-librca
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# <img src="src/docs/spring-framework.png" width="80" height="80"> Spring Framework [](https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-5.3.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%3A5.3.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".
|
||||
|
||||
|
||||
+19
-17
@@ -28,18 +28,18 @@ configure(allprojects) { project ->
|
||||
dependencyManagement {
|
||||
imports {
|
||||
mavenBom "com.fasterxml.jackson:jackson-bom:2.12.7"
|
||||
mavenBom "io.netty:netty-bom:4.1.91.Final"
|
||||
mavenBom "io.projectreactor:reactor-bom:2020.0.31"
|
||||
mavenBom "io.netty:netty-bom:4.1.108.Final"
|
||||
mavenBom "io.projectreactor:reactor-bom:2020.0.43"
|
||||
mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR13"
|
||||
mavenBom "io.rsocket:rsocket-bom:1.1.3"
|
||||
mavenBom "org.eclipse.jetty:jetty-bom:9.4.51.v20230217"
|
||||
mavenBom "org.eclipse.jetty:jetty-bom:9.4.54.v20240208"
|
||||
mavenBom "org.jetbrains.kotlin:kotlin-bom:1.5.32"
|
||||
mavenBom "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.5.2"
|
||||
mavenBom "org.jetbrains.kotlinx:kotlinx-serialization-bom:1.2.2"
|
||||
mavenBom "org.junit:junit-bom:5.8.2"
|
||||
}
|
||||
dependencies {
|
||||
dependencySet(group: 'org.apache.logging.log4j', version: '2.20.0') {
|
||||
dependencySet(group: 'org.apache.logging.log4j', version: '2.21.1') {
|
||||
entry 'log4j-api'
|
||||
entry 'log4j-core'
|
||||
entry 'log4j-jul'
|
||||
@@ -67,9 +67,9 @@ configure(allprojects) { project ->
|
||||
dependency "io.reactivex:rxjava:1.3.8"
|
||||
dependency "io.reactivex:rxjava-reactive-streams:1.2.1"
|
||||
dependency "io.reactivex.rxjava2:rxjava:2.2.21"
|
||||
dependency "io.reactivex.rxjava3:rxjava:3.1.5"
|
||||
dependency "io.smallrye.reactive:mutiny:1.8.0"
|
||||
dependency "io.projectreactor.tools:blockhound:1.0.6.RELEASE"
|
||||
dependency "io.reactivex.rxjava3:rxjava:3.1.8"
|
||||
dependency "io.smallrye.reactive:mutiny:1.9.0"
|
||||
dependency "io.projectreactor.tools:blockhound:1.0.8.RELEASE"
|
||||
|
||||
dependency "com.caucho:hessian:4.0.63"
|
||||
dependency "com.fasterxml:aalto-xml:1.3.1"
|
||||
@@ -96,7 +96,7 @@ configure(allprojects) { project ->
|
||||
|
||||
dependency "com.h2database:h2:2.1.214"
|
||||
dependency "com.github.ben-manes.caffeine:caffeine:2.9.3"
|
||||
dependency "com.github.librepdf:openpdf:1.3.30"
|
||||
dependency "com.github.librepdf:openpdf:1.3.33"
|
||||
dependency "com.rometools:rome:1.18.0"
|
||||
dependency "commons-io:commons-io:2.5"
|
||||
dependency "io.vavr:vavr:0.10.4"
|
||||
@@ -128,18 +128,18 @@ configure(allprojects) { project ->
|
||||
dependency "org.webjars:webjars-locator-core:0.48"
|
||||
dependency "org.webjars:underscorejs:1.8.3"
|
||||
|
||||
dependencySet(group: 'org.apache.tomcat', version: '9.0.73') {
|
||||
dependencySet(group: 'org.apache.tomcat', version: '9.0.82') {
|
||||
entry 'tomcat-util'
|
||||
entry('tomcat-websocket') {
|
||||
exclude group: "org.apache.tomcat", name: "tomcat-servlet-api"
|
||||
exclude group: "org.apache.tomcat", name: "tomcat-websocket-api"
|
||||
}
|
||||
}
|
||||
dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.73') {
|
||||
dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.82') {
|
||||
entry 'tomcat-embed-core'
|
||||
entry 'tomcat-embed-websocket'
|
||||
}
|
||||
dependencySet(group: 'io.undertow', version: '2.2.23.Final') {
|
||||
dependencySet(group: 'io.undertow', version: '2.2.29.Final') {
|
||||
entry 'undertow-core'
|
||||
entry('undertow-servlet') {
|
||||
exclude group: "org.jboss.spec.javax.servlet", name: "jboss-servlet-api_4.0_spec"
|
||||
@@ -238,7 +238,7 @@ configure(allprojects) { project ->
|
||||
dependency "com.ibm.websphere:uow:6.0.2.17"
|
||||
dependency "com.jamonapi:jamon:2.82"
|
||||
dependency "joda-time:joda-time:2.10.13"
|
||||
dependency "org.eclipse.persistence:org.eclipse.persistence.jpa:2.7.10"
|
||||
dependency "org.eclipse.persistence:org.eclipse.persistence.jpa:2.7.12"
|
||||
dependency "org.javamoney:moneta:1.3"
|
||||
|
||||
dependency "com.sun.activation:javax.activation:1.2.0"
|
||||
@@ -340,7 +340,7 @@ configure([rootProject] + javaProjects) { project ->
|
||||
}
|
||||
|
||||
checkstyle {
|
||||
toolVersion = "10.9.3"
|
||||
toolVersion = "10.12.7"
|
||||
configDirectory.set(rootProject.file("src/checkstyle"))
|
||||
}
|
||||
|
||||
@@ -362,7 +362,7 @@ configure([rootProject] + javaProjects) { project ->
|
||||
// JSR-305 only used for non-required meta-annotations
|
||||
compileOnly("com.google.code.findbugs:jsr305")
|
||||
testCompileOnly("com.google.code.findbugs:jsr305")
|
||||
checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:0.0.31")
|
||||
checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:0.0.41")
|
||||
}
|
||||
|
||||
ext.javadocLinks = [
|
||||
@@ -375,8 +375,9 @@ configure([rootProject] + javaProjects) { project ->
|
||||
"https://tiles.apache.org/tiles-request/apidocs/",
|
||||
"https://tiles.apache.org/framework/apidocs/",
|
||||
"https://www.eclipse.org/aspectj/doc/released/aspectj5rt-api/",
|
||||
"https://www.ehcache.org/apidocs/2.10.4/",
|
||||
"https://www.quartz-scheduler.org/api/2.3.0/",
|
||||
// Temporarily commenting out Ehcache and Quartz since javadoc on JDK 8 cannot access them.
|
||||
// "https://www.ehcache.org/apidocs/2.10.4/",
|
||||
// "https://www.quartz-scheduler.org/api/2.3.0/",
|
||||
"https://www.javadoc.io/doc/com.fasterxml.jackson.core/jackson-core/2.12.7/",
|
||||
"https://www.javadoc.io/doc/com.fasterxml.jackson.core/jackson-databind/2.12.7/",
|
||||
"https://www.javadoc.io/doc/com.fasterxml.jackson.dataformat/jackson-dataformat-xml/2.12.7/",
|
||||
@@ -388,7 +389,8 @@ configure([rootProject] + javaProjects) { project ->
|
||||
// "https://junit.org/junit5/docs/5.8.2/api/",
|
||||
"https://www.reactive-streams.org/reactive-streams-1.0.3-javadoc/",
|
||||
"https://javadoc.io/static/io.rsocket/rsocket-core/1.1.1/",
|
||||
"https://r2dbc.io/spec/0.8.5.RELEASE/api/",
|
||||
// Temporarily commenting out R2DBC since javadoc on JDK 8 cannot access it.
|
||||
// "https://r2dbc.io/spec/0.8.5.RELEASE/api/",
|
||||
// The external Javadoc link for JSR 305 must come last to ensure that types from
|
||||
// JSR 250 (such as @PostConstruct) are still supported. This is due to the fact
|
||||
// that JSR 250 and JSR 305 both define types in javax.annotation, which results
|
||||
|
||||
@@ -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-5.3.x[Spring Framework 5.3.x].
|
||||
|
||||
@@ -17,4 +17,4 @@ changelog:
|
||||
- "type: dependency-upgrade"
|
||||
contributors:
|
||||
exclude:
|
||||
names: ["bclozel", "jhoeller", "poutsma", "rstoyanchev", "sbrannen", "sdeleuze", "snicoll"]
|
||||
names: ["bclozel", "jhoeller", "poutsma", "rstoyanchev", "sbrannen", "sdeleuze", "snicoll", "simonbasle"]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM ubuntu:focal-20220922
|
||||
FROM ubuntu:jammy-20240125
|
||||
|
||||
ADD setup.sh /setup.sh
|
||||
ADD get-jdk-url.sh /get-jdk-url.sh
|
||||
|
||||
@@ -3,10 +3,10 @@ set -e
|
||||
|
||||
case "$1" in
|
||||
java8)
|
||||
echo "https://github.com/bell-sw/Liberica/releases/download/8u345+1/bellsoft-jdk8u345+1-linux-amd64.tar.gz"
|
||||
echo "https://github.com/bell-sw/Liberica/releases/download/8u402%2B7/bellsoft-jdk8u402+7-linux-amd64.tar.gz"
|
||||
;;
|
||||
java17)
|
||||
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.4+8/bellsoft-jdk17.0.4+8-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"
|
||||
|
||||
@@ -8,4 +8,3 @@ milestone: "5.3.x"
|
||||
build-name: "spring-framework"
|
||||
pipeline-name: "spring-framework"
|
||||
concourse-url: "https://ci.spring.io"
|
||||
task-timeout: 1h00m
|
||||
|
||||
+13
-166
@@ -23,14 +23,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 +37,7 @@ resource_types:
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: concourse/registry-image-resource
|
||||
tag: 1.7.1
|
||||
tag: 1.8.0
|
||||
- name: artifactory-resource
|
||||
type: registry-image
|
||||
source:
|
||||
@@ -64,31 +56,12 @@ resource_types:
|
||||
<<: *docker-resource-source
|
||||
repository: dpb587/github-status-resource
|
||||
tag: master
|
||||
- name: pull-request
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: teliaoss/github-pr-resource
|
||||
tag: v0.23.0
|
||||
- name: slack-notification
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: cfcommunity/slack-notification-resource
|
||||
tag: latest
|
||||
resources:
|
||||
- name: git-repo
|
||||
type: git
|
||||
icon: github
|
||||
source:
|
||||
<<: *git-repo-resource-source
|
||||
- name: every-morning
|
||||
type: time
|
||||
icon: alarm
|
||||
source:
|
||||
start: 8:00 AM
|
||||
stop: 9:00 AM
|
||||
location: Europe/Vienna
|
||||
- name: ci-images-git-repo
|
||||
type: git
|
||||
icon: github
|
||||
@@ -111,35 +84,6 @@ resources:
|
||||
username: ((artifactory-username))
|
||||
password: ((artifactory-password))
|
||||
build_name: ((build-name))
|
||||
- name: git-pull-request
|
||||
type: pull-request
|
||||
icon: source-pull
|
||||
source:
|
||||
access_token: ((github-ci-pull-request-token))
|
||||
repository: ((github-repo-name))
|
||||
base_branch: ((branch))
|
||||
ignore_paths: ["ci/*"]
|
||||
- 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-jdk17-build
|
||||
type: github-status-resource
|
||||
icon: eye-check-outline
|
||||
source:
|
||||
repository: ((github-repo-name))
|
||||
access_token: ((github-ci-status-token))
|
||||
branch: ((branch))
|
||||
context: jdk17-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
|
||||
@@ -174,37 +118,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}"
|
||||
@@ -229,86 +159,9 @@ jobs:
|
||||
- "/**/spring-*-schema.zip"
|
||||
properties:
|
||||
"zip.type": "schema"
|
||||
get_params:
|
||||
threads: 8
|
||||
- name: jdk17-build
|
||||
serial: true
|
||||
public: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
- get: every-morning
|
||||
trigger: true
|
||||
- put: repo-status-jdk17-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: 17
|
||||
<<: *build-project-task-params
|
||||
on_failure:
|
||||
do:
|
||||
- put: repo-status-jdk17-build
|
||||
params: { state: "failure", commit: "git-repo" }
|
||||
- put: slack-alert
|
||||
params:
|
||||
<<: *slack-fail-params
|
||||
- put: repo-status-jdk17-build
|
||||
params: { state: "success", commit: "git-repo" }
|
||||
- name: build-pull-requests
|
||||
serial: true
|
||||
public: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
resource: git-pull-request
|
||||
trigger: true
|
||||
version: every
|
||||
- do:
|
||||
- put: git-pull-request
|
||||
params:
|
||||
path: git-repo
|
||||
status: pending
|
||||
- task: build-pr
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/build-pr.yml
|
||||
privileged: true
|
||||
timeout: ((task-timeout))
|
||||
params:
|
||||
BRANCH: ((branch))
|
||||
on_success:
|
||||
put: git-pull-request
|
||||
params:
|
||||
path: git-repo
|
||||
status: success
|
||||
on_failure:
|
||||
put: git-pull-request
|
||||
params:
|
||||
path: git-repo
|
||||
status: failure
|
||||
- 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:
|
||||
@@ -349,7 +202,6 @@ jobs:
|
||||
- put: artifactory-repo
|
||||
params:
|
||||
<<: *artifactory-params
|
||||
repo: libs-staging-local
|
||||
- put: git-repo
|
||||
params:
|
||||
repository: stage-git-repo
|
||||
@@ -393,7 +245,6 @@ jobs:
|
||||
- put: artifactory-repo
|
||||
params:
|
||||
<<: *artifactory-params
|
||||
repo: libs-staging-local
|
||||
- put: git-repo
|
||||
params:
|
||||
repository: stage-git-repo
|
||||
@@ -436,11 +287,7 @@ jobs:
|
||||
<<: *changelog-task-params
|
||||
|
||||
groups:
|
||||
- name: "builds"
|
||||
jobs: ["build", "jdk17-build"]
|
||||
- name: "releases"
|
||||
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release"]
|
||||
- name: "ci-images"
|
||||
jobs: ["build-ci-images"]
|
||||
- name: "pull-requests"
|
||||
jobs: [ "build-pull-requests" ]
|
||||
|
||||
@@ -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 \
|
||||
-PmainToolchain=${MAIN_TOOLCHAIN} -PtestToolchain=${TEST_TOOLCHAIN} --no-daemon --max-workers=4 check
|
||||
popd > /dev/null
|
||||
@@ -1,6 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
source $(dirname $0)/common.sh
|
||||
CONFIG_DIR=git-repo/ci/config
|
||||
|
||||
version=$( cat artifactory-repo/build-info.json | jq -r '.buildInfo.modules[0].id' | sed 's/.*:.*:\(.*\)/\1/' )
|
||||
|
||||
@@ -26,4 +26,5 @@ run:
|
||||
cat > /root/.docker/config.json <<EOF
|
||||
{ "auths": { "https://index.docker.io/v1/": { "auth": "$DOCKER_HUB_AUTH" }}}
|
||||
EOF
|
||||
build
|
||||
build
|
||||
|
||||
|
||||
@@ -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,9 @@ image_resource:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: springio/github-changelog-generator
|
||||
tag: '0.0.7'
|
||||
tag: '0.0.8'
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
inputs:
|
||||
- name: git-repo
|
||||
- name: artifactory-repo
|
||||
|
||||
@@ -4,7 +4,9 @@ image_resource:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: springio/concourse-release-scripts
|
||||
tag: '0.3.4'
|
||||
tag: '0.4.0'
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
inputs:
|
||||
- name: git-repo
|
||||
- name: artifactory-repo
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
version=5.3.27
|
||||
version=5.3.34
|
||||
org.gradle.jvmargs=-Xmx2048m
|
||||
org.gradle.caching=true
|
||||
org.gradle.parallel=true
|
||||
|
||||
@@ -5,7 +5,7 @@ tasks.findByName("dokkaHtmlPartial")?.configure {
|
||||
sourceRoots.setFrom(file("src/main/kotlin"))
|
||||
classpath.from(sourceSets["main"].runtimeClasspath)
|
||||
externalDocumentationLink {
|
||||
url.set(new URL("https://docs.spring.io/spring-framework/docs/current/javadoc-api/"))
|
||||
url.set(new URL("https://docs.spring.io/spring-framework/docs/5.3.x/javadoc-api/"))
|
||||
}
|
||||
externalDocumentationLink {
|
||||
url.set(new URL("https://projectreactor.io/docs/core/release/api/"))
|
||||
|
||||
+3
-4
@@ -3,7 +3,7 @@ configurations {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
asciidoctorExt("io.spring.asciidoctor:spring-asciidoctor-extensions-block-switch:0.5.0")
|
||||
asciidoctorExt("io.spring.asciidoctor:spring-asciidoctor-extensions-block-switch:0.6.1")
|
||||
}
|
||||
|
||||
repositories {
|
||||
@@ -73,9 +73,8 @@ pluginManager.withPlugin("kotlin") {
|
||||
}
|
||||
|
||||
task downloadResources(type: Download) {
|
||||
def version = "0.2.5"
|
||||
src "https://repo.spring.io/release/io/spring/docresources/" +
|
||||
"spring-doc-resources/$version/spring-doc-resources-${version}.zip"
|
||||
src "https://repo.spring.io/artifactory/snapshot/io/spring/docresources/" +
|
||||
"spring-doc-resources/0.2.6-SNAPSHOT/spring-doc-resources-0.2.6-20210308.231804-2.zip"
|
||||
dest project.file("$buildDir/docs/spring-doc-resources.zip")
|
||||
onlyIfModified true
|
||||
useETag "all"
|
||||
|
||||
+6
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -60,8 +60,8 @@ class ScheduledAndTransactionalAnnotationIntegrationTests {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(Config.class, JdkProxyTxConfig.class, RepoConfigA.class);
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(ctx::refresh)
|
||||
.withCauseInstanceOf(IllegalStateException.class);
|
||||
.isThrownBy(ctx::refresh)
|
||||
.withCauseInstanceOf(IllegalStateException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -70,7 +70,7 @@ class ScheduledAndTransactionalAnnotationIntegrationTests {
|
||||
ctx.register(Config.class, SubclassProxyTxConfig.class, RepoConfigA.class);
|
||||
ctx.refresh();
|
||||
|
||||
Thread.sleep(100); // allow @Scheduled method to be called several times
|
||||
Thread.sleep(200); // allow @Scheduled method to be called several times
|
||||
|
||||
MyRepository repository = ctx.getBean(MyRepository.class);
|
||||
CallCountingTransactionManager txManager = ctx.getBean(CallCountingTransactionManager.class);
|
||||
@@ -85,7 +85,7 @@ class ScheduledAndTransactionalAnnotationIntegrationTests {
|
||||
ctx.register(Config.class, JdkProxyTxConfig.class, RepoConfigB.class);
|
||||
ctx.refresh();
|
||||
|
||||
Thread.sleep(100); // allow @Scheduled method to be called several times
|
||||
Thread.sleep(200); // allow @Scheduled method to be called several times
|
||||
|
||||
MyRepositoryWithScheduledMethod repository = ctx.getBean(MyRepositoryWithScheduledMethod.class);
|
||||
CallCountingTransactionManager txManager = ctx.getBean(CallCountingTransactionManager.class);
|
||||
@@ -100,7 +100,7 @@ class ScheduledAndTransactionalAnnotationIntegrationTests {
|
||||
ctx.register(AspectConfig.class, MyRepositoryWithScheduledMethodImpl.class);
|
||||
ctx.refresh();
|
||||
|
||||
Thread.sleep(100); // allow @Scheduled method to be called several times
|
||||
Thread.sleep(200); // allow @Scheduled method to be called several times
|
||||
|
||||
MyRepositoryWithScheduledMethod repository = ctx.getBean(MyRepositoryWithScheduledMethod.class);
|
||||
assertThat(AopUtils.isCglibProxy(repository)).isTrue();
|
||||
|
||||
+26
-29
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -38,6 +38,14 @@ import org.springframework.util.StringUtils;
|
||||
* for an advice method from the pointcut expression, returning, and throwing clauses.
|
||||
* If an unambiguous interpretation is not available, it returns {@code null}.
|
||||
*
|
||||
* <h3>Algorithm Summary</h3>
|
||||
* <p>If an unambiguous binding can be deduced, then it is.
|
||||
* If the advice requirements cannot possibly be satisfied, then {@code null}
|
||||
* is returned. By setting the {@link #setRaiseExceptions(boolean) raiseExceptions}
|
||||
* property to {@code true}, descriptive exceptions will be thrown instead of
|
||||
* returning {@code null} in the case that the parameter names cannot be discovered.
|
||||
*
|
||||
* <h3>Algorithm Details</h3>
|
||||
* <p>This class interprets arguments in the following way:
|
||||
* <ol>
|
||||
* <li>If the first parameter of the method is of type {@link JoinPoint}
|
||||
@@ -65,15 +73,15 @@ import org.springframework.util.StringUtils;
|
||||
* zero we proceed to the next stage. If {@code a} > 1 then an
|
||||
* {@code AmbiguousBindingException} is raised. If {@code a} == 1,
|
||||
* and there are no unbound arguments of type {@code Annotation+},
|
||||
* then an {@code IllegalArgumentException} is raised. if there is
|
||||
* then an {@code IllegalArgumentException} is raised. If there is
|
||||
* exactly one such argument, then the corresponding parameter name is
|
||||
* assigned the value from the pointcut expression.</li>
|
||||
* <li>If a returningName has been set, and there are no unbound arguments
|
||||
* <li>If a {@code returningName} has been set, and there are no unbound arguments
|
||||
* then an {@code IllegalArgumentException} is raised. If there is
|
||||
* more than one unbound argument then an
|
||||
* {@code AmbiguousBindingException} is raised. If there is exactly
|
||||
* one unbound argument then the corresponding parameter name is assigned
|
||||
* the value <returningName>.</li>
|
||||
* the value of the {@code returningName}.</li>
|
||||
* <li>If there remain unbound arguments, then the pointcut expression is
|
||||
* examined once more for {@code this}, {@code target}, and
|
||||
* {@code args} pointcut expressions used in the binding form (binding
|
||||
@@ -99,20 +107,12 @@ import org.springframework.util.StringUtils;
|
||||
* <p>The behavior on raising an {@code IllegalArgumentException} or
|
||||
* {@code AmbiguousBindingException} is configurable to allow this discoverer
|
||||
* to be used as part of a chain-of-responsibility. By default the condition will
|
||||
* be logged and the {@code getParameterNames(..)} method will simply return
|
||||
* be logged and the {@link #getParameterNames(Method)} method will simply return
|
||||
* {@code null}. If the {@link #setRaiseExceptions(boolean) raiseExceptions}
|
||||
* property is set to {@code true}, the conditions will be thrown as
|
||||
* {@code IllegalArgumentException} and {@code AmbiguousBindingException},
|
||||
* respectively.
|
||||
*
|
||||
* <p>Was that perfectly clear? ;)
|
||||
*
|
||||
* <p>Short version: If an unambiguous binding can be deduced, then it is.
|
||||
* If the advice requirements cannot possibly be satisfied, then {@code null}
|
||||
* is returned. By setting the {@link #setRaiseExceptions(boolean) raiseExceptions}
|
||||
* property to {@code true}, descriptive exceptions will be thrown instead of
|
||||
* returning {@code null} in the case that the parameter names cannot be discovered.
|
||||
*
|
||||
* @author Adrian Colyer
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
@@ -158,7 +158,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
|
||||
|
||||
/** The pointcut expression associated with the advice, as a simple String. */
|
||||
@Nullable
|
||||
private String pointcutExpression;
|
||||
private final String pointcutExpression;
|
||||
|
||||
private boolean raiseExceptions;
|
||||
|
||||
@@ -197,7 +197,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
|
||||
|
||||
/**
|
||||
* If {@code afterReturning} advice binds the return value, the
|
||||
* returning variable name must be specified.
|
||||
* {@code returning} variable name must be specified.
|
||||
* @param returningName the name of the returning variable
|
||||
*/
|
||||
public void setReturningName(@Nullable String returningName) {
|
||||
@@ -206,18 +206,17 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
|
||||
|
||||
/**
|
||||
* If {@code afterThrowing} advice binds the thrown value, the
|
||||
* throwing variable name must be specified.
|
||||
* {@code throwing} variable name must be specified.
|
||||
* @param throwingName the name of the throwing variable
|
||||
*/
|
||||
public void setThrowingName(@Nullable String throwingName) {
|
||||
this.throwingName = throwingName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deduce the parameter names for an advice method.
|
||||
* <p>See the {@link AspectJAdviceParameterNameDiscoverer class level javadoc}
|
||||
* for this class for details of the algorithm used.
|
||||
* <p>See the {@link AspectJAdviceParameterNameDiscoverer class-level javadoc}
|
||||
* for this class for details on the algorithm used.
|
||||
* @param method the target {@link Method}
|
||||
* @return the parameter names
|
||||
*/
|
||||
@@ -316,13 +315,13 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
|
||||
}
|
||||
|
||||
|
||||
private void bindParameterName(int index, String name) {
|
||||
private void bindParameterName(int index, @Nullable String name) {
|
||||
this.parameterNameBindings[index] = name;
|
||||
this.numberOfRemainingUnboundArguments--;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the first parameter is of type JoinPoint or ProceedingJoinPoint,bind "thisJoinPoint" as
|
||||
* If the first parameter is of type JoinPoint or ProceedingJoinPoint, bind "thisJoinPoint" as
|
||||
* parameter name and return true, else return false.
|
||||
*/
|
||||
private boolean maybeBindThisJoinPoint() {
|
||||
@@ -367,8 +366,8 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
|
||||
}
|
||||
|
||||
if (throwableIndex == -1) {
|
||||
throw new IllegalStateException("Binding of throwing parameter '" + this.throwingName
|
||||
+ "' could not be completed as no available arguments are a subtype of Throwable");
|
||||
throw new IllegalStateException("Binding of throwing parameter '" + this.throwingName +
|
||||
"' could not be completed as no available arguments are a subtype of Throwable");
|
||||
}
|
||||
else {
|
||||
bindParameterName(throwableIndex, this.throwingName);
|
||||
@@ -400,7 +399,6 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse the string pointcut expression looking for:
|
||||
* @this, @target, @args, @within, @withincode, @annotation.
|
||||
@@ -465,7 +463,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* If the token starts meets Java identifier conventions, it's in.
|
||||
*/
|
||||
@Nullable
|
||||
@@ -533,7 +531,6 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (varNames.size() > 1) {
|
||||
throw new AmbiguousBindingException("Found " + varNames.size() +
|
||||
" candidate this(), target() or args() variables but only one unbound argument slot");
|
||||
@@ -609,7 +606,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
|
||||
// else varNames.size must be 0 and we have nothing to bind.
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* We've found the start of a binding pointcut at the given index into the
|
||||
* token array. Now we need to extract the pointcut body and return it.
|
||||
*/
|
||||
@@ -709,7 +706,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Return {@code true} if the given argument type is a subclass
|
||||
* of the given supertype.
|
||||
*/
|
||||
@@ -737,7 +734,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Find the argument index with the given type, and bind the given
|
||||
* {@code varName} in that position.
|
||||
*/
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -248,8 +248,8 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
|
||||
/**
|
||||
* If a pointcut expression has been specified in XML, the user cannot
|
||||
* write {@code and} as "&&" (though && will work).
|
||||
* We also allow {@code and} between two pointcut sub-expressions.
|
||||
* write "and" as "&&" (though {@code &&} will work).
|
||||
* <p>We also allow "and" between two pointcut sub-expressions.
|
||||
* <p>This method converts back to {@code &&} for the AspectJ pointcut parser.
|
||||
*/
|
||||
private String replaceBooleanOperators(String pcExpr) {
|
||||
@@ -527,7 +527,7 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
return false;
|
||||
}
|
||||
AspectJExpressionPointcut otherPc = (AspectJExpressionPointcut) other;
|
||||
return ObjectUtils.nullSafeEquals(this.getExpression(), otherPc.getExpression()) &&
|
||||
return ObjectUtils.nullSafeEquals(getExpression(), otherPc.getExpression()) &&
|
||||
ObjectUtils.nullSafeEquals(this.pointcutDeclarationScope, otherPc.pointcutDeclarationScope) &&
|
||||
ObjectUtils.nullSafeEquals(this.pointcutParameterNames, otherPc.pointcutParameterNames) &&
|
||||
ObjectUtils.nullSafeEquals(this.pointcutParameterTypes, otherPc.pointcutParameterTypes);
|
||||
@@ -535,7 +535,7 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hashCode = ObjectUtils.nullSafeHashCode(this.getExpression());
|
||||
int hashCode = ObjectUtils.nullSafeHashCode(getExpression());
|
||||
hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutDeclarationScope);
|
||||
hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutParameterNames);
|
||||
hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutParameterTypes);
|
||||
|
||||
+4
-3
@@ -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.
|
||||
@@ -35,7 +35,8 @@ import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Internal implementation of AspectJPointcutAdvisor.
|
||||
* Note that there will be one instance of this advisor for each target method.
|
||||
*
|
||||
* <p>Note that there will be one instance of this advisor for each target method.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
@@ -293,7 +294,7 @@ final class InstantiationModelAwarePointcutAdvisorImpl
|
||||
@Override
|
||||
public boolean matches(Method method, Class<?> targetClass, Object... args) {
|
||||
// This can match only on declared pointcut.
|
||||
return (isAspectMaterialized() && this.declaredPointcut.matches(method, targetClass));
|
||||
return (isAspectMaterialized() && this.declaredPointcut.matches(method, targetClass, args));
|
||||
}
|
||||
|
||||
private boolean isAspectMaterialized() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -521,6 +521,7 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
copy.copyFrom(this);
|
||||
copy.targetSource = EmptyTargetSource.forClass(getTargetClass(), getTargetSource().isStatic());
|
||||
copy.advisorChainFactory = this.advisorChainFactory;
|
||||
copy.methodCache = this.methodCache;
|
||||
copy.interfaces = new ArrayList<>(this.interfaces);
|
||||
copy.advisors = new ArrayList<>(this.advisors);
|
||||
return copy;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -283,8 +283,8 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
|
||||
private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
|
||||
// Parameters used for optimization choices...
|
||||
boolean exposeProxy = this.advised.isExposeProxy();
|
||||
boolean isFrozen = this.advised.isFrozen();
|
||||
boolean exposeProxy = this.advised.isExposeProxy();
|
||||
boolean isStatic = this.advised.getTargetSource().isStatic();
|
||||
|
||||
// Choose an "aop" interceptor (used for AOP calls).
|
||||
@@ -424,10 +424,9 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
|
||||
|
||||
/**
|
||||
* Method interceptor used for static targets with no advice chain. The call
|
||||
* is passed directly back to the target. Used when the proxy needs to be
|
||||
* exposed and it can't be determined that the method won't return
|
||||
* {@code this}.
|
||||
* Method interceptor used for static targets with no advice chain. The call is
|
||||
* passed directly back to the target. Used when the proxy needs to be exposed
|
||||
* and it can't be determined that the method won't return {@code this}.
|
||||
*/
|
||||
private static class StaticUnadvisedInterceptor implements MethodInterceptor, Serializable {
|
||||
|
||||
@@ -899,9 +898,9 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
// Proxy is not yet available, but that shouldn't matter.
|
||||
List<?> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
|
||||
boolean haveAdvice = !chain.isEmpty();
|
||||
boolean isFrozen = this.advised.isFrozen();
|
||||
boolean exposeProxy = this.advised.isExposeProxy();
|
||||
boolean isStatic = this.advised.getTargetSource().isStatic();
|
||||
boolean isFrozen = this.advised.isFrozen();
|
||||
if (haveAdvice || !isFrozen) {
|
||||
// If exposing the proxy, then AOP_PROXY must be used.
|
||||
if (exposeProxy) {
|
||||
@@ -970,6 +969,9 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
if (this.advised.isExposeProxy() != otherAdvised.isExposeProxy()) {
|
||||
return false;
|
||||
}
|
||||
if (this.advised.isOpaque() != otherAdvised.isOpaque()) {
|
||||
return false;
|
||||
}
|
||||
if (this.advised.getTargetSource().isStatic() != otherAdvised.getTargetSource().isStatic()) {
|
||||
return false;
|
||||
}
|
||||
@@ -1016,10 +1018,6 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
Advice advice = advisor.getAdvice();
|
||||
hashCode = 13 * hashCode + advice.getClass().hashCode();
|
||||
}
|
||||
hashCode = 13 * hashCode + (this.advised.isFrozen() ? 1 : 0);
|
||||
hashCode = 13 * hashCode + (this.advised.isExposeProxy() ? 1 : 0);
|
||||
hashCode = 13 * hashCode + (this.advised.isOptimize() ? 1 : 0);
|
||||
hashCode = 13 * hashCode + (this.advised.isOpaque() ? 1 : 0);
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,12 +123,33 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
|
||||
}
|
||||
if (classLoader == null || classLoader.getParent() == null) {
|
||||
// JDK bootstrap loader or platform loader suggested ->
|
||||
// use higher-level loader which can see Spring infrastructure classes
|
||||
classLoader = getClass().getClassLoader();
|
||||
return Proxy.newProxyInstance(determineClassLoader(classLoader), this.proxiedInterfaces, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the JDK bootstrap or platform loader has been suggested ->
|
||||
* use higher-level loader which can see Spring infrastructure classes instead.
|
||||
*/
|
||||
private ClassLoader determineClassLoader(@Nullable ClassLoader classLoader) {
|
||||
if (classLoader == null) {
|
||||
// JDK bootstrap loader -> use spring-aop ClassLoader instead.
|
||||
return getClass().getClassLoader();
|
||||
}
|
||||
return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
|
||||
if (classLoader.getParent() == null) {
|
||||
// Potentially the JDK platform loader on JDK 9+
|
||||
ClassLoader aopClassLoader = getClass().getClassLoader();
|
||||
ClassLoader aopParent = aopClassLoader.getParent();
|
||||
while (aopParent != null) {
|
||||
if (classLoader == aopParent) {
|
||||
// Suggested ClassLoader is ancestor of spring-aop ClassLoader
|
||||
// -> use spring-aop ClassLoader itself instead.
|
||||
return aopClassLoader;
|
||||
}
|
||||
aopParent = aopParent.getParent();
|
||||
}
|
||||
}
|
||||
// Regular case: use suggested ClassLoader as-is.
|
||||
return classLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+9
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -34,6 +34,7 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.GenericBeanDefinition;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Convenient superclass for
|
||||
@@ -81,6 +82,11 @@ public abstract class AbstractBeanFactoryBasedTargetSourceCreator
|
||||
return this.beanFactory;
|
||||
}
|
||||
|
||||
private ConfigurableBeanFactory getConfigurableBeanFactory() {
|
||||
Assert.state(this.beanFactory != null, "BeanFactory not set");
|
||||
return this.beanFactory;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Implementation of the TargetSourceCreator interface
|
||||
@@ -104,7 +110,7 @@ public abstract class AbstractBeanFactoryBasedTargetSourceCreator
|
||||
// We need to override just this bean definition, as it may reference other beans
|
||||
// and we're happy to take the parent's definition for those.
|
||||
// Always use prototype scope if demanded.
|
||||
BeanDefinition bd = this.beanFactory.getMergedBeanDefinition(beanName);
|
||||
BeanDefinition bd = getConfigurableBeanFactory().getMergedBeanDefinition(beanName);
|
||||
GenericBeanDefinition bdCopy = new GenericBeanDefinition(bd);
|
||||
if (isPrototypeBased()) {
|
||||
bdCopy.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||
@@ -126,7 +132,7 @@ public abstract class AbstractBeanFactoryBasedTargetSourceCreator
|
||||
protected DefaultListableBeanFactory getInternalBeanFactoryForBean(String beanName) {
|
||||
synchronized (this.internalBeanFactories) {
|
||||
return this.internalBeanFactories.computeIfAbsent(beanName,
|
||||
name -> buildInternalBeanFactory(this.beanFactory));
|
||||
name -> buildInternalBeanFactory(getConfigurableBeanFactory()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
+14
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -24,6 +24,8 @@ import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.aop.TargetSource;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
@@ -57,15 +59,18 @@ public abstract class AbstractBeanFactoryBasedTargetSource implements TargetSour
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
/** Name of the target bean we will create on each invocation. */
|
||||
@Nullable
|
||||
private String targetBeanName;
|
||||
|
||||
/** Class of the target. */
|
||||
@Nullable
|
||||
private volatile Class<?> targetClass;
|
||||
|
||||
/**
|
||||
* BeanFactory that owns this TargetSource. We need to hold onto this
|
||||
* reference so that we can create new prototype instances as necessary.
|
||||
*/
|
||||
@Nullable
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
|
||||
@@ -86,6 +91,7 @@ public abstract class AbstractBeanFactoryBasedTargetSource implements TargetSour
|
||||
* Return the name of the target bean in the factory.
|
||||
*/
|
||||
public String getTargetBeanName() {
|
||||
Assert.state(this.targetBeanName != null, "Target bean name not set");
|
||||
return this.targetBeanName;
|
||||
}
|
||||
|
||||
@@ -115,11 +121,13 @@ public abstract class AbstractBeanFactoryBasedTargetSource implements TargetSour
|
||||
* Return the owning BeanFactory.
|
||||
*/
|
||||
public BeanFactory getBeanFactory() {
|
||||
Assert.state(this.beanFactory != null, "BeanFactory not set");
|
||||
return this.beanFactory;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Class<?> getTargetClass() {
|
||||
Class<?> targetClass = this.targetClass;
|
||||
if (targetClass != null) {
|
||||
@@ -128,7 +136,7 @@ public abstract class AbstractBeanFactoryBasedTargetSource implements TargetSour
|
||||
synchronized (this) {
|
||||
// Full check within synchronization, entering the BeanFactory interaction algorithm only once...
|
||||
targetClass = this.targetClass;
|
||||
if (targetClass == null && this.beanFactory != null) {
|
||||
if (targetClass == null && this.beanFactory != null && this.targetBeanName != null) {
|
||||
// Determine type of the target bean.
|
||||
targetClass = this.beanFactory.getType(this.targetBeanName);
|
||||
if (targetClass == null) {
|
||||
@@ -182,18 +190,16 @@ public abstract class AbstractBeanFactoryBasedTargetSource implements TargetSour
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hashCode = getClass().hashCode();
|
||||
hashCode = 13 * hashCode + ObjectUtils.nullSafeHashCode(this.beanFactory);
|
||||
hashCode = 13 * hashCode + ObjectUtils.nullSafeHashCode(this.targetBeanName);
|
||||
return hashCode;
|
||||
return getClass().hashCode() * 13 + ObjectUtils.nullSafeHashCode(this.targetBeanName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder(getClass().getSimpleName());
|
||||
sb.append(" for target bean '").append(this.targetBeanName).append('\'');
|
||||
if (this.targetClass != null) {
|
||||
sb.append(" of type [").append(this.targetClass.getName()).append(']');
|
||||
Class<?> targetClass = this.targetClass;
|
||||
if (targetClass != null) {
|
||||
sb.append(" of type [").append(targetClass.getName()).append(']');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -46,6 +46,7 @@ public abstract class AbstractLazyCreationTargetSource implements TargetSource {
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
/** The lazily initialized target object. */
|
||||
@Nullable
|
||||
private Object lazyTarget;
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -70,6 +70,7 @@ public final class EmptyTargetSource implements TargetSource, Serializable {
|
||||
// Instance implementation
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
@Nullable
|
||||
private final Class<?> targetClass;
|
||||
|
||||
private final boolean isStatic;
|
||||
|
||||
+2
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -95,8 +95,7 @@ public class HotSwappableTargetSource implements TargetSource, Serializable {
|
||||
|
||||
|
||||
/**
|
||||
* Two HotSwappableTargetSources are equal if the current target
|
||||
* objects are equal.
|
||||
* Two HotSwappableTargetSources are equal if the current target objects are equal.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -82,14 +82,8 @@ public class SingletonTargetSource implements TargetSource, Serializable {
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof SingletonTargetSource)) {
|
||||
return false;
|
||||
}
|
||||
SingletonTargetSource otherTargetSource = (SingletonTargetSource) other;
|
||||
return this.target.equals(otherTargetSource.target);
|
||||
return (this == other || (other instanceof SingletonTargetSource &&
|
||||
this.target.equals(((SingletonTargetSource) other).target)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+7
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -58,7 +58,12 @@ public class ThreadLocalTargetSource extends AbstractPrototypeBasedTargetSource
|
||||
* is meant to be per thread per instance of the ThreadLocalTargetSource class.
|
||||
*/
|
||||
private final ThreadLocal<Object> targetInThread =
|
||||
new NamedThreadLocal<>("Thread-local instance of bean '" + getTargetBeanName() + "'");
|
||||
new NamedThreadLocal<Object>("Thread-local instance of bean") {
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + " '" + getTargetBeanName() + "'";
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set of managed targets, enabling us to keep track of the targets we've created.
|
||||
|
||||
+74
-55
@@ -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.
|
||||
@@ -38,56 +38,67 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
* @author Adrian Colyer
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
public class ArgumentBindingTests {
|
||||
class ArgumentBindingTests {
|
||||
|
||||
@Test
|
||||
public void testBindingInPointcutUsedByAdvice() {
|
||||
TestBean tb = new TestBean();
|
||||
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(tb);
|
||||
proxyFactory.addAspect(NamedPointcutWithArgs.class);
|
||||
|
||||
ITestBean proxiedTestBean = proxyFactory.getProxy();
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
proxiedTestBean.setName("Supercalifragalisticexpialidocious"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAnnotationArgumentNameBinding() {
|
||||
TransactionalBean tb = new TransactionalBean();
|
||||
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(tb);
|
||||
void annotationArgumentNameBinding() {
|
||||
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new TransactionalBean());
|
||||
proxyFactory.addAspect(PointcutWithAnnotationArgument.class);
|
||||
|
||||
ITransactionalBean proxiedTestBean = proxyFactory.getProxy();
|
||||
assertThatIllegalStateException().isThrownBy(
|
||||
proxiedTestBean::doInTransaction);
|
||||
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(proxiedTestBean::doInTransaction)
|
||||
.withMessage("Invoked with @Transactional");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParameterNameDiscoverWithReferencePointcut() throws Exception {
|
||||
void bindingInPointcutUsedByAdvice() {
|
||||
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new TestBean());
|
||||
proxyFactory.addAspect(NamedPointcutWithArgs.class);
|
||||
ITestBean proxiedTestBean = proxyFactory.getProxy();
|
||||
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> proxiedTestBean.setName("enigma"))
|
||||
.withMessage("enigma");
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindingWithDynamicAdvice() {
|
||||
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new TestBean());
|
||||
proxyFactory.addAspect(DynamicPointcutWithArgs.class);
|
||||
ITestBean proxiedTestBean = proxyFactory.getProxy();
|
||||
|
||||
proxiedTestBean.applyName(1);
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> proxiedTestBean.applyName("enigma"))
|
||||
.withMessage("enigma");
|
||||
}
|
||||
|
||||
@Test
|
||||
void parameterNameDiscoverWithReferencePointcut() throws Exception {
|
||||
AspectJAdviceParameterNameDiscoverer discoverer =
|
||||
new AspectJAdviceParameterNameDiscoverer("somepc(formal) && set(* *)");
|
||||
discoverer.setRaiseExceptions(true);
|
||||
Method methodUsedForParameterTypeDiscovery =
|
||||
getClass().getMethod("methodWithOneParam", String.class);
|
||||
String[] pnames = discoverer.getParameterNames(methodUsedForParameterTypeDiscovery);
|
||||
assertThat(pnames.length).as("one parameter name").isEqualTo(1);
|
||||
assertThat(pnames[0]).isEqualTo("formal");
|
||||
Method method = getClass().getDeclaredMethod("methodWithOneParam", String.class);
|
||||
assertThat(discoverer.getParameterNames(method)).containsExactly("formal");
|
||||
}
|
||||
|
||||
|
||||
public void methodWithOneParam(String aParam) {
|
||||
@SuppressWarnings("unused")
|
||||
private void methodWithOneParam(String aParam) {
|
||||
}
|
||||
|
||||
|
||||
public interface ITransactionalBean {
|
||||
interface ITransactionalBean {
|
||||
|
||||
@Transactional
|
||||
void doInTransaction();
|
||||
}
|
||||
|
||||
|
||||
public static class TransactionalBean implements ITransactionalBean {
|
||||
static class TransactionalBean implements ITransactionalBean {
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@@ -95,38 +106,46 @@ public class ArgumentBindingTests {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents Spring's Transactional annotation without actually introducing the dependency
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Transactional {
|
||||
}
|
||||
/**
|
||||
* Mimics Spring's @Transactional annotation without actually introducing the dependency.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Transactional {
|
||||
}
|
||||
|
||||
|
||||
@Aspect
|
||||
class PointcutWithAnnotationArgument {
|
||||
@Aspect
|
||||
static class PointcutWithAnnotationArgument {
|
||||
|
||||
@Around(value = "execution(* org.springframework..*.*(..)) && @annotation(transaction)")
|
||||
public Object around(ProceedingJoinPoint pjp, Transactional transaction) throws Throwable {
|
||||
System.out.println("Invoked with transaction " + transaction);
|
||||
throw new IllegalStateException();
|
||||
@Around("execution(* org.springframework..*.*(..)) && @annotation(transactional)")
|
||||
public Object around(ProceedingJoinPoint pjp, Transactional transactional) throws Throwable {
|
||||
throw new IllegalStateException("Invoked with @Transactional");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Aspect
|
||||
static class NamedPointcutWithArgs {
|
||||
|
||||
@Pointcut("execution(* *(..)) && args(s,..)")
|
||||
public void pointcutWithArgs(String s) {}
|
||||
|
||||
@Around("pointcutWithArgs(aString)")
|
||||
public Object doAround(ProceedingJoinPoint pjp, String aString) throws Throwable {
|
||||
throw new IllegalArgumentException(aString);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Aspect("pertarget(execution(* *(..)))")
|
||||
static class DynamicPointcutWithArgs {
|
||||
|
||||
@Around("execution(* *(..)) && args(java.lang.String)")
|
||||
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
|
||||
throw new IllegalArgumentException(String.valueOf(pjp.getArgs()[0]));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Aspect
|
||||
class NamedPointcutWithArgs {
|
||||
|
||||
@Pointcut("execution(* *(..)) && args(s,..)")
|
||||
public void pointcutWithArgs(String s) {}
|
||||
|
||||
@Around("pointcutWithArgs(aString)")
|
||||
public Object doAround(ProceedingJoinPoint pjp, String aString) throws Throwable {
|
||||
System.out.println("got '" + aString + "' at '" + pjp + "'");
|
||||
throw new IllegalArgumentException(aString);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -388,7 +388,8 @@ public class ProxyFactoryTests {
|
||||
CharSequence target = "test";
|
||||
ProxyFactory pf = new ProxyFactory(target);
|
||||
ClassLoader cl = target.getClass().getClassLoader();
|
||||
assertThat(((CharSequence) pf.getProxy(cl)).toString()).isEqualTo(target);
|
||||
CharSequence proxy = (CharSequence) pf.getProxy(cl);
|
||||
assertThat(proxy.toString()).isEqualTo(target);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -397,7 +398,8 @@ public class ProxyFactoryTests {
|
||||
ProxyFactory pf = new ProxyFactory(target);
|
||||
pf.setProxyTargetClass(true);
|
||||
ClassLoader cl = target.getClass().getClassLoader();
|
||||
assertThat(((Date) pf.getProxy(cl)).getTime()).isEqualTo(target.getTime());
|
||||
Date proxy = (Date) pf.getProxy(cl);
|
||||
assertThat(proxy.getTime()).isEqualTo(target.getTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -414,7 +416,8 @@ public class ProxyFactoryTests {
|
||||
};
|
||||
ProxyFactory pf = new ProxyFactory(target);
|
||||
ClassLoader cl = Savepoint.class.getClassLoader();
|
||||
assertThat(((Savepoint) pf.getProxy(cl)).getSavepointName()).isEqualTo("sp");
|
||||
Savepoint proxy = (Savepoint) pf.getProxy(cl);
|
||||
assertThat(proxy.getSavepointName()).isEqualTo("sp");
|
||||
}
|
||||
|
||||
|
||||
|
||||
+4
-10
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -77,9 +77,7 @@ public class ThrowsAdviceInterceptorTests {
|
||||
given(mi.getMethod()).willReturn(Object.class.getMethod("hashCode"));
|
||||
given(mi.getThis()).willReturn(new Object());
|
||||
given(mi.proceed()).willThrow(ex);
|
||||
assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(() ->
|
||||
ti.invoke(mi))
|
||||
.isSameAs(ex);
|
||||
assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(() -> ti.invoke(mi)).isSameAs(ex);
|
||||
assertThat(th.getCalls()).isEqualTo(1);
|
||||
assertThat(th.getCalls("ioException")).isEqualTo(1);
|
||||
}
|
||||
@@ -92,9 +90,7 @@ public class ThrowsAdviceInterceptorTests {
|
||||
ConnectException ex = new ConnectException("");
|
||||
MethodInvocation mi = mock(MethodInvocation.class);
|
||||
given(mi.proceed()).willThrow(ex);
|
||||
assertThatExceptionOfType(ConnectException.class).isThrownBy(() ->
|
||||
ti.invoke(mi))
|
||||
.isSameAs(ex);
|
||||
assertThatExceptionOfType(ConnectException.class).isThrownBy(() -> ti.invoke(mi)).isSameAs(ex);
|
||||
assertThat(th.getCalls()).isEqualTo(1);
|
||||
assertThat(th.getCalls("remoteException")).isEqualTo(1);
|
||||
}
|
||||
@@ -117,9 +113,7 @@ public class ThrowsAdviceInterceptorTests {
|
||||
ConnectException ex = new ConnectException("");
|
||||
MethodInvocation mi = mock(MethodInvocation.class);
|
||||
given(mi.proceed()).willThrow(ex);
|
||||
assertThatExceptionOfType(Throwable.class).isThrownBy(() ->
|
||||
ti.invoke(mi))
|
||||
.isSameAs(t);
|
||||
assertThatExceptionOfType(Throwable.class).isThrownBy(() -> ti.invoke(mi)).isSameAs(t);
|
||||
assertThat(th.getCalls()).isEqualTo(1);
|
||||
assertThat(th.getCalls("remoteException")).isEqualTo(1);
|
||||
}
|
||||
|
||||
@@ -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()).getProxy(getClass().getClassLoader()).getClass();
|
||||
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()).getProxy(getClass().getClassLoader()).getClass();
|
||||
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) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -1004,18 +1004,20 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA
|
||||
*/
|
||||
protected abstract static class PropertyHandler {
|
||||
|
||||
@Nullable
|
||||
private final Class<?> propertyType;
|
||||
|
||||
private final boolean readable;
|
||||
|
||||
private final boolean writable;
|
||||
|
||||
public PropertyHandler(Class<?> propertyType, boolean readable, boolean writable) {
|
||||
public PropertyHandler(@Nullable Class<?> propertyType, boolean readable, boolean writable) {
|
||||
this.propertyType = propertyType;
|
||||
this.readable = readable;
|
||||
this.writable = writable;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Class<?> getPropertyType() {
|
||||
return this.propertyType;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Type;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.time.temporal.Temporal;
|
||||
@@ -30,6 +31,7 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
@@ -787,38 +789,28 @@ public abstract class BeanUtils {
|
||||
if (editable != null) {
|
||||
if (!editable.isInstance(target)) {
|
||||
throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
|
||||
"] not assignable to Editable class [" + editable.getName() + "]");
|
||||
"] not assignable to editable class [" + editable.getName() + "]");
|
||||
}
|
||||
actualEditable = editable;
|
||||
}
|
||||
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
|
||||
List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);
|
||||
Set<String> ignoredProps = (ignoreProperties != null ? new HashSet<>(Arrays.asList(ignoreProperties)) : null);
|
||||
CachedIntrospectionResults sourceResults = (actualEditable != source.getClass() ?
|
||||
CachedIntrospectionResults.forClass(source.getClass()) : null);
|
||||
|
||||
for (PropertyDescriptor targetPd : targetPds) {
|
||||
Method writeMethod = targetPd.getWriteMethod();
|
||||
if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
|
||||
PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
|
||||
if (writeMethod != null && (ignoredProps == null || !ignoredProps.contains(targetPd.getName()))) {
|
||||
PropertyDescriptor sourcePd = (sourceResults != null ?
|
||||
sourceResults.getPropertyDescriptor(targetPd.getName()) : targetPd);
|
||||
if (sourcePd != null) {
|
||||
Method readMethod = sourcePd.getReadMethod();
|
||||
if (readMethod != null) {
|
||||
ResolvableType sourceResolvableType = ResolvableType.forMethodReturnType(readMethod);
|
||||
ResolvableType targetResolvableType = ResolvableType.forMethodParameter(writeMethod, 0);
|
||||
|
||||
// Ignore generic types in assignable check if either ResolvableType has unresolvable generics.
|
||||
boolean isAssignable =
|
||||
(sourceResolvableType.hasUnresolvableGenerics() || targetResolvableType.hasUnresolvableGenerics() ?
|
||||
ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType()) :
|
||||
targetResolvableType.isAssignableFrom(sourceResolvableType));
|
||||
|
||||
if (isAssignable) {
|
||||
if (isAssignable(writeMethod, readMethod)) {
|
||||
try {
|
||||
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
|
||||
readMethod.setAccessible(true);
|
||||
}
|
||||
ReflectionUtils.makeAccessible(readMethod);
|
||||
Object value = readMethod.invoke(source);
|
||||
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
|
||||
writeMethod.setAccessible(true);
|
||||
}
|
||||
ReflectionUtils.makeAccessible(writeMethod);
|
||||
writeMethod.invoke(target, value);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
@@ -832,6 +824,24 @@ public abstract class BeanUtils {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isAssignable(Method writeMethod, Method readMethod) {
|
||||
Type paramType = writeMethod.getGenericParameterTypes()[0];
|
||||
if (paramType instanceof Class) {
|
||||
return ClassUtils.isAssignable((Class<?>) paramType, readMethod.getReturnType());
|
||||
}
|
||||
else if (paramType.equals(readMethod.getGenericReturnType())) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
ResolvableType sourceType = ResolvableType.forMethodReturnType(readMethod);
|
||||
ResolvableType targetType = ResolvableType.forMethodParameter(writeMethod, 0);
|
||||
// Ignore generic types in assignable check if either ResolvableType has unresolvable generics.
|
||||
return (sourceType.hasUnresolvableGenerics() || targetType.hasUnresolvableGenerics() ?
|
||||
ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType()) :
|
||||
targetType.isAssignableFrom(sourceType));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inner class to avoid a hard dependency on Kotlin at runtime.
|
||||
@@ -892,7 +902,6 @@ public abstract class BeanUtils {
|
||||
}
|
||||
return kotlinConstructor.callBy(argParameters);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+15
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -248,9 +248,22 @@ public final class CachedIntrospectionResults {
|
||||
return beanInfo;
|
||||
}
|
||||
}
|
||||
return (shouldIntrospectorIgnoreBeaninfoClasses ?
|
||||
|
||||
BeanInfo beanInfo = (shouldIntrospectorIgnoreBeaninfoClasses ?
|
||||
Introspector.getBeanInfo(beanClass, Introspector.IGNORE_ALL_BEANINFO) :
|
||||
Introspector.getBeanInfo(beanClass));
|
||||
|
||||
// Immediately remove class from Introspector cache to allow for proper garbage
|
||||
// collection on class loader shutdown; we cache it in CachedIntrospectionResults
|
||||
// in a GC-friendly manner. This is necessary (again) for the JDK ClassInfo cache.
|
||||
Class<?> classToFlush = beanClass;
|
||||
do {
|
||||
Introspector.flushFromCaches(classToFlush);
|
||||
classToFlush = classToFlush.getSuperclass();
|
||||
}
|
||||
while (classToFlush != null && classToFlush != Object.class);
|
||||
|
||||
return beanInfo;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -129,7 +129,6 @@ public class DirectFieldAccessor extends AbstractNestablePropertyAccessor {
|
||||
ReflectionUtils.makeAccessible(this.field);
|
||||
return this.field.get(getWrappedInstance());
|
||||
}
|
||||
|
||||
catch (IllegalAccessException ex) {
|
||||
throw new InvalidPropertyException(getWrappedClass(),
|
||||
this.field.getName(), "Field is not accessible", ex);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -136,7 +136,10 @@ public interface BeanFactory {
|
||||
* <p>Translates aliases back to the corresponding canonical bean name.
|
||||
* <p>Will ask the parent factory if the bean cannot be found in this factory instance.
|
||||
* @param name the name of the bean to retrieve
|
||||
* @return an instance of the bean
|
||||
* @return an instance of the bean.
|
||||
* Note that the return value will never be {@code null} but possibly a stub for
|
||||
* {@code null} returned from a factory method, to be checked via {@code equals(null)}.
|
||||
* Consider using {@link #getBeanProvider(Class)} for resolving optional dependencies.
|
||||
* @throws NoSuchBeanDefinitionException if there is no bean with the specified name
|
||||
* @throws BeansException if the bean could not be obtained
|
||||
*/
|
||||
@@ -152,7 +155,11 @@ public interface BeanFactory {
|
||||
* <p>Will ask the parent factory if the bean cannot be found in this factory instance.
|
||||
* @param name the name of the bean to retrieve
|
||||
* @param requiredType type the bean must match; can be an interface or superclass
|
||||
* @return an instance of the bean
|
||||
* @return an instance of the bean.
|
||||
* Note that the return value will never be {@code null}. In case of a stub for
|
||||
* {@code null} from a factory method having been resolved for the requested bean, a
|
||||
* {@code BeanNotOfRequiredTypeException} against the NullBean stub will be raised.
|
||||
* Consider using {@link #getBeanProvider(Class)} for resolving optional dependencies.
|
||||
* @throws NoSuchBeanDefinitionException if there is no such bean definition
|
||||
* @throws BeanNotOfRequiredTypeException if the bean is not of the required type
|
||||
* @throws BeansException if the bean could not be created
|
||||
|
||||
+93
-39
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.beans.factory.annotation;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Constructor;
|
||||
@@ -62,6 +63,10 @@ import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.annotation.MergedAnnotation;
|
||||
import org.springframework.core.annotation.MergedAnnotations;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.core.type.MethodMetadata;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
@@ -144,6 +149,9 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
@Nullable
|
||||
private ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
@Nullable
|
||||
private MetadataReaderFactory metadataReaderFactory;
|
||||
|
||||
private final Set<String> lookupMethodsChecked = Collections.newSetFromMap(new ConcurrentHashMap<>(256));
|
||||
|
||||
private final Map<Class<?>, Constructor<?>[]> candidateConstructorsCache = new ConcurrentHashMap<>(256);
|
||||
@@ -238,6 +246,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
"AutowiredAnnotationBeanPostProcessor requires a ConfigurableListableBeanFactory: " + beanFactory);
|
||||
}
|
||||
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
|
||||
this.metadataReaderFactory = new SimpleMetadataReaderFactory(this.beanFactory.getBeanClassLoader());
|
||||
}
|
||||
|
||||
|
||||
@@ -463,12 +472,11 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
return InjectionMetadata.EMPTY;
|
||||
}
|
||||
|
||||
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
|
||||
final List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
|
||||
Class<?> targetClass = clazz;
|
||||
|
||||
do {
|
||||
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
|
||||
|
||||
final List<InjectionMetadata.InjectedElement> fieldElements = new ArrayList<>();
|
||||
ReflectionUtils.doWithLocalFields(targetClass, field -> {
|
||||
MergedAnnotation<?> ann = findAutowiredAnnotation(field);
|
||||
if (ann != null) {
|
||||
@@ -479,10 +487,11 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
return;
|
||||
}
|
||||
boolean required = determineRequiredStatus(ann);
|
||||
currElements.add(new AutowiredFieldElement(field, required));
|
||||
fieldElements.add(new AutowiredFieldElement(field, required));
|
||||
}
|
||||
});
|
||||
|
||||
final List<InjectionMetadata.InjectedElement> methodElements = new ArrayList<>();
|
||||
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
|
||||
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
|
||||
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
|
||||
@@ -504,11 +513,12 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
}
|
||||
boolean required = determineRequiredStatus(ann);
|
||||
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
|
||||
currElements.add(new AutowiredMethodElement(method, required, pd));
|
||||
methodElements.add(new AutowiredMethodElement(method, required, pd));
|
||||
}
|
||||
});
|
||||
|
||||
elements.addAll(0, currElements);
|
||||
elements.addAll(0, sortMethodElements(methodElements, targetClass));
|
||||
elements.addAll(0, fieldElements);
|
||||
targetClass = targetClass.getSuperclass();
|
||||
}
|
||||
while (targetClass != null && targetClass != Object.class);
|
||||
@@ -573,6 +583,47 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
return BeanFactoryUtils.beansOfTypeIncludingAncestors(this.beanFactory, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort the method elements via ASM for deterministic declaration order if possible.
|
||||
*/
|
||||
private List<InjectionMetadata.InjectedElement> sortMethodElements(
|
||||
List<InjectionMetadata.InjectedElement> methodElements, Class<?> targetClass) {
|
||||
|
||||
if (this.metadataReaderFactory != null && methodElements.size() > 1) {
|
||||
// Try reading the class file via ASM for deterministic declaration order...
|
||||
// Unfortunately, the JVM's standard reflection returns methods in arbitrary
|
||||
// order, even between different runs of the same application on the same JVM.
|
||||
try {
|
||||
AnnotationMetadata asm =
|
||||
this.metadataReaderFactory.getMetadataReader(targetClass.getName()).getAnnotationMetadata();
|
||||
Set<MethodMetadata> asmMethods = asm.getAnnotatedMethods(Autowired.class.getName());
|
||||
if (asmMethods.size() >= methodElements.size()) {
|
||||
List<InjectionMetadata.InjectedElement> candidateMethods = new ArrayList<>(methodElements);
|
||||
List<InjectionMetadata.InjectedElement> selectedMethods = new ArrayList<>(asmMethods.size());
|
||||
for (MethodMetadata asmMethod : asmMethods) {
|
||||
for (Iterator<InjectionMetadata.InjectedElement> it = candidateMethods.iterator(); it.hasNext();) {
|
||||
InjectionMetadata.InjectedElement element = it.next();
|
||||
if (element.getMember().getName().equals(asmMethod.getMethodName())) {
|
||||
selectedMethods.add(element);
|
||||
it.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (selectedMethods.size() == methodElements.size()) {
|
||||
// All reflection-detected methods found in ASM method set -> proceed
|
||||
return selectedMethods;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
logger.debug("Failed to read class file via ASM for determining @Autowired method order", ex);
|
||||
// No worries, let's continue with the reflection metadata we started with...
|
||||
}
|
||||
}
|
||||
return methodElements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the specified bean as dependent on the autowired beans.
|
||||
*/
|
||||
@@ -594,7 +645,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
* Resolve the specified cached method argument or field value.
|
||||
*/
|
||||
@Nullable
|
||||
private Object resolvedCachedArgument(@Nullable String beanName, @Nullable Object cachedArgument) {
|
||||
private Object resolveCachedArgument(@Nullable String beanName, @Nullable Object cachedArgument) {
|
||||
if (cachedArgument instanceof DependencyDescriptor) {
|
||||
DependencyDescriptor descriptor = (DependencyDescriptor) cachedArgument;
|
||||
Assert.state(this.beanFactory != null, "No BeanFactory available");
|
||||
@@ -629,10 +680,12 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
Object value;
|
||||
if (this.cached) {
|
||||
try {
|
||||
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
|
||||
value = resolveCachedArgument(beanName, this.cachedFieldValue);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
// Unexpected removal of target bean for cached argument -> re-resolve
|
||||
catch (BeansException ex) {
|
||||
// Unexpected target bean mismatch for cached argument -> re-resolve
|
||||
this.cached = false;
|
||||
logger.debug("Failed to resolve cached argument", ex);
|
||||
value = resolveFieldValue(field, bean, beanName);
|
||||
}
|
||||
}
|
||||
@@ -649,7 +702,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {
|
||||
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
|
||||
desc.setContainingClass(bean.getClass());
|
||||
Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
|
||||
Set<String> autowiredBeanNames = new LinkedHashSet<>(2);
|
||||
Assert.state(beanFactory != null, "No BeanFactory available");
|
||||
TypeConverter typeConverter = beanFactory.getTypeConverter();
|
||||
Object value;
|
||||
@@ -661,21 +714,23 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
}
|
||||
synchronized (this) {
|
||||
if (!this.cached) {
|
||||
Object cachedFieldValue = null;
|
||||
if (value != null || this.required) {
|
||||
cachedFieldValue = desc;
|
||||
Object cachedFieldValue = desc;
|
||||
registerDependentBeans(beanName, autowiredBeanNames);
|
||||
if (autowiredBeanNames.size() == 1) {
|
||||
if (value != null && autowiredBeanNames.size() == 1) {
|
||||
String autowiredBeanName = autowiredBeanNames.iterator().next();
|
||||
if (beanFactory.containsBean(autowiredBeanName) &&
|
||||
beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
|
||||
cachedFieldValue = new ShortcutDependencyDescriptor(
|
||||
desc, autowiredBeanName, field.getType());
|
||||
cachedFieldValue = new ShortcutDependencyDescriptor(desc, autowiredBeanName);
|
||||
}
|
||||
}
|
||||
this.cachedFieldValue = cachedFieldValue;
|
||||
this.cached = true;
|
||||
}
|
||||
else {
|
||||
this.cachedFieldValue = null;
|
||||
// cached flag remains false
|
||||
}
|
||||
this.cachedFieldValue = cachedFieldValue;
|
||||
this.cached = true;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
@@ -709,10 +764,12 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
Object[] arguments;
|
||||
if (this.cached) {
|
||||
try {
|
||||
arguments = resolveCachedArguments(beanName);
|
||||
arguments = resolveCachedArguments(beanName, this.cachedMethodArguments);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
// Unexpected removal of target bean for cached argument -> re-resolve
|
||||
catch (BeansException ex) {
|
||||
// Unexpected target bean mismatch for cached argument -> re-resolve
|
||||
this.cached = false;
|
||||
logger.debug("Failed to resolve cached argument", ex);
|
||||
arguments = resolveMethodArguments(method, bean, beanName);
|
||||
}
|
||||
}
|
||||
@@ -731,14 +788,13 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Object[] resolveCachedArguments(@Nullable String beanName) {
|
||||
Object[] cachedMethodArguments = this.cachedMethodArguments;
|
||||
private Object[] resolveCachedArguments(@Nullable String beanName, @Nullable Object[] cachedMethodArguments) {
|
||||
if (cachedMethodArguments == null) {
|
||||
return null;
|
||||
}
|
||||
Object[] arguments = new Object[cachedMethodArguments.length];
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
arguments[i] = resolvedCachedArgument(beanName, cachedMethodArguments[i]);
|
||||
arguments[i] = resolveCachedArgument(beanName, cachedMethodArguments[i]);
|
||||
}
|
||||
return arguments;
|
||||
}
|
||||
@@ -748,7 +804,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
int argumentCount = method.getParameterCount();
|
||||
Object[] arguments = new Object[argumentCount];
|
||||
DependencyDescriptor[] descriptors = new DependencyDescriptor[argumentCount];
|
||||
Set<String> autowiredBeans = new LinkedHashSet<>(argumentCount);
|
||||
Set<String> autowiredBeanNames = new LinkedHashSet<>(argumentCount * 2);
|
||||
Assert.state(beanFactory != null, "No BeanFactory available");
|
||||
TypeConverter typeConverter = beanFactory.getTypeConverter();
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
@@ -757,7 +813,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
currDesc.setContainingClass(bean.getClass());
|
||||
descriptors[i] = currDesc;
|
||||
try {
|
||||
Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter);
|
||||
Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeanNames, typeConverter);
|
||||
if (arg == null && !this.required) {
|
||||
arguments = null;
|
||||
break;
|
||||
@@ -771,26 +827,27 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
synchronized (this) {
|
||||
if (!this.cached) {
|
||||
if (arguments != null) {
|
||||
DependencyDescriptor[] cachedMethodArguments = Arrays.copyOf(descriptors, arguments.length);
|
||||
registerDependentBeans(beanName, autowiredBeans);
|
||||
if (autowiredBeans.size() == argumentCount) {
|
||||
Iterator<String> it = autowiredBeans.iterator();
|
||||
DependencyDescriptor[] cachedMethodArguments = Arrays.copyOf(descriptors, argumentCount);
|
||||
registerDependentBeans(beanName, autowiredBeanNames);
|
||||
if (autowiredBeanNames.size() == argumentCount) {
|
||||
Iterator<String> it = autowiredBeanNames.iterator();
|
||||
Class<?>[] paramTypes = method.getParameterTypes();
|
||||
for (int i = 0; i < paramTypes.length; i++) {
|
||||
String autowiredBeanName = it.next();
|
||||
if (beanFactory.containsBean(autowiredBeanName) &&
|
||||
if (arguments[i] != null && beanFactory.containsBean(autowiredBeanName) &&
|
||||
beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) {
|
||||
cachedMethodArguments[i] = new ShortcutDependencyDescriptor(
|
||||
descriptors[i], autowiredBeanName, paramTypes[i]);
|
||||
descriptors[i], autowiredBeanName);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.cachedMethodArguments = cachedMethodArguments;
|
||||
this.cached = true;
|
||||
}
|
||||
else {
|
||||
this.cachedMethodArguments = null;
|
||||
// cached flag remains false
|
||||
}
|
||||
this.cached = true;
|
||||
}
|
||||
}
|
||||
return arguments;
|
||||
@@ -806,17 +863,14 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
|
||||
private final String shortcut;
|
||||
|
||||
private final Class<?> requiredType;
|
||||
|
||||
public ShortcutDependencyDescriptor(DependencyDescriptor original, String shortcut, Class<?> requiredType) {
|
||||
public ShortcutDependencyDescriptor(DependencyDescriptor original, String shortcut) {
|
||||
super(original);
|
||||
this.shortcut = shortcut;
|
||||
this.requiredType = requiredType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object resolveShortcut(BeanFactory beanFactory) {
|
||||
return beanFactory.getBean(this.shortcut, this.requiredType);
|
||||
return beanFactory.getBean(this.shortcut, getDependencyType());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+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;
|
||||
}
|
||||
}
|
||||
|
||||
+7
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -97,10 +97,10 @@ import org.springframework.util.StringUtils;
|
||||
* Supports autowiring constructors, properties by name, and properties by type.
|
||||
*
|
||||
* <p>The main template method to be implemented by subclasses is
|
||||
* {@link #resolveDependency(DependencyDescriptor, String, Set, TypeConverter)},
|
||||
* used for autowiring by type. In case of a factory which is capable of searching
|
||||
* its bean definitions, matching beans will typically be implemented through such
|
||||
* a search. For other factory styles, simplified matching algorithms can be implemented.
|
||||
* {@link #resolveDependency(DependencyDescriptor, String, Set, TypeConverter)}, used for
|
||||
* autowiring. In case of a {@link org.springframework.beans.factory.ListableBeanFactory}
|
||||
* which is capable of searching its bean definitions, matching beans will typically be
|
||||
* implemented through such a search. Otherwise, simplified matching can be implemented.
|
||||
*
|
||||
* <p>Note that this class does <i>not</i> assume or implement bean definition
|
||||
* registry capabilities. See {@link DefaultListableBeanFactory} for an implementation
|
||||
@@ -675,7 +675,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||
// Apply SmartInstantiationAwareBeanPostProcessors to predict the
|
||||
// eventual type after a before-instantiation shortcut.
|
||||
if (targetType != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
|
||||
boolean matchingOnlyFactoryBean = typesToMatch.length == 1 && typesToMatch[0] == FactoryBean.class;
|
||||
boolean matchingOnlyFactoryBean = (typesToMatch.length == 1 && typesToMatch[0] == FactoryBean.class);
|
||||
for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
|
||||
Class<?> predicted = bp.predictBeanType(targetType, beanName);
|
||||
if (predicted != null &&
|
||||
@@ -1504,8 +1504,8 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||
converter = bw;
|
||||
}
|
||||
|
||||
Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
|
||||
String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
|
||||
Set<String> autowiredBeanNames = new LinkedHashSet<>(propertyNames.length * 2);
|
||||
for (String propertyName : propertyNames) {
|
||||
try {
|
||||
PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
|
||||
|
||||
+5
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -166,6 +166,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;
|
||||
|
||||
/** Security context used when running with a SecurityManager. */
|
||||
@Nullable
|
||||
private SecurityContextProvider securityContextProvider;
|
||||
@@ -180,8 +183,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.
|
||||
@@ -749,7 +750,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
aliases.add(fullBeanName);
|
||||
}
|
||||
String[] retrievedAliases = super.getAliases(beanName);
|
||||
String prefix = factoryPrefix ? FACTORY_BEAN_PREFIX : "";
|
||||
String prefix = (factoryPrefix ? FACTORY_BEAN_PREFIX : "");
|
||||
for (String retrievedAlias : retrievedAliases) {
|
||||
String alias = prefix + retrievedAlias;
|
||||
if (!alias.equals(name)) {
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -134,7 +134,7 @@ class BeanDefinitionValueResolver {
|
||||
return resolveInnerBean(argName, innerBeanName, bd);
|
||||
}
|
||||
else if (value instanceof DependencyDescriptor) {
|
||||
Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
|
||||
Set<String> autowiredBeanNames = new LinkedHashSet<>(2);
|
||||
Object result = this.beanFactory.resolveDependency(
|
||||
(DependencyDescriptor) value, this.beanName, autowiredBeanNames, this.typeConverter);
|
||||
for (String autowiredBeanName : autowiredBeanNames) {
|
||||
|
||||
+125
-53
@@ -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.
|
||||
@@ -45,6 +45,7 @@ import org.springframework.beans.TypeConverter;
|
||||
import org.springframework.beans.TypeMismatchException;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.InjectionPoint;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
|
||||
@@ -85,12 +86,6 @@ class ConstructorResolver {
|
||||
|
||||
private static final Object[] EMPTY_ARGS = new Object[0];
|
||||
|
||||
/**
|
||||
* Marker for autowired arguments in a cached argument array, to be replaced
|
||||
* by a {@linkplain #resolveAutowiredArgument resolved autowired argument}.
|
||||
*/
|
||||
private static final Object autowiredArgumentMarker = new Object();
|
||||
|
||||
private static final NamedThreadLocal<InjectionPoint> currentInjectionPoint =
|
||||
new NamedThreadLocal<>("Current injection point");
|
||||
|
||||
@@ -112,11 +107,7 @@ class ConstructorResolver {
|
||||
|
||||
/**
|
||||
* "autowire constructor" (with constructor arguments by type) behavior.
|
||||
* Also applied if explicit constructor argument values are specified,
|
||||
* matching all remaining arguments with beans from the bean factory.
|
||||
* <p>This corresponds to constructor injection: In this mode, a Spring
|
||||
* bean factory is able to host components that expect constructor-based
|
||||
* dependency resolution.
|
||||
* Also applied if explicit constructor argument values are specified.
|
||||
* @param beanName the name of the bean
|
||||
* @param mbd the merged bean definition for the bean
|
||||
* @param chosenCtors chosen candidate constructors (or {@code null} if none)
|
||||
@@ -609,13 +600,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,
|
||||
@@ -729,7 +717,7 @@ class ConstructorResolver {
|
||||
|
||||
ArgumentsHolder args = new ArgumentsHolder(paramTypes.length);
|
||||
Set<ConstructorArgumentValues.ValueHolder> usedValueHolders = new HashSet<>(paramTypes.length);
|
||||
Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
|
||||
Set<String> allAutowiredBeanNames = new LinkedHashSet<>(paramTypes.length * 2);
|
||||
|
||||
for (int paramIndex = 0; paramIndex < paramTypes.length; paramIndex++) {
|
||||
Class<?> paramType = paramTypes[paramIndex];
|
||||
@@ -764,8 +752,8 @@ class ConstructorResolver {
|
||||
throw new UnsatisfiedDependencyException(
|
||||
mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam),
|
||||
"Could not convert argument value of type [" +
|
||||
ObjectUtils.nullSafeClassName(valueHolder.getValue()) +
|
||||
"] to required type [" + paramType.getName() + "]: " + ex.getMessage());
|
||||
ObjectUtils.nullSafeClassName(valueHolder.getValue()) +
|
||||
"] to required type [" + paramType.getName() + "]: " + ex.getMessage());
|
||||
}
|
||||
Object sourceHolder = valueHolder.getSource();
|
||||
if (sourceHolder instanceof ConstructorArgumentValues.ValueHolder) {
|
||||
@@ -788,11 +776,17 @@ class ConstructorResolver {
|
||||
"] - did you specify the correct bean references as arguments?");
|
||||
}
|
||||
try {
|
||||
Object autowiredArgument = resolveAutowiredArgument(
|
||||
methodParam, beanName, autowiredBeanNames, converter, fallback);
|
||||
args.rawArguments[paramIndex] = autowiredArgument;
|
||||
args.arguments[paramIndex] = autowiredArgument;
|
||||
args.preparedArguments[paramIndex] = autowiredArgumentMarker;
|
||||
ConstructorDependencyDescriptor desc = new ConstructorDependencyDescriptor(methodParam, true);
|
||||
Set<String> autowiredBeanNames = new LinkedHashSet<>(2);
|
||||
Object arg = resolveAutowiredArgument(
|
||||
desc, paramType, beanName, autowiredBeanNames, converter, fallback);
|
||||
if (arg != null) {
|
||||
setShortcutIfPossible(desc, paramType, autowiredBeanNames);
|
||||
}
|
||||
allAutowiredBeanNames.addAll(autowiredBeanNames);
|
||||
args.rawArguments[paramIndex] = arg;
|
||||
args.arguments[paramIndex] = arg;
|
||||
args.preparedArguments[paramIndex] = desc;
|
||||
args.resolveNecessary = true;
|
||||
}
|
||||
catch (BeansException ex) {
|
||||
@@ -802,14 +796,7 @@ class ConstructorResolver {
|
||||
}
|
||||
}
|
||||
|
||||
for (String autowiredBeanName : autowiredBeanNames) {
|
||||
this.beanFactory.registerDependentBean(autowiredBeanName, beanName);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Autowiring by type from bean name '" + beanName +
|
||||
"' via " + (executable instanceof Constructor ? "constructor" : "factory method") +
|
||||
" to bean named '" + autowiredBeanName + "'");
|
||||
}
|
||||
}
|
||||
registerDependentBeans(executable, beanName, allAutowiredBeanNames);
|
||||
|
||||
return args;
|
||||
}
|
||||
@@ -829,31 +816,61 @@ class ConstructorResolver {
|
||||
Object[] resolvedArgs = new Object[argsToResolve.length];
|
||||
for (int argIndex = 0; argIndex < argsToResolve.length; argIndex++) {
|
||||
Object argValue = argsToResolve[argIndex];
|
||||
MethodParameter methodParam = MethodParameter.forExecutable(executable, argIndex);
|
||||
if (argValue == autowiredArgumentMarker) {
|
||||
argValue = resolveAutowiredArgument(methodParam, beanName, null, converter, true);
|
||||
Class<?> paramType = paramTypes[argIndex];
|
||||
boolean convertNecessary = false;
|
||||
if (argValue instanceof ConstructorDependencyDescriptor) {
|
||||
ConstructorDependencyDescriptor descriptor = (ConstructorDependencyDescriptor) argValue;
|
||||
try {
|
||||
argValue = resolveAutowiredArgument(descriptor, paramType, beanName,
|
||||
null, converter, true);
|
||||
}
|
||||
catch (BeansException ex) {
|
||||
// Unexpected target bean mismatch for cached argument -> re-resolve
|
||||
Set<String> autowiredBeanNames = null;
|
||||
if (descriptor.hasShortcut()) {
|
||||
// Reset shortcut and try to re-resolve it in this thread...
|
||||
descriptor.setShortcut(null);
|
||||
autowiredBeanNames = new LinkedHashSet<>(2);
|
||||
}
|
||||
logger.debug("Failed to resolve cached argument", ex);
|
||||
argValue = resolveAutowiredArgument(descriptor, paramType, beanName,
|
||||
autowiredBeanNames, converter, true);
|
||||
if (autowiredBeanNames != null && !descriptor.hasShortcut()) {
|
||||
// We encountered as stale shortcut before, and the shortcut has
|
||||
// not been re-resolved by another thread in the meantime...
|
||||
if (argValue != null) {
|
||||
setShortcutIfPossible(descriptor, paramType, autowiredBeanNames);
|
||||
}
|
||||
registerDependentBeans(executable, beanName, autowiredBeanNames);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (argValue instanceof BeanMetadataElement) {
|
||||
argValue = valueResolver.resolveValueIfNecessary("constructor argument", argValue);
|
||||
convertNecessary = true;
|
||||
}
|
||||
else if (argValue instanceof String) {
|
||||
argValue = this.beanFactory.evaluateBeanDefinitionString((String) argValue, mbd);
|
||||
convertNecessary = true;
|
||||
}
|
||||
Class<?> paramType = paramTypes[argIndex];
|
||||
try {
|
||||
resolvedArgs[argIndex] = converter.convertIfNecessary(argValue, paramType, methodParam);
|
||||
}
|
||||
catch (TypeMismatchException ex) {
|
||||
throw new UnsatisfiedDependencyException(
|
||||
mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam),
|
||||
"Could not convert argument value of type [" + ObjectUtils.nullSafeClassName(argValue) +
|
||||
"] to required type [" + paramType.getName() + "]: " + ex.getMessage());
|
||||
if (convertNecessary) {
|
||||
MethodParameter methodParam = MethodParameter.forExecutable(executable, argIndex);
|
||||
try {
|
||||
argValue = converter.convertIfNecessary(argValue, paramType, methodParam);
|
||||
}
|
||||
catch (TypeMismatchException ex) {
|
||||
throw new UnsatisfiedDependencyException(
|
||||
mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam),
|
||||
"Could not convert argument value of type [" + ObjectUtils.nullSafeClassName(argValue) +
|
||||
"] to required type [" + paramType.getName() + "]: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
resolvedArgs[argIndex] = argValue;
|
||||
}
|
||||
return resolvedArgs;
|
||||
}
|
||||
|
||||
protected Constructor<?> getUserDeclaredConstructor(Constructor<?> constructor) {
|
||||
private Constructor<?> getUserDeclaredConstructor(Constructor<?> constructor) {
|
||||
Class<?> declaringClass = constructor.getDeclaringClass();
|
||||
Class<?> userClass = ClassUtils.getUserClass(declaringClass);
|
||||
if (userClass != declaringClass) {
|
||||
@@ -869,23 +886,22 @@ class ConstructorResolver {
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method for resolving the specified argument which is supposed to be autowired.
|
||||
* Resolve the specified argument which is supposed to be autowired.
|
||||
*/
|
||||
@Nullable
|
||||
protected Object resolveAutowiredArgument(MethodParameter param, String beanName,
|
||||
Object resolveAutowiredArgument(DependencyDescriptor descriptor, Class<?> paramType, String beanName,
|
||||
@Nullable Set<String> autowiredBeanNames, TypeConverter typeConverter, boolean fallback) {
|
||||
|
||||
Class<?> paramType = param.getParameterType();
|
||||
if (InjectionPoint.class.isAssignableFrom(paramType)) {
|
||||
InjectionPoint injectionPoint = currentInjectionPoint.get();
|
||||
if (injectionPoint == null) {
|
||||
throw new IllegalStateException("No current InjectionPoint available for " + param);
|
||||
throw new IllegalStateException("No current InjectionPoint available for " + descriptor);
|
||||
}
|
||||
return injectionPoint;
|
||||
}
|
||||
|
||||
try {
|
||||
return this.beanFactory.resolveDependency(
|
||||
new DependencyDescriptor(param, true), beanName, autowiredBeanNames, typeConverter);
|
||||
return this.beanFactory.resolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter);
|
||||
}
|
||||
catch (NoUniqueBeanDefinitionException ex) {
|
||||
throw ex;
|
||||
@@ -908,6 +924,31 @@ class ConstructorResolver {
|
||||
}
|
||||
}
|
||||
|
||||
private void setShortcutIfPossible(
|
||||
ConstructorDependencyDescriptor descriptor, Class<?> paramType, Set<String> autowiredBeanNames) {
|
||||
|
||||
if (autowiredBeanNames.size() == 1) {
|
||||
String autowiredBeanName = autowiredBeanNames.iterator().next();
|
||||
if (this.beanFactory.containsBean(autowiredBeanName) &&
|
||||
this.beanFactory.isTypeMatch(autowiredBeanName, paramType)) {
|
||||
descriptor.setShortcut(autowiredBeanName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void registerDependentBeans(
|
||||
Executable executable, String beanName, Set<String> autowiredBeanNames) {
|
||||
|
||||
for (String autowiredBeanName : autowiredBeanNames) {
|
||||
this.beanFactory.registerDependentBean(autowiredBeanName, beanName);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Autowiring by type from bean name '" + beanName + "' via " +
|
||||
(executable instanceof Constructor ? "constructor" : "factory method") +
|
||||
" to bean named '" + autowiredBeanName + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static InjectionPoint setCurrentInjectionPoint(@Nullable InjectionPoint injectionPoint) {
|
||||
InjectionPoint old = currentInjectionPoint.get();
|
||||
if (injectionPoint != null) {
|
||||
@@ -1006,4 +1047,35 @@ class ConstructorResolver {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* DependencyDescriptor marker for constructor arguments,
|
||||
* for differentiating between a provided DependencyDescriptor instance
|
||||
* and an internally built DependencyDescriptor for autowiring purposes.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
private static class ConstructorDependencyDescriptor extends DependencyDescriptor {
|
||||
|
||||
@Nullable
|
||||
private volatile String shortcut;
|
||||
|
||||
public ConstructorDependencyDescriptor(MethodParameter methodParameter, boolean required) {
|
||||
super(methodParameter, required);
|
||||
}
|
||||
|
||||
public void setShortcut(@Nullable String shortcut) {
|
||||
this.shortcut = shortcut;
|
||||
}
|
||||
|
||||
public boolean hasShortcut() {
|
||||
return (this.shortcut != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object resolveShortcut(BeanFactory beanFactory) {
|
||||
String shortcut = this.shortcut;
|
||||
return (shortcut != null ? beanFactory.getBean(shortcut, getDependencyType()) : null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -961,7 +961,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
for (String beanName : beanNames) {
|
||||
Object singletonInstance = getSingleton(beanName);
|
||||
if (singletonInstance instanceof SmartInitializingSingleton) {
|
||||
StartupStep smartInitialize = this.getApplicationStartup().start("spring.beans.smart-initialize")
|
||||
StartupStep smartInitialize = getApplicationStartup().start("spring.beans.smart-initialize")
|
||||
.tag("beanName", beanName);
|
||||
SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
|
||||
if (System.getSecurityManager() != null) {
|
||||
|
||||
+14
-14
@@ -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.
|
||||
@@ -102,7 +102,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
|
||||
private boolean singletonsCurrentlyInDestruction = false;
|
||||
|
||||
/** Disposable bean instances: bean name to disposable instance. */
|
||||
private final Map<String, Object> disposableBeans = new LinkedHashMap<>();
|
||||
private final Map<String, DisposableBean> disposableBeans = new LinkedHashMap<>();
|
||||
|
||||
/** Map between containing bean names: bean name to Set of bean names that the bean contains. */
|
||||
private final Map<String, Set<String>> containedBeanMap = new ConcurrentHashMap<>(16);
|
||||
@@ -340,7 +340,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
|
||||
* (within the entire factory).
|
||||
* @param beanName the name of the bean
|
||||
*/
|
||||
public boolean isSingletonCurrentlyInCreation(String beanName) {
|
||||
public boolean isSingletonCurrentlyInCreation(@Nullable String beanName) {
|
||||
return this.singletonsCurrentlyInCreation.contains(beanName);
|
||||
}
|
||||
|
||||
@@ -447,17 +447,17 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
|
||||
}
|
||||
String canonicalName = canonicalName(beanName);
|
||||
Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
|
||||
if (dependentBeans == null) {
|
||||
if (dependentBeans == null || dependentBeans.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
if (dependentBeans.contains(dependentBeanName)) {
|
||||
return true;
|
||||
}
|
||||
if (alreadySeen == null) {
|
||||
alreadySeen = new HashSet<>();
|
||||
}
|
||||
alreadySeen.add(beanName);
|
||||
for (String transitiveDependency : dependentBeans) {
|
||||
if (alreadySeen == null) {
|
||||
alreadySeen = new HashSet<>();
|
||||
}
|
||||
alreadySeen.add(beanName);
|
||||
if (isDependent(transitiveDependency, dependentBeanName, alreadySeen)) {
|
||||
return true;
|
||||
}
|
||||
@@ -554,7 +554,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
|
||||
// Destroy the corresponding DisposableBean instance.
|
||||
DisposableBean disposableBean;
|
||||
synchronized (this.disposableBeans) {
|
||||
disposableBean = (DisposableBean) this.disposableBeans.remove(beanName);
|
||||
disposableBean = this.disposableBeans.remove(beanName);
|
||||
}
|
||||
destroyBean(beanName, disposableBean);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -68,8 +68,8 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||
|
||||
private static final String SHUTDOWN_METHOD_NAME = "shutdown";
|
||||
|
||||
private static final Log logger = LogFactory.getLog(DisposableBeanAdapter.class);
|
||||
|
||||
private static final Log logger = LogFactory.getLog(DisposableBeanAdapter.class);
|
||||
|
||||
private final Object bean;
|
||||
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -112,7 +112,7 @@ public class LookupOverride extends MethodOverride {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (29 * super.hashCode() + ObjectUtils.nullSafeHashCode(this.beanName));
|
||||
return super.hashCode() * 29 + ObjectUtils.nullSafeHashCode(this.beanName);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+16
-23
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -506,23 +506,12 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
|
||||
if (isExternallyManagedInitMethod(initMethod)) {
|
||||
return true;
|
||||
}
|
||||
if (this.externallyManagedInitMethods != null) {
|
||||
for (String candidate : this.externallyManagedInitMethods) {
|
||||
int indexOfDot = candidate.lastIndexOf('.');
|
||||
if (indexOfDot >= 0) {
|
||||
String methodName = candidate.substring(indexOfDot + 1);
|
||||
if (methodName.equals(initMethod)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return hasAnyExternallyManagedMethod(this.externallyManagedInitMethods, initMethod);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all externally managed initialization methods (as an immutable Set).
|
||||
* Get all externally managed initialization methods (as an immutable Set).
|
||||
* <p>See {@link #registerExternallyManagedInitMethod} for details
|
||||
* regarding the format for the initialization methods in the returned set.
|
||||
* @since 5.3.11
|
||||
@@ -583,19 +572,23 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
|
||||
if (isExternallyManagedDestroyMethod(destroyMethod)) {
|
||||
return true;
|
||||
}
|
||||
if (this.externallyManagedDestroyMethods != null) {
|
||||
for (String candidate : this.externallyManagedDestroyMethods) {
|
||||
int indexOfDot = candidate.lastIndexOf('.');
|
||||
if (indexOfDot >= 0) {
|
||||
String methodName = candidate.substring(indexOfDot + 1);
|
||||
if (methodName.equals(destroyMethod)) {
|
||||
return true;
|
||||
}
|
||||
return hasAnyExternallyManagedMethod(this.externallyManagedDestroyMethods, destroyMethod);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasAnyExternallyManagedMethod(Set<String> candidates, String methodName) {
|
||||
if (candidates != null) {
|
||||
for (String candidate : candidates) {
|
||||
int indexOfDot = candidate.lastIndexOf('.');
|
||||
if (indexOfDot > 0) {
|
||||
String candidateMethodName = candidate.substring(indexOfDot + 1);
|
||||
if (candidateMethodName.equals(methodName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+7
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -78,8 +78,10 @@ public class PathEditor extends PropertyEditorSupport {
|
||||
if (nioPathCandidate && !text.startsWith("/")) {
|
||||
try {
|
||||
URI uri = new URI(text);
|
||||
if (uri.getScheme() != null) {
|
||||
nioPathCandidate = false;
|
||||
String scheme = uri.getScheme();
|
||||
if (scheme != null) {
|
||||
// No NIO candidate except for "C:" style drive letters
|
||||
nioPathCandidate = (scheme.length() == 1);
|
||||
// Let's try NIO file system providers via Paths.get(URI)
|
||||
setValue(Paths.get(uri).normalize());
|
||||
return;
|
||||
@@ -109,7 +111,8 @@ public class PathEditor extends PropertyEditorSupport {
|
||||
setValue(resource.getFile().toPath());
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalArgumentException("Failed to retrieve file for " + resource, ex);
|
||||
throw new IllegalArgumentException(
|
||||
"Could not retrieve file for " + resource + ": " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+45
-37
@@ -1271,10 +1271,11 @@ class DefaultListableBeanFactoryTests {
|
||||
lbf.registerBeanDefinition("rod", bd);
|
||||
RootBeanDefinition bd2 = new RootBeanDefinition(TestBean.class);
|
||||
lbf.registerBeanDefinition("rod2", bd2);
|
||||
assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(() ->
|
||||
lbf.autowire(ConstructorDependency.class, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, false))
|
||||
.withMessageContaining("rod")
|
||||
.withMessageContaining("rod2");
|
||||
|
||||
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
|
||||
.isThrownBy(() -> lbf.autowire(ConstructorDependency.class, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, false))
|
||||
.withMessageContaining("rod")
|
||||
.withMessageContaining("rod2");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1336,11 +1337,12 @@ class DefaultListableBeanFactoryTests {
|
||||
RootBeanDefinition bd2 = new RootBeanDefinition(TestBean.class);
|
||||
bd2.setDependsOn("tb1");
|
||||
lbf.registerBeanDefinition("tb2", bd2);
|
||||
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() ->
|
||||
lbf.preInstantiateSingletons())
|
||||
.withMessageContaining("Circular")
|
||||
.withMessageContaining("'tb2'")
|
||||
.withMessageContaining("'tb1'");
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(() -> lbf.preInstantiateSingletons())
|
||||
.withMessageContaining("Circular")
|
||||
.withMessageContaining("'tb2'")
|
||||
.withMessageContaining("'tb1'");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1354,11 +1356,12 @@ class DefaultListableBeanFactoryTests {
|
||||
RootBeanDefinition bd3 = new RootBeanDefinition(TestBean.class);
|
||||
bd3.setDependsOn("tb1");
|
||||
lbf.registerBeanDefinition("tb3", bd3);
|
||||
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
|
||||
lbf::preInstantiateSingletons)
|
||||
.withMessageContaining("Circular")
|
||||
.withMessageContaining("'tb3'")
|
||||
.withMessageContaining("'tb1'");
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(lbf::preInstantiateSingletons)
|
||||
.withMessageContaining("Circular")
|
||||
.withMessageContaining("'tb3'")
|
||||
.withMessageContaining("'tb1'");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1493,10 +1496,11 @@ class DefaultListableBeanFactoryTests {
|
||||
RootBeanDefinition bd2 = new RootBeanDefinition(HighPriorityTestBean.class);
|
||||
lbf.registerBeanDefinition("bd1", bd1);
|
||||
lbf.registerBeanDefinition("bd2", bd2);
|
||||
assertThatExceptionOfType(NoUniqueBeanDefinitionException.class).isThrownBy(() ->
|
||||
lbf.getBean(TestBean.class))
|
||||
.withMessageContaining("Multiple beans found with the same priority")
|
||||
.withMessageContaining("5"); // conflicting priority
|
||||
|
||||
assertThatExceptionOfType(NoUniqueBeanDefinitionException.class)
|
||||
.isThrownBy(() -> lbf.getBean(TestBean.class))
|
||||
.withMessageContaining("Multiple beans found with the same priority")
|
||||
.withMessageContaining("5"); // conflicting priority
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1698,9 +1702,9 @@ class DefaultListableBeanFactoryTests {
|
||||
lbf.registerBeanDefinition("bd1", bd1);
|
||||
lbf.registerBeanDefinition("bd2", bd2);
|
||||
|
||||
assertThatExceptionOfType(NoUniqueBeanDefinitionException.class).isThrownBy(() ->
|
||||
lbf.getBean(ConstructorDependency.class, 42))
|
||||
.withMessageContaining("more than one 'primary'");
|
||||
assertThatExceptionOfType(NoUniqueBeanDefinitionException.class)
|
||||
.isThrownBy(() -> lbf.getBean(ConstructorDependency.class, 42))
|
||||
.withMessageContaining("more than one 'primary'");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1881,10 +1885,11 @@ class DefaultListableBeanFactoryTests {
|
||||
RootBeanDefinition bd2 = new RootBeanDefinition(TestBean.class);
|
||||
lbf.registerBeanDefinition("test", bd);
|
||||
lbf.registerBeanDefinition("spouse", bd2);
|
||||
assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(() ->
|
||||
lbf.autowire(DependenciesBean.class, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true))
|
||||
.withMessageContaining("test")
|
||||
.withMessageContaining("spouse");
|
||||
|
||||
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
|
||||
.isThrownBy(() -> lbf.autowire(DependenciesBean.class, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true))
|
||||
.withMessageContaining("test")
|
||||
.withMessageContaining("spouse");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1946,10 +1951,11 @@ class DefaultListableBeanFactoryTests {
|
||||
RootBeanDefinition bd2 = new RootBeanDefinition(HighPriorityTestBean.class);
|
||||
lbf.registerBeanDefinition("test", bd);
|
||||
lbf.registerBeanDefinition("spouse", bd2);
|
||||
assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(() ->
|
||||
lbf.autowire(DependenciesBean.class, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true))
|
||||
.withCauseExactlyInstanceOf(NoUniqueBeanDefinitionException.class)
|
||||
.withMessageContaining("5");
|
||||
|
||||
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
|
||||
.isThrownBy(() -> lbf.autowire(DependenciesBean.class, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true))
|
||||
.withCauseExactlyInstanceOf(NoUniqueBeanDefinitionException.class)
|
||||
.withMessageContaining("5");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -2185,19 +2191,21 @@ class DefaultListableBeanFactoryTests {
|
||||
@Test
|
||||
void beanDefinitionWithInterface() {
|
||||
lbf.registerBeanDefinition("test", new RootBeanDefinition(ITestBean.class));
|
||||
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() ->
|
||||
lbf.getBean("test"))
|
||||
.withMessageContaining("interface")
|
||||
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("test"));
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(() -> lbf.getBean("test"))
|
||||
.withMessageContaining("interface")
|
||||
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("test"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void beanDefinitionWithAbstractClass() {
|
||||
lbf.registerBeanDefinition("test", new RootBeanDefinition(AbstractBeanFactory.class));
|
||||
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() ->
|
||||
lbf.getBean("test"))
|
||||
.withMessageContaining("abstract")
|
||||
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("test"));
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(() -> lbf.getBean("test"))
|
||||
.withMessageContaining("abstract")
|
||||
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("test"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+419
-133
File diff suppressed because it is too large
Load Diff
+15
-24
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -28,36 +28,32 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* @author Chris Beams
|
||||
* @since 04.07.2006
|
||||
*/
|
||||
public class DefaultSingletonBeanRegistryTests {
|
||||
class DefaultSingletonBeanRegistryTests {
|
||||
|
||||
private final DefaultSingletonBeanRegistry beanRegistry = new DefaultSingletonBeanRegistry();
|
||||
|
||||
|
||||
@Test
|
||||
public void testSingletons() {
|
||||
DefaultSingletonBeanRegistry beanRegistry = new DefaultSingletonBeanRegistry();
|
||||
|
||||
void singletons() {
|
||||
TestBean tb = new TestBean();
|
||||
beanRegistry.registerSingleton("tb", tb);
|
||||
assertThat(beanRegistry.getSingleton("tb")).isSameAs(tb);
|
||||
|
||||
TestBean tb2 = (TestBean) beanRegistry.getSingleton("tb2", () -> new TestBean());
|
||||
TestBean tb2 = (TestBean) beanRegistry.getSingleton("tb2", TestBean::new);
|
||||
assertThat(beanRegistry.getSingleton("tb2")).isSameAs(tb2);
|
||||
|
||||
assertThat(beanRegistry.getSingleton("tb")).isSameAs(tb);
|
||||
assertThat(beanRegistry.getSingleton("tb2")).isSameAs(tb2);
|
||||
assertThat(beanRegistry.getSingletonCount()).isEqualTo(2);
|
||||
String[] names = beanRegistry.getSingletonNames();
|
||||
assertThat(names.length).isEqualTo(2);
|
||||
assertThat(names[0]).isEqualTo("tb");
|
||||
assertThat(names[1]).isEqualTo("tb2");
|
||||
assertThat(beanRegistry.getSingletonNames()).containsExactly("tb", "tb2");
|
||||
|
||||
beanRegistry.destroySingletons();
|
||||
assertThat(beanRegistry.getSingletonCount()).isEqualTo(0);
|
||||
assertThat(beanRegistry.getSingletonNames().length).isEqualTo(0);
|
||||
assertThat(beanRegistry.getSingletonCount()).isZero();
|
||||
assertThat(beanRegistry.getSingletonNames()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDisposableBean() {
|
||||
DefaultSingletonBeanRegistry beanRegistry = new DefaultSingletonBeanRegistry();
|
||||
|
||||
void disposableBean() {
|
||||
DerivedTestBean tb = new DerivedTestBean();
|
||||
beanRegistry.registerSingleton("tb", tb);
|
||||
beanRegistry.registerDisposableBean("tb", tb);
|
||||
@@ -65,21 +61,16 @@ public class DefaultSingletonBeanRegistryTests {
|
||||
|
||||
assertThat(beanRegistry.getSingleton("tb")).isSameAs(tb);
|
||||
assertThat(beanRegistry.getSingletonCount()).isEqualTo(1);
|
||||
String[] names = beanRegistry.getSingletonNames();
|
||||
assertThat(names.length).isEqualTo(1);
|
||||
assertThat(names[0]).isEqualTo("tb");
|
||||
assertThat(beanRegistry.getSingletonNames()).containsExactly("tb");
|
||||
assertThat(tb.wasDestroyed()).isFalse();
|
||||
|
||||
beanRegistry.destroySingletons();
|
||||
assertThat(beanRegistry.getSingletonCount()).isEqualTo(0);
|
||||
assertThat(beanRegistry.getSingletonNames().length).isEqualTo(0);
|
||||
assertThat(tb.wasDestroyed()).isTrue();
|
||||
assertThat(beanRegistry.getSingletonCount()).isZero();
|
||||
assertThat(beanRegistry.getSingletonNames()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDependentRegistration() {
|
||||
DefaultSingletonBeanRegistry beanRegistry = new DefaultSingletonBeanRegistry();
|
||||
|
||||
void dependentRegistration() {
|
||||
beanRegistry.registerDependentBean("a", "b");
|
||||
beanRegistry.registerDependentBean("b", "c");
|
||||
beanRegistry.registerDependentBean("c", "b");
|
||||
|
||||
+14
-12
@@ -4,25 +4,27 @@
|
||||
<beans default-lazy-init="true">
|
||||
|
||||
<bean name="beta" class="org.springframework.beans.factory.FactoryBeanTests$Beta" autowire="byType">
|
||||
<property name="name" value="${myName}"/>
|
||||
<property name="name" value="${myName}"/>
|
||||
</bean>
|
||||
|
||||
<bean id="alpha" class="org.springframework.beans.factory.FactoryBeanTests$Alpha" autowire="byType"/>
|
||||
|
||||
<bean id="gamma" class="org.springframework.beans.factory.FactoryBeanTests$Gamma"/>
|
||||
|
||||
<bean id="betaFactory" class="org.springframework.beans.factory.FactoryBeanTests$BetaFactoryBean" autowire="constructor">
|
||||
<property name="beta" ref="beta"/>
|
||||
</bean>
|
||||
<bean id="betaFactory" class="org.springframework.beans.factory.FactoryBeanTests$BetaFactoryBean" autowire="constructor">
|
||||
<property name="beta" ref="beta"/>
|
||||
</bean>
|
||||
|
||||
<bean id="gammaFactory" factory-bean="betaFactory" factory-method="getGamma"/>
|
||||
<bean id="gammaFactory" factory-bean="${gammaFactory}" factory-method="${gamma}"/>
|
||||
|
||||
<bean id="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
|
||||
<property name="properties">
|
||||
<props>
|
||||
<prop key="myName">yourName</prop>
|
||||
</props>
|
||||
</property>
|
||||
</bean>
|
||||
<bean id="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
|
||||
<property name="properties">
|
||||
<props>
|
||||
<prop key="myName">yourName</prop>
|
||||
<prop key="gammaFactory">betaFactory</prop>
|
||||
<prop key="gamma">getGamma</prop>
|
||||
</props>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
|
||||
+5
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -33,6 +33,10 @@ public interface ITestBean extends AgeHolder {
|
||||
|
||||
void setName(String name);
|
||||
|
||||
default void applyName(Object name) {
|
||||
setName(String.valueOf(name));
|
||||
}
|
||||
|
||||
ITestBean getSpouse();
|
||||
|
||||
void setSpouse(ITestBean spouse);
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -477,7 +477,7 @@ public class TestBean implements BeanNameAware, BeanFactoryAware, ITestBean, IOt
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.age;
|
||||
return TestBean.class.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+9
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -59,8 +59,8 @@ public class CaffeineCache extends AbstractValueAdaptingCache {
|
||||
* given internal {@link com.github.benmanes.caffeine.cache.Cache} to use.
|
||||
* @param name the name of the cache
|
||||
* @param cache the backing Caffeine Cache instance
|
||||
* @param allowNullValues whether to accept and convert {@code null}
|
||||
* values for this cache
|
||||
* @param allowNullValues whether to accept and convert {@code null} values
|
||||
* for this cache
|
||||
*/
|
||||
public CaffeineCache(String name, com.github.benmanes.caffeine.cache.Cache<Object, Object> cache,
|
||||
boolean allowNullValues) {
|
||||
@@ -86,7 +86,7 @@ public class CaffeineCache extends AbstractValueAdaptingCache {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
@Nullable
|
||||
public <T> T get(Object key, final Callable<T> valueLoader) {
|
||||
public <T> T get(Object key, Callable<T> valueLoader) {
|
||||
return (T) fromStoreValue(this.cache.get(key, new LoadFunction(valueLoader)));
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ public class CaffeineCache extends AbstractValueAdaptingCache {
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public ValueWrapper putIfAbsent(Object key, @Nullable final Object value) {
|
||||
public ValueWrapper putIfAbsent(Object key, @Nullable Object value) {
|
||||
PutIfAbsentFunction callable = new PutIfAbsentFunction(value);
|
||||
Object result = this.cache.get(key, callable);
|
||||
return (callable.called ? null : toValueWrapper(result));
|
||||
@@ -140,7 +140,7 @@ public class CaffeineCache extends AbstractValueAdaptingCache {
|
||||
@Nullable
|
||||
private final Object value;
|
||||
|
||||
private boolean called;
|
||||
boolean called;
|
||||
|
||||
public PutIfAbsentFunction(@Nullable Object value) {
|
||||
this.value = value;
|
||||
@@ -159,16 +159,17 @@ public class CaffeineCache extends AbstractValueAdaptingCache {
|
||||
private final Callable<?> valueLoader;
|
||||
|
||||
public LoadFunction(Callable<?> valueLoader) {
|
||||
Assert.notNull(valueLoader, "Callable must not be null");
|
||||
this.valueLoader = valueLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object apply(Object o) {
|
||||
public Object apply(Object key) {
|
||||
try {
|
||||
return toStoreValue(this.valueLoader.call());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new ValueRetrievalException(o, this.valueLoader, ex);
|
||||
throw new ValueRetrievalException(key, this.valueLoader, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+9
-11
@@ -110,7 +110,7 @@ public class CaffeineCacheManager implements CacheManager {
|
||||
* Set the Caffeine to use for building each individual
|
||||
* {@link CaffeineCache} instance.
|
||||
* @see #createNativeCaffeineCache
|
||||
* @see com.github.benmanes.caffeine.cache.Caffeine#build()
|
||||
* @see Caffeine#build()
|
||||
*/
|
||||
public void setCaffeine(Caffeine<Object, Object> caffeine) {
|
||||
Assert.notNull(caffeine, "Caffeine must not be null");
|
||||
@@ -121,7 +121,7 @@ public class CaffeineCacheManager implements CacheManager {
|
||||
* Set the {@link CaffeineSpec} to use for building each individual
|
||||
* {@link CaffeineCache} instance.
|
||||
* @see #createNativeCaffeineCache
|
||||
* @see com.github.benmanes.caffeine.cache.Caffeine#from(CaffeineSpec)
|
||||
* @see Caffeine#from(CaffeineSpec)
|
||||
*/
|
||||
public void setCaffeineSpec(CaffeineSpec caffeineSpec) {
|
||||
doSetCaffeine(Caffeine.from(caffeineSpec));
|
||||
@@ -132,7 +132,7 @@ public class CaffeineCacheManager implements CacheManager {
|
||||
* individual {@link CaffeineCache} instance. The given value needs to
|
||||
* comply with Caffeine's {@link CaffeineSpec} (see its javadoc).
|
||||
* @see #createNativeCaffeineCache
|
||||
* @see com.github.benmanes.caffeine.cache.Caffeine#from(String)
|
||||
* @see Caffeine#from(String)
|
||||
*/
|
||||
public void setCacheSpecification(String cacheSpecification) {
|
||||
doSetCaffeine(Caffeine.from(cacheSpecification));
|
||||
@@ -149,7 +149,7 @@ public class CaffeineCacheManager implements CacheManager {
|
||||
* Set the Caffeine CacheLoader to use for building each individual
|
||||
* {@link CaffeineCache} instance, turning it into a LoadingCache.
|
||||
* @see #createNativeCaffeineCache
|
||||
* @see com.github.benmanes.caffeine.cache.Caffeine#build(CacheLoader)
|
||||
* @see Caffeine#build(CacheLoader)
|
||||
* @see com.github.benmanes.caffeine.cache.LoadingCache
|
||||
*/
|
||||
public void setCacheLoader(CacheLoader<Object, Object> cacheLoader) {
|
||||
@@ -189,13 +189,11 @@ public class CaffeineCacheManager implements CacheManager {
|
||||
@Override
|
||||
@Nullable
|
||||
public Cache getCache(String name) {
|
||||
if (this.dynamic) {
|
||||
Cache cache = this.cacheMap.get(name);
|
||||
return (cache != null) ? cache : this.cacheMap.computeIfAbsent(name, this::createCaffeineCache);
|
||||
}
|
||||
else {
|
||||
return this.cacheMap.get(name);
|
||||
Cache cache = this.cacheMap.get(name);
|
||||
if (cache == null && this.dynamic) {
|
||||
cache = this.cacheMap.computeIfAbsent(name, this::createCaffeineCache);
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
|
||||
@@ -238,7 +236,7 @@ public class CaffeineCacheManager implements CacheManager {
|
||||
* Build a common {@link CaffeineCache} instance for the specified cache name,
|
||||
* using the common Caffeine configuration specified on this cache manager.
|
||||
* <p>Delegates to {@link #adaptCaffeineCache} as the adaptation method to
|
||||
* Spring's cache abstraction (allowing for centralized decoration etc),
|
||||
* Spring's cache abstraction (allowing for centralized decoration etc.),
|
||||
* passing in a freshly built native Caffeine Cache instance.
|
||||
* @param name the name of the cache
|
||||
* @return the Spring CaffeineCache adapter (or a decorator thereof)
|
||||
|
||||
+18
-18
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -38,19 +38,17 @@ public class CaffeineCacheManagerTests {
|
||||
@Test
|
||||
public void testDynamicMode() {
|
||||
CacheManager cm = new CaffeineCacheManager();
|
||||
|
||||
Cache cache1 = cm.getCache("c1");
|
||||
boolean condition2 = cache1 instanceof CaffeineCache;
|
||||
assertThat(condition2).isTrue();
|
||||
assertThat(cache1).isInstanceOf(CaffeineCache.class);
|
||||
Cache cache1again = cm.getCache("c1");
|
||||
assertThat(cache1).isSameAs(cache1again);
|
||||
Cache cache2 = cm.getCache("c2");
|
||||
boolean condition1 = cache2 instanceof CaffeineCache;
|
||||
assertThat(condition1).isTrue();
|
||||
assertThat(cache2).isInstanceOf(CaffeineCache.class);
|
||||
Cache cache2again = cm.getCache("c2");
|
||||
assertThat(cache2).isSameAs(cache2again);
|
||||
Cache cache3 = cm.getCache("c3");
|
||||
boolean condition = cache3 instanceof CaffeineCache;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(cache3).isInstanceOf(CaffeineCache.class);
|
||||
Cache cache3again = cm.getCache("c3");
|
||||
assertThat(cache3).isSameAs(cache3again);
|
||||
|
||||
@@ -62,19 +60,23 @@ public class CaffeineCacheManagerTests {
|
||||
assertThat(cache1.get("key3").get()).isNull();
|
||||
cache1.evict("key3");
|
||||
assertThat(cache1.get("key3")).isNull();
|
||||
assertThat(cache1.get("key3", () -> "value3")).isEqualTo("value3");
|
||||
assertThat(cache1.get("key3", () -> "value3")).isEqualTo("value3");
|
||||
cache1.evict("key3");
|
||||
assertThat(cache1.get("key3", () -> (String) null)).isNull();
|
||||
assertThat(cache1.get("key3", () -> (String) null)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStaticMode() {
|
||||
CaffeineCacheManager cm = new CaffeineCacheManager("c1", "c2");
|
||||
|
||||
Cache cache1 = cm.getCache("c1");
|
||||
boolean condition3 = cache1 instanceof CaffeineCache;
|
||||
assertThat(condition3).isTrue();
|
||||
assertThat(cache1).isInstanceOf(CaffeineCache.class);
|
||||
Cache cache1again = cm.getCache("c1");
|
||||
assertThat(cache1).isSameAs(cache1again);
|
||||
Cache cache2 = cm.getCache("c2");
|
||||
boolean condition2 = cache2 instanceof CaffeineCache;
|
||||
assertThat(condition2).isTrue();
|
||||
assertThat(cache2).isInstanceOf(CaffeineCache.class);
|
||||
Cache cache2again = cm.getCache("c2");
|
||||
assertThat(cache2).isSameAs(cache2again);
|
||||
Cache cache3 = cm.getCache("c3");
|
||||
@@ -91,13 +93,11 @@ public class CaffeineCacheManagerTests {
|
||||
|
||||
cm.setAllowNullValues(false);
|
||||
Cache cache1x = cm.getCache("c1");
|
||||
boolean condition1 = cache1x instanceof CaffeineCache;
|
||||
assertThat(condition1).isTrue();
|
||||
assertThat(cache1x != cache1).isTrue();
|
||||
assertThat(cache1x).isInstanceOf(CaffeineCache.class);
|
||||
assertThat(cache1x).isNotSameAs(cache1);
|
||||
Cache cache2x = cm.getCache("c2");
|
||||
boolean condition = cache2x instanceof CaffeineCache;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(cache2x != cache2).isTrue();
|
||||
assertThat(cache2x).isInstanceOf(CaffeineCache.class);
|
||||
assertThat(cache2x).isNotSameAs(cache2);
|
||||
Cache cache3x = cm.getCache("c3");
|
||||
assertThat(cache3x).isNull();
|
||||
|
||||
@@ -190,7 +190,7 @@ public class CaffeineCacheManagerTests {
|
||||
assertThat(value.get()).isEqualTo("pong");
|
||||
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> assertThat(cache1.get("foo")).isNull())
|
||||
.withMessageContaining("I only know ping");
|
||||
.withMessageContaining("I only know ping");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+13
-13
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -114,7 +114,7 @@ class QuartzSupportTests {
|
||||
trigger.setName("myTrigger");
|
||||
trigger.setJobDetail(jobDetail);
|
||||
trigger.setStartDelay(1);
|
||||
trigger.setRepeatInterval(500);
|
||||
trigger.setRepeatInterval(100);
|
||||
trigger.setRepeatCount(1);
|
||||
trigger.afterPropertiesSet();
|
||||
|
||||
@@ -126,14 +126,14 @@ class QuartzSupportTests {
|
||||
bean.start();
|
||||
|
||||
Thread.sleep(500);
|
||||
assertThat(DummyJob.count > 0).as("DummyJob should have been executed at least once.").isTrue();
|
||||
assertThat(DummyJob.count).as("DummyJob should have been executed at least once.").isGreaterThan(0);
|
||||
assertThat(taskExecutor.count).isEqualTo(DummyJob.count);
|
||||
|
||||
bean.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
void jobDetailWithRunnableInsteadOfJob() {
|
||||
JobDetailImpl jobDetail = new JobDetailImpl();
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
@@ -156,7 +156,7 @@ class QuartzSupportTests {
|
||||
trigger.setName("myTrigger");
|
||||
trigger.setJobDetail(jobDetail);
|
||||
trigger.setStartDelay(1);
|
||||
trigger.setRepeatInterval(500);
|
||||
trigger.setRepeatInterval(100);
|
||||
trigger.setRepeatCount(1);
|
||||
trigger.afterPropertiesSet();
|
||||
|
||||
@@ -168,7 +168,7 @@ class QuartzSupportTests {
|
||||
|
||||
Thread.sleep(500);
|
||||
assertThat(DummyJobBean.param).isEqualTo(10);
|
||||
assertThat(DummyJobBean.count > 0).isTrue();
|
||||
assertThat(DummyJobBean.count).isGreaterThan(0);
|
||||
|
||||
bean.destroy();
|
||||
}
|
||||
@@ -190,7 +190,7 @@ class QuartzSupportTests {
|
||||
trigger.setName("myTrigger");
|
||||
trigger.setJobDetail(jobDetail);
|
||||
trigger.setStartDelay(1);
|
||||
trigger.setRepeatInterval(500);
|
||||
trigger.setRepeatInterval(100);
|
||||
trigger.setRepeatCount(1);
|
||||
trigger.afterPropertiesSet();
|
||||
|
||||
@@ -203,7 +203,7 @@ class QuartzSupportTests {
|
||||
|
||||
Thread.sleep(500);
|
||||
assertThat(DummyJob.param).isEqualTo(10);
|
||||
assertThat(DummyJob.count > 0).as("DummyJob should have been executed at least once.").isTrue();
|
||||
assertThat(DummyJob.count).as("DummyJob should have been executed at least once.").isGreaterThan(0);
|
||||
|
||||
bean.destroy();
|
||||
}
|
||||
@@ -225,7 +225,7 @@ class QuartzSupportTests {
|
||||
trigger.setName("myTrigger");
|
||||
trigger.setJobDetail(jobDetail);
|
||||
trigger.setStartDelay(1);
|
||||
trigger.setRepeatInterval(500);
|
||||
trigger.setRepeatInterval(100);
|
||||
trigger.setRepeatCount(1);
|
||||
trigger.afterPropertiesSet();
|
||||
|
||||
@@ -239,7 +239,7 @@ class QuartzSupportTests {
|
||||
|
||||
Thread.sleep(500);
|
||||
assertThat(DummyJob.param).isEqualTo(0);
|
||||
assertThat(DummyJob.count == 0).isTrue();
|
||||
assertThat(DummyJob.count).isEqualTo(0);
|
||||
|
||||
bean.destroy();
|
||||
}
|
||||
@@ -260,7 +260,7 @@ class QuartzSupportTests {
|
||||
trigger.setName("myTrigger");
|
||||
trigger.setJobDetail(jobDetail);
|
||||
trigger.setStartDelay(1);
|
||||
trigger.setRepeatInterval(500);
|
||||
trigger.setRepeatInterval(100);
|
||||
trigger.setRepeatCount(1);
|
||||
trigger.afterPropertiesSet();
|
||||
|
||||
@@ -273,7 +273,7 @@ class QuartzSupportTests {
|
||||
|
||||
Thread.sleep(500);
|
||||
assertThat(DummyJobBean.param).isEqualTo(10);
|
||||
assertThat(DummyJobBean.count > 0).isTrue();
|
||||
assertThat(DummyJobBean.count).isGreaterThan(0);
|
||||
|
||||
bean.destroy();
|
||||
}
|
||||
@@ -292,7 +292,7 @@ class QuartzSupportTests {
|
||||
|
||||
Thread.sleep(500);
|
||||
assertThat(DummyJob.param).isEqualTo(10);
|
||||
assertThat(DummyJob.count > 0).as("DummyJob should have been executed at least once.").isTrue();
|
||||
assertThat(DummyJob.count).as("DummyJob should have been executed at least once.").isGreaterThan(0);
|
||||
|
||||
bean.destroy();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,14 +23,20 @@ import org.springframework.lang.Nullable;
|
||||
/**
|
||||
* Interface that defines common cache operations.
|
||||
*
|
||||
* <b>Note:</b> Due to the generic use of caching, it is recommended that
|
||||
* implementations allow storage of {@code null} values (for example to
|
||||
* cache methods that return {@code null}).
|
||||
* <p>Serves as an SPI for Spring's annotation-based caching model
|
||||
* ({@link org.springframework.cache.annotation.Cacheable} and co)
|
||||
* as well as an API for direct usage in applications.
|
||||
*
|
||||
* <p><b>Note:</b> Due to the generic use of caching, it is recommended
|
||||
* that implementations allow storage of {@code null} values
|
||||
* (for example to cache methods that return {@code null}).
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Juergen Hoeller
|
||||
* @author Stephane Nicoll
|
||||
* @since 3.1
|
||||
* @see CacheManager
|
||||
* @see org.springframework.cache.annotation.Cacheable
|
||||
*/
|
||||
public interface Cache {
|
||||
|
||||
|
||||
+6
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -32,6 +32,7 @@ import java.lang.annotation.Target;
|
||||
* @author Stephane Nicoll
|
||||
* @author Sam Brannen
|
||||
* @since 4.1
|
||||
* @see Cacheable
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@@ -42,8 +43,10 @@ public @interface CacheConfig {
|
||||
* Names of the default caches to consider for caching operations defined
|
||||
* in the annotated class.
|
||||
* <p>If none is set at the operation level, these are used instead of the default.
|
||||
* <p>May be used to determine the target cache (or caches), matching the
|
||||
* qualifier value or the bean names of a specific bean definition.
|
||||
* <p>Names may be used to determine the target cache(s), to be resolved via the
|
||||
* configured {@link #cacheResolver()} which typically delegates to
|
||||
* {@link org.springframework.cache.CacheManager#getCache}.
|
||||
* For further details see {@link Cacheable#cacheNames()}.
|
||||
*/
|
||||
String[] cacheNames() default {};
|
||||
|
||||
|
||||
+10
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -68,8 +68,12 @@ public @interface Cacheable {
|
||||
|
||||
/**
|
||||
* Names of the caches in which method invocation results are stored.
|
||||
* <p>Names may be used to determine the target cache (or caches), matching
|
||||
* the qualifier value or bean name of a specific bean definition.
|
||||
* <p>Names may be used to determine the target cache(s), to be resolved via the
|
||||
* configured {@link #cacheResolver()} which typically delegates to
|
||||
* {@link org.springframework.cache.CacheManager#getCache}.
|
||||
* <p>This will usually be a single cache name. If multiple names are specified,
|
||||
* they will be consulted for a cache hit in the order of definition, and they
|
||||
* will all receive a put/evict request for the same newly cached value.
|
||||
* @since 4.2
|
||||
* @see #value
|
||||
* @see CacheConfig#cacheNames
|
||||
@@ -175,9 +179,9 @@ public @interface Cacheable {
|
||||
* <li>Only one cache may be specified</li>
|
||||
* <li>No other cache-related operation can be combined</li>
|
||||
* </ol>
|
||||
* This is effectively a hint and the actual cache provider that you are
|
||||
* using may not support it in a synchronized fashion. Check your provider
|
||||
* documentation for more details on the actual semantics.
|
||||
* This is effectively a hint and the chosen cache provider might not actually
|
||||
* support it in a synchronized fashion. Check your provider documentation for
|
||||
* more details on the actual semantics.
|
||||
* @since 4.3
|
||||
* @see org.springframework.cache.Cache#get(Object, Callable)
|
||||
*/
|
||||
|
||||
Vendored
+2
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -166,13 +166,7 @@ public class ConcurrentMapCacheManager implements CacheManager, BeanClassLoaderA
|
||||
public Cache getCache(String name) {
|
||||
Cache cache = this.cacheMap.get(name);
|
||||
if (cache == null && this.dynamic) {
|
||||
synchronized (this.cacheMap) {
|
||||
cache = this.cacheMap.get(name);
|
||||
if (cache == null) {
|
||||
cache = createConcurrentMapCache(name);
|
||||
this.cacheMap.put(name, cache);
|
||||
}
|
||||
}
|
||||
cache = this.cacheMap.computeIfAbsent(name, this::createConcurrentMapCache);
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
Vendored
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -82,12 +82,12 @@ public abstract class AbstractCacheInvoker {
|
||||
* Execute {@link Cache#put(Object, Object)} on the specified {@link Cache}
|
||||
* and invoke the error handler if an exception occurs.
|
||||
*/
|
||||
protected void doPut(Cache cache, Object key, @Nullable Object result) {
|
||||
protected void doPut(Cache cache, Object key, @Nullable Object value) {
|
||||
try {
|
||||
cache.put(key, result);
|
||||
cache.put(key, value);
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
getErrorHandler().handleCachePutError(ex, cache, key, result);
|
||||
getErrorHandler().handleCachePutError(ex, cache, key, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+31
-31
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -214,7 +214,7 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
||||
@Override
|
||||
public void afterSingletonsInstantiated() {
|
||||
if (getCacheResolver() == null) {
|
||||
// Lazily initialize cache resolver via default cache manager...
|
||||
// Lazily initialize cache resolver via default cache manager
|
||||
Assert.state(this.beanFactory != null, "CacheResolver or BeanFactory must be set on cache aspect");
|
||||
try {
|
||||
setCacheManager(this.beanFactory.getBean(CacheManager.class));
|
||||
@@ -307,22 +307,22 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a bean with the specified name and type. Used to resolve services that
|
||||
* are referenced by name in a {@link CacheOperation}.
|
||||
* @param beanName the name of the bean, as defined by the operation
|
||||
* @param expectedType type for the bean
|
||||
* @return the bean matching that name
|
||||
* Retrieve a bean with the specified name and type.
|
||||
* Used to resolve services that are referenced by name in a {@link CacheOperation}.
|
||||
* @param name the name of the bean, as defined by the cache operation
|
||||
* @param serviceType the type expected by the operation's service reference
|
||||
* @return the bean matching the expected type, qualified by the given name
|
||||
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException if such bean does not exist
|
||||
* @see CacheOperation#getKeyGenerator()
|
||||
* @see CacheOperation#getCacheManager()
|
||||
* @see CacheOperation#getCacheResolver()
|
||||
*/
|
||||
protected <T> T getBean(String beanName, Class<T> expectedType) {
|
||||
protected <T> T getBean(String name, Class<T> serviceType) {
|
||||
if (this.beanFactory == null) {
|
||||
throw new IllegalStateException(
|
||||
"BeanFactory must be set on cache aspect for " + expectedType.getSimpleName() + " retrieval");
|
||||
"BeanFactory must be set on cache aspect for " + serviceType.getSimpleName() + " retrieval");
|
||||
}
|
||||
return BeanFactoryAnnotationUtils.qualifiedBeanOfType(this.beanFactory, expectedType, beanName);
|
||||
return BeanFactoryAnnotationUtils.qualifiedBeanOfType(this.beanFactory, serviceType, name);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -388,21 +388,20 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
||||
}
|
||||
}
|
||||
else {
|
||||
// No caching required, only call the underlying method
|
||||
// No caching required, just call the underlying method
|
||||
return invokeOperation(invoker);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Process any early evictions
|
||||
processCacheEvicts(contexts.get(CacheEvictOperation.class), true,
|
||||
CacheOperationExpressionEvaluator.NO_RESULT);
|
||||
|
||||
// Check if we have a cached item matching the conditions
|
||||
// Check if we have a cached value matching the conditions
|
||||
Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class));
|
||||
|
||||
// Collect puts from any @Cacheable miss, if no cached item is found
|
||||
List<CachePutRequest> cachePutRequests = new ArrayList<>();
|
||||
// Collect puts from any @Cacheable miss, if no cached value is found
|
||||
List<CachePutRequest> cachePutRequests = new ArrayList<>(1);
|
||||
if (cacheHit == null) {
|
||||
collectPutRequests(contexts.get(CacheableOperation.class),
|
||||
CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);
|
||||
@@ -469,7 +468,7 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
||||
private boolean hasCachePut(CacheOperationContexts contexts) {
|
||||
// Evaluate the conditions *without* the result object because we don't have it yet...
|
||||
Collection<CacheOperationContext> cachePutContexts = contexts.get(CachePutOperation.class);
|
||||
Collection<CacheOperationContext> excluded = new ArrayList<>();
|
||||
Collection<CacheOperationContext> excluded = new ArrayList<>(1);
|
||||
for (CacheOperationContext context : cachePutContexts) {
|
||||
try {
|
||||
if (!context.isConditionPassing(CacheOperationExpressionEvaluator.RESULT_UNAVAILABLE)) {
|
||||
@@ -522,9 +521,9 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a cached item only for {@link CacheableOperation} that passes the condition.
|
||||
* Find a cached value only for {@link CacheableOperation} that passes the condition.
|
||||
* @param contexts the cacheable operations
|
||||
* @return a {@link Cache.ValueWrapper} holding the cached item,
|
||||
* @return a {@link Cache.ValueWrapper} holding the cached value,
|
||||
* or {@code null} if none is found
|
||||
*/
|
||||
@Nullable
|
||||
@@ -548,10 +547,10 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect the {@link CachePutRequest} for all {@link CacheOperation} using
|
||||
* the specified result item.
|
||||
* Collect a {@link CachePutRequest} for every {@link CacheOperation}
|
||||
* using the specified result value.
|
||||
* @param contexts the contexts to handle
|
||||
* @param result the result item (never {@code null})
|
||||
* @param result the result value
|
||||
* @param putRequests the collection to update
|
||||
*/
|
||||
private void collectPutRequests(Collection<CacheOperationContext> contexts,
|
||||
@@ -641,21 +640,22 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
||||
if (syncEnabled) {
|
||||
if (this.contexts.size() > 1) {
|
||||
throw new IllegalStateException(
|
||||
"@Cacheable(sync=true) cannot be combined with other cache operations on '" + method + "'");
|
||||
"A sync=true operation cannot be combined with other cache operations on '" + method + "'");
|
||||
}
|
||||
if (cacheOperationContexts.size() > 1) {
|
||||
throw new IllegalStateException(
|
||||
"Only one @Cacheable(sync=true) entry is allowed on '" + method + "'");
|
||||
"Only one sync=true operation is allowed on '" + method + "'");
|
||||
}
|
||||
CacheOperationContext cacheOperationContext = cacheOperationContexts.iterator().next();
|
||||
CacheableOperation operation = (CacheableOperation) cacheOperationContext.getOperation();
|
||||
CacheOperation operation = cacheOperationContext.getOperation();
|
||||
if (cacheOperationContext.getCaches().size() > 1) {
|
||||
throw new IllegalStateException(
|
||||
"@Cacheable(sync=true) only allows a single cache on '" + operation + "'");
|
||||
"A sync=true operation is restricted to a single cache on '" + operation + "'");
|
||||
}
|
||||
if (StringUtils.hasText(operation.getUnless())) {
|
||||
if (operation instanceof CacheableOperation &&
|
||||
StringUtils.hasText(((CacheableOperation) operation).getUnless())) {
|
||||
throw new IllegalStateException(
|
||||
"@Cacheable(sync=true) does not support unless attribute on '" + operation + "'");
|
||||
"A sync=true operation does not support the unless attribute on '" + operation + "'");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -722,7 +722,7 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
||||
this.args = extractArgs(metadata.method, args);
|
||||
this.target = target;
|
||||
this.caches = CacheAspectSupport.this.getCaches(this, metadata.cacheResolver);
|
||||
this.cacheNames = createCacheNames(this.caches);
|
||||
this.cacheNames = prepareCacheNames(this.caches);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -810,8 +810,8 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
||||
return this.cacheNames;
|
||||
}
|
||||
|
||||
private Collection<String> createCacheNames(Collection<? extends Cache> caches) {
|
||||
Collection<String> names = new ArrayList<>();
|
||||
private Collection<String> prepareCacheNames(Collection<? extends Cache> caches) {
|
||||
Collection<String> names = new ArrayList<>(caches.size());
|
||||
for (Cache cache : caches) {
|
||||
names.add(cache.getName());
|
||||
}
|
||||
@@ -885,13 +885,13 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal holder class for recording that a cache method was invoked.
|
||||
*/
|
||||
private static class InvocationAwareResult {
|
||||
|
||||
boolean invoked;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Vendored
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,7 +22,7 @@ import org.springframework.lang.Nullable;
|
||||
* Abstract the invocation of a cache operation.
|
||||
*
|
||||
* <p>Does not provide a way to transmit checked exceptions but
|
||||
* provide a special exception that should be used to wrap any
|
||||
* provides a special exception that should be used to wrap any
|
||||
* exception that was thrown by the underlying invocation.
|
||||
* Callers are expected to handle this issue type specifically.
|
||||
*
|
||||
|
||||
+5
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -47,7 +47,7 @@ public class NameMatchCacheOperationSource implements CacheOperationSource, Seri
|
||||
|
||||
|
||||
/** Keys are method names; values are TransactionAttributes. */
|
||||
private Map<String, Collection<CacheOperation>> nameMap = new LinkedHashMap<>();
|
||||
private final Map<String, Collection<CacheOperation>> nameMap = new LinkedHashMap<>();
|
||||
|
||||
|
||||
/**
|
||||
@@ -117,8 +117,8 @@ public class NameMatchCacheOperationSource implements CacheOperationSource, Seri
|
||||
if (!(other instanceof NameMatchCacheOperationSource)) {
|
||||
return false;
|
||||
}
|
||||
NameMatchCacheOperationSource otherTas = (NameMatchCacheOperationSource) other;
|
||||
return ObjectUtils.nullSafeEquals(this.nameMap, otherTas.nameMap);
|
||||
NameMatchCacheOperationSource otherCos = (NameMatchCacheOperationSource) other;
|
||||
return ObjectUtils.nullSafeEquals(this.nameMap, otherCos.nameMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -130,4 +130,5 @@ public class NameMatchCacheOperationSource implements CacheOperationSource, Seri
|
||||
public String toString() {
|
||||
return getClass().getName() + ": " + this.nameMap;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+5
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -24,14 +24,16 @@ import org.springframework.cache.Cache;
|
||||
/**
|
||||
* Simple cache manager working against a given collection of caches.
|
||||
* Useful for testing or simple caching declarations.
|
||||
* <p>
|
||||
* When using this implementation directly, i.e. not via a regular
|
||||
*
|
||||
* <p>When using this implementation directly, i.e. not via a regular
|
||||
* bean registration, {@link #initializeCaches()} should be invoked
|
||||
* to initialize its internal state once the
|
||||
* {@linkplain #setCaches(Collection) caches have been provided}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @since 3.1
|
||||
* @see NoOpCache
|
||||
* @see org.springframework.cache.concurrent.ConcurrentMapCache
|
||||
*/
|
||||
public class SimpleCacheManager extends AbstractCacheManager {
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -65,7 +65,7 @@ public class AnnotationConfigApplicationContext extends GenericApplicationContex
|
||||
* through {@link #register} calls and then manually {@linkplain #refresh refreshed}.
|
||||
*/
|
||||
public AnnotationConfigApplicationContext() {
|
||||
StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
|
||||
StartupStep createAnnotatedBeanDefReader = getApplicationStartup().start("spring.context.annotated-bean-reader.create");
|
||||
this.reader = new AnnotatedBeanDefinitionReader(this);
|
||||
createAnnotatedBeanDefReader.end();
|
||||
this.scanner = new ClassPathBeanDefinitionScanner(this);
|
||||
@@ -163,7 +163,7 @@ public class AnnotationConfigApplicationContext extends GenericApplicationContex
|
||||
@Override
|
||||
public void register(Class<?>... componentClasses) {
|
||||
Assert.notEmpty(componentClasses, "At least one component class must be specified");
|
||||
StartupStep registerComponentClass = this.getApplicationStartup().start("spring.context.component-classes.register")
|
||||
StartupStep registerComponentClass = getApplicationStartup().start("spring.context.component-classes.register")
|
||||
.tag("classes", () -> Arrays.toString(componentClasses));
|
||||
this.reader.register(componentClasses);
|
||||
registerComponentClass.end();
|
||||
@@ -180,7 +180,7 @@ public class AnnotationConfigApplicationContext extends GenericApplicationContex
|
||||
@Override
|
||||
public void scan(String... basePackages) {
|
||||
Assert.notEmpty(basePackages, "At least one base package must be specified");
|
||||
StartupStep scanPackages = this.getApplicationStartup().start("spring.context.base-packages.scan")
|
||||
StartupStep scanPackages = getApplicationStartup().start("spring.context.base-packages.scan")
|
||||
.tag("packages", () -> Arrays.toString(basePackages));
|
||||
this.scanner.scan(basePackages);
|
||||
scanPackages.end();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -39,6 +39,7 @@ final class BeanMethod extends ConfigurationMethod {
|
||||
super(metadata, configurationClass);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void validate(ProblemReporter problemReporter) {
|
||||
if (getMetadata().isStatic()) {
|
||||
@@ -55,9 +56,9 @@ final class BeanMethod extends ConfigurationMethod {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
return ((this == obj) || ((obj instanceof BeanMethod) &&
|
||||
this.metadata.equals(((BeanMethod) obj).metadata)));
|
||||
public boolean equals(@Nullable Object other) {
|
||||
return (this == other || (other instanceof BeanMethod &&
|
||||
this.metadata.equals(((BeanMethod) other).metadata)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -70,6 +71,7 @@ final class BeanMethod extends ConfigurationMethod {
|
||||
return "BeanMethod: " + this.metadata;
|
||||
}
|
||||
|
||||
|
||||
private class NonOverridableMethodError extends Problem {
|
||||
|
||||
NonOverridableMethodError() {
|
||||
|
||||
+9
-9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -329,8 +329,8 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo
|
||||
* @return {@code true} if the bean can be registered as-is;
|
||||
* {@code false} if it should be skipped because there is an
|
||||
* existing, compatible bean definition for the specified name
|
||||
* @throws ConflictingBeanDefinitionException if an existing, incompatible
|
||||
* bean definition has been found for the specified name
|
||||
* @throws IllegalStateException if an existing, incompatible bean definition
|
||||
* has been found for the specified name
|
||||
*/
|
||||
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
|
||||
if (!this.registry.containsBeanDefinition(beanName)) {
|
||||
@@ -354,16 +354,16 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo
|
||||
* the given existing bean definition.
|
||||
* <p>The default implementation considers them as compatible when the existing
|
||||
* bean definition comes from the same source or from a non-scanning source.
|
||||
* @param newDefinition the new bean definition, originated from scanning
|
||||
* @param existingDefinition the existing bean definition, potentially an
|
||||
* @param newDef the new bean definition, originated from scanning
|
||||
* @param existingDef the existing bean definition, potentially an
|
||||
* explicitly defined one or a previously generated one from scanning
|
||||
* @return whether the definitions are considered as compatible, with the
|
||||
* new definition to be skipped in favor of the existing definition
|
||||
*/
|
||||
protected boolean isCompatible(BeanDefinition newDefinition, BeanDefinition existingDefinition) {
|
||||
return (!(existingDefinition instanceof ScannedGenericBeanDefinition) || // explicitly registered overriding bean
|
||||
(newDefinition.getSource() != null && newDefinition.getSource().equals(existingDefinition.getSource())) || // scanned same file twice
|
||||
newDefinition.equals(existingDefinition)); // scanned equivalent class twice
|
||||
protected boolean isCompatible(BeanDefinition newDef, BeanDefinition existingDef) {
|
||||
return (!(existingDef instanceof ScannedGenericBeanDefinition) || // explicitly registered overriding bean
|
||||
(newDef.getSource() != null && newDef.getSource().equals(existingDef.getSource())) || // scanned same file twice
|
||||
newDef.equals(existingDef)); // scanned equivalent class twice
|
||||
}
|
||||
|
||||
|
||||
|
||||
+11
-10
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -62,11 +62,13 @@ import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* A component provider that provides candidate components from a base package. Can
|
||||
* use {@link CandidateComponentsIndex the index} if it is available of scans the
|
||||
* classpath otherwise. Candidate components are identified by applying exclude and
|
||||
* include filters. {@link AnnotationTypeFilter}, {@link AssignableTypeFilter} include
|
||||
* filters on an annotation/superclass that are annotated with {@link Indexed} are
|
||||
* A component provider that scans for candidate components starting from a
|
||||
* specified base package. Can use the {@linkplain CandidateComponentsIndex component
|
||||
* index}, if it is available, and scans the classpath otherwise.
|
||||
*
|
||||
* <p>Candidate components are identified by applying exclude and include filters.
|
||||
* {@link AnnotationTypeFilter} and {@link AssignableTypeFilter} include filters
|
||||
* for an annotation/target-type that is annotated with {@link Indexed} are
|
||||
* supported: if any other include filter is specified, the index is ignored and
|
||||
* classpath scanning is used instead.
|
||||
*
|
||||
@@ -304,7 +306,7 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
|
||||
|
||||
|
||||
/**
|
||||
* Scan the class path for candidate components.
|
||||
* Scan the component index or class path for candidate components.
|
||||
* @param basePackage the package to check for annotated classes
|
||||
* @return a corresponding Set of autodetected bean definitions
|
||||
*/
|
||||
@@ -318,7 +320,7 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the index can be used by this instance.
|
||||
* Determine if the component index can be used by this instance.
|
||||
* @return {@code true} if the index is available and the configuration of this
|
||||
* instance is supported by it, {@code false} otherwise
|
||||
* @since 5.0
|
||||
@@ -454,8 +456,7 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new BeanDefinitionStoreException(
|
||||
"Failed to read candidate component class: " + resource, ex);
|
||||
throw new BeanDefinitionStoreException("Failed to read candidate component class: " + resource, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-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.
|
||||
@@ -402,8 +402,8 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
|
||||
return;
|
||||
}
|
||||
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
|
||||
if (webServiceRefClass != null && bridgedMethod.isAnnotationPresent(webServiceRefClass)) {
|
||||
if (webServiceRefClass != null && bridgedMethod.isAnnotationPresent(webServiceRefClass)) {
|
||||
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
|
||||
if (Modifier.isStatic(method.getModifiers())) {
|
||||
throw new IllegalStateException("@WebServiceRef annotation is not supported on static methods");
|
||||
}
|
||||
@@ -413,7 +413,9 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
|
||||
currElements.add(new WebServiceRefElement(method, bridgedMethod, pd));
|
||||
}
|
||||
else if (ejbClass != null && bridgedMethod.isAnnotationPresent(ejbClass)) {
|
||||
}
|
||||
else if (ejbClass != null && bridgedMethod.isAnnotationPresent(ejbClass)) {
|
||||
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
|
||||
if (Modifier.isStatic(method.getModifiers())) {
|
||||
throw new IllegalStateException("@EJB annotation is not supported on static methods");
|
||||
}
|
||||
@@ -423,7 +425,9 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
|
||||
currElements.add(new EjbRefElement(method, bridgedMethod, pd));
|
||||
}
|
||||
else if (bridgedMethod.isAnnotationPresent(Resource.class)) {
|
||||
}
|
||||
else if (bridgedMethod.isAnnotationPresent(Resource.class)) {
|
||||
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
|
||||
if (Modifier.isStatic(method.getModifiers())) {
|
||||
throw new IllegalStateException("@Resource annotation is not supported on static methods");
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -104,8 +104,8 @@ import org.springframework.stereotype.Component;
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>{@code @Configuration} classes may not only be bootstrapped using
|
||||
* component scanning, but may also themselves <em>configure</em> component scanning using
|
||||
* <p>{@code @Configuration} classes may not only be bootstrapped using component
|
||||
* scanning, but may also themselves <em>configure</em> component scanning using
|
||||
* the {@link ComponentScan @ComponentScan} annotation:
|
||||
*
|
||||
* <pre class="code">
|
||||
|
||||
+12
-15
@@ -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.
|
||||
@@ -72,7 +72,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");
|
||||
@@ -86,10 +85,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);
|
||||
@@ -99,7 +98,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");
|
||||
@@ -113,10 +111,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);
|
||||
@@ -126,7 +124,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");
|
||||
@@ -148,12 +145,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;
|
||||
}
|
||||
|
||||
@@ -163,7 +160,7 @@ final class ConfigurationClass {
|
||||
* @since 3.1.1
|
||||
* @see #getImportedBy()
|
||||
*/
|
||||
public boolean isImported() {
|
||||
boolean isImported() {
|
||||
return !this.importedBy.isEmpty();
|
||||
}
|
||||
|
||||
@@ -197,6 +194,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);
|
||||
}
|
||||
@@ -205,10 +206,6 @@ final class ConfigurationClass {
|
||||
return this.importBeanDefinitionRegistrars;
|
||||
}
|
||||
|
||||
Map<String, Class<? extends BeanDefinitionReader>> getImportedResources() {
|
||||
return this.importedResources;
|
||||
}
|
||||
|
||||
void validate(ProblemReporter problemReporter) {
|
||||
// A configuration class may not be final (CGLIB limitation) unless it declares proxyBeanMethods=false
|
||||
Map<String, Object> attributes = this.metadata.getAnnotationAttributes(Configuration.class.getName());
|
||||
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -424,6 +424,7 @@ class ConfigurationClassBeanDefinitionReader {
|
||||
|
||||
public ConfigurationClassBeanDefinition(RootBeanDefinition original,
|
||||
ConfigurationClass configClass, MethodMetadata beanMethodMetadata, String derivedBeanName) {
|
||||
|
||||
super(original);
|
||||
this.annotationMetadata = configClass.getMetadata();
|
||||
this.factoryMethodMetadata = beanMethodMetadata;
|
||||
|
||||
+5
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -409,11 +409,14 @@ class ConfigurationClassParser {
|
||||
this.metadataReaderFactory.getMetadataReader(original.getClassName()).getAnnotationMetadata();
|
||||
Set<MethodMetadata> asmMethods = asm.getAnnotatedMethods(Bean.class.getName());
|
||||
if (asmMethods.size() >= beanMethods.size()) {
|
||||
Set<MethodMetadata> candidateMethods = new LinkedHashSet<>(beanMethods);
|
||||
Set<MethodMetadata> selectedMethods = new LinkedHashSet<>(asmMethods.size());
|
||||
for (MethodMetadata asmMethod : asmMethods) {
|
||||
for (MethodMetadata beanMethod : beanMethods) {
|
||||
for (Iterator<MethodMetadata> it = candidateMethods.iterator(); it.hasNext();) {
|
||||
MethodMetadata beanMethod = it.next();
|
||||
if (beanMethod.getMethodName().equals(asmMethod.getMethodName())) {
|
||||
selectedMethods.add(beanMethod);
|
||||
it.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,8 +21,8 @@ import org.springframework.instrument.classloading.LoadTimeWeaver;
|
||||
/**
|
||||
* Interface to be implemented by
|
||||
* {@link org.springframework.context.annotation.Configuration @Configuration}
|
||||
* classes annotated with {@link EnableLoadTimeWeaving @EnableLoadTimeWeaving} that wish to
|
||||
* customize the {@link LoadTimeWeaver} instance to be used.
|
||||
* classes annotated with {@link EnableLoadTimeWeaving @EnableLoadTimeWeaving}
|
||||
* that wish to customize the {@link LoadTimeWeaver} instance to be used.
|
||||
*
|
||||
* <p>See {@link org.springframework.scheduling.annotation.EnableAsync @EnableAsync}
|
||||
* for usage examples and information on how a default {@code LoadTimeWeaver}
|
||||
@@ -36,9 +36,9 @@ import org.springframework.instrument.classloading.LoadTimeWeaver;
|
||||
public interface LoadTimeWeavingConfigurer {
|
||||
|
||||
/**
|
||||
* Create, configure and return the {@code LoadTimeWeaver} instance to be used. Note
|
||||
* that it is unnecessary to annotate this method with {@code @Bean}, because the
|
||||
* object returned will automatically be registered as a bean by
|
||||
* Create, configure and return the {@code LoadTimeWeaver} instance to be used.
|
||||
* Note that it is unnecessary to annotate this method with {@code @Bean}
|
||||
* because the object returned will automatically be registered as a bean by
|
||||
* {@link LoadTimeWeavingConfiguration#loadTimeWeaver()}
|
||||
*/
|
||||
LoadTimeWeaver getLoadTimeWeaver();
|
||||
|
||||
+9
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -274,7 +274,7 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
|
||||
handleAsyncError(ex);
|
||||
}
|
||||
else if (event != null) {
|
||||
publishEvent(event);
|
||||
publishEvents(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -367,7 +367,7 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
|
||||
* Return the target bean instance to use.
|
||||
*/
|
||||
protected Object getTargetBean() {
|
||||
Assert.notNull(this.applicationContext, "ApplicationContext must no be null");
|
||||
Assert.notNull(this.applicationContext, "ApplicationContext must not be null");
|
||||
return this.applicationContext.getBean(this.beanName);
|
||||
}
|
||||
|
||||
@@ -468,6 +468,9 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inner class to avoid a hard dependency on the Reactive Streams API at runtime.
|
||||
*/
|
||||
private class ReactiveResultHandler {
|
||||
|
||||
public boolean subscribeToPublisher(Object result) {
|
||||
@@ -481,6 +484,9 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reactive Streams Subscriber for publishing follow-up events.
|
||||
*/
|
||||
private class EventPublicationSubscriber implements Subscriber<Object> {
|
||||
|
||||
@Override
|
||||
|
||||
+7
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -79,10 +79,11 @@ public class SimpleApplicationEventMulticaster extends AbstractApplicationEventM
|
||||
* to invoke each listener with.
|
||||
* <p>Default is equivalent to {@link org.springframework.core.task.SyncTaskExecutor},
|
||||
* executing all listeners synchronously in the calling thread.
|
||||
* <p>Consider specifying an asynchronous task executor here to not block the
|
||||
* caller until all listeners have been executed. However, note that asynchronous
|
||||
* execution will not participate in the caller's thread context (class loader,
|
||||
* transaction association) unless the TaskExecutor explicitly supports this.
|
||||
* <p>Consider specifying an asynchronous task executor here to not block the caller
|
||||
* until all listeners have been executed. However, note that asynchronous execution
|
||||
* will not participate in the caller's thread context (class loader, transaction context)
|
||||
* unless the TaskExecutor explicitly supports this.
|
||||
* @since 2.0
|
||||
* @see org.springframework.core.task.SyncTaskExecutor
|
||||
* @see org.springframework.core.task.SimpleAsyncTaskExecutor
|
||||
*/
|
||||
@@ -92,6 +93,7 @@ public class SimpleApplicationEventMulticaster extends AbstractApplicationEventM
|
||||
|
||||
/**
|
||||
* Return the current task executor for this multicaster.
|
||||
* @since 2.0
|
||||
*/
|
||||
@Nullable
|
||||
protected Executor getTaskExecutor() {
|
||||
|
||||
+43
-28
@@ -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.
|
||||
@@ -137,31 +137,40 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
implements ConfigurableApplicationContext {
|
||||
|
||||
/**
|
||||
* Name of the MessageSource bean in the factory.
|
||||
* The name of the {@link MessageSource} bean in the context.
|
||||
* If none is supplied, message resolution is delegated to the parent.
|
||||
* @see MessageSource
|
||||
* @see org.springframework.context.MessageSource
|
||||
* @see org.springframework.context.support.ResourceBundleMessageSource
|
||||
* @see org.springframework.context.support.ReloadableResourceBundleMessageSource
|
||||
* @see #getMessage(MessageSourceResolvable, Locale)
|
||||
*/
|
||||
public static final String MESSAGE_SOURCE_BEAN_NAME = "messageSource";
|
||||
|
||||
/**
|
||||
* Name of the LifecycleProcessor bean in the factory.
|
||||
* If none is supplied, a DefaultLifecycleProcessor is used.
|
||||
* @see org.springframework.context.LifecycleProcessor
|
||||
* @see org.springframework.context.support.DefaultLifecycleProcessor
|
||||
*/
|
||||
public static final String LIFECYCLE_PROCESSOR_BEAN_NAME = "lifecycleProcessor";
|
||||
|
||||
/**
|
||||
* Name of the ApplicationEventMulticaster bean in the factory.
|
||||
* If none is supplied, a default SimpleApplicationEventMulticaster is used.
|
||||
* The name of the {@link ApplicationEventMulticaster} bean in the context.
|
||||
* If none is supplied, a {@link SimpleApplicationEventMulticaster} is used.
|
||||
* @see org.springframework.context.event.ApplicationEventMulticaster
|
||||
* @see org.springframework.context.event.SimpleApplicationEventMulticaster
|
||||
* @see #publishEvent(ApplicationEvent)
|
||||
* @see #addApplicationListener(ApplicationListener)
|
||||
*/
|
||||
public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
|
||||
|
||||
/**
|
||||
* Boolean flag controlled by a {@code spring.spel.ignore} system property that instructs Spring to
|
||||
* ignore SpEL, i.e. to not initialize the SpEL infrastructure.
|
||||
* 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";
|
||||
|
||||
|
||||
/**
|
||||
* Boolean flag controlled by a {@code spring.spel.ignore} system property that
|
||||
* instructs Spring to ignore SpEL, i.e. to not initialize the SpEL infrastructure.
|
||||
* <p>The default is "false".
|
||||
*/
|
||||
private static final boolean shouldIgnoreSpel = SpringProperties.getFlag("spring.spel.ignore");
|
||||
@@ -203,7 +212,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
/** Flag that indicates whether this context has been closed already. */
|
||||
private final AtomicBoolean closed = new AtomicBoolean();
|
||||
|
||||
/** Synchronization monitor for the "refresh" and "destroy". */
|
||||
/** Synchronization monitor for "refresh" and "close". */
|
||||
private final Object startupShutdownMonitor = new Object();
|
||||
|
||||
/** Reference to the JVM shutdown hook, if registered. */
|
||||
@@ -211,7 +220,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
private Thread shutdownHook;
|
||||
|
||||
/** ResourcePatternResolver used by this context. */
|
||||
private ResourcePatternResolver resourcePatternResolver;
|
||||
private final ResourcePatternResolver resourcePatternResolver;
|
||||
|
||||
/** LifecycleProcessor for managing the lifecycle of beans within this context. */
|
||||
@Nullable
|
||||
@@ -447,7 +456,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
|
||||
@Override
|
||||
public void setApplicationStartup(ApplicationStartup applicationStartup) {
|
||||
Assert.notNull(applicationStartup, "applicationStartup should not be null");
|
||||
Assert.notNull(applicationStartup, "ApplicationStartup must not be null");
|
||||
this.applicationStartup = applicationStartup;
|
||||
}
|
||||
|
||||
@@ -562,7 +571,6 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
|
||||
// Invoke factory processors registered as beans in the context.
|
||||
invokeBeanFactoryPostProcessors(beanFactory);
|
||||
|
||||
// Register bean processors that intercept bean creation.
|
||||
registerBeanPostProcessors(beanFactory);
|
||||
beanPostProcess.end();
|
||||
@@ -749,7 +757,8 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
|
||||
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
|
||||
// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
|
||||
if (!NativeDetector.inNativeImage() && beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
|
||||
if (!NativeDetector.inNativeImage() && beanFactory.getTempClassLoader() == null &&
|
||||
beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
|
||||
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
|
||||
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
|
||||
}
|
||||
@@ -765,8 +774,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();
|
||||
@@ -798,8 +808,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() {
|
||||
@@ -822,15 +833,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 + "]");
|
||||
}
|
||||
@@ -1082,6 +1094,9 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
// Let subclasses do some final clean-up if they wish...
|
||||
onClose();
|
||||
|
||||
// Reset common introspection caches to avoid class reference leaks.
|
||||
resetCommonCaches();
|
||||
|
||||
// Reset local application listeners to pre-refresh state.
|
||||
if (this.earlyApplicationListeners != null) {
|
||||
this.applicationListeners.clear();
|
||||
|
||||
+8
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,6 +20,7 @@ import java.io.IOException;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionDocumentReader;
|
||||
import org.springframework.beans.factory.xml.ResourceEntityResolver;
|
||||
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
@@ -84,7 +85,7 @@ public abstract class AbstractXmlApplicationContext extends AbstractRefreshableC
|
||||
|
||||
// Configure the bean definition reader with this context's
|
||||
// resource loading environment.
|
||||
beanDefinitionReader.setEnvironment(this.getEnvironment());
|
||||
beanDefinitionReader.setEnvironment(getEnvironment());
|
||||
beanDefinitionReader.setResourceLoader(this);
|
||||
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
|
||||
|
||||
@@ -95,12 +96,13 @@ public abstract class AbstractXmlApplicationContext extends AbstractRefreshableC
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the bean definition reader used for loading the bean
|
||||
* definitions of this context. Default implementation is empty.
|
||||
* Initialize the bean definition reader used for loading the bean definitions
|
||||
* of this context. The default implementation sets the validating flag.
|
||||
* <p>Can be overridden in subclasses, e.g. for turning off XML validation
|
||||
* or using a different XmlBeanDefinitionParser implementation.
|
||||
* or using a different {@link BeanDefinitionDocumentReader} implementation.
|
||||
* @param reader the bean definition reader used by this context
|
||||
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader#setDocumentReaderClass
|
||||
* @see XmlBeanDefinitionReader#setValidating
|
||||
* @see XmlBeanDefinitionReader#setDocumentReaderClass
|
||||
*/
|
||||
protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) {
|
||||
reader.setValidating(this.validating);
|
||||
|
||||
+26
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,6 +21,8 @@ import java.security.ProtectionDomain;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.DecoratingClassLoader;
|
||||
import org.springframework.core.OverridingClassLoader;
|
||||
import org.springframework.core.SmartClassLoader;
|
||||
@@ -45,15 +47,27 @@ class ContextTypeMatchClassLoader extends DecoratingClassLoader implements Smart
|
||||
}
|
||||
|
||||
|
||||
private static Method findLoadedClassMethod;
|
||||
@Nullable
|
||||
private static final Method findLoadedClassMethod;
|
||||
|
||||
static {
|
||||
// Try to enable findLoadedClass optimization which allows us to selectively
|
||||
// override classes that have not been loaded yet. If not accessible, we will
|
||||
// always override requested classes, even when the classes have been loaded
|
||||
// by the parent ClassLoader already and cannot be transformed anymore anyway.
|
||||
Method method;
|
||||
try {
|
||||
findLoadedClassMethod = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
|
||||
method = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
|
||||
ReflectionUtils.makeAccessible(method);
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
throw new IllegalStateException("Invalid [java.lang.ClassLoader] class: no 'findLoadedClass' method defined!");
|
||||
catch (Throwable ex) {
|
||||
// Typically a JDK 9+ InaccessibleObjectException...
|
||||
// Avoid through JVM startup with --add-opens=java.base/java.lang=ALL-UNNAMED
|
||||
method = null;
|
||||
LogFactory.getLog(ContextTypeMatchClassLoader.class).debug(
|
||||
"ClassLoader.findLoadedClass not accessible -> will always override requested class", ex);
|
||||
}
|
||||
findLoadedClassMethod = method;
|
||||
}
|
||||
|
||||
|
||||
@@ -96,13 +110,14 @@ class ContextTypeMatchClassLoader extends DecoratingClassLoader implements Smart
|
||||
if (isExcluded(className) || ContextTypeMatchClassLoader.this.isExcluded(className)) {
|
||||
return false;
|
||||
}
|
||||
ReflectionUtils.makeAccessible(findLoadedClassMethod);
|
||||
ClassLoader parent = getParent();
|
||||
while (parent != null) {
|
||||
if (ReflectionUtils.invokeMethod(findLoadedClassMethod, parent, className) != null) {
|
||||
return false;
|
||||
if (findLoadedClassMethod != null) {
|
||||
ClassLoader parent = getParent();
|
||||
while (parent != null) {
|
||||
if (ReflectionUtils.invokeMethod(findLoadedClassMethod, parent, className) != null) {
|
||||
return false;
|
||||
}
|
||||
parent = parent.getParent();
|
||||
}
|
||||
parent = parent.getParent();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
+5
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -64,9 +64,10 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
|
||||
|
||||
|
||||
/**
|
||||
* Specify the maximum time allotted in milliseconds for the shutdown of
|
||||
* any phase (group of SmartLifecycle beans with the same 'phase' value).
|
||||
* <p>The default value is 30 seconds.
|
||||
* Specify the maximum time allotted in milliseconds for the shutdown of any
|
||||
* phase (group of {@link SmartLifecycle} beans with the same 'phase' value).
|
||||
* <p>The default value is 30000 milliseconds (30 seconds).
|
||||
* @see SmartLifecycle#getPhase()
|
||||
*/
|
||||
public void setTimeoutPerShutdownPhase(long timeoutPerShutdownPhase) {
|
||||
this.timeoutPerShutdownPhase = timeoutPerShutdownPhase;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user