Compare commits

..

1 Commits

Author SHA1 Message Date
Spring Builds 508cc346e0 Release v5.3.22 2022-07-14 08:39:33 +00:00
861 changed files with 9913 additions and 17881 deletions
@@ -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
-34
View File
@@ -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,64 +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'
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GRADLE_ENTERPRISE_CACHE_USER }}
GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }}
run: ./gradlew -PdeploymentRepository=$(pwd)/deployment-repository build publishAllPublicationsToDeploymentRepository
- name: Deploy
uses: spring-io/artifactory-deploy-action@v0.0.1
with:
uri: 'https://repo.spring.io'
username: ${{ secrets.ARTIFACTORY_USERNAME }}
password: ${{ secrets.ARTIFACTORY_PASSWORD }}
build-name: ${{ format('spring-framework-{0}', github.ref_name)}}
repository: 'libs-snapshot-local'
folder: 'deployment-repository'
signing-key: ${{ secrets.GPG_PRIVATE_KEY }}
signing-passphrase: ${{ secrets.GPG_PASSPHRASE }}
artifact-properties: |
/**/framework-api-*.zip::zip.name=spring-framework,zip.deployed=false
/**/framework-api-*-docs.zip::zip.type=docs
/**/framework-api-*-schema.zip::zip.type=schema
- name: Send notification
uses: ./.github/actions/send-notification
if: always()
with:
webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
status: ${{ job.status }}
build-scan-url: ${{ steps.build.outputs.build-scan-url }}
run-name: ${{ format('{0} | Linux | Java 8', github.ref_name) }}
-82
View File
@@ -1,82 +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'
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GRADLE_ENTERPRISE_CACHE_USER }}
GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }}
run: ./gradlew check
- name: Send notification
uses: ./.github/actions/send-notification
if: always()
with:
webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
status: ${{ job.status }}
build-scan-url: ${{ steps.build.outputs.build-scan-url }}
run-name: ${{ format('{0} | {1} | Java {2}', github.ref_name, matrix.os.name, matrix.java.version) }}
@@ -1,13 +1,10 @@
name: "Validate Gradle Wrapper"
on: [push, pull_request]
permissions:
contents: read
jobs:
validation:
name: "Validation"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: gradle/wrapper-validation-action@v2
- uses: actions/checkout@v2
- uses: gradle/wrapper-validation-action@v1
+1 -5
View File
@@ -24,11 +24,9 @@ buildSrc/build
/spring-core/graalvm/build
/spring-core/kotlin-coroutines/build
/framework-bom/build
/framework-docs/build
/integration-tests/build
/src/asciidoc/build
target/
/target/
# Eclipse artifacts, including WTP generated manifests
.classpath
@@ -46,6 +44,4 @@ atlassian-ide-plugin.xml
.gradletasknamecache
# VS Code
.vscode/
cached-antora-playbook.yml
.vscode/
+1 -1
View File
@@ -1,3 +1,3 @@
# Enable auto-env through the sdkman_auto_env config
# Add key=value pairs of SDKs to use below
java=8.0.382-librca
java=8.0.333-librca
+1 -1
View File
@@ -1,4 +1,4 @@
# <img src="framework-docs/src/docs/spring-framework.png" width="80" height="80"> Spring Framework [![Build Status](https://github.com/spring-projects/spring-framework/actions/workflows/build-and-deploy-snapshot.yml/badge.svg?branch=5.3.x)](https://github.com/spring-projects/spring-framework/actions/workflows/build-and-deploy-snapshot.yml?query=branch%3A5.3.x) [![Revved up by Develocity](https://img.shields.io/badge/Revved%20up%20by-Develocity-06A0CE?logo=Gradle&labelColor=02303A)](https://ge.spring.io/scans?search.rootProjectNames=spring)
# <img src="src/docs/spring-framework.png" width="80" height="80"> Spring Framework [![Build Status](https://ci.spring.io/api/v1/teams/spring-framework/pipelines/spring-framework-5.3.x/jobs/build/badge)](https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-5.3.x?groups=Build") [![Revved up by Gradle Enterprise](https://img.shields.io/badge/Revved%20up%20by-Gradle%20Enterprise-06A0CE?logo=Gradle&labelColor=02303A)](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".
+40 -44
View File
@@ -1,17 +1,17 @@
plugins {
id 'io.spring.dependency-management' version '1.0.11.RELEASE' apply false
id 'io.spring.nohttp' version '0.0.10'
id 'io.freefair.aspectj' version '6.2.0' apply false
id "io.freefair.aspectj" version '6.2.0' apply false
id 'org.jetbrains.dokka' version '1.6.10' apply false
id 'org.jetbrains.kotlin.jvm' version '1.5.32' apply false
id 'org.jetbrains.kotlin.plugin.serialization' version "1.5.32" apply false
id "org.jetbrains.kotlin.plugin.serialization" version "1.5.32" apply false
id 'org.asciidoctor.jvm.convert' version '3.3.2'
id 'org.asciidoctor.jvm.pdf' version '3.3.2'
id 'org.unbroken-dome.xjc' version '2.0.0' apply false
id 'com.github.ben-manes.versions' version '0.39.0'
id 'com.github.johnrengelman.shadow' version '7.0.0' apply false
id "org.unbroken-dome.xjc" version '2.0.0' apply false
id "com.github.ben-manes.versions" version '0.39.0'
id "com.github.johnrengelman.shadow" version '7.0.0' apply false
id 'de.undercouch.download' version '4.1.2'
id 'me.champeau.jmh' version "0.6.6" apply false
id "me.champeau.jmh" version "0.6.6" apply false
}
ext {
@@ -28,18 +28,18 @@ configure(allprojects) { project ->
dependencyManagement {
imports {
mavenBom "com.fasterxml.jackson:jackson-bom:2.12.7"
mavenBom "io.netty:netty-bom:4.1.108.Final"
mavenBom "io.projectreactor:reactor-bom:2020.0.43"
mavenBom "io.netty:netty-bom:4.1.79.Final"
mavenBom "io.projectreactor:reactor-bom:2020.0.21"
mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR13"
mavenBom "io.rsocket:rsocket-bom:1.1.3"
mavenBom "org.eclipse.jetty:jetty-bom:9.4.54.v20240208"
mavenBom "io.rsocket:rsocket-bom:1.1.2"
mavenBom "org.eclipse.jetty:jetty-bom:9.4.48.v20220622"
mavenBom "org.jetbrains.kotlin:kotlin-bom:1.5.32"
mavenBom "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.5.2"
mavenBom "org.jetbrains.kotlinx:kotlinx-serialization-bom:1.2.2"
mavenBom "org.junit:junit-bom:5.8.2"
}
dependencies {
dependencySet(group: 'org.apache.logging.log4j', version: '2.21.1') {
dependencySet(group: 'org.apache.logging.log4j', version: '2.18.0') {
entry 'log4j-api'
entry 'log4j-core'
entry 'log4j-jul'
@@ -67,9 +67,9 @@ configure(allprojects) { project ->
dependency "io.reactivex:rxjava:1.3.8"
dependency "io.reactivex:rxjava-reactive-streams:1.2.1"
dependency "io.reactivex.rxjava2:rxjava:2.2.21"
dependency "io.reactivex.rxjava3:rxjava:3.1.8"
dependency "io.smallrye.reactive:mutiny:1.9.0"
dependency "io.projectreactor.tools:blockhound:1.0.8.RELEASE"
dependency "io.reactivex.rxjava3:rxjava:3.1.4"
dependency "io.smallrye.reactive:mutiny:1.4.0"
dependency "io.projectreactor.tools:blockhound:1.0.6.RELEASE"
dependency "com.caucho:hessian:4.0.63"
dependency "com.fasterxml:aalto-xml:1.3.1"
@@ -92,11 +92,11 @@ configure(allprojects) { project ->
entry 'jibx-run'
}
dependency "org.ogce:xpp3:1.1.6"
dependency "org.yaml:snakeyaml:1.33"
dependency "org.yaml:snakeyaml:1.30"
dependency "com.h2database:h2:2.1.214"
dependency "com.github.ben-manes.caffeine:caffeine:2.9.3"
dependency "com.github.librepdf:openpdf:1.3.33"
dependency "com.github.librepdf:openpdf:1.3.29"
dependency "com.rometools:rome:1.18.0"
dependency "commons-io:commons-io:2.5"
dependency "io.vavr:vavr:0.10.4"
@@ -124,22 +124,22 @@ configure(allprojects) { project ->
dependency "org.ehcache:jcache:1.0.1"
dependency "org.ehcache:ehcache:3.4.0"
dependency "org.hibernate:hibernate-core:5.4.33.Final"
dependency "org.hibernate:hibernate-validator:6.2.5.Final"
dependency "org.hibernate:hibernate-validator:6.2.3.Final"
dependency "org.webjars:webjars-locator-core:0.48"
dependency "org.webjars:underscorejs:1.8.3"
dependencySet(group: 'org.apache.tomcat', version: '9.0.82') {
dependencySet(group: 'org.apache.tomcat', version: '9.0.64') {
entry 'tomcat-util'
entry('tomcat-websocket') {
exclude group: "org.apache.tomcat", name: "tomcat-servlet-api"
exclude group: "org.apache.tomcat", name: "tomcat-websocket-api"
}
}
dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.82') {
dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.64') {
entry 'tomcat-embed-core'
entry 'tomcat-embed-websocket'
}
dependencySet(group: 'io.undertow', version: '2.2.29.Final') {
dependencySet(group: 'io.undertow', version: '2.2.18.Final') {
entry 'undertow-core'
entry('undertow-servlet') {
exclude group: "org.jboss.spec.javax.servlet", name: "jboss-servlet-api_4.0_spec"
@@ -150,7 +150,7 @@ configure(allprojects) { project ->
}
}
dependency "org.eclipse.jetty:jetty-reactive-httpclient:1.1.13"
dependency "org.eclipse.jetty:jetty-reactive-httpclient:1.1.12"
dependency 'org.apache.httpcomponents.client5:httpclient5:5.1.3'
dependency 'org.apache.httpcomponents.core5:httpcore5-reactive:5.1.3'
dependency("org.apache.httpcomponents:httpclient:4.5.13") {
@@ -191,14 +191,14 @@ configure(allprojects) { project ->
dependency "org.junit.support:testng-engine:1.0.4"
dependency "org.hamcrest:hamcrest:2.2"
dependency "org.awaitility:awaitility:3.1.6"
dependency "org.assertj:assertj-core:3.24.2"
dependency "org.assertj:assertj-core:3.23.0"
dependencySet(group: 'org.xmlunit', version: '2.9.0') {
entry 'xmlunit-assertj'
entry('xmlunit-matchers') {
exclude group: "org.hamcrest", name: "hamcrest-core"
}
}
dependencySet(group: 'org.mockito', version: '4.9.0') { // spring-beans tests fail with 4.10+
dependencySet(group: 'org.mockito', version: '4.6.1') {
entry('mockito-core') {
exclude group: "org.hamcrest", name: "hamcrest-core"
}
@@ -206,10 +206,10 @@ configure(allprojects) { project ->
}
dependency "io.mockk:mockk:1.12.1"
dependency("net.sourceforge.htmlunit:htmlunit:2.70.0") {
dependency("net.sourceforge.htmlunit:htmlunit:2.63.0") {
exclude group: "commons-logging", name: "commons-logging"
}
dependency("org.seleniumhq.selenium:htmlunit-driver:2.70.0") {
dependency("org.seleniumhq.selenium:htmlunit-driver:2.63.0") {
exclude group: "commons-logging", name: "commons-logging"
}
dependency("org.seleniumhq.selenium:selenium-java:3.141.59") {
@@ -217,7 +217,7 @@ configure(allprojects) { project ->
exclude group: "io.netty", name: "netty"
}
dependency "org.skyscreamer:jsonassert:1.5.0"
dependency "com.jayway.jsonpath:json-path:2.7.0"
dependency "com.jayway.jsonpath:json-path:2.6.0"
dependency "org.bouncycastle:bcpkix-jdk18on:1.71"
dependencySet(group: 'org.apache.tiles', version: '3.0.8') {
@@ -238,7 +238,7 @@ configure(allprojects) { project ->
dependency "com.ibm.websphere:uow:6.0.2.17"
dependency "com.jamonapi:jamon:2.82"
dependency "joda-time:joda-time:2.10.13"
dependency "org.eclipse.persistence:org.eclipse.persistence.jpa:2.7.12"
dependency "org.eclipse.persistence:org.eclipse.persistence.jpa:2.7.10"
dependency "org.javamoney:moneta:1.3"
dependency "com.sun.activation:javax.activation:1.2.0"
@@ -340,7 +340,7 @@ configure([rootProject] + javaProjects) { project ->
}
checkstyle {
toolVersion = "10.12.7"
toolVersion = "10.3.1"
configDirectory.set(rootProject.file("src/checkstyle"))
}
@@ -362,7 +362,7 @@ configure([rootProject] + javaProjects) { project ->
// JSR-305 only used for non-required meta-annotations
compileOnly("com.google.code.findbugs:jsr305")
testCompileOnly("com.google.code.findbugs:jsr305")
checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:0.0.41")
checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:0.0.31")
}
ext.javadocLinks = [
@@ -375,13 +375,13 @@ configure([rootProject] + javaProjects) { project ->
"https://tiles.apache.org/tiles-request/apidocs/",
"https://tiles.apache.org/framework/apidocs/",
"https://www.eclipse.org/aspectj/doc/released/aspectj5rt-api/",
// Temporarily commenting out Ehcache and Quartz since javadoc on JDK 8 cannot access them.
// "https://www.ehcache.org/apidocs/2.10.4/",
// "https://www.quartz-scheduler.org/api/2.3.0/",
"https://www.javadoc.io/doc/com.fasterxml.jackson.core/jackson-core/2.12.7/",
"https://www.javadoc.io/doc/com.fasterxml.jackson.core/jackson-databind/2.12.7/",
"https://www.javadoc.io/doc/com.fasterxml.jackson.dataformat/jackson-dataformat-xml/2.12.7/",
"https://hc.apache.org/httpcomponents-client-5.1.x/current/httpclient5/apidocs/",
"https://www.ehcache.org/apidocs/2.10.4/",
"https://www.quartz-scheduler.org/api/2.3.0/",
"https://fasterxml.github.io/jackson-core/javadoc/2.10/",
"https://fasterxml.github.io/jackson-databind/javadoc/2.10/",
"https://fasterxml.github.io/jackson-dataformat-xml/javadoc/2.10/",
// TODO Uncomment once httpclient5 API docs are again online.
// "https://hc.apache.org/httpcomponents-client-5.1.x/current/httpclient5/apidocs/",
"https://projectreactor.io/docs/test/release/api/",
"https://junit.org/junit4/javadoc/4.13.2/",
// Disabling linking to JUnit 5.8.2, since the `package-list` file no longer exists due to
@@ -389,8 +389,7 @@ configure([rootProject] + javaProjects) { project ->
// "https://junit.org/junit5/docs/5.8.2/api/",
"https://www.reactive-streams.org/reactive-streams-1.0.3-javadoc/",
"https://javadoc.io/static/io.rsocket/rsocket-core/1.1.1/",
// Temporarily commenting out R2DBC since javadoc on JDK 8 cannot access it.
// "https://r2dbc.io/spec/0.8.5.RELEASE/api/",
"https://r2dbc.io/spec/0.8.5.RELEASE/api/",
// The external Javadoc link for JSR 305 must come last to ensure that types from
// JSR 250 (such as @PostConstruct) are still supported. This is due to the fact
// that JSR 250 and JSR 305 both define types in javax.annotation, which results
@@ -415,9 +414,11 @@ configure(rootProject) {
nohttp {
source.exclude "**/test-output/**"
source.exclude "spring-core/graalvm/build/**" // only available on `main`
source.exclude "spring-core-test/build/**" // only available on `main`
allowlistFile = project.file("src/nohttp/allowlist.lines")
def rootPath = file(rootDir).toPath()
def projectDirs = allprojects.collect { it.projectDir } + "${rootDir}/spring-core-test" + "${rootDir}/buildSrc" + "${rootDir}/framework-docs"
def projectDirs = allprojects.collect { it.projectDir } + "${rootDir}/buildSrc"
projectDirs.forEach { dir ->
[ 'bin', 'build', 'out', '.settings' ]
.collect { rootPath.relativize(new File(dir, it).toPath()) }
@@ -428,10 +429,6 @@ configure(rootProject) {
}
}
tasks.named("checkstyleNohttp").configure {
maxHeapSize = "1g"
}
publishing {
publications {
mavenJava(MavenPublication) {
@@ -441,5 +438,4 @@ configure(rootProject) {
}
}
}
}
-2
View File
@@ -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].
-3
View File
@@ -15,6 +15,3 @@ changelog:
sort: "title"
labels:
- "type: dependency-upgrade"
contributors:
exclude:
names: ["bclozel", "jhoeller", "poutsma", "rstoyanchev", "sbrannen", "sdeleuze", "snicoll", "simonbasle"]
+2 -1
View File
@@ -1,10 +1,11 @@
FROM ubuntu:jammy-20240125
FROM ubuntu:focal-20220531
ADD setup.sh /setup.sh
ADD get-jdk-url.sh /get-jdk-url.sh
RUN ./setup.sh java8
ENV JAVA_HOME /opt/openjdk/java8
ENV JDK11 /opt/openjdk/java11
ENV JDK17 /opt/openjdk/java17
ENV PATH $JAVA_HOME/bin:$PATH
+8 -2
View File
@@ -3,10 +3,16 @@ set -e
case "$1" in
java8)
echo "https://github.com/bell-sw/Liberica/releases/download/8u402%2B7/bellsoft-jdk8u402+7-linux-amd64.tar.gz"
echo "https://github.com/bell-sw/Liberica/releases/download/8u333+2/bellsoft-jdk8u333+2-linux-amd64.tar.gz"
;;
java11)
echo "https://github.com/bell-sw/Liberica/releases/download/11.0.15.1+2/bellsoft-jdk11.0.15.1+2-linux-amd64.tar.gz"
;;
java17)
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.10%2B13/bellsoft-jdk17.0.10+13-linux-amd64.tar.gz"
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.3.1+2/bellsoft-jdk17.0.3.1+2-linux-amd64.tar.gz"
;;
java18)
echo "https://github.com/bell-sw/Liberica/releases/download/18.0.1.1+2/bellsoft-jdk18.0.1.1+2-linux-amd64.tar.gz"
;;
*)
echo $"Unknown java version"
+1 -1
View File
@@ -20,7 +20,7 @@ curl https://raw.githubusercontent.com/spring-io/concourse-java-scripts/v0.0.4/c
mkdir -p /opt/openjdk
pushd /opt/openjdk > /dev/null
for jdk in java8 java17
for jdk in java8 java11 java17
do
JDK_URL=$( /get-jdk-url.sh $jdk )
mkdir $jdk
+4
View File
@@ -8,3 +8,7 @@ milestone: "5.3.x"
build-name: "spring-framework"
pipeline-name: "spring-framework"
concourse-url: "https://ci.spring.io"
registry-mirror-host: docker.repo.spring.io
registry-mirror-username: ((artifactory-username))
registry-mirror-password: ((artifactory-password))
task-timeout: 1h00m
+208 -21
View File
@@ -23,6 +23,19 @@ anchors:
docker-resource-source: &docker-resource-source
username: ((docker-hub-username))
password: ((docker-hub-password))
tag: ((milestone))
registry-mirror-vars: &registry-mirror-vars
registry-mirror-host: ((registry-mirror-host))
registry-mirror-username: ((registry-mirror-username))
registry-mirror-password: ((registry-mirror-password))
slack-fail-params: &slack-fail-params
text: >
:concourse-failed: <https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}|${BUILD_PIPELINE_NAME} ${BUILD_JOB_NAME} failed!>
[$TEXT_FILE_CONTENT]
text_file: git-repo/build/build-scan-uri.txt
silent: true
icon_emoji: ":concourse:"
username: concourse-ci
changelog-task-params: &changelog-task-params
name: generated-changelog/tag
tag: generated-changelog/tag
@@ -35,33 +48,46 @@ resource_types:
- name: registry-image
type: registry-image
source:
<<: *docker-resource-source
repository: concourse/registry-image-resource
tag: 1.8.0
tag: 1.5.0
- name: artifactory-resource
type: registry-image
source:
<<: *docker-resource-source
repository: springio/artifactory-resource
tag: 0.0.18
tag: 0.0.17
- name: github-release
type: registry-image
source:
<<: *docker-resource-source
repository: concourse/github-release-resource
tag: 1.8.0
tag: 1.5.5
- name: github-status-resource
type: registry-image
source:
<<: *docker-resource-source
repository: dpb587/github-status-resource
tag: master
- name: pull-request
type: registry-image
source:
repository: teliaoss/github-pr-resource
tag: v0.23.0
- name: slack-notification
type: registry-image
source:
repository: cfcommunity/slack-notification-resource
tag: latest
resources:
- name: git-repo
type: git
icon: github
source:
<<: *git-repo-resource-source
- name: every-morning
type: time
icon: alarm
source:
start: 8:00 AM
stop: 9:00 AM
location: Europe/Vienna
- name: ci-images-git-repo
type: git
icon: github
@@ -75,7 +101,6 @@ resources:
source:
<<: *docker-resource-source
repository: ((docker-hub-organization))/spring-framework-ci
tag: ((milestone))
- name: artifactory-repo
type: artifactory-resource
icon: package-variant
@@ -84,6 +109,43 @@ resources:
username: ((artifactory-username))
password: ((artifactory-password))
build_name: ((build-name))
- name: git-pull-request
type: pull-request
icon: source-pull
source:
access_token: ((github-ci-pull-request-token))
repository: ((github-repo-name))
base_branch: ((branch))
ignore_paths: ["ci/*"]
- name: repo-status-build
type: github-status-resource
icon: eye-check-outline
source:
repository: ((github-repo-name))
access_token: ((github-ci-status-token))
branch: ((branch))
context: build
- name: repo-status-jdk11-build
type: github-status-resource
icon: eye-check-outline
source:
repository: ((github-repo-name))
access_token: ((github-ci-status-token))
branch: ((branch))
context: jdk11-build
- name: repo-status-jdk17-build
type: github-status-resource
icon: eye-check-outline
source:
repository: ((github-repo-name))
access_token: ((github-ci-status-token))
branch: ((branch))
context: jdk17-build
- name: slack-alert
type: slack-notification
icon: slack
source:
url: ((slack-webhook-url))
- name: github-pre-release
type: github-release
icon: briefcase-download-outline
@@ -114,27 +176,41 @@ jobs:
image: ci-image
vars:
ci-image-name: ci-image
<<: *docker-resource-source
<<: *registry-mirror-vars
- 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}"
@@ -159,9 +235,114 @@ jobs:
- "/**/spring-*-schema.zip"
properties:
"zip.type": "schema"
- put: git-repo
params:
repository: stage-git-repo
get_params:
threads: 8
- name: jdk11-build
serial: true
public: true
plan:
- get: ci-image
- get: git-repo
- get: every-morning
trigger: true
- put: repo-status-jdk11-build
params: { state: "pending", commit: "git-repo" }
- do:
- task: check-project
image: ci-image
file: git-repo/ci/tasks/check-project.yml
privileged: true
timeout: ((task-timeout))
params:
TEST_TOOLCHAIN: 11
<<: *build-project-task-params
on_failure:
do:
- put: repo-status-jdk11-build
params: { state: "failure", commit: "git-repo" }
- put: slack-alert
params:
<<: *slack-fail-params
- put: repo-status-jdk11-build
params: { state: "success", commit: "git-repo" }
- name: jdk17-build
serial: true
public: true
plan:
- get: ci-image
- get: git-repo
- get: every-morning
trigger: true
- put: repo-status-jdk17-build
params: { state: "pending", commit: "git-repo" }
- do:
- task: check-project
image: ci-image
file: git-repo/ci/tasks/check-project.yml
privileged: true
timeout: ((task-timeout))
params:
TEST_TOOLCHAIN: 15
<<: *build-project-task-params
on_failure:
do:
- put: repo-status-jdk17-build
params: { state: "failure", commit: "git-repo" }
- put: slack-alert
params:
<<: *slack-fail-params
- put: repo-status-jdk17-build
params: { state: "success", commit: "git-repo" }
- name: build-pull-requests
serial: true
public: true
plan:
- get: ci-image
- get: git-repo
resource: git-pull-request
trigger: true
version: every
- do:
- put: git-pull-request
params:
path: git-repo
status: pending
- task: build-pr
image: ci-image
file: git-repo/ci/tasks/build-pr.yml
privileged: true
timeout: ((task-timeout))
params:
BRANCH: ((branch))
on_success:
put: git-pull-request
params:
path: git-repo
status: success
on_failure:
put: git-pull-request
params:
path: git-repo
status: failure
- name: stage-milestone
serial: true
plan:
- get: ci-image
- get: git-repo
trigger: false
- task: stage
image: ci-image
file: git-repo/ci/tasks/stage-version.yml
params:
RELEASE_TYPE: M
<<: *gradle-enterprise-task-params
- put: artifactory-repo
params:
<<: *artifactory-params
repo: libs-staging-local
- put: git-repo
params:
repository: stage-git-repo
- name: promote-milestone
serial: true
plan:
@@ -202,6 +383,7 @@ jobs:
- put: artifactory-repo
params:
<<: *artifactory-params
repo: libs-staging-local
- put: git-repo
params:
repository: stage-git-repo
@@ -245,6 +427,7 @@ jobs:
- put: artifactory-repo
params:
<<: *artifactory-params
repo: libs-staging-local
- put: git-repo
params:
repository: stage-git-repo
@@ -287,7 +470,11 @@ jobs:
<<: *changelog-task-params
groups:
- name: "builds"
jobs: ["build", "jdk11-build", "jdk17-build"]
- name: "releases"
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release"]
- name: "ci-images"
jobs: ["build-ci-images"]
- name: "pull-requests"
jobs: [ "build-pull-requests" ]
+8
View File
@@ -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
+9
View File
@@ -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
+9
View File
@@ -0,0 +1,9 @@
#!/bin/bash
set -e
source $(dirname $0)/common.sh
pushd git-repo > /dev/null
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK11,JDK15 \
-PmainToolchain=${MAIN_TOOLCHAIN} -PtestToolchain=${TEST_TOOLCHAIN} --no-daemon --max-workers=4 check
popd > /dev/null
+1
View File
@@ -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/' )
+6 -5
View File
@@ -4,9 +4,11 @@ image_resource:
type: registry-image
source:
repository: concourse/oci-build-task
tag: 0.10.0
username: ((docker-hub-username))
password: ((docker-hub-password))
tag: 0.9.1
registry_mirror:
host: ((registry-mirror-host))
username: ((registry-mirror-username))
password: ((registry-mirror-password))
inputs:
- name: ci-images-git-repo
outputs:
@@ -26,5 +28,4 @@ run:
cat > /root/.docker/config.json <<EOF
{ "auths": { "https://index.docker.io/v1/": { "auth": "$DOCKER_HUB_AUTH" }}}
EOF
build
build
+19
View File
@@ -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
+22
View File
@@ -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
+24
View File
@@ -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
+1 -3
View File
@@ -4,9 +4,7 @@ image_resource:
type: registry-image
source:
repository: springio/github-changelog-generator
tag: '0.0.8'
username: ((docker-hub-username))
password: ((docker-hub-password))
tag: '0.0.7'
inputs:
- name: git-repo
- name: artifactory-repo
+1 -3
View File
@@ -4,9 +4,7 @@ image_resource:
type: registry-image
source:
repository: springio/concourse-release-scripts
tag: '0.4.0'
username: ((docker-hub-username))
password: ((docker-hub-password))
tag: '0.3.4'
inputs:
- name: git-repo
- name: artifactory-repo
+1 -1
View File
@@ -1,4 +1,4 @@
version=5.3.34
version=5.3.22
org.gradle.jvmargs=-Xmx2048m
org.gradle.caching=true
org.gradle.parallel=true
+1 -1
View File
@@ -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/"))
+5 -4
View File
@@ -3,7 +3,7 @@ configurations {
}
dependencies {
asciidoctorExt("io.spring.asciidoctor:spring-asciidoctor-extensions-block-switch:0.6.1")
asciidoctorExt("io.spring.asciidoctor:spring-asciidoctor-extensions-block-switch:0.5.0")
}
repositories {
@@ -73,8 +73,9 @@ pluginManager.withPlugin("kotlin") {
}
task downloadResources(type: Download) {
src "https://repo.spring.io/artifactory/snapshot/io/spring/docresources/" +
"spring-doc-resources/0.2.6-SNAPSHOT/spring-doc-resources-0.2.6-20210308.231804-2.zip"
def version = "0.2.5"
src "https://repo.spring.io/release/io/spring/docresources/" +
"spring-doc-resources/$version/spring-doc-resources-${version}.zip"
dest project.file("$buildDir/docs/spring-doc-resources.zip")
onlyIfModified true
useETag "all"
@@ -152,7 +153,7 @@ asciidoctorPdf {
task docsZip(type: Zip, dependsOn: ['api', 'asciidoctor', 'asciidoctorPdf', 'dokkaHtmlMultiModule']) {
group = "Distribution"
description = "Builds -${archiveClassifier} archive containing api and reference " +
"for deployment at https://docs.spring.io/spring-framework/docs/."
"for deployment at https://docs.spring.io/spring-framework/docs."
archiveBaseName.set("spring-framework")
archiveClassifier.set("docs")
+15 -4
View File
@@ -81,10 +81,6 @@ plugins.withType(JavaPlugin) {
javaLauncher = javaToolchains.launcherFor {
languageVersion = testLanguageVersion
}
if(testLanguageVersion == JavaLanguageVersion.of(17)) {
jvmArgs(["--add-opens=java.base/java.lang=ALL-UNNAMED",
"--add-opens=java.base/java.util=ALL-UNNAMED"])
}
}
}
}
@@ -134,6 +130,21 @@ pluginManager.withPlugin("kotlin") {
}
}
}
if (testToolchainConfigured()) {
def testLanguageVersion = testToolchainLanguageVersion()
def compiler = javaToolchains.compilerFor {
languageVersion = testLanguageVersion
}
// See https://kotlinlang.org/docs/gradle.html#attributes-specific-for-jvm
def javaVersion = testLanguageVersion.toString() == '8' ? '1.8' : testLanguageVersion.toString()
compileTestKotlin {
kotlinOptions {
jvmTarget = javaVersion
jdkHome = compiler.get().metadata.installationPath.asFile.absolutePath
}
}
}
}
// Configure the JMH plugin to use the toolchain for generating and running JMH bytecode
+1 -1
View File
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
+1 -1
View File
@@ -31,6 +31,6 @@ You'll notice these files are already intentionally in .gitignore. The same poli
## FAQ
Q. What about IntelliJ IDEA's own [Gradle support](https://www.jetbrains.com/help/idea/gradle.html)?
Q. What about IntelliJ IDEA's own [Gradle support](https://confluence.jetbrains.net/display/IDEADEV/Gradle+integration)?
A. Keep an eye on https://youtrack.jetbrains.com/issue/IDEA-53476
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -60,8 +60,8 @@ class ScheduledAndTransactionalAnnotationIntegrationTests {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(Config.class, JdkProxyTxConfig.class, RepoConfigA.class);
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(ctx::refresh)
.withCauseInstanceOf(IllegalStateException.class);
.isThrownBy(ctx::refresh)
.withCauseInstanceOf(IllegalStateException.class);
}
@Test
@@ -70,7 +70,7 @@ class ScheduledAndTransactionalAnnotationIntegrationTests {
ctx.register(Config.class, SubclassProxyTxConfig.class, RepoConfigA.class);
ctx.refresh();
Thread.sleep(200); // allow @Scheduled method to be called several times
Thread.sleep(100); // allow @Scheduled method to be called several times
MyRepository repository = ctx.getBean(MyRepository.class);
CallCountingTransactionManager txManager = ctx.getBean(CallCountingTransactionManager.class);
@@ -85,7 +85,7 @@ class ScheduledAndTransactionalAnnotationIntegrationTests {
ctx.register(Config.class, JdkProxyTxConfig.class, RepoConfigB.class);
ctx.refresh();
Thread.sleep(200); // allow @Scheduled method to be called several times
Thread.sleep(100); // allow @Scheduled method to be called several times
MyRepositoryWithScheduledMethod repository = ctx.getBean(MyRepositoryWithScheduledMethod.class);
CallCountingTransactionManager txManager = ctx.getBean(CallCountingTransactionManager.class);
@@ -100,7 +100,7 @@ class ScheduledAndTransactionalAnnotationIntegrationTests {
ctx.register(AspectConfig.class, MyRepositoryWithScheduledMethodImpl.class);
ctx.refresh();
Thread.sleep(200); // allow @Scheduled method to be called several times
Thread.sleep(100); // allow @Scheduled method to be called several times
MyRepositoryWithScheduledMethod repository = ctx.getBean(MyRepositoryWithScheduledMethod.class);
assertThat(AopUtils.isCglibProxy(repository)).isTrue();
+2 -2
View File
@@ -7,8 +7,8 @@ pluginManagement {
}
plugins {
id "com.gradle.enterprise" version "3.12.1"
id "io.spring.ge.conventions" version "0.0.13"
id "com.gradle.enterprise" version "3.10.2"
id "io.spring.ge.conventions" version "0.0.10"
}
include "spring-aop"
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,14 +38,6 @@ import org.springframework.util.StringUtils;
* for an advice method from the pointcut expression, returning, and throwing clauses.
* If an unambiguous interpretation is not available, it returns {@code null}.
*
* <h3>Algorithm Summary</h3>
* <p>If an unambiguous binding can be deduced, then it is.
* If the advice requirements cannot possibly be satisfied, then {@code null}
* is returned. By setting the {@link #setRaiseExceptions(boolean) raiseExceptions}
* property to {@code true}, descriptive exceptions will be thrown instead of
* returning {@code null} in the case that the parameter names cannot be discovered.
*
* <h3>Algorithm Details</h3>
* <p>This class interprets arguments in the following way:
* <ol>
* <li>If the first parameter of the method is of type {@link JoinPoint}
@@ -73,15 +65,15 @@ import org.springframework.util.StringUtils;
* zero we proceed to the next stage. If {@code a} &gt; 1 then an
* {@code AmbiguousBindingException} is raised. If {@code a} == 1,
* and there are no unbound arguments of type {@code Annotation+},
* then an {@code IllegalArgumentException} is raised. If there is
* then an {@code IllegalArgumentException} is raised. if there is
* exactly one such argument, then the corresponding parameter name is
* assigned the value from the pointcut expression.</li>
* <li>If a {@code returningName} has been set, and there are no unbound arguments
* <li>If a returningName has been set, and there are no unbound arguments
* then an {@code IllegalArgumentException} is raised. If there is
* more than one unbound argument then an
* {@code AmbiguousBindingException} is raised. If there is exactly
* one unbound argument then the corresponding parameter name is assigned
* the value of the {@code returningName}.</li>
* the value &lt;returningName&gt;.</li>
* <li>If there remain unbound arguments, then the pointcut expression is
* examined once more for {@code this}, {@code target}, and
* {@code args} pointcut expressions used in the binding form (binding
@@ -107,12 +99,20 @@ import org.springframework.util.StringUtils;
* <p>The behavior on raising an {@code IllegalArgumentException} or
* {@code AmbiguousBindingException} is configurable to allow this discoverer
* to be used as part of a chain-of-responsibility. By default the condition will
* be logged and the {@link #getParameterNames(Method)} method will simply return
* be logged and the {@code getParameterNames(..)} method will simply return
* {@code null}. If the {@link #setRaiseExceptions(boolean) raiseExceptions}
* property is set to {@code true}, the conditions will be thrown as
* {@code IllegalArgumentException} and {@code AmbiguousBindingException},
* respectively.
*
* <p>Was that perfectly clear? ;)
*
* <p>Short version: If an unambiguous binding can be deduced, then it is.
* If the advice requirements cannot possibly be satisfied, then {@code null}
* is returned. By setting the {@link #setRaiseExceptions(boolean) raiseExceptions}
* property to {@code true}, descriptive exceptions will be thrown instead of
* returning {@code null} in the case that the parameter names cannot be discovered.
*
* @author Adrian Colyer
* @author Juergen Hoeller
* @since 2.0
@@ -158,7 +158,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
/** The pointcut expression associated with the advice, as a simple String. */
@Nullable
private final String pointcutExpression;
private String pointcutExpression;
private boolean raiseExceptions;
@@ -197,7 +197,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
/**
* If {@code afterReturning} advice binds the return value, the
* {@code returning} variable name must be specified.
* returning variable name must be specified.
* @param returningName the name of the returning variable
*/
public void setReturningName(@Nullable String returningName) {
@@ -206,17 +206,18 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
/**
* If {@code afterThrowing} advice binds the thrown value, the
* {@code throwing} variable name must be specified.
* throwing variable name must be specified.
* @param throwingName the name of the throwing variable
*/
public void setThrowingName(@Nullable String throwingName) {
this.throwingName = throwingName;
}
/**
* Deduce the parameter names for an advice method.
* <p>See the {@link AspectJAdviceParameterNameDiscoverer class-level javadoc}
* for this class for details on the algorithm used.
* <p>See the {@link AspectJAdviceParameterNameDiscoverer class level javadoc}
* for this class for details of the algorithm used.
* @param method the target {@link Method}
* @return the parameter names
*/
@@ -315,13 +316,13 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
}
private void bindParameterName(int index, @Nullable String name) {
private void bindParameterName(int index, String name) {
this.parameterNameBindings[index] = name;
this.numberOfRemainingUnboundArguments--;
}
/**
* If the first parameter is of type JoinPoint or ProceedingJoinPoint, bind "thisJoinPoint" as
* If the first parameter is of type JoinPoint or ProceedingJoinPoint,bind "thisJoinPoint" as
* parameter name and return true, else return false.
*/
private boolean maybeBindThisJoinPoint() {
@@ -366,8 +367,8 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
}
if (throwableIndex == -1) {
throw new IllegalStateException("Binding of throwing parameter '" + this.throwingName +
"' could not be completed as no available arguments are a subtype of Throwable");
throw new IllegalStateException("Binding of throwing parameter '" + this.throwingName
+ "' could not be completed as no available arguments are a subtype of Throwable");
}
else {
bindParameterName(throwableIndex, this.throwingName);
@@ -399,6 +400,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
}
}
/**
* Parse the string pointcut expression looking for:
* &#64;this, &#64;target, &#64;args, &#64;within, &#64;withincode, &#64;annotation.
@@ -463,7 +465,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
}
}
/**
/*
* If the token starts meets Java identifier conventions, it's in.
*/
@Nullable
@@ -531,6 +533,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
}
}
if (varNames.size() > 1) {
throw new AmbiguousBindingException("Found " + varNames.size() +
" candidate this(), target() or args() variables but only one unbound argument slot");
@@ -606,7 +609,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
// else varNames.size must be 0 and we have nothing to bind.
}
/**
/*
* We've found the start of a binding pointcut at the given index into the
* token array. Now we need to extract the pointcut body and return it.
*/
@@ -706,7 +709,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
return false;
}
/**
/*
* Return {@code true} if the given argument type is a subclass
* of the given supertype.
*/
@@ -734,7 +737,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
return count;
}
/**
/*
* Find the argument index with the given type, and bind the given
* {@code varName} in that position.
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -248,8 +248,8 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
/**
* If a pointcut expression has been specified in XML, the user cannot
* write "and" as "&&" (though {@code &amp;&amp;} will work).
* <p>We also allow "and" between two pointcut sub-expressions.
* write {@code and} as "&&" (though &amp;&amp; will work).
* We also allow {@code and} between two pointcut sub-expressions.
* <p>This method converts back to {@code &&} for the AspectJ pointcut parser.
*/
private String replaceBooleanOperators(String pcExpr) {
@@ -527,7 +527,7 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
return false;
}
AspectJExpressionPointcut otherPc = (AspectJExpressionPointcut) other;
return ObjectUtils.nullSafeEquals(getExpression(), otherPc.getExpression()) &&
return ObjectUtils.nullSafeEquals(this.getExpression(), otherPc.getExpression()) &&
ObjectUtils.nullSafeEquals(this.pointcutDeclarationScope, otherPc.pointcutDeclarationScope) &&
ObjectUtils.nullSafeEquals(this.pointcutParameterNames, otherPc.pointcutParameterNames) &&
ObjectUtils.nullSafeEquals(this.pointcutParameterTypes, otherPc.pointcutParameterTypes);
@@ -535,7 +535,7 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
@Override
public int hashCode() {
int hashCode = ObjectUtils.nullSafeHashCode(getExpression());
int hashCode = ObjectUtils.nullSafeHashCode(this.getExpression());
hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutDeclarationScope);
hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutParameterNames);
hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutParameterTypes);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,8 +35,7 @@ import org.springframework.lang.Nullable;
/**
* Internal implementation of AspectJPointcutAdvisor.
*
* <p>Note that there will be one instance of this advisor for each target method.
* Note that there will be one instance of this advisor for each target method.
*
* @author Rod Johnson
* @author Juergen Hoeller
@@ -294,7 +293,7 @@ final class InstantiationModelAwarePointcutAdvisorImpl
@Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
// This can match only on declared pointcut.
return (isAspectMaterialized() && this.declaredPointcut.matches(method, targetClass, args));
return (isAspectMaterialized() && this.declaredPointcut.matches(method, targetClass));
}
private boolean isAspectMaterialized() {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +23,10 @@ import org.springframework.lang.Nullable;
* Subinterface of {@link org.springframework.aop.aspectj.AspectInstanceFactory}
* that returns {@link AspectMetadata} associated with AspectJ-annotated classes.
*
* <p>Ideally, AspectInstanceFactory would include this method itself, but because
* AspectMetadata uses Java-5-only {@link org.aspectj.lang.reflect.AjType},
* we need to split out this subinterface.
*
* @author Rod Johnson
* @since 2.0
* @see AspectMetadata
@@ -31,13 +35,13 @@ import org.springframework.lang.Nullable;
public interface MetadataAwareAspectInstanceFactory extends AspectInstanceFactory {
/**
* Get the AspectJ AspectMetadata for this factory's aspect.
* Return the AspectJ AspectMetadata for this factory's aspect.
* @return the aspect metadata
*/
AspectMetadata getAspectMetadata();
/**
* Get the best possible creation mutex for this factory.
* Return the best possible creation mutex for this factory.
* @return the mutex object (may be {@code null} for no mutex to use)
* @since 4.3
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -521,7 +521,6 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
copy.copyFrom(this);
copy.targetSource = EmptyTargetSource.forClass(getTargetClass(), getTargetSource().isStatic());
copy.advisorChainFactory = this.advisorChainFactory;
copy.methodCache = this.methodCache;
copy.interfaces = new ArrayList<>(this.interfaces);
copy.advisors = new ArrayList<>(this.advisors);
return copy;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -283,8 +283,8 @@ class CglibAopProxy implements AopProxy, Serializable {
private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
// Parameters used for optimization choices...
boolean isFrozen = this.advised.isFrozen();
boolean exposeProxy = this.advised.isExposeProxy();
boolean isFrozen = this.advised.isFrozen();
boolean isStatic = this.advised.getTargetSource().isStatic();
// Choose an "aop" interceptor (used for AOP calls).
@@ -424,9 +424,10 @@ class CglibAopProxy implements AopProxy, Serializable {
/**
* Method interceptor used for static targets with no advice chain. The call is
* passed directly back to the target. Used when the proxy needs to be exposed
* and it can't be determined that the method won't return {@code this}.
* Method interceptor used for static targets with no advice chain. The call
* is passed directly back to the target. Used when the proxy needs to be
* exposed and it can't be determined that the method won't return
* {@code this}.
*/
private static class StaticUnadvisedInterceptor implements MethodInterceptor, Serializable {
@@ -898,9 +899,9 @@ class CglibAopProxy implements AopProxy, Serializable {
// Proxy is not yet available, but that shouldn't matter.
List<?> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
boolean haveAdvice = !chain.isEmpty();
boolean isFrozen = this.advised.isFrozen();
boolean exposeProxy = this.advised.isExposeProxy();
boolean isStatic = this.advised.getTargetSource().isStatic();
boolean isFrozen = this.advised.isFrozen();
if (haveAdvice || !isFrozen) {
// If exposing the proxy, then AOP_PROXY must be used.
if (exposeProxy) {
@@ -969,9 +970,6 @@ class CglibAopProxy implements AopProxy, Serializable {
if (this.advised.isExposeProxy() != otherAdvised.isExposeProxy()) {
return false;
}
if (this.advised.isOpaque() != otherAdvised.isOpaque()) {
return false;
}
if (this.advised.getTargetSource().isStatic() != otherAdvised.getTargetSource().isStatic()) {
return false;
}
@@ -1018,6 +1016,10 @@ class CglibAopProxy implements AopProxy, Serializable {
Advice advice = advisor.getAdvice();
hashCode = 13 * hashCode + advice.getClass().hashCode();
}
hashCode = 13 * hashCode + (this.advised.isFrozen() ? 1 : 0);
hashCode = 13 * hashCode + (this.advised.isExposeProxy() ? 1 : 0);
hashCode = 13 * hashCode + (this.advised.isOptimize() ? 1 : 0);
hashCode = 13 * hashCode + (this.advised.isOpaque() ? 1 : 0);
return hashCode;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -123,33 +123,7 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
if (logger.isTraceEnabled()) {
logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
}
return Proxy.newProxyInstance(determineClassLoader(classLoader), this.proxiedInterfaces, this);
}
/**
* Determine whether the JDK bootstrap or platform loader has been suggested ->
* use higher-level loader which can see Spring infrastructure classes instead.
*/
private ClassLoader determineClassLoader(@Nullable ClassLoader classLoader) {
if (classLoader == null) {
// JDK bootstrap loader -> use spring-aop ClassLoader instead.
return getClass().getClassLoader();
}
if (classLoader.getParent() == null) {
// Potentially the JDK platform loader on JDK 9+
ClassLoader aopClassLoader = getClass().getClassLoader();
ClassLoader aopParent = aopClassLoader.getParent();
while (aopParent != null) {
if (classLoader == aopParent) {
// Suggested ClassLoader is ancestor of spring-aop ClassLoader
// -> use spring-aop ClassLoader itself instead.
return aopClassLoader;
}
aopParent = aopParent.getParent();
}
}
// Regular case: use suggested ClassLoader as-is.
return classLoader;
return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
}
/**
@@ -147,7 +147,7 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
/**
* Set whether the proxy should be frozen, preventing advice
* from being added to it once it is created.
* <p>Overridden from the superclass to prevent the proxy configuration
* <p>Overridden from the super class to prevent the proxy configuration
* from being frozen before the proxy is created.
*/
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,7 +34,6 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Convenient superclass for
@@ -82,11 +81,6 @@ public abstract class AbstractBeanFactoryBasedTargetSourceCreator
return this.beanFactory;
}
private ConfigurableBeanFactory getConfigurableBeanFactory() {
Assert.state(this.beanFactory != null, "BeanFactory not set");
return this.beanFactory;
}
//---------------------------------------------------------------------
// Implementation of the TargetSourceCreator interface
@@ -110,7 +104,7 @@ public abstract class AbstractBeanFactoryBasedTargetSourceCreator
// We need to override just this bean definition, as it may reference other beans
// and we're happy to take the parent's definition for those.
// Always use prototype scope if demanded.
BeanDefinition bd = getConfigurableBeanFactory().getMergedBeanDefinition(beanName);
BeanDefinition bd = this.beanFactory.getMergedBeanDefinition(beanName);
GenericBeanDefinition bdCopy = new GenericBeanDefinition(bd);
if (isPrototypeBased()) {
bdCopy.setScope(BeanDefinition.SCOPE_PROTOTYPE);
@@ -132,7 +126,7 @@ public abstract class AbstractBeanFactoryBasedTargetSourceCreator
protected DefaultListableBeanFactory getInternalBeanFactoryForBean(String beanName) {
synchronized (this.internalBeanFactories) {
return this.internalBeanFactories.computeIfAbsent(beanName,
name -> buildInternalBeanFactory(getConfigurableBeanFactory()));
name -> buildInternalBeanFactory(this.beanFactory));
}
}
@@ -1,10 +0,0 @@
/**
* Various {@link org.springframework.aop.framework.autoproxy.TargetSourceCreator}
* implementations for use with Spring's AOP auto-proxying support.
*/
@NonNullApi
@NonNullFields
package org.springframework.aop.framework.autoproxy.target;
import org.springframework.lang.NonNullApi;
import org.springframework.lang.NonNullFields;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -186,10 +186,10 @@ public abstract class AopUtils {
* this method resolves bridge methods in order to retrieve attributes from
* the <i>original</i> method definition.
* @param method the method to be invoked, which may come from an interface
* @param targetClass the target class for the current invocation
* (can be {@code null} or may not even implement the method)
* @param targetClass the target class for the current invocation.
* May be {@code null} or may not even implement the method.
* @return the specific target method, or the original method if the
* {@code targetClass} does not implement it
* {@code targetClass} doesn't implement it or is {@code null}
* @see org.springframework.util.ClassUtils#getMostSpecificMethod
*/
public static Method getMostSpecificMethod(Method method, @Nullable Class<?> targetClass) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,8 +24,6 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.aop.TargetSource;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
@@ -59,18 +57,15 @@ public abstract class AbstractBeanFactoryBasedTargetSource implements TargetSour
protected final Log logger = LogFactory.getLog(getClass());
/** Name of the target bean we will create on each invocation. */
@Nullable
private String targetBeanName;
/** Class of the target. */
@Nullable
private volatile Class<?> targetClass;
/**
* BeanFactory that owns this TargetSource. We need to hold onto this
* reference so that we can create new prototype instances as necessary.
*/
@Nullable
private BeanFactory beanFactory;
@@ -91,7 +86,6 @@ public abstract class AbstractBeanFactoryBasedTargetSource implements TargetSour
* Return the name of the target bean in the factory.
*/
public String getTargetBeanName() {
Assert.state(this.targetBeanName != null, "Target bean name not set");
return this.targetBeanName;
}
@@ -121,13 +115,11 @@ public abstract class AbstractBeanFactoryBasedTargetSource implements TargetSour
* Return the owning BeanFactory.
*/
public BeanFactory getBeanFactory() {
Assert.state(this.beanFactory != null, "BeanFactory not set");
return this.beanFactory;
}
@Override
@Nullable
public Class<?> getTargetClass() {
Class<?> targetClass = this.targetClass;
if (targetClass != null) {
@@ -136,7 +128,7 @@ public abstract class AbstractBeanFactoryBasedTargetSource implements TargetSour
synchronized (this) {
// Full check within synchronization, entering the BeanFactory interaction algorithm only once...
targetClass = this.targetClass;
if (targetClass == null && this.beanFactory != null && this.targetBeanName != null) {
if (targetClass == null && this.beanFactory != null) {
// Determine type of the target bean.
targetClass = this.beanFactory.getType(this.targetBeanName);
if (targetClass == null) {
@@ -190,16 +182,18 @@ public abstract class AbstractBeanFactoryBasedTargetSource implements TargetSour
@Override
public int hashCode() {
return getClass().hashCode() * 13 + ObjectUtils.nullSafeHashCode(this.targetBeanName);
int hashCode = getClass().hashCode();
hashCode = 13 * hashCode + ObjectUtils.nullSafeHashCode(this.beanFactory);
hashCode = 13 * hashCode + ObjectUtils.nullSafeHashCode(this.targetBeanName);
return hashCode;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(getClass().getSimpleName());
sb.append(" for target bean '").append(this.targetBeanName).append('\'');
Class<?> targetClass = this.targetClass;
if (targetClass != null) {
sb.append(" of type [").append(targetClass.getName()).append(']');
if (this.targetClass != null) {
sb.append(" of type [").append(this.targetClass.getName()).append(']');
}
return sb.toString();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -46,7 +46,6 @@ public abstract class AbstractLazyCreationTargetSource implements TargetSource {
protected final Log logger = LogFactory.getLog(getClass());
/** The lazily initialized target object. */
@Nullable
private Object lazyTarget;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -70,7 +70,6 @@ public final class EmptyTargetSource implements TargetSource, Serializable {
// Instance implementation
//---------------------------------------------------------------------
@Nullable
private final Class<?> targetClass;
private final boolean isStatic;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -95,7 +95,8 @@ public class HotSwappableTargetSource implements TargetSource, Serializable {
/**
* Two HotSwappableTargetSources are equal if the current target objects are equal.
* Two HotSwappableTargetSources are equal if the current target
* objects are equal.
*/
@Override
public boolean equals(Object other) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -82,8 +82,14 @@ public class SingletonTargetSource implements TargetSource, Serializable {
*/
@Override
public boolean equals(Object other) {
return (this == other || (other instanceof SingletonTargetSource &&
this.target.equals(((SingletonTargetSource) other).target)));
if (this == other) {
return true;
}
if (!(other instanceof SingletonTargetSource)) {
return false;
}
SingletonTargetSource otherTargetSource = (SingletonTargetSource) other;
return this.target.equals(otherTargetSource.target);
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -58,12 +58,7 @@ public class ThreadLocalTargetSource extends AbstractPrototypeBasedTargetSource
* is meant to be per thread per instance of the ThreadLocalTargetSource class.
*/
private final ThreadLocal<Object> targetInThread =
new NamedThreadLocal<Object>("Thread-local instance of bean") {
@Override
public String toString() {
return super.toString() + " '" + getTargetBeanName() + "'";
}
};
new NamedThreadLocal<>("Thread-local instance of bean '" + getTargetBeanName() + "'");
/**
* Set of managed targets, enabling us to keep track of the targets we've created.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,67 +38,56 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
* @author Adrian Colyer
* @author Juergen Hoeller
* @author Chris Beams
* @author Sam Brannen
*/
class ArgumentBindingTests {
public class ArgumentBindingTests {
@Test
void annotationArgumentNameBinding() {
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new TransactionalBean());
proxyFactory.addAspect(PointcutWithAnnotationArgument.class);
ITransactionalBean proxiedTestBean = proxyFactory.getProxy();
assertThatIllegalStateException()
.isThrownBy(proxiedTestBean::doInTransaction)
.withMessage("Invoked with @Transactional");
}
@Test
void bindingInPointcutUsedByAdvice() {
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new TestBean());
public void testBindingInPointcutUsedByAdvice() {
TestBean tb = new TestBean();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(tb);
proxyFactory.addAspect(NamedPointcutWithArgs.class);
ITestBean proxiedTestBean = proxyFactory.getProxy();
assertThatIllegalArgumentException()
.isThrownBy(() -> proxiedTestBean.setName("enigma"))
.withMessage("enigma");
ITestBean proxiedTestBean = proxyFactory.getProxy();
assertThatIllegalArgumentException().isThrownBy(() ->
proxiedTestBean.setName("Supercalifragalisticexpialidocious"));
}
@Test
void bindingWithDynamicAdvice() {
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new TestBean());
proxyFactory.addAspect(DynamicPointcutWithArgs.class);
ITestBean proxiedTestBean = proxyFactory.getProxy();
public void testAnnotationArgumentNameBinding() {
TransactionalBean tb = new TransactionalBean();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(tb);
proxyFactory.addAspect(PointcutWithAnnotationArgument.class);
proxiedTestBean.applyName(1);
assertThatIllegalArgumentException()
.isThrownBy(() -> proxiedTestBean.applyName("enigma"))
.withMessage("enigma");
ITransactionalBean proxiedTestBean = proxyFactory.getProxy();
assertThatIllegalStateException().isThrownBy(
proxiedTestBean::doInTransaction);
}
@Test
void parameterNameDiscoverWithReferencePointcut() throws Exception {
public void testParameterNameDiscoverWithReferencePointcut() throws Exception {
AspectJAdviceParameterNameDiscoverer discoverer =
new AspectJAdviceParameterNameDiscoverer("somepc(formal) && set(* *)");
discoverer.setRaiseExceptions(true);
Method method = getClass().getDeclaredMethod("methodWithOneParam", String.class);
assertThat(discoverer.getParameterNames(method)).containsExactly("formal");
Method methodUsedForParameterTypeDiscovery =
getClass().getMethod("methodWithOneParam", String.class);
String[] pnames = discoverer.getParameterNames(methodUsedForParameterTypeDiscovery);
assertThat(pnames.length).as("one parameter name").isEqualTo(1);
assertThat(pnames[0]).isEqualTo("formal");
}
@SuppressWarnings("unused")
private void methodWithOneParam(String aParam) {
public void methodWithOneParam(String aParam) {
}
interface ITransactionalBean {
public interface ITransactionalBean {
@Transactional
void doInTransaction();
}
static class TransactionalBean implements ITransactionalBean {
public static class TransactionalBean implements ITransactionalBean {
@Override
@Transactional
@@ -106,46 +95,38 @@ class ArgumentBindingTests {
}
}
}
/**
* Mimics Spring's @Transactional annotation without actually introducing the dependency.
*/
@Retention(RetentionPolicy.RUNTIME)
@interface Transactional {
}
/**
* Represents Spring's Transactional annotation without actually introducing the dependency
*/
@Retention(RetentionPolicy.RUNTIME)
@interface Transactional {
}
@Aspect
static class PointcutWithAnnotationArgument {
@Aspect
class PointcutWithAnnotationArgument {
@Around("execution(* org.springframework..*.*(..)) && @annotation(transactional)")
public Object around(ProceedingJoinPoint pjp, Transactional transactional) throws Throwable {
throw new IllegalStateException("Invoked with @Transactional");
}
}
@Aspect
static class NamedPointcutWithArgs {
@Pointcut("execution(* *(..)) && args(s,..)")
public void pointcutWithArgs(String s) {}
@Around("pointcutWithArgs(aString)")
public Object doAround(ProceedingJoinPoint pjp, String aString) throws Throwable {
throw new IllegalArgumentException(aString);
}
}
@Aspect("pertarget(execution(* *(..)))")
static class DynamicPointcutWithArgs {
@Around("execution(* *(..)) && args(java.lang.String)")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
throw new IllegalArgumentException(String.valueOf(pjp.getArgs()[0]));
}
@Around(value = "execution(* org.springframework..*.*(..)) && @annotation(transaction)")
public Object around(ProceedingJoinPoint pjp, Transactional transaction) throws Throwable {
System.out.println("Invoked with transaction " + transaction);
throw new IllegalStateException();
}
}
@Aspect
class NamedPointcutWithArgs {
@Pointcut("execution(* *(..)) && args(s,..)")
public void pointcutWithArgs(String s) {}
@Around("pointcutWithArgs(aString)")
public Object doAround(ProceedingJoinPoint pjp, String aString) throws Throwable {
System.out.println("got '" + aString + "' at '" + pjp + "'");
throw new IllegalArgumentException(aString);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,10 +16,7 @@
package org.springframework.aop.framework;
import java.sql.SQLException;
import java.sql.Savepoint;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.accessibility.Accessible;
@@ -383,43 +380,6 @@ public class ProxyFactoryTests {
assertThat(proxy.getName()).isEqualTo("tb");
}
@Test
public void testCharSequenceProxy() {
CharSequence target = "test";
ProxyFactory pf = new ProxyFactory(target);
ClassLoader cl = target.getClass().getClassLoader();
CharSequence proxy = (CharSequence) pf.getProxy(cl);
assertThat(proxy.toString()).isEqualTo(target);
}
@Test
public void testDateProxy() {
Date target = new Date();
ProxyFactory pf = new ProxyFactory(target);
pf.setProxyTargetClass(true);
ClassLoader cl = target.getClass().getClassLoader();
Date proxy = (Date) pf.getProxy(cl);
assertThat(proxy.getTime()).isEqualTo(target.getTime());
}
@Test
public void testJdbcSavepointProxy() throws SQLException {
Savepoint target = new Savepoint() {
@Override
public int getSavepointId() throws SQLException {
return 1;
}
@Override
public String getSavepointName() throws SQLException {
return "sp";
}
};
ProxyFactory pf = new ProxyFactory(target);
ClassLoader cl = Savepoint.class.getClassLoader();
Savepoint proxy = (Savepoint) pf.getProxy(cl);
assertThat(proxy.getSavepointName()).isEqualTo("sp");
}
@Order(2)
public static class A implements Runnable {
@@ -431,7 +391,7 @@ public class ProxyFactoryTests {
@Order(1)
public static class B implements Runnable {
public static class B implements Runnable{
@Override
public void run() {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -77,7 +77,9 @@ public class ThrowsAdviceInterceptorTests {
given(mi.getMethod()).willReturn(Object.class.getMethod("hashCode"));
given(mi.getThis()).willReturn(new Object());
given(mi.proceed()).willThrow(ex);
assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(() -> ti.invoke(mi)).isSameAs(ex);
assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(() ->
ti.invoke(mi))
.isSameAs(ex);
assertThat(th.getCalls()).isEqualTo(1);
assertThat(th.getCalls("ioException")).isEqualTo(1);
}
@@ -90,7 +92,9 @@ public class ThrowsAdviceInterceptorTests {
ConnectException ex = new ConnectException("");
MethodInvocation mi = mock(MethodInvocation.class);
given(mi.proceed()).willThrow(ex);
assertThatExceptionOfType(ConnectException.class).isThrownBy(() -> ti.invoke(mi)).isSameAs(ex);
assertThatExceptionOfType(ConnectException.class).isThrownBy(() ->
ti.invoke(mi))
.isSameAs(ex);
assertThat(th.getCalls()).isEqualTo(1);
assertThat(th.getCalls("remoteException")).isEqualTo(1);
}
@@ -113,7 +117,9 @@ public class ThrowsAdviceInterceptorTests {
ConnectException ex = new ConnectException("");
MethodInvocation mi = mock(MethodInvocation.class);
given(mi.proceed()).willThrow(ex);
assertThatExceptionOfType(Throwable.class).isThrownBy(() -> ti.invoke(mi)).isSameAs(t);
assertThatExceptionOfType(Throwable.class).isThrownBy(() ->
ti.invoke(mi))
.isSameAs(t);
assertThat(th.getCalls()).isEqualTo(1);
assertThat(th.getCalls("remoteException")).isEqualTo(1);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,35 +17,29 @@
package org.springframework.aop.support;
import java.lang.reflect.Method;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
import org.springframework.aop.target.EmptyTargetSource;
import org.springframework.aop.testfixture.interceptor.NopInterceptor;
import org.springframework.beans.testfixture.beans.TestBean;
import org.springframework.core.ResolvableType;
import org.springframework.core.testfixture.io.SerializationTestUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Rod Johnson
* @author Chris Beams
* @author Sebastien Deleuze
* @author Juergen Hoeller
*/
class AopUtilsTests {
public class AopUtilsTests {
@Test
void testPointcutCanNeverApply() {
public void testPointcutCanNeverApply() {
class TestPointcut extends StaticMethodMatcherPointcut {
@Override
public boolean matches(Method method, @Nullable Class<?> clazzy) {
@@ -58,13 +52,13 @@ class AopUtilsTests {
}
@Test
void testPointcutAlwaysApplies() {
public void testPointcutAlwaysApplies() {
assertThat(AopUtils.canApply(new DefaultPointcutAdvisor(new NopInterceptor()), Object.class)).isTrue();
assertThat(AopUtils.canApply(new DefaultPointcutAdvisor(new NopInterceptor()), TestBean.class)).isTrue();
}
@Test
void testPointcutAppliesToOneMethodOnObject() {
public void testPointcutAppliesToOneMethodOnObject() {
class TestPointcut extends StaticMethodMatcherPointcut {
@Override
public boolean matches(Method method, @Nullable Class<?> clazz) {
@@ -84,7 +78,7 @@ class AopUtilsTests {
* that's subverted the singleton construction limitation.
*/
@Test
void testCanonicalFrameworkClassesStillCanonicalOnDeserialization() throws Exception {
public void testCanonicalFrameworkClassesStillCanonicalOnDeserialization() throws Exception {
assertThat(SerializationTestUtils.serializeAndDeserialize(MethodMatcher.TRUE)).isSameAs(MethodMatcher.TRUE);
assertThat(SerializationTestUtils.serializeAndDeserialize(ClassFilter.TRUE)).isSameAs(ClassFilter.TRUE);
assertThat(SerializationTestUtils.serializeAndDeserialize(Pointcut.TRUE)).isSameAs(Pointcut.TRUE);
@@ -94,45 +88,4 @@ class AopUtilsTests {
assertThat(SerializationTestUtils.serializeAndDeserialize(ExposeInvocationInterceptor.INSTANCE)).isSameAs(ExposeInvocationInterceptor.INSTANCE);
}
@Test
void testInvokeJoinpointUsingReflection() throws Throwable {
String name = "foo";
TestBean testBean = new TestBean(name);
Method method = ReflectionUtils.findMethod(TestBean.class, "getName");
Object result = AopUtils.invokeJoinpointUsingReflection(testBean, method, new Object[0]);
assertThat(result).isEqualTo(name);
}
@Test // gh-32365
void mostSpecificMethodBetweenJdkProxyAndTarget() throws Exception {
Class<?> proxyClass = new ProxyFactory(new WithInterface()).getProxy(getClass().getClassLoader()).getClass();
Method specificMethod = AopUtils.getMostSpecificMethod(proxyClass.getMethod("handle", List.class), WithInterface.class);
assertThat(ResolvableType.forMethodParameter(specificMethod, 0).getGeneric().toClass()).isEqualTo(String.class);
}
@Test // gh-32365
void mostSpecificMethodBetweenCglibProxyAndTarget() throws Exception {
Class<?> proxyClass = new ProxyFactory(new WithoutInterface()).getProxy(getClass().getClassLoader()).getClass();
Method specificMethod = AopUtils.getMostSpecificMethod(proxyClass.getMethod("handle", List.class), WithoutInterface.class);
assertThat(ResolvableType.forMethodParameter(specificMethod, 0).getGeneric().toClass()).isEqualTo(String.class);
}
interface ProxyInterface {
void handle(List<String> list);
}
static class WithInterface implements ProxyInterface {
public void handle(List<String> list) {
}
}
static class WithoutInterface {
public void handle(List<String> list) {
}
}
}
@@ -35,7 +35,7 @@ public abstract aspect AbstractDependencyInjectionAspect {
mostSpecificSubTypeConstruction() && !preConstructionConfiguration();
/**
* Select least specific supertype that is marked for DI
* Select least specific super type that is marked for DI
* (so that injection occurs only once with pre-construction injection).
*/
public abstract pointcut leastSpecificSuperTypeConstruction();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1004,20 +1004,18 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA
*/
protected abstract static class PropertyHandler {
@Nullable
private final Class<?> propertyType;
private final boolean readable;
private final boolean writable;
public PropertyHandler(@Nullable Class<?> propertyType, boolean readable, boolean writable) {
public PropertyHandler(Class<?> propertyType, boolean readable, boolean writable) {
this.propertyType = propertyType;
this.readable = readable;
this.writable = writable;
}
@Nullable
public Class<?> getPropertyType() {
return this.propertyType;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,7 +23,6 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.net.URI;
import java.net.URL;
import java.time.temporal.Temporal;
@@ -31,7 +30,6 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -64,7 +62,7 @@ import org.springframework.util.StringUtils;
* <p>Mainly for internal use within the framework, but to some degree also
* useful for application classes. Consider
* <a href="https://commons.apache.org/proper/commons-beanutils/">Apache Commons BeanUtils</a>,
* <a href="https://github.com/ExpediaGroup/bull">BULL - Bean Utils Light Library</a>,
* <a href="https://hotelsdotcom.github.io/bull/">BULL - Bean Utils Light Library</a>,
* or similar third-party frameworks for more comprehensive bean utilities.
*
* @author Rod Johnson
@@ -249,8 +247,7 @@ public abstract class BeanUtils {
// A single public constructor
return (Constructor<T>) ctors[0];
}
else if (ctors.length == 0) {
// No public constructors -> check non-public
else if (ctors.length == 0){
ctors = clazz.getDeclaredConstructors();
if (ctors.length == 1) {
// A single non-public constructor, e.g. from a non-public record type
@@ -789,28 +786,38 @@ public abstract class BeanUtils {
if (editable != null) {
if (!editable.isInstance(target)) {
throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
"] not assignable to editable class [" + editable.getName() + "]");
"] not assignable to Editable class [" + editable.getName() + "]");
}
actualEditable = editable;
}
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
Set<String> ignoredProps = (ignoreProperties != null ? new HashSet<>(Arrays.asList(ignoreProperties)) : null);
CachedIntrospectionResults sourceResults = (actualEditable != source.getClass() ?
CachedIntrospectionResults.forClass(source.getClass()) : null);
List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);
for (PropertyDescriptor targetPd : targetPds) {
Method writeMethod = targetPd.getWriteMethod();
if (writeMethod != null && (ignoredProps == null || !ignoredProps.contains(targetPd.getName()))) {
PropertyDescriptor sourcePd = (sourceResults != null ?
sourceResults.getPropertyDescriptor(targetPd.getName()) : targetPd);
if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
if (sourcePd != null) {
Method readMethod = sourcePd.getReadMethod();
if (readMethod != null) {
if (isAssignable(writeMethod, readMethod)) {
ResolvableType sourceResolvableType = ResolvableType.forMethodReturnType(readMethod);
ResolvableType targetResolvableType = ResolvableType.forMethodParameter(writeMethod, 0);
// Ignore generic types in assignable check if either ResolvableType has unresolvable generics.
boolean isAssignable =
(sourceResolvableType.hasUnresolvableGenerics() || targetResolvableType.hasUnresolvableGenerics() ?
ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType()) :
targetResolvableType.isAssignableFrom(sourceResolvableType));
if (isAssignable) {
try {
ReflectionUtils.makeAccessible(readMethod);
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
readMethod.setAccessible(true);
}
Object value = readMethod.invoke(source);
ReflectionUtils.makeAccessible(writeMethod);
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
writeMethod.setAccessible(true);
}
writeMethod.invoke(target, value);
}
catch (Throwable ex) {
@@ -824,24 +831,6 @@ public abstract class BeanUtils {
}
}
private static boolean isAssignable(Method writeMethod, Method readMethod) {
Type paramType = writeMethod.getGenericParameterTypes()[0];
if (paramType instanceof Class) {
return ClassUtils.isAssignable((Class<?>) paramType, readMethod.getReturnType());
}
else if (paramType.equals(readMethod.getGenericReturnType())) {
return true;
}
else {
ResolvableType sourceType = ResolvableType.forMethodReturnType(readMethod);
ResolvableType targetType = ResolvableType.forMethodParameter(writeMethod, 0);
// Ignore generic types in assignable check if either ResolvableType has unresolvable generics.
return (sourceType.hasUnresolvableGenerics() || targetType.hasUnresolvableGenerics() ?
ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType()) :
targetType.isAssignableFrom(sourceType));
}
}
/**
* Inner class to avoid a hard dependency on Kotlin at runtime.
@@ -902,6 +891,7 @@ public abstract class BeanUtils {
}
return kotlinConstructor.callBy(argParameters);
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -97,6 +97,8 @@ public final class CachedIntrospectionResults {
*/
public static final String IGNORE_BEANINFO_PROPERTY_NAME = "spring.beaninfo.ignore";
private static final PropertyDescriptor[] EMPTY_PROPERTY_DESCRIPTOR_ARRAY = {};
private static final boolean shouldIntrospectorIgnoreBeaninfoClasses =
SpringProperties.getFlag(IGNORE_BEANINFO_PROPERTY_NAME);
@@ -248,22 +250,9 @@ public final class CachedIntrospectionResults {
return beanInfo;
}
}
BeanInfo beanInfo = (shouldIntrospectorIgnoreBeaninfoClasses ?
return (shouldIntrospectorIgnoreBeaninfoClasses ?
Introspector.getBeanInfo(beanClass, Introspector.IGNORE_ALL_BEANINFO) :
Introspector.getBeanInfo(beanClass));
// Immediately remove class from Introspector cache to allow for proper garbage
// collection on class loader shutdown; we cache it in CachedIntrospectionResults
// in a GC-friendly manner. This is necessary (again) for the JDK ClassInfo cache.
Class<?> classToFlush = beanClass;
do {
Introspector.flushFromCaches(classToFlush);
classToFlush = classToFlush.getSuperclass();
}
while (classToFlush != null && classToFlush != Object.class);
return beanInfo;
}
@@ -308,7 +297,7 @@ public final class CachedIntrospectionResults {
// Only allow URL attribute introspection, not content resolution
continue;
}
if (pd.getWriteMethod() == null && isInvalidReadOnlyPropertyType(pd.getPropertyType(), beanClass)) {
if (pd.getWriteMethod() == null && isInvalidReadOnlyPropertyType(pd.getPropertyType())) {
// Ignore read-only properties such as ClassLoader - no need to bind to those
continue;
}
@@ -358,8 +347,7 @@ public final class CachedIntrospectionResults {
// GenericTypeAwarePropertyDescriptor leniently resolves a set* write method
// against a declared read method, so we prefer read method descriptors here.
pd = buildGenericTypeAwarePropertyDescriptor(beanClass, pd);
if (pd.getWriteMethod() == null &&
isInvalidReadOnlyPropertyType(pd.getPropertyType(), beanClass)) {
if (pd.getWriteMethod() == null && isInvalidReadOnlyPropertyType(pd.getPropertyType())) {
// Ignore read-only properties such as ClassLoader - no need to bind to those
continue;
}
@@ -392,7 +380,7 @@ public final class CachedIntrospectionResults {
if (Modifier.isStatic(method.getModifiers()) ||
method.getDeclaringClass() == Object.class || method.getDeclaringClass() == Class.class ||
method.getParameterCount() > 0 || method.getReturnType() == void.class ||
isInvalidReadOnlyPropertyType(method.getReturnType(), method.getDeclaringClass())) {
isInvalidReadOnlyPropertyType(method.getReturnType())) {
return false;
}
try {
@@ -405,11 +393,10 @@ public final class CachedIntrospectionResults {
}
}
private boolean isInvalidReadOnlyPropertyType(@Nullable Class<?> returnType, Class<?> beanClass) {
return (returnType != null && (ClassLoader.class.isAssignableFrom(returnType) ||
ProtectionDomain.class.isAssignableFrom(returnType) ||
(AutoCloseable.class.isAssignableFrom(returnType) &&
!AutoCloseable.class.isAssignableFrom(beanClass))));
private boolean isInvalidReadOnlyPropertyType(@Nullable Class<?> returnType) {
return (returnType != null && (AutoCloseable.class.isAssignableFrom(returnType) ||
ClassLoader.class.isAssignableFrom(returnType) ||
ProtectionDomain.class.isAssignableFrom(returnType)));
}
@@ -435,7 +422,7 @@ public final class CachedIntrospectionResults {
}
PropertyDescriptor[] getPropertyDescriptors() {
return this.propertyDescriptors.values().toArray(PropertyDescriptorUtils.EMPTY_PROPERTY_DESCRIPTOR_ARRAY);
return this.propertyDescriptors.values().toArray(EMPTY_PROPERTY_DESCRIPTOR_ARRAY);
}
private PropertyDescriptor buildGenericTypeAwarePropertyDescriptor(Class<?> beanClass, PropertyDescriptor pd) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -129,6 +129,7 @@ public class DirectFieldAccessor extends AbstractNestablePropertyAccessor {
ReflectionUtils.makeAccessible(this.field);
return this.field.get(getWrappedInstance());
}
catch (IllegalAccessException ex) {
throw new InvalidPropertyException(getWrappedClass(),
this.field.getName(), "Field is not accessible", ex);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,15 +17,9 @@
package org.springframework.beans;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
@@ -38,80 +32,6 @@ import org.springframework.util.ObjectUtils;
*/
abstract class PropertyDescriptorUtils {
public static final PropertyDescriptor[] EMPTY_PROPERTY_DESCRIPTOR_ARRAY = {};
/**
* Simple introspection algorithm for basic set/get/is accessor methods,
* building corresponding JavaBeans property descriptors for them.
* <p>This just supports the basic JavaBeans conventions, without indexed
* properties or any customizers, and without other BeanInfo metadata.
* For standard JavaBeans introspection, use the JavaBeans Introspector.
* @param beanClass the target class to introspect
* @return a collection of property descriptors
* @throws IntrospectionException from introspecting the given bean class
* @since 5.3.24
* @see SimpleBeanInfoFactory
* @see java.beans.Introspector#getBeanInfo(Class)
*/
public static Collection<? extends PropertyDescriptor> determineBasicProperties(Class<?> beanClass)
throws IntrospectionException {
Map<String, BasicPropertyDescriptor> pdMap = new TreeMap<>();
for (Method method : beanClass.getMethods()) {
String methodName = method.getName();
boolean setter;
int nameIndex;
if (methodName.startsWith("set") && method.getParameterCount() == 1) {
setter = true;
nameIndex = 3;
}
else if (methodName.startsWith("get") && method.getParameterCount() == 0 && method.getReturnType() != Void.TYPE) {
setter = false;
nameIndex = 3;
}
else if (methodName.startsWith("is") && method.getParameterCount() == 0 && method.getReturnType() == boolean.class) {
setter = false;
nameIndex = 2;
}
else {
continue;
}
String propertyName = Introspector.decapitalize(methodName.substring(nameIndex));
if (propertyName.isEmpty()) {
continue;
}
BasicPropertyDescriptor pd = pdMap.get(propertyName);
if (pd != null) {
if (setter) {
if (pd.getWriteMethod() == null ||
pd.getWriteMethod().getParameterTypes()[0].isAssignableFrom(method.getParameterTypes()[0])) {
pd.setWriteMethod(method);
}
else {
pd.addWriteMethod(method);
}
}
else {
if (pd.getReadMethod() == null ||
(pd.getReadMethod().getReturnType() == method.getReturnType() && method.getName().startsWith("is"))) {
pd.setReadMethod(method);
}
}
}
else {
pd = new BasicPropertyDescriptor(propertyName, (!setter ? method : null), (setter ? method : null));
pdMap.put(propertyName, pd);
}
}
return pdMap.values();
}
/**
* See {@link java.beans.FeatureDescriptor}.
*/
@@ -253,72 +173,4 @@ abstract class PropertyDescriptorUtils {
pd.isBound() == otherPd.isBound() && pd.isConstrained() == otherPd.isConstrained());
}
/**
* PropertyDescriptor for {@link #determineBasicProperties(Class)},
* not performing any early type determination for
* {@link #setReadMethod}/{@link #setWriteMethod}.
* @since 5.3.24
*/
private static class BasicPropertyDescriptor extends PropertyDescriptor {
@Nullable
private Method readMethod;
@Nullable
private Method writeMethod;
private final List<Method> alternativeWriteMethods = new ArrayList<>();
public BasicPropertyDescriptor(String propertyName, @Nullable Method readMethod, @Nullable Method writeMethod)
throws IntrospectionException {
super(propertyName, readMethod, writeMethod);
}
@Override
public void setReadMethod(@Nullable Method readMethod) {
this.readMethod = readMethod;
}
@Override
@Nullable
public Method getReadMethod() {
return this.readMethod;
}
@Override
public void setWriteMethod(@Nullable Method writeMethod) {
this.writeMethod = writeMethod;
}
public void addWriteMethod(Method writeMethod) {
if (this.writeMethod != null) {
this.alternativeWriteMethods.add(this.writeMethod);
this.writeMethod = null;
}
this.alternativeWriteMethods.add(writeMethod);
}
@Override
@Nullable
public Method getWriteMethod() {
if (this.writeMethod == null && !this.alternativeWriteMethods.isEmpty()) {
if (this.readMethod == null) {
return this.alternativeWriteMethods.get(0);
}
else {
for (Method method : this.alternativeWriteMethods) {
if (this.readMethod.getReturnType().isAssignableFrom(method.getParameterTypes()[0])) {
this.writeMethod = method;
break;
}
}
}
}
return this.writeMethod;
}
}
}
@@ -1,71 +0,0 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.beans;
import java.beans.BeanDescriptor;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.beans.SimpleBeanInfo;
import java.util.Collection;
import org.springframework.core.Ordered;
import org.springframework.lang.NonNull;
/**
* {@link BeanInfoFactory} implementation that bypasses the standard {@link java.beans.Introspector}
* for faster introspection, reduced to basic property determination (as commonly needed in Spring).
*
* <p>To be configured via a {@code META-INF/spring.factories} file with the following content,
* overriding other custom {@code org.springframework.beans.BeanInfoFactory} declarations:
* {@code org.springframework.beans.BeanInfoFactory=org.springframework.beans.SimpleBeanInfoFactory}
*
* <p>Ordered at {@code Ordered.LOWEST_PRECEDENCE - 1} to override {@link ExtendedBeanInfoFactory}
* (registered by default in 5.3) if necessary while still allowing other user-defined
* {@link BeanInfoFactory} types to take precedence.
*
* @author Juergen Hoeller
* @since 5.3.24
* @see ExtendedBeanInfoFactory
* @see CachedIntrospectionResults
*/
public class SimpleBeanInfoFactory implements BeanInfoFactory, Ordered {
@Override
@NonNull
public BeanInfo getBeanInfo(Class<?> beanClass) throws IntrospectionException {
Collection<? extends PropertyDescriptor> pds =
PropertyDescriptorUtils.determineBasicProperties(beanClass);
return new SimpleBeanInfo() {
@Override
public BeanDescriptor getBeanDescriptor() {
return new BeanDescriptor(beanClass);
}
@Override
public PropertyDescriptor[] getPropertyDescriptors() {
return pds.toArray(PropertyDescriptorUtils.EMPTY_PROPERTY_DESCRIPTOR_ARRAY);
}
};
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE - 1;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -136,10 +136,7 @@ public interface BeanFactory {
* <p>Translates aliases back to the corresponding canonical bean name.
* <p>Will ask the parent factory if the bean cannot be found in this factory instance.
* @param name the name of the bean to retrieve
* @return an instance of the bean.
* Note that the return value will never be {@code null} but possibly a stub for
* {@code null} returned from a factory method, to be checked via {@code equals(null)}.
* Consider using {@link #getBeanProvider(Class)} for resolving optional dependencies.
* @return an instance of the bean
* @throws NoSuchBeanDefinitionException if there is no bean with the specified name
* @throws BeansException if the bean could not be obtained
*/
@@ -155,11 +152,7 @@ public interface BeanFactory {
* <p>Will ask the parent factory if the bean cannot be found in this factory instance.
* @param name the name of the bean to retrieve
* @param requiredType type the bean must match; can be an interface or superclass
* @return an instance of the bean.
* Note that the return value will never be {@code null}. In case of a stub for
* {@code null} from a factory method having been resolved for the requested bean, a
* {@code BeanNotOfRequiredTypeException} against the NullBean stub will be raised.
* Consider using {@link #getBeanProvider(Class)} for resolving optional dependencies.
* @return an instance of the bean
* @throws NoSuchBeanDefinitionException if there is no such bean definition
* @throws BeanNotOfRequiredTypeException if the bean is not of the required type
* @throws BeansException if the bean could not be created
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,7 +17,6 @@
package org.springframework.beans.factory.annotation;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
@@ -63,10 +62,6 @@ import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.MergedAnnotation;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@@ -149,9 +144,6 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
@Nullable
private ConfigurableListableBeanFactory beanFactory;
@Nullable
private MetadataReaderFactory metadataReaderFactory;
private final Set<String> lookupMethodsChecked = Collections.newSetFromMap(new ConcurrentHashMap<>(256));
private final Map<Class<?>, Constructor<?>[]> candidateConstructorsCache = new ConcurrentHashMap<>(256);
@@ -246,7 +238,6 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
"AutowiredAnnotationBeanPostProcessor requires a ConfigurableListableBeanFactory: " + beanFactory);
}
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
this.metadataReaderFactory = new SimpleMetadataReaderFactory(this.beanFactory.getBeanClassLoader());
}
@@ -447,6 +438,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
}
}
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
// Fall back to class name as cache key, for backwards compatibility with custom callers.
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
@@ -472,11 +464,12 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
return InjectionMetadata.EMPTY;
}
final List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
Class<?> targetClass = clazz;
do {
final List<InjectionMetadata.InjectedElement> fieldElements = new ArrayList<>();
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
ReflectionUtils.doWithLocalFields(targetClass, field -> {
MergedAnnotation<?> ann = findAutowiredAnnotation(field);
if (ann != null) {
@@ -487,11 +480,10 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
return;
}
boolean required = determineRequiredStatus(ann);
fieldElements.add(new AutowiredFieldElement(field, required));
currElements.add(new AutowiredFieldElement(field, required));
}
});
final List<InjectionMetadata.InjectedElement> methodElements = new ArrayList<>();
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
@@ -513,12 +505,11 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
}
boolean required = determineRequiredStatus(ann);
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
methodElements.add(new AutowiredMethodElement(method, required, pd));
currElements.add(new AutowiredMethodElement(method, required, pd));
}
});
elements.addAll(0, sortMethodElements(methodElements, targetClass));
elements.addAll(0, fieldElements);
elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
@@ -572,9 +563,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
* @param type the type of the bean
* @return the target beans, or an empty Collection if no bean of this type is found
* @throws BeansException if bean retrieval failed
* @deprecated since 5.3.24 since it is unused in the meantime
*/
@Deprecated
protected <T> Map<String, T> findAutowireCandidates(Class<T> type) throws BeansException {
if (this.beanFactory == null) {
throw new IllegalStateException("No BeanFactory configured - " +
@@ -583,47 +572,6 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
return BeanFactoryUtils.beansOfTypeIncludingAncestors(this.beanFactory, type);
}
/**
* Sort the method elements via ASM for deterministic declaration order if possible.
*/
private List<InjectionMetadata.InjectedElement> sortMethodElements(
List<InjectionMetadata.InjectedElement> methodElements, Class<?> targetClass) {
if (this.metadataReaderFactory != null && methodElements.size() > 1) {
// Try reading the class file via ASM for deterministic declaration order...
// Unfortunately, the JVM's standard reflection returns methods in arbitrary
// order, even between different runs of the same application on the same JVM.
try {
AnnotationMetadata asm =
this.metadataReaderFactory.getMetadataReader(targetClass.getName()).getAnnotationMetadata();
Set<MethodMetadata> asmMethods = asm.getAnnotatedMethods(Autowired.class.getName());
if (asmMethods.size() >= methodElements.size()) {
List<InjectionMetadata.InjectedElement> candidateMethods = new ArrayList<>(methodElements);
List<InjectionMetadata.InjectedElement> selectedMethods = new ArrayList<>(asmMethods.size());
for (MethodMetadata asmMethod : asmMethods) {
for (Iterator<InjectionMetadata.InjectedElement> it = candidateMethods.iterator(); it.hasNext();) {
InjectionMetadata.InjectedElement element = it.next();
if (element.getMember().getName().equals(asmMethod.getMethodName())) {
selectedMethods.add(element);
it.remove();
break;
}
}
}
if (selectedMethods.size() == methodElements.size()) {
// All reflection-detected methods found in ASM method set -> proceed
return selectedMethods;
}
}
}
catch (IOException ex) {
logger.debug("Failed to read class file via ASM for determining @Autowired method order", ex);
// No worries, let's continue with the reflection metadata we started with...
}
}
return methodElements;
}
/**
* Register the specified bean as dependent on the autowired beans.
*/
@@ -645,7 +593,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
* Resolve the specified cached method argument or field value.
*/
@Nullable
private Object resolveCachedArgument(@Nullable String beanName, @Nullable Object cachedArgument) {
private Object resolvedCachedArgument(@Nullable String beanName, @Nullable Object cachedArgument) {
if (cachedArgument instanceof DependencyDescriptor) {
DependencyDescriptor descriptor = (DependencyDescriptor) cachedArgument;
Assert.state(this.beanFactory != null, "No BeanFactory available");
@@ -680,12 +628,10 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
Object value;
if (this.cached) {
try {
value = resolveCachedArgument(beanName, this.cachedFieldValue);
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
}
catch (BeansException ex) {
// Unexpected target bean mismatch for cached argument -> re-resolve
this.cached = false;
logger.debug("Failed to resolve cached argument", ex);
catch (NoSuchBeanDefinitionException ex) {
// Unexpected removal of target bean for cached argument -> re-resolve
value = resolveFieldValue(field, bean, beanName);
}
}
@@ -702,7 +648,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
desc.setContainingClass(bean.getClass());
Set<String> autowiredBeanNames = new LinkedHashSet<>(2);
Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
Assert.state(beanFactory != null, "No BeanFactory available");
TypeConverter typeConverter = beanFactory.getTypeConverter();
Object value;
@@ -714,23 +660,21 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
}
synchronized (this) {
if (!this.cached) {
Object cachedFieldValue = null;
if (value != null || this.required) {
Object cachedFieldValue = desc;
cachedFieldValue = desc;
registerDependentBeans(beanName, autowiredBeanNames);
if (value != null && autowiredBeanNames.size() == 1) {
if (autowiredBeanNames.size() == 1) {
String autowiredBeanName = autowiredBeanNames.iterator().next();
if (beanFactory.containsBean(autowiredBeanName) &&
beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
cachedFieldValue = new ShortcutDependencyDescriptor(desc, autowiredBeanName);
cachedFieldValue = new ShortcutDependencyDescriptor(
desc, autowiredBeanName, field.getType());
}
}
this.cachedFieldValue = cachedFieldValue;
this.cached = true;
}
else {
this.cachedFieldValue = null;
// cached flag remains false
}
this.cachedFieldValue = cachedFieldValue;
this.cached = true;
}
}
return value;
@@ -764,12 +708,10 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
Object[] arguments;
if (this.cached) {
try {
arguments = resolveCachedArguments(beanName, this.cachedMethodArguments);
arguments = resolveCachedArguments(beanName);
}
catch (BeansException ex) {
// Unexpected target bean mismatch for cached argument -> re-resolve
this.cached = false;
logger.debug("Failed to resolve cached argument", ex);
catch (NoSuchBeanDefinitionException ex) {
// Unexpected removal of target bean for cached argument -> re-resolve
arguments = resolveMethodArguments(method, bean, beanName);
}
}
@@ -788,13 +730,14 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
}
@Nullable
private Object[] resolveCachedArguments(@Nullable String beanName, @Nullable Object[] cachedMethodArguments) {
private Object[] resolveCachedArguments(@Nullable String beanName) {
Object[] cachedMethodArguments = this.cachedMethodArguments;
if (cachedMethodArguments == null) {
return null;
}
Object[] arguments = new Object[cachedMethodArguments.length];
for (int i = 0; i < arguments.length; i++) {
arguments[i] = resolveCachedArgument(beanName, cachedMethodArguments[i]);
arguments[i] = resolvedCachedArgument(beanName, cachedMethodArguments[i]);
}
return arguments;
}
@@ -804,7 +747,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
int argumentCount = method.getParameterCount();
Object[] arguments = new Object[argumentCount];
DependencyDescriptor[] descriptors = new DependencyDescriptor[argumentCount];
Set<String> autowiredBeanNames = new LinkedHashSet<>(argumentCount * 2);
Set<String> autowiredBeans = new LinkedHashSet<>(argumentCount);
Assert.state(beanFactory != null, "No BeanFactory available");
TypeConverter typeConverter = beanFactory.getTypeConverter();
for (int i = 0; i < arguments.length; i++) {
@@ -813,7 +756,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
currDesc.setContainingClass(bean.getClass());
descriptors[i] = currDesc;
try {
Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeanNames, typeConverter);
Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter);
if (arg == null && !this.required) {
arguments = null;
break;
@@ -827,27 +770,26 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
synchronized (this) {
if (!this.cached) {
if (arguments != null) {
DependencyDescriptor[] cachedMethodArguments = Arrays.copyOf(descriptors, argumentCount);
registerDependentBeans(beanName, autowiredBeanNames);
if (autowiredBeanNames.size() == argumentCount) {
Iterator<String> it = autowiredBeanNames.iterator();
DependencyDescriptor[] cachedMethodArguments = Arrays.copyOf(descriptors, arguments.length);
registerDependentBeans(beanName, autowiredBeans);
if (autowiredBeans.size() == argumentCount) {
Iterator<String> it = autowiredBeans.iterator();
Class<?>[] paramTypes = method.getParameterTypes();
for (int i = 0; i < paramTypes.length; i++) {
String autowiredBeanName = it.next();
if (arguments[i] != null && beanFactory.containsBean(autowiredBeanName) &&
if (beanFactory.containsBean(autowiredBeanName) &&
beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) {
cachedMethodArguments[i] = new ShortcutDependencyDescriptor(
descriptors[i], autowiredBeanName);
descriptors[i], autowiredBeanName, paramTypes[i]);
}
}
}
this.cachedMethodArguments = cachedMethodArguments;
this.cached = true;
}
else {
this.cachedMethodArguments = null;
// cached flag remains false
}
this.cached = true;
}
}
return arguments;
@@ -863,14 +805,17 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
private final String shortcut;
public ShortcutDependencyDescriptor(DependencyDescriptor original, String shortcut) {
private final Class<?> requiredType;
public ShortcutDependencyDescriptor(DependencyDescriptor original, String shortcut, Class<?> requiredType) {
super(original);
this.shortcut = shortcut;
this.requiredType = requiredType;
}
@Override
public Object resolveShortcut(BeanFactory beanFactory) {
return beanFactory.getBean(this.shortcut, getDependencyType());
return beanFactory.getBean(this.shortcut, this.requiredType);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,7 +25,6 @@ import java.util.Set;
import org.springframework.beans.SimpleTypeConverter;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.DependencyDescriptor;
@@ -241,11 +240,10 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa
}
}
if (targetAnnotation == null) {
BeanFactory beanFactory = getBeanFactory();
// Look for matching annotation on the target class
if (beanFactory != null) {
if (getBeanFactory() != null) {
try {
Class<?> beanType = beanFactory.getType(bdHolder.getBeanName());
Class<?> beanType = getBeanFactory().getType(bdHolder.getBeanName());
if (beanType != null) {
targetAnnotation = AnnotationUtils.getAnnotation(ClassUtils.getUserClass(beanType), type);
}
@@ -292,7 +290,7 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa
if (actualValue != null) {
actualValue = typeConverter.convertIfNecessary(actualValue, expectedValue.getClass());
}
if (!ObjectUtils.nullSafeEquals(expectedValue, actualValue)) {
if (!expectedValue.equals(actualValue)) {
return false;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -184,9 +184,8 @@ public abstract class YamlProcessor {
protected Yaml createYaml() {
LoaderOptions loaderOptions = new LoaderOptions();
loaderOptions.setAllowDuplicateKeys(false);
DumperOptions dumperOptions = new DumperOptions();
return new Yaml(new FilteringConstructor(loaderOptions), new Representer(dumperOptions),
dumperOptions, loaderOptions);
return new Yaml(new FilteringConstructor(loaderOptions), new Representer(),
new DumperOptions(), loaderOptions);
}
private boolean process(MatchCallback callback, Yaml yaml, Resource resource) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -97,10 +97,10 @@ import org.springframework.util.StringUtils;
* Supports autowiring constructors, properties by name, and properties by type.
*
* <p>The main template method to be implemented by subclasses is
* {@link #resolveDependency(DependencyDescriptor, String, Set, TypeConverter)}, used for
* autowiring. In case of a {@link org.springframework.beans.factory.ListableBeanFactory}
* which is capable of searching its bean definitions, matching beans will typically be
* implemented through such a search. Otherwise, simplified matching can be implemented.
* {@link #resolveDependency(DependencyDescriptor, String, Set, TypeConverter)},
* used for autowiring by type. In case of a factory which is capable of searching
* its bean definitions, matching beans will typically be implemented through such
* a search. For other factory styles, simplified matching algorithms can be implemented.
*
* <p>Note that this class does <i>not</i> assume or implement bean definition
* registry capabilities. See {@link DefaultListableBeanFactory} for an implementation
@@ -675,7 +675,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
// Apply SmartInstantiationAwareBeanPostProcessors to predict the
// eventual type after a before-instantiation shortcut.
if (targetType != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
boolean matchingOnlyFactoryBean = (typesToMatch.length == 1 && typesToMatch[0] == FactoryBean.class);
boolean matchingOnlyFactoryBean = typesToMatch.length == 1 && typesToMatch[0] == FactoryBean.class;
for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
Class<?> predicted = bp.predictBeanType(targetType, beanName);
if (predicted != null &&
@@ -1504,8 +1504,8 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
converter = bw;
}
Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
Set<String> autowiredBeanNames = new LinkedHashSet<>(propertyNames.length * 2);
for (String propertyName : propertyNames) {
try {
PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -161,14 +161,11 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
/** Cache of pre-filtered post-processors. */
@Nullable
private BeanPostProcessorCache beanPostProcessorCache;
private volatile BeanPostProcessorCache beanPostProcessorCache;
/** Map from scope identifier String to corresponding Scope. */
private final Map<String, Scope> scopes = new LinkedHashMap<>(8);
/** Application startup metrics. **/
private ApplicationStartup applicationStartup = ApplicationStartup.DEFAULT;
/** Security context used when running with a SecurityManager. */
@Nullable
private SecurityContextProvider securityContextProvider;
@@ -183,6 +180,8 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
private final ThreadLocal<Object> prototypesCurrentlyInCreation =
new NamedThreadLocal<>("Prototype beans currently in creation");
/** Application startup metrics. **/
private ApplicationStartup applicationStartup = ApplicationStartup.DEFAULT;
/**
* Create a new AbstractBeanFactory.
@@ -750,7 +749,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
aliases.add(fullBeanName);
}
String[] retrievedAliases = super.getAliases(beanName);
String prefix = (factoryPrefix ? FACTORY_BEAN_PREFIX : "");
String prefix = factoryPrefix ? FACTORY_BEAN_PREFIX : "";
for (String retrievedAlias : retrievedAliases) {
String alias = prefix + retrievedAlias;
if (!alias.equals(name)) {
@@ -945,12 +944,10 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
@Override
public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
Assert.notNull(beanPostProcessor, "BeanPostProcessor must not be null");
synchronized (this.beanPostProcessors) {
// Remove from old position, if any
this.beanPostProcessors.remove(beanPostProcessor);
// Add to end of list
this.beanPostProcessors.add(beanPostProcessor);
}
// Remove from old position, if any
this.beanPostProcessors.remove(beanPostProcessor);
// Add to end of list
this.beanPostProcessors.add(beanPostProcessor);
}
/**
@@ -960,12 +957,8 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
* @see #addBeanPostProcessor
*/
public void addBeanPostProcessors(Collection<? extends BeanPostProcessor> beanPostProcessors) {
synchronized (this.beanPostProcessors) {
// Remove from old position, if any
this.beanPostProcessors.removeAll(beanPostProcessors);
// Add to end of list
this.beanPostProcessors.addAll(beanPostProcessors);
}
this.beanPostProcessors.removeAll(beanPostProcessors);
this.beanPostProcessors.addAll(beanPostProcessors);
}
@Override
@@ -987,34 +980,26 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
* @since 5.3
*/
BeanPostProcessorCache getBeanPostProcessorCache() {
synchronized (this.beanPostProcessors) {
BeanPostProcessorCache bppCache = this.beanPostProcessorCache;
if (bppCache == null) {
bppCache = new BeanPostProcessorCache();
for (BeanPostProcessor bpp : this.beanPostProcessors) {
if (bpp instanceof InstantiationAwareBeanPostProcessor) {
bppCache.instantiationAware.add((InstantiationAwareBeanPostProcessor) bpp);
if (bpp instanceof SmartInstantiationAwareBeanPostProcessor) {
bppCache.smartInstantiationAware.add((SmartInstantiationAwareBeanPostProcessor) bpp);
}
}
if (bpp instanceof DestructionAwareBeanPostProcessor) {
bppCache.destructionAware.add((DestructionAwareBeanPostProcessor) bpp);
}
if (bpp instanceof MergedBeanDefinitionPostProcessor) {
bppCache.mergedDefinition.add((MergedBeanDefinitionPostProcessor) bpp);
BeanPostProcessorCache bpCache = this.beanPostProcessorCache;
if (bpCache == null) {
bpCache = new BeanPostProcessorCache();
for (BeanPostProcessor bp : this.beanPostProcessors) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
bpCache.instantiationAware.add((InstantiationAwareBeanPostProcessor) bp);
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
bpCache.smartInstantiationAware.add((SmartInstantiationAwareBeanPostProcessor) bp);
}
}
this.beanPostProcessorCache = bppCache;
if (bp instanceof DestructionAwareBeanPostProcessor) {
bpCache.destructionAware.add((DestructionAwareBeanPostProcessor) bp);
}
if (bp instanceof MergedBeanDefinitionPostProcessor) {
bpCache.mergedDefinition.add((MergedBeanDefinitionPostProcessor) bp);
}
}
return bppCache;
}
}
private void resetBeanPostProcessorCache() {
synchronized (this.beanPostProcessors) {
this.beanPostProcessorCache = null;
this.beanPostProcessorCache = bpCache;
}
return bpCache;
}
/**
@@ -2029,27 +2014,27 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
@Override
public BeanPostProcessor set(int index, BeanPostProcessor element) {
BeanPostProcessor result = super.set(index, element);
resetBeanPostProcessorCache();
beanPostProcessorCache = null;
return result;
}
@Override
public boolean add(BeanPostProcessor o) {
boolean success = super.add(o);
resetBeanPostProcessorCache();
beanPostProcessorCache = null;
return success;
}
@Override
public void add(int index, BeanPostProcessor element) {
super.add(index, element);
resetBeanPostProcessorCache();
beanPostProcessorCache = null;
}
@Override
public BeanPostProcessor remove(int index) {
BeanPostProcessor result = super.remove(index);
resetBeanPostProcessorCache();
beanPostProcessorCache = null;
return result;
}
@@ -2057,7 +2042,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
public boolean remove(Object o) {
boolean success = super.remove(o);
if (success) {
resetBeanPostProcessorCache();
beanPostProcessorCache = null;
}
return success;
}
@@ -2066,7 +2051,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
public boolean removeAll(Collection<?> c) {
boolean success = super.removeAll(c);
if (success) {
resetBeanPostProcessorCache();
beanPostProcessorCache = null;
}
return success;
}
@@ -2075,7 +2060,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
public boolean retainAll(Collection<?> c) {
boolean success = super.retainAll(c);
if (success) {
resetBeanPostProcessorCache();
beanPostProcessorCache = null;
}
return success;
}
@@ -2084,7 +2069,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
public boolean addAll(Collection<? extends BeanPostProcessor> c) {
boolean success = super.addAll(c);
if (success) {
resetBeanPostProcessorCache();
beanPostProcessorCache = null;
}
return success;
}
@@ -2093,7 +2078,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
public boolean addAll(int index, Collection<? extends BeanPostProcessor> c) {
boolean success = super.addAll(index, c);
if (success) {
resetBeanPostProcessorCache();
beanPostProcessorCache = null;
}
return success;
}
@@ -2102,7 +2087,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
public boolean removeIf(Predicate<? super BeanPostProcessor> filter) {
boolean success = super.removeIf(filter);
if (success) {
resetBeanPostProcessorCache();
beanPostProcessorCache = null;
}
return success;
}
@@ -2110,7 +2095,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
@Override
public void replaceAll(UnaryOperator<BeanPostProcessor> operator) {
super.replaceAll(operator);
resetBeanPostProcessorCache();
beanPostProcessorCache = null;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -134,7 +134,7 @@ class BeanDefinitionValueResolver {
return resolveInnerBean(argName, innerBeanName, bd);
}
else if (value instanceof DependencyDescriptor) {
Set<String> autowiredBeanNames = new LinkedHashSet<>(2);
Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
Object result = this.beanFactory.resolveDependency(
(DependencyDescriptor) value, this.beanName, autowiredBeanNames, this.typeConverter);
for (String autowiredBeanName : autowiredBeanNames) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -45,7 +45,6 @@ import org.springframework.beans.TypeConverter;
import org.springframework.beans.TypeMismatchException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.InjectionPoint;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
@@ -86,6 +85,12 @@ class ConstructorResolver {
private static final Object[] EMPTY_ARGS = new Object[0];
/**
* Marker for autowired arguments in a cached argument array, to be replaced
* by a {@linkplain #resolveAutowiredArgument resolved autowired argument}.
*/
private static final Object autowiredArgumentMarker = new Object();
private static final NamedThreadLocal<InjectionPoint> currentInjectionPoint =
new NamedThreadLocal<>("Current injection point");
@@ -107,7 +112,11 @@ class ConstructorResolver {
/**
* "autowire constructor" (with constructor arguments by type) behavior.
* Also applied if explicit constructor argument values are specified.
* Also applied if explicit constructor argument values are specified,
* matching all remaining arguments with beans from the bean factory.
* <p>This corresponds to constructor injection: In this mode, a Spring
* bean factory is able to host components that expect constructor-based
* dependency resolution.
* @param beanName the name of the bean
* @param mbd the merged bean definition for the bean
* @param chosenCtors chosen candidate constructors (or {@code null} if none)
@@ -600,10 +609,13 @@ class ConstructorResolver {
String argDesc = StringUtils.collectionToCommaDelimitedString(argTypes);
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"No matching factory method found on class [" + factoryClass.getName() + "]: " +
(mbd.getFactoryBeanName() != null ? "factory bean '" + mbd.getFactoryBeanName() + "'; " : "") +
(mbd.getFactoryBeanName() != null ?
"factory bean '" + mbd.getFactoryBeanName() + "'; " : "") +
"factory method '" + mbd.getFactoryMethodName() + "(" + argDesc + ")'. " +
"Check that a method with the specified name " + (minNrOfArgs > 0 ? "and arguments " : "") +
"exists and that it is " + (isStatic ? "static" : "non-static") + ".");
"Check that a method with the specified name " +
(minNrOfArgs > 0 ? "and arguments " : "") +
"exists and that it is " +
(isStatic ? "static" : "non-static") + ".");
}
else if (void.class == factoryMethodToUse.getReturnType()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
@@ -717,7 +729,7 @@ class ConstructorResolver {
ArgumentsHolder args = new ArgumentsHolder(paramTypes.length);
Set<ConstructorArgumentValues.ValueHolder> usedValueHolders = new HashSet<>(paramTypes.length);
Set<String> allAutowiredBeanNames = new LinkedHashSet<>(paramTypes.length * 2);
Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
for (int paramIndex = 0; paramIndex < paramTypes.length; paramIndex++) {
Class<?> paramType = paramTypes[paramIndex];
@@ -752,8 +764,8 @@ class ConstructorResolver {
throw new UnsatisfiedDependencyException(
mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam),
"Could not convert argument value of type [" +
ObjectUtils.nullSafeClassName(valueHolder.getValue()) +
"] to required type [" + paramType.getName() + "]: " + ex.getMessage());
ObjectUtils.nullSafeClassName(valueHolder.getValue()) +
"] to required type [" + paramType.getName() + "]: " + ex.getMessage());
}
Object sourceHolder = valueHolder.getSource();
if (sourceHolder instanceof ConstructorArgumentValues.ValueHolder) {
@@ -776,17 +788,11 @@ class ConstructorResolver {
"] - did you specify the correct bean references as arguments?");
}
try {
ConstructorDependencyDescriptor desc = new ConstructorDependencyDescriptor(methodParam, true);
Set<String> autowiredBeanNames = new LinkedHashSet<>(2);
Object arg = resolveAutowiredArgument(
desc, paramType, beanName, autowiredBeanNames, converter, fallback);
if (arg != null) {
setShortcutIfPossible(desc, paramType, autowiredBeanNames);
}
allAutowiredBeanNames.addAll(autowiredBeanNames);
args.rawArguments[paramIndex] = arg;
args.arguments[paramIndex] = arg;
args.preparedArguments[paramIndex] = desc;
Object autowiredArgument = resolveAutowiredArgument(
methodParam, beanName, autowiredBeanNames, converter, fallback);
args.rawArguments[paramIndex] = autowiredArgument;
args.arguments[paramIndex] = autowiredArgument;
args.preparedArguments[paramIndex] = autowiredArgumentMarker;
args.resolveNecessary = true;
}
catch (BeansException ex) {
@@ -796,7 +802,14 @@ class ConstructorResolver {
}
}
registerDependentBeans(executable, beanName, allAutowiredBeanNames);
for (String autowiredBeanName : autowiredBeanNames) {
this.beanFactory.registerDependentBean(autowiredBeanName, beanName);
if (logger.isDebugEnabled()) {
logger.debug("Autowiring by type from bean name '" + beanName +
"' via " + (executable instanceof Constructor ? "constructor" : "factory method") +
" to bean named '" + autowiredBeanName + "'");
}
}
return args;
}
@@ -816,61 +829,31 @@ class ConstructorResolver {
Object[] resolvedArgs = new Object[argsToResolve.length];
for (int argIndex = 0; argIndex < argsToResolve.length; argIndex++) {
Object argValue = argsToResolve[argIndex];
Class<?> paramType = paramTypes[argIndex];
boolean convertNecessary = false;
if (argValue instanceof ConstructorDependencyDescriptor) {
ConstructorDependencyDescriptor descriptor = (ConstructorDependencyDescriptor) argValue;
try {
argValue = resolveAutowiredArgument(descriptor, paramType, beanName,
null, converter, true);
}
catch (BeansException ex) {
// Unexpected target bean mismatch for cached argument -> re-resolve
Set<String> autowiredBeanNames = null;
if (descriptor.hasShortcut()) {
// Reset shortcut and try to re-resolve it in this thread...
descriptor.setShortcut(null);
autowiredBeanNames = new LinkedHashSet<>(2);
}
logger.debug("Failed to resolve cached argument", ex);
argValue = resolveAutowiredArgument(descriptor, paramType, beanName,
autowiredBeanNames, converter, true);
if (autowiredBeanNames != null && !descriptor.hasShortcut()) {
// We encountered as stale shortcut before, and the shortcut has
// not been re-resolved by another thread in the meantime...
if (argValue != null) {
setShortcutIfPossible(descriptor, paramType, autowiredBeanNames);
}
registerDependentBeans(executable, beanName, autowiredBeanNames);
}
}
MethodParameter methodParam = MethodParameter.forExecutable(executable, argIndex);
if (argValue == autowiredArgumentMarker) {
argValue = resolveAutowiredArgument(methodParam, beanName, null, converter, true);
}
else if (argValue instanceof BeanMetadataElement) {
argValue = valueResolver.resolveValueIfNecessary("constructor argument", argValue);
convertNecessary = true;
}
else if (argValue instanceof String) {
argValue = this.beanFactory.evaluateBeanDefinitionString((String) argValue, mbd);
convertNecessary = true;
}
if (convertNecessary) {
MethodParameter methodParam = MethodParameter.forExecutable(executable, argIndex);
try {
argValue = converter.convertIfNecessary(argValue, paramType, methodParam);
}
catch (TypeMismatchException ex) {
throw new UnsatisfiedDependencyException(
mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam),
"Could not convert argument value of type [" + ObjectUtils.nullSafeClassName(argValue) +
"] to required type [" + paramType.getName() + "]: " + ex.getMessage());
}
Class<?> paramType = paramTypes[argIndex];
try {
resolvedArgs[argIndex] = converter.convertIfNecessary(argValue, paramType, methodParam);
}
catch (TypeMismatchException ex) {
throw new UnsatisfiedDependencyException(
mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam),
"Could not convert argument value of type [" + ObjectUtils.nullSafeClassName(argValue) +
"] to required type [" + paramType.getName() + "]: " + ex.getMessage());
}
resolvedArgs[argIndex] = argValue;
}
return resolvedArgs;
}
private Constructor<?> getUserDeclaredConstructor(Constructor<?> constructor) {
protected Constructor<?> getUserDeclaredConstructor(Constructor<?> constructor) {
Class<?> declaringClass = constructor.getDeclaringClass();
Class<?> userClass = ClassUtils.getUserClass(declaringClass);
if (userClass != declaringClass) {
@@ -886,22 +869,23 @@ class ConstructorResolver {
}
/**
* Resolve the specified argument which is supposed to be autowired.
* Template method for resolving the specified argument which is supposed to be autowired.
*/
@Nullable
Object resolveAutowiredArgument(DependencyDescriptor descriptor, Class<?> paramType, String beanName,
protected Object resolveAutowiredArgument(MethodParameter param, String beanName,
@Nullable Set<String> autowiredBeanNames, TypeConverter typeConverter, boolean fallback) {
Class<?> paramType = param.getParameterType();
if (InjectionPoint.class.isAssignableFrom(paramType)) {
InjectionPoint injectionPoint = currentInjectionPoint.get();
if (injectionPoint == null) {
throw new IllegalStateException("No current InjectionPoint available for " + descriptor);
throw new IllegalStateException("No current InjectionPoint available for " + param);
}
return injectionPoint;
}
try {
return this.beanFactory.resolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter);
return this.beanFactory.resolveDependency(
new DependencyDescriptor(param, true), beanName, autowiredBeanNames, typeConverter);
}
catch (NoUniqueBeanDefinitionException ex) {
throw ex;
@@ -924,31 +908,6 @@ class ConstructorResolver {
}
}
private void setShortcutIfPossible(
ConstructorDependencyDescriptor descriptor, Class<?> paramType, Set<String> autowiredBeanNames) {
if (autowiredBeanNames.size() == 1) {
String autowiredBeanName = autowiredBeanNames.iterator().next();
if (this.beanFactory.containsBean(autowiredBeanName) &&
this.beanFactory.isTypeMatch(autowiredBeanName, paramType)) {
descriptor.setShortcut(autowiredBeanName);
}
}
}
private void registerDependentBeans(
Executable executable, String beanName, Set<String> autowiredBeanNames) {
for (String autowiredBeanName : autowiredBeanNames) {
this.beanFactory.registerDependentBean(autowiredBeanName, beanName);
if (logger.isDebugEnabled()) {
logger.debug("Autowiring by type from bean name '" + beanName + "' via " +
(executable instanceof Constructor ? "constructor" : "factory method") +
" to bean named '" + autowiredBeanName + "'");
}
}
}
static InjectionPoint setCurrentInjectionPoint(@Nullable InjectionPoint injectionPoint) {
InjectionPoint old = currentInjectionPoint.get();
if (injectionPoint != null) {
@@ -1047,35 +1006,4 @@ class ConstructorResolver {
}
}
/**
* DependencyDescriptor marker for constructor arguments,
* for differentiating between a provided DependencyDescriptor instance
* and an internally built DependencyDescriptor for autowiring purposes.
*/
@SuppressWarnings("serial")
private static class ConstructorDependencyDescriptor extends DependencyDescriptor {
@Nullable
private volatile String shortcut;
public ConstructorDependencyDescriptor(MethodParameter methodParameter, boolean required) {
super(methodParameter, required);
}
public void setShortcut(@Nullable String shortcut) {
this.shortcut = shortcut;
}
public boolean hasShortcut() {
return (this.shortcut != null);
}
@Override
public Object resolveShortcut(BeanFactory beanFactory) {
String shortcut = this.shortcut;
return (shortcut != null ? beanFactory.getBean(shortcut, getDependencyType()) : null);
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -961,7 +961,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
StartupStep smartInitialize = getApplicationStartup().start("spring.beans.smart-initialize")
StartupStep smartInitialize = this.getApplicationStartup().start("spring.beans.smart-initialize")
.tag("beanName", beanName);
SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if (System.getSecurityManager() != null) {
@@ -2167,25 +2167,20 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
@Nullable
public Object getOrderSource(Object obj) {
String beanName = this.instancesToBeanNames.get(obj);
if (beanName == null) {
if (beanName == null || !containsBeanDefinition(beanName)) {
return null;
}
try {
RootBeanDefinition beanDefinition = (RootBeanDefinition) getMergedBeanDefinition(beanName);
List<Object> sources = new ArrayList<>(2);
Method factoryMethod = beanDefinition.getResolvedFactoryMethod();
if (factoryMethod != null) {
sources.add(factoryMethod);
}
Class<?> targetType = beanDefinition.getTargetType();
if (targetType != null && targetType != obj.getClass()) {
sources.add(targetType);
}
return sources.toArray();
RootBeanDefinition beanDefinition = getMergedLocalBeanDefinition(beanName);
List<Object> sources = new ArrayList<>(2);
Method factoryMethod = beanDefinition.getResolvedFactoryMethod();
if (factoryMethod != null) {
sources.add(factoryMethod);
}
catch (NoSuchBeanDefinitionException ex) {
return null;
Class<?> targetType = beanDefinition.getTargetType();
if (targetType != null && targetType != obj.getClass()) {
sources.add(targetType);
}
return sources.toArray();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -102,7 +102,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
private boolean singletonsCurrentlyInDestruction = false;
/** Disposable bean instances: bean name to disposable instance. */
private final Map<String, DisposableBean> disposableBeans = new LinkedHashMap<>();
private final Map<String, Object> disposableBeans = new LinkedHashMap<>();
/** Map between containing bean names: bean name to Set of bean names that the bean contains. */
private final Map<String, Set<String>> containedBeanMap = new ConcurrentHashMap<>(16);
@@ -340,7 +340,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
* (within the entire factory).
* @param beanName the name of the bean
*/
public boolean isSingletonCurrentlyInCreation(@Nullable String beanName) {
public boolean isSingletonCurrentlyInCreation(String beanName) {
return this.singletonsCurrentlyInCreation.contains(beanName);
}
@@ -447,17 +447,17 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
}
String canonicalName = canonicalName(beanName);
Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
if (dependentBeans == null || dependentBeans.isEmpty()) {
if (dependentBeans == null) {
return false;
}
if (dependentBeans.contains(dependentBeanName)) {
return true;
}
if (alreadySeen == null) {
alreadySeen = new HashSet<>();
}
alreadySeen.add(beanName);
for (String transitiveDependency : dependentBeans) {
if (alreadySeen == null) {
alreadySeen = new HashSet<>();
}
alreadySeen.add(beanName);
if (isDependent(transitiveDependency, dependentBeanName, alreadySeen)) {
return true;
}
@@ -554,7 +554,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
// Destroy the corresponding DisposableBean instance.
DisposableBean disposableBean;
synchronized (this.disposableBeans) {
disposableBean = this.disposableBeans.remove(beanName);
disposableBean = (DisposableBean) this.disposableBeans.remove(beanName);
}
destroyBean(beanName, disposableBean);
}
@@ -567,16 +567,16 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
*/
protected void destroyBean(String beanName, @Nullable DisposableBean bean) {
// Trigger destruction of dependent beans first...
Set<String> dependentBeanNames;
Set<String> dependencies;
synchronized (this.dependentBeanMap) {
// Within full synchronization in order to guarantee a disconnected Set
dependentBeanNames = this.dependentBeanMap.remove(beanName);
dependencies = this.dependentBeanMap.remove(beanName);
}
if (dependentBeanNames != null) {
if (dependencies != null) {
if (logger.isTraceEnabled()) {
logger.trace("Retrieved dependent beans for bean '" + beanName + "': " + dependentBeanNames);
logger.trace("Retrieved dependent beans for bean '" + beanName + "': " + dependencies);
}
for (String dependentBeanName : dependentBeanNames) {
for (String dependentBeanName : dependencies) {
destroySingleton(dependentBeanName);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -68,9 +68,9 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
private static final String SHUTDOWN_METHOD_NAME = "shutdown";
private static final Log logger = LogFactory.getLog(DisposableBeanAdapter.class);
private final Object bean;
private final String beanName;
@@ -214,15 +214,12 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
}
}
catch (Throwable ex) {
if (logger.isWarnEnabled()) {
String msg = "Invocation of destroy method failed on bean with name '" + this.beanName + "'";
if (logger.isDebugEnabled()) {
// Log at warn level like below but add the exception stacktrace only with debug level
logger.warn(msg, ex);
}
else {
logger.warn(msg + ": " + ex);
}
String msg = "Invocation of destroy method failed on bean with name '" + this.beanName + "'";
if (logger.isDebugEnabled()) {
logger.warn(msg, ex);
}
else {
logger.warn(msg + ": " + ex);
}
}
}
@@ -243,15 +240,12 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
}
}
catch (Throwable ex) {
if (logger.isWarnEnabled()) {
String msg = "Invocation of close method failed on bean with name '" + this.beanName + "'";
if (logger.isDebugEnabled()) {
// Log at warn level like below but add the exception stacktrace only with debug level
logger.warn(msg, ex);
}
else {
logger.warn(msg + ": " + ex);
}
String msg = "Invocation of close method failed on bean with name '" + this.beanName + "'";
if (logger.isDebugEnabled()) {
logger.warn(msg, ex);
}
else {
logger.warn(msg + ": " + ex);
}
}
}
@@ -326,23 +320,18 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
}
}
catch (InvocationTargetException ex) {
if (logger.isWarnEnabled()) {
String msg = "Custom destroy method '" + this.destroyMethodName + "' on bean with name '" +
this.beanName + "' threw an exception";
if (logger.isDebugEnabled()) {
// Log at warn level like below but add the exception stacktrace only with debug level
logger.warn(msg, ex.getTargetException());
}
else {
logger.warn(msg + ": " + ex.getTargetException());
}
String msg = "Custom destroy method '" + this.destroyMethodName + "' on bean with name '" +
this.beanName + "' threw an exception";
if (logger.isDebugEnabled()) {
logger.warn(msg, ex.getTargetException());
}
else {
logger.warn(msg + ": " + ex.getTargetException());
}
}
catch (Throwable ex) {
if (logger.isWarnEnabled()) {
logger.warn("Failed to invoke custom destroy method '" + this.destroyMethodName +
"' on bean with name '" + this.beanName + "'", ex);
}
logger.warn("Failed to invoke custom destroy method '" + this.destroyMethodName +
"' on bean with name '" + this.beanName + "'", ex);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -112,7 +112,7 @@ public class LookupOverride extends MethodOverride {
@Override
public int hashCode() {
return super.hashCode() * 29 + ObjectUtils.nullSafeHashCode(this.beanName);
return (29 * super.hashCode() + ObjectUtils.nullSafeHashCode(this.beanName));
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -506,12 +506,23 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
if (isExternallyManagedInitMethod(initMethod)) {
return true;
}
return hasAnyExternallyManagedMethod(this.externallyManagedInitMethods, initMethod);
if (this.externallyManagedInitMethods != null) {
for (String candidate : this.externallyManagedInitMethods) {
int indexOfDot = candidate.lastIndexOf('.');
if (indexOfDot >= 0) {
String methodName = candidate.substring(indexOfDot + 1);
if (methodName.equals(initMethod)) {
return true;
}
}
}
}
return false;
}
}
/**
* Get all externally managed initialization methods (as an immutable Set).
* Return all externally managed initialization methods (as an immutable Set).
* <p>See {@link #registerExternallyManagedInitMethod} for details
* regarding the format for the initialization methods in the returned set.
* @since 5.3.11
@@ -572,23 +583,19 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
if (isExternallyManagedDestroyMethod(destroyMethod)) {
return true;
}
return hasAnyExternallyManagedMethod(this.externallyManagedDestroyMethods, destroyMethod);
}
}
private static boolean hasAnyExternallyManagedMethod(Set<String> candidates, String methodName) {
if (candidates != null) {
for (String candidate : candidates) {
int indexOfDot = candidate.lastIndexOf('.');
if (indexOfDot > 0) {
String candidateMethodName = candidate.substring(indexOfDot + 1);
if (candidateMethodName.equals(methodName)) {
return true;
if (this.externallyManagedDestroyMethods != null) {
for (String candidate : this.externallyManagedDestroyMethods) {
int indexOfDot = candidate.lastIndexOf('.');
if (indexOfDot >= 0) {
String methodName = candidate.substring(indexOfDot + 1);
if (methodName.equals(destroyMethod)) {
return true;
}
}
}
}
return false;
}
return false;
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -547,8 +547,7 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
* @see DefaultNamespaceHandlerResolver#DefaultNamespaceHandlerResolver(ClassLoader)
*/
protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
ResourceLoader resourceLoader = getResourceLoader();
ClassLoader cl = (resourceLoader != null ? resourceLoader.getClassLoader() : getBeanClassLoader());
ClassLoader cl = (getResourceLoader() != null ? getResourceLoader().getClassLoader() : getBeanClassLoader());
return new DefaultNamespaceHandlerResolver(cl);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -78,10 +78,8 @@ public class PathEditor extends PropertyEditorSupport {
if (nioPathCandidate && !text.startsWith("/")) {
try {
URI uri = new URI(text);
String scheme = uri.getScheme();
if (scheme != null) {
// No NIO candidate except for "C:" style drive letters
nioPathCandidate = (scheme.length() == 1);
if (uri.getScheme() != null) {
nioPathCandidate = false;
// Let's try NIO file system providers via Paths.get(URI)
setValue(Paths.get(uri).normalize());
return;
@@ -111,8 +109,7 @@ public class PathEditor extends PropertyEditorSupport {
setValue(resource.getFile().toPath());
}
catch (IOException ex) {
throw new IllegalArgumentException(
"Could not retrieve file for " + resource + ": " + ex.getMessage());
throw new IllegalArgumentException("Failed to retrieve file for " + resource, ex);
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -77,21 +77,6 @@ class BeanWrapperTests extends AbstractPropertyAccessorTests {
assertThat(accessor.getPropertyValue("aliasedName")).isEqualTo("tom");
}
@Test
void replaceWrappedInstance() {
GetterBean target = new GetterBean();
BeanWrapperImpl accessor = createAccessor(target);
accessor.setPropertyValue("name", "tom");
assertThat(target.getAliasedName()).isEqualTo("tom");
assertThat(accessor.getPropertyValue("aliasedName")).isEqualTo("tom");
target = new GetterBean();
accessor.setWrappedInstance(target);
accessor.setPropertyValue("name", "tom");
assertThat(target.getAliasedName()).isEqualTo("tom");
assertThat(accessor.getPropertyValue("aliasedName")).isEqualTo("tom");
}
@Test
void setValidAndInvalidPropertyValuesShouldContainExceptionDetails() {
TestBean target = new TestBean();
@@ -168,16 +153,6 @@ class BeanWrapperTests extends AbstractPropertyAccessorTests {
assertThat(accessor.getPropertyValue("object")).isEqualTo(8);
}
@Test
void setterOverload() {
SetterOverload target = new SetterOverload();
BeanWrapper accessor = createAccessor(target);
accessor.setPropertyValue("object", "a String");
assertThat(target.value).isEqualTo("a String");
assertThat(target.getObject()).isEqualTo("a String");
assertThat(accessor.getPropertyValue("object")).isEqualTo("a String");
}
@Test
void propertyDescriptors() throws Exception {
TestBean target = new TestBean();
@@ -231,10 +206,6 @@ class BeanWrapperTests extends AbstractPropertyAccessorTests {
assertThat(accessor.isReadableProperty("inputStream")).isFalse();
assertThat(accessor.isReadableProperty("filename")).isTrue();
assertThat(accessor.isReadableProperty("description")).isTrue();
accessor = createAccessor(new ActiveResource());
assertThat(accessor.isReadableProperty("resource")).isTrue();
}
@Test
@@ -377,37 +348,6 @@ class BeanWrapperTests extends AbstractPropertyAccessorTests {
}
public static class SetterOverload {
public String value;
public void setObject(Integer length) {
this.value = length.toString();
}
public void setObject(String object) {
this.value = object;
}
public String getObject() {
return this.value;
}
}
@SuppressWarnings("try")
public static class ActiveResource implements AutoCloseable {
public ActiveResource getResource() {
return this;
}
@Override
public void close() throws Exception {
}
}
public static class GetterWithOptional {
public TestBean value;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -436,6 +436,7 @@ class DefaultListableBeanFactoryTests {
@Test
void empty() {
ListableBeanFactory lbf = new DefaultListableBeanFactory();
assertThat(lbf.getBeanDefinitionNames() != null).as("No beans defined --> array != null").isTrue();
assertThat(lbf.getBeanDefinitionNames().length == 0).as("No beans defined after no arg constructor").isTrue();
assertThat(lbf.getBeanDefinitionCount() == 0).as("No beans defined after no arg constructor").isTrue();
@@ -777,6 +778,21 @@ class DefaultListableBeanFactoryTests {
assertThat(factory.getType("child")).isEqualTo(DerivedTestBean.class);
}
@Test
void nameAlreadyBound() {
Properties p = new Properties();
p.setProperty("kerry.(class)", TestBean.class.getName());
p.setProperty("kerry.age", "35");
registerBeanDefinitions(p);
try {
registerBeanDefinitions(p);
}
catch (BeanDefinitionStoreException ex) {
assertThat(ex.getBeanName()).isEqualTo("kerry");
// expected
}
}
private void singleTestBean(ListableBeanFactory lbf) {
assertThat(lbf.getBeanDefinitionCount() == 1).as("1 beans defined").isTrue();
String[] names = lbf.getBeanDefinitionNames();
@@ -1271,11 +1287,10 @@ class DefaultListableBeanFactoryTests {
lbf.registerBeanDefinition("rod", bd);
RootBeanDefinition bd2 = new RootBeanDefinition(TestBean.class);
lbf.registerBeanDefinition("rod2", bd2);
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
.isThrownBy(() -> lbf.autowire(ConstructorDependency.class, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, false))
.withMessageContaining("rod")
.withMessageContaining("rod2");
assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(() ->
lbf.autowire(ConstructorDependency.class, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, false))
.withMessageContaining("rod")
.withMessageContaining("rod2");
}
@Test
@@ -1337,12 +1352,11 @@ class DefaultListableBeanFactoryTests {
RootBeanDefinition bd2 = new RootBeanDefinition(TestBean.class);
bd2.setDependsOn("tb1");
lbf.registerBeanDefinition("tb2", bd2);
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(() -> lbf.preInstantiateSingletons())
.withMessageContaining("Circular")
.withMessageContaining("'tb2'")
.withMessageContaining("'tb1'");
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() ->
lbf.preInstantiateSingletons())
.withMessageContaining("Circular")
.withMessageContaining("'tb2'")
.withMessageContaining("'tb1'");
}
@Test
@@ -1356,12 +1370,11 @@ class DefaultListableBeanFactoryTests {
RootBeanDefinition bd3 = new RootBeanDefinition(TestBean.class);
bd3.setDependsOn("tb1");
lbf.registerBeanDefinition("tb3", bd3);
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(lbf::preInstantiateSingletons)
.withMessageContaining("Circular")
.withMessageContaining("'tb3'")
.withMessageContaining("'tb1'");
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
lbf::preInstantiateSingletons)
.withMessageContaining("Circular")
.withMessageContaining("'tb3'")
.withMessageContaining("'tb1'");
}
@Test
@@ -1496,11 +1509,10 @@ class DefaultListableBeanFactoryTests {
RootBeanDefinition bd2 = new RootBeanDefinition(HighPriorityTestBean.class);
lbf.registerBeanDefinition("bd1", bd1);
lbf.registerBeanDefinition("bd2", bd2);
assertThatExceptionOfType(NoUniqueBeanDefinitionException.class)
.isThrownBy(() -> lbf.getBean(TestBean.class))
.withMessageContaining("Multiple beans found with the same priority")
.withMessageContaining("5"); // conflicting priority
assertThatExceptionOfType(NoUniqueBeanDefinitionException.class).isThrownBy(() ->
lbf.getBean(TestBean.class))
.withMessageContaining("Multiple beans found with the same priority")
.withMessageContaining("5"); // conflicting priority
}
@Test
@@ -1702,9 +1714,9 @@ class DefaultListableBeanFactoryTests {
lbf.registerBeanDefinition("bd1", bd1);
lbf.registerBeanDefinition("bd2", bd2);
assertThatExceptionOfType(NoUniqueBeanDefinitionException.class)
.isThrownBy(() -> lbf.getBean(ConstructorDependency.class, 42))
.withMessageContaining("more than one 'primary'");
assertThatExceptionOfType(NoUniqueBeanDefinitionException.class).isThrownBy(() ->
lbf.getBean(ConstructorDependency.class, 42))
.withMessageContaining("more than one 'primary'");
}
@Test
@@ -1885,11 +1897,10 @@ class DefaultListableBeanFactoryTests {
RootBeanDefinition bd2 = new RootBeanDefinition(TestBean.class);
lbf.registerBeanDefinition("test", bd);
lbf.registerBeanDefinition("spouse", bd2);
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
.isThrownBy(() -> lbf.autowire(DependenciesBean.class, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true))
.withMessageContaining("test")
.withMessageContaining("spouse");
assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(() ->
lbf.autowire(DependenciesBean.class, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true))
.withMessageContaining("test")
.withMessageContaining("spouse");
}
@Test
@@ -1951,11 +1962,10 @@ class DefaultListableBeanFactoryTests {
RootBeanDefinition bd2 = new RootBeanDefinition(HighPriorityTestBean.class);
lbf.registerBeanDefinition("test", bd);
lbf.registerBeanDefinition("spouse", bd2);
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
.isThrownBy(() -> lbf.autowire(DependenciesBean.class, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true))
.withCauseExactlyInstanceOf(NoUniqueBeanDefinitionException.class)
.withMessageContaining("5");
assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(() ->
lbf.autowire(DependenciesBean.class, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true))
.withCauseExactlyInstanceOf(NoUniqueBeanDefinitionException.class)
.withMessageContaining("5");
}
@Test
@@ -1972,21 +1982,6 @@ class DefaultListableBeanFactoryTests {
assertThat(bean.getSpouse()).isEqualTo(lbf.getBean("spouse"));
}
@Test
void beanProviderWithParentBeanFactoryReuseOrder() {
DefaultListableBeanFactory parentBf = new DefaultListableBeanFactory();
parentBf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
parentBf.registerBeanDefinition("regular", new RootBeanDefinition(TestBean.class));
parentBf.registerBeanDefinition("test", new RootBeanDefinition(HighPriorityTestBean.class));
lbf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
lbf.setParentBeanFactory(parentBf);
lbf.registerBeanDefinition("low", new RootBeanDefinition(LowPriorityTestBean.class));
List<Class<?>> orderedTypes = lbf.getBeanProvider(TestBean.class).orderedStream()
.map(Object::getClass).collect(Collectors.toList());
assertThat(orderedTypes).containsExactly(
HighPriorityTestBean.class, LowPriorityTestBean.class, TestBean.class);
}
@Test
void autowireExistingBeanByName() {
RootBeanDefinition bd = new RootBeanDefinition(TestBean.class);
@@ -2191,21 +2186,19 @@ class DefaultListableBeanFactoryTests {
@Test
void beanDefinitionWithInterface() {
lbf.registerBeanDefinition("test", new RootBeanDefinition(ITestBean.class));
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(() -> lbf.getBean("test"))
.withMessageContaining("interface")
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("test"));
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() ->
lbf.getBean("test"))
.withMessageContaining("interface")
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("test"));
}
@Test
void beanDefinitionWithAbstractClass() {
lbf.registerBeanDefinition("test", new RootBeanDefinition(AbstractBeanFactory.class));
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(() -> lbf.getBean("test"))
.withMessageContaining("abstract")
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("test"));
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() ->
lbf.getBean("test"))
.withMessageContaining("abstract")
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("test"));
}
@Test
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 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.
@@ -28,32 +28,36 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Chris Beams
* @since 04.07.2006
*/
class DefaultSingletonBeanRegistryTests {
private final DefaultSingletonBeanRegistry beanRegistry = new DefaultSingletonBeanRegistry();
public class DefaultSingletonBeanRegistryTests {
@Test
void singletons() {
public void testSingletons() {
DefaultSingletonBeanRegistry beanRegistry = new DefaultSingletonBeanRegistry();
TestBean tb = new TestBean();
beanRegistry.registerSingleton("tb", tb);
assertThat(beanRegistry.getSingleton("tb")).isSameAs(tb);
TestBean tb2 = (TestBean) beanRegistry.getSingleton("tb2", TestBean::new);
TestBean tb2 = (TestBean) beanRegistry.getSingleton("tb2", () -> new TestBean());
assertThat(beanRegistry.getSingleton("tb2")).isSameAs(tb2);
assertThat(beanRegistry.getSingleton("tb")).isSameAs(tb);
assertThat(beanRegistry.getSingleton("tb2")).isSameAs(tb2);
assertThat(beanRegistry.getSingletonCount()).isEqualTo(2);
assertThat(beanRegistry.getSingletonNames()).containsExactly("tb", "tb2");
String[] names = beanRegistry.getSingletonNames();
assertThat(names.length).isEqualTo(2);
assertThat(names[0]).isEqualTo("tb");
assertThat(names[1]).isEqualTo("tb2");
beanRegistry.destroySingletons();
assertThat(beanRegistry.getSingletonCount()).isZero();
assertThat(beanRegistry.getSingletonNames()).isEmpty();
assertThat(beanRegistry.getSingletonCount()).isEqualTo(0);
assertThat(beanRegistry.getSingletonNames().length).isEqualTo(0);
}
@Test
void disposableBean() {
public void testDisposableBean() {
DefaultSingletonBeanRegistry beanRegistry = new DefaultSingletonBeanRegistry();
DerivedTestBean tb = new DerivedTestBean();
beanRegistry.registerSingleton("tb", tb);
beanRegistry.registerDisposableBean("tb", tb);
@@ -61,16 +65,21 @@ class DefaultSingletonBeanRegistryTests {
assertThat(beanRegistry.getSingleton("tb")).isSameAs(tb);
assertThat(beanRegistry.getSingletonCount()).isEqualTo(1);
assertThat(beanRegistry.getSingletonNames()).containsExactly("tb");
String[] names = beanRegistry.getSingletonNames();
assertThat(names.length).isEqualTo(1);
assertThat(names[0]).isEqualTo("tb");
assertThat(tb.wasDestroyed()).isFalse();
beanRegistry.destroySingletons();
assertThat(beanRegistry.getSingletonCount()).isZero();
assertThat(beanRegistry.getSingletonNames()).isEmpty();
assertThat(beanRegistry.getSingletonCount()).isEqualTo(0);
assertThat(beanRegistry.getSingletonNames().length).isEqualTo(0);
assertThat(tb.wasDestroyed()).isTrue();
}
@Test
void dependentRegistration() {
public void testDependentRegistration() {
DefaultSingletonBeanRegistry beanRegistry = new DefaultSingletonBeanRegistry();
beanRegistry.registerDependentBean("a", "b");
beanRegistry.registerDependentBean("b", "c");
beanRegistry.registerDependentBean("c", "b");
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 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.
@@ -27,7 +27,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.MethodParameter;
import org.springframework.util.ClassUtils;
@@ -148,7 +148,7 @@ public class QualifierAnnotationAutowireBeanFactoryTests {
lbf.registerBeanDefinition(MARK, person2);
MethodParameter param = new MethodParameter(QualifiedTestBean.class.getDeclaredConstructor(Person.class), 0);
DependencyDescriptor qualifiedDescriptor = new DependencyDescriptor(param, false);
param.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
param.initParameterNameDiscovery(new LocalVariableTableParameterNameDiscoverer());
assertThat(param.getParameterName()).isEqualTo("tpb");
assertThat(lbf.isAutowireCandidate(JUERGEN, null)).isTrue();
assertThat(lbf.isAutowireCandidate(JUERGEN, qualifiedDescriptor)).isTrue();
@@ -174,9 +174,9 @@ public class QualifierAnnotationAutowireBeanFactoryTests {
new MethodParameter(QualifiedTestBean.class.getDeclaredMethod("autowireNonqualified", Person.class), 0);
DependencyDescriptor qualifiedDescriptor = new DependencyDescriptor(qualifiedParam, false);
DependencyDescriptor nonqualifiedDescriptor = new DependencyDescriptor(nonqualifiedParam, false);
qualifiedParam.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
qualifiedParam.initParameterNameDiscovery(new LocalVariableTableParameterNameDiscoverer());
assertThat(qualifiedParam.getParameterName()).isEqualTo("tpb");
nonqualifiedParam.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
nonqualifiedParam.initParameterNameDiscovery(new LocalVariableTableParameterNameDiscoverer());
assertThat(nonqualifiedParam.getParameterName()).isEqualTo("tpb");
assertThat(lbf.isAutowireCandidate(JUERGEN, null)).isTrue();
assertThat(lbf.isAutowireCandidate(JUERGEN, nonqualifiedDescriptor)).isTrue();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 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.
@@ -502,7 +502,7 @@ class CustomEditorTests {
CharBean cb = new CharBean();
BeanWrapper bw = new BeanWrapperImpl(cb);
bw.setPropertyValue("myChar", 'c');
bw.setPropertyValue("myChar", new Character('c'));
assertThat(cb.getMyChar()).isEqualTo('c');
bw.setPropertyValue("myChar", "c");
@@ -526,16 +526,16 @@ class CustomEditorTests {
bw.registerCustomEditor(Character.class, new CharacterEditor(true));
bw.setPropertyValue("myCharacter", 'c');
assertThat(cb.getMyCharacter()).isEqualTo('c');
assertThat(cb.getMyCharacter()).isEqualTo(Character.valueOf('c'));
bw.setPropertyValue("myCharacter", "c");
assertThat(cb.getMyCharacter()).isEqualTo('c');
assertThat(cb.getMyCharacter()).isEqualTo(Character.valueOf('c'));
bw.setPropertyValue("myCharacter", "\u0041");
assertThat(cb.getMyCharacter()).isEqualTo('A');
assertThat(cb.getMyCharacter()).isEqualTo(Character.valueOf('A'));
bw.setPropertyValue("myCharacter", " ");
assertThat(cb.getMyCharacter()).isEqualTo(' ');
assertThat(cb.getMyCharacter()).isEqualTo(Character.valueOf(' '));
bw.setPropertyValue("myCharacter", "");
assertThat(cb.getMyCharacter()).isNull();
@@ -722,7 +722,7 @@ class CustomEditorTests {
@Test
void testCustomDateEditor() {
CustomDateEditor editor = new CustomDateEditor(new SimpleDateFormat("MM/dd/yyyy"), false);
CustomDateEditor editor = new CustomDateEditor(null, false);
editor.setValue(null);
assertThat(editor.getValue()).isNull();
assertThat(editor.getAsText()).isEmpty();
@@ -730,7 +730,7 @@ class CustomEditorTests {
@Test
void testCustomDateEditorWithEmptyAsNull() {
CustomDateEditor editor = new CustomDateEditor(new SimpleDateFormat("MM/dd/yyyy"), true);
CustomDateEditor editor = new CustomDateEditor(null, true);
editor.setValue(null);
assertThat(editor.getValue()).isNull();
assertThat(editor.getAsText()).isEmpty();
@@ -4,27 +4,25 @@
<beans default-lazy-init="true">
<bean name="beta" class="org.springframework.beans.factory.FactoryBeanTests$Beta" autowire="byType">
<property name="name" value="${myName}"/>
<property name="name" value="${myName}"/>
</bean>
<bean id="alpha" class="org.springframework.beans.factory.FactoryBeanTests$Alpha" autowire="byType"/>
<bean id="gamma" class="org.springframework.beans.factory.FactoryBeanTests$Gamma"/>
<bean id="betaFactory" class="org.springframework.beans.factory.FactoryBeanTests$BetaFactoryBean" autowire="constructor">
<property name="beta" ref="beta"/>
</bean>
<bean id="betaFactory" class="org.springframework.beans.factory.FactoryBeanTests$BetaFactoryBean" autowire="constructor">
<property name="beta" ref="beta"/>
</bean>
<bean id="gammaFactory" factory-bean="${gammaFactory}" factory-method="${gamma}"/>
<bean id="gammaFactory" factory-bean="betaFactory" factory-method="getGamma"/>
<bean id="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="properties">
<props>
<prop key="myName">yourName</prop>
<prop key="gammaFactory">betaFactory</prop>
<prop key="gamma">getGamma</prop>
</props>
</property>
</bean>
<bean id="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="properties">
<props>
<prop key="myName">yourName</prop>
</props>
</property>
</bean>
</beans>
@@ -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.
@@ -33,10 +33,6 @@ public interface ITestBean extends AgeHolder {
void setName(String name);
default void applyName(Object name) {
setName(String.valueOf(name));
}
ITestBean getSpouse();
void setSpouse(ITestBean spouse);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -477,7 +477,7 @@ public class TestBean implements BeanNameAware, BeanFactoryAware, ITestBean, IOt
@Override
public int hashCode() {
return TestBean.class.hashCode();
return this.age;
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -81,7 +81,7 @@ class TypeHelper {
}
/**
* Return the superclass of the specified {@link Element} or null if this
* Return the super class of the specified {@link Element} or null if this
* {@code element} represents {@link Object}.
*/
public Element getSuperClass(Element element) {
@@ -100,7 +100,7 @@ class TypeHelper {
public List<Element> getDirectInterfaces(Element element) {
List<? extends TypeMirror> superTypes = this.types.directSupertypes(element.asType());
List<Element> directInterfaces = new ArrayList<>();
if (superTypes.size() > 1) { // index 0 is the superclass
if (superTypes.size() > 1) { // index 0 is the super class
for (int i = 1; i < superTypes.size(); i++) {
Element e = this.types.asElement(superTypes.get(i));
if (e != null) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -59,8 +59,8 @@ public class CaffeineCache extends AbstractValueAdaptingCache {
* given internal {@link com.github.benmanes.caffeine.cache.Cache} to use.
* @param name the name of the cache
* @param cache the backing Caffeine Cache instance
* @param allowNullValues whether to accept and convert {@code null} values
* for this cache
* @param allowNullValues whether to accept and convert {@code null}
* values for this cache
*/
public CaffeineCache(String name, com.github.benmanes.caffeine.cache.Cache<Object, Object> cache,
boolean allowNullValues) {
@@ -86,7 +86,7 @@ public class CaffeineCache extends AbstractValueAdaptingCache {
@SuppressWarnings("unchecked")
@Override
@Nullable
public <T> T get(Object key, Callable<T> valueLoader) {
public <T> T get(Object key, final Callable<T> valueLoader) {
return (T) fromStoreValue(this.cache.get(key, new LoadFunction(valueLoader)));
}
@@ -106,7 +106,7 @@ public class CaffeineCache extends AbstractValueAdaptingCache {
@Override
@Nullable
public ValueWrapper putIfAbsent(Object key, @Nullable Object value) {
public ValueWrapper putIfAbsent(Object key, @Nullable final Object value) {
PutIfAbsentFunction callable = new PutIfAbsentFunction(value);
Object result = this.cache.get(key, callable);
return (callable.called ? null : toValueWrapper(result));
@@ -140,7 +140,7 @@ public class CaffeineCache extends AbstractValueAdaptingCache {
@Nullable
private final Object value;
boolean called;
private boolean called;
public PutIfAbsentFunction(@Nullable Object value) {
this.value = value;
@@ -159,17 +159,16 @@ public class CaffeineCache extends AbstractValueAdaptingCache {
private final Callable<?> valueLoader;
public LoadFunction(Callable<?> valueLoader) {
Assert.notNull(valueLoader, "Callable must not be null");
this.valueLoader = valueLoader;
}
@Override
public Object apply(Object key) {
public Object apply(Object o) {
try {
return toStoreValue(this.valueLoader.call());
}
catch (Exception ex) {
throw new ValueRetrievalException(key, this.valueLoader, ex);
throw new ValueRetrievalException(o, this.valueLoader, ex);
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -51,7 +51,6 @@ import org.springframework.util.ObjectUtils;
* @author Juergen Hoeller
* @author Stephane Nicoll
* @author Sam Brannen
* @author Brian Clozel
* @since 4.3
* @see CaffeineCache
*/
@@ -110,7 +109,7 @@ public class CaffeineCacheManager implements CacheManager {
* Set the Caffeine to use for building each individual
* {@link CaffeineCache} instance.
* @see #createNativeCaffeineCache
* @see Caffeine#build()
* @see com.github.benmanes.caffeine.cache.Caffeine#build()
*/
public void setCaffeine(Caffeine<Object, Object> caffeine) {
Assert.notNull(caffeine, "Caffeine must not be null");
@@ -121,7 +120,7 @@ public class CaffeineCacheManager implements CacheManager {
* Set the {@link CaffeineSpec} to use for building each individual
* {@link CaffeineCache} instance.
* @see #createNativeCaffeineCache
* @see Caffeine#from(CaffeineSpec)
* @see com.github.benmanes.caffeine.cache.Caffeine#from(CaffeineSpec)
*/
public void setCaffeineSpec(CaffeineSpec caffeineSpec) {
doSetCaffeine(Caffeine.from(caffeineSpec));
@@ -132,7 +131,7 @@ public class CaffeineCacheManager implements CacheManager {
* individual {@link CaffeineCache} instance. The given value needs to
* comply with Caffeine's {@link CaffeineSpec} (see its javadoc).
* @see #createNativeCaffeineCache
* @see Caffeine#from(String)
* @see com.github.benmanes.caffeine.cache.Caffeine#from(String)
*/
public void setCacheSpecification(String cacheSpecification) {
doSetCaffeine(Caffeine.from(cacheSpecification));
@@ -149,7 +148,7 @@ public class CaffeineCacheManager implements CacheManager {
* Set the Caffeine CacheLoader to use for building each individual
* {@link CaffeineCache} instance, turning it into a LoadingCache.
* @see #createNativeCaffeineCache
* @see Caffeine#build(CacheLoader)
* @see com.github.benmanes.caffeine.cache.Caffeine#build(CacheLoader)
* @see com.github.benmanes.caffeine.cache.LoadingCache
*/
public void setCacheLoader(CacheLoader<Object, Object> cacheLoader) {
@@ -189,11 +188,8 @@ public class CaffeineCacheManager implements CacheManager {
@Override
@Nullable
public Cache getCache(String name) {
Cache cache = this.cacheMap.get(name);
if (cache == null && this.dynamic) {
cache = this.cacheMap.computeIfAbsent(name, this::createCaffeineCache);
}
return cache;
return this.cacheMap.computeIfAbsent(name, cacheName ->
this.dynamic ? createCaffeineCache(cacheName) : null);
}
@@ -236,7 +232,7 @@ public class CaffeineCacheManager implements CacheManager {
* Build a common {@link CaffeineCache} instance for the specified cache name,
* using the common Caffeine configuration specified on this cache manager.
* <p>Delegates to {@link #adaptCaffeineCache} as the adaptation method to
* Spring's cache abstraction (allowing for centralized decoration etc.),
* Spring's cache abstraction (allowing for centralized decoration etc),
* passing in a freshly built native Caffeine Cache instance.
* @param name the name of the cache
* @return the Spring CaffeineCache adapter (or a decorator thereof)
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 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.
@@ -93,7 +93,7 @@ public class SimpleMailMessage implements MailMessage, Serializable {
@Override
public void setFrom(@Nullable String from) {
public void setFrom(String from) {
this.from = from;
}
@@ -103,7 +103,7 @@ public class SimpleMailMessage implements MailMessage, Serializable {
}
@Override
public void setReplyTo(@Nullable String replyTo) {
public void setReplyTo(String replyTo) {
this.replyTo = replyTo;
}
@@ -113,7 +113,7 @@ public class SimpleMailMessage implements MailMessage, Serializable {
}
@Override
public void setTo(@Nullable String to) {
public void setTo(String to) {
this.to = new String[] {to};
}
@@ -128,12 +128,12 @@ public class SimpleMailMessage implements MailMessage, Serializable {
}
@Override
public void setCc(@Nullable String cc) {
public void setCc(String cc) {
this.cc = new String[] {cc};
}
@Override
public void setCc(@Nullable String... cc) {
public void setCc(String... cc) {
this.cc = cc;
}
@@ -143,12 +143,12 @@ public class SimpleMailMessage implements MailMessage, Serializable {
}
@Override
public void setBcc(@Nullable String bcc) {
public void setBcc(String bcc) {
this.bcc = new String[] {bcc};
}
@Override
public void setBcc(@Nullable String... bcc) {
public void setBcc(String... bcc) {
this.bcc = bcc;
}
@@ -158,7 +158,7 @@ public class SimpleMailMessage implements MailMessage, Serializable {
}
@Override
public void setSentDate(@Nullable Date sentDate) {
public void setSentDate(Date sentDate) {
this.sentDate = sentDate;
}
@@ -168,7 +168,7 @@ public class SimpleMailMessage implements MailMessage, Serializable {
}
@Override
public void setSubject(@Nullable String subject) {
public void setSubject(String subject) {
this.subject = subject;
}
@@ -178,7 +178,7 @@ public class SimpleMailMessage implements MailMessage, Serializable {
}
@Override
public void setText(@Nullable String text) {
public void setText(String text) {
this.text = text;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -132,10 +132,10 @@ public class JavaMailSenderImpl implements JavaMailSender {
}
/**
* Allow {@code Map} access to the JavaMail properties of this sender,
* Allow {code Map} access to the JavaMail properties of this sender,
* with the option to add or override specific entries.
* <p>Useful for specifying entries directly, for example via
* {@code javaMailProperties[mail.smtp.auth]}.
* {code javaMailProperties[mail.smtp.auth]}.
*/
public Properties getJavaMailProperties() {
return this.javaMailProperties;
@@ -50,7 +50,7 @@ video/x-msvideo avi
################################################################################
#
# Additional file types adapted from
# https://web.archive.org/web/20220119153325/http%3A//sites.utoronto.ca/webdocs/HTMLdocs/Book/Book-3ed/appb/mimetype.html
# http://sites.utoronto.ca/webdocs/HTMLdocs/Book/Book-3ed/appb/mimetype.html
# kindly re-licensed to Apache Software License 2.0 by Ian Graham.
#
################################################################################
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,17 +38,19 @@ public class CaffeineCacheManagerTests {
@Test
public void testDynamicMode() {
CacheManager cm = new CaffeineCacheManager();
Cache cache1 = cm.getCache("c1");
assertThat(cache1).isInstanceOf(CaffeineCache.class);
boolean condition2 = cache1 instanceof CaffeineCache;
assertThat(condition2).isTrue();
Cache cache1again = cm.getCache("c1");
assertThat(cache1).isSameAs(cache1again);
Cache cache2 = cm.getCache("c2");
assertThat(cache2).isInstanceOf(CaffeineCache.class);
boolean condition1 = cache2 instanceof CaffeineCache;
assertThat(condition1).isTrue();
Cache cache2again = cm.getCache("c2");
assertThat(cache2).isSameAs(cache2again);
Cache cache3 = cm.getCache("c3");
assertThat(cache3).isInstanceOf(CaffeineCache.class);
boolean condition = cache3 instanceof CaffeineCache;
assertThat(condition).isTrue();
Cache cache3again = cm.getCache("c3");
assertThat(cache3).isSameAs(cache3again);
@@ -60,23 +62,19 @@ public class CaffeineCacheManagerTests {
assertThat(cache1.get("key3").get()).isNull();
cache1.evict("key3");
assertThat(cache1.get("key3")).isNull();
assertThat(cache1.get("key3", () -> "value3")).isEqualTo("value3");
assertThat(cache1.get("key3", () -> "value3")).isEqualTo("value3");
cache1.evict("key3");
assertThat(cache1.get("key3", () -> (String) null)).isNull();
assertThat(cache1.get("key3", () -> (String) null)).isNull();
}
@Test
public void testStaticMode() {
CaffeineCacheManager cm = new CaffeineCacheManager("c1", "c2");
Cache cache1 = cm.getCache("c1");
assertThat(cache1).isInstanceOf(CaffeineCache.class);
boolean condition3 = cache1 instanceof CaffeineCache;
assertThat(condition3).isTrue();
Cache cache1again = cm.getCache("c1");
assertThat(cache1).isSameAs(cache1again);
Cache cache2 = cm.getCache("c2");
assertThat(cache2).isInstanceOf(CaffeineCache.class);
boolean condition2 = cache2 instanceof CaffeineCache;
assertThat(condition2).isTrue();
Cache cache2again = cm.getCache("c2");
assertThat(cache2).isSameAs(cache2again);
Cache cache3 = cm.getCache("c3");
@@ -93,11 +91,13 @@ public class CaffeineCacheManagerTests {
cm.setAllowNullValues(false);
Cache cache1x = cm.getCache("c1");
assertThat(cache1x).isInstanceOf(CaffeineCache.class);
assertThat(cache1x).isNotSameAs(cache1);
boolean condition1 = cache1x instanceof CaffeineCache;
assertThat(condition1).isTrue();
assertThat(cache1x != cache1).isTrue();
Cache cache2x = cm.getCache("c2");
assertThat(cache2x).isInstanceOf(CaffeineCache.class);
assertThat(cache2x).isNotSameAs(cache2);
boolean condition = cache2x instanceof CaffeineCache;
assertThat(condition).isTrue();
assertThat(cache2x != cache2).isTrue();
Cache cache3x = cm.getCache("c3");
assertThat(cache3x).isNull();
@@ -190,7 +190,7 @@ public class CaffeineCacheManagerTests {
assertThat(value.get()).isEqualTo("pong");
assertThatIllegalArgumentException().isThrownBy(() -> assertThat(cache1.get("foo")).isNull())
.withMessageContaining("I only know ping");
.withMessageContaining("I only know ping");
}
@Test
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -114,7 +114,7 @@ class QuartzSupportTests {
trigger.setName("myTrigger");
trigger.setJobDetail(jobDetail);
trigger.setStartDelay(1);
trigger.setRepeatInterval(100);
trigger.setRepeatInterval(500);
trigger.setRepeatCount(1);
trigger.afterPropertiesSet();
@@ -126,14 +126,14 @@ class QuartzSupportTests {
bean.start();
Thread.sleep(500);
assertThat(DummyJob.count).as("DummyJob should have been executed at least once.").isGreaterThan(0);
assertThat(DummyJob.count > 0).as("DummyJob should have been executed at least once.").isTrue();
assertThat(taskExecutor.count).isEqualTo(DummyJob.count);
bean.destroy();
}
@Test
@SuppressWarnings({"unchecked", "rawtypes"})
@SuppressWarnings({ "unchecked", "rawtypes" })
void jobDetailWithRunnableInsteadOfJob() {
JobDetailImpl jobDetail = new JobDetailImpl();
assertThatIllegalArgumentException().isThrownBy(() ->
@@ -156,7 +156,7 @@ class QuartzSupportTests {
trigger.setName("myTrigger");
trigger.setJobDetail(jobDetail);
trigger.setStartDelay(1);
trigger.setRepeatInterval(100);
trigger.setRepeatInterval(500);
trigger.setRepeatCount(1);
trigger.afterPropertiesSet();
@@ -168,7 +168,7 @@ class QuartzSupportTests {
Thread.sleep(500);
assertThat(DummyJobBean.param).isEqualTo(10);
assertThat(DummyJobBean.count).isGreaterThan(0);
assertThat(DummyJobBean.count > 0).isTrue();
bean.destroy();
}
@@ -190,7 +190,7 @@ class QuartzSupportTests {
trigger.setName("myTrigger");
trigger.setJobDetail(jobDetail);
trigger.setStartDelay(1);
trigger.setRepeatInterval(100);
trigger.setRepeatInterval(500);
trigger.setRepeatCount(1);
trigger.afterPropertiesSet();
@@ -203,7 +203,7 @@ class QuartzSupportTests {
Thread.sleep(500);
assertThat(DummyJob.param).isEqualTo(10);
assertThat(DummyJob.count).as("DummyJob should have been executed at least once.").isGreaterThan(0);
assertThat(DummyJob.count > 0).as("DummyJob should have been executed at least once.").isTrue();
bean.destroy();
}
@@ -225,7 +225,7 @@ class QuartzSupportTests {
trigger.setName("myTrigger");
trigger.setJobDetail(jobDetail);
trigger.setStartDelay(1);
trigger.setRepeatInterval(100);
trigger.setRepeatInterval(500);
trigger.setRepeatCount(1);
trigger.afterPropertiesSet();
@@ -239,7 +239,7 @@ class QuartzSupportTests {
Thread.sleep(500);
assertThat(DummyJob.param).isEqualTo(0);
assertThat(DummyJob.count).isEqualTo(0);
assertThat(DummyJob.count == 0).isTrue();
bean.destroy();
}
@@ -260,7 +260,7 @@ class QuartzSupportTests {
trigger.setName("myTrigger");
trigger.setJobDetail(jobDetail);
trigger.setStartDelay(1);
trigger.setRepeatInterval(100);
trigger.setRepeatInterval(500);
trigger.setRepeatCount(1);
trigger.afterPropertiesSet();
@@ -273,7 +273,7 @@ class QuartzSupportTests {
Thread.sleep(500);
assertThat(DummyJobBean.param).isEqualTo(10);
assertThat(DummyJobBean.count).isGreaterThan(0);
assertThat(DummyJobBean.count > 0).isTrue();
bean.destroy();
}
@@ -292,7 +292,7 @@ class QuartzSupportTests {
Thread.sleep(500);
assertThat(DummyJob.param).isEqualTo(10);
assertThat(DummyJob.count).as("DummyJob should have been executed at least once.").isGreaterThan(0);
assertThat(DummyJob.count > 0).as("DummyJob should have been executed at least once.").isTrue();
bean.destroy();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 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,20 +23,14 @@ import org.springframework.lang.Nullable;
/**
* Interface that defines common cache operations.
*
* <p>Serves as an SPI for Spring's annotation-based caching model
* ({@link org.springframework.cache.annotation.Cacheable} and co)
* as well as an API for direct usage in applications.
*
* <p><b>Note:</b> Due to the generic use of caching, it is recommended
* that implementations allow storage of {@code null} values
* (for example to cache methods that return {@code null}).
* <b>Note:</b> Due to the generic use of caching, it is recommended that
* implementations allow storage of <tt>null</tt> values (for example to
* cache methods that return {@code null}).
*
* @author Costin Leau
* @author Juergen Hoeller
* @author Stephane Nicoll
* @since 3.1
* @see CacheManager
* @see org.springframework.cache.annotation.Cacheable
*/
public interface Cache {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 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.
@@ -32,7 +32,6 @@ import java.lang.annotation.Target;
* @author Stephane Nicoll
* @author Sam Brannen
* @since 4.1
* @see Cacheable
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@@ -43,10 +42,8 @@ public @interface CacheConfig {
* Names of the default caches to consider for caching operations defined
* in the annotated class.
* <p>If none is set at the operation level, these are used instead of the default.
* <p>Names may be used to determine the target cache(s), to be resolved via the
* configured {@link #cacheResolver()} which typically delegates to
* {@link org.springframework.cache.CacheManager#getCache}.
* For further details see {@link Cacheable#cacheNames()}.
* <p>May be used to determine the target cache (or caches), matching the
* qualifier value or the bean names of a specific bean definition.
*/
String[] cacheNames() default {};
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -111,8 +111,7 @@ public @interface CacheEvict {
/**
* Spring Expression Language (SpEL) expression used for making the cache
* eviction operation conditional. Evict that cache if the condition evaluates
* to {@code true}.
* eviction operation conditional.
* <p>Default is {@code ""}, meaning the cache eviction is always performed.
* <p>The SpEL expression evaluates against a dedicated context that provides the
* following meta-data:
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 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.
@@ -118,8 +118,7 @@ public @interface CachePut {
/**
* Spring Expression Language (SpEL) expression used for making the cache
* put operation conditional. Update the cache if the condition evaluates to
* {@code true}.
* put operation conditional.
* <p>This expression is evaluated after the method has been called due to the
* nature of the put operation and can therefore refer to the {@code result}.
* <p>Default is {@code ""}, meaning the method result is always cached.
@@ -143,7 +142,6 @@ public @interface CachePut {
/**
* Spring Expression Language (SpEL) expression used to veto the cache put operation.
* Veto updating the cache if the condition evaluates to {@code true}.
* <p>Default is {@code ""}, meaning that caching is never vetoed.
* <p>The SpEL expression evaluates against a dedicated context that provides the
* following meta-data:
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -68,12 +68,8 @@ public @interface Cacheable {
/**
* Names of the caches in which method invocation results are stored.
* <p>Names may be used to determine the target cache(s), to be resolved via the
* configured {@link #cacheResolver()} which typically delegates to
* {@link org.springframework.cache.CacheManager#getCache}.
* <p>This will usually be a single cache name. If multiple names are specified,
* they will be consulted for a cache hit in the order of definition, and they
* will all receive a put/evict request for the same newly cached value.
* <p>Names may be used to determine the target cache (or caches), matching
* the qualifier value or bean name of a specific bean definition.
* @since 4.2
* @see #value
* @see CacheConfig#cacheNames
@@ -127,8 +123,7 @@ public @interface Cacheable {
/**
* Spring Expression Language (SpEL) expression used for making the method
* caching conditional. Cache the result if the condition evaluates to
* {@code true}.
* caching conditional.
* <p>Default is {@code ""}, meaning the method result is always cached.
* <p>The SpEL expression evaluates against a dedicated context that provides the
* following meta-data:
@@ -147,7 +142,6 @@ public @interface Cacheable {
/**
* Spring Expression Language (SpEL) expression used to veto method caching.
* Veto caching the result if the condition evaluates to {@code true}.
* <p>Unlike {@link #condition}, this expression is evaluated after the method
* has been called and can therefore refer to the {@code result}.
* <p>Default is {@code ""}, meaning that caching is never vetoed.
@@ -179,9 +173,9 @@ public @interface Cacheable {
* <li>Only one cache may be specified</li>
* <li>No other cache-related operation can be combined</li>
* </ol>
* This is effectively a hint and the chosen cache provider might not actually
* support it in a synchronized fashion. Check your provider documentation for
* more details on the actual semantics.
* This is effectively a hint and the actual cache provider that you are
* using may not support it in a synchronized fashion. Check your provider
* documentation for more details on the actual semantics.
* @since 4.3
* @see org.springframework.cache.Cache#get(Object, Callable)
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -166,7 +166,13 @@ public class ConcurrentMapCacheManager implements CacheManager, BeanClassLoaderA
public Cache getCache(String name) {
Cache cache = this.cacheMap.get(name);
if (cache == null && this.dynamic) {
cache = this.cacheMap.computeIfAbsent(name, this::createConcurrentMapCache);
synchronized (this.cacheMap) {
cache = this.cacheMap.get(name);
if (cache == null) {
cache = createConcurrentMapCache(name);
this.cacheMap.put(name, cache);
}
}
}
return cache;
}

Some files were not shown because too many files have changed in this diff Show More