mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
380 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1195bbd50d | |||
| 9ea049ad6f | |||
| 1e31cd685e | |||
| 0d60f266ad | |||
| a018ae6e02 | |||
| ea419d2dcd | |||
| c02d07fdef | |||
| 7460be617b | |||
| ccf4b028d2 | |||
| b5c89c91a9 | |||
| 01ae0a7c26 | |||
| b48d72cbd0 | |||
| 48628194dc | |||
| 3cf1fbf222 | |||
| ecd943c7ec | |||
| 9d6d67188a | |||
| 24fd0940bd | |||
| 5b928f47a8 | |||
| 5e52baee86 | |||
| c10596a1fd | |||
| a68b7289f0 | |||
| a5b9e667e3 | |||
| 78fe55f4d1 | |||
| 65913c3655 | |||
| b7a996a64b | |||
| 5b1a7c7f21 | |||
| ebd80bdd6c | |||
| a4fc68b8e8 | |||
| c333946b0b | |||
| b0a8a3ec5f | |||
| bb5be2119c | |||
| cf662368a5 | |||
| e1b06ccfaa | |||
| 9eefdb8041 | |||
| c50cb10964 | |||
| 245341231f | |||
| 875cc828cf | |||
| 898d3ec86a | |||
| 8ccaabe778 | |||
| 9c934b5019 | |||
| 4b45338ae6 | |||
| 95003e3512 | |||
| 68368621f3 | |||
| 890a8f4311 | |||
| 0976d1a252 | |||
| 2c0ce790d8 | |||
| d512aafc36 | |||
| faa000f330 | |||
| daf1b3d7a7 | |||
| a48897a241 | |||
| 7ffa12f90f | |||
| 0e74ffa144 | |||
| 357195289d | |||
| 4fbca99f20 | |||
| c48ca5151f | |||
| aaf2e8fbe6 | |||
| 3635ff0c17 | |||
| f20e76e227 | |||
| dd32f95192 | |||
| 75a920bc9f | |||
| 92874adae9 | |||
| 55167b7f50 | |||
| 3129afba19 | |||
| 1366d2926b | |||
| 5145cf476f | |||
| 6cb41dc5e3 | |||
| 1e95332f62 | |||
| fec6ba4dfe | |||
| bfde33a514 | |||
| 14b9865de7 | |||
| 62eb21f938 | |||
| 01c85b1afb | |||
| df376d9343 | |||
| 4d792d0e45 | |||
| 0a5bd89129 | |||
| 53b9a2cb78 | |||
| e78179b96e | |||
| 49a63e2c37 | |||
| 5666e363d1 | |||
| f06853a339 | |||
| 8afd01ba2c | |||
| 9724f9b9c8 | |||
| 0d9033592b | |||
| fc7b8ae966 | |||
| c457131f1c | |||
| 39cfe136da | |||
| 05a880e3b5 | |||
| c93af1f76d | |||
| 541866fd70 | |||
| 0219ee656f | |||
| f6e1a5de09 | |||
| 9b3cb15389 | |||
| 4ef2b429e0 | |||
| 0beb56a58c | |||
| 6bd4687706 | |||
| 07b12666b4 | |||
| 438d6de3c1 | |||
| d43126705f | |||
| 57bbf0ca0f | |||
| 27912b1ed1 | |||
| 8ffbafd384 | |||
| 022fdcd67e | |||
| 11ebceee38 | |||
| fa21dffcf8 | |||
| 323de1208a | |||
| 94d46eba3c | |||
| e340e45f5a | |||
| a06bbccf9e | |||
| bbe362c0e6 | |||
| 4a81f2c904 | |||
| fb0a108254 | |||
| c98f314665 | |||
| d22924c728 | |||
| e235e661d8 | |||
| 09fe0adb40 | |||
| 84e762b470 | |||
| e90a2da05d | |||
| d48f388c6a | |||
| bbbb7c396e | |||
| d72c8b32b7 | |||
| 67d78eb61c | |||
| 73fd9133e9 | |||
| 5e28a25a30 | |||
| 4c44b91cf9 | |||
| 912c067e23 | |||
| a55701588e | |||
| fde7116ae4 | |||
| 0a645916cd | |||
| 5302e7aa25 | |||
| cad02c1743 | |||
| c765d03a59 | |||
| 11d4272ff4 | |||
| 23656aebc6 | |||
| feb6a5f52d | |||
| a228eb8bd6 | |||
| c015814140 | |||
| fb7890d739 | |||
| 7c2c4d7c9a | |||
| 3bfbe30a78 | |||
| 32962894a0 | |||
| 97bce6d361 | |||
| 8da31e1db7 | |||
| f991c19b30 | |||
| 3c80d4c978 | |||
| 6da32b4631 | |||
| 91cadf7f24 | |||
| 90637b6344 | |||
| 2622db1dbe | |||
| 3dac274d20 | |||
| 6b975592bf | |||
| c44e43d791 | |||
| c41f8ea3c8 | |||
| a668580f77 | |||
| b54420f8aa | |||
| 0b3b1391c1 | |||
| 64daba34de | |||
| 87157d3200 | |||
| 95f181352f | |||
| e32a2f339d | |||
| 9f4968ed05 | |||
| b6cfa2db0b | |||
| 26054d1ab0 | |||
| f2b522211f | |||
| 49da92b6a3 | |||
| daa109e2ec | |||
| ca0448260f | |||
| 2ab0101b8a | |||
| 478aa250a0 | |||
| 5cc4d0a2ca | |||
| 602ac903ff | |||
| af3a26ab06 | |||
| 1cecf52883 | |||
| 88905ea20d | |||
| 7df9db3bb7 | |||
| bd9c05a909 | |||
| 94c04821ff | |||
| 776811bdb8 | |||
| de4ff4b44b | |||
| f6e96a24d4 | |||
| daea92397f | |||
| 5efb385e64 | |||
| b388ff60dd | |||
| 412f5f677b | |||
| 6a20987933 | |||
| 2ae62dec8c | |||
| d86bf8b205 | |||
| c77de8a6fb | |||
| 4e49f67ec5 | |||
| 54dbb9f52a | |||
| 34f649c3f5 | |||
| 5b01f31387 | |||
| 9150c81d92 | |||
| 4e1756d738 | |||
| 5c1ab7ecd5 | |||
| 7655329463 | |||
| 3d1bf28445 | |||
| b7c7823315 | |||
| caaa86d6cf | |||
| 46655f367e | |||
| 4b7292f431 | |||
| 83be0f2dac | |||
| 3c2831030b | |||
| 1f6ab1a0c9 | |||
| ce5869eeb0 | |||
| 1db9faf248 | |||
| 5d80d75051 | |||
| debba6545b | |||
| fab889009a | |||
| 5927b70dfe | |||
| dca55236c4 | |||
| 61b5b1edd8 | |||
| 22abcf9aef | |||
| 4fa27197a7 | |||
| acf82e7c6d | |||
| 29fdcf56eb | |||
| 186deb777f | |||
| 57cb8c7abf | |||
| 871539ab28 | |||
| aca5db817d | |||
| b2c1f978a4 | |||
| 50be08480d | |||
| 0101945708 | |||
| d41ca095ab | |||
| 143736e59b | |||
| e0cb7aa0a0 | |||
| 46c29b9175 | |||
| 1634e63506 | |||
| bb17ad8314 | |||
| 63486bf19c | |||
| d0bff584d7 | |||
| 8be5010f5d | |||
| 86761cd233 | |||
| 07cdef410e | |||
| e74406afd0 | |||
| fe4fd00429 | |||
| 1703b71563 | |||
| 1911ca728d | |||
| f4a73b79b8 | |||
| 289c35c829 | |||
| 749145be2a | |||
| 28eece3da1 | |||
| 2b8225f11c | |||
| 1e6767ac03 | |||
| 7a250aa8ea | |||
| 21bc8726d6 | |||
| df7ac9b308 | |||
| 43217bdf06 | |||
| a31945f8b6 | |||
| a0edf129eb | |||
| bcffa15c7d | |||
| f9d2641fd5 | |||
| 3b506e11a1 | |||
| ddee309e11 | |||
| 7e4884fcfa | |||
| 38a3d18256 | |||
| 6c74fee5e8 | |||
| 36e84a5209 | |||
| 1e97b2137b | |||
| 5e4e530c59 | |||
| 76b2d13b2c | |||
| 9b85a246d8 | |||
| 5a99407847 | |||
| 05bd246cab | |||
| 0127de5a7a | |||
| fcc99a67b6 | |||
| 8afff3359b | |||
| c57c2272a1 | |||
| 8ec23a0fcc | |||
| 8badae9d72 | |||
| 444e7eeb74 | |||
| cb6a5baac5 | |||
| 3b2a818f1e | |||
| 84d1122190 | |||
| 722dcae1b1 | |||
| 850a0de1b0 | |||
| 321e8a58ae | |||
| 46ba13b645 | |||
| 9d9e621efe | |||
| 5aa38833dd | |||
| 9aac24c18a | |||
| ea665d776d | |||
| 7a2fc2ff09 | |||
| 52849819de | |||
| bcdc991838 | |||
| 589f17f48b | |||
| 9366308285 | |||
| 12b996b07d | |||
| 656d08f075 | |||
| 6e9a19212f | |||
| 83ff8e4e98 | |||
| cbdaafd409 | |||
| e5c505b71a | |||
| af8dc44469 | |||
| 955602bdbd | |||
| b62662296e | |||
| a3b737ed00 | |||
| 61d1fde797 | |||
| 0bb309f433 | |||
| e8630f3409 | |||
| c250817284 | |||
| 77bdbf7e37 | |||
| 2fe7ab1f92 | |||
| 6becfe2508 | |||
| dfcd8374c7 | |||
| ae5dd54115 | |||
| e088892fc1 | |||
| 152914a752 | |||
| 0f3f979d16 | |||
| f8875ea970 | |||
| c9375167fd | |||
| 48a6bd6ce7 | |||
| e15f123486 | |||
| 2bfff7fc37 | |||
| 1880e109c3 | |||
| 611d3e5551 | |||
| fa2a58b9db | |||
| 83ca2c0cff | |||
| dc16f3cffb | |||
| f2b3263fff | |||
| 1d890a8952 | |||
| 3ccaefe38f | |||
| 300f4585be | |||
| a0f5c16627 | |||
| b5a86dec92 | |||
| c55c64427c | |||
| a4fcd301f2 | |||
| ec4558dc14 | |||
| d6c623be40 | |||
| 86da45ba15 | |||
| 6a60298024 | |||
| f8b448e949 | |||
| 52a0e6f900 | |||
| 3d2fb3af3d | |||
| 4f38079042 | |||
| 3008d97f93 | |||
| f0c758498b | |||
| bc2deef951 | |||
| 7322a0d3c5 | |||
| 7227c30917 | |||
| 7e7f55cb23 | |||
| 84f20cd0a7 | |||
| 3d8f1fb00f | |||
| e06115c06f | |||
| 69850cad27 | |||
| bbbc95f773 | |||
| 83f7996c79 | |||
| 0544dfe090 | |||
| eacfd77d6a | |||
| 89338c91a9 | |||
| 51641ece72 | |||
| 06d267f04e | |||
| dfa6b4bd42 | |||
| 099a97740e | |||
| daea3f0eae | |||
| 8974da2a5a | |||
| ab236c7741 | |||
| 61adf2dd25 | |||
| 61894af0bd | |||
| c74666a883 | |||
| 976b4f3533 | |||
| 100da83913 | |||
| e881c70a93 | |||
| fea237c065 | |||
| 8b11ee9ee2 | |||
| 1cf5264163 | |||
| c68c6faa03 | |||
| 6dd5c85ed0 | |||
| d133ab60ee | |||
| 5d6e143ff4 | |||
| 4e2fb308f6 | |||
| 5f765fc8ce | |||
| 6b456b6157 | |||
| 000b563e83 | |||
| c571ee1f95 | |||
| 899f6308d9 | |||
| a580d6d6fc | |||
| 66eddf99af | |||
| e94ec80df4 | |||
| 2861e570fd | |||
| ee7a1e8b7e |
@@ -0,0 +1,20 @@
|
||||
name: Await HTTP Resource
|
||||
description: 'Waits for an HTTP resource to be available (a HEAD request succeeds)'
|
||||
inputs:
|
||||
url:
|
||||
description: 'URL of the resource to await'
|
||||
required: true
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Await HTTP resource
|
||||
shell: bash
|
||||
run: |
|
||||
url=${{ inputs.url }}
|
||||
echo "Waiting for $url"
|
||||
until curl --fail --head --silent ${{ inputs.url }} > /dev/null
|
||||
do
|
||||
echo "."
|
||||
sleep 60
|
||||
done
|
||||
echo "$url is available"
|
||||
@@ -0,0 +1,61 @@
|
||||
name: 'Build'
|
||||
description: 'Builds the project, optionally publishing it to a local deployment repository'
|
||||
inputs:
|
||||
develocity-access-key:
|
||||
description: 'Access key for authentication with ge.spring.io'
|
||||
required: false
|
||||
java-distribution:
|
||||
description: 'Java distribution to use'
|
||||
required: false
|
||||
default: 'liberica'
|
||||
java-early-access:
|
||||
description: 'Whether the Java version is in early access'
|
||||
required: false
|
||||
default: 'false'
|
||||
java-toolchain:
|
||||
description: 'Whether a Java toolchain should be used'
|
||||
required: false
|
||||
default: 'false'
|
||||
java-version:
|
||||
description: 'Java version to compile and test with'
|
||||
required: false
|
||||
default: '17'
|
||||
publish:
|
||||
description: 'Whether to publish artifacts ready for deployment to Artifactory'
|
||||
required: false
|
||||
default: 'false'
|
||||
outputs:
|
||||
build-scan-url:
|
||||
description: 'URL, if any, of the build scan produced by the build'
|
||||
value: ${{ (inputs.publish == 'true' && steps.publish.outputs.build-scan-url) || steps.build.outputs.build-scan-url }}
|
||||
version:
|
||||
description: 'Version that was built'
|
||||
value: ${{ steps.read-version.outputs.version }}
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Prepare Gradle Build
|
||||
uses: ./.github/actions/prepare-gradle-build
|
||||
with:
|
||||
develocity-access-key: ${{ inputs.develocity-access-key }}
|
||||
java-distribution: ${{ inputs.java-distribution }}
|
||||
java-early-access: ${{ inputs.java-early-access }}
|
||||
java-toolchain: ${{ inputs.java-toolchain }}
|
||||
java-version: ${{ inputs.java-version }}
|
||||
- name: Build
|
||||
id: build
|
||||
if: ${{ inputs.publish == 'false' }}
|
||||
shell: bash
|
||||
run: ./gradlew check antora
|
||||
- name: Publish
|
||||
id: publish
|
||||
if: ${{ inputs.publish == 'true' }}
|
||||
shell: bash
|
||||
run: ./gradlew -PdeploymentRepository=$(pwd)/deployment-repository build publishAllPublicationsToDeploymentRepository
|
||||
- name: Read Version From gradle.properties
|
||||
id: read-version
|
||||
shell: bash
|
||||
run: |
|
||||
version=$(sed -n 's/version=\(.*\)/\1/p' gradle.properties)
|
||||
echo "Version is $version"
|
||||
echo "version=$version" >> $GITHUB_OUTPUT
|
||||
@@ -0,0 +1,23 @@
|
||||
name: Create GitHub Release
|
||||
description: 'Create the release on GitHub with a changelog'
|
||||
inputs:
|
||||
milestone:
|
||||
description: 'Name of the GitHub milestone for which a release will be created'
|
||||
required: true
|
||||
token:
|
||||
description: 'Token to use for authentication with GitHub'
|
||||
required: true
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Generate Changelog
|
||||
uses: spring-io/github-changelog-generator@185319ad7eaa75b0e8e72e4b6db19c8b2cb8c4c1 #v0.0.11
|
||||
with:
|
||||
config-file: .github/actions/create-github-release/changelog-generator.yml
|
||||
milestone: ${{ inputs.milestone }}
|
||||
token: ${{ inputs.token }}
|
||||
- name: Create GitHub Release
|
||||
shell: bash
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ inputs.token }}
|
||||
run: gh release create ${{ format('v{0}', inputs.milestone) }} --notes-file changelog.md
|
||||
+9
-1
@@ -17,4 +17,12 @@ changelog:
|
||||
- "type: dependency-upgrade"
|
||||
contributors:
|
||||
exclude:
|
||||
names: ["bclozel", "jhoeller", "poutsma", "rstoyanchev", "sbrannen", "sdeleuze", "snicoll", "simonbasle"]
|
||||
names:
|
||||
- "bclozel"
|
||||
- "jhoeller"
|
||||
- "poutsma"
|
||||
- "rstoyanchev"
|
||||
- "sbrannen"
|
||||
- "sdeleuze"
|
||||
- "simonbasle"
|
||||
- "snicoll"
|
||||
@@ -0,0 +1,53 @@
|
||||
name: Prepare Gradle Build
|
||||
description: 'Prepares a Gradle build. Sets up Java and Gradle and configures Gradle properties'
|
||||
inputs:
|
||||
develocity-access-key:
|
||||
description: 'Access key for authentication with ge.spring.io'
|
||||
required: false
|
||||
java-distribution:
|
||||
description: 'Java distribution to use'
|
||||
required: false
|
||||
default: 'liberica'
|
||||
java-early-access:
|
||||
description: 'Whether the Java version is in early access. When true, forces java-distribution to temurin'
|
||||
required: false
|
||||
default: 'false'
|
||||
java-toolchain:
|
||||
description: 'Whether a Java toolchain should be used'
|
||||
required: false
|
||||
default: 'false'
|
||||
java-version:
|
||||
description: 'Java version to use for the build'
|
||||
required: false
|
||||
default: '17'
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Set Up Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: ${{ inputs.java-early-access == 'true' && 'temurin' || (inputs.java-distribution || 'liberica') }}
|
||||
java-version: |
|
||||
${{ inputs.java-early-access == 'true' && format('{0}-ea', inputs.java-version) || inputs.java-version }}
|
||||
${{ inputs.java-toolchain == 'true' && '17' || '' }}
|
||||
- name: Set Up Gradle
|
||||
uses: gradle/actions/setup-gradle@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0
|
||||
with:
|
||||
cache-read-only: false
|
||||
develocity-access-key: ${{ inputs.develocity-access-key }}
|
||||
- name: Configure Gradle Properties
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p $HOME/.gradle
|
||||
echo 'systemProp.user.name=spring-builds+github' >> $HOME/.gradle/gradle.properties
|
||||
echo 'systemProp.org.gradle.internal.launcher.welcomeMessageEnabled=false' >> $HOME/.gradle/gradle.properties
|
||||
echo 'org.gradle.daemon=false' >> $HOME/.gradle/gradle.properties
|
||||
echo 'org.gradle.daemon=4' >> $HOME/.gradle/gradle.properties
|
||||
- name: Configure Toolchain Properties
|
||||
if: ${{ inputs.java-toolchain == 'true' }}
|
||||
shell: bash
|
||||
run: |
|
||||
echo toolchainVersion=${{ inputs.java-version }} >> $HOME/.gradle/gradle.properties
|
||||
echo systemProp.org.gradle.java.installations.auto-detect=false >> $HOME/.gradle/gradle.properties
|
||||
echo systemProp.org.gradle.java.installations.auto-download=false >> $HOME/.gradle/gradle.properties
|
||||
echo systemProp.org.gradle.java.installations.paths=${{ format('$JAVA_HOME_{0}_X64', inputs.java-version) }} >> $HOME/.gradle/gradle.properties
|
||||
@@ -0,0 +1,17 @@
|
||||
name: Print JVM thread dumps
|
||||
description: 'Prints a thread dump for all running JVMs'
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- if: ${{ runner.os == 'Linux' }}
|
||||
shell: bash
|
||||
run: |
|
||||
for jvm_pid in $(jps -q -J-XX:+PerfDisableSharedMem); do
|
||||
jcmd $jvm_pid Thread.print
|
||||
done
|
||||
- if: ${{ runner.os == 'Windows' }}
|
||||
shell: powershell
|
||||
run: |
|
||||
foreach ($jvm_pid in $(jps -q -J-XX:+PerfDisableSharedMem)) {
|
||||
jcmd $jvm_pid Thread.print
|
||||
}
|
||||
@@ -1,33 +1,39 @@
|
||||
name: Send notification
|
||||
description: Sends a Google Chat message as a notification of the job's outcome
|
||||
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
|
||||
build-scan-url:
|
||||
description: 'URL of the build scan to include in the notification'
|
||||
required: false
|
||||
run-name:
|
||||
description: 'Name of the run to include in the notification'
|
||||
required: false
|
||||
default: ${{ format('{0} {1}', github.ref_name, github.job) }}
|
||||
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) }}
|
||||
webhook-url:
|
||||
description: 'Google Chat Webhook URL'
|
||||
required: true
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- shell: bash
|
||||
- name: Prepare Variables
|
||||
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
|
||||
- name: Success Notification
|
||||
if: ${{ inputs.status == 'success' }}
|
||||
shell: bash
|
||||
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
|
||||
- name: Failure Notification
|
||||
if: ${{ inputs.status == 'failure' }}
|
||||
shell: bash
|
||||
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
|
||||
- name: Cancel Notification
|
||||
if: ${{ inputs.status == 'cancelled' }}
|
||||
shell: bash
|
||||
run: |
|
||||
curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<${{ env.RUN_URL }}|${{ inputs.run-name }}> was cancelled"}' || true
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
name: Sync to Maven Central
|
||||
description: 'Syncs a release to Maven Central and waits for it to be available for use'
|
||||
inputs:
|
||||
jfrog-cli-config-token:
|
||||
description: 'Config token for the JFrog CLI'
|
||||
required: true
|
||||
ossrh-s01-staging-profile:
|
||||
description: 'Staging profile to use when syncing to Central'
|
||||
required: true
|
||||
ossrh-s01-token-password:
|
||||
description: 'Password for authentication with s01.oss.sonatype.org'
|
||||
required: true
|
||||
ossrh-s01-token-username:
|
||||
description: 'Username for authentication with s01.oss.sonatype.org'
|
||||
required: true
|
||||
spring-framework-version:
|
||||
description: 'Version of Spring Framework that is being synced to Central'
|
||||
required: true
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Set Up JFrog CLI
|
||||
uses: jfrog/setup-jfrog-cli@9fe0f98bd45b19e6e931d457f4e98f8f84461fb5 # v4.4.1
|
||||
env:
|
||||
JF_ENV_SPRING: ${{ inputs.jfrog-cli-config-token }}
|
||||
- name: Download Release Artifacts
|
||||
shell: bash
|
||||
run: jf rt download --spec ${{ format('{0}/artifacts.spec', github.action_path) }} --spec-vars 'buildName=${{ format('spring-framework-{0}', inputs.spring-framework-version) }};buildNumber=${{ github.run_number }}'
|
||||
- name: Sync
|
||||
uses: spring-io/nexus-sync-action@42477a2230a2f694f9eaa4643fa9e76b99b7ab84 # v0.0.1
|
||||
with:
|
||||
close: true
|
||||
create: true
|
||||
generate-checksums: true
|
||||
password: ${{ inputs.ossrh-s01-token-password }}
|
||||
release: true
|
||||
staging-profile-name: ${{ inputs.ossrh-s01-staging-profile }}
|
||||
upload: true
|
||||
username: ${{ inputs.ossrh-s01-token-username }}
|
||||
- name: Await
|
||||
uses: ./.github/actions/await-http-resource
|
||||
with:
|
||||
url: ${{ format('https://repo.maven.apache.org/maven2/org/springframework/spring-context/{0}/spring-context-{0}.jar', inputs.spring-framework-version) }}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"aql": {
|
||||
"items.find": {
|
||||
"$and": [
|
||||
{
|
||||
"@build.name": "${buildName}",
|
||||
"@build.number": "${buildNumber}",
|
||||
"path": {
|
||||
"$nmatch": "org/springframework/framework-api/*"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"target": "nexus/"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
name: Backport Bot
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [labeled]
|
||||
@@ -29,6 +28,6 @@ jobs:
|
||||
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) }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
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,4 +1,4 @@
|
||||
name: Build and deploy snapshot
|
||||
name: Build and Deploy Snapshot
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
@@ -7,56 +7,52 @@ concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
jobs:
|
||||
build-and-deploy-snapshot:
|
||||
name: Build and Deploy Snapshot
|
||||
if: ${{ github.repository == 'spring-projects/spring-framework' }}
|
||||
name: Build and deploy snapshot
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- name: Set up Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'liberica'
|
||||
java-version: 17
|
||||
- name: Check out code
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
|
||||
- name: Build and Publish
|
||||
id: build-and-publish
|
||||
uses: ./.github/actions/build
|
||||
with:
|
||||
cache-read-only: false
|
||||
- name: Configure Gradle properties
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p $HOME/.gradle
|
||||
echo 'systemProp.user.name=spring-builds+github' >> $HOME/.gradle/gradle.properties
|
||||
echo 'systemProp.org.gradle.internal.launcher.welcomeMessageEnabled=false' >> $HOME/.gradle/gradle.properties
|
||||
echo 'org.gradle.daemon=false' >> $HOME/.gradle/gradle.properties
|
||||
echo 'org.gradle.daemon=4' >> $HOME/.gradle/gradle.properties
|
||||
- name: Build and publish
|
||||
id: build
|
||||
env:
|
||||
CI: 'true'
|
||||
GRADLE_ENTERPRISE_URL: 'https://ge.spring.io'
|
||||
DEVELOCITY_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||
run: ./gradlew -PdeploymentRepository=$(pwd)/deployment-repository build publishAllPublicationsToDeploymentRepository
|
||||
develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
|
||||
publish: true
|
||||
- name: Deploy
|
||||
uses: spring-io/artifactory-deploy-action@v0.0.1
|
||||
uses: spring-io/artifactory-deploy-action@26bbe925a75f4f863e1e529e85be2d0093cac116 # v0.0.1
|
||||
with:
|
||||
uri: 'https://repo.spring.io'
|
||||
username: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
password: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
build-name: ${{ format('spring-framework-{0}', 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
|
||||
build-name: 'spring-framework-6.1.x'
|
||||
folder: 'deployment-repository'
|
||||
password: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
repository: 'libs-snapshot-local'
|
||||
signing-key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
signing-passphrase: ${{ secrets.GPG_PASSPHRASE }}
|
||||
uri: 'https://repo.spring.io'
|
||||
username: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
- name: Send Notification
|
||||
if: always()
|
||||
uses: ./.github/actions/send-notification
|
||||
with:
|
||||
webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
|
||||
build-scan-url: ${{ steps.build-and-publish.outputs.build-scan-url }}
|
||||
run-name: ${{ format('{0} | Linux | Java 17', github.ref_name) }}
|
||||
status: ${{ job.status }}
|
||||
build-scan-url: ${{ steps.build.outputs.build-scan-url }}
|
||||
run-name: ${{ format('{0} | Linux | Java 17', github.ref_name) }}
|
||||
webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
|
||||
outputs:
|
||||
version: ${{ steps.build-and-publish.outputs.version }}
|
||||
verify:
|
||||
name: Verify
|
||||
needs: build-and-deploy-snapshot
|
||||
uses: ./.github/workflows/verify.yml
|
||||
secrets:
|
||||
google-chat-webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
|
||||
repository-password: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
repository-username: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
with:
|
||||
version: ${{ needs.build-and-deploy-snapshot.outputs.version }}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
name: Build Pull Request
|
||||
on: pull_request
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
build:
|
||||
name: Build Pull Request
|
||||
if: ${{ github.repository == 'spring-projects/spring-framework' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- name: Set Up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'liberica'
|
||||
java-version: '17'
|
||||
- name: Check Out
|
||||
uses: actions/checkout@v4
|
||||
- name: Validate Gradle Wrapper
|
||||
uses: gradle/actions/wrapper-validation@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0
|
||||
- name: Set Up Gradle
|
||||
uses: gradle/actions/setup-gradle@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0
|
||||
- name: Build
|
||||
env:
|
||||
CI: 'true'
|
||||
GRADLE_ENTERPRISE_URL: 'https://ge.spring.io'
|
||||
run: ./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --no-parallel --continue build
|
||||
- name: Print JVM Thread Dumps When Cancelled
|
||||
if: cancelled()
|
||||
uses: ./.github/actions/print-jvm-thread-dumps
|
||||
- name: Upload Build Reports
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: build-reports
|
||||
path: '**/build/reports/'
|
||||
+21
-41
@@ -7,7 +7,10 @@ concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
jobs:
|
||||
ci:
|
||||
name: '${{ matrix.os.name}} | Java ${{ matrix.java.version}}'
|
||||
if: ${{ github.repository == 'spring-projects/spring-framework' }}
|
||||
runs-on: ${{ matrix.os.id }}
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
@@ -18,61 +21,38 @@ jobs:
|
||||
toolchain: false
|
||||
- version: 21
|
||||
toolchain: true
|
||||
- version: 22
|
||||
toolchain: true
|
||||
- version: 23
|
||||
toolchain: true
|
||||
exclude:
|
||||
- os:
|
||||
name: Linux
|
||||
java:
|
||||
version: 17
|
||||
name: '${{ matrix.os.name}} | Java ${{ matrix.java.version}}'
|
||||
runs-on: ${{ matrix.os.id }}
|
||||
steps:
|
||||
- name: Set up Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'liberica'
|
||||
java-version: |
|
||||
${{ matrix.java.version }}
|
||||
${{ matrix.java.toolchain && '17' || '' }}
|
||||
- name: Prepare Windows runner
|
||||
if: ${{ runner.os == 'Windows' }}
|
||||
run: |
|
||||
git config --global core.autocrlf true
|
||||
git config --global core.longPaths true
|
||||
Stop-Service -name Docker
|
||||
- name: Check out code
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
|
||||
with:
|
||||
cache-read-only: false
|
||||
- name: Configure Gradle properties
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p $HOME/.gradle
|
||||
echo 'systemProp.user.name=spring-builds+github' >> $HOME/.gradle/gradle.properties
|
||||
echo 'systemProp.org.gradle.internal.launcher.welcomeMessageEnabled=false' >> $HOME/.gradle/gradle.properties
|
||||
echo 'org.gradle.daemon=false' >> $HOME/.gradle/gradle.properties
|
||||
echo 'org.gradle.daemon=4' >> $HOME/.gradle/gradle.properties
|
||||
- name: Configure toolchain properties
|
||||
if: ${{ matrix.java.toolchain }}
|
||||
shell: bash
|
||||
run: |
|
||||
echo toolchainVersion=${{ matrix.java.version }} >> $HOME/.gradle/gradle.properties
|
||||
echo systemProp.org.gradle.java.installations.auto-detect=false >> $HOME/.gradle/gradle.properties
|
||||
echo systemProp.org.gradle.java.installations.auto-download=false >> $HOME/.gradle/gradle.properties
|
||||
echo systemProp.org.gradle.java.installations.paths=${{ format('$JAVA_HOME_{0}_X64', matrix.java.version) }} >> $HOME/.gradle/gradle.properties
|
||||
- name: Build
|
||||
id: build
|
||||
env:
|
||||
CI: 'true'
|
||||
GRADLE_ENTERPRISE_URL: 'https://ge.spring.io'
|
||||
DEVELOCITY_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||
run: ./gradlew check antora
|
||||
- name: Send notification
|
||||
uses: ./.github/actions/send-notification
|
||||
if: always()
|
||||
uses: ./.github/actions/build
|
||||
with:
|
||||
develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
|
||||
java-early-access: ${{ matrix.java.early-access || 'false' }}
|
||||
java-distribution: ${{ matrix.java.distribution }}
|
||||
java-toolchain: ${{ matrix.java.toolchain }}
|
||||
java-version: ${{ matrix.java.version }}
|
||||
- name: Send Notification
|
||||
if: always()
|
||||
uses: ./.github/actions/send-notification
|
||||
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) }}
|
||||
run-name: ${{ format('{0} | {1} | Java {2}', github.ref_name, matrix.os.name, matrix.java.version) }}
|
||||
status: ${{ job.status }}
|
||||
webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
|
||||
|
||||
@@ -14,14 +14,15 @@ permissions:
|
||||
actions: write
|
||||
jobs:
|
||||
build:
|
||||
name: Dispatch docs deployment
|
||||
if: ${{ github.repository == 'spring-projects/spring-framework' }}
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository_owner == 'spring-projects'
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: docs-build
|
||||
fetch-depth: 1
|
||||
ref: docs-build
|
||||
- name: Dispatch (partial build)
|
||||
if: github.ref_type == 'branch'
|
||||
env:
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
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
|
||||
@@ -0,0 +1,94 @@
|
||||
name: Release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v6.1.[0-9]+
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
jobs:
|
||||
build-and-stage-release:
|
||||
name: Build and Stage Release
|
||||
if: ${{ github.repository == 'spring-projects/spring-framework' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@v4
|
||||
- name: Build and Publish
|
||||
id: build-and-publish
|
||||
uses: ./.github/actions/build
|
||||
with:
|
||||
develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
|
||||
publish: true
|
||||
- name: Stage Release
|
||||
uses: spring-io/artifactory-deploy-action@26bbe925a75f4f863e1e529e85be2d0093cac116 # v0.0.1
|
||||
with:
|
||||
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
|
||||
build-name: ${{ format('spring-framework-{0}', steps.build-and-publish.outputs.version)}}
|
||||
folder: 'deployment-repository'
|
||||
password: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
repository: 'libs-staging-local'
|
||||
signing-key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
signing-passphrase: ${{ secrets.GPG_PASSPHRASE }}
|
||||
uri: 'https://repo.spring.io'
|
||||
username: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
outputs:
|
||||
version: ${{ steps.build-and-publish.outputs.version }}
|
||||
verify:
|
||||
name: Verify
|
||||
needs: build-and-stage-release
|
||||
uses: ./.github/workflows/verify.yml
|
||||
secrets:
|
||||
google-chat-webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
|
||||
repository-password: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
repository-username: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
with:
|
||||
staging: true
|
||||
version: ${{ needs.build-and-stage-release.outputs.version }}
|
||||
sync-to-maven-central:
|
||||
name: Sync to Maven Central
|
||||
needs:
|
||||
- build-and-stage-release
|
||||
- verify
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Sync to Maven Central
|
||||
uses: ./.github/actions/sync-to-maven-central
|
||||
with:
|
||||
jfrog-cli-config-token: ${{ secrets.JF_ARTIFACTORY_SPRING }}
|
||||
ossrh-s01-staging-profile: ${{ secrets.OSSRH_S01_STAGING_PROFILE }}
|
||||
ossrh-s01-token-password: ${{ secrets.OSSRH_S01_TOKEN_PASSWORD }}
|
||||
ossrh-s01-token-username: ${{ secrets.OSSRH_S01_TOKEN_USERNAME }}
|
||||
spring-framework-version: ${{ needs.build-and-stage-release.outputs.version }}
|
||||
promote-release:
|
||||
name: Promote Release
|
||||
needs:
|
||||
- build-and-stage-release
|
||||
- sync-to-maven-central
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up JFrog CLI
|
||||
uses: jfrog/setup-jfrog-cli@9fe0f98bd45b19e6e931d457f4e98f8f84461fb5 # v4.4.1
|
||||
env:
|
||||
JF_ENV_SPRING: ${{ secrets.JF_ARTIFACTORY_SPRING }}
|
||||
- name: Promote build
|
||||
run: jfrog rt build-promote ${{ format('spring-framework-{0}', needs.build-and-stage-release.outputs.version)}} ${{ github.run_number }} libs-release-local
|
||||
create-github-release:
|
||||
name: Create GitHub Release
|
||||
needs:
|
||||
- build-and-stage-release
|
||||
- promote-release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Create GitHub Release
|
||||
uses: ./.github/actions/create-github-release
|
||||
with:
|
||||
milestone: ${{ needs.build-and-stage-release.outputs.version }}
|
||||
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
@@ -0,0 +1,77 @@
|
||||
name: Verify
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
staging:
|
||||
description: 'Whether the release to verify is in the staging repository'
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
version:
|
||||
description: 'Version to verify'
|
||||
required: true
|
||||
type: string
|
||||
secrets:
|
||||
google-chat-webhook-url:
|
||||
description: 'Google Chat Webhook URL'
|
||||
required: true
|
||||
repository-password:
|
||||
description: 'Password for authentication with the repository'
|
||||
required: false
|
||||
repository-username:
|
||||
description: 'Username for authentication with the repository'
|
||||
required: false
|
||||
token:
|
||||
description: 'Token to use for authentication with GitHub'
|
||||
required: true
|
||||
jobs:
|
||||
verify:
|
||||
name: Verify
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check Out Release Verification Tests
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: 'v0.0.2'
|
||||
repository: spring-projects/spring-framework-release-verification
|
||||
token: ${{ secrets.token }}
|
||||
- name: Check Out Send Notification Action
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: send-notification
|
||||
sparse-checkout: .github/actions/send-notification
|
||||
- name: Set Up Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'liberica'
|
||||
java-version: 17
|
||||
- name: Set Up Gradle
|
||||
uses: gradle/actions/setup-gradle@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0
|
||||
with:
|
||||
cache-read-only: false
|
||||
- name: Configure Gradle Properties
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p $HOME/.gradle
|
||||
echo 'org.gradle.daemon=false' >> $HOME/.gradle/gradle.properties
|
||||
- name: Run Release Verification Tests
|
||||
env:
|
||||
RVT_OSS_REPOSITORY_PASSWORD: ${{ secrets.repository-password }}
|
||||
RVT_OSS_REPOSITORY_USERNAME: ${{ secrets.repository-username }}
|
||||
RVT_RELEASE_TYPE: oss
|
||||
RVT_STAGING: ${{ inputs.staging }}
|
||||
RVT_VERSION: ${{ inputs.version }}
|
||||
run: ./gradlew spring-framework-release-verification-tests:test
|
||||
- name: Upload Build Reports on Failure
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: build-reports
|
||||
path: '**/build/reports/'
|
||||
- name: Send Notification
|
||||
if: failure()
|
||||
uses: ./send-notification/.github/actions/send-notification
|
||||
with:
|
||||
run-name: ${{ format('{0} | Verification | {1}', github.ref_name, inputs.version) }}
|
||||
status: ${{ job.status }}
|
||||
webhook-url: ${{ secrets.google-chat-webhook-url }}
|
||||
@@ -1,3 +1,3 @@
|
||||
# Enable auto-env through the sdkman_auto_env config
|
||||
# Add key=value pairs of SDKs to use below
|
||||
java=17.0.11-librca
|
||||
java=17.0.13-librca
|
||||
|
||||
+4
-4
@@ -3,10 +3,10 @@ plugins {
|
||||
// kotlinVersion is managed in gradle.properties
|
||||
id 'org.jetbrains.kotlin.plugin.serialization' version "${kotlinVersion}" apply false
|
||||
id 'org.jetbrains.dokka' version '1.8.20'
|
||||
id 'org.unbroken-dome.xjc' version '2.0.0' apply false
|
||||
id 'com.github.ben-manes.versions' version '0.51.0'
|
||||
id 'com.github.johnrengelman.shadow' version '8.1.1' apply false
|
||||
id 'com.github.bjornvester.xjc' version '1.8.2' apply false
|
||||
id 'de.undercouch.download' version '5.4.0'
|
||||
id 'io.github.goooler.shadow' version '8.1.8' apply false
|
||||
id 'me.champeau.jmh' version '0.7.2' apply false
|
||||
id 'me.champeau.mrjar' version '0.1.1'
|
||||
}
|
||||
@@ -95,13 +95,13 @@ configure([rootProject] + javaProjects) { project ->
|
||||
"https://fasterxml.github.io/jackson-core/javadoc/2.14/",
|
||||
"https://fasterxml.github.io/jackson-databind/javadoc/2.14/",
|
||||
"https://fasterxml.github.io/jackson-dataformat-xml/javadoc/2.14/",
|
||||
"https://hc.apache.org/httpcomponents-client-5.2.x/current/httpclient5/apidocs/",
|
||||
"https://hc.apache.org/httpcomponents-client-5.4.x/current/httpclient5/apidocs/",
|
||||
"https://projectreactor.io/docs/test/release/api/",
|
||||
"https://junit.org/junit4/javadoc/4.13.2/",
|
||||
// TODO Uncomment link to JUnit 5 docs once we execute Gradle with Java 18+.
|
||||
// See https://github.com/spring-projects/spring-framework/issues/27497
|
||||
//
|
||||
// "https://junit.org/junit5/docs/5.10.2/api/",
|
||||
// "https://junit.org/junit5/docs/5.10.4/api/",
|
||||
"https://www.reactive-streams.org/reactive-streams-1.0.3-javadoc/",
|
||||
//"https://javadoc.io/static/io.rsocket/rsocket-core/1.1.1/",
|
||||
"https://r2dbc.io/spec/1.0.0.RELEASE/api/",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -50,7 +50,7 @@ public class CheckstyleConventions {
|
||||
project.getPlugins().apply(CheckstylePlugin.class);
|
||||
project.getTasks().withType(Checkstyle.class).forEach(checkstyle -> checkstyle.getMaxHeapSize().set("1g"));
|
||||
CheckstyleExtension checkstyle = project.getExtensions().getByType(CheckstyleExtension.class);
|
||||
checkstyle.setToolVersion("10.17.0");
|
||||
checkstyle.setToolVersion("10.21.2");
|
||||
checkstyle.getConfigDirectory().set(project.getRootProject().file("src/checkstyle"));
|
||||
String version = SpringJavaFormatPlugin.class.getPackage().getImplementationVersion();
|
||||
DependencySet checkstyleDependencies = project.getConfigurations().getByName("checkstyle").getDependencies();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -62,9 +62,11 @@ class TestConventions {
|
||||
if (project.hasProperty("testGroups")) {
|
||||
test.systemProperty("testGroups", project.getProperties().get("testGroups"));
|
||||
}
|
||||
test.jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED",
|
||||
test.jvmArgs(
|
||||
"--add-opens=java.base/java.lang=ALL-UNNAMED",
|
||||
"--add-opens=java.base/java.util=ALL-UNNAMED",
|
||||
"-Djava.locale.providers=COMPAT");
|
||||
"-Xshare:off"
|
||||
);
|
||||
}
|
||||
|
||||
private void configureTestRetryPlugin(Project project, Test test) {
|
||||
|
||||
@@ -26,7 +26,9 @@ import org.gradle.api.attributes.LibraryElements;
|
||||
import org.gradle.api.attributes.Usage;
|
||||
import org.gradle.api.attributes.java.TargetJvmVersion;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.plugins.jvm.JvmTestSuite;
|
||||
import org.gradle.api.tasks.testing.Test;
|
||||
import org.gradle.testing.base.TestingExtension;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
@@ -47,6 +49,8 @@ public class RuntimeHintsAgentPlugin implements Plugin<Project> {
|
||||
public void apply(Project project) {
|
||||
|
||||
project.getPlugins().withType(JavaPlugin.class, javaPlugin -> {
|
||||
TestingExtension testing = project.getExtensions().getByType(TestingExtension.class);
|
||||
JvmTestSuite jvmTestSuite = (JvmTestSuite) testing.getSuites().getByName("test");
|
||||
RuntimeHintsAgentExtension agentExtension = createRuntimeHintsAgentExtension(project);
|
||||
Test agentTest = project.getTasks().create(RUNTIMEHINTS_TEST_TASK, Test.class, test -> {
|
||||
test.useJUnitPlatform(options -> {
|
||||
@@ -55,6 +59,8 @@ public class RuntimeHintsAgentPlugin implements Plugin<Project> {
|
||||
test.include("**/*Tests.class", "**/*Test.class");
|
||||
test.systemProperty("java.awt.headless", "true");
|
||||
test.systemProperty("org.graalvm.nativeimage.imagecode", "runtime");
|
||||
test.setTestClassesDirs(jvmTestSuite.getSources().getOutput().getClassesDirs());
|
||||
test.setClasspath(jvmTestSuite.getSources().getRuntimeClasspath());
|
||||
test.getJvmArgumentProviders().add(createRuntimeHintsAgentArgumentProvider(project, agentExtension));
|
||||
});
|
||||
project.getTasks().getByName("check", task -> task.dependsOn(agentTest));
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
== Spring Framework Concourse pipeline
|
||||
|
||||
NOTE: CI is being migrated to GitHub Actions.
|
||||
|
||||
The Spring Framework uses https://concourse-ci.org/[Concourse] for its CI build and other automated tasks.
|
||||
The Spring team has a dedicated Concourse instance available at https://ci.spring.io with a build pipeline
|
||||
for https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-6.1.x[Spring Framework 6.1.x].
|
||||
|
||||
=== Setting up your development environment
|
||||
|
||||
If you're part of the Spring Framework project on GitHub, you can get access to CI management features.
|
||||
First, you need to go to https://ci.spring.io and install the client CLI for your platform (see bottom right of the screen).
|
||||
|
||||
You can then login with the instance using:
|
||||
|
||||
[source]
|
||||
----
|
||||
$ fly -t spring login -n spring-framework -c https://ci.spring.io
|
||||
----
|
||||
|
||||
Once logged in, you should get something like:
|
||||
|
||||
[source]
|
||||
----
|
||||
$ fly ts
|
||||
name url team expiry
|
||||
spring https://ci.spring.io spring-framework Wed, 25 Mar 2020 17:45:26 UTC
|
||||
----
|
||||
|
||||
=== Pipeline configuration and structure
|
||||
|
||||
The build pipelines are described in `pipeline.yml` file.
|
||||
|
||||
This file is listing Concourse resources, i.e. build inputs and outputs such as container images, artifact repositories, source repositories, notification services, etc.
|
||||
|
||||
It also describes jobs (a job is a sequence of inputs, tasks and outputs); jobs are organized by groups.
|
||||
|
||||
The `pipeline.yml` definition contains `((parameters))` which are loaded from the `parameters.yml` file or from our https://docs.cloudfoundry.org/credhub/[credhub instance].
|
||||
|
||||
You'll find in this folder the following resources:
|
||||
|
||||
* `pipeline.yml` the build pipeline
|
||||
* `parameters.yml` the build parameters used for the pipeline
|
||||
* `images/` holds the container images definitions used in this pipeline
|
||||
* `scripts/` holds the build scripts that ship within the CI container images
|
||||
* `tasks` contains the task definitions used in the main `pipeline.yml`
|
||||
|
||||
=== Updating the build pipeline
|
||||
|
||||
Updating files on the repository is not enough to update the build pipeline, as changes need to be applied.
|
||||
|
||||
The pipeline can be deployed using the following command:
|
||||
|
||||
[source]
|
||||
----
|
||||
$ fly -t spring set-pipeline -p spring-framework-6.1.x -c ci/pipeline.yml -l ci/parameters.yml
|
||||
----
|
||||
|
||||
NOTE: This assumes that you have credhub integration configured with the appropriate secrets.
|
||||
@@ -1,10 +0,0 @@
|
||||
logging:
|
||||
level:
|
||||
io.spring.concourse: DEBUG
|
||||
spring:
|
||||
main:
|
||||
banner-mode: off
|
||||
sonatype:
|
||||
exclude:
|
||||
- 'build-info\.json'
|
||||
- '.*\.zip'
|
||||
@@ -1,21 +0,0 @@
|
||||
== CI Images
|
||||
|
||||
These images are used by CI to run the actual builds.
|
||||
|
||||
To build the image locally run the following from this directory:
|
||||
|
||||
----
|
||||
$ docker build --no-cache -f <image-folder>/Dockerfile .
|
||||
----
|
||||
|
||||
For example
|
||||
|
||||
----
|
||||
$ docker build --no-cache -f spring-framework-ci-image/Dockerfile .
|
||||
----
|
||||
|
||||
To test run:
|
||||
|
||||
----
|
||||
$ docker run -it --entrypoint /bin/bash <SHA>
|
||||
----
|
||||
@@ -1,12 +0,0 @@
|
||||
FROM ubuntu:jammy-20240125
|
||||
|
||||
ADD setup.sh /setup.sh
|
||||
ADD get-jdk-url.sh /get-jdk-url.sh
|
||||
RUN ./setup.sh
|
||||
|
||||
ENV JAVA_HOME /opt/openjdk/java17
|
||||
ENV JDK17 /opt/openjdk/java17
|
||||
ENV JDK21 /opt/openjdk/java21
|
||||
ENV JDK23 /opt/openjdk/java23
|
||||
|
||||
ENV PATH $JAVA_HOME/bin:$PATH
|
||||
@@ -1,17 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
case "$1" in
|
||||
java17)
|
||||
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.10%2B13/bellsoft-jdk17.0.10+13-linux-amd64.tar.gz"
|
||||
;;
|
||||
java21)
|
||||
echo "https://github.com/bell-sw/Liberica/releases/download/21.0.2%2B14/bellsoft-jdk21.0.2+14-linux-amd64.tar.gz"
|
||||
;;
|
||||
java23)
|
||||
echo "https://download.java.net/java/early_access/jdk23/17/GPL/openjdk-23-ea+17_linux-x64_bin.tar.gz"
|
||||
;;
|
||||
*)
|
||||
echo $"Unknown java version"
|
||||
exit 1
|
||||
esac
|
||||
@@ -1,40 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -ex
|
||||
|
||||
###########################################################
|
||||
# UTILS
|
||||
###########################################################
|
||||
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update
|
||||
apt-get install --no-install-recommends -y tzdata ca-certificates net-tools libxml2-utils git curl libudev1 libxml2-utils iptables iproute2 jq fontconfig
|
||||
ln -fs /usr/share/zoneinfo/UTC /etc/localtime
|
||||
dpkg-reconfigure --frontend noninteractive tzdata
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
curl https://raw.githubusercontent.com/spring-io/concourse-java-scripts/v0.0.4/concourse-java.sh > /opt/concourse-java.sh
|
||||
|
||||
###########################################################
|
||||
# JAVA
|
||||
###########################################################
|
||||
|
||||
mkdir -p /opt/openjdk
|
||||
pushd /opt/openjdk > /dev/null
|
||||
for jdk in java17 java21 java23
|
||||
do
|
||||
JDK_URL=$( /get-jdk-url.sh $jdk )
|
||||
mkdir $jdk
|
||||
pushd $jdk > /dev/null
|
||||
curl -L ${JDK_URL} | tar zx --strip-components=1
|
||||
test -f bin/java
|
||||
test -f bin/javac
|
||||
popd > /dev/null
|
||||
done
|
||||
popd
|
||||
|
||||
###########################################################
|
||||
# GRADLE ENTERPRISE
|
||||
###########################################################
|
||||
cd /
|
||||
mkdir ~/.gradle
|
||||
echo 'systemProp.user.name=concourse' > ~/.gradle/gradle.properties
|
||||
@@ -1,10 +0,0 @@
|
||||
github-repo: "https://github.com/spring-projects/spring-framework.git"
|
||||
github-repo-name: "spring-projects/spring-framework"
|
||||
sonatype-staging-profile: "org.springframework"
|
||||
docker-hub-organization: "springci"
|
||||
artifactory-server: "https://repo.spring.io"
|
||||
branch: "6.1.x"
|
||||
milestone: "6.1.x"
|
||||
build-name: "spring-framework"
|
||||
pipeline-name: "spring-framework"
|
||||
concourse-url: "https://ci.spring.io"
|
||||
-293
@@ -1,293 +0,0 @@
|
||||
anchors:
|
||||
git-repo-resource-source: &git-repo-resource-source
|
||||
uri: ((github-repo))
|
||||
username: ((github-username))
|
||||
password: ((github-ci-release-token))
|
||||
branch: ((branch))
|
||||
gradle-enterprise-task-params: &gradle-enterprise-task-params
|
||||
DEVELOCITY_ACCESS_KEY: ((gradle_enterprise_secret_access_key))
|
||||
sonatype-task-params: &sonatype-task-params
|
||||
SONATYPE_USERNAME: ((s01-user-token))
|
||||
SONATYPE_PASSWORD: ((s01-user-token-password))
|
||||
SONATYPE_URL: ((sonatype-url))
|
||||
SONATYPE_STAGING_PROFILE: ((sonatype-staging-profile))
|
||||
artifactory-task-params: &artifactory-task-params
|
||||
ARTIFACTORY_SERVER: ((artifactory-server))
|
||||
ARTIFACTORY_USERNAME: ((artifactory-username))
|
||||
ARTIFACTORY_PASSWORD: ((artifactory-password))
|
||||
build-project-task-params: &build-project-task-params
|
||||
BRANCH: ((branch))
|
||||
<<: *gradle-enterprise-task-params
|
||||
docker-resource-source: &docker-resource-source
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
changelog-task-params: &changelog-task-params
|
||||
name: generated-changelog/tag
|
||||
tag: generated-changelog/tag
|
||||
body: generated-changelog/changelog.md
|
||||
github-task-params: &github-task-params
|
||||
GITHUB_USERNAME: ((github-username))
|
||||
GITHUB_TOKEN: ((github-ci-release-token))
|
||||
|
||||
resource_types:
|
||||
- name: registry-image
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: concourse/registry-image-resource
|
||||
tag: 1.8.0
|
||||
- name: artifactory-resource
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: springio/artifactory-resource
|
||||
tag: 0.0.18
|
||||
- name: github-release
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: concourse/github-release-resource
|
||||
tag: 1.8.0
|
||||
- name: github-status-resource
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: dpb587/github-status-resource
|
||||
tag: master
|
||||
resources:
|
||||
- name: git-repo
|
||||
type: git
|
||||
icon: github
|
||||
source:
|
||||
<<: *git-repo-resource-source
|
||||
- name: ci-images-git-repo
|
||||
type: git
|
||||
icon: github
|
||||
source:
|
||||
uri: ((github-repo))
|
||||
branch: ((branch))
|
||||
paths: ["ci/images/*"]
|
||||
- name: ci-image
|
||||
type: registry-image
|
||||
icon: docker
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: ((docker-hub-organization))/spring-framework-ci
|
||||
tag: ((milestone))
|
||||
- name: artifactory-repo
|
||||
type: artifactory-resource
|
||||
icon: package-variant
|
||||
source:
|
||||
uri: ((artifactory-server))
|
||||
username: ((artifactory-username))
|
||||
password: ((artifactory-password))
|
||||
build_name: ((build-name))
|
||||
- name: github-pre-release
|
||||
type: github-release
|
||||
icon: briefcase-download-outline
|
||||
source:
|
||||
owner: spring-projects
|
||||
repository: spring-framework
|
||||
access_token: ((github-ci-release-token))
|
||||
pre_release: true
|
||||
release: false
|
||||
- name: github-release
|
||||
type: github-release
|
||||
icon: briefcase-download
|
||||
source:
|
||||
owner: spring-projects
|
||||
repository: spring-framework
|
||||
access_token: ((github-ci-release-token))
|
||||
pre_release: false
|
||||
jobs:
|
||||
- name: build-ci-images
|
||||
plan:
|
||||
- get: git-repo
|
||||
- get: ci-images-git-repo
|
||||
trigger: true
|
||||
- task: build-ci-image
|
||||
privileged: true
|
||||
file: git-repo/ci/tasks/build-ci-image.yml
|
||||
output_mapping:
|
||||
image: ci-image
|
||||
vars:
|
||||
ci-image-name: ci-image
|
||||
<<: *docker-resource-source
|
||||
- put: ci-image
|
||||
params:
|
||||
image: ci-image/image.tar
|
||||
- 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
|
||||
signing_key: ((signing-key))
|
||||
signing_passphrase: ((signing-passphrase))
|
||||
repo: libs-staging-local
|
||||
folder: distribution-repository
|
||||
build_uri: "https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}"
|
||||
build_number: "${BUILD_PIPELINE_NAME}-${BUILD_JOB_NAME}-${BUILD_NAME}"
|
||||
disable_checksum_uploads: true
|
||||
threads: 8
|
||||
artifact_set:
|
||||
- include:
|
||||
- "/**/framework-api-*.zip"
|
||||
properties:
|
||||
"zip.name": "spring-framework"
|
||||
"zip.displayname": "Spring Framework"
|
||||
"zip.deployed": "false"
|
||||
- include:
|
||||
- "/**/framework-api-*-docs.zip"
|
||||
properties:
|
||||
"zip.type": "docs"
|
||||
- include:
|
||||
- "/**/framework-api-*-schema.zip"
|
||||
properties:
|
||||
"zip.type": "schema"
|
||||
get_params:
|
||||
threads: 8
|
||||
- put: git-repo
|
||||
params:
|
||||
repository: stage-git-repo
|
||||
- name: promote-milestone
|
||||
serial: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
trigger: false
|
||||
- get: artifactory-repo
|
||||
trigger: false
|
||||
passed: [stage-milestone]
|
||||
params:
|
||||
download_artifacts: false
|
||||
save_build_info: true
|
||||
- task: promote
|
||||
file: git-repo/ci/tasks/promote-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: M
|
||||
<<: *artifactory-task-params
|
||||
- task: generate-changelog
|
||||
file: git-repo/ci/tasks/generate-changelog.yml
|
||||
params:
|
||||
RELEASE_TYPE: M
|
||||
<<: *github-task-params
|
||||
<<: *docker-resource-source
|
||||
- put: github-pre-release
|
||||
params:
|
||||
<<: *changelog-task-params
|
||||
- name: stage-rc
|
||||
serial: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
trigger: false
|
||||
- task: stage
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/stage-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: RC
|
||||
<<: *gradle-enterprise-task-params
|
||||
- put: artifactory-repo
|
||||
params:
|
||||
<<: *artifactory-params
|
||||
- put: git-repo
|
||||
params:
|
||||
repository: stage-git-repo
|
||||
- name: promote-rc
|
||||
serial: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
trigger: false
|
||||
- get: artifactory-repo
|
||||
trigger: false
|
||||
passed: [stage-rc]
|
||||
params:
|
||||
download_artifacts: false
|
||||
save_build_info: true
|
||||
- task: promote
|
||||
file: git-repo/ci/tasks/promote-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: RC
|
||||
<<: *docker-resource-source
|
||||
<<: *artifactory-task-params
|
||||
- task: generate-changelog
|
||||
file: git-repo/ci/tasks/generate-changelog.yml
|
||||
params:
|
||||
RELEASE_TYPE: RC
|
||||
<<: *github-task-params
|
||||
- put: github-pre-release
|
||||
params:
|
||||
<<: *changelog-task-params
|
||||
- name: stage-release
|
||||
serial: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
trigger: false
|
||||
- task: stage
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/stage-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: RELEASE
|
||||
<<: *gradle-enterprise-task-params
|
||||
- put: artifactory-repo
|
||||
params:
|
||||
<<: *artifactory-params
|
||||
- put: git-repo
|
||||
params:
|
||||
repository: stage-git-repo
|
||||
- name: promote-release
|
||||
serial: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
trigger: false
|
||||
- get: artifactory-repo
|
||||
trigger: false
|
||||
passed: [stage-release]
|
||||
params:
|
||||
download_artifacts: true
|
||||
save_build_info: true
|
||||
- task: promote
|
||||
file: git-repo/ci/tasks/promote-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: RELEASE
|
||||
<<: *docker-resource-source
|
||||
<<: *artifactory-task-params
|
||||
<<: *sonatype-task-params
|
||||
- name: create-github-release
|
||||
serial: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
- get: artifactory-repo
|
||||
trigger: true
|
||||
passed: [promote-release]
|
||||
params:
|
||||
download_artifacts: false
|
||||
save_build_info: true
|
||||
- task: generate-changelog
|
||||
file: git-repo/ci/tasks/generate-changelog.yml
|
||||
params:
|
||||
RELEASE_TYPE: RELEASE
|
||||
<<: *docker-resource-source
|
||||
<<: *github-task-params
|
||||
- put: github-release
|
||||
params:
|
||||
<<: *changelog-task-params
|
||||
|
||||
groups:
|
||||
- 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"]
|
||||
@@ -1,2 +0,0 @@
|
||||
source /opt/concourse-java.sh
|
||||
setup_symlinks
|
||||
@@ -1,12 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
CONFIG_DIR=git-repo/ci/config
|
||||
version=$( cat artifactory-repo/build-info.json | jq -r '.buildInfo.modules[0].id' | sed 's/.*:.*:\(.*\)/\1/' )
|
||||
|
||||
java -jar /github-changelog-generator.jar \
|
||||
--spring.config.location=${CONFIG_DIR}/changelog-generator.yml \
|
||||
${version} generated-changelog/changelog.md
|
||||
|
||||
echo ${version} > generated-changelog/version
|
||||
echo v${version} > generated-changelog/tag
|
||||
@@ -1,17 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
CONFIG_DIR=git-repo/ci/config
|
||||
|
||||
version=$( cat artifactory-repo/build-info.json | jq -r '.buildInfo.modules[0].id' | sed 's/.*:.*:\(.*\)/\1/' )
|
||||
export BUILD_INFO_LOCATION=$(pwd)/artifactory-repo/build-info.json
|
||||
|
||||
java -jar /concourse-release-scripts.jar \
|
||||
--spring.config.location=${CONFIG_DIR}/release-scripts.yml \
|
||||
publishToCentral $RELEASE_TYPE $BUILD_INFO_LOCATION artifactory-repo || { exit 1; }
|
||||
|
||||
java -jar /concourse-release-scripts.jar \
|
||||
--spring.config.location=${CONFIG_DIR}/release-scripts.yml \
|
||||
promote $RELEASE_TYPE $BUILD_INFO_LOCATION || { exit 1; }
|
||||
|
||||
echo "Promotion complete"
|
||||
echo $version > version/version
|
||||
@@ -1,51 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
source $(dirname $0)/common.sh
|
||||
repository=$(pwd)/distribution-repository
|
||||
|
||||
pushd git-repo > /dev/null
|
||||
git fetch --tags --all > /dev/null
|
||||
popd > /dev/null
|
||||
|
||||
git clone git-repo stage-git-repo > /dev/null
|
||||
|
||||
pushd stage-git-repo > /dev/null
|
||||
|
||||
snapshotVersion=$( awk -F '=' '$1 == "version" { print $2 }' gradle.properties )
|
||||
if [[ $RELEASE_TYPE = "M" ]]; then
|
||||
stageVersion=$( get_next_milestone_release $snapshotVersion)
|
||||
nextVersion=$snapshotVersion
|
||||
elif [[ $RELEASE_TYPE = "RC" ]]; then
|
||||
stageVersion=$( get_next_rc_release $snapshotVersion)
|
||||
nextVersion=$snapshotVersion
|
||||
elif [[ $RELEASE_TYPE = "RELEASE" ]]; then
|
||||
stageVersion=$( get_next_release $snapshotVersion)
|
||||
nextVersion=$( bump_version_number $snapshotVersion)
|
||||
else
|
||||
echo "Unknown release type $RELEASE_TYPE" >&2; exit 1;
|
||||
fi
|
||||
|
||||
echo "Staging $stageVersion (next version will be $nextVersion)"
|
||||
sed -i "s/version=$snapshotVersion/version=$stageVersion/" gradle.properties
|
||||
|
||||
git config user.name "Spring Builds" > /dev/null
|
||||
git config user.email "spring-builds@users.noreply.github.com" > /dev/null
|
||||
git add gradle.properties > /dev/null
|
||||
git commit -m"Release v$stageVersion" > /dev/null
|
||||
git tag -a "v$stageVersion" -m"Release v$stageVersion" > /dev/null
|
||||
|
||||
./gradlew --no-daemon --max-workers=4 -PdeploymentRepository=${repository} -Porg.gradle.java.installations.fromEnv=JDK17,JDK21 \
|
||||
build publishAllPublicationsToDeploymentRepository
|
||||
|
||||
git reset --hard HEAD^ > /dev/null
|
||||
if [[ $nextVersion != $snapshotVersion ]]; then
|
||||
echo "Setting next development version (v$nextVersion)"
|
||||
sed -i "s/version=$snapshotVersion/version=$nextVersion/" gradle.properties
|
||||
git add gradle.properties > /dev/null
|
||||
git commit -m"Next development version (v$nextVersion)" > /dev/null
|
||||
fi;
|
||||
|
||||
echo "Staging Complete"
|
||||
|
||||
popd > /dev/null
|
||||
@@ -1,30 +0,0 @@
|
||||
---
|
||||
platform: linux
|
||||
image_resource:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: concourse/oci-build-task
|
||||
tag: 0.10.0
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
inputs:
|
||||
- name: ci-images-git-repo
|
||||
outputs:
|
||||
- name: image
|
||||
caches:
|
||||
- path: ci-image-cache
|
||||
params:
|
||||
CONTEXT: ci-images-git-repo/ci/images
|
||||
DOCKERFILE: ci-images-git-repo/ci/images/ci-image/Dockerfile
|
||||
DOCKER_HUB_AUTH: ((docker-hub-auth))
|
||||
run:
|
||||
path: /bin/sh
|
||||
args:
|
||||
- "-c"
|
||||
- |
|
||||
mkdir -p /root/.docker
|
||||
cat > /root/.docker/config.json <<EOF
|
||||
{ "auths": { "https://index.docker.io/v1/": { "auth": "$DOCKER_HUB_AUTH" }}}
|
||||
EOF
|
||||
build
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
---
|
||||
platform: linux
|
||||
image_resource:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: springio/github-changelog-generator
|
||||
tag: '0.0.8'
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
inputs:
|
||||
- name: git-repo
|
||||
- name: artifactory-repo
|
||||
outputs:
|
||||
- name: generated-changelog
|
||||
params:
|
||||
GITHUB_ORGANIZATION:
|
||||
GITHUB_REPO:
|
||||
GITHUB_USERNAME:
|
||||
GITHUB_TOKEN:
|
||||
RELEASE_TYPE:
|
||||
run:
|
||||
path: git-repo/ci/scripts/generate-changelog.sh
|
||||
@@ -1,25 +0,0 @@
|
||||
---
|
||||
platform: linux
|
||||
image_resource:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: springio/concourse-release-scripts
|
||||
tag: '0.4.0'
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
inputs:
|
||||
- name: git-repo
|
||||
- name: artifactory-repo
|
||||
outputs:
|
||||
- name: version
|
||||
params:
|
||||
RELEASE_TYPE:
|
||||
ARTIFACTORY_SERVER:
|
||||
ARTIFACTORY_USERNAME:
|
||||
ARTIFACTORY_PASSWORD:
|
||||
SONATYPE_USER:
|
||||
SONATYPE_PASSWORD:
|
||||
SONATYPE_URL:
|
||||
SONATYPE_STAGING_PROFILE:
|
||||
run:
|
||||
path: git-repo/ci/scripts/promote-version.sh
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
platform: linux
|
||||
inputs:
|
||||
- name: git-repo
|
||||
outputs:
|
||||
- name: stage-git-repo
|
||||
- name: distribution-repository
|
||||
params:
|
||||
RELEASE_TYPE:
|
||||
CI: true
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME:
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD:
|
||||
GRADLE_ENTERPRISE_URL: https://ge.spring.io
|
||||
caches:
|
||||
- path: gradle
|
||||
run:
|
||||
path: git-repo/ci/scripts/stage-version.sh
|
||||
@@ -28,7 +28,7 @@ javadoc {
|
||||
header = rootProject.description
|
||||
use = true
|
||||
overview = project.relativePath("$rootProject.rootDir/framework-docs/src/docs/api/overview.html")
|
||||
destinationDir = file("$project.docsDir/javadoc-api")
|
||||
destinationDir = project.java.docsDir.dir("javadoc-api").get().asFile
|
||||
splitIndex = true
|
||||
links(rootProject.ext.javadocLinks)
|
||||
addBooleanOption('Xdoclint:syntax,reference', true) // only check syntax and reference with doclint
|
||||
@@ -52,7 +52,7 @@ rootProject.tasks.dokkaHtmlMultiModule.configure {
|
||||
tasks.named("javadoc")
|
||||
}
|
||||
moduleName.set("spring-framework")
|
||||
outputDirectory.set(file("$docsDir/kdoc-api"))
|
||||
outputDirectory.set(project.java.docsDir.dir("kdoc-api").get().asFile)
|
||||
includes.from("$rootProject.rootDir/framework-docs/src/docs/api/dokka-overview.md")
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ content:
|
||||
- url: https://github.com/spring-projects/spring-framework
|
||||
# Refname matching:
|
||||
# https://docs.antora.org/antora/latest/playbook/content-refname-matching/
|
||||
branches: ['main', '{6..9}.+({0..9}).x']
|
||||
branches: ['main', '{6..9}.+({1..9}).x']
|
||||
tags: ['v{6..9}.+({0..9}).+({0..9})?(-{RC,M}*)', '!(v6.0.{0..8})', '!(v6.0.0-{RC,M}{0..9})']
|
||||
start_path: framework-docs
|
||||
asciidoc:
|
||||
@@ -36,4 +36,4 @@ runtime:
|
||||
failure_level: warn
|
||||
ui:
|
||||
bundle:
|
||||
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.16/ui-bundle.zip
|
||||
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.18/ui-bundle.zip
|
||||
|
||||
@@ -20,6 +20,7 @@ asciidoc:
|
||||
fold: 'all'
|
||||
table-stripes: 'odd'
|
||||
include-java: 'example$docs-src/main/java/org/springframework/docs'
|
||||
include-kotlin: 'example$docs-src/main/kotlin/org/springframework/docs'
|
||||
spring-site: 'https://spring.io'
|
||||
spring-site-blog: '{spring-site}/blog'
|
||||
spring-site-cve: "{spring-site}/security"
|
||||
@@ -70,6 +71,9 @@ asciidoc:
|
||||
kotlin-coroutines-api: '{kotlin-site}/api/kotlinx.coroutines'
|
||||
kotlin-github-org: 'https://github.com/Kotlin'
|
||||
kotlin-issues: 'https://youtrack.jetbrains.com/issue'
|
||||
micrometer-docs: 'https://docs.micrometer.io/micrometer/reference'
|
||||
micrometer-context-propagation-docs: 'https://docs.micrometer.io/context-propagation/reference'
|
||||
petclinic-github-org: 'https://github.com/spring-petclinic'
|
||||
reactive-streams-site: 'https://www.reactive-streams.org'
|
||||
reactive-streams-spec: 'https://github.com/reactive-streams/reactive-streams-jvm/blob/master/README.md#specification'
|
||||
reactor-github-org: 'https://github.com/reactor'
|
||||
@@ -87,4 +91,5 @@ asciidoc:
|
||||
stackoverflow-questions: '{stackoverflow-site}/questions'
|
||||
stackoverflow-spring-tag: "{stackoverflow-questions}/tagged/spring"
|
||||
stackoverflow-spring-kotlin-tags: "{stackoverflow-spring-tag}+kotlin"
|
||||
testcontainers-site: 'https://www.testcontainers.org'
|
||||
testcontainers-site: 'https://www.testcontainers.org'
|
||||
vavr-docs: 'https://vavr-io.github.io/vavr-docs'
|
||||
@@ -43,10 +43,15 @@ repositories {
|
||||
|
||||
dependencies {
|
||||
api(project(":spring-context"))
|
||||
api(project(":spring-jdbc"))
|
||||
api(project(":spring-jms"))
|
||||
api(project(":spring-web"))
|
||||
api(project(":spring-webflux"))
|
||||
|
||||
api("com.oracle.database.jdbc:ojdbc11")
|
||||
api("jakarta.jms:jakarta.jms-api")
|
||||
api("jakarta.servlet:jakarta.servlet-api")
|
||||
api("org.jetbrains.kotlin:kotlin-stdlib")
|
||||
|
||||
implementation(project(":spring-core-test"))
|
||||
implementation("org.assertj:assertj-core")
|
||||
|
||||
@@ -23,6 +23,12 @@ The following table lists all currently supported Spring properties.
|
||||
|===
|
||||
| Name | Description
|
||||
|
||||
| `spring.aop.ajc.ignore`
|
||||
| Instructs Spring to ignore ajc-compiled aspects for Spring AOP proxying, restoring traditional
|
||||
Spring behavior for scenarios where both weaving and AspectJ auto-proxying are enabled. See
|
||||
{spring-framework-api}++/aop/aspectj/annotation/AbstractAspectJAdvisorFactory.html#IGNORE_AJC_PROPERTY_NAME++[`AbstractAspectJAdvisorFactory`]
|
||||
for details.
|
||||
|
||||
| `spring.aot.enabled`
|
||||
| Indicates the application should run with AOT generated artifacts. See
|
||||
xref:core/aot.adoc[Ahead of Time Optimizations] and
|
||||
@@ -32,7 +38,7 @@ for details.
|
||||
| `spring.beaninfo.ignore`
|
||||
| Instructs Spring to use the `Introspector.IGNORE_ALL_BEANINFO` mode when calling the
|
||||
JavaBeans `Introspector`. See
|
||||
{spring-framework-api}++/beans/StandardBeanInfoFactory.html#IGNORE_BEANINFO_PROPERTY_NAME++[`CachedIntrospectionResults`]
|
||||
{spring-framework-api}++/beans/StandardBeanInfoFactory.html#IGNORE_BEANINFO_PROPERTY_NAME++[`StandardBeanInfoFactory`]
|
||||
for details.
|
||||
|
||||
| `spring.cache.reactivestreams.ignore`
|
||||
@@ -49,15 +55,13 @@ for details.
|
||||
|
||||
| `spring.context.checkpoint`
|
||||
| Property that specifies a common context checkpoint. See
|
||||
xref:integration/checkpoint-restore.adoc#_automatic_checkpointrestore_at_startup[Automatic
|
||||
checkpoint/restore at startup] and
|
||||
xref:integration/checkpoint-restore.adoc#_automatic_checkpointrestore_at_startup[Automatic checkpoint/restore at startup] and
|
||||
{spring-framework-api}++/context/support/DefaultLifecycleProcessor.html#CHECKPOINT_PROPERTY_NAME++[`DefaultLifecycleProcessor`]
|
||||
for details.
|
||||
|
||||
| `spring.context.exit`
|
||||
| Property for terminating the JVM when the context reaches a specific phase. See
|
||||
xref:integration/checkpoint-restore.adoc#_automatic_checkpointrestore_at_startup[Automatic
|
||||
checkpoint/restore at startup] and
|
||||
xref:integration/checkpoint-restore.adoc#_automatic_checkpointrestore_at_startup[Automatic checkpoint/restore at startup] and
|
||||
{spring-framework-api}++/context/support/DefaultLifecycleProcessor.html#EXIT_PROPERTY_NAME++[`DefaultLifecycleProcessor`]
|
||||
for details.
|
||||
|
||||
|
||||
@@ -420,7 +420,7 @@ who typically are in charge of the deployment configuration, such as the launch
|
||||
Now that the sales pitch is over, let us first walk through a quick example of AspectJ
|
||||
LTW that uses Spring, followed by detailed specifics about elements introduced in the
|
||||
example. For a complete example, see the
|
||||
{spring-github-org}/spring-petclinic[Petclinic sample application].
|
||||
{petclinic-github-org}/spring-framework-petclinic[Petclinic sample application based on Spring Framework].
|
||||
|
||||
|
||||
[[aop-aj-ltw-first-example]]
|
||||
|
||||
@@ -36,7 +36,7 @@ NOTE: At the moment, AOT is focused on allowing Spring applications to be deploy
|
||||
We intend to support more JVM-based use cases in future generations.
|
||||
|
||||
[[aot.basics]]
|
||||
== AOT engine overview
|
||||
== AOT Engine Overview
|
||||
|
||||
The entry point of the AOT engine for processing an `ApplicationContext` is `ApplicationContextAotGenerator`. It takes care of the following steps, based on a `GenericApplicationContext` that represents the application to optimize and a {spring-framework-api}/aot/generate/GenerationContext.html[`GenerationContext`]:
|
||||
|
||||
@@ -225,7 +225,7 @@ When a `datasource` instance is required, a `BeanInstanceSupplier` is called.
|
||||
This supplier invokes the `dataSource()` method on the `dataSourceConfiguration` bean.
|
||||
|
||||
[[aot.running]]
|
||||
== Running with AOT optimizations
|
||||
== Running with AOT Optimizations
|
||||
|
||||
AOT is a mandatory step to transform a Spring application to a native executable, so it
|
||||
is automatically enabled when running in this mode. It is possible to use those optimizations
|
||||
@@ -244,7 +244,7 @@ However, keep in mind that some optimizations are made at build time based on a
|
||||
This section lists the best practices that make sure your application is ready for AOT.
|
||||
|
||||
[[aot.bestpractices.bean-registration]]
|
||||
== Programmatic bean registration
|
||||
=== Programmatic Bean Registration
|
||||
|
||||
The AOT engine takes care of the `@Configuration` model and any callback that might be
|
||||
invoked as part of processing your configuration. If you need to register additional
|
||||
@@ -266,7 +266,7 @@ notion of a classpath. For cases like this, it is crucial that the scanning happ
|
||||
build time.
|
||||
|
||||
[[aot.bestpractices.bean-type]]
|
||||
=== Expose The Most Precise Bean Type
|
||||
=== Expose the Most Precise Bean Type
|
||||
|
||||
While your application may interact with an interface that a bean implements, it is still very important to declare the most precise type.
|
||||
The AOT engine performs additional checks on the bean type, such as detecting the presence of `@Autowired` members or lifecycle callback methods.
|
||||
@@ -326,21 +326,21 @@ However, this is not a best practice and flagging the preferred constructor with
|
||||
|
||||
In case you are working on a code base that you cannot modify, you can set the {spring-framework-api}/beans/factory/support/AbstractBeanDefinition.html#PREFERRED_CONSTRUCTORS_ATTRIBUTE[`preferredConstructors` attribute] on the related bean definition to indicate which constructor should be used.
|
||||
|
||||
[[aot.bestpractices.comlext-data-structure]]
|
||||
=== Avoid Complex Data Structure for Constructor Parameters and Properties
|
||||
[[aot.bestpractices.complex-data-structures]]
|
||||
=== Avoid Complex Data Structures for Constructor Parameters and Properties
|
||||
|
||||
When crafting a `RootBeanDefinition` programmatically, you are not constrained in terms of types that you can use.
|
||||
For instance, you may have a custom `record` with several properties that your bean takes as a constructor argument.
|
||||
|
||||
While this works fine with the regular runtime, AOT does not know how to generate the code of your custom data structure.
|
||||
A good rule of thumb is to keep in mind that bean definitions are an abstraction on top of several models.
|
||||
Rather than using such structure, decomposing to simple types or referring to a bean that is built as such is recommended.
|
||||
Rather than using such structures, decomposing to simple types or referring to a bean that is built as such is recommended.
|
||||
|
||||
As a last resort, you can implement your own `org.springframework.aot.generate.ValueCodeGenerator$Delegate`.
|
||||
To use it, register its fully qualified name in `META-INF/spring/aot.factories` using the `Delegate` as the key.
|
||||
|
||||
[[aot.bestpractices.custom-arguments]]
|
||||
=== Avoid Creating Bean with Custom Arguments
|
||||
=== Avoid Creating Beans with Custom Arguments
|
||||
|
||||
Spring AOT detects what needs to be done to create a bean and translates that in generated code using an instance supplier.
|
||||
The container also supports creating a bean with {spring-framework-api}++/beans/factory/BeanFactory.html#getBean(java.lang.String,java.lang.Object...)++[custom arguments] that leads to several issues with AOT:
|
||||
@@ -352,6 +352,20 @@ For instance, autowiring on fields and methods will be skipped as they are handl
|
||||
|
||||
Rather than having prototype-scoped beans created with custom arguments, we recommend a manual factory pattern where a bean is responsible for the creation of the instance.
|
||||
|
||||
[[aot.bestpractices.circular-dependencies]]
|
||||
=== Avoid Circular Dependencies
|
||||
|
||||
Certain use cases can result in circular dependencies between one or more beans. With the
|
||||
regular runtime, it may be possible to wire those circular dependencies via `@Autowired`
|
||||
on setter methods or fields. However, an AOT-optimized context will fail to start with
|
||||
explicit circular dependencies.
|
||||
|
||||
In an AOT-optimized application, you should therefore strive to avoid circular
|
||||
dependencies. If that is not possible, you can use `@Lazy` injection points or
|
||||
`ObjectProvider` to lazily access or retrieve the necessary collaborating beans. See
|
||||
xref:core/beans/classpath-scanning.adoc#beans-factorybeans-annotations-lazy-injection-points[this tip]
|
||||
for further information.
|
||||
|
||||
[[aot.bestpractices.factory-bean]]
|
||||
=== FactoryBean
|
||||
|
||||
|
||||
@@ -317,8 +317,8 @@ exposed based on security policies in some environments -- for example, standalo
|
||||
JDK 1.7.0_45 and higher (which requires 'Trusted-Library' setup in your manifests -- see
|
||||
{stackoverflow-questions}/19394570/java-jre-7u45-breaks-classloader-getresources).
|
||||
|
||||
On JDK 9's module path (Jigsaw), Spring's classpath scanning generally works as expected.
|
||||
However, make sure that your component classes are exported in your `module-info`
|
||||
On the module path (Java Module System), Spring's classpath scanning generally works as
|
||||
expected. However, make sure that your component classes are exported in your `module-info`
|
||||
descriptors. If you expect Spring to invoke non-public members of your classes, make
|
||||
sure that they are 'opened' (that is, that they use an `opens` declaration instead of an
|
||||
`exports` declaration in your `module-info` descriptor).
|
||||
@@ -480,11 +480,15 @@ factory method and other bean definition properties, such as a qualifier value t
|
||||
the `@Qualifier` annotation. Other method-level annotations that can be specified are
|
||||
`@Scope`, `@Lazy`, and custom qualifier annotations.
|
||||
|
||||
TIP: In addition to its role for component initialization, you can also place the `@Lazy`
|
||||
[[beans-factorybeans-annotations-lazy-injection-points]]
|
||||
[TIP]
|
||||
====
|
||||
In addition to its role for component initialization, you can also place the `@Lazy`
|
||||
annotation on injection points marked with `@Autowired` or `@Inject`. In this context,
|
||||
it leads to the injection of a lazy-resolution proxy. However, such a proxy approach
|
||||
is rather limited. For sophisticated lazy interactions, in particular in combination
|
||||
with optional dependencies, we recommend `ObjectProvider<MyTargetBean>` instead.
|
||||
====
|
||||
|
||||
Autowired fields and methods are supported, as previously discussed, with additional
|
||||
support for autowiring of `@Bean` methods. The following example shows how to do so:
|
||||
@@ -751,7 +755,7 @@ and bean definition show.
|
||||
TIP: If you run into naming conflicts due to multiple autodetected components having the
|
||||
same non-qualified class name (i.e., classes with identical names but residing in
|
||||
different packages), you may need to configure a `BeanNameGenerator` that defaults to the
|
||||
fully qualified class name for the generated bean name. As of Spring Framework 5.2.3, the
|
||||
fully qualified class name for the generated bean name. The
|
||||
`FullyQualifiedAnnotationBeanNameGenerator` located in package
|
||||
`org.springframework.context.annotation` can be used for such purposes.
|
||||
|
||||
@@ -920,7 +924,8 @@ Kotlin::
|
||||
[[beans-scanning-qualifiers]]
|
||||
== Providing Qualifier Metadata with Annotations
|
||||
|
||||
The `@Qualifier` annotation is discussed in xref:core/beans/annotation-config/autowired-qualifiers.adoc[Fine-tuning Annotation-based Autowiring with Qualifiers].
|
||||
The `@Qualifier` annotation is discussed in
|
||||
xref:core/beans/annotation-config/autowired-qualifiers.adoc[Fine-tuning Annotation-based Autowiring with Qualifiers].
|
||||
The examples in that section demonstrate the use of the `@Qualifier` annotation and
|
||||
custom qualifier annotations to provide fine-grained control when you resolve autowire
|
||||
candidates. Because those examples were based on XML bean definitions, the qualifier
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[[context-introduction]]
|
||||
= Additional Capabilities of the `ApplicationContext`
|
||||
|
||||
As discussed in the xref:web/webmvc-view/mvc-xslt.adoc#mvc-view-xslt-beandefs[chapter introduction], the `org.springframework.beans.factory`
|
||||
As discussed in the xref:core/beans/introduction.adoc[chapter introduction], the `org.springframework.beans.factory`
|
||||
package provides basic functionality for managing and manipulating beans, including in a
|
||||
programmatic way. The `org.springframework.context` package adds the
|
||||
{spring-framework-api}/context/ApplicationContext.html[`ApplicationContext`]
|
||||
@@ -644,7 +644,7 @@ Each `SpEL` expression evaluates against a dedicated context. The following tabl
|
||||
items made available to the context so that you can use them for conditional event processing:
|
||||
|
||||
[[context-functionality-events-annotation-tbl]]
|
||||
.Event SpEL available metadata
|
||||
.Event metadata available in SpEL expressions
|
||||
|===
|
||||
| Name| Location| Description| Example
|
||||
|
||||
@@ -660,8 +660,8 @@ items made available to the context so that you can use them for conditional eve
|
||||
|
||||
| __Argument name__
|
||||
| evaluation context
|
||||
| The name of any of the method arguments. If, for some reason, the names are not available
|
||||
(for example, because there is no debug information in the compiled byte code), individual
|
||||
| The name of a particular method argument. If the names are not available
|
||||
(for example, because the code was compiled without the `-parameters` flag), individual
|
||||
arguments are also available using the `#a<#arg>` syntax where `<#arg>` stands for the
|
||||
argument index (starting from 0).
|
||||
| `#blEvent` or `#a0` (you can also use `#p0` or `#p<#arg>` parameter notation as an alias)
|
||||
@@ -883,11 +883,12 @@ e.g. for processing all events asynchronously and/or for handling listener excep
|
||||
== Convenient Access to Low-level Resources
|
||||
|
||||
For optimal usage and understanding of application contexts, you should familiarize
|
||||
yourself with Spring's `Resource` abstraction, as described in xref:web/webflux-webclient/client-builder.adoc#webflux-client-builder-reactor-resources[Resources].
|
||||
yourself with Spring's `Resource` abstraction, as described in
|
||||
xref:core/resources.adoc[Resources].
|
||||
|
||||
An application context is a `ResourceLoader`, which can be used to load `Resource` objects.
|
||||
A `Resource` is essentially a more feature rich version of the JDK `java.net.URL` class.
|
||||
In fact, the implementations of the `Resource` wrap an instance of `java.net.URL`, where
|
||||
In fact, implementations of `Resource` wrap an instance of `java.net.URL`, where
|
||||
appropriate. A `Resource` can obtain low-level resources from almost any location in a
|
||||
transparent fashion, including from the classpath, a filesystem location, anywhere
|
||||
describable with a standard URL, and some other variations. If the resource location
|
||||
|
||||
@@ -60,6 +60,7 @@ instance's values consist of all bean instances that match the expected type, an
|
||||
`Map` instance's keys contain the corresponding bean names.
|
||||
|
||||
|
||||
|
||||
[[beans-autowired-exceptions]]
|
||||
== Limitations and Disadvantages of Autowiring
|
||||
|
||||
@@ -101,10 +102,10 @@ In the latter scenario, you have several options:
|
||||
== Excluding a Bean from Autowiring
|
||||
|
||||
On a per-bean basis, you can exclude a bean from autowiring. In Spring's XML format, set
|
||||
the `autowire-candidate` attribute of the `<bean/>` element to `false`. The container
|
||||
makes that specific bean definition unavailable to the autowiring infrastructure
|
||||
(including annotation style configurations such as xref:core/beans/annotation-config/autowired.adoc[`@Autowired`]
|
||||
).
|
||||
the `autowire-candidate` attribute of the `<bean/>` element to `false`; with the `@Bean`
|
||||
annotation, the attribute is named `autowireCandidate`. The container makes that specific
|
||||
bean definition unavailable to the autowiring infrastructure, including annotation-based
|
||||
injection points such as xref:core/beans/annotation-config/autowired.adoc[`@Autowired`].
|
||||
|
||||
NOTE: The `autowire-candidate` attribute is designed to only affect type-based autowiring.
|
||||
It does not affect explicit references by name, which get resolved even if the
|
||||
@@ -119,8 +120,8 @@ provide multiple patterns, define them in a comma-separated list. An explicit va
|
||||
`true` or `false` for a bean definition's `autowire-candidate` attribute always takes
|
||||
precedence. For such beans, the pattern matching rules do not apply.
|
||||
|
||||
These techniques are useful for beans that you never want to be injected into other
|
||||
beans by autowiring. It does not mean that an excluded bean cannot itself be configured by
|
||||
These techniques are useful for beans that you never want to be injected into other beans
|
||||
by autowiring. It does not mean that an excluded bean cannot itself be configured by
|
||||
using autowiring. Rather, the bean itself is not a candidate for autowiring other beans.
|
||||
|
||||
|
||||
|
||||
+15
-12
@@ -159,10 +159,12 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
.[[beans-factory-ctor-arguments-type]]Constructor argument type matching
|
||||
--
|
||||
[discrete]
|
||||
[[beans-factory-ctor-arguments-type]]
|
||||
==== Constructor argument type matching
|
||||
|
||||
In the preceding scenario, the container can use type matching with simple types if
|
||||
you explicitly specify the type of the constructor argument by using the `type` attribute,
|
||||
you explicitly specify the type of the constructor argument via the `type` attribute,
|
||||
as the following example shows:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
@@ -172,10 +174,11 @@ as the following example shows:
|
||||
<constructor-arg type="java.lang.String" value="42"/>
|
||||
</bean>
|
||||
----
|
||||
--
|
||||
|
||||
.[[beans-factory-ctor-arguments-index]]Constructor argument index
|
||||
--
|
||||
[discrete]
|
||||
[[beans-factory-ctor-arguments-index]]
|
||||
==== Constructor argument index
|
||||
|
||||
You can use the `index` attribute to specify explicitly the index of constructor arguments,
|
||||
as the following example shows:
|
||||
|
||||
@@ -191,10 +194,11 @@ In addition to resolving the ambiguity of multiple simple values, specifying an
|
||||
resolves ambiguity where a constructor has two arguments of the same type.
|
||||
|
||||
NOTE: The index is 0-based.
|
||||
--
|
||||
|
||||
.[[beans-factory-ctor-arguments-name]]Constructor argument name
|
||||
--
|
||||
[discrete]
|
||||
[[beans-factory-ctor-arguments-name]]
|
||||
==== Constructor argument name
|
||||
|
||||
You can also use the constructor parameter name for value disambiguation, as the following
|
||||
example shows:
|
||||
|
||||
@@ -207,8 +211,8 @@ example shows:
|
||||
----
|
||||
|
||||
Keep in mind that, to make this work out of the box, your code must be compiled with the
|
||||
debug flag enabled so that Spring can look up the parameter name from the constructor.
|
||||
If you cannot or do not want to compile your code with the debug flag, you can use the
|
||||
`-parameters` flag enabled so that Spring can look up the parameter name from the constructor.
|
||||
If you cannot or do not want to compile your code with the `-parameters` flag, you can use the
|
||||
https://download.oracle.com/javase/8/docs/api/java/beans/ConstructorProperties.html[@ConstructorProperties]
|
||||
JDK annotation to explicitly name your constructor arguments. The sample class would
|
||||
then have to look as follows:
|
||||
@@ -244,7 +248,6 @@ Kotlin::
|
||||
constructor(val years: Int, val ultimateAnswer: String)
|
||||
----
|
||||
======
|
||||
--
|
||||
|
||||
|
||||
[[beans-setter-injection]]
|
||||
|
||||
@@ -2,13 +2,15 @@
|
||||
= Using `depends-on`
|
||||
|
||||
If a bean is a dependency of another bean, that usually means that one bean is set as a
|
||||
property of another. Typically you accomplish this with the <<beans-ref-element, `<ref/>`
|
||||
element>> in XML-based configuration metadata. However, sometimes dependencies between
|
||||
beans are less direct. An example is when a static initializer in a class needs to be
|
||||
triggered, such as for database driver registration. The `depends-on` attribute can
|
||||
explicitly force one or more beans to be initialized before the bean using this element
|
||||
is initialized. The following example uses the `depends-on` attribute to express a
|
||||
dependency on a single bean:
|
||||
property of another. Typically you accomplish this with the
|
||||
xref:core/beans/dependencies/factory-properties-detailed.adoc#beans-ref-element[`<ref/>` element>]
|
||||
in XML-based metadata or through xref:core/beans/dependencies/factory-autowire.adoc[autowiring].
|
||||
|
||||
However, sometimes dependencies between beans are less direct. An example is when a static
|
||||
initializer in a class needs to be triggered, such as for database driver registration.
|
||||
The `depends-on` attribute or `@DependsOn` annotation can explicitly force one or more beans
|
||||
to be initialized before the bean using this element is initialized. The following example
|
||||
uses the `depends-on` attribute to express a dependency on a single bean:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@@ -31,10 +33,10 @@ delimiters):
|
||||
----
|
||||
|
||||
NOTE: The `depends-on` attribute can specify both an initialization-time dependency and,
|
||||
in the case of xref:core/beans/factory-scopes.adoc#beans-factory-scopes-singleton[singleton] beans only, a corresponding
|
||||
destruction-time dependency. Dependent beans that define a `depends-on` relationship
|
||||
with a given bean are destroyed first, prior to the given bean itself being destroyed.
|
||||
Thus, `depends-on` can also control shutdown order.
|
||||
in the case of xref:core/beans/factory-scopes.adoc#beans-factory-scopes-singleton[singleton]
|
||||
beans only, a corresponding destruction-time dependency. Dependent beans that define a
|
||||
`depends-on` relationship with a given bean are destroyed first, prior to the given bean
|
||||
itself being destroyed. Thus, `depends-on` can also control shutdown order.
|
||||
|
||||
|
||||
|
||||
|
||||
+1
-8
@@ -120,8 +120,6 @@ dynamically generate a subclass that overrides the method.
|
||||
subclasses cannot be `final`, and the method to be overridden cannot be `final`, either.
|
||||
* Unit-testing a class that has an `abstract` method requires you to subclass the class
|
||||
yourself and to supply a stub implementation of the `abstract` method.
|
||||
* Concrete methods are also necessary for component scanning, which requires concrete
|
||||
classes to pick up.
|
||||
* A further key limitation is that lookup methods do not work with factory methods and
|
||||
in particular not with `@Bean` methods in configuration classes, since, in that case,
|
||||
the container is not in charge of creating the instance and therefore cannot create
|
||||
@@ -201,7 +199,7 @@ the original class. Consider the following example:
|
||||
<!-- inject dependencies here as required -->
|
||||
</bean>
|
||||
|
||||
<!-- commandProcessor uses statefulCommandHelper -->
|
||||
<!-- commandManager uses myCommand prototype bean -->
|
||||
<bean id="commandManager" class="fiona.apple.CommandManager">
|
||||
<lookup-method name="createCommand" bean="myCommand"/>
|
||||
</bean>
|
||||
@@ -293,11 +291,6 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
Note that you should typically declare such annotated lookup methods with a concrete
|
||||
stub implementation, in order for them to be compatible with Spring's component
|
||||
scanning rules where abstract classes get ignored by default. This limitation does not
|
||||
apply to explicitly registered or explicitly imported bean classes.
|
||||
|
||||
[TIP]
|
||||
====
|
||||
Another way of accessing differently scoped target beans is an `ObjectFactory`/
|
||||
|
||||
+3
-3
@@ -102,8 +102,8 @@ following snippet:
|
||||
----
|
||||
<bean id="theTargetBean" class="..." />
|
||||
|
||||
<bean id="client" class="...">
|
||||
<property name="targetName" value="theTargetBean"/>
|
||||
<bean id="theClientBean" class="...">
|
||||
<property name="targetName" ref="theTargetBean"/>
|
||||
</bean>
|
||||
----
|
||||
|
||||
@@ -582,7 +582,7 @@ it needs to be declared in the XML file even though it is not defined in an XSD
|
||||
(it exists inside the Spring core).
|
||||
|
||||
For the rare cases where the constructor argument names are not available (usually if
|
||||
the bytecode was compiled without debugging information), you can use fallback to the
|
||||
the bytecode was compiled without the `-parameters` flag), you can fall back to the
|
||||
argument indexes, as follows:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
|
||||
+55
-32
@@ -116,7 +116,7 @@ the configuration model, in that references to other beans must be valid Java sy
|
||||
Fortunately, solving this problem is simple. As
|
||||
xref:core/beans/java/bean-annotation.adoc#beans-java-dependencies[we already discussed],
|
||||
a `@Bean` method can have an arbitrary number of parameters that describe the bean
|
||||
dependencies. Consider the following more real-world scenario with several `@Configuration`
|
||||
dependencies. Consider the following more realistic scenario with several `@Configuration`
|
||||
classes, each depending on beans declared in the others:
|
||||
|
||||
[tabs]
|
||||
@@ -331,8 +331,10 @@ TIP: Constructor injection in `@Configuration` classes is only supported as of S
|
||||
Framework 4.3. Note also that there is no need to specify `@Autowired` if the target
|
||||
bean defines only one constructor.
|
||||
|
||||
.[[beans-java-injecting-imported-beans-fq]]Fully-qualifying imported beans for ease of navigation
|
||||
--
|
||||
[discrete]
|
||||
[[beans-java-injecting-imported-beans-fq]]
|
||||
==== Fully-qualifying imported beans for ease of navigation
|
||||
|
||||
In the preceding scenario, using `@Autowired` works well and provides the desired
|
||||
modularity, but determining exactly where the autowired bean definitions are declared is
|
||||
still somewhat ambiguous. For example, as a developer looking at `ServiceConfig`, how do
|
||||
@@ -501,7 +503,6 @@ Now `ServiceConfig` is loosely coupled with respect to the concrete
|
||||
get a type hierarchy of `RepositoryConfig` implementations. In this
|
||||
way, navigating `@Configuration` classes and their dependencies becomes no different
|
||||
than the usual process of navigating interface-based code.
|
||||
--
|
||||
|
||||
TIP: If you want to influence the startup creation order of certain beans, consider
|
||||
declaring some of them as `@Lazy` (for creation on first access instead of on startup)
|
||||
@@ -540,7 +541,7 @@ Java::
|
||||
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
|
||||
if (attrs != null) {
|
||||
for (Object value : attrs.get("value")) {
|
||||
if (context.getEnvironment().acceptsProfiles(((String[]) value))) {
|
||||
if (context.getEnvironment().matchesProfiles((String[]) value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -559,7 +560,7 @@ Kotlin::
|
||||
val attrs = metadata.getAllAnnotationAttributes(Profile::class.java.name)
|
||||
if (attrs != null) {
|
||||
for (value in attrs["value"]!!) {
|
||||
if (context.environment.acceptsProfiles(Profiles.of(*value as Array<String>))) {
|
||||
if (context.environment.matchesProfiles(*value as Array<String>)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -594,16 +595,18 @@ that uses Spring XML, it is easier to create `@Configuration` classes on an
|
||||
as-needed basis and include them from the existing XML files. Later in this section, we cover the
|
||||
options for using `@Configuration` classes in this kind of "`XML-centric`" situation.
|
||||
|
||||
.[[beans-java-combining-xml-centric-declare-as-bean]]Declaring `@Configuration` classes as plain Spring `<bean/>` elements
|
||||
--
|
||||
Remember that `@Configuration` classes are ultimately bean definitions in the
|
||||
container. In this series examples, we create a `@Configuration` class named `AppConfig` and
|
||||
[discrete]
|
||||
[[beans-java-combining-xml-centric-declare-as-bean]]
|
||||
==== Declaring `@Configuration` classes as plain Spring `<bean/>` elements
|
||||
|
||||
Remember that `@Configuration` classes are ultimately bean definitions in the container.
|
||||
In this series of examples, we create a `@Configuration` class named `AppConfig` and
|
||||
include it within `system-test-config.xml` as a `<bean/>` definition. Because
|
||||
`<context:annotation-config/>` is switched on, the container recognizes the
|
||||
`@Configuration` annotation and processes the `@Bean` methods declared in `AppConfig`
|
||||
properly.
|
||||
|
||||
The following example shows an ordinary configuration class in Java:
|
||||
The following example shows the `AppConfig` configuration class in Java and Kotlin:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@@ -624,7 +627,7 @@ Java::
|
||||
|
||||
@Bean
|
||||
public TransferService transferService() {
|
||||
return new TransferService(accountRepository());
|
||||
return new TransferServiceImpl(accountRepository());
|
||||
}
|
||||
}
|
||||
----
|
||||
@@ -657,6 +660,7 @@ The following example shows part of a sample `system-test-config.xml` file:
|
||||
<beans>
|
||||
<!-- enable processing of annotations such as @Autowired and @Configuration -->
|
||||
<context:annotation-config/>
|
||||
|
||||
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
|
||||
|
||||
<bean class="com.acme.AppConfig"/>
|
||||
@@ -703,20 +707,20 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
NOTE: In `system-test-config.xml` file, the `AppConfig` `<bean/>` does not declare an `id`
|
||||
element. While it would be acceptable to do so, it is unnecessary, given that no other bean
|
||||
NOTE: In the `system-test-config.xml` file, the `AppConfig` `<bean/>` does not declare an `id`
|
||||
attribute. While it would be acceptable to do so, it is unnecessary, given that no other bean
|
||||
ever refers to it, and it is unlikely to be explicitly fetched from the container by name.
|
||||
Similarly, the `DataSource` bean is only ever autowired by type, so an explicit bean `id`
|
||||
is not strictly required.
|
||||
--
|
||||
|
||||
.[[beans-java-combining-xml-centric-component-scan]] Using <context:component-scan/> to pick up `@Configuration` classes
|
||||
--
|
||||
[discrete]
|
||||
[[beans-java-combining-xml-centric-component-scan]]
|
||||
==== Using <context:component-scan/> to pick up `@Configuration` classes
|
||||
|
||||
Because `@Configuration` is meta-annotated with `@Component`, `@Configuration`-annotated
|
||||
classes are automatically candidates for component scanning. Using the same scenario as
|
||||
described in the previous example, we can redefine `system-test-config.xml` to take advantage of component-scanning.
|
||||
Note that, in this case, we need not explicitly declare
|
||||
described in the previous example, we can redefine `system-test-config.xml` to take
|
||||
advantage of component-scanning. Note that, in this case, we need not explicitly declare
|
||||
`<context:annotation-config/>`, because `<context:component-scan/>` enables the same
|
||||
functionality.
|
||||
|
||||
@@ -727,6 +731,7 @@ The following example shows the modified `system-test-config.xml` file:
|
||||
<beans>
|
||||
<!-- picks up and registers AppConfig as a bean definition -->
|
||||
<context:component-scan base-package="com.acme"/>
|
||||
|
||||
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
|
||||
|
||||
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
|
||||
@@ -736,19 +741,17 @@ The following example shows the modified `system-test-config.xml` file:
|
||||
</bean>
|
||||
</beans>
|
||||
----
|
||||
--
|
||||
|
||||
[[beans-java-combining-java-centric]]
|
||||
=== `@Configuration` Class-centric Use of XML with `@ImportResource`
|
||||
|
||||
In applications where `@Configuration` classes are the primary mechanism for configuring
|
||||
the container, it is still likely necessary to use at least some XML. In these
|
||||
scenarios, you can use `@ImportResource` and define only as much XML as you need. Doing
|
||||
so achieves a "`Java-centric`" approach to configuring the container and keeps XML to a
|
||||
bare minimum. The following example (which includes a configuration class, an XML file
|
||||
that defines a bean, a properties file, and the `main` class) shows how to use
|
||||
the `@ImportResource` annotation to achieve "`Java-centric`" configuration that uses XML
|
||||
as needed:
|
||||
the container, it may still be necessary to use at least some XML. In such scenarios, you
|
||||
can use `@ImportResource` and define only as much XML as you need. Doing so achieves a
|
||||
"`Java-centric`" approach to configuring the container and keeps XML to a bare minimum.
|
||||
The following example (which includes a configuration class, an XML file that defines a
|
||||
bean, a properties file, and the `main()` method) shows how to use the `@ImportResource`
|
||||
annotation to achieve "`Java-centric`" configuration that uses XML as needed:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@@ -773,6 +776,17 @@ Java::
|
||||
public DataSource dataSource() {
|
||||
return new DriverManagerDataSource(url, username, password);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AccountRepository accountRepository(DataSource dataSource) {
|
||||
return new JdbcAccountRepository(dataSource);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TransferService transferService(AccountRepository accountRepository) {
|
||||
return new TransferServiceImpl(accountRepository);
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
@@ -797,21 +811,32 @@ Kotlin::
|
||||
fun dataSource(): DataSource {
|
||||
return DriverManagerDataSource(url, username, password)
|
||||
}
|
||||
|
||||
@Bean
|
||||
fun accountRepository(dataSource: DataSource): AccountRepository {
|
||||
return JdbcAccountRepository(dataSource)
|
||||
}
|
||||
|
||||
@Bean
|
||||
fun transferService(accountRepository: AccountRepository): TransferService {
|
||||
return TransferServiceImpl(accountRepository)
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
.properties-config.xml
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
properties-config.xml
|
||||
<beans>
|
||||
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
|
||||
</beans>
|
||||
----
|
||||
|
||||
.jdbc.properties
|
||||
[literal,subs="verbatim,quotes"]
|
||||
----
|
||||
jdbc.properties
|
||||
jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
|
||||
jdbc.username=sa
|
||||
jdbc.password=
|
||||
@@ -844,5 +869,3 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -222,29 +222,38 @@ Kotlin::
|
||||
[[expressions-evaluation-context]]
|
||||
== Understanding `EvaluationContext`
|
||||
|
||||
The `EvaluationContext` interface is used when evaluating an expression to resolve
|
||||
properties, methods, or fields and to help perform type conversion. Spring provides two
|
||||
The `EvaluationContext` API is used when evaluating an expression to resolve properties,
|
||||
methods, or fields and to help perform type conversion. Spring provides two
|
||||
implementations.
|
||||
|
||||
* `SimpleEvaluationContext`: Exposes a subset of essential SpEL language features and
|
||||
configuration options, for categories of expressions that do not require the full extent
|
||||
of the SpEL language syntax and should be meaningfully restricted. Examples include but
|
||||
are not limited to data binding expressions and property-based filters.
|
||||
`SimpleEvaluationContext`::
|
||||
Exposes a subset of essential SpEL language features and configuration options, for
|
||||
categories of expressions that do not require the full extent of the SpEL language
|
||||
syntax and should be meaningfully restricted. Examples include but are not limited to
|
||||
data binding expressions and property-based filters.
|
||||
|
||||
* `StandardEvaluationContext`: Exposes the full set of SpEL language features and
|
||||
configuration options. You can use it to specify a default root object and to configure
|
||||
every available evaluation-related strategy.
|
||||
`StandardEvaluationContext`::
|
||||
Exposes the full set of SpEL language features and configuration options. You can use
|
||||
it to specify a default root object and to configure every available evaluation-related
|
||||
strategy.
|
||||
|
||||
`SimpleEvaluationContext` is designed to support only a subset of the SpEL language syntax.
|
||||
It excludes Java type references, constructors, and bean references. It also requires
|
||||
you to explicitly choose the level of support for properties and methods in expressions.
|
||||
By default, the `create()` static factory method enables only read access to properties.
|
||||
You can also obtain a builder to configure the exact level of support needed, targeting
|
||||
one or some combination of the following.
|
||||
`SimpleEvaluationContext` is designed to support only a subset of the SpEL language
|
||||
syntax. For example, it excludes Java type references, constructors, and bean references.
|
||||
It also requires you to explicitly choose the level of support for properties and methods
|
||||
in expressions. When creating a `SimpleEvaluationContext` you need to choose the level of
|
||||
support that you need for data binding in SpEL expressions:
|
||||
|
||||
* Custom `PropertyAccessor` only (no reflection)
|
||||
* Data binding properties for read-only access
|
||||
* Data binding properties for read and write
|
||||
* Data binding for read-only access
|
||||
* Data binding for read and write access
|
||||
* A custom `PropertyAccessor` (typically not reflection-based), potentially combined with
|
||||
a `DataBindingPropertyAccessor`
|
||||
|
||||
Conveniently, `SimpleEvaluationContext.forReadOnlyDataBinding()` enables read-only access
|
||||
to properties via `DataBindingPropertyAccessor`. Similarly,
|
||||
`SimpleEvaluationContext.forReadWriteDataBinding()` enables read and write access to
|
||||
properties. Alternatively, configure custom accessors via
|
||||
`SimpleEvaluationContext.forPropertyAccessors(...)`, potentially disable assignment, and
|
||||
optionally activate method resolution and/or a type converter through the builder.
|
||||
|
||||
|
||||
[[expressions-type-conversion]]
|
||||
@@ -314,17 +323,17 @@ Kotlin::
|
||||
It is possible to configure the SpEL expression parser by using a parser configuration
|
||||
object (`org.springframework.expression.spel.SpelParserConfiguration`). The configuration
|
||||
object controls the behavior of some of the expression components. For example, if you
|
||||
index into an array or collection and the element at the specified index is `null`, SpEL
|
||||
can automatically create the element. This is useful when using expressions made up of a
|
||||
chain of property references. If you index into an array or list and specify an index
|
||||
that is beyond the end of the current size of the array or list, SpEL can automatically
|
||||
grow the array or list to accommodate that index. In order to add an element at the
|
||||
index into a collection and the element at the specified index is `null`, SpEL can
|
||||
automatically create the element. This is useful when using expressions made up of a
|
||||
chain of property references. Similarly, if you index into a collection and specify an
|
||||
index that is greater than the current size of the collection, SpEL can automatically
|
||||
grow the collection to accommodate that index. In order to add an element at the
|
||||
specified index, SpEL will try to create the element using the element type's default
|
||||
constructor before setting the specified value. If the element type does not have a
|
||||
default constructor, `null` will be added to the array or list. If there is no built-in
|
||||
or custom converter that knows how to set the value, `null` will remain in the array or
|
||||
list at the specified index. The following example demonstrates how to automatically grow
|
||||
the list.
|
||||
default constructor, `null` will be added to the collection. If there is no built-in
|
||||
converter or custom converter that knows how to set the value, `null` will remain in the
|
||||
collection at the specified index. The following example demonstrates how to
|
||||
automatically grow a `List`.
|
||||
|
||||
[tabs]
|
||||
======
|
||||
|
||||
@@ -12,27 +12,27 @@ Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
Inventor einstein = p.parseExpression(
|
||||
"new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German')")
|
||||
Inventor einstein = parser.parseExpression(
|
||||
"new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German')")
|
||||
.getValue(Inventor.class);
|
||||
|
||||
// create new Inventor instance within the add() method of List
|
||||
p.parseExpression(
|
||||
"Members.add(new org.spring.samples.spel.inventor.Inventor(
|
||||
'Albert Einstein', 'German'))").getValue(societyContext);
|
||||
parser.parseExpression(
|
||||
"Members.add(new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German'))")
|
||||
.getValue(societyContext);
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
val einstein = p.parseExpression(
|
||||
"new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German')")
|
||||
val einstein = parser.parseExpression(
|
||||
"new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German')")
|
||||
.getValue(Inventor::class.java)
|
||||
|
||||
// create new Inventor instance within the add() method of List
|
||||
p.parseExpression(
|
||||
"Members.add(new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German'))")
|
||||
parser.parseExpression(
|
||||
"Members.add(new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German'))")
|
||||
.getValue(societyContext)
|
||||
----
|
||||
======
|
||||
|
||||
@@ -110,8 +110,8 @@ potentially more efficient use cases if the `MethodHandle` target and parameters
|
||||
been fully bound prior to registration; however, partially bound handles are also
|
||||
supported.
|
||||
|
||||
Consider the `String#formatted(String, Object...)` instance method, which produces a
|
||||
message according to a template and a variable number of arguments.
|
||||
Consider the `String#formatted(Object...)` instance method, which produces a message
|
||||
according to a template and a variable number of arguments.
|
||||
|
||||
You can register and use the `formatted` method as a `MethodHandle`, as the following
|
||||
example shows:
|
||||
@@ -151,10 +151,10 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
As hinted above, binding a `MethodHandle` and registering the bound `MethodHandle` is also
|
||||
supported. This is likely to be more performant if both the target and all the arguments
|
||||
are bound. In that case no arguments are necessary in the SpEL expression, as the
|
||||
following example shows:
|
||||
As mentioned above, binding a `MethodHandle` and registering the bound `MethodHandle` is
|
||||
also supported. This is likely to be more performant if both the target and all the
|
||||
arguments are bound. In that case no arguments are necessary in the SpEL expression, as
|
||||
the following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@@ -168,9 +168,10 @@ Java::
|
||||
String template = "This is a %s message with %s words: <%s>";
|
||||
Object varargs = new Object[] { "prerecorded", 3, "Oh Hello World!", "ignored" };
|
||||
MethodHandle mh = MethodHandles.lookup().findVirtual(String.class, "formatted",
|
||||
MethodType.methodType(String.class, Object[].class))
|
||||
MethodType.methodType(String.class, Object[].class))
|
||||
.bindTo(template)
|
||||
.bindTo(varargs); //here we have to provide arguments in a single array binding
|
||||
// Here we have to provide the arguments in a single array binding:
|
||||
.bindTo(varargs);
|
||||
context.setVariable("message", mh);
|
||||
|
||||
// evaluates to "This is a prerecorded message with 3 words: <Oh Hello World!>"
|
||||
@@ -189,9 +190,10 @@ Kotlin::
|
||||
val varargs = arrayOf("prerecorded", 3, "Oh Hello World!", "ignored")
|
||||
|
||||
val mh = MethodHandles.lookup().findVirtual(String::class.java, "formatted",
|
||||
MethodType.methodType(String::class.java, Array<Any>::class.java))
|
||||
MethodType.methodType(String::class.java, Array<Any>::class.java))
|
||||
.bindTo(template)
|
||||
.bindTo(varargs) //here we have to provide arguments in a single array binding
|
||||
// Here we have to provide the arguments in a single array binding:
|
||||
.bindTo(varargs)
|
||||
context.setVariable("message", mh)
|
||||
|
||||
// evaluates to "This is a prerecorded message with 3 words: <Oh Hello World!>"
|
||||
|
||||
@@ -196,7 +196,7 @@ Kotlin::
|
||||
|
||||
When used as `org.springframework.validation.Validator`, `LocalValidatorFactoryBean`
|
||||
invokes the underlying `jakarta.validation.Validator`, and then adapts
|
||||
``ContraintViolation``s to ``FieldError``s, and registers them with the `Errors` object
|
||||
``ConstraintViolation``s to ``FieldError``s, and registers them with the `Errors` object
|
||||
passed into the `validate` method.
|
||||
|
||||
|
||||
@@ -431,7 +431,7 @@ Kotlin::
|
||||
|
||||
A `ConstraintViolation` on `Person.name()` is adapted to a `FieldError` with the following:
|
||||
|
||||
- Error codes `"Size.student.name"`, `"Size.name"`, `"Size.java.lang.String"`, and `"Size"`
|
||||
- Error codes `"Size.person.name"`, `"Size.name"`, `"Size.java.lang.String"`, and `"Size"`
|
||||
- Message arguments `"name"`, `10`, and `1` (the field name and the constraint attributes)
|
||||
- Default message "size must be between 1 and 10"
|
||||
|
||||
@@ -439,14 +439,14 @@ To customize the default message, you can add properties to
|
||||
xref:core/beans/context-introduction.adoc#context-functionality-messagesource[MessageSource]
|
||||
resource bundles using any of the above errors codes and message arguments. Note also that the
|
||||
message argument `"name"` is itself a `MessagreSourceResolvable` with error codes
|
||||
`"student.name"` and `"name"` and can customized too. For example:
|
||||
`"person.name"` and `"name"` and can customized too. For example:
|
||||
|
||||
Properties::
|
||||
+
|
||||
[source,properties,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
Size.student.name=Please, provide a {0} that is between {2} and {1} characters long
|
||||
student.name=username
|
||||
Size.person.name=Please, provide a {0} that is between {2} and {1} characters long
|
||||
person.name=username
|
||||
----
|
||||
|
||||
A `ConstraintViolation` on the `degrees` method parameter is adapted to a
|
||||
|
||||
@@ -280,10 +280,11 @@ Kotlin::
|
||||
|
||||
A portable format annotation API exists in the `org.springframework.format.annotation`
|
||||
package. You can use `@NumberFormat` to format `Number` fields such as `Double` and
|
||||
`Long`, and `@DateTimeFormat` to format `java.util.Date`, `java.util.Calendar`, `Long`
|
||||
(for millisecond timestamps) as well as JSR-310 `java.time`.
|
||||
`Long`, and `@DateTimeFormat` to format fields such as `java.util.Date`,
|
||||
`java.util.Calendar`, and `Long` (for millisecond timestamps) as well as JSR-310
|
||||
`java.time` types.
|
||||
|
||||
The following example uses `@DateTimeFormat` to format a `java.util.Date` as an ISO Date
|
||||
The following example uses `@DateTimeFormat` to format a `java.util.Date` as an ISO date
|
||||
(yyyy-MM-dd):
|
||||
|
||||
[tabs]
|
||||
@@ -309,6 +310,28 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
For further details, see the javadoc for
|
||||
{spring-framework-api}/format/annotation/DateTimeFormat.html[`@DateTimeFormat`] and
|
||||
{spring-framework-api}/format/annotation/NumberFormat.html[`@NumberFormat`].
|
||||
|
||||
[WARNING]
|
||||
====
|
||||
Style-based formatting and parsing rely on locale-sensitive patterns which may change
|
||||
depending on the Java runtime. Specifically, applications that rely on date, time, or
|
||||
number parsing and formatting may encounter incompatible changes in behavior when running
|
||||
on JDK 20 or higher.
|
||||
|
||||
Using an ISO standardized format or a concrete pattern that you control allows for
|
||||
reliable system-independent and locale-independent parsing and formatting of date, time,
|
||||
and number values.
|
||||
|
||||
For `@DateTimeFormat`, the use of fallback patterns can also help to address
|
||||
compatibility issues.
|
||||
|
||||
For further details, see the
|
||||
https://github.com/spring-projects/spring-framework/wiki/Date-and-Time-Formatting-with-JDK-20-and-higher[Date and Time Formatting with JDK 20 and higher]
|
||||
page in the Spring Framework wiki.
|
||||
====
|
||||
|
||||
[[format-FormatterRegistry-SPI]]
|
||||
== The `FormatterRegistry` SPI
|
||||
|
||||
@@ -198,7 +198,7 @@ methods it offers can be found in the {spring-framework-api}/validation/Errors.h
|
||||
Validators may also get locally invoked for the immediate validation of a given object,
|
||||
not involving a binding process. As of 6.1, this has been simplified through a new
|
||||
`Validator.validateObject(Object)` method which is available by default now, returning
|
||||
a simple ´Errors` representation which can be inspected: typically calling `hasErrors()`
|
||||
a simple `Errors` representation which can be inspected: typically calling `hasErrors()`
|
||||
or the new `failOnError` method for turning the error summary message into an exception
|
||||
(e.g. `validator.validateObject(myObject).failOnError(IllegalArgumentException::new)`).
|
||||
|
||||
|
||||
@@ -211,19 +211,28 @@ the JDBC driver. If the count is not available, the JDBC driver returns a value
|
||||
====
|
||||
In such a scenario, with automatic setting of values on an underlying `PreparedStatement`,
|
||||
the corresponding JDBC type for each value needs to be derived from the given Java type.
|
||||
While this usually works well, there is a potential for issues (for example, with Map-contained
|
||||
`null` values). Spring, by default, calls `ParameterMetaData.getParameterType` in such a
|
||||
case, which can be expensive with your JDBC driver. You should use a recent driver
|
||||
While this usually works well, there is a potential for issues (for example, with
|
||||
Map-contained `null` values). Spring, by default, calls `ParameterMetaData.getParameterType`
|
||||
in such a case, which can be expensive with your JDBC driver. You should use a recent driver
|
||||
version and consider setting the `spring.jdbc.getParameterType.ignore` property to `true`
|
||||
(as a JVM system property or via the
|
||||
xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism) if you encounter
|
||||
a performance issue (as reported on Oracle 12c, JBoss, and PostgreSQL).
|
||||
xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism)
|
||||
if you encounter a specific performance issue for your application.
|
||||
|
||||
Alternatively, you might consider specifying the corresponding JDBC types explicitly,
|
||||
either through a `BatchPreparedStatementSetter` (as shown earlier), through an explicit type
|
||||
array given to a `List<Object[]>` based call, through `registerSqlType` calls on a
|
||||
custom `MapSqlParameterSource` instance, or through a `BeanPropertySqlParameterSource`
|
||||
that derives the SQL type from the Java-declared property type even for a null value.
|
||||
As of 6.1.2, Spring bypasses the default `getParameterType` resolution on PostgreSQL and
|
||||
MS SQL Server. This is a common optimization to avoid further roundtrips to the DBMS just
|
||||
for parameter type resolution which is known to make a very significant difference on
|
||||
PostgreSQL and MS SQL Server specifically, in particular for batch operations. If you
|
||||
happen to see a side effect e.g. when setting a byte array to null without specific type
|
||||
indication, you may explicitly set the `spring.jdbc.getParameterType.ignore=false` flag
|
||||
as a system property (see above) to restore full `getParameterType` resolution.
|
||||
|
||||
Alternatively, you could consider specifying the corresponding JDBC types explicitly,
|
||||
either through a `BatchPreparedStatementSetter` (as shown earlier), through an explicit
|
||||
type array given to a `List<Object[]>` based call, through `registerSqlType` calls on a
|
||||
custom `MapSqlParameterSource` instance, through a `BeanPropertySqlParameterSource`
|
||||
that derives the SQL type from the Java-declared property type even for a null value, or
|
||||
through providing individual `SqlParameterValue` instances instead of plain null values.
|
||||
====
|
||||
|
||||
|
||||
|
||||
@@ -209,141 +209,26 @@ are passed in as a parameter to the stored procedure.
|
||||
|
||||
The `SqlReturnType` interface has a single method (named `getTypeValue`) that must be
|
||||
implemented. This interface is used as part of the declaration of an `SqlOutParameter`.
|
||||
The following example shows returning the value of an Oracle `STRUCT` object of the user
|
||||
The following example shows returning the value of a `java.sql.Struct` object of the user
|
||||
declared type `ITEM_TYPE`:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
public class TestItemStoredProcedure extends StoredProcedure {
|
||||
|
||||
public TestItemStoredProcedure(DataSource dataSource) {
|
||||
// ...
|
||||
declareParameter(new SqlOutParameter("item", OracleTypes.STRUCT, "ITEM_TYPE",
|
||||
(CallableStatement cs, int colIndx, int sqlType, String typeName) -> {
|
||||
STRUCT struct = (STRUCT) cs.getObject(colIndx);
|
||||
Object[] attr = struct.getAttributes();
|
||||
TestItem item = new TestItem();
|
||||
item.setId(((Number) attr[0]).longValue());
|
||||
item.setDescription((String) attr[1]);
|
||||
item.setExpirationDate((java.util.Date) attr[2]);
|
||||
return item;
|
||||
}));
|
||||
// ...
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
class TestItemStoredProcedure(dataSource: DataSource) : StoredProcedure() {
|
||||
|
||||
init {
|
||||
// ...
|
||||
declareParameter(SqlOutParameter("item", OracleTypes.STRUCT, "ITEM_TYPE") { cs, colIndx, sqlType, typeName ->
|
||||
val struct = cs.getObject(colIndx) as STRUCT
|
||||
val attr = struct.getAttributes()
|
||||
TestItem((attr[0] as Long, attr[1] as String, attr[2] as Date)
|
||||
})
|
||||
// ...
|
||||
}
|
||||
}
|
||||
----
|
||||
======
|
||||
include-code::./TestItemStoredProcedure[]
|
||||
|
||||
You can use `SqlTypeValue` to pass the value of a Java object (such as `TestItem`) to a
|
||||
stored procedure. The `SqlTypeValue` interface has a single method (named
|
||||
`createTypeValue`) that you must implement. The active connection is passed in, and you
|
||||
can use it to create database-specific objects, such as `StructDescriptor` instances
|
||||
or `ArrayDescriptor` instances. The following example creates a `StructDescriptor` instance:
|
||||
can use it to create database-specific objects, such as `java.sql.Struct` instances
|
||||
or `java.sql.Array` instances. The following example creates a `java.sql.Struct` instance:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
final TestItem testItem = new TestItem(123L, "A test item",
|
||||
new SimpleDateFormat("yyyy-M-d").parse("2010-12-31"));
|
||||
|
||||
SqlTypeValue value = new AbstractSqlTypeValue() {
|
||||
protected Object createTypeValue(Connection conn, int sqlType, String typeName) throws SQLException {
|
||||
StructDescriptor itemDescriptor = new StructDescriptor(typeName, conn);
|
||||
Struct item = new STRUCT(itemDescriptor, conn,
|
||||
new Object[] {
|
||||
testItem.getId(),
|
||||
testItem.getDescription(),
|
||||
new java.sql.Date(testItem.getExpirationDate().getTime())
|
||||
});
|
||||
return item;
|
||||
}
|
||||
};
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
val (id, description, expirationDate) = TestItem(123L, "A test item",
|
||||
SimpleDateFormat("yyyy-M-d").parse("2010-12-31"))
|
||||
|
||||
val value = object : AbstractSqlTypeValue() {
|
||||
override fun createTypeValue(conn: Connection, sqlType: Int, typeName: String?): Any {
|
||||
val itemDescriptor = StructDescriptor(typeName, conn)
|
||||
return STRUCT(itemDescriptor, conn,
|
||||
arrayOf(id, description, java.sql.Date(expirationDate.time)))
|
||||
}
|
||||
}
|
||||
----
|
||||
======
|
||||
include-code::./SqlTypeValueFactory[tag=struct,indent=0]
|
||||
|
||||
You can now add this `SqlTypeValue` to the `Map` that contains the input parameters for the
|
||||
`execute` call of the stored procedure.
|
||||
|
||||
Another use for the `SqlTypeValue` is passing in an array of values to an Oracle stored
|
||||
procedure. Oracle has its own internal `ARRAY` class that must be used in this case, and
|
||||
you can use the `SqlTypeValue` to create an instance of the Oracle `ARRAY` and populate
|
||||
it with values from the Java `ARRAY`, as the following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
final Long[] ids = new Long[] {1L, 2L};
|
||||
|
||||
SqlTypeValue value = new AbstractSqlTypeValue() {
|
||||
protected Object createTypeValue(Connection conn, int sqlType, String typeName) throws SQLException {
|
||||
ArrayDescriptor arrayDescriptor = new ArrayDescriptor(typeName, conn);
|
||||
ARRAY idArray = new ARRAY(arrayDescriptor, conn, ids);
|
||||
return idArray;
|
||||
}
|
||||
};
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
class TestItemStoredProcedure(dataSource: DataSource) : StoredProcedure() {
|
||||
|
||||
init {
|
||||
val ids = arrayOf(1L, 2L)
|
||||
val value = object : AbstractSqlTypeValue() {
|
||||
override fun createTypeValue(conn: Connection, sqlType: Int, typeName: String?): Any {
|
||||
val arrayDescriptor = ArrayDescriptor(typeName, conn)
|
||||
return ARRAY(arrayDescriptor, conn, ids)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
procedure. Oracle has an `createOracleArray` method on `OracleConnection` that you can
|
||||
access by unwrapping it. You can use the `SqlTypeValue` to create an array and populate
|
||||
it with values from the Java `java.sql.Array`, as the following example shows:
|
||||
|
||||
include-code::./SqlTypeValueFactory[tag=oracle-array,indent=0]
|
||||
|
||||
|
||||
@@ -323,12 +323,12 @@ use these alternative input classes.
|
||||
|
||||
The `SimpleJdbcCall` class uses metadata in the database to look up names of `in`
|
||||
and `out` parameters so that you do not have to explicitly declare them. You can
|
||||
declare parameters if you prefer to do that or if you have parameters (such as `ARRAY`
|
||||
or `STRUCT`) that do not have an automatic mapping to a Java class. The first example
|
||||
shows a simple procedure that returns only scalar values in `VARCHAR` and `DATE` format
|
||||
from a MySQL database. The example procedure reads a specified actor entry and returns
|
||||
`first_name`, `last_name`, and `birth_date` columns in the form of `out` parameters.
|
||||
The following listing shows the first example:
|
||||
declare parameters if you prefer to do that or if you have parameters that do not
|
||||
have an automatic mapping to a Java class. The first example shows a simple procedure
|
||||
that returns only scalar values in `VARCHAR` and `DATE` format from a MySQL database.
|
||||
The example procedure reads a specified actor entry and returns `first_name`,
|
||||
`last_name`, and `birth_date` columns in the form of `out` parameters. The following
|
||||
listing shows the first example:
|
||||
|
||||
[source,sql,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ As of Spring Framework 5.2, the default configuration also provides support for
|
||||
Vavr's `Try` method to trigger transaction rollbacks when it returns a 'Failure'.
|
||||
This allows you to handle functional-style errors using Try and have the transaction
|
||||
automatically rolled back in case of a failure. For more information on Vavr's Try,
|
||||
refer to the https://docs.vavr.io/#_try[official Vavr documentation].
|
||||
refer to the {vavr-docs}/#_try[official Vavr documentation].
|
||||
Here's an example of how to use Vavr's Try with a transactional method:
|
||||
|
||||
[tabs]
|
||||
|
||||
@@ -332,7 +332,7 @@ metadata, such as the argument names. The following table describes the items ma
|
||||
available to the context so that you can use them for key and conditional computations:
|
||||
|
||||
[[cache-spel-context-tbl]]
|
||||
.Cache SpEL available metadata
|
||||
.Cache metadata available in SpEL expressions
|
||||
|===
|
||||
| Name| Location| Description| Example
|
||||
|
||||
@@ -358,7 +358,7 @@ available to the context so that you can use them for key and conditional comput
|
||||
|
||||
| `args`
|
||||
| Root object
|
||||
| The arguments (as array) used for invoking the target
|
||||
| The arguments (as an object array) used for invoking the target
|
||||
| `#root.args[0]`
|
||||
|
||||
| `caches`
|
||||
@@ -368,9 +368,10 @@ available to the context so that you can use them for key and conditional comput
|
||||
|
||||
| Argument name
|
||||
| Evaluation context
|
||||
| Name of any of the method arguments. If the names are not available
|
||||
(perhaps due to having no debug information), the argument names are also available under the `#a<#arg>`
|
||||
where `#arg` stands for the argument index (starting from `0`).
|
||||
| The name of a particular method argument. If the names are not available
|
||||
(for example, because the code was compiled without the `-parameters` flag), individual
|
||||
arguments are also available using the `#a<#arg>` syntax where `<#arg>` stands for the
|
||||
argument index (starting from 0).
|
||||
| `#iban` or `#a0` (you can also use `#p0` or `#p<#arg>` notation as an alias).
|
||||
|
||||
| `result`
|
||||
@@ -524,7 +525,7 @@ To enable caching annotations add the annotation `@EnableCaching` to one of your
|
||||
----
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
public class AppConfig {
|
||||
class AppConfig {
|
||||
|
||||
@Bean
|
||||
CacheManager cacheManager() {
|
||||
|
||||
@@ -65,7 +65,7 @@ a "shared objects file" source, as shown in the following example:
|
||||
If CDS can't be enabled or if you have a large number of classes that are not loaded from the cache, make sure that
|
||||
the following conditions are fulfilled when creating and using the archive:
|
||||
|
||||
- The very same JVM must used.
|
||||
- The very same JVM must be used.
|
||||
- The classpath must be specified as a list of JARs, and avoid the usage of directories and `*` wildcard characters.
|
||||
- The timestamps of the JARs must be preserved.
|
||||
- When using the archive, the classpath must be the same than the one used to create the archive, in the same order.
|
||||
|
||||
@@ -19,6 +19,8 @@ A checkpoint can be created on demand, for example using a command like `jcmd ap
|
||||
|
||||
WARNING: Leveraging checkpoint/restore of a running application typically requires additional lifecycle management to gracefully stop and start using resources like files or sockets and stop active threads.
|
||||
|
||||
WARNING: Be aware that when defining scheduling tasks at a fixed rate, for example with an annotation like `@Scheduled(fixedRate = 5000)`, all missed executions between checkpoint and restore will be performed when the JVM is restored with on-demand checkpoint/restore. If this is not the behavior you want, it is recommended to schedule tasks at a fixed delay (for example with `@Scheduled(fixedDelay = 5000)`) or with a cron expression as those are calculated after every task execution.
|
||||
|
||||
NOTE: If the checkpoint is created on a warmed-up JVM, the restored JVM will be equally warmed-up, allowing potentially peak performance immediately. This method typically requires access to remote services, and thus requires some level of platform integration.
|
||||
|
||||
== Automatic checkpoint/restore at startup
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[[observability]]
|
||||
= Observability Support
|
||||
|
||||
Micrometer defines an https://micrometer.io/docs/observation[Observation concept that enables both Metrics and Traces] in applications.
|
||||
Micrometer defines an {micrometer-docs}/observation.html[Observation concept that enables both Metrics and Traces] in applications.
|
||||
Metrics support offers a way to create timers, gauges, or counters for collecting statistics about the runtime behavior of your application.
|
||||
Metrics can help you to track error rates, usage patterns, performance, and more.
|
||||
Traces provide a holistic view of an entire system, crossing application boundaries; you can zoom in on particular user requests and follow their entire completion across applications.
|
||||
@@ -38,7 +38,7 @@ As outlined xref:integration/observability.adoc[at the beginning of this section
|
||||
|===
|
||||
|
||||
NOTE: Observations are using Micrometer's official naming convention, but Metrics names will be automatically converted
|
||||
https://micrometer.io/docs/concepts#_naming_meters[to the format preferred by the monitoring system backend]
|
||||
{micrometer-docs}/concepts/naming.html[to the format preferred by the monitoring system backend]
|
||||
(Prometheus, Atlas, Graphite, InfluxDB...).
|
||||
|
||||
|
||||
@@ -162,6 +162,8 @@ include-code::./JmsTemplatePublish[]
|
||||
|
||||
It uses the `io.micrometer.jakarta9.instrument.jms.DefaultJmsPublishObservationConvention` by default, backed by the `io.micrometer.jakarta9.instrument.jms.JmsPublishObservationContext`.
|
||||
|
||||
Similar observations are recorded with `@JmsListener` annotated methods when response messages are returned from the listener method.
|
||||
|
||||
[[observability.jms.process]]
|
||||
=== JMS message Processing instrumentation
|
||||
|
||||
@@ -366,7 +368,7 @@ This means that during the execution of that task, the ThreadLocals and logging
|
||||
|
||||
If the application globally configures a custom `ApplicationEventMulticaster` with a strategy that schedules event processing on different threads, this is no longer true.
|
||||
All `@EventListener` methods will be processed on a different thread, outside the main event publication thread.
|
||||
In these cases, the https://micrometer.io/docs/contextPropagation[Micrometer Context Propagation library] can help propagate such values and better correlate the processing of the events.
|
||||
In these cases, the {micrometer-context-propagation-docs}/[Micrometer Context Propagation library] can help propagate such values and better correlate the processing of the events.
|
||||
The application can configure the chosen `TaskExecutor` to use a `ContextPropagatingTaskDecorator` that decorates tasks and propagates context.
|
||||
For this to work, the `io.micrometer:context-propagation` library must be present on the classpath:
|
||||
|
||||
|
||||
@@ -289,7 +289,7 @@ String result = restClient.get() <1>
|
||||
.uri("https://example.com/this-url-does-not-exist") <1>
|
||||
.retrieve()
|
||||
.onStatus(HttpStatusCode::is4xxClientError, (request, response) -> { <2>
|
||||
throw new MyCustomRuntimeException(response.getStatusCode(), response.getHeaders()) <3>
|
||||
throw new MyCustomRuntimeException(response.getStatusCode(), response.getHeaders()); <3>
|
||||
})
|
||||
.body(String.class);
|
||||
----
|
||||
|
||||
@@ -733,7 +733,7 @@ reached, does the executor create a new thread beyond the core size. If the max
|
||||
has also been reached, then the executor rejects the task.
|
||||
|
||||
By default, the queue is unbounded, but this is rarely the desired configuration,
|
||||
because it can lead to `OutOfMemoryErrors` if enough tasks are added to that queue while
|
||||
because it can lead to `OutOfMemoryError` if enough tasks are added to that queue while
|
||||
all pool threads are busy. Furthermore, if the queue is unbounded, the max size has
|
||||
no effect at all. Since the executor always tries the queue before creating a new
|
||||
thread beyond the core size, a queue must have a finite capacity for the thread pool to
|
||||
|
||||
@@ -31,5 +31,5 @@ provide convenience methods that delegate to the aforementioned methods in
|
||||
The `spring-jdbc` module provides support for configuring and launching an embedded
|
||||
database, which you can use in integration tests that interact with a database.
|
||||
For details, see xref:data-access/jdbc/embedded-database-support.adoc[Embedded Database Support]
|
||||
and <<data-access.adoc#jdbc-embedded-database-dao-testing, Testing Data Access
|
||||
Logic with an Embedded Database>>.
|
||||
and xref:data-access/jdbc/embedded-database-support.adoc#jdbc-embedded-database-dao-testing[Testing Data Access
|
||||
Logic with an Embedded Database].
|
||||
|
||||
@@ -193,7 +193,7 @@ Kotlin::
|
||||
[[webtestclient-fn-config]]
|
||||
=== Bind to Router Function
|
||||
|
||||
This setup allows you to test <<web-reactive.adoc#webflux-fn, functional endpoints>> via
|
||||
This setup allows you to test xref:web/webflux-functional.adoc[functional endpoints] via
|
||||
mock request and response objects, without a running server.
|
||||
|
||||
For WebFlux, use the following which delegates to `RouterFunctions.toWebHandler` to
|
||||
@@ -339,6 +339,19 @@ Java::
|
||||
spec -> spec.expectHeader().contentType(MediaType.APPLICATION_JSON)
|
||||
);
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
client.get().uri("/persons/1")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
.expectAll(
|
||||
{ spec -> spec.expectStatus().isOk() },
|
||||
{ spec -> spec.expectHeader().contentType(MediaType.APPLICATION_JSON) }
|
||||
)
|
||||
----
|
||||
======
|
||||
|
||||
You can then choose to decode the response body through one of the following:
|
||||
@@ -704,4 +717,3 @@ Kotlin::
|
||||
.andExpect(model().attribute("string", "a string value"));
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
[[webflux-cors]]
|
||||
= CORS
|
||||
|
||||
[.small]#xref:web/webmvc-cors.adoc[See equivalent in the Servlet stack]#
|
||||
|
||||
Spring WebFlux lets you handle CORS (Cross-Origin Resource Sharing). This section
|
||||
@@ -364,7 +365,7 @@ Kotlin::
|
||||
|
||||
You can apply CORS support through the built-in
|
||||
{spring-framework-api}/web/cors/reactive/CorsWebFilter.html[`CorsWebFilter`], which is a
|
||||
good fit with <<webflux-fn, functional endpoints>>.
|
||||
good fit with xref:web/webflux-functional.adoc[functional endpoints].
|
||||
|
||||
NOTE: If you try to use the `CorsFilter` with Spring Security, keep in mind that Spring
|
||||
Security has {docs-spring-security}/servlet/integrations/cors.html[built-in support] for
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
[[webflux-fn]]
|
||||
= Functional Endpoints
|
||||
|
||||
[.small]#xref:web/webmvc-functional.adoc[See equivalent in the Servlet stack]#
|
||||
|
||||
Spring WebFlux includes WebFlux.fn, a lightweight functional programming model in which functions
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
[[webflux-websocket]]
|
||||
= WebSockets
|
||||
|
||||
[.small]#xref:web/websocket.adoc[See equivalent in the Servlet stack]#
|
||||
|
||||
This part of the reference documentation covers support for reactive-stack WebSocket
|
||||
|
||||
@@ -91,7 +91,7 @@ class WebConfig : WebFluxConfigurer {
|
||||
[.small]#xref:web/webmvc/mvc-config/conversion.adoc[See equivalent in the Servlet stack]#
|
||||
|
||||
By default, formatters for various number and date types are installed, along with support
|
||||
for customization via `@NumberFormat` and `@DateTimeFormat` on fields.
|
||||
for customization via `@NumberFormat` and `@DateTimeFormat` on fields and parameters.
|
||||
|
||||
To register custom formatters and converters in Java config, use the following:
|
||||
|
||||
@@ -356,7 +356,6 @@ which customizes Jackson's default properties with the following ones:
|
||||
|
||||
It also automatically registers the following well-known modules if they are detected on the classpath:
|
||||
|
||||
* {jackson-github-org}/jackson-datatype-joda[`jackson-datatype-joda`]: Support for Joda-Time types.
|
||||
* {jackson-github-org}/jackson-datatype-jsr310[`jackson-datatype-jsr310`]: Support for Java 8 Date and Time API types.
|
||||
* {jackson-github-org}/jackson-datatype-jdk8[`jackson-datatype-jdk8`]: Support for other Java 8 types, such as `Optional`.
|
||||
* {jackson-github-org}/jackson-module-kotlin[`jackson-module-kotlin`]: Support for Kotlin classes and data classes.
|
||||
@@ -688,40 +687,7 @@ You can customize options related to path matching. For details on the individua
|
||||
{spring-framework-api}/web/reactive/config/PathMatchConfigurer.html[`PathMatchConfigurer`] javadoc.
|
||||
The following example shows how to use `PathMatchConfigurer`:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
@Configuration
|
||||
@EnableWebFlux
|
||||
public class WebConfig implements WebFluxConfigurer {
|
||||
|
||||
@Override
|
||||
public void configurePathMatch(PathMatchConfigurer configurer) {
|
||||
configurer.addPathPrefix(
|
||||
"/api", HandlerTypePredicate.forAnnotation(RestController.class));
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
@Configuration
|
||||
@EnableWebFlux
|
||||
class WebConfig : WebFluxConfigurer {
|
||||
|
||||
@Override
|
||||
fun configurePathMatch(configurer: PathMatchConfigurer) {
|
||||
configurer.addPathPrefix(
|
||||
"/api", HandlerTypePredicate.forAnnotation(RestController::class.java))
|
||||
}
|
||||
}
|
||||
----
|
||||
======
|
||||
include-code::./WebConfig[]
|
||||
|
||||
[TIP]
|
||||
====
|
||||
|
||||
@@ -77,7 +77,7 @@ and others) and is equivalent to `required=false`.
|
||||
| For access to a part in a `multipart/form-data` request. Supports reactive types.
|
||||
See xref:web/webflux/controller/ann-methods/multipart-forms.adoc[Multipart Content] and xref:web/webflux/reactive-spring.adoc#webflux-multipart[Multipart Data].
|
||||
|
||||
| `java.util.Map`, `org.springframework.ui.Model`, and `org.springframework.ui.ModelMap`.
|
||||
| `java.util.Map` or `org.springframework.ui.Model`
|
||||
| For access to the model that is used in HTML controllers and is exposed to templates as
|
||||
part of view rendering.
|
||||
|
||||
@@ -89,9 +89,9 @@ and others) and is equivalent to `required=false`.
|
||||
Note that use of `@ModelAttribute` is optional -- for example, to set its attributes.
|
||||
See "`Any other argument`" later in this table.
|
||||
|
||||
| `Errors`, `BindingResult`
|
||||
| `Errors` or `BindingResult`
|
||||
| For access to errors from validation and data binding for a command object, i.e. a
|
||||
`@ModelAttribute` argument. An `Errors`, or `BindingResult` argument must be declared
|
||||
`@ModelAttribute` argument. An `Errors` or `BindingResult` argument must be declared
|
||||
immediately after the validated method argument.
|
||||
|
||||
| `SessionStatus` + class-level `@SessionAttributes`
|
||||
|
||||
@@ -13,7 +13,7 @@ xref:web/webflux/controller/ann-methods/multipart-forms.adoc[@RequestPart] argum
|
||||
resolvers validate a method argument individually if the method parameter is annotated
|
||||
with Jakarta `@Valid` or Spring's `@Validated`, _AND_ there is no `Errors` or
|
||||
`BindingResult` parameter immediately after, _AND_ method validation is not needed (to be
|
||||
discussed next). The exception raised in this case is `MethodArgumentNotValidException`.
|
||||
discussed next). The exception raised in this case is `WebExchangeBindException`.
|
||||
|
||||
2. When `@Constraint` annotations such as `@Min`, `@NotBlank` and others are declared
|
||||
directly on method parameters, or on the method (for the return value), then method
|
||||
@@ -21,7 +21,7 @@ validation must be applied, and that supersedes validation at the method argumen
|
||||
because method validation covers both method parameter constraints and nested constraints
|
||||
via `@Valid`. The exception raised in this case is `HandlerMethodValidationException`.
|
||||
|
||||
Applications must handle both `MethodArgumentNotValidException` and
|
||||
Applications must handle both `WebExchangeBindException` and
|
||||
`HandlerMethodValidationException` as either may be raised depending on the controller
|
||||
method signature. The two exceptions, however are designed to be very similar, and can be
|
||||
handled with almost identical code. The main difference is that the former is for a single
|
||||
@@ -39,7 +39,7 @@ method parameters with an `Errors` immediately after. If there are validation er
|
||||
any other method parameter then `HandlerMethodValidationException` is raised.
|
||||
|
||||
You can configure a `Validator` globally through the
|
||||
xref:web/webflux/config.adoc#webflux-config-validation[WebMvc config], or locally
|
||||
xref:web/webflux/config.adoc#webflux-config-validation[WebFlux config], or locally
|
||||
through an xref:web/webflux/controller/ann-initbinder.adoc[@InitBinder] method in an
|
||||
`@Controller` or `@ControllerAdvice`. You can also use multiple validators.
|
||||
|
||||
@@ -49,8 +49,8 @@ through an AOP proxy. In order to take advantage of the Spring MVC built-in supp
|
||||
method validation added in Spring Framework 6.1, you need to remove the class level
|
||||
`@Validated` annotation from the controller.
|
||||
|
||||
The xref:web/webmvc/mvc-ann-rest-exceptions.adoc[Error Responses] section provides further
|
||||
details on how `MethodArgumentNotValidException` and `HandlerMethodValidationException`
|
||||
The xref:web/webflux/ann-rest-exceptions.adoc[Error Responses] section provides further
|
||||
details on how `WebExchangeBindException` and `HandlerMethodValidationException`
|
||||
are handled, and also how their rendering can be customized through a `MessageSource` and
|
||||
locale and language specific resource bundles.
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ On that foundation, Spring WebFlux provides a choice of two programming models:
|
||||
from the `spring-web` module. Both Spring MVC and WebFlux controllers support reactive
|
||||
(Reactor and RxJava) return types, and, as a result, it is not easy to tell them apart. One notable
|
||||
difference is that WebFlux also supports reactive `@RequestBody` arguments.
|
||||
* <<webflux-fn>>: Lambda-based, lightweight, and functional programming model. You can think of
|
||||
* xref:web/webflux-functional.adoc[Functional Endpoints]: Lambda-based, lightweight, and functional programming model. You can think of
|
||||
this as a small library or a set of utilities that an application can use to route and
|
||||
handle requests. The big difference with annotated controllers is that the application
|
||||
is in charge of request handling from start to finish versus declaring intent through
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
[[mvc-cors]]
|
||||
= CORS
|
||||
|
||||
[.small]#xref:web/webflux-cors.adoc[See equivalent in the Reactive stack]#
|
||||
|
||||
Spring MVC lets you handle CORS (Cross-Origin Resource Sharing). This section
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
[[webmvc-fn]]
|
||||
= Functional Endpoints
|
||||
[.small]#<<web-reactive.adoc#webflux-fn, See equivalent in the Reactive stack>>#
|
||||
|
||||
[.small]#xref:web/webflux-functional.adoc[See equivalent in the Reactive stack]#
|
||||
|
||||
Spring Web MVC includes WebMvc.fn, a lightweight functional programming model in which functions
|
||||
are used to route and handle requests and contracts are designed for immutability.
|
||||
|
||||
@@ -26,7 +26,7 @@ available through the `ServletRequest.getParameter{asterisk}()` family of method
|
||||
|
||||
|
||||
|
||||
[[forwarded-headers]]
|
||||
[[filters-forwarded-headers]]
|
||||
== Forwarded Headers
|
||||
[.small]#xref:web/webflux/reactive-spring.adoc#webflux-forwarded-headers[See equivalent in the Reactive stack]#
|
||||
|
||||
|
||||
@@ -445,9 +445,8 @@ directly. For example:
|
||||
}
|
||||
----
|
||||
|
||||
For more details, see the
|
||||
https://micrometer.io/docs/contextPropagation[documentation] of the Micrometer Context
|
||||
Propagation library.
|
||||
For more details, see the {micrometer-context-propagation-docs}/[documentation] of the
|
||||
Micrometer Context Propagation library.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
[.small]#xref:web/webflux/config.adoc#webflux-config-conversion[See equivalent in the Reactive stack]#
|
||||
|
||||
By default, formatters for various number and date types are installed, along with support
|
||||
for customization via `@NumberFormat` and `@DateTimeFormat` on fields.
|
||||
for customization via `@NumberFormat` and `@DateTimeFormat` on fields and parameters.
|
||||
|
||||
To register custom formatters and converters in Java config, use the following:
|
||||
|
||||
|
||||
@@ -73,7 +73,6 @@ This builder customizes Jackson's default properties as follows:
|
||||
|
||||
It also automatically registers the following well-known modules if they are detected on the classpath:
|
||||
|
||||
* {jackson-github-org}/jackson-datatype-joda[jackson-datatype-joda]: Support for Joda-Time types.
|
||||
* {jackson-github-org}/jackson-datatype-jsr310[jackson-datatype-jsr310]: Support for Java 8 Date and Time API types.
|
||||
* {jackson-github-org}/jackson-datatype-jdk8[jackson-datatype-jdk8]: Support for other Java 8 types, such as `Optional`.
|
||||
* {jackson-github-org}/jackson-module-kotlin[`jackson-module-kotlin`]: Support for Kotlin classes and data classes.
|
||||
|
||||
+4
@@ -34,6 +34,10 @@ Kotlin::
|
||||
You can use the xref:web/webmvc/mvc-config/message-converters.adoc[Message Converters] option of the xref:web/webmvc/mvc-config.adoc[MVC Config] to
|
||||
configure or customize message conversion.
|
||||
|
||||
NOTE: Form data should be read using xref:web/webmvc/mvc-controller/ann-methods/requestparam.adoc[`@RequestParam`],
|
||||
not with `@RequestBody` which can't always be used reliably since in the Servlet API, request parameter
|
||||
access causes the request body to be parsed, and it can't be read again.
|
||||
|
||||
You can use `@RequestBody` in combination with `jakarta.validation.Valid` or Spring's
|
||||
`@Validated` annotation, both of which cause Standard Bean Validation to be applied.
|
||||
By default, validation errors cause a `MethodArgumentNotValidException`, which is turned
|
||||
|
||||
+43
-1
@@ -61,7 +61,7 @@ Kotlin::
|
||||
|
||||
By default, method parameters that use this annotation are required, but you can specify that
|
||||
a method parameter is optional by setting the `@RequestParam` annotation's `required` flag to
|
||||
`false` or by declaring the argument with an `java.util.Optional` wrapper.
|
||||
`false` or by declaring the argument with a `java.util.Optional` wrapper.
|
||||
|
||||
Type conversion is automatically applied if the target method parameter type is not
|
||||
`String`. See xref:web/webmvc/mvc-controller/ann-methods/typeconversion.adoc[Type Conversion].
|
||||
@@ -72,6 +72,48 @@ values for the same parameter name.
|
||||
When an `@RequestParam` annotation is declared as a `Map<String, String>` or
|
||||
`MultiValueMap<String, String>`, without a parameter name specified in the annotation,
|
||||
then the map is populated with the request parameter values for each given parameter name.
|
||||
The following example shows how to do so with form data processing:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
@Controller
|
||||
@RequestMapping("/pets")
|
||||
class EditPetForm {
|
||||
|
||||
// ...
|
||||
|
||||
@PostMapping(path = "/process", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||
public String processForm(@RequestParam MultiValueMap<String, String> params) {
|
||||
// ...
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
----
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
@Controller
|
||||
@RequestMapping("/pets")
|
||||
class EditPetForm {
|
||||
|
||||
// ...
|
||||
|
||||
@PostMapping("/process", consumes = [MediaType.APPLICATION_FORM_URLENCODED_VALUE])
|
||||
fun processForm(@RequestParam params: MultiValueMap<String, String>): String {
|
||||
// ...
|
||||
}
|
||||
|
||||
// ...
|
||||
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
Note that use of `@RequestParam` is optional (for example, to set its attributes).
|
||||
By default, any argument that is a simple value type (as determined by
|
||||
|
||||
+3
-1
@@ -40,7 +40,9 @@ content of the provided resource to the response `OutputStream`. Note that the
|
||||
`InputStream` should be lazily retrieved by the `Resource` handle in order to reliably
|
||||
close it after it has been copied to the response. If you are using `InputStreamResource`
|
||||
for such a purpose, make sure to construct it with an on-demand `InputStreamSource`
|
||||
(e.g. through a lambda expression that retrieves the actual `InputStream`).
|
||||
(e.g. through a lambda expression that retrieves the actual `InputStream`). Also, custom
|
||||
subclasses of `InputStreamResource` are only supported in combination with a custom
|
||||
`contentLength()` implementation which avoids consuming the stream for that purpose.
|
||||
|
||||
Spring MVC supports using a single value xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-reactive-types[reactive type]
|
||||
to produce the `ResponseEntity` asynchronously, and/or single and multi-value reactive
|
||||
|
||||
@@ -119,7 +119,7 @@ Some example patterns:
|
||||
* `+"/resources/*.png"+` - match zero or more characters in a path segment
|
||||
* `+"/resources/**"+` - match multiple path segments
|
||||
* `+"/projects/{project}/versions"+` - match a path segment and capture it as a variable
|
||||
* `+"/projects/{project:[a-z]+}/versions"+` - match and capture a variable with a regex
|
||||
* `++"/projects/{project:[a-z]+}/versions"++` - match and capture a variable with a regex
|
||||
|
||||
Captured URI variables can be accessed with `@PathVariable`. For example:
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
[[websocket]]
|
||||
= WebSockets
|
||||
:page-section-summary-toc: 1
|
||||
|
||||
[.small]#xref:web/webflux-websocket.adoc[See equivalent in the Reactive stack]#
|
||||
|
||||
This part of the reference documentation covers support for Servlet stack, WebSocket
|
||||
|
||||
@@ -238,7 +238,7 @@ heartbeats to be exchanged, the SockJS heartbeats are disabled.
|
||||
|
||||
The Spring SockJS support also lets you configure the `TaskScheduler` to
|
||||
schedule heartbeats tasks. The task scheduler is backed by a thread pool,
|
||||
with default settings based on the number of available processors. Your
|
||||
with default settings based on the number of available processors. You
|
||||
should consider customizing the settings according to your specific needs.
|
||||
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ input is ignored. This is in contrast to property binding which by default binds
|
||||
request parameter for which there is a matching property.
|
||||
|
||||
If neither a dedicated model object nor constructor binding is sufficient, and you must
|
||||
use property binding, we strongy recommend registering `allowedFields` patterns (case
|
||||
use property binding, we strongly recommend registering `allowedFields` patterns (case
|
||||
sensitive) on `WebDataBinder` in order to prevent unexpected properties from being set.
|
||||
For example:
|
||||
|
||||
|
||||
@@ -389,7 +389,7 @@ template.
|
||||
encode URI component value _after_ URI variables are expanded.
|
||||
* `NONE`: No encoding is applied.
|
||||
|
||||
The `RestTemplate` is set to `EncodingMode.URI_COMPONENT` for historic
|
||||
The `RestTemplate` is set to `EncodingMode.URI_COMPONENT` for historical
|
||||
reasons and for backwards compatibility. The `WebClient` relies on the default value
|
||||
in `DefaultUriBuilderFactory`, which was changed from `EncodingMode.URI_COMPONENT` in
|
||||
5.0.x to `EncodingMode.TEMPLATE_AND_VALUES` in 5.1.
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"@antora/collector-extension": "1.0.0-alpha.3",
|
||||
"@asciidoctor/tabs": "1.0.0-beta.6",
|
||||
"@springio/antora-extensions": "1.11.1",
|
||||
"fast-xml-parser": "4.5.2",
|
||||
"@springio/asciidoctor-extensions": "1.0.0-alpha.10"
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -263,10 +263,10 @@ JavaPoet 1.13.0 is licensed under the Apache License, version 2.0, the text of
|
||||
which is included above.
|
||||
|
||||
|
||||
>>> Objenesis 3.2 (org.objenesis:objenesis:3.2):
|
||||
>>> Objenesis 3.4 (org.objenesis:objenesis:3.4):
|
||||
|
||||
Per the LICENSE file in the Objenesis ZIP distribution downloaded from
|
||||
http://objenesis.org/download.html, Objenesis 3.2 is licensed under the
|
||||
http://objenesis.org/download.html, Objenesis 3.4 is licensed under the
|
||||
Apache License, version 2.0, the text of which is included above.
|
||||
|
||||
Per the NOTICE file in the Objenesis ZIP distribution downloaded from
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.dataaccess.jdbc.jdbccomplextypes;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
|
||||
import oracle.jdbc.driver.OracleConnection;
|
||||
|
||||
import org.springframework.jdbc.core.SqlTypeValue;
|
||||
import org.springframework.jdbc.core.support.AbstractSqlTypeValue;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
class SqlTypeValueFactory {
|
||||
|
||||
void createStructSample() throws ParseException {
|
||||
// tag::struct[]
|
||||
TestItem testItem = new TestItem(123L, "A test item",
|
||||
new SimpleDateFormat("yyyy-M-d").parse("2010-12-31"));
|
||||
|
||||
SqlTypeValue value = new AbstractSqlTypeValue() {
|
||||
protected Object createTypeValue(Connection connection, int sqlType, String typeName) throws SQLException {
|
||||
Object[] item = new Object[] { testItem.getId(), testItem.getDescription(),
|
||||
new java.sql.Date(testItem.getExpirationDate().getTime()) };
|
||||
return connection.createStruct(typeName, item);
|
||||
}
|
||||
};
|
||||
// end::struct[]
|
||||
}
|
||||
|
||||
void createOracleArray() {
|
||||
// tag::oracle-array[]
|
||||
Long[] ids = new Long[] {1L, 2L};
|
||||
|
||||
SqlTypeValue value = new AbstractSqlTypeValue() {
|
||||
protected Object createTypeValue(Connection conn, int sqlType, String typeName) throws SQLException {
|
||||
return conn.unwrap(OracleConnection.class).createOracleArray(typeName, ids);
|
||||
}
|
||||
};
|
||||
// end::oracle-array[]
|
||||
}
|
||||
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.dataaccess.jdbc.jdbccomplextypes;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
class TestItem {
|
||||
|
||||
private Long id;
|
||||
|
||||
private String description;
|
||||
|
||||
private Date expirationDate;
|
||||
|
||||
public TestItem() {
|
||||
}
|
||||
|
||||
public TestItem(Long id, String description, Date expirationDate) {
|
||||
this.id = id;
|
||||
this.description = description;
|
||||
this.expirationDate = expirationDate;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Date getExpirationDate() {
|
||||
return this.expirationDate;
|
||||
}
|
||||
|
||||
public void setExpirationDate(Date expirationDate) {
|
||||
this.expirationDate = expirationDate;
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.dataaccess.jdbc.jdbccomplextypes;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.Struct;
|
||||
import java.sql.Types;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.jdbc.core.SqlOutParameter;
|
||||
import org.springframework.jdbc.object.StoredProcedure;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class TestItemStoredProcedure extends StoredProcedure {
|
||||
|
||||
public TestItemStoredProcedure(DataSource dataSource) {
|
||||
super(dataSource, "get_item");
|
||||
declareParameter(new SqlOutParameter("item", Types.STRUCT, "ITEM_TYPE",
|
||||
(CallableStatement cs, int colIndx, int sqlType, String typeName) -> {
|
||||
Struct struct = (Struct) cs.getObject(colIndx);
|
||||
Object[] attr = struct.getAttributes();
|
||||
TestItem item = new TestItem();
|
||||
item.setId(((Number) attr[0]).longValue());
|
||||
item.setDescription((String) attr[1]);
|
||||
item.setExpirationDate((java.util.Date) attr[2]);
|
||||
return item;
|
||||
}));
|
||||
// ...
|
||||
}
|
||||
|
||||
}
|
||||
+4
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.docs.integration.observability.config.conventions;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import io.micrometer.common.KeyValue;
|
||||
import io.micrometer.common.KeyValues;
|
||||
|
||||
@@ -34,7 +36,7 @@ public class CustomServerRequestObservationConvention implements ServerRequestOb
|
||||
@Override
|
||||
public String getContextualName(ServerRequestObservationContext context) {
|
||||
// will be used for the trace name
|
||||
return "http " + context.getCarrier().getMethod().toLowerCase();
|
||||
return "http " + context.getCarrier().getMethod().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webflux.webfluxconfigpathmatching;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.method.HandlerTypePredicate;
|
||||
import org.springframework.web.reactive.config.EnableWebFlux;
|
||||
import org.springframework.web.reactive.config.PathMatchConfigurer;
|
||||
import org.springframework.web.reactive.config.WebFluxConfigurer;
|
||||
|
||||
@Configuration
|
||||
@EnableWebFlux
|
||||
public class WebConfig implements WebFluxConfigurer {
|
||||
|
||||
@Override
|
||||
public void configurePathMatching(PathMatchConfigurer configurer) {
|
||||
configurer.addPathPrefix(
|
||||
"/api", HandlerTypePredicate.forAnnotation(RestController.class));
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.dataaccess.jdbc.jdbccomplextypes
|
||||
|
||||
import oracle.jdbc.driver.OracleConnection
|
||||
import org.springframework.jdbc.core.SqlTypeValue
|
||||
import org.springframework.jdbc.core.support.AbstractSqlTypeValue
|
||||
import java.sql.Connection
|
||||
import java.sql.Date
|
||||
import java.text.SimpleDateFormat
|
||||
|
||||
@Suppress("unused")
|
||||
class SqlTypeValueFactory {
|
||||
|
||||
fun createStructSample(): AbstractSqlTypeValue {
|
||||
// tag::struct[]
|
||||
val testItem = TestItem(123L, "A test item",
|
||||
SimpleDateFormat("yyyy-M-d").parse("2010-12-31"))
|
||||
|
||||
val value = object : AbstractSqlTypeValue() {
|
||||
override fun createTypeValue(connection: Connection, sqlType: Int, typeName: String?): Any {
|
||||
val item = arrayOf<Any>(testItem.id, testItem.description,
|
||||
Date(testItem.expirationDate.time))
|
||||
return connection.createStruct(typeName, item)
|
||||
}
|
||||
}
|
||||
// end::struct[]
|
||||
return value
|
||||
}
|
||||
|
||||
fun createOracleArray() : SqlTypeValue {
|
||||
// tag::oracle-array[]
|
||||
val ids = arrayOf(1L, 2L)
|
||||
val value: SqlTypeValue = object : AbstractSqlTypeValue() {
|
||||
override fun createTypeValue(conn: Connection, sqlType: Int, typeName: String?): Any {
|
||||
return conn.unwrap(OracleConnection::class.java).createOracleArray(typeName, ids)
|
||||
}
|
||||
}
|
||||
// end::oracle-array[]
|
||||
return value
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user