mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1827776d2e |
@@ -1,33 +0,0 @@
|
||||
name: Send notification
|
||||
description: Sends a Google Chat message as a notification of the job's outcome
|
||||
inputs:
|
||||
webhook-url:
|
||||
description: 'Google Chat Webhook URL'
|
||||
required: true
|
||||
status:
|
||||
description: 'Status of the job'
|
||||
required: true
|
||||
build-scan-url:
|
||||
description: 'URL of the build scan to include in the notification'
|
||||
run-name:
|
||||
description: 'Name of the run to include in the notification'
|
||||
default: ${{ format('{0} {1}', github.ref_name, github.job) }}
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- shell: bash
|
||||
run: |
|
||||
echo "BUILD_SCAN=${{ inputs.build-scan-url == '' && ' [build scan unavailable]' || format(' [<{0}|Build Scan>]', inputs.build-scan-url) }}" >> "$GITHUB_ENV"
|
||||
echo "RUN_URL=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> "$GITHUB_ENV"
|
||||
- shell: bash
|
||||
if: ${{ inputs.status == 'success' }}
|
||||
run: |
|
||||
curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<${{ env.RUN_URL }}|${{ inputs.run-name }}> was successful ${{ env.BUILD_SCAN }}"}' || true
|
||||
- shell: bash
|
||||
if: ${{ inputs.status == 'failure' }}
|
||||
run: |
|
||||
curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<users/all> *<${{ env.RUN_URL }}|${{ inputs.run-name }}> failed* ${{ env.BUILD_SCAN }}"}' || true
|
||||
- shell: bash
|
||||
if: ${{ inputs.status == 'cancelled' }}
|
||||
run: |
|
||||
curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<${{ env.RUN_URL }}|${{ inputs.run-name }}> was cancelled"}' || true
|
||||
@@ -1,34 +0,0 @@
|
||||
name: Backport Bot
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [labeled]
|
||||
pull_request:
|
||||
types: [labeled]
|
||||
push:
|
||||
branches:
|
||||
- '*.x'
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
build:
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'liberica'
|
||||
java-version: 17
|
||||
- name: Download BackportBot
|
||||
run: wget https://github.com/spring-io/backport-bot/releases/download/latest/backport-bot-0.0.1-SNAPSHOT.jar
|
||||
- name: Backport
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_EVENT: ${{ toJSON(github.event) }}
|
||||
run: java -jar backport-bot-0.0.1-SNAPSHOT.jar --github.accessToken="$GITHUB_TOKEN" --github.event_name "$GITHUB_EVENT_NAME" --github.event "$GITHUB_EVENT"
|
||||
@@ -1,63 +0,0 @@
|
||||
name: Build and deploy snapshot
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 5.3.x
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
jobs:
|
||||
build-and-deploy-snapshot:
|
||||
if: ${{ github.repository == 'spring-projects/spring-framework' }}
|
||||
name: Build and deploy snapshot
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'liberica'
|
||||
java-version: 8
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
|
||||
with:
|
||||
cache-read-only: false
|
||||
- name: Configure Gradle properties
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p $HOME/.gradle
|
||||
echo 'systemProp.user.name=spring-builds+github' >> $HOME/.gradle/gradle.properties
|
||||
echo 'systemProp.org.gradle.internal.launcher.welcomeMessageEnabled=false' >> $HOME/.gradle/gradle.properties
|
||||
echo 'org.gradle.daemon=false' >> $HOME/.gradle/gradle.properties
|
||||
echo 'org.gradle.daemon=4' >> $HOME/.gradle/gradle.properties
|
||||
- name: Build and publish
|
||||
id: build
|
||||
env:
|
||||
CI: 'true'
|
||||
GRADLE_ENTERPRISE_URL: 'https://ge.spring.io'
|
||||
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: |
|
||||
/**/spring-*.zip::zip.name=spring-framework,zip.deployed=false
|
||||
/**/spring-*-docs.zip::zip.type=docs
|
||||
/**/spring-*-dist.zip::zip.type=dist
|
||||
/**/spring-*-schema.zip::zip.type=schema
|
||||
- name: Send notification
|
||||
uses: ./.github/actions/send-notification
|
||||
if: always()
|
||||
with:
|
||||
webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
|
||||
status: ${{ job.status }}
|
||||
build-scan-url: ${{ steps.build.outputs.build-scan-url }}
|
||||
run-name: ${{ format('{0} | Linux | Java 8', github.ref_name) }}
|
||||
@@ -1,80 +0,0 @@
|
||||
name: CI
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 5.3.x
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
jobs:
|
||||
ci:
|
||||
if: ${{ github.repository == 'spring-projects/spring-framework' }}
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- id: ubuntu-latest
|
||||
name: Linux
|
||||
java:
|
||||
- version: 8
|
||||
toolchain: false
|
||||
- version: 17
|
||||
toolchain: true
|
||||
- version: 21
|
||||
toolchain: true
|
||||
exclude:
|
||||
- os:
|
||||
name: Linux
|
||||
java:
|
||||
version: 8
|
||||
name: '${{ matrix.os.name}} | Java ${{ matrix.java.version}}'
|
||||
runs-on: ${{ matrix.os.id }}
|
||||
steps:
|
||||
- name: Set up Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'liberica'
|
||||
java-version: |
|
||||
${{ matrix.java.version }}
|
||||
${{ matrix.java.toolchain && '8' || '' }}
|
||||
- name: Prepare Windows runner
|
||||
if: ${{ runner.os == 'Windows' }}
|
||||
run: |
|
||||
git config --global core.autocrlf true
|
||||
git config --global core.longPaths true
|
||||
Stop-Service -name Docker
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
|
||||
with:
|
||||
cache-read-only: false
|
||||
- name: Configure Gradle properties
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p $HOME/.gradle
|
||||
echo 'systemProp.user.name=spring-builds+github' >> $HOME/.gradle/gradle.properties
|
||||
echo 'systemProp.org.gradle.internal.launcher.welcomeMessageEnabled=false' >> $HOME/.gradle/gradle.properties
|
||||
echo 'org.gradle.daemon=false' >> $HOME/.gradle/gradle.properties
|
||||
echo 'org.gradle.daemon=4' >> $HOME/.gradle/gradle.properties
|
||||
- name: Configure toolchain properties
|
||||
if: ${{ matrix.java.toolchain }}
|
||||
shell: bash
|
||||
run: |
|
||||
echo toolchainVersion=${{ matrix.java.version }} >> $HOME/.gradle/gradle.properties
|
||||
echo systemProp.org.gradle.java.installations.auto-detect=false >> $HOME/.gradle/gradle.properties
|
||||
echo systemProp.org.gradle.java.installations.auto-download=false >> $HOME/.gradle/gradle.properties
|
||||
echo systemProp.org.gradle.java.installations.paths=${{ format('$JAVA_HOME_{0}_X64', matrix.java.version) }} >> $HOME/.gradle/gradle.properties
|
||||
- name: Build
|
||||
id: build
|
||||
env:
|
||||
CI: 'true'
|
||||
GRADLE_ENTERPRISE_URL: 'https://ge.spring.io'
|
||||
DEVELOCITY_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||
run: ./gradlew check
|
||||
- name: Send notification
|
||||
uses: ./.github/actions/send-notification
|
||||
if: always()
|
||||
with:
|
||||
webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
|
||||
status: ${{ job.status }}
|
||||
build-scan-url: ${{ steps.build.outputs.build-scan-url }}
|
||||
run-name: ${{ format('{0} | {1} | Java {2}', github.ref_name, matrix.os.name, matrix.java.version) }}
|
||||
@@ -9,5 +9,5 @@ jobs:
|
||||
name: "Validation"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: gradle/wrapper-validation-action@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: gradle/wrapper-validation-action@v1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# <img src="framework-docs/src/docs/spring-framework.png" width="80" height="80"> Spring Framework [](https://github.com/spring-projects/spring-framework/actions/workflows/build-and-deploy-snapshot.yml?query=branch%3A5.3.x) [](https://ge.spring.io/scans?search.rootProjectNames=spring)
|
||||
# <img src="src/docs/spring-framework.png" width="80" height="80"> Spring Framework [](https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-5.3.x?groups=Build") [](https://ge.spring.io/scans?search.rootProjectNames=spring)
|
||||
|
||||
This is the home of the Spring Framework: the foundation for all [Spring projects](https://spring.io/projects). Collectively the Spring Framework and the family of Spring projects are often referred to simply as "Spring".
|
||||
|
||||
|
||||
+6
-8
@@ -28,8 +28,8 @@ configure(allprojects) { project ->
|
||||
dependencyManagement {
|
||||
imports {
|
||||
mavenBom "com.fasterxml.jackson:jackson-bom:2.12.7"
|
||||
mavenBom "io.netty:netty-bom:4.1.111.Final"
|
||||
mavenBom "io.projectreactor:reactor-bom:2020.0.45"
|
||||
mavenBom "io.netty:netty-bom:4.1.107.Final"
|
||||
mavenBom "io.projectreactor:reactor-bom:2020.0.41"
|
||||
mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR13"
|
||||
mavenBom "io.rsocket:rsocket-bom:1.1.3"
|
||||
mavenBom "org.eclipse.jetty:jetty-bom:9.4.54.v20240208"
|
||||
@@ -362,7 +362,7 @@ configure([rootProject] + javaProjects) { project ->
|
||||
// JSR-305 only used for non-required meta-annotations
|
||||
compileOnly("com.google.code.findbugs:jsr305")
|
||||
testCompileOnly("com.google.code.findbugs:jsr305")
|
||||
checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:0.0.42")
|
||||
checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:0.0.41")
|
||||
}
|
||||
|
||||
ext.javadocLinks = [
|
||||
@@ -375,9 +375,8 @@ configure([rootProject] + javaProjects) { project ->
|
||||
"https://tiles.apache.org/tiles-request/apidocs/",
|
||||
"https://tiles.apache.org/framework/apidocs/",
|
||||
"https://www.eclipse.org/aspectj/doc/released/aspectj5rt-api/",
|
||||
// Temporarily commenting out Ehcache and Quartz since javadoc on JDK 8 cannot access them.
|
||||
// "https://www.ehcache.org/apidocs/2.10.4/",
|
||||
// "https://www.quartz-scheduler.org/api/2.3.0/",
|
||||
"https://www.ehcache.org/apidocs/2.10.4/",
|
||||
"https://www.quartz-scheduler.org/api/2.3.0/",
|
||||
"https://www.javadoc.io/doc/com.fasterxml.jackson.core/jackson-core/2.12.7/",
|
||||
"https://www.javadoc.io/doc/com.fasterxml.jackson.core/jackson-databind/2.12.7/",
|
||||
"https://www.javadoc.io/doc/com.fasterxml.jackson.dataformat/jackson-dataformat-xml/2.12.7/",
|
||||
@@ -389,8 +388,7 @@ configure([rootProject] + javaProjects) { project ->
|
||||
// "https://junit.org/junit5/docs/5.8.2/api/",
|
||||
"https://www.reactive-streams.org/reactive-streams-1.0.3-javadoc/",
|
||||
"https://javadoc.io/static/io.rsocket/rsocket-core/1.1.1/",
|
||||
// Temporarily commenting out R2DBC since javadoc on JDK 8 cannot access it.
|
||||
// "https://r2dbc.io/spec/0.8.5.RELEASE/api/",
|
||||
"https://r2dbc.io/spec/0.8.5.RELEASE/api/",
|
||||
// The external Javadoc link for JSR 305 must come last to ensure that types from
|
||||
// JSR 250 (such as @PostConstruct) are still supported. This is due to the fact
|
||||
// that JSR 250 and JSR 305 both define types in javax.annotation, which results
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
== Spring Framework Concourse pipeline
|
||||
|
||||
NOTE: CI is being migrated to GitHub Actions.
|
||||
|
||||
The Spring Framework uses https://concourse-ci.org/[Concourse] for its CI build and other automated tasks.
|
||||
The Spring team has a dedicated Concourse instance available at https://ci.spring.io with a build pipeline
|
||||
for https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-5.3.x[Spring Framework 5.3.x].
|
||||
|
||||
@@ -17,4 +17,4 @@ changelog:
|
||||
- "type: dependency-upgrade"
|
||||
contributors:
|
||||
exclude:
|
||||
names: ["bclozel", "jhoeller", "poutsma", "rstoyanchev", "sbrannen", "sdeleuze", "snicoll", "simonbasle"]
|
||||
names: ["bclozel", "jhoeller", "poutsma", "rstoyanchev", "sbrannen", "sdeleuze", "snicoll"]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM ubuntu:jammy-20240125
|
||||
FROM ubuntu:jammy-20240111
|
||||
|
||||
ADD setup.sh /setup.sh
|
||||
ADD get-jdk-url.sh /get-jdk-url.sh
|
||||
|
||||
@@ -8,3 +8,4 @@ milestone: "5.3.x"
|
||||
build-name: "spring-framework"
|
||||
pipeline-name: "spring-framework"
|
||||
concourse-url: "https://ci.spring.io"
|
||||
task-timeout: 1h00m
|
||||
|
||||
+122
-14
@@ -5,7 +5,9 @@ anchors:
|
||||
password: ((github-ci-release-token))
|
||||
branch: ((branch))
|
||||
gradle-enterprise-task-params: &gradle-enterprise-task-params
|
||||
DEVELOCITY_ACCESS_KEY: ((gradle_enterprise_secret_access_key))
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY: ((gradle_enterprise_secret_access_key))
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME: ((gradle_enterprise_cache_user.username))
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD: ((gradle_enterprise_cache_user.password))
|
||||
sonatype-task-params: &sonatype-task-params
|
||||
SONATYPE_USERNAME: ((sonatype-username))
|
||||
SONATYPE_PASSWORD: ((sonatype-password))
|
||||
@@ -21,6 +23,14 @@ 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
|
||||
@@ -35,7 +45,7 @@ resource_types:
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: concourse/registry-image-resource
|
||||
tag: 1.8.0
|
||||
tag: 1.7.1
|
||||
- name: artifactory-resource
|
||||
type: registry-image
|
||||
source:
|
||||
@@ -54,12 +64,25 @@ resource_types:
|
||||
<<: *docker-resource-source
|
||||
repository: dpb587/github-status-resource
|
||||
tag: master
|
||||
- name: slack-notification
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: cfcommunity/slack-notification-resource
|
||||
tag: latest
|
||||
resources:
|
||||
- name: git-repo
|
||||
type: git
|
||||
icon: github
|
||||
source:
|
||||
<<: *git-repo-resource-source
|
||||
- name: every-morning
|
||||
type: time
|
||||
icon: alarm
|
||||
source:
|
||||
start: 8:00 AM
|
||||
stop: 9:00 AM
|
||||
location: Europe/Vienna
|
||||
- name: ci-images-git-repo
|
||||
type: git
|
||||
icon: github
|
||||
@@ -82,6 +105,27 @@ resources:
|
||||
username: ((artifactory-username))
|
||||
password: ((artifactory-password))
|
||||
build_name: ((build-name))
|
||||
- name: repo-status-build
|
||||
type: github-status-resource
|
||||
icon: eye-check-outline
|
||||
source:
|
||||
repository: ((github-repo-name))
|
||||
access_token: ((github-ci-status-token))
|
||||
branch: ((branch))
|
||||
context: build
|
||||
- name: repo-status-jdk17-build
|
||||
type: github-status-resource
|
||||
icon: eye-check-outline
|
||||
source:
|
||||
repository: ((github-repo-name))
|
||||
access_token: ((github-ci-status-token))
|
||||
branch: ((branch))
|
||||
context: jdk17-build
|
||||
- name: slack-alert
|
||||
type: slack-notification
|
||||
icon: slack
|
||||
source:
|
||||
url: ((slack-webhook-url))
|
||||
- name: github-pre-release
|
||||
type: github-release
|
||||
icon: briefcase-download-outline
|
||||
@@ -116,23 +160,37 @@ jobs:
|
||||
- put: ci-image
|
||||
params:
|
||||
image: ci-image/image.tar
|
||||
- name: stage-milestone
|
||||
- name: build
|
||||
serial: true
|
||||
public: 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
|
||||
trigger: true
|
||||
- put: repo-status-build
|
||||
params: { state: "pending", commit: "git-repo" }
|
||||
- do:
|
||||
- task: build-project
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/build-project.yml
|
||||
privileged: true
|
||||
timeout: ((task-timeout))
|
||||
params:
|
||||
<<: *build-project-task-params
|
||||
on_failure:
|
||||
do:
|
||||
- put: repo-status-build
|
||||
params: { state: "failure", commit: "git-repo" }
|
||||
- put: slack-alert
|
||||
params:
|
||||
<<: *slack-fail-params
|
||||
- put: repo-status-build
|
||||
params: { state: "success", commit: "git-repo" }
|
||||
- put: artifactory-repo
|
||||
params: &artifactory-params
|
||||
signing_key: ((signing-key))
|
||||
signing_passphrase: ((signing-passphrase))
|
||||
repo: libs-staging-local
|
||||
repo: libs-snapshot-local
|
||||
folder: distribution-repository
|
||||
build_uri: "https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}"
|
||||
build_number: "${BUILD_PIPELINE_NAME}-${BUILD_JOB_NAME}-${BUILD_NAME}"
|
||||
@@ -157,9 +215,55 @@ jobs:
|
||||
- "/**/spring-*-schema.zip"
|
||||
properties:
|
||||
"zip.type": "schema"
|
||||
- put: git-repo
|
||||
params:
|
||||
repository: stage-git-repo
|
||||
get_params:
|
||||
threads: 8
|
||||
- name: jdk17-build
|
||||
serial: true
|
||||
public: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
- get: every-morning
|
||||
trigger: true
|
||||
- put: repo-status-jdk17-build
|
||||
params: { state: "pending", commit: "git-repo" }
|
||||
- do:
|
||||
- task: check-project
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/check-project.yml
|
||||
privileged: true
|
||||
timeout: ((task-timeout))
|
||||
params:
|
||||
TEST_TOOLCHAIN: 17
|
||||
<<: *build-project-task-params
|
||||
on_failure:
|
||||
do:
|
||||
- put: repo-status-jdk17-build
|
||||
params: { state: "failure", commit: "git-repo" }
|
||||
- put: slack-alert
|
||||
params:
|
||||
<<: *slack-fail-params
|
||||
- put: repo-status-jdk17-build
|
||||
params: { state: "success", commit: "git-repo" }
|
||||
- name: stage-milestone
|
||||
serial: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
trigger: false
|
||||
- task: stage
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/stage-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: M
|
||||
<<: *gradle-enterprise-task-params
|
||||
- put: artifactory-repo
|
||||
params:
|
||||
<<: *artifactory-params
|
||||
repo: libs-staging-local
|
||||
- put: git-repo
|
||||
params:
|
||||
repository: stage-git-repo
|
||||
- name: promote-milestone
|
||||
serial: true
|
||||
plan:
|
||||
@@ -200,6 +304,7 @@ jobs:
|
||||
- put: artifactory-repo
|
||||
params:
|
||||
<<: *artifactory-params
|
||||
repo: libs-staging-local
|
||||
- put: git-repo
|
||||
params:
|
||||
repository: stage-git-repo
|
||||
@@ -243,6 +348,7 @@ jobs:
|
||||
- put: artifactory-repo
|
||||
params:
|
||||
<<: *artifactory-params
|
||||
repo: libs-staging-local
|
||||
- put: git-repo
|
||||
params:
|
||||
repository: stage-git-repo
|
||||
@@ -285,6 +391,8 @@ jobs:
|
||||
<<: *changelog-task-params
|
||||
|
||||
groups:
|
||||
- name: "builds"
|
||||
jobs: ["build", "jdk17-build"]
|
||||
- name: "releases"
|
||||
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release"]
|
||||
- name: "ci-images"
|
||||
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
source $(dirname $0)/common.sh
|
||||
|
||||
pushd git-repo > /dev/null
|
||||
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --max-workers=4 check
|
||||
popd > /dev/null
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
source $(dirname $0)/common.sh
|
||||
repository=$(pwd)/distribution-repository
|
||||
|
||||
pushd git-repo > /dev/null
|
||||
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --max-workers=4 -PdeploymentRepository=${repository} build publishAllPublicationsToDeploymentRepository
|
||||
popd > /dev/null
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
source $(dirname $0)/common.sh
|
||||
|
||||
pushd git-repo > /dev/null
|
||||
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK17 \
|
||||
-PmainToolchain=${MAIN_TOOLCHAIN} -PtestToolchain=${TEST_TOOLCHAIN} --no-daemon --max-workers=4 check
|
||||
popd > /dev/null
|
||||
@@ -1,5 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
source $(dirname $0)/common.sh
|
||||
CONFIG_DIR=git-repo/ci/config
|
||||
|
||||
version=$( cat artifactory-repo/build-info.json | jq -r '.buildInfo.modules[0].id' | sed 's/.*:.*:\(.*\)/\1/' )
|
||||
|
||||
@@ -26,5 +26,4 @@ run:
|
||||
cat > /root/.docker/config.json <<EOF
|
||||
{ "auths": { "https://index.docker.io/v1/": { "auth": "$DOCKER_HUB_AUTH" }}}
|
||||
EOF
|
||||
build
|
||||
|
||||
build
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
platform: linux
|
||||
inputs:
|
||||
- name: git-repo
|
||||
caches:
|
||||
- path: gradle
|
||||
params:
|
||||
BRANCH:
|
||||
CI: true
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY:
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME:
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD:
|
||||
GRADLE_ENTERPRISE_URL: https://ge.spring.io
|
||||
run:
|
||||
path: bash
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
${PWD}/git-repo/ci/scripts/build-pr.sh
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
platform: linux
|
||||
inputs:
|
||||
- name: git-repo
|
||||
outputs:
|
||||
- name: distribution-repository
|
||||
- name: git-repo
|
||||
caches:
|
||||
- path: gradle
|
||||
params:
|
||||
BRANCH:
|
||||
CI: true
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY:
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME:
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD:
|
||||
GRADLE_ENTERPRISE_URL: https://ge.spring.io
|
||||
run:
|
||||
path: bash
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
${PWD}/git-repo/ci/scripts/build-project.sh
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
platform: linux
|
||||
inputs:
|
||||
- name: git-repo
|
||||
outputs:
|
||||
- name: distribution-repository
|
||||
- name: git-repo
|
||||
caches:
|
||||
- path: gradle
|
||||
params:
|
||||
BRANCH:
|
||||
CI: true
|
||||
MAIN_TOOLCHAIN:
|
||||
TEST_TOOLCHAIN:
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY:
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME:
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD:
|
||||
GRADLE_ENTERPRISE_URL: https://ge.spring.io
|
||||
run:
|
||||
path: bash
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
${PWD}/git-repo/ci/scripts/check-project.sh
|
||||
@@ -4,7 +4,7 @@ image_resource:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: springio/github-changelog-generator
|
||||
tag: '0.0.8'
|
||||
tag: '0.0.7'
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
inputs:
|
||||
|
||||
@@ -4,7 +4,7 @@ image_resource:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: springio/concourse-release-scripts
|
||||
tag: '0.4.0'
|
||||
tag: '0.3.4'
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
inputs:
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
version=5.3.37
|
||||
version=5.3.32
|
||||
org.gradle.jvmargs=-Xmx2048m
|
||||
org.gradle.caching=true
|
||||
org.gradle.parallel=true
|
||||
|
||||
@@ -5,7 +5,7 @@ tasks.findByName("dokkaHtmlPartial")?.configure {
|
||||
sourceRoots.setFrom(file("src/main/kotlin"))
|
||||
classpath.from(sourceSets["main"].runtimeClasspath)
|
||||
externalDocumentationLink {
|
||||
url.set(new URL("https://docs.spring.io/spring-framework/docs/5.3.x/javadoc-api/"))
|
||||
url.set(new URL("https://docs.spring.io/spring-framework/docs/current/javadoc-api/"))
|
||||
}
|
||||
externalDocumentationLink {
|
||||
url.set(new URL("https://projectreactor.io/docs/core/release/api/"))
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@ class EnableCachingIntegrationTests {
|
||||
// attempt was made to look up the AJ aspect. It's due to classpath issues
|
||||
// in .integration-tests that it's not found.
|
||||
assertThatException().isThrownBy(ctx::refresh)
|
||||
.withMessageContaining("AspectJCachingConfiguration");
|
||||
.withMessageContaining("AspectJCachingConfiguration");
|
||||
}
|
||||
|
||||
|
||||
|
||||
Vendored
+2
-1
@@ -561,7 +561,8 @@ public class EnvironmentSystemIntegrationTests {
|
||||
{
|
||||
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.getEnvironment().setRequiredProperties("foo", "bar");
|
||||
assertThatExceptionOfType(MissingRequiredPropertiesException.class).isThrownBy(ctx::refresh);
|
||||
assertThatExceptionOfType(MissingRequiredPropertiesException.class).isThrownBy(
|
||||
ctx::refresh);
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
+1
-1
@@ -97,7 +97,7 @@ class EnableTransactionManagementIntegrationTests {
|
||||
ctx.register(Config.class, AspectJTxConfig.class);
|
||||
// this test is a bit fragile, but gets the job done, proving that an
|
||||
// attempt was made to look up the AJ aspect. It's due to classpath issues
|
||||
// in integration-tests that it's not found.
|
||||
// in .integration-tests that it's not found.
|
||||
assertThatException()
|
||||
.isThrownBy(ctx::refresh)
|
||||
.withMessageContaining("AspectJJtaTransactionManagementConfiguration");
|
||||
|
||||
+3
-3
@@ -7,8 +7,8 @@ pluginManagement {
|
||||
}
|
||||
|
||||
plugins {
|
||||
id "com.gradle.develocity" version "3.17.2"
|
||||
id "io.spring.ge.conventions" version "0.0.17"
|
||||
id "com.gradle.enterprise" version "3.12.1"
|
||||
id "io.spring.ge.conventions" version "0.0.13"
|
||||
}
|
||||
|
||||
include "spring-aop"
|
||||
@@ -42,7 +42,7 @@ rootProject.children.each {project ->
|
||||
}
|
||||
|
||||
settings.gradle.projectsLoaded {
|
||||
develocity {
|
||||
gradleEnterprise {
|
||||
buildScan {
|
||||
File buildDir = settings.gradle.rootProject.getBuildDir()
|
||||
buildDir.mkdirs()
|
||||
|
||||
+11
-46
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,7 +18,6 @@ package org.springframework.aop.aspectj;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Arrays;
|
||||
@@ -43,7 +42,6 @@ import org.aspectj.weaver.tools.PointcutParameter;
|
||||
import org.aspectj.weaver.tools.PointcutParser;
|
||||
import org.aspectj.weaver.tools.PointcutPrimitive;
|
||||
import org.aspectj.weaver.tools.ShadowMatch;
|
||||
import org.aspectj.weaver.tools.UnsupportedPointcutPrimitiveException;
|
||||
|
||||
import org.springframework.aop.ClassFilter;
|
||||
import org.springframework.aop.IntroductionAwareMethodMatcher;
|
||||
@@ -87,8 +85,6 @@ import org.springframework.util.StringUtils;
|
||||
public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
implements ClassFilter, IntroductionAwareMethodMatcher, BeanFactoryAware {
|
||||
|
||||
private static final String AJC_MAGIC = "ajc$";
|
||||
|
||||
private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = new HashSet<>();
|
||||
|
||||
static {
|
||||
@@ -110,8 +106,6 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
@Nullable
|
||||
private Class<?> pointcutDeclarationScope;
|
||||
|
||||
private boolean aspectCompiledByAjc;
|
||||
|
||||
private String[] pointcutParameterNames = new String[0];
|
||||
|
||||
private Class<?>[] pointcutParameterTypes = new Class<?>[0];
|
||||
@@ -125,8 +119,6 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
@Nullable
|
||||
private transient PointcutExpression pointcutExpression;
|
||||
|
||||
private transient boolean pointcutParsingFailed = false;
|
||||
|
||||
private transient Map<Method, ShadowMatch> shadowMatchCache = new ConcurrentHashMap<>(32);
|
||||
|
||||
|
||||
@@ -143,7 +135,7 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
* @param paramTypes the parameter types for the pointcut
|
||||
*/
|
||||
public AspectJExpressionPointcut(Class<?> declarationScope, String[] paramNames, Class<?>[] paramTypes) {
|
||||
setPointcutDeclarationScope(declarationScope);
|
||||
this.pointcutDeclarationScope = declarationScope;
|
||||
if (paramNames.length != paramTypes.length) {
|
||||
throw new IllegalStateException(
|
||||
"Number of pointcut parameter names must match number of pointcut parameter types");
|
||||
@@ -158,7 +150,6 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
*/
|
||||
public void setPointcutDeclarationScope(Class<?> pointcutDeclarationScope) {
|
||||
this.pointcutDeclarationScope = pointcutDeclarationScope;
|
||||
this.aspectCompiledByAjc = compiledByAjc(pointcutDeclarationScope);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -183,30 +174,25 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
|
||||
@Override
|
||||
public ClassFilter getClassFilter() {
|
||||
checkExpression();
|
||||
obtainPointcutExpression();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodMatcher getMethodMatcher() {
|
||||
checkExpression();
|
||||
obtainPointcutExpression();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check whether this pointcut is ready to match.
|
||||
* Check whether this pointcut is ready to match,
|
||||
* lazily building the underlying AspectJ pointcut expression.
|
||||
*/
|
||||
private void checkExpression() {
|
||||
private PointcutExpression obtainPointcutExpression() {
|
||||
if (getExpression() == null) {
|
||||
throw new IllegalStateException("Must set property 'expression' before attempting to match");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazily build the underlying AspectJ pointcut expression.
|
||||
*/
|
||||
private PointcutExpression obtainPointcutExpression() {
|
||||
if (this.pointcutExpression == null) {
|
||||
this.pointcutClassLoader = determinePointcutClassLoader();
|
||||
this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader);
|
||||
@@ -283,18 +269,10 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
|
||||
@Override
|
||||
public boolean matches(Class<?> targetClass) {
|
||||
if (this.pointcutParsingFailed) {
|
||||
// Pointcut parsing failed before below -> avoid trying again.
|
||||
return false;
|
||||
}
|
||||
if (this.aspectCompiledByAjc && compiledByAjc(targetClass)) {
|
||||
// ajc-compiled aspect class for ajc-compiled target class -> already weaved.
|
||||
return false;
|
||||
}
|
||||
|
||||
PointcutExpression pointcutExpression = obtainPointcutExpression();
|
||||
try {
|
||||
try {
|
||||
return obtainPointcutExpression().couldMatchJoinPointsInType(targetClass);
|
||||
return pointcutExpression.couldMatchJoinPointsInType(targetClass);
|
||||
}
|
||||
catch (ReflectionWorldException ex) {
|
||||
logger.debug("PointcutExpression matching rejected target class - trying fallback expression", ex);
|
||||
@@ -305,12 +283,6 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IllegalArgumentException | IllegalStateException | UnsupportedPointcutPrimitiveException ex) {
|
||||
this.pointcutParsingFailed = true;
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Pointcut parser rejected expression [" + getExpression() + "]: " + ex);
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
logger.debug("PointcutExpression matching rejected target class", ex);
|
||||
}
|
||||
@@ -319,6 +291,7 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
|
||||
@Override
|
||||
public boolean matches(Method method, Class<?> targetClass, boolean hasIntroductions) {
|
||||
obtainPointcutExpression();
|
||||
ShadowMatch shadowMatch = getTargetShadowMatch(method, targetClass);
|
||||
|
||||
// Special handling for this, target, @this, @target, @annotation
|
||||
@@ -356,6 +329,7 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
|
||||
@Override
|
||||
public boolean matches(Method method, Class<?> targetClass, Object... args) {
|
||||
obtainPointcutExpression();
|
||||
ShadowMatch shadowMatch = getTargetShadowMatch(method, targetClass);
|
||||
|
||||
// Bind Spring AOP proxy to AspectJ "this" and Spring AOP target to AspectJ target,
|
||||
@@ -543,15 +517,6 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
return shadowMatch;
|
||||
}
|
||||
|
||||
private static boolean compiledByAjc(Class<?> clazz) {
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
if (field.getName().startsWith(AJC_MAGIC)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object other) {
|
||||
|
||||
+31
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,6 +18,7 @@ package org.springframework.aop.aspectj.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashMap;
|
||||
@@ -56,6 +57,8 @@ import org.springframework.lang.Nullable;
|
||||
*/
|
||||
public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFactory {
|
||||
|
||||
private static final String AJC_MAGIC = "ajc$";
|
||||
|
||||
private static final Class<?>[] ASPECTJ_ANNOTATION_CLASSES = new Class<?>[] {
|
||||
Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class};
|
||||
|
||||
@@ -66,11 +69,37 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac
|
||||
protected final ParameterNameDiscoverer parameterNameDiscoverer = new AspectJAnnotationParameterNameDiscoverer();
|
||||
|
||||
|
||||
/**
|
||||
* We consider something to be an AspectJ aspect suitable for use by the Spring AOP system
|
||||
* if it has the @Aspect annotation, and was not compiled by ajc. The reason for this latter test
|
||||
* is that aspects written in the code-style (AspectJ language) also have the annotation present
|
||||
* when compiled by ajc with the -1.5 flag, yet they cannot be consumed by Spring AOP.
|
||||
*/
|
||||
@Override
|
||||
public boolean isAspect(Class<?> clazz) {
|
||||
return (hasAspectAnnotation(clazz) && !compiledByAjc(clazz));
|
||||
}
|
||||
|
||||
private boolean hasAspectAnnotation(Class<?> clazz) {
|
||||
return (AnnotationUtils.findAnnotation(clazz, Aspect.class) != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* We need to detect this as "code-style" AspectJ aspects should not be
|
||||
* interpreted by Spring AOP.
|
||||
*/
|
||||
private boolean compiledByAjc(Class<?> clazz) {
|
||||
// The AJTypeSystem goes to great lengths to provide a uniform appearance between code-style and
|
||||
// annotation-style aspects. Therefore there is no 'clean' way to tell them apart. Here we rely on
|
||||
// an implementation detail of the AspectJ compiler.
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
if (field.getName().startsWith(AJC_MAGIC)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(Class<?> aspectClass) throws AopConfigException {
|
||||
// If the parent has the annotation and isn't abstract it's an error
|
||||
@@ -95,7 +124,6 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find and return the first AspectJ annotation on the given method
|
||||
* (there <i>should</i> only be one anyway...).
|
||||
@@ -135,7 +163,7 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac
|
||||
|
||||
|
||||
/**
|
||||
* Class modeling an AspectJ annotation, exposing its type enumeration and
|
||||
* Class modelling an AspectJ annotation, exposing its type enumeration and
|
||||
* pointcut String.
|
||||
* @param <A> the annotation type
|
||||
*/
|
||||
|
||||
+5
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -126,16 +126,10 @@ public class AspectMetadata implements Serializable {
|
||||
* Extract contents from String of form {@code pertarget(contents)}.
|
||||
*/
|
||||
private String findPerClause(Class<?> aspectClass) {
|
||||
Aspect ann = aspectClass.getAnnotation(Aspect.class);
|
||||
if (ann == null) {
|
||||
return "";
|
||||
}
|
||||
String value = ann.value();
|
||||
int beginIndex = value.indexOf('(');
|
||||
if (beginIndex < 0) {
|
||||
return "";
|
||||
}
|
||||
return value.substring(beginIndex + 1, value.length() - 1);
|
||||
String str = aspectClass.getAnnotation(Aspect.class).value();
|
||||
int beginIndex = str.indexOf('(') + 1;
|
||||
int endIndex = str.length() - 1;
|
||||
return str.substring(beginIndex, endIndex);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+19
-31
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,12 +22,9 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.aspectj.lang.reflect.PerClauseKind;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.aop.framework.AopConfigException;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -43,8 +40,6 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class BeanFactoryAspectJAdvisorsBuilder {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(BeanFactoryAspectJAdvisorsBuilder.class);
|
||||
|
||||
private final ListableBeanFactory beanFactory;
|
||||
|
||||
private final AspectJAdvisorFactory advisorFactory;
|
||||
@@ -107,37 +102,30 @@ public class BeanFactoryAspectJAdvisorsBuilder {
|
||||
continue;
|
||||
}
|
||||
if (this.advisorFactory.isAspect(beanType)) {
|
||||
try {
|
||||
AspectMetadata amd = new AspectMetadata(beanType, beanName);
|
||||
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
|
||||
MetadataAwareAspectInstanceFactory factory =
|
||||
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
|
||||
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
|
||||
if (this.beanFactory.isSingleton(beanName)) {
|
||||
this.advisorsCache.put(beanName, classAdvisors);
|
||||
}
|
||||
else {
|
||||
this.aspectFactoryCache.put(beanName, factory);
|
||||
}
|
||||
advisors.addAll(classAdvisors);
|
||||
aspectNames.add(beanName);
|
||||
AspectMetadata amd = new AspectMetadata(beanType, beanName);
|
||||
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
|
||||
MetadataAwareAspectInstanceFactory factory =
|
||||
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
|
||||
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
|
||||
if (this.beanFactory.isSingleton(beanName)) {
|
||||
this.advisorsCache.put(beanName, classAdvisors);
|
||||
}
|
||||
else {
|
||||
// Per target or per this.
|
||||
if (this.beanFactory.isSingleton(beanName)) {
|
||||
throw new IllegalArgumentException("Bean with name '" + beanName +
|
||||
"' is a singleton, but aspect instantiation model is not singleton");
|
||||
}
|
||||
MetadataAwareAspectInstanceFactory factory =
|
||||
new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
|
||||
this.aspectFactoryCache.put(beanName, factory);
|
||||
advisors.addAll(this.advisorFactory.getAdvisors(factory));
|
||||
}
|
||||
aspectNames.add(beanName);
|
||||
advisors.addAll(classAdvisors);
|
||||
}
|
||||
catch (IllegalArgumentException | IllegalStateException | AopConfigException ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Ignoring incompatible aspect [" + beanType.getName() + "]: " + ex);
|
||||
else {
|
||||
// Per target or per this.
|
||||
if (this.beanFactory.isSingleton(beanName)) {
|
||||
throw new IllegalArgumentException("Bean with name '" + beanName +
|
||||
"' is a singleton, but aspect instantiation model is not singleton");
|
||||
}
|
||||
MetadataAwareAspectInstanceFactory factory =
|
||||
new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
|
||||
this.aspectFactoryCache.put(beanName, factory);
|
||||
advisors.addAll(this.advisorFactory.getAdvisors(factory));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+14
-25
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -50,7 +50,6 @@ import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.converter.ConvertingComparator;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.ReflectionUtils.MethodFilter;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -134,19 +133,17 @@ public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFacto
|
||||
|
||||
List<Advisor> advisors = new ArrayList<>();
|
||||
for (Method method : getAdvisorMethods(aspectClass)) {
|
||||
if (method.equals(ClassUtils.getMostSpecificMethod(method, aspectClass))) {
|
||||
// Prior to Spring Framework 5.2.7, advisors.size() was supplied as the declarationOrderInAspect
|
||||
// to getAdvisor(...) to represent the "current position" in the declared methods list.
|
||||
// However, since Java 7 the "current position" is not valid since the JDK no longer
|
||||
// returns declared methods in the order in which they are declared in the source code.
|
||||
// Thus, we now hard code the declarationOrderInAspect to 0 for all advice methods
|
||||
// discovered via reflection in order to support reliable advice ordering across JVM launches.
|
||||
// Specifically, a value of 0 aligns with the default value used in
|
||||
// AspectJPrecedenceComparator.getAspectDeclarationOrder(Advisor).
|
||||
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
|
||||
if (advisor != null) {
|
||||
advisors.add(advisor);
|
||||
}
|
||||
// Prior to Spring Framework 5.2.7, advisors.size() was supplied as the declarationOrderInAspect
|
||||
// to getAdvisor(...) to represent the "current position" in the declared methods list.
|
||||
// However, since Java 7 the "current position" is not valid since the JDK no longer
|
||||
// returns declared methods in the order in which they are declared in the source code.
|
||||
// Thus, we now hard code the declarationOrderInAspect to 0 for all advice methods
|
||||
// discovered via reflection in order to support reliable advice ordering across JVM launches.
|
||||
// Specifically, a value of 0 aligns with the default value used in
|
||||
// AspectJPrecedenceComparator.getAspectDeclarationOrder(Advisor).
|
||||
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
|
||||
if (advisor != null) {
|
||||
advisors.add(advisor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,16 +210,8 @@ public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFacto
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
|
||||
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
|
||||
}
|
||||
catch (IllegalArgumentException | IllegalStateException ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Ignoring incompatible advice method: " + candidateAdviceMethod, ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
|
||||
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -44,8 +44,7 @@ import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Base class for AOP proxy configuration managers.
|
||||
*
|
||||
* <p>These are not themselves AOP proxies, but subclasses of this class are
|
||||
* These are not themselves AOP proxies, but subclasses of this class are
|
||||
* normally factories from which AOP proxy instances are obtained directly.
|
||||
*
|
||||
* <p>This class frees subclasses of the housekeeping of Advices
|
||||
@@ -53,8 +52,7 @@ import org.springframework.util.CollectionUtils;
|
||||
* methods, which are provided by subclasses.
|
||||
*
|
||||
* <p>This class is serializable; subclasses need not be.
|
||||
*
|
||||
* <p>This class is used to hold snapshots of proxies.
|
||||
* This class is used to hold snapshots of proxies.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
@@ -106,7 +104,7 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@code AdvisedSupport} instance with the given parameters.
|
||||
* Create a AdvisedSupport instance with the given parameters.
|
||||
* @param interfaces the proxied interfaces
|
||||
*/
|
||||
public AdvisedSupport(Class<?>... interfaces) {
|
||||
@@ -117,7 +115,7 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
|
||||
/**
|
||||
* Set the given object as target.
|
||||
* <p>Will create a SingletonTargetSource for the object.
|
||||
* Will create a SingletonTargetSource for the object.
|
||||
* @see #setTargetSource
|
||||
* @see org.springframework.aop.target.SingletonTargetSource
|
||||
*/
|
||||
@@ -346,7 +344,8 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
private void validateIntroductionAdvisor(IntroductionAdvisor advisor) {
|
||||
advisor.validateInterfaces();
|
||||
// If the advisor passed validation, we can make the change.
|
||||
for (Class<?> ifc : advisor.getInterfaces()) {
|
||||
Class<?>[] ifcs = advisor.getInterfaces();
|
||||
for (Class<?> ifc : ifcs) {
|
||||
addInterface(ifc);
|
||||
}
|
||||
}
|
||||
@@ -492,9 +491,9 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the AOP configuration from the given {@link AdvisedSupport} object,
|
||||
* but allow substitution of a fresh {@link TargetSource} and a given interceptor chain.
|
||||
* @param other the {@code AdvisedSupport} object to take proxy configuration from
|
||||
* Copy the AOP configuration from the given AdvisedSupport object,
|
||||
* but allow substitution of a fresh TargetSource and a given interceptor chain.
|
||||
* @param other the AdvisedSupport object to take proxy configuration from
|
||||
* @param targetSource the new TargetSource
|
||||
* @param advisors the Advisors for the chain
|
||||
*/
|
||||
@@ -514,8 +513,8 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a configuration-only copy of this {@link AdvisedSupport},
|
||||
* replacing the {@link TargetSource}.
|
||||
* Build a configuration-only copy of this AdvisedSupport,
|
||||
* replacing the TargetSource.
|
||||
*/
|
||||
AdvisedSupport getConfigurationOnlyCopy() {
|
||||
AdvisedSupport copy = new AdvisedSupport();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -186,10 +186,10 @@ public abstract class AopUtils {
|
||||
* this method resolves bridge methods in order to retrieve attributes from
|
||||
* the <i>original</i> method definition.
|
||||
* @param method the method to be invoked, which may come from an interface
|
||||
* @param targetClass the target class for the current invocation
|
||||
* (can be {@code null} or may not even implement the method)
|
||||
* @param targetClass the target class for the current invocation.
|
||||
* May be {@code null} or may not even implement the method.
|
||||
* @return the specific target method, or the original method if the
|
||||
* {@code targetClass} does not implement it
|
||||
* {@code targetClass} doesn't implement it or is {@code null}
|
||||
* @see org.springframework.util.ClassUtils#getMostSpecificMethod
|
||||
*/
|
||||
public static Method getMostSpecificMethod(Method method, @Nullable Class<?> targetClass) {
|
||||
|
||||
+40
-35
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,6 +23,9 @@ import java.util.Map;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.aspectj.weaver.tools.PointcutExpression;
|
||||
import org.aspectj.weaver.tools.PointcutPrimitive;
|
||||
import org.aspectj.weaver.tools.UnsupportedPointcutPrimitiveException;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import test.annotation.EmptySpringAnnotation;
|
||||
@@ -39,13 +42,14 @@ import org.springframework.beans.testfixture.beans.TestBean;
|
||||
import org.springframework.beans.testfixture.beans.subpkg.DeepBean;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
|
||||
/**
|
||||
* @author Rob Harrop
|
||||
* @author Rod Johnson
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class AspectJExpressionPointcutTests {
|
||||
|
||||
@@ -61,7 +65,7 @@ public class AspectJExpressionPointcutTests {
|
||||
|
||||
|
||||
@BeforeEach
|
||||
public void setup() throws NoSuchMethodException {
|
||||
public void setUp() throws NoSuchMethodException {
|
||||
getAge = TestBean.class.getMethod("getAge");
|
||||
setAge = TestBean.class.getMethod("setAge", int.class);
|
||||
setSomeNumber = TestBean.class.getMethod("setSomeNumber", Number.class);
|
||||
@@ -171,25 +175,25 @@ public class AspectJExpressionPointcutTests {
|
||||
@Test
|
||||
public void testFriendlyErrorOnNoLocationClassMatching() {
|
||||
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> pc.getClassFilter().matches(ITestBean.class))
|
||||
.withMessageContaining("expression");
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
pc.matches(ITestBean.class))
|
||||
.withMessageContaining("expression");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFriendlyErrorOnNoLocation2ArgMatching() {
|
||||
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> pc.getMethodMatcher().matches(getAge, ITestBean.class))
|
||||
.withMessageContaining("expression");
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
pc.matches(getAge, ITestBean.class))
|
||||
.withMessageContaining("expression");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFriendlyErrorOnNoLocation3ArgMatching() {
|
||||
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> pc.getMethodMatcher().matches(getAge, ITestBean.class, (Object[]) null))
|
||||
.withMessageContaining("expression");
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
pc.matches(getAge, ITestBean.class, (Object[]) null))
|
||||
.withMessageContaining("expression");
|
||||
}
|
||||
|
||||
|
||||
@@ -206,10 +210,8 @@ public class AspectJExpressionPointcutTests {
|
||||
// not currently testable in a reliable fashion
|
||||
//assertDoesNotMatchStringClass(classFilter);
|
||||
|
||||
assertThat(methodMatcher.matches(setSomeNumber, TestBean.class, 12D))
|
||||
.as("Should match with setSomeNumber with Double input").isTrue();
|
||||
assertThat(methodMatcher.matches(setSomeNumber, TestBean.class, 11))
|
||||
.as("Should not match setSomeNumber with Integer input").isFalse();
|
||||
assertThat(methodMatcher.matches(setSomeNumber, TestBean.class, 12D)).as("Should match with setSomeNumber with Double input").isTrue();
|
||||
assertThat(methodMatcher.matches(setSomeNumber, TestBean.class, 11)).as("Should not match setSomeNumber with Integer input").isFalse();
|
||||
assertThat(methodMatcher.matches(getAge, TestBean.class)).as("Should not match getAge").isFalse();
|
||||
assertThat(methodMatcher.isRuntime()).as("Should be a runtime match").isTrue();
|
||||
}
|
||||
@@ -244,13 +246,14 @@ public class AspectJExpressionPointcutTests {
|
||||
@Test
|
||||
public void testInvalidExpression() {
|
||||
String expression = "execution(void org.springframework.beans.testfixture.beans.TestBean.setSomeNumber(Number) && args(Double)";
|
||||
assertThat(getPointcut(expression).getClassFilter().matches(Object.class)).isFalse();
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
getPointcut(expression)::getClassFilter); // call to getClassFilter forces resolution
|
||||
}
|
||||
|
||||
private TestBean getAdvisedProxy(String pointcutExpression, CallCountingInterceptor interceptor) {
|
||||
TestBean target = new TestBean();
|
||||
|
||||
AspectJExpressionPointcut pointcut = getPointcut(pointcutExpression);
|
||||
Pointcut pointcut = getPointcut(pointcutExpression);
|
||||
|
||||
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
|
||||
advisor.setAdvice(interceptor);
|
||||
@@ -274,29 +277,40 @@ public class AspectJExpressionPointcutTests {
|
||||
@Test
|
||||
public void testWithUnsupportedPointcutPrimitive() {
|
||||
String expression = "call(int org.springframework.beans.testfixture.beans.TestBean.getAge())";
|
||||
assertThat(getPointcut(expression).getClassFilter().matches(Object.class)).isFalse();
|
||||
assertThatExceptionOfType(UnsupportedPointcutPrimitiveException.class).isThrownBy(() ->
|
||||
getPointcut(expression).getClassFilter()) // call to getClassFilter forces resolution...
|
||||
.satisfies(ex -> assertThat(ex.getUnsupportedPrimitive()).isEqualTo(PointcutPrimitive.CALL));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAndSubstitution() {
|
||||
AspectJExpressionPointcut pc = getPointcut("execution(* *(..)) and args(String)");
|
||||
String expr = pc.getPointcutExpression().getPointcutExpression();
|
||||
assertThat(expr).isEqualTo("execution(* *(..)) && args(String)");
|
||||
Pointcut pc = getPointcut("execution(* *(..)) and args(String)");
|
||||
PointcutExpression expr = ((AspectJExpressionPointcut) pc).getPointcutExpression();
|
||||
assertThat(expr.getPointcutExpression()).isEqualTo("execution(* *(..)) && args(String)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleAndSubstitutions() {
|
||||
AspectJExpressionPointcut pc = getPointcut("execution(* *(..)) and args(String) and this(Object)");
|
||||
String expr = pc.getPointcutExpression().getPointcutExpression();
|
||||
assertThat(expr).isEqualTo("execution(* *(..)) && args(String) && this(Object)");
|
||||
Pointcut pc = getPointcut("execution(* *(..)) and args(String) and this(Object)");
|
||||
PointcutExpression expr = ((AspectJExpressionPointcut) pc).getPointcutExpression();
|
||||
assertThat(expr.getPointcutExpression()).isEqualTo("execution(* *(..)) && args(String) && this(Object)");
|
||||
}
|
||||
|
||||
private AspectJExpressionPointcut getPointcut(String expression) {
|
||||
private Pointcut getPointcut(String expression) {
|
||||
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
|
||||
pointcut.setExpression(expression);
|
||||
return pointcut;
|
||||
}
|
||||
|
||||
|
||||
public static class OtherIOther implements IOther {
|
||||
|
||||
@Override
|
||||
public void absquatulate() {
|
||||
// Empty
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatchGenericArgument() {
|
||||
String expression = "execution(* set*(java.util.List<org.springframework.beans.testfixture.beans.TestBean>) )";
|
||||
@@ -491,15 +505,6 @@ public class AspectJExpressionPointcutTests {
|
||||
}
|
||||
|
||||
|
||||
public static class OtherIOther implements IOther {
|
||||
|
||||
@Override
|
||||
public void absquatulate() {
|
||||
// Empty
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class HasGeneric {
|
||||
|
||||
public void setFriends(List<TestBean> friends) {
|
||||
|
||||
+18
-48
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -37,6 +37,7 @@ import org.aspectj.lang.annotation.DeclareParents;
|
||||
import org.aspectj.lang.annotation.DeclarePrecedence;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import test.aop.DefaultLockable;
|
||||
import test.aop.Lockable;
|
||||
@@ -75,24 +76,25 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
|
||||
/**
|
||||
* To be overridden by concrete test subclasses.
|
||||
* @return the fixture
|
||||
*/
|
||||
protected abstract AspectJAdvisorFactory getFixture();
|
||||
|
||||
|
||||
@Test
|
||||
void rejectsPerCflowAspect() {
|
||||
assertThatExceptionOfType(AopConfigException.class)
|
||||
.isThrownBy(() -> getFixture().getAdvisors(
|
||||
assertThatExceptionOfType(AopConfigException.class).isThrownBy(() ->
|
||||
getFixture().getAdvisors(
|
||||
new SingletonMetadataAwareAspectInstanceFactory(new PerCflowAspect(), "someBean")))
|
||||
.withMessageContaining("PERCFLOW");
|
||||
.withMessageContaining("PERCFLOW");
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsPerCflowBelowAspect() {
|
||||
assertThatExceptionOfType(AopConfigException.class)
|
||||
.isThrownBy(() -> getFixture().getAdvisors(
|
||||
new SingletonMetadataAwareAspectInstanceFactory(new PerCflowBelowAspect(), "someBean")))
|
||||
.withMessageContaining("PERCFLOWBELOW");
|
||||
assertThatExceptionOfType(AopConfigException.class).isThrownBy(() ->
|
||||
getFixture().getAdvisors(
|
||||
new SingletonMetadataAwareAspectInstanceFactory(new PerCflowBelowAspect(), "someBean")))
|
||||
.withMessageContaining("PERCFLOWBELOW");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -383,7 +385,8 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
assertThat(lockable.locked()).as("Already locked").isTrue();
|
||||
lockable.lock();
|
||||
assertThat(lockable.locked()).as("Real target ignores locking").isTrue();
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(lockable::unlock);
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() ->
|
||||
lockable.unlock());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -410,7 +413,9 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
lockable.locked();
|
||||
}
|
||||
|
||||
// TODO: Why does this test fail? It hasn't been run before, so it maybe never actually passed...
|
||||
@Test
|
||||
@Disabled
|
||||
void introductionWithArgumentBinding() {
|
||||
TestBean target = new TestBean();
|
||||
|
||||
@@ -518,16 +523,6 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
assertThat(aspect.invocations).containsExactly("around - start", "before", "after throwing", "after", "around - end");
|
||||
}
|
||||
|
||||
@Test
|
||||
void parentAspect() {
|
||||
TestBean target = new TestBean("Jane", 42);
|
||||
MetadataAwareAspectInstanceFactory aspectInstanceFactory = new SingletonMetadataAwareAspectInstanceFactory(
|
||||
new IncrementingAspect(), "incrementingAspect");
|
||||
ITestBean proxy = (ITestBean) createProxy(target,
|
||||
getFixture().getAdvisors(aspectInstanceFactory), ITestBean.class);
|
||||
assertThat(proxy.getAge()).isEqualTo(86); // (42 + 1) * 2
|
||||
}
|
||||
|
||||
@Test
|
||||
void failureWithoutExplicitDeclarePrecedence() {
|
||||
TestBean target = new TestBean();
|
||||
@@ -652,7 +647,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
static class NamedPointcutAspectWithFQN {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean();
|
||||
private ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean();
|
||||
|
||||
@Pointcut("execution(* getAge())")
|
||||
void getAge() {
|
||||
@@ -772,31 +767,6 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
}
|
||||
|
||||
|
||||
@Aspect
|
||||
abstract static class DoublingAspect {
|
||||
|
||||
@Around("execution(* getAge())")
|
||||
public Object doubleAge(ProceedingJoinPoint pjp) throws Throwable {
|
||||
return ((int) pjp.proceed()) * 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Aspect
|
||||
static class IncrementingAspect extends DoublingAspect {
|
||||
|
||||
@Override
|
||||
public Object doubleAge(ProceedingJoinPoint pjp) throws Throwable {
|
||||
return ((int) pjp.proceed()) * 2;
|
||||
}
|
||||
|
||||
@Around("execution(* getAge())")
|
||||
public int incrementAge(ProceedingJoinPoint pjp) throws Throwable {
|
||||
return ((int) pjp.proceed()) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Aspect
|
||||
private static class InvocationTrackingAspect {
|
||||
|
||||
@@ -854,7 +824,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
|
||||
@Around("getAge()")
|
||||
int preventExecution(ProceedingJoinPoint pjp) {
|
||||
return 42;
|
||||
return 666;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -874,7 +844,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
|
||||
@Around("getAge()")
|
||||
int preventExecution(ProceedingJoinPoint pjp) {
|
||||
return 42;
|
||||
return 666;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1096,7 +1066,7 @@ class PerThisAspect {
|
||||
|
||||
// Just to check that this doesn't cause problems with introduction processing
|
||||
@SuppressWarnings("unused")
|
||||
private final ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean();
|
||||
private ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean();
|
||||
|
||||
@Around("execution(int *.getAge())")
|
||||
int returnCountAsAge() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,35 +17,29 @@
|
||||
package org.springframework.aop.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aop.ClassFilter;
|
||||
import org.springframework.aop.MethodMatcher;
|
||||
import org.springframework.aop.Pointcut;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
|
||||
import org.springframework.aop.target.EmptyTargetSource;
|
||||
import org.springframework.aop.testfixture.interceptor.NopInterceptor;
|
||||
import org.springframework.beans.testfixture.beans.TestBean;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.testfixture.io.SerializationTestUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Rod Johnson
|
||||
* @author Chris Beams
|
||||
* @author Sebastien Deleuze
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
class AopUtilsTests {
|
||||
public class AopUtilsTests {
|
||||
|
||||
@Test
|
||||
void testPointcutCanNeverApply() {
|
||||
public void testPointcutCanNeverApply() {
|
||||
class TestPointcut extends StaticMethodMatcherPointcut {
|
||||
@Override
|
||||
public boolean matches(Method method, @Nullable Class<?> clazzy) {
|
||||
@@ -58,13 +52,13 @@ class AopUtilsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPointcutAlwaysApplies() {
|
||||
public void testPointcutAlwaysApplies() {
|
||||
assertThat(AopUtils.canApply(new DefaultPointcutAdvisor(new NopInterceptor()), Object.class)).isTrue();
|
||||
assertThat(AopUtils.canApply(new DefaultPointcutAdvisor(new NopInterceptor()), TestBean.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPointcutAppliesToOneMethodOnObject() {
|
||||
public void testPointcutAppliesToOneMethodOnObject() {
|
||||
class TestPointcut extends StaticMethodMatcherPointcut {
|
||||
@Override
|
||||
public boolean matches(Method method, @Nullable Class<?> clazz) {
|
||||
@@ -84,7 +78,7 @@ class AopUtilsTests {
|
||||
* that's subverted the singleton construction limitation.
|
||||
*/
|
||||
@Test
|
||||
void testCanonicalFrameworkClassesStillCanonicalOnDeserialization() throws Exception {
|
||||
public void testCanonicalFrameworkClassesStillCanonicalOnDeserialization() throws Exception {
|
||||
assertThat(SerializationTestUtils.serializeAndDeserialize(MethodMatcher.TRUE)).isSameAs(MethodMatcher.TRUE);
|
||||
assertThat(SerializationTestUtils.serializeAndDeserialize(ClassFilter.TRUE)).isSameAs(ClassFilter.TRUE);
|
||||
assertThat(SerializationTestUtils.serializeAndDeserialize(Pointcut.TRUE)).isSameAs(Pointcut.TRUE);
|
||||
@@ -94,45 +88,4 @@ class AopUtilsTests {
|
||||
assertThat(SerializationTestUtils.serializeAndDeserialize(ExposeInvocationInterceptor.INSTANCE)).isSameAs(ExposeInvocationInterceptor.INSTANCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInvokeJoinpointUsingReflection() throws Throwable {
|
||||
String name = "foo";
|
||||
TestBean testBean = new TestBean(name);
|
||||
Method method = ReflectionUtils.findMethod(TestBean.class, "getName");
|
||||
Object result = AopUtils.invokeJoinpointUsingReflection(testBean, method, new Object[0]);
|
||||
assertThat(result).isEqualTo(name);
|
||||
}
|
||||
|
||||
@Test // gh-32365
|
||||
void mostSpecificMethodBetweenJdkProxyAndTarget() throws Exception {
|
||||
Class<?> proxyClass = new ProxyFactory(new WithInterface()).getProxy(getClass().getClassLoader()).getClass();
|
||||
Method specificMethod = AopUtils.getMostSpecificMethod(proxyClass.getMethod("handle", List.class), WithInterface.class);
|
||||
assertThat(ResolvableType.forMethodParameter(specificMethod, 0).getGeneric().toClass()).isEqualTo(String.class);
|
||||
}
|
||||
|
||||
@Test // gh-32365
|
||||
void mostSpecificMethodBetweenCglibProxyAndTarget() throws Exception {
|
||||
Class<?> proxyClass = new ProxyFactory(new WithoutInterface()).getProxy(getClass().getClassLoader()).getClass();
|
||||
Method specificMethod = AopUtils.getMostSpecificMethod(proxyClass.getMethod("handle", List.class), WithoutInterface.class);
|
||||
assertThat(ResolvableType.forMethodParameter(specificMethod, 0).getGeneric().toClass()).isEqualTo(String.class);
|
||||
}
|
||||
|
||||
|
||||
interface ProxyInterface {
|
||||
|
||||
void handle(List<String> list);
|
||||
}
|
||||
|
||||
static class WithInterface implements ProxyInterface {
|
||||
|
||||
public void handle(List<String> list) {
|
||||
}
|
||||
}
|
||||
|
||||
static class WithoutInterface {
|
||||
|
||||
public void handle(List<String> list) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,13 +22,12 @@ import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
|
||||
/**
|
||||
* @author Adrian Colyer
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class AutoProxyWithCodeStyleAspectsTests {
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("resource")
|
||||
public void noAutoProxyingOfAjcCompiledAspects() {
|
||||
public void noAutoproxyingOfAjcCompiledAspects() {
|
||||
new ClassPathXmlApplicationContext("org/springframework/aop/aspectj/autoproxy/ajcAutoproxyTests.xml");
|
||||
}
|
||||
|
||||
|
||||
+1
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,10 +20,6 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
|
||||
/**
|
||||
* @author Ramnivas Laddad
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class SpringConfiguredWithAutoProxyingTests {
|
||||
|
||||
@Test
|
||||
|
||||
+4
-17
@@ -2,29 +2,16 @@
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:aop="http://www.springframework.org/schema/aop"
|
||||
xmlns:cache="http://www.springframework.org/schema/cache"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-2.0.xsd
|
||||
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-2.0.xsd
|
||||
http://www.springframework.org/schema/cache https://www.springframework.org/schema/cache/spring-cache-3.1.xsd
|
||||
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context-2.5.xsd">
|
||||
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
|
||||
|
||||
<aop:aspectj-autoproxy/>
|
||||
|
||||
<context:spring-configured/>
|
||||
|
||||
<cache:annotation-driven mode="aspectj"/>
|
||||
|
||||
<bean id="cacheManager" class="org.springframework.cache.support.NoOpCacheManager"/>
|
||||
|
||||
<bean id="myAspect" class="org.springframework.aop.aspectj.autoproxy.CodeStyleAspect" factory-method="aspectOf">
|
||||
<bean id="myAspect" class="org.springframework.aop.aspectj.autoproxy.CodeStyleAspect"
|
||||
factory-method="aspectOf">
|
||||
<property name="foo" value="bar"/>
|
||||
</bean>
|
||||
|
||||
<bean id="otherBean" class="org.springframework.beans.factory.aspectj.ShouldBeConfiguredBySpring"/>
|
||||
|
||||
<bean id="yetAnotherBean" class="org.springframework.beans.factory.aspectj.ShouldBeConfiguredBySpring"/>
|
||||
|
||||
<bean id="configuredBean" class="org.springframework.beans.factory.aspectj.ShouldBeConfiguredBySpring" lazy-init="true"/>
|
||||
<bean id="otherBean" class="java.lang.Object"/>
|
||||
|
||||
</beans>
|
||||
|
||||
Vendored
+2
-1
@@ -24,7 +24,8 @@
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="defaultCache" class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
|
||||
<bean id="defaultCache"
|
||||
class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
|
||||
<property name="name" value="default"/>
|
||||
</bean>
|
||||
|
||||
|
||||
+4
-2
@@ -7,10 +7,12 @@
|
||||
http://www.springframework.org/schema/task
|
||||
https://www.springframework.org/schema/task/spring-task.xsd">
|
||||
|
||||
<task:annotation-driven mode="aspectj" executor="testExecutor" exception-handler="testExceptionHandler"/>
|
||||
<task:annotation-driven mode="aspectj" executor="testExecutor"
|
||||
exception-handler="testExceptionHandler"/>
|
||||
|
||||
<task:executor id="testExecutor"/>
|
||||
|
||||
<bean id="testExceptionHandler" class="org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler"/>
|
||||
<bean id="testExceptionHandler"
|
||||
class="org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler"/>
|
||||
|
||||
</beans>
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -835,11 +835,11 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||
/**
|
||||
* This implementation attempts to query the FactoryBean's generic parameter metadata
|
||||
* if present to determine the object type. If not present, i.e. the FactoryBean is
|
||||
* declared as a raw type, it checks the FactoryBean's {@code getObjectType} method
|
||||
* declared as a raw type, checks the FactoryBean's {@code getObjectType} method
|
||||
* on a plain instance of the FactoryBean, without bean properties applied yet.
|
||||
* If this doesn't return a type yet and {@code allowInit} is {@code true}, full
|
||||
* creation of the FactoryBean is attempted as fallback (through delegation to the
|
||||
* superclass implementation).
|
||||
* If this doesn't return a type yet, and {@code allowInit} is {@code true} a
|
||||
* full creation of the FactoryBean is used as fallback (through delegation to the
|
||||
* superclass's implementation).
|
||||
* <p>The shortcut check for a FactoryBean is only applied in case of a singleton
|
||||
* FactoryBean. If the FactoryBean instance itself is not kept as singleton,
|
||||
* it will be fully created to check the type of its exposed object.
|
||||
|
||||
+5
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -166,9 +166,6 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
/** Map from scope identifier String to corresponding Scope. */
|
||||
private final Map<String, Scope> scopes = new LinkedHashMap<>(8);
|
||||
|
||||
/** Application startup metrics. **/
|
||||
private ApplicationStartup applicationStartup = ApplicationStartup.DEFAULT;
|
||||
|
||||
/** Security context used when running with a SecurityManager. */
|
||||
@Nullable
|
||||
private SecurityContextProvider securityContextProvider;
|
||||
@@ -183,6 +180,8 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
private final ThreadLocal<Object> prototypesCurrentlyInCreation =
|
||||
new NamedThreadLocal<>("Prototype beans currently in creation");
|
||||
|
||||
/** Application startup metrics. **/
|
||||
private ApplicationStartup applicationStartup = ApplicationStartup.DEFAULT;
|
||||
|
||||
/**
|
||||
* Create a new AbstractBeanFactory.
|
||||
@@ -1080,7 +1079,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
|
||||
@Override
|
||||
public void setApplicationStartup(ApplicationStartup applicationStartup) {
|
||||
Assert.notNull(applicationStartup, "ApplicationStartup must not be null");
|
||||
Assert.notNull(applicationStartup, "applicationStartup should not be null");
|
||||
this.applicationStartup = applicationStartup;
|
||||
}
|
||||
|
||||
@@ -1695,7 +1694,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
* already. The implementation is allowed to instantiate the target factory bean if
|
||||
* {@code allowInit} is {@code true} and the type cannot be determined another way;
|
||||
* otherwise it is restricted to introspecting signatures and related metadata.
|
||||
* <p>If no {@link FactoryBean#OBJECT_TYPE_ATTRIBUTE} is set on the bean definition
|
||||
* <p>If no {@link FactoryBean#OBJECT_TYPE_ATTRIBUTE} if set on the bean definition
|
||||
* and {@code allowInit} is {@code true}, the default implementation will create
|
||||
* the FactoryBean via {@code getBean} to call its {@code getObjectType} method.
|
||||
* Subclasses are encouraged to optimize this, typically by inspecting the generic
|
||||
|
||||
+7
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -600,10 +600,13 @@ class ConstructorResolver {
|
||||
String argDesc = StringUtils.collectionToCommaDelimitedString(argTypes);
|
||||
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
|
||||
"No matching factory method found on class [" + factoryClass.getName() + "]: " +
|
||||
(mbd.getFactoryBeanName() != null ? "factory bean '" + mbd.getFactoryBeanName() + "'; " : "") +
|
||||
(mbd.getFactoryBeanName() != null ?
|
||||
"factory bean '" + mbd.getFactoryBeanName() + "'; " : "") +
|
||||
"factory method '" + mbd.getFactoryMethodName() + "(" + argDesc + ")'. " +
|
||||
"Check that a method with the specified name " + (minNrOfArgs > 0 ? "and arguments " : "") +
|
||||
"exists and that it is " + (isStatic ? "static" : "non-static") + ".");
|
||||
"Check that a method with the specified name " +
|
||||
(minNrOfArgs > 0 ? "and arguments " : "") +
|
||||
"exists and that it is " +
|
||||
(isStatic ? "static" : "non-static") + ".");
|
||||
}
|
||||
else if (void.class == factoryMethodToUse.getReturnType()) {
|
||||
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
|
||||
|
||||
+6
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -567,16 +567,16 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
|
||||
*/
|
||||
protected void destroyBean(String beanName, @Nullable DisposableBean bean) {
|
||||
// Trigger destruction of dependent beans first...
|
||||
Set<String> dependentBeanNames;
|
||||
Set<String> dependencies;
|
||||
synchronized (this.dependentBeanMap) {
|
||||
// Within full synchronization in order to guarantee a disconnected Set
|
||||
dependentBeanNames = this.dependentBeanMap.remove(beanName);
|
||||
dependencies = this.dependentBeanMap.remove(beanName);
|
||||
}
|
||||
if (dependentBeanNames != null) {
|
||||
if (dependencies != null) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Retrieved dependent beans for bean '" + beanName + "': " + dependentBeanNames);
|
||||
logger.trace("Retrieved dependent beans for bean '" + beanName + "': " + dependencies);
|
||||
}
|
||||
for (String dependentBeanName : dependentBeanNames) {
|
||||
for (String dependentBeanName : dependencies) {
|
||||
destroySingleton(dependentBeanName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -42,8 +42,6 @@ import org.springframework.beans.propertyeditors.CustomDateEditor;
|
||||
import org.springframework.beans.testfixture.beans.DerivedTestBean;
|
||||
import org.springframework.beans.testfixture.beans.ITestBean;
|
||||
import org.springframework.beans.testfixture.beans.TestBean;
|
||||
import org.springframework.cglib.proxy.Enhancer;
|
||||
import org.springframework.cglib.proxy.MethodInterceptor;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceEditor;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -54,7 +52,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
import static org.assertj.core.api.SoftAssertions.assertSoftly;
|
||||
|
||||
/**
|
||||
* Tests for {@link BeanUtils}.
|
||||
* Unit tests for {@link BeanUtils}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Rob Harrop
|
||||
@@ -138,7 +136,7 @@ class BeanUtilsTests {
|
||||
PropertyDescriptor[] actual = Introspector.getBeanInfo(TestBean.class).getPropertyDescriptors();
|
||||
PropertyDescriptor[] descriptors = BeanUtils.getPropertyDescriptors(TestBean.class);
|
||||
assertThat(descriptors).as("Descriptors should not be null").isNotNull();
|
||||
assertThat(descriptors).as("Invalid number of descriptors returned").hasSameSizeAs(actual);
|
||||
assertThat(descriptors.length).as("Invalid number of descriptors returned").isEqualTo(actual.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -164,13 +162,13 @@ class BeanUtilsTests {
|
||||
tb.setAge(32);
|
||||
tb.setTouchy("touchy");
|
||||
TestBean tb2 = new TestBean();
|
||||
assertThat(tb2.getName()).as("Name empty").isNull();
|
||||
assertThat(tb2.getAge()).as("Age empty").isEqualTo(0);
|
||||
assertThat(tb2.getTouchy()).as("Touchy empty").isNull();
|
||||
assertThat(tb2.getName() == null).as("Name empty").isTrue();
|
||||
assertThat(tb2.getAge() == 0).as("Age empty").isTrue();
|
||||
assertThat(tb2.getTouchy() == null).as("Touchy empty").isTrue();
|
||||
BeanUtils.copyProperties(tb, tb2);
|
||||
assertThat(tb2.getName()).as("Name copied").isEqualTo(tb.getName());
|
||||
assertThat(tb2.getAge()).as("Age copied").isEqualTo(tb.getAge());
|
||||
assertThat(tb2.getTouchy()).as("Touchy copied").isEqualTo(tb.getTouchy());
|
||||
assertThat(tb2.getName().equals(tb.getName())).as("Name copied").isTrue();
|
||||
assertThat(tb2.getAge() == tb.getAge()).as("Age copied").isTrue();
|
||||
assertThat(tb2.getTouchy().equals(tb.getTouchy())).as("Touchy copied").isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -180,13 +178,13 @@ class BeanUtilsTests {
|
||||
tb.setAge(32);
|
||||
tb.setTouchy("touchy");
|
||||
TestBean tb2 = new TestBean();
|
||||
assertThat(tb2.getName()).as("Name empty").isNull();
|
||||
assertThat(tb2.getAge()).as("Age empty").isEqualTo(0);
|
||||
assertThat(tb2.getTouchy()).as("Touchy empty").isNull();
|
||||
assertThat(tb2.getName() == null).as("Name empty").isTrue();
|
||||
assertThat(tb2.getAge() == 0).as("Age empty").isTrue();
|
||||
assertThat(tb2.getTouchy() == null).as("Touchy empty").isTrue();
|
||||
BeanUtils.copyProperties(tb, tb2);
|
||||
assertThat(tb2.getName()).as("Name copied").isEqualTo(tb.getName());
|
||||
assertThat(tb2.getAge()).as("Age copied").isEqualTo(tb.getAge());
|
||||
assertThat(tb2.getTouchy()).as("Touchy copied").isEqualTo(tb.getTouchy());
|
||||
assertThat(tb2.getName().equals(tb.getName())).as("Name copied").isTrue();
|
||||
assertThat(tb2.getAge() == tb.getAge()).as("Age copied").isTrue();
|
||||
assertThat(tb2.getTouchy().equals(tb.getTouchy())).as("Touchy copied").isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -196,13 +194,13 @@ class BeanUtilsTests {
|
||||
tb.setAge(32);
|
||||
tb.setTouchy("touchy");
|
||||
DerivedTestBean tb2 = new DerivedTestBean();
|
||||
assertThat(tb2.getName()).as("Name empty").isNull();
|
||||
assertThat(tb2.getAge()).as("Age empty").isEqualTo(0);
|
||||
assertThat(tb2.getTouchy()).as("Touchy empty").isNull();
|
||||
assertThat(tb2.getName() == null).as("Name empty").isTrue();
|
||||
assertThat(tb2.getAge() == 0).as("Age empty").isTrue();
|
||||
assertThat(tb2.getTouchy() == null).as("Touchy empty").isTrue();
|
||||
BeanUtils.copyProperties(tb, tb2);
|
||||
assertThat(tb2.getName()).as("Name copied").isEqualTo(tb.getName());
|
||||
assertThat(tb2.getAge()).as("Age copied").isEqualTo(tb.getAge());
|
||||
assertThat(tb2.getTouchy()).as("Touchy copied").isEqualTo(tb.getTouchy());
|
||||
assertThat(tb2.getName().equals(tb.getName())).as("Name copied").isTrue();
|
||||
assertThat(tb2.getAge() == tb.getAge()).as("Age copied").isTrue();
|
||||
assertThat(tb2.getTouchy().equals(tb.getTouchy())).as("Touchy copied").isTrue();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -229,8 +227,8 @@ class BeanUtilsTests {
|
||||
IntegerListHolder2 integerListHolder2 = new IntegerListHolder2();
|
||||
|
||||
BeanUtils.copyProperties(integerListHolder1, integerListHolder2);
|
||||
assertThat(integerListHolder1.getList()).containsExactly(42);
|
||||
assertThat(integerListHolder2.getList()).containsExactly(42);
|
||||
assertThat(integerListHolder1.getList()).containsOnly(42);
|
||||
assertThat(integerListHolder2.getList()).containsOnly(42);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -259,7 +257,7 @@ class BeanUtilsTests {
|
||||
WildcardListHolder2 wildcardListHolder2 = new WildcardListHolder2();
|
||||
|
||||
BeanUtils.copyProperties(integerListHolder1, wildcardListHolder2);
|
||||
assertThat(integerListHolder1.getList()).containsExactly(42);
|
||||
assertThat(integerListHolder1.getList()).containsOnly(42);
|
||||
assertThat(wildcardListHolder2.getList()).isEqualTo(Arrays.asList(42));
|
||||
}
|
||||
|
||||
@@ -273,8 +271,9 @@ class BeanUtilsTests {
|
||||
NumberUpperBoundedWildcardListHolder numberListHolder = new NumberUpperBoundedWildcardListHolder();
|
||||
|
||||
BeanUtils.copyProperties(integerListHolder1, numberListHolder);
|
||||
assertThat(integerListHolder1.getList()).containsExactly(42);
|
||||
assertThat(numberListHolder.getList()).isEqualTo(Arrays.asList(42));
|
||||
assertThat(integerListHolder1.getList()).containsOnly(42);
|
||||
assertThat(numberListHolder.getList()).hasSize(1);
|
||||
assertThat(numberListHolder.getList().contains(Integer.valueOf(42))).isTrue();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -283,7 +282,7 @@ class BeanUtilsTests {
|
||||
@Test
|
||||
void copyPropertiesDoesNotCopyFromSuperTypeToSubType() {
|
||||
NumberHolder numberHolder = new NumberHolder();
|
||||
numberHolder.setNumber(42);
|
||||
numberHolder.setNumber(Integer.valueOf(42));
|
||||
IntegerHolder integerHolder = new IntegerHolder();
|
||||
|
||||
BeanUtils.copyProperties(numberHolder, integerHolder);
|
||||
@@ -301,7 +300,7 @@ class BeanUtilsTests {
|
||||
LongListHolder longListHolder = new LongListHolder();
|
||||
|
||||
BeanUtils.copyProperties(integerListHolder, longListHolder);
|
||||
assertThat(integerListHolder.getList()).containsExactly(42);
|
||||
assertThat(integerListHolder.getList()).containsOnly(42);
|
||||
assertThat(longListHolder.getList()).isEmpty();
|
||||
}
|
||||
|
||||
@@ -315,7 +314,7 @@ class BeanUtilsTests {
|
||||
NumberListHolder numberListHolder = new NumberListHolder();
|
||||
|
||||
BeanUtils.copyProperties(integerListHolder, numberListHolder);
|
||||
assertThat(integerListHolder.getList()).containsExactly(42);
|
||||
assertThat(integerListHolder.getList()).containsOnly(42);
|
||||
assertThat(numberListHolder.getList()).isEmpty();
|
||||
}
|
||||
|
||||
@@ -324,13 +323,12 @@ class BeanUtilsTests {
|
||||
Order original = new Order("test", Arrays.asList("foo", "bar"));
|
||||
|
||||
// Create a Proxy that loses the generic type information for the getLineItems() method.
|
||||
OrderSummary proxy = (OrderSummary) Proxy.newProxyInstance(getClass().getClassLoader(),
|
||||
new Class<?>[] {OrderSummary.class}, new OrderInvocationHandler(original));
|
||||
OrderSummary proxy = proxyOrder(original);
|
||||
assertThat(OrderSummary.class.getDeclaredMethod("getLineItems").toGenericString())
|
||||
.contains("java.util.List<java.lang.String>");
|
||||
.contains("java.util.List<java.lang.String>");
|
||||
assertThat(proxy.getClass().getDeclaredMethod("getLineItems").toGenericString())
|
||||
.contains("java.util.List")
|
||||
.doesNotContain("<java.lang.String>");
|
||||
.contains("java.util.List")
|
||||
.doesNotContain("<java.lang.String>");
|
||||
|
||||
// Ensure that our custom Proxy works as expected.
|
||||
assertThat(proxy.getId()).isEqualTo("test");
|
||||
@@ -343,57 +341,40 @@ class BeanUtilsTests {
|
||||
assertThat(target.getLineItems()).containsExactly("foo", "bar");
|
||||
}
|
||||
|
||||
@Test // gh-32888
|
||||
public void copyPropertiesWithGenericCglibClass() {
|
||||
Enhancer enhancer = new Enhancer();
|
||||
enhancer.setSuperclass(User.class);
|
||||
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> proxy.invokeSuper(obj, args));
|
||||
User user = (User) enhancer.create();
|
||||
user.setId(1);
|
||||
user.setName("proxy");
|
||||
user.setAddress("addr");
|
||||
|
||||
User target = new User();
|
||||
BeanUtils.copyProperties(user, target);
|
||||
assertThat(target.getId()).isEqualTo(user.getId());
|
||||
assertThat(target.getName()).isEqualTo(user.getName());
|
||||
assertThat(target.getAddress()).isEqualTo(user.getAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
void copyPropertiesWithEditable() throws Exception {
|
||||
TestBean tb = new TestBean();
|
||||
assertThat(tb.getName()).as("Name empty").isNull();
|
||||
assertThat(tb.getName() == null).as("Name empty").isTrue();
|
||||
tb.setAge(32);
|
||||
tb.setTouchy("bla");
|
||||
TestBean tb2 = new TestBean();
|
||||
tb2.setName("rod");
|
||||
assertThat(tb2.getAge()).as("Age empty").isEqualTo(0);
|
||||
assertThat(tb2.getTouchy()).as("Touchy empty").isNull();
|
||||
assertThat(tb2.getAge() == 0).as("Age empty").isTrue();
|
||||
assertThat(tb2.getTouchy() == null).as("Touchy empty").isTrue();
|
||||
|
||||
// "touchy" should not be copied: it's not defined in ITestBean
|
||||
BeanUtils.copyProperties(tb, tb2, ITestBean.class);
|
||||
assertThat(tb2.getName()).as("Name copied").isNull();
|
||||
assertThat(tb2.getAge()).as("Age copied").isEqualTo(32);
|
||||
assertThat(tb2.getTouchy()).as("Touchy still empty").isNull();
|
||||
assertThat(tb2.getName() == null).as("Name copied").isTrue();
|
||||
assertThat(tb2.getAge() == 32).as("Age copied").isTrue();
|
||||
assertThat(tb2.getTouchy() == null).as("Touchy still empty").isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void copyPropertiesWithIgnore() throws Exception {
|
||||
TestBean tb = new TestBean();
|
||||
assertThat(tb.getName()).as("Name empty").isNull();
|
||||
assertThat(tb.getName() == null).as("Name empty").isTrue();
|
||||
tb.setAge(32);
|
||||
tb.setTouchy("bla");
|
||||
TestBean tb2 = new TestBean();
|
||||
tb2.setName("rod");
|
||||
assertThat(tb2.getAge()).as("Age empty").isEqualTo(0);
|
||||
assertThat(tb2.getTouchy()).as("Touchy empty").isNull();
|
||||
assertThat(tb2.getAge() == 0).as("Age empty").isTrue();
|
||||
assertThat(tb2.getTouchy() == null).as("Touchy empty").isTrue();
|
||||
|
||||
// "spouse", "touchy", "age" should not be copied
|
||||
BeanUtils.copyProperties(tb, tb2, "spouse", "touchy", "age");
|
||||
assertThat(tb2.getName()).as("Name copied").isNull();
|
||||
assertThat(tb2.getAge()).as("Age still empty").isEqualTo(0);
|
||||
assertThat(tb2.getTouchy()).as("Touchy still empty").isNull();
|
||||
assertThat(tb2.getName() == null).as("Name copied").isTrue();
|
||||
assertThat(tb2.getAge() == 0).as("Age still empty").isTrue();
|
||||
assertThat(tb2.getTouchy() == null).as("Touchy still empty").isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -402,7 +383,7 @@ class BeanUtilsTests {
|
||||
source.setName("name");
|
||||
TestBean target = new TestBean();
|
||||
BeanUtils.copyProperties(source, target, "specialProperty");
|
||||
assertThat(target.getName()).isEqualTo("name");
|
||||
assertThat("name").isEqualTo(target.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -539,7 +520,6 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class IntegerHolder {
|
||||
|
||||
@@ -554,7 +534,6 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class WildcardListHolder1 {
|
||||
|
||||
@@ -569,7 +548,6 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class WildcardListHolder2 {
|
||||
|
||||
@@ -584,7 +562,6 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class NumberUpperBoundedWildcardListHolder {
|
||||
|
||||
@@ -599,7 +576,6 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class NumberListHolder {
|
||||
|
||||
@@ -614,7 +590,6 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class IntegerListHolder1 {
|
||||
|
||||
@@ -629,7 +604,6 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class IntegerListHolder2 {
|
||||
|
||||
@@ -644,7 +618,6 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class LongListHolder {
|
||||
|
||||
@@ -825,7 +798,6 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class BeanWithNullableTypes {
|
||||
|
||||
private Integer counter;
|
||||
@@ -856,7 +828,6 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class BeanWithPrimitiveTypes {
|
||||
|
||||
private boolean flag;
|
||||
@@ -869,6 +840,7 @@ class BeanUtilsTests {
|
||||
private char character;
|
||||
private String text;
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public BeanWithPrimitiveTypes(boolean flag, byte byteCount, short shortCount, int intCount, long longCount,
|
||||
float floatCount, double doubleCount, char character, String text) {
|
||||
@@ -919,8 +891,8 @@ class BeanUtilsTests {
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class PrivateBeanWithPrivateConstructor {
|
||||
|
||||
@@ -928,14 +900,13 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class Order {
|
||||
|
||||
private String id;
|
||||
|
||||
private List<String> lineItems;
|
||||
|
||||
|
||||
Order() {
|
||||
}
|
||||
|
||||
@@ -966,7 +937,6 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private interface OrderSummary {
|
||||
|
||||
String getId();
|
||||
@@ -975,10 +945,17 @@ class BeanUtilsTests {
|
||||
}
|
||||
|
||||
|
||||
private OrderSummary proxyOrder(Order order) {
|
||||
return (OrderSummary) Proxy.newProxyInstance(getClass().getClassLoader(),
|
||||
new Class<?>[] { OrderSummary.class }, new OrderInvocationHandler(order));
|
||||
}
|
||||
|
||||
|
||||
private static class OrderInvocationHandler implements InvocationHandler {
|
||||
|
||||
private final Order order;
|
||||
|
||||
|
||||
OrderInvocationHandler(Order order) {
|
||||
this.order = order;
|
||||
}
|
||||
@@ -996,46 +973,4 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class GenericBaseModel<T> {
|
||||
|
||||
private T id;
|
||||
|
||||
private String name;
|
||||
|
||||
public T getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(T id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class User extends GenericBaseModel<Integer> {
|
||||
|
||||
private String address;
|
||||
|
||||
public User() {
|
||||
super();
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+15
-12
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -72,6 +72,7 @@ final class ConfigurationClass {
|
||||
* Create a new {@link ConfigurationClass} with the given name.
|
||||
* @param metadataReader reader used to parse the underlying {@link Class}
|
||||
* @param beanName must not be {@code null}
|
||||
* @see ConfigurationClass#ConfigurationClass(Class, ConfigurationClass)
|
||||
*/
|
||||
ConfigurationClass(MetadataReader metadataReader, String beanName) {
|
||||
Assert.notNull(beanName, "Bean name must not be null");
|
||||
@@ -85,10 +86,10 @@ final class ConfigurationClass {
|
||||
* using the {@link Import} annotation or automatically processed as a nested
|
||||
* configuration class (if importedBy is not {@code null}).
|
||||
* @param metadataReader reader used to parse the underlying {@link Class}
|
||||
* @param importedBy the configuration class importing this one
|
||||
* @param importedBy the configuration class importing this one or {@code null}
|
||||
* @since 3.1.1
|
||||
*/
|
||||
ConfigurationClass(MetadataReader metadataReader, ConfigurationClass importedBy) {
|
||||
ConfigurationClass(MetadataReader metadataReader, @Nullable ConfigurationClass importedBy) {
|
||||
this.metadata = metadataReader.getAnnotationMetadata();
|
||||
this.resource = metadataReader.getResource();
|
||||
this.importedBy.add(importedBy);
|
||||
@@ -98,6 +99,7 @@ final class ConfigurationClass {
|
||||
* Create a new {@link ConfigurationClass} with the given name.
|
||||
* @param clazz the underlying {@link Class} to represent
|
||||
* @param beanName name of the {@code @Configuration} class bean
|
||||
* @see ConfigurationClass#ConfigurationClass(Class, ConfigurationClass)
|
||||
*/
|
||||
ConfigurationClass(Class<?> clazz, String beanName) {
|
||||
Assert.notNull(beanName, "Bean name must not be null");
|
||||
@@ -111,10 +113,10 @@ final class ConfigurationClass {
|
||||
* using the {@link Import} annotation or automatically processed as a nested
|
||||
* configuration class (if imported is {@code true}).
|
||||
* @param clazz the underlying {@link Class} to represent
|
||||
* @param importedBy the configuration class importing this one
|
||||
* @param importedBy the configuration class importing this one (or {@code null})
|
||||
* @since 3.1.1
|
||||
*/
|
||||
ConfigurationClass(Class<?> clazz, ConfigurationClass importedBy) {
|
||||
ConfigurationClass(Class<?> clazz, @Nullable ConfigurationClass importedBy) {
|
||||
this.metadata = AnnotationMetadata.introspect(clazz);
|
||||
this.resource = new DescriptiveResource(clazz.getName());
|
||||
this.importedBy.add(importedBy);
|
||||
@@ -124,6 +126,7 @@ final class ConfigurationClass {
|
||||
* Create a new {@link ConfigurationClass} with the given name.
|
||||
* @param metadata the metadata for the underlying class to represent
|
||||
* @param beanName name of the {@code @Configuration} class bean
|
||||
* @see ConfigurationClass#ConfigurationClass(Class, ConfigurationClass)
|
||||
*/
|
||||
ConfigurationClass(AnnotationMetadata metadata, String beanName) {
|
||||
Assert.notNull(beanName, "Bean name must not be null");
|
||||
@@ -145,12 +148,12 @@ final class ConfigurationClass {
|
||||
return ClassUtils.getShortName(getMetadata().getClassName());
|
||||
}
|
||||
|
||||
void setBeanName(@Nullable String beanName) {
|
||||
void setBeanName(String beanName) {
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
String getBeanName() {
|
||||
public String getBeanName() {
|
||||
return this.beanName;
|
||||
}
|
||||
|
||||
@@ -160,7 +163,7 @@ final class ConfigurationClass {
|
||||
* @since 3.1.1
|
||||
* @see #getImportedBy()
|
||||
*/
|
||||
boolean isImported() {
|
||||
public boolean isImported() {
|
||||
return !this.importedBy.isEmpty();
|
||||
}
|
||||
|
||||
@@ -194,10 +197,6 @@ final class ConfigurationClass {
|
||||
this.importedResources.put(importedResource, readerClass);
|
||||
}
|
||||
|
||||
Map<String, Class<? extends BeanDefinitionReader>> getImportedResources() {
|
||||
return this.importedResources;
|
||||
}
|
||||
|
||||
void addImportBeanDefinitionRegistrar(ImportBeanDefinitionRegistrar registrar, AnnotationMetadata importingClassMetadata) {
|
||||
this.importBeanDefinitionRegistrars.put(registrar, importingClassMetadata);
|
||||
}
|
||||
@@ -206,6 +205,10 @@ final class ConfigurationClass {
|
||||
return this.importBeanDefinitionRegistrars;
|
||||
}
|
||||
|
||||
Map<String, Class<? extends BeanDefinitionReader>> getImportedResources() {
|
||||
return this.importedResources;
|
||||
}
|
||||
|
||||
void validate(ProblemReporter problemReporter) {
|
||||
// A configuration class may not be final (CGLIB limitation) unless it declares proxyBeanMethods=false
|
||||
Map<String, Object> attributes = this.metadata.getAnnotationAttributes(Configuration.class.getName());
|
||||
|
||||
+21
-24
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -136,6 +136,17 @@ import org.springframework.util.ReflectionUtils;
|
||||
public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
implements ConfigurableApplicationContext {
|
||||
|
||||
/**
|
||||
* The name of the {@link LifecycleProcessor} bean in the context.
|
||||
* If none is supplied, a {@link DefaultLifecycleProcessor} is used.
|
||||
* @since 3.0
|
||||
* @see org.springframework.context.LifecycleProcessor
|
||||
* @see org.springframework.context.support.DefaultLifecycleProcessor
|
||||
* @see #start()
|
||||
* @see #stop()
|
||||
*/
|
||||
public static final String LIFECYCLE_PROCESSOR_BEAN_NAME = "lifecycleProcessor";
|
||||
|
||||
/**
|
||||
* The name of the {@link MessageSource} bean in the context.
|
||||
* If none is supplied, message resolution is delegated to the parent.
|
||||
@@ -156,18 +167,6 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
*/
|
||||
public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
|
||||
|
||||
/**
|
||||
* The name of the {@link LifecycleProcessor} bean in the context.
|
||||
* If none is supplied, a {@link DefaultLifecycleProcessor} is used.
|
||||
* @since 3.0
|
||||
* @see org.springframework.context.LifecycleProcessor
|
||||
* @see org.springframework.context.support.DefaultLifecycleProcessor
|
||||
* @see #start()
|
||||
* @see #stop()
|
||||
*/
|
||||
public static final String LIFECYCLE_PROCESSOR_BEAN_NAME = "lifecycleProcessor";
|
||||
|
||||
|
||||
/**
|
||||
* Boolean flag controlled by a {@code spring.spel.ignore} system property that
|
||||
* instructs Spring to ignore SpEL, i.e. to not initialize the SpEL infrastructure.
|
||||
@@ -571,6 +570,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
|
||||
// Invoke factory processors registered as beans in the context.
|
||||
invokeBeanFactoryPostProcessors(beanFactory);
|
||||
|
||||
// Register bean processors that intercept bean creation.
|
||||
registerBeanPostProcessors(beanFactory);
|
||||
beanPostProcess.end();
|
||||
@@ -774,9 +774,8 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the {@link MessageSource}.
|
||||
* <p>Uses parent's {@code MessageSource} if none defined in this context.
|
||||
* @see #MESSAGE_SOURCE_BEAN_NAME
|
||||
* Initialize the MessageSource.
|
||||
* Use parent's if none defined in this context.
|
||||
*/
|
||||
protected void initMessageSource() {
|
||||
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
|
||||
@@ -808,9 +807,8 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the {@link ApplicationEventMulticaster}.
|
||||
* <p>Uses {@link SimpleApplicationEventMulticaster} if none defined in the context.
|
||||
* @see #APPLICATION_EVENT_MULTICASTER_BEAN_NAME
|
||||
* Initialize the ApplicationEventMulticaster.
|
||||
* Uses SimpleApplicationEventMulticaster if none defined in the context.
|
||||
* @see org.springframework.context.event.SimpleApplicationEventMulticaster
|
||||
*/
|
||||
protected void initApplicationEventMulticaster() {
|
||||
@@ -833,16 +831,15 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the {@link LifecycleProcessor}.
|
||||
* <p>Uses {@link DefaultLifecycleProcessor} if none defined in the context.
|
||||
* @since 3.0
|
||||
* @see #LIFECYCLE_PROCESSOR_BEAN_NAME
|
||||
* Initialize the LifecycleProcessor.
|
||||
* Uses DefaultLifecycleProcessor if none defined in the context.
|
||||
* @see org.springframework.context.support.DefaultLifecycleProcessor
|
||||
*/
|
||||
protected void initLifecycleProcessor() {
|
||||
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
|
||||
if (beanFactory.containsLocalBean(LIFECYCLE_PROCESSOR_BEAN_NAME)) {
|
||||
this.lifecycleProcessor = beanFactory.getBean(LIFECYCLE_PROCESSOR_BEAN_NAME, LifecycleProcessor.class);
|
||||
this.lifecycleProcessor =
|
||||
beanFactory.getBean(LIFECYCLE_PROCESSOR_BEAN_NAME, LifecycleProcessor.class);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Using LifecycleProcessor [" + this.lifecycleProcessor + "]");
|
||||
}
|
||||
|
||||
+1
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -126,7 +126,6 @@ public abstract class AbstractRefreshableApplicationContext extends AbstractAppl
|
||||
try {
|
||||
DefaultListableBeanFactory beanFactory = createBeanFactory();
|
||||
beanFactory.setSerializationId(getId());
|
||||
beanFactory.setApplicationStartup(getApplicationStartup());
|
||||
customizeBeanFactory(beanFactory);
|
||||
loadBeanDefinitions(beanFactory);
|
||||
this.beanFactory = beanFactory;
|
||||
|
||||
+6
-14
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -45,11 +45,7 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Spring's default implementation of the {@link LifecycleProcessor} strategy.
|
||||
*
|
||||
* <p>Provides interaction with {@link Lifecycle} and {@link SmartLifecycle} beans in
|
||||
* groups for specific phases, on startup/shutdown as well as for explicit start/stop
|
||||
* interactions on a {@link org.springframework.context.ConfigurableApplicationContext}.
|
||||
* Default implementation of the {@link LifecycleProcessor} strategy.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Juergen Hoeller
|
||||
@@ -149,13 +145,13 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
|
||||
|
||||
lifecycleBeans.forEach((beanName, bean) -> {
|
||||
if (!autoStartupOnly || (bean instanceof SmartLifecycle && ((SmartLifecycle) bean).isAutoStartup())) {
|
||||
int startupPhase = getPhase(bean);
|
||||
phases.computeIfAbsent(startupPhase,
|
||||
phase -> new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly)
|
||||
int phase = getPhase(bean);
|
||||
phases.computeIfAbsent(
|
||||
phase,
|
||||
p -> new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly)
|
||||
).add(beanName, bean);
|
||||
}
|
||||
});
|
||||
|
||||
if (!phases.isEmpty()) {
|
||||
phases.values().forEach(LifecycleGroup::start);
|
||||
}
|
||||
@@ -195,7 +191,6 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
|
||||
private void stopBeans() {
|
||||
Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
|
||||
Map<Integer, LifecycleGroup> phases = new HashMap<>();
|
||||
|
||||
lifecycleBeans.forEach((beanName, bean) -> {
|
||||
int shutdownPhase = getPhase(bean);
|
||||
LifecycleGroup group = phases.get(shutdownPhase);
|
||||
@@ -205,7 +200,6 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
|
||||
}
|
||||
group.add(beanName, bean);
|
||||
});
|
||||
|
||||
if (!phases.isEmpty()) {
|
||||
List<Integer> keys = new ArrayList<>(phases.keySet());
|
||||
keys.sort(Collections.reverseOrder());
|
||||
@@ -320,8 +314,6 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
|
||||
/**
|
||||
* Helper class for maintaining a group of Lifecycle beans that should be started
|
||||
* and stopped together based on their 'phase' value (or the default value of 0).
|
||||
* The group is expected to be created in an ad-hoc fashion and group members are
|
||||
* expected to always have the same 'phase' value.
|
||||
*/
|
||||
private class LifecycleGroup {
|
||||
|
||||
|
||||
+4
-28
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,10 +22,8 @@ import java.text.SimpleDateFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.EnumMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.springframework.format.Formatter;
|
||||
@@ -37,14 +35,9 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A formatter for {@link java.util.Date} types.
|
||||
*
|
||||
* <p>Supports the configuration of an explicit date time pattern, timezone,
|
||||
* locale, and fallback date time patterns for lenient parsing.
|
||||
*
|
||||
* <p>Common ISO patterns for UTC instants are applied at millisecond precision.
|
||||
* Note that {@link org.springframework.format.datetime.standard.InstantFormatter}
|
||||
* is recommended for flexible UTC parsing into a {@link java.time.Instant} instead.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Juergen Hoeller
|
||||
* @author Phillip Webb
|
||||
@@ -58,21 +51,12 @@ public class DateFormatter implements Formatter<Date> {
|
||||
|
||||
private static final Map<ISO, String> ISO_PATTERNS;
|
||||
|
||||
private static final Map<ISO, String> ISO_FALLBACK_PATTERNS;
|
||||
|
||||
static {
|
||||
// We use an EnumMap instead of Map.of(...) since the former provides better performance.
|
||||
Map<ISO, String> formats = new EnumMap<>(ISO.class);
|
||||
formats.put(ISO.DATE, "yyyy-MM-dd");
|
||||
formats.put(ISO.TIME, "HH:mm:ss.SSSXXX");
|
||||
formats.put(ISO.DATE_TIME, "yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
|
||||
ISO_PATTERNS = Collections.unmodifiableMap(formats);
|
||||
|
||||
// Fallback format for the time part without milliseconds.
|
||||
Map<ISO, String> fallbackFormats = new EnumMap<>(ISO.class);
|
||||
fallbackFormats.put(ISO.TIME, "HH:mm:ssXXX");
|
||||
fallbackFormats.put(ISO.DATE_TIME, "yyyy-MM-dd'T'HH:mm:ssXXX");
|
||||
ISO_FALLBACK_PATTERNS = Collections.unmodifiableMap(fallbackFormats);
|
||||
}
|
||||
|
||||
|
||||
@@ -217,16 +201,8 @@ public class DateFormatter implements Formatter<Date> {
|
||||
return getDateFormat(locale).parse(text);
|
||||
}
|
||||
catch (ParseException ex) {
|
||||
Set<String> fallbackPatterns = new LinkedHashSet<>();
|
||||
String isoPattern = ISO_FALLBACK_PATTERNS.get(this.iso);
|
||||
if (isoPattern != null) {
|
||||
fallbackPatterns.add(isoPattern);
|
||||
}
|
||||
if (!ObjectUtils.isEmpty(this.fallbackPatterns)) {
|
||||
Collections.addAll(fallbackPatterns, this.fallbackPatterns);
|
||||
}
|
||||
if (!fallbackPatterns.isEmpty()) {
|
||||
for (String pattern : fallbackPatterns) {
|
||||
for (String pattern : this.fallbackPatterns) {
|
||||
try {
|
||||
DateFormat dateFormat = configureDateFormat(new SimpleDateFormat(pattern, locale));
|
||||
// Align timezone for parsing format with printing format if ISO is set.
|
||||
@@ -244,8 +220,8 @@ public class DateFormatter implements Formatter<Date> {
|
||||
}
|
||||
if (this.source != null) {
|
||||
ParseException parseException = new ParseException(
|
||||
String.format("Unable to parse date time value \"%s\" using configuration from %s", text, this.source),
|
||||
ex.getErrorOffset());
|
||||
String.format("Unable to parse date time value \"%s\" using configuration from %s", text, this.source),
|
||||
ex.getErrorOffset());
|
||||
parseException.initCause(ex);
|
||||
throw parseException;
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -41,12 +41,12 @@ public class InstantFormatter implements Formatter<Instant> {
|
||||
|
||||
@Override
|
||||
public Instant parse(String text, Locale locale) throws ParseException {
|
||||
if (!text.isEmpty() && Character.isAlphabetic(text.charAt(0))) {
|
||||
if (text.length() > 0 && Character.isAlphabetic(text.charAt(0))) {
|
||||
// assuming RFC-1123 value a la "Tue, 3 Jun 2008 11:05:30 GMT"
|
||||
return Instant.from(DateTimeFormatter.RFC_1123_DATE_TIME.parse(text));
|
||||
}
|
||||
else {
|
||||
// assuming UTC instant a la "2007-12-03T10:15:30.000Z"
|
||||
// assuming UTC instant a la "2007-12-03T10:15:30.00Z"
|
||||
return Instant.parse(text);
|
||||
}
|
||||
}
|
||||
|
||||
+11
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -133,6 +133,11 @@ 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) {
|
||||
@@ -173,10 +178,11 @@ public class ConcurrentTaskExecutor implements AsyncListenableTaskExecutor, Sche
|
||||
}
|
||||
|
||||
|
||||
private TaskExecutorAdapter getAdaptedExecutor(Executor originalExecutor) {
|
||||
TaskExecutorAdapter adapter =
|
||||
(managedExecutorServiceClass != null && managedExecutorServiceClass.isInstance(originalExecutor) ?
|
||||
new ManagedTaskExecutorAdapter(originalExecutor) : new TaskExecutorAdapter(originalExecutor));
|
||||
private TaskExecutorAdapter getAdaptedExecutor(Executor concurrentExecutor) {
|
||||
if (managedExecutorServiceClass != null && managedExecutorServiceClass.isInstance(concurrentExecutor)) {
|
||||
return new ManagedTaskExecutorAdapter(concurrentExecutor);
|
||||
}
|
||||
TaskExecutorAdapter adapter = new TaskExecutorAdapter(concurrentExecutor);
|
||||
if (this.taskDecorator != null) {
|
||||
adapter.setTaskDecorator(this.taskDecorator);
|
||||
}
|
||||
|
||||
+1
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -177,7 +177,6 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
+3
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -45,9 +45,8 @@ import org.springframework.util.concurrent.ListenableFuture;
|
||||
import org.springframework.util.concurrent.ListenableFutureTask;
|
||||
|
||||
/**
|
||||
* A standard implementation of Spring's {@link TaskScheduler} interface, wrapping
|
||||
* a native {@link java.util.concurrent.ScheduledThreadPoolExecutor} and providing
|
||||
* all applicable configuration options for it.
|
||||
* Implementation of Spring's {@link TaskScheduler} interface, wrapping
|
||||
* a native {@link java.util.concurrent.ScheduledThreadPoolExecutor}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Mark Fisher
|
||||
@@ -155,7 +154,6 @@ public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport
|
||||
* @see Clock#systemDefaultZone()
|
||||
*/
|
||||
public void setClock(Clock clock) {
|
||||
Assert.notNull(clock, "Clock must not be null");
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
|
||||
+6
-3
@@ -28,14 +28,17 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
*
|
||||
* @author Adrian Colyer
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
class OverloadedAdviceTests {
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("resource")
|
||||
void testConfigParsingWithMismatchedAdviceMethod() {
|
||||
new ClassPathXmlApplicationContext(getClass().getSimpleName() + ".xml", getClass());
|
||||
void testExceptionOnConfigParsingWithMismatchedAdviceMethod() {
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(() -> new ClassPathXmlApplicationContext(getClass().getSimpleName() + ".xml", getClass()))
|
||||
.havingRootCause()
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.as("invalidAbsoluteTypeName should be detected by AJ").withMessageContaining("invalidAbsoluteTypeName");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+1
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -268,7 +268,6 @@ public class ClassPathBeanDefinitionScannerTests {
|
||||
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
|
||||
scanner.setIncludeAnnotationConfig(false);
|
||||
scanner.scan("org.springframework.context.annotation2");
|
||||
|
||||
assertThatIllegalStateException().isThrownBy(() -> scanner.scan(BASE_PACKAGE))
|
||||
.withMessageContaining("myNamedDao")
|
||||
.withMessageContaining(NamedStubDao.class.getName())
|
||||
|
||||
+40
-60
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,9 +29,7 @@ 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;
|
||||
@@ -41,62 +39,51 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* {@link FactoryBean FactoryBeans} defined in the configuration.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
class ConfigurationWithFactoryBeanEarlyDeductionTests {
|
||||
public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
|
||||
|
||||
@Test
|
||||
void preFreezeDirect() {
|
||||
public void preFreezeDirect() {
|
||||
assertPreFreeze(DirectConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void postFreezeDirect() {
|
||||
public void postFreezeDirect() {
|
||||
assertPostFreeze(DirectConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void preFreezeGenericMethod() {
|
||||
public void preFreezeGenericMethod() {
|
||||
assertPreFreeze(GenericMethodConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void postFreezeGenericMethod() {
|
||||
public void postFreezeGenericMethod() {
|
||||
assertPostFreeze(GenericMethodConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void preFreezeGenericClass() {
|
||||
public void preFreezeGenericClass() {
|
||||
assertPreFreeze(GenericClassConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void postFreezeGenericClass() {
|
||||
public void postFreezeGenericClass() {
|
||||
assertPostFreeze(GenericClassConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void preFreezeAttribute() {
|
||||
public void preFreezeAttribute() {
|
||||
assertPreFreeze(AttributeClassConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void postFreezeAttribute() {
|
||||
public void postFreezeAttribute() {
|
||||
assertPostFreeze(AttributeClassConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void preFreezeTargetType() {
|
||||
assertPreFreeze(TargetTypeConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void postFreezeTargetType() {
|
||||
assertPostFreeze(TargetTypeConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void preFreezeUnresolvedGenericFactoryBean() {
|
||||
public 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
|
||||
@@ -121,13 +108,14 @@ class ConfigurationWithFactoryBeanEarlyDeductionTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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 {
|
||||
@@ -150,38 +138,41 @@ class ConfigurationWithFactoryBeanEarlyDeductionTests {
|
||||
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 {
|
||||
ResolvableType typeToMatch = ResolvableType.forClassWithGenerics(MyBean.class, String.class);
|
||||
this.names = beanFactory.getBeanNamesForType(typeToMatch, true, false);
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
|
||||
throws BeansException {
|
||||
this.names = beanFactory.getBeanNamesForType(MyBean.class, true, false);
|
||||
}
|
||||
|
||||
public String[] getNames() {
|
||||
return this.names;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class DirectConfiguration {
|
||||
|
||||
@Bean
|
||||
MyBean<String> myBean() {
|
||||
return new MyBean<>();
|
||||
MyBean myBean() {
|
||||
return new MyBean();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class GenericMethodConfiguration {
|
||||
|
||||
@Bean
|
||||
FactoryBean<MyBean<String>> myBean() {
|
||||
return new TestFactoryBean<>(new MyBean<>());
|
||||
FactoryBean<MyBean> myBean() {
|
||||
return new TestFactoryBean<>(new MyBean());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@@ -191,11 +182,13 @@ class ConfigurationWithFactoryBeanEarlyDeductionTests {
|
||||
MyFactoryBean myBean() {
|
||||
return new MyFactoryBean();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(AttributeClassRegistrar.class)
|
||||
static class AttributeClassConfiguration {
|
||||
|
||||
}
|
||||
|
||||
static class AttributeClassRegistrar implements ImportBeanDefinitionRegistrar {
|
||||
@@ -204,34 +197,16 @@ class ConfigurationWithFactoryBeanEarlyDeductionTests {
|
||||
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
|
||||
BeanDefinition definition = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
RawWithAbstractObjectTypeFactoryBean.class).getBeanDefinition();
|
||||
definition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE,
|
||||
ResolvableType.forClassWithGenerics(MyBean.class, String.class));
|
||||
definition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, MyBean.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)));
|
||||
definition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE,
|
||||
ResolvableType.forClassWithGenerics(MyBean.class, String.class));
|
||||
registry.registerBeanDefinition("myBean", definition);
|
||||
}
|
||||
}
|
||||
|
||||
abstract static class AbstractMyBean {
|
||||
}
|
||||
|
||||
static class MyBean<T> extends AbstractMyBean {
|
||||
static class MyBean extends AbstractMyBean {
|
||||
}
|
||||
|
||||
static class TestFactoryBean<T> implements FactoryBean<T> {
|
||||
@@ -243,7 +218,7 @@ class ConfigurationWithFactoryBeanEarlyDeductionTests {
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getObject() {
|
||||
public T getObject() throws Exception {
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
@@ -251,26 +226,31 @@ class ConfigurationWithFactoryBeanEarlyDeductionTests {
|
||||
public Class<?> getObjectType() {
|
||||
return this.instance.getClass();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class MyFactoryBean extends TestFactoryBean<MyBean<String>> {
|
||||
static class MyFactoryBean extends TestFactoryBean<MyBean> {
|
||||
|
||||
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 {
|
||||
throw new IllegalStateException();
|
||||
return object;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return MyBean.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+57
-36
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,6 +23,9 @@ import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.format.annotation.DateTimeFormat.ISO;
|
||||
@@ -30,88 +33,83 @@ import org.springframework.format.annotation.DateTimeFormat.ISO;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Tests for {@link DateFormatter}.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Phillip Webb
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
class DateFormatterTests {
|
||||
public class DateFormatterTests {
|
||||
|
||||
private static final TimeZone UTC = TimeZone.getTimeZone("UTC");
|
||||
|
||||
|
||||
@Test
|
||||
void shouldPrintAndParseDefault() throws Exception {
|
||||
public void shouldPrintAndParseDefault() throws Exception {
|
||||
DateFormatter formatter = new DateFormatter();
|
||||
formatter.setTimeZone(UTC);
|
||||
|
||||
Date date = getDate(2009, Calendar.JUNE, 1);
|
||||
assertThat(formatter.print(date, Locale.US)).isEqualTo("Jun 1, 2009");
|
||||
assertThat(formatter.parse("Jun 1, 2009", Locale.US)).isEqualTo(date);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPrintAndParseFromPattern() throws ParseException {
|
||||
public void shouldPrintAndParseFromPattern() throws ParseException {
|
||||
DateFormatter formatter = new DateFormatter("yyyy-MM-dd");
|
||||
formatter.setTimeZone(UTC);
|
||||
|
||||
Date date = getDate(2009, Calendar.JUNE, 1);
|
||||
assertThat(formatter.print(date, Locale.US)).isEqualTo("2009-06-01");
|
||||
assertThat(formatter.parse("2009-06-01", Locale.US)).isEqualTo(date);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPrintAndParseShort() throws Exception {
|
||||
public void shouldPrintAndParseShort() throws Exception {
|
||||
DateFormatter formatter = new DateFormatter();
|
||||
formatter.setTimeZone(UTC);
|
||||
formatter.setStyle(DateFormat.SHORT);
|
||||
|
||||
Date date = getDate(2009, Calendar.JUNE, 1);
|
||||
assertThat(formatter.print(date, Locale.US)).isEqualTo("6/1/09");
|
||||
assertThat(formatter.parse("6/1/09", Locale.US)).isEqualTo(date);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPrintAndParseMedium() throws Exception {
|
||||
public void shouldPrintAndParseMedium() throws Exception {
|
||||
DateFormatter formatter = new DateFormatter();
|
||||
formatter.setTimeZone(UTC);
|
||||
formatter.setStyle(DateFormat.MEDIUM);
|
||||
|
||||
Date date = getDate(2009, Calendar.JUNE, 1);
|
||||
assertThat(formatter.print(date, Locale.US)).isEqualTo("Jun 1, 2009");
|
||||
assertThat(formatter.parse("Jun 1, 2009", Locale.US)).isEqualTo(date);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPrintAndParseLong() throws Exception {
|
||||
public void shouldPrintAndParseLong() throws Exception {
|
||||
DateFormatter formatter = new DateFormatter();
|
||||
formatter.setTimeZone(UTC);
|
||||
formatter.setStyle(DateFormat.LONG);
|
||||
|
||||
Date date = getDate(2009, Calendar.JUNE, 1);
|
||||
assertThat(formatter.print(date, Locale.US)).isEqualTo("June 1, 2009");
|
||||
assertThat(formatter.parse("June 1, 2009", Locale.US)).isEqualTo(date);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPrintAndParseFull() throws Exception {
|
||||
public void shouldPrintAndParseFull() throws Exception {
|
||||
DateFormatter formatter = new DateFormatter();
|
||||
formatter.setTimeZone(UTC);
|
||||
formatter.setStyle(DateFormat.FULL);
|
||||
|
||||
Date date = getDate(2009, Calendar.JUNE, 1);
|
||||
assertThat(formatter.print(date, Locale.US)).isEqualTo("Monday, June 1, 2009");
|
||||
assertThat(formatter.parse("Monday, June 1, 2009", Locale.US)).isEqualTo(date);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPrintAndParseIsoDate() throws Exception {
|
||||
public void shouldPrintAndParseISODate() throws Exception {
|
||||
DateFormatter formatter = new DateFormatter();
|
||||
formatter.setTimeZone(UTC);
|
||||
formatter.setIso(ISO.DATE);
|
||||
|
||||
Date date = getDate(2009, Calendar.JUNE, 1, 14, 23, 5, 3);
|
||||
assertThat(formatter.print(date, Locale.US)).isEqualTo("2009-06-01");
|
||||
assertThat(formatter.parse("2009-6-01", Locale.US))
|
||||
@@ -119,56 +117,79 @@ class DateFormatterTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPrintAndParseIsoTime() throws Exception {
|
||||
public void shouldPrintAndParseISOTime() throws Exception {
|
||||
DateFormatter formatter = new DateFormatter();
|
||||
formatter.setTimeZone(UTC);
|
||||
formatter.setIso(ISO.TIME);
|
||||
|
||||
Date date = getDate(2009, Calendar.JANUARY, 1, 14, 23, 5, 3);
|
||||
assertThat(formatter.print(date, Locale.US)).isEqualTo("14:23:05.003Z");
|
||||
assertThat(formatter.parse("14:23:05.003Z", Locale.US))
|
||||
.isEqualTo(getDate(1970, Calendar.JANUARY, 1, 14, 23, 5, 3));
|
||||
|
||||
date = getDate(2009, Calendar.JANUARY, 1, 14, 23, 5, 0);
|
||||
assertThat(formatter.print(date, Locale.US)).isEqualTo("14:23:05.000Z");
|
||||
assertThat(formatter.parse("14:23:05Z", Locale.US))
|
||||
.isEqualTo(getDate(1970, Calendar.JANUARY, 1, 14, 23, 5, 0).toInstant());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPrintAndParseIsoDateTime() throws Exception {
|
||||
public void shouldPrintAndParseISODateTime() throws Exception {
|
||||
DateFormatter formatter = new DateFormatter();
|
||||
formatter.setTimeZone(UTC);
|
||||
formatter.setIso(ISO.DATE_TIME);
|
||||
|
||||
Date date = getDate(2009, Calendar.JUNE, 1, 14, 23, 5, 3);
|
||||
assertThat(formatter.print(date, Locale.US)).isEqualTo("2009-06-01T14:23:05.003Z");
|
||||
assertThat(formatter.parse("2009-06-01T14:23:05.003Z", Locale.US)).isEqualTo(date);
|
||||
|
||||
date = getDate(2009, Calendar.JUNE, 1, 14, 23, 5, 0);
|
||||
assertThat(formatter.print(date, Locale.US)).isEqualTo("2009-06-01T14:23:05.000Z");
|
||||
assertThat(formatter.parse("2009-06-01T14:23:05Z", Locale.US)).isEqualTo(date.toInstant());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldThrowOnUnsupportedStylePattern() {
|
||||
public void shouldSupportJodaStylePatterns() throws Exception {
|
||||
String[] chars = { "S", "M", "-" };
|
||||
for (String d : chars) {
|
||||
for (String t : chars) {
|
||||
String style = d + t;
|
||||
if (!style.equals("--")) {
|
||||
Date date = getDate(2009, Calendar.JUNE, 10, 14, 23, 0, 0);
|
||||
if (t.equals("-")) {
|
||||
date = getDate(2009, Calendar.JUNE, 10);
|
||||
}
|
||||
else if (d.equals("-")) {
|
||||
date = getDate(1970, Calendar.JANUARY, 1, 14, 23, 0, 0);
|
||||
}
|
||||
testJodaStylePatterns(style, Locale.US, date);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void testJodaStylePatterns(String style, Locale locale, Date date) throws Exception {
|
||||
DateFormatter formatter = new DateFormatter();
|
||||
formatter.setTimeZone(UTC);
|
||||
formatter.setStylePattern(style);
|
||||
DateTimeFormatter jodaFormatter = DateTimeFormat.forStyle(style).withLocale(locale).withZone(DateTimeZone.UTC);
|
||||
String jodaPrinted = jodaFormatter.print(date.getTime());
|
||||
assertThat(formatter.print(date, locale))
|
||||
.as("Unable to print style pattern " + style)
|
||||
.isEqualTo(jodaPrinted);
|
||||
assertThat(formatter.parse(jodaPrinted, locale))
|
||||
.as("Unable to parse style pattern " + style)
|
||||
.isEqualTo(date);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldThrowOnUnsupportedStylePattern() throws Exception {
|
||||
DateFormatter formatter = new DateFormatter();
|
||||
formatter.setStylePattern("OO");
|
||||
|
||||
assertThatIllegalStateException().isThrownBy(() -> formatter.parse("2009", Locale.US))
|
||||
.withMessageContaining("Unsupported style pattern 'OO'");
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
formatter.parse("2009", Locale.US))
|
||||
.withMessageContaining("Unsupported style pattern 'OO'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUseCorrectOrder() {
|
||||
public void shouldUseCorrectOrder() throws Exception {
|
||||
DateFormatter formatter = new DateFormatter();
|
||||
formatter.setTimeZone(UTC);
|
||||
formatter.setStyle(DateFormat.SHORT);
|
||||
formatter.setStylePattern("L-");
|
||||
formatter.setIso(ISO.DATE_TIME);
|
||||
formatter.setPattern("yyyy");
|
||||
|
||||
Date date = getDate(2009, Calendar.JUNE, 1, 14, 23, 5, 3);
|
||||
|
||||
assertThat(formatter.print(date, Locale.US)).as("uses pattern").isEqualTo("2009");
|
||||
|
||||
formatter.setPattern("");
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -52,7 +52,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
class DateFormattingTests {
|
||||
public class DateFormattingTests {
|
||||
|
||||
private final FormattingConversionService conversionService = new FormattingConversionService();
|
||||
|
||||
|
||||
+59
-57
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -105,7 +105,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localDate", "10/31/09");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("localDate")).isEqualTo("10/31/09");
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localDate", "October 31, 2009");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("localDate")).isEqualTo("October 31, 2009");
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localDate", "20091031");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("localDate")).isEqualTo("20091031");
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localDate", new String[] {"10/31/09"});
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -146,7 +146,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("styleLocalDate", "Oct 31, 2009");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("styleLocalDate")).isEqualTo("Oct 31, 2009");
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("children[0].styleLocalDate", "Oct 31, 2009");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("children[0].styleLocalDate")).isEqualTo("Oct 31, 2009");
|
||||
}
|
||||
|
||||
@@ -174,7 +174,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("styleLocalDate", "Oct 31, 2009");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("styleLocalDate")).isEqualTo("Oct 31, 2009");
|
||||
}
|
||||
|
||||
@@ -193,7 +193,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localDate", new GregorianCalendar(2009, 9, 31, 0, 0));
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("localDate")).isEqualTo("10/31/09");
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localTime", "12:00 PM");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("localTime")).isEqualTo("12:00 PM");
|
||||
}
|
||||
|
||||
@@ -214,7 +214,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localTime", "12:00:00 PM");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("localTime")).isEqualTo("12:00:00 PM");
|
||||
}
|
||||
|
||||
@@ -226,7 +226,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localTime", "130000");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("localTime")).isEqualTo("130000");
|
||||
}
|
||||
|
||||
@@ -235,7 +235,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("styleLocalTime", "12:00:00 PM");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("styleLocalTime")).isEqualTo("12:00:00 PM");
|
||||
}
|
||||
|
||||
@@ -244,7 +244,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localTime", new GregorianCalendar(1970, 0, 0, 12, 0));
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("localTime")).isEqualTo("12:00 PM");
|
||||
}
|
||||
|
||||
@@ -253,9 +253,10 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localDateTime", LocalDateTime.of(2009, 10, 31, 12, 0));
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
String value = binder.getBindingResult().getFieldValue("localDateTime").toString();
|
||||
assertThat(value).startsWith("10/31/09").endsWith("12:00 PM");
|
||||
assertThat(value.startsWith("10/31/09")).isTrue();
|
||||
assertThat(value.endsWith("12:00 PM")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -263,9 +264,10 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("styleLocalDateTime", LocalDateTime.of(2009, 10, 31, 12, 0));
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
String value = binder.getBindingResult().getFieldValue("styleLocalDateTime").toString();
|
||||
assertThat(value).startsWith("Oct 31, 2009").endsWith("12:00:00 PM");
|
||||
assertThat(value.startsWith("Oct 31, 2009")).isTrue();
|
||||
assertThat(value.endsWith("12:00:00 PM")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -273,9 +275,10 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localDateTime", new GregorianCalendar(2009, 9, 31, 12, 0));
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
String value = binder.getBindingResult().getFieldValue("localDateTime").toString();
|
||||
assertThat(value).startsWith("10/31/09").endsWith("12:00 PM");
|
||||
assertThat(value.startsWith("10/31/09")).isTrue();
|
||||
assertThat(value.endsWith("12:00 PM")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -286,9 +289,10 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localDateTime", LocalDateTime.of(2009, 10, 31, 12, 0));
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
String value = binder.getBindingResult().getFieldValue("localDateTime").toString();
|
||||
assertThat(value).startsWith("Oct 31, 2009").endsWith("12:00:00 PM");
|
||||
assertThat(value.startsWith("Oct 31, 2009")).isTrue();
|
||||
assertThat(value.endsWith("12:00:00 PM")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -296,7 +300,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("patternLocalDateTime", "10/31/09 12:00 PM");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("patternLocalDateTime")).isEqualTo("10/31/09 12:00 PM");
|
||||
}
|
||||
|
||||
@@ -313,7 +317,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("isoLocalDate", "2009-10-31");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("isoLocalDate")).isEqualTo("2009-10-31");
|
||||
}
|
||||
|
||||
@@ -352,7 +356,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("isoLocalTime", "12:00:00");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("isoLocalTime")).isEqualTo("12:00:00");
|
||||
}
|
||||
|
||||
@@ -361,7 +365,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("isoLocalTime", "12:00:00.000-05:00");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("isoLocalTime")).isEqualTo("12:00:00");
|
||||
}
|
||||
|
||||
@@ -370,7 +374,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("isoLocalDateTime", "2009-10-31T12:00:00");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("isoLocalDateTime")).isEqualTo("2009-10-31T12:00:00");
|
||||
}
|
||||
|
||||
@@ -379,7 +383,7 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("isoLocalDateTime", "2009-10-31T12:00:00.000Z");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("isoLocalDateTime")).isEqualTo("2009-10-31T12:00:00");
|
||||
}
|
||||
|
||||
@@ -388,8 +392,8 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("instant", "2009-10-31T12:00:00.000Z");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("instant").toString()).startsWith("2009-10-31T12:00");
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("instant").toString().startsWith("2009-10-31T12:00")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -401,8 +405,8 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("instant", new Date(109, 9, 31, 12, 0));
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("instant").toString()).startsWith("2009-10-31");
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("instant").toString().startsWith("2009-10-31")).isTrue();
|
||||
}
|
||||
finally {
|
||||
TimeZone.setDefault(defaultZone);
|
||||
@@ -414,8 +418,8 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("period", "P6Y3M1D");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("period").toString()).isEqualTo("P6Y3M1D");
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("period").toString().equals("P6Y3M1D")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -423,8 +427,8 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("duration", "PT8H6M12.345S");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("duration").toString()).isEqualTo("PT8H6M12.345S");
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("duration").toString().equals("PT8H6M12.345S")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -432,8 +436,8 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("year", "2007");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("year").toString()).isEqualTo("2007");
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("year").toString().equals("2007")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -441,8 +445,8 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("month", "JULY");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("month").toString()).isEqualTo("JULY");
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("month").toString().equals("JULY")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -450,8 +454,8 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("month", "July");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("month").toString()).isEqualTo("JULY");
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("month").toString().equals("JULY")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -459,8 +463,8 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("yearMonth", "2007-12");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("yearMonth").toString()).isEqualTo("2007-12");
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("yearMonth").toString().equals("2007-12")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -468,11 +472,10 @@ class DateTimeFormattingTests {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("monthDay", "--12-03");
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
assertThat(binder.getBindingResult().getFieldValue("monthDay").toString()).isEqualTo("--12-03");
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isEqualTo(0);
|
||||
assertThat(binder.getBindingResult().getFieldValue("monthDay").toString().equals("--12-03")).isTrue();
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
class FallbackPatternTests {
|
||||
|
||||
@@ -484,7 +487,7 @@ class DateTimeFormattingTests {
|
||||
propertyValues.add(propertyName, propertyValue);
|
||||
binder.bind(propertyValues);
|
||||
BindingResult bindingResult = binder.getBindingResult();
|
||||
assertThat(bindingResult.getErrorCount()).isZero();
|
||||
assertThat(bindingResult.getErrorCount()).isEqualTo(0);
|
||||
assertThat(bindingResult.getFieldValue(propertyName)).isEqualTo("3/2/21");
|
||||
}
|
||||
|
||||
@@ -496,12 +499,11 @@ class DateTimeFormattingTests {
|
||||
propertyValues.add(propertyName, propertyValue);
|
||||
binder.bind(propertyValues);
|
||||
BindingResult bindingResult = binder.getBindingResult();
|
||||
assertThat(bindingResult.getErrorCount()).isZero();
|
||||
assertThat(bindingResult.getErrorCount()).isEqualTo(0);
|
||||
assertThat(bindingResult.getFieldValue(propertyName)).isEqualTo("2021-03-02");
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "input date: {0}")
|
||||
// @ValueSource(strings = {"12:00:00\u202FPM", "12:00:00", "12:00"})
|
||||
@ValueSource(strings = {"12:00:00 PM", "12:00:00", "12:00"})
|
||||
void styleLocalTime(String propertyValue) {
|
||||
String propertyName = "styleLocalTimeWithFallbackPatterns";
|
||||
@@ -509,8 +511,7 @@ class DateTimeFormattingTests {
|
||||
propertyValues.add(propertyName, propertyValue);
|
||||
binder.bind(propertyValues);
|
||||
BindingResult bindingResult = binder.getBindingResult();
|
||||
assertThat(bindingResult.getErrorCount()).isZero();
|
||||
// assertThat(bindingResult.getFieldValue(propertyName)).asString().matches("12:00:00\\SPM");
|
||||
assertThat(bindingResult.getErrorCount()).isEqualTo(0);
|
||||
assertThat(bindingResult.getFieldValue(propertyName)).isEqualTo("12:00:00 PM");
|
||||
}
|
||||
|
||||
@@ -522,7 +523,7 @@ class DateTimeFormattingTests {
|
||||
propertyValues.add(propertyName, propertyValue);
|
||||
binder.bind(propertyValues);
|
||||
BindingResult bindingResult = binder.getBindingResult();
|
||||
assertThat(bindingResult.getErrorCount()).isZero();
|
||||
assertThat(bindingResult.getErrorCount()).isEqualTo(0);
|
||||
assertThat(bindingResult.getFieldValue(propertyName)).isEqualTo("2021-03-02T12:00:00");
|
||||
}
|
||||
|
||||
@@ -564,10 +565,10 @@ class DateTimeFormattingTests {
|
||||
@DateTimeFormat(style = "M-")
|
||||
private LocalDate styleLocalDate;
|
||||
|
||||
@DateTimeFormat(style = "S-", fallbackPatterns = {"yyyy-MM-dd", "yyyyMMdd", "yyyy.MM.dd"})
|
||||
@DateTimeFormat(style = "S-", fallbackPatterns = { "yyyy-MM-dd", "yyyyMMdd", "yyyy.MM.dd" })
|
||||
private LocalDate styleLocalDateWithFallbackPatterns;
|
||||
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd", fallbackPatterns = {"M/d/yy", "yyyyMMdd", "yyyy.MM.dd"})
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd", fallbackPatterns = { "M/d/yy", "yyyyMMdd", "yyyy.MM.dd" })
|
||||
private LocalDate patternLocalDateWithFallbackPatterns;
|
||||
|
||||
private LocalTime localTime;
|
||||
@@ -575,7 +576,7 @@ class DateTimeFormattingTests {
|
||||
@DateTimeFormat(style = "-M")
|
||||
private LocalTime styleLocalTime;
|
||||
|
||||
@DateTimeFormat(style = "-M", fallbackPatterns = {"HH:mm:ss", "HH:mm"})
|
||||
@DateTimeFormat(style = "-M", fallbackPatterns = { "HH:mm:ss", "HH:mm"})
|
||||
private LocalTime styleLocalTimeWithFallbackPatterns;
|
||||
|
||||
private LocalDateTime localDateTime;
|
||||
@@ -595,7 +596,7 @@ class DateTimeFormattingTests {
|
||||
@DateTimeFormat(iso = ISO.DATE_TIME)
|
||||
private LocalDateTime isoLocalDateTime;
|
||||
|
||||
@DateTimeFormat(iso = ISO.DATE_TIME, fallbackPatterns = {"yyyy-MM-dd HH:mm:ss", "M/d/yy HH:mm"})
|
||||
@DateTimeFormat(iso = ISO.DATE_TIME, fallbackPatterns = { "yyyy-MM-dd HH:mm:ss", "M/d/yy HH:mm"})
|
||||
private LocalDateTime isoLocalDateTimeWithFallbackPatterns;
|
||||
|
||||
private Instant instant;
|
||||
@@ -614,6 +615,7 @@ class DateTimeFormattingTests {
|
||||
|
||||
private final List<DateTimeBean> children = new ArrayList<>();
|
||||
|
||||
|
||||
public LocalDate getLocalDate() {
|
||||
return this.localDate;
|
||||
}
|
||||
|
||||
+10
-9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,7 +19,6 @@ package org.springframework.format.datetime.standard;
|
||||
import java.text.ParseException;
|
||||
import java.time.Instant;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Locale;
|
||||
import java.util.Random;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -50,12 +49,13 @@ class InstantFormatterTests {
|
||||
|
||||
private final InstantFormatter instantFormatter = new InstantFormatter();
|
||||
|
||||
|
||||
@ParameterizedTest
|
||||
@ArgumentsSource(ISOSerializedInstantProvider.class)
|
||||
void should_parse_an_ISO_formatted_string_representation_of_an_Instant(String input) throws ParseException {
|
||||
Instant expected = DateTimeFormatter.ISO_INSTANT.parse(input, Instant::from);
|
||||
Instant actual = instantFormatter.parse(input, Locale.US);
|
||||
|
||||
Instant actual = instantFormatter.parse(input, null);
|
||||
|
||||
assertThat(actual).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@@ -63,7 +63,9 @@ class InstantFormatterTests {
|
||||
@ArgumentsSource(RFC1123SerializedInstantProvider.class)
|
||||
void should_parse_an_RFC1123_formatted_string_representation_of_an_Instant(String input) throws ParseException {
|
||||
Instant expected = DateTimeFormatter.RFC_1123_DATE_TIME.parse(input, Instant::from);
|
||||
Instant actual = instantFormatter.parse(input, Locale.US);
|
||||
|
||||
Instant actual = instantFormatter.parse(input, null);
|
||||
|
||||
assertThat(actual).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@@ -71,11 +73,12 @@ class InstantFormatterTests {
|
||||
@ArgumentsSource(RandomInstantProvider.class)
|
||||
void should_serialize_an_Instant_using_ISO_format_and_ignoring_Locale(Instant input) {
|
||||
String expected = DateTimeFormatter.ISO_INSTANT.format(input);
|
||||
String actual = instantFormatter.print(input, Locale.US);
|
||||
|
||||
String actual = instantFormatter.print(input, null);
|
||||
|
||||
assertThat(actual).isEqualTo(expected);
|
||||
}
|
||||
|
||||
|
||||
private static class RandomInstantProvider implements ArgumentsProvider {
|
||||
|
||||
private static final long DATA_SET_SIZE = 10;
|
||||
@@ -97,7 +100,6 @@ class InstantFormatterTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class ISOSerializedInstantProvider extends RandomInstantProvider {
|
||||
|
||||
@Override
|
||||
@@ -106,7 +108,6 @@ class InstantFormatterTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class RFC1123SerializedInstantProvider extends RandomInstantProvider {
|
||||
|
||||
// RFC-1123 supports only 4-digit years
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,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 Future) {
|
||||
((Future<?>) task).cancel(true);
|
||||
if (task instanceof RunnableFuture) {
|
||||
((RunnableFuture<?>) task).cancel(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -39,7 +39,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
* @author Sam Brannen
|
||||
* @since 3.0
|
||||
*/
|
||||
class ThreadPoolTaskSchedulerTests extends AbstractSchedulingTaskExecutorTests {
|
||||
public class ThreadPoolTaskSchedulerTests extends AbstractSchedulingTaskExecutorTests {
|
||||
|
||||
private final ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
|
||||
|
||||
@@ -97,7 +97,7 @@ class ThreadPoolTaskSchedulerTests extends AbstractSchedulingTaskExecutorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void scheduleOneTimeFailingTaskWithoutErrorHandler() {
|
||||
void scheduleOneTimeFailingTaskWithoutErrorHandler() throws Exception {
|
||||
TestTask task = new TestTask(this.testName, 0);
|
||||
Future<?> future = scheduler.schedule(task, new Date());
|
||||
assertThatExceptionOfType(ExecutionException.class).isThrownBy(() -> future.get(1000, TimeUnit.MILLISECONDS));
|
||||
@@ -149,7 +149,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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
-2
@@ -18,6 +18,4 @@
|
||||
|
||||
<bean id="testBean" class="org.springframework.beans.testfixture.beans.TestBean"/>
|
||||
|
||||
<bean id="testBean2" class="org.springframework.beans.testfixture.beans.TestBean"/>
|
||||
|
||||
</beans>
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -57,11 +57,11 @@ public final class BridgeMethodResolver {
|
||||
|
||||
|
||||
/**
|
||||
* Find the local original method for the supplied {@link Method bridge Method}.
|
||||
* Find the original method for the supplied {@link Method bridge Method}.
|
||||
* <p>It is safe to call this method passing in a non-bridge {@link Method} instance.
|
||||
* In such a case, the supplied {@link Method} instance is returned directly to the caller.
|
||||
* Callers are <strong>not</strong> required to check for bridging before calling this method.
|
||||
* @param bridgeMethod the method to introspect against its declaring class
|
||||
* @param bridgeMethod the method to introspect
|
||||
* @return the original method (either the bridged method or the passed-in method
|
||||
* if no more specific one could be found)
|
||||
*/
|
||||
@@ -73,7 +73,8 @@ public final class BridgeMethodResolver {
|
||||
if (bridgedMethod == null) {
|
||||
// Gather all methods with matching name and parameter size.
|
||||
List<Method> candidateMethods = new ArrayList<>();
|
||||
MethodFilter filter = (candidateMethod -> isBridgedCandidateFor(candidateMethod, bridgeMethod));
|
||||
MethodFilter filter = candidateMethod ->
|
||||
isBridgedCandidateFor(candidateMethod, bridgeMethod);
|
||||
ReflectionUtils.doWithMethods(bridgeMethod.getDeclaringClass(), candidateMethods::add, filter);
|
||||
if (!candidateMethods.isEmpty()) {
|
||||
bridgedMethod = candidateMethods.size() == 1 ?
|
||||
@@ -94,10 +95,10 @@ public final class BridgeMethodResolver {
|
||||
* Returns {@code true} if the supplied '{@code candidateMethod}' can be
|
||||
* considered a valid candidate for the {@link Method} that is {@link Method#isBridge() bridged}
|
||||
* by the supplied {@link Method bridge Method}. This method performs inexpensive
|
||||
* checks and can be used to quickly filter for a set of possible matches.
|
||||
* checks and can be used quickly to filter for a set of possible matches.
|
||||
*/
|
||||
private static boolean isBridgedCandidateFor(Method candidateMethod, Method bridgeMethod) {
|
||||
return (!candidateMethod.isBridge() &&
|
||||
return (!candidateMethod.isBridge() && !candidateMethod.equals(bridgeMethod) &&
|
||||
candidateMethod.getName().equals(bridgeMethod.getName()) &&
|
||||
candidateMethod.getParameterCount() == bridgeMethod.getParameterCount());
|
||||
}
|
||||
@@ -120,8 +121,8 @@ public final class BridgeMethodResolver {
|
||||
return candidateMethod;
|
||||
}
|
||||
else if (previousMethod != null) {
|
||||
sameSig = sameSig && Arrays.equals(
|
||||
candidateMethod.getGenericParameterTypes(), previousMethod.getGenericParameterTypes());
|
||||
sameSig = sameSig &&
|
||||
Arrays.equals(candidateMethod.getGenericParameterTypes(), previousMethod.getGenericParameterTypes());
|
||||
}
|
||||
previousMethod = candidateMethod;
|
||||
}
|
||||
@@ -162,8 +163,7 @@ public final class BridgeMethodResolver {
|
||||
}
|
||||
}
|
||||
// A non-array type: compare the type itself.
|
||||
if (!ClassUtils.resolvePrimitiveIfNecessary(candidateParameter).equals(
|
||||
ClassUtils.resolvePrimitiveIfNecessary(genericParameter.toClass()))) {
|
||||
if (!ClassUtils.resolvePrimitiveIfNecessary(candidateParameter).equals(ClassUtils.resolvePrimitiveIfNecessary(genericParameter.toClass()))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -226,8 +226,8 @@ public final class BridgeMethodResolver {
|
||||
/**
|
||||
* Compare the signatures of the bridge method and the method which it bridges. If
|
||||
* the parameter and return types are the same, it is a 'visibility' bridge method
|
||||
* introduced in Java 6 to fix <a href="https://bugs.openjdk.org/browse/JDK-6342411">
|
||||
* JDK-6342411</a>.
|
||||
* introduced in Java 6 to fix https://bugs.openjdk.org/browse/JDK-6342411.
|
||||
* See also https://stas-blogspot.blogspot.com/2010/03/java-bridge-methods-explained.html
|
||||
* @return whether signatures match as described
|
||||
*/
|
||||
public static boolean isVisibilityBridgeMethodPair(Method bridgeMethod, Method bridgedMethod) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -36,7 +36,6 @@ import org.springframework.util.ReflectionUtils;
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Sam Brannen
|
||||
* @since 4.2.3
|
||||
*/
|
||||
public final class MethodIntrospector {
|
||||
@@ -76,7 +75,6 @@ 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-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -98,9 +98,7 @@ public final class ReactiveTypeDescriptor {
|
||||
*/
|
||||
public Object getEmptyValue() {
|
||||
Assert.state(this.emptySupplier != null, "Empty values not supported");
|
||||
Object emptyValue = this.emptySupplier.get();
|
||||
Assert.notNull(emptyValue, "Invalid null return value from emptySupplier");
|
||||
return emptyValue;
|
||||
return this.emptySupplier.get();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,7 +130,7 @@ public final class ReactiveTypeDescriptor {
|
||||
|
||||
|
||||
/**
|
||||
* Descriptor for a reactive type that can produce {@code 0..N} values.
|
||||
* Descriptor for a reactive type that can produce 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-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -750,7 +750,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()
|
||||
*/
|
||||
|
||||
@@ -1052,16 +1052,16 @@ public abstract class AnnotationUtils {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
for (Method method : annotation.annotationType().getDeclaredMethods()) {
|
||||
if (method.getName().equals(attributeName) && method.getParameterCount() == 0) {
|
||||
return invokeAnnotationMethod(method, annotation);
|
||||
}
|
||||
}
|
||||
Method method = annotation.annotationType().getDeclaredMethod(attributeName);
|
||||
return invokeAnnotationMethod(method, annotation);
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
return null;
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
handleValueRetrievalFailure(annotation, ex);
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+2
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -410,10 +410,7 @@ final class TypeMappedAnnotations implements MergedAnnotations {
|
||||
|
||||
Annotation[] repeatedAnnotations = repeatableContainers.findRepeatedAnnotations(annotation);
|
||||
if (repeatedAnnotations != null) {
|
||||
MergedAnnotation<A> result = doWithAnnotations(type, aggregateIndex, source, repeatedAnnotations);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
return doWithAnnotations(type, aggregateIndex, source, repeatedAnnotations);
|
||||
}
|
||||
AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(
|
||||
annotation.annotationType(), repeatableContainers, annotationFilter);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -95,19 +95,20 @@ 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) {
|
||||
Throwable cause = ex.getCause();
|
||||
throw (cause instanceof CodecException ? (CodecException) cause :
|
||||
new DecodingException("Failed to decode: " + (cause != null ? cause.getMessage() : ex), cause));
|
||||
failure = ex.getCause();
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
throw new DecodingException("Interrupted during decode", ex);
|
||||
failure = ex;
|
||||
}
|
||||
throw (failure instanceof CodecException ? (CodecException) failure :
|
||||
new DecodingException("Failed to decode: " + failure.getMessage(), failure));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -76,11 +76,10 @@ 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;
|
||||
}
|
||||
@@ -93,7 +92,6 @@ 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-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -52,6 +52,8 @@ 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 = {
|
||||
@@ -82,7 +84,7 @@ public class TypeDescriptor implements Serializable {
|
||||
public TypeDescriptor(MethodParameter methodParameter) {
|
||||
this.resolvableType = ResolvableType.forMethodParameter(methodParameter);
|
||||
this.type = this.resolvableType.resolve(methodParameter.getNestedParameterType());
|
||||
this.annotatedElement = AnnotatedElementAdapter.from(methodParameter.getParameterIndex() == -1 ?
|
||||
this.annotatedElement = new AnnotatedElementAdapter(methodParameter.getParameterIndex() == -1 ?
|
||||
methodParameter.getMethodAnnotations() : methodParameter.getParameterAnnotations());
|
||||
}
|
||||
|
||||
@@ -94,7 +96,7 @@ public class TypeDescriptor implements Serializable {
|
||||
public TypeDescriptor(Field field) {
|
||||
this.resolvableType = ResolvableType.forField(field);
|
||||
this.type = this.resolvableType.resolve(field.getType());
|
||||
this.annotatedElement = AnnotatedElementAdapter.from(field.getAnnotations());
|
||||
this.annotatedElement = new AnnotatedElementAdapter(field.getAnnotations());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -107,7 +109,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 = AnnotatedElementAdapter.from(property.getAnnotations());
|
||||
this.annotatedElement = new AnnotatedElementAdapter(property.getAnnotations());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -123,7 +125,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 = AnnotatedElementAdapter.from(annotations);
|
||||
this.annotatedElement = new AnnotatedElementAdapter(annotations);
|
||||
}
|
||||
|
||||
|
||||
@@ -732,26 +734,18 @@ public class TypeDescriptor implements Serializable {
|
||||
* @see AnnotatedElementUtils#isAnnotated(AnnotatedElement, Class)
|
||||
* @see AnnotatedElementUtils#getMergedAnnotation(AnnotatedElement, Class)
|
||||
*/
|
||||
private static final class AnnotatedElementAdapter implements AnnotatedElement, Serializable {
|
||||
|
||||
private static final AnnotatedElementAdapter EMPTY = new AnnotatedElementAdapter(new Annotation[0]);
|
||||
private class AnnotatedElementAdapter implements AnnotatedElement, Serializable {
|
||||
|
||||
@Nullable
|
||||
private final Annotation[] annotations;
|
||||
|
||||
private AnnotatedElementAdapter(Annotation[] annotations) {
|
||||
public AnnotatedElementAdapter(@Nullable 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 : this.annotations) {
|
||||
for (Annotation annotation : getAnnotations()) {
|
||||
if (annotation.annotationType() == annotationClass) {
|
||||
return true;
|
||||
}
|
||||
@@ -763,7 +757,7 @@ public class TypeDescriptor implements Serializable {
|
||||
@Nullable
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
|
||||
for (Annotation annotation : this.annotations) {
|
||||
for (Annotation annotation : getAnnotations()) {
|
||||
if (annotation.annotationType() == annotationClass) {
|
||||
return (T) annotation;
|
||||
}
|
||||
@@ -773,7 +767,7 @@ public class TypeDescriptor implements Serializable {
|
||||
|
||||
@Override
|
||||
public Annotation[] getAnnotations() {
|
||||
return (isEmpty() ? this.annotations : this.annotations.clone());
|
||||
return (this.annotations != null ? this.annotations.clone() : EMPTY_ANNOTATION_ARRAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -782,7 +776,7 @@ public class TypeDescriptor implements Serializable {
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return (this.annotations.length == 0);
|
||||
return ObjectUtils.isEmpty(this.annotations);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -798,7 +792,7 @@ public class TypeDescriptor implements Serializable {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Arrays.toString(this.annotations);
|
||||
return TypeDescriptor.this.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -138,7 +138,7 @@ final class ObjectToObjectConverter implements ConditionalGenericConverter {
|
||||
@Nullable
|
||||
private static Executable getValidatedExecutable(Class<?> targetClass, Class<?> sourceClass) {
|
||||
Executable executable = conversionExecutableCache.get(targetClass);
|
||||
if (executable != null && isApplicable(executable, sourceClass)) {
|
||||
if (isApplicable(executable, sourceClass)) {
|
||||
return executable;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -48,7 +48,7 @@ public class PropertiesPropertySource extends MapPropertySource {
|
||||
@Override
|
||||
public String[] getPropertyNames() {
|
||||
synchronized (this.source) {
|
||||
return ((Map<?, ?>) this.source).keySet().stream().filter(k -> k instanceof String).toArray(String[]::new);
|
||||
return super.getPropertyNames();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -62,7 +62,7 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public abstract class DataBufferUtils {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(DataBufferUtils.class);
|
||||
private final static Log logger = LogFactory.getLog(DataBufferUtils.class);
|
||||
|
||||
private static final Consumer<DataBuffer> RELEASE_CONSUMER = DataBufferUtils::release;
|
||||
|
||||
@@ -728,7 +728,7 @@ public abstract class DataBufferUtils {
|
||||
*/
|
||||
private static class SingleByteMatcher implements NestedMatcher {
|
||||
|
||||
static final SingleByteMatcher NEWLINE_MATCHER = new SingleByteMatcher(new byte[] {10});
|
||||
static SingleByteMatcher NEWLINE_MATCHER = new SingleByteMatcher(new byte[] {10});
|
||||
|
||||
private final byte[] delimiter;
|
||||
|
||||
@@ -767,7 +767,7 @@ public abstract class DataBufferUtils {
|
||||
/**
|
||||
* Base class for a {@link NestedMatcher}.
|
||||
*/
|
||||
private abstract static class AbstractNestedMatcher implements NestedMatcher {
|
||||
private static abstract class AbstractNestedMatcher implements NestedMatcher {
|
||||
|
||||
private final byte[] delimiter;
|
||||
|
||||
@@ -1005,11 +1005,11 @@ public abstract class DataBufferUtils {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable ex, DataBuffer dataBuffer) {
|
||||
public void failed(Throwable exc, DataBuffer dataBuffer) {
|
||||
release(dataBuffer);
|
||||
closeChannel(this.channel);
|
||||
this.state.set(State.DISPOSED);
|
||||
this.sink.error(ex);
|
||||
this.sink.error(exc);
|
||||
}
|
||||
|
||||
private enum State {
|
||||
@@ -1064,6 +1064,7 @@ public abstract class DataBufferUtils {
|
||||
public Context currentContext() {
|
||||
return Context.of(this.sink.contextView());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1144,9 +1145,9 @@ public abstract class DataBufferUtils {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable ex, ByteBuffer byteBuffer) {
|
||||
public void failed(Throwable exc, ByteBuffer byteBuffer) {
|
||||
sinkDataBuffer();
|
||||
this.sink.error(ex);
|
||||
this.sink.error(exc);
|
||||
}
|
||||
|
||||
private void sinkDataBuffer() {
|
||||
@@ -1160,6 +1161,7 @@ public abstract class DataBufferUtils {
|
||||
public Context currentContext() {
|
||||
return Context.of(this.sink.contextView());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+5
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -496,7 +496,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
String rootDirPath = determineRootDir(locationPattern);
|
||||
String subPattern = locationPattern.substring(rootDirPath.length());
|
||||
Resource[] rootDirResources = getResources(rootDirPath);
|
||||
Set<Resource> result = new LinkedHashSet<>(64);
|
||||
Set<Resource> result = new LinkedHashSet<>(16);
|
||||
for (Resource rootDirResource : rootDirResources) {
|
||||
rootDirResource = resolveRootDirResource(rootDirResource);
|
||||
URL rootDirUrl = rootDirResource.getURL();
|
||||
@@ -648,7 +648,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
// The Sun JRE does not return a slash here, but BEA JRockit does.
|
||||
rootEntryPath = rootEntryPath + "/";
|
||||
}
|
||||
Set<Resource> result = new LinkedHashSet<>(64);
|
||||
Set<Resource> result = new LinkedHashSet<>(8);
|
||||
for (Enumeration<JarEntry> entries = jarFile.entries(); entries.hasMoreElements();) {
|
||||
JarEntry entry = entries.nextElement();
|
||||
String entryPath = entry.getName();
|
||||
@@ -864,7 +864,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
|
||||
private final String rootPath;
|
||||
|
||||
private final Set<Resource> resources = new LinkedHashSet<>(64);
|
||||
private final Set<Resource> resources = new LinkedHashSet<>();
|
||||
|
||||
public PatternVirtualFileVisitor(String rootPath, String subPattern, PathMatcher pathMatcher) {
|
||||
this.subPattern = subPattern;
|
||||
@@ -895,6 +895,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
else if ("toString".equals(methodName)) {
|
||||
return toString();
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Unexpected method invocation: " + method);
|
||||
}
|
||||
|
||||
|
||||
+2
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,8 +20,6 @@ import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Default "no op" {@code ApplicationStartup} implementation.
|
||||
*
|
||||
@@ -54,7 +52,6 @@ class DefaultApplicationStartup implements ApplicationStartup {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Long getParentId() {
|
||||
return null;
|
||||
}
|
||||
@@ -76,6 +73,7 @@ class DefaultApplicationStartup implements ApplicationStartup {
|
||||
|
||||
@Override
|
||||
public void end() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -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-2024 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -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) {
|
||||
if (superClassMatch.booleanValue()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Need to read superclass to determine a match...
|
||||
try {
|
||||
if (match(superClassName, metadataReaderFactory)) {
|
||||
if (match(metadata.getSuperClassName(), metadataReaderFactory)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Could not read superclass [" + superClassName +
|
||||
logger.debug("Could not read superclass [" + metadata.getSuperClassName() +
|
||||
"] 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) {
|
||||
if (interfaceMatch.booleanValue()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -44,8 +44,7 @@ import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Miscellaneous {@code java.lang.Class} utility methods.
|
||||
*
|
||||
* <p>Mainly for internal use within the framework.
|
||||
* Mainly for internal use within the framework.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Keith Donald
|
||||
@@ -244,7 +243,7 @@ public abstract class ClassUtils {
|
||||
* style (e.g. "java.lang.Thread.State" instead of "java.lang.Thread$State").
|
||||
* @param name the name of the Class
|
||||
* @param classLoader the class loader to use
|
||||
* (can be {@code null}, which indicates the default class loader)
|
||||
* (may be {@code null}, which indicates the default class loader)
|
||||
* @return a class instance for the supplied name
|
||||
* @throws ClassNotFoundException if the class was not found
|
||||
* @throws LinkageError if the class file could not be loaded
|
||||
@@ -315,7 +314,7 @@ public abstract class ClassUtils {
|
||||
* the exceptions thrown in case of class loading failure.
|
||||
* @param className the name of the Class
|
||||
* @param classLoader the class loader to use
|
||||
* (can be {@code null}, which indicates the default class loader)
|
||||
* (may be {@code null}, which indicates the default class loader)
|
||||
* @return a class instance for the supplied name
|
||||
* @throws IllegalArgumentException if the class name was not resolvable
|
||||
* (that is, the class could not be found or the class file could not be loaded)
|
||||
@@ -349,7 +348,7 @@ public abstract class ClassUtils {
|
||||
* one of its dependencies is not present or cannot be loaded.
|
||||
* @param className the name of the class to check
|
||||
* @param classLoader the class loader to use
|
||||
* (can be {@code null} which indicates the default class loader)
|
||||
* (may be {@code null} which indicates the default class loader)
|
||||
* @return whether the specified class is present (including all of its
|
||||
* superclasses and interfaces)
|
||||
* @throws IllegalStateException if the corresponding class is resolvable but
|
||||
@@ -376,7 +375,7 @@ public abstract class ClassUtils {
|
||||
* Check whether the given class is visible in the given ClassLoader.
|
||||
* @param clazz the class to check (typically an interface)
|
||||
* @param classLoader the ClassLoader to check against
|
||||
* (can be {@code null} in which case this method will always return {@code true})
|
||||
* (may be {@code null} in which case this method will always return {@code true})
|
||||
*/
|
||||
public static boolean isVisible(Class<?> clazz, @Nullable ClassLoader classLoader) {
|
||||
if (classLoader == null) {
|
||||
@@ -400,7 +399,7 @@ public abstract class ClassUtils {
|
||||
* i.e. whether it is loaded by the given ClassLoader or a parent of it.
|
||||
* @param clazz the class to analyze
|
||||
* @param classLoader the ClassLoader to potentially cache metadata in
|
||||
* (can be {@code null} which indicates the system class loader)
|
||||
* (may be {@code null} which indicates the system class loader)
|
||||
*/
|
||||
public static boolean isCacheSafe(Class<?> clazz, @Nullable ClassLoader classLoader) {
|
||||
Assert.notNull(clazz, "Class must not be null");
|
||||
@@ -540,10 +539,9 @@ public abstract class ClassUtils {
|
||||
* Check if the right-hand side type may be assigned to the left-hand side
|
||||
* type, assuming setting by reflection. Considers primitive wrapper
|
||||
* classes as assignable to the corresponding primitive types.
|
||||
* @param lhsType the target type (left-hand side (LHS) type)
|
||||
* @param rhsType the value type (right-hand side (RHS) type) that should
|
||||
* be assigned to the target type
|
||||
* @return {@code true} if {@code rhsType} is assignable to {@code lhsType}
|
||||
* @param lhsType the target type
|
||||
* @param rhsType the value type that should be assigned to the target type
|
||||
* @return if the target type is assignable from the value type
|
||||
* @see TypeUtils#isAssignable(java.lang.reflect.Type, java.lang.reflect.Type)
|
||||
*/
|
||||
public static boolean isAssignable(Class<?> lhsType, Class<?> rhsType) {
|
||||
@@ -664,7 +662,7 @@ public abstract class ClassUtils {
|
||||
* in the given collection.
|
||||
* <p>Basically like {@code AbstractCollection.toString()}, but stripping
|
||||
* the "class "/"interface " prefix before every class name.
|
||||
* @param classes a Collection of Class objects (can be {@code null})
|
||||
* @param classes a Collection of Class objects (may be {@code null})
|
||||
* @return a String of form "[com.foo.Bar, com.foo.Baz]"
|
||||
* @see java.util.AbstractCollection#toString()
|
||||
*/
|
||||
@@ -719,7 +717,7 @@ public abstract class ClassUtils {
|
||||
* <p>If the class itself is an interface, it gets returned as sole interface.
|
||||
* @param clazz the class to analyze for interfaces
|
||||
* @param classLoader the ClassLoader that the interfaces need to be visible in
|
||||
* (can be {@code null} when accepting all declared interfaces)
|
||||
* (may be {@code null} when accepting all declared interfaces)
|
||||
* @return all interfaces that the given object implements as an array
|
||||
*/
|
||||
public static Class<?>[] getAllInterfacesForClass(Class<?> clazz, @Nullable ClassLoader classLoader) {
|
||||
@@ -754,7 +752,7 @@ public abstract class ClassUtils {
|
||||
* <p>If the class itself is an interface, it gets returned as sole interface.
|
||||
* @param clazz the class to analyze for interfaces
|
||||
* @param classLoader the ClassLoader that the interfaces need to be visible in
|
||||
* (can be {@code null} when accepting all declared interfaces)
|
||||
* (may be {@code null} when accepting all declared interfaces)
|
||||
* @return all interfaces that the given object implements as a Set
|
||||
*/
|
||||
public static Set<Class<?>> getAllInterfacesForClassAsSet(Class<?> clazz, @Nullable ClassLoader classLoader) {
|
||||
@@ -868,9 +866,9 @@ public abstract class ClassUtils {
|
||||
/**
|
||||
* Check whether the given object is a CGLIB proxy.
|
||||
* @param object the object to check
|
||||
* @see #isCglibProxyClass(Class)
|
||||
* @see org.springframework.aop.support.AopUtils#isCglibProxy(Object)
|
||||
* @deprecated as of 5.2, in favor of custom (possibly narrower) checks
|
||||
* such as for a Spring AOP proxy
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean isCglibProxy(Object object) {
|
||||
@@ -880,9 +878,8 @@ public abstract class ClassUtils {
|
||||
/**
|
||||
* Check whether the specified class is a CGLIB-generated class.
|
||||
* @param clazz the class to check
|
||||
* @see #getUserClass(Class)
|
||||
* @see #isCglibProxyClassName(String)
|
||||
* @deprecated as of 5.2, in favor of custom (possibly narrower) checks
|
||||
* or simply a check for containing {@link #CGLIB_CLASS_SEPARATOR}
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean isCglibProxyClass(@Nullable Class<?> clazz) {
|
||||
@@ -892,9 +889,7 @@ public abstract class ClassUtils {
|
||||
/**
|
||||
* Check whether the specified class name is a CGLIB-generated class.
|
||||
* @param className the class name to check
|
||||
* @see #CGLIB_CLASS_SEPARATOR
|
||||
* @deprecated as of 5.2, in favor of custom (possibly narrower) checks
|
||||
* or simply a check for containing {@link #CGLIB_CLASS_SEPARATOR}
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean isCglibProxyClassName(@Nullable String className) {
|
||||
@@ -918,7 +913,6 @@ public abstract class ClassUtils {
|
||||
* class, but the original class in case of a CGLIB-generated subclass.
|
||||
* @param clazz the class to check
|
||||
* @return the user-defined class
|
||||
* @see #CGLIB_CLASS_SEPARATOR
|
||||
*/
|
||||
public static Class<?> getUserClass(Class<?> clazz) {
|
||||
if (clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) {
|
||||
@@ -1071,7 +1065,7 @@ public abstract class ClassUtils {
|
||||
* fully qualified interface/class name + "." + method name.
|
||||
* @param method the method
|
||||
* @param clazz the clazz that the method is being invoked on
|
||||
* (can be {@code null} to indicate the method's declaring class)
|
||||
* (may be {@code null} to indicate the method's declaring class)
|
||||
* @return the qualified name of the method
|
||||
* @since 4.3.4
|
||||
*/
|
||||
@@ -1152,7 +1146,7 @@ public abstract class ClassUtils {
|
||||
* @param clazz the clazz to analyze
|
||||
* @param methodName the name of the method
|
||||
* @param paramTypes the parameter types of the method
|
||||
* (can be {@code null} to indicate any signature)
|
||||
* (may be {@code null} to indicate any signature)
|
||||
* @return the method (never {@code null})
|
||||
* @throws IllegalStateException if the method has not been found
|
||||
* @see Class#getMethod
|
||||
@@ -1191,7 +1185,7 @@ public abstract class ClassUtils {
|
||||
* @param clazz the clazz to analyze
|
||||
* @param methodName the name of the method
|
||||
* @param paramTypes the parameter types of the method
|
||||
* (can be {@code null} to indicate any signature)
|
||||
* (may be {@code null} to indicate any signature)
|
||||
* @return the method, or {@code null} if not found
|
||||
* @see Class#getMethod
|
||||
*/
|
||||
@@ -1267,27 +1261,26 @@ public abstract class ClassUtils {
|
||||
/**
|
||||
* Given a method, which may come from an interface, and a target class used
|
||||
* in the current reflective invocation, find the corresponding target method
|
||||
* if there is one — for example, the method may be {@code IFoo.bar()},
|
||||
* and the target class may be {@code DefaultFoo}. In this case, the method may be
|
||||
* if there is one. E.g. the method may be {@code IFoo.bar()} and the
|
||||
* target class may be {@code DefaultFoo}. In this case, the method may be
|
||||
* {@code DefaultFoo.bar()}. This enables attributes on that method to be found.
|
||||
* <p><b>NOTE:</b> In contrast to {@link org.springframework.aop.support.AopUtils#getMostSpecificMethod},
|
||||
* this method does <i>not</i> resolve bridge methods automatically.
|
||||
* Call {@link org.springframework.core.BridgeMethodResolver#findBridgedMethod}
|
||||
* if bridge method resolution is desirable — for example, to obtain
|
||||
* metadata from the original method definition.
|
||||
* <p><b>NOTE:</b> If Java security settings disallow reflective access —
|
||||
* for example, calls to {@code Class#getDeclaredMethods}, etc. — this
|
||||
* implementation will fall back to returning the originally provided method.
|
||||
* if bridge method resolution is desirable (e.g. for obtaining metadata from
|
||||
* the original method definition).
|
||||
* <p><b>NOTE:</b> Since Spring 3.1.1, if Java security settings disallow reflective
|
||||
* access (e.g. calls to {@code Class#getDeclaredMethods} etc, this implementation
|
||||
* will fall back to returning the originally provided method.
|
||||
* @param method the method to be invoked, which may come from an interface
|
||||
* @param targetClass the target class for the current invocation
|
||||
* (can be {@code null} or may not even implement the method)
|
||||
* (may be {@code null} or may not even implement the method)
|
||||
* @return the specific target method, or the original method if the
|
||||
* {@code targetClass} does not implement it
|
||||
* @see #getInterfaceMethodIfPossible(Method, Class)
|
||||
*/
|
||||
public static Method getMostSpecificMethod(Method method, @Nullable Class<?> targetClass) {
|
||||
if (targetClass != null && targetClass != method.getDeclaringClass() &&
|
||||
(isOverridable(method, targetClass) || !method.getDeclaringClass().isAssignableFrom(targetClass))) {
|
||||
if (targetClass != null && targetClass != method.getDeclaringClass() && isOverridable(method, targetClass)) {
|
||||
try {
|
||||
if (Modifier.isPublic(method.getModifiers())) {
|
||||
try {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -58,7 +58,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 (!CollectionUtils.isEmpty(values) ? values.get(0) : null);
|
||||
return (values != null && !values.isEmpty() ? values.get(0) : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -69,7 +69,7 @@ public class MultiValueMapAdapter<K, V> implements MultiValueMap<K, V>, Serializ
|
||||
|
||||
@Override
|
||||
public void addAll(K key, List<? extends V> values) {
|
||||
List<V> currentValues = this.targetMap.computeIfAbsent(key, k -> new ArrayList<>(values.size()));
|
||||
List<V> currentValues = this.targetMap.computeIfAbsent(key, k -> new ArrayList<>(1));
|
||||
currentValues.addAll(values);
|
||||
}
|
||||
|
||||
@@ -96,7 +96,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 (!CollectionUtils.isEmpty(values)) {
|
||||
if (values != null && !values.isEmpty()) {
|
||||
singleValueMap.put(key, values.get(0));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -781,7 +781,6 @@ 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
|
||||
@@ -830,7 +829,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
|
||||
@@ -860,7 +859,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
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
/*
|
||||
* 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() {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+1
-63
@@ -24,7 +24,6 @@ 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;
|
||||
|
||||
@@ -176,7 +175,7 @@ class MergedAnnotationsRepeatableAnnotationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void typeHierarchyWhenOnSuperclassReturnsAnnotations() {
|
||||
void typeHierarchyWhenWhenOnSuperclassReturnsAnnotations() {
|
||||
Set<PeteRepeat> annotations = getAnnotations(null, PeteRepeat.class,
|
||||
SearchStrategy.TYPE_HIERARCHY, SubRepeatableClass.class);
|
||||
assertThat(annotations.stream().map(PeteRepeat::value)).containsExactly("A", "B",
|
||||
@@ -241,44 +240,6 @@ 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) {
|
||||
|
||||
@@ -488,27 +449,4 @@ 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 {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+3
-13
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,10 +18,7 @@ package org.springframework.core.env;
|
||||
|
||||
import java.security.AccessControlException;
|
||||
import java.security.Permission;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -310,12 +307,6 @@ class StandardEnvironmentTests {
|
||||
// non-string keys and values work fine... until the security manager is introduced below
|
||||
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(new HashSet<>(Arrays.asList(systemPropertySource.getPropertyNames()))).isEqualTo(expectedKeys);
|
||||
}
|
||||
|
||||
SecurityManager securityManager = new SecurityManager() {
|
||||
@@ -416,7 +407,6 @@ class StandardEnvironmentTests {
|
||||
EnvironmentTestUtils.getModifiableSystemEnvironment().remove(DISALLOWED_PROPERTY_NAME);
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
class GetActiveProfiles {
|
||||
|
||||
@@ -466,7 +456,6 @@ class StandardEnvironmentTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
class AcceptsProfilesTests {
|
||||
|
||||
@@ -549,8 +538,8 @@ class StandardEnvironmentTests {
|
||||
environment.addActiveProfile("p2");
|
||||
assertThat(environment.acceptsProfiles(Profiles.of("p1 & p2"))).isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
class MatchesProfilesTests {
|
||||
@@ -661,6 +650,7 @@ class StandardEnvironmentTests {
|
||||
assertThat(environment.matchesProfiles("p2 & (foo | p1)")).isTrue();
|
||||
assertThat(environment.matchesProfiles("foo", "(p2 & p1)")).isTrue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+8
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -48,14 +48,14 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
*/
|
||||
class PathMatchingResourcePatternResolverTests {
|
||||
|
||||
private static final String[] CLASSES_IN_CORE_IO_SUPPORT = {"EncodedResource.class",
|
||||
private static final String[] CLASSES_IN_CORE_IO_SUPPORT = { "EncodedResource.class",
|
||||
"LocalizedResourceHelper.class", "PathMatchingResourcePatternResolver.class", "PropertiesLoaderSupport.class",
|
||||
"PropertiesLoaderUtils.class", "ResourceArrayPropertyEditor.class", "ResourcePatternResolver.class",
|
||||
"ResourcePatternUtils.class", "SpringFactoriesLoader.class"};
|
||||
"ResourcePatternUtils.class", "SpringFactoriesLoader.class" };
|
||||
|
||||
private static final String[] TEST_CLASSES_IN_CORE_IO_SUPPORT = {"PathMatchingResourcePatternResolverTests.class"};
|
||||
private static final String[] TEST_CLASSES_IN_CORE_IO_SUPPORT = { "PathMatchingResourcePatternResolverTests.class" };
|
||||
|
||||
private static final String[] CLASSES_IN_REACTOR_UTIL_ANNOTATION = {"NonNull.class", "NonNullApi.class", "Nullable.class"};
|
||||
private static final String[] CLASSES_IN_REACTOR_UTIL_ANNOTATION = { "NonNull.class", "NonNullApi.class", "Nullable.class" };
|
||||
|
||||
|
||||
private final PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
|
||||
@@ -166,7 +166,7 @@ class PathMatchingResourcePatternResolverTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void usingFileProtocolWithoutWildcardInPatternAndEndingInSlashStarStar() {
|
||||
void usingFileProtocolWithoutWildcardInPatternAndEndingInSlashStarStar() {
|
||||
Path testResourcesDir = Paths.get("src/test/resources").toAbsolutePath();
|
||||
String pattern = String.format("file:%s/scanned-resources/**", testResourcesDir);
|
||||
String pathPrefix = ".+?resources/";
|
||||
@@ -294,8 +294,8 @@ class PathMatchingResourcePatternResolverTests {
|
||||
}
|
||||
|
||||
private String getPath(Resource resource) {
|
||||
// Tests fail if we use resource.getURL().getPath(). They would also fail on macOS when
|
||||
// using resource.getURI().getPath() if the resource paths are not Unicode normalized.
|
||||
// Tests fail if we use resouce.getURL().getPath(). They would also fail on Mac OS when
|
||||
// using resouce.getURI().getPath() if the resource paths are not Unicode normalized.
|
||||
//
|
||||
// On the JVM, all tests should pass when using resouce.getFile().getPath(); however,
|
||||
// we use FileSystemResource#getPath since this test class is sometimes run within a
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -45,7 +45,6 @@ class StreamUtilsTests {
|
||||
|
||||
private String string = "";
|
||||
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
new Random().nextBytes(bytes);
|
||||
@@ -54,7 +53,6 @@ class StreamUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void copyToByteArray() throws Exception {
|
||||
InputStream inputStream = new ByteArrayInputStream(bytes);
|
||||
@@ -129,5 +127,4 @@ class StreamUtilsTests {
|
||||
ordered.verify(source).write(bytes, 1, 2);
|
||||
ordered.verify(source, never()).close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+12
-13
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -226,8 +226,6 @@ public class Indexer extends SpelNodeImpl {
|
||||
cf.loadTarget(mv);
|
||||
}
|
||||
|
||||
SpelNodeImpl index = this.children[0];
|
||||
|
||||
if (this.indexedType == IndexedType.ARRAY) {
|
||||
int insn;
|
||||
if ("D".equals(this.exitTypeDescriptor)) {
|
||||
@@ -264,14 +262,18 @@ public class Indexer extends SpelNodeImpl {
|
||||
//depthPlusOne(exitTypeDescriptor)+"Ljava/lang/Object;");
|
||||
insn = AALOAD;
|
||||
}
|
||||
|
||||
generateIndexCode(mv, cf, index, int.class);
|
||||
SpelNodeImpl index = this.children[0];
|
||||
cf.enterCompilationScope();
|
||||
index.generateCode(mv, cf);
|
||||
cf.exitCompilationScope();
|
||||
mv.visitInsn(insn);
|
||||
}
|
||||
|
||||
else if (this.indexedType == IndexedType.LIST) {
|
||||
mv.visitTypeInsn(CHECKCAST, "java/util/List");
|
||||
generateIndexCode(mv, cf, index, int.class);
|
||||
cf.enterCompilationScope();
|
||||
this.children[0].generateCode(mv, cf);
|
||||
cf.exitCompilationScope();
|
||||
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "get", "(I)Ljava/lang/Object;", true);
|
||||
}
|
||||
|
||||
@@ -279,13 +281,15 @@ public class Indexer extends SpelNodeImpl {
|
||||
mv.visitTypeInsn(CHECKCAST, "java/util/Map");
|
||||
// Special case when the key is an unquoted string literal that will be parsed as
|
||||
// a property/field reference
|
||||
if (index instanceof PropertyOrFieldReference) {
|
||||
if ((this.children[0] instanceof PropertyOrFieldReference)) {
|
||||
PropertyOrFieldReference reference = (PropertyOrFieldReference) this.children[0];
|
||||
String mapKeyName = reference.getName();
|
||||
mv.visitLdcInsn(mapKeyName);
|
||||
}
|
||||
else {
|
||||
generateIndexCode(mv, cf, index, Object.class);
|
||||
cf.enterCompilationScope();
|
||||
this.children[0].generateCode(mv, cf);
|
||||
cf.exitCompilationScope();
|
||||
}
|
||||
mv.visitMethodInsn(
|
||||
INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", true);
|
||||
@@ -321,11 +325,6 @@ public class Indexer extends SpelNodeImpl {
|
||||
cf.pushDescriptor(this.exitTypeDescriptor);
|
||||
}
|
||||
|
||||
private void generateIndexCode(MethodVisitor mv, CodeFlow cf, SpelNodeImpl indexNode, Class<?> indexType) {
|
||||
String indexDesc = CodeFlow.toDescriptor(indexType);
|
||||
generateCodeForArgument(mv, cf, indexNode, indexDesc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toStringAST() {
|
||||
StringJoiner sj = new StringJoiner(",", "[", "]");
|
||||
|
||||
+1
-489
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,8 +20,6 @@ import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@@ -29,10 +27,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
@@ -60,7 +55,6 @@ import static org.assertj.core.api.InstanceOfAssertFactories.BOOLEAN;
|
||||
* Checks SpelCompiler behavior. This should cover compilation all compiled node types.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Sam Brannen
|
||||
* @since 4.1
|
||||
*/
|
||||
public class SpelCompilationCoverageTests extends AbstractExpressionTests {
|
||||
@@ -135,488 +129,6 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
|
||||
private SpelNodeImpl ast;
|
||||
|
||||
|
||||
@Nested
|
||||
class VariableReferenceTests {
|
||||
|
||||
@Test
|
||||
void userDefinedVariable() {
|
||||
EvaluationContext ctx = new StandardEvaluationContext();
|
||||
ctx.setVariable("target", "abc");
|
||||
expression = parser.parseExpression("#target");
|
||||
assertThat(expression.getValue(ctx)).isEqualTo("abc");
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(ctx)).isEqualTo("abc");
|
||||
ctx.setVariable("target", "123");
|
||||
assertThat(expression.getValue(ctx)).isEqualTo("123");
|
||||
|
||||
// Changing the variable type from String to Integer results in a
|
||||
// ClassCastException in the compiled code.
|
||||
ctx.setVariable("target", 42);
|
||||
assertThatExceptionOfType(SpelEvaluationException.class)
|
||||
.isThrownBy(() -> expression.getValue(ctx))
|
||||
.withCauseInstanceOf(ClassCastException.class);
|
||||
|
||||
ctx.setVariable("target", "abc");
|
||||
expression = parser.parseExpression("#target.charAt(0)");
|
||||
assertThat(expression.getValue(ctx)).isEqualTo('a');
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(ctx)).isEqualTo('a');
|
||||
ctx.setVariable("target", "1");
|
||||
assertThat(expression.getValue(ctx)).isEqualTo('1');
|
||||
|
||||
// Changing the variable type from String to Integer results in a
|
||||
// ClassCastException in the compiled code.
|
||||
ctx.setVariable("target", 42);
|
||||
assertThatExceptionOfType(SpelEvaluationException.class)
|
||||
.isThrownBy(() -> expression.getValue(ctx))
|
||||
.withCauseInstanceOf(ClassCastException.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
class IndexingTests {
|
||||
|
||||
@Test
|
||||
void indexIntoPrimitiveShortArray() {
|
||||
short[] shorts = { (short) 33, (short) 44, (short) 55 };
|
||||
|
||||
expression = parser.parseExpression("[2]");
|
||||
|
||||
assertThat(expression.getValue(shorts)).isEqualTo((short) 55);
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(shorts)).isEqualTo((short) 55);
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("S");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexIntoPrimitiveByteArray() {
|
||||
byte[] bytes = { (byte) 2, (byte) 3, (byte) 4 };
|
||||
|
||||
expression = parser.parseExpression("[2]");
|
||||
|
||||
assertThat(expression.getValue(bytes)).isEqualTo((byte) 4);
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(bytes)).isEqualTo((byte) 4);
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("B");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexIntoPrimitiveIntArray() {
|
||||
int[] ints = { 8, 9, 10 };
|
||||
|
||||
expression = parser.parseExpression("[2]");
|
||||
|
||||
assertThat(expression.getValue(ints)).isEqualTo(10);
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(ints)).isEqualTo(10);
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("I");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexIntoPrimitiveLongArray() {
|
||||
long[] longs = { 2L, 3L, 4L };
|
||||
|
||||
expression = parser.parseExpression("[0]");
|
||||
|
||||
assertThat(expression.getValue(longs)).isEqualTo(2L);
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(longs)).isEqualTo(2L);
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("J");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexIntoPrimitiveFloatArray() {
|
||||
float[] floats = { 6.0f, 7.0f, 8.0f };
|
||||
|
||||
expression = parser.parseExpression("[0]");
|
||||
|
||||
assertThat(expression.getValue(floats)).isEqualTo(6.0f);
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(floats)).isEqualTo(6.0f);
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("F");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexIntoPrimitiveDoubleArray() {
|
||||
double[] doubles = { 3.0d, 4.0d, 5.0d };
|
||||
|
||||
expression = parser.parseExpression("[1]");
|
||||
|
||||
assertThat(expression.getValue(doubles)).isEqualTo(4.0d);
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(doubles)).isEqualTo(4.0d);
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("D");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexIntoPrimitiveCharArray() {
|
||||
char[] chars = { 'a', 'b', 'c' };
|
||||
|
||||
expression = parser.parseExpression("[1]");
|
||||
|
||||
assertThat(expression.getValue(chars)).isEqualTo('b');
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(chars)).isEqualTo('b');
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("C");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexIntoStringArray() {
|
||||
String[] strings = { "a", "b", "c" };
|
||||
|
||||
expression = parser.parseExpression("[0]");
|
||||
|
||||
assertThat(expression.getValue(strings)).isEqualTo("a");
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(strings)).isEqualTo("a");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/String");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexIntoNumberArray() {
|
||||
Number[] numbers = { 2, 8, 9 };
|
||||
|
||||
expression = parser.parseExpression("[1]");
|
||||
|
||||
assertThat(expression.getValue(numbers)).isEqualTo(8);
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(numbers)).isEqualTo(8);
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Number");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexInto2DPrimitiveIntArray() {
|
||||
int[][] array = new int[][] {
|
||||
{ 1, 2, 3 },
|
||||
{ 4, 5, 6 }
|
||||
};
|
||||
|
||||
expression = parser.parseExpression("[1]");
|
||||
|
||||
assertThat(stringify(expression.getValue(array))).isEqualTo("4 5 6");
|
||||
assertCanCompile(expression);
|
||||
assertThat(stringify(expression.getValue(array))).isEqualTo("4 5 6");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("[I");
|
||||
|
||||
expression = parser.parseExpression("[1][2]");
|
||||
|
||||
assertThat(stringify(expression.getValue(array))).isEqualTo("6");
|
||||
assertCanCompile(expression);
|
||||
assertThat(stringify(expression.getValue(array))).isEqualTo("6");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("I");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexInto2DStringArray() {
|
||||
String[][] array = new String[][] {
|
||||
{ "a", "b", "c" },
|
||||
{ "d", "e", "f" }
|
||||
};
|
||||
|
||||
expression = parser.parseExpression("[1]");
|
||||
|
||||
assertThat(stringify(expression.getValue(array))).isEqualTo("d e f");
|
||||
assertCanCompile(expression);
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("[Ljava/lang/String");
|
||||
assertThat(stringify(expression.getValue(array))).isEqualTo("d e f");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("[Ljava/lang/String");
|
||||
|
||||
expression = parser.parseExpression("[1][2]");
|
||||
|
||||
assertThat(stringify(expression.getValue(array))).isEqualTo("f");
|
||||
assertCanCompile(expression);
|
||||
assertThat(stringify(expression.getValue(array))).isEqualTo("f");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/String");
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void indexIntoArrayOfListOfString() {
|
||||
List<String>[] array = new List[] {
|
||||
Arrays.asList("a", "b", "c"),
|
||||
Arrays.asList("d", "e", "f")
|
||||
};
|
||||
|
||||
expression = parser.parseExpression("[1]");
|
||||
|
||||
assertThat(stringify(expression.getValue(array))).isEqualTo("d e f");
|
||||
assertCanCompile(expression);
|
||||
assertThat(stringify(expression.getValue(array))).isEqualTo("d e f");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/util/List");
|
||||
|
||||
expression = parser.parseExpression("[1][2]");
|
||||
|
||||
assertThat(stringify(expression.getValue(array))).isEqualTo("f");
|
||||
assertCanCompile(expression);
|
||||
assertThat(stringify(expression.getValue(array))).isEqualTo("f");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void indexIntoArrayOfMap() {
|
||||
Map<String, String>[] array = new Map[] { Collections.singletonMap("key", "value1") };
|
||||
|
||||
expression = parser.parseExpression("[0]");
|
||||
|
||||
assertThat(stringify(expression.getValue(array))).isEqualTo("{key=value1}");
|
||||
assertCanCompile(expression);
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/util/Map");
|
||||
assertThat(stringify(expression.getValue(array))).isEqualTo("{key=value1}");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/util/Map");
|
||||
|
||||
expression = parser.parseExpression("[0]['key']");
|
||||
|
||||
assertThat(stringify(expression.getValue(array))).isEqualTo("value1");
|
||||
assertCanCompile(expression);
|
||||
assertThat(stringify(expression.getValue(array))).isEqualTo("value1");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexIntoListOfString() {
|
||||
List<String> list = Arrays.asList("aaa", "bbb", "ccc");
|
||||
|
||||
expression = parser.parseExpression("[1]");
|
||||
|
||||
assertThat(expression.getValue(list)).isEqualTo("bbb");
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(list)).isEqualTo("bbb");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexIntoListOfInteger() {
|
||||
List<Integer> list = Arrays.asList(123, 456, 789);
|
||||
|
||||
expression = parser.parseExpression("[2]");
|
||||
|
||||
assertThat(expression.getValue(list)).isEqualTo(789);
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(list)).isEqualTo(789);
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexIntoListOfStringArray() {
|
||||
List<String[]> list = Arrays.asList(
|
||||
new String[] { "a", "b", "c" },
|
||||
new String[] { "d", "e", "f" }
|
||||
);
|
||||
|
||||
expression = parser.parseExpression("[1]");
|
||||
|
||||
assertThat(stringify(expression.getValue(list))).isEqualTo("d e f");
|
||||
assertCanCompile(expression);
|
||||
assertThat(stringify(expression.getValue(list))).isEqualTo("d e f");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
|
||||
expression = parser.parseExpression("[1][0]");
|
||||
|
||||
assertThat(stringify(expression.getValue(list))).isEqualTo("d");
|
||||
assertCanCompile(expression);
|
||||
assertThat(stringify(expression.getValue(list))).isEqualTo("d");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/String");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexIntoListOfIntegerArray() {
|
||||
List<Integer[]> list = Arrays.asList(
|
||||
new Integer[] { 1, 2, 3 },
|
||||
new Integer[] { 4, 5, 6 }
|
||||
);
|
||||
|
||||
expression = parser.parseExpression("[0]");
|
||||
|
||||
assertThat(stringify(expression.getValue(list))).isEqualTo("1 2 3");
|
||||
assertCanCompile(expression);
|
||||
assertThat(stringify(expression.getValue(list))).isEqualTo("1 2 3");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
|
||||
expression = parser.parseExpression("[0][1]");
|
||||
|
||||
assertThat(expression.getValue(list)).isEqualTo(2);
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(list)).isEqualTo(2);
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Integer");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexIntoListOfListOfString() {
|
||||
List<List<String>> list = Arrays.asList(
|
||||
Arrays.asList("a", "b", "c"),
|
||||
Arrays.asList("d", "e", "f")
|
||||
);
|
||||
|
||||
expression = parser.parseExpression("[1]");
|
||||
|
||||
assertThat(stringify(expression.getValue(list))).isEqualTo("d e f");
|
||||
assertCanCompile(expression);
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
assertThat(stringify(expression.getValue(list))).isEqualTo("d e f");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
|
||||
expression = parser.parseExpression("[1][2]");
|
||||
|
||||
assertThat(stringify(expression.getValue(list))).isEqualTo("f");
|
||||
assertCanCompile(expression);
|
||||
assertThat(stringify(expression.getValue(list))).isEqualTo("f");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexIntoMap() {
|
||||
Map<String, Integer> map = Collections.singletonMap("aaa", 111);
|
||||
|
||||
expression = parser.parseExpression("['aaa']");
|
||||
|
||||
assertThat(expression.getValue(map)).isEqualTo(111);
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(map)).isEqualTo(111);
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexIntoMapOfListOfString() {
|
||||
Map<String, List<String>> map = Collections.singletonMap("foo", Arrays.asList("a", "b", "c"));
|
||||
|
||||
expression = parser.parseExpression("['foo']");
|
||||
|
||||
assertThat(stringify(expression.getValue(map))).isEqualTo("a b c");
|
||||
assertCanCompile(expression);
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
assertThat(stringify(expression.getValue(map))).isEqualTo("a b c");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
|
||||
expression = parser.parseExpression("['foo'][2]");
|
||||
|
||||
assertThat(stringify(expression.getValue(map))).isEqualTo("c");
|
||||
assertCanCompile(expression);
|
||||
assertThat(stringify(expression.getValue(map))).isEqualTo("c");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexIntoObject() {
|
||||
TestClass6 tc = new TestClass6();
|
||||
|
||||
// field access
|
||||
expression = parser.parseExpression("['orange']");
|
||||
|
||||
assertThat(expression.getValue(tc)).isEqualTo("value1");
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(tc)).isEqualTo("value1");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/String");
|
||||
|
||||
// field access
|
||||
expression = parser.parseExpression("['peach']");
|
||||
|
||||
assertThat(expression.getValue(tc)).isEqualTo(34L);
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(tc)).isEqualTo(34L);
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("J");
|
||||
|
||||
// property access (getter)
|
||||
expression = parser.parseExpression("['banana']");
|
||||
|
||||
assertThat(expression.getValue(tc)).isEqualTo("value3");
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(tc)).isEqualTo("value3");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/String");
|
||||
}
|
||||
|
||||
@Test // gh-32694, gh-32908
|
||||
void indexIntoArrayUsingIntegerWrapper() {
|
||||
context.setVariable("array", new int[] {1, 2, 3, 4});
|
||||
context.setVariable("index", 2);
|
||||
|
||||
expression = parser.parseExpression("#array[#index]");
|
||||
|
||||
assertThat(expression.getValue(context)).isEqualTo(3);
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(context)).isEqualTo(3);
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("I");
|
||||
}
|
||||
|
||||
@Test // gh-32694, gh-32908
|
||||
void indexIntoListUsingIntegerWrapper() {
|
||||
context.setVariable("list", Arrays.asList(1, 2, 3, 4));
|
||||
context.setVariable("index", 2);
|
||||
|
||||
expression = parser.parseExpression("#list[#index]");
|
||||
|
||||
assertThat(expression.getValue(context)).isEqualTo(3);
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(context)).isEqualTo(3);
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
}
|
||||
|
||||
@Test // gh-32903
|
||||
void indexIntoMapUsingPrimitiveLiteral() {
|
||||
Map<Object, String> map = new HashMap<>();
|
||||
map.put(false, "0"); // BooleanLiteral
|
||||
map.put(1, "ABC"); // IntLiteral
|
||||
map.put(2L, "XYZ"); // LongLiteral
|
||||
map.put(9.99F, "~10"); // FloatLiteral
|
||||
map.put(3.14159, "PI"); // RealLiteral
|
||||
context.setVariable("map", map);
|
||||
|
||||
// BooleanLiteral
|
||||
expression = parser.parseExpression("#map[false]");
|
||||
assertThat(expression.getValue(context)).isEqualTo("0");
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(context)).isEqualTo("0");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
|
||||
// IntLiteral
|
||||
expression = parser.parseExpression("#map[1]");
|
||||
assertThat(expression.getValue(context)).isEqualTo("ABC");
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(context)).isEqualTo("ABC");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
|
||||
// LongLiteral
|
||||
expression = parser.parseExpression("#map[2L]");
|
||||
assertThat(expression.getValue(context)).isEqualTo("XYZ");
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(context)).isEqualTo("XYZ");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
|
||||
// FloatLiteral
|
||||
expression = parser.parseExpression("#map[9.99F]");
|
||||
assertThat(expression.getValue(context)).isEqualTo("~10");
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(context)).isEqualTo("~10");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
|
||||
// RealLiteral
|
||||
expression = parser.parseExpression("#map[3.14159]");
|
||||
assertThat(expression.getValue(context)).isEqualTo("PI");
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(context)).isEqualTo("PI");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
}
|
||||
|
||||
private String stringify(Object object) {
|
||||
Stream<? extends Object> stream;
|
||||
if (object instanceof Collection) {
|
||||
stream = ((Collection<?>) object).stream();
|
||||
}
|
||||
else if (object instanceof Object[]) {
|
||||
stream = Arrays.stream((Object[]) object);
|
||||
}
|
||||
else if (object instanceof int[]) {
|
||||
stream = Arrays.stream((int[]) object).mapToObj(Integer::valueOf);
|
||||
}
|
||||
else {
|
||||
return String.valueOf(object);
|
||||
}
|
||||
return stream.map(Object::toString).collect(Collectors.joining(" "));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void typeReference() {
|
||||
expression = parse("T(String)");
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,7 +20,7 @@ import org.springframework.dao.DataRetrievalFailureException;
|
||||
|
||||
/**
|
||||
* Data access exception thrown when a result set did not have the correct column count,
|
||||
* for example when expecting a single column but getting 0 or more than 1 column.
|
||||
* for example when expecting a single column but getting 0 or more than 1 columns.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,7 +20,7 @@ import org.springframework.dao.IncorrectUpdateSemanticsDataAccessException;
|
||||
|
||||
/**
|
||||
* Exception thrown when a JDBC update affects an unexpected number of rows.
|
||||
* Typically, we expect an update to affect a single row, meaning it is an
|
||||
* Typically we expect an update to affect a single row, meaning it's an
|
||||
* error if it affects multiple rows.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,7 +22,7 @@ package org.springframework.jdbc.core;
|
||||
*
|
||||
* <p>This interface allows you to signal the end of a batch rather than
|
||||
* having to determine the exact batch size upfront. Batch size is still
|
||||
* being honored, but it is now the maximum size of the batch.
|
||||
* being honored but it is now the maximum size of the batch.
|
||||
*
|
||||
* <p>The {@link #isBatchExhausted} method is called after each call to
|
||||
* {@link #setValues} to determine whether there were some values added,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -128,8 +128,8 @@ public abstract class RdbmsOperation implements InitializingBean {
|
||||
|
||||
/**
|
||||
* Set the maximum number of rows for this RDBMS operation. This is important
|
||||
* for processing subsets of large result sets, in order to avoid reading and
|
||||
* holding the entire result set in the database or in the JDBC driver.
|
||||
* for processing subsets of large result sets, avoiding to read and hold
|
||||
* the entire result set in the database or in the JDBC driver.
|
||||
* <p>Default is -1, indicating to use the driver's default.
|
||||
* @see org.springframework.jdbc.core.JdbcTemplate#setMaxRows
|
||||
*/
|
||||
@@ -175,7 +175,7 @@ public abstract class RdbmsOperation implements InitializingBean {
|
||||
public void setUpdatableResults(boolean updatableResults) {
|
||||
if (isCompiled()) {
|
||||
throw new InvalidDataAccessApiUsageException(
|
||||
"The updatableResults flag must be set before the operation is compiled");
|
||||
"The updateableResults flag must be set before the operation is compiled");
|
||||
}
|
||||
this.updatableResults = updatableResults;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,7 +29,7 @@ import org.springframework.jdbc.core.SqlParameter;
|
||||
|
||||
/**
|
||||
* Superclass for object abstractions of RDBMS stored procedures.
|
||||
* This class is abstract, and it is intended that subclasses will provide a typed
|
||||
* This class is abstract and it is intended that subclasses will provide a typed
|
||||
* method for invocation that delegates to the supplied {@link #execute} method.
|
||||
*
|
||||
* <p>The inherited {@link #setSql sql} property is the name of the stored procedure
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -25,9 +25,9 @@ import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Registry for custom {@link SQLExceptionTranslator} instances associated with
|
||||
* specific databases allowing for overriding translation based on values
|
||||
* contained in the configuration file named "sql-error-codes.xml".
|
||||
* Registry for custom {@link org.springframework.jdbc.support.SQLExceptionTranslator} instances associated with
|
||||
* specific databases allowing for overriding translation based on values contained in the configuration file
|
||||
* named "sql-error-codes.xml".
|
||||
*
|
||||
* @author Thomas Risberg
|
||||
* @since 3.1.1
|
||||
@@ -38,7 +38,7 @@ public final class CustomSQLExceptionTranslatorRegistry {
|
||||
private static final Log logger = LogFactory.getLog(CustomSQLExceptionTranslatorRegistry.class);
|
||||
|
||||
/**
|
||||
* Keep track of a single instance, so we can return it to classes that request it.
|
||||
* Keep track of a single instance so we can return it to classes that request it.
|
||||
*/
|
||||
private static final CustomSQLExceptionTranslatorRegistry instance = new CustomSQLExceptionTranslatorRegistry();
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -218,7 +218,7 @@ public abstract class JdbcUtils {
|
||||
return NumberUtils.convertNumberToTargetClass((Number) obj, Integer.class);
|
||||
}
|
||||
else {
|
||||
// e.g. on Postgres: getObject returns a PGObject, but we need a String
|
||||
// e.g. on Postgres: getObject returns a PGObject but we need a String
|
||||
return rs.getString(index);
|
||||
}
|
||||
}
|
||||
@@ -228,14 +228,14 @@ public abstract class JdbcUtils {
|
||||
try {
|
||||
return rs.getObject(index, requiredType);
|
||||
}
|
||||
catch (SQLFeatureNotSupportedException | AbstractMethodError ex) {
|
||||
catch (AbstractMethodError err) {
|
||||
logger.debug("JDBC driver does not implement JDBC 4.1 'getObject(int, Class)' method", err);
|
||||
}
|
||||
catch (SQLFeatureNotSupportedException ex) {
|
||||
logger.debug("JDBC driver does not support JDBC 4.1 'getObject(int, Class)' method", ex);
|
||||
}
|
||||
catch (SQLException ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("JDBC driver has limited support for 'getObject(int, Class)' with column type: " +
|
||||
requiredType.getName(), ex);
|
||||
}
|
||||
logger.debug("JDBC driver has limited support for JDBC 4.1 'getObject(int, Class)' method", ex);
|
||||
}
|
||||
|
||||
// Corresponding SQL types for JSR-310 / Joda-Time types, left up
|
||||
@@ -416,14 +416,14 @@ public abstract class JdbcUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the given JDBC driver supports JDBC batch updates.
|
||||
* Return whether the given JDBC driver supports JDBC 2.0 batch updates.
|
||||
* <p>Typically invoked right before execution of a given set of statements:
|
||||
* to decide whether the set of SQL statements should be executed through
|
||||
* the JDBC batch mechanism or simply in a traditional one-by-one fashion.
|
||||
* the JDBC 2.0 batch mechanism or simply in a traditional one-by-one fashion.
|
||||
* <p>Logs a warning if the "supportsBatchUpdates" methods throws an exception
|
||||
* and simply returns {@code false} in that case.
|
||||
* @param con the Connection to check
|
||||
* @return whether JDBC batch updates are supported
|
||||
* @return whether JDBC 2.0 batch updates are supported
|
||||
* @see java.sql.DatabaseMetaData#supportsBatchUpdates()
|
||||
*/
|
||||
public static boolean supportsBatchUpdates(Connection con) {
|
||||
@@ -496,8 +496,8 @@ public abstract class JdbcUtils {
|
||||
/**
|
||||
* Determine the column name to use. The column name is determined based on a
|
||||
* lookup using ResultSetMetaData.
|
||||
* <p>This method's implementation takes into account clarifications expressed
|
||||
* in the JDBC 4.0 specification:
|
||||
* <p>This method implementation takes into account recent clarifications
|
||||
* expressed in the JDBC 4.0 specification:
|
||||
* <p><i>columnLabel - the label for the column specified with the SQL AS clause.
|
||||
* If the SQL AS clause was not specified, then the label is the name of the column</i>.
|
||||
* @param resultSetMetaData the current meta-data to use
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user