mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
89 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b932df6ad2 | |||
| 494ed4e852 | |||
| 39cd31613b | |||
| ca2b3c170c | |||
| 019ce4418c | |||
| a0136a276a | |||
| 8d05028c2e | |||
| 4a68c44a27 | |||
| e702733c7b | |||
| 187f641409 | |||
| ac74116d76 | |||
| c5590ae9e6 | |||
| 39b551c334 | |||
| 23696b7db8 | |||
| 7b7cfb27a3 | |||
| f2889b1b43 | |||
| 802967fc98 | |||
| 22328905da | |||
| 67a81004a2 | |||
| 84963ba4d8 | |||
| 07472758ee | |||
| 47c9c7eb07 | |||
| 8568aa691c | |||
| b24221754a | |||
| 7a666cfd86 | |||
| 4a7c24d90f | |||
| d955549037 | |||
| b68f76c86e | |||
| 521cda009b | |||
| 08c9b5c96c | |||
| a942a6e172 | |||
| 9b1d5ea6e0 | |||
| 640b97f33c | |||
| 1fb99e4192 | |||
| 23d89362b0 | |||
| 048e4f0aa2 | |||
| b28153e1d7 | |||
| 88b9c0567d | |||
| c7c9da56da | |||
| 290a41d398 | |||
| 1b563f8ba4 | |||
| dea31dd55e | |||
| 06b91c4ea5 | |||
| a63ebe7e9d | |||
| c4b615052e | |||
| dca5f1dcd8 | |||
| a16e8c34b2 | |||
| 52b8c71dcd | |||
| f941754db6 | |||
| d4495a5654 | |||
| c7c61e13e1 | |||
| 4d722414da | |||
| 87dfa492af | |||
| cf9033a60d | |||
| 1e80694daf | |||
| e17c3d3be4 | |||
| 2e98a8a2a4 | |||
| 592f23674d | |||
| 0b3a05b463 | |||
| ef02f0bad8 | |||
| 836a0b3a40 | |||
| 09f210f5f7 | |||
| aa0fb97ba5 | |||
| 71a117c0fd | |||
| bd27ddf83a | |||
| c531a8a705 | |||
| cd7ba1835c | |||
| 5dc6a16c0b | |||
| 42a4f28962 | |||
| 521d4f24d0 | |||
| 039cbb3466 | |||
| d50c94fca9 | |||
| c1424acb94 | |||
| 36d1bc57d7 | |||
| b1c3b6e34b | |||
| eb8b7c4331 | |||
| 58bd057a24 | |||
| a78704af24 | |||
| d2000efbd8 | |||
| c23fd784a0 | |||
| 68a3993897 | |||
| dfc1b839f3 | |||
| 302c24aaa8 | |||
| 7357812b4e | |||
| 30087e1aea | |||
| cd5758e610 | |||
| b82a0dd31a | |||
| b7280400ee | |||
| a3345920ec |
@@ -0,0 +1,33 @@
|
||||
name: Send notification
|
||||
description: Sends a Google Chat message as a notification of the job's outcome
|
||||
inputs:
|
||||
webhook-url:
|
||||
description: 'Google Chat Webhook URL'
|
||||
required: true
|
||||
status:
|
||||
description: 'Status of the job'
|
||||
required: true
|
||||
build-scan-url:
|
||||
description: 'URL of the build scan to include in the notification'
|
||||
run-name:
|
||||
description: 'Name of the run to include in the notification'
|
||||
default: ${{ format('{0} {1}', github.ref_name, github.job) }}
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- shell: bash
|
||||
run: |
|
||||
echo "BUILD_SCAN=${{ inputs.build-scan-url == '' && ' [build scan unavailable]' || format(' [<{0}|Build Scan>]', inputs.build-scan-url) }}" >> "$GITHUB_ENV"
|
||||
echo "RUN_URL=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> "$GITHUB_ENV"
|
||||
- shell: bash
|
||||
if: ${{ inputs.status == 'success' }}
|
||||
run: |
|
||||
curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<${{ env.RUN_URL }}|${{ inputs.run-name }}> was successful ${{ env.BUILD_SCAN }}"}' || true
|
||||
- shell: bash
|
||||
if: ${{ inputs.status == 'failure' }}
|
||||
run: |
|
||||
curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<users/all> *<${{ env.RUN_URL }}|${{ inputs.run-name }}> failed* ${{ env.BUILD_SCAN }}"}' || true
|
||||
- shell: bash
|
||||
if: ${{ inputs.status == 'cancelled' }}
|
||||
run: |
|
||||
curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<${{ env.RUN_URL }}|${{ inputs.run-name }}> was cancelled"}' || true
|
||||
@@ -18,11 +18,13 @@ jobs:
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-java@v3
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '17'
|
||||
distribution: 'liberica'
|
||||
java-version: 17
|
||||
- name: Download BackportBot
|
||||
run: wget https://github.com/spring-io/backport-bot/releases/download/latest/backport-bot-0.0.1-SNAPSHOT.jar
|
||||
- name: Backport
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
name: Build and deploy snapshot
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 6.1.x
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
jobs:
|
||||
build-and-deploy-snapshot:
|
||||
if: ${{ github.repository == 'spring-projects/spring-framework' }}
|
||||
name: Build and deploy snapshot
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'liberica'
|
||||
java-version: 17
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
|
||||
with:
|
||||
cache-read-only: false
|
||||
- name: Configure Gradle properties
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p $HOME/.gradle
|
||||
echo 'systemProp.user.name=spring-builds+github' >> $HOME/.gradle/gradle.properties
|
||||
echo 'systemProp.org.gradle.internal.launcher.welcomeMessageEnabled=false' >> $HOME/.gradle/gradle.properties
|
||||
echo 'org.gradle.daemon=false' >> $HOME/.gradle/gradle.properties
|
||||
echo 'org.gradle.daemon=4' >> $HOME/.gradle/gradle.properties
|
||||
- name: Build and publish
|
||||
id: build
|
||||
env:
|
||||
CI: 'true'
|
||||
GRADLE_ENTERPRISE_URL: 'https://ge.spring.io'
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GRADLE_ENTERPRISE_CACHE_USER }}
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }}
|
||||
run: ./gradlew -PdeploymentRepository=$(pwd)/deployment-repository build publishAllPublicationsToDeploymentRepository
|
||||
- name: Deploy
|
||||
uses: spring-io/artifactory-deploy-action@v0.0.1
|
||||
with:
|
||||
uri: 'https://repo.spring.io'
|
||||
username: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
password: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
build-name: ${{ format('spring-framework-{0}', github.ref_name)}}
|
||||
repository: 'libs-snapshot-local'
|
||||
folder: 'deployment-repository'
|
||||
signing-key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
signing-passphrase: ${{ secrets.GPG_PASSPHRASE }}
|
||||
artifact-properties: |
|
||||
/**/framework-api-*.zip::zip.name=spring-framework,zip.deployed=false
|
||||
/**/framework-api-*-docs.zip::zip.type=docs
|
||||
/**/framework-api-*-schema.zip::zip.type=schema
|
||||
- name: Send notification
|
||||
uses: ./.github/actions/send-notification
|
||||
if: always()
|
||||
with:
|
||||
webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
|
||||
status: ${{ job.status }}
|
||||
build-scan-url: ${{ steps.build.outputs.build-scan-url }}
|
||||
run-name: ${{ format('{0} | Linux | Java 17', github.ref_name) }}
|
||||
@@ -0,0 +1,80 @@
|
||||
name: CI
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 6.1.x
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
jobs:
|
||||
ci:
|
||||
if: ${{ github.repository == 'spring-projects/spring-framework' }}
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- id: ubuntu-latest
|
||||
name: Linux
|
||||
java:
|
||||
- version: 17
|
||||
toolchain: false
|
||||
- version: 21
|
||||
toolchain: true
|
||||
exclude:
|
||||
- os:
|
||||
name: Linux
|
||||
java:
|
||||
version: 17
|
||||
name: '${{ matrix.os.name}} | Java ${{ matrix.java.version}}'
|
||||
runs-on: ${{ matrix.os.id }}
|
||||
steps:
|
||||
- name: Set up Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'liberica'
|
||||
java-version: |
|
||||
${{ matrix.java.version }}
|
||||
${{ matrix.java.toolchain && '17' || '' }}
|
||||
- name: Prepare Windows runner
|
||||
if: ${{ runner.os == 'Windows' }}
|
||||
run: |
|
||||
git config --global core.autocrlf true
|
||||
git config --global core.longPaths true
|
||||
Stop-Service -name Docker
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
|
||||
with:
|
||||
cache-read-only: false
|
||||
- name: Configure Gradle properties
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p $HOME/.gradle
|
||||
echo 'systemProp.user.name=spring-builds+github' >> $HOME/.gradle/gradle.properties
|
||||
echo 'systemProp.org.gradle.internal.launcher.welcomeMessageEnabled=false' >> $HOME/.gradle/gradle.properties
|
||||
echo 'org.gradle.daemon=false' >> $HOME/.gradle/gradle.properties
|
||||
echo 'org.gradle.daemon=4' >> $HOME/.gradle/gradle.properties
|
||||
- name: Configure toolchain properties
|
||||
if: ${{ matrix.java.toolchain }}
|
||||
shell: bash
|
||||
run: |
|
||||
echo toolchainVersion=${{ matrix.java.version }} >> $HOME/.gradle/gradle.properties
|
||||
echo systemProp.org.gradle.java.installations.auto-detect=false >> $HOME/.gradle/gradle.properties
|
||||
echo systemProp.org.gradle.java.installations.auto-download=false >> $HOME/.gradle/gradle.properties
|
||||
echo systemProp.org.gradle.java.installations.paths=${{ format('$JAVA_HOME_{0}_X64', matrix.java.version) }} >> $HOME/.gradle/gradle.properties
|
||||
- name: Build
|
||||
id: build
|
||||
env:
|
||||
CI: 'true'
|
||||
GRADLE_ENTERPRISE_URL: 'https://ge.spring.io'
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GRADLE_ENTERPRISE_CACHE_USER }}
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }}
|
||||
run: ./gradlew check antora
|
||||
- name: Send notification
|
||||
uses: ./.github/actions/send-notification
|
||||
if: always()
|
||||
with:
|
||||
webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
|
||||
status: ${{ job.status }}
|
||||
build-scan-url: ${{ steps.build.outputs.build-scan-url }}
|
||||
run-name: ${{ format('{0} | {1} | Java {2}', github.ref_name, matrix.os.name, matrix.java.version) }}
|
||||
@@ -1,12 +1,14 @@
|
||||
name: Deploy Docs
|
||||
on:
|
||||
push:
|
||||
branches-ignore: [ gh-pages ]
|
||||
tags: '**'
|
||||
branches:
|
||||
- 'main'
|
||||
- '*.x'
|
||||
- '!gh-pages'
|
||||
tags:
|
||||
- 'v*'
|
||||
repository_dispatch:
|
||||
types: request-build-reference # legacy
|
||||
schedule:
|
||||
- cron: '0 10 * * *' # Once per day at 10am UTC
|
||||
workflow_dispatch:
|
||||
permissions:
|
||||
actions: write
|
||||
@@ -15,8 +17,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository_owner == 'spring-projects'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: docs-build
|
||||
fetch-depth: 1
|
||||
|
||||
@@ -9,5 +9,5 @@ jobs:
|
||||
name: "Validation"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: gradle/wrapper-validation-action@v1
|
||||
- uses: actions/checkout@v4
|
||||
- uses: gradle/wrapper-validation-action@v2
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# <img src="framework-docs/src/docs/spring-framework.png" width="80" height="80"> Spring Framework [](https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-6.1.x?groups=Build") [](https://ge.spring.io/scans?search.rootProjectNames=spring)
|
||||
# <img src="framework-docs/src/docs/spring-framework.png" width="80" height="80"> Spring Framework [](https://github.com/spring-projects/spring-framework/actions/workflows/build-and-deploy-snapshot.yml?query=branch%3A6.1.x) [](https://ge.spring.io/scans?search.rootProjectNames=spring)
|
||||
|
||||
This is the home of the Spring Framework: the foundation for all [Spring projects](https://spring.io/projects). Collectively the Spring Framework and the family of Spring projects are often referred to simply as "Spring".
|
||||
|
||||
|
||||
@@ -90,7 +90,6 @@ configure([rootProject] + javaProjects) { project ->
|
||||
"https://docs.oracle.com/en/java/javase/17/docs/api/",
|
||||
"https://jakarta.ee/specifications/platform/9/apidocs/",
|
||||
"https://docs.oracle.com/cd/E13222_01/wls/docs90/javadocs/", // CommonJ and weblogic.* packages
|
||||
"https://www.ibm.com/docs/api/v1/content/SSEQTP_8.5.5/com.ibm.websphere.javadoc.doc/web/apidocs/", // com.ibm.*
|
||||
"https://docs.jboss.org/jbossas/javadoc/4.0.5/connector/", // org.jboss.resource.*
|
||||
"https://docs.jboss.org/hibernate/orm/5.6/javadocs/",
|
||||
"https://eclipse.dev/aspectj/doc/released/aspectj5rt-api",
|
||||
|
||||
@@ -50,7 +50,7 @@ public class CheckstyleConventions {
|
||||
project.getPlugins().apply(CheckstylePlugin.class);
|
||||
project.getTasks().withType(Checkstyle.class).forEach(checkstyle -> checkstyle.getMaxHeapSize().set("1g"));
|
||||
CheckstyleExtension checkstyle = project.getExtensions().getByType(CheckstyleExtension.class);
|
||||
checkstyle.setToolVersion("10.14.1");
|
||||
checkstyle.setToolVersion("10.15.0");
|
||||
checkstyle.getConfigDirectory().set(project.getRootProject().file("src/checkstyle"));
|
||||
String version = SpringJavaFormatPlugin.class.getPackage().getImplementationVersion();
|
||||
DependencySet checkstyleDependencies = project.getConfigurations().getByName("checkstyle").getDependencies();
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
== Spring Framework Concourse pipeline
|
||||
|
||||
NOTE: CI is being migrated to GitHub Actions.
|
||||
|
||||
The Spring Framework uses https://concourse-ci.org/[Concourse] for its CI build and other automated tasks.
|
||||
The Spring team has a dedicated Concourse instance available at https://ci.spring.io with a build pipeline
|
||||
for https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-6.1.x[Spring Framework 6.1.x].
|
||||
|
||||
@@ -8,4 +8,3 @@ milestone: "6.1.x"
|
||||
build-name: "spring-framework"
|
||||
pipeline-name: "spring-framework"
|
||||
concourse-url: "https://ci.spring.io"
|
||||
task-timeout: 1h00m
|
||||
|
||||
+25
-165
@@ -23,14 +23,6 @@ anchors:
|
||||
docker-resource-source: &docker-resource-source
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
slack-fail-params: &slack-fail-params
|
||||
text: >
|
||||
:concourse-failed: <https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}|${BUILD_PIPELINE_NAME} ${BUILD_JOB_NAME} failed!>
|
||||
[$TEXT_FILE_CONTENT]
|
||||
text_file: git-repo/build/build-scan-uri.txt
|
||||
silent: true
|
||||
icon_emoji: ":concourse:"
|
||||
username: concourse-ci
|
||||
changelog-task-params: &changelog-task-params
|
||||
name: generated-changelog/tag
|
||||
tag: generated-changelog/tag
|
||||
@@ -64,12 +56,6 @@ resource_types:
|
||||
<<: *docker-resource-source
|
||||
repository: dpb587/github-status-resource
|
||||
tag: master
|
||||
- name: slack-notification
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: cfcommunity/slack-notification-resource
|
||||
tag: latest
|
||||
resources:
|
||||
- name: git-repo
|
||||
type: git
|
||||
@@ -90,13 +76,6 @@ resources:
|
||||
<<: *docker-resource-source
|
||||
repository: ((docker-hub-organization))/spring-framework-ci
|
||||
tag: ((milestone))
|
||||
- name: every-morning
|
||||
type: time
|
||||
icon: alarm
|
||||
source:
|
||||
start: 8:00 AM
|
||||
stop: 9:00 AM
|
||||
location: Europe/Vienna
|
||||
- name: artifactory-repo
|
||||
type: artifactory-resource
|
||||
icon: package-variant
|
||||
@@ -105,35 +84,6 @@ resources:
|
||||
username: ((artifactory-username))
|
||||
password: ((artifactory-password))
|
||||
build_name: ((build-name))
|
||||
- name: repo-status-build
|
||||
type: github-status-resource
|
||||
icon: eye-check-outline
|
||||
source:
|
||||
repository: ((github-repo-name))
|
||||
access_token: ((github-ci-status-token))
|
||||
branch: ((branch))
|
||||
context: build
|
||||
- name: repo-status-jdk21-build
|
||||
type: github-status-resource
|
||||
icon: eye-check-outline
|
||||
source:
|
||||
repository: ((github-repo-name))
|
||||
access_token: ((github-ci-status-token))
|
||||
branch: ((branch))
|
||||
context: jdk21-build
|
||||
- name: repo-status-jdk23-build
|
||||
type: github-status-resource
|
||||
icon: eye-check-outline
|
||||
source:
|
||||
repository: ((github-repo-name))
|
||||
access_token: ((github-ci-status-token))
|
||||
branch: ((branch))
|
||||
context: jdk23-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
|
||||
@@ -168,115 +118,6 @@ jobs:
|
||||
- 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:
|
||||
- "/**/framework-api-*.zip"
|
||||
properties:
|
||||
"zip.name": "spring-framework"
|
||||
"zip.displayname": "Spring Framework"
|
||||
"zip.deployed": "false"
|
||||
- include:
|
||||
- "/**/framework-api-*-docs.zip"
|
||||
properties:
|
||||
"zip.type": "docs"
|
||||
- include:
|
||||
- "/**/framework-api-*-schema.zip"
|
||||
properties:
|
||||
"zip.type": "schema"
|
||||
get_params:
|
||||
threads: 8
|
||||
- name: jdk21-build
|
||||
serial: true
|
||||
public: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
- get: every-morning
|
||||
trigger: true
|
||||
- put: repo-status-jdk21-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: 21
|
||||
<<: *build-project-task-params
|
||||
on_failure:
|
||||
do:
|
||||
- put: repo-status-jdk21-build
|
||||
params: { state: "failure", commit: "git-repo" }
|
||||
- put: slack-alert
|
||||
params:
|
||||
<<: *slack-fail-params
|
||||
- put: repo-status-jdk21-build
|
||||
params: { state: "success", commit: "git-repo" }
|
||||
- name: jdk23-build
|
||||
serial: true
|
||||
public: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
- get: every-morning
|
||||
trigger: false
|
||||
- put: repo-status-jdk23-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: 23
|
||||
<<: *build-project-task-params
|
||||
on_failure:
|
||||
do:
|
||||
- put: repo-status-jdk23-build
|
||||
params: { state: "failure", commit: "git-repo" }
|
||||
- put: slack-alert
|
||||
params:
|
||||
<<: *slack-fail-params
|
||||
- put: repo-status-jdk23-build
|
||||
params: { state: "success", commit: "git-repo" }
|
||||
- name: stage-milestone
|
||||
serial: true
|
||||
plan:
|
||||
@@ -290,9 +131,32 @@ jobs:
|
||||
RELEASE_TYPE: M
|
||||
<<: *gradle-enterprise-task-params
|
||||
- put: artifactory-repo
|
||||
params:
|
||||
<<: *artifactory-params
|
||||
params: &artifactory-params
|
||||
signing_key: ((signing-key))
|
||||
signing_passphrase: ((signing-passphrase))
|
||||
repo: libs-staging-local
|
||||
folder: distribution-repository
|
||||
build_uri: "https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}"
|
||||
build_number: "${BUILD_PIPELINE_NAME}-${BUILD_JOB_NAME}-${BUILD_NAME}"
|
||||
disable_checksum_uploads: true
|
||||
threads: 8
|
||||
artifact_set:
|
||||
- include:
|
||||
- "/**/framework-api-*.zip"
|
||||
properties:
|
||||
"zip.name": "spring-framework"
|
||||
"zip.displayname": "Spring Framework"
|
||||
"zip.deployed": "false"
|
||||
- include:
|
||||
- "/**/framework-api-*-docs.zip"
|
||||
properties:
|
||||
"zip.type": "docs"
|
||||
- include:
|
||||
- "/**/framework-api-*-schema.zip"
|
||||
properties:
|
||||
"zip.type": "schema"
|
||||
get_params:
|
||||
threads: 8
|
||||
- put: git-repo
|
||||
params:
|
||||
repository: stage-git-repo
|
||||
@@ -337,7 +201,6 @@ jobs:
|
||||
- put: artifactory-repo
|
||||
params:
|
||||
<<: *artifactory-params
|
||||
repo: libs-staging-local
|
||||
- put: git-repo
|
||||
params:
|
||||
repository: stage-git-repo
|
||||
@@ -382,7 +245,6 @@ jobs:
|
||||
- put: artifactory-repo
|
||||
params:
|
||||
<<: *artifactory-params
|
||||
repo: libs-staging-local
|
||||
- put: git-repo
|
||||
params:
|
||||
repository: stage-git-repo
|
||||
@@ -427,8 +289,6 @@ jobs:
|
||||
<<: *changelog-task-params
|
||||
|
||||
groups:
|
||||
- name: "builds"
|
||||
jobs: ["build", "jdk21-build", "jdk23-build"]
|
||||
- name: "releases"
|
||||
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release"]
|
||||
- name: "ci-images"
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
source $(dirname $0)/common.sh
|
||||
|
||||
pushd git-repo > /dev/null
|
||||
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK17,JDK21 \
|
||||
--no-daemon --max-workers=4 check
|
||||
popd > /dev/null
|
||||
@@ -1,10 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
source $(dirname $0)/common.sh
|
||||
repository=$(pwd)/distribution-repository
|
||||
|
||||
pushd git-repo > /dev/null
|
||||
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK17,JDK21,JDK23 \
|
||||
--no-daemon --max-workers=4 -PdeploymentRepository=${repository} build publishAllPublicationsToDeploymentRepository
|
||||
popd > /dev/null
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
source $(dirname $0)/common.sh
|
||||
|
||||
pushd git-repo > /dev/null
|
||||
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK17,JDK21,JDK23 \
|
||||
-PmainToolchain=${MAIN_TOOLCHAIN} -PtestToolchain=${TEST_TOOLCHAIN} --no-daemon --max-workers=4 check antora
|
||||
popd > /dev/null
|
||||
@@ -1,19 +0,0 @@
|
||||
---
|
||||
platform: linux
|
||||
inputs:
|
||||
- name: git-repo
|
||||
caches:
|
||||
- path: gradle
|
||||
params:
|
||||
BRANCH:
|
||||
CI: true
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY:
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME:
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD:
|
||||
GRADLE_ENTERPRISE_URL: https://ge.spring.io
|
||||
run:
|
||||
path: bash
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
${PWD}/git-repo/ci/scripts/build-pr.sh
|
||||
@@ -1,22 +0,0 @@
|
||||
---
|
||||
platform: linux
|
||||
inputs:
|
||||
- name: git-repo
|
||||
outputs:
|
||||
- name: distribution-repository
|
||||
- name: git-repo
|
||||
caches:
|
||||
- path: gradle
|
||||
params:
|
||||
BRANCH:
|
||||
CI: true
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY:
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME:
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD:
|
||||
GRADLE_ENTERPRISE_URL: https://ge.spring.io
|
||||
run:
|
||||
path: bash
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
${PWD}/git-repo/ci/scripts/build-project.sh
|
||||
@@ -1,24 +0,0 @@
|
||||
---
|
||||
platform: linux
|
||||
inputs:
|
||||
- name: git-repo
|
||||
outputs:
|
||||
- name: distribution-repository
|
||||
- name: git-repo
|
||||
caches:
|
||||
- path: gradle
|
||||
params:
|
||||
BRANCH:
|
||||
CI: true
|
||||
MAIN_TOOLCHAIN:
|
||||
TEST_TOOLCHAIN:
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY:
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME:
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD:
|
||||
GRADLE_ENTERPRISE_URL: https://ge.spring.io
|
||||
run:
|
||||
path: bash
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
${PWD}/git-repo/ci/scripts/check-project.sh
|
||||
@@ -15,7 +15,7 @@ Applying such optimizations early implies the following restrictions:
|
||||
|
||||
* The classpath is fixed and fully defined at build time.
|
||||
* The beans defined in your application cannot change at runtime, meaning:
|
||||
** `@Profile`, in particular profile-specific configuration needs to be chosen at build time.
|
||||
** `@Profile`, in particular profile-specific configuration, needs to be chosen at build time and is automatically enabled at runtime when AOT is enabled.
|
||||
** `Environment` properties that impact the presence of a bean (`@Conditional`) are only considered at build time.
|
||||
* Bean definitions with instance suppliers (lambdas or method references) cannot be transformed ahead-of-time.
|
||||
* Beans registered as singletons (using `registerSingleton`, typically from
|
||||
@@ -152,8 +152,23 @@ Java::
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
class DataSourceConfiguration {
|
||||
|
||||
@Bean
|
||||
fun dataSource() = SimpleDataSource()
|
||||
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
WARNING: Kotlin class names with backticks that use invalid Java identifiers (not starting with a letter, containing spaces, etc.) are not supported.
|
||||
|
||||
Since there isn't any particular condition on this class, `dataSourceConfiguration` and `dataSource` are identified as candidates.
|
||||
The AOT engine will convert the configuration class above to code similar to the following:
|
||||
|
||||
@@ -209,6 +224,17 @@ There is a bean definition for `dataSourceConfiguration` and one for `dataSource
|
||||
When a `datasource` instance is required, a `BeanInstanceSupplier` is called.
|
||||
This supplier invokes the `dataSource()` method on the `dataSourceConfiguration` bean.
|
||||
|
||||
[[aot.running]]
|
||||
== Running with AOT optimizations
|
||||
|
||||
AOT is a mandatory step to transform a Spring application to a native executable, so it
|
||||
is automatically enabled when running in this mode. It is possible to use those optimizations
|
||||
on the JVM by setting the `spring.aot.enabled` System property to `true`.
|
||||
|
||||
NOTE: When AOT optimizations are included, some decisions that have been taken at build-time
|
||||
are hard-coded in the application setup. For instance, profiles that have been enabled at
|
||||
build-time are automatically enabled at runtime as well.
|
||||
|
||||
[[aot.bestpractices]]
|
||||
== Best Practices
|
||||
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ As of Spring Framework 5.2, the default configuration also provides support for
|
||||
Vavr's `Try` method to trigger transaction rollbacks when it returns a 'Failure'.
|
||||
This allows you to handle functional-style errors using Try and have the transaction
|
||||
automatically rolled back in case of a failure. For more information on Vavr's Try,
|
||||
refer to the [official Vavr documentation](https://www.vavr.io/vavr-docs/#_try).
|
||||
refer to the https://docs.vavr.io/#_try[official Vavr documentation].
|
||||
|
||||
Here's an example of how to use Vavr's Try with a transactional method:
|
||||
[tabs]
|
||||
|
||||
@@ -81,7 +81,7 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
NOTE: `@JsonView` allows an array of view classes but you can only specify only one per
|
||||
NOTE: `@JsonView` allows an array of view classes but you can specify only one per
|
||||
controller method. Use a composite interface if you need to activate multiple views.
|
||||
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ to render the response content as XML. If the model contains multiple entries, y
|
||||
explicitly set the object to be serialized by using the `modelKey` bean property. If the
|
||||
model contains a single entry, it is serialized automatically.
|
||||
|
||||
You can customized XML mapping as needed by using JAXB or Jackson's provided
|
||||
You can customize XML mapping as needed by using JAXB or Jackson's provided
|
||||
annotations. When you need further control, you can inject a custom `XmlMapper`
|
||||
through the `ObjectMapper` property, for cases where custom XML
|
||||
you need to provide serializers and deserializers for specific types.
|
||||
|
||||
@@ -683,7 +683,7 @@ the HTML would be as follows:
|
||||
----
|
||||
|
||||
What if we want to display the entire list of errors for a given page? The next example
|
||||
shows that the `errors` tag also supports some basic wildcarding functionality.
|
||||
shows that the `errors` tag also supports some basic wildcard functionality.
|
||||
|
||||
* `path="{asterisk}"`: Displays all errors.
|
||||
* `path="lastName"`: Displays all errors associated with the `lastName` field.
|
||||
|
||||
@@ -500,8 +500,8 @@ The MVC configuration exposes the following options for asynchronous request pro
|
||||
|
||||
You can configure the following:
|
||||
|
||||
* Default timeout value for async requests, which if not set, depends
|
||||
on the underlying Servlet container.
|
||||
* The default timeout value for async requests depends
|
||||
on the underlying Servlet container, unless it is set explicitly.
|
||||
* `AsyncTaskExecutor` to use for blocking writes when streaming with
|
||||
xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-reactive-types[Reactive Types] and for
|
||||
executing `Callable` instances returned from controller methods.
|
||||
|
||||
@@ -79,9 +79,11 @@ message code arguments for the "detail" field. `ResponseEntityExceptionHandler`
|
||||
these through a xref:core/beans/context-introduction.adoc#context-functionality-messagesource[MessageSource]
|
||||
and updates the corresponding `ProblemDetail` fields accordingly.
|
||||
|
||||
The default strategy for message codes follows the pattern:
|
||||
The default strategy for message codes is as follows:
|
||||
|
||||
`problemDetail.[type|title|detail].[fully qualified exception class name]`
|
||||
* "type": `problemDetail.type.[fully qualified exception class name]`
|
||||
* "title": `problemDetail.title.[fully qualified exception class name]`
|
||||
* "detail": `problemDetail.[fully qualified exception class name][suffix]`
|
||||
|
||||
An `ErrorResponse` may expose more than one message code, typically adding a suffix
|
||||
to the default message code. The table below lists message codes, and arguments for
|
||||
@@ -136,7 +138,7 @@ Message codes and arguments for each error are also resolved via `MessageSource`
|
||||
| `MethodArgumentNotValidException`
|
||||
| (default)
|
||||
| `+{0}+` the list of global errors, `+{1}+` the list of field errors.
|
||||
Message codes and arguments for each error are also resolvedvia `MessageSource`.
|
||||
Message codes and arguments for each error are also resolved via `MessageSource`.
|
||||
|
||||
| `MissingRequestHeaderException`
|
||||
| (default)
|
||||
|
||||
@@ -8,12 +8,12 @@ javaPlatform {
|
||||
|
||||
dependencies {
|
||||
api(platform("com.fasterxml.jackson:jackson-bom:2.15.4"))
|
||||
api(platform("io.micrometer:micrometer-bom:1.12.4"))
|
||||
api(platform("io.netty:netty-bom:4.1.107.Final"))
|
||||
api(platform("io.micrometer:micrometer-bom:1.12.5"))
|
||||
api(platform("io.netty:netty-bom:4.1.108.Final"))
|
||||
api(platform("io.netty:netty5-bom:5.0.0.Alpha5"))
|
||||
api(platform("io.projectreactor:reactor-bom:2023.0.4"))
|
||||
api(platform("io.projectreactor:reactor-bom:2023.0.5"))
|
||||
api(platform("io.rsocket:rsocket-bom:1.1.3"))
|
||||
api(platform("org.apache.groovy:groovy-bom:4.0.19"))
|
||||
api(platform("org.apache.groovy:groovy-bom:4.0.20"))
|
||||
api(platform("org.apache.logging.log4j:log4j-bom:2.21.1"))
|
||||
api(platform("org.assertj:assertj-bom:3.25.3"))
|
||||
api(platform("org.eclipse.jetty:jetty-bom:12.0.7"))
|
||||
@@ -25,9 +25,9 @@ dependencies {
|
||||
|
||||
constraints {
|
||||
api("com.fasterxml:aalto-xml:1.3.2")
|
||||
api("com.fasterxml.woodstox:woodstox-core:6.6.1")
|
||||
api("com.fasterxml.woodstox:woodstox-core:6.6.2")
|
||||
api("com.github.ben-manes.caffeine:caffeine:3.1.8")
|
||||
api("com.github.librepdf:openpdf:1.3.42")
|
||||
api("com.github.librepdf:openpdf:1.3.43")
|
||||
api("com.google.code.findbugs:findbugs:3.0.1")
|
||||
api("com.google.code.findbugs:jsr305:3.0.2")
|
||||
api("com.google.code.gson:gson:2.10.1")
|
||||
@@ -103,13 +103,13 @@ dependencies {
|
||||
api("org.apache.httpcomponents.client5:httpclient5:5.3.1")
|
||||
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.2.4")
|
||||
api("org.apache.poi:poi-ooxml:5.2.5")
|
||||
api("org.apache.tomcat.embed:tomcat-embed-core:10.1.19")
|
||||
api("org.apache.tomcat.embed:tomcat-embed-websocket:10.1.19")
|
||||
api("org.apache.tomcat:tomcat-util:10.1.19")
|
||||
api("org.apache.tomcat:tomcat-websocket:10.1.19")
|
||||
api("org.aspectj:aspectjrt:1.9.21.1")
|
||||
api("org.aspectj:aspectjtools:1.9.21.1")
|
||||
api("org.aspectj:aspectjweaver:1.9.21.1")
|
||||
api("org.apache.tomcat.embed:tomcat-embed-core:10.1.20")
|
||||
api("org.apache.tomcat.embed:tomcat-embed-websocket:10.1.20")
|
||||
api("org.apache.tomcat:tomcat-util:10.1.20")
|
||||
api("org.apache.tomcat:tomcat-websocket:10.1.20")
|
||||
api("org.aspectj:aspectjrt:1.9.22")
|
||||
api("org.aspectj:aspectjtools:1.9.22")
|
||||
api("org.aspectj:aspectjweaver:1.9.22")
|
||||
api("org.awaitility:awaitility:4.2.0")
|
||||
api("org.bouncycastle:bcpkix-jdk18on:1.72")
|
||||
api("org.codehaus.jettison:jettison:1.5.4")
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
version=6.1.5-SNAPSHOT
|
||||
version=6.1.6
|
||||
|
||||
org.gradle.caching=true
|
||||
org.gradle.jvmargs=-Xmx2048m
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+1
-1
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
+2
@@ -22,6 +22,7 @@ import org.springframework.beans.factory.aot.BeanRegistrationAotContribution;
|
||||
import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor;
|
||||
import org.springframework.beans.factory.aot.BeanRegistrationCode;
|
||||
import org.springframework.beans.factory.support.RegisteredBean;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
@@ -38,6 +39,7 @@ class AspectJAdvisorBeanRegistrationAotProcessor implements BeanRegistrationAotP
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
|
||||
if (aspectjPresent) {
|
||||
Class<?> beanClass = registeredBean.getBeanClass();
|
||||
|
||||
+2
@@ -23,6 +23,7 @@ import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* {@link BeanDefinitionParser} responsible for parsing the
|
||||
@@ -51,6 +52,7 @@ class SpringConfiguredBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
if (!parserContext.getRegistry().containsBeanDefinition(BEAN_CONFIGURER_ASPECT_BEAN_NAME)) {
|
||||
RootBeanDefinition def = new RootBeanDefinition();
|
||||
|
||||
@@ -94,14 +94,17 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
private static final int INVOKE_HASHCODE = 6;
|
||||
|
||||
|
||||
private static final String COROUTINES_FLOW_CLASS_NAME = "kotlinx.coroutines.flow.Flow";
|
||||
|
||||
private static final boolean coroutinesReactorPresent = ClassUtils.isPresent(
|
||||
"kotlinx.coroutines.reactor.MonoKt", CglibAopProxy.class.getClassLoader());
|
||||
|
||||
/** Logger available to subclasses; static to optimize serialization. */
|
||||
protected static final Log logger = LogFactory.getLog(CglibAopProxy.class);
|
||||
|
||||
/** Keeps track of the Classes that we have validated for final methods. */
|
||||
private static final Map<Class<?>, Boolean> validatedClasses = new WeakHashMap<>();
|
||||
|
||||
private static final String COROUTINES_FLOW_CLASS_NAME = "kotlinx.coroutines.flow.Flow";
|
||||
|
||||
|
||||
/** The configuration used to configure this proxy. */
|
||||
protected final AdvisedSupport advised;
|
||||
@@ -421,7 +424,7 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
throw new AopInvocationException(
|
||||
"Null return value from advice does not match primitive return type for: " + method);
|
||||
}
|
||||
if (KotlinDetector.isSuspendingFunction(method)) {
|
||||
if (coroutinesReactorPresent && KotlinDetector.isSuspendingFunction(method)) {
|
||||
return COROUTINES_FLOW_CLASS_NAME.equals(new MethodParameter(method, -1).getParameterType().getName()) ?
|
||||
CoroutinesUtils.asFlow(returnValue) :
|
||||
CoroutinesUtils.awaitSingleOrNull(returnValue, arguments[arguments.length - 1]);
|
||||
|
||||
@@ -75,6 +75,9 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
|
||||
|
||||
private static final String COROUTINES_FLOW_CLASS_NAME = "kotlinx.coroutines.flow.Flow";
|
||||
|
||||
private static final boolean coroutinesReactorPresent = ClassUtils.isPresent(
|
||||
"kotlinx.coroutines.reactor.MonoKt", JdkDynamicAopProxy.class.getClassLoader());
|
||||
|
||||
/** We use a static Log to avoid serialization issues. */
|
||||
private static final Log logger = LogFactory.getLog(JdkDynamicAopProxy.class);
|
||||
|
||||
@@ -234,7 +237,7 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
|
||||
throw new AopInvocationException(
|
||||
"Null return value from advice does not match primitive return type for: " + method);
|
||||
}
|
||||
if (KotlinDetector.isSuspendingFunction(method)) {
|
||||
if (coroutinesReactorPresent && KotlinDetector.isSuspendingFunction(method)) {
|
||||
return COROUTINES_FLOW_CLASS_NAME.equals(new MethodParameter(method, -1).getParameterType().getName()) ?
|
||||
CoroutinesUtils.asFlow(retVal) : CoroutinesUtils.awaitSingleOrNull(retVal, args[args.length - 1]);
|
||||
}
|
||||
|
||||
+2
@@ -270,6 +270,7 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
|
||||
Object cacheKey = getCacheKey(beanClass, beanName);
|
||||
|
||||
@@ -311,6 +312,7 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
|
||||
* @see #getAdvicesAndAdvisorsForBean
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
|
||||
if (bean != null) {
|
||||
Object cacheKey = getCacheKey(bean.getClass(), beanName);
|
||||
|
||||
+1
@@ -81,6 +81,7 @@ public class BeanNameAutoProxyCreator extends AbstractAutoProxyCreator {
|
||||
* @see #setBeanNames(String...)
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
protected TargetSource getCustomTargetSource(Class<?> beanClass, String beanName) {
|
||||
return (isSupportedBeanName(beanClass, beanName) ?
|
||||
super.getCustomTargetSource(beanClass, beanName) : null);
|
||||
|
||||
+1
@@ -53,6 +53,7 @@ class ScopedProxyBeanRegistrationAotProcessor implements BeanRegistrationAotProc
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
|
||||
Class<?> beanClass = registeredBean.getBeanClass();
|
||||
if (beanClass.equals(ScopedProxyFactoryBean.class)) {
|
||||
|
||||
@@ -117,6 +117,7 @@ public class ScopedProxyFactoryBean extends ProxyConfig
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object getObject() {
|
||||
if (this.proxy == null) {
|
||||
throw new FactoryBeanNotInitializedException();
|
||||
@@ -125,6 +126,7 @@ public class ScopedProxyFactoryBean extends ProxyConfig
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Class<?> getObjectType() {
|
||||
if (this.proxy != null) {
|
||||
return this.proxy.getClass();
|
||||
|
||||
@@ -28,7 +28,6 @@ import java.util.Set;
|
||||
import kotlin.coroutines.Continuation;
|
||||
import kotlin.coroutines.CoroutineContext;
|
||||
import kotlinx.coroutines.Job;
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.aop.AopInvocationException;
|
||||
@@ -65,6 +64,10 @@ import org.springframework.util.ReflectionUtils;
|
||||
*/
|
||||
public abstract class AopUtils {
|
||||
|
||||
private static final boolean coroutinesReactorPresent = ClassUtils.isPresent(
|
||||
"kotlinx.coroutines.reactor.MonoKt", AopUtils.class.getClassLoader());
|
||||
|
||||
|
||||
/**
|
||||
* Check whether the given object is a JDK dynamic proxy or a CGLIB proxy.
|
||||
* <p>This method additionally checks if the given object is an instance
|
||||
@@ -347,8 +350,8 @@ public abstract class AopUtils {
|
||||
// Use reflection to invoke the method.
|
||||
try {
|
||||
ReflectionUtils.makeAccessible(method);
|
||||
return KotlinDetector.isSuspendingFunction(method) ?
|
||||
KotlinDelegate.invokeSuspendingFunction(method, target, args) : method.invoke(target, args);
|
||||
return (coroutinesReactorPresent && KotlinDetector.isSuspendingFunction(method) ?
|
||||
KotlinDelegate.invokeSuspendingFunction(method, target, args) : method.invoke(target, args));
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
// Invoked method threw a checked exception.
|
||||
@@ -364,18 +367,18 @@ public abstract class AopUtils {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inner class to avoid a hard dependency on Kotlin at runtime.
|
||||
*/
|
||||
private static class KotlinDelegate {
|
||||
|
||||
public static Publisher<?> invokeSuspendingFunction(Method method, Object target, Object... args) {
|
||||
public static Object invokeSuspendingFunction(Method method, @Nullable Object target, Object... args) {
|
||||
Continuation<?> continuation = (Continuation<?>) args[args.length -1];
|
||||
Assert.state(continuation != null, "No Continuation available");
|
||||
CoroutineContext context = continuation.getContext().minusKey(Job.Key);
|
||||
return CoroutinesUtils.invokeSuspendingFunction(context, method, target, args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+7
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -845,8 +845,10 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA
|
||||
* @return the PropertyAccessor instance, either cached or newly created
|
||||
*/
|
||||
private AbstractNestablePropertyAccessor getNestedPropertyAccessor(String nestedProperty) {
|
||||
if (this.nestedPropertyAccessors == null) {
|
||||
this.nestedPropertyAccessors = new HashMap<>();
|
||||
Map<String, AbstractNestablePropertyAccessor> nestedAccessors = this.nestedPropertyAccessors;
|
||||
if (nestedAccessors == null) {
|
||||
nestedAccessors = new HashMap<>();
|
||||
this.nestedPropertyAccessors = nestedAccessors;
|
||||
}
|
||||
// Get value of bean property.
|
||||
PropertyTokenHolder tokens = getPropertyNameTokens(nestedProperty);
|
||||
@@ -862,7 +864,7 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA
|
||||
}
|
||||
|
||||
// Lookup cached sub-PropertyAccessor, create new one if not found.
|
||||
AbstractNestablePropertyAccessor nestedPa = this.nestedPropertyAccessors.get(canonicalName);
|
||||
AbstractNestablePropertyAccessor nestedPa = nestedAccessors.get(canonicalName);
|
||||
if (nestedPa == null || nestedPa.getWrappedInstance() != ObjectUtils.unwrapOptional(value)) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Creating new nested " + getClass().getSimpleName() + " for property '" + canonicalName + "'");
|
||||
@@ -871,7 +873,7 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA
|
||||
// Inherit all type-specific PropertyEditors.
|
||||
copyDefaultEditorsTo(nestedPa);
|
||||
copyCustomEditorsTo(nestedPa, canonicalName);
|
||||
this.nestedPropertyAccessors.put(canonicalName, nestedPa);
|
||||
nestedAccessors.put(canonicalName, nestedPa);
|
||||
}
|
||||
else {
|
||||
if (logger.isTraceEnabled()) {
|
||||
|
||||
+2
-2
@@ -69,7 +69,7 @@ public class BeanInstantiationException extends FatalBeanException {
|
||||
* @param cause the root cause
|
||||
* @since 4.3
|
||||
*/
|
||||
public BeanInstantiationException(Constructor<?> constructor, String msg, @Nullable Throwable cause) {
|
||||
public BeanInstantiationException(Constructor<?> constructor, @Nullable String msg, @Nullable Throwable cause) {
|
||||
super("Failed to instantiate [" + constructor.getDeclaringClass().getName() + "]: " + msg, cause);
|
||||
this.beanClass = constructor.getDeclaringClass();
|
||||
this.constructor = constructor;
|
||||
@@ -84,7 +84,7 @@ public class BeanInstantiationException extends FatalBeanException {
|
||||
* @param cause the root cause
|
||||
* @since 4.3
|
||||
*/
|
||||
public BeanInstantiationException(Method constructingMethod, String msg, @Nullable Throwable cause) {
|
||||
public BeanInstantiationException(Method constructingMethod, @Nullable String msg, @Nullable Throwable cause) {
|
||||
super("Failed to instantiate [" + constructingMethod.getReturnType().getName() + "]: " + msg, cause);
|
||||
this.beanClass = constructingMethod.getReturnType();
|
||||
this.constructor = null;
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.beans;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Thrown when a bean property getter or setter method throws an exception,
|
||||
* analogous to an InvocationTargetException.
|
||||
@@ -38,7 +40,7 @@ public class MethodInvocationException extends PropertyAccessException {
|
||||
* @param propertyChangeEvent the PropertyChangeEvent that resulted in an exception
|
||||
* @param cause the Throwable raised by the invoked method
|
||||
*/
|
||||
public MethodInvocationException(PropertyChangeEvent propertyChangeEvent, Throwable cause) {
|
||||
public MethodInvocationException(PropertyChangeEvent propertyChangeEvent, @Nullable Throwable cause) {
|
||||
super(propertyChangeEvent, "Property '" + propertyChangeEvent.getPropertyName() + "' threw exception", cause);
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -94,7 +94,7 @@ public class BeanCreationException extends FatalBeanException {
|
||||
* @param beanName the name of the bean requested
|
||||
* @param msg the detail message
|
||||
*/
|
||||
public BeanCreationException(@Nullable String resourceDescription, @Nullable String beanName, String msg) {
|
||||
public BeanCreationException(@Nullable String resourceDescription, @Nullable String beanName, @Nullable String msg) {
|
||||
super("Error creating bean with name '" + beanName + "'" +
|
||||
(resourceDescription != null ? " defined in " + resourceDescription : "") + ": " + msg);
|
||||
this.resourceDescription = resourceDescription;
|
||||
@@ -110,7 +110,7 @@ public class BeanCreationException extends FatalBeanException {
|
||||
* @param msg the detail message
|
||||
* @param cause the root cause
|
||||
*/
|
||||
public BeanCreationException(@Nullable String resourceDescription, String beanName, String msg, Throwable cause) {
|
||||
public BeanCreationException(@Nullable String resourceDescription, String beanName, @Nullable String msg, Throwable cause) {
|
||||
this(resourceDescription, beanName, msg);
|
||||
initCause(cause);
|
||||
}
|
||||
|
||||
+2
-2
@@ -44,7 +44,7 @@ public class UnsatisfiedDependencyException extends BeanCreationException {
|
||||
* @param msg the detail message
|
||||
*/
|
||||
public UnsatisfiedDependencyException(
|
||||
@Nullable String resourceDescription, @Nullable String beanName, String propertyName, String msg) {
|
||||
@Nullable String resourceDescription, @Nullable String beanName, String propertyName, @Nullable String msg) {
|
||||
|
||||
super(resourceDescription, beanName,
|
||||
"Unsatisfied dependency expressed through bean property '" + propertyName + "'" +
|
||||
@@ -75,7 +75,7 @@ public class UnsatisfiedDependencyException extends BeanCreationException {
|
||||
* @since 4.3
|
||||
*/
|
||||
public UnsatisfiedDependencyException(
|
||||
@Nullable String resourceDescription, @Nullable String beanName, @Nullable InjectionPoint injectionPoint, String msg) {
|
||||
@Nullable String resourceDescription, @Nullable String beanName, @Nullable InjectionPoint injectionPoint, @Nullable String msg) {
|
||||
|
||||
super(resourceDescription, beanName,
|
||||
"Unsatisfied dependency expressed through " + injectionPoint +
|
||||
|
||||
+5
@@ -37,6 +37,7 @@ import org.springframework.beans.factory.support.ManagedMap;
|
||||
import org.springframework.beans.factory.support.ManagedSet;
|
||||
import org.springframework.javapoet.AnnotationSpec;
|
||||
import org.springframework.javapoet.CodeBlock;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Code generator {@link Delegate} for common bean definition property values.
|
||||
@@ -101,6 +102,7 @@ abstract class BeanDefinitionPropertyValueCodeGeneratorDelegates {
|
||||
private static final CodeBlock EMPTY_RESULT = CodeBlock.of("$T.ofEntries()", ManagedMap.class);
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public CodeBlock generateCode(ValueCodeGenerator valueCodeGenerator, Object value) {
|
||||
if (value instanceof ManagedMap<?, ?> managedMap) {
|
||||
return generateManagedMapCode(valueCodeGenerator, managedMap);
|
||||
@@ -137,6 +139,7 @@ abstract class BeanDefinitionPropertyValueCodeGeneratorDelegates {
|
||||
private static class LinkedHashMapDelegate extends MapDelegate {
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected CodeBlock generateMapCode(ValueCodeGenerator valueCodeGenerator, Map<?, ?> map) {
|
||||
GeneratedMethods generatedMethods = valueCodeGenerator.getGeneratedMethods();
|
||||
if (map instanceof LinkedHashMap<?, ?> && generatedMethods != null) {
|
||||
@@ -172,6 +175,7 @@ abstract class BeanDefinitionPropertyValueCodeGeneratorDelegates {
|
||||
private static class BeanReferenceDelegate implements Delegate {
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public CodeBlock generateCode(ValueCodeGenerator valueCodeGenerator, Object value) {
|
||||
if (value instanceof RuntimeBeanReference runtimeBeanReference &&
|
||||
runtimeBeanReference.getBeanType() != null) {
|
||||
@@ -193,6 +197,7 @@ abstract class BeanDefinitionPropertyValueCodeGeneratorDelegates {
|
||||
private static class TypedStringValueDelegate implements Delegate {
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public CodeBlock generateCode(ValueCodeGenerator valueCodeGenerator, Object value) {
|
||||
if (value instanceof TypedStringValue typedStringValue) {
|
||||
return generateTypeStringValueCode(valueCodeGenerator, typedStringValue);
|
||||
|
||||
+1
@@ -226,6 +226,7 @@ public class FieldRetrievingFactoryBean
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Class<?> getObjectType() {
|
||||
return (this.fieldObject != null ? this.fieldObject.getType() : null);
|
||||
}
|
||||
|
||||
+1
@@ -136,6 +136,7 @@ public class MethodInvokingFactoryBean extends MethodInvokingBean implements Fac
|
||||
* or {@code null} if not known in advance.
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public Class<?> getObjectType() {
|
||||
if (!isPrepared()) {
|
||||
// Not fully initialized yet -> return null to indicate "not known yet".
|
||||
|
||||
+1
@@ -224,6 +224,7 @@ public class PropertyPathFactoryBean implements FactoryBean<Object>, BeanNameAwa
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Class<?> getObjectType() {
|
||||
return this.resultType;
|
||||
}
|
||||
|
||||
+1
@@ -335,6 +335,7 @@ public class ServiceLocatorFactoryBean implements FactoryBean<Object>, BeanFacto
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Class<?> getObjectType() {
|
||||
return this.serviceLocatorInterface;
|
||||
}
|
||||
|
||||
+12
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -54,6 +54,7 @@ import org.springframework.core.io.DescriptiveResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.EncodedResource;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -250,6 +251,7 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
|
||||
@SuppressWarnings("serial")
|
||||
Closure<Object> beans = new Closure<>(this) {
|
||||
@Override
|
||||
@Nullable
|
||||
public Object call(Object... args) {
|
||||
invokeBeanDefiningClosure((Closure<?>) args[0]);
|
||||
return null;
|
||||
@@ -425,6 +427,7 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
|
||||
|
||||
private boolean addDeferredProperty(String property, Object newValue) {
|
||||
if (newValue instanceof List || newValue instanceof Map) {
|
||||
Assert.state(this.currentBeanDefinition != null, "No current bean definition set");
|
||||
this.deferredProperties.put(this.currentBeanDefinition.getBeanName() + '.' + property,
|
||||
new DeferredProperty(this.currentBeanDefinition, property, newValue));
|
||||
return true;
|
||||
@@ -640,6 +643,7 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
|
||||
this.currentBeanDefinition = current;
|
||||
}
|
||||
}
|
||||
Assert.state(this.currentBeanDefinition != null, "No current bean definition set");
|
||||
this.currentBeanDefinition.addProperty(name, value);
|
||||
}
|
||||
|
||||
@@ -654,6 +658,7 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
|
||||
* </ul>
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public Object getProperty(String name) {
|
||||
Binding binding = getBinding();
|
||||
if (binding != null && binding.hasVariable(name)) {
|
||||
@@ -727,9 +732,10 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
|
||||
|
||||
private final String name;
|
||||
|
||||
@Nullable
|
||||
public Object value;
|
||||
|
||||
public DeferredProperty(GroovyBeanDefinitionWrapper beanDefinition, String name, Object value) {
|
||||
public DeferredProperty(GroovyBeanDefinitionWrapper beanDefinition, String name, @Nullable Object value) {
|
||||
this.beanDefinition = beanDefinition;
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
@@ -762,6 +768,7 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object getProperty(String property) {
|
||||
if (property.equals("beanName")) {
|
||||
return getBeanName();
|
||||
@@ -769,13 +776,10 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
|
||||
else if (property.equals("source")) {
|
||||
return getSource();
|
||||
}
|
||||
else if (this.beanDefinition != null) {
|
||||
else {
|
||||
return new GroovyPropertyValue(
|
||||
property, this.beanDefinition.getBeanDefinition().getPropertyValues().get(property));
|
||||
}
|
||||
else {
|
||||
return this.metaClass.getProperty(this, property);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -804,9 +808,10 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
|
||||
|
||||
private final String propertyName;
|
||||
|
||||
@Nullable
|
||||
private final Object propertyValue;
|
||||
|
||||
public GroovyPropertyValue(String propertyName, Object propertyValue) {
|
||||
public GroovyPropertyValue(String propertyName, @Nullable Object propertyValue) {
|
||||
this.propertyName = propertyName;
|
||||
this.propertyValue = propertyValue;
|
||||
}
|
||||
|
||||
+9
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -84,7 +84,7 @@ class GroovyBeanDefinitionWrapper extends GroovyObjectSupport {
|
||||
this(beanName, clazz, null);
|
||||
}
|
||||
|
||||
GroovyBeanDefinitionWrapper(@Nullable String beanName, Class<?> clazz, @Nullable Collection<?> constructorArgs) {
|
||||
GroovyBeanDefinitionWrapper(@Nullable String beanName, @Nullable Class<?> clazz, @Nullable Collection<?> constructorArgs) {
|
||||
this.beanName = beanName;
|
||||
this.clazz = clazz;
|
||||
this.constructorArgs = constructorArgs;
|
||||
@@ -130,11 +130,12 @@ class GroovyBeanDefinitionWrapper extends GroovyObjectSupport {
|
||||
}
|
||||
|
||||
BeanDefinitionHolder getBeanDefinitionHolder() {
|
||||
return new BeanDefinitionHolder(getBeanDefinition(), getBeanName());
|
||||
Assert.state(this.beanName != null, "Bean name must be set");
|
||||
return new BeanDefinitionHolder(getBeanDefinition(), this.beanName);
|
||||
}
|
||||
|
||||
void setParent(Object obj) {
|
||||
Assert.notNull(obj, "Parent bean cannot be set to a null runtime bean reference.");
|
||||
void setParent(@Nullable Object obj) {
|
||||
Assert.notNull(obj, "Parent bean cannot be set to a null runtime bean reference");
|
||||
if (obj instanceof String name) {
|
||||
this.parentName = name;
|
||||
}
|
||||
@@ -148,7 +149,7 @@ class GroovyBeanDefinitionWrapper extends GroovyObjectSupport {
|
||||
getBeanDefinition().setAbstract(false);
|
||||
}
|
||||
|
||||
GroovyBeanDefinitionWrapper addProperty(String propertyName, Object propertyValue) {
|
||||
GroovyBeanDefinitionWrapper addProperty(String propertyName, @Nullable Object propertyValue) {
|
||||
if (propertyValue instanceof GroovyBeanDefinitionWrapper wrapper) {
|
||||
propertyValue = wrapper.getBeanDefinition();
|
||||
}
|
||||
@@ -158,6 +159,7 @@ class GroovyBeanDefinitionWrapper extends GroovyObjectSupport {
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object getProperty(String property) {
|
||||
Assert.state(this.definitionWrapper != null, "BeanDefinition wrapper not initialized");
|
||||
if (this.definitionWrapper.isReadableProperty(property)) {
|
||||
@@ -170,7 +172,7 @@ class GroovyBeanDefinitionWrapper extends GroovyObjectSupport {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProperty(String property, Object newValue) {
|
||||
public void setProperty(String property, @Nullable Object newValue) {
|
||||
if (PARENT.equals(property)) {
|
||||
setParent(newValue);
|
||||
}
|
||||
|
||||
+58
-27
@@ -525,42 +525,73 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
// Check manually registered singletons.
|
||||
Object beanInstance = getSingleton(beanName, false);
|
||||
if (beanInstance != null && beanInstance.getClass() != NullBean.class) {
|
||||
|
||||
// Determine target for FactoryBean match if necessary.
|
||||
if (beanInstance instanceof FactoryBean<?> factoryBean) {
|
||||
if (!isFactoryDereference) {
|
||||
Class<?> type = getTypeForFactoryBean(factoryBean);
|
||||
return (type != null && typeToMatch.isAssignableFrom(type));
|
||||
}
|
||||
else {
|
||||
return typeToMatch.isInstance(beanInstance);
|
||||
}
|
||||
}
|
||||
else if (!isFactoryDereference) {
|
||||
if (typeToMatch.isInstance(beanInstance)) {
|
||||
// Direct match for exposed instance?
|
||||
return true;
|
||||
}
|
||||
else if (typeToMatch.hasGenerics() && containsBeanDefinition(beanName)) {
|
||||
// Generics potentially only match on the target class, not on the proxy...
|
||||
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
|
||||
Class<?> targetType = mbd.getTargetType();
|
||||
if (targetType != null && targetType != ClassUtils.getUserClass(beanInstance)) {
|
||||
// Check raw class match as well, making sure it's exposed on the proxy.
|
||||
Class<?> classToMatch = typeToMatch.resolve();
|
||||
if (classToMatch != null && !classToMatch.isInstance(beanInstance)) {
|
||||
if (type == null) {
|
||||
return false;
|
||||
}
|
||||
if (typeToMatch.isAssignableFrom(type)) {
|
||||
return true;
|
||||
}
|
||||
else if (typeToMatch.hasGenerics() && containsBeanDefinition(beanName)) {
|
||||
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
|
||||
ResolvableType targetType = mbd.targetType;
|
||||
if (targetType == null) {
|
||||
targetType = mbd.factoryMethodReturnType;
|
||||
}
|
||||
if (targetType == null) {
|
||||
return false;
|
||||
}
|
||||
if (typeToMatch.isAssignableFrom(targetType)) {
|
||||
return true;
|
||||
Class<?> targetClass = targetType.resolve();
|
||||
if (targetClass != null && FactoryBean.class.isAssignableFrom(targetClass)) {
|
||||
Class<?> classToMatch = typeToMatch.resolve();
|
||||
if (classToMatch != null && !FactoryBean.class.isAssignableFrom(classToMatch) &&
|
||||
!classToMatch.isAssignableFrom(targetType.toClass())) {
|
||||
return typeToMatch.isAssignableFrom(targetType.getGeneric());
|
||||
}
|
||||
}
|
||||
else {
|
||||
return typeToMatch.isAssignableFrom(targetType);
|
||||
}
|
||||
}
|
||||
ResolvableType resolvableType = mbd.targetType;
|
||||
if (resolvableType == null) {
|
||||
resolvableType = mbd.factoryMethodReturnType;
|
||||
}
|
||||
return (resolvableType != null && typeToMatch.isAssignableFrom(resolvableType));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
else if (isFactoryDereference) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Actual matching against bean instance...
|
||||
if (typeToMatch.isInstance(beanInstance)) {
|
||||
// Direct match for exposed instance?
|
||||
return true;
|
||||
}
|
||||
else if (typeToMatch.hasGenerics() && containsBeanDefinition(beanName)) {
|
||||
// Generics potentially only match on the target class, not on the proxy...
|
||||
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
|
||||
Class<?> targetType = mbd.getTargetType();
|
||||
if (targetType != null && targetType != ClassUtils.getUserClass(beanInstance)) {
|
||||
// Check raw class match as well, making sure it's exposed on the proxy.
|
||||
Class<?> classToMatch = typeToMatch.resolve();
|
||||
if (classToMatch != null && !classToMatch.isInstance(beanInstance)) {
|
||||
return false;
|
||||
}
|
||||
if (typeToMatch.isAssignableFrom(targetType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
ResolvableType resolvableType = mbd.targetType;
|
||||
if (resolvableType == null) {
|
||||
resolvableType = mbd.factoryMethodReturnType;
|
||||
}
|
||||
return (resolvableType != null && typeToMatch.isAssignableFrom(resolvableType));
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (containsSingleton(beanName) && !containsBeanDefinition(beanName)) {
|
||||
// null instance registered
|
||||
|
||||
+1
@@ -241,6 +241,7 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
|
||||
// Cast is safe, as CallbackFilter filters are used selectively.
|
||||
LookupOverride lo = (LookupOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
|
||||
|
||||
+1
@@ -1434,6 +1434,7 @@ class ConstructorResolver {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object resolveShortcut(BeanFactory beanFactory) {
|
||||
String shortcut = this.shortcut;
|
||||
return (shortcut != null ? beanFactory.getBean(shortcut, getDependencyType()) : null);
|
||||
|
||||
+1
-1
@@ -342,7 +342,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
void logDestroyMethodException(Method destroyMethod, Throwable ex) {
|
||||
void logDestroyMethodException(Method destroyMethod, @Nullable Throwable ex) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
String msg = "Custom destroy method '" + destroyMethod.getName() + "' on bean with name '" +
|
||||
this.beanName + "' propagated an exception";
|
||||
|
||||
+18
-17
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -102,22 +102,6 @@ public class GenericTypeAwareAutowireCandidateResolver extends SimpleAutowireCan
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Pre-existing target type: In case of a generic FactoryBean type,
|
||||
// unwrap nested generic type when matching a non-FactoryBean type.
|
||||
Class<?> resolvedClass = targetType.resolve();
|
||||
if (resolvedClass != null && FactoryBean.class.isAssignableFrom(resolvedClass)) {
|
||||
Class<?> typeToBeMatched = dependencyType.resolve();
|
||||
if (typeToBeMatched != null && !FactoryBean.class.isAssignableFrom(typeToBeMatched)) {
|
||||
targetType = targetType.getGeneric();
|
||||
if (descriptor.fallbackMatchAllowed()) {
|
||||
// Matching the Class-based type determination for FactoryBean
|
||||
// objects in the lazy-determination getType code path below.
|
||||
targetType = ResolvableType.forClass(targetType.resolve());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (targetType == null) {
|
||||
@@ -144,6 +128,23 @@ public class GenericTypeAwareAutowireCandidateResolver extends SimpleAutowireCan
|
||||
if (cacheType) {
|
||||
rbd.targetType = targetType;
|
||||
}
|
||||
|
||||
// Pre-declared target type: In case of a generic FactoryBean type,
|
||||
// unwrap nested generic type when matching a non-FactoryBean type.
|
||||
Class<?> targetClass = targetType.resolve();
|
||||
if (targetClass != null && FactoryBean.class.isAssignableFrom(targetClass)) {
|
||||
Class<?> classToMatch = dependencyType.resolve();
|
||||
if (classToMatch != null && !FactoryBean.class.isAssignableFrom(classToMatch) &&
|
||||
!classToMatch.isAssignableFrom(targetClass)) {
|
||||
targetType = targetType.getGeneric();
|
||||
if (descriptor.fallbackMatchAllowed()) {
|
||||
// Matching the Class-based type determination for FactoryBean
|
||||
// objects in the lazy-determination getType code path above.
|
||||
targetType = ResolvableType.forClass(targetType.resolve());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (descriptor.fallbackMatchAllowed() &&
|
||||
(targetType.hasUnresolvableGenerics() || targetType.resolve() == Properties.class)) {
|
||||
// Fallback matches allow unresolvable generics, e.g. plain HashMap to Map<String,String>;
|
||||
|
||||
+2
@@ -83,6 +83,7 @@ public interface InstanceSupplier<T> extends ThrowingSupplier<T> {
|
||||
return after.applyWithException(registeredBean, InstanceSupplier.this.get(registeredBean));
|
||||
}
|
||||
@Override
|
||||
@Nullable
|
||||
public Method getFactoryMethod() {
|
||||
return InstanceSupplier.this.getFactoryMethod();
|
||||
}
|
||||
@@ -126,6 +127,7 @@ public interface InstanceSupplier<T> extends ThrowingSupplier<T> {
|
||||
return supplier.getWithException();
|
||||
}
|
||||
@Override
|
||||
@Nullable
|
||||
public Method getFactoryMethod() {
|
||||
return factoryMethod;
|
||||
}
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -277,6 +277,7 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getParentName() {
|
||||
return null;
|
||||
}
|
||||
@@ -638,7 +639,7 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasAnyExternallyManagedMethod(Set<String> candidates, String methodName) {
|
||||
private static boolean hasAnyExternallyManagedMethod(@Nullable Set<String> candidates, String methodName) {
|
||||
if (candidates != null) {
|
||||
for (String candidate : candidates) {
|
||||
int indexOfDot = candidate.lastIndexOf('.');
|
||||
|
||||
+2
@@ -232,11 +232,13 @@ public class StaticListableBeanFactory implements ListableBeanFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Class<?> getType(String name) throws NoSuchBeanDefinitionException {
|
||||
return getType(name, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException {
|
||||
String beanName = BeanFactoryUtils.transformedBeanName(name);
|
||||
|
||||
|
||||
+5
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -126,9 +126,10 @@ public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocume
|
||||
// then ultimately reset this.delegate back to its original (parent) reference.
|
||||
// this behavior emulates a stack of delegates without actually necessitating one.
|
||||
BeanDefinitionParserDelegate parent = this.delegate;
|
||||
this.delegate = createDelegate(getReaderContext(), root, parent);
|
||||
BeanDefinitionParserDelegate current = createDelegate(getReaderContext(), root, parent);
|
||||
this.delegate = current;
|
||||
|
||||
if (this.delegate.isDefaultNamespace(root)) {
|
||||
if (current.isDefaultNamespace(root)) {
|
||||
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
|
||||
if (StringUtils.hasText(profileSpec)) {
|
||||
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
|
||||
@@ -146,7 +147,7 @@ public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocume
|
||||
}
|
||||
|
||||
preProcessXml(root);
|
||||
parseBeanDefinitions(root, this.delegate);
|
||||
parseBeanDefinitions(root, current);
|
||||
postProcessXml(root);
|
||||
|
||||
this.delegate = parent;
|
||||
|
||||
+283
-187
@@ -29,9 +29,12 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.config.TypedStringValue;
|
||||
@@ -63,27 +66,25 @@ class BeanFactoryGenericsTests {
|
||||
@Test
|
||||
void genericSetProperty() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class);
|
||||
|
||||
rbd.getPropertyValues().add("integerSet", Set.of("4", "5"));
|
||||
RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class);
|
||||
bd.getPropertyValues().add("integerSet", Set.of("4", "5"));
|
||||
bf.registerBeanDefinition("genericBean", bd);
|
||||
|
||||
bf.registerBeanDefinition("genericBean", rbd);
|
||||
GenericBean<?> gb = (GenericBean<?>) bf.getBean("genericBean");
|
||||
|
||||
assertThat(gb.getIntegerSet()).containsExactlyInAnyOrder(4, 5);
|
||||
}
|
||||
|
||||
@Test
|
||||
void genericListProperty() throws Exception {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class);
|
||||
|
||||
RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class);
|
||||
List<String> input = List.of("http://localhost:8080", "http://localhost:9090");
|
||||
rbd.getPropertyValues().add("resourceList", input);
|
||||
bd.getPropertyValues().add("resourceList", input);
|
||||
bf.registerBeanDefinition("genericBean", bd);
|
||||
|
||||
bf.registerBeanDefinition("genericBean", rbd);
|
||||
GenericBean<?> gb = (GenericBean<?>) bf.getBean("genericBean");
|
||||
|
||||
assertThat(gb.getResourceList())
|
||||
.containsExactly(new UrlResource("http://localhost:8080"), new UrlResource("http://localhost:9090"));
|
||||
}
|
||||
@@ -94,11 +95,11 @@ class BeanFactoryGenericsTests {
|
||||
bf.registerSingleton("resource1", new UrlResource("http://localhost:8080"));
|
||||
bf.registerSingleton("resource2", new UrlResource("http://localhost:9090"));
|
||||
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(GenericIntegerBean.class);
|
||||
rbd.setAutowireMode(RootBeanDefinition.AUTOWIRE_BY_TYPE);
|
||||
bf.registerBeanDefinition("genericBean", rbd);
|
||||
GenericIntegerBean gb = (GenericIntegerBean) bf.getBean("genericBean");
|
||||
RootBeanDefinition bd = new RootBeanDefinition(GenericIntegerBean.class);
|
||||
bd.setAutowireMode(RootBeanDefinition.AUTOWIRE_BY_TYPE);
|
||||
bf.registerBeanDefinition("genericBean", bd);
|
||||
|
||||
GenericIntegerBean gb = (GenericIntegerBean) bf.getBean("genericBean");
|
||||
assertThat(gb.getResourceList())
|
||||
.containsExactly(new UrlResource("http://localhost:8080"), new UrlResource("http://localhost:9090"));
|
||||
}
|
||||
@@ -106,43 +107,42 @@ class BeanFactoryGenericsTests {
|
||||
@Test
|
||||
void genericListPropertyWithInvalidElementType() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(GenericIntegerBean.class);
|
||||
|
||||
rbd.getPropertyValues().add("testBeanList", List.of(1));
|
||||
RootBeanDefinition bd = new RootBeanDefinition(GenericIntegerBean.class);
|
||||
bd.getPropertyValues().add("testBeanList", List.of(1));
|
||||
bf.registerBeanDefinition("genericBean", bd);
|
||||
|
||||
bf.registerBeanDefinition("genericBean", rbd);
|
||||
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> bf.getBean("genericBean"))
|
||||
.withMessageContaining("genericBean")
|
||||
.withMessageContaining("testBeanList[0]")
|
||||
.withMessageContaining(TestBean.class.getName())
|
||||
.withMessageContaining("Integer");
|
||||
.withMessageContaining("genericBean")
|
||||
.withMessageContaining("testBeanList[0]")
|
||||
.withMessageContaining(TestBean.class.getName())
|
||||
.withMessageContaining("Integer");
|
||||
}
|
||||
|
||||
@Test
|
||||
void genericListPropertyWithOptionalAutowiring() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class);
|
||||
rbd.setAutowireMode(RootBeanDefinition.AUTOWIRE_BY_TYPE);
|
||||
bf.registerBeanDefinition("genericBean", rbd);
|
||||
GenericBean<?> gb = (GenericBean<?>) bf.getBean("genericBean");
|
||||
RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class);
|
||||
bd.setAutowireMode(RootBeanDefinition.AUTOWIRE_BY_TYPE);
|
||||
bf.registerBeanDefinition("genericBean", bd);
|
||||
|
||||
GenericBean<?> gb = (GenericBean<?>) bf.getBean("genericBean");
|
||||
assertThat(gb.getResourceList()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void genericMapProperty() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class);
|
||||
|
||||
RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class);
|
||||
Map<String, String> input = Map.of(
|
||||
"4", "5",
|
||||
"6", "7");
|
||||
rbd.getPropertyValues().add("shortMap", input);
|
||||
bd.getPropertyValues().add("shortMap", input);
|
||||
bf.registerBeanDefinition("genericBean", bd);
|
||||
|
||||
bf.registerBeanDefinition("genericBean", rbd);
|
||||
GenericBean<?> gb = (GenericBean<?>) bf.getBean("genericBean");
|
||||
|
||||
assertThat(gb.getShortMap().get(Short.valueOf("4"))).isEqualTo(5);
|
||||
assertThat(gb.getShortMap().get(Short.valueOf("6"))).isEqualTo(7);
|
||||
}
|
||||
@@ -152,22 +152,21 @@ class BeanFactoryGenericsTests {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(
|
||||
new ClassPathResource("genericBeanTests.xml", getClass()));
|
||||
GenericBean<?> gb = (GenericBean<?>) bf.getBean("listOfArrays");
|
||||
|
||||
GenericBean<?> gb = (GenericBean<?>) bf.getBean("listOfArrays");
|
||||
assertThat(gb.getListOfArrays()).containsExactly(new String[] {"value1", "value2"});
|
||||
}
|
||||
|
||||
@Test
|
||||
void genericSetConstructor() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class);
|
||||
|
||||
RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class);
|
||||
Set<String> input = Set.of("4", "5");
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue(input);
|
||||
bd.getConstructorArgumentValues().addGenericArgumentValue(input);
|
||||
bf.registerBeanDefinition("genericBean", bd);
|
||||
|
||||
bf.registerBeanDefinition("genericBean", rbd);
|
||||
GenericBean<?> gb = (GenericBean<?>) bf.getBean("genericBean");
|
||||
|
||||
assertThat(gb.getIntegerSet()).containsExactlyInAnyOrder(4, 5);
|
||||
}
|
||||
|
||||
@@ -177,11 +176,11 @@ class BeanFactoryGenericsTests {
|
||||
bf.registerSingleton("integer1", 4);
|
||||
bf.registerSingleton("integer2", 5);
|
||||
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class);
|
||||
rbd.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);
|
||||
bf.registerBeanDefinition("genericBean", rbd);
|
||||
GenericBean<?> gb = (GenericBean<?>) bf.getBean("genericBean");
|
||||
RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class);
|
||||
bd.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);
|
||||
bf.registerBeanDefinition("genericBean", bd);
|
||||
|
||||
GenericBean<?> gb = (GenericBean<?>) bf.getBean("genericBean");
|
||||
assertThat(gb.getIntegerSet()).containsExactlyInAnyOrder(4, 5);
|
||||
}
|
||||
|
||||
@@ -189,27 +188,26 @@ class BeanFactoryGenericsTests {
|
||||
void genericSetConstructorWithOptionalAutowiring() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class);
|
||||
rbd.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);
|
||||
bf.registerBeanDefinition("genericBean", rbd);
|
||||
GenericBean<?> gb = (GenericBean<?>) bf.getBean("genericBean");
|
||||
RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class);
|
||||
bd.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);
|
||||
bf.registerBeanDefinition("genericBean", bd);
|
||||
|
||||
GenericBean<?> gb = (GenericBean<?>) bf.getBean("genericBean");
|
||||
assertThat(gb.getIntegerSet()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void genericSetListConstructor() throws Exception {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class);
|
||||
|
||||
RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class);
|
||||
Set<String> input1 = Set.of("4", "5");
|
||||
List<String> input2 = List.of("http://localhost:8080", "http://localhost:9090");
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue(input1);
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue(input2);
|
||||
bd.getConstructorArgumentValues().addGenericArgumentValue(input1);
|
||||
bd.getConstructorArgumentValues().addGenericArgumentValue(input2);
|
||||
bf.registerBeanDefinition("genericBean", bd);
|
||||
|
||||
bf.registerBeanDefinition("genericBean", rbd);
|
||||
GenericBean<?> gb = (GenericBean<?>) bf.getBean("genericBean");
|
||||
|
||||
assertThat(gb.getIntegerSet()).containsExactlyInAnyOrder(4, 5);
|
||||
assertThat(gb.getResourceList())
|
||||
.containsExactly(new UrlResource("http://localhost:8080"), new UrlResource("http://localhost:9090"));
|
||||
@@ -223,11 +221,11 @@ class BeanFactoryGenericsTests {
|
||||
bf.registerSingleton("resource1", new UrlResource("http://localhost:8080"));
|
||||
bf.registerSingleton("resource2", new UrlResource("http://localhost:9090"));
|
||||
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class);
|
||||
rbd.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);
|
||||
bf.registerBeanDefinition("genericBean", rbd);
|
||||
GenericBean<?> gb = (GenericBean<?>) bf.getBean("genericBean");
|
||||
RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class);
|
||||
bd.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);
|
||||
bf.registerBeanDefinition("genericBean", bd);
|
||||
|
||||
GenericBean<?> gb = (GenericBean<?>) bf.getBean("genericBean");
|
||||
assertThat(gb.getIntegerSet()).containsExactlyInAnyOrder(4, 5);
|
||||
assertThat(gb.getResourceList())
|
||||
.containsExactly(new UrlResource("http://localhost:8080"), new UrlResource("http://localhost:9090"));
|
||||
@@ -239,11 +237,11 @@ class BeanFactoryGenericsTests {
|
||||
bf.registerSingleton("resource1", new UrlResource("http://localhost:8080"));
|
||||
bf.registerSingleton("resource2", new UrlResource("http://localhost:9090"));
|
||||
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class);
|
||||
rbd.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);
|
||||
bf.registerBeanDefinition("genericBean", rbd);
|
||||
GenericBean<?> gb = (GenericBean<?>) bf.getBean("genericBean");
|
||||
RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class);
|
||||
bd.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);
|
||||
bf.registerBeanDefinition("genericBean", bd);
|
||||
|
||||
GenericBean<?> gb = (GenericBean<?>) bf.getBean("genericBean");
|
||||
assertThat(gb.getIntegerSet()).isNull();
|
||||
assertThat(gb.getResourceList()).isNull();
|
||||
}
|
||||
@@ -251,18 +249,17 @@ class BeanFactoryGenericsTests {
|
||||
@Test
|
||||
void genericSetMapConstructor() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class);
|
||||
|
||||
RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class);
|
||||
Set<String> input1 = Set.of("4", "5");
|
||||
Map<String, String> input2 = Map.of(
|
||||
"4", "5",
|
||||
"6", "7");
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue(input1);
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue(input2);
|
||||
bd.getConstructorArgumentValues().addGenericArgumentValue(input1);
|
||||
bd.getConstructorArgumentValues().addGenericArgumentValue(input2);
|
||||
bf.registerBeanDefinition("genericBean", bd);
|
||||
|
||||
bf.registerBeanDefinition("genericBean", rbd);
|
||||
GenericBean<?> gb = (GenericBean<?>) bf.getBean("genericBean");
|
||||
|
||||
assertThat(gb.getIntegerSet()).containsExactlyInAnyOrder(4, 5);
|
||||
assertThat(gb.getShortMap().get(Short.valueOf("4"))).isEqualTo(5);
|
||||
assertThat(gb.getShortMap().get(Short.valueOf("6"))).isEqualTo(7);
|
||||
@@ -271,17 +268,16 @@ class BeanFactoryGenericsTests {
|
||||
@Test
|
||||
void genericMapResourceConstructor() throws Exception {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class);
|
||||
|
||||
RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class);
|
||||
Map<String, String> input = Map.of(
|
||||
"4", "5",
|
||||
"6", "7");
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue(input);
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue("http://localhost:8080");
|
||||
bd.getConstructorArgumentValues().addGenericArgumentValue(input);
|
||||
bd.getConstructorArgumentValues().addGenericArgumentValue("http://localhost:8080");
|
||||
bf.registerBeanDefinition("genericBean", bd);
|
||||
|
||||
bf.registerBeanDefinition("genericBean", rbd);
|
||||
GenericBean<?> gb = (GenericBean<?>) bf.getBean("genericBean");
|
||||
|
||||
assertThat(gb.getShortMap().get(Short.valueOf("4"))).isEqualTo(5);
|
||||
assertThat(gb.getShortMap().get(Short.valueOf("6"))).isEqualTo(7);
|
||||
assertThat(gb.getResourceList()).containsExactly(new UrlResource("http://localhost:8080"));
|
||||
@@ -290,20 +286,19 @@ class BeanFactoryGenericsTests {
|
||||
@Test
|
||||
void genericMapMapConstructor() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class);
|
||||
|
||||
RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class);
|
||||
Map<String, String> input1 = Map.of(
|
||||
"1", "0",
|
||||
"2", "3");
|
||||
Map<String, String> input2 = Map.of(
|
||||
"4", "5",
|
||||
"6", "7");
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue(input1);
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue(input2);
|
||||
bd.getConstructorArgumentValues().addGenericArgumentValue(input1);
|
||||
bd.getConstructorArgumentValues().addGenericArgumentValue(input2);
|
||||
bf.registerBeanDefinition("genericBean", bd);
|
||||
|
||||
bf.registerBeanDefinition("genericBean", rbd);
|
||||
GenericBean<?> gb = (GenericBean<?>) bf.getBean("genericBean");
|
||||
|
||||
assertThat(gb.getShortMap()).isNotSameAs(gb.getPlainMap());
|
||||
assertThat(gb.getPlainMap()).hasSize(2);
|
||||
assertThat(gb.getPlainMap().get("1")).isEqualTo("0");
|
||||
@@ -316,17 +311,16 @@ class BeanFactoryGenericsTests {
|
||||
@Test
|
||||
void genericMapMapConstructorWithSameRefAndConversion() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class);
|
||||
|
||||
RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class);
|
||||
Map<String, String> input = Map.of(
|
||||
"1", "0",
|
||||
"2", "3");
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue(input);
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue(input);
|
||||
bd.getConstructorArgumentValues().addGenericArgumentValue(input);
|
||||
bd.getConstructorArgumentValues().addGenericArgumentValue(input);
|
||||
bf.registerBeanDefinition("genericBean", bd);
|
||||
|
||||
bf.registerBeanDefinition("genericBean", rbd);
|
||||
GenericBean<?> gb = (GenericBean<?>) bf.getBean("genericBean");
|
||||
|
||||
assertThat(gb.getShortMap()).isNotSameAs(gb.getPlainMap());
|
||||
assertThat(gb.getPlainMap()).hasSize(2);
|
||||
assertThat(gb.getPlainMap().get("1")).isEqualTo("0");
|
||||
@@ -339,17 +333,16 @@ class BeanFactoryGenericsTests {
|
||||
@Test
|
||||
void genericMapMapConstructorWithSameRefAndNoConversion() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class);
|
||||
|
||||
RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class);
|
||||
Map<Short, Integer> input = new HashMap<>();
|
||||
input.put((short) 1, 0);
|
||||
input.put((short) 2, 3);
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue(input);
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue(input);
|
||||
bd.getConstructorArgumentValues().addGenericArgumentValue(input);
|
||||
bd.getConstructorArgumentValues().addGenericArgumentValue(input);
|
||||
bf.registerBeanDefinition("genericBean", bd);
|
||||
|
||||
bf.registerBeanDefinition("genericBean", rbd);
|
||||
GenericBean<?> gb = (GenericBean<?>) bf.getBean("genericBean");
|
||||
|
||||
assertThat(gb.getShortMap()).isSameAs(gb.getPlainMap());
|
||||
assertThat(gb.getShortMap()).hasSize(2);
|
||||
assertThat(gb.getShortMap().get(Short.valueOf("1"))).isEqualTo(0);
|
||||
@@ -359,16 +352,15 @@ class BeanFactoryGenericsTests {
|
||||
@Test
|
||||
void genericMapWithKeyTypeConstructor() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class);
|
||||
|
||||
RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class);
|
||||
Map<String, String> input = Map.of(
|
||||
"4", "5",
|
||||
"6", "7");
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue(input);
|
||||
bd.getConstructorArgumentValues().addGenericArgumentValue(input);
|
||||
bf.registerBeanDefinition("genericBean", bd);
|
||||
|
||||
bf.registerBeanDefinition("genericBean", rbd);
|
||||
GenericBean<?> gb = (GenericBean<?>) bf.getBean("genericBean");
|
||||
|
||||
assertThat(gb.getLongMap().get(4L)).isEqualTo("5");
|
||||
assertThat(gb.getLongMap().get(6L)).isEqualTo("7");
|
||||
}
|
||||
@@ -376,18 +368,18 @@ class BeanFactoryGenericsTests {
|
||||
@Test
|
||||
void genericMapWithCollectionValueConstructor() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.addPropertyEditorRegistrar(registry -> registry.registerCustomEditor(Number.class, new CustomNumberEditor(Integer.class, false)));
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class);
|
||||
bf.addPropertyEditorRegistrar(registry ->
|
||||
registry.registerCustomEditor(Number.class, new CustomNumberEditor(Integer.class, false)));
|
||||
|
||||
RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class);
|
||||
Map<String, Collection<?>> input = Map.of(
|
||||
"1", Set.of(1),
|
||||
"2", List.of(Boolean.TRUE));
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue(Boolean.TRUE);
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue(input);
|
||||
bd.getConstructorArgumentValues().addGenericArgumentValue(Boolean.TRUE);
|
||||
bd.getConstructorArgumentValues().addGenericArgumentValue(input);
|
||||
bf.registerBeanDefinition("genericBean", bd);
|
||||
|
||||
bf.registerBeanDefinition("genericBean", rbd);
|
||||
GenericBean<?> gb = (GenericBean<?>) bf.getBean("genericBean");
|
||||
|
||||
assertThat(gb.getCollectionMap().get(1)).isInstanceOf(Set.class);
|
||||
assertThat(gb.getCollectionMap().get(2)).isInstanceOf(List.class);
|
||||
}
|
||||
@@ -395,32 +387,30 @@ class BeanFactoryGenericsTests {
|
||||
@Test
|
||||
void genericSetFactoryMethod() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class);
|
||||
rbd.setFactoryMethodName("createInstance");
|
||||
|
||||
RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class);
|
||||
bd.setFactoryMethodName("createInstance");
|
||||
Set<String> input = Set.of("4", "5");
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue(input);
|
||||
bd.getConstructorArgumentValues().addGenericArgumentValue(input);
|
||||
bf.registerBeanDefinition("genericBean", bd);
|
||||
|
||||
bf.registerBeanDefinition("genericBean", rbd);
|
||||
GenericBean<?> gb = (GenericBean<?>) bf.getBean("genericBean");
|
||||
|
||||
assertThat(gb.getIntegerSet()).containsExactlyInAnyOrder(4, 5);
|
||||
}
|
||||
|
||||
@Test
|
||||
void genericSetListFactoryMethod() throws Exception {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class);
|
||||
rbd.setFactoryMethodName("createInstance");
|
||||
|
||||
RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class);
|
||||
bd.setFactoryMethodName("createInstance");
|
||||
Set<String> input1 = Set.of("4", "5");
|
||||
List<String> input2 = List.of("http://localhost:8080", "http://localhost:9090");
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue(input1);
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue(input2);
|
||||
bd.getConstructorArgumentValues().addGenericArgumentValue(input1);
|
||||
bd.getConstructorArgumentValues().addGenericArgumentValue(input2);
|
||||
bf.registerBeanDefinition("genericBean", bd);
|
||||
|
||||
bf.registerBeanDefinition("genericBean", rbd);
|
||||
GenericBean<?> gb = (GenericBean<?>) bf.getBean("genericBean");
|
||||
|
||||
assertThat(gb.getIntegerSet()).containsExactlyInAnyOrder(4, 5);
|
||||
assertThat(gb.getResourceList())
|
||||
.containsExactly(new UrlResource("http://localhost:8080"), new UrlResource("http://localhost:9090"));
|
||||
@@ -429,19 +419,18 @@ class BeanFactoryGenericsTests {
|
||||
@Test
|
||||
void genericSetMapFactoryMethod() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class);
|
||||
rbd.setFactoryMethodName("createInstance");
|
||||
|
||||
RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class);
|
||||
bd.setFactoryMethodName("createInstance");
|
||||
Set<String> input1 = Set.of("4", "5");
|
||||
Map<String, String> input2 = Map.of(
|
||||
"4", "5",
|
||||
"6", "7");
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue(input1);
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue(input2);
|
||||
bd.getConstructorArgumentValues().addGenericArgumentValue(input1);
|
||||
bd.getConstructorArgumentValues().addGenericArgumentValue(input2);
|
||||
bf.registerBeanDefinition("genericBean", bd);
|
||||
|
||||
bf.registerBeanDefinition("genericBean", rbd);
|
||||
GenericBean<?> gb = (GenericBean<?>) bf.getBean("genericBean");
|
||||
|
||||
assertThat(gb.getIntegerSet()).containsExactlyInAnyOrder(4, 5);
|
||||
assertThat(gb.getShortMap().get(Short.valueOf("4"))).isEqualTo(5);
|
||||
assertThat(gb.getShortMap().get(Short.valueOf("6"))).isEqualTo(7);
|
||||
@@ -450,18 +439,17 @@ class BeanFactoryGenericsTests {
|
||||
@Test
|
||||
void genericMapResourceFactoryMethod() throws Exception {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class);
|
||||
rbd.setFactoryMethodName("createInstance");
|
||||
|
||||
RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class);
|
||||
bd.setFactoryMethodName("createInstance");
|
||||
Map<String, String> input = Map.of(
|
||||
"4", "5",
|
||||
"6", "7");
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue(input);
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue("http://localhost:8080");
|
||||
bd.getConstructorArgumentValues().addGenericArgumentValue(input);
|
||||
bd.getConstructorArgumentValues().addGenericArgumentValue("http://localhost:8080");
|
||||
bf.registerBeanDefinition("genericBean", bd);
|
||||
|
||||
bf.registerBeanDefinition("genericBean", rbd);
|
||||
GenericBean<?> gb = (GenericBean<?>) bf.getBean("genericBean");
|
||||
|
||||
assertThat(gb.getShortMap().get(Short.valueOf("4"))).isEqualTo(5);
|
||||
assertThat(gb.getShortMap().get(Short.valueOf("6"))).isEqualTo(7);
|
||||
assertThat(gb.getResourceList()).containsExactly(new UrlResource("http://localhost:8080"));
|
||||
@@ -470,21 +458,20 @@ class BeanFactoryGenericsTests {
|
||||
@Test
|
||||
void genericMapMapFactoryMethod() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class);
|
||||
rbd.setFactoryMethodName("createInstance");
|
||||
|
||||
RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class);
|
||||
bd.setFactoryMethodName("createInstance");
|
||||
Map<String, String> input1 = Map.of(
|
||||
"1", "0",
|
||||
"2", "3");
|
||||
Map<String, String> input2 = Map.of(
|
||||
"4", "5",
|
||||
"6", "7");
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue(input1);
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue(input2);
|
||||
bd.getConstructorArgumentValues().addGenericArgumentValue(input1);
|
||||
bd.getConstructorArgumentValues().addGenericArgumentValue(input2);
|
||||
bf.registerBeanDefinition("genericBean", bd);
|
||||
|
||||
bf.registerBeanDefinition("genericBean", rbd);
|
||||
GenericBean<?> gb = (GenericBean<?>) bf.getBean("genericBean");
|
||||
|
||||
assertThat(gb.getPlainMap().get("1")).isEqualTo("0");
|
||||
assertThat(gb.getPlainMap().get("2")).isEqualTo("3");
|
||||
assertThat(gb.getShortMap().get(Short.valueOf("4"))).isEqualTo(5);
|
||||
@@ -494,17 +481,16 @@ class BeanFactoryGenericsTests {
|
||||
@Test
|
||||
void genericMapWithKeyTypeFactoryMethod() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class);
|
||||
rbd.setFactoryMethodName("createInstance");
|
||||
|
||||
RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class);
|
||||
bd.setFactoryMethodName("createInstance");
|
||||
Map<String, String> input = Map.of(
|
||||
"4", "5",
|
||||
"6", "7");
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue(input);
|
||||
bd.getConstructorArgumentValues().addGenericArgumentValue(input);
|
||||
bf.registerBeanDefinition("genericBean", bd);
|
||||
|
||||
bf.registerBeanDefinition("genericBean", rbd);
|
||||
GenericBean<?> gb = (GenericBean<?>) bf.getBean("genericBean");
|
||||
|
||||
assertThat(gb.getLongMap().get(Long.valueOf("4"))).isEqualTo("5");
|
||||
assertThat(gb.getLongMap().get(Long.valueOf("6"))).isEqualTo("7");
|
||||
}
|
||||
@@ -512,19 +498,19 @@ class BeanFactoryGenericsTests {
|
||||
@Test
|
||||
void genericMapWithCollectionValueFactoryMethod() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.addPropertyEditorRegistrar(registry -> registry.registerCustomEditor(Number.class, new CustomNumberEditor(Integer.class, false)));
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class);
|
||||
rbd.setFactoryMethodName("createInstance");
|
||||
bf.addPropertyEditorRegistrar(registry ->
|
||||
registry.registerCustomEditor(Number.class, new CustomNumberEditor(Integer.class, false)));
|
||||
|
||||
RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class);
|
||||
bd.setFactoryMethodName("createInstance");
|
||||
Map<String, Collection<?>> input = Map.of(
|
||||
"1", Set.of(1),
|
||||
"2", List.of(Boolean.TRUE));
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue(Boolean.TRUE);
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue(input);
|
||||
bd.getConstructorArgumentValues().addGenericArgumentValue(Boolean.TRUE);
|
||||
bd.getConstructorArgumentValues().addGenericArgumentValue(input);
|
||||
bf.registerBeanDefinition("genericBean", bd);
|
||||
|
||||
bf.registerBeanDefinition("genericBean", rbd);
|
||||
GenericBean<?> gb = (GenericBean<?>) bf.getBean("genericBean");
|
||||
|
||||
assertThat(gb.getCollectionMap().get(1)).isInstanceOf(Set.class);
|
||||
assertThat(gb.getCollectionMap().get(2)).isInstanceOf(List.class);
|
||||
}
|
||||
@@ -534,6 +520,7 @@ class BeanFactoryGenericsTests {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(
|
||||
new ClassPathResource("genericBeanTests.xml", getClass()));
|
||||
|
||||
NamedUrlList list = bf.getBean("list", NamedUrlList.class);
|
||||
assertThat(list).containsExactly(new URL("http://localhost:8080"));
|
||||
}
|
||||
@@ -543,6 +530,7 @@ class BeanFactoryGenericsTests {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(
|
||||
new ClassPathResource("genericBeanTests.xml", getClass()));
|
||||
|
||||
NamedUrlSet set = bf.getBean("set", NamedUrlSet.class);
|
||||
assertThat(set).containsExactly(new URL("http://localhost:8080"));
|
||||
}
|
||||
@@ -552,6 +540,7 @@ class BeanFactoryGenericsTests {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(
|
||||
new ClassPathResource("genericBeanTests.xml", getClass()));
|
||||
|
||||
NamedUrlMap map = bf.getBean("map", NamedUrlMap.class);
|
||||
assertThat(map).containsExactly(entry(10, new URL("http://localhost:8080")));
|
||||
}
|
||||
@@ -561,6 +550,7 @@ class BeanFactoryGenericsTests {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(
|
||||
new ClassPathResource("genericBeanTests.xml", getClass()));
|
||||
|
||||
GenericIntegerBean gb = (GenericIntegerBean) bf.getBean("integerBean");
|
||||
assertThat(gb.getGenericProperty()).isEqualTo(10);
|
||||
assertThat(gb.getGenericListProperty()).containsExactly(20, 30);
|
||||
@@ -571,6 +561,7 @@ class BeanFactoryGenericsTests {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(
|
||||
new ClassPathResource("genericBeanTests.xml", getClass()));
|
||||
|
||||
GenericSetOfIntegerBean gb = (GenericSetOfIntegerBean) bf.getBean("setOfIntegerBean");
|
||||
assertThat(gb.getGenericProperty()).singleElement().isEqualTo(10);
|
||||
assertThat(gb.getGenericListProperty()).satisfiesExactly(
|
||||
@@ -583,6 +574,7 @@ class BeanFactoryGenericsTests {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(
|
||||
new ClassPathResource("genericBeanTests.xml", getClass()));
|
||||
|
||||
UrlSet urlSet = bf.getBean("setBean", UrlSet.class);
|
||||
assertThat(urlSet).containsExactly(new URL("https://www.springframework.org"));
|
||||
}
|
||||
@@ -599,27 +591,27 @@ class BeanFactoryGenericsTests {
|
||||
*/
|
||||
@Test
|
||||
void parameterizedStaticFactoryMethod() {
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(getClass());
|
||||
rbd.setFactoryMethodName("createMockitoMock");
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue(Runnable.class);
|
||||
RootBeanDefinition bd = new RootBeanDefinition(getClass());
|
||||
bd.setFactoryMethodName("createMockitoMock");
|
||||
bd.getConstructorArgumentValues().addGenericArgumentValue(Runnable.class);
|
||||
|
||||
assertRunnableMockFactory(rbd);
|
||||
assertRunnableMockFactory(bd);
|
||||
}
|
||||
|
||||
@Test
|
||||
void parameterizedStaticFactoryMethodWithWrappedClassName() {
|
||||
RootBeanDefinition rbd = new RootBeanDefinition();
|
||||
rbd.setBeanClassName(getClass().getName());
|
||||
rbd.setFactoryMethodName("createMockitoMock");
|
||||
RootBeanDefinition bd = new RootBeanDefinition();
|
||||
bd.setBeanClassName(getClass().getName());
|
||||
bd.setFactoryMethodName("createMockitoMock");
|
||||
// TypedStringValue is used as an equivalent to an XML-defined argument String
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue(new TypedStringValue(Runnable.class.getName()));
|
||||
bd.getConstructorArgumentValues().addGenericArgumentValue(new TypedStringValue(Runnable.class.getName()));
|
||||
|
||||
assertRunnableMockFactory(rbd);
|
||||
assertRunnableMockFactory(bd);
|
||||
}
|
||||
|
||||
private void assertRunnableMockFactory(RootBeanDefinition rbd) {
|
||||
private void assertRunnableMockFactory(RootBeanDefinition bd) {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.registerBeanDefinition("mock", rbd);
|
||||
bf.registerBeanDefinition("mock", bd);
|
||||
|
||||
assertThat(bf.isTypeMatch("mock", Runnable.class)).isTrue();
|
||||
assertThat(bf.getType("mock")).isEqualTo(Runnable.class);
|
||||
@@ -642,14 +634,14 @@ class BeanFactoryGenericsTests {
|
||||
void parameterizedInstanceFactoryMethod() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(MocksControl.class);
|
||||
bf.registerBeanDefinition("mocksControl", rbd);
|
||||
RootBeanDefinition bd1 = new RootBeanDefinition(MocksControl.class);
|
||||
bf.registerBeanDefinition("mocksControl", bd1);
|
||||
|
||||
rbd = new RootBeanDefinition();
|
||||
rbd.setFactoryBeanName("mocksControl");
|
||||
rbd.setFactoryMethodName("createMock");
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue(Runnable.class);
|
||||
bf.registerBeanDefinition("mock", rbd);
|
||||
RootBeanDefinition bd2 = new RootBeanDefinition();
|
||||
bd2.setFactoryBeanName("mocksControl");
|
||||
bd2.setFactoryMethodName("createMock");
|
||||
bd2.getConstructorArgumentValues().addGenericArgumentValue(Runnable.class);
|
||||
bf.registerBeanDefinition("mock", bd2);
|
||||
|
||||
assertThat(bf.isTypeMatch("mock", Runnable.class)).isTrue();
|
||||
assertThat(bf.isTypeMatch("mock", Runnable.class)).isTrue();
|
||||
@@ -663,14 +655,14 @@ class BeanFactoryGenericsTests {
|
||||
void parameterizedInstanceFactoryMethodWithNonResolvedClassName() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(MocksControl.class);
|
||||
bf.registerBeanDefinition("mocksControl", rbd);
|
||||
RootBeanDefinition bd1 = new RootBeanDefinition(MocksControl.class);
|
||||
bf.registerBeanDefinition("mocksControl", bd1);
|
||||
|
||||
rbd = new RootBeanDefinition();
|
||||
rbd.setFactoryBeanName("mocksControl");
|
||||
rbd.setFactoryMethodName("createMock");
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue(Runnable.class.getName());
|
||||
bf.registerBeanDefinition("mock", rbd);
|
||||
RootBeanDefinition bd2 = new RootBeanDefinition();
|
||||
bd2.setFactoryBeanName("mocksControl");
|
||||
bd2.setFactoryMethodName("createMock");
|
||||
bd2.getConstructorArgumentValues().addGenericArgumentValue(Runnable.class.getName());
|
||||
bf.registerBeanDefinition("mock", bd2);
|
||||
|
||||
assertThat(bf.isTypeMatch("mock", Runnable.class)).isTrue();
|
||||
assertThat(bf.isTypeMatch("mock", Runnable.class)).isTrue();
|
||||
@@ -684,14 +676,14 @@ class BeanFactoryGenericsTests {
|
||||
void parameterizedInstanceFactoryMethodWithInvalidClassName() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(MocksControl.class);
|
||||
bf.registerBeanDefinition("mocksControl", rbd);
|
||||
RootBeanDefinition bd1 = new RootBeanDefinition(MocksControl.class);
|
||||
bf.registerBeanDefinition("mocksControl", bd1);
|
||||
|
||||
rbd = new RootBeanDefinition();
|
||||
rbd.setFactoryBeanName("mocksControl");
|
||||
rbd.setFactoryMethodName("createMock");
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue("x");
|
||||
bf.registerBeanDefinition("mock", rbd);
|
||||
RootBeanDefinition rbd2 = new RootBeanDefinition();
|
||||
rbd2.setFactoryBeanName("mocksControl");
|
||||
rbd2.setFactoryMethodName("createMock");
|
||||
rbd2.getConstructorArgumentValues().addGenericArgumentValue("x");
|
||||
bf.registerBeanDefinition("mock", rbd2);
|
||||
|
||||
assertThat(bf.isTypeMatch("mock", Runnable.class)).isFalse();
|
||||
assertThat(bf.isTypeMatch("mock", Runnable.class)).isFalse();
|
||||
@@ -705,14 +697,14 @@ class BeanFactoryGenericsTests {
|
||||
void parameterizedInstanceFactoryMethodWithIndexedArgument() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(MocksControl.class);
|
||||
bf.registerBeanDefinition("mocksControl", rbd);
|
||||
RootBeanDefinition bd1 = new RootBeanDefinition(MocksControl.class);
|
||||
bf.registerBeanDefinition("mocksControl", bd1);
|
||||
|
||||
rbd = new RootBeanDefinition();
|
||||
rbd.setFactoryBeanName("mocksControl");
|
||||
rbd.setFactoryMethodName("createMock");
|
||||
rbd.getConstructorArgumentValues().addIndexedArgumentValue(0, Runnable.class);
|
||||
bf.registerBeanDefinition("mock", rbd);
|
||||
RootBeanDefinition bd2 = new RootBeanDefinition();
|
||||
bd2.setFactoryBeanName("mocksControl");
|
||||
bd2.setFactoryMethodName("createMock");
|
||||
bd2.getConstructorArgumentValues().addIndexedArgumentValue(0, Runnable.class);
|
||||
bf.registerBeanDefinition("mock", bd2);
|
||||
|
||||
assertThat(bf.isTypeMatch("mock", Runnable.class)).isTrue();
|
||||
assertThat(bf.isTypeMatch("mock", Runnable.class)).isTrue();
|
||||
@@ -727,14 +719,14 @@ class BeanFactoryGenericsTests {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setTempClassLoader(new OverridingClassLoader(getClass().getClassLoader()));
|
||||
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(MocksControl.class);
|
||||
bf.registerBeanDefinition("mocksControl", rbd);
|
||||
RootBeanDefinition bd1 = new RootBeanDefinition(MocksControl.class);
|
||||
bf.registerBeanDefinition("mocksControl", bd1);
|
||||
|
||||
rbd = new RootBeanDefinition();
|
||||
rbd.setFactoryBeanName("mocksControl");
|
||||
rbd.setFactoryMethodName("createMock");
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue(Runnable.class);
|
||||
bf.registerBeanDefinition("mock", rbd);
|
||||
RootBeanDefinition bd2 = new RootBeanDefinition();
|
||||
bd2.setFactoryBeanName("mocksControl");
|
||||
bd2.setFactoryMethodName("createMock");
|
||||
bd2.getConstructorArgumentValues().addGenericArgumentValue(Runnable.class);
|
||||
bf.registerBeanDefinition("mock", bd2);
|
||||
|
||||
assertThat(bf.isTypeMatch("mock", Runnable.class)).isTrue();
|
||||
assertThat(bf.isTypeMatch("mock", Runnable.class)).isTrue();
|
||||
@@ -766,25 +758,31 @@ class BeanFactoryGenericsTests {
|
||||
assertThat(floatStoreNames).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void genericMatchingWithFullTypeDifferentiation() {
|
||||
@ParameterizedTest
|
||||
@ValueSource(classes = {NumberStoreFactory.class, NumberStoreFactoryBeans.class})
|
||||
void genericMatchingWithFullTypeDifferentiation(Class<?> factoryClass) {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
|
||||
bf.setAutowireCandidateResolver(new GenericTypeAwareAutowireCandidateResolver());
|
||||
|
||||
RootBeanDefinition bd1 = new RootBeanDefinition(NumberStoreFactory.class);
|
||||
RootBeanDefinition bd1 = new RootBeanDefinition(factoryClass);
|
||||
bd1.setFactoryMethodName("newDoubleStore");
|
||||
bf.registerBeanDefinition("store1", bd1);
|
||||
RootBeanDefinition bd2 = new RootBeanDefinition(NumberStoreFactory.class);
|
||||
RootBeanDefinition bd2 = new RootBeanDefinition(factoryClass);
|
||||
bd2.setFactoryMethodName("newFloatStore");
|
||||
bf.registerBeanDefinition("store2", bd2);
|
||||
bf.registerBeanDefinition("numberBean",
|
||||
new RootBeanDefinition(NumberBean.class, RootBeanDefinition.AUTOWIRE_CONSTRUCTOR, false));
|
||||
RootBeanDefinition bd3 = new RootBeanDefinition(NumberBean.class);
|
||||
bd3.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
|
||||
bd3.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);
|
||||
bf.registerBeanDefinition("numberBean", bd3);
|
||||
|
||||
NumberBean nb = bf.getBean(NumberBean.class);
|
||||
NumberStore<?> store1 = bf.getBean("store1", NumberStore.class);
|
||||
assertThat(nb.getDoubleStore()).isSameAs(store1);
|
||||
NumberStore<?> store2 = bf.getBean("store2", NumberStore.class);
|
||||
NumberBean nb = bf.getBean(NumberBean.class);
|
||||
assertThat(nb.getDoubleStore()).isSameAs(store1);
|
||||
assertThat(nb.getFloatStore()).isSameAs(store2);
|
||||
nb = bf.getBean(NumberBean.class);
|
||||
assertThat(nb.getDoubleStore()).isSameAs(store1);
|
||||
assertThat(nb.getFloatStore()).isSameAs(store2);
|
||||
|
||||
String[] numberStoreNames = bf.getBeanNamesForType(ResolvableType.forClass(NumberStore.class));
|
||||
@@ -832,16 +830,17 @@ class BeanFactoryGenericsTests {
|
||||
assertThat(floatStoreProvider.orderedStream()).singleElement().isEqualTo(store2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void genericMatchingWithUnresolvedOrderedStream() {
|
||||
@ParameterizedTest
|
||||
@ValueSource(classes = {NumberStoreFactory.class, NumberStoreFactoryBeans.class})
|
||||
void genericMatchingWithUnresolvedOrderedStream(Class<?> factoryClass) {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
|
||||
bf.setAutowireCandidateResolver(new GenericTypeAwareAutowireCandidateResolver());
|
||||
|
||||
RootBeanDefinition bd1 = new RootBeanDefinition(NumberStoreFactory.class);
|
||||
RootBeanDefinition bd1 = new RootBeanDefinition(factoryClass);
|
||||
bd1.setFactoryMethodName("newDoubleStore");
|
||||
bf.registerBeanDefinition("store1", bd1);
|
||||
RootBeanDefinition bd2 = new RootBeanDefinition(NumberStoreFactory.class);
|
||||
RootBeanDefinition bd2 = new RootBeanDefinition(factoryClass);
|
||||
bd2.setFactoryMethodName("newFloatStore");
|
||||
bf.registerBeanDefinition("store2", bd2);
|
||||
|
||||
@@ -850,6 +849,43 @@ class BeanFactoryGenericsTests {
|
||||
bf.getBean("store2", NumberStore.class), bf.getBean("store1", NumberStore.class));
|
||||
}
|
||||
|
||||
@Test // gh-32489
|
||||
void genericMatchingAgainstFactoryBeanClass() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setAutowireCandidateResolver(new GenericTypeAwareAutowireCandidateResolver());
|
||||
|
||||
RootBeanDefinition bd = new RootBeanDefinition(MyFactoryBean.class);
|
||||
bd.setTargetType(ResolvableType.forClassWithGenerics(MyFactoryBean.class, String.class));
|
||||
bf.registerBeanDefinition("myFactoryBean", bd);
|
||||
bf.registerBeanDefinition("myFactoryBeanHolder",
|
||||
new RootBeanDefinition(MyFactoryBeanHolder.class, AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR, false));
|
||||
|
||||
assertThat(bf.getBean(MyFactoryBeanHolder.class).factoryBeans).containsOnly(bf.getBean(MyFactoryBean.class));
|
||||
assertThat(bf.getBeanProvider(MyGenericInterfaceForFactoryBeans.class)).containsOnly(bf.getBean(MyFactoryBean.class));
|
||||
assertThat(bf.getBeanProvider(bd.getResolvableType())).containsOnly(bf.getBean(MyFactoryBean.class));
|
||||
}
|
||||
|
||||
@Test // gh-32489
|
||||
void genericMatchingAgainstLazyFactoryBeanClass() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setAutowireCandidateResolver(new GenericTypeAwareAutowireCandidateResolver());
|
||||
|
||||
RootBeanDefinition bd = new RootBeanDefinition(MyFactoryBean.class);
|
||||
// Replicate org.springframework.data.repository.config.RepositoryConfigurationDelegate#registerRepositoriesIn
|
||||
// behavior of setting targetType, required to hit other branch in
|
||||
// org.springframework.beans.factory.support.GenericTypeAwareAutowireCandidateResolver.checkGenericTypeMatch
|
||||
bd.setTargetType(ResolvableType.forClassWithGenerics(MyFactoryBean.class, String.class));
|
||||
bd.setLazyInit(true);
|
||||
bf.registerBeanDefinition("myFactoryBean", bd);
|
||||
bf.registerBeanDefinition("myFactoryBeanHolder",
|
||||
new RootBeanDefinition(MyFactoryBeanHolder.class, AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR, false));
|
||||
|
||||
assertThat(bf.getBeanProvider(bd.getResolvableType())).containsOnly(bf.getBean(MyFactoryBean.class));
|
||||
assertThat(bf.getBeanProvider(MyGenericInterfaceForFactoryBeans.class)).containsOnly(bf.getBean(MyFactoryBean.class));
|
||||
assertThat(bf.getBean(MyFactoryBeanHolder.class).factoryBeans).containsOnly(bf.getBean(MyFactoryBean.class));
|
||||
assertThat(bf.getBeanProvider(bd.getResolvableType())).containsOnly(bf.getBean(MyFactoryBean.class));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Mimics and delegates to {@link Mockito#mock(Class)} -- created here to avoid factory
|
||||
@@ -966,4 +1002,64 @@ class BeanFactoryGenericsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class NumberStoreFactoryBeans {
|
||||
|
||||
@Order(1)
|
||||
public static FactoryBean<NumberStore<Double>> newDoubleStore() {
|
||||
return new FactoryBean<>() {
|
||||
@Override
|
||||
public NumberStore<Double> getObject() {
|
||||
return new DoubleStore();
|
||||
}
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return DoubleStore.class;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Order(0)
|
||||
public static FactoryBean<NumberStore<Float>> newFloatStore() {
|
||||
return new FactoryBean<>() {
|
||||
@Override
|
||||
public NumberStore<Float> getObject() {
|
||||
return new FloatStore();
|
||||
}
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return FloatStore.class;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public interface MyGenericInterfaceForFactoryBeans<T> {
|
||||
}
|
||||
|
||||
|
||||
public static class MyFactoryBean<T extends CharSequence> implements FactoryBean<T>, MyGenericInterfaceForFactoryBeans<T> {
|
||||
|
||||
@Override
|
||||
public T getObject() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return String.class;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class MyFactoryBeanHolder {
|
||||
|
||||
List<MyGenericInterfaceForFactoryBeans<?>> factoryBeans; // Requested type is not a FactoryBean type
|
||||
|
||||
public MyFactoryBeanHolder(List<MyGenericInterfaceForFactoryBeans<?>> factoryBeans) {
|
||||
this.factoryBeans = factoryBeans;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Vendored
+1
@@ -121,6 +121,7 @@ public class JCacheCacheManager extends AbstractTransactionSupportingCacheManage
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected Cache getMissingCache(String name) {
|
||||
CacheManager cacheManager = getCacheManager();
|
||||
Assert.state(cacheManager != null, "No CacheManager set");
|
||||
|
||||
+1
@@ -46,6 +46,7 @@ import org.springframework.util.StringUtils;
|
||||
public abstract class AnnotationJCacheOperationSource extends AbstractFallbackJCacheOperationSource {
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected JCacheOperation<?> findCacheOperation(Method method, @Nullable Class<?> targetType) {
|
||||
CacheResult cacheResult = method.getAnnotation(CacheResult.class);
|
||||
CachePut cachePut = method.getAnnotation(CachePut.class);
|
||||
|
||||
+2
@@ -23,6 +23,7 @@ import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.interceptor.CacheErrorHandler;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvoker;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Intercept methods annotated with {@link CachePut}.
|
||||
@@ -39,6 +40,7 @@ class CachePutInterceptor extends AbstractKeyCacheInterceptor<CachePutOperation,
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected Object invoke(
|
||||
CacheOperationInvocationContext<CachePutOperation> context, CacheOperationInvoker invoker) {
|
||||
|
||||
|
||||
+2
@@ -22,6 +22,7 @@ import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.interceptor.CacheErrorHandler;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvoker;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Intercept methods annotated with {@link CacheRemoveAll}.
|
||||
@@ -38,6 +39,7 @@ class CacheRemoveAllInterceptor extends AbstractCacheInterceptor<CacheRemoveAllO
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected Object invoke(
|
||||
CacheOperationInvocationContext<CacheRemoveAllOperation> context, CacheOperationInvoker invoker) {
|
||||
|
||||
|
||||
+2
@@ -22,6 +22,7 @@ import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.interceptor.CacheErrorHandler;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvoker;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Intercept methods annotated with {@link CacheRemove}.
|
||||
@@ -38,6 +39,7 @@ class CacheRemoveEntryInterceptor extends AbstractKeyCacheInterceptor<CacheRemov
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected Object invoke(
|
||||
CacheOperationInvocationContext<CacheRemoveOperation> context, CacheOperationInvoker invoker) {
|
||||
|
||||
|
||||
+1
@@ -180,6 +180,7 @@ public class JCacheAspectSupport extends AbstractCacheInvoker implements Initial
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object invoke() throws ThrowableWrapper {
|
||||
return invokeOperation(this.delegate);
|
||||
}
|
||||
|
||||
+2
@@ -24,6 +24,7 @@ import org.springframework.cache.interceptor.AbstractCacheResolver;
|
||||
import org.springframework.cache.interceptor.BasicOperation;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* A simple {@link CacheResolver} that resolves the exception cache
|
||||
@@ -41,6 +42,7 @@ public class SimpleExceptionCacheResolver extends AbstractCacheResolver {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected Collection<String> getCacheNames(CacheOperationInvocationContext<?> context) {
|
||||
BasicOperation operation = context.getOperation();
|
||||
if (!(operation instanceof CacheResultOperation cacheResultOperation)) {
|
||||
|
||||
+1
@@ -83,6 +83,7 @@ public class TransactionAwareCacheDecorator implements Cache {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public <T> T get(Object key, @Nullable Class<T> type) {
|
||||
return this.targetCache.get(key, type);
|
||||
}
|
||||
|
||||
+2
@@ -199,6 +199,7 @@ public class MethodInvokingJobDetailFactoryBean extends ArgumentConvertingMethod
|
||||
* Overridden to support the {@link #setTargetBeanName "targetBeanName"} feature.
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public Class<?> getTargetClass() {
|
||||
Class<?> targetClass = super.getTargetClass();
|
||||
if (targetClass == null && this.targetBeanName != null) {
|
||||
@@ -212,6 +213,7 @@ public class MethodInvokingJobDetailFactoryBean extends ArgumentConvertingMethod
|
||||
* Overridden to support the {@link #setTargetBeanName "targetBeanName"} feature.
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public Object getTargetObject() {
|
||||
Object targetObject = super.getTargetObject();
|
||||
if (targetObject == null && this.targetBeanName != null) {
|
||||
|
||||
+2
-1
@@ -22,6 +22,7 @@ import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.aot.hint.TypeHint.Builder;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.aot.hint.annotation.ReflectiveRuntimeHintsRegistrar;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
@@ -40,7 +41,7 @@ class SchedulerFactoryBeanRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
||||
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
|
||||
if (!ClassUtils.isPresent(SCHEDULER_FACTORY_CLASS_NAME, classLoader)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -315,7 +315,7 @@ public interface Cache {
|
||||
@Nullable
|
||||
private final Object key;
|
||||
|
||||
public ValueRetrievalException(@Nullable Object key, Callable<?> loader, Throwable ex) {
|
||||
public ValueRetrievalException(@Nullable Object key, Callable<?> loader, @Nullable Throwable ex) {
|
||||
super(String.format("Value for key '%s' could not be loaded using '%s'", key, loader), ex);
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
+1
@@ -223,6 +223,7 @@ public class ConcurrentMapCache extends AbstractValueAdaptingCache {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected Object fromStoreValue(@Nullable Object storeValue) {
|
||||
if (storeValue != null && this.serialization != null) {
|
||||
try {
|
||||
|
||||
+8
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -1096,7 +1096,13 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
||||
}
|
||||
}
|
||||
if (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isSuspendingFunction(method)) {
|
||||
return Mono.fromFuture(cache.retrieve(key, () -> ((Mono<?>) invokeOperation(invoker)).toFuture()));
|
||||
return Mono.fromFuture(cache.retrieve(key, () -> {
|
||||
Mono<?> mono = ((Mono<?>) invokeOperation(invoker));
|
||||
if (mono == null) {
|
||||
mono = Mono.empty();
|
||||
}
|
||||
return mono.toFuture();
|
||||
}));
|
||||
}
|
||||
return NOT_HANDLED;
|
||||
}
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -78,12 +78,13 @@ public class CacheInterceptor extends CacheAspectSupport implements MethodInterc
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inner class to avoid a hard dependency on Kotlin at runtime.
|
||||
*/
|
||||
private static class KotlinDelegate {
|
||||
|
||||
public static Publisher<?> invokeSuspendingFunction(Method method, Object target, Object... args) {
|
||||
public static Publisher<?> invokeSuspendingFunction(Method method, @Nullable Object target, Object... args) {
|
||||
Continuation<?> continuation = (Continuation<?>) args[args.length - 1];
|
||||
CoroutineContext coroutineContext = continuation.getContext().minusKey(Job.Key);
|
||||
return CoroutinesUtils.invokeSuspendingFunction(coroutineContext, method, target, args);
|
||||
|
||||
+1
@@ -52,6 +52,7 @@ public class NamedCacheResolver extends AbstractCacheResolver {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected Collection<String> getCacheNames(CacheOperationInvocationContext<?> context) {
|
||||
return this.cacheNames;
|
||||
}
|
||||
|
||||
+1
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -100,7 +100,6 @@ public class AnnotatedBeanDefinitionReader {
|
||||
* Set the {@code Environment} to use when evaluating whether
|
||||
* {@link Conditional @Conditional}-annotated component classes should be registered.
|
||||
* <p>The default is a {@link StandardEnvironment}.
|
||||
* @see #registerBean(Class, String, Class...)
|
||||
*/
|
||||
public void setEnvironment(Environment environment) {
|
||||
this.conditionEvaluator = new ConditionEvaluator(this.registry, environment, null);
|
||||
|
||||
+2
@@ -314,6 +314,7 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
|
||||
BeanRegistrationAotContribution parentAotContribution = super.processAheadOfTime(registeredBean);
|
||||
Class<?> beanClass = registeredBean.getBeanClass();
|
||||
@@ -350,6 +351,7 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
+1
@@ -334,6 +334,7 @@ class ConfigurationClassEnhancer {
|
||||
return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Object resolveBeanReference(Method beanMethod, Object[] beanMethodArgs,
|
||||
ConfigurableBeanFactory beanFactory, String beanName) {
|
||||
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -257,6 +257,7 @@ public abstract class AbstractAotProcessor<T> {
|
||||
* @return this builder for method chaining
|
||||
*/
|
||||
public Builder groupId(String groupId) {
|
||||
Assert.hasText(groupId, "'groupId' must not be empty");
|
||||
this.groupId = groupId;
|
||||
return this;
|
||||
}
|
||||
@@ -268,6 +269,7 @@ public abstract class AbstractAotProcessor<T> {
|
||||
* @return this builder for method chaining
|
||||
*/
|
||||
public Builder artifactId(String artifactId) {
|
||||
Assert.hasText(artifactId, "'artifactId' must not be empty");
|
||||
this.artifactId = artifactId;
|
||||
return this;
|
||||
}
|
||||
@@ -279,14 +281,12 @@ public abstract class AbstractAotProcessor<T> {
|
||||
Assert.notNull(this.sourceOutput, "'sourceOutput' must not be null");
|
||||
Assert.notNull(this.resourceOutput, "'resourceOutput' must not be null");
|
||||
Assert.notNull(this.classOutput, "'classOutput' must not be null");
|
||||
Assert.hasText(this.groupId, "'groupId' must not be null or empty");
|
||||
Assert.hasText(this.artifactId, "'artifactId' must not be null or empty");
|
||||
Assert.notNull(this.groupId, "'groupId' must not be null");
|
||||
Assert.notNull(this.artifactId, "'artifactId' must not be null");
|
||||
return new Settings(this.sourceOutput, this.resourceOutput, this.classOutput,
|
||||
this.groupId, this.artifactId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+7
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -35,8 +35,8 @@ import org.springframework.lang.Nullable;
|
||||
*/
|
||||
class KotlinReflectionBeanRegistrationAotProcessor implements BeanRegistrationAotProcessor {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
@Nullable
|
||||
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
|
||||
Class<?> beanClass = registeredBean.getBeanClass();
|
||||
if (KotlinDetector.isKotlinType(beanClass)) {
|
||||
@@ -45,6 +45,7 @@ class KotlinReflectionBeanRegistrationAotProcessor implements BeanRegistrationAo
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private static class AotContribution implements BeanRegistrationAotContribution {
|
||||
|
||||
private final Class<?> beanClass;
|
||||
@@ -66,6 +67,10 @@ class KotlinReflectionBeanRegistrationAotProcessor implements BeanRegistrationAo
|
||||
if (superClass != null) {
|
||||
registerHints(superClass, runtimeHints);
|
||||
}
|
||||
Class<?> enclosingClass = type.getEnclosingClass();
|
||||
if (enclosingClass != null) {
|
||||
runtimeHints.reflection().registerType(enclosingClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+8
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -318,8 +318,8 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
|
||||
}
|
||||
}
|
||||
|
||||
private void publishEvents(Object result) {
|
||||
if (result.getClass().isArray()) {
|
||||
private void publishEvents(@Nullable Object result) {
|
||||
if (result != null && result.getClass().isArray()) {
|
||||
Object[] events = ObjectUtils.toObjectArray(result);
|
||||
for (Object event : events) {
|
||||
publishEvent(event);
|
||||
@@ -350,7 +350,7 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
|
||||
* Invoke the event listener method with the given argument values.
|
||||
*/
|
||||
@Nullable
|
||||
protected Object doInvoke(Object... args) {
|
||||
protected Object doInvoke(@Nullable Object... args) {
|
||||
Object bean = getTargetBean();
|
||||
// Detect package-protected NullBean instance through equals(null) check
|
||||
if (bean.equals(null)) {
|
||||
@@ -416,8 +416,8 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
|
||||
* the given error message.
|
||||
* @param message error message to append the HandlerMethod details to
|
||||
*/
|
||||
protected String getDetailedErrorMessage(Object bean, String message) {
|
||||
StringBuilder sb = new StringBuilder(message).append('\n');
|
||||
protected String getDetailedErrorMessage(Object bean, @Nullable String message) {
|
||||
StringBuilder sb = (StringUtils.hasLength(message) ? new StringBuilder(message).append('\n') : new StringBuilder());
|
||||
sb.append("HandlerMethod details: \n");
|
||||
sb.append("Bean [").append(bean.getClass().getName()).append("]\n");
|
||||
sb.append("Method [").append(this.method.toGenericString()).append("]\n");
|
||||
@@ -431,7 +431,7 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
|
||||
* beans, and others). Event listener beans that require proxying should prefer
|
||||
* class-based proxy mechanisms.
|
||||
*/
|
||||
private void assertTargetBean(Method method, Object targetBean, Object[] args) {
|
||||
private void assertTargetBean(Method method, Object targetBean, @Nullable Object[] args) {
|
||||
Class<?> methodDeclaringClass = method.getDeclaringClass();
|
||||
Class<?> targetBeanClass = targetBean.getClass();
|
||||
if (!methodDeclaringClass.isAssignableFrom(targetBeanClass)) {
|
||||
@@ -443,7 +443,7 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
|
||||
}
|
||||
}
|
||||
|
||||
private String getInvocationErrorMessage(Object bean, String message, Object[] resolvedArgs) {
|
||||
private String getInvocationErrorMessage(Object bean, @Nullable String message, @Nullable Object[] resolvedArgs) {
|
||||
StringBuilder sb = new StringBuilder(getDetailedErrorMessage(bean, message));
|
||||
sb.append("Resolved arguments: \n");
|
||||
for (int i = 0; i < resolvedArgs.length; i++) {
|
||||
|
||||
+1
@@ -1463,6 +1463,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale) {
|
||||
return getMessageSource().getMessage(code, args, defaultMessage, locale);
|
||||
}
|
||||
|
||||
+1
@@ -137,6 +137,7 @@ public abstract class AbstractMessageSource extends MessageSourceSupport impleme
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public final String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale) {
|
||||
String msg = getMessageInternal(code, args, locale);
|
||||
if (msg != null) {
|
||||
|
||||
+1
@@ -123,6 +123,7 @@ class ContextTypeMatchClassLoader extends DecoratingClassLoader implements Smart
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected Class<?> loadClassForOverriding(String name) throws ClassNotFoundException {
|
||||
byte[] bytes = bytesCache.get(name);
|
||||
if (bytes == null) {
|
||||
|
||||
+1
@@ -191,6 +191,7 @@ public class ReloadableResourceBundleMessageSource extends AbstractResourceBased
|
||||
* returning the value found in the bundle as-is (without MessageFormat parsing).
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
protected String resolveCodeWithoutArguments(String code, Locale locale) {
|
||||
if (getCacheMillis() < 0) {
|
||||
PropertiesHolder propHolder = getMergedProperties(locale);
|
||||
|
||||
+1
@@ -145,6 +145,7 @@ public class ResourceBundleMessageSource extends AbstractResourceBasedMessageSou
|
||||
* returning the value found in the bundle as-is (without MessageFormat parsing).
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
protected String resolveCodeWithoutArguments(String code, Locale locale) {
|
||||
Set<String> basenames = getBasenameSet();
|
||||
for (String basename : basenames) {
|
||||
|
||||
+2
-1
@@ -19,6 +19,7 @@ package org.springframework.format.support;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* {@link RuntimeHintsRegistrar} to register hints for {@link DefaultFormattingConversionService}.
|
||||
@@ -29,7 +30,7 @@ import org.springframework.aot.hint.TypeReference;
|
||||
class FormattingConversionServiceRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
||||
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
|
||||
hints.reflection().registerType(TypeReference.of("javax.money.MonetaryAmount"));
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -18,6 +18,8 @@ package org.springframework.jmx.access;
|
||||
|
||||
import javax.management.JMRuntimeException;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Thrown when trying to invoke an operation on a proxy that is not exposed
|
||||
* by the proxied MBean resource's management interface.
|
||||
@@ -35,7 +37,7 @@ public class InvalidInvocationException extends JMRuntimeException {
|
||||
* error message.
|
||||
* @param msg the detail message
|
||||
*/
|
||||
public InvalidInvocationException(String msg) {
|
||||
public InvalidInvocationException(@Nullable String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
|
||||
+9
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -83,18 +83,21 @@ public class MBeanProxyFactoryBean extends MBeanClientInterceptor
|
||||
public void afterPropertiesSet() throws MBeanServerNotFoundException, MBeanInfoRetrievalException {
|
||||
super.afterPropertiesSet();
|
||||
|
||||
Class<?> interfaceToUse;
|
||||
if (this.proxyInterface == null) {
|
||||
this.proxyInterface = getManagementInterface();
|
||||
if (this.proxyInterface == null) {
|
||||
interfaceToUse = getManagementInterface();
|
||||
if (interfaceToUse == null) {
|
||||
throw new IllegalArgumentException("Property 'proxyInterface' or 'managementInterface' is required");
|
||||
}
|
||||
this.proxyInterface = interfaceToUse;
|
||||
}
|
||||
else {
|
||||
interfaceToUse = this.proxyInterface;
|
||||
if (getManagementInterface() == null) {
|
||||
setManagementInterface(this.proxyInterface);
|
||||
setManagementInterface(interfaceToUse);
|
||||
}
|
||||
}
|
||||
this.mbeanProxy = new ProxyFactory(this.proxyInterface, this).getProxy(this.beanClassLoader);
|
||||
this.mbeanProxy = new ProxyFactory(interfaceToUse, this).getProxy(this.beanClassLoader);
|
||||
}
|
||||
|
||||
|
||||
@@ -105,6 +108,7 @@ public class MBeanProxyFactoryBean extends MBeanClientInterceptor
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Class<?> getObjectType() {
|
||||
return this.proxyInterface;
|
||||
}
|
||||
|
||||
@@ -273,6 +273,7 @@ public class JndiObjectFactoryBean extends JndiObjectLocator
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Class<?> getObjectType() {
|
||||
if (this.proxyInterfaces != null) {
|
||||
if (this.proxyInterfaces.length == 1) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -68,7 +68,7 @@ public interface TaskScheduler {
|
||||
* @param trigger an implementation of the {@link Trigger} interface,
|
||||
* e.g. a {@link org.springframework.scheduling.support.CronTrigger} object
|
||||
* wrapping a cron expression
|
||||
* @return a {@link ScheduledFuture} representing pending completion of the task,
|
||||
* @return a {@link ScheduledFuture} representing pending execution of the task,
|
||||
* or {@code null} if the given Trigger object never fires (i.e. returns
|
||||
* {@code null} from {@link Trigger#nextExecution})
|
||||
* @throws org.springframework.core.task.TaskRejectedException if the given task was not accepted
|
||||
@@ -85,7 +85,7 @@ public interface TaskScheduler {
|
||||
* @param task the Runnable to execute whenever the trigger fires
|
||||
* @param startTime the desired execution time for the task
|
||||
* (if this is in the past, the task will be executed immediately, i.e. as soon as possible)
|
||||
* @return a {@link ScheduledFuture} representing pending completion of the task
|
||||
* @return a {@link ScheduledFuture} representing pending execution of the task
|
||||
* @throws org.springframework.core.task.TaskRejectedException if the given task was not accepted
|
||||
* for internal reasons (e.g. a pool overload handling policy or a pool shutdown in progress)
|
||||
* @since 5.0
|
||||
@@ -99,7 +99,7 @@ public interface TaskScheduler {
|
||||
* @param task the Runnable to execute whenever the trigger fires
|
||||
* @param startTime the desired execution time for the task
|
||||
* (if this is in the past, the task will be executed immediately, i.e. as soon as possible)
|
||||
* @return a {@link ScheduledFuture} representing pending completion of the task
|
||||
* @return a {@link ScheduledFuture} representing pending execution of the task
|
||||
* @throws org.springframework.core.task.TaskRejectedException if the given task was not accepted
|
||||
* for internal reasons (e.g. a pool overload handling policy or a pool shutdown in progress)
|
||||
* @deprecated as of 6.0, in favor of {@link #schedule(Runnable, Instant)}
|
||||
@@ -118,7 +118,7 @@ public interface TaskScheduler {
|
||||
* @param startTime the desired first execution time for the task
|
||||
* (if this is in the past, the task will be executed immediately, i.e. as soon as possible)
|
||||
* @param period the interval between successive executions of the task
|
||||
* @return a {@link ScheduledFuture} representing pending completion of the task
|
||||
* @return a {@link ScheduledFuture} representing pending execution of the task
|
||||
* @throws org.springframework.core.task.TaskRejectedException if the given task was not accepted
|
||||
* for internal reasons (e.g. a pool overload handling policy or a pool shutdown in progress)
|
||||
* @since 5.0
|
||||
@@ -134,7 +134,7 @@ public interface TaskScheduler {
|
||||
* @param startTime the desired first execution time for the task
|
||||
* (if this is in the past, the task will be executed immediately, i.e. as soon as possible)
|
||||
* @param period the interval between successive executions of the task (in milliseconds)
|
||||
* @return a {@link ScheduledFuture} representing pending completion of the task
|
||||
* @return a {@link ScheduledFuture} representing pending execution of the task
|
||||
* @throws org.springframework.core.task.TaskRejectedException if the given task was not accepted
|
||||
* for internal reasons (e.g. a pool overload handling policy or a pool shutdown in progress)
|
||||
* @deprecated as of 6.0, in favor of {@link #scheduleAtFixedRate(Runnable, Instant, Duration)}
|
||||
@@ -151,7 +151,7 @@ public interface TaskScheduler {
|
||||
* {@link ScheduledFuture} gets cancelled.
|
||||
* @param task the Runnable to execute whenever the trigger fires
|
||||
* @param period the interval between successive executions of the task
|
||||
* @return a {@link ScheduledFuture} representing pending completion of the task
|
||||
* @return a {@link ScheduledFuture} representing pending execution of the task
|
||||
* @throws org.springframework.core.task.TaskRejectedException if the given task was not accepted
|
||||
* for internal reasons (e.g. a pool overload handling policy or a pool shutdown in progress)
|
||||
* @since 5.0
|
||||
@@ -165,7 +165,7 @@ public interface TaskScheduler {
|
||||
* {@link ScheduledFuture} gets cancelled.
|
||||
* @param task the Runnable to execute whenever the trigger fires
|
||||
* @param period the interval between successive executions of the task (in milliseconds)
|
||||
* @return a {@link ScheduledFuture} representing pending completion of the task
|
||||
* @return a {@link ScheduledFuture} representing pending execution of the task
|
||||
* @throws org.springframework.core.task.TaskRejectedException if the given task was not accepted
|
||||
* for internal reasons (e.g. a pool overload handling policy or a pool shutdown in progress)
|
||||
* @deprecated as of 6.0, in favor of {@link #scheduleAtFixedRate(Runnable, Duration)}
|
||||
@@ -185,7 +185,7 @@ public interface TaskScheduler {
|
||||
* @param startTime the desired first execution time for the task
|
||||
* (if this is in the past, the task will be executed immediately, i.e. as soon as possible)
|
||||
* @param delay the delay between the completion of one execution and the start of the next
|
||||
* @return a {@link ScheduledFuture} representing pending completion of the task
|
||||
* @return a {@link ScheduledFuture} representing pending execution of the task
|
||||
* @throws org.springframework.core.task.TaskRejectedException if the given task was not accepted
|
||||
* for internal reasons (e.g. a pool overload handling policy or a pool shutdown in progress)
|
||||
* @since 5.0
|
||||
@@ -203,7 +203,7 @@ public interface TaskScheduler {
|
||||
* (if this is in the past, the task will be executed immediately, i.e. as soon as possible)
|
||||
* @param delay the delay between the completion of one execution and the start of the next
|
||||
* (in milliseconds)
|
||||
* @return a {@link ScheduledFuture} representing pending completion of the task
|
||||
* @return a {@link ScheduledFuture} representing pending execution of the task
|
||||
* @throws org.springframework.core.task.TaskRejectedException if the given task was not accepted
|
||||
* for internal reasons (e.g. a pool overload handling policy or a pool shutdown in progress)
|
||||
* @deprecated as of 6.0, in favor of {@link #scheduleWithFixedDelay(Runnable, Instant, Duration)}
|
||||
@@ -220,7 +220,7 @@ public interface TaskScheduler {
|
||||
* {@link ScheduledFuture} gets cancelled.
|
||||
* @param task the Runnable to execute whenever the trigger fires
|
||||
* @param delay the delay between the completion of one execution and the start of the next
|
||||
* @return a {@link ScheduledFuture} representing pending completion of the task
|
||||
* @return a {@link ScheduledFuture} representing pending execution of the task
|
||||
* @throws org.springframework.core.task.TaskRejectedException if the given task was not accepted
|
||||
* for internal reasons (e.g. a pool overload handling policy or a pool shutdown in progress)
|
||||
* @since 5.0
|
||||
@@ -235,7 +235,7 @@ public interface TaskScheduler {
|
||||
* @param task the Runnable to execute whenever the trigger fires
|
||||
* @param delay the delay between the completion of one execution and the start of the next
|
||||
* (in milliseconds)
|
||||
* @return a {@link ScheduledFuture} representing pending completion of the task
|
||||
* @return a {@link ScheduledFuture} representing pending execution of the task
|
||||
* @throws org.springframework.core.task.TaskRejectedException if the given task was not accepted
|
||||
* for internal reasons (e.g. a pool overload handling policy or a pool shutdown in progress)
|
||||
* @deprecated as of 6.0, in favor of {@link #scheduleWithFixedDelay(Runnable, Duration)}
|
||||
|
||||
+1
@@ -34,6 +34,7 @@ import org.springframework.lang.Nullable;
|
||||
public class AsyncConfigurerSupport implements AsyncConfigurer {
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Executor getAsyncExecutor() {
|
||||
return null;
|
||||
}
|
||||
|
||||
+5
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -143,11 +143,6 @@ public class ConcurrentTaskExecutor implements AsyncListenableTaskExecutor, Sche
|
||||
* execution callback (which may be a wrapper around the user-supplied task).
|
||||
* <p>The primary use case is to set some execution context around the task's
|
||||
* invocation, or to provide some monitoring/statistics for task execution.
|
||||
* <p><b>NOTE:</b> Exception handling in {@code TaskDecorator} implementations
|
||||
* is limited to plain {@code Runnable} execution via {@code execute} calls.
|
||||
* In case of {@code #submit} calls, the exposed {@code Runnable} will be a
|
||||
* {@code FutureTask} which does not propagate any exceptions; you might
|
||||
* have to cast it and call {@code Future#get} to evaluate exceptions.
|
||||
* @since 4.3
|
||||
*/
|
||||
public final void setTaskDecorator(TaskDecorator taskDecorator) {
|
||||
@@ -188,11 +183,10 @@ public class ConcurrentTaskExecutor implements AsyncListenableTaskExecutor, Sche
|
||||
}
|
||||
|
||||
|
||||
private TaskExecutorAdapter getAdaptedExecutor(Executor concurrentExecutor) {
|
||||
if (managedExecutorServiceClass != null && managedExecutorServiceClass.isInstance(concurrentExecutor)) {
|
||||
return new ManagedTaskExecutorAdapter(concurrentExecutor);
|
||||
}
|
||||
TaskExecutorAdapter adapter = new TaskExecutorAdapter(concurrentExecutor);
|
||||
private TaskExecutorAdapter getAdaptedExecutor(Executor originalExecutor) {
|
||||
TaskExecutorAdapter adapter =
|
||||
(managedExecutorServiceClass != null && managedExecutorServiceClass.isInstance(originalExecutor) ?
|
||||
new ManagedTaskExecutorAdapter(originalExecutor) : new TaskExecutorAdapter(originalExecutor));
|
||||
if (this.taskDecorator != null) {
|
||||
adapter.setTaskDecorator(this.taskDecorator);
|
||||
}
|
||||
|
||||
+5
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -191,6 +191,7 @@ public class ConcurrentTaskScheduler extends ConcurrentTaskExecutor implements T
|
||||
* @see Clock#systemDefaultZone()
|
||||
*/
|
||||
public void setClock(Clock clock) {
|
||||
Assert.notNull(clock, "Clock must not be null");
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
@@ -333,16 +334,19 @@ public class ConcurrentTaskScheduler extends ConcurrentTaskExecutor implements T
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Instant lastScheduledExecution() {
|
||||
return (this.le != null ? toInstant(this.le.getScheduledStart()) : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Instant lastActualExecution() {
|
||||
return (this.le != null ? toInstant(this.le.getRunStart()) : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Instant lastCompletion() {
|
||||
return (this.le != null ? toInstant(this.le.getRunEnd()) : null);
|
||||
}
|
||||
|
||||
+8
-1
@@ -41,6 +41,7 @@ import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.scheduling.Trigger;
|
||||
import org.springframework.scheduling.support.DelegatingErrorHandlingRunnable;
|
||||
import org.springframework.scheduling.support.TaskUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ErrorHandler;
|
||||
|
||||
/**
|
||||
@@ -73,6 +74,12 @@ import org.springframework.util.ErrorHandler;
|
||||
* which tend to have specific constraints for the scheduler thread pool,
|
||||
* requiring a separate thread pool for general executor purposes in practice.
|
||||
*
|
||||
* <p><b>NOTE: This scheduler variant does not track the actual completion of tasks
|
||||
* but rather just the hand-off to an execution thread.</b> As a consequence,
|
||||
* a {@link ScheduledFuture} handle (e.g. from {@link #schedule(Runnable, Instant)})
|
||||
* represents that hand-off rather than the actual completion of the provided task
|
||||
* (or series of repeated tasks).
|
||||
*
|
||||
* <p>As an alternative to the built-in thread-per-task capability, this scheduler
|
||||
* can also be configured with a separate target executor for scheduled task
|
||||
* execution through {@link #setTargetTaskExecutor}: e.g. pointing to a shared
|
||||
@@ -114,10 +121,10 @@ public class SimpleAsyncTaskScheduler extends SimpleAsyncTaskExecutor implements
|
||||
/**
|
||||
* Set the clock to use for scheduling purposes.
|
||||
* <p>The default clock is the system clock for the default time zone.
|
||||
* @since 5.3
|
||||
* @see Clock#systemDefaultZone()
|
||||
*/
|
||||
public void setClock(Clock clock) {
|
||||
Assert.notNull(clock, "Clock must not be null");
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
|
||||
+12
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -48,7 +48,14 @@ import org.springframework.util.concurrent.ListenableFutureTask;
|
||||
/**
|
||||
* A standard implementation of Spring's {@link TaskScheduler} interface, wrapping
|
||||
* a native {@link java.util.concurrent.ScheduledThreadPoolExecutor} and providing
|
||||
* all applicable configuration options for it.
|
||||
* all applicable configuration options for it. The default number of scheduler
|
||||
* threads is 1; a higher number can be configured through {@link #setPoolSize}.
|
||||
*
|
||||
* <p>This is Spring's traditional scheduler variant, staying as close as possible to
|
||||
* {@link java.util.concurrent.ScheduledExecutorService} semantics. Task execution happens
|
||||
* on the scheduler thread(s) rather than on separate execution threads. As a consequence,
|
||||
* a {@link ScheduledFuture} handle (e.g. from {@link #schedule(Runnable, Instant)})
|
||||
* represents the actual completion of the provided task (or series of repeated tasks).
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Mark Fisher
|
||||
@@ -59,6 +66,8 @@ import org.springframework.util.concurrent.ListenableFutureTask;
|
||||
* @see #setExecuteExistingDelayedTasksAfterShutdownPolicy
|
||||
* @see #setThreadFactory
|
||||
* @see #setErrorHandler
|
||||
* @see ThreadPoolTaskExecutor
|
||||
* @see SimpleAsyncTaskScheduler
|
||||
*/
|
||||
@SuppressWarnings({"serial", "deprecation"})
|
||||
public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport
|
||||
@@ -159,6 +168,7 @@ public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport
|
||||
* @see Clock#systemDefaultZone()
|
||||
*/
|
||||
public void setClock(Clock clock) {
|
||||
Assert.notNull(clock, "Clock must not be null");
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -106,6 +106,7 @@ public class TaskSchedulerRouter implements TaskScheduler, BeanNameAware, BeanFa
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public ScheduledFuture<?> schedule(Runnable task, Trigger trigger) {
|
||||
return determineTargetScheduler(task).schedule(task, trigger);
|
||||
}
|
||||
|
||||
@@ -112,6 +112,7 @@ public class CronTrigger implements Trigger {
|
||||
* previous execution; therefore, overlapping executions won't occur.
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public Instant nextExecution(TriggerContext triggerContext) {
|
||||
Instant timestamp = determineLatestTimestamp(triggerContext);
|
||||
ZoneId zone = (this.zoneId != null ? this.zoneId : triggerContext.getClock().getZone());
|
||||
|
||||
+1
@@ -338,6 +338,7 @@ final class QuartzCronField extends CronField {
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public <T extends Temporal & Comparable<? super T>> T nextOrSame(T temporal) {
|
||||
T result = adjust(temporal);
|
||||
if (result != null) {
|
||||
|
||||
+2
@@ -22,6 +22,7 @@ import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.TypedStringValue;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -38,6 +39,7 @@ class ScriptingDefaultsParser implements BeanDefinitionParser {
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
BeanDefinition bd =
|
||||
LangNamespaceUtils.registerScriptFactoryPostProcessorIfNecessary(parserContext.getRegistry());
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user