mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
62 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4c7f939e01 | |||
| ea208dc304 | |||
| c8c95e360f | |||
| 9fb36a5dcb | |||
| 4caf6bc5b8 | |||
| ee3e1591de | |||
| e81c788274 | |||
| 09f23a578b | |||
| 5288504ceb | |||
| 8b6a54c45f | |||
| f3f3063091 | |||
| c1f3e37acd | |||
| 427a96befc | |||
| 77951dc622 | |||
| 43dd22ba31 | |||
| dfe437ac88 | |||
| 9c775d2643 | |||
| 3b50b6ef94 | |||
| 9a31f3b5a5 | |||
| b11d118376 | |||
| 0931769822 | |||
| f7b7e29fd7 | |||
| 08a63a4180 | |||
| 4e3b834310 | |||
| fb67f97a41 | |||
| cafb5cfbbe | |||
| 9d2c6f80b8 | |||
| d9330bcac7 | |||
| 1fb179b057 | |||
| 5c9f364352 | |||
| 55f9581743 | |||
| 190397d5e0 | |||
| 94097fb112 | |||
| e4ab2aa775 | |||
| 40bf550d56 | |||
| f3bdce455e | |||
| 402246df28 | |||
| 88a68ddbd2 | |||
| 7d178c6771 | |||
| ba776d7201 | |||
| aba5f421fc | |||
| 2bac1629e6 | |||
| 9412d782ce | |||
| a0bd13ceb1 | |||
| 46a0821205 | |||
| 2c531344c6 | |||
| 6610ee360e | |||
| afde96c540 | |||
| da799bc519 | |||
| 755968fd2c | |||
| eea00628f9 | |||
| 8d745462b4 | |||
| ed6c25fb6e | |||
| 8460a2d285 | |||
| c3d186b4d0 | |||
| f8926d6a66 | |||
| 19b21b15c1 | |||
| 547425275c | |||
| 658194c155 | |||
| 769f73ebf1 | |||
| 80809bbc14 | |||
| 6d323d710a |
@@ -0,0 +1,33 @@
|
||||
name: Send notification
|
||||
description: Sends a Google Chat message as a notification of the job's outcome
|
||||
inputs:
|
||||
webhook-url:
|
||||
description: 'Google Chat Webhook URL'
|
||||
required: true
|
||||
status:
|
||||
description: 'Status of the job'
|
||||
required: true
|
||||
build-scan-url:
|
||||
description: 'URL of the build scan to include in the notification'
|
||||
run-name:
|
||||
description: 'Name of the run to include in the notification'
|
||||
default: ${{ format('{0} {1}', github.ref_name, github.job) }}
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- shell: bash
|
||||
run: |
|
||||
echo "BUILD_SCAN=${{ inputs.build-scan-url == '' && ' [build scan unavailable]' || format(' [<{0}|Build Scan>]', inputs.build-scan-url) }}" >> "$GITHUB_ENV"
|
||||
echo "RUN_URL=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> "$GITHUB_ENV"
|
||||
- shell: bash
|
||||
if: ${{ inputs.status == 'success' }}
|
||||
run: |
|
||||
curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<${{ env.RUN_URL }}|${{ inputs.run-name }}> was successful ${{ env.BUILD_SCAN }}"}' || true
|
||||
- shell: bash
|
||||
if: ${{ inputs.status == 'failure' }}
|
||||
run: |
|
||||
curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<users/all> *<${{ env.RUN_URL }}|${{ inputs.run-name }}> failed* ${{ env.BUILD_SCAN }}"}' || true
|
||||
- shell: bash
|
||||
if: ${{ inputs.status == 'cancelled' }}
|
||||
run: |
|
||||
curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<${{ env.RUN_URL }}|${{ inputs.run-name }}> was cancelled"}' || true
|
||||
@@ -18,11 +18,13 @@ jobs:
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-java@v3
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '17'
|
||||
distribution: 'liberica'
|
||||
java-version: 17
|
||||
- name: Download BackportBot
|
||||
run: wget https://github.com/spring-io/backport-bot/releases/download/latest/backport-bot-0.0.1-SNAPSHOT.jar
|
||||
- name: Backport
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
name: Build and deploy snapshot
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 6.0.x
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
jobs:
|
||||
build-and-deploy-snapshot:
|
||||
if: ${{ github.repository == 'spring-projects/spring-framework' }}
|
||||
name: Build and deploy snapshot
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'liberica'
|
||||
java-version: 17
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
|
||||
with:
|
||||
cache-read-only: false
|
||||
- name: Configure Gradle properties
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p $HOME/.gradle
|
||||
echo 'systemProp.user.name=spring-builds+github' >> $HOME/.gradle/gradle.properties
|
||||
echo 'systemProp.org.gradle.internal.launcher.welcomeMessageEnabled=false' >> $HOME/.gradle/gradle.properties
|
||||
echo 'org.gradle.daemon=false' >> $HOME/.gradle/gradle.properties
|
||||
echo 'org.gradle.daemon=4' >> $HOME/.gradle/gradle.properties
|
||||
- name: Build and publish
|
||||
id: build
|
||||
env:
|
||||
CI: 'true'
|
||||
GRADLE_ENTERPRISE_URL: 'https://ge.spring.io'
|
||||
DEVELOCITY_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||
run: ./gradlew -PdeploymentRepository=$(pwd)/deployment-repository build publishAllPublicationsToDeploymentRepository
|
||||
- name: Deploy
|
||||
uses: spring-io/artifactory-deploy-action@v0.0.1
|
||||
with:
|
||||
uri: 'https://repo.spring.io'
|
||||
username: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
password: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
build-name: ${{ format('spring-framework-{0}', github.ref_name)}}
|
||||
repository: 'libs-snapshot-local'
|
||||
folder: 'deployment-repository'
|
||||
signing-key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
signing-passphrase: ${{ secrets.GPG_PASSPHRASE }}
|
||||
artifact-properties: |
|
||||
/**/framework-docs-*.zip::zip.name=spring-framework,zip.deployed=false
|
||||
/**/framework-docs-*-docs.zip::zip.type=docs
|
||||
/**/framework-docs-*-dist.zip::zip.type=dist
|
||||
/**/framework-docs-*-schema.zip::zip.type=schema
|
||||
- name: Send notification
|
||||
uses: ./.github/actions/send-notification
|
||||
if: always()
|
||||
with:
|
||||
webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
|
||||
status: ${{ job.status }}
|
||||
build-scan-url: ${{ steps.build.outputs.build-scan-url }}
|
||||
run-name: ${{ format('{0} | Linux | Java 17', github.ref_name) }}
|
||||
@@ -0,0 +1,78 @@
|
||||
name: CI
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 6.0.x
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
jobs:
|
||||
ci:
|
||||
if: ${{ github.repository == 'spring-projects/spring-framework' }}
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- id: ubuntu-latest
|
||||
name: Linux
|
||||
java:
|
||||
- version: 17
|
||||
toolchain: false
|
||||
- version: 21
|
||||
toolchain: true
|
||||
exclude:
|
||||
- os:
|
||||
name: Linux
|
||||
java:
|
||||
version: 17
|
||||
name: '${{ matrix.os.name}} | Java ${{ matrix.java.version}}'
|
||||
runs-on: ${{ matrix.os.id }}
|
||||
steps:
|
||||
- name: Set up Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'liberica'
|
||||
java-version: |
|
||||
${{ matrix.java.version }}
|
||||
${{ matrix.java.toolchain && '17' || '' }}
|
||||
- name: Prepare Windows runner
|
||||
if: ${{ runner.os == 'Windows' }}
|
||||
run: |
|
||||
git config --global core.autocrlf true
|
||||
git config --global core.longPaths true
|
||||
Stop-Service -name Docker
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
|
||||
with:
|
||||
cache-read-only: false
|
||||
- name: Configure Gradle properties
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p $HOME/.gradle
|
||||
echo 'systemProp.user.name=spring-builds+github' >> $HOME/.gradle/gradle.properties
|
||||
echo 'systemProp.org.gradle.internal.launcher.welcomeMessageEnabled=false' >> $HOME/.gradle/gradle.properties
|
||||
echo 'org.gradle.daemon=false' >> $HOME/.gradle/gradle.properties
|
||||
echo 'org.gradle.daemon=4' >> $HOME/.gradle/gradle.properties
|
||||
- name: Configure toolchain properties
|
||||
if: ${{ matrix.java.toolchain }}
|
||||
shell: bash
|
||||
run: |
|
||||
echo toolchainVersion=${{ matrix.java.version }} >> $HOME/.gradle/gradle.properties
|
||||
echo systemProp.org.gradle.java.installations.auto-detect=false >> $HOME/.gradle/gradle.properties
|
||||
echo systemProp.org.gradle.java.installations.auto-download=false >> $HOME/.gradle/gradle.properties
|
||||
echo systemProp.org.gradle.java.installations.paths=${{ format('$JAVA_HOME_{0}_X64', matrix.java.version) }} >> $HOME/.gradle/gradle.properties
|
||||
- name: Build
|
||||
id: build
|
||||
env:
|
||||
CI: 'true'
|
||||
GRADLE_ENTERPRISE_URL: 'https://ge.spring.io'
|
||||
DEVELOCITY_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||
run: ./gradlew check antora
|
||||
- name: Send notification
|
||||
uses: ./.github/actions/send-notification
|
||||
if: always()
|
||||
with:
|
||||
webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
|
||||
status: ${{ job.status }}
|
||||
build-scan-url: ${{ steps.build.outputs.build-scan-url }}
|
||||
run-name: ${{ format('{0} | {1} | Java {2}', github.ref_name, matrix.os.name, matrix.java.version) }}
|
||||
@@ -1,12 +1,14 @@
|
||||
name: Deploy Docs
|
||||
on:
|
||||
push:
|
||||
branches-ignore: [ gh-pages ]
|
||||
tags: '**'
|
||||
branches:
|
||||
- 'main'
|
||||
- '*.x'
|
||||
- '!gh-pages'
|
||||
tags:
|
||||
- 'v*'
|
||||
repository_dispatch:
|
||||
types: request-build-reference # legacy
|
||||
schedule:
|
||||
- cron: '0 10 * * *' # Once per day at 10am UTC
|
||||
workflow_dispatch:
|
||||
permissions:
|
||||
actions: write
|
||||
@@ -15,8 +17,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository_owner == 'spring-projects'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: docs-build
|
||||
fetch-depth: 1
|
||||
|
||||
@@ -9,5 +9,5 @@ jobs:
|
||||
name: "Validation"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: gradle/wrapper-validation-action@v1
|
||||
- uses: actions/checkout@v4
|
||||
- uses: gradle/wrapper-validation-action@v2
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# Enable auto-env through the sdkman_auto_env config
|
||||
# Add key=value pairs of SDKs to use below
|
||||
java=17.0.8.1-librca
|
||||
java=17.0.11-librca
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# <img src="framework-docs/src/docs/spring-framework.png" width="80" height="80"> Spring Framework [](https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-6.0.x?groups=Build") [](https://ge.spring.io/scans?search.rootProjectNames=spring)
|
||||
# <img src="framework-docs/src/docs/spring-framework.png" width="80" height="80"> Spring Framework [](https://github.com/spring-projects/spring-framework/actions/workflows/build-and-deploy-snapshot.yml?query=branch%3A6.0.x) [](https://ge.spring.io/scans?search.rootProjectNames=spring)
|
||||
|
||||
This is the home of the Spring Framework: the foundation for all [Spring projects](https://spring.io/projects). Collectively the Spring Framework and the family of Spring projects are often referred to simply as "Spring".
|
||||
|
||||
|
||||
+3
-4
@@ -3,12 +3,12 @@ plugins {
|
||||
id 'io.freefair.aspectj' version '8.0.1' apply false
|
||||
// kotlinVersion is managed in gradle.properties
|
||||
id 'org.jetbrains.kotlin.plugin.serialization' version "${kotlinVersion}" apply false
|
||||
id 'org.jetbrains.dokka' version '1.8.10'
|
||||
id 'org.jetbrains.dokka' version '1.8.20'
|
||||
id 'org.unbroken-dome.xjc' version '2.0.0' apply false
|
||||
id 'com.github.ben-manes.versions' version '0.49.0'
|
||||
id 'com.github.ben-manes.versions' version '0.51.0'
|
||||
id 'com.github.johnrengelman.shadow' version '8.1.1' apply false
|
||||
id 'de.undercouch.download' version '5.4.0'
|
||||
id 'me.champeau.jmh' version '0.7.1' apply false
|
||||
id 'me.champeau.jmh' version '0.7.2' apply false
|
||||
}
|
||||
|
||||
ext {
|
||||
@@ -113,7 +113,6 @@ configure([rootProject] + javaProjects) { project ->
|
||||
"https://docs.oracle.com/en/java/javase/17/docs/api/",
|
||||
"https://jakarta.ee/specifications/platform/9/apidocs/",
|
||||
"https://docs.oracle.com/cd/E13222_01/wls/docs90/javadocs/", // CommonJ and weblogic.* packages
|
||||
"https://www.ibm.com/docs/api/v1/content/SSEQTP_8.5.5/com.ibm.websphere.javadoc.doc/web/apidocs/", // com.ibm.*
|
||||
"https://docs.jboss.org/jbossas/javadoc/4.0.5/connector/", // org.jboss.resource.*
|
||||
"https://docs.jboss.org/hibernate/orm/5.6/javadocs/",
|
||||
"https://eclipse.dev/aspectj/doc/released/aspectj5rt-api",
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
== Spring Framework Concourse pipeline
|
||||
|
||||
NOTE: CI is being migrated to GitHub Actions.
|
||||
|
||||
The Spring Framework uses https://concourse-ci.org/[Concourse] for its CI build and other automated tasks.
|
||||
The Spring team has a dedicated Concourse instance available at https://ci.spring.io with a build pipeline
|
||||
for https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-6.0.x[Spring Framework 6.0.x].
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM ubuntu:jammy-20240111
|
||||
FROM ubuntu:jammy-20240125
|
||||
|
||||
ADD setup.sh /setup.sh
|
||||
ADD get-jdk-url.sh /get-jdk-url.sh
|
||||
|
||||
@@ -8,4 +8,3 @@ milestone: "6.0.x"
|
||||
build-name: "spring-framework"
|
||||
pipeline-name: "spring-framework"
|
||||
concourse-url: "https://ci.spring.io"
|
||||
task-timeout: 1h00m
|
||||
|
||||
+15
-80
@@ -5,9 +5,7 @@ anchors:
|
||||
password: ((github-ci-release-token))
|
||||
branch: ((branch))
|
||||
gradle-enterprise-task-params: &gradle-enterprise-task-params
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY: ((gradle_enterprise_secret_access_key))
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME: ((gradle_enterprise_cache_user.username))
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD: ((gradle_enterprise_cache_user.password))
|
||||
DEVELOCITY_ACCESS_KEY: ((gradle_enterprise_secret_access_key))
|
||||
sonatype-task-params: &sonatype-task-params
|
||||
SONATYPE_USERNAME: ((sonatype-username))
|
||||
SONATYPE_PASSWORD: ((sonatype-password))
|
||||
@@ -23,14 +21,6 @@ anchors:
|
||||
docker-resource-source: &docker-resource-source
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
slack-fail-params: &slack-fail-params
|
||||
text: >
|
||||
:concourse-failed: <https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}|${BUILD_PIPELINE_NAME} ${BUILD_JOB_NAME} failed!>
|
||||
[$TEXT_FILE_CONTENT]
|
||||
text_file: git-repo/build/build-scan-uri.txt
|
||||
silent: true
|
||||
icon_emoji: ":concourse:"
|
||||
username: concourse-ci
|
||||
changelog-task-params: &changelog-task-params
|
||||
name: generated-changelog/tag
|
||||
tag: generated-changelog/tag
|
||||
@@ -45,7 +35,7 @@ resource_types:
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: concourse/registry-image-resource
|
||||
tag: 1.5.0
|
||||
tag: 1.8.0
|
||||
- name: artifactory-resource
|
||||
type: registry-image
|
||||
source:
|
||||
@@ -57,19 +47,13 @@ resource_types:
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: concourse/github-release-resource
|
||||
tag: 1.5.5
|
||||
tag: 1.8.0
|
||||
- name: github-status-resource
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: dpb587/github-status-resource
|
||||
tag: master
|
||||
- name: slack-notification
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: cfcommunity/slack-notification-resource
|
||||
tag: latest
|
||||
resources:
|
||||
- name: git-repo
|
||||
type: git
|
||||
@@ -98,19 +82,6 @@ resources:
|
||||
username: ((artifactory-username))
|
||||
password: ((artifactory-password))
|
||||
build_name: ((build-name))
|
||||
- name: repo-status-build
|
||||
type: github-status-resource
|
||||
icon: eye-check-outline
|
||||
source:
|
||||
repository: ((github-repo-name))
|
||||
access_token: ((github-ci-status-token))
|
||||
branch: ((branch))
|
||||
context: build
|
||||
- name: slack-alert
|
||||
type: slack-notification
|
||||
icon: slack
|
||||
source:
|
||||
url: ((slack-webhook-url))
|
||||
- name: github-pre-release
|
||||
type: github-release
|
||||
icon: briefcase-download-outline
|
||||
@@ -145,37 +116,23 @@ jobs:
|
||||
- put: ci-image
|
||||
params:
|
||||
image: ci-image/image.tar
|
||||
- name: build
|
||||
- name: stage-milestone
|
||||
serial: true
|
||||
public: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
trigger: true
|
||||
- put: repo-status-build
|
||||
params: { state: "pending", commit: "git-repo" }
|
||||
- do:
|
||||
- task: build-project
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/build-project.yml
|
||||
privileged: true
|
||||
timeout: ((task-timeout))
|
||||
params:
|
||||
<<: *build-project-task-params
|
||||
on_failure:
|
||||
do:
|
||||
- put: repo-status-build
|
||||
params: { state: "failure", commit: "git-repo" }
|
||||
- put: slack-alert
|
||||
params:
|
||||
<<: *slack-fail-params
|
||||
- put: repo-status-build
|
||||
params: { state: "success", commit: "git-repo" }
|
||||
trigger: false
|
||||
- task: stage
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/stage-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: M
|
||||
<<: *gradle-enterprise-task-params
|
||||
- put: artifactory-repo
|
||||
params: &artifactory-params
|
||||
signing_key: ((signing-key))
|
||||
signing_passphrase: ((signing-passphrase))
|
||||
repo: libs-snapshot-local
|
||||
repo: libs-staging-local
|
||||
folder: distribution-repository
|
||||
build_uri: "https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}"
|
||||
build_number: "${BUILD_PIPELINE_NAME}-${BUILD_JOB_NAME}-${BUILD_NAME}"
|
||||
@@ -200,27 +157,9 @@ jobs:
|
||||
- "/**/framework-docs-*-schema.zip"
|
||||
properties:
|
||||
"zip.type": "schema"
|
||||
get_params:
|
||||
threads: 8
|
||||
- name: stage-milestone
|
||||
serial: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
trigger: false
|
||||
- task: stage
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/stage-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: M
|
||||
<<: *gradle-enterprise-task-params
|
||||
- put: artifactory-repo
|
||||
params:
|
||||
<<: *artifactory-params
|
||||
repo: libs-staging-local
|
||||
- put: git-repo
|
||||
params:
|
||||
repository: stage-git-repo
|
||||
- put: git-repo
|
||||
params:
|
||||
repository: stage-git-repo
|
||||
- name: promote-milestone
|
||||
serial: true
|
||||
plan:
|
||||
@@ -262,7 +201,6 @@ jobs:
|
||||
- put: artifactory-repo
|
||||
params:
|
||||
<<: *artifactory-params
|
||||
repo: libs-staging-local
|
||||
- put: git-repo
|
||||
params:
|
||||
repository: stage-git-repo
|
||||
@@ -307,7 +245,6 @@ jobs:
|
||||
- put: artifactory-repo
|
||||
params:
|
||||
<<: *artifactory-params
|
||||
repo: libs-staging-local
|
||||
- put: git-repo
|
||||
params:
|
||||
repository: stage-git-repo
|
||||
@@ -352,8 +289,6 @@ jobs:
|
||||
<<: *changelog-task-params
|
||||
|
||||
groups:
|
||||
- name: "builds"
|
||||
jobs: ["build"]
|
||||
- name: "releases"
|
||||
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release"]
|
||||
- name: "ci-images"
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
source $(dirname $0)/common.sh
|
||||
|
||||
pushd git-repo > /dev/null
|
||||
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --max-workers=4 check
|
||||
popd > /dev/null
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
source $(dirname $0)/common.sh
|
||||
repository=$(pwd)/distribution-repository
|
||||
|
||||
pushd git-repo > /dev/null
|
||||
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --max-workers=4 -PdeploymentRepository=${repository} build publishAllPublicationsToDeploymentRepository
|
||||
popd > /dev/null
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
source $(dirname $0)/common.sh
|
||||
|
||||
pushd git-repo > /dev/null
|
||||
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK17 \
|
||||
-PmainToolchain=${MAIN_TOOLCHAIN} -PtestToolchain=${TEST_TOOLCHAIN} --no-daemon --max-workers=4 check antora
|
||||
popd > /dev/null
|
||||
@@ -1,19 +0,0 @@
|
||||
---
|
||||
platform: linux
|
||||
inputs:
|
||||
- name: git-repo
|
||||
caches:
|
||||
- path: gradle
|
||||
params:
|
||||
BRANCH:
|
||||
CI: true
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY:
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME:
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD:
|
||||
GRADLE_ENTERPRISE_URL: https://ge.spring.io
|
||||
run:
|
||||
path: bash
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
${PWD}/git-repo/ci/scripts/build-pr.sh
|
||||
@@ -1,22 +0,0 @@
|
||||
---
|
||||
platform: linux
|
||||
inputs:
|
||||
- name: git-repo
|
||||
outputs:
|
||||
- name: distribution-repository
|
||||
- name: git-repo
|
||||
caches:
|
||||
- path: gradle
|
||||
params:
|
||||
BRANCH:
|
||||
CI: true
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY:
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME:
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD:
|
||||
GRADLE_ENTERPRISE_URL: https://ge.spring.io
|
||||
run:
|
||||
path: bash
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
${PWD}/git-repo/ci/scripts/build-project.sh
|
||||
@@ -1,24 +0,0 @@
|
||||
---
|
||||
platform: linux
|
||||
inputs:
|
||||
- name: git-repo
|
||||
outputs:
|
||||
- name: distribution-repository
|
||||
- name: git-repo
|
||||
caches:
|
||||
- path: gradle
|
||||
params:
|
||||
BRANCH:
|
||||
CI: true
|
||||
MAIN_TOOLCHAIN:
|
||||
TEST_TOOLCHAIN:
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY:
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME:
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD:
|
||||
GRADLE_ENTERPRISE_URL: https://ge.spring.io
|
||||
run:
|
||||
path: bash
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
${PWD}/git-repo/ci/scripts/check-project.sh
|
||||
@@ -4,7 +4,7 @@ image_resource:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: springio/concourse-release-scripts
|
||||
tag: '0.4.0-SNAPSHOT'
|
||||
tag: '0.4.0'
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
inputs:
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ As of Spring Framework 5.2, the default configuration also provides support for
|
||||
Vavr's `Try` method to trigger transaction rollbacks when it returns a 'Failure'.
|
||||
This allows you to handle functional-style errors using Try and have the transaction
|
||||
automatically rolled back in case of a failure. For more information on Vavr's Try,
|
||||
refer to the [official Vavr documentation](https://www.vavr.io/vavr-docs/#_try).
|
||||
refer to the https://docs.vavr.io/#_try[official Vavr documentation].
|
||||
|
||||
Here's an example of how to use Vavr's Try with a transactional method:
|
||||
[tabs]
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
[[spring-mvc-test-vs-end-to-end-integration-tests]]
|
||||
= MockMvc vs End-to-End Tests
|
||||
|
||||
MockMVc is built on Servlet API mock implementations from the
|
||||
MockMvc is built on Servlet API mock implementations from the
|
||||
`spring-test` module and does not rely on a running container. Therefore, there are
|
||||
some differences when compared to full end-to-end integration tests with an actual
|
||||
client and a live server running.
|
||||
|
||||
+10
-33
@@ -1,38 +1,15 @@
|
||||
[[spring-mvc-test-vs-streaming-response]]
|
||||
= Streaming Responses
|
||||
|
||||
The best way to test streaming responses such as Server-Sent Events is through the
|
||||
<<WebTestClient>> which can be used as a test client to connect to a `MockMvc` instance
|
||||
to perform tests on Spring MVC controllers without a running server. For example:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
WebTestClient client = MockMvcWebTestClient.bindToController(new SseController()).build();
|
||||
|
||||
FluxExchangeResult<Person> exchangeResult = client.get()
|
||||
.uri("/persons")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectHeader().contentType("text/event-stream")
|
||||
.returnResult(Person.class);
|
||||
|
||||
// Use StepVerifier from Project Reactor to test the streaming response
|
||||
|
||||
StepVerifier.create(exchangeResult.getResponseBody())
|
||||
.expectNext(new Person("N0"), new Person("N1"), new Person("N2"))
|
||||
.expectNextCount(4)
|
||||
.consumeNextWith(person -> assertThat(person.getName()).endsWith("7"))
|
||||
.thenCancel()
|
||||
.verify();
|
||||
----
|
||||
======
|
||||
|
||||
`WebTestClient` can also connect to a live server and perform full end-to-end integration
|
||||
tests. This is also supported in Spring Boot where you can
|
||||
{docs-spring-boot}/html/spring-boot-features.html#boot-features-testing-spring-boot-applications-testing-with-running-server[test a running server].
|
||||
You can use `WebTestClient` to test xref:testing/webtestclient.adoc#webtestclient-stream[streaming responses]
|
||||
such as Server-Sent Events. However, `MockMvcWebTestClient` doesn't support infinite
|
||||
streams because there is no way to cancel the server stream from the client side.
|
||||
To test infinite streams, you'll need to
|
||||
xref:testing/webtestclient.adoc#webtestclient-server-config[bind to] a running server,
|
||||
or when using Spring Boot,
|
||||
{docs-spring-boot}/spring-boot-features.html#boot-features-testing-spring-boot-applications-testing-with-running-server[test with a running server].
|
||||
|
||||
`MockMvcWebTestClient` does support asynchronous responses, and even streaming responses.
|
||||
The limitation is that it can't influence the server to stop, and therefore the server
|
||||
must finish writing the response on its own.
|
||||
|
||||
|
||||
@@ -52,14 +52,10 @@ The following example shows how to achieve the same configuration in XML:
|
||||
</mvc:interceptors>
|
||||
----
|
||||
|
||||
NOTE: Interceptors are not ideally suited as a security layer due to the potential
|
||||
for a mismatch with annotated controller path matching, which can also match trailing
|
||||
slashes and path extensions transparently, along with other path matching options. Many
|
||||
of these options have been deprecated but the potential for a mismatch remains.
|
||||
Generally, we recommend using Spring Security which includes a dedicated
|
||||
https://docs.spring.io/spring-security/reference/servlet/integrations/mvc.html#mvc-requestmatcher[MvcRequestMatcher]
|
||||
to align with Spring MVC path matching and also has a security firewall that blocks many
|
||||
unwanted characters in URL paths.
|
||||
WARNING: Interceptors are not ideally suited as a security layer due to the potential for
|
||||
a mismatch with annotated controller path matching. Generally, we recommend using Spring
|
||||
Security, or alternatively a similar approach integrated with the Servlet filter chain,
|
||||
and applied as early as possible.
|
||||
|
||||
NOTE: The XML config declares interceptors as `MappedInterceptor` beans, and those are in
|
||||
turn detected by any `HandlerMapping` bean, including those from other frameworks.
|
||||
|
||||
+18
-23
@@ -1,34 +1,29 @@
|
||||
[[mvc-handlermapping-interceptor]]
|
||||
= Interception
|
||||
|
||||
All `HandlerMapping` implementations support handler interceptors that are useful when
|
||||
you want to apply specific functionality to certain requests -- for example, checking for
|
||||
a principal. Interceptors must implement `HandlerInterceptor` from the
|
||||
`org.springframework.web.servlet` package with three methods that should provide enough
|
||||
flexibility to do all kinds of pre-processing and post-processing:
|
||||
All `HandlerMapping` implementations support handler interception which is useful when
|
||||
you want to apply functionality across requests. A `HandlerInterceptor` can implement the
|
||||
following:
|
||||
|
||||
* `preHandle(..)`: Before the actual handler is run
|
||||
* `postHandle(..)`: After the handler is run
|
||||
* `afterCompletion(..)`: After the complete request has finished
|
||||
* `preHandle(..)` -- callback before the actual handler is run that returns a boolean.
|
||||
If the method returns `true`, execution continues; if it returns `false`, the rest of the
|
||||
execution chain is bypassed and the handler is not called.
|
||||
* `postHandle(..)` -- callback after the handler is run.
|
||||
* `afterCompletion(..)` -- callback after the complete request has finished.
|
||||
|
||||
The `preHandle(..)` method returns a boolean value. You can use this method to break or
|
||||
continue the processing of the execution chain. When this method returns `true`, the
|
||||
handler execution chain continues. When it returns false, the `DispatcherServlet`
|
||||
assumes the interceptor itself has taken care of requests (and, for example, rendered an
|
||||
appropriate view) and does not continue executing the other interceptors and the actual
|
||||
handler in the execution chain.
|
||||
NOTE: For `@ResponseBody` and `ResponseEntity` controller methods, the response is written
|
||||
and committed within the `HandlerAdapter`, before `postHandle` is called. That means it is
|
||||
too late to change the response, such as to add an extra header. You can implement
|
||||
`ResponseBodyAdvice` and declare it as an
|
||||
xref:web/webmvc/mvc-controller/ann-advice.adoc[Controller Advice] bean or configure it
|
||||
directly on `RequestMappingHandlerAdapter`.
|
||||
|
||||
See xref:web/webmvc/mvc-config/interceptors.adoc[Interceptors] in the section on MVC configuration for examples of how to
|
||||
configure interceptors. You can also register them directly by using setters on individual
|
||||
`HandlerMapping` implementations.
|
||||
|
||||
`postHandle` method is less useful with `@ResponseBody` and `ResponseEntity` methods for
|
||||
which the response is written and committed within the `HandlerAdapter` and before
|
||||
`postHandle`. That means it is too late to make any changes to the response, such as adding
|
||||
an extra header. For such scenarios, you can implement `ResponseBodyAdvice` and either
|
||||
declare it as an xref:web/webmvc/mvc-controller/ann-advice.adoc[Controller Advice] bean or configure it directly on
|
||||
`RequestMappingHandlerAdapter`.
|
||||
|
||||
|
||||
|
||||
WARNING: Interceptors are not ideally suited as a security layer due to the potential for
|
||||
a mismatch with annotated controller path matching. Generally, we recommend using Spring
|
||||
Security, or alternatively a similar approach integrated with the Servlet filter chain,
|
||||
and applied as early as possible.
|
||||
|
||||
|
||||
@@ -9,23 +9,23 @@ javaPlatform {
|
||||
dependencies {
|
||||
api(platform("com.fasterxml.jackson:jackson-bom:2.14.3"))
|
||||
api(platform("io.micrometer:micrometer-bom:1.10.13"))
|
||||
api(platform("io.netty:netty-bom:4.1.107.Final"))
|
||||
api(platform("io.netty:netty-bom:4.1.109.Final"))
|
||||
api(platform("io.netty:netty5-bom:5.0.0.Alpha5"))
|
||||
api(platform("io.projectreactor:reactor-bom:2022.0.17"))
|
||||
api(platform("io.projectreactor:reactor-bom:2022.0.19"))
|
||||
api(platform("io.rsocket:rsocket-bom:1.1.3"))
|
||||
api(platform("org.apache.groovy:groovy-bom:4.0.19"))
|
||||
api(platform("org.apache.groovy:groovy-bom:4.0.21"))
|
||||
api(platform("org.apache.logging.log4j:log4j-bom:2.21.1"))
|
||||
api(platform("org.eclipse.jetty:jetty-bom:11.0.20"))
|
||||
api(platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.6.4"))
|
||||
api(platform("org.jetbrains.kotlinx:kotlinx-serialization-bom:1.4.0"))
|
||||
api(platform("org.junit:junit-bom:5.9.3"))
|
||||
api(platform("org.mockito:mockito-bom:5.11.0"))
|
||||
api(platform("org.mockito:mockito-bom:5.12.0"))
|
||||
|
||||
constraints {
|
||||
api("com.fasterxml:aalto-xml:1.3.2")
|
||||
api("com.fasterxml.woodstox:woodstox-core:6.5.1")
|
||||
api("com.github.ben-manes.caffeine:caffeine:3.1.8")
|
||||
api("com.github.librepdf:openpdf:1.3.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")
|
||||
@@ -54,9 +54,9 @@ dependencies {
|
||||
api("io.r2dbc:r2dbc-spi:1.0.0.RELEASE")
|
||||
api("io.reactivex.rxjava3:rxjava:3.1.8")
|
||||
api("io.smallrye.reactive:mutiny:1.10.0")
|
||||
api("io.undertow:undertow-core:2.3.12.Final")
|
||||
api("io.undertow:undertow-servlet:2.3.12.Final")
|
||||
api("io.undertow:undertow-websockets-jsr:2.3.12.Final")
|
||||
api("io.undertow:undertow-core:2.3.13.Final")
|
||||
api("io.undertow:undertow-servlet:2.3.13.Final")
|
||||
api("io.undertow:undertow-websockets-jsr:2.3.13.Final")
|
||||
api("io.vavr:vavr:0.10.4")
|
||||
api("jakarta.activation:jakarta.activation-api:2.0.1")
|
||||
api("jakarta.annotation:jakarta.annotation-api:2.0.0")
|
||||
@@ -103,9 +103,9 @@ dependencies {
|
||||
api("org.apache.tomcat.embed:tomcat-embed-websocket:10.1.15")
|
||||
api("org.apache.tomcat:tomcat-util:10.1.15")
|
||||
api("org.apache.tomcat:tomcat-websocket:10.1.15")
|
||||
api("org.aspectj:aspectjrt:1.9.20.1")
|
||||
api("org.aspectj:aspectjtools:1.9.20.1")
|
||||
api("org.aspectj:aspectjweaver:1.9.20.1")
|
||||
api("org.aspectj:aspectjrt:1.9.22.1")
|
||||
api("org.aspectj:aspectjtools:1.9.22.1")
|
||||
api("org.aspectj:aspectjweaver:1.9.22.1")
|
||||
api("org.assertj:assertj-core:3.24.2")
|
||||
api("org.awaitility:awaitility:4.2.0")
|
||||
api("org.bouncycastle:bcpkix-jdk18on:1.72")
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
version=6.0.18-SNAPSHOT
|
||||
version=6.0.20
|
||||
|
||||
org.gradle.caching=true
|
||||
org.gradle.jvmargs=-Xmx2048m
|
||||
|
||||
@@ -6,6 +6,7 @@ tasks.findByName("dokkaHtmlPartial")?.configure {
|
||||
classpath.from(sourceSets["main"].runtimeClasspath)
|
||||
externalDocumentationLink {
|
||||
url.set(new URL("https://docs.spring.io/spring-framework/docs/current/javadoc-api/"))
|
||||
packageListUrl.set(new URL("https://docs.spring.io/spring-framework/docs/current/javadoc-api/element-list"))
|
||||
}
|
||||
externalDocumentationLink {
|
||||
url.set(new URL("https://projectreactor.io/docs/core/release/api/"))
|
||||
@@ -21,6 +22,7 @@ tasks.findByName("dokkaHtmlPartial")?.configure {
|
||||
}
|
||||
externalDocumentationLink {
|
||||
url.set(new URL("https://javadoc.io/doc/jakarta.servlet/jakarta.servlet-api/latest/"))
|
||||
packageListUrl.set(new URL("https://javadoc.io/doc/jakarta.servlet/jakarta.servlet-api/latest/element-list"))
|
||||
}
|
||||
externalDocumentationLink {
|
||||
url.set(new URL("https://javadoc.io/static/io.rsocket/rsocket-core/1.1.1/"))
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+1
-1
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
+3
-3
@@ -7,8 +7,8 @@ pluginManagement {
|
||||
}
|
||||
|
||||
plugins {
|
||||
id "com.gradle.enterprise" version "3.12.6"
|
||||
id "io.spring.ge.conventions" version "0.0.13"
|
||||
id "com.gradle.develocity" version "3.17.2"
|
||||
id "io.spring.ge.conventions" version "0.0.17"
|
||||
}
|
||||
|
||||
include "spring-aop"
|
||||
@@ -45,7 +45,7 @@ rootProject.children.each {project ->
|
||||
}
|
||||
|
||||
settings.gradle.projectsLoaded {
|
||||
gradleEnterprise {
|
||||
develocity {
|
||||
buildScan {
|
||||
File buildDir = settings.gradle.rootProject
|
||||
.getLayout().getBuildDirectory().getAsFile().get()
|
||||
|
||||
+14
-9
@@ -169,25 +169,30 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
|
||||
@Override
|
||||
public ClassFilter getClassFilter() {
|
||||
obtainPointcutExpression();
|
||||
checkExpression();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodMatcher getMethodMatcher() {
|
||||
obtainPointcutExpression();
|
||||
checkExpression();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check whether this pointcut is ready to match,
|
||||
* lazily building the underlying AspectJ pointcut expression.
|
||||
* Check whether this pointcut is ready to match.
|
||||
*/
|
||||
private PointcutExpression obtainPointcutExpression() {
|
||||
private void checkExpression() {
|
||||
if (getExpression() == null) {
|
||||
throw new IllegalStateException("Must set property 'expression' before attempting to match");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazily build the underlying AspectJ pointcut expression.
|
||||
*/
|
||||
private PointcutExpression obtainPointcutExpression() {
|
||||
if (this.pointcutExpression == null) {
|
||||
this.pointcutClassLoader = determinePointcutClassLoader();
|
||||
this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader);
|
||||
@@ -264,10 +269,9 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
|
||||
@Override
|
||||
public boolean matches(Class<?> targetClass) {
|
||||
PointcutExpression pointcutExpression = obtainPointcutExpression();
|
||||
try {
|
||||
try {
|
||||
return pointcutExpression.couldMatchJoinPointsInType(targetClass);
|
||||
return obtainPointcutExpression().couldMatchJoinPointsInType(targetClass);
|
||||
}
|
||||
catch (ReflectionWorldException ex) {
|
||||
logger.debug("PointcutExpression matching rejected target class - trying fallback expression", ex);
|
||||
@@ -278,6 +282,9 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IllegalArgumentException | IllegalStateException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
logger.debug("PointcutExpression matching rejected target class", ex);
|
||||
}
|
||||
@@ -286,7 +293,6 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
|
||||
@Override
|
||||
public boolean matches(Method method, Class<?> targetClass, boolean hasIntroductions) {
|
||||
obtainPointcutExpression();
|
||||
ShadowMatch shadowMatch = getTargetShadowMatch(method, targetClass);
|
||||
|
||||
// Special handling for this, target, @this, @target, @annotation
|
||||
@@ -324,7 +330,6 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
|
||||
@Override
|
||||
public boolean matches(Method method, Class<?> targetClass, Object... args) {
|
||||
obtainPointcutExpression();
|
||||
ShadowMatch shadowMatch = getTargetShadowMatch(method, targetClass);
|
||||
|
||||
// Bind Spring AOP proxy to AspectJ "this" and Spring AOP target to AspectJ target,
|
||||
|
||||
+2
-30
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,7 +18,6 @@ package org.springframework.aop.aspectj.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
import java.util.StringTokenizer;
|
||||
@@ -56,8 +55,6 @@ import org.springframework.lang.Nullable;
|
||||
*/
|
||||
public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFactory {
|
||||
|
||||
private static final String AJC_MAGIC = "ajc$";
|
||||
|
||||
private static final Class<?>[] ASPECTJ_ANNOTATION_CLASSES = new Class<?>[] {
|
||||
Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class};
|
||||
|
||||
@@ -68,37 +65,11 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac
|
||||
protected final ParameterNameDiscoverer parameterNameDiscoverer = new AspectJAnnotationParameterNameDiscoverer();
|
||||
|
||||
|
||||
/**
|
||||
* We consider something to be an AspectJ aspect suitable for use by the Spring AOP system
|
||||
* if it has the @Aspect annotation, and was not compiled by ajc. The reason for this latter test
|
||||
* is that aspects written in the code-style (AspectJ language) also have the annotation present
|
||||
* when compiled by ajc with the -1.5 flag, yet they cannot be consumed by Spring AOP.
|
||||
*/
|
||||
@Override
|
||||
public boolean isAspect(Class<?> clazz) {
|
||||
return (hasAspectAnnotation(clazz) && !compiledByAjc(clazz));
|
||||
}
|
||||
|
||||
private boolean hasAspectAnnotation(Class<?> clazz) {
|
||||
return (AnnotationUtils.findAnnotation(clazz, Aspect.class) != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* We need to detect this as "code-style" AspectJ aspects should not be
|
||||
* interpreted by Spring AOP.
|
||||
*/
|
||||
private boolean compiledByAjc(Class<?> clazz) {
|
||||
// The AJTypeSystem goes to great lengths to provide a uniform appearance between code-style and
|
||||
// annotation-style aspects. Therefore there is no 'clean' way to tell them apart. Here we rely on
|
||||
// an implementation detail of the AspectJ compiler.
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
if (field.getName().startsWith(AJC_MAGIC)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(Class<?> aspectClass) throws AopConfigException {
|
||||
AjType<?> ajType = AjTypeSystem.getAjType(aspectClass);
|
||||
@@ -115,6 +86,7 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find and return the first AspectJ annotation on the given method
|
||||
* (there <i>should</i> only be one anyway...).
|
||||
|
||||
+11
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -124,10 +124,16 @@ public class AspectMetadata implements Serializable {
|
||||
* Extract contents from String of form {@code pertarget(contents)}.
|
||||
*/
|
||||
private String findPerClause(Class<?> aspectClass) {
|
||||
String str = aspectClass.getAnnotation(Aspect.class).value();
|
||||
int beginIndex = str.indexOf('(') + 1;
|
||||
int endIndex = str.length() - 1;
|
||||
return str.substring(beginIndex, endIndex);
|
||||
Aspect ann = aspectClass.getAnnotation(Aspect.class);
|
||||
if (ann == null) {
|
||||
return "";
|
||||
}
|
||||
String value = ann.value();
|
||||
int beginIndex = value.indexOf('(');
|
||||
if (beginIndex < 0) {
|
||||
return "";
|
||||
}
|
||||
return value.substring(beginIndex + 1, value.length() - 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -368,8 +368,7 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
private void validateIntroductionAdvisor(IntroductionAdvisor advisor) {
|
||||
advisor.validateInterfaces();
|
||||
// If the advisor passed validation, we can make the change.
|
||||
Class<?>[] ifcs = advisor.getInterfaces();
|
||||
for (Class<?> ifc : ifcs) {
|
||||
for (Class<?> ifc : advisor.getInterfaces()) {
|
||||
addInterface(ifc);
|
||||
}
|
||||
}
|
||||
|
||||
+15
-18
@@ -23,8 +23,6 @@ import java.util.Map;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.aspectj.weaver.tools.PointcutPrimitive;
|
||||
import org.aspectj.weaver.tools.UnsupportedPointcutPrimitiveException;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import test.annotation.EmptySpringAnnotation;
|
||||
@@ -41,7 +39,6 @@ import org.springframework.beans.testfixture.beans.TestBean;
|
||||
import org.springframework.beans.testfixture.beans.subpkg.DeepBean;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
|
||||
@@ -174,25 +171,25 @@ public class AspectJExpressionPointcutTests {
|
||||
@Test
|
||||
public void testFriendlyErrorOnNoLocationClassMatching() {
|
||||
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
pc.matches(ITestBean.class))
|
||||
.withMessageContaining("expression");
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> pc.getClassFilter().matches(ITestBean.class))
|
||||
.withMessageContaining("expression");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFriendlyErrorOnNoLocation2ArgMatching() {
|
||||
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
pc.matches(getAge, ITestBean.class))
|
||||
.withMessageContaining("expression");
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> pc.getMethodMatcher().matches(getAge, ITestBean.class))
|
||||
.withMessageContaining("expression");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFriendlyErrorOnNoLocation3ArgMatching() {
|
||||
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
pc.matches(getAge, ITestBean.class, (Object[]) null))
|
||||
.withMessageContaining("expression");
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> pc.getMethodMatcher().matches(getAge, ITestBean.class, (Object[]) null))
|
||||
.withMessageContaining("expression");
|
||||
}
|
||||
|
||||
|
||||
@@ -209,8 +206,10 @@ public class AspectJExpressionPointcutTests {
|
||||
// not currently testable in a reliable fashion
|
||||
//assertDoesNotMatchStringClass(classFilter);
|
||||
|
||||
assertThat(methodMatcher.matches(setSomeNumber, TestBean.class, 12D)).as("Should match with setSomeNumber with Double input").isTrue();
|
||||
assertThat(methodMatcher.matches(setSomeNumber, TestBean.class, 11)).as("Should not match setSomeNumber with Integer input").isFalse();
|
||||
assertThat(methodMatcher.matches(setSomeNumber, TestBean.class, 12D))
|
||||
.as("Should match with setSomeNumber with Double input").isTrue();
|
||||
assertThat(methodMatcher.matches(setSomeNumber, TestBean.class, 11))
|
||||
.as("Should not match setSomeNumber with Integer input").isFalse();
|
||||
assertThat(methodMatcher.matches(getAge, TestBean.class)).as("Should not match getAge").isFalse();
|
||||
assertThat(methodMatcher.isRuntime()).as("Should be a runtime match").isTrue();
|
||||
}
|
||||
@@ -245,7 +244,7 @@ public class AspectJExpressionPointcutTests {
|
||||
@Test
|
||||
public void testInvalidExpression() {
|
||||
String expression = "execution(void org.springframework.beans.testfixture.beans.TestBean.setSomeNumber(Number) && args(Double)";
|
||||
assertThatIllegalArgumentException().isThrownBy(getPointcut(expression)::getClassFilter); // call to getClassFilter forces resolution
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> getPointcut(expression).getClassFilter().matches(Object.class));
|
||||
}
|
||||
|
||||
private TestBean getAdvisedProxy(String pointcutExpression, CallCountingInterceptor interceptor) {
|
||||
@@ -275,9 +274,7 @@ public class AspectJExpressionPointcutTests {
|
||||
@Test
|
||||
public void testWithUnsupportedPointcutPrimitive() {
|
||||
String expression = "call(int org.springframework.beans.testfixture.beans.TestBean.getAge())";
|
||||
assertThatExceptionOfType(UnsupportedPointcutPrimitiveException.class)
|
||||
.isThrownBy(() -> getPointcut(expression).getClassFilter()) // call to getClassFilter forces resolution...
|
||||
.satisfies(ex -> assertThat(ex.getUnsupportedPrimitive()).isEqualTo(PointcutPrimitive.CALL));
|
||||
assertThat(getPointcut(expression).getClassFilter().matches(Object.class)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -818,11 +818,11 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||
/**
|
||||
* This implementation attempts to query the FactoryBean's generic parameter metadata
|
||||
* if present to determine the object type. If not present, i.e. the FactoryBean is
|
||||
* declared as a raw type, checks the FactoryBean's {@code getObjectType} method
|
||||
* declared as a raw type, it checks the FactoryBean's {@code getObjectType} method
|
||||
* on a plain instance of the FactoryBean, without bean properties applied yet.
|
||||
* If this doesn't return a type yet, and {@code allowInit} is {@code true} a
|
||||
* full creation of the FactoryBean is used as fallback (through delegation to the
|
||||
* superclass's implementation).
|
||||
* If this doesn't return a type yet and {@code allowInit} is {@code true}, full
|
||||
* creation of the FactoryBean is attempted as fallback (through delegation to the
|
||||
* superclass implementation).
|
||||
* <p>The shortcut check for a FactoryBean is only applied in case of a singleton
|
||||
* FactoryBean. If the FactoryBean instance itself is not kept as singleton,
|
||||
* it will be fully created to check the type of its exposed object.
|
||||
|
||||
+2
-2
@@ -1053,7 +1053,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
|
||||
@Override
|
||||
public void setApplicationStartup(ApplicationStartup applicationStartup) {
|
||||
Assert.notNull(applicationStartup, "applicationStartup must not be null");
|
||||
Assert.notNull(applicationStartup, "ApplicationStartup must not be null");
|
||||
this.applicationStartup = applicationStartup;
|
||||
}
|
||||
|
||||
@@ -1649,7 +1649,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
* already. The implementation is allowed to instantiate the target factory bean if
|
||||
* {@code allowInit} is {@code true} and the type cannot be determined another way;
|
||||
* otherwise it is restricted to introspecting signatures and related metadata.
|
||||
* <p>If no {@link FactoryBean#OBJECT_TYPE_ATTRIBUTE} if set on the bean definition
|
||||
* <p>If no {@link FactoryBean#OBJECT_TYPE_ATTRIBUTE} is set on the bean definition
|
||||
* and {@code allowInit} is {@code true}, the default implementation will create
|
||||
* the FactoryBean via {@code getBean} to call its {@code getObjectType} method.
|
||||
* Subclasses are encouraged to optimize this, typically by inspecting the generic
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -108,7 +108,8 @@ public class GenericTypeAwareAutowireCandidateResolver extends SimpleAutowireCan
|
||||
Class<?> resolvedClass = targetType.resolve();
|
||||
if (resolvedClass != null && FactoryBean.class.isAssignableFrom(resolvedClass)) {
|
||||
Class<?> typeToBeMatched = dependencyType.resolve();
|
||||
if (typeToBeMatched != null && !FactoryBean.class.isAssignableFrom(typeToBeMatched)) {
|
||||
if (typeToBeMatched != null && !FactoryBean.class.isAssignableFrom(typeToBeMatched) &&
|
||||
!typeToBeMatched.isAssignableFrom(resolvedClass)) {
|
||||
targetType = targetType.getGeneric();
|
||||
if (descriptor.fallbackMatchAllowed()) {
|
||||
// Matching the Class-based type determination for FactoryBean
|
||||
|
||||
+436
-431
File diff suppressed because it is too large
Load Diff
+6
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,11 +22,12 @@ import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.aot.hint.TypeHint.Builder;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.aot.hint.annotation.ReflectiveRuntimeHintsRegistrar;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link RuntimeHintsRegistrar} implementation that makes sure {@link SchedulerFactoryBean}
|
||||
* reflection entries are registered.
|
||||
* reflection hints are registered.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Stephane Nicoll
|
||||
@@ -36,11 +37,11 @@ class SchedulerFactoryBeanRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
private static final String SCHEDULER_FACTORY_CLASS_NAME = "org.quartz.impl.StdSchedulerFactory";
|
||||
|
||||
private final ReflectiveRuntimeHintsRegistrar reflectiveRegistrar = new ReflectiveRuntimeHintsRegistrar();
|
||||
private static final ReflectiveRuntimeHintsRegistrar registrar = new ReflectiveRuntimeHintsRegistrar();
|
||||
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
||||
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
|
||||
if (!ClassUtils.isPresent(SCHEDULER_FACTORY_CLASS_NAME, classLoader)) {
|
||||
return;
|
||||
}
|
||||
@@ -48,7 +49,7 @@ class SchedulerFactoryBeanRuntimeHints implements RuntimeHintsRegistrar {
|
||||
.registerType(TypeReference.of(SCHEDULER_FACTORY_CLASS_NAME), this::typeHint)
|
||||
.registerTypes(TypeReference.listOf(ResourceLoaderClassLoadHelper.class,
|
||||
LocalTaskExecutorThreadPool.class, LocalDataSourceJobStore.class), this::typeHint);
|
||||
this.reflectiveRegistrar.registerRuntimeHints(hints, LocalTaskExecutorThreadPool.class);
|
||||
registrar.registerRuntimeHints(hints, LocalTaskExecutorThreadPool.class);
|
||||
}
|
||||
|
||||
private void typeHint(Builder typeHint) {
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -225,7 +225,6 @@ class ConfigurationClassEnhancer {
|
||||
};
|
||||
return new TransformingClassGenerator(cg, transformer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -334,6 +333,7 @@ class ConfigurationClassEnhancer {
|
||||
return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Object resolveBeanReference(Method beanMethod, Object[] beanMethodArgs,
|
||||
ConfigurableBeanFactory beanFactory, String beanName) {
|
||||
|
||||
|
||||
+13
-12
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -386,11 +386,11 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
});
|
||||
|
||||
// Detect any custom bean name generation strategy supplied through the enclosing application context
|
||||
SingletonBeanRegistry sbr = null;
|
||||
if (registry instanceof SingletonBeanRegistry _sbr) {
|
||||
sbr = _sbr;
|
||||
SingletonBeanRegistry singletonRegistry = null;
|
||||
if (registry instanceof SingletonBeanRegistry sbr) {
|
||||
singletonRegistry = sbr;
|
||||
if (!this.localBeanNameGeneratorSet) {
|
||||
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
|
||||
BeanNameGenerator generator = (BeanNameGenerator) singletonRegistry.getSingleton(
|
||||
AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
|
||||
if (generator != null) {
|
||||
this.componentScanBeanNameGenerator = generator;
|
||||
@@ -451,8 +451,8 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
while (!candidates.isEmpty());
|
||||
|
||||
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
|
||||
if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
|
||||
sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
|
||||
if (singletonRegistry != null && !singletonRegistry.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
|
||||
singletonRegistry.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
|
||||
}
|
||||
|
||||
// Store the PropertySourceDescriptors to contribute them Ahead-of-time if necessary
|
||||
@@ -550,6 +550,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public PropertyValues postProcessProperties(@Nullable PropertyValues pvs, Object bean, String beanName) {
|
||||
// Inject the BeanFactory before AutowiredAnnotationBeanPostProcessor's
|
||||
// postProcessProperties method attempts to autowire other configuration beans.
|
||||
@@ -645,9 +646,9 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
}
|
||||
return mappings;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static class PropertySourcesAotContribution implements BeanFactoryInitializationAotContribution {
|
||||
|
||||
private static final String ENVIRONMENT_VARIABLE = "environment";
|
||||
@@ -743,15 +744,14 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
return nonNull.get();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static class ConfigurationClassProxyBeanRegistrationCodeFragments extends BeanRegistrationCodeFragmentsDecorator {
|
||||
|
||||
private final Class<?> proxyClass;
|
||||
|
||||
public ConfigurationClassProxyBeanRegistrationCodeFragments(BeanRegistrationCodeFragments codeFragments,
|
||||
Class<?> proxyClass) {
|
||||
public ConfigurationClassProxyBeanRegistrationCodeFragments(BeanRegistrationCodeFragments codeFragments, Class<?> proxyClass) {
|
||||
super(codeFragments);
|
||||
this.proxyClass = proxyClass;
|
||||
}
|
||||
@@ -759,6 +759,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
@Override
|
||||
public CodeBlock generateSetBeanDefinitionPropertiesCode(GenerationContext generationContext,
|
||||
BeanRegistrationCode beanRegistrationCode, RootBeanDefinition beanDefinition, Predicate<String> attributeFilter) {
|
||||
|
||||
CodeBlock.Builder code = CodeBlock.builder();
|
||||
code.add(super.generateSetBeanDefinitionPropertiesCode(generationContext,
|
||||
beanRegistrationCode, beanDefinition, attributeFilter));
|
||||
@@ -771,6 +772,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
public CodeBlock generateInstanceSupplierCode(GenerationContext generationContext,
|
||||
BeanRegistrationCode beanRegistrationCode, Executable constructorOrFactoryMethod,
|
||||
boolean allowDirectSupplierShortcut) {
|
||||
|
||||
Executable executableToUse = proxyExecutable(generationContext.getRuntimeHints(), constructorOrFactoryMethod);
|
||||
return super.generateInstanceSupplierCode(generationContext, beanRegistrationCode,
|
||||
executableToUse, allowDirectSupplierShortcut);
|
||||
@@ -788,7 +790,6 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
}
|
||||
return userExecutable;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+7
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -39,7 +39,8 @@ import org.springframework.beans.factory.support.RegisteredBean;
|
||||
*/
|
||||
class ReflectiveProcessorBeanFactoryInitializationAotProcessor implements BeanFactoryInitializationAotProcessor {
|
||||
|
||||
private static final ReflectiveRuntimeHintsRegistrar REGISTRAR = new ReflectiveRuntimeHintsRegistrar();
|
||||
private static final ReflectiveRuntimeHintsRegistrar registrar = new ReflectiveRuntimeHintsRegistrar();
|
||||
|
||||
|
||||
@Override
|
||||
public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) {
|
||||
@@ -49,7 +50,9 @@ class ReflectiveProcessorBeanFactoryInitializationAotProcessor implements BeanFa
|
||||
return new ReflectiveProcessorBeanFactoryInitializationAotContribution(beanTypes);
|
||||
}
|
||||
|
||||
private static class ReflectiveProcessorBeanFactoryInitializationAotContribution implements BeanFactoryInitializationAotContribution {
|
||||
|
||||
private static class ReflectiveProcessorBeanFactoryInitializationAotContribution
|
||||
implements BeanFactoryInitializationAotContribution {
|
||||
|
||||
private final Class<?>[] types;
|
||||
|
||||
@@ -60,9 +63,8 @@ class ReflectiveProcessorBeanFactoryInitializationAotProcessor implements BeanFa
|
||||
@Override
|
||||
public void applyTo(GenerationContext generationContext, BeanFactoryInitializationCode beanFactoryInitializationCode) {
|
||||
RuntimeHints runtimeHints = generationContext.getRuntimeHints();
|
||||
REGISTRAR.registerRuntimeHints(runtimeHints, this.types);
|
||||
registrar.registerRuntimeHints(runtimeHints, this.types);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -126,6 +126,7 @@ public abstract class AbstractRefreshableApplicationContext extends AbstractAppl
|
||||
try {
|
||||
DefaultListableBeanFactory beanFactory = createBeanFactory();
|
||||
beanFactory.setSerializationId(getId());
|
||||
beanFactory.setApplicationStartup(getApplicationStartup());
|
||||
customizeBeanFactory(beanFactory);
|
||||
loadBeanDefinitions(beanFactory);
|
||||
this.beanFactory = beanFactory;
|
||||
|
||||
+32
-10
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,6 +18,7 @@ package org.springframework.context.support;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Supplier;
|
||||
@@ -423,16 +424,37 @@ public class GenericApplicationContext extends AbstractApplicationContext implem
|
||||
PostProcessorRegistrationDelegate.loadBeanPostProcessors(
|
||||
this.beanFactory, SmartInstantiationAwareBeanPostProcessor.class);
|
||||
|
||||
List<String> lazyBeans = new ArrayList<>();
|
||||
|
||||
// First round: non-lazy singleton beans in definition order,
|
||||
// matching preInstantiateSingletons.
|
||||
for (String beanName : this.beanFactory.getBeanDefinitionNames()) {
|
||||
Class<?> beanType = this.beanFactory.getType(beanName);
|
||||
if (beanType != null) {
|
||||
ClassHintUtils.registerProxyIfNecessary(beanType, runtimeHints);
|
||||
for (SmartInstantiationAwareBeanPostProcessor bpp : bpps) {
|
||||
Class<?> newBeanType = bpp.determineBeanType(beanType, beanName);
|
||||
if (newBeanType != beanType) {
|
||||
ClassHintUtils.registerProxyIfNecessary(newBeanType, runtimeHints);
|
||||
beanType = newBeanType;
|
||||
}
|
||||
BeanDefinition bd = getBeanDefinition(beanName);
|
||||
if (bd.isSingleton() && !bd.isLazyInit()) {
|
||||
preDetermineBeanType(beanName, bpps, runtimeHints);
|
||||
}
|
||||
else {
|
||||
lazyBeans.add(beanName);
|
||||
}
|
||||
}
|
||||
|
||||
// Second round: lazy singleton beans and scoped beans.
|
||||
for (String beanName : lazyBeans) {
|
||||
preDetermineBeanType(beanName, bpps, runtimeHints);
|
||||
}
|
||||
}
|
||||
|
||||
private void preDetermineBeanType(String beanName, List<SmartInstantiationAwareBeanPostProcessor> bpps,
|
||||
RuntimeHints runtimeHints) {
|
||||
|
||||
Class<?> beanType = this.beanFactory.getType(beanName);
|
||||
if (beanType != null) {
|
||||
ClassHintUtils.registerProxyIfNecessary(beanType, runtimeHints);
|
||||
for (SmartInstantiationAwareBeanPostProcessor bpp : bpps) {
|
||||
Class<?> newBeanType = bpp.determineBeanType(beanType, beanName);
|
||||
if (newBeanType != beanType) {
|
||||
ClassHintUtils.registerProxyIfNecessary(newBeanType, runtimeHints);
|
||||
beanType = newBeanType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -134,11 +134,6 @@ public class ConcurrentTaskExecutor implements AsyncListenableTaskExecutor, Sche
|
||||
* execution callback (which may be a wrapper around the user-supplied task).
|
||||
* <p>The primary use case is to set some execution context around the task's
|
||||
* invocation, or to provide some monitoring/statistics for task execution.
|
||||
* <p><b>NOTE:</b> Exception handling in {@code TaskDecorator} implementations
|
||||
* is limited to plain {@code Runnable} execution via {@code execute} calls.
|
||||
* In case of {@code #submit} calls, the exposed {@code Runnable} will be a
|
||||
* {@code FutureTask} which does not propagate any exceptions; you might
|
||||
* have to cast it and call {@code Future#get} to evaluate exceptions.
|
||||
* @since 4.3
|
||||
*/
|
||||
public final void setTaskDecorator(TaskDecorator taskDecorator) {
|
||||
@@ -179,11 +174,10 @@ public class ConcurrentTaskExecutor implements AsyncListenableTaskExecutor, Sche
|
||||
}
|
||||
|
||||
|
||||
private TaskExecutorAdapter getAdaptedExecutor(Executor concurrentExecutor) {
|
||||
if (managedExecutorServiceClass != null && managedExecutorServiceClass.isInstance(concurrentExecutor)) {
|
||||
return new ManagedTaskExecutorAdapter(concurrentExecutor);
|
||||
}
|
||||
TaskExecutorAdapter adapter = new TaskExecutorAdapter(concurrentExecutor);
|
||||
private TaskExecutorAdapter getAdaptedExecutor(Executor originalExecutor) {
|
||||
TaskExecutorAdapter adapter =
|
||||
(managedExecutorServiceClass != null && managedExecutorServiceClass.isInstance(originalExecutor) ?
|
||||
new ManagedTaskExecutorAdapter(originalExecutor) : new TaskExecutorAdapter(originalExecutor));
|
||||
if (this.taskDecorator != null) {
|
||||
adapter.setTaskDecorator(this.taskDecorator);
|
||||
}
|
||||
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -183,6 +183,7 @@ public class ConcurrentTaskScheduler extends ConcurrentTaskExecutor implements T
|
||||
* @see Clock#systemDefaultZone()
|
||||
*/
|
||||
public void setClock(Clock clock) {
|
||||
Assert.notNull(clock, "Clock must not be null");
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
|
||||
+5
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -46,8 +46,9 @@ import org.springframework.util.concurrent.ListenableFuture;
|
||||
import org.springframework.util.concurrent.ListenableFutureTask;
|
||||
|
||||
/**
|
||||
* Implementation of Spring's {@link TaskScheduler} interface, wrapping
|
||||
* a native {@link java.util.concurrent.ScheduledThreadPoolExecutor}.
|
||||
* A standard implementation of Spring's {@link TaskScheduler} interface, wrapping
|
||||
* a native {@link java.util.concurrent.ScheduledThreadPoolExecutor} and providing
|
||||
* all applicable configuration options for it.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Mark Fisher
|
||||
@@ -158,6 +159,7 @@ public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport
|
||||
* @see Clock#systemDefaultZone()
|
||||
*/
|
||||
public void setClock(Clock clock) {
|
||||
Assert.notNull(clock, "Clock must not be null");
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
|
||||
+20
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -307,8 +307,8 @@ class AnnotationConfigApplicationContextTests {
|
||||
assertThat(ObjectUtils.containsElement(context.getBeanNamesForType(BeanC.class), "c")).isTrue();
|
||||
|
||||
assertThat(context.getBeansOfType(BeanA.class)).isEmpty();
|
||||
assertThat(context.getBeansOfType(BeanB.class).values().iterator().next()).isSameAs(context.getBean(BeanB.class));
|
||||
assertThat(context.getBeansOfType(BeanC.class).values().iterator().next()).isSameAs(context.getBean(BeanC.class));
|
||||
assertThat(context.getBeansOfType(BeanB.class).values()).singleElement().isSameAs(context.getBean(BeanB.class));
|
||||
assertThat(context.getBeansOfType(BeanC.class).values()).singleElement().isSameAs(context.getBean(BeanC.class));
|
||||
|
||||
assertThatExceptionOfType(NoSuchBeanDefinitionException.class)
|
||||
.isThrownBy(() -> context.getBeanFactory().resolveNamedBean(BeanA.class));
|
||||
@@ -409,15 +409,17 @@ class AnnotationConfigApplicationContextTests {
|
||||
bd2.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class, ResolvableType.forClassWithGenerics(GenericHolder.class, Integer.class)));
|
||||
bd2.setLazyInit(true);
|
||||
context.registerBeanDefinition("fb2", bd2);
|
||||
context.registerBeanDefinition("ip", new RootBeanDefinition(FactoryBeanInjectionPoints.class));
|
||||
RootBeanDefinition bd3 = new RootBeanDefinition(FactoryBeanInjectionPoints.class);
|
||||
bd3.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
|
||||
context.registerBeanDefinition("ip", bd3);
|
||||
context.refresh();
|
||||
|
||||
assertThat(context.getBean("ip", FactoryBeanInjectionPoints.class).factoryBean).isSameAs(context.getBean("&fb1"));
|
||||
assertThat(context.getBean("ip", FactoryBeanInjectionPoints.class).factoryResult).isSameAs(context.getBean("fb1"));
|
||||
assertThat(context.getType("&fb1")).isEqualTo(GenericHolderFactoryBean.class);
|
||||
assertThat(context.getType("fb1")).isEqualTo(GenericHolder.class);
|
||||
assertThat(context.getBeanNamesForType(FactoryBean.class)).hasSize(2);
|
||||
assertThat(context.getBeanNamesForType(GenericHolderFactoryBean.class)).hasSize(1);
|
||||
assertThat(context.getBean("ip", FactoryBeanInjectionPoints.class).factoryBean).isSameAs(context.getBean("&fb1"));
|
||||
assertThat(context.getBean("ip", FactoryBeanInjectionPoints.class).factoryResult).isSameAs(context.getBean("fb1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -425,7 +427,7 @@ class AnnotationConfigApplicationContextTests {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
RootBeanDefinition bd1 = new RootBeanDefinition();
|
||||
bd1.setBeanClass(GenericHolderFactoryBean.class);
|
||||
bd1.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class, ResolvableType.forClassWithGenerics(GenericHolder.class, Object.class)));
|
||||
bd1.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class, ResolvableType.forClassWithGenerics(GenericHolder.class, String.class)));
|
||||
bd1.setLazyInit(true);
|
||||
context.registerBeanDefinition("fb1", bd1);
|
||||
RootBeanDefinition bd2 = new RootBeanDefinition();
|
||||
@@ -433,13 +435,17 @@ class AnnotationConfigApplicationContextTests {
|
||||
bd2.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class, ResolvableType.forClassWithGenerics(GenericHolder.class, Integer.class)));
|
||||
bd2.setLazyInit(true);
|
||||
context.registerBeanDefinition("fb2", bd2);
|
||||
context.registerBeanDefinition("ip", new RootBeanDefinition(FactoryResultInjectionPoint.class));
|
||||
RootBeanDefinition bd3 = new RootBeanDefinition(FactoryBeanInjectionPoints.class);
|
||||
bd3.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
|
||||
context.registerBeanDefinition("ip", bd3);
|
||||
context.refresh();
|
||||
|
||||
assertThat(context.getBean("ip", FactoryResultInjectionPoint.class).factoryResult).isSameAs(context.getBean("fb1"));
|
||||
assertThat(context.getBean("ip", FactoryResultInjectionPoint.class).factoryResult).isSameAs(context.getBean("fb1"));
|
||||
assertThat(context.getType("&fb1")).isEqualTo(GenericHolderFactoryBean.class);
|
||||
assertThat(context.getType("fb1")).isEqualTo(GenericHolder.class);
|
||||
assertThat(context.getBeanNamesForType(FactoryBean.class)).hasSize(2);
|
||||
assertThat(context.getBean("ip", FactoryResultInjectionPoint.class).factoryResult).isSameAs(context.getBean("fb1"));
|
||||
assertThat(context.getBeanNamesForType(FactoryBean.class)).hasSize(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -453,14 +459,17 @@ class AnnotationConfigApplicationContextTests {
|
||||
bd2.setBeanClass(UntypedFactoryBean.class);
|
||||
bd2.setTargetType(ResolvableType.forClassWithGenerics(GenericHolder.class, Integer.class));
|
||||
context.registerBeanDefinition("fb2", bd2);
|
||||
context.registerBeanDefinition("ip", new RootBeanDefinition(FactoryResultInjectionPoint.class));
|
||||
RootBeanDefinition bd3 = new RootBeanDefinition(FactoryResultInjectionPoint.class);
|
||||
bd3.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
|
||||
context.registerBeanDefinition("ip", bd3);
|
||||
context.refresh();
|
||||
|
||||
assertThat(context.getBean("ip", FactoryResultInjectionPoint.class).factoryResult).isSameAs(context.getBean("fb1"));
|
||||
assertThat(context.getBean("ip", FactoryResultInjectionPoint.class).factoryResult).isSameAs(context.getBean("fb1"));
|
||||
assertThat(context.getType("&fb1")).isEqualTo(GenericHolderFactoryBean.class);
|
||||
assertThat(context.getType("fb1")).isEqualTo(GenericHolder.class);
|
||||
assertThat(context.getBeanNamesForType(FactoryBean.class)).hasSize(2);
|
||||
assertThat(context.getBeanNamesForType(GenericHolderFactoryBean.class)).hasSize(1);
|
||||
assertThat(context.getBean("ip", FactoryResultInjectionPoint.class).factoryResult).isSameAs(context.getBean("fb1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+10
-10
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -99,8 +99,8 @@ class ConfigurationClassPostProcessorAotContributionTests {
|
||||
initializer.accept(freshBeanFactory);
|
||||
freshContext.refresh();
|
||||
assertThat(freshBeanFactory.getBeanPostProcessors()).filteredOn(ImportAwareAotBeanPostProcessor.class::isInstance)
|
||||
.singleElement().satisfies(postProcessor -> assertPostProcessorEntry(postProcessor, ImportAwareConfiguration.class,
|
||||
ImportConfiguration.class));
|
||||
.singleElement().satisfies(postProcessor ->
|
||||
assertPostProcessorEntry(postProcessor, ImportAwareConfiguration.class, ImportConfiguration.class));
|
||||
freshContext.close();
|
||||
});
|
||||
}
|
||||
@@ -117,8 +117,8 @@ class ConfigurationClassPostProcessorAotContributionTests {
|
||||
freshContext.refresh();
|
||||
TestAwareCallbackBean bean = freshContext.getBean(TestAwareCallbackBean.class);
|
||||
assertThat(bean.instances).hasSize(2);
|
||||
assertThat(bean.instances.get(0)).isEqualTo(freshContext);
|
||||
assertThat(bean.instances.get(1)).isInstanceOfSatisfying(AnnotationMetadata.class, metadata ->
|
||||
assertThat(bean.instances).element(0).isEqualTo(freshContext);
|
||||
assertThat(bean.instances).element(1).isInstanceOfSatisfying(AnnotationMetadata.class, metadata ->
|
||||
assertThat(metadata.getClassName()).isEqualTo(TestAwareCallbackConfiguration.class.getName()));
|
||||
freshContext.close();
|
||||
});
|
||||
@@ -236,13 +236,14 @@ class ConfigurationClassPostProcessorAotContributionTests {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
public void afterPropertiesSet() {
|
||||
Assert.notNull(this.metadata, "Metadata was not injected");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
class PropertySourceTests {
|
||||
|
||||
@@ -362,9 +363,9 @@ class ConfigurationClassPostProcessorAotContributionTests {
|
||||
static class PropertySourceWithCustomFactoryConfiguration {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
class ConfigurationClassProxyTests {
|
||||
|
||||
@@ -384,15 +385,14 @@ class ConfigurationClassPostProcessorAotContributionTests {
|
||||
getRegisteredBean(CglibConfiguration.class))).isNotNull();
|
||||
}
|
||||
|
||||
|
||||
private RegisteredBean getRegisteredBean(Class<?> bean) {
|
||||
this.beanFactory.registerBeanDefinition("test", new RootBeanDefinition(bean));
|
||||
this.processor.postProcessBeanFactory(this.beanFactory);
|
||||
return RegisteredBean.of(this.beanFactory, "test");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
private BeanFactoryInitializationAotContribution getContribution(Class<?>... types) {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
@@ -410,8 +410,8 @@ class ConfigurationClassPostProcessorAotContributionTests {
|
||||
.containsExactly(entry(key.getName(), value.getName()));
|
||||
}
|
||||
|
||||
static class CustomPropertySourcesFactory extends DefaultPropertySourceFactory {
|
||||
|
||||
static class CustomPropertySourcesFactory extends DefaultPropertySourceFactory {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+58
-40
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,7 +29,9 @@ import org.springframework.beans.factory.support.AbstractBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.GenericBeanDefinition;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -39,51 +41,62 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* {@link FactoryBean FactoryBeans} defined in the configuration.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
|
||||
class ConfigurationWithFactoryBeanEarlyDeductionTests {
|
||||
|
||||
@Test
|
||||
public void preFreezeDirect() {
|
||||
void preFreezeDirect() {
|
||||
assertPreFreeze(DirectConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postFreezeDirect() {
|
||||
void postFreezeDirect() {
|
||||
assertPostFreeze(DirectConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preFreezeGenericMethod() {
|
||||
void preFreezeGenericMethod() {
|
||||
assertPreFreeze(GenericMethodConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postFreezeGenericMethod() {
|
||||
void postFreezeGenericMethod() {
|
||||
assertPostFreeze(GenericMethodConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preFreezeGenericClass() {
|
||||
void preFreezeGenericClass() {
|
||||
assertPreFreeze(GenericClassConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postFreezeGenericClass() {
|
||||
void postFreezeGenericClass() {
|
||||
assertPostFreeze(GenericClassConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preFreezeAttribute() {
|
||||
void preFreezeAttribute() {
|
||||
assertPreFreeze(AttributeClassConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postFreezeAttribute() {
|
||||
void postFreezeAttribute() {
|
||||
assertPostFreeze(AttributeClassConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preFreezeUnresolvedGenericFactoryBean() {
|
||||
void preFreezeTargetType() {
|
||||
assertPreFreeze(TargetTypeConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void postFreezeTargetType() {
|
||||
assertPostFreeze(TargetTypeConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void preFreezeUnresolvedGenericFactoryBean() {
|
||||
// Covers the case where a @Configuration is picked up via component scanning
|
||||
// and its bean definition only has a String bean class. In such cases
|
||||
// beanDefinition.hasBeanClass() returns false so we need to actually
|
||||
@@ -105,14 +118,13 @@ public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void assertPostFreeze(Class<?> configurationClass) {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
||||
configurationClass);
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(configurationClass);
|
||||
assertContainsMyBeanName(context);
|
||||
}
|
||||
|
||||
private void assertPreFreeze(Class<?> configurationClass,
|
||||
BeanFactoryPostProcessor... postProcessors) {
|
||||
private void assertPreFreeze(Class<?> configurationClass, BeanFactoryPostProcessor... postProcessors) {
|
||||
NameCollectingBeanFactoryPostProcessor postProcessor = new NameCollectingBeanFactoryPostProcessor();
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
try (context) {
|
||||
@@ -132,41 +144,38 @@ public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
|
||||
assertThat(names).containsExactly("myBean");
|
||||
}
|
||||
|
||||
private static class NameCollectingBeanFactoryPostProcessor
|
||||
implements BeanFactoryPostProcessor {
|
||||
|
||||
private static class NameCollectingBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
|
||||
|
||||
private String[] names;
|
||||
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
|
||||
throws BeansException {
|
||||
this.names = beanFactory.getBeanNamesForType(MyBean.class, true, false);
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||
ResolvableType typeToMatch = ResolvableType.forClassWithGenerics(MyBean.class, String.class);
|
||||
this.names = beanFactory.getBeanNamesForType(typeToMatch, true, false);
|
||||
}
|
||||
|
||||
public String[] getNames() {
|
||||
return this.names;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class DirectConfiguration {
|
||||
|
||||
@Bean
|
||||
MyBean myBean() {
|
||||
return new MyBean();
|
||||
MyBean<String> myBean() {
|
||||
return new MyBean<>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class GenericMethodConfiguration {
|
||||
|
||||
@Bean
|
||||
FactoryBean<MyBean> myBean() {
|
||||
return new TestFactoryBean<>(new MyBean());
|
||||
FactoryBean<MyBean<String>> myBean() {
|
||||
return new TestFactoryBean<>(new MyBean<>());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@@ -176,13 +185,11 @@ public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
|
||||
MyFactoryBean myBean() {
|
||||
return new MyFactoryBean();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(AttributeClassRegistrar.class)
|
||||
static class AttributeClassConfiguration {
|
||||
|
||||
}
|
||||
|
||||
static class AttributeClassRegistrar implements ImportBeanDefinitionRegistrar {
|
||||
@@ -191,16 +198,32 @@ public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
|
||||
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
|
||||
BeanDefinition definition = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
RawWithAbstractObjectTypeFactoryBean.class).getBeanDefinition();
|
||||
definition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, MyBean.class);
|
||||
definition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE,
|
||||
ResolvableType.forClassWithGenerics(MyBean.class, String.class));
|
||||
registry.registerBeanDefinition("myBean", definition);
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(TargetTypeRegistrar.class)
|
||||
static class TargetTypeConfiguration {
|
||||
}
|
||||
|
||||
static class TargetTypeRegistrar implements ImportBeanDefinitionRegistrar {
|
||||
|
||||
@Override
|
||||
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
|
||||
RootBeanDefinition definition = new RootBeanDefinition(RawWithAbstractObjectTypeFactoryBean.class);
|
||||
definition.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class,
|
||||
ResolvableType.forClassWithGenerics(MyBean.class, String.class)));
|
||||
registry.registerBeanDefinition("myBean", definition);
|
||||
}
|
||||
}
|
||||
|
||||
abstract static class AbstractMyBean {
|
||||
}
|
||||
|
||||
static class MyBean extends AbstractMyBean {
|
||||
static class MyBean<T> extends AbstractMyBean {
|
||||
}
|
||||
|
||||
static class TestFactoryBean<T> implements FactoryBean<T> {
|
||||
@@ -212,7 +235,7 @@ public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getObject() throws Exception {
|
||||
public T getObject() {
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
@@ -220,31 +243,26 @@ public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
|
||||
public Class<?> getObjectType() {
|
||||
return this.instance.getClass();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class MyFactoryBean extends TestFactoryBean<MyBean> {
|
||||
static class MyFactoryBean extends TestFactoryBean<MyBean<String>> {
|
||||
|
||||
public MyFactoryBean() {
|
||||
super(new MyBean());
|
||||
super(new MyBean<>());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class RawWithAbstractObjectTypeFactoryBean implements FactoryBean<Object> {
|
||||
|
||||
private final Object object = new MyBean();
|
||||
|
||||
@Override
|
||||
public Object getObject() throws Exception {
|
||||
return object;
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return MyBean.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,8 +17,8 @@
|
||||
package org.springframework.scheduling.concurrent;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.RunnableFuture;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -52,8 +52,8 @@ class ConcurrentTaskExecutorTests extends AbstractSchedulingTaskExecutorTests {
|
||||
@AfterEach
|
||||
void shutdownExecutor() {
|
||||
for (Runnable task : concurrentExecutor.shutdownNow()) {
|
||||
if (task instanceof RunnableFuture) {
|
||||
((RunnableFuture<?>) task).cancel(true);
|
||||
if (task instanceof Future) {
|
||||
((Future<?>) task).cancel(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -101,7 +101,7 @@ class ThreadPoolTaskSchedulerTests extends AbstractSchedulingTaskExecutorTests {
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("deprecation")
|
||||
void scheduleOneTimeFailingTaskWithoutErrorHandler() throws Exception {
|
||||
void scheduleOneTimeFailingTaskWithoutErrorHandler() {
|
||||
TestTask task = new TestTask(this.testName, 0);
|
||||
Future<?> future = scheduler.schedule(task, new Date());
|
||||
assertThatExceptionOfType(ExecutionException.class).isThrownBy(() -> future.get(1000, TimeUnit.MILLISECONDS));
|
||||
@@ -147,7 +147,7 @@ class ThreadPoolTaskSchedulerTests extends AbstractSchedulingTaskExecutorTests {
|
||||
catch (InterruptedException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
assertThat(latch.getCount()).as("latch did not count down,").isEqualTo(0);
|
||||
assertThat(latch.getCount()).as("latch did not count down").isEqualTo(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -39,8 +39,7 @@ import org.springframework.core.annotation.AliasFor;
|
||||
* @see ReflectiveRuntimeHintsRegistrar
|
||||
* @see RegisterReflectionForBinding @RegisterReflectionForBinding
|
||||
*/
|
||||
@Target({ ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.CONSTRUCTOR,
|
||||
ElementType.FIELD, ElementType.METHOD })
|
||||
@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Reflective {
|
||||
|
||||
+4
-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.
|
||||
@@ -36,7 +36,7 @@ import org.springframework.core.annotation.AliasFor;
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @RegisterReflectionForBinding({ Foo.class, Bar.class })
|
||||
* @RegisterReflectionForBinding({Foo.class, Bar.class})
|
||||
* public class MyConfig {
|
||||
* // ...
|
||||
* }</pre>
|
||||
@@ -63,7 +63,7 @@ import org.springframework.core.annotation.AliasFor;
|
||||
* @see org.springframework.aot.hint.BindingReflectionHintsRegistrar
|
||||
* @see Reflective @Reflective
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Reflective(RegisterReflectionForBindingProcessor.class)
|
||||
@@ -77,8 +77,7 @@ public @interface RegisterReflectionForBinding {
|
||||
|
||||
/**
|
||||
* Classes for which reflection hints should be registered.
|
||||
* <p>At least one class must be specified either via {@link #value} or
|
||||
* {@link #classes}.
|
||||
* <p>At least one class must be specified either via {@link #value} or {@code classes}.
|
||||
* @see #value()
|
||||
*/
|
||||
@AliasFor("value")
|
||||
|
||||
+7
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -47,9 +47,11 @@ class SpringFactoriesLoaderRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
||||
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
|
||||
ClassLoader classLoaderToUse = (classLoader != null ? classLoader :
|
||||
SpringFactoriesLoaderRuntimeHints.class.getClassLoader());
|
||||
for (String resourceLocation : RESOURCE_LOCATIONS) {
|
||||
registerHints(hints, classLoader, resourceLocation);
|
||||
registerHints(hints, classLoaderToUse, resourceLocation);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +65,7 @@ class SpringFactoriesLoaderRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
private void registerHints(RuntimeHints hints, ClassLoader classLoader,
|
||||
String factoryClassName, List<String> implementationClassNames) {
|
||||
|
||||
Class<?> factoryClass = resolveClassName(classLoader, factoryClassName);
|
||||
if (factoryClass == null) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
@@ -100,6 +103,7 @@ class SpringFactoriesLoaderRuntimeHints implements RuntimeHintsRegistrar {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class ExtendedSpringFactoriesLoader extends SpringFactoriesLoader {
|
||||
|
||||
ExtendedSpringFactoriesLoader(@Nullable ClassLoader classLoader, Map<String, List<String>> factories) {
|
||||
@@ -109,7 +113,6 @@ class SpringFactoriesLoaderRuntimeHints implements RuntimeHintsRegistrar {
|
||||
static Map<String, List<String>> accessLoadFactoriesResource(ClassLoader classLoader, String resourceLocation) {
|
||||
return SpringFactoriesLoader.loadFactoriesResource(classLoader, resourceLocation);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ public class ReflectUtils {
|
||||
Throwable throwable = null;
|
||||
try {
|
||||
classLoaderDefineClass = ClassLoader.class.getDeclaredMethod("defineClass",
|
||||
String.class, byte[].class, Integer.TYPE, Integer.TYPE, ProtectionDomain.class);
|
||||
String.class, byte[].class, Integer.TYPE, Integer.TYPE, ProtectionDomain.class);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
classLoaderDefineClass = null;
|
||||
|
||||
@@ -57,7 +57,7 @@ public class MethodProxy {
|
||||
proxy.createInfo = new CreateInfo(c1, c2);
|
||||
|
||||
// SPRING PATCH BEGIN
|
||||
if (!c1.isInterface() && c1 != Object.class && !Factory.class.isAssignableFrom(c2)) {
|
||||
if (c1 != Object.class && c1.isAssignableFrom(c2.getSuperclass()) && !Factory.class.isAssignableFrom(c2)) {
|
||||
// Try early initialization for overridden methods on specifically purposed subclasses
|
||||
try {
|
||||
proxy.init();
|
||||
|
||||
@@ -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.
|
||||
@@ -36,6 +36,7 @@ import org.springframework.util.ReflectionUtils;
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Sam Brannen
|
||||
* @since 4.2.3
|
||||
*/
|
||||
public final class MethodIntrospector {
|
||||
@@ -75,6 +76,7 @@ public final class MethodIntrospector {
|
||||
if (result != null) {
|
||||
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
|
||||
if (bridgedMethod == specificMethod || bridgedMethod == method ||
|
||||
bridgedMethod.equals(specificMethod) || bridgedMethod.equals(method) ||
|
||||
metadataLookup.inspect(bridgedMethod) == null) {
|
||||
methodMap.put(specificMethod, result);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -98,7 +98,9 @@ public final class ReactiveTypeDescriptor {
|
||||
*/
|
||||
public Object getEmptyValue() {
|
||||
Assert.state(this.emptySupplier != null, "Empty values not supported");
|
||||
return this.emptySupplier.get();
|
||||
Object emptyValue = this.emptySupplier.get();
|
||||
Assert.notNull(emptyValue, "Invalid null return value from emptySupplier");
|
||||
return emptyValue;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,7 +132,7 @@ public final class ReactiveTypeDescriptor {
|
||||
|
||||
|
||||
/**
|
||||
* Descriptor for a reactive type that can produce 0..N values.
|
||||
* Descriptor for a reactive type that can produce {@code 0..N} values.
|
||||
* @param type the reactive type
|
||||
* @param emptySupplier a supplier of an empty-value instance of the reactive type
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
@@ -747,7 +747,7 @@ public class ResolvableType implements Serializable {
|
||||
* Convenience method that will {@link #getGenerics() get} and
|
||||
* {@link #resolve() resolve} generic parameters.
|
||||
* @return an array of resolved generic parameters (the resulting array
|
||||
* will never be {@code null}, but it may contain {@code null} elements})
|
||||
* will never be {@code null}, but it may contain {@code null} elements)
|
||||
* @see #getGenerics()
|
||||
* @see #resolve()
|
||||
*/
|
||||
|
||||
+5
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -418,7 +418,10 @@ final class TypeMappedAnnotations implements MergedAnnotations {
|
||||
|
||||
Annotation[] repeatedAnnotations = repeatableContainers.findRepeatedAnnotations(annotation);
|
||||
if (repeatedAnnotations != null) {
|
||||
return doWithAnnotations(type, aggregateIndex, source, repeatedAnnotations);
|
||||
MergedAnnotation<A> result = doWithAnnotations(type, aggregateIndex, source, repeatedAnnotations);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(
|
||||
annotation.annotationType(), repeatableContainers, annotationFilter);
|
||||
|
||||
@@ -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.
|
||||
@@ -95,20 +95,19 @@ public interface Decoder<T> {
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) throws DecodingException {
|
||||
|
||||
CompletableFuture<T> future = decodeToMono(Mono.just(buffer), targetType, mimeType, hints).toFuture();
|
||||
Assert.state(future.isDone(), "DataBuffer decoding should have completed.");
|
||||
Assert.state(future.isDone(), "DataBuffer decoding should have completed");
|
||||
|
||||
Throwable failure;
|
||||
try {
|
||||
return future.get();
|
||||
}
|
||||
catch (ExecutionException ex) {
|
||||
failure = ex.getCause();
|
||||
Throwable cause = ex.getCause();
|
||||
throw (cause instanceof CodecException codecException ? codecException :
|
||||
new DecodingException("Failed to decode: " + (cause != null ? cause.getMessage() : ex), cause));
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
failure = ex;
|
||||
throw new DecodingException("Interrupted during decode", ex);
|
||||
}
|
||||
throw (failure instanceof CodecException codecException ? codecException :
|
||||
new DecodingException("Failed to decode: " + failure.getMessage(), failure));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.
|
||||
@@ -76,10 +76,11 @@ public class ResourceDecoder extends AbstractDataBufferDecoder<Resource> {
|
||||
}
|
||||
|
||||
Class<?> clazz = elementType.toClass();
|
||||
String filename = hints != null ? (String) hints.get(FILENAME_HINT) : null;
|
||||
String filename = (hints != null ? (String) hints.get(FILENAME_HINT) : null);
|
||||
if (clazz == InputStreamResource.class) {
|
||||
return new InputStreamResource(new ByteArrayInputStream(bytes)) {
|
||||
@Override
|
||||
@Nullable
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
@@ -92,6 +93,7 @@ public class ResourceDecoder extends AbstractDataBufferDecoder<Resource> {
|
||||
else if (Resource.class.isAssignableFrom(clazz)) {
|
||||
return new ByteArrayResource(bytes) {
|
||||
@Override
|
||||
@Nullable
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -52,8 +52,6 @@ import org.springframework.util.ObjectUtils;
|
||||
@SuppressWarnings("serial")
|
||||
public class TypeDescriptor implements Serializable {
|
||||
|
||||
private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0];
|
||||
|
||||
private static final Map<Class<?>, TypeDescriptor> commonTypesCache = new HashMap<>(32);
|
||||
|
||||
private static final Class<?>[] CACHED_COMMON_TYPES = {
|
||||
@@ -84,7 +82,7 @@ public class TypeDescriptor implements Serializable {
|
||||
public TypeDescriptor(MethodParameter methodParameter) {
|
||||
this.resolvableType = ResolvableType.forMethodParameter(methodParameter);
|
||||
this.type = this.resolvableType.resolve(methodParameter.getNestedParameterType());
|
||||
this.annotatedElement = new AnnotatedElementAdapter(methodParameter.getParameterIndex() == -1 ?
|
||||
this.annotatedElement = AnnotatedElementAdapter.from(methodParameter.getParameterIndex() == -1 ?
|
||||
methodParameter.getMethodAnnotations() : methodParameter.getParameterAnnotations());
|
||||
}
|
||||
|
||||
@@ -96,7 +94,7 @@ public class TypeDescriptor implements Serializable {
|
||||
public TypeDescriptor(Field field) {
|
||||
this.resolvableType = ResolvableType.forField(field);
|
||||
this.type = this.resolvableType.resolve(field.getType());
|
||||
this.annotatedElement = new AnnotatedElementAdapter(field.getAnnotations());
|
||||
this.annotatedElement = AnnotatedElementAdapter.from(field.getAnnotations());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,7 +107,7 @@ public class TypeDescriptor implements Serializable {
|
||||
Assert.notNull(property, "Property must not be null");
|
||||
this.resolvableType = ResolvableType.forMethodParameter(property.getMethodParameter());
|
||||
this.type = this.resolvableType.resolve(property.getType());
|
||||
this.annotatedElement = new AnnotatedElementAdapter(property.getAnnotations());
|
||||
this.annotatedElement = AnnotatedElementAdapter.from(property.getAnnotations());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,7 +123,7 @@ public class TypeDescriptor implements Serializable {
|
||||
public TypeDescriptor(ResolvableType resolvableType, @Nullable Class<?> type, @Nullable Annotation[] annotations) {
|
||||
this.resolvableType = resolvableType;
|
||||
this.type = (type != null ? type : resolvableType.toClass());
|
||||
this.annotatedElement = new AnnotatedElementAdapter(annotations);
|
||||
this.annotatedElement = AnnotatedElementAdapter.from(annotations);
|
||||
}
|
||||
|
||||
|
||||
@@ -733,18 +731,26 @@ public class TypeDescriptor implements Serializable {
|
||||
* @see AnnotatedElementUtils#isAnnotated(AnnotatedElement, Class)
|
||||
* @see AnnotatedElementUtils#getMergedAnnotation(AnnotatedElement, Class)
|
||||
*/
|
||||
private class AnnotatedElementAdapter implements AnnotatedElement, Serializable {
|
||||
private static final class AnnotatedElementAdapter implements AnnotatedElement, Serializable {
|
||||
|
||||
private static final AnnotatedElementAdapter EMPTY = new AnnotatedElementAdapter(new Annotation[0]);
|
||||
|
||||
@Nullable
|
||||
private final Annotation[] annotations;
|
||||
|
||||
public AnnotatedElementAdapter(@Nullable Annotation[] annotations) {
|
||||
private AnnotatedElementAdapter(Annotation[] annotations) {
|
||||
this.annotations = annotations;
|
||||
}
|
||||
|
||||
private static AnnotatedElementAdapter from(@Nullable Annotation[] annotations) {
|
||||
if (annotations == null || annotations.length == 0) {
|
||||
return EMPTY;
|
||||
}
|
||||
return new AnnotatedElementAdapter(annotations);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
|
||||
for (Annotation annotation : getAnnotations()) {
|
||||
for (Annotation annotation : this.annotations) {
|
||||
if (annotation.annotationType() == annotationClass) {
|
||||
return true;
|
||||
}
|
||||
@@ -756,7 +762,7 @@ public class TypeDescriptor implements Serializable {
|
||||
@Nullable
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
|
||||
for (Annotation annotation : getAnnotations()) {
|
||||
for (Annotation annotation : this.annotations) {
|
||||
if (annotation.annotationType() == annotationClass) {
|
||||
return (T) annotation;
|
||||
}
|
||||
@@ -766,7 +772,7 @@ public class TypeDescriptor implements Serializable {
|
||||
|
||||
@Override
|
||||
public Annotation[] getAnnotations() {
|
||||
return (this.annotations != null ? this.annotations.clone() : EMPTY_ANNOTATION_ARRAY);
|
||||
return (isEmpty() ? this.annotations : this.annotations.clone());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -775,7 +781,7 @@ public class TypeDescriptor implements Serializable {
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return ObjectUtils.isEmpty(this.annotations);
|
||||
return (this.annotations.length == 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -791,7 +797,7 @@ public class TypeDescriptor implements Serializable {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return TypeDescriptor.this.toString();
|
||||
return Arrays.toString(this.annotations);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -136,7 +136,7 @@ final class ObjectToObjectConverter implements ConditionalGenericConverter {
|
||||
@Nullable
|
||||
private static Executable getValidatedExecutable(Class<?> targetClass, Class<?> sourceClass) {
|
||||
Executable executable = conversionExecutableCache.get(targetClass);
|
||||
if (isApplicable(executable, sourceClass)) {
|
||||
if (executable != null && isApplicable(executable, sourceClass)) {
|
||||
return executable;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -48,7 +48,7 @@ public class PropertiesPropertySource extends MapPropertySource {
|
||||
@Override
|
||||
public String[] getPropertyNames() {
|
||||
synchronized (this.source) {
|
||||
return super.getPropertyNames();
|
||||
return ((Map<?, ?>) this.source).keySet().stream().filter(k -> k instanceof String).toArray(String[]::new);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -62,7 +62,7 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public abstract class DataBufferUtils {
|
||||
|
||||
private final static Log logger = LogFactory.getLog(DataBufferUtils.class);
|
||||
private static final Log logger = LogFactory.getLog(DataBufferUtils.class);
|
||||
|
||||
private static final Consumer<DataBuffer> RELEASE_CONSUMER = DataBufferUtils::release;
|
||||
|
||||
@@ -745,7 +745,7 @@ public abstract class DataBufferUtils {
|
||||
*/
|
||||
private static class SingleByteMatcher implements NestedMatcher {
|
||||
|
||||
static SingleByteMatcher NEWLINE_MATCHER = new SingleByteMatcher(new byte[] {10});
|
||||
static final SingleByteMatcher NEWLINE_MATCHER = new SingleByteMatcher(new byte[] {10});
|
||||
|
||||
private final byte[] delimiter;
|
||||
|
||||
@@ -784,7 +784,7 @@ public abstract class DataBufferUtils {
|
||||
/**
|
||||
* Base class for a {@link NestedMatcher}.
|
||||
*/
|
||||
private static abstract class AbstractNestedMatcher implements NestedMatcher {
|
||||
private abstract static class AbstractNestedMatcher implements NestedMatcher {
|
||||
|
||||
private final byte[] delimiter;
|
||||
|
||||
@@ -990,7 +990,7 @@ public abstract class DataBufferUtils {
|
||||
DataBuffer.ByteBufferIterator iterator = dataBuffer.writableByteBuffers();
|
||||
Assert.state(iterator.hasNext(), "No ByteBuffer available");
|
||||
ByteBuffer byteBuffer = iterator.next();
|
||||
Attachment attachment = new Attachment(dataBuffer, iterator);
|
||||
Attachment attachment = new Attachment(dataBuffer, iterator);
|
||||
this.channel.read(byteBuffer, this.position.get(), attachment, this);
|
||||
}
|
||||
|
||||
@@ -999,7 +999,7 @@ public abstract class DataBufferUtils {
|
||||
attachment.iterator().close();
|
||||
DataBuffer dataBuffer = attachment.dataBuffer();
|
||||
|
||||
if (this.state.get().equals(State.DISPOSED)) {
|
||||
if (this.state.get() == State.DISPOSED) {
|
||||
release(dataBuffer);
|
||||
closeChannel(this.channel);
|
||||
return;
|
||||
@@ -1030,13 +1030,13 @@ public abstract class DataBufferUtils {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, Attachment attachment) {
|
||||
public void failed(Throwable ex, Attachment attachment) {
|
||||
attachment.iterator().close();
|
||||
release(attachment.dataBuffer());
|
||||
|
||||
closeChannel(this.channel);
|
||||
this.state.set(State.DISPOSED);
|
||||
this.sink.error(exc);
|
||||
this.sink.error(ex);
|
||||
}
|
||||
|
||||
private enum State {
|
||||
@@ -1095,7 +1095,6 @@ public abstract class DataBufferUtils {
|
||||
public Context currentContext() {
|
||||
return Context.of(this.sink.contextView());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1190,13 +1189,13 @@ public abstract class DataBufferUtils {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, Attachment attachment) {
|
||||
public void failed(Throwable ex, Attachment attachment) {
|
||||
attachment.iterator().close();
|
||||
|
||||
this.sink.next(attachment.dataBuffer());
|
||||
this.writing.set(false);
|
||||
|
||||
this.sink.error(exc);
|
||||
this.sink.error(ex);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1205,9 +1204,6 @@ public abstract class DataBufferUtils {
|
||||
}
|
||||
|
||||
private record Attachment(ByteBuffer byteBuffer, DataBuffer dataBuffer, DataBuffer.ByteBufferIterator iterator) {}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+124
-102
@@ -71,18 +71,18 @@ import org.springframework.util.StringUtils;
|
||||
/**
|
||||
* A {@link ResourcePatternResolver} implementation that is able to resolve a
|
||||
* specified resource location path into one or more matching Resources.
|
||||
* The source path may be a simple path which has a one-to-one mapping to a
|
||||
* target {@link org.springframework.core.io.Resource}, or alternatively
|
||||
* may contain the special "{@code classpath*:}" prefix and/or
|
||||
* internal Ant-style regular expressions (matched using Spring's
|
||||
* {@link org.springframework.util.AntPathMatcher} utility).
|
||||
* Both of the latter are effectively wildcards.
|
||||
*
|
||||
* <p><b>No Wildcards:</b>
|
||||
* <p>The source path may be a simple path which has a one-to-one mapping to a
|
||||
* target {@link org.springframework.core.io.Resource}, or alternatively may
|
||||
* contain the special "{@code classpath*:}" prefix and/or internal Ant-style
|
||||
* path patterns (matched using Spring's {@link AntPathMatcher} utility). Both
|
||||
* of the latter are effectively wildcards.
|
||||
*
|
||||
* <h3>No Wildcards</h3>
|
||||
*
|
||||
* <p>In the simple case, if the specified location path does not start with the
|
||||
* {@code "classpath*:}" prefix, and does not contain a PathMatcher pattern,
|
||||
* this resolver will simply return a single resource via a
|
||||
* {@code "classpath*:}" prefix and does not contain a {@link PathMatcher}
|
||||
* pattern, this resolver will simply return a single resource via a
|
||||
* {@code getResource()} call on the underlying {@code ResourceLoader}.
|
||||
* Examples are real URLs such as "{@code file:C:/context.xml}", pseudo-URLs
|
||||
* such as "{@code classpath:/context.xml}", and simple unprefixed paths
|
||||
@@ -90,14 +90,14 @@ import org.springframework.util.StringUtils;
|
||||
* fashion specific to the underlying {@code ResourceLoader} (e.g.
|
||||
* {@code ServletContextResource} for a {@code WebApplicationContext}).
|
||||
*
|
||||
* <p><b>Ant-style Patterns:</b>
|
||||
* <h3>Ant-style Patterns</h3>
|
||||
*
|
||||
* <p>When the path location contains an Ant-style pattern, e.g.:
|
||||
* <p>When the path location contains an Ant-style pattern, for example:
|
||||
* <pre class="code">
|
||||
* /WEB-INF/*-context.xml
|
||||
* com/mycompany/**/applicationContext.xml
|
||||
* com/example/**/applicationContext.xml
|
||||
* file:C:/some/path/*-context.xml
|
||||
* classpath:com/mycompany/**/applicationContext.xml</pre>
|
||||
* classpath:com/example/**/applicationContext.xml</pre>
|
||||
* the resolver follows a more complex but defined procedure to try to resolve
|
||||
* the wildcard. It produces a {@code Resource} for the path up to the last
|
||||
* non-wildcard segment and obtains a {@code URL} from it. If this URL is not a
|
||||
@@ -108,31 +108,31 @@ import org.springframework.util.StringUtils;
|
||||
* {@code java.net.JarURLConnection} from it, or manually parses the jar URL, and
|
||||
* then traverses the contents of the jar file, to resolve the wildcards.
|
||||
*
|
||||
* <p><b>Implications on portability:</b>
|
||||
* <h3>Implications on Portability</h3>
|
||||
*
|
||||
* <p>If the specified path is already a file URL (either explicitly, or
|
||||
* implicitly because the base {@code ResourceLoader} is a filesystem one),
|
||||
* then wildcarding is guaranteed to work in a completely portable fashion.
|
||||
*
|
||||
* <p>If the specified path is a classpath location, then the resolver must
|
||||
* <p>If the specified path is a class path location, then the resolver must
|
||||
* obtain the last non-wildcard path segment URL via a
|
||||
* {@code Classloader.getResource()} call. Since this is just a
|
||||
* node of the path (not the file at the end) it is actually undefined
|
||||
* (in the ClassLoader Javadocs) exactly what sort of URL is returned in
|
||||
* this case. In practice, it is usually a {@code java.io.File} representing
|
||||
* the directory, where the classpath resource resolves to a filesystem
|
||||
* location, or a jar URL of some sort, where the classpath resource resolves
|
||||
* the directory, where the class path resource resolves to a filesystem
|
||||
* location, or a jar URL of some sort, where the class path resource resolves
|
||||
* to a jar location. Still, there is a portability concern on this operation.
|
||||
*
|
||||
* <p>If a jar URL is obtained for the last non-wildcard segment, the resolver
|
||||
* must be able to get a {@code java.net.JarURLConnection} from it, or
|
||||
* manually parse the jar URL, to be able to walk the contents of the jar,
|
||||
* and resolve the wildcard. This will work in most environments, but will
|
||||
* manually parse the jar URL, to be able to walk the contents of the jar
|
||||
* and resolve the wildcard. This will work in most environments but will
|
||||
* fail in others, and it is strongly recommended that the wildcard
|
||||
* resolution of resources coming from jars be thoroughly tested in your
|
||||
* specific environment before you rely on it.
|
||||
*
|
||||
* <p><b>{@code classpath*:} Prefix:</b>
|
||||
* <h3>{@code classpath*:} Prefix</h3>
|
||||
*
|
||||
* <p>There is special support for retrieving multiple class path resources with
|
||||
* the same name, via the "{@code classpath*:}" prefix. For example,
|
||||
@@ -142,22 +142,22 @@ import org.springframework.util.StringUtils;
|
||||
* at the same location within each jar file. Internally, this happens via a
|
||||
* {@code ClassLoader.getResources()} call, and is completely portable.
|
||||
*
|
||||
* <p>The "classpath*:" prefix can also be combined with a PathMatcher pattern in
|
||||
* the rest of the location path, for example "classpath*:META-INF/*-beans.xml".
|
||||
* In this case, the resolution strategy is fairly simple: a
|
||||
* {@code ClassLoader.getResources()} call is used on the last non-wildcard
|
||||
* path segment to get all the matching resources in the class loader hierarchy,
|
||||
* and then off each resource the same PathMatcher resolution strategy described
|
||||
* above is used for the wildcard sub pattern.
|
||||
* <p>The "{@code classpath*:}" prefix can also be combined with a {@code PathMatcher}
|
||||
* pattern in the rest of the location path — for example,
|
||||
* "{@code classpath*:META-INF/*-beans.xml"}. In this case, the resolution strategy
|
||||
* is fairly simple: a {@code ClassLoader.getResources()} call is used on the last
|
||||
* non-wildcard path segment to get all the matching resources in the class loader
|
||||
* hierarchy, and then off each resource the same {@code PathMatcher} resolution
|
||||
* strategy described above is used for the wildcard sub pattern.
|
||||
*
|
||||
* <p><b>Other notes:</b>
|
||||
* <h3>Other Notes</h3>
|
||||
*
|
||||
* <p>As of Spring Framework 6.0, if {@link #getResources(String)} is invoked
|
||||
* with a location pattern using the "classpath*:" prefix it will first search
|
||||
* <p>As of Spring Framework 6.0, if {@link #getResources(String)} is invoked with
|
||||
* a location pattern using the "{@code classpath*:}" prefix it will first search
|
||||
* all modules in the {@linkplain ModuleLayer#boot() boot layer}, excluding
|
||||
* {@linkplain ModuleFinder#ofSystem() system modules}. It will then search the
|
||||
* classpath using {@link ClassLoader} APIs as described previously and return the
|
||||
* combined results. Consequently, some of the limitations of classpath searches
|
||||
* class path using {@link ClassLoader} APIs as described previously and return the
|
||||
* combined results. Consequently, some of the limitations of class path searches
|
||||
* may not apply when applications are deployed as modules.
|
||||
*
|
||||
* <p><b>WARNING:</b> Note that "{@code classpath*:}" when combined with
|
||||
@@ -168,26 +168,26 @@ import org.springframework.util.StringUtils;
|
||||
* root of expanded directories. This originates from a limitation in the JDK's
|
||||
* {@code ClassLoader.getResources()} method which only returns file system
|
||||
* locations for a passed-in empty String (indicating potential roots to search).
|
||||
* This {@code ResourcePatternResolver} implementation is trying to mitigate the
|
||||
* This {@code ResourcePatternResolver} implementation tries to mitigate the
|
||||
* jar root lookup limitation through {@link URLClassLoader} introspection and
|
||||
* "java.class.path" manifest evaluation; however, without portability guarantees.
|
||||
* "{@code java.class.path}" manifest evaluation; however, without portability
|
||||
* guarantees.
|
||||
*
|
||||
* <p><b>WARNING:</b> Ant-style patterns with "classpath:" resources are not
|
||||
* guaranteed to find matching resources if the root package to search is available
|
||||
* <p><b>WARNING:</b> Ant-style patterns with "{@code classpath:}" resources are not
|
||||
* guaranteed to find matching resources if the base package to search is available
|
||||
* in multiple class path locations. This is because a resource such as
|
||||
* <pre class="code">
|
||||
* com/mycompany/package1/service-context.xml
|
||||
* </pre>
|
||||
* may be in only one location, but when a path such as
|
||||
* com/example/package1/service-context.xml</pre>
|
||||
* may exist in only one class path location, but when a location pattern such as
|
||||
* <pre class="code">
|
||||
* classpath:com/mycompany/**/service-context.xml
|
||||
* </pre>
|
||||
* classpath:com/example/**/service-context.xml</pre>
|
||||
* is used to try to resolve it, the resolver will work off the (first) URL
|
||||
* returned by {@code getResource("com/mycompany");}. If this base package node
|
||||
* exists in multiple classloader locations, the actual end resource may not be
|
||||
* underneath. Therefore, preferably, use "{@code classpath*:}" with the same
|
||||
* Ant-style pattern in such a case, which will search <i>all</i> class path
|
||||
* locations that contain the root package.
|
||||
* returned by {@code getResource("com/example")}. If the {@code com/example} base
|
||||
* package node exists in multiple class path locations, the actual desired resource
|
||||
* may not be present under the {@code com/example} base package in the first URL.
|
||||
* Therefore, preferably, use "{@code classpath*:}" with the same Ant-style pattern
|
||||
* in such a case, which will search <i>all</i> class path locations that contain
|
||||
* the base package.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Colin Sampaleanu
|
||||
@@ -249,19 +249,21 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
|
||||
|
||||
/**
|
||||
* Create a new PathMatchingResourcePatternResolver with a DefaultResourceLoader.
|
||||
* Create a {@code PathMatchingResourcePatternResolver} with a
|
||||
* {@link DefaultResourceLoader}.
|
||||
* <p>ClassLoader access will happen via the thread context class loader.
|
||||
* @see org.springframework.core.io.DefaultResourceLoader
|
||||
* @see DefaultResourceLoader
|
||||
*/
|
||||
public PathMatchingResourcePatternResolver() {
|
||||
this.resourceLoader = new DefaultResourceLoader();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new PathMatchingResourcePatternResolver.
|
||||
* Create a {@code PathMatchingResourcePatternResolver} with the supplied
|
||||
* {@link ResourceLoader}.
|
||||
* <p>ClassLoader access will happen via the thread context class loader.
|
||||
* @param resourceLoader the ResourceLoader to load root directories and
|
||||
* actual resources with
|
||||
* @param resourceLoader the {@code ResourceLoader} to load root directories
|
||||
* and actual resources with
|
||||
*/
|
||||
public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
|
||||
Assert.notNull(resourceLoader, "ResourceLoader must not be null");
|
||||
@@ -269,8 +271,9 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new PathMatchingResourcePatternResolver with a DefaultResourceLoader.
|
||||
* @param classLoader the ClassLoader to load classpath resources with,
|
||||
* Create a {@code PathMatchingResourcePatternResolver} with a
|
||||
* {@link DefaultResourceLoader} and the supplied {@link ClassLoader}.
|
||||
* @param classLoader the ClassLoader to load class path resources with,
|
||||
* or {@code null} for using the thread context class loader
|
||||
* at the time of actual resource access
|
||||
* @see org.springframework.core.io.DefaultResourceLoader
|
||||
@@ -281,7 +284,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
|
||||
|
||||
/**
|
||||
* Return the ResourceLoader that this pattern resolver works with.
|
||||
* Return the {@link ResourceLoader} that this pattern resolver works with.
|
||||
*/
|
||||
public ResourceLoader getResourceLoader() {
|
||||
return this.resourceLoader;
|
||||
@@ -294,9 +297,10 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the PathMatcher implementation to use for this
|
||||
* resource pattern resolver. Default is AntPathMatcher.
|
||||
* @see org.springframework.util.AntPathMatcher
|
||||
* Set the {@link PathMatcher} implementation to use for this
|
||||
* resource pattern resolver.
|
||||
* <p>Default is {@link AntPathMatcher}.
|
||||
* @see AntPathMatcher
|
||||
*/
|
||||
public void setPathMatcher(PathMatcher pathMatcher) {
|
||||
Assert.notNull(pathMatcher, "PathMatcher must not be null");
|
||||
@@ -304,7 +308,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the PathMatcher that this resource pattern resolver uses.
|
||||
* Return the {@link PathMatcher} that this resource pattern resolver uses.
|
||||
*/
|
||||
public PathMatcher getPathMatcher() {
|
||||
return this.pathMatcher;
|
||||
@@ -353,8 +357,8 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
|
||||
/**
|
||||
* Find all class location resources with the given location via the ClassLoader.
|
||||
* Delegates to {@link #doFindAllClassPathResources(String)}.
|
||||
* @param location the absolute path within the classpath
|
||||
* <p>Delegates to {@link #doFindAllClassPathResources(String)}.
|
||||
* @param location the absolute path within the class path
|
||||
* @return the result as Resource array
|
||||
* @throws IOException in case of I/O errors
|
||||
* @see java.lang.ClassLoader#getResources
|
||||
@@ -364,15 +368,16 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
String path = stripLeadingSlash(location);
|
||||
Set<Resource> result = doFindAllClassPathResources(path);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Resolved classpath location [" + path + "] to resources " + result);
|
||||
logger.trace("Resolved class path location [" + path + "] to resources " + result);
|
||||
}
|
||||
return result.toArray(new Resource[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all class location resources with the given path via the ClassLoader.
|
||||
* Called by {@link #findAllClassPathResources(String)}.
|
||||
* @param path the absolute path within the classpath (never a leading slash)
|
||||
* Find all class path resources with the given path via the configured
|
||||
* {@link #getClassLoader() ClassLoader}.
|
||||
* <p>Called by {@link #findAllClassPathResources(String)}.
|
||||
* @param path the absolute path within the class path (never a leading slash)
|
||||
* @return a mutable Set of matching Resource instances
|
||||
* @since 4.1.1
|
||||
*/
|
||||
@@ -386,20 +391,21 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
}
|
||||
if (!StringUtils.hasLength(path)) {
|
||||
// The above result is likely to be incomplete, i.e. only containing file system references.
|
||||
// We need to have pointers to each of the jar files on the classpath as well...
|
||||
// We need to have pointers to each of the jar files on the class path as well...
|
||||
addAllClassLoaderJarRoots(cl, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given URL as returned from the ClassLoader into a {@link Resource},
|
||||
* applying to path lookups without a pattern ({@link #findAllClassPathResources}).
|
||||
* Convert the given URL as returned from the configured
|
||||
* {@link #getClassLoader() ClassLoader} into a {@link Resource}, applying
|
||||
* to path lookups without a pattern (see {@link #findAllClassPathResources}).
|
||||
* <p>As of 6.0.5, the default implementation creates a {@link FileSystemResource}
|
||||
* in case of the "file" protocol or a {@link UrlResource} otherwise, matching
|
||||
* the outcome of pattern-based classpath traversal in the same resource layout,
|
||||
* the outcome of pattern-based class path traversal in the same resource layout,
|
||||
* as well as matching the outcome of module path searches.
|
||||
* @param url a URL as returned from the ClassLoader
|
||||
* @param url a URL as returned from the configured ClassLoader
|
||||
* @return the corresponding Resource object
|
||||
* @see java.lang.ClassLoader#getResources
|
||||
* @see #doFindAllClassPathResources
|
||||
@@ -422,8 +428,8 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
}
|
||||
|
||||
/**
|
||||
* Search all {@link URLClassLoader} URLs for jar file references and add them to the
|
||||
* given set of resources in the form of pointers to the root of the jar file content.
|
||||
* Search all {@link URLClassLoader} URLs for jar file references and add each to the
|
||||
* given set of resources in the form of a pointer to the root of the jar file content.
|
||||
* @param classLoader the ClassLoader to search (including its ancestors)
|
||||
* @param result the set of resources to add jar roots to
|
||||
* @since 4.1.1
|
||||
@@ -457,7 +463,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
}
|
||||
|
||||
if (classLoader == ClassLoader.getSystemClassLoader()) {
|
||||
// "java.class.path" manifest evaluation...
|
||||
// JAR "Class-Path" manifest header evaluation...
|
||||
addClassPathManifestEntries(result);
|
||||
}
|
||||
|
||||
@@ -476,16 +482,17 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine jar file references from the "java.class.path." manifest property and add them
|
||||
* to the given set of resources in the form of pointers to the root of the jar file content.
|
||||
* Determine jar file references from {@code Class-Path} manifest entries (which
|
||||
* are added to the {@code java.class.path} JVM system property by the system
|
||||
* class loader) and add each to the given set of resources in the form of
|
||||
* a pointer to the root of the jar file content.
|
||||
* @param result the set of resources to add jar roots to
|
||||
* @since 4.3
|
||||
*/
|
||||
protected void addClassPathManifestEntries(Set<Resource> result) {
|
||||
try {
|
||||
String javaClassPathProperty = System.getProperty("java.class.path");
|
||||
for (String path : StringUtils.delimitedListToStringArray(
|
||||
javaClassPathProperty, System.getProperty("path.separator"))) {
|
||||
for (String path : StringUtils.delimitedListToStringArray(javaClassPathProperty, File.pathSeparator)) {
|
||||
try {
|
||||
String filePath = new File(path).getAbsolutePath();
|
||||
int prefixIndex = filePath.indexOf(':');
|
||||
@@ -499,7 +506,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
// Build URL that points to the root of the jar file
|
||||
UrlResource jarResource = new UrlResource(ResourceUtils.JAR_URL_PREFIX +
|
||||
ResourceUtils.FILE_URL_PREFIX + filePath + ResourceUtils.JAR_URL_SEPARATOR);
|
||||
// Potentially overlapping with URLClassLoader.getURLs() result above!
|
||||
// Potentially overlapping with URLClassLoader.getURLs() result in addAllClassLoaderJarRoots().
|
||||
if (!result.contains(jarResource) && !hasDuplicate(filePath, result) && jarResource.exists()) {
|
||||
result.add(jarResource);
|
||||
}
|
||||
@@ -543,14 +550,18 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all resources that match the given location pattern via the
|
||||
* Ant-style PathMatcher. Supports resources in OSGi bundles, JBoss VFS,
|
||||
* jar files, zip files, and file systems.
|
||||
* Find all resources that match the given location pattern via the Ant-style
|
||||
* {@link #getPathMatcher() PathMatcher}.
|
||||
* <p>Supports resources in OSGi bundles, JBoss VFS, jar files, zip files,
|
||||
* and file systems.
|
||||
* @param locationPattern the location pattern to match
|
||||
* @return the result as Resource array
|
||||
* @throws IOException in case of I/O errors
|
||||
* @see #doFindPathMatchingJarResources
|
||||
* @see #doFindPathMatchingFileResources
|
||||
* @see #determineRootDir(String)
|
||||
* @see #resolveRootDirResource(Resource)
|
||||
* @see #isJarResource(Resource)
|
||||
* @see #doFindPathMatchingJarResources(Resource, URL, String)
|
||||
* @see #doFindPathMatchingFileResources(Resource, String)
|
||||
* @see org.springframework.util.PathMatcher
|
||||
*/
|
||||
protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {
|
||||
@@ -607,29 +618,39 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the specified resource for path matching.
|
||||
* <p>By default, Equinox OSGi "bundleresource:" / "bundleentry:" URL will be
|
||||
* resolved into a standard jar file URL that be traversed using Spring's
|
||||
* standard jar file traversal algorithm. For any preceding custom resolution,
|
||||
* override this method and replace the resource handle accordingly.
|
||||
* Resolve the supplied root directory resource for path matching.
|
||||
* <p>By default, {@link #findPathMatchingResources(String)} resolves Equinox
|
||||
* OSGi "bundleresource:" and "bundleentry:" URLs into standard jar file URLs
|
||||
* that will be traversed using Spring's standard jar file traversal algorithm.
|
||||
* <p>For any custom resolution, override this template method and replace the
|
||||
* supplied resource handle accordingly.
|
||||
* <p>The default implementation of this method returns the supplied resource
|
||||
* unmodified.
|
||||
* @param original the resource to resolve
|
||||
* @return the resolved resource (may be identical to the passed-in resource)
|
||||
* @return the resolved resource (may be identical to the supplied resource)
|
||||
* @throws IOException in case of resolution failure
|
||||
* @see #findPathMatchingResources(String)
|
||||
*/
|
||||
protected Resource resolveRootDirResource(Resource original) throws IOException {
|
||||
return original;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the given resource handle indicates a jar resource
|
||||
* that the {@link #doFindPathMatchingJarResources} method can handle.
|
||||
* <p>By default, the URL protocols "jar", "zip", "vfszip, and "wsjar"
|
||||
* will be treated as jar resources. This template method allows for
|
||||
* detecting further kinds of jar-like resources, e.g. through
|
||||
* {@code instanceof} checks on the resource handle type.
|
||||
* @param resource the resource handle to check
|
||||
* (usually the root directory to start path matching from)
|
||||
* @see #doFindPathMatchingJarResources
|
||||
* Determine if the given resource handle indicates a jar resource that the
|
||||
* {@link #doFindPathMatchingJarResources} method can handle.
|
||||
* <p>{@link #findPathMatchingResources(String)} delegates to
|
||||
* {@link ResourceUtils#isJarURL(URL)} to determine whether the given URL
|
||||
* points to a resource in a jar file, and only invokes this method as a fallback.
|
||||
* <p>This template method therefore allows for detecting further kinds of
|
||||
* jar-like resources — for example, via {@code instanceof} checks on
|
||||
* the resource handle type.
|
||||
* <p>The default implementation of this method returns {@code false}.
|
||||
* @param resource the resource handle to check (usually the root directory
|
||||
* to start path matching from)
|
||||
* @return {@code true} if the given resource handle indicates a jar resource
|
||||
* @throws IOException in case of I/O errors
|
||||
* @see #findPathMatchingResources(String)
|
||||
* @see #doFindPathMatchingJarResources(Resource, URL, String)
|
||||
* @see org.springframework.util.ResourceUtils#isJarURL
|
||||
*/
|
||||
protected boolean isJarResource(Resource resource) throws IOException {
|
||||
@@ -638,7 +659,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
|
||||
/**
|
||||
* Find all resources in jar files that match the given location pattern
|
||||
* via the Ant-style PathMatcher.
|
||||
* via the Ant-style {@link #getPathMatcher() PathMatcher}.
|
||||
* @param rootDirResource the root directory as Resource
|
||||
* @param rootDirUrl the pre-resolved root directory URL
|
||||
* @param subPattern the sub pattern to match (below the root directory)
|
||||
@@ -691,7 +712,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
}
|
||||
catch (ZipException ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Skipping invalid jar classpath entry [" + urlFile + "]");
|
||||
logger.debug("Skipping invalid jar class path entry [" + urlFile + "]");
|
||||
}
|
||||
return Collections.emptySet();
|
||||
}
|
||||
@@ -746,7 +767,8 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
|
||||
/**
|
||||
* Find all resources in the file system of the supplied root directory that
|
||||
* match the given location sub pattern via the Ant-style PathMatcher.
|
||||
* match the given location sub pattern via the Ant-style {@link #getPathMatcher()
|
||||
* PathMatcher}.
|
||||
* @param rootDirResource the root directory as a Resource
|
||||
* @param subPattern the sub pattern to match (below the root directory)
|
||||
* @return a mutable Set of matching Resource instances
|
||||
@@ -924,8 +946,8 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
}
|
||||
|
||||
/**
|
||||
* If it's a "file:" URI, use FileSystemResource to avoid duplicates
|
||||
* for the same path discovered via class-path scanning.
|
||||
* If it's a "file:" URI, use {@link FileSystemResource} to avoid duplicates
|
||||
* for the same path discovered via class path scanning.
|
||||
*/
|
||||
private Resource convertModuleSystemURI(URI uri) {
|
||||
return (ResourceUtils.URL_PROTOCOL_FILE.equals(uri.getScheme()) ?
|
||||
|
||||
+4
-2
@@ -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.
|
||||
@@ -20,6 +20,8 @@ import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Default "no op" {@code ApplicationStartup} implementation.
|
||||
*
|
||||
@@ -52,6 +54,7 @@ class DefaultApplicationStartup implements ApplicationStartup {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Long getParentId() {
|
||||
return null;
|
||||
}
|
||||
@@ -73,7 +76,6 @@ class DefaultApplicationStartup implements ApplicationStartup {
|
||||
|
||||
@Override
|
||||
public void end() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
* Copyright 2012-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.
|
||||
@@ -21,10 +21,10 @@ import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.core.metrics.StartupStep;
|
||||
import org.springframework.lang.NonNull;
|
||||
|
||||
/**
|
||||
* {@link StartupStep} implementation for the Java Flight Recorder.
|
||||
*
|
||||
* <p>This variant delegates to a {@link FlightRecorderStartupEvent JFR event extension}
|
||||
* to collect and record data in Java Flight Recorder.
|
||||
*
|
||||
@@ -114,12 +114,12 @@ class FlightRecorderStartupStep implements StartupStep {
|
||||
add(key, value.get());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Iterator<Tag> iterator() {
|
||||
return new TagsIterator();
|
||||
}
|
||||
|
||||
|
||||
private class TagsIterator implements Iterator<Tag> {
|
||||
|
||||
private int idx = 0;
|
||||
|
||||
+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.
|
||||
@@ -73,20 +73,20 @@ public abstract class AbstractTypeHierarchyTraversingFilter implements TypeFilte
|
||||
// Optimization to avoid creating ClassReader for superclass.
|
||||
Boolean superClassMatch = matchSuperClass(superClassName);
|
||||
if (superClassMatch != null) {
|
||||
if (superClassMatch.booleanValue()) {
|
||||
if (superClassMatch) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Need to read superclass to determine a match...
|
||||
try {
|
||||
if (match(metadata.getSuperClassName(), metadataReaderFactory)) {
|
||||
if (match(superClassName, metadataReaderFactory)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Could not read superclass [" + metadata.getSuperClassName() +
|
||||
logger.debug("Could not read superclass [" + superClassName +
|
||||
"] of type-filtered class [" + metadata.getClassName() + "]");
|
||||
}
|
||||
}
|
||||
@@ -99,7 +99,7 @@ public abstract class AbstractTypeHierarchyTraversingFilter implements TypeFilte
|
||||
// Optimization to avoid creating ClassReader for superclass
|
||||
Boolean interfaceMatch = matchInterface(ifc);
|
||||
if (interfaceMatch != null) {
|
||||
if (interfaceMatch.booleanValue()) {
|
||||
if (interfaceMatch) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -59,7 +59,7 @@ public class MultiValueMapAdapter<K, V> implements MultiValueMap<K, V>, Serializ
|
||||
@Nullable
|
||||
public V getFirst(K key) {
|
||||
List<V> values = this.targetMap.get(key);
|
||||
return (values != null && !values.isEmpty() ? values.get(0) : null);
|
||||
return (!CollectionUtils.isEmpty(values) ? values.get(0) : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -95,7 +95,7 @@ public class MultiValueMapAdapter<K, V> implements MultiValueMap<K, V>, Serializ
|
||||
public Map<K, V> toSingleValueMap() {
|
||||
Map<K, V> singleValueMap = CollectionUtils.newLinkedHashMap(this.targetMap.size());
|
||||
this.targetMap.forEach((key, values) -> {
|
||||
if (values != null && !values.isEmpty()) {
|
||||
if (!CollectionUtils.isEmpty(values)) {
|
||||
singleValueMap.put(key, values.get(0));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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,6 +100,7 @@ public abstract class ResourceUtils {
|
||||
* @return whether the location qualifies as a URL
|
||||
* @see #CLASSPATH_URL_PREFIX
|
||||
* @see java.net.URL
|
||||
* @see #toURL(String)
|
||||
*/
|
||||
public static boolean isUrl(@Nullable String resourceLocation) {
|
||||
if (resourceLocation == null) {
|
||||
@@ -125,6 +126,7 @@ public abstract class ResourceUtils {
|
||||
* "classpath:" pseudo URL, a "file:" URL, or a plain file path
|
||||
* @return a corresponding URL object
|
||||
* @throws FileNotFoundException if the resource cannot be resolved to a URL
|
||||
* @see #toURL(String)
|
||||
*/
|
||||
public static URL getURL(String resourceLocation) throws FileNotFoundException {
|
||||
Assert.notNull(resourceLocation, "Resource location must not be null");
|
||||
@@ -165,6 +167,7 @@ public abstract class ResourceUtils {
|
||||
* @return a corresponding File object
|
||||
* @throws FileNotFoundException if the resource cannot be resolved to
|
||||
* a file in the file system
|
||||
* @see #getFile(URL)
|
||||
*/
|
||||
public static File getFile(String resourceLocation) throws FileNotFoundException {
|
||||
Assert.notNull(resourceLocation, "Resource location must not be null");
|
||||
@@ -196,6 +199,7 @@ public abstract class ResourceUtils {
|
||||
* @return a corresponding File object
|
||||
* @throws FileNotFoundException if the URL cannot be resolved to
|
||||
* a file in the file system
|
||||
* @see #getFile(URL, String)
|
||||
*/
|
||||
public static File getFile(URL resourceUrl) throws FileNotFoundException {
|
||||
return getFile(resourceUrl, "URL");
|
||||
@@ -236,6 +240,7 @@ public abstract class ResourceUtils {
|
||||
* @throws FileNotFoundException if the URL cannot be resolved to
|
||||
* a file in the file system
|
||||
* @since 2.5
|
||||
* @see #getFile(URI, String)
|
||||
*/
|
||||
public static File getFile(URI resourceUri) throws FileNotFoundException {
|
||||
return getFile(resourceUri, "URI");
|
||||
@@ -267,6 +272,7 @@ public abstract class ResourceUtils {
|
||||
* i.e. has protocol "file", "vfsfile" or "vfs".
|
||||
* @param url the URL to check
|
||||
* @return whether the URL has been identified as a file system URL
|
||||
* @see #isJarURL(URL)
|
||||
*/
|
||||
public static boolean isFileURL(URL url) {
|
||||
String protocol = url.getProtocol();
|
||||
@@ -275,10 +281,12 @@ public abstract class ResourceUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the given URL points to a resource in a jar file.
|
||||
* i.e. has protocol "jar", "war, ""zip", "vfszip" or "wsjar".
|
||||
* Determine whether the given URL points to a resource in a jar file
|
||||
* — for example, whether the URL has protocol "jar", "war, "zip",
|
||||
* "vfszip", or "wsjar".
|
||||
* @param url the URL to check
|
||||
* @return whether the URL has been identified as a JAR URL
|
||||
* @see #isJarFileURL(URL)
|
||||
*/
|
||||
public static boolean isJarURL(URL url) {
|
||||
String protocol = url.getProtocol();
|
||||
@@ -293,6 +301,7 @@ public abstract class ResourceUtils {
|
||||
* @param url the URL to check
|
||||
* @return whether the URL has been identified as a JAR file URL
|
||||
* @since 4.1
|
||||
* @see #extractJarFileURL(URL)
|
||||
*/
|
||||
public static boolean isJarFileURL(URL url) {
|
||||
return (URL_PROTOCOL_FILE.equals(url.getProtocol()) &&
|
||||
@@ -305,6 +314,7 @@ public abstract class ResourceUtils {
|
||||
* @param jarUrl the original URL
|
||||
* @return the URL for the actual jar file
|
||||
* @throws MalformedURLException if no valid jar file URL could be extracted
|
||||
* @see #extractArchiveURL(URL)
|
||||
*/
|
||||
public static URL extractJarFileURL(URL jarUrl) throws MalformedURLException {
|
||||
String urlFile = jarUrl.getFile();
|
||||
@@ -366,6 +376,7 @@ public abstract class ResourceUtils {
|
||||
* @return the URI instance
|
||||
* @throws URISyntaxException if the URL wasn't a valid URI
|
||||
* @see java.net.URL#toURI()
|
||||
* @see #toURI(String)
|
||||
*/
|
||||
public static URI toURI(URL url) throws URISyntaxException {
|
||||
return toURI(url.toString());
|
||||
@@ -377,6 +388,7 @@ public abstract class ResourceUtils {
|
||||
* @param location the location String to convert into a URI instance
|
||||
* @return the URI instance
|
||||
* @throws URISyntaxException if the location wasn't a valid URI
|
||||
* @see #toURI(URL)
|
||||
*/
|
||||
public static URI toURI(String location) throws URISyntaxException {
|
||||
return new URI(StringUtils.replace(location, " ", "%20"));
|
||||
@@ -389,6 +401,7 @@ public abstract class ResourceUtils {
|
||||
* @return the URL instance
|
||||
* @throws MalformedURLException if the location wasn't a valid URL
|
||||
* @since 6.0
|
||||
* @see java.net.URL#URL(String)
|
||||
*/
|
||||
public static URL toURL(String location) throws MalformedURLException {
|
||||
// Equivalent without java.net.URL constructor - for building on JDK 20+
|
||||
@@ -414,6 +427,7 @@ public abstract class ResourceUtils {
|
||||
* @return the relative URL instance
|
||||
* @throws MalformedURLException if the end result is not a valid URL
|
||||
* @since 6.0
|
||||
* @see java.net.URL#URL(URL, String)
|
||||
*/
|
||||
public static URL toRelativeURL(URL root, String relativePath) throws MalformedURLException {
|
||||
// # can appear in filenames, java.net.URL should not treat it as a fragment
|
||||
@@ -429,9 +443,10 @@ public abstract class ResourceUtils {
|
||||
|
||||
/**
|
||||
* Set the {@link URLConnection#setUseCaches "useCaches"} flag on the
|
||||
* given connection, preferring {@code false} but leaving the
|
||||
* flag at {@code true} for JNLP based resources.
|
||||
* given connection, preferring {@code false} but leaving the flag at
|
||||
* {@code true} for JNLP based resources.
|
||||
* @param con the URLConnection to set the flag on
|
||||
* @see URLConnection#setUseCaches
|
||||
*/
|
||||
public static void useCachesIfNecessary(URLConnection con) {
|
||||
con.setUseCaches(con.getClass().getSimpleName().startsWith("JNLP"));
|
||||
|
||||
@@ -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.
|
||||
@@ -123,7 +123,7 @@ public abstract class StringUtils {
|
||||
* @see #hasText(CharSequence)
|
||||
*/
|
||||
public static boolean hasLength(@Nullable CharSequence str) {
|
||||
return (str != null && str.length() > 0);
|
||||
return (str != null && !str.isEmpty()); // as of JDK 15
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -791,6 +791,7 @@ public abstract class StringUtils {
|
||||
* and {@code "0"} through {@code "9"} stay the same.</li>
|
||||
* <li>Special characters {@code "-"}, {@code "_"}, {@code "."}, and {@code "*"} stay the same.</li>
|
||||
* <li>A sequence "{@code %<i>xy</i>}" is interpreted as a hexadecimal representation of the character.</li>
|
||||
* <li>For all other characters (including those already decoded), the output is undefined.</li>
|
||||
* </ul>
|
||||
* @param source the encoded String
|
||||
* @param charset the character set
|
||||
@@ -839,7 +840,7 @@ public abstract class StringUtils {
|
||||
* the {@link Locale#toString} format as well as BCP 47 language tags as
|
||||
* specified by {@link Locale#forLanguageTag}.
|
||||
* @param localeValue the locale value: following either {@code Locale's}
|
||||
* {@code toString()} format ("en", "en_UK", etc), also accepting spaces as
|
||||
* {@code toString()} format ("en", "en_UK", etc.), also accepting spaces as
|
||||
* separators (as an alternative to underscores), or BCP 47 (e.g. "en-UK")
|
||||
* @return a corresponding {@code Locale} instance, or {@code null} if none
|
||||
* @throws IllegalArgumentException in case of an invalid locale specification
|
||||
@@ -852,7 +853,7 @@ public abstract class StringUtils {
|
||||
if (!localeValue.contains("_") && !localeValue.contains(" ")) {
|
||||
validateLocalePart(localeValue);
|
||||
Locale resolved = Locale.forLanguageTag(localeValue);
|
||||
if (resolved.getLanguage().length() > 0) {
|
||||
if (!resolved.getLanguage().isEmpty()) {
|
||||
return resolved;
|
||||
}
|
||||
}
|
||||
@@ -868,7 +869,7 @@ public abstract class StringUtils {
|
||||
* <p><b>Note: This delegate does not accept the BCP 47 language tag format.
|
||||
* Please use {@link #parseLocale} for lenient parsing of both formats.</b>
|
||||
* @param localeString the locale {@code String}: following {@code Locale's}
|
||||
* {@code toString()} format ("en", "en_UK", etc), also accepting spaces as
|
||||
* {@code toString()} format ("en", "en_UK", etc.), also accepting spaces as
|
||||
* separators (as an alternative to underscores)
|
||||
* @return a corresponding {@code Locale} instance, or {@code null} if none
|
||||
* @throws IllegalArgumentException in case of an invalid locale specification
|
||||
@@ -876,7 +877,7 @@ public abstract class StringUtils {
|
||||
@SuppressWarnings("deprecation") // for Locale constructors on JDK 19
|
||||
@Nullable
|
||||
public static Locale parseLocaleString(String localeString) {
|
||||
if (localeString.equals("")) {
|
||||
if (localeString.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1181,7 +1182,7 @@ public abstract class StringUtils {
|
||||
if (trimTokens) {
|
||||
token = token.trim();
|
||||
}
|
||||
if (!ignoreEmptyTokens || token.length() > 0) {
|
||||
if (!ignoreEmptyTokens || !token.isEmpty()) {
|
||||
tokens.add(token);
|
||||
}
|
||||
}
|
||||
@@ -1243,7 +1244,7 @@ public abstract class StringUtils {
|
||||
result.add(deleteAny(str.substring(pos, delPos), charsToDelete));
|
||||
pos = delPos + delimiter.length();
|
||||
}
|
||||
if (str.length() > 0 && pos <= str.length()) {
|
||||
if (!str.isEmpty() && pos <= str.length()) {
|
||||
// Add rest of String, but not in case of empty input.
|
||||
result.add(deleteAny(str.substring(pos), charsToDelete));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.core;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.MethodIntrospector.MetadataLookup;
|
||||
import org.springframework.core.annotation.MergedAnnotations;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.core.annotation.MergedAnnotations.SearchStrategy.TYPE_HIERARCHY;
|
||||
|
||||
/**
|
||||
* Tests for {@link MethodIntrospector}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 5.3.34
|
||||
*/
|
||||
class MethodIntrospectorTests {
|
||||
|
||||
@Test // gh-32586
|
||||
void selectMethodsAndClearDeclaredMethodsCacheBetweenInvocations() {
|
||||
Class<?> targetType = ActualController.class;
|
||||
|
||||
// Preconditions for this use case.
|
||||
assertThat(targetType).isPublic();
|
||||
assertThat(targetType.getSuperclass()).isPackagePrivate();
|
||||
|
||||
MetadataLookup<String> metadataLookup = (MetadataLookup<String>) method -> {
|
||||
if (MergedAnnotations.from(method, TYPE_HIERARCHY).isPresent(Mapped.class)) {
|
||||
return method.getName();
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
// Start with a clean slate.
|
||||
ReflectionUtils.clearCache();
|
||||
|
||||
// Round #1
|
||||
Map<Method, String> methods = MethodIntrospector.selectMethods(targetType, metadataLookup);
|
||||
assertThat(methods.values()).containsExactlyInAnyOrder("update", "delete");
|
||||
|
||||
// Simulate ConfigurableApplicationContext#refresh() which clears the
|
||||
// ReflectionUtils#declaredMethodsCache but NOT the BridgeMethodResolver#cache.
|
||||
// As a consequence, ReflectionUtils.getDeclaredMethods(...) will return a
|
||||
// new set of methods that are logically equivalent to but not identical
|
||||
// to (in terms of object identity) any bridged methods cached in the
|
||||
// BridgeMethodResolver cache.
|
||||
ReflectionUtils.clearCache();
|
||||
|
||||
// Round #2
|
||||
methods = MethodIntrospector.selectMethods(targetType, metadataLookup);
|
||||
assertThat(methods.values()).containsExactlyInAnyOrder("update", "delete");
|
||||
}
|
||||
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Mapped {
|
||||
}
|
||||
|
||||
interface Controller {
|
||||
|
||||
void unmappedMethod();
|
||||
|
||||
@Mapped
|
||||
void update();
|
||||
|
||||
@Mapped
|
||||
void delete();
|
||||
}
|
||||
|
||||
// Must NOT be public.
|
||||
abstract static class AbstractController implements Controller {
|
||||
|
||||
@Override
|
||||
public void unmappedMethod() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
}
|
||||
}
|
||||
|
||||
// MUST be public.
|
||||
public static class ActualController extends AbstractController {
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+63
-1
@@ -24,6 +24,7 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -175,7 +176,7 @@ class MergedAnnotationsRepeatableAnnotationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void typeHierarchyWhenWhenOnSuperclassReturnsAnnotations() {
|
||||
void typeHierarchyWhenOnSuperclassReturnsAnnotations() {
|
||||
Set<PeteRepeat> annotations = getAnnotations(null, PeteRepeat.class,
|
||||
SearchStrategy.TYPE_HIERARCHY, SubRepeatableClass.class);
|
||||
assertThat(annotations.stream().map(PeteRepeat::value)).containsExactly("A", "B",
|
||||
@@ -240,6 +241,44 @@ class MergedAnnotationsRepeatableAnnotationTests {
|
||||
assertThat(annotationTypes).containsExactly(WithRepeatedMetaAnnotations.class, Noninherited.class, Noninherited.class);
|
||||
}
|
||||
|
||||
@Test // gh-32731
|
||||
void searchFindsRepeatableContainerAnnotationAndRepeatedAnnotations() {
|
||||
Class<?> clazz = StandardRepeatablesWithContainerWithMultipleAttributesTestCase.class;
|
||||
|
||||
// NO RepeatableContainers
|
||||
MergedAnnotations mergedAnnotations = MergedAnnotations.from(clazz, SearchStrategy.TYPE_HIERARCHY, RepeatableContainers.none());
|
||||
ContainerWithMultipleAttributes container = mergedAnnotations
|
||||
.get(ContainerWithMultipleAttributes.class)
|
||||
.synthesize(MergedAnnotation::isPresent).orElse(null);
|
||||
assertThat(container).as("container").isNotNull();
|
||||
assertThat(container.name()).isEqualTo("enigma");
|
||||
RepeatableWithContainerWithMultipleAttributes[] repeatedAnnotations = container.value();
|
||||
assertThat(Arrays.stream(repeatedAnnotations).map(RepeatableWithContainerWithMultipleAttributes::value))
|
||||
.containsExactly("A", "B");
|
||||
Set<RepeatableWithContainerWithMultipleAttributes> set =
|
||||
mergedAnnotations.stream(RepeatableWithContainerWithMultipleAttributes.class)
|
||||
.collect(MergedAnnotationCollectors.toAnnotationSet());
|
||||
// Only finds the locally declared repeated annotation.
|
||||
assertThat(set.stream().map(RepeatableWithContainerWithMultipleAttributes::value))
|
||||
.containsExactly("C");
|
||||
|
||||
// Standard RepeatableContainers
|
||||
mergedAnnotations = MergedAnnotations.from(clazz, SearchStrategy.TYPE_HIERARCHY, RepeatableContainers.standardRepeatables());
|
||||
container = mergedAnnotations
|
||||
.get(ContainerWithMultipleAttributes.class)
|
||||
.synthesize(MergedAnnotation::isPresent).orElse(null);
|
||||
assertThat(container).as("container").isNotNull();
|
||||
assertThat(container.name()).isEqualTo("enigma");
|
||||
repeatedAnnotations = container.value();
|
||||
assertThat(Arrays.stream(repeatedAnnotations).map(RepeatableWithContainerWithMultipleAttributes::value))
|
||||
.containsExactly("A", "B");
|
||||
set = mergedAnnotations.stream(RepeatableWithContainerWithMultipleAttributes.class)
|
||||
.collect(MergedAnnotationCollectors.toAnnotationSet());
|
||||
// Finds the locally declared repeated annotation plus the 2 in the container.
|
||||
assertThat(set.stream().map(RepeatableWithContainerWithMultipleAttributes::value))
|
||||
.containsExactly("A", "B", "C");
|
||||
}
|
||||
|
||||
private <A extends Annotation> Set<A> getAnnotations(Class<? extends Annotation> container,
|
||||
Class<A> repeatable, SearchStrategy searchStrategy, AnnotatedElement element) {
|
||||
|
||||
@@ -449,4 +488,27 @@ class MergedAnnotationsRepeatableAnnotationTests {
|
||||
static class WithRepeatedMetaAnnotationsClass {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface ContainerWithMultipleAttributes {
|
||||
|
||||
RepeatableWithContainerWithMultipleAttributes[] value();
|
||||
|
||||
String name() default "";
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Repeatable(ContainerWithMultipleAttributes.class)
|
||||
@interface RepeatableWithContainerWithMultipleAttributes {
|
||||
|
||||
String value() default "";
|
||||
}
|
||||
|
||||
@ContainerWithMultipleAttributes(name = "enigma", value = {
|
||||
@RepeatableWithContainerWithMultipleAttributes("A"),
|
||||
@RepeatableWithContainerWithMultipleAttributes("B")
|
||||
})
|
||||
@RepeatableWithContainerWithMultipleAttributes("C")
|
||||
static class StandardRepeatablesWithContainerWithMultipleAttributesTestCase {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+11
-2
@@ -16,7 +16,9 @@
|
||||
|
||||
package org.springframework.core.env;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -300,6 +302,12 @@ class StandardEnvironmentTests {
|
||||
assertThat(systemProperties.get(DISALLOWED_PROPERTY_NAME)).isEqualTo(DISALLOWED_PROPERTY_VALUE);
|
||||
assertThat(systemProperties.get(STRING_PROPERTY_NAME)).isEqualTo(NON_STRING_PROPERTY_VALUE);
|
||||
assertThat(systemProperties.get(NON_STRING_PROPERTY_NAME)).isEqualTo(STRING_PROPERTY_VALUE);
|
||||
|
||||
PropertiesPropertySource systemPropertySource = (PropertiesPropertySource)
|
||||
environment.getPropertySources().get(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME);
|
||||
Set<String> expectedKeys = new HashSet<>(System.getProperties().stringPropertyNames());
|
||||
expectedKeys.add(STRING_PROPERTY_NAME); // filtered out by stringPropertyNames due to non-String value
|
||||
assertThat(Set.of(systemPropertySource.getPropertyNames())).isEqualTo(expectedKeys);
|
||||
}
|
||||
finally {
|
||||
System.clearProperty(ALLOWED_PROPERTY_NAME);
|
||||
@@ -316,6 +324,7 @@ class StandardEnvironmentTests {
|
||||
assertThat(System.getenv()).isSameAs(systemEnvironment);
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
class GetActiveProfiles {
|
||||
|
||||
@@ -365,6 +374,7 @@ class StandardEnvironmentTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
class AcceptsProfilesTests {
|
||||
|
||||
@@ -447,9 +457,9 @@ class StandardEnvironmentTests {
|
||||
environment.addActiveProfile("p2");
|
||||
assertThat(environment.acceptsProfiles(Profiles.of("p1 & p2"))).isTrue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
class MatchesProfilesTests {
|
||||
|
||||
@@ -559,7 +569,6 @@ class StandardEnvironmentTests {
|
||||
assertThat(environment.matchesProfiles("p2 & (foo | p1)")).isTrue();
|
||||
assertThat(environment.matchesProfiles("foo", "(p2 & p1)")).isTrue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -45,6 +45,7 @@ class StreamUtilsTests {
|
||||
|
||||
private String string = "";
|
||||
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
new Random().nextBytes(bytes);
|
||||
@@ -53,6 +54,7 @@ class StreamUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void copyToByteArray() throws Exception {
|
||||
InputStream inputStream = new ByteArrayInputStream(bytes);
|
||||
@@ -127,4 +129,5 @@ class StreamUtilsTests {
|
||||
ordered.verify(source).write(bytes, 1, 2);
|
||||
ordered.verify(source, never()).close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,11 +21,12 @@ import java.util.Collections;
|
||||
import org.springframework.aot.hint.ExecutableMode;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* {@link RuntimeHintsRegistrar} implementation that registers reflection hints for
|
||||
* {@code EmbeddedDataSourceProxy#shutdown} in order to allow it to be used as a bean
|
||||
* destroy method.
|
||||
* {@link RuntimeHintsRegistrar} implementation that registers reflection hints
|
||||
* for {@code EmbeddedDataSourceProxy#shutdown} in order to allow it to be used
|
||||
* as a bean destroy method.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 6.0
|
||||
@@ -33,7 +34,7 @@ import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
class EmbeddedDatabaseFactoryRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
||||
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
|
||||
hints.reflection().registerTypeIfPresent(classLoader,
|
||||
"org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactory$EmbeddedDataSourceProxy",
|
||||
builder -> builder
|
||||
|
||||
@@ -229,14 +229,14 @@ public abstract class JdbcUtils {
|
||||
try {
|
||||
return rs.getObject(index, requiredType);
|
||||
}
|
||||
catch (AbstractMethodError err) {
|
||||
logger.debug("JDBC driver does not implement JDBC 4.1 'getObject(int, Class)' method", err);
|
||||
}
|
||||
catch (SQLFeatureNotSupportedException ex) {
|
||||
catch (SQLFeatureNotSupportedException | AbstractMethodError ex) {
|
||||
logger.debug("JDBC driver does not support JDBC 4.1 'getObject(int, Class)' method", ex);
|
||||
}
|
||||
catch (SQLException ex) {
|
||||
logger.debug("JDBC driver has limited support for JDBC 4.1 'getObject(int, Class)' method", ex);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("JDBC driver has limited support for 'getObject(int, Class)' with column type: " +
|
||||
requiredType.getName(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
// Corresponding SQL types for JSR-310 / Joda-Time types, left up
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -32,7 +32,7 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* The default implementation of Spring's {@link SqlRowSet} interface, wrapping a
|
||||
* The common implementation of Spring's {@link SqlRowSet} interface, wrapping a
|
||||
* {@link java.sql.ResultSet}, catching any {@link SQLException SQLExceptions} and
|
||||
* translating them to a corresponding Spring {@link InvalidResultSetAccessException}.
|
||||
*
|
||||
|
||||
+30
-29
@@ -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.
|
||||
@@ -37,167 +37,168 @@ import static org.mockito.Mockito.mock;
|
||||
/**
|
||||
* @author Thomas Risberg
|
||||
*/
|
||||
public class ResultSetWrappingRowSetTests {
|
||||
class ResultSetWrappingRowSetTests {
|
||||
|
||||
private ResultSet resultSet = mock();
|
||||
private final ResultSet resultSet = mock();
|
||||
|
||||
private ResultSetWrappingSqlRowSet rowSet = new ResultSetWrappingSqlRowSet(resultSet);
|
||||
private final ResultSetWrappingSqlRowSet rowSet = new ResultSetWrappingSqlRowSet(resultSet);
|
||||
|
||||
|
||||
@Test
|
||||
public void testGetBigDecimalInt() throws Exception {
|
||||
void testGetBigDecimalInt() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getBigDecimal", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getBigDecimal", int.class);
|
||||
doTest(rset, rowset, 1, BigDecimal.ONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBigDecimalString() throws Exception {
|
||||
void testGetBigDecimalString() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getBigDecimal", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getBigDecimal", String.class);
|
||||
doTest(rset, rowset, "test", BigDecimal.ONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetStringInt() throws Exception {
|
||||
void testGetStringInt() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getString", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getString", int.class);
|
||||
doTest(rset, rowset, 1, "test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetStringString() throws Exception {
|
||||
void testGetStringString() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getString", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getString", String.class);
|
||||
doTest(rset, rowset, "test", "test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTimestampInt() throws Exception {
|
||||
void testGetTimestampInt() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getTimestamp", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getTimestamp", int.class);
|
||||
doTest(rset, rowset, 1, new Timestamp(1234L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTimestampString() throws Exception {
|
||||
void testGetTimestampString() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getTimestamp", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getTimestamp", String.class);
|
||||
doTest(rset, rowset, "test", new Timestamp(1234L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDateInt() throws Exception {
|
||||
void testGetDateInt() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getDate", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getDate", int.class);
|
||||
doTest(rset, rowset, 1, new Date(1234L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDateString() throws Exception {
|
||||
void testGetDateString() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getDate", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getDate", String.class);
|
||||
doTest(rset, rowset, "test", new Date(1234L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTimeInt() throws Exception {
|
||||
void testGetTimeInt() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getTime", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getTime", int.class);
|
||||
doTest(rset, rowset, 1, new Time(1234L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTimeString() throws Exception {
|
||||
void testGetTimeString() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getTime", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getTime", String.class);
|
||||
doTest(rset, rowset, "test", new Time(1234L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetObjectInt() throws Exception {
|
||||
void testGetObjectInt() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getObject", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getObject", int.class);
|
||||
doTest(rset, rowset, 1, new Object());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetObjectString() throws Exception {
|
||||
void testGetObjectString() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getObject", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getObject", String.class);
|
||||
doTest(rset, rowset, "test", new Object());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetIntInt() throws Exception {
|
||||
void testGetIntInt() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getInt", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getInt", int.class);
|
||||
doTest(rset, rowset, 1, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetIntString() throws Exception {
|
||||
void testGetIntString() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getInt", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getInt", String.class);
|
||||
doTest(rset, rowset, "test", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFloatInt() throws Exception {
|
||||
void testGetFloatInt() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getFloat", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getFloat", int.class);
|
||||
doTest(rset, rowset, 1, 1.0f);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFloatString() throws Exception {
|
||||
void testGetFloatString() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getFloat", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getFloat", String.class);
|
||||
doTest(rset, rowset, "test", 1.0f);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDoubleInt() throws Exception {
|
||||
void testGetDoubleInt() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getDouble", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getDouble", int.class);
|
||||
doTest(rset, rowset, 1, 1.0d);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDoubleString() throws Exception {
|
||||
void testGetDoubleString() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getDouble", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getDouble", String.class);
|
||||
doTest(rset, rowset, "test", 1.0d);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetLongInt() throws Exception {
|
||||
void testGetLongInt() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getLong", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getLong", int.class);
|
||||
doTest(rset, rowset, 1, 1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetLongString() throws Exception {
|
||||
void testGetLongString() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getLong", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getLong", String.class);
|
||||
doTest(rset, rowset, "test", 1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBooleanInt() throws Exception {
|
||||
void testGetBooleanInt() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getBoolean", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getBoolean", int.class);
|
||||
doTest(rset, rowset, 1, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBooleanString() throws Exception {
|
||||
void testGetBooleanString() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getBoolean", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getBoolean", String.class);
|
||||
doTest(rset, rowset, "test", true);
|
||||
}
|
||||
|
||||
|
||||
private void doTest(Method rsetMethod, Method rowsetMethod, Object arg, Object ret) throws Exception {
|
||||
if (arg instanceof String) {
|
||||
given(resultSet.findColumn((String) arg)).willReturn(1);
|
||||
@@ -207,9 +208,9 @@ public class ResultSetWrappingRowSetTests {
|
||||
given(rsetMethod.invoke(resultSet, arg)).willReturn(ret).willThrow(new SQLException("test"));
|
||||
}
|
||||
rowsetMethod.invoke(rowSet, arg);
|
||||
assertThatExceptionOfType(InvocationTargetException.class).isThrownBy(() ->
|
||||
rowsetMethod.invoke(rowSet, arg)).
|
||||
satisfies(ex -> assertThat(ex.getTargetException()).isExactlyInstanceOf(InvalidResultSetAccessException.class));
|
||||
assertThatExceptionOfType(InvocationTargetException.class)
|
||||
.isThrownBy(() -> rowsetMethod.invoke(rowSet, arg))
|
||||
.satisfies(ex -> assertThat(ex.getTargetException()).isExactlyInstanceOf(InvalidResultSetAccessException.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -189,11 +189,6 @@ public class JmsMessagingTemplate extends AbstractMessagingTemplate<Destination>
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void convertAndSend(Object payload) throws MessagingException {
|
||||
convertAndSend(payload, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void convertAndSend(Object payload, @Nullable MessagePostProcessor postProcessor) throws MessagingException {
|
||||
Destination defaultDestination = getDefaultDestination();
|
||||
|
||||
@@ -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.
|
||||
@@ -290,7 +290,7 @@ public interface JmsOperations {
|
||||
* <p>This method should be used carefully, since it will block the thread
|
||||
* until the message becomes available or until the timeout value is exceeded.
|
||||
* <p>This will only work with a default destination specified!
|
||||
* @return the message produced for the consumer or {@code null} if the timeout expires.
|
||||
* @return the message produced for the consumer, or {@code null} if the timeout expires
|
||||
* @throws JmsException checked JMSException converted to unchecked
|
||||
*/
|
||||
@Nullable
|
||||
@@ -303,7 +303,7 @@ public interface JmsOperations {
|
||||
* <p>This method should be used carefully, since it will block the thread
|
||||
* until the message becomes available or until the timeout value is exceeded.
|
||||
* @param destination the destination to receive a message from
|
||||
* @return the message produced for the consumer or {@code null} if the timeout expires.
|
||||
* @return the message produced for the consumer, or {@code null} if the timeout expires
|
||||
* @throws JmsException checked JMSException converted to unchecked
|
||||
*/
|
||||
@Nullable
|
||||
@@ -317,7 +317,7 @@ public interface JmsOperations {
|
||||
* until the message becomes available or until the timeout value is exceeded.
|
||||
* @param destinationName the name of the destination to send this message to
|
||||
* (to be resolved to an actual destination by a DestinationResolver)
|
||||
* @return the message produced for the consumer or {@code null} if the timeout expires.
|
||||
* @return the message produced for the consumer, or {@code null} if the timeout expires
|
||||
* @throws JmsException checked JMSException converted to unchecked
|
||||
*/
|
||||
@Nullable
|
||||
@@ -332,7 +332,7 @@ public interface JmsOperations {
|
||||
* <p>This will only work with a default destination specified!
|
||||
* @param messageSelector the JMS message selector expression (or {@code null} if none).
|
||||
* See the JMS specification for a detailed definition of selector expressions.
|
||||
* @return the message produced for the consumer or {@code null} if the timeout expires.
|
||||
* @return the message produced for the consumer, or {@code null} if the timeout expires
|
||||
* @throws JmsException checked JMSException converted to unchecked
|
||||
*/
|
||||
@Nullable
|
||||
@@ -347,7 +347,7 @@ public interface JmsOperations {
|
||||
* @param destination the destination to receive a message from
|
||||
* @param messageSelector the JMS message selector expression (or {@code null} if none).
|
||||
* See the JMS specification for a detailed definition of selector expressions.
|
||||
* @return the message produced for the consumer or {@code null} if the timeout expires.
|
||||
* @return the message produced for the consumer, or {@code null} if the timeout expires
|
||||
* @throws JmsException checked JMSException converted to unchecked
|
||||
*/
|
||||
@Nullable
|
||||
@@ -363,7 +363,7 @@ public interface JmsOperations {
|
||||
* (to be resolved to an actual destination by a DestinationResolver)
|
||||
* @param messageSelector the JMS message selector expression (or {@code null} if none).
|
||||
* See the JMS specification for a detailed definition of selector expressions.
|
||||
* @return the message produced for the consumer or {@code null} if the timeout expires.
|
||||
* @return the message produced for the consumer, or {@code null} if the timeout expires
|
||||
* @throws JmsException checked JMSException converted to unchecked
|
||||
*/
|
||||
@Nullable
|
||||
|
||||
@@ -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.
|
||||
@@ -217,7 +217,7 @@ public abstract class JmsUtils {
|
||||
try {
|
||||
session.commit();
|
||||
}
|
||||
catch (jakarta.jms.TransactionInProgressException | jakarta.jms.IllegalStateException ex) {
|
||||
catch (jakarta.jms.TransactionInProgressException ex) {
|
||||
// Ignore -> can only happen in case of a JTA transaction.
|
||||
}
|
||||
}
|
||||
@@ -232,7 +232,7 @@ public abstract class JmsUtils {
|
||||
try {
|
||||
session.rollback();
|
||||
}
|
||||
catch (jakarta.jms.TransactionInProgressException | jakarta.jms.IllegalStateException ex) {
|
||||
catch (jakarta.jms.TransactionInProgressException ex) {
|
||||
// Ignore -> can only happen in case of a JTA transaction.
|
||||
}
|
||||
}
|
||||
|
||||
+2
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -168,8 +168,7 @@ public abstract class AbstractMessageSendingTemplate<D> implements MessageSendin
|
||||
|
||||
Map<String, Object> headersToUse = processHeadersToSend(headers);
|
||||
if (headersToUse != null) {
|
||||
messageHeaders = (headersToUse instanceof MessageHeaders _messageHeaders ?
|
||||
_messageHeaders : new MessageHeaders(headersToUse));
|
||||
messageHeaders = (headersToUse instanceof MessageHeaders mh ? mh : new MessageHeaders(headersToUse));
|
||||
}
|
||||
|
||||
MessageConverter converter = getMessageConverter();
|
||||
|
||||
@@ -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.
|
||||
@@ -22,6 +22,7 @@ import org.springframework.aot.hint.ExecutableMode;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
@@ -41,8 +42,9 @@ class EntityManagerRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
private static final String NATIVE_QUERY_IMPL_CLASS_NAME = "org.hibernate.query.sql.internal.NativeQueryImpl";
|
||||
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
||||
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
|
||||
if (ClassUtils.isPresent(HIBERNATE_SESSION_FACTORY_CLASS_NAME, classLoader)) {
|
||||
hints.proxies().registerJdkProxy(TypeReference.of(HIBERNATE_SESSION_FACTORY_CLASS_NAME),
|
||||
TypeReference.of(EntityManagerFactoryInfo.class));
|
||||
@@ -70,4 +72,5 @@ class EntityManagerRuntimeHints implements RuntimeHintsRegistrar {
|
||||
catch (ClassNotFoundException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
-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.
|
||||
@@ -385,7 +385,9 @@ public abstract class SharedEntityManagerCreator {
|
||||
else if (targetClass.isInstance(proxy)) {
|
||||
return proxy;
|
||||
}
|
||||
break;
|
||||
else {
|
||||
return this.target.unwrap(targetClass);
|
||||
}
|
||||
case "getOutputParameterValue":
|
||||
if (this.entityManager == null) {
|
||||
Object key = args[0];
|
||||
|
||||
+70
-50
@@ -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.
|
||||
@@ -37,16 +37,16 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.withSettings;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link SharedEntityManagerCreator}.
|
||||
* Tests for {@link SharedEntityManagerCreator}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class SharedEntityManagerCreatorTests {
|
||||
class SharedEntityManagerCreatorTests {
|
||||
|
||||
@Test
|
||||
public void proxyingWorksIfInfoReturnsNullEntityManagerInterface() {
|
||||
void proxyingWorksIfInfoReturnsNullEntityManagerInterface() {
|
||||
EntityManagerFactory emf = mock(EntityManagerFactory.class,
|
||||
withSettings().extraInterfaces(EntityManagerFactoryInfo.class));
|
||||
// EntityManagerFactoryInfo.getEntityManagerInterface returns null
|
||||
@@ -54,7 +54,7 @@ public class SharedEntityManagerCreatorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transactionRequiredExceptionOnJoinTransaction() {
|
||||
void transactionRequiredExceptionOnJoinTransaction() {
|
||||
EntityManagerFactory emf = mock();
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(
|
||||
@@ -62,7 +62,7 @@ public class SharedEntityManagerCreatorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transactionRequiredExceptionOnFlush() {
|
||||
void transactionRequiredExceptionOnFlush() {
|
||||
EntityManagerFactory emf = mock();
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(
|
||||
@@ -70,7 +70,7 @@ public class SharedEntityManagerCreatorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transactionRequiredExceptionOnPersist() {
|
||||
void transactionRequiredExceptionOnPersist() {
|
||||
EntityManagerFactory emf = mock();
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(() ->
|
||||
@@ -78,7 +78,7 @@ public class SharedEntityManagerCreatorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transactionRequiredExceptionOnMerge() {
|
||||
void transactionRequiredExceptionOnMerge() {
|
||||
EntityManagerFactory emf = mock();
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(() ->
|
||||
@@ -86,7 +86,7 @@ public class SharedEntityManagerCreatorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transactionRequiredExceptionOnRemove() {
|
||||
void transactionRequiredExceptionOnRemove() {
|
||||
EntityManagerFactory emf = mock();
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(() ->
|
||||
@@ -94,7 +94,7 @@ public class SharedEntityManagerCreatorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transactionRequiredExceptionOnRefresh() {
|
||||
void transactionRequiredExceptionOnRefresh() {
|
||||
EntityManagerFactory emf = mock();
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(() ->
|
||||
@@ -102,78 +102,98 @@ public class SharedEntityManagerCreatorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deferredQueryWithUpdate() {
|
||||
void deferredQueryWithUpdate() {
|
||||
EntityManagerFactory emf = mock();
|
||||
EntityManager targetEm = mock();
|
||||
Query query = mock();
|
||||
Query targetQuery = mock();
|
||||
given(emf.createEntityManager()).willReturn(targetEm);
|
||||
given(targetEm.createQuery("x")).willReturn(query);
|
||||
given(targetEm.createQuery("x")).willReturn(targetQuery);
|
||||
given(targetEm.isOpen()).willReturn(true);
|
||||
given((Query) targetQuery.unwrap(targetQuery.getClass())).willReturn(targetQuery);
|
||||
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
em.createQuery("x").executeUpdate();
|
||||
Query query = em.createQuery("x");
|
||||
assertThat((Query) query.unwrap(null)).isSameAs(targetQuery);
|
||||
assertThat((Query) query.unwrap(targetQuery.getClass())).isSameAs(targetQuery);
|
||||
assertThat(query.unwrap(Query.class)).isSameAs(query);
|
||||
query.executeUpdate();
|
||||
|
||||
verify(query).executeUpdate();
|
||||
verify(targetQuery).executeUpdate();
|
||||
verify(targetEm).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deferredQueryWithSingleResult() {
|
||||
void deferredQueryWithSingleResult() {
|
||||
EntityManagerFactory emf = mock();
|
||||
EntityManager targetEm = mock();
|
||||
Query query = mock();
|
||||
Query targetQuery = mock();
|
||||
given(emf.createEntityManager()).willReturn(targetEm);
|
||||
given(targetEm.createQuery("x")).willReturn(query);
|
||||
given(targetEm.createQuery("x")).willReturn(targetQuery);
|
||||
given(targetEm.isOpen()).willReturn(true);
|
||||
given((Query) targetQuery.unwrap(targetQuery.getClass())).willReturn(targetQuery);
|
||||
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
em.createQuery("x").getSingleResult();
|
||||
Query query = em.createQuery("x");
|
||||
assertThat((Query) query.unwrap(null)).isSameAs(targetQuery);
|
||||
assertThat((Query) query.unwrap(targetQuery.getClass())).isSameAs(targetQuery);
|
||||
assertThat(query.unwrap(Query.class)).isSameAs(query);
|
||||
query.getSingleResult();
|
||||
|
||||
verify(query).getSingleResult();
|
||||
verify(targetQuery).getSingleResult();
|
||||
verify(targetEm).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deferredQueryWithResultList() {
|
||||
void deferredQueryWithResultList() {
|
||||
EntityManagerFactory emf = mock();
|
||||
EntityManager targetEm = mock();
|
||||
Query query = mock();
|
||||
Query targetQuery = mock();
|
||||
given(emf.createEntityManager()).willReturn(targetEm);
|
||||
given(targetEm.createQuery("x")).willReturn(query);
|
||||
given(targetEm.createQuery("x")).willReturn(targetQuery);
|
||||
given(targetEm.isOpen()).willReturn(true);
|
||||
given((Query) targetQuery.unwrap(targetQuery.getClass())).willReturn(targetQuery);
|
||||
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
em.createQuery("x").getResultList();
|
||||
Query query = em.createQuery("x");
|
||||
assertThat((Query) query.unwrap(null)).isSameAs(targetQuery);
|
||||
assertThat((Query) query.unwrap(targetQuery.getClass())).isSameAs(targetQuery);
|
||||
assertThat(query.unwrap(Query.class)).isSameAs(query);
|
||||
query.getResultList();
|
||||
|
||||
verify(query).getResultList();
|
||||
verify(targetQuery).getResultList();
|
||||
verify(targetEm).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deferredQueryWithResultStream() {
|
||||
void deferredQueryWithResultStream() {
|
||||
EntityManagerFactory emf = mock();
|
||||
EntityManager targetEm = mock();
|
||||
Query query = mock();
|
||||
Query targetQuery = mock();
|
||||
given(emf.createEntityManager()).willReturn(targetEm);
|
||||
given(targetEm.createQuery("x")).willReturn(query);
|
||||
given(targetEm.createQuery("x")).willReturn(targetQuery);
|
||||
given(targetEm.isOpen()).willReturn(true);
|
||||
given((Query) targetQuery.unwrap(targetQuery.getClass())).willReturn(targetQuery);
|
||||
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
em.createQuery("x").getResultStream();
|
||||
Query query = em.createQuery("x");
|
||||
assertThat((Query) query.unwrap(null)).isSameAs(targetQuery);
|
||||
assertThat((Query) query.unwrap(targetQuery.getClass())).isSameAs(targetQuery);
|
||||
assertThat(query.unwrap(Query.class)).isSameAs(query);
|
||||
query.getResultStream();
|
||||
|
||||
verify(query).getResultStream();
|
||||
verify(targetQuery).getResultStream();
|
||||
verify(targetEm).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deferredStoredProcedureQueryWithIndexedParameters() {
|
||||
void deferredStoredProcedureQueryWithIndexedParameters() {
|
||||
EntityManagerFactory emf = mock();
|
||||
EntityManager targetEm = mock();
|
||||
StoredProcedureQuery query = mock();
|
||||
StoredProcedureQuery targetQuery = mock();
|
||||
given(emf.createEntityManager()).willReturn(targetEm);
|
||||
given(targetEm.createStoredProcedureQuery("x")).willReturn(query);
|
||||
willReturn("y").given(query).getOutputParameterValue(0);
|
||||
willReturn("z").given(query).getOutputParameterValue(2);
|
||||
given(targetEm.createStoredProcedureQuery("x")).willReturn(targetQuery);
|
||||
willReturn("y").given(targetQuery).getOutputParameterValue(0);
|
||||
willReturn("z").given(targetQuery).getOutputParameterValue(2);
|
||||
given(targetEm.isOpen()).willReturn(true);
|
||||
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
@@ -187,24 +207,24 @@ public class SharedEntityManagerCreatorTests {
|
||||
spq.getOutputParameterValue(1));
|
||||
assertThat(spq.getOutputParameterValue(2)).isEqualTo("z");
|
||||
|
||||
verify(query).registerStoredProcedureParameter(0, String.class, ParameterMode.OUT);
|
||||
verify(query).registerStoredProcedureParameter(1, Number.class, ParameterMode.IN);
|
||||
verify(query).registerStoredProcedureParameter(2, Object.class, ParameterMode.INOUT);
|
||||
verify(query).execute();
|
||||
verify(targetQuery).registerStoredProcedureParameter(0, String.class, ParameterMode.OUT);
|
||||
verify(targetQuery).registerStoredProcedureParameter(1, Number.class, ParameterMode.IN);
|
||||
verify(targetQuery).registerStoredProcedureParameter(2, Object.class, ParameterMode.INOUT);
|
||||
verify(targetQuery).execute();
|
||||
verify(targetEm).close();
|
||||
verifyNoMoreInteractions(query);
|
||||
verifyNoMoreInteractions(targetQuery);
|
||||
verifyNoMoreInteractions(targetEm);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deferredStoredProcedureQueryWithNamedParameters() {
|
||||
void deferredStoredProcedureQueryWithNamedParameters() {
|
||||
EntityManagerFactory emf = mock();
|
||||
EntityManager targetEm = mock();
|
||||
StoredProcedureQuery query = mock();
|
||||
StoredProcedureQuery targetQuery = mock();
|
||||
given(emf.createEntityManager()).willReturn(targetEm);
|
||||
given(targetEm.createStoredProcedureQuery("x")).willReturn(query);
|
||||
willReturn("y").given(query).getOutputParameterValue("a");
|
||||
willReturn("z").given(query).getOutputParameterValue("c");
|
||||
given(targetEm.createStoredProcedureQuery("x")).willReturn(targetQuery);
|
||||
willReturn("y").given(targetQuery).getOutputParameterValue("a");
|
||||
willReturn("z").given(targetQuery).getOutputParameterValue("c");
|
||||
given(targetEm.isOpen()).willReturn(true);
|
||||
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
@@ -218,12 +238,12 @@ public class SharedEntityManagerCreatorTests {
|
||||
spq.getOutputParameterValue("b"));
|
||||
assertThat(spq.getOutputParameterValue("c")).isEqualTo("z");
|
||||
|
||||
verify(query).registerStoredProcedureParameter("a", String.class, ParameterMode.OUT);
|
||||
verify(query).registerStoredProcedureParameter("b", Number.class, ParameterMode.IN);
|
||||
verify(query).registerStoredProcedureParameter("c", Object.class, ParameterMode.INOUT);
|
||||
verify(query).execute();
|
||||
verify(targetQuery).registerStoredProcedureParameter("a", String.class, ParameterMode.OUT);
|
||||
verify(targetQuery).registerStoredProcedureParameter("b", Number.class, ParameterMode.IN);
|
||||
verify(targetQuery).registerStoredProcedureParameter("c", Object.class, ParameterMode.INOUT);
|
||||
verify(targetQuery).execute();
|
||||
verify(targetEm).close();
|
||||
verifyNoMoreInteractions(query);
|
||||
verifyNoMoreInteractions(targetQuery);
|
||||
verifyNoMoreInteractions(targetEm);
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -63,7 +63,6 @@ import jakarta.servlet.http.Part;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.LinkedCaseInsensitiveMap;
|
||||
@@ -1020,7 +1019,7 @@ public class MockHttpServletRequest implements HttpServletRequest {
|
||||
}
|
||||
}
|
||||
|
||||
private static String encodeCookies(@NonNull Cookie... cookies) {
|
||||
private static String encodeCookies(Cookie... cookies) {
|
||||
return Arrays.stream(cookies)
|
||||
.map(c -> c.getName() + '=' + (c.getValue() == null ? "" : c.getValue()))
|
||||
.collect(Collectors.joining("; "));
|
||||
|
||||
+1
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -35,7 +35,6 @@ import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.test.context.TestContext;
|
||||
import org.springframework.test.context.TestContextAnnotationUtils;
|
||||
@@ -315,7 +314,6 @@ public class SqlScriptsTestExecutionListener extends AbstractTestExecutionListen
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private ResourceDatabasePopulator createDatabasePopulator(MergedSqlConfig mergedSqlConfig) {
|
||||
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
|
||||
populator.setSqlScriptEncoding(mergedSqlConfig.getEncoding());
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,7 +17,8 @@
|
||||
package org.springframework.dao;
|
||||
|
||||
/**
|
||||
* Exception thrown when an attempt to insert or update data
|
||||
* Exception thrown when an attempt to execute an SQL statement fails to map
|
||||
* the given data, typically but no limited to an insert or update data
|
||||
* results in violation of an integrity constraint. Note that this
|
||||
* is not purely a relational concept; integrity constraints such
|
||||
* as unique primary keys are required by most database types.
|
||||
|
||||
+7
-6
@@ -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.
|
||||
@@ -21,11 +21,12 @@ import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.aot.hint.TypeHint;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
|
||||
/**
|
||||
* {@link RuntimeHintsRegistrar} implementation that registers runtime hints for
|
||||
* transaction management.
|
||||
* {@link RuntimeHintsRegistrar} implementation that registers runtime hints
|
||||
* for transaction management.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 6.0
|
||||
@@ -34,9 +35,9 @@ import org.springframework.transaction.TransactionDefinition;
|
||||
class TransactionRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
||||
hints.reflection().registerTypes(TypeReference.listOf(
|
||||
Isolation.class, Propagation.class, TransactionDefinition.class),
|
||||
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
|
||||
hints.reflection().registerTypes(
|
||||
TypeReference.listOf(Isolation.class, Propagation.class, TransactionDefinition.class),
|
||||
TypeHint.builtWith(MemberCategory.DECLARED_FIELDS));
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -51,7 +51,7 @@ public class DefaultTransactionAttribute extends DefaultTransactionDefinition im
|
||||
|
||||
|
||||
/**
|
||||
* Create a new DefaultTransactionAttribute, with default settings.
|
||||
* Create a new {@code DefaultTransactionAttribute} with default settings.
|
||||
* Can be modified through bean property setters.
|
||||
* @see #setPropagationBehavior
|
||||
* @see #setIsolationLevel
|
||||
@@ -76,7 +76,7 @@ public class DefaultTransactionAttribute extends DefaultTransactionDefinition im
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new DefaultTransactionAttribute with the given
|
||||
* Create a new {@code DefaultTransactionAttribute} with the given
|
||||
* propagation behavior. Can be modified through bean property setters.
|
||||
* @param propagationBehavior one of the propagation constants in the
|
||||
* TransactionDefinition interface
|
||||
|
||||
+6
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -210,11 +210,13 @@ public abstract class AbstractReactiveTransactionManager implements ReactiveTran
|
||||
prepareSynchronization(synchronizationManager, status, definition)).thenReturn(status);
|
||||
}
|
||||
|
||||
// Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
|
||||
// PROPAGATION_REQUIRED, PROPAGATION_SUPPORTS, PROPAGATION_MANDATORY:
|
||||
// regular participation in existing transaction.
|
||||
if (debugEnabled) {
|
||||
logger.debug("Participating in existing transaction");
|
||||
}
|
||||
return Mono.just(prepareReactiveTransaction(synchronizationManager, definition, transaction, false, debugEnabled, null));
|
||||
return Mono.just(prepareReactiveTransaction(
|
||||
synchronizationManager, definition, transaction, false, debugEnabled, null));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -330,7 +332,7 @@ public abstract class AbstractReactiveTransactionManager implements ReactiveTran
|
||||
if (resourcesHolder != null) {
|
||||
Object suspendedResources = resourcesHolder.suspendedResources;
|
||||
if (suspendedResources != null) {
|
||||
resume = doResume(synchronizationManager, transaction, suspendedResources);
|
||||
resume = doResume(synchronizationManager, transaction, suspendedResources);
|
||||
}
|
||||
List<TransactionSynchronization> suspendedSynchronizations = resourcesHolder.suspendedSynchronizations;
|
||||
if (suspendedSynchronizations != null) {
|
||||
|
||||
+4
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -451,7 +451,7 @@ public abstract class AbstractPlatformTransactionManager implements PlatformTran
|
||||
if (useSavepointForNestedTransaction()) {
|
||||
// Create savepoint within existing Spring-managed transaction,
|
||||
// through the SavepointManager API implemented by TransactionStatus.
|
||||
// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
|
||||
// Usually uses JDBC savepoints. Never activates Spring synchronization.
|
||||
DefaultTransactionStatus status =
|
||||
prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
|
||||
status.createAndHoldSavepoint();
|
||||
@@ -465,7 +465,8 @@ public abstract class AbstractPlatformTransactionManager implements PlatformTran
|
||||
}
|
||||
}
|
||||
|
||||
// Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
|
||||
// PROPAGATION_REQUIRED, PROPAGATION_SUPPORTS, PROPAGATION_MANDATORY:
|
||||
// regular participation in existing transaction.
|
||||
if (debugEnabled) {
|
||||
logger.debug("Participating in existing transaction");
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -65,7 +65,7 @@ public class DefaultTransactionDefinition implements TransactionDefinition, Seri
|
||||
|
||||
|
||||
/**
|
||||
* Create a new DefaultTransactionDefinition, with default settings.
|
||||
* Create a new {@code DefaultTransactionDefinition} with default settings.
|
||||
* Can be modified through bean property setters.
|
||||
* @see #setPropagationBehavior
|
||||
* @see #setIsolationLevel
|
||||
@@ -93,7 +93,7 @@ public class DefaultTransactionDefinition implements TransactionDefinition, Seri
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new DefaultTransactionDefinition with the given
|
||||
* Create a new {@code DefaultTransactionDefinition} with the given
|
||||
* propagation behavior. Can be modified through bean property setters.
|
||||
* @param propagationBehavior one of the propagation constants in the
|
||||
* TransactionDefinition interface
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,7 +18,6 @@ package org.springframework.http;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
@@ -80,8 +79,8 @@ final class DefaultHttpStatusCode implements HttpStatusCode, Comparable<HttpStat
|
||||
|
||||
|
||||
@Override
|
||||
public int compareTo(@NonNull HttpStatusCode o) {
|
||||
return Integer.compare(this.value, o.value());
|
||||
public int compareTo(HttpStatusCode other) {
|
||||
return Integer.compare(this.value, other.value());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user