mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5ea8ba1b96 |
@@ -1,20 +0,0 @@
|
||||
name: Await HTTP Resource
|
||||
description: Waits for an HTTP resource to be available (a HEAD request succeeds)
|
||||
inputs:
|
||||
url:
|
||||
description: 'The URL of the resource to await'
|
||||
required: true
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Await HTTP resource
|
||||
shell: bash
|
||||
run: |
|
||||
url=${{ inputs.url }}
|
||||
echo "Waiting for $url"
|
||||
until curl --fail --head --silent ${{ inputs.url }} > /dev/null
|
||||
do
|
||||
echo "."
|
||||
sleep 60
|
||||
done
|
||||
echo "$url is available"
|
||||
@@ -1,56 +0,0 @@
|
||||
name: 'Build'
|
||||
description: 'Builds the project, optionally publishing it to a local deployment repository'
|
||||
inputs:
|
||||
java-version:
|
||||
required: false
|
||||
default: '8'
|
||||
description: 'The Java version to compile and test with'
|
||||
java-early-access:
|
||||
required: false
|
||||
default: 'false'
|
||||
description: 'Whether the Java version is in early access'
|
||||
java-toolchain:
|
||||
required: false
|
||||
default: 'false'
|
||||
description: 'Whether a Java toolchain should be used'
|
||||
publish:
|
||||
required: false
|
||||
default: 'false'
|
||||
description: 'Whether to publish artifacts ready for deployment to Artifactory'
|
||||
develocity-access-key:
|
||||
required: false
|
||||
description: 'The access key for authentication with ge.spring.io'
|
||||
outputs:
|
||||
build-scan-url:
|
||||
description: 'The URL, if any, of the build scan produced by the build'
|
||||
value: ${{ (inputs.publish == 'true' && steps.publish.outputs.build-scan-url) || steps.build.outputs.build-scan-url }}
|
||||
version:
|
||||
description: 'The version that was built'
|
||||
value: ${{ steps.read-version.outputs.version }}
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Prepare Gradle Build
|
||||
uses: ./.github/actions/prepare-gradle-build
|
||||
with:
|
||||
develocity-access-key: ${{ inputs.develocity-access-key }}
|
||||
java-version: ${{ inputs.java-version }}
|
||||
java-early-access: ${{ inputs.java-early-access }}
|
||||
java-toolchain: ${{ inputs.java-toolchain }}
|
||||
- name: Build
|
||||
id: build
|
||||
if: ${{ inputs.publish == 'false' }}
|
||||
shell: bash
|
||||
run: ./gradlew check
|
||||
- name: Publish
|
||||
id: publish
|
||||
if: ${{ inputs.publish == 'true' }}
|
||||
shell: bash
|
||||
run: ./gradlew -PdeploymentRepository=$(pwd)/deployment-repository build publishAllPublicationsToDeploymentRepository
|
||||
- name: Read Version From gradle.properties
|
||||
id: read-version
|
||||
shell: bash
|
||||
run: |
|
||||
version=$(sed -n 's/version=\(.*\)/\1/p' gradle.properties)
|
||||
echo "Version is $version"
|
||||
echo "version=$version" >> $GITHUB_OUTPUT
|
||||
@@ -1,23 +0,0 @@
|
||||
name: Create GitHub Release
|
||||
description: Create the release on GitHub with a changelog
|
||||
inputs:
|
||||
milestone:
|
||||
description: Name of the GitHub milestone for which a release will be created
|
||||
required: true
|
||||
token:
|
||||
description: Token to use for authentication with GitHub
|
||||
required: true
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Generate Changelog
|
||||
uses: spring-io/github-changelog-generator@185319ad7eaa75b0e8e72e4b6db19c8b2cb8c4c1 #v0.0.11
|
||||
with:
|
||||
milestone: ${{ inputs.milestone }}
|
||||
token: ${{ inputs.token }}
|
||||
config-file: .github/actions/create-github-release/changelog-generator.yml
|
||||
- name: Create GitHub Release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ inputs.token }}
|
||||
shell: bash
|
||||
run: gh release create ${{ format('v{0}', inputs.milestone) }} --notes-file changelog.md
|
||||
@@ -1,49 +0,0 @@
|
||||
name: 'Prepare Gradle Build'
|
||||
description: 'Prepares a Gradle build. Sets up Java and Gradle and configures Gradle properties'
|
||||
inputs:
|
||||
java-version:
|
||||
required: false
|
||||
default: '8'
|
||||
description: 'The Java version to use for the build'
|
||||
java-early-access:
|
||||
required: false
|
||||
default: 'false'
|
||||
description: 'Whether the Java version is in early access'
|
||||
java-toolchain:
|
||||
required: false
|
||||
default: 'false'
|
||||
description: 'Whether a Java toolchain should be used'
|
||||
develocity-access-key:
|
||||
required: false
|
||||
description: 'The access key for authentication with ge.spring.io'
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Set Up Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: ${{ inputs.java-early-access == 'true' && 'temurin' || 'liberica' }}
|
||||
java-version: |
|
||||
${{ inputs.java-early-access == 'true' && format('{0}-ea', inputs.java-version) || inputs.java-version }}
|
||||
${{ inputs.java-toolchain == 'true' && '8' || '' }}
|
||||
- name: Set Up Gradle
|
||||
uses: gradle/actions/setup-gradle@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3.5.0
|
||||
with:
|
||||
cache-read-only: false
|
||||
develocity-access-key: ${{ inputs.develocity-access-key }}
|
||||
- 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: ${{ inputs.java-toolchain == 'true' }}
|
||||
shell: bash
|
||||
run: |
|
||||
echo toolchainVersion=${{ inputs.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', inputs.java-version) }} >> $HOME/.gradle/gradle.properties
|
||||
@@ -1,33 +0,0 @@
|
||||
name: Send Notification
|
||||
description: Sends a Google Chat message as a notification of the job's outcome
|
||||
inputs:
|
||||
webhook-url:
|
||||
description: 'Google Chat Webhook URL'
|
||||
required: true
|
||||
status:
|
||||
description: 'Status of the job'
|
||||
required: true
|
||||
build-scan-url:
|
||||
description: 'URL of the build scan to include in the notification'
|
||||
run-name:
|
||||
description: 'Name of the run to include in the notification'
|
||||
default: ${{ format('{0} {1}', github.ref_name, github.job) }}
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- shell: bash
|
||||
run: |
|
||||
echo "BUILD_SCAN=${{ inputs.build-scan-url == '' && ' [build scan unavailable]' || format(' [<{0}|Build Scan>]', inputs.build-scan-url) }}" >> "$GITHUB_ENV"
|
||||
echo "RUN_URL=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> "$GITHUB_ENV"
|
||||
- shell: bash
|
||||
if: ${{ inputs.status == 'success' }}
|
||||
run: |
|
||||
curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<${{ env.RUN_URL }}|${{ inputs.run-name }}> was successful ${{ env.BUILD_SCAN }}"}' || true
|
||||
- shell: bash
|
||||
if: ${{ inputs.status == 'failure' }}
|
||||
run: |
|
||||
curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<users/all> *<${{ env.RUN_URL }}|${{ inputs.run-name }}> failed* ${{ env.BUILD_SCAN }}"}' || true
|
||||
- shell: bash
|
||||
if: ${{ inputs.status == 'cancelled' }}
|
||||
run: |
|
||||
curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<${{ env.RUN_URL }}|${{ inputs.run-name }}> was cancelled"}' || true
|
||||
@@ -1,43 +0,0 @@
|
||||
name: Sync to Maven Central
|
||||
description: Syncs a release to Maven Central and waits for it to be available for use
|
||||
inputs:
|
||||
jfrog-cli-config-token:
|
||||
description: 'Config token for the JFrog CLI'
|
||||
required: true
|
||||
spring-framework-version:
|
||||
description: 'The version of Spring Framework that is being synced to Central'
|
||||
required: true
|
||||
ossrh-s01-token-username:
|
||||
description: 'Username for authentication with s01.oss.sonatype.org'
|
||||
required: true
|
||||
ossrh-s01-token-password:
|
||||
description: 'Password for authentication with s01.oss.sonatype.org'
|
||||
required: true
|
||||
ossrh-s01-staging-profile:
|
||||
description: 'Staging profile to use when syncing to Central'
|
||||
required: true
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Set Up JFrog CLI
|
||||
uses: jfrog/setup-jfrog-cli@105617d23456a69a92485207c4f28ae12297581d # v4.2.1
|
||||
env:
|
||||
JF_ENV_SPRING: ${{ inputs.jfrog-cli-config-token }}
|
||||
- name: Download Release Artifacts
|
||||
shell: bash
|
||||
run: jf rt download --spec ${{ format('{0}/artifacts.spec', github.action_path) }} --spec-vars 'buildName=${{ format('spring-framework-{0}', inputs.spring-framework-version) }};buildNumber=${{ github.run_number }}'
|
||||
- name: Sync
|
||||
uses: spring-io/nexus-sync-action@42477a2230a2f694f9eaa4643fa9e76b99b7ab84 # v0.0.1
|
||||
with:
|
||||
username: ${{ inputs.ossrh-s01-token-username }}
|
||||
password: ${{ inputs.ossrh-s01-token-password }}
|
||||
staging-profile-name: ${{ inputs.ossrh-s01-staging-profile }}
|
||||
create: true
|
||||
upload: true
|
||||
close: true
|
||||
release: true
|
||||
generate-checksums: true
|
||||
- name: Await
|
||||
uses: ./.github/actions/await-http-resource
|
||||
with:
|
||||
url: ${{ format('https://repo.maven.apache.org/maven2/org/springframework/spring-context/{0}/spring-context-{0}.jar', inputs.spring-framework-version) }}
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"aql": {
|
||||
"items.find": {
|
||||
"$and": [
|
||||
{
|
||||
"@build.name": "${buildName}",
|
||||
"@build.number": "${buildNumber}",
|
||||
"path": {
|
||||
"$nmatch": "org/springframework/spring-*.zip"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"target": "nexus/"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
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"
|
||||
@@ -1,59 +0,0 @@
|
||||
name: Build and Deploy Snapshot
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 5.3.x
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
jobs:
|
||||
build-and-deploy-snapshot:
|
||||
name: Build and Deploy Snapshot
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
if: ${{ github.repository == 'spring-projects/spring-framework' }}
|
||||
steps:
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@v4
|
||||
- name: Build and Publish
|
||||
id: build-and-publish
|
||||
uses: ./.github/actions/build
|
||||
with:
|
||||
develocity-access-key: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||
publish: true
|
||||
- name: Deploy
|
||||
uses: spring-io/artifactory-deploy-action@26bbe925a75f4f863e1e529e85be2d0093cac116 # v0.0.1
|
||||
with:
|
||||
uri: 'https://repo.spring.io'
|
||||
username: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
password: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
build-name: 'spring-framework-5.3.x'
|
||||
repository: 'libs-snapshot-local'
|
||||
folder: 'deployment-repository'
|
||||
signing-key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
signing-passphrase: ${{ secrets.GPG_PASSPHRASE }}
|
||||
artifact-properties: |
|
||||
/**/spring-*.zip::zip.name=spring-framework,zip.deployed=false
|
||||
/**/spring-*-docs.zip::zip.type=docs
|
||||
/**/spring-*-dist.zip::zip.type=dist
|
||||
/**/spring-*-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-and-publish.outputs.build-scan-url }}
|
||||
run-name: ${{ format('{0} | Linux | Java 8', github.ref_name) }}
|
||||
outputs:
|
||||
version: ${{ steps.build-and-publish.outputs.version }}
|
||||
verify:
|
||||
name: Verify
|
||||
needs: build-and-deploy-snapshot
|
||||
uses: ./.github/workflows/verify.yml
|
||||
secrets:
|
||||
google-chat-webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
|
||||
repository-password: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
repository-username: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
with:
|
||||
version: ${{ needs.build-and-deploy-snapshot.outputs.version }}
|
||||
@@ -1,55 +0,0 @@
|
||||
name: CI
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 5.3.x
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
jobs:
|
||||
ci:
|
||||
name: '${{ matrix.os.name}} | Java ${{ matrix.java.version}}'
|
||||
runs-on: ${{ matrix.os.id }}
|
||||
timeout-minutes: 60
|
||||
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
|
||||
steps:
|
||||
- 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: Build
|
||||
id: build
|
||||
uses: ./.github/actions/build
|
||||
with:
|
||||
java-version: ${{ matrix.java.version }}
|
||||
java-early-access: ${{ matrix.java.early-access || 'false' }}
|
||||
java-toolchain: ${{ matrix.java.toolchain }}
|
||||
develocity-access-key: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||
- 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) }}
|
||||
@@ -0,0 +1,13 @@
|
||||
name: "Validate Gradle Wrapper"
|
||||
on: [push, pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
validation:
|
||||
name: "Validation"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: gradle/wrapper-validation-action@v1
|
||||
@@ -1,95 +0,0 @@
|
||||
name: Release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v5.3.[0-9]+
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
jobs:
|
||||
build-and-stage-release:
|
||||
if: ${{ github.repository == 'spring-projects/spring-framework' }}
|
||||
name: Build and Stage Release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@v4
|
||||
- name: Build and Publish
|
||||
id: build-and-publish
|
||||
uses: ./.github/actions/build
|
||||
with:
|
||||
develocity-access-key: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||
publish: true
|
||||
- name: Stage Release
|
||||
uses: spring-io/artifactory-deploy-action@26bbe925a75f4f863e1e529e85be2d0093cac116 # v0.0.1
|
||||
with:
|
||||
uri: 'https://repo.spring.io'
|
||||
username: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
password: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
build-name: ${{ format('spring-framework-{0}', steps.build-and-publish.outputs.version)}}
|
||||
repository: 'libs-staging-local'
|
||||
folder: 'deployment-repository'
|
||||
signing-key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
signing-passphrase: ${{ secrets.GPG_PASSPHRASE }}
|
||||
artifact-properties: |
|
||||
/**/spring-*.zip::zip.name=spring-framework,zip.deployed=false
|
||||
/**/spring-*-docs.zip::zip.type=docs
|
||||
/**/spring-*-dist.zip::zip.type=dist
|
||||
/**/spring-*-schema.zip::zip.type=schema
|
||||
outputs:
|
||||
version: ${{ steps.build-and-publish.outputs.version }}
|
||||
verify:
|
||||
name: Verify
|
||||
needs: build-and-stage-release
|
||||
uses: ./.github/workflows/verify.yml
|
||||
with:
|
||||
staging: true
|
||||
version: ${{ needs.build-and-stage-release.outputs.version }}
|
||||
secrets:
|
||||
google-chat-webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
|
||||
repository-password: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
repository-username: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
sync-to-maven-central:
|
||||
name: Sync to Maven Central
|
||||
needs:
|
||||
- build-and-stage-release
|
||||
- verify
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Sync to Maven Central
|
||||
uses: ./.github/actions/sync-to-maven-central
|
||||
with:
|
||||
jfrog-cli-config-token: ${{ secrets.JF_ARTIFACTORY_SPRING }}
|
||||
ossrh-s01-staging-profile: ${{ secrets.OSSRH_S01_STAGING_PROFILE }}
|
||||
ossrh-s01-token-password: ${{ secrets.OSSRH_S01_TOKEN_PASSWORD }}
|
||||
ossrh-s01-token-username: ${{ secrets.OSSRH_S01_TOKEN_USERNAME }}
|
||||
spring-framework-version: ${{ needs.build-and-stage-release.outputs.version }}
|
||||
promote-release:
|
||||
name: Promote Release
|
||||
needs:
|
||||
- build-and-stage-release
|
||||
- sync-to-maven-central
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up JFrog CLI
|
||||
uses: jfrog/setup-jfrog-cli@105617d23456a69a92485207c4f28ae12297581d # v4.2.1
|
||||
env:
|
||||
JF_ENV_SPRING: ${{ secrets.JF_ARTIFACTORY_SPRING }}
|
||||
- name: Promote build
|
||||
run: jfrog rt build-promote ${{ format('spring-framework-{0}', needs.build-and-stage-release.outputs.version)}} ${{ github.run_number }} libs-release-local
|
||||
create-github-release:
|
||||
name: Create GitHub Release
|
||||
needs:
|
||||
- build-and-stage-release
|
||||
- promote-release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Create GitHub Release
|
||||
uses: ./.github/actions/create-github-release
|
||||
with:
|
||||
milestone: ${{ needs.build-and-stage-release.outputs.version }}
|
||||
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
@@ -1,11 +0,0 @@
|
||||
name: "Validate Gradle Wrapper"
|
||||
on: [push, pull_request]
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
validation:
|
||||
name: "Validate Gradle Wrapper"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: gradle/actions/wrapper-validation@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3.5.0
|
||||
@@ -1,71 +0,0 @@
|
||||
name: Verify
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
version:
|
||||
required: true
|
||||
type: string
|
||||
staging:
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
secrets:
|
||||
repository-username:
|
||||
required: false
|
||||
repository-password:
|
||||
required: false
|
||||
google-chat-webhook-url:
|
||||
required: true
|
||||
token:
|
||||
required: true
|
||||
jobs:
|
||||
verify:
|
||||
name: Verify
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check Out Release Verification Tests
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: spring-projects/spring-framework-release-verification
|
||||
ref: 'v0.0.2'
|
||||
token: ${{ secrets.token }}
|
||||
- name: Check Out Send Notification Action
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: spring-framework
|
||||
sparse-checkout: .github/actions/send-notification
|
||||
- name: Set Up Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'liberica'
|
||||
java-version: 8
|
||||
- name: Set Up Gradle
|
||||
uses: gradle/actions/setup-gradle@dbbdc275be76ac10734476cc723d82dfe7ec6eda # v3.4.2
|
||||
with:
|
||||
cache-read-only: false
|
||||
- name: Configure Gradle Properties
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p $HOME/.gradle
|
||||
echo 'org.gradle.daemon=false' >> $HOME/.gradle/gradle.properties
|
||||
- name: Run Release Verification Tests
|
||||
env:
|
||||
RVT_VERSION: ${{ inputs.version }}
|
||||
RVT_RELEASE_TYPE: oss
|
||||
RVT_STAGING: ${{ inputs.staging }}
|
||||
RVT_OSS_REPOSITORY_USERNAME: ${{ secrets.repository-username }}
|
||||
RVT_OSS_REPOSITORY_PASSWORD: ${{ secrets.repository-password }}
|
||||
run: ./gradlew spring-framework-release-verification-tests:test
|
||||
- name: Upload Build Reports on Failure
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: build-reports
|
||||
path: '**/build/reports/'
|
||||
- name: Send Notification
|
||||
uses: ./spring-framework/.github/actions/send-notification
|
||||
if: failure()
|
||||
with:
|
||||
webhook-url: ${{ secrets.google-chat-webhook-url }}
|
||||
status: ${{ job.status }}
|
||||
run-name: ${{ format('{0} | Verification | {1}', github.ref_name, inputs.version) }}
|
||||
+1
-5
@@ -24,11 +24,9 @@ buildSrc/build
|
||||
/spring-core/graalvm/build
|
||||
/spring-core/kotlin-coroutines/build
|
||||
/framework-bom/build
|
||||
/framework-docs/build
|
||||
/integration-tests/build
|
||||
/src/asciidoc/build
|
||||
target/
|
||||
/target/
|
||||
|
||||
# Eclipse artifacts, including WTP generated manifests
|
||||
.classpath
|
||||
@@ -46,6 +44,4 @@ atlassian-ide-plugin.xml
|
||||
.gradletasknamecache
|
||||
|
||||
# VS Code
|
||||
.vscode/
|
||||
|
||||
cached-antora-playbook.yml
|
||||
.vscode/
|
||||
@@ -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.382-librca
|
||||
java=8.0.345-librca
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# <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)
|
||||
# <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)
|
||||
|
||||
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".
|
||||
|
||||
|
||||
+35
-40
@@ -1,17 +1,17 @@
|
||||
plugins {
|
||||
id 'io.spring.dependency-management' version '1.0.11.RELEASE' apply false
|
||||
id 'io.spring.nohttp' version '0.0.10'
|
||||
id 'io.freefair.aspectj' version '6.2.0' apply false
|
||||
id "io.freefair.aspectj" version '6.2.0' apply false
|
||||
id 'org.jetbrains.dokka' version '1.6.10' apply false
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.5.32' apply false
|
||||
id 'org.jetbrains.kotlin.plugin.serialization' version "1.5.32" apply false
|
||||
id "org.jetbrains.kotlin.plugin.serialization" version "1.5.32" apply false
|
||||
id 'org.asciidoctor.jvm.convert' version '3.3.2'
|
||||
id 'org.asciidoctor.jvm.pdf' version '3.3.2'
|
||||
id 'org.unbroken-dome.xjc' version '2.0.0' apply false
|
||||
id 'com.github.ben-manes.versions' version '0.39.0'
|
||||
id 'com.github.johnrengelman.shadow' version '7.0.0' apply false
|
||||
id "org.unbroken-dome.xjc" version '2.0.0' apply false
|
||||
id "com.github.ben-manes.versions" version '0.39.0'
|
||||
id "com.github.johnrengelman.shadow" version '7.0.0' apply false
|
||||
id 'de.undercouch.download' version '4.1.2'
|
||||
id 'me.champeau.jmh' version "0.6.6" apply false
|
||||
id "me.champeau.jmh" version "0.6.6" apply false
|
||||
}
|
||||
|
||||
ext {
|
||||
@@ -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.112.Final"
|
||||
mavenBom "io.projectreactor:reactor-bom:2020.0.47"
|
||||
mavenBom "io.netty:netty-bom:4.1.82.Final"
|
||||
mavenBom "io.projectreactor:reactor-bom:2020.0.23"
|
||||
mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR13"
|
||||
mavenBom "io.rsocket:rsocket-bom:1.1.3"
|
||||
mavenBom "org.eclipse.jetty:jetty-bom:9.4.54.v20240208"
|
||||
mavenBom "org.eclipse.jetty:jetty-bom:9.4.48.v20220622"
|
||||
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.21.1') {
|
||||
dependencySet(group: 'org.apache.logging.log4j', version: '2.18.0') {
|
||||
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.8"
|
||||
dependency "io.smallrye.reactive:mutiny:1.9.0"
|
||||
dependency "io.projectreactor.tools:blockhound:1.0.8.RELEASE"
|
||||
dependency "io.reactivex.rxjava3:rxjava:3.1.5"
|
||||
dependency "io.smallrye.reactive:mutiny:1.7.0"
|
||||
dependency "io.projectreactor.tools:blockhound:1.0.6.RELEASE"
|
||||
|
||||
dependency "com.caucho:hessian:4.0.63"
|
||||
dependency "com.fasterxml:aalto-xml:1.3.1"
|
||||
@@ -92,11 +92,11 @@ configure(allprojects) { project ->
|
||||
entry 'jibx-run'
|
||||
}
|
||||
dependency "org.ogce:xpp3:1.1.6"
|
||||
dependency "org.yaml:snakeyaml:1.33"
|
||||
dependency "org.yaml:snakeyaml:1.30"
|
||||
|
||||
dependency "com.h2database:h2:2.1.214"
|
||||
dependency "com.github.ben-manes.caffeine:caffeine:2.9.3"
|
||||
dependency "com.github.librepdf:openpdf:1.3.33"
|
||||
dependency "com.github.librepdf:openpdf:1.3.29"
|
||||
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.82') {
|
||||
dependencySet(group: 'org.apache.tomcat', version: '9.0.65') {
|
||||
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.82') {
|
||||
dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.65') {
|
||||
entry 'tomcat-embed-core'
|
||||
entry 'tomcat-embed-websocket'
|
||||
}
|
||||
dependencySet(group: 'io.undertow', version: '2.2.29.Final') {
|
||||
dependencySet(group: 'io.undertow', version: '2.2.19.Final') {
|
||||
entry 'undertow-core'
|
||||
entry('undertow-servlet') {
|
||||
exclude group: "org.jboss.spec.javax.servlet", name: "jboss-servlet-api_4.0_spec"
|
||||
@@ -150,7 +150,7 @@ configure(allprojects) { project ->
|
||||
}
|
||||
}
|
||||
|
||||
dependency "org.eclipse.jetty:jetty-reactive-httpclient:1.1.13"
|
||||
dependency "org.eclipse.jetty:jetty-reactive-httpclient:1.1.12"
|
||||
dependency 'org.apache.httpcomponents.client5:httpclient5:5.1.3'
|
||||
dependency 'org.apache.httpcomponents.core5:httpcore5-reactive:5.1.3'
|
||||
dependency("org.apache.httpcomponents:httpclient:4.5.13") {
|
||||
@@ -191,14 +191,14 @@ configure(allprojects) { project ->
|
||||
dependency "org.junit.support:testng-engine:1.0.4"
|
||||
dependency "org.hamcrest:hamcrest:2.2"
|
||||
dependency "org.awaitility:awaitility:3.1.6"
|
||||
dependency "org.assertj:assertj-core:3.24.2"
|
||||
dependency "org.assertj:assertj-core:3.23.0"
|
||||
dependencySet(group: 'org.xmlunit', version: '2.9.0') {
|
||||
entry 'xmlunit-assertj'
|
||||
entry('xmlunit-matchers') {
|
||||
exclude group: "org.hamcrest", name: "hamcrest-core"
|
||||
}
|
||||
}
|
||||
dependencySet(group: 'org.mockito', version: '4.9.0') { // spring-beans tests fail with 4.10+
|
||||
dependencySet(group: 'org.mockito', version: '4.6.1') {
|
||||
entry('mockito-core') {
|
||||
exclude group: "org.hamcrest", name: "hamcrest-core"
|
||||
}
|
||||
@@ -206,10 +206,10 @@ configure(allprojects) { project ->
|
||||
}
|
||||
dependency "io.mockk:mockk:1.12.1"
|
||||
|
||||
dependency("net.sourceforge.htmlunit:htmlunit:2.70.0") {
|
||||
dependency("net.sourceforge.htmlunit:htmlunit:2.64.0") {
|
||||
exclude group: "commons-logging", name: "commons-logging"
|
||||
}
|
||||
dependency("org.seleniumhq.selenium:htmlunit-driver:2.70.0") {
|
||||
dependency("org.seleniumhq.selenium:htmlunit-driver:2.64.0") {
|
||||
exclude group: "commons-logging", name: "commons-logging"
|
||||
}
|
||||
dependency("org.seleniumhq.selenium:selenium-java:3.141.59") {
|
||||
@@ -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.12"
|
||||
dependency "org.eclipse.persistence:org.eclipse.persistence.jpa:2.7.10"
|
||||
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.12.7"
|
||||
toolVersion = "10.3.3"
|
||||
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.42")
|
||||
checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:0.0.31")
|
||||
}
|
||||
|
||||
ext.javadocLinks = [
|
||||
@@ -375,12 +375,11 @@ 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/",
|
||||
// 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/",
|
||||
"https://www.ehcache.org/apidocs/2.10.4/",
|
||||
"https://www.quartz-scheduler.org/api/2.3.0/",
|
||||
"https://fasterxml.github.io/jackson-core/javadoc/2.10/",
|
||||
"https://fasterxml.github.io/jackson-databind/javadoc/2.10/",
|
||||
"https://fasterxml.github.io/jackson-dataformat-xml/javadoc/2.10/",
|
||||
"https://hc.apache.org/httpcomponents-client-5.1.x/current/httpclient5/apidocs/",
|
||||
"https://projectreactor.io/docs/test/release/api/",
|
||||
"https://junit.org/junit4/javadoc/4.13.2/",
|
||||
@@ -389,8 +388,7 @@ 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/",
|
||||
// Temporarily commenting out R2DBC since javadoc on JDK 8 cannot access it.
|
||||
// "https://r2dbc.io/spec/0.8.5.RELEASE/api/",
|
||||
"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
|
||||
@@ -415,9 +413,11 @@ configure(rootProject) {
|
||||
|
||||
nohttp {
|
||||
source.exclude "**/test-output/**"
|
||||
source.exclude "spring-core/graalvm/build/**" // only available on `main`
|
||||
source.exclude "spring-core-test/build/**" // only available on `main`
|
||||
allowlistFile = project.file("src/nohttp/allowlist.lines")
|
||||
def rootPath = file(rootDir).toPath()
|
||||
def projectDirs = allprojects.collect { it.projectDir } + "${rootDir}/spring-core-test" + "${rootDir}/buildSrc" + "${rootDir}/framework-docs"
|
||||
def projectDirs = allprojects.collect { it.projectDir } + "${rootDir}/buildSrc"
|
||||
projectDirs.forEach { dir ->
|
||||
[ 'bin', 'build', 'out', '.settings' ]
|
||||
.collect { rootPath.relativize(new File(dir, it).toPath()) }
|
||||
@@ -428,10 +428,6 @@ configure(rootProject) {
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named("checkstyleNohttp").configure {
|
||||
maxHeapSize = "1g"
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
@@ -441,5 +437,4 @@ configure(rootProject) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
== Spring Framework Concourse pipeline
|
||||
|
||||
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].
|
||||
|
||||
=== Setting up your development environment
|
||||
|
||||
If you're part of the Spring Framework project on GitHub, you can get access to CI management features.
|
||||
First, you need to go to https://ci.spring.io and install the client CLI for your platform (see bottom right of the screen).
|
||||
|
||||
You can then login with the instance using:
|
||||
|
||||
[source]
|
||||
----
|
||||
$ fly -t spring login -n spring-framework -c https://ci.spring.io
|
||||
----
|
||||
|
||||
Once logged in, you should get something like:
|
||||
|
||||
[source]
|
||||
----
|
||||
$ fly ts
|
||||
name url team expiry
|
||||
spring https://ci.spring.io spring-framework Wed, 25 Mar 2020 17:45:26 UTC
|
||||
----
|
||||
|
||||
=== Pipeline configuration and structure
|
||||
|
||||
The build pipelines are described in `pipeline.yml` file.
|
||||
|
||||
This file is listing Concourse resources, i.e. build inputs and outputs such as container images, artifact repositories, source repositories, notification services, etc.
|
||||
|
||||
It also describes jobs (a job is a sequence of inputs, tasks and outputs); jobs are organized by groups.
|
||||
|
||||
The `pipeline.yml` definition contains `((parameters))` which are loaded from the `parameters.yml` file or from our https://docs.cloudfoundry.org/credhub/[credhub instance].
|
||||
|
||||
You'll find in this folder the following resources:
|
||||
|
||||
* `pipeline.yml` the build pipeline
|
||||
* `parameters.yml` the build parameters used for the pipeline
|
||||
* `images/` holds the container images definitions used in this pipeline
|
||||
* `scripts/` holds the build scripts that ship within the CI container images
|
||||
* `tasks` contains the task definitions used in the main `pipeline.yml`
|
||||
|
||||
=== Updating the build pipeline
|
||||
|
||||
Updating files on the repository is not enough to update the build pipeline, as changes need to be applied.
|
||||
|
||||
The pipeline can be deployed using the following command:
|
||||
|
||||
[source]
|
||||
----
|
||||
$ fly -t spring set-pipeline -p spring-framework-5.3.x -c ci/pipeline.yml -l ci/parameters.yml
|
||||
----
|
||||
|
||||
NOTE: This assumes that you have credhub integration configured with the appropriate secrets.
|
||||
-11
@@ -15,14 +15,3 @@ changelog:
|
||||
sort: "title"
|
||||
labels:
|
||||
- "type: dependency-upgrade"
|
||||
contributors:
|
||||
exclude:
|
||||
names:
|
||||
- "bclozel"
|
||||
- "jhoeller"
|
||||
- "poutsma"
|
||||
- "rstoyanchev"
|
||||
- "sbrannen"
|
||||
- "sdeleuze"
|
||||
- "simonbasle"
|
||||
- "snicoll"
|
||||
@@ -0,0 +1,10 @@
|
||||
logging:
|
||||
level:
|
||||
io.spring.concourse: DEBUG
|
||||
spring:
|
||||
main:
|
||||
banner-mode: off
|
||||
sonatype:
|
||||
exclude:
|
||||
- 'build-info\.json'
|
||||
- '.*\.zip'
|
||||
@@ -0,0 +1,21 @@
|
||||
== CI Images
|
||||
|
||||
These images are used by CI to run the actual builds.
|
||||
|
||||
To build the image locally run the following from this directory:
|
||||
|
||||
----
|
||||
$ docker build --no-cache -f <image-folder>/Dockerfile .
|
||||
----
|
||||
|
||||
For example
|
||||
|
||||
----
|
||||
$ docker build --no-cache -f spring-framework-ci-image/Dockerfile .
|
||||
----
|
||||
|
||||
To test run:
|
||||
|
||||
----
|
||||
$ docker run -it --entrypoint /bin/bash <SHA>
|
||||
----
|
||||
@@ -0,0 +1,11 @@
|
||||
FROM ubuntu:focal-20220826
|
||||
|
||||
ADD setup.sh /setup.sh
|
||||
ADD get-jdk-url.sh /get-jdk-url.sh
|
||||
RUN ./setup.sh java8
|
||||
|
||||
ENV JAVA_HOME /opt/openjdk/java8
|
||||
ENV JDK11 /opt/openjdk/java11
|
||||
ENV JDK17 /opt/openjdk/java17
|
||||
|
||||
ENV PATH $JAVA_HOME/bin:$PATH
|
||||
Executable
+20
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
case "$1" in
|
||||
java8)
|
||||
echo "https://github.com/bell-sw/Liberica/releases/download/8u345+1/bellsoft-jdk8u345+1-linux-amd64.tar.gz"
|
||||
;;
|
||||
java11)
|
||||
echo "https://github.com/bell-sw/Liberica/releases/download/11.0.16+8/bellsoft-jdk11.0.16+8-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"
|
||||
;;
|
||||
java18)
|
||||
echo "https://github.com/bell-sw/Liberica/releases/download/18.0.2+10/bellsoft-jdk18.0.2+10-linux-amd64.tar.gz"
|
||||
;;
|
||||
*)
|
||||
echo $"Unknown java version"
|
||||
exit 1
|
||||
esac
|
||||
Executable
+40
@@ -0,0 +1,40 @@
|
||||
#!/bin/bash
|
||||
set -ex
|
||||
|
||||
###########################################################
|
||||
# UTILS
|
||||
###########################################################
|
||||
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update
|
||||
apt-get install --no-install-recommends -y tzdata ca-certificates net-tools libxml2-utils git curl libudev1 libxml2-utils iptables iproute2 jq fontconfig
|
||||
ln -fs /usr/share/zoneinfo/UTC /etc/localtime
|
||||
dpkg-reconfigure --frontend noninteractive tzdata
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
curl https://raw.githubusercontent.com/spring-io/concourse-java-scripts/v0.0.4/concourse-java.sh > /opt/concourse-java.sh
|
||||
|
||||
###########################################################
|
||||
# JAVA
|
||||
###########################################################
|
||||
|
||||
mkdir -p /opt/openjdk
|
||||
pushd /opt/openjdk > /dev/null
|
||||
for jdk in java8 java11 java17
|
||||
do
|
||||
JDK_URL=$( /get-jdk-url.sh $jdk )
|
||||
mkdir $jdk
|
||||
pushd $jdk > /dev/null
|
||||
curl -L ${JDK_URL} | tar zx --strip-components=1
|
||||
test -f bin/java
|
||||
test -f bin/javac
|
||||
popd > /dev/null
|
||||
done
|
||||
popd
|
||||
|
||||
###########################################################
|
||||
# GRADLE ENTERPRISE
|
||||
###########################################################
|
||||
cd /
|
||||
mkdir ~/.gradle
|
||||
echo 'systemProp.user.name=concourse' > ~/.gradle/gradle.properties
|
||||
@@ -0,0 +1,14 @@
|
||||
github-repo: "https://github.com/spring-projects/spring-framework.git"
|
||||
github-repo-name: "spring-projects/spring-framework"
|
||||
sonatype-staging-profile: "org.springframework"
|
||||
docker-hub-organization: "springci"
|
||||
artifactory-server: "https://repo.spring.io"
|
||||
branch: "5.3.x"
|
||||
milestone: "5.3.x"
|
||||
build-name: "spring-framework"
|
||||
pipeline-name: "spring-framework"
|
||||
concourse-url: "https://ci.spring.io"
|
||||
registry-mirror-host: docker.repo.spring.io
|
||||
registry-mirror-username: ((artifactory-username))
|
||||
registry-mirror-password: ((artifactory-password))
|
||||
task-timeout: 1h00m
|
||||
+480
@@ -0,0 +1,480 @@
|
||||
anchors:
|
||||
git-repo-resource-source: &git-repo-resource-source
|
||||
uri: ((github-repo))
|
||||
username: ((github-username))
|
||||
password: ((github-ci-release-token))
|
||||
branch: ((branch))
|
||||
gradle-enterprise-task-params: &gradle-enterprise-task-params
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY: ((gradle_enterprise_secret_access_key))
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME: ((gradle_enterprise_cache_user.username))
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD: ((gradle_enterprise_cache_user.password))
|
||||
sonatype-task-params: &sonatype-task-params
|
||||
SONATYPE_USERNAME: ((sonatype-username))
|
||||
SONATYPE_PASSWORD: ((sonatype-password))
|
||||
SONATYPE_URL: ((sonatype-url))
|
||||
SONATYPE_STAGING_PROFILE: ((sonatype-staging-profile))
|
||||
artifactory-task-params: &artifactory-task-params
|
||||
ARTIFACTORY_SERVER: ((artifactory-server))
|
||||
ARTIFACTORY_USERNAME: ((artifactory-username))
|
||||
ARTIFACTORY_PASSWORD: ((artifactory-password))
|
||||
build-project-task-params: &build-project-task-params
|
||||
BRANCH: ((branch))
|
||||
<<: *gradle-enterprise-task-params
|
||||
docker-resource-source: &docker-resource-source
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
tag: ((milestone))
|
||||
registry-mirror-vars: ®istry-mirror-vars
|
||||
registry-mirror-host: ((registry-mirror-host))
|
||||
registry-mirror-username: ((registry-mirror-username))
|
||||
registry-mirror-password: ((registry-mirror-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
|
||||
body: generated-changelog/changelog.md
|
||||
github-task-params: &github-task-params
|
||||
GITHUB_USERNAME: ((github-username))
|
||||
GITHUB_TOKEN: ((github-ci-release-token))
|
||||
|
||||
resource_types:
|
||||
- name: registry-image
|
||||
type: registry-image
|
||||
source:
|
||||
repository: concourse/registry-image-resource
|
||||
tag: 1.5.0
|
||||
- name: artifactory-resource
|
||||
type: registry-image
|
||||
source:
|
||||
repository: springio/artifactory-resource
|
||||
tag: 0.0.17
|
||||
- name: github-release
|
||||
type: registry-image
|
||||
source:
|
||||
repository: concourse/github-release-resource
|
||||
tag: 1.5.5
|
||||
- name: github-status-resource
|
||||
type: registry-image
|
||||
source:
|
||||
repository: dpb587/github-status-resource
|
||||
tag: master
|
||||
- name: pull-request
|
||||
type: registry-image
|
||||
source:
|
||||
repository: teliaoss/github-pr-resource
|
||||
tag: v0.23.0
|
||||
- name: slack-notification
|
||||
type: registry-image
|
||||
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
|
||||
source:
|
||||
uri: ((github-repo))
|
||||
branch: ((branch))
|
||||
paths: ["ci/images/*"]
|
||||
- name: ci-image
|
||||
type: registry-image
|
||||
icon: docker
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: ((docker-hub-organization))/spring-framework-ci
|
||||
- name: artifactory-repo
|
||||
type: artifactory-resource
|
||||
icon: package-variant
|
||||
source:
|
||||
uri: ((artifactory-server))
|
||||
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-jdk11-build
|
||||
type: github-status-resource
|
||||
icon: eye-check-outline
|
||||
source:
|
||||
repository: ((github-repo-name))
|
||||
access_token: ((github-ci-status-token))
|
||||
branch: ((branch))
|
||||
context: jdk11-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
|
||||
source:
|
||||
owner: spring-projects
|
||||
repository: spring-framework
|
||||
access_token: ((github-ci-release-token))
|
||||
pre_release: true
|
||||
release: false
|
||||
- name: github-release
|
||||
type: github-release
|
||||
icon: briefcase-download
|
||||
source:
|
||||
owner: spring-projects
|
||||
repository: spring-framework
|
||||
access_token: ((github-ci-release-token))
|
||||
pre_release: false
|
||||
jobs:
|
||||
- name: build-ci-images
|
||||
plan:
|
||||
- get: git-repo
|
||||
- get: ci-images-git-repo
|
||||
trigger: true
|
||||
- task: build-ci-image
|
||||
privileged: true
|
||||
file: git-repo/ci/tasks/build-ci-image.yml
|
||||
output_mapping:
|
||||
image: ci-image
|
||||
vars:
|
||||
ci-image-name: ci-image
|
||||
<<: *registry-mirror-vars
|
||||
- put: ci-image
|
||||
params:
|
||||
image: ci-image/image.tar
|
||||
- name: build
|
||||
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" }
|
||||
- put: artifactory-repo
|
||||
params: &artifactory-params
|
||||
signing_key: ((signing-key))
|
||||
signing_passphrase: ((signing-passphrase))
|
||||
repo: libs-snapshot-local
|
||||
folder: distribution-repository
|
||||
build_uri: "https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}"
|
||||
build_number: "${BUILD_PIPELINE_NAME}-${BUILD_JOB_NAME}-${BUILD_NAME}"
|
||||
disable_checksum_uploads: true
|
||||
threads: 8
|
||||
artifact_set:
|
||||
- include:
|
||||
- "/**/spring-*.zip"
|
||||
properties:
|
||||
"zip.name": "spring-framework"
|
||||
"zip.displayname": "Spring Framework"
|
||||
"zip.deployed": "false"
|
||||
- include:
|
||||
- "/**/spring-*-docs.zip"
|
||||
properties:
|
||||
"zip.type": "docs"
|
||||
- include:
|
||||
- "/**/spring-*-dist.zip"
|
||||
properties:
|
||||
"zip.type": "dist"
|
||||
- include:
|
||||
- "/**/spring-*-schema.zip"
|
||||
properties:
|
||||
"zip.type": "schema"
|
||||
get_params:
|
||||
threads: 8
|
||||
- name: jdk11-build
|
||||
serial: true
|
||||
public: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
- get: every-morning
|
||||
trigger: true
|
||||
- put: repo-status-jdk11-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: 11
|
||||
<<: *build-project-task-params
|
||||
on_failure:
|
||||
do:
|
||||
- put: repo-status-jdk11-build
|
||||
params: { state: "failure", commit: "git-repo" }
|
||||
- put: slack-alert
|
||||
params:
|
||||
<<: *slack-fail-params
|
||||
- put: repo-status-jdk11-build
|
||||
params: { state: "success", commit: "git-repo" }
|
||||
- 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: 15
|
||||
<<: *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
|
||||
- name: promote-milestone
|
||||
serial: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
trigger: false
|
||||
- get: artifactory-repo
|
||||
trigger: false
|
||||
passed: [stage-milestone]
|
||||
params:
|
||||
download_artifacts: false
|
||||
save_build_info: true
|
||||
- task: promote
|
||||
file: git-repo/ci/tasks/promote-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: M
|
||||
<<: *artifactory-task-params
|
||||
- task: generate-changelog
|
||||
file: git-repo/ci/tasks/generate-changelog.yml
|
||||
params:
|
||||
RELEASE_TYPE: M
|
||||
<<: *github-task-params
|
||||
- put: github-pre-release
|
||||
params:
|
||||
<<: *changelog-task-params
|
||||
- name: stage-rc
|
||||
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: RC
|
||||
<<: *gradle-enterprise-task-params
|
||||
- put: artifactory-repo
|
||||
params:
|
||||
<<: *artifactory-params
|
||||
repo: libs-staging-local
|
||||
- put: git-repo
|
||||
params:
|
||||
repository: stage-git-repo
|
||||
- name: promote-rc
|
||||
serial: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
trigger: false
|
||||
- get: artifactory-repo
|
||||
trigger: false
|
||||
passed: [stage-rc]
|
||||
params:
|
||||
download_artifacts: false
|
||||
save_build_info: true
|
||||
- task: promote
|
||||
file: git-repo/ci/tasks/promote-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: RC
|
||||
<<: *artifactory-task-params
|
||||
- task: generate-changelog
|
||||
file: git-repo/ci/tasks/generate-changelog.yml
|
||||
params:
|
||||
RELEASE_TYPE: RC
|
||||
<<: *github-task-params
|
||||
- put: github-pre-release
|
||||
params:
|
||||
<<: *changelog-task-params
|
||||
- name: stage-release
|
||||
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: RELEASE
|
||||
<<: *gradle-enterprise-task-params
|
||||
- put: artifactory-repo
|
||||
params:
|
||||
<<: *artifactory-params
|
||||
repo: libs-staging-local
|
||||
- put: git-repo
|
||||
params:
|
||||
repository: stage-git-repo
|
||||
- name: promote-release
|
||||
serial: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
trigger: false
|
||||
- get: artifactory-repo
|
||||
trigger: false
|
||||
passed: [stage-release]
|
||||
params:
|
||||
download_artifacts: true
|
||||
save_build_info: true
|
||||
- task: promote
|
||||
file: git-repo/ci/tasks/promote-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: RELEASE
|
||||
<<: *artifactory-task-params
|
||||
<<: *sonatype-task-params
|
||||
- name: create-github-release
|
||||
serial: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
- get: artifactory-repo
|
||||
trigger: true
|
||||
passed: [promote-release]
|
||||
params:
|
||||
download_artifacts: false
|
||||
save_build_info: true
|
||||
- task: generate-changelog
|
||||
file: git-repo/ci/tasks/generate-changelog.yml
|
||||
params:
|
||||
RELEASE_TYPE: RELEASE
|
||||
<<: *github-task-params
|
||||
- put: github-release
|
||||
params:
|
||||
<<: *changelog-task-params
|
||||
|
||||
groups:
|
||||
- name: "builds"
|
||||
jobs: ["build", "jdk11-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" ]
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
source $(dirname $0)/common.sh
|
||||
|
||||
pushd git-repo > /dev/null
|
||||
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --max-workers=4 check
|
||||
popd > /dev/null
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
source $(dirname $0)/common.sh
|
||||
repository=$(pwd)/distribution-repository
|
||||
|
||||
pushd git-repo > /dev/null
|
||||
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --max-workers=4 -PdeploymentRepository=${repository} build publishAllPublicationsToDeploymentRepository
|
||||
popd > /dev/null
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
source $(dirname $0)/common.sh
|
||||
|
||||
pushd git-repo > /dev/null
|
||||
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK11,JDK15 \
|
||||
-PmainToolchain=${MAIN_TOOLCHAIN} -PtestToolchain=${TEST_TOOLCHAIN} --no-daemon --max-workers=4 check
|
||||
popd > /dev/null
|
||||
@@ -0,0 +1,2 @@
|
||||
source /opt/concourse-java.sh
|
||||
setup_symlinks
|
||||
Executable
+12
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
CONFIG_DIR=git-repo/ci/config
|
||||
version=$( cat artifactory-repo/build-info.json | jq -r '.buildInfo.modules[0].id' | sed 's/.*:.*:\(.*\)/\1/' )
|
||||
|
||||
java -jar /github-changelog-generator.jar \
|
||||
--spring.config.location=${CONFIG_DIR}/changelog-generator.yml \
|
||||
${version} generated-changelog/changelog.md
|
||||
|
||||
echo ${version} > generated-changelog/version
|
||||
echo v${version} > generated-changelog/tag
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
#!/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/' )
|
||||
export BUILD_INFO_LOCATION=$(pwd)/artifactory-repo/build-info.json
|
||||
|
||||
java -jar /concourse-release-scripts.jar \
|
||||
--spring.config.location=${CONFIG_DIR}/release-scripts.yml \
|
||||
publishToCentral $RELEASE_TYPE $BUILD_INFO_LOCATION artifactory-repo || { exit 1; }
|
||||
|
||||
java -jar /concourse-release-scripts.jar \
|
||||
--spring.config.location=${CONFIG_DIR}/release-scripts.yml \
|
||||
promote $RELEASE_TYPE $BUILD_INFO_LOCATION || { exit 1; }
|
||||
|
||||
echo "Promotion complete"
|
||||
echo $version > version/version
|
||||
Executable
+50
@@ -0,0 +1,50 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
source $(dirname $0)/common.sh
|
||||
repository=$(pwd)/distribution-repository
|
||||
|
||||
pushd git-repo > /dev/null
|
||||
git fetch --tags --all > /dev/null
|
||||
popd > /dev/null
|
||||
|
||||
git clone git-repo stage-git-repo > /dev/null
|
||||
|
||||
pushd stage-git-repo > /dev/null
|
||||
|
||||
snapshotVersion=$( awk -F '=' '$1 == "version" { print $2 }' gradle.properties )
|
||||
if [[ $RELEASE_TYPE = "M" ]]; then
|
||||
stageVersion=$( get_next_milestone_release $snapshotVersion)
|
||||
nextVersion=$snapshotVersion
|
||||
elif [[ $RELEASE_TYPE = "RC" ]]; then
|
||||
stageVersion=$( get_next_rc_release $snapshotVersion)
|
||||
nextVersion=$snapshotVersion
|
||||
elif [[ $RELEASE_TYPE = "RELEASE" ]]; then
|
||||
stageVersion=$( get_next_release $snapshotVersion)
|
||||
nextVersion=$( bump_version_number $snapshotVersion)
|
||||
else
|
||||
echo "Unknown release type $RELEASE_TYPE" >&2; exit 1;
|
||||
fi
|
||||
|
||||
echo "Staging $stageVersion (next version will be $nextVersion)"
|
||||
sed -i "s/version=$snapshotVersion/version=$stageVersion/" gradle.properties
|
||||
|
||||
git config user.name "Spring Builds" > /dev/null
|
||||
git config user.email "spring-builds@users.noreply.github.com" > /dev/null
|
||||
git add gradle.properties > /dev/null
|
||||
git commit -m"Release v$stageVersion" > /dev/null
|
||||
git tag -a "v$stageVersion" -m"Release v$stageVersion" > /dev/null
|
||||
|
||||
./gradlew --no-daemon --max-workers=4 -PdeploymentRepository=${repository} build publishAllPublicationsToDeploymentRepository
|
||||
|
||||
git reset --hard HEAD^ > /dev/null
|
||||
if [[ $nextVersion != $snapshotVersion ]]; then
|
||||
echo "Setting next development version (v$nextVersion)"
|
||||
sed -i "s/version=$snapshotVersion/version=$nextVersion/" gradle.properties
|
||||
git add gradle.properties > /dev/null
|
||||
git commit -m"Next development version (v$nextVersion)" > /dev/null
|
||||
fi;
|
||||
|
||||
echo "Staging Complete"
|
||||
|
||||
popd > /dev/null
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
platform: linux
|
||||
image_resource:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: concourse/oci-build-task
|
||||
tag: 0.9.1
|
||||
registry_mirror:
|
||||
host: ((registry-mirror-host))
|
||||
username: ((registry-mirror-username))
|
||||
password: ((registry-mirror-password))
|
||||
inputs:
|
||||
- name: ci-images-git-repo
|
||||
outputs:
|
||||
- name: image
|
||||
caches:
|
||||
- path: ci-image-cache
|
||||
params:
|
||||
CONTEXT: ci-images-git-repo/ci/images
|
||||
DOCKERFILE: ci-images-git-repo/ci/images/ci-image/Dockerfile
|
||||
DOCKER_HUB_AUTH: ((docker-hub-auth))
|
||||
run:
|
||||
path: /bin/sh
|
||||
args:
|
||||
- "-c"
|
||||
- |
|
||||
mkdir -p /root/.docker
|
||||
cat > /root/.docker/config.json <<EOF
|
||||
{ "auths": { "https://index.docker.io/v1/": { "auth": "$DOCKER_HUB_AUTH" }}}
|
||||
EOF
|
||||
build
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
platform: linux
|
||||
inputs:
|
||||
- name: git-repo
|
||||
caches:
|
||||
- path: gradle
|
||||
params:
|
||||
BRANCH:
|
||||
CI: true
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY:
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME:
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD:
|
||||
GRADLE_ENTERPRISE_URL: https://ge.spring.io
|
||||
run:
|
||||
path: bash
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
${PWD}/git-repo/ci/scripts/build-pr.sh
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
platform: linux
|
||||
inputs:
|
||||
- name: git-repo
|
||||
outputs:
|
||||
- name: distribution-repository
|
||||
- name: git-repo
|
||||
caches:
|
||||
- path: gradle
|
||||
params:
|
||||
BRANCH:
|
||||
CI: true
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY:
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME:
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD:
|
||||
GRADLE_ENTERPRISE_URL: https://ge.spring.io
|
||||
run:
|
||||
path: bash
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
${PWD}/git-repo/ci/scripts/build-project.sh
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
platform: linux
|
||||
inputs:
|
||||
- name: git-repo
|
||||
outputs:
|
||||
- name: distribution-repository
|
||||
- name: git-repo
|
||||
caches:
|
||||
- path: gradle
|
||||
params:
|
||||
BRANCH:
|
||||
CI: true
|
||||
MAIN_TOOLCHAIN:
|
||||
TEST_TOOLCHAIN:
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY:
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME:
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD:
|
||||
GRADLE_ENTERPRISE_URL: https://ge.spring.io
|
||||
run:
|
||||
path: bash
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
${PWD}/git-repo/ci/scripts/check-project.sh
|
||||
Executable
+20
@@ -0,0 +1,20 @@
|
||||
---
|
||||
platform: linux
|
||||
image_resource:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: springio/github-changelog-generator
|
||||
tag: '0.0.7'
|
||||
inputs:
|
||||
- name: git-repo
|
||||
- name: artifactory-repo
|
||||
outputs:
|
||||
- name: generated-changelog
|
||||
params:
|
||||
GITHUB_ORGANIZATION:
|
||||
GITHUB_REPO:
|
||||
GITHUB_USERNAME:
|
||||
GITHUB_TOKEN:
|
||||
RELEASE_TYPE:
|
||||
run:
|
||||
path: git-repo/ci/scripts/generate-changelog.sh
|
||||
@@ -0,0 +1,23 @@
|
||||
---
|
||||
platform: linux
|
||||
image_resource:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: springio/concourse-release-scripts
|
||||
tag: '0.3.4'
|
||||
inputs:
|
||||
- name: git-repo
|
||||
- name: artifactory-repo
|
||||
outputs:
|
||||
- name: version
|
||||
params:
|
||||
RELEASE_TYPE:
|
||||
ARTIFACTORY_SERVER:
|
||||
ARTIFACTORY_USERNAME:
|
||||
ARTIFACTORY_PASSWORD:
|
||||
SONATYPE_USER:
|
||||
SONATYPE_PASSWORD:
|
||||
SONATYPE_URL:
|
||||
SONATYPE_STAGING_PROFILE:
|
||||
run:
|
||||
path: git-repo/ci/scripts/promote-version.sh
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
platform: linux
|
||||
inputs:
|
||||
- name: git-repo
|
||||
outputs:
|
||||
- name: stage-git-repo
|
||||
- name: distribution-repository
|
||||
params:
|
||||
RELEASE_TYPE:
|
||||
CI: true
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME:
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD:
|
||||
GRADLE_ENTERPRISE_URL: https://ge.spring.io
|
||||
caches:
|
||||
- path: gradle
|
||||
run:
|
||||
path: git-repo/ci/scripts/stage-version.sh
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
version=5.3.39-SNAPSHOT
|
||||
version=5.3.23
|
||||
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/5.3.x/javadoc-api/"))
|
||||
url.set(new URL("https://docs.spring.io/spring-framework/docs/current/javadoc-api/"))
|
||||
}
|
||||
externalDocumentationLink {
|
||||
url.set(new URL("https://projectreactor.io/docs/core/release/api/"))
|
||||
|
||||
+4
-3
@@ -3,7 +3,7 @@ configurations {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
asciidoctorExt("io.spring.asciidoctor:spring-asciidoctor-extensions-block-switch:0.6.1")
|
||||
asciidoctorExt("io.spring.asciidoctor:spring-asciidoctor-extensions-block-switch:0.5.0")
|
||||
}
|
||||
|
||||
repositories {
|
||||
@@ -73,8 +73,9 @@ pluginManager.withPlugin("kotlin") {
|
||||
}
|
||||
|
||||
task downloadResources(type: Download) {
|
||||
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"
|
||||
def version = "0.2.5"
|
||||
src "https://repo.spring.io/release/io/spring/docresources/" +
|
||||
"spring-doc-resources/$version/spring-doc-resources-${version}.zip"
|
||||
dest project.file("$buildDir/docs/spring-doc-resources.zip")
|
||||
onlyIfModified true
|
||||
useETag "all"
|
||||
|
||||
@@ -81,10 +81,6 @@ plugins.withType(JavaPlugin) {
|
||||
javaLauncher = javaToolchains.launcherFor {
|
||||
languageVersion = testLanguageVersion
|
||||
}
|
||||
if(testLanguageVersion == JavaLanguageVersion.of(17)) {
|
||||
jvmArgs(["--add-opens=java.base/java.lang=ALL-UNNAMED",
|
||||
"--add-opens=java.base/java.util=ALL-UNNAMED"])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,6 +130,21 @@ pluginManager.withPlugin("kotlin") {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (testToolchainConfigured()) {
|
||||
def testLanguageVersion = testToolchainLanguageVersion()
|
||||
def compiler = javaToolchains.compilerFor {
|
||||
languageVersion = testLanguageVersion
|
||||
}
|
||||
// See https://kotlinlang.org/docs/gradle.html#attributes-specific-for-jvm
|
||||
def javaVersion = testLanguageVersion.toString() == '8' ? '1.8' : testLanguageVersion.toString()
|
||||
compileTestKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = javaVersion
|
||||
jdkHome = compiler.get().metadata.installationPath.asFile.absolutePath
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Configure the JMH plugin to use the toolchain for generating and running JMH bytecode
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@ class EnableCachingIntegrationTests {
|
||||
// attempt was made to look up the AJ aspect. It's due to classpath issues
|
||||
// in .integration-tests that it's not found.
|
||||
assertThatException().isThrownBy(ctx::refresh)
|
||||
.withMessageContaining("AspectJCachingConfiguration");
|
||||
.withMessageContaining("AspectJCachingConfiguration");
|
||||
}
|
||||
|
||||
|
||||
|
||||
Vendored
+2
-1
@@ -561,7 +561,8 @@ public class EnvironmentSystemIntegrationTests {
|
||||
{
|
||||
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.getEnvironment().setRequiredProperties("foo", "bar");
|
||||
assertThatExceptionOfType(MissingRequiredPropertiesException.class).isThrownBy(ctx::refresh);
|
||||
assertThatExceptionOfType(MissingRequiredPropertiesException.class).isThrownBy(
|
||||
ctx::refresh);
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
+6
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -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(200); // allow @Scheduled method to be called several times
|
||||
Thread.sleep(100); // 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(200); // allow @Scheduled method to be called several times
|
||||
Thread.sleep(100); // 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(200); // allow @Scheduled method to be called several times
|
||||
Thread.sleep(100); // allow @Scheduled method to be called several times
|
||||
|
||||
MyRepositoryWithScheduledMethod repository = ctx.getBean(MyRepositoryWithScheduledMethod.class);
|
||||
assertThat(AopUtils.isCglibProxy(repository)).isTrue();
|
||||
|
||||
+1
-1
@@ -97,7 +97,7 @@ class EnableTransactionManagementIntegrationTests {
|
||||
ctx.register(Config.class, AspectJTxConfig.class);
|
||||
// this test is a bit fragile, but gets the job done, proving that an
|
||||
// attempt was made to look up the AJ aspect. It's due to classpath issues
|
||||
// in integration-tests that it's not found.
|
||||
// in .integration-tests that it's not found.
|
||||
assertThatException()
|
||||
.isThrownBy(ctx::refresh)
|
||||
.withMessageContaining("AspectJJtaTransactionManagementConfiguration");
|
||||
|
||||
+3
-3
@@ -7,8 +7,8 @@ pluginManagement {
|
||||
}
|
||||
|
||||
plugins {
|
||||
id "com.gradle.develocity" version "3.17.2"
|
||||
id "io.spring.ge.conventions" version "0.0.17"
|
||||
id "com.gradle.enterprise" version "3.10.2"
|
||||
id "io.spring.ge.conventions" version "0.0.10"
|
||||
}
|
||||
|
||||
include "spring-aop"
|
||||
@@ -42,7 +42,7 @@ rootProject.children.each {project ->
|
||||
}
|
||||
|
||||
settings.gradle.projectsLoaded {
|
||||
develocity {
|
||||
gradleEnterprise {
|
||||
buildScan {
|
||||
File buildDir = settings.gradle.rootProject.getBuildDir()
|
||||
buildDir.mkdirs()
|
||||
|
||||
+29
-26
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -38,14 +38,6 @@ 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}
|
||||
@@ -73,15 +65,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 {@code returningName} has been set, and there are no unbound arguments
|
||||
* <li>If a 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 of the {@code returningName}.</li>
|
||||
* the value <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
|
||||
@@ -107,12 +99,20 @@ 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 {@link #getParameterNames(Method)} method will simply return
|
||||
* be logged and the {@code getParameterNames(..)} 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 final String pointcutExpression;
|
||||
private String pointcutExpression;
|
||||
|
||||
private boolean raiseExceptions;
|
||||
|
||||
@@ -197,7 +197,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
|
||||
|
||||
/**
|
||||
* If {@code afterReturning} advice binds the return value, the
|
||||
* {@code returning} variable name must be specified.
|
||||
* returning variable name must be specified.
|
||||
* @param returningName the name of the returning variable
|
||||
*/
|
||||
public void setReturningName(@Nullable String returningName) {
|
||||
@@ -206,17 +206,18 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
|
||||
|
||||
/**
|
||||
* If {@code afterThrowing} advice binds the thrown value, the
|
||||
* {@code throwing} variable name must be specified.
|
||||
* 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 on the algorithm used.
|
||||
* <p>See the {@link AspectJAdviceParameterNameDiscoverer class level javadoc}
|
||||
* for this class for details of the algorithm used.
|
||||
* @param method the target {@link Method}
|
||||
* @return the parameter names
|
||||
*/
|
||||
@@ -315,13 +316,13 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
|
||||
}
|
||||
|
||||
|
||||
private void bindParameterName(int index, @Nullable String name) {
|
||||
private void bindParameterName(int index, 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() {
|
||||
@@ -366,8 +367,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);
|
||||
@@ -399,6 +400,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse the string pointcut expression looking for:
|
||||
* @this, @target, @args, @within, @withincode, @annotation.
|
||||
@@ -463,7 +465,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* If the token starts meets Java identifier conventions, it's in.
|
||||
*/
|
||||
@Nullable
|
||||
@@ -531,6 +533,7 @@ 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");
|
||||
@@ -606,7 +609,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.
|
||||
*/
|
||||
@@ -706,7 +709,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* Return {@code true} if the given argument type is a subclass
|
||||
* of the given supertype.
|
||||
*/
|
||||
@@ -734,7 +737,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.
|
||||
*/
|
||||
|
||||
+15
-51
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,7 +18,6 @@ package org.springframework.aop.aspectj;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Arrays;
|
||||
@@ -43,7 +42,6 @@ import org.aspectj.weaver.tools.PointcutParameter;
|
||||
import org.aspectj.weaver.tools.PointcutParser;
|
||||
import org.aspectj.weaver.tools.PointcutPrimitive;
|
||||
import org.aspectj.weaver.tools.ShadowMatch;
|
||||
import org.aspectj.weaver.tools.UnsupportedPointcutPrimitiveException;
|
||||
|
||||
import org.springframework.aop.ClassFilter;
|
||||
import org.springframework.aop.IntroductionAwareMethodMatcher;
|
||||
@@ -87,8 +85,6 @@ import org.springframework.util.StringUtils;
|
||||
public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
implements ClassFilter, IntroductionAwareMethodMatcher, BeanFactoryAware {
|
||||
|
||||
private static final String AJC_MAGIC = "ajc$";
|
||||
|
||||
private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = new HashSet<>();
|
||||
|
||||
static {
|
||||
@@ -110,8 +106,6 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
@Nullable
|
||||
private Class<?> pointcutDeclarationScope;
|
||||
|
||||
private boolean aspectCompiledByAjc;
|
||||
|
||||
private String[] pointcutParameterNames = new String[0];
|
||||
|
||||
private Class<?>[] pointcutParameterTypes = new Class<?>[0];
|
||||
@@ -125,8 +119,6 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
@Nullable
|
||||
private transient PointcutExpression pointcutExpression;
|
||||
|
||||
private transient boolean pointcutParsingFailed = false;
|
||||
|
||||
private transient Map<Method, ShadowMatch> shadowMatchCache = new ConcurrentHashMap<>(32);
|
||||
|
||||
|
||||
@@ -143,7 +135,7 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
* @param paramTypes the parameter types for the pointcut
|
||||
*/
|
||||
public AspectJExpressionPointcut(Class<?> declarationScope, String[] paramNames, Class<?>[] paramTypes) {
|
||||
setPointcutDeclarationScope(declarationScope);
|
||||
this.pointcutDeclarationScope = declarationScope;
|
||||
if (paramNames.length != paramTypes.length) {
|
||||
throw new IllegalStateException(
|
||||
"Number of pointcut parameter names must match number of pointcut parameter types");
|
||||
@@ -158,7 +150,6 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
*/
|
||||
public void setPointcutDeclarationScope(Class<?> pointcutDeclarationScope) {
|
||||
this.pointcutDeclarationScope = pointcutDeclarationScope;
|
||||
this.aspectCompiledByAjc = compiledByAjc(pointcutDeclarationScope);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -183,30 +174,25 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
|
||||
@Override
|
||||
public ClassFilter getClassFilter() {
|
||||
checkExpression();
|
||||
obtainPointcutExpression();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodMatcher getMethodMatcher() {
|
||||
checkExpression();
|
||||
obtainPointcutExpression();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check whether this pointcut is ready to match.
|
||||
* Check whether this pointcut is ready to match,
|
||||
* lazily building the underlying AspectJ pointcut expression.
|
||||
*/
|
||||
private void checkExpression() {
|
||||
private PointcutExpression obtainPointcutExpression() {
|
||||
if (getExpression() == null) {
|
||||
throw new IllegalStateException("Must set property 'expression' before attempting to match");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazily build the underlying AspectJ pointcut expression.
|
||||
*/
|
||||
private PointcutExpression obtainPointcutExpression() {
|
||||
if (this.pointcutExpression == null) {
|
||||
this.pointcutClassLoader = determinePointcutClassLoader();
|
||||
this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader);
|
||||
@@ -262,8 +248,8 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
|
||||
/**
|
||||
* If a pointcut expression has been specified in XML, the user cannot
|
||||
* write "and" as "&&" (though {@code &&} will work).
|
||||
* <p>We also allow "and" between two pointcut sub-expressions.
|
||||
* write {@code and} as "&&" (though && will work).
|
||||
* We also allow {@code and} between two pointcut sub-expressions.
|
||||
* <p>This method converts back to {@code &&} for the AspectJ pointcut parser.
|
||||
*/
|
||||
private String replaceBooleanOperators(String pcExpr) {
|
||||
@@ -283,18 +269,10 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
|
||||
@Override
|
||||
public boolean matches(Class<?> targetClass) {
|
||||
if (this.pointcutParsingFailed) {
|
||||
// Pointcut parsing failed before below -> avoid trying again.
|
||||
return false;
|
||||
}
|
||||
if (this.aspectCompiledByAjc && compiledByAjc(targetClass)) {
|
||||
// ajc-compiled aspect class for ajc-compiled target class -> already weaved.
|
||||
return false;
|
||||
}
|
||||
|
||||
PointcutExpression pointcutExpression = obtainPointcutExpression();
|
||||
try {
|
||||
try {
|
||||
return obtainPointcutExpression().couldMatchJoinPointsInType(targetClass);
|
||||
return pointcutExpression.couldMatchJoinPointsInType(targetClass);
|
||||
}
|
||||
catch (ReflectionWorldException ex) {
|
||||
logger.debug("PointcutExpression matching rejected target class - trying fallback expression", ex);
|
||||
@@ -305,12 +283,6 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IllegalArgumentException | IllegalStateException | UnsupportedPointcutPrimitiveException ex) {
|
||||
this.pointcutParsingFailed = true;
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Pointcut parser rejected expression [" + getExpression() + "]: " + ex);
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
logger.debug("PointcutExpression matching rejected target class", ex);
|
||||
}
|
||||
@@ -319,6 +291,7 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
|
||||
@Override
|
||||
public boolean matches(Method method, Class<?> targetClass, boolean hasIntroductions) {
|
||||
obtainPointcutExpression();
|
||||
ShadowMatch shadowMatch = getTargetShadowMatch(method, targetClass);
|
||||
|
||||
// Special handling for this, target, @this, @target, @annotation
|
||||
@@ -356,6 +329,7 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
|
||||
@Override
|
||||
public boolean matches(Method method, Class<?> targetClass, Object... args) {
|
||||
obtainPointcutExpression();
|
||||
ShadowMatch shadowMatch = getTargetShadowMatch(method, targetClass);
|
||||
|
||||
// Bind Spring AOP proxy to AspectJ "this" and Spring AOP target to AspectJ target,
|
||||
@@ -543,16 +517,6 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
return shadowMatch;
|
||||
}
|
||||
|
||||
private static boolean compiledByAjc(Class<?> clazz) {
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
if (field.getName().startsWith(AJC_MAGIC)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Class<?> superclass = clazz.getSuperclass();
|
||||
return (superclass != null && compiledByAjc(superclass));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object other) {
|
||||
@@ -563,7 +527,7 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
return false;
|
||||
}
|
||||
AspectJExpressionPointcut otherPc = (AspectJExpressionPointcut) other;
|
||||
return ObjectUtils.nullSafeEquals(getExpression(), otherPc.getExpression()) &&
|
||||
return ObjectUtils.nullSafeEquals(this.getExpression(), otherPc.getExpression()) &&
|
||||
ObjectUtils.nullSafeEquals(this.pointcutDeclarationScope, otherPc.pointcutDeclarationScope) &&
|
||||
ObjectUtils.nullSafeEquals(this.pointcutParameterNames, otherPc.pointcutParameterNames) &&
|
||||
ObjectUtils.nullSafeEquals(this.pointcutParameterTypes, otherPc.pointcutParameterTypes);
|
||||
@@ -571,7 +535,7 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hashCode = ObjectUtils.nullSafeHashCode(getExpression());
|
||||
int hashCode = ObjectUtils.nullSafeHashCode(this.getExpression());
|
||||
hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutDeclarationScope);
|
||||
hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutParameterNames);
|
||||
hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutParameterTypes);
|
||||
|
||||
+31
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,6 +18,7 @@ package org.springframework.aop.aspectj.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashMap;
|
||||
@@ -56,6 +57,8 @@ import org.springframework.lang.Nullable;
|
||||
*/
|
||||
public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFactory {
|
||||
|
||||
private static final String AJC_MAGIC = "ajc$";
|
||||
|
||||
private static final Class<?>[] ASPECTJ_ANNOTATION_CLASSES = new Class<?>[] {
|
||||
Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class};
|
||||
|
||||
@@ -66,11 +69,37 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac
|
||||
protected final ParameterNameDiscoverer parameterNameDiscoverer = new AspectJAnnotationParameterNameDiscoverer();
|
||||
|
||||
|
||||
/**
|
||||
* We consider something to be an AspectJ aspect suitable for use by the Spring AOP system
|
||||
* if it has the @Aspect annotation, and was not compiled by ajc. The reason for this latter test
|
||||
* is that aspects written in the code-style (AspectJ language) also have the annotation present
|
||||
* when compiled by ajc with the -1.5 flag, yet they cannot be consumed by Spring AOP.
|
||||
*/
|
||||
@Override
|
||||
public boolean isAspect(Class<?> clazz) {
|
||||
return (hasAspectAnnotation(clazz) && !compiledByAjc(clazz));
|
||||
}
|
||||
|
||||
private boolean hasAspectAnnotation(Class<?> clazz) {
|
||||
return (AnnotationUtils.findAnnotation(clazz, Aspect.class) != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* We need to detect this as "code-style" AspectJ aspects should not be
|
||||
* interpreted by Spring AOP.
|
||||
*/
|
||||
private boolean compiledByAjc(Class<?> clazz) {
|
||||
// The AJTypeSystem goes to great lengths to provide a uniform appearance between code-style and
|
||||
// annotation-style aspects. Therefore there is no 'clean' way to tell them apart. Here we rely on
|
||||
// an implementation detail of the AspectJ compiler.
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
if (field.getName().startsWith(AJC_MAGIC)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(Class<?> aspectClass) throws AopConfigException {
|
||||
// If the parent has the annotation and isn't abstract it's an error
|
||||
@@ -95,7 +124,6 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find and return the first AspectJ annotation on the given method
|
||||
* (there <i>should</i> only be one anyway...).
|
||||
@@ -135,7 +163,7 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac
|
||||
|
||||
|
||||
/**
|
||||
* Class modeling an AspectJ annotation, exposing its type enumeration and
|
||||
* Class modelling an AspectJ annotation, exposing its type enumeration and
|
||||
* pointcut String.
|
||||
* @param <A> the annotation type
|
||||
*/
|
||||
|
||||
+5
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -126,16 +126,10 @@ public class AspectMetadata implements Serializable {
|
||||
* Extract contents from String of form {@code pertarget(contents)}.
|
||||
*/
|
||||
private String findPerClause(Class<?> aspectClass) {
|
||||
Aspect ann = aspectClass.getAnnotation(Aspect.class);
|
||||
if (ann == null) {
|
||||
return "";
|
||||
}
|
||||
String value = ann.value();
|
||||
int beginIndex = value.indexOf('(');
|
||||
if (beginIndex < 0) {
|
||||
return "";
|
||||
}
|
||||
return value.substring(beginIndex + 1, value.length() - 1);
|
||||
String str = aspectClass.getAnnotation(Aspect.class).value();
|
||||
int beginIndex = str.indexOf('(') + 1;
|
||||
int endIndex = str.length() - 1;
|
||||
return str.substring(beginIndex, endIndex);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+19
-31
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,12 +22,9 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.aspectj.lang.reflect.PerClauseKind;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.aop.framework.AopConfigException;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -43,8 +40,6 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class BeanFactoryAspectJAdvisorsBuilder {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(BeanFactoryAspectJAdvisorsBuilder.class);
|
||||
|
||||
private final ListableBeanFactory beanFactory;
|
||||
|
||||
private final AspectJAdvisorFactory advisorFactory;
|
||||
@@ -107,37 +102,30 @@ public class BeanFactoryAspectJAdvisorsBuilder {
|
||||
continue;
|
||||
}
|
||||
if (this.advisorFactory.isAspect(beanType)) {
|
||||
try {
|
||||
AspectMetadata amd = new AspectMetadata(beanType, beanName);
|
||||
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
|
||||
MetadataAwareAspectInstanceFactory factory =
|
||||
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
|
||||
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
|
||||
if (this.beanFactory.isSingleton(beanName)) {
|
||||
this.advisorsCache.put(beanName, classAdvisors);
|
||||
}
|
||||
else {
|
||||
this.aspectFactoryCache.put(beanName, factory);
|
||||
}
|
||||
advisors.addAll(classAdvisors);
|
||||
aspectNames.add(beanName);
|
||||
AspectMetadata amd = new AspectMetadata(beanType, beanName);
|
||||
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
|
||||
MetadataAwareAspectInstanceFactory factory =
|
||||
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
|
||||
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
|
||||
if (this.beanFactory.isSingleton(beanName)) {
|
||||
this.advisorsCache.put(beanName, classAdvisors);
|
||||
}
|
||||
else {
|
||||
// Per target or per this.
|
||||
if (this.beanFactory.isSingleton(beanName)) {
|
||||
throw new IllegalArgumentException("Bean with name '" + beanName +
|
||||
"' is a singleton, but aspect instantiation model is not singleton");
|
||||
}
|
||||
MetadataAwareAspectInstanceFactory factory =
|
||||
new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
|
||||
this.aspectFactoryCache.put(beanName, factory);
|
||||
advisors.addAll(this.advisorFactory.getAdvisors(factory));
|
||||
}
|
||||
aspectNames.add(beanName);
|
||||
advisors.addAll(classAdvisors);
|
||||
}
|
||||
catch (IllegalArgumentException | IllegalStateException | AopConfigException ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Ignoring incompatible aspect [" + beanType.getName() + "]: " + ex);
|
||||
else {
|
||||
// Per target or per this.
|
||||
if (this.beanFactory.isSingleton(beanName)) {
|
||||
throw new IllegalArgumentException("Bean with name '" + beanName +
|
||||
"' is a singleton, but aspect instantiation model is not singleton");
|
||||
}
|
||||
MetadataAwareAspectInstanceFactory factory =
|
||||
new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
|
||||
this.aspectFactoryCache.put(beanName, factory);
|
||||
advisors.addAll(this.advisorFactory.getAdvisors(factory));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -35,8 +35,7 @@ import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Internal implementation of AspectJPointcutAdvisor.
|
||||
*
|
||||
* <p>Note that there will be one instance of this advisor for each target method.
|
||||
* Note that there will be one instance of this advisor for each target method.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
@@ -294,7 +293,7 @@ final class InstantiationModelAwarePointcutAdvisorImpl
|
||||
@Override
|
||||
public boolean matches(Method method, Class<?> targetClass, Object... args) {
|
||||
// This can match only on declared pointcut.
|
||||
return (isAspectMaterialized() && this.declaredPointcut.matches(method, targetClass, args));
|
||||
return (isAspectMaterialized() && this.declaredPointcut.matches(method, targetClass));
|
||||
}
|
||||
|
||||
private boolean isAspectMaterialized() {
|
||||
|
||||
+7
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2016 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,6 +23,10 @@ import org.springframework.lang.Nullable;
|
||||
* Subinterface of {@link org.springframework.aop.aspectj.AspectInstanceFactory}
|
||||
* that returns {@link AspectMetadata} associated with AspectJ-annotated classes.
|
||||
*
|
||||
* <p>Ideally, AspectInstanceFactory would include this method itself, but because
|
||||
* AspectMetadata uses Java-5-only {@link org.aspectj.lang.reflect.AjType},
|
||||
* we need to split out this subinterface.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @since 2.0
|
||||
* @see AspectMetadata
|
||||
@@ -31,13 +35,13 @@ import org.springframework.lang.Nullable;
|
||||
public interface MetadataAwareAspectInstanceFactory extends AspectInstanceFactory {
|
||||
|
||||
/**
|
||||
* Get the AspectJ AspectMetadata for this factory's aspect.
|
||||
* Return the AspectJ AspectMetadata for this factory's aspect.
|
||||
* @return the aspect metadata
|
||||
*/
|
||||
AspectMetadata getAspectMetadata();
|
||||
|
||||
/**
|
||||
* Get the best possible creation mutex for this factory.
|
||||
* Return the best possible creation mutex for this factory.
|
||||
* @return the mutex object (may be {@code null} for no mutex to use)
|
||||
* @since 4.3
|
||||
*/
|
||||
|
||||
+14
-25
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -50,7 +50,6 @@ import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.converter.ConvertingComparator;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.ReflectionUtils.MethodFilter;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -134,19 +133,17 @@ public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFacto
|
||||
|
||||
List<Advisor> advisors = new ArrayList<>();
|
||||
for (Method method : getAdvisorMethods(aspectClass)) {
|
||||
if (method.equals(ClassUtils.getMostSpecificMethod(method, aspectClass))) {
|
||||
// Prior to Spring Framework 5.2.7, advisors.size() was supplied as the declarationOrderInAspect
|
||||
// to getAdvisor(...) to represent the "current position" in the declared methods list.
|
||||
// However, since Java 7 the "current position" is not valid since the JDK no longer
|
||||
// returns declared methods in the order in which they are declared in the source code.
|
||||
// Thus, we now hard code the declarationOrderInAspect to 0 for all advice methods
|
||||
// discovered via reflection in order to support reliable advice ordering across JVM launches.
|
||||
// Specifically, a value of 0 aligns with the default value used in
|
||||
// AspectJPrecedenceComparator.getAspectDeclarationOrder(Advisor).
|
||||
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
|
||||
if (advisor != null) {
|
||||
advisors.add(advisor);
|
||||
}
|
||||
// Prior to Spring Framework 5.2.7, advisors.size() was supplied as the declarationOrderInAspect
|
||||
// to getAdvisor(...) to represent the "current position" in the declared methods list.
|
||||
// However, since Java 7 the "current position" is not valid since the JDK no longer
|
||||
// returns declared methods in the order in which they are declared in the source code.
|
||||
// Thus, we now hard code the declarationOrderInAspect to 0 for all advice methods
|
||||
// discovered via reflection in order to support reliable advice ordering across JVM launches.
|
||||
// Specifically, a value of 0 aligns with the default value used in
|
||||
// AspectJPrecedenceComparator.getAspectDeclarationOrder(Advisor).
|
||||
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
|
||||
if (advisor != null) {
|
||||
advisors.add(advisor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,16 +210,8 @@ public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFacto
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
|
||||
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
|
||||
}
|
||||
catch (IllegalArgumentException | IllegalStateException ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Ignoring incompatible advice method: " + candidateAdviceMethod, ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
|
||||
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -44,8 +44,7 @@ import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Base class for AOP proxy configuration managers.
|
||||
*
|
||||
* <p>These are not themselves AOP proxies, but subclasses of this class are
|
||||
* These are not themselves AOP proxies, but subclasses of this class are
|
||||
* normally factories from which AOP proxy instances are obtained directly.
|
||||
*
|
||||
* <p>This class frees subclasses of the housekeeping of Advices
|
||||
@@ -53,8 +52,7 @@ import org.springframework.util.CollectionUtils;
|
||||
* methods, which are provided by subclasses.
|
||||
*
|
||||
* <p>This class is serializable; subclasses need not be.
|
||||
*
|
||||
* <p>This class is used to hold snapshots of proxies.
|
||||
* This class is used to hold snapshots of proxies.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
@@ -106,7 +104,7 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@code AdvisedSupport} instance with the given parameters.
|
||||
* Create a AdvisedSupport instance with the given parameters.
|
||||
* @param interfaces the proxied interfaces
|
||||
*/
|
||||
public AdvisedSupport(Class<?>... interfaces) {
|
||||
@@ -117,7 +115,7 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
|
||||
/**
|
||||
* Set the given object as target.
|
||||
* <p>Will create a SingletonTargetSource for the object.
|
||||
* Will create a SingletonTargetSource for the object.
|
||||
* @see #setTargetSource
|
||||
* @see org.springframework.aop.target.SingletonTargetSource
|
||||
*/
|
||||
@@ -346,7 +344,8 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
private void validateIntroductionAdvisor(IntroductionAdvisor advisor) {
|
||||
advisor.validateInterfaces();
|
||||
// If the advisor passed validation, we can make the change.
|
||||
for (Class<?> ifc : advisor.getInterfaces()) {
|
||||
Class<?>[] ifcs = advisor.getInterfaces();
|
||||
for (Class<?> ifc : ifcs) {
|
||||
addInterface(ifc);
|
||||
}
|
||||
}
|
||||
@@ -492,9 +491,9 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the AOP configuration from the given {@link AdvisedSupport} object,
|
||||
* but allow substitution of a fresh {@link TargetSource} and a given interceptor chain.
|
||||
* @param other the {@code AdvisedSupport} object to take proxy configuration from
|
||||
* Copy the AOP configuration from the given AdvisedSupport object,
|
||||
* but allow substitution of a fresh TargetSource and a given interceptor chain.
|
||||
* @param other the AdvisedSupport object to take proxy configuration from
|
||||
* @param targetSource the new TargetSource
|
||||
* @param advisors the Advisors for the chain
|
||||
*/
|
||||
@@ -514,15 +513,14 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a configuration-only copy of this {@link AdvisedSupport},
|
||||
* replacing the {@link TargetSource}.
|
||||
* Build a configuration-only copy of this AdvisedSupport,
|
||||
* replacing the TargetSource.
|
||||
*/
|
||||
AdvisedSupport getConfigurationOnlyCopy() {
|
||||
AdvisedSupport copy = new AdvisedSupport();
|
||||
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-2023 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -283,8 +283,8 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
|
||||
private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
|
||||
// Parameters used for optimization choices...
|
||||
boolean isFrozen = this.advised.isFrozen();
|
||||
boolean exposeProxy = this.advised.isExposeProxy();
|
||||
boolean isFrozen = this.advised.isFrozen();
|
||||
boolean isStatic = this.advised.getTargetSource().isStatic();
|
||||
|
||||
// Choose an "aop" interceptor (used for AOP calls).
|
||||
@@ -424,9 +424,10 @@ 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 {
|
||||
|
||||
@@ -898,9 +899,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) {
|
||||
@@ -969,9 +970,6 @@ 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;
|
||||
}
|
||||
@@ -1018,6 +1016,10 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -123,33 +123,7 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
|
||||
}
|
||||
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();
|
||||
}
|
||||
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;
|
||||
return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+3
-9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -34,7 +34,6 @@ 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
|
||||
@@ -82,11 +81,6 @@ 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
|
||||
@@ -110,7 +104,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 = getConfigurableBeanFactory().getMergedBeanDefinition(beanName);
|
||||
BeanDefinition bd = this.beanFactory.getMergedBeanDefinition(beanName);
|
||||
GenericBeanDefinition bdCopy = new GenericBeanDefinition(bd);
|
||||
if (isPrototypeBased()) {
|
||||
bdCopy.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||
@@ -132,7 +126,7 @@ public abstract class AbstractBeanFactoryBasedTargetSourceCreator
|
||||
protected DefaultListableBeanFactory getInternalBeanFactoryForBean(String beanName) {
|
||||
synchronized (this.internalBeanFactories) {
|
||||
return this.internalBeanFactories.computeIfAbsent(beanName,
|
||||
name -> buildInternalBeanFactory(getConfigurableBeanFactory()));
|
||||
name -> buildInternalBeanFactory(this.beanFactory));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
/**
|
||||
* Various {@link org.springframework.aop.framework.autoproxy.TargetSourceCreator}
|
||||
* implementations for use with Spring's AOP auto-proxying support.
|
||||
*/
|
||||
@NonNullApi
|
||||
@NonNullFields
|
||||
package org.springframework.aop.framework.autoproxy.target;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
import org.springframework.lang.NonNullFields;
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -186,10 +186,10 @@ public abstract class AopUtils {
|
||||
* this method resolves bridge methods in order to retrieve attributes from
|
||||
* the <i>original</i> method definition.
|
||||
* @param method the method to be invoked, which may come from an interface
|
||||
* @param targetClass the target class for the current invocation
|
||||
* (can be {@code null} or may not even implement the method)
|
||||
* @param targetClass the target class for the current invocation.
|
||||
* May be {@code null} or may not even implement the method.
|
||||
* @return the specific target method, or the original method if the
|
||||
* {@code targetClass} does not implement it
|
||||
* {@code targetClass} doesn't implement it or is {@code null}
|
||||
* @see org.springframework.util.ClassUtils#getMostSpecificMethod
|
||||
*/
|
||||
public static Method getMostSpecificMethod(Method method, @Nullable Class<?> targetClass) {
|
||||
|
||||
+8
-14
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -24,8 +24,6 @@ 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;
|
||||
|
||||
/**
|
||||
@@ -59,18 +57,15 @@ 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;
|
||||
|
||||
|
||||
@@ -91,7 +86,6 @@ 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;
|
||||
}
|
||||
|
||||
@@ -121,13 +115,11 @@ 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) {
|
||||
@@ -136,7 +128,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 && this.targetBeanName != null) {
|
||||
if (targetClass == null && this.beanFactory != null) {
|
||||
// Determine type of the target bean.
|
||||
targetClass = this.beanFactory.getType(this.targetBeanName);
|
||||
if (targetClass == null) {
|
||||
@@ -190,16 +182,18 @@ public abstract class AbstractBeanFactoryBasedTargetSource implements TargetSour
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getClass().hashCode() * 13 + ObjectUtils.nullSafeHashCode(this.targetBeanName);
|
||||
int hashCode = getClass().hashCode();
|
||||
hashCode = 13 * hashCode + ObjectUtils.nullSafeHashCode(this.beanFactory);
|
||||
hashCode = 13 * hashCode + ObjectUtils.nullSafeHashCode(this.targetBeanName);
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder(getClass().getSimpleName());
|
||||
sb.append(" for target bean '").append(this.targetBeanName).append('\'');
|
||||
Class<?> targetClass = this.targetClass;
|
||||
if (targetClass != null) {
|
||||
sb.append(" of type [").append(targetClass.getName()).append(']');
|
||||
if (this.targetClass != null) {
|
||||
sb.append(" of type [").append(this.targetClass.getName()).append(']');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
+1
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -46,7 +46,6 @@ 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-2023 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -70,7 +70,6 @@ public final class EmptyTargetSource implements TargetSource, Serializable {
|
||||
// Instance implementation
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
@Nullable
|
||||
private final Class<?> targetClass;
|
||||
|
||||
private final boolean isStatic;
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -95,7 +95,8 @@ 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-2023 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -82,8 +82,14 @@ public class SingletonTargetSource implements TargetSource, Serializable {
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return (this == other || (other instanceof SingletonTargetSource &&
|
||||
this.target.equals(((SingletonTargetSource) other).target)));
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof SingletonTargetSource)) {
|
||||
return false;
|
||||
}
|
||||
SingletonTargetSource otherTargetSource = (SingletonTargetSource) other;
|
||||
return this.target.equals(otherTargetSource.target);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+2
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2016 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,12 +58,7 @@ public class ThreadLocalTargetSource extends AbstractPrototypeBasedTargetSource
|
||||
* is meant to be per thread per instance of the ThreadLocalTargetSource class.
|
||||
*/
|
||||
private final ThreadLocal<Object> targetInThread =
|
||||
new NamedThreadLocal<Object>("Thread-local instance of bean") {
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + " '" + getTargetBeanName() + "'";
|
||||
}
|
||||
};
|
||||
new NamedThreadLocal<>("Thread-local instance of bean '" + getTargetBeanName() + "'");
|
||||
|
||||
/**
|
||||
* Set of managed targets, enabling us to keep track of the targets we've created.
|
||||
|
||||
+40
-35
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,6 +23,9 @@ import java.util.Map;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.aspectj.weaver.tools.PointcutExpression;
|
||||
import org.aspectj.weaver.tools.PointcutPrimitive;
|
||||
import org.aspectj.weaver.tools.UnsupportedPointcutPrimitiveException;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import test.annotation.EmptySpringAnnotation;
|
||||
@@ -39,13 +42,14 @@ import org.springframework.beans.testfixture.beans.TestBean;
|
||||
import org.springframework.beans.testfixture.beans.subpkg.DeepBean;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
|
||||
/**
|
||||
* @author Rob Harrop
|
||||
* @author Rod Johnson
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class AspectJExpressionPointcutTests {
|
||||
|
||||
@@ -61,7 +65,7 @@ public class AspectJExpressionPointcutTests {
|
||||
|
||||
|
||||
@BeforeEach
|
||||
public void setup() throws NoSuchMethodException {
|
||||
public void setUp() throws NoSuchMethodException {
|
||||
getAge = TestBean.class.getMethod("getAge");
|
||||
setAge = TestBean.class.getMethod("setAge", int.class);
|
||||
setSomeNumber = TestBean.class.getMethod("setSomeNumber", Number.class);
|
||||
@@ -171,25 +175,25 @@ public class AspectJExpressionPointcutTests {
|
||||
@Test
|
||||
public void testFriendlyErrorOnNoLocationClassMatching() {
|
||||
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> pc.getClassFilter().matches(ITestBean.class))
|
||||
.withMessageContaining("expression");
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
pc.matches(ITestBean.class))
|
||||
.withMessageContaining("expression");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFriendlyErrorOnNoLocation2ArgMatching() {
|
||||
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> pc.getMethodMatcher().matches(getAge, ITestBean.class))
|
||||
.withMessageContaining("expression");
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
pc.matches(getAge, ITestBean.class))
|
||||
.withMessageContaining("expression");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFriendlyErrorOnNoLocation3ArgMatching() {
|
||||
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> pc.getMethodMatcher().matches(getAge, ITestBean.class, (Object[]) null))
|
||||
.withMessageContaining("expression");
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
pc.matches(getAge, ITestBean.class, (Object[]) null))
|
||||
.withMessageContaining("expression");
|
||||
}
|
||||
|
||||
|
||||
@@ -206,10 +210,8 @@ public class AspectJExpressionPointcutTests {
|
||||
// not currently testable in a reliable fashion
|
||||
//assertDoesNotMatchStringClass(classFilter);
|
||||
|
||||
assertThat(methodMatcher.matches(setSomeNumber, TestBean.class, 12D))
|
||||
.as("Should match with setSomeNumber with Double input").isTrue();
|
||||
assertThat(methodMatcher.matches(setSomeNumber, TestBean.class, 11))
|
||||
.as("Should not match setSomeNumber with Integer input").isFalse();
|
||||
assertThat(methodMatcher.matches(setSomeNumber, TestBean.class, 12D)).as("Should match with setSomeNumber with Double input").isTrue();
|
||||
assertThat(methodMatcher.matches(setSomeNumber, TestBean.class, 11)).as("Should not match setSomeNumber with Integer input").isFalse();
|
||||
assertThat(methodMatcher.matches(getAge, TestBean.class)).as("Should not match getAge").isFalse();
|
||||
assertThat(methodMatcher.isRuntime()).as("Should be a runtime match").isTrue();
|
||||
}
|
||||
@@ -244,13 +246,14 @@ public class AspectJExpressionPointcutTests {
|
||||
@Test
|
||||
public void testInvalidExpression() {
|
||||
String expression = "execution(void org.springframework.beans.testfixture.beans.TestBean.setSomeNumber(Number) && args(Double)";
|
||||
assertThat(getPointcut(expression).getClassFilter().matches(Object.class)).isFalse();
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
getPointcut(expression)::getClassFilter); // call to getClassFilter forces resolution
|
||||
}
|
||||
|
||||
private TestBean getAdvisedProxy(String pointcutExpression, CallCountingInterceptor interceptor) {
|
||||
TestBean target = new TestBean();
|
||||
|
||||
AspectJExpressionPointcut pointcut = getPointcut(pointcutExpression);
|
||||
Pointcut pointcut = getPointcut(pointcutExpression);
|
||||
|
||||
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
|
||||
advisor.setAdvice(interceptor);
|
||||
@@ -274,29 +277,40 @@ public class AspectJExpressionPointcutTests {
|
||||
@Test
|
||||
public void testWithUnsupportedPointcutPrimitive() {
|
||||
String expression = "call(int org.springframework.beans.testfixture.beans.TestBean.getAge())";
|
||||
assertThat(getPointcut(expression).getClassFilter().matches(Object.class)).isFalse();
|
||||
assertThatExceptionOfType(UnsupportedPointcutPrimitiveException.class).isThrownBy(() ->
|
||||
getPointcut(expression).getClassFilter()) // call to getClassFilter forces resolution...
|
||||
.satisfies(ex -> assertThat(ex.getUnsupportedPrimitive()).isEqualTo(PointcutPrimitive.CALL));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAndSubstitution() {
|
||||
AspectJExpressionPointcut pc = getPointcut("execution(* *(..)) and args(String)");
|
||||
String expr = pc.getPointcutExpression().getPointcutExpression();
|
||||
assertThat(expr).isEqualTo("execution(* *(..)) && args(String)");
|
||||
Pointcut pc = getPointcut("execution(* *(..)) and args(String)");
|
||||
PointcutExpression expr = ((AspectJExpressionPointcut) pc).getPointcutExpression();
|
||||
assertThat(expr.getPointcutExpression()).isEqualTo("execution(* *(..)) && args(String)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleAndSubstitutions() {
|
||||
AspectJExpressionPointcut pc = getPointcut("execution(* *(..)) and args(String) and this(Object)");
|
||||
String expr = pc.getPointcutExpression().getPointcutExpression();
|
||||
assertThat(expr).isEqualTo("execution(* *(..)) && args(String) && this(Object)");
|
||||
Pointcut pc = getPointcut("execution(* *(..)) and args(String) and this(Object)");
|
||||
PointcutExpression expr = ((AspectJExpressionPointcut) pc).getPointcutExpression();
|
||||
assertThat(expr.getPointcutExpression()).isEqualTo("execution(* *(..)) && args(String) && this(Object)");
|
||||
}
|
||||
|
||||
private AspectJExpressionPointcut getPointcut(String expression) {
|
||||
private Pointcut getPointcut(String expression) {
|
||||
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
|
||||
pointcut.setExpression(expression);
|
||||
return pointcut;
|
||||
}
|
||||
|
||||
|
||||
public static class OtherIOther implements IOther {
|
||||
|
||||
@Override
|
||||
public void absquatulate() {
|
||||
// Empty
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatchGenericArgument() {
|
||||
String expression = "execution(* set*(java.util.List<org.springframework.beans.testfixture.beans.TestBean>) )";
|
||||
@@ -491,15 +505,6 @@ public class AspectJExpressionPointcutTests {
|
||||
}
|
||||
|
||||
|
||||
public static class OtherIOther implements IOther {
|
||||
|
||||
@Override
|
||||
public void absquatulate() {
|
||||
// Empty
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class HasGeneric {
|
||||
|
||||
public void setFriends(List<TestBean> friends) {
|
||||
|
||||
+18
-48
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -37,6 +37,7 @@ import org.aspectj.lang.annotation.DeclareParents;
|
||||
import org.aspectj.lang.annotation.DeclarePrecedence;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import test.aop.DefaultLockable;
|
||||
import test.aop.Lockable;
|
||||
@@ -75,24 +76,25 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
|
||||
/**
|
||||
* To be overridden by concrete test subclasses.
|
||||
* @return the fixture
|
||||
*/
|
||||
protected abstract AspectJAdvisorFactory getFixture();
|
||||
|
||||
|
||||
@Test
|
||||
void rejectsPerCflowAspect() {
|
||||
assertThatExceptionOfType(AopConfigException.class)
|
||||
.isThrownBy(() -> getFixture().getAdvisors(
|
||||
assertThatExceptionOfType(AopConfigException.class).isThrownBy(() ->
|
||||
getFixture().getAdvisors(
|
||||
new SingletonMetadataAwareAspectInstanceFactory(new PerCflowAspect(), "someBean")))
|
||||
.withMessageContaining("PERCFLOW");
|
||||
.withMessageContaining("PERCFLOW");
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsPerCflowBelowAspect() {
|
||||
assertThatExceptionOfType(AopConfigException.class)
|
||||
.isThrownBy(() -> getFixture().getAdvisors(
|
||||
new SingletonMetadataAwareAspectInstanceFactory(new PerCflowBelowAspect(), "someBean")))
|
||||
.withMessageContaining("PERCFLOWBELOW");
|
||||
assertThatExceptionOfType(AopConfigException.class).isThrownBy(() ->
|
||||
getFixture().getAdvisors(
|
||||
new SingletonMetadataAwareAspectInstanceFactory(new PerCflowBelowAspect(), "someBean")))
|
||||
.withMessageContaining("PERCFLOWBELOW");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -383,7 +385,8 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
assertThat(lockable.locked()).as("Already locked").isTrue();
|
||||
lockable.lock();
|
||||
assertThat(lockable.locked()).as("Real target ignores locking").isTrue();
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(lockable::unlock);
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() ->
|
||||
lockable.unlock());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -410,7 +413,9 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
lockable.locked();
|
||||
}
|
||||
|
||||
// TODO: Why does this test fail? It hasn't been run before, so it maybe never actually passed...
|
||||
@Test
|
||||
@Disabled
|
||||
void introductionWithArgumentBinding() {
|
||||
TestBean target = new TestBean();
|
||||
|
||||
@@ -518,16 +523,6 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
assertThat(aspect.invocations).containsExactly("around - start", "before", "after throwing", "after", "around - end");
|
||||
}
|
||||
|
||||
@Test
|
||||
void parentAspect() {
|
||||
TestBean target = new TestBean("Jane", 42);
|
||||
MetadataAwareAspectInstanceFactory aspectInstanceFactory = new SingletonMetadataAwareAspectInstanceFactory(
|
||||
new IncrementingAspect(), "incrementingAspect");
|
||||
ITestBean proxy = (ITestBean) createProxy(target,
|
||||
getFixture().getAdvisors(aspectInstanceFactory), ITestBean.class);
|
||||
assertThat(proxy.getAge()).isEqualTo(86); // (42 + 1) * 2
|
||||
}
|
||||
|
||||
@Test
|
||||
void failureWithoutExplicitDeclarePrecedence() {
|
||||
TestBean target = new TestBean();
|
||||
@@ -652,7 +647,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
static class NamedPointcutAspectWithFQN {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean();
|
||||
private ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean();
|
||||
|
||||
@Pointcut("execution(* getAge())")
|
||||
void getAge() {
|
||||
@@ -772,31 +767,6 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
}
|
||||
|
||||
|
||||
@Aspect
|
||||
abstract static class DoublingAspect {
|
||||
|
||||
@Around("execution(* getAge())")
|
||||
public Object doubleAge(ProceedingJoinPoint pjp) throws Throwable {
|
||||
return ((int) pjp.proceed()) * 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Aspect
|
||||
static class IncrementingAspect extends DoublingAspect {
|
||||
|
||||
@Override
|
||||
public Object doubleAge(ProceedingJoinPoint pjp) throws Throwable {
|
||||
return ((int) pjp.proceed()) * 2;
|
||||
}
|
||||
|
||||
@Around("execution(* getAge())")
|
||||
public int incrementAge(ProceedingJoinPoint pjp) throws Throwable {
|
||||
return ((int) pjp.proceed()) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Aspect
|
||||
private static class InvocationTrackingAspect {
|
||||
|
||||
@@ -854,7 +824,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
|
||||
@Around("getAge()")
|
||||
int preventExecution(ProceedingJoinPoint pjp) {
|
||||
return 42;
|
||||
return 666;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -874,7 +844,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
|
||||
@Around("getAge()")
|
||||
int preventExecution(ProceedingJoinPoint pjp) {
|
||||
return 42;
|
||||
return 666;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1096,7 +1066,7 @@ class PerThisAspect {
|
||||
|
||||
// Just to check that this doesn't cause problems with introduction processing
|
||||
@SuppressWarnings("unused")
|
||||
private final ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean();
|
||||
private ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean();
|
||||
|
||||
@Around("execution(int *.getAge())")
|
||||
int returnCountAsAge() {
|
||||
|
||||
+51
-70
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -38,67 +38,56 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
* @author Adrian Colyer
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
class ArgumentBindingTests {
|
||||
public class ArgumentBindingTests {
|
||||
|
||||
@Test
|
||||
void annotationArgumentNameBinding() {
|
||||
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new TransactionalBean());
|
||||
proxyFactory.addAspect(PointcutWithAnnotationArgument.class);
|
||||
ITransactionalBean proxiedTestBean = proxyFactory.getProxy();
|
||||
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(proxiedTestBean::doInTransaction)
|
||||
.withMessage("Invoked with @Transactional");
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindingInPointcutUsedByAdvice() {
|
||||
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new TestBean());
|
||||
public void testBindingInPointcutUsedByAdvice() {
|
||||
TestBean tb = new TestBean();
|
||||
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(tb);
|
||||
proxyFactory.addAspect(NamedPointcutWithArgs.class);
|
||||
ITestBean proxiedTestBean = proxyFactory.getProxy();
|
||||
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> proxiedTestBean.setName("enigma"))
|
||||
.withMessage("enigma");
|
||||
ITestBean proxiedTestBean = proxyFactory.getProxy();
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
proxiedTestBean.setName("Supercalifragalisticexpialidocious"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindingWithDynamicAdvice() {
|
||||
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new TestBean());
|
||||
proxyFactory.addAspect(DynamicPointcutWithArgs.class);
|
||||
ITestBean proxiedTestBean = proxyFactory.getProxy();
|
||||
public void testAnnotationArgumentNameBinding() {
|
||||
TransactionalBean tb = new TransactionalBean();
|
||||
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(tb);
|
||||
proxyFactory.addAspect(PointcutWithAnnotationArgument.class);
|
||||
|
||||
proxiedTestBean.applyName(1);
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> proxiedTestBean.applyName("enigma"))
|
||||
.withMessage("enigma");
|
||||
ITransactionalBean proxiedTestBean = proxyFactory.getProxy();
|
||||
assertThatIllegalStateException().isThrownBy(
|
||||
proxiedTestBean::doInTransaction);
|
||||
}
|
||||
|
||||
@Test
|
||||
void parameterNameDiscoverWithReferencePointcut() throws Exception {
|
||||
public void testParameterNameDiscoverWithReferencePointcut() throws Exception {
|
||||
AspectJAdviceParameterNameDiscoverer discoverer =
|
||||
new AspectJAdviceParameterNameDiscoverer("somepc(formal) && set(* *)");
|
||||
discoverer.setRaiseExceptions(true);
|
||||
Method method = getClass().getDeclaredMethod("methodWithOneParam", String.class);
|
||||
assertThat(discoverer.getParameterNames(method)).containsExactly("formal");
|
||||
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");
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void methodWithOneParam(String aParam) {
|
||||
public void methodWithOneParam(String aParam) {
|
||||
}
|
||||
|
||||
|
||||
interface ITransactionalBean {
|
||||
public interface ITransactionalBean {
|
||||
|
||||
@Transactional
|
||||
void doInTransaction();
|
||||
}
|
||||
|
||||
|
||||
static class TransactionalBean implements ITransactionalBean {
|
||||
public static class TransactionalBean implements ITransactionalBean {
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@@ -106,46 +95,38 @@ class ArgumentBindingTests {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Mimics Spring's @Transactional annotation without actually introducing the dependency.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Transactional {
|
||||
}
|
||||
/**
|
||||
* Represents Spring's Transactional annotation without actually introducing the dependency
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Transactional {
|
||||
}
|
||||
|
||||
|
||||
@Aspect
|
||||
static class PointcutWithAnnotationArgument {
|
||||
@Aspect
|
||||
class PointcutWithAnnotationArgument {
|
||||
|
||||
@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]));
|
||||
}
|
||||
@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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,10 +16,7 @@
|
||||
|
||||
package org.springframework.aop.framework;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Savepoint;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import javax.accessibility.Accessible;
|
||||
@@ -383,43 +380,6 @@ public class ProxyFactoryTests {
|
||||
assertThat(proxy.getName()).isEqualTo("tb");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCharSequenceProxy() {
|
||||
CharSequence target = "test";
|
||||
ProxyFactory pf = new ProxyFactory(target);
|
||||
ClassLoader cl = target.getClass().getClassLoader();
|
||||
CharSequence proxy = (CharSequence) pf.getProxy(cl);
|
||||
assertThat(proxy.toString()).isEqualTo(target);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDateProxy() {
|
||||
Date target = new Date();
|
||||
ProxyFactory pf = new ProxyFactory(target);
|
||||
pf.setProxyTargetClass(true);
|
||||
ClassLoader cl = target.getClass().getClassLoader();
|
||||
Date proxy = (Date) pf.getProxy(cl);
|
||||
assertThat(proxy.getTime()).isEqualTo(target.getTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJdbcSavepointProxy() throws SQLException {
|
||||
Savepoint target = new Savepoint() {
|
||||
@Override
|
||||
public int getSavepointId() throws SQLException {
|
||||
return 1;
|
||||
}
|
||||
@Override
|
||||
public String getSavepointName() throws SQLException {
|
||||
return "sp";
|
||||
}
|
||||
};
|
||||
ProxyFactory pf = new ProxyFactory(target);
|
||||
ClassLoader cl = Savepoint.class.getClassLoader();
|
||||
Savepoint proxy = (Savepoint) pf.getProxy(cl);
|
||||
assertThat(proxy.getSavepointName()).isEqualTo("sp");
|
||||
}
|
||||
|
||||
|
||||
@Order(2)
|
||||
public static class A implements Runnable {
|
||||
@@ -431,7 +391,7 @@ public class ProxyFactoryTests {
|
||||
|
||||
|
||||
@Order(1)
|
||||
public static class B implements Runnable {
|
||||
public static class B implements Runnable{
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
+10
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -77,7 +77,9 @@ 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);
|
||||
}
|
||||
@@ -90,7 +92,9 @@ 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);
|
||||
}
|
||||
@@ -113,7 +117,9 @@ 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-2024 the original author or authors.
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,35 +17,29 @@
|
||||
package org.springframework.aop.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aop.ClassFilter;
|
||||
import org.springframework.aop.MethodMatcher;
|
||||
import org.springframework.aop.Pointcut;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
|
||||
import org.springframework.aop.target.EmptyTargetSource;
|
||||
import org.springframework.aop.testfixture.interceptor.NopInterceptor;
|
||||
import org.springframework.beans.testfixture.beans.TestBean;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.testfixture.io.SerializationTestUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Rod Johnson
|
||||
* @author Chris Beams
|
||||
* @author Sebastien Deleuze
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
class AopUtilsTests {
|
||||
public class AopUtilsTests {
|
||||
|
||||
@Test
|
||||
void testPointcutCanNeverApply() {
|
||||
public void testPointcutCanNeverApply() {
|
||||
class TestPointcut extends StaticMethodMatcherPointcut {
|
||||
@Override
|
||||
public boolean matches(Method method, @Nullable Class<?> clazzy) {
|
||||
@@ -58,13 +52,13 @@ class AopUtilsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPointcutAlwaysApplies() {
|
||||
public void testPointcutAlwaysApplies() {
|
||||
assertThat(AopUtils.canApply(new DefaultPointcutAdvisor(new NopInterceptor()), Object.class)).isTrue();
|
||||
assertThat(AopUtils.canApply(new DefaultPointcutAdvisor(new NopInterceptor()), TestBean.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPointcutAppliesToOneMethodOnObject() {
|
||||
public void testPointcutAppliesToOneMethodOnObject() {
|
||||
class TestPointcut extends StaticMethodMatcherPointcut {
|
||||
@Override
|
||||
public boolean matches(Method method, @Nullable Class<?> clazz) {
|
||||
@@ -84,7 +78,7 @@ class AopUtilsTests {
|
||||
* that's subverted the singleton construction limitation.
|
||||
*/
|
||||
@Test
|
||||
void testCanonicalFrameworkClassesStillCanonicalOnDeserialization() throws Exception {
|
||||
public void testCanonicalFrameworkClassesStillCanonicalOnDeserialization() throws Exception {
|
||||
assertThat(SerializationTestUtils.serializeAndDeserialize(MethodMatcher.TRUE)).isSameAs(MethodMatcher.TRUE);
|
||||
assertThat(SerializationTestUtils.serializeAndDeserialize(ClassFilter.TRUE)).isSameAs(ClassFilter.TRUE);
|
||||
assertThat(SerializationTestUtils.serializeAndDeserialize(Pointcut.TRUE)).isSameAs(Pointcut.TRUE);
|
||||
@@ -94,45 +88,4 @@ class AopUtilsTests {
|
||||
assertThat(SerializationTestUtils.serializeAndDeserialize(ExposeInvocationInterceptor.INSTANCE)).isSameAs(ExposeInvocationInterceptor.INSTANCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInvokeJoinpointUsingReflection() throws Throwable {
|
||||
String name = "foo";
|
||||
TestBean testBean = new TestBean(name);
|
||||
Method method = ReflectionUtils.findMethod(TestBean.class, "getName");
|
||||
Object result = AopUtils.invokeJoinpointUsingReflection(testBean, method, new Object[0]);
|
||||
assertThat(result).isEqualTo(name);
|
||||
}
|
||||
|
||||
@Test // gh-32365
|
||||
void mostSpecificMethodBetweenJdkProxyAndTarget() throws Exception {
|
||||
Class<?> proxyClass = new ProxyFactory(new WithInterface()).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) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2015 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,13 +22,12 @@ import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
|
||||
/**
|
||||
* @author Adrian Colyer
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class AutoProxyWithCodeStyleAspectsTests {
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("resource")
|
||||
public void noAutoProxyingOfAjcCompiledAspects() {
|
||||
public void noAutoproxyingOfAjcCompiledAspects() {
|
||||
new ClassPathXmlApplicationContext("org/springframework/aop/aspectj/autoproxy/ajcAutoproxyTests.xml");
|
||||
}
|
||||
|
||||
|
||||
+1
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2015 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,10 +20,6 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
|
||||
/**
|
||||
* @author Ramnivas Laddad
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class SpringConfiguredWithAutoProxyingTests {
|
||||
|
||||
@Test
|
||||
|
||||
+4
-17
@@ -2,29 +2,16 @@
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:aop="http://www.springframework.org/schema/aop"
|
||||
xmlns:cache="http://www.springframework.org/schema/cache"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-2.0.xsd
|
||||
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-2.0.xsd
|
||||
http://www.springframework.org/schema/cache https://www.springframework.org/schema/cache/spring-cache-3.1.xsd
|
||||
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context-2.5.xsd">
|
||||
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
|
||||
|
||||
<aop:aspectj-autoproxy/>
|
||||
|
||||
<context:spring-configured/>
|
||||
|
||||
<cache:annotation-driven mode="aspectj"/>
|
||||
|
||||
<bean id="cacheManager" class="org.springframework.cache.support.NoOpCacheManager"/>
|
||||
|
||||
<bean id="myAspect" class="org.springframework.aop.aspectj.autoproxy.CodeStyleAspect" factory-method="aspectOf">
|
||||
<bean id="myAspect" class="org.springframework.aop.aspectj.autoproxy.CodeStyleAspect"
|
||||
factory-method="aspectOf">
|
||||
<property name="foo" value="bar"/>
|
||||
</bean>
|
||||
|
||||
<bean id="otherBean" class="org.springframework.beans.factory.aspectj.ShouldBeConfiguredBySpring"/>
|
||||
|
||||
<bean id="yetAnotherBean" class="org.springframework.beans.factory.aspectj.ShouldBeConfiguredBySpring"/>
|
||||
|
||||
<bean id="configuredBean" class="org.springframework.beans.factory.aspectj.ShouldBeConfiguredBySpring" lazy-init="true"/>
|
||||
<bean id="otherBean" class="java.lang.Object"/>
|
||||
|
||||
</beans>
|
||||
|
||||
Vendored
+2
-1
@@ -24,7 +24,8 @@
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="defaultCache" class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
|
||||
<bean id="defaultCache"
|
||||
class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
|
||||
<property name="name" value="default"/>
|
||||
</bean>
|
||||
|
||||
|
||||
+4
-2
@@ -7,10 +7,12 @@
|
||||
http://www.springframework.org/schema/task
|
||||
https://www.springframework.org/schema/task/spring-task.xsd">
|
||||
|
||||
<task:annotation-driven mode="aspectj" executor="testExecutor" exception-handler="testExceptionHandler"/>
|
||||
<task:annotation-driven mode="aspectj" executor="testExecutor"
|
||||
exception-handler="testExceptionHandler"/>
|
||||
|
||||
<task:executor id="testExecutor"/>
|
||||
|
||||
<bean id="testExceptionHandler" class="org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler"/>
|
||||
<bean id="testExceptionHandler"
|
||||
class="org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler"/>
|
||||
|
||||
</beans>
|
||||
|
||||
+2
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -1004,20 +1004,18 @@ 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(@Nullable Class<?> propertyType, boolean readable, boolean writable) {
|
||||
public PropertyHandler(Class<?> propertyType, boolean readable, boolean writable) {
|
||||
this.propertyType = propertyType;
|
||||
this.readable = readable;
|
||||
this.writable = writable;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Class<?> getPropertyType() {
|
||||
return this.propertyType;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,7 +23,6 @@ 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;
|
||||
@@ -31,7 +30,6 @@ 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;
|
||||
@@ -64,7 +62,7 @@ import org.springframework.util.StringUtils;
|
||||
* <p>Mainly for internal use within the framework, but to some degree also
|
||||
* useful for application classes. Consider
|
||||
* <a href="https://commons.apache.org/proper/commons-beanutils/">Apache Commons BeanUtils</a>,
|
||||
* <a href="https://github.com/ExpediaGroup/bull">BULL - Bean Utils Light Library</a>,
|
||||
* <a href="https://hotelsdotcom.github.io/bull/">BULL - Bean Utils Light Library</a>,
|
||||
* or similar third-party frameworks for more comprehensive bean utilities.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
@@ -249,8 +247,7 @@ public abstract class BeanUtils {
|
||||
// A single public constructor
|
||||
return (Constructor<T>) ctors[0];
|
||||
}
|
||||
else if (ctors.length == 0) {
|
||||
// No public constructors -> check non-public
|
||||
else if (ctors.length == 0){
|
||||
ctors = clazz.getDeclaredConstructors();
|
||||
if (ctors.length == 1) {
|
||||
// A single non-public constructor, e.g. from a non-public record type
|
||||
@@ -789,28 +786,38 @@ public abstract class BeanUtils {
|
||||
if (editable != null) {
|
||||
if (!editable.isInstance(target)) {
|
||||
throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
|
||||
"] not assignable to editable class [" + editable.getName() + "]");
|
||||
"] not assignable to Editable class [" + editable.getName() + "]");
|
||||
}
|
||||
actualEditable = editable;
|
||||
}
|
||||
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
|
||||
Set<String> ignoredProps = (ignoreProperties != null ? new HashSet<>(Arrays.asList(ignoreProperties)) : null);
|
||||
CachedIntrospectionResults sourceResults = (actualEditable != source.getClass() ?
|
||||
CachedIntrospectionResults.forClass(source.getClass()) : null);
|
||||
List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);
|
||||
|
||||
for (PropertyDescriptor targetPd : targetPds) {
|
||||
Method writeMethod = targetPd.getWriteMethod();
|
||||
if (writeMethod != null && (ignoredProps == null || !ignoredProps.contains(targetPd.getName()))) {
|
||||
PropertyDescriptor sourcePd = (sourceResults != null ?
|
||||
sourceResults.getPropertyDescriptor(targetPd.getName()) : targetPd);
|
||||
if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
|
||||
PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
|
||||
if (sourcePd != null) {
|
||||
Method readMethod = sourcePd.getReadMethod();
|
||||
if (readMethod != null) {
|
||||
if (isAssignable(writeMethod, readMethod)) {
|
||||
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) {
|
||||
try {
|
||||
ReflectionUtils.makeAccessible(readMethod);
|
||||
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
|
||||
readMethod.setAccessible(true);
|
||||
}
|
||||
Object value = readMethod.invoke(source);
|
||||
ReflectionUtils.makeAccessible(writeMethod);
|
||||
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
|
||||
writeMethod.setAccessible(true);
|
||||
}
|
||||
writeMethod.invoke(target, value);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
@@ -824,24 +831,6 @@ 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.
|
||||
@@ -902,6 +891,7 @@ public abstract class BeanUtils {
|
||||
}
|
||||
return kotlinConstructor.callBy(argParameters);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+12
-25
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -97,6 +97,8 @@ public final class CachedIntrospectionResults {
|
||||
*/
|
||||
public static final String IGNORE_BEANINFO_PROPERTY_NAME = "spring.beaninfo.ignore";
|
||||
|
||||
private static final PropertyDescriptor[] EMPTY_PROPERTY_DESCRIPTOR_ARRAY = {};
|
||||
|
||||
|
||||
private static final boolean shouldIntrospectorIgnoreBeaninfoClasses =
|
||||
SpringProperties.getFlag(IGNORE_BEANINFO_PROPERTY_NAME);
|
||||
@@ -248,22 +250,9 @@ public final class CachedIntrospectionResults {
|
||||
return beanInfo;
|
||||
}
|
||||
}
|
||||
|
||||
BeanInfo beanInfo = (shouldIntrospectorIgnoreBeaninfoClasses ?
|
||||
return (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;
|
||||
}
|
||||
|
||||
|
||||
@@ -308,7 +297,7 @@ public final class CachedIntrospectionResults {
|
||||
// Only allow URL attribute introspection, not content resolution
|
||||
continue;
|
||||
}
|
||||
if (pd.getWriteMethod() == null && isInvalidReadOnlyPropertyType(pd.getPropertyType(), beanClass)) {
|
||||
if (pd.getWriteMethod() == null && isInvalidReadOnlyPropertyType(pd.getPropertyType())) {
|
||||
// Ignore read-only properties such as ClassLoader - no need to bind to those
|
||||
continue;
|
||||
}
|
||||
@@ -358,8 +347,7 @@ public final class CachedIntrospectionResults {
|
||||
// GenericTypeAwarePropertyDescriptor leniently resolves a set* write method
|
||||
// against a declared read method, so we prefer read method descriptors here.
|
||||
pd = buildGenericTypeAwarePropertyDescriptor(beanClass, pd);
|
||||
if (pd.getWriteMethod() == null &&
|
||||
isInvalidReadOnlyPropertyType(pd.getPropertyType(), beanClass)) {
|
||||
if (pd.getWriteMethod() == null && isInvalidReadOnlyPropertyType(pd.getPropertyType())) {
|
||||
// Ignore read-only properties such as ClassLoader - no need to bind to those
|
||||
continue;
|
||||
}
|
||||
@@ -392,7 +380,7 @@ public final class CachedIntrospectionResults {
|
||||
if (Modifier.isStatic(method.getModifiers()) ||
|
||||
method.getDeclaringClass() == Object.class || method.getDeclaringClass() == Class.class ||
|
||||
method.getParameterCount() > 0 || method.getReturnType() == void.class ||
|
||||
isInvalidReadOnlyPropertyType(method.getReturnType(), method.getDeclaringClass())) {
|
||||
isInvalidReadOnlyPropertyType(method.getReturnType())) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
@@ -405,11 +393,10 @@ public final class CachedIntrospectionResults {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isInvalidReadOnlyPropertyType(@Nullable Class<?> returnType, Class<?> beanClass) {
|
||||
return (returnType != null && (ClassLoader.class.isAssignableFrom(returnType) ||
|
||||
ProtectionDomain.class.isAssignableFrom(returnType) ||
|
||||
(AutoCloseable.class.isAssignableFrom(returnType) &&
|
||||
!AutoCloseable.class.isAssignableFrom(beanClass))));
|
||||
private boolean isInvalidReadOnlyPropertyType(@Nullable Class<?> returnType) {
|
||||
return (returnType != null && (AutoCloseable.class.isAssignableFrom(returnType) ||
|
||||
ClassLoader.class.isAssignableFrom(returnType) ||
|
||||
ProtectionDomain.class.isAssignableFrom(returnType)));
|
||||
}
|
||||
|
||||
|
||||
@@ -435,7 +422,7 @@ public final class CachedIntrospectionResults {
|
||||
}
|
||||
|
||||
PropertyDescriptor[] getPropertyDescriptors() {
|
||||
return this.propertyDescriptors.values().toArray(PropertyDescriptorUtils.EMPTY_PROPERTY_DESCRIPTOR_ARRAY);
|
||||
return this.propertyDescriptors.values().toArray(EMPTY_PROPERTY_DESCRIPTOR_ARRAY);
|
||||
}
|
||||
|
||||
private PropertyDescriptor buildGenericTypeAwarePropertyDescriptor(Class<?> beanClass, PropertyDescriptor pd) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -129,6 +129,7 @@ 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-2022 the original author or authors.
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,15 +17,9 @@
|
||||
package org.springframework.beans;
|
||||
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
@@ -38,80 +32,6 @@ import org.springframework.util.ObjectUtils;
|
||||
*/
|
||||
abstract class PropertyDescriptorUtils {
|
||||
|
||||
public static final PropertyDescriptor[] EMPTY_PROPERTY_DESCRIPTOR_ARRAY = {};
|
||||
|
||||
|
||||
/**
|
||||
* Simple introspection algorithm for basic set/get/is accessor methods,
|
||||
* building corresponding JavaBeans property descriptors for them.
|
||||
* <p>This just supports the basic JavaBeans conventions, without indexed
|
||||
* properties or any customizers, and without other BeanInfo metadata.
|
||||
* For standard JavaBeans introspection, use the JavaBeans Introspector.
|
||||
* @param beanClass the target class to introspect
|
||||
* @return a collection of property descriptors
|
||||
* @throws IntrospectionException from introspecting the given bean class
|
||||
* @since 5.3.24
|
||||
* @see SimpleBeanInfoFactory
|
||||
* @see java.beans.Introspector#getBeanInfo(Class)
|
||||
*/
|
||||
public static Collection<? extends PropertyDescriptor> determineBasicProperties(Class<?> beanClass)
|
||||
throws IntrospectionException {
|
||||
|
||||
Map<String, BasicPropertyDescriptor> pdMap = new TreeMap<>();
|
||||
|
||||
for (Method method : beanClass.getMethods()) {
|
||||
String methodName = method.getName();
|
||||
|
||||
boolean setter;
|
||||
int nameIndex;
|
||||
if (methodName.startsWith("set") && method.getParameterCount() == 1) {
|
||||
setter = true;
|
||||
nameIndex = 3;
|
||||
}
|
||||
else if (methodName.startsWith("get") && method.getParameterCount() == 0 && method.getReturnType() != Void.TYPE) {
|
||||
setter = false;
|
||||
nameIndex = 3;
|
||||
}
|
||||
else if (methodName.startsWith("is") && method.getParameterCount() == 0 && method.getReturnType() == boolean.class) {
|
||||
setter = false;
|
||||
nameIndex = 2;
|
||||
}
|
||||
else {
|
||||
continue;
|
||||
}
|
||||
|
||||
String propertyName = Introspector.decapitalize(methodName.substring(nameIndex));
|
||||
if (propertyName.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BasicPropertyDescriptor pd = pdMap.get(propertyName);
|
||||
if (pd != null) {
|
||||
if (setter) {
|
||||
if (pd.getWriteMethod() == null ||
|
||||
pd.getWriteMethod().getParameterTypes()[0].isAssignableFrom(method.getParameterTypes()[0])) {
|
||||
pd.setWriteMethod(method);
|
||||
}
|
||||
else {
|
||||
pd.addWriteMethod(method);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (pd.getReadMethod() == null ||
|
||||
(pd.getReadMethod().getReturnType() == method.getReturnType() && method.getName().startsWith("is"))) {
|
||||
pd.setReadMethod(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
pd = new BasicPropertyDescriptor(propertyName, (!setter ? method : null), (setter ? method : null));
|
||||
pdMap.put(propertyName, pd);
|
||||
}
|
||||
}
|
||||
|
||||
return pdMap.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link java.beans.FeatureDescriptor}.
|
||||
*/
|
||||
@@ -253,72 +173,4 @@ abstract class PropertyDescriptorUtils {
|
||||
pd.isBound() == otherPd.isBound() && pd.isConstrained() == otherPd.isConstrained());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* PropertyDescriptor for {@link #determineBasicProperties(Class)},
|
||||
* not performing any early type determination for
|
||||
* {@link #setReadMethod}/{@link #setWriteMethod}.
|
||||
* @since 5.3.24
|
||||
*/
|
||||
private static class BasicPropertyDescriptor extends PropertyDescriptor {
|
||||
|
||||
@Nullable
|
||||
private Method readMethod;
|
||||
|
||||
@Nullable
|
||||
private Method writeMethod;
|
||||
|
||||
private final List<Method> alternativeWriteMethods = new ArrayList<>();
|
||||
|
||||
public BasicPropertyDescriptor(String propertyName, @Nullable Method readMethod, @Nullable Method writeMethod)
|
||||
throws IntrospectionException {
|
||||
|
||||
super(propertyName, readMethod, writeMethod);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadMethod(@Nullable Method readMethod) {
|
||||
this.readMethod = readMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Method getReadMethod() {
|
||||
return this.readMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWriteMethod(@Nullable Method writeMethod) {
|
||||
this.writeMethod = writeMethod;
|
||||
}
|
||||
|
||||
public void addWriteMethod(Method writeMethod) {
|
||||
if (this.writeMethod != null) {
|
||||
this.alternativeWriteMethods.add(this.writeMethod);
|
||||
this.writeMethod = null;
|
||||
}
|
||||
this.alternativeWriteMethods.add(writeMethod);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Method getWriteMethod() {
|
||||
if (this.writeMethod == null && !this.alternativeWriteMethods.isEmpty()) {
|
||||
if (this.readMethod == null) {
|
||||
return this.alternativeWriteMethods.get(0);
|
||||
}
|
||||
else {
|
||||
for (Method method : this.alternativeWriteMethods) {
|
||||
if (this.readMethod.getReturnType().isAssignableFrom(method.getParameterTypes()[0])) {
|
||||
this.writeMethod = method;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.writeMethod;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.beans;
|
||||
|
||||
import java.beans.BeanDescriptor;
|
||||
import java.beans.BeanInfo;
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.beans.SimpleBeanInfo;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.lang.NonNull;
|
||||
|
||||
/**
|
||||
* {@link BeanInfoFactory} implementation that bypasses the standard {@link java.beans.Introspector}
|
||||
* for faster introspection, reduced to basic property determination (as commonly needed in Spring).
|
||||
*
|
||||
* <p>To be configured via a {@code META-INF/spring.factories} file with the following content,
|
||||
* overriding other custom {@code org.springframework.beans.BeanInfoFactory} declarations:
|
||||
* {@code org.springframework.beans.BeanInfoFactory=org.springframework.beans.SimpleBeanInfoFactory}
|
||||
*
|
||||
* <p>Ordered at {@code Ordered.LOWEST_PRECEDENCE - 1} to override {@link ExtendedBeanInfoFactory}
|
||||
* (registered by default in 5.3) if necessary while still allowing other user-defined
|
||||
* {@link BeanInfoFactory} types to take precedence.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 5.3.24
|
||||
* @see ExtendedBeanInfoFactory
|
||||
* @see CachedIntrospectionResults
|
||||
*/
|
||||
public class SimpleBeanInfoFactory implements BeanInfoFactory, Ordered {
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public BeanInfo getBeanInfo(Class<?> beanClass) throws IntrospectionException {
|
||||
Collection<? extends PropertyDescriptor> pds =
|
||||
PropertyDescriptorUtils.determineBasicProperties(beanClass);
|
||||
|
||||
return new SimpleBeanInfo() {
|
||||
@Override
|
||||
public BeanDescriptor getBeanDescriptor() {
|
||||
return new BeanDescriptor(beanClass);
|
||||
}
|
||||
@Override
|
||||
public PropertyDescriptor[] getPropertyDescriptors() {
|
||||
return pds.toArray(PropertyDescriptorUtils.EMPTY_PROPERTY_DESCRIPTOR_ARRAY);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return Ordered.LOWEST_PRECEDENCE - 1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -136,10 +136,7 @@ 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.
|
||||
* 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.
|
||||
* @return an instance of the bean
|
||||
* @throws NoSuchBeanDefinitionException if there is no bean with the specified name
|
||||
* @throws BeansException if the bean could not be obtained
|
||||
*/
|
||||
@@ -155,11 +152,7 @@ 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.
|
||||
* 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.
|
||||
* @return an instance of the bean
|
||||
* @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
|
||||
@@ -239,7 +232,7 @@ public interface BeanFactory {
|
||||
* specific type, specify the actual bean type as an argument here and subsequently
|
||||
* use {@link ObjectProvider#orderedStream()} or its lazy streaming/iteration options.
|
||||
* <p>Also, generics matching is strict here, as per the Java assignment rules.
|
||||
* For lenient fallback matching with unchecked semantics (similar to the 'unchecked'
|
||||
* For lenient fallback matching with unchecked semantics (similar to the ´unchecked´
|
||||
* Java compiler warning), consider calling {@link #getBeanProvider(Class)} with the
|
||||
* raw type as a second step if no full generic match is
|
||||
* {@link ObjectProvider#getIfAvailable() available} with this variant.
|
||||
|
||||
+40
-95
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,7 +17,6 @@
|
||||
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;
|
||||
@@ -63,10 +62,6 @@ 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;
|
||||
@@ -149,9 +144,6 @@ 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);
|
||||
@@ -246,7 +238,6 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
"AutowiredAnnotationBeanPostProcessor requires a ConfigurableListableBeanFactory: " + beanFactory);
|
||||
}
|
||||
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
|
||||
this.metadataReaderFactory = new SimpleMetadataReaderFactory(this.beanFactory.getBeanClassLoader());
|
||||
}
|
||||
|
||||
|
||||
@@ -447,6 +438,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
|
||||
// Fall back to class name as cache key, for backwards compatibility with custom callers.
|
||||
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
|
||||
@@ -472,11 +464,12 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
return InjectionMetadata.EMPTY;
|
||||
}
|
||||
|
||||
final List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
|
||||
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
|
||||
Class<?> targetClass = clazz;
|
||||
|
||||
do {
|
||||
final List<InjectionMetadata.InjectedElement> fieldElements = new ArrayList<>();
|
||||
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
|
||||
|
||||
ReflectionUtils.doWithLocalFields(targetClass, field -> {
|
||||
MergedAnnotation<?> ann = findAutowiredAnnotation(field);
|
||||
if (ann != null) {
|
||||
@@ -487,11 +480,10 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
return;
|
||||
}
|
||||
boolean required = determineRequiredStatus(ann);
|
||||
fieldElements.add(new AutowiredFieldElement(field, required));
|
||||
currElements.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)) {
|
||||
@@ -513,12 +505,11 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
}
|
||||
boolean required = determineRequiredStatus(ann);
|
||||
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
|
||||
methodElements.add(new AutowiredMethodElement(method, required, pd));
|
||||
currElements.add(new AutowiredMethodElement(method, required, pd));
|
||||
}
|
||||
});
|
||||
|
||||
elements.addAll(0, sortMethodElements(methodElements, targetClass));
|
||||
elements.addAll(0, fieldElements);
|
||||
elements.addAll(0, currElements);
|
||||
targetClass = targetClass.getSuperclass();
|
||||
}
|
||||
while (targetClass != null && targetClass != Object.class);
|
||||
@@ -572,9 +563,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
* @param type the type of the bean
|
||||
* @return the target beans, or an empty Collection if no bean of this type is found
|
||||
* @throws BeansException if bean retrieval failed
|
||||
* @deprecated since 5.3.24 since it is unused in the meantime
|
||||
*/
|
||||
@Deprecated
|
||||
protected <T> Map<String, T> findAutowireCandidates(Class<T> type) throws BeansException {
|
||||
if (this.beanFactory == null) {
|
||||
throw new IllegalStateException("No BeanFactory configured - " +
|
||||
@@ -583,47 +572,6 @@ 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.
|
||||
*/
|
||||
@@ -645,7 +593,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
* Resolve the specified cached method argument or field value.
|
||||
*/
|
||||
@Nullable
|
||||
private Object resolveCachedArgument(@Nullable String beanName, @Nullable Object cachedArgument) {
|
||||
private Object resolvedCachedArgument(@Nullable String beanName, @Nullable Object cachedArgument) {
|
||||
if (cachedArgument instanceof DependencyDescriptor) {
|
||||
DependencyDescriptor descriptor = (DependencyDescriptor) cachedArgument;
|
||||
Assert.state(this.beanFactory != null, "No BeanFactory available");
|
||||
@@ -680,12 +628,10 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
Object value;
|
||||
if (this.cached) {
|
||||
try {
|
||||
value = resolveCachedArgument(beanName, this.cachedFieldValue);
|
||||
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
|
||||
}
|
||||
catch (BeansException ex) {
|
||||
// Unexpected target bean mismatch for cached argument -> re-resolve
|
||||
this.cached = false;
|
||||
logger.debug("Failed to resolve cached argument", ex);
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
// Unexpected removal of target bean for cached argument -> re-resolve
|
||||
value = resolveFieldValue(field, bean, beanName);
|
||||
}
|
||||
}
|
||||
@@ -702,7 +648,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<>(2);
|
||||
Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
|
||||
Assert.state(beanFactory != null, "No BeanFactory available");
|
||||
TypeConverter typeConverter = beanFactory.getTypeConverter();
|
||||
Object value;
|
||||
@@ -714,23 +660,21 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
}
|
||||
synchronized (this) {
|
||||
if (!this.cached) {
|
||||
Object cachedFieldValue = null;
|
||||
if (value != null || this.required) {
|
||||
Object cachedFieldValue = desc;
|
||||
cachedFieldValue = desc;
|
||||
registerDependentBeans(beanName, autowiredBeanNames);
|
||||
if (value != null && autowiredBeanNames.size() == 1) {
|
||||
if (autowiredBeanNames.size() == 1) {
|
||||
String autowiredBeanName = autowiredBeanNames.iterator().next();
|
||||
if (beanFactory.containsBean(autowiredBeanName) &&
|
||||
beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
|
||||
cachedFieldValue = new ShortcutDependencyDescriptor(desc, autowiredBeanName);
|
||||
cachedFieldValue = new ShortcutDependencyDescriptor(
|
||||
desc, autowiredBeanName, field.getType());
|
||||
}
|
||||
}
|
||||
this.cachedFieldValue = cachedFieldValue;
|
||||
this.cached = true;
|
||||
}
|
||||
else {
|
||||
this.cachedFieldValue = null;
|
||||
// cached flag remains false
|
||||
}
|
||||
this.cachedFieldValue = cachedFieldValue;
|
||||
this.cached = true;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
@@ -764,12 +708,10 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
Object[] arguments;
|
||||
if (this.cached) {
|
||||
try {
|
||||
arguments = resolveCachedArguments(beanName, this.cachedMethodArguments);
|
||||
arguments = resolveCachedArguments(beanName);
|
||||
}
|
||||
catch (BeansException ex) {
|
||||
// Unexpected target bean mismatch for cached argument -> re-resolve
|
||||
this.cached = false;
|
||||
logger.debug("Failed to resolve cached argument", ex);
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
// Unexpected removal of target bean for cached argument -> re-resolve
|
||||
arguments = resolveMethodArguments(method, bean, beanName);
|
||||
}
|
||||
}
|
||||
@@ -788,13 +730,14 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Object[] resolveCachedArguments(@Nullable String beanName, @Nullable Object[] cachedMethodArguments) {
|
||||
private Object[] resolveCachedArguments(@Nullable String beanName) {
|
||||
Object[] cachedMethodArguments = this.cachedMethodArguments;
|
||||
if (cachedMethodArguments == null) {
|
||||
return null;
|
||||
}
|
||||
Object[] arguments = new Object[cachedMethodArguments.length];
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
arguments[i] = resolveCachedArgument(beanName, cachedMethodArguments[i]);
|
||||
arguments[i] = resolvedCachedArgument(beanName, cachedMethodArguments[i]);
|
||||
}
|
||||
return arguments;
|
||||
}
|
||||
@@ -804,7 +747,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
int argumentCount = method.getParameterCount();
|
||||
Object[] arguments = new Object[argumentCount];
|
||||
DependencyDescriptor[] descriptors = new DependencyDescriptor[argumentCount];
|
||||
Set<String> autowiredBeanNames = new LinkedHashSet<>(argumentCount * 2);
|
||||
Set<String> autowiredBeans = new LinkedHashSet<>(argumentCount);
|
||||
Assert.state(beanFactory != null, "No BeanFactory available");
|
||||
TypeConverter typeConverter = beanFactory.getTypeConverter();
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
@@ -813,7 +756,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
currDesc.setContainingClass(bean.getClass());
|
||||
descriptors[i] = currDesc;
|
||||
try {
|
||||
Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeanNames, typeConverter);
|
||||
Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter);
|
||||
if (arg == null && !this.required) {
|
||||
arguments = null;
|
||||
break;
|
||||
@@ -827,27 +770,26 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
synchronized (this) {
|
||||
if (!this.cached) {
|
||||
if (arguments != null) {
|
||||
DependencyDescriptor[] cachedMethodArguments = Arrays.copyOf(descriptors, argumentCount);
|
||||
registerDependentBeans(beanName, autowiredBeanNames);
|
||||
if (autowiredBeanNames.size() == argumentCount) {
|
||||
Iterator<String> it = autowiredBeanNames.iterator();
|
||||
DependencyDescriptor[] cachedMethodArguments = Arrays.copyOf(descriptors, arguments.length);
|
||||
registerDependentBeans(beanName, autowiredBeans);
|
||||
if (autowiredBeans.size() == argumentCount) {
|
||||
Iterator<String> it = autowiredBeans.iterator();
|
||||
Class<?>[] paramTypes = method.getParameterTypes();
|
||||
for (int i = 0; i < paramTypes.length; i++) {
|
||||
String autowiredBeanName = it.next();
|
||||
if (arguments[i] != null && beanFactory.containsBean(autowiredBeanName) &&
|
||||
if (beanFactory.containsBean(autowiredBeanName) &&
|
||||
beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) {
|
||||
cachedMethodArguments[i] = new ShortcutDependencyDescriptor(
|
||||
descriptors[i], autowiredBeanName);
|
||||
descriptors[i], autowiredBeanName, paramTypes[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.cachedMethodArguments = cachedMethodArguments;
|
||||
this.cached = true;
|
||||
}
|
||||
else {
|
||||
this.cachedMethodArguments = null;
|
||||
// cached flag remains false
|
||||
}
|
||||
this.cached = true;
|
||||
}
|
||||
}
|
||||
return arguments;
|
||||
@@ -863,14 +805,17 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
|
||||
private final String shortcut;
|
||||
|
||||
public ShortcutDependencyDescriptor(DependencyDescriptor original, String shortcut) {
|
||||
private final Class<?> requiredType;
|
||||
|
||||
public ShortcutDependencyDescriptor(DependencyDescriptor original, String shortcut, Class<?> requiredType) {
|
||||
super(original);
|
||||
this.shortcut = shortcut;
|
||||
this.requiredType = requiredType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object resolveShortcut(BeanFactory beanFactory) {
|
||||
return beanFactory.getBean(this.shortcut, getDependencyType());
|
||||
return beanFactory.getBean(this.shortcut, this.requiredType);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -25,7 +25,6 @@ import java.util.Set;
|
||||
|
||||
import org.springframework.beans.SimpleTypeConverter;
|
||||
import org.springframework.beans.TypeConverter;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||
@@ -241,11 +240,10 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa
|
||||
}
|
||||
}
|
||||
if (targetAnnotation == null) {
|
||||
BeanFactory beanFactory = getBeanFactory();
|
||||
// Look for matching annotation on the target class
|
||||
if (beanFactory != null) {
|
||||
if (getBeanFactory() != null) {
|
||||
try {
|
||||
Class<?> beanType = beanFactory.getType(bdHolder.getBeanName());
|
||||
Class<?> beanType = getBeanFactory().getType(bdHolder.getBeanName());
|
||||
if (beanType != null) {
|
||||
targetAnnotation = AnnotationUtils.getAnnotation(ClassUtils.getUserClass(beanType), type);
|
||||
}
|
||||
@@ -292,7 +290,7 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa
|
||||
if (actualValue != null) {
|
||||
actualValue = typeConverter.convertIfNecessary(actualValue, expectedValue.getClass());
|
||||
}
|
||||
if (!ObjectUtils.nullSafeEquals(expectedValue, actualValue)) {
|
||||
if (!expectedValue.equals(actualValue)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+3
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -184,9 +184,8 @@ public abstract class YamlProcessor {
|
||||
protected Yaml createYaml() {
|
||||
LoaderOptions loaderOptions = new LoaderOptions();
|
||||
loaderOptions.setAllowDuplicateKeys(false);
|
||||
DumperOptions dumperOptions = new DumperOptions();
|
||||
return new Yaml(new FilteringConstructor(loaderOptions), new Representer(dumperOptions),
|
||||
dumperOptions, loaderOptions);
|
||||
return new Yaml(new FilteringConstructor(loaderOptions), new Representer(),
|
||||
new DumperOptions(), loaderOptions);
|
||||
}
|
||||
|
||||
private boolean process(MatchCallback callback, Yaml yaml, Resource resource) {
|
||||
|
||||
+11
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -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. 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.
|
||||
* {@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.
|
||||
*
|
||||
* <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 &&
|
||||
@@ -835,11 +835,11 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||
/**
|
||||
* This implementation attempts to query the FactoryBean's generic parameter metadata
|
||||
* if present to determine the object type. If not present, i.e. the FactoryBean is
|
||||
* declared as a raw type, it checks the FactoryBean's {@code getObjectType} method
|
||||
* declared as a raw type, checks the FactoryBean's {@code getObjectType} method
|
||||
* on a plain instance of the FactoryBean, without bean properties applied yet.
|
||||
* If this doesn't return a type yet and {@code allowInit} is {@code true}, full
|
||||
* creation of the FactoryBean is attempted as fallback (through delegation to the
|
||||
* superclass implementation).
|
||||
* If this doesn't return a type yet, and {@code allowInit} is {@code true} a
|
||||
* full creation of the FactoryBean is used as fallback (through delegation to the
|
||||
* superclass's implementation).
|
||||
* <p>The shortcut check for a FactoryBean is only applied in case of a singleton
|
||||
* FactoryBean. If the FactoryBean instance itself is not kept as singleton,
|
||||
* it will be fully created to check the type of its exposed object.
|
||||
@@ -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);
|
||||
|
||||
+40
-55
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -161,14 +161,11 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
|
||||
/** Cache of pre-filtered post-processors. */
|
||||
@Nullable
|
||||
private BeanPostProcessorCache beanPostProcessorCache;
|
||||
private volatile BeanPostProcessorCache beanPostProcessorCache;
|
||||
|
||||
/** 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;
|
||||
@@ -183,6 +180,8 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
private final ThreadLocal<Object> prototypesCurrentlyInCreation =
|
||||
new NamedThreadLocal<>("Prototype beans currently in creation");
|
||||
|
||||
/** Application startup metrics. **/
|
||||
private ApplicationStartup applicationStartup = ApplicationStartup.DEFAULT;
|
||||
|
||||
/**
|
||||
* Create a new AbstractBeanFactory.
|
||||
@@ -750,7 +749,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)) {
|
||||
@@ -945,12 +944,10 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
@Override
|
||||
public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
|
||||
Assert.notNull(beanPostProcessor, "BeanPostProcessor must not be null");
|
||||
synchronized (this.beanPostProcessors) {
|
||||
// Remove from old position, if any
|
||||
this.beanPostProcessors.remove(beanPostProcessor);
|
||||
// Add to end of list
|
||||
this.beanPostProcessors.add(beanPostProcessor);
|
||||
}
|
||||
// Remove from old position, if any
|
||||
this.beanPostProcessors.remove(beanPostProcessor);
|
||||
// Add to end of list
|
||||
this.beanPostProcessors.add(beanPostProcessor);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -960,12 +957,8 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
* @see #addBeanPostProcessor
|
||||
*/
|
||||
public void addBeanPostProcessors(Collection<? extends BeanPostProcessor> beanPostProcessors) {
|
||||
synchronized (this.beanPostProcessors) {
|
||||
// Remove from old position, if any
|
||||
this.beanPostProcessors.removeAll(beanPostProcessors);
|
||||
// Add to end of list
|
||||
this.beanPostProcessors.addAll(beanPostProcessors);
|
||||
}
|
||||
this.beanPostProcessors.removeAll(beanPostProcessors);
|
||||
this.beanPostProcessors.addAll(beanPostProcessors);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -987,34 +980,26 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
* @since 5.3
|
||||
*/
|
||||
BeanPostProcessorCache getBeanPostProcessorCache() {
|
||||
synchronized (this.beanPostProcessors) {
|
||||
BeanPostProcessorCache bppCache = this.beanPostProcessorCache;
|
||||
if (bppCache == null) {
|
||||
bppCache = new BeanPostProcessorCache();
|
||||
for (BeanPostProcessor bpp : this.beanPostProcessors) {
|
||||
if (bpp instanceof InstantiationAwareBeanPostProcessor) {
|
||||
bppCache.instantiationAware.add((InstantiationAwareBeanPostProcessor) bpp);
|
||||
if (bpp instanceof SmartInstantiationAwareBeanPostProcessor) {
|
||||
bppCache.smartInstantiationAware.add((SmartInstantiationAwareBeanPostProcessor) bpp);
|
||||
}
|
||||
}
|
||||
if (bpp instanceof DestructionAwareBeanPostProcessor) {
|
||||
bppCache.destructionAware.add((DestructionAwareBeanPostProcessor) bpp);
|
||||
}
|
||||
if (bpp instanceof MergedBeanDefinitionPostProcessor) {
|
||||
bppCache.mergedDefinition.add((MergedBeanDefinitionPostProcessor) bpp);
|
||||
BeanPostProcessorCache bpCache = this.beanPostProcessorCache;
|
||||
if (bpCache == null) {
|
||||
bpCache = new BeanPostProcessorCache();
|
||||
for (BeanPostProcessor bp : this.beanPostProcessors) {
|
||||
if (bp instanceof InstantiationAwareBeanPostProcessor) {
|
||||
bpCache.instantiationAware.add((InstantiationAwareBeanPostProcessor) bp);
|
||||
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
|
||||
bpCache.smartInstantiationAware.add((SmartInstantiationAwareBeanPostProcessor) bp);
|
||||
}
|
||||
}
|
||||
this.beanPostProcessorCache = bppCache;
|
||||
if (bp instanceof DestructionAwareBeanPostProcessor) {
|
||||
bpCache.destructionAware.add((DestructionAwareBeanPostProcessor) bp);
|
||||
}
|
||||
if (bp instanceof MergedBeanDefinitionPostProcessor) {
|
||||
bpCache.mergedDefinition.add((MergedBeanDefinitionPostProcessor) bp);
|
||||
}
|
||||
}
|
||||
return bppCache;
|
||||
}
|
||||
}
|
||||
|
||||
private void resetBeanPostProcessorCache() {
|
||||
synchronized (this.beanPostProcessors) {
|
||||
this.beanPostProcessorCache = null;
|
||||
this.beanPostProcessorCache = bpCache;
|
||||
}
|
||||
return bpCache;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1080,7 +1065,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
|
||||
@Override
|
||||
public void setApplicationStartup(ApplicationStartup applicationStartup) {
|
||||
Assert.notNull(applicationStartup, "ApplicationStartup must not be null");
|
||||
Assert.notNull(applicationStartup, "applicationStartup should not be null");
|
||||
this.applicationStartup = applicationStartup;
|
||||
}
|
||||
|
||||
@@ -1695,7 +1680,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
* already. The implementation is allowed to instantiate the target factory bean if
|
||||
* {@code allowInit} is {@code true} and the type cannot be determined another way;
|
||||
* otherwise it is restricted to introspecting signatures and related metadata.
|
||||
* <p>If no {@link FactoryBean#OBJECT_TYPE_ATTRIBUTE} is set on the bean definition
|
||||
* <p>If no {@link FactoryBean#OBJECT_TYPE_ATTRIBUTE} if set on the bean definition
|
||||
* and {@code allowInit} is {@code true}, the default implementation will create
|
||||
* the FactoryBean via {@code getBean} to call its {@code getObjectType} method.
|
||||
* Subclasses are encouraged to optimize this, typically by inspecting the generic
|
||||
@@ -2029,27 +2014,27 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
@Override
|
||||
public BeanPostProcessor set(int index, BeanPostProcessor element) {
|
||||
BeanPostProcessor result = super.set(index, element);
|
||||
resetBeanPostProcessorCache();
|
||||
beanPostProcessorCache = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(BeanPostProcessor o) {
|
||||
boolean success = super.add(o);
|
||||
resetBeanPostProcessorCache();
|
||||
beanPostProcessorCache = null;
|
||||
return success;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int index, BeanPostProcessor element) {
|
||||
super.add(index, element);
|
||||
resetBeanPostProcessorCache();
|
||||
beanPostProcessorCache = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanPostProcessor remove(int index) {
|
||||
BeanPostProcessor result = super.remove(index);
|
||||
resetBeanPostProcessorCache();
|
||||
beanPostProcessorCache = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -2057,7 +2042,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
public boolean remove(Object o) {
|
||||
boolean success = super.remove(o);
|
||||
if (success) {
|
||||
resetBeanPostProcessorCache();
|
||||
beanPostProcessorCache = null;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
@@ -2066,7 +2051,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
public boolean removeAll(Collection<?> c) {
|
||||
boolean success = super.removeAll(c);
|
||||
if (success) {
|
||||
resetBeanPostProcessorCache();
|
||||
beanPostProcessorCache = null;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
@@ -2075,7 +2060,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
public boolean retainAll(Collection<?> c) {
|
||||
boolean success = super.retainAll(c);
|
||||
if (success) {
|
||||
resetBeanPostProcessorCache();
|
||||
beanPostProcessorCache = null;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
@@ -2084,7 +2069,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
public boolean addAll(Collection<? extends BeanPostProcessor> c) {
|
||||
boolean success = super.addAll(c);
|
||||
if (success) {
|
||||
resetBeanPostProcessorCache();
|
||||
beanPostProcessorCache = null;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
@@ -2093,7 +2078,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
public boolean addAll(int index, Collection<? extends BeanPostProcessor> c) {
|
||||
boolean success = super.addAll(index, c);
|
||||
if (success) {
|
||||
resetBeanPostProcessorCache();
|
||||
beanPostProcessorCache = null;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
@@ -2102,7 +2087,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
public boolean removeIf(Predicate<? super BeanPostProcessor> filter) {
|
||||
boolean success = super.removeIf(filter);
|
||||
if (success) {
|
||||
resetBeanPostProcessorCache();
|
||||
beanPostProcessorCache = null;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
@@ -2110,7 +2095,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
@Override
|
||||
public void replaceAll(UnaryOperator<BeanPostProcessor> operator) {
|
||||
super.replaceAll(operator);
|
||||
resetBeanPostProcessorCache();
|
||||
beanPostProcessorCache = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -134,7 +134,7 @@ class BeanDefinitionValueResolver {
|
||||
return resolveInnerBean(argName, innerBeanName, bd);
|
||||
}
|
||||
else if (value instanceof DependencyDescriptor) {
|
||||
Set<String> autowiredBeanNames = new LinkedHashSet<>(2);
|
||||
Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
|
||||
Object result = this.beanFactory.resolveDependency(
|
||||
(DependencyDescriptor) value, this.beanName, autowiredBeanNames, this.typeConverter);
|
||||
for (String autowiredBeanName : autowiredBeanNames) {
|
||||
|
||||
+53
-125
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -45,7 +45,6 @@ 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;
|
||||
@@ -86,6 +85,12 @@ 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");
|
||||
|
||||
@@ -107,7 +112,11 @@ class ConstructorResolver {
|
||||
|
||||
/**
|
||||
* "autowire constructor" (with constructor arguments by type) behavior.
|
||||
* Also applied if explicit constructor argument values are specified.
|
||||
* 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.
|
||||
* @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)
|
||||
@@ -600,10 +609,13 @@ class ConstructorResolver {
|
||||
String argDesc = StringUtils.collectionToCommaDelimitedString(argTypes);
|
||||
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
|
||||
"No matching factory method found on class [" + factoryClass.getName() + "]: " +
|
||||
(mbd.getFactoryBeanName() != null ? "factory bean '" + mbd.getFactoryBeanName() + "'; " : "") +
|
||||
(mbd.getFactoryBeanName() != null ?
|
||||
"factory bean '" + mbd.getFactoryBeanName() + "'; " : "") +
|
||||
"factory method '" + mbd.getFactoryMethodName() + "(" + argDesc + ")'. " +
|
||||
"Check that a method with the specified name " + (minNrOfArgs > 0 ? "and arguments " : "") +
|
||||
"exists and that it is " + (isStatic ? "static" : "non-static") + ".");
|
||||
"Check that a method with the specified name " +
|
||||
(minNrOfArgs > 0 ? "and arguments " : "") +
|
||||
"exists and that it is " +
|
||||
(isStatic ? "static" : "non-static") + ".");
|
||||
}
|
||||
else if (void.class == factoryMethodToUse.getReturnType()) {
|
||||
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
|
||||
@@ -717,7 +729,7 @@ class ConstructorResolver {
|
||||
|
||||
ArgumentsHolder args = new ArgumentsHolder(paramTypes.length);
|
||||
Set<ConstructorArgumentValues.ValueHolder> usedValueHolders = new HashSet<>(paramTypes.length);
|
||||
Set<String> allAutowiredBeanNames = new LinkedHashSet<>(paramTypes.length * 2);
|
||||
Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
|
||||
|
||||
for (int paramIndex = 0; paramIndex < paramTypes.length; paramIndex++) {
|
||||
Class<?> paramType = paramTypes[paramIndex];
|
||||
@@ -752,8 +764,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) {
|
||||
@@ -776,17 +788,11 @@ class ConstructorResolver {
|
||||
"] - did you specify the correct bean references as arguments?");
|
||||
}
|
||||
try {
|
||||
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;
|
||||
Object autowiredArgument = resolveAutowiredArgument(
|
||||
methodParam, beanName, autowiredBeanNames, converter, fallback);
|
||||
args.rawArguments[paramIndex] = autowiredArgument;
|
||||
args.arguments[paramIndex] = autowiredArgument;
|
||||
args.preparedArguments[paramIndex] = autowiredArgumentMarker;
|
||||
args.resolveNecessary = true;
|
||||
}
|
||||
catch (BeansException ex) {
|
||||
@@ -796,7 +802,14 @@ class ConstructorResolver {
|
||||
}
|
||||
}
|
||||
|
||||
registerDependentBeans(executable, beanName, allAutowiredBeanNames);
|
||||
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 + "'");
|
||||
}
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
@@ -816,61 +829,31 @@ class ConstructorResolver {
|
||||
Object[] resolvedArgs = new Object[argsToResolve.length];
|
||||
for (int argIndex = 0; argIndex < argsToResolve.length; argIndex++) {
|
||||
Object argValue = argsToResolve[argIndex];
|
||||
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);
|
||||
}
|
||||
}
|
||||
MethodParameter methodParam = MethodParameter.forExecutable(executable, argIndex);
|
||||
if (argValue == autowiredArgumentMarker) {
|
||||
argValue = resolveAutowiredArgument(methodParam, beanName, null, converter, true);
|
||||
}
|
||||
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;
|
||||
}
|
||||
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());
|
||||
}
|
||||
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());
|
||||
}
|
||||
resolvedArgs[argIndex] = argValue;
|
||||
}
|
||||
return resolvedArgs;
|
||||
}
|
||||
|
||||
private Constructor<?> getUserDeclaredConstructor(Constructor<?> constructor) {
|
||||
protected Constructor<?> getUserDeclaredConstructor(Constructor<?> constructor) {
|
||||
Class<?> declaringClass = constructor.getDeclaringClass();
|
||||
Class<?> userClass = ClassUtils.getUserClass(declaringClass);
|
||||
if (userClass != declaringClass) {
|
||||
@@ -886,22 +869,23 @@ class ConstructorResolver {
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the specified argument which is supposed to be autowired.
|
||||
* Template method for resolving the specified argument which is supposed to be autowired.
|
||||
*/
|
||||
@Nullable
|
||||
Object resolveAutowiredArgument(DependencyDescriptor descriptor, Class<?> paramType, String beanName,
|
||||
protected Object resolveAutowiredArgument(MethodParameter param, 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 " + descriptor);
|
||||
throw new IllegalStateException("No current InjectionPoint available for " + param);
|
||||
}
|
||||
return injectionPoint;
|
||||
}
|
||||
|
||||
try {
|
||||
return this.beanFactory.resolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter);
|
||||
return this.beanFactory.resolveDependency(
|
||||
new DependencyDescriptor(param, true), beanName, autowiredBeanNames, typeConverter);
|
||||
}
|
||||
catch (NoUniqueBeanDefinitionException ex) {
|
||||
throw ex;
|
||||
@@ -924,31 +908,6 @@ 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) {
|
||||
@@ -1047,35 +1006,4 @@ 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-2023 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -961,7 +961,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
for (String beanName : beanNames) {
|
||||
Object singletonInstance = getSingleton(beanName);
|
||||
if (singletonInstance instanceof SmartInitializingSingleton) {
|
||||
StartupStep smartInitialize = getApplicationStartup().start("spring.beans.smart-initialize")
|
||||
StartupStep smartInitialize = this.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-2024 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -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, DisposableBean> disposableBeans = new LinkedHashMap<>();
|
||||
private final Map<String, Object> 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(@Nullable String beanName) {
|
||||
public boolean isSingletonCurrentlyInCreation(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 || dependentBeans.isEmpty()) {
|
||||
if (dependentBeans == null) {
|
||||
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 = this.disposableBeans.remove(beanName);
|
||||
disposableBean = (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> dependentBeanNames;
|
||||
Set<String> dependencies;
|
||||
synchronized (this.dependentBeanMap) {
|
||||
// Within full synchronization in order to guarantee a disconnected Set
|
||||
dependentBeanNames = this.dependentBeanMap.remove(beanName);
|
||||
dependencies = this.dependentBeanMap.remove(beanName);
|
||||
}
|
||||
if (dependentBeanNames != null) {
|
||||
if (dependencies != null) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Retrieved dependent beans for bean '" + beanName + "': " + dependentBeanNames);
|
||||
logger.trace("Retrieved dependent beans for bean '" + beanName + "': " + dependencies);
|
||||
}
|
||||
for (String dependentBeanName : dependentBeanNames) {
|
||||
for (String dependentBeanName : dependencies) {
|
||||
destroySingleton(dependentBeanName);
|
||||
}
|
||||
}
|
||||
|
||||
+23
-34
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -68,9 +68,9 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||
|
||||
private static final String SHUTDOWN_METHOD_NAME = "shutdown";
|
||||
|
||||
|
||||
private static final Log logger = LogFactory.getLog(DisposableBeanAdapter.class);
|
||||
|
||||
|
||||
private final Object bean;
|
||||
|
||||
private final String beanName;
|
||||
@@ -214,15 +214,12 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
String msg = "Invocation of destroy method failed on bean with name '" + this.beanName + "'";
|
||||
if (logger.isDebugEnabled()) {
|
||||
// Log at warn level like below but add the exception stacktrace only with debug level
|
||||
logger.warn(msg, ex);
|
||||
}
|
||||
else {
|
||||
logger.warn(msg + ": " + ex);
|
||||
}
|
||||
String msg = "Invocation of destroy method failed on bean with name '" + this.beanName + "'";
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.warn(msg, ex);
|
||||
}
|
||||
else {
|
||||
logger.warn(msg + ": " + ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -243,15 +240,12 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
String msg = "Invocation of close method failed on bean with name '" + this.beanName + "'";
|
||||
if (logger.isDebugEnabled()) {
|
||||
// Log at warn level like below but add the exception stacktrace only with debug level
|
||||
logger.warn(msg, ex);
|
||||
}
|
||||
else {
|
||||
logger.warn(msg + ": " + ex);
|
||||
}
|
||||
String msg = "Invocation of close method failed on bean with name '" + this.beanName + "'";
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.warn(msg, ex);
|
||||
}
|
||||
else {
|
||||
logger.warn(msg + ": " + ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -326,23 +320,18 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||
}
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
String msg = "Custom destroy method '" + this.destroyMethodName + "' on bean with name '" +
|
||||
this.beanName + "' threw an exception";
|
||||
if (logger.isDebugEnabled()) {
|
||||
// Log at warn level like below but add the exception stacktrace only with debug level
|
||||
logger.warn(msg, ex.getTargetException());
|
||||
}
|
||||
else {
|
||||
logger.warn(msg + ": " + ex.getTargetException());
|
||||
}
|
||||
String msg = "Custom destroy method '" + this.destroyMethodName + "' on bean with name '" +
|
||||
this.beanName + "' threw an exception";
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.warn(msg, ex.getTargetException());
|
||||
}
|
||||
else {
|
||||
logger.warn(msg + ": " + ex.getTargetException());
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Failed to invoke custom destroy method '" + this.destroyMethodName +
|
||||
"' on bean with name '" + this.beanName + "'", ex);
|
||||
}
|
||||
logger.warn("Failed to invoke custom destroy method '" + this.destroyMethodName +
|
||||
"' on bean with name '" + this.beanName + "'", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -112,7 +112,7 @@ public class LookupOverride extends MethodOverride {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode() * 29 + ObjectUtils.nullSafeHashCode(this.beanName);
|
||||
return (29 * super.hashCode() + ObjectUtils.nullSafeHashCode(this.beanName));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user