mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
568 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 22281d36b0 | |||
| 8d16a50907 | |||
| ab161ca796 | |||
| b5377ee9cf | |||
| eff1ca92f0 | |||
| 79c7bfdbad | |||
| f78b09fddf | |||
| b7ca746493 | |||
| 6c062399ea | |||
| e1ab306506 | |||
| 2165b1651b | |||
| bf72e92145 | |||
| 3e7372491c | |||
| e11a419cc3 | |||
| d33f66d9b5 | |||
| 8da2963644 | |||
| 9a11c12740 | |||
| 743c8f755b | |||
| 0376ebeaf3 | |||
| a2a793e324 | |||
| fd7e7633d4 | |||
| be7029c35d | |||
| 84efba682d | |||
| e66be91005 | |||
| c7fd5c9c34 | |||
| 09123def57 | |||
| 069d0819dd | |||
| a352d884c9 | |||
| 3498a35377 | |||
| 06fc5bc3ea | |||
| f59b56c4c9 | |||
| 5a83fc8f86 | |||
| db8d2d1626 | |||
| 12949becfc | |||
| debf571722 | |||
| f87b1bd6ce | |||
| e17afed0fe | |||
| 77964374f9 | |||
| 67686d776e | |||
| 2d83fca571 | |||
| f285df692c | |||
| b9eeee8341 | |||
| df1f4812aa | |||
| 230848c8e5 | |||
| eda868792a | |||
| 76604db8da | |||
| 38b7209dc5 | |||
| df0764db5d | |||
| 1e2c4635a3 | |||
| 628b0504ec | |||
| 8b589db028 | |||
| aed1d5f762 | |||
| 079d53c8d6 | |||
| 206a89017c | |||
| 8d1bf9607b | |||
| e12440a7af | |||
| 093e6a8e8d | |||
| c993615f98 | |||
| d068f5a4c6 | |||
| e73d68b0a8 | |||
| 0ca47e5e03 | |||
| 33d3496a16 | |||
| 84a5a8a61e | |||
| 2b8a1faeaa | |||
| 4c9de3cbbd | |||
| a0f07af375 | |||
| 97e12bd0e8 | |||
| c374c46cdc | |||
| ea208dc304 | |||
| c8c95e360f | |||
| 9fb36a5dcb | |||
| 4caf6bc5b8 | |||
| ee3e1591de | |||
| e81c788274 | |||
| 09f23a578b | |||
| 5288504ceb | |||
| 8b6a54c45f | |||
| f3f3063091 | |||
| c1f3e37acd | |||
| 427a96befc | |||
| 77951dc622 | |||
| 43dd22ba31 | |||
| dfe437ac88 | |||
| 9c775d2643 | |||
| 3b50b6ef94 | |||
| 9a31f3b5a5 | |||
| b11d118376 | |||
| 0931769822 | |||
| f7b7e29fd7 | |||
| 08a63a4180 | |||
| 4e3b834310 | |||
| fb67f97a41 | |||
| cafb5cfbbe | |||
| 9d2c6f80b8 | |||
| d9330bcac7 | |||
| 1fb179b057 | |||
| 5c9f364352 | |||
| 55f9581743 | |||
| 190397d5e0 | |||
| 94097fb112 | |||
| e4ab2aa775 | |||
| 40bf550d56 | |||
| f3bdce455e | |||
| 402246df28 | |||
| 88a68ddbd2 | |||
| 7d178c6771 | |||
| ba776d7201 | |||
| aba5f421fc | |||
| 2bac1629e6 | |||
| 9412d782ce | |||
| a0bd13ceb1 | |||
| 46a0821205 | |||
| 2c531344c6 | |||
| 6610ee360e | |||
| afde96c540 | |||
| da799bc519 | |||
| 755968fd2c | |||
| eea00628f9 | |||
| 8d745462b4 | |||
| ed6c25fb6e | |||
| 8460a2d285 | |||
| c3d186b4d0 | |||
| f8926d6a66 | |||
| 19b21b15c1 | |||
| 547425275c | |||
| 658194c155 | |||
| 769f73ebf1 | |||
| 80809bbc14 | |||
| 6d323d710a | |||
| f2fd2f1226 | |||
| 072ebb6ffc | |||
| 08e7f7efa4 | |||
| b1fafbf7e1 | |||
| a3647a8c5e | |||
| 5c8d9cd0b2 | |||
| 40d5196243 | |||
| ec2c9b5d0e | |||
| 36539bdaa9 | |||
| 67ba7dd1da | |||
| 7b3fcf2647 | |||
| fd76c33589 | |||
| 1a7a6f421f | |||
| 9ac1feceb5 | |||
| 7029042e44 | |||
| 1a5661d426 | |||
| b208c63414 | |||
| 814c003b43 | |||
| 5c34e1d11a | |||
| d57775bbb2 | |||
| b598ad3f33 | |||
| b44ef70bc3 | |||
| d1b3107398 | |||
| 629c560316 | |||
| 7bf07ef393 | |||
| ca602ef874 | |||
| e9bf5f5569 | |||
| 9a6f636e17 | |||
| 7686f5467e | |||
| 429c477f6a | |||
| 5187281b50 | |||
| 24a4487050 | |||
| 8fba4a448a | |||
| 8f286de773 | |||
| ee100cfba7 | |||
| aaf90f5a96 | |||
| 70be04ba81 | |||
| b976ee3f67 | |||
| b11ff96652 | |||
| 667e4e7531 | |||
| 5fd9fab0ce | |||
| 95a8646309 | |||
| 5434edd726 | |||
| 5f13ea95fb | |||
| 74bb42b78f | |||
| 55717adf88 | |||
| 72835f10b9 | |||
| 125ebd029c | |||
| d8d4fa0e24 | |||
| bfd3b3ad88 | |||
| 0909161ba1 | |||
| 4910c217dc | |||
| f262046ef9 | |||
| d50d4a9092 | |||
| b06305e64f | |||
| c749a14326 | |||
| 3817936ca5 | |||
| 9bd2be80b9 | |||
| c6e9cd0c62 | |||
| e2a5cfb459 | |||
| b484ab116f | |||
| c668473186 | |||
| 38595c6a99 | |||
| d756c2b128 | |||
| 3be4322f71 | |||
| 328e444db3 | |||
| 94bdd4380f | |||
| 3f9d479583 | |||
| 338922f03d | |||
| fad857e06a | |||
| b8395a2321 | |||
| 6df8be8be3 | |||
| 4a599d0b22 | |||
| a1463c2bf2 | |||
| 23eff5c650 | |||
| c44bb29aa5 | |||
| 0c22866b72 | |||
| 0c6957e395 | |||
| 8d51fc0444 | |||
| bad01011da | |||
| 867a199507 | |||
| 1b4a4ac51f | |||
| dd0d26b4ba | |||
| d074f660a1 | |||
| 1b8baffbd7 | |||
| 56c63c779f | |||
| 198cf063fd | |||
| b1b6b544a2 | |||
| 9d13ea290f | |||
| c9163b77df | |||
| 033c8df53f | |||
| b272f4e615 | |||
| bafff5079f | |||
| ccaecab500 | |||
| 67e03105b5 | |||
| 76bc9cf325 | |||
| db52c77cca | |||
| 1e742aae34 | |||
| 20dd585c93 | |||
| 707eb701dc | |||
| 2c97996796 | |||
| 3a068b807b | |||
| f54b19ff90 | |||
| 494d2ab727 | |||
| 627d9cf8be | |||
| dd3a67c7ab | |||
| 85cc229063 | |||
| fa95f12be0 | |||
| 035cc72fc8 | |||
| 29a39b617e | |||
| a1471a9266 | |||
| 21793b4f93 | |||
| 2d255c4d5e | |||
| 10391586d1 | |||
| edadc79835 | |||
| 3783d31c09 | |||
| 87730f76b1 | |||
| 6fae3e150e | |||
| ad44e8ab0a | |||
| aadf96ba92 | |||
| 2baf064d04 | |||
| 6121e2f526 | |||
| 8e33805d29 | |||
| 1f19bb2311 | |||
| 2784410cc6 | |||
| f4ac323409 | |||
| 6db00e63c7 | |||
| ed613e767a | |||
| 65781046cf | |||
| 0ee36095e7 | |||
| 0fc38117df | |||
| 86b8a70ce6 | |||
| 4bf759d872 | |||
| 770cbd2fb5 | |||
| e5f04e5ddf | |||
| c18784678d | |||
| 51cdff591c | |||
| d93114df9a | |||
| 5ac4c3bd76 | |||
| 05c3ffb2fb | |||
| 19e8ed130c | |||
| 4464251754 | |||
| 8868fe2ea5 | |||
| c373f496f3 | |||
| eb1883bdc4 | |||
| df6f66110f | |||
| ac235a0c43 | |||
| a4e3af5cbe | |||
| e71117dcdf | |||
| d5874ab99e | |||
| 99327b7db1 | |||
| 3e06441d97 | |||
| 01f2925048 | |||
| 44a37000ec | |||
| 53fe5fafed | |||
| b9bd98fc5b | |||
| 7714110940 | |||
| 75d1278bde | |||
| 620f558547 | |||
| 1e78cc35e5 | |||
| 11fdb5ba17 | |||
| 9957bb6918 | |||
| 6a7a0bddb7 | |||
| 5c012bbb0c | |||
| 5df6e8825d | |||
| 654e822676 | |||
| e943058b18 | |||
| cafb38ad1d | |||
| e778d2e908 | |||
| c5bcfc7682 | |||
| 5752e03d97 | |||
| dc26d3b0ec | |||
| 7f94c64b72 | |||
| 50e55d5219 | |||
| fcd4ba2f1f | |||
| e73107341c | |||
| f8a33cd66e | |||
| f088d4a05b | |||
| 84c28995fb | |||
| 925fa0272b | |||
| 09aa59f9e7 | |||
| 7874a59771 | |||
| 4dab35205d | |||
| 5c6b9be3a1 | |||
| 33fba8ea0c | |||
| 9aa707ec4b | |||
| ea30c8fb5b | |||
| 6bdf7ad36a | |||
| d2108d2db6 | |||
| f9be717602 | |||
| bb446a3905 | |||
| f3dce4bb9a | |||
| 70cb96c1d8 | |||
| 6ec264252b | |||
| 187b4e5ea6 | |||
| 3bc607df53 | |||
| 69c92f9ac7 | |||
| 875eeabb6f | |||
| 7a60e2024b | |||
| da95542d8f | |||
| 6669ab1ae7 | |||
| d05ac097dd | |||
| 35103b0cd1 | |||
| 3b50f992fe | |||
| 4bd54dffbc | |||
| 86650d1a39 | |||
| 87424cd605 | |||
| 80e82cd43f | |||
| 66ce8c9a25 | |||
| e9fcb21d55 | |||
| ee9dff30c5 | |||
| 2158410853 | |||
| e0a55c2caa | |||
| 0b96da4b6d | |||
| 387a16bd4e | |||
| 8b5d993e61 | |||
| 5459304a4b | |||
| 00d4830e5c | |||
| 3c2e21a578 | |||
| 5f053401e2 | |||
| 63770fb074 | |||
| 9df4bce043 | |||
| 19fd8159b2 | |||
| 147abc91a5 | |||
| e12eb9436d | |||
| 23162bb306 | |||
| a2c5fed494 | |||
| 957b6b2caf | |||
| 35f458fa5f | |||
| 8e83f93bcb | |||
| 407113945d | |||
| 4cf5c7796d | |||
| 847e8a2b23 | |||
| 9aab4a60f5 | |||
| 1fe2216c59 | |||
| 5ff9e6955c | |||
| 0cb4043aac | |||
| 6030e62766 | |||
| 867b9f61b2 | |||
| 6300fb37ad | |||
| 0d22569422 | |||
| 06658c3c71 | |||
| a37abd5e54 | |||
| 18456dec52 | |||
| d17c75a7ef | |||
| f547b6ad2a | |||
| e1bd13d3d6 | |||
| 08237da4b4 | |||
| f7496a393d | |||
| 0f92ba1663 | |||
| 865fa33927 | |||
| 9120f87897 | |||
| edd1e9134f | |||
| fef3cf8e58 | |||
| ce0923b946 | |||
| 227049824c | |||
| 8f130316d2 | |||
| 54c4f1b226 | |||
| 062c6241e1 | |||
| a51eb29e50 | |||
| 50d4a44dfc | |||
| ebf2cef94e | |||
| c89002a0fb | |||
| 29a4dabbe7 | |||
| f5f8eab405 | |||
| 659500bc1f | |||
| 4235a11c4f | |||
| 966b0a92c6 | |||
| 4ca70256d6 | |||
| c892ce5537 | |||
| 76b8bb2c75 | |||
| db7654225e | |||
| 78fce80c43 | |||
| 268043e9c9 | |||
| 2880e6fba5 | |||
| 11dc11e989 | |||
| 10de295a72 | |||
| 1227fe5774 | |||
| ea41051651 | |||
| 311c58ea2d | |||
| 40f1cf67bd | |||
| 6da9aed055 | |||
| 12a01a680b | |||
| 740f3b797f | |||
| ab48b88f91 | |||
| eda35e8074 | |||
| e4887f3ed9 | |||
| 1cdbd68aec | |||
| 453c0e5191 | |||
| a6b567a630 | |||
| 7a516170ef | |||
| 249f6f2da5 | |||
| 73766c01e6 | |||
| 278f228688 | |||
| c0c4298048 | |||
| 61f89a1f04 | |||
| 3e3f05109f | |||
| 2d1d14b145 | |||
| 1641cb75e2 | |||
| c01e1b8901 | |||
| 2e4e43b5bd | |||
| 6876c284f4 | |||
| b167152108 | |||
| bbf73848b5 | |||
| 1af259f928 | |||
| 1396daa4b6 | |||
| 1b409d5290 | |||
| ca14202d78 | |||
| 7231f22c23 | |||
| 3470240ef0 | |||
| 7598bca799 | |||
| f9588de247 | |||
| 2b3539a6de | |||
| 2731d4f100 | |||
| 00fffb7ab0 | |||
| 89b7a6bf47 | |||
| 19570338c9 | |||
| 906a9f7982 | |||
| 6fed3a0d6b | |||
| 8934eb8464 | |||
| 4b9f89101d | |||
| b204f2e396 | |||
| 112f755e17 | |||
| 542d0ef0b4 | |||
| 452b2df849 | |||
| 229b4782ee | |||
| ad1554a631 | |||
| 8be77cc650 | |||
| d0d0ed0578 | |||
| 9911d91f08 | |||
| 620a87bcbc | |||
| 368036cab4 | |||
| 8a6c0cd221 | |||
| b0fc2fe473 | |||
| 2952cb95f5 | |||
| c91708c1c0 | |||
| 47b1a2bc55 | |||
| e6565c600a | |||
| 8af9648c43 | |||
| c5bdd9d79d | |||
| 9efa99e0d8 | |||
| 957eb021d7 | |||
| c8a4026512 | |||
| 2111bf9b9d | |||
| c72dd1ff66 | |||
| 43bd78913c | |||
| 5458e0dccc | |||
| 78a73e5f57 | |||
| c7269feeaa | |||
| 1e75041b00 | |||
| 08bc7ed8f0 | |||
| 2ce75dc415 | |||
| 8b3ddeed05 | |||
| 389238f622 | |||
| 6f2a13fafd | |||
| 6baa60d454 | |||
| 92410395e3 | |||
| d03af15516 | |||
| d781f299c0 | |||
| 6fc4898a1b | |||
| 0c15be004e | |||
| 1c6ef3fe38 | |||
| d254bff197 | |||
| 6fc5a78252 | |||
| d58e48d9f5 | |||
| 8973d1ad8a | |||
| 2aae0a4e0c | |||
| 156b3696a7 | |||
| c36174b263 | |||
| 6e5af9dccb | |||
| 4a81814dbb | |||
| 1451f30781 | |||
| 4b54ca46d3 | |||
| 169392e132 | |||
| c050642290 | |||
| 7636eecb48 | |||
| d890827bae | |||
| 07a1aea9c7 | |||
| da7b68a643 | |||
| e83793ba7f | |||
| 18966d048c | |||
| 7e6612a920 | |||
| 9333ed22f6 | |||
| 4b6fabbd2f | |||
| cba2b6eaf4 | |||
| 34747baed0 | |||
| 7c5b2db5bf | |||
| 2e07a72119 | |||
| 9ba5622efd | |||
| 3ff81a47c9 | |||
| dcec61ab7a | |||
| 4922e0e439 | |||
| 7adacd5ce5 | |||
| 08d89f7aac | |||
| d250a5155a | |||
| 52176edcbf | |||
| ae279eaced | |||
| 18e72d5c01 | |||
| 148f5c459e | |||
| abbea39855 | |||
| 6fd40a8f60 | |||
| 333249e7b0 | |||
| 2573ba4a50 | |||
| bbde68c49e | |||
| fdf1418dfb | |||
| 5bcf5c6f7c | |||
| 87d4afda81 | |||
| 3a9e0ea8a7 | |||
| 4ce1ac0dcb | |||
| 8cc6dd629a | |||
| 391d7f2c6a | |||
| c64a322e19 | |||
| 2f33e77ab4 | |||
| bbcc788f60 | |||
| 038dda97f8 | |||
| 1ac0549881 | |||
| 616f728afa | |||
| 161a717639 | |||
| 28e63e9279 | |||
| 11de70ed08 | |||
| 9283fd2162 | |||
| 544f594592 | |||
| c21a8aa8b0 | |||
| 30d6ec3398 | |||
| ab83972c3e | |||
| 9e3f3bee71 | |||
| 2ba9939bd8 | |||
| 3a278cc66d | |||
| 6183f06846 | |||
| a34f9fa66c | |||
| f5db8bd1e8 | |||
| 317c6fbec2 | |||
| c354b1014d | |||
| a28ec3a0a8 | |||
| 6ad647d7ce | |||
| 384246c360 | |||
| e30391661d | |||
| 75f5dac16b | |||
| bddd3feb83 |
@@ -0,0 +1,20 @@
|
||||
name: Await HTTP Resource
|
||||
description: Waits for an HTTP resource to be available (a HEAD request succeeds)
|
||||
inputs:
|
||||
url:
|
||||
description: 'The 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,56 @@
|
||||
name: 'Build'
|
||||
description: 'Builds the project, optionally publishing it to a local deployment repository'
|
||||
inputs:
|
||||
java-version:
|
||||
required: false
|
||||
default: '17'
|
||||
description: 'The Java version to compile and test with'
|
||||
java-early-access:
|
||||
required: false
|
||||
default: 'false'
|
||||
description: 'Whether the Java version is in early access'
|
||||
java-toolchain:
|
||||
required: false
|
||||
default: 'false'
|
||||
description: 'Whether a Java toolchain should be used'
|
||||
publish:
|
||||
required: false
|
||||
default: 'false'
|
||||
description: 'Whether to publish artifacts ready for deployment to Artifactory'
|
||||
develocity-access-key:
|
||||
required: false
|
||||
description: 'The access key for authentication with ge.spring.io'
|
||||
outputs:
|
||||
build-scan-url:
|
||||
description: 'The 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: 'The 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-version: ${{ inputs.java-version }}
|
||||
java-early-access: ${{ inputs.java-early-access }}
|
||||
java-toolchain: ${{ inputs.java-toolchain }}
|
||||
- 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:
|
||||
milestone: ${{ inputs.milestone }}
|
||||
token: ${{ inputs.token }}
|
||||
config-file: .github/actions/create-github-release/changelog-generator.yml
|
||||
- name: Create GitHub Release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ inputs.token }}
|
||||
shell: bash
|
||||
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,49 @@
|
||||
name: 'Prepare Gradle Build'
|
||||
description: 'Prepares a Gradle build. Sets up Java and Gradle and configures Gradle properties'
|
||||
inputs:
|
||||
java-version:
|
||||
required: false
|
||||
default: '17'
|
||||
description: 'The Java version to use for the build'
|
||||
java-early-access:
|
||||
required: false
|
||||
default: 'false'
|
||||
description: 'Whether the Java version is in early access'
|
||||
java-toolchain:
|
||||
required: false
|
||||
default: 'false'
|
||||
description: 'Whether a Java toolchain should be used'
|
||||
develocity-access-key:
|
||||
required: false
|
||||
description: 'The access key for authentication with ge.spring.io'
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Set Up Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: ${{ inputs.java-early-access == 'true' && 'temurin' || '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@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3.5.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,33 @@
|
||||
name: Send Notification
|
||||
description: Sends a Google Chat message as a notification of the job's outcome
|
||||
inputs:
|
||||
webhook-url:
|
||||
description: 'Google Chat Webhook URL'
|
||||
required: true
|
||||
status:
|
||||
description: 'Status of the job'
|
||||
required: true
|
||||
build-scan-url:
|
||||
description: 'URL of the build scan to include in the notification'
|
||||
run-name:
|
||||
description: 'Name of the run to include in the notification'
|
||||
default: ${{ format('{0} {1}', github.ref_name, github.job) }}
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- shell: bash
|
||||
run: |
|
||||
echo "BUILD_SCAN=${{ inputs.build-scan-url == '' && ' [build scan unavailable]' || format(' [<{0}|Build Scan>]', inputs.build-scan-url) }}" >> "$GITHUB_ENV"
|
||||
echo "RUN_URL=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> "$GITHUB_ENV"
|
||||
- shell: bash
|
||||
if: ${{ inputs.status == 'success' }}
|
||||
run: |
|
||||
curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<${{ env.RUN_URL }}|${{ inputs.run-name }}> was successful ${{ env.BUILD_SCAN }}"}' || true
|
||||
- shell: bash
|
||||
if: ${{ inputs.status == 'failure' }}
|
||||
run: |
|
||||
curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<users/all> *<${{ env.RUN_URL }}|${{ inputs.run-name }}> failed* ${{ env.BUILD_SCAN }}"}' || true
|
||||
- shell: bash
|
||||
if: ${{ inputs.status == 'cancelled' }}
|
||||
run: |
|
||||
curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<${{ env.RUN_URL }}|${{ inputs.run-name }}> was cancelled"}' || true
|
||||
@@ -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
|
||||
spring-framework-version:
|
||||
description: 'The version of Spring Framework that is being synced to Central'
|
||||
required: true
|
||||
ossrh-s01-token-username:
|
||||
description: 'Username for authentication with s01.oss.sonatype.org'
|
||||
required: true
|
||||
ossrh-s01-token-password:
|
||||
description: 'Password for authentication with s01.oss.sonatype.org'
|
||||
required: true
|
||||
ossrh-s01-staging-profile:
|
||||
description: 'Staging profile to use when syncing to Central'
|
||||
required: true
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Set Up JFrog CLI
|
||||
uses: jfrog/setup-jfrog-cli@105617d23456a69a92485207c4f28ae12297581d # v4.2.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:
|
||||
username: ${{ inputs.ossrh-s01-token-username }}
|
||||
password: ${{ inputs.ossrh-s01-token-password }}
|
||||
staging-profile-name: ${{ inputs.ossrh-s01-staging-profile }}
|
||||
create: true
|
||||
upload: true
|
||||
close: true
|
||||
release: true
|
||||
generate-checksums: true
|
||||
- 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-docs/*"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"target": "nexus/"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -18,11 +18,13 @@ jobs:
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-java@v3
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '17'
|
||||
distribution: 'liberica'
|
||||
java-version: 17
|
||||
- name: Download BackportBot
|
||||
run: wget https://github.com/spring-io/backport-bot/releases/download/latest/backport-bot-0.0.1-SNAPSHOT.jar
|
||||
- name: Backport
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
name: Build and Deploy Snapshot
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 6.0.x
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
jobs:
|
||||
build-and-deploy-snapshot:
|
||||
name: Build and Deploy Snapshot
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
if: ${{ github.repository == 'spring-projects/spring-framework' }}
|
||||
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.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||
publish: true
|
||||
- name: Deploy
|
||||
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: 'spring-framework-6.0.x'
|
||||
repository: 'libs-snapshot-local'
|
||||
folder: 'deployment-repository'
|
||||
signing-key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
signing-passphrase: ${{ secrets.GPG_PASSPHRASE }}
|
||||
artifact-properties: |
|
||||
/**/framework-docs-*.zip::zip.name=spring-framework,zip.deployed=false
|
||||
/**/framework-docs-*-docs.zip::zip.type=docs
|
||||
/**/framework-docs-*-dist.zip::zip.type=dist
|
||||
/**/framework-docs-*-schema.zip::zip.type=schema
|
||||
- name: Send Notification
|
||||
uses: ./.github/actions/send-notification
|
||||
if: always()
|
||||
with:
|
||||
webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
|
||||
status: ${{ job.status }}
|
||||
build-scan-url: ${{ steps.build-and-publish.outputs.build-scan-url }}
|
||||
run-name: ${{ format('{0} | Linux | Java 17', github.ref_name) }}
|
||||
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,53 @@
|
||||
name: CI
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 6.0.x
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
jobs:
|
||||
ci:
|
||||
name: '${{ matrix.os.name}} | Java ${{ matrix.java.version}}'
|
||||
runs-on: ${{ matrix.os.id }}
|
||||
timeout-minutes: 60
|
||||
if: ${{ github.repository == 'spring-projects/spring-framework' }}
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- id: ubuntu-latest
|
||||
name: Linux
|
||||
java:
|
||||
- version: 17
|
||||
toolchain: false
|
||||
- version: 21
|
||||
toolchain: true
|
||||
exclude:
|
||||
- os:
|
||||
name: Linux
|
||||
java:
|
||||
version: 17
|
||||
steps:
|
||||
- name: Prepare Windows runner
|
||||
if: ${{ runner.os == 'Windows' }}
|
||||
run: |
|
||||
git config --global core.autocrlf true
|
||||
git config --global core.longPaths true
|
||||
Stop-Service -name Docker
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@v4
|
||||
- name: Build
|
||||
id: build
|
||||
uses: ./.github/actions/build
|
||||
with:
|
||||
java-version: ${{ matrix.java.version }}
|
||||
java-early-access: ${{ matrix.java.early-access || 'false' }}
|
||||
java-toolchain: ${{ matrix.java.toolchain }}
|
||||
develocity-access-key: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||
- name: Send Notification
|
||||
uses: ./.github/actions/send-notification
|
||||
if: always()
|
||||
with:
|
||||
webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
|
||||
status: ${{ job.status }}
|
||||
build-scan-url: ${{ steps.build.outputs.build-scan-url }}
|
||||
run-name: ${{ format('{0} | {1} | Java {2}', github.ref_name, matrix.os.name, matrix.java.version) }}
|
||||
@@ -1,12 +1,14 @@
|
||||
name: Deploy Docs
|
||||
on:
|
||||
push:
|
||||
branches-ignore: [ gh-pages ]
|
||||
tags: '**'
|
||||
branches:
|
||||
- 'main'
|
||||
- '*.x'
|
||||
- '!gh-pages'
|
||||
tags:
|
||||
- 'v*'
|
||||
repository_dispatch:
|
||||
types: request-build-reference # legacy
|
||||
schedule:
|
||||
- cron: '0 10 * * *' # Once per day at 10am UTC
|
||||
workflow_dispatch:
|
||||
permissions:
|
||||
actions: write
|
||||
@@ -15,8 +17,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository_owner == 'spring-projects'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: docs-build
|
||||
fetch-depth: 1
|
||||
|
||||
@@ -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@v3
|
||||
- uses: gradle/wrapper-validation-action@v1
|
||||
@@ -0,0 +1,95 @@
|
||||
name: Release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v6.0.[0-9]+
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
jobs:
|
||||
build-and-stage-release:
|
||||
if: ${{ github.repository == 'spring-projects/spring-framework' }}
|
||||
name: Build and Stage Release
|
||||
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.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||
publish: true
|
||||
- name: Stage Release
|
||||
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}', steps.build-and-publish.outputs.version)}}
|
||||
repository: 'libs-staging-local'
|
||||
folder: 'deployment-repository'
|
||||
signing-key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
signing-passphrase: ${{ secrets.GPG_PASSPHRASE }}
|
||||
artifact-properties: |
|
||||
/**/framework-docs-*.zip::zip.name=spring-framework,zip.deployed=false
|
||||
/**/framework-docs-*-docs.zip::zip.type=docs
|
||||
/**/framework-docs-*-dist.zip::zip.type=dist
|
||||
/**/framework-docs-*-schema.zip::zip.type=schema
|
||||
outputs:
|
||||
version: ${{ steps.build-and-publish.outputs.version }}
|
||||
verify:
|
||||
name: Verify
|
||||
needs: build-and-stage-release
|
||||
uses: ./.github/workflows/verify.yml
|
||||
with:
|
||||
staging: true
|
||||
version: ${{ needs.build-and-stage-release.outputs.version }}
|
||||
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 }}
|
||||
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@105617d23456a69a92485207c4f28ae12297581d # v4.2.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,11 @@
|
||||
name: "Validate Gradle Wrapper"
|
||||
on: [push, pull_request]
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
validation:
|
||||
name: "Validate Gradle Wrapper"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: gradle/actions/wrapper-validation@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3.5.0
|
||||
@@ -0,0 +1,71 @@
|
||||
name: Verify
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
version:
|
||||
required: true
|
||||
type: string
|
||||
staging:
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
secrets:
|
||||
repository-username:
|
||||
required: false
|
||||
repository-password:
|
||||
required: false
|
||||
google-chat-webhook-url:
|
||||
required: true
|
||||
token:
|
||||
required: true
|
||||
jobs:
|
||||
verify:
|
||||
name: Verify
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check Out Release Verification Tests
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: spring-projects/spring-framework-release-verification
|
||||
ref: 'v0.0.2'
|
||||
token: ${{ secrets.token }}
|
||||
- name: Check Out Send Notification Action
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: spring-framework
|
||||
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@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3.5.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_VERSION: ${{ inputs.version }}
|
||||
RVT_RELEASE_TYPE: oss
|
||||
RVT_STAGING: ${{ inputs.staging }}
|
||||
RVT_OSS_REPOSITORY_USERNAME: ${{ secrets.repository-username }}
|
||||
RVT_OSS_REPOSITORY_PASSWORD: ${{ secrets.repository-password }}
|
||||
run: ./gradlew spring-framework-release-verification-tests:test
|
||||
- name: Upload Build Reports on Failure
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: build-reports
|
||||
path: '**/build/reports/'
|
||||
- name: Send Notification
|
||||
uses: ./spring-framework/.github/actions/send-notification
|
||||
if: failure()
|
||||
with:
|
||||
webhook-url: ${{ secrets.google-chat-webhook-url }}
|
||||
status: ${{ job.status }}
|
||||
run-name: ${{ format('{0} | Verification | {1}', github.ref_name, inputs.version) }}
|
||||
+3
-2
@@ -21,8 +21,7 @@ derby.log
|
||||
/build
|
||||
buildSrc/build
|
||||
/spring-*/build
|
||||
/framework-bom/build
|
||||
/framework-docs/build
|
||||
/framework-*/build
|
||||
/integration-tests/build
|
||||
/src/asciidoc/build
|
||||
spring-test/test-output/
|
||||
@@ -52,3 +51,5 @@ atlassian-ide-plugin.xml
|
||||
.vscode/
|
||||
|
||||
cached-antora-playbook.yml
|
||||
|
||||
node_modules
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
Juergen Hoeller <jhoeller@vmware.com>
|
||||
Juergen Hoeller <jhoeller@vmware.com> <jhoeller@pivotal.io>
|
||||
Juergen Hoeller <jhoeller@vmware.com> <jhoeller@gopivotal.com>
|
||||
Rossen Stoyanchev <rstoyanchev@vmware.com>
|
||||
Rossen Stoyanchev <rstoyanchev@vmware.com> <rstoyanchev@pivotal.io>
|
||||
Rossen Stoyanchev <rstoyanchev@vmware.com> <rstoyanchev@gopivotal.com>
|
||||
Phillip Webb <pwebb@vmware.com>
|
||||
Phillip Webb <pwebb@vmware.com> <pwebb@pivotal.io>
|
||||
Phillip Webb <pwebb@vmware.com> <pwebb@gopivotal.com>
|
||||
Chris Beams <cbeams@vmware.com>
|
||||
Chris Beams <cbeams@vmware.com> <cbeams@pivotal.io>
|
||||
Chris Beams <cbeams@vmware.com> <cbeams@gopivotal.com>
|
||||
Arjen Poutsma <poutsmaa@vmware.com>
|
||||
Arjen Poutsma <poutsmaa@vmware.com> <apoutsma@pivotal.io>
|
||||
Arjen Poutsma <poutsmaa@vmware.com> <apoutsma@gopivotal.com>
|
||||
Arjen Poutsma <poutsmaa@vmware.com> <poutsma@mac.com>
|
||||
Arjen Poutsma <poutsmaa@vmware.com> <apoutsma@vmware.com>
|
||||
Oliver Drotbohm <odrotbohm@vmware.com>
|
||||
Oliver Drotbohm <odrotbohm@vmware.com> <ogierke@vmware.com>
|
||||
Oliver Drotbohm <odrotbohm@vmware.com> <ogierke@pivotal.io>
|
||||
Oliver Drotbohm <odrotbohm@vmware.com> <ogierke@gopivotal.com>
|
||||
Dave Syer <dsyer@vmware.com>
|
||||
Dave Syer <dsyer@vmware.com> <dsyer@pivotal.io>
|
||||
Dave Syer <dsyer@vmware.com> <dsyer@gopivotal.com>
|
||||
Dave Syer <dsyer@vmware.com> <david_syer@hotmail.com>
|
||||
Andy Clement <aclement@vmware.com>
|
||||
Andy Clement <aclement@vmware.com> <aclement@pivotal.io>
|
||||
Andy Clement <aclement@vmware.com> <aclement@gopivotal.com>
|
||||
Andy Clement <aclement@vmware.com> <andrew.clement@gmail.com>
|
||||
Sam Brannen <sbrannen@vmware.com>
|
||||
Sam Brannen <sbrannen@vmware.com> <sbrannen@pivotal.io>
|
||||
Sam Brannen <sbrannen@vmware.com> <sam@sambrannen.com>
|
||||
Simon Basle <sbasle@vmware.com>
|
||||
Simon Baslé <sbasle@vmware.com>
|
||||
<dmitry.katsubo@gmail.com> <dmitry.katsubo@gmai.com>
|
||||
Nick Williams <nicholas@nicholaswilliams.net>
|
||||
@@ -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.7-librca
|
||||
java=17.0.11-librca
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# <img src="framework-docs/src/docs/spring-framework.png" width="80" height="80"> Spring Framework [](https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-6.0.x?groups=Build") [](https://ge.spring.io/scans?search.rootProjectNames=spring)
|
||||
# <img src="framework-docs/src/docs/spring-framework.png" width="80" height="80"> Spring Framework [](https://github.com/spring-projects/spring-framework/actions/workflows/build-and-deploy-snapshot.yml?query=branch%3A6.0.x) [](https://ge.spring.io/scans?search.rootProjectNames=spring)
|
||||
|
||||
This is the home of the Spring Framework: the foundation for all [Spring projects](https://spring.io/projects). Collectively the Spring Framework and the family of Spring projects are often referred to simply as "Spring".
|
||||
|
||||
|
||||
+6
-10
@@ -3,12 +3,12 @@ plugins {
|
||||
id 'io.freefair.aspectj' version '8.0.1' apply false
|
||||
// kotlinVersion is managed in gradle.properties
|
||||
id 'org.jetbrains.kotlin.plugin.serialization' version "${kotlinVersion}" apply false
|
||||
id 'org.jetbrains.dokka' version '1.8.10'
|
||||
id 'org.jetbrains.dokka' version '1.8.20'
|
||||
id 'org.unbroken-dome.xjc' version '2.0.0' apply false
|
||||
id 'com.github.ben-manes.versions' version '0.46.0'
|
||||
id 'com.github.ben-manes.versions' version '0.51.0'
|
||||
id 'com.github.johnrengelman.shadow' version '8.1.1' apply false
|
||||
id 'de.undercouch.download' version '5.4.0'
|
||||
id 'me.champeau.jmh' version '0.7.1' apply false
|
||||
id 'me.champeau.jmh' version '0.7.2' apply false
|
||||
}
|
||||
|
||||
ext {
|
||||
@@ -75,7 +75,7 @@ configure([rootProject] + javaProjects) { project ->
|
||||
}
|
||||
|
||||
checkstyle {
|
||||
toolVersion = "10.12.1"
|
||||
toolVersion = "10.12.7"
|
||||
configDirectory.set(rootProject.file("src/checkstyle"))
|
||||
}
|
||||
|
||||
@@ -106,15 +106,12 @@ configure([rootProject] + javaProjects) { project ->
|
||||
// JSR-305 only used for non-required meta-annotations
|
||||
compileOnly("com.google.code.findbugs:jsr305")
|
||||
testCompileOnly("com.google.code.findbugs:jsr305")
|
||||
checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:0.0.38")
|
||||
checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:0.0.42")
|
||||
}
|
||||
|
||||
ext.javadocLinks = [
|
||||
"https://docs.oracle.com/en/java/javase/17/docs/api/",
|
||||
"https://jakarta.ee/specifications/platform/9/apidocs/",
|
||||
"https://docs.oracle.com/cd/E13222_01/wls/docs90/javadocs/", // CommonJ and weblogic.* packages
|
||||
"https://www.ibm.com/docs/api/v1/content/SSEQTP_8.5.5/com.ibm.websphere.javadoc.doc/web/apidocs/", // com.ibm.*
|
||||
"https://docs.jboss.org/jbossas/javadoc/4.0.5/connector/", // org.jboss.resource.*
|
||||
"https://docs.jboss.org/hibernate/orm/5.6/javadocs/",
|
||||
"https://eclipse.dev/aspectj/doc/released/aspectj5rt-api",
|
||||
"https://www.quartz-scheduler.org/api/2.3.0/",
|
||||
@@ -146,7 +143,6 @@ configure(rootProject) {
|
||||
description = "Spring Framework"
|
||||
|
||||
apply plugin: "io.spring.nohttp"
|
||||
apply plugin: 'org.springframework.build.api-diff'
|
||||
|
||||
nohttp {
|
||||
source.exclude "**/test-output/**"
|
||||
@@ -155,7 +151,7 @@ configure(rootProject) {
|
||||
def rootPath = file(rootDir).toPath()
|
||||
def projectDirs = allprojects.collect { it.projectDir } + "${rootDir}/buildSrc"
|
||||
projectDirs.forEach { dir ->
|
||||
[ 'bin', 'build', 'out', '.settings' ]
|
||||
[ 'bin', 'build', 'out', '.settings', 'node_modules' ]
|
||||
.collect { rootPath.relativize(new File(dir, it).toPath()) }
|
||||
.forEach { source.exclude "$it/**" }
|
||||
[ '.classpath', '.project' ]
|
||||
|
||||
@@ -22,21 +22,6 @@ but doesn't affect the classpath of dependent projects.
|
||||
This plugin does not provide a `provided` configuration, as the native `compileOnly` and `testCompileOnly`
|
||||
configurations are preferred.
|
||||
|
||||
### API Diff
|
||||
|
||||
This plugin uses the [Gradle JApiCmp](https://github.com/melix/japicmp-gradle-plugin) plugin
|
||||
to generate API Diff reports for each Spring Framework module. This plugin is applied once on the root
|
||||
project and creates tasks in each framework module. Unlike previous versions of this part of the build,
|
||||
there is no need for checking out a specific tag. The plugin will fetch the JARs we want to compare the
|
||||
current working version with. You can generate the reports for all modules or a single module:
|
||||
|
||||
```
|
||||
./gradlew apiDiff -PbaselineVersion=5.1.0.RELEASE
|
||||
./gradlew :spring-core:apiDiff -PbaselineVersion=5.1.0.RELEASE
|
||||
```
|
||||
|
||||
The reports are located under `build/reports/api-diff/$OLDVERSION_to_$NEWVERSION/`.
|
||||
|
||||
|
||||
### RuntimeHints Java Agent
|
||||
|
||||
|
||||
@@ -19,16 +19,11 @@ ext {
|
||||
dependencies {
|
||||
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}")
|
||||
implementation("org.jetbrains.kotlin:kotlin-compiler-embeddable:${kotlinVersion}")
|
||||
implementation "me.champeau.gradle:japicmp-gradle-plugin:0.3.0"
|
||||
implementation "org.gradle:test-retry-gradle-plugin:1.4.1"
|
||||
implementation "org.gradle:test-retry-gradle-plugin:1.5.6"
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
plugins {
|
||||
apiDiffPlugin {
|
||||
id = "org.springframework.build.api-diff"
|
||||
implementationClass = "org.springframework.build.api.ApiDiffPlugin"
|
||||
}
|
||||
conventionsPlugin {
|
||||
id = "org.springframework.build.conventions"
|
||||
implementationClass = "org.springframework.build.ConventionsPlugin"
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.build.api;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import me.champeau.gradle.japicmp.JapicmpPlugin;
|
||||
import me.champeau.gradle.japicmp.JapicmpTask;
|
||||
import org.gradle.api.GradleException;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.artifacts.Dependency;
|
||||
import org.gradle.api.plugins.JavaBasePlugin;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin;
|
||||
import org.gradle.api.tasks.TaskProvider;
|
||||
import org.gradle.jvm.tasks.Jar;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* {@link Plugin} that applies the {@code "japicmp-gradle-plugin"}
|
||||
* and create tasks for all subprojects named {@code "spring-*"}, diffing the public API one by one
|
||||
* and creating the reports in {@code "build/reports/api-diff/$OLDVERSION_to_$NEWVERSION/"}.
|
||||
* <p>{@code "./gradlew apiDiff -PbaselineVersion=5.1.0.RELEASE"} will output the
|
||||
* reports for the API diff between the baseline version and the current one for all modules.
|
||||
* You can limit the report to a single module with
|
||||
* {@code "./gradlew :spring-core:apiDiff -PbaselineVersion=5.1.0.RELEASE"}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
public class ApiDiffPlugin implements Plugin<Project> {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ApiDiffPlugin.class);
|
||||
|
||||
public static final String TASK_NAME = "apiDiff";
|
||||
|
||||
private static final String BASELINE_VERSION_PROPERTY = "baselineVersion";
|
||||
|
||||
private static final List<String> PACKAGE_INCLUDES = Collections.singletonList("org.springframework.*");
|
||||
|
||||
private static final URI SPRING_MILESTONE_REPOSITORY = URI.create("https://repo.spring.io/milestone");
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
if (project.hasProperty(BASELINE_VERSION_PROPERTY) && project.equals(project.getRootProject())) {
|
||||
project.getPluginManager().apply(JapicmpPlugin.class);
|
||||
project.getPlugins().withType(JapicmpPlugin.class,
|
||||
plugin -> applyApiDiffConventions(project));
|
||||
}
|
||||
}
|
||||
|
||||
private void applyApiDiffConventions(Project project) {
|
||||
String baselineVersion = project.property(BASELINE_VERSION_PROPERTY).toString();
|
||||
project.subprojects(subProject -> {
|
||||
if (subProject.getName().startsWith("spring-")) {
|
||||
createApiDiffTask(baselineVersion, subProject);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void createApiDiffTask(String baselineVersion, Project project) {
|
||||
if (isProjectEligible(project)) {
|
||||
// Add Spring Milestone repository for generating diffs against previous milestones
|
||||
project.getRootProject()
|
||||
.getRepositories()
|
||||
.maven(mavenArtifactRepository -> mavenArtifactRepository.setUrl(SPRING_MILESTONE_REPOSITORY));
|
||||
JapicmpTask apiDiff = project.getTasks().create(TASK_NAME, JapicmpTask.class);
|
||||
apiDiff.setDescription("Generates an API diff report with japicmp");
|
||||
apiDiff.setGroup(JavaBasePlugin.DOCUMENTATION_GROUP);
|
||||
|
||||
apiDiff.setOldClasspath(createBaselineConfiguration(baselineVersion, project));
|
||||
TaskProvider<Jar> jar = project.getTasks().withType(Jar.class).named("jar");
|
||||
apiDiff.setNewArchives(project.getLayout().files(jar.get().getArchiveFile().get().getAsFile()));
|
||||
apiDiff.setNewClasspath(getRuntimeClassPath(project));
|
||||
apiDiff.setPackageIncludes(PACKAGE_INCLUDES);
|
||||
apiDiff.setOnlyModified(true);
|
||||
apiDiff.setIgnoreMissingClasses(true);
|
||||
// Ignore Kotlin metadata annotations since they contain
|
||||
// illegal HTML characters and fail the report generation
|
||||
apiDiff.setAnnotationExcludes(Collections.singletonList("@kotlin.Metadata"));
|
||||
|
||||
apiDiff.setHtmlOutputFile(getOutputFile(baselineVersion, project));
|
||||
|
||||
apiDiff.dependsOn(project.getTasks().getByName("jar"));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isProjectEligible(Project project) {
|
||||
return project.getPlugins().hasPlugin(JavaPlugin.class)
|
||||
&& project.getPlugins().hasPlugin(MavenPublishPlugin.class);
|
||||
}
|
||||
|
||||
private Configuration createBaselineConfiguration(String baselineVersion, Project project) {
|
||||
String baseline = String.join(":",
|
||||
project.getGroup().toString(), project.getName(), baselineVersion);
|
||||
Dependency baselineDependency = project.getDependencies().create(baseline + "@jar");
|
||||
Configuration baselineConfiguration = project.getRootProject().getConfigurations().detachedConfiguration(baselineDependency);
|
||||
try {
|
||||
// eagerly resolve the baseline configuration to check whether this is a new Spring module
|
||||
baselineConfiguration.resolve();
|
||||
return baselineConfiguration;
|
||||
}
|
||||
catch (GradleException exception) {
|
||||
logger.warn("Could not resolve {} - assuming this is a new Spring module.", baseline);
|
||||
}
|
||||
return project.getRootProject().getConfigurations().detachedConfiguration();
|
||||
}
|
||||
|
||||
private Configuration getRuntimeClassPath(Project project) {
|
||||
return project.getConfigurations().getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME);
|
||||
}
|
||||
|
||||
private File getOutputFile(String baseLineVersion, Project project) {
|
||||
Path outDir = Paths.get(project.getRootProject().getBuildDir().getAbsolutePath(),
|
||||
"reports", "api-diff",
|
||||
baseLineVersion + "_to_" + project.getRootProject().getVersion());
|
||||
return project.file(outDir.resolve(project.getName() + ".html").toString());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
== Spring Framework Concourse pipeline
|
||||
|
||||
The Spring Framework uses https://concourse-ci.org/[Concourse] for its CI build and other automated tasks.
|
||||
The Spring team has a dedicated Concourse instance available at https://ci.spring.io with a build pipeline
|
||||
for https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-6.0.x[Spring Framework 6.0.x].
|
||||
|
||||
=== 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.0.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,11 +0,0 @@
|
||||
FROM ubuntu:jammy-20230425
|
||||
|
||||
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 JDK20 /opt/openjdk/java20
|
||||
|
||||
ENV PATH $JAVA_HOME/bin:$PATH
|
||||
@@ -1,14 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
case "$1" in
|
||||
java17)
|
||||
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.7+7/bellsoft-jdk17.0.7+7-linux-amd64.tar.gz"
|
||||
;;
|
||||
java20)
|
||||
echo "https://github.com/bell-sw/Liberica/releases/download/20.0.1+10/bellsoft-jdk20.0.1+10-linux-amd64.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 java20
|
||||
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,11 +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.0.x"
|
||||
milestone: "6.0.x"
|
||||
build-name: "spring-framework"
|
||||
pipeline-name: "spring-framework"
|
||||
concourse-url: "https://ci.spring.io"
|
||||
task-timeout: 1h00m
|
||||
-403
@@ -1,403 +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
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY: ((gradle_enterprise_secret_access_key))
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME: ((gradle_enterprise_cache_user.username))
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD: ((gradle_enterprise_cache_user.password))
|
||||
sonatype-task-params: &sonatype-task-params
|
||||
SONATYPE_USERNAME: ((sonatype-username))
|
||||
SONATYPE_PASSWORD: ((sonatype-password))
|
||||
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))
|
||||
slack-fail-params: &slack-fail-params
|
||||
text: >
|
||||
:concourse-failed: <https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}|${BUILD_PIPELINE_NAME} ${BUILD_JOB_NAME} failed!>
|
||||
[$TEXT_FILE_CONTENT]
|
||||
text_file: git-repo/build/build-scan-uri.txt
|
||||
silent: true
|
||||
icon_emoji: ":concourse:"
|
||||
username: concourse-ci
|
||||
changelog-task-params: &changelog-task-params
|
||||
name: generated-changelog/tag
|
||||
tag: generated-changelog/tag
|
||||
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.5.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.5.5
|
||||
- name: github-status-resource
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: dpb587/github-status-resource
|
||||
tag: master
|
||||
- name: slack-notification
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: cfcommunity/slack-notification-resource
|
||||
tag: latest
|
||||
resources:
|
||||
- name: git-repo
|
||||
type: git
|
||||
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: every-morning
|
||||
type: time
|
||||
icon: alarm
|
||||
source:
|
||||
start: 8:00 AM
|
||||
stop: 9:00 AM
|
||||
location: Europe/Vienna
|
||||
- name: artifactory-repo
|
||||
type: artifactory-resource
|
||||
icon: package-variant
|
||||
source:
|
||||
uri: ((artifactory-server))
|
||||
username: ((artifactory-username))
|
||||
password: ((artifactory-password))
|
||||
build_name: ((build-name))
|
||||
- name: repo-status-build
|
||||
type: github-status-resource
|
||||
icon: eye-check-outline
|
||||
source:
|
||||
repository: ((github-repo-name))
|
||||
access_token: ((github-ci-status-token))
|
||||
branch: ((branch))
|
||||
context: build
|
||||
- name: repo-status-jdk20-build
|
||||
type: github-status-resource
|
||||
icon: eye-check-outline
|
||||
source:
|
||||
repository: ((github-repo-name))
|
||||
access_token: ((github-ci-status-token))
|
||||
branch: ((branch))
|
||||
context: jdk20-build
|
||||
- name: slack-alert
|
||||
type: slack-notification
|
||||
icon: slack
|
||||
source:
|
||||
url: ((slack-webhook-url))
|
||||
- name: github-pre-release
|
||||
type: github-release
|
||||
icon: briefcase-download-outline
|
||||
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: build
|
||||
serial: true
|
||||
public: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
trigger: true
|
||||
- put: repo-status-build
|
||||
params: { state: "pending", commit: "git-repo" }
|
||||
- do:
|
||||
- task: build-project
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/build-project.yml
|
||||
privileged: true
|
||||
timeout: ((task-timeout))
|
||||
params:
|
||||
<<: *build-project-task-params
|
||||
on_failure:
|
||||
do:
|
||||
- put: repo-status-build
|
||||
params: { state: "failure", commit: "git-repo" }
|
||||
- put: slack-alert
|
||||
params:
|
||||
<<: *slack-fail-params
|
||||
- put: repo-status-build
|
||||
params: { state: "success", commit: "git-repo" }
|
||||
- put: artifactory-repo
|
||||
params: &artifactory-params
|
||||
signing_key: ((signing-key))
|
||||
signing_passphrase: ((signing-passphrase))
|
||||
repo: libs-snapshot-local
|
||||
folder: distribution-repository
|
||||
build_uri: "https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}"
|
||||
build_number: "${BUILD_PIPELINE_NAME}-${BUILD_JOB_NAME}-${BUILD_NAME}"
|
||||
disable_checksum_uploads: true
|
||||
threads: 8
|
||||
artifact_set:
|
||||
- include:
|
||||
- "/**/framework-docs-*.zip"
|
||||
properties:
|
||||
"zip.name": "spring-framework"
|
||||
"zip.displayname": "Spring Framework"
|
||||
"zip.deployed": "false"
|
||||
- include:
|
||||
- "/**/framework-docs-*-docs.zip"
|
||||
properties:
|
||||
"zip.type": "docs"
|
||||
- include:
|
||||
- "/**/framework-docs-*-dist.zip"
|
||||
properties:
|
||||
"zip.type": "dist"
|
||||
- include:
|
||||
- "/**/framework-docs-*-schema.zip"
|
||||
properties:
|
||||
"zip.type": "schema"
|
||||
get_params:
|
||||
threads: 8
|
||||
- name: jdk20-build
|
||||
serial: true
|
||||
public: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
- get: every-morning
|
||||
trigger: true
|
||||
- put: repo-status-jdk20-build
|
||||
params: { state: "pending", commit: "git-repo" }
|
||||
- do:
|
||||
- task: check-project
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/check-project.yml
|
||||
privileged: true
|
||||
timeout: ((task-timeout))
|
||||
params:
|
||||
TEST_TOOLCHAIN: 20
|
||||
<<: *build-project-task-params
|
||||
on_failure:
|
||||
do:
|
||||
- put: repo-status-jdk20-build
|
||||
params: { state: "failure", commit: "git-repo" }
|
||||
- put: slack-alert
|
||||
params:
|
||||
<<: *slack-fail-params
|
||||
- put: repo-status-jdk20-build
|
||||
params: { state: "success", commit: "git-repo" }
|
||||
- name: stage-milestone
|
||||
serial: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
trigger: false
|
||||
- task: stage
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/stage-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: M
|
||||
<<: *gradle-enterprise-task-params
|
||||
- put: artifactory-repo
|
||||
params:
|
||||
<<: *artifactory-params
|
||||
repo: libs-staging-local
|
||||
- put: git-repo
|
||||
params:
|
||||
repository: stage-git-repo
|
||||
- name: promote-milestone
|
||||
serial: true
|
||||
plan:
|
||||
- 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
|
||||
repo: libs-staging-local
|
||||
- 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
|
||||
repo: libs-staging-local
|
||||
- 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: "builds"
|
||||
jobs: ["build", "jdk20-build"]
|
||||
- name: "releases"
|
||||
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release"]
|
||||
- name: "ci-images"
|
||||
jobs: ["build-ci-images"]
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
source $(dirname $0)/common.sh
|
||||
|
||||
pushd git-repo > /dev/null
|
||||
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --max-workers=4 check
|
||||
popd > /dev/null
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
source $(dirname $0)/common.sh
|
||||
repository=$(pwd)/distribution-repository
|
||||
|
||||
pushd git-repo > /dev/null
|
||||
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --max-workers=4 -PdeploymentRepository=${repository} build publishAllPublicationsToDeploymentRepository
|
||||
popd > /dev/null
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
source $(dirname $0)/common.sh
|
||||
|
||||
pushd git-repo > /dev/null
|
||||
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK17,JDK20 \
|
||||
-PmainToolchain=${MAIN_TOOLCHAIN} -PtestToolchain=${TEST_TOOLCHAIN} --no-daemon --max-workers=4 check antora
|
||||
popd > /dev/null
|
||||
@@ -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,50 +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} 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,19 +0,0 @@
|
||||
---
|
||||
platform: linux
|
||||
inputs:
|
||||
- name: git-repo
|
||||
caches:
|
||||
- path: gradle
|
||||
params:
|
||||
BRANCH:
|
||||
CI: true
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY:
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME:
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD:
|
||||
GRADLE_ENTERPRISE_URL: https://ge.spring.io
|
||||
run:
|
||||
path: bash
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
${PWD}/git-repo/ci/scripts/build-pr.sh
|
||||
@@ -1,22 +0,0 @@
|
||||
---
|
||||
platform: linux
|
||||
inputs:
|
||||
- name: git-repo
|
||||
outputs:
|
||||
- name: distribution-repository
|
||||
- name: git-repo
|
||||
caches:
|
||||
- path: gradle
|
||||
params:
|
||||
BRANCH:
|
||||
CI: true
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY:
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME:
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD:
|
||||
GRADLE_ENTERPRISE_URL: https://ge.spring.io
|
||||
run:
|
||||
path: bash
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
${PWD}/git-repo/ci/scripts/build-project.sh
|
||||
@@ -1,24 +0,0 @@
|
||||
---
|
||||
platform: linux
|
||||
inputs:
|
||||
- name: git-repo
|
||||
outputs:
|
||||
- name: distribution-repository
|
||||
- name: git-repo
|
||||
caches:
|
||||
- path: gradle
|
||||
params:
|
||||
BRANCH:
|
||||
CI: true
|
||||
MAIN_TOOLCHAIN:
|
||||
TEST_TOOLCHAIN:
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY:
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME:
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD:
|
||||
GRADLE_ENTERPRISE_URL: https://ge.spring.io
|
||||
run:
|
||||
path: bash
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
${PWD}/git-repo/ci/scripts/check-project.sh
|
||||
@@ -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.3.4'
|
||||
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
|
||||
@@ -0,0 +1,39 @@
|
||||
antora:
|
||||
extensions:
|
||||
- require: '@springio/antora-extensions'
|
||||
root_component_name: 'framework'
|
||||
site:
|
||||
title: Spring Framework
|
||||
url: https://docs.spring.io/spring-framework/reference
|
||||
robots: allow
|
||||
git:
|
||||
ensure_git_suffix: false
|
||||
content:
|
||||
sources:
|
||||
- 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']
|
||||
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:
|
||||
extensions:
|
||||
- '@asciidoctor/tabs'
|
||||
- '@springio/asciidoctor-extensions'
|
||||
- '@springio/asciidoctor-extensions/include-code-extension'
|
||||
attributes:
|
||||
page-stackoverflow-url: https://stackoverflow.com/questions/tagged/spring
|
||||
page-pagination: ''
|
||||
hide-uri-scheme: '@'
|
||||
tabs-sync-option: '@'
|
||||
include-java: 'example$docs-src/main/java/org/springframework/docs'
|
||||
urls:
|
||||
latest_version_segment_strategy: redirect:to
|
||||
latest_version_segment: ''
|
||||
redirect_facility: httpd
|
||||
runtime:
|
||||
log:
|
||||
failure_level: warn
|
||||
ui:
|
||||
bundle:
|
||||
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.16/ui-bundle.zip
|
||||
@@ -10,27 +10,10 @@ apply from: "${rootDir}/gradle/publications.gradle"
|
||||
|
||||
|
||||
antora {
|
||||
version = '3.2.0-alpha.2'
|
||||
playbook = 'cached-antora-playbook.yml'
|
||||
playbookProvider {
|
||||
repository = 'spring-projects/spring-framework'
|
||||
branch = 'docs-build'
|
||||
path = 'lib/antora/templates/per-branch-antora-playbook.yml'
|
||||
checkLocalBranch = true
|
||||
}
|
||||
options = ['--clean', '--stacktrace']
|
||||
options = [clean: true, fetch: !project.gradle.startParameter.offline, stacktrace: true]
|
||||
environment = [
|
||||
'ALGOLIA_API_KEY': '82c7ead946afbac3cf98c32446154691',
|
||||
'ALGOLIA_APP_ID': '244V8V9FGG',
|
||||
'ALGOLIA_INDEX_NAME': 'framework-docs'
|
||||
]
|
||||
dependencies = [
|
||||
'@antora/atlas-extension': '1.0.0-alpha.1',
|
||||
'@antora/collector-extension': '1.0.0-alpha.3',
|
||||
'@asciidoctor/tabs': '1.0.0-beta.3',
|
||||
'@opendevise/antora-release-line-extension': '1.0.0-alpha.2',
|
||||
'@springio/antora-extensions': '1.3.0',
|
||||
'@springio/asciidoctor-extensions': '1.0.0-alpha.9'
|
||||
'BUILD_REFNAME': 'HEAD',
|
||||
'BUILD_VERSION': project.version,
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -39,8 +39,8 @@
|
||||
** xref:core/resources.adoc[]
|
||||
** xref:core/validation.adoc[]
|
||||
*** xref:core/validation/validator.adoc[]
|
||||
*** xref:core/validation/conversion.adoc[]
|
||||
*** xref:core/validation/beans-beans.adoc[]
|
||||
*** xref:core/validation/conversion.adoc[]
|
||||
*** xref:core/validation/convert.adoc[]
|
||||
*** xref:core/validation/format.adoc[]
|
||||
*** xref:core/validation/format-configuring-formatting-globaldatetimeformat.adoc[]
|
||||
@@ -431,4 +431,4 @@
|
||||
** xref:languages/groovy.adoc[]
|
||||
** xref:languages/dynamic.adoc[]
|
||||
* xref:appendix.adoc[]
|
||||
* https://github.com/spring-projects/spring-framework/wiki[Wiki]
|
||||
* https://github.com/spring-projects/spring-framework/wiki[Wiki]
|
||||
|
||||
@@ -168,7 +168,7 @@ Kotlin::
|
||||
======
|
||||
|
||||
NOTE: Pooling stateless service objects is not usually necessary. We do not believe it should
|
||||
be the default choice, as most stateless objects are naturally thread safe, and instance
|
||||
be the default choice, as most stateless objects are naturally thread-safe, and instance
|
||||
pooling is problematic if resources are cached.
|
||||
|
||||
Simpler pooling is available by using auto-proxying. You can set the `TargetSource` implementations
|
||||
|
||||
@@ -176,7 +176,7 @@ Kotlin::
|
||||
@AfterReturning(
|
||||
pointcut = "execution(* com.xyz.dao.*.*(..))",
|
||||
returning = "retVal")
|
||||
fun doAccessCheck(retVal: Any) {
|
||||
fun doAccessCheck(retVal: Any?) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
@@ -448,7 +448,7 @@ Kotlin::
|
||||
class AroundExample {
|
||||
|
||||
@Around("execution(* com.xyz..service.*.*(..))")
|
||||
fun doBasicProfiling(pjp: ProceedingJoinPoint): Any {
|
||||
fun doBasicProfiling(pjp: ProceedingJoinPoint): Any? {
|
||||
// start stopwatch
|
||||
val retVal = pjp.proceed()
|
||||
// stop stopwatch
|
||||
@@ -893,7 +893,7 @@ Kotlin::
|
||||
"com.xyz.CommonPointcuts.inDataAccessLayer() && " +
|
||||
"args(accountHolderNamePattern)") // <1>
|
||||
fun preProcessQueryPattern(pjp: ProceedingJoinPoint,
|
||||
accountHolderNamePattern: String): Any {
|
||||
accountHolderNamePattern: String): Any? {
|
||||
val newPattern = preProcess(accountHolderNamePattern)
|
||||
return pjp.proceed(arrayOf<Any>(newPattern))
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ Kotlin::
|
||||
}
|
||||
|
||||
@Around("com.xyz.CommonPointcuts.businessService()") // <1>
|
||||
fun doConcurrentOperation(pjp: ProceedingJoinPoint): Any {
|
||||
fun doConcurrentOperation(pjp: ProceedingJoinPoint): Any? {
|
||||
var numAttempts = 0
|
||||
var lockFailureException: PessimisticLockingFailureException
|
||||
do {
|
||||
@@ -173,7 +173,7 @@ Kotlin::
|
||||
----
|
||||
@Around("execution(* com.xyz..service.*.*(..)) && " +
|
||||
"@annotation(com.xyz.service.Idempotent)")
|
||||
fun doConcurrentOperation(pjp: ProceedingJoinPoint): Any {
|
||||
fun doConcurrentOperation(pjp: ProceedingJoinPoint): Any? {
|
||||
// ...
|
||||
}
|
||||
----
|
||||
|
||||
@@ -6,8 +6,8 @@ it until later.
|
||||
|
||||
By default, there is a single instance of each aspect within the application
|
||||
context. AspectJ calls this the singleton instantiation model. It is possible to define
|
||||
aspects with alternate lifecycles. Spring supports AspectJ's `perthis` and `pertarget`
|
||||
instantiation models; `percflow`, `percflowbelow`, and `pertypewithin` are not currently
|
||||
aspects with alternate lifecycles. Spring supports AspectJ's `perthis`, `pertarget`, and
|
||||
`pertypewithin` instantiation models; `percflow` and `percflowbelow` are not currently
|
||||
supported.
|
||||
|
||||
You can declare a `perthis` aspect by specifying a `perthis` clause in the `@Aspect`
|
||||
|
||||
@@ -435,7 +435,7 @@ Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
|
||||
----
|
||||
fun doBasicProfiling(pjp: ProceedingJoinPoint): Any {
|
||||
fun doBasicProfiling(pjp: ProceedingJoinPoint): Any? {
|
||||
// start stopwatch
|
||||
val retVal = pjp.proceed()
|
||||
// stop stopwatch
|
||||
@@ -554,7 +554,7 @@ Kotlin::
|
||||
|
||||
class SimpleProfiler {
|
||||
|
||||
fun profile(call: ProceedingJoinPoint, name: String, age: Int): Any {
|
||||
fun profile(call: ProceedingJoinPoint, name: String, age: Int): Any? {
|
||||
val clock = StopWatch("Profiling for '$name' and '$age'")
|
||||
try {
|
||||
clock.start(call.toShortString())
|
||||
@@ -890,7 +890,7 @@ Kotlin::
|
||||
this.order = order
|
||||
}
|
||||
|
||||
fun doConcurrentOperation(pjp: ProceedingJoinPoint): Any {
|
||||
fun doConcurrentOperation(pjp: ProceedingJoinPoint): Any? {
|
||||
var numAttempts = 0
|
||||
var lockFailureException: PessimisticLockingFailureException
|
||||
do {
|
||||
|
||||
@@ -493,7 +493,7 @@ Kotlin::
|
||||
class ProfilingAspect {
|
||||
|
||||
@Around("methodsToBeProfiled()")
|
||||
fun profile(pjp: ProceedingJoinPoint): Any {
|
||||
fun profile(pjp: ProceedingJoinPoint): Any? {
|
||||
val sw = StopWatch(javaClass.simpleName)
|
||||
try {
|
||||
sw.start(pjp.getSignature().getName())
|
||||
@@ -828,13 +828,6 @@ The following table summarizes various `LoadTimeWeaver` implementations:
|
||||
| Running in Red Hat's https://www.jboss.org/jbossas/[JBoss AS] or https://www.wildfly.org/[WildFly]
|
||||
| `JBossLoadTimeWeaver`
|
||||
|
||||
| Running in IBM's https://www-01.ibm.com/software/webservers/appserv/was/[WebSphere]
|
||||
| `WebSphereLoadTimeWeaver`
|
||||
|
||||
| Running in Oracle's
|
||||
https://www.oracle.com/technetwork/middleware/weblogic/overview/index-085209.html[WebLogic]
|
||||
| `WebLogicLoadTimeWeaver`
|
||||
|
||||
| JVM started with Spring `InstrumentationSavingAgent`
|
||||
(`java -javaagent:path/to/spring-instrument.jar`)
|
||||
| `InstrumentationLoadTimeWeaver`
|
||||
@@ -949,11 +942,11 @@ when you use Spring's LTW support in environments such as application servers an
|
||||
containers.
|
||||
|
||||
[[aop-aj-ltw-environments-tomcat-jboss-etc]]
|
||||
==== Tomcat, JBoss, WebSphere, WebLogic
|
||||
==== Tomcat, JBoss, WildFly
|
||||
|
||||
Tomcat, JBoss/WildFly, IBM WebSphere Application Server and Oracle WebLogic Server all
|
||||
provide a general app `ClassLoader` that is capable of local instrumentation. Spring's
|
||||
native LTW may leverage those ClassLoader implementations to provide AspectJ weaving.
|
||||
Tomcat and JBoss/WildFly provide a general app `ClassLoader` that is capable of local
|
||||
instrumentation. Spring's native LTW may leverage those ClassLoader implementations
|
||||
to provide AspectJ weaving.
|
||||
You can simply enable load-time weaving, as xref:core/aop/using-aspectj.adoc[described earlier].
|
||||
Specifically, you do not need to modify the JVM launch script to add
|
||||
`-javaagent:path/to/spring-instrument.jar`.
|
||||
|
||||
@@ -565,6 +565,17 @@ is a convenience mechanism that sets up a xref:core/beans/factory-extension.adoc
|
||||
for you. If you need more control over the specific
|
||||
`PropertySourcesPlaceholderConfigurer` setup, you can explicitly define it as a bean yourself.
|
||||
|
||||
[WARNING]
|
||||
=====
|
||||
Only one such element should be defined for a given application with the properties
|
||||
that it needs. Several property placeholders can be configured as long as they have distinct
|
||||
placeholder syntax (`${...}`).
|
||||
|
||||
If you need to modularize the source of properties used for the replacement, you should
|
||||
not create multiple properties placeholders. Rather, each module should contribute a
|
||||
`PropertySource` to the `Environment`. Alternatively, you can create your own
|
||||
`PropertySourcesPlaceholderConfigurer` bean that gathers the properties to use.
|
||||
=====
|
||||
|
||||
[[xsd-schemas-context-ac]]
|
||||
=== Using `<annotation-config/>`
|
||||
|
||||
+8
-7
@@ -153,15 +153,16 @@ Letting qualifier values select against target bean names, within the type-match
|
||||
candidates, does not require a `@Qualifier` annotation at the injection point.
|
||||
If there is no other resolution indicator (such as a qualifier or a primary marker),
|
||||
for a non-unique dependency situation, Spring matches the injection point name
|
||||
(that is, the field name or parameter name) against the target bean names and chooses the
|
||||
same-named candidate, if any.
|
||||
(that is, the field name or parameter name) against the target bean names and chooses
|
||||
the same-named candidate, if any (either by bean name or by associated alias).
|
||||
|
||||
This requires the `-parameters` Java compiler flag to be present. As a fallback,
|
||||
version 6.0 still supports parameter names from debug symbols via `-debug` as well.
|
||||
====
|
||||
|
||||
That said, if you intend to express annotation-driven injection by name, do not
|
||||
primarily use `@Autowired`, even if it is capable of selecting by bean name among
|
||||
type-matching candidates. Instead, use the JSR-250 `@Resource` annotation, which is
|
||||
semantically defined to identify a specific target component by its unique name, with
|
||||
the declared type being irrelevant for the matching process. `@Autowired` has rather
|
||||
As an alternative for injection by name, consider the JSR-250 `@Resource` annotation
|
||||
which is semantically defined to identify a specific target component by its unique name,
|
||||
with the declared type being irrelevant for the matching process. `@Autowired` has rather
|
||||
different semantics: After selecting candidate beans by type, the specified `String`
|
||||
qualifier value is considered within those type-selected candidates only (for example,
|
||||
matching an `account` qualifier against beans marked with the same qualifier label).
|
||||
|
||||
@@ -364,8 +364,6 @@ ignored if it cannot be autowired. This allows properties to be assigned default
|
||||
that can be optionally overridden via dependency injection.
|
||||
====
|
||||
|
||||
|
||||
|
||||
[[beans-autowired-annotation-constructor-resolution]]
|
||||
Injected constructor and factory method arguments are a special case since the `required`
|
||||
attribute in `@Autowired` has a somewhat different meaning due to Spring's constructor
|
||||
|
||||
@@ -22,10 +22,11 @@ additional metadata formats.
|
||||
In most application scenarios, explicit user code is not required to instantiate one or
|
||||
more instances of a Spring IoC container. For example, in a web application scenario, a
|
||||
simple eight (or so) lines of boilerplate web descriptor XML in the `web.xml` file
|
||||
of the application typically suffices (see xref:core/beans/context-introduction.adoc#context-create[Convenient ApplicationContext Instantiation for Web Applications]). If you use the
|
||||
https://spring.io/tools[Spring Tools for Eclipse] (an Eclipse-powered development
|
||||
environment), you can easily create this boilerplate configuration with a few mouse clicks or
|
||||
keystrokes.
|
||||
of the application typically suffices (see
|
||||
xref:core/beans/context-introduction.adoc#context-create[Convenient ApplicationContext Instantiation for Web Applications]).
|
||||
If you use the https://spring.io/tools[Spring Tools for Eclipse] (an Eclipse-powered
|
||||
development environment), you can easily create this boilerplate configuration with a
|
||||
few mouse clicks or keystrokes.
|
||||
|
||||
The following diagram shows a high-level view of how Spring works. Your application classes
|
||||
are combined with configuration metadata so that, after the `ApplicationContext` is
|
||||
@@ -139,9 +140,11 @@ Kotlin::
|
||||
[NOTE]
|
||||
====
|
||||
After you learn about Spring's IoC container, you may want to know more about Spring's
|
||||
`Resource` abstraction (as described in xref:web/webflux-webclient/client-builder.adoc#webflux-client-builder-reactor-resources[Resources]), which provides a convenient
|
||||
mechanism for reading an InputStream from locations defined in a URI syntax. In particular,
|
||||
`Resource` paths are used to construct applications contexts, as described in xref:core/resources.adoc#resources-app-ctx[Application Contexts and Resource Paths].
|
||||
`Resource` abstraction (as described in
|
||||
xref:core/resources.adoc[Resources])
|
||||
which provides a convenient mechanism for reading an InputStream from locations defined
|
||||
in a URI syntax. In particular, `Resource` paths are used to construct applications contexts,
|
||||
as described in xref:core/resources.adoc#resources-app-ctx[Application Contexts and Resource Paths].
|
||||
====
|
||||
|
||||
The following example shows the service layer objects `(services.xml)` configuration file:
|
||||
@@ -208,9 +211,9 @@ XML configuration file represents a logical layer or module in your architecture
|
||||
|
||||
You can use the application context constructor to load bean definitions from all these
|
||||
XML fragments. This constructor takes multiple `Resource` locations, as was shown in the
|
||||
xref:core/beans/basics.adoc#beans-factory-instantiation[previous section]. Alternatively, use one or more
|
||||
occurrences of the `<import/>` element to load bean definitions from another file or
|
||||
files. The following example shows how to do so:
|
||||
xref:core/beans/basics.adoc#beans-factory-instantiation[previous section]. Alternatively,
|
||||
use one or more occurrences of the `<import/>` element to load bean definitions from
|
||||
another file or files. The following example shows how to do so:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
|
||||
@@ -484,13 +484,14 @@ custom event (`BlockedListEvent` in the preceding example). This means that the
|
||||
You can register as many event listeners as you wish, but note that, by default, event
|
||||
listeners receive events synchronously. This means that the `publishEvent()` method
|
||||
blocks until all listeners have finished processing the event. One advantage of this
|
||||
synchronous and single-threaded approach is that, when a listener receives an event, it
|
||||
operates inside the transaction context of the publisher if a transaction context is
|
||||
available. If another strategy for event publication becomes necessary, see the javadoc
|
||||
for Spring's
|
||||
synchronous and single-threaded approach is that, when a listener receives an event,
|
||||
it operates inside the transaction context of the publisher if a transaction context
|
||||
is available. If another strategy for event publication becomes necessary, e.g.
|
||||
asynchronous event processing by default, see the javadoc for Spring's
|
||||
{api-spring-framework}/context/event/ApplicationEventMulticaster.html[`ApplicationEventMulticaster`] interface
|
||||
and {api-spring-framework}/context/event/SimpleApplicationEventMulticaster.html[`SimpleApplicationEventMulticaster`]
|
||||
implementation for configuration options.
|
||||
implementation for configuration options which can be applied to a custom
|
||||
"applicationEventMulticaster" bean definition.
|
||||
|
||||
The following example shows the bean definitions used to register and configure each of
|
||||
the classes above:
|
||||
@@ -510,6 +511,12 @@ the classes above:
|
||||
<bean id="blockedListNotifier" class="example.BlockedListNotifier">
|
||||
<property name="notificationAddress" value="blockedlist@example.org"/>
|
||||
</bean>
|
||||
|
||||
<!-- optional: a custom ApplicationEventMulticaster definition -->
|
||||
<bean id="applicationEventMulticaster" class="org.springframework.context.event.SimpleApplicationEventMulticaster">
|
||||
<property name="taskExecutor" ref="..."/>
|
||||
<property name="errorHandler" ref="..."/>
|
||||
</bean>
|
||||
----
|
||||
|
||||
Putting it all together, when the `sendEmail()` method of the `emailService` bean is
|
||||
@@ -849,6 +856,23 @@ Kotlin::
|
||||
TIP: This works not only for `ApplicationEvent` but any arbitrary object that you send as
|
||||
an event.
|
||||
|
||||
Finally, as with classic `ApplicationListener` implementations, the actual multicasting
|
||||
happens via a context-wide `ApplicationEventMulticaster` at runtime. By default, this is a
|
||||
`SimpleApplicationEventMulticaster` with synchronous event publication in the caller thread.
|
||||
This can be replaced/customized through an "applicationEventMulticaster" bean definition,
|
||||
e.g. for processing all events asynchronously and/or for handling listener exceptions:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Bean
|
||||
ApplicationEventMulticaster applicationEventMulticaster() {
|
||||
SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
|
||||
multicaster.setTaskExecutor(...);
|
||||
multicaster.setErrorHandler(...);
|
||||
return multicaster;
|
||||
}
|
||||
----
|
||||
|
||||
|
||||
|
||||
[[context-functionality-resources]]
|
||||
@@ -910,7 +934,7 @@ Java::
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
// create a startup step and start recording
|
||||
StartupStep scanPackages = this.getApplicationStartup().start("spring.context.base-packages.scan");
|
||||
StartupStep scanPackages = getApplicationStartup().start("spring.context.base-packages.scan");
|
||||
// add tagging information to the current step
|
||||
scanPackages.tag("packages", () -> Arrays.toString(basePackages));
|
||||
// perform the actual phase we're instrumenting
|
||||
@@ -924,7 +948,7 @@ Kotlin::
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
// create a startup step and start recording
|
||||
val scanPackages = this.getApplicationStartup().start("spring.context.base-packages.scan")
|
||||
val scanPackages = getApplicationStartup().start("spring.context.base-packages.scan")
|
||||
// add tagging information to the current step
|
||||
scanPackages.tag("packages", () -> Arrays.toString(basePackages))
|
||||
// perform the actual phase we're instrumenting
|
||||
|
||||
@@ -309,7 +309,7 @@ how to configure such a bean:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
<!-- the factory bean, which contains a method called createInstance() -->
|
||||
<!-- the factory bean, which contains a method called createClientServiceInstance() -->
|
||||
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
|
||||
<!-- inject any dependencies required by this locator bean -->
|
||||
</bean>
|
||||
@@ -416,8 +416,8 @@ Kotlin::
|
||||
======
|
||||
|
||||
This approach shows that the factory bean itself can be managed and configured through
|
||||
dependency injection (DI). See xref:core/beans/dependencies/factory-properties-detailed.adoc[Dependencies and Configuration in Detail]
|
||||
.
|
||||
dependency injection (DI).
|
||||
See xref:core/beans/dependencies/factory-properties-detailed.adoc[Dependencies and Configuration in Detail].
|
||||
|
||||
NOTE: In Spring documentation, "factory bean" refers to a bean that is configured in the
|
||||
Spring container and that creates objects through an
|
||||
@@ -444,5 +444,3 @@ cases into account and returns the type of object that a `BeanFactory.getBean` c
|
||||
going to return for the same bean name.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -562,8 +562,9 @@ If no profile is active, the `dataSource` is created. You can see this
|
||||
as a way to provide a default definition for one or more beans. If any
|
||||
profile is enabled, the default profile does not apply.
|
||||
|
||||
You can change the name of the default profile by using `setDefaultProfiles()` on
|
||||
the `Environment` or, declaratively, by using the `spring.profiles.default` property.
|
||||
The name of the default profile is `default`. You can change the name of
|
||||
the default profile by using `setDefaultProfiles()` on the `Environment` or,
|
||||
declaratively, by using the `spring.profiles.default` property.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -23,9 +23,8 @@ You can set this property only if the `BeanPostProcessor` implements the `Ordere
|
||||
interface. If you write your own `BeanPostProcessor`, you should consider implementing
|
||||
the `Ordered` interface, too. For further details, see the javadoc of the
|
||||
{api-spring-framework}/beans/factory/config/BeanPostProcessor.html[`BeanPostProcessor`]
|
||||
and {api-spring-framework}/core/Ordered.html[`Ordered`] interfaces. See also the note
|
||||
on xref:core/beans/factory-extension.adoc#beans-factory-programmatically-registering-beanpostprocessors[programmatic registration of `BeanPostProcessor` instances]
|
||||
.
|
||||
and {api-spring-framework}/core/Ordered.html[`Ordered`] interfaces. See also the note on
|
||||
xref:core/beans/factory-extension.adoc#beans-factory-programmatically-registering-beanpostprocessors[programmatic registration of `BeanPostProcessor` instances].
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
@@ -280,11 +279,12 @@ and {api-spring-framework}/core/Ordered.html[`Ordered`] interfaces for more deta
|
||||
====
|
||||
If you want to change the actual bean instances (that is, the objects that are created
|
||||
from the configuration metadata), then you instead need to use a `BeanPostProcessor`
|
||||
(described earlier in xref:core/beans/factory-extension.adoc#beans-factory-extension-bpp[Customizing Beans by Using a `BeanPostProcessor`]). While it is technically possible
|
||||
to work with bean instances within a `BeanFactoryPostProcessor` (for example, by using
|
||||
`BeanFactory.getBean()`), doing so causes premature bean instantiation, violating the
|
||||
standard container lifecycle. This may cause negative side effects, such as bypassing
|
||||
bean post processing.
|
||||
(described earlier in
|
||||
xref:core/beans/factory-extension.adoc#beans-factory-extension-bpp[Customizing Beans by Using a `BeanPostProcessor`]).
|
||||
While it is technically possible to work with bean instances within a `BeanFactoryPostProcessor`
|
||||
(for example, by using `BeanFactory.getBean()`), doing so causes premature bean instantiation,
|
||||
violating the standard container lifecycle. This may cause negative side effects, such as
|
||||
bypassing bean post processing.
|
||||
|
||||
Also, `BeanFactoryPostProcessor` instances are scoped per-container. This is only relevant
|
||||
if you use container hierarchies. If you define a `BeanFactoryPostProcessor` in one
|
||||
@@ -331,8 +331,7 @@ with placeholder values is defined:
|
||||
<property name="locations" value="classpath:com/something/jdbc.properties"/>
|
||||
</bean>
|
||||
|
||||
<bean id="dataSource" destroy-method="close"
|
||||
class="org.apache.commons.dbcp.BasicDataSource">
|
||||
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
|
||||
<property name="driverClassName" value="${jdbc.driverClassName}"/>
|
||||
<property name="url" value="${jdbc.url}"/>
|
||||
<property name="username" value="${jdbc.username}"/>
|
||||
@@ -373,6 +372,17 @@ The `PropertySourcesPlaceholderConfigurer` not only looks for properties in the
|
||||
file you specify. By default, if it cannot find a property in the specified properties files,
|
||||
it checks against Spring `Environment` properties and regular Java `System` properties.
|
||||
|
||||
[WARNING]
|
||||
=====
|
||||
Only one such element should be defined for a given application with the properties
|
||||
that it needs. Several property placeholders can be configured as long as they have distinct
|
||||
placeholder syntax (`${...}`).
|
||||
|
||||
If you need to modularize the source of properties used for the replacement, you should
|
||||
not create multiple properties placeholders. Rather, you should create your own
|
||||
`PropertySourcesPlaceholderConfigurer` bean that gathers the properties to use.
|
||||
=====
|
||||
|
||||
[TIP]
|
||||
=====
|
||||
You can use the `PropertySourcesPlaceholderConfigurer` to substitute class names, which
|
||||
@@ -429,12 +439,12 @@ dataSource.url=jdbc:mysql:mydb
|
||||
----
|
||||
|
||||
This example file can be used with a container definition that contains a bean called
|
||||
`dataSource` that has `driver` and `url` properties.
|
||||
`dataSource` that has `driverClassName` and `url` properties.
|
||||
|
||||
Compound property names are also supported, as long as every component of the path
|
||||
except the final property being overridden is already non-null (presumably initialized
|
||||
by the constructors). In the following example, the `sammy` property of the `bob` property of the `fred` property of the `tom` bean
|
||||
is set to the scalar value `123`:
|
||||
by the constructors). In the following example, the `sammy` property of the `bob`
|
||||
property of the `fred` property of the `tom` bean is set to the scalar value `123`:
|
||||
|
||||
[literal,subs="verbatim,quotes"]
|
||||
----
|
||||
|
||||
@@ -42,6 +42,7 @@ startup and shutdown process, as driven by the container's own lifecycle.
|
||||
The lifecycle callback interfaces are described in this section.
|
||||
|
||||
|
||||
|
||||
[[beans-factory-lifecycle-initializingbean]]
|
||||
=== Initialization Callbacks
|
||||
|
||||
@@ -132,6 +133,30 @@ Kotlin::
|
||||
|
||||
However, the first of the two preceding examples does not couple the code to Spring.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
Be aware that `@PostConstruct` and initialization methods in general are executed
|
||||
within the container's singleton creation lock. The bean instance is only considered
|
||||
as fully initialized and ready to be published to others after returning from the
|
||||
`@PostConstruct` method. Such individual initialization methods are only meant
|
||||
for validating the configuration state and possibly preparing some data structures
|
||||
based on the given configuration but no further activity with external bean access.
|
||||
Otherwise there is a risk for an initialization deadlock.
|
||||
|
||||
For a scenario where expensive post-initialization activity is to be triggered,
|
||||
e.g. asynchronous database preparation steps, your bean should either implement
|
||||
`SmartInitializingSingleton.afterSingletonsInstantiated()` or rely on the context
|
||||
refresh event: implementing `ApplicationListener<ContextRefreshedEvent>` or
|
||||
declaring its annotation equivalent `@EventListener(ContextRefreshedEvent.class)`.
|
||||
Those variants come after all regular singleton initialization and therefore
|
||||
outside of any singleton creation lock.
|
||||
|
||||
Alternatively, you may implement the `(Smart)Lifecycle` interface and integrate with
|
||||
the container's overall lifecycle management, including an auto-startup mechanism,
|
||||
a pre-destroy stop step, and potential stop/restart callbacks (see below).
|
||||
====
|
||||
|
||||
|
||||
|
||||
[[beans-factory-lifecycle-disposablebean]]
|
||||
=== Destruction Callbacks
|
||||
@@ -155,7 +180,7 @@ xref:core/beans/java/bean-annotation.adoc#beans-java-lifecycle-callbacks[Receivi
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
|
||||
<bean id="exampleDestructionBean" class="examples.ExampleBean" destroy-method="cleanup"/>
|
||||
----
|
||||
|
||||
[tabs]
|
||||
@@ -189,7 +214,7 @@ The preceding definition has almost exactly the same effect as the following def
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
|
||||
<bean id="exampleDestructionBean" class="examples.AnotherExampleBean"/>
|
||||
----
|
||||
|
||||
[tabs]
|
||||
@@ -222,32 +247,45 @@ Kotlin::
|
||||
|
||||
However, the first of the two preceding definitions does not couple the code to Spring.
|
||||
|
||||
TIP: You can assign the `destroy-method` attribute of a `<bean>` element a special
|
||||
`(inferred)` value, which instructs Spring to automatically detect a public `close` or
|
||||
`shutdown` method on the specific bean class. (Any class that implements
|
||||
`java.lang.AutoCloseable` or `java.io.Closeable` would therefore match.) You can also set
|
||||
this special `(inferred)` value on the `default-destroy-method` attribute of a
|
||||
`<beans>` element to apply this behavior to an entire set of beans (see
|
||||
xref:core/beans/factory-nature.adoc#beans-factory-lifecycle-default-init-destroy-methods[Default Initialization and Destroy Methods]). Note that this is the
|
||||
default behavior with Java configuration.
|
||||
Note that Spring also supports inference of destroy methods, detecting a public `close` or
|
||||
`shutdown` method. This is the default behavior for `@Bean` methods in Java configuration
|
||||
classes and automatically matches `java.lang.AutoCloseable` or `java.io.Closeable`
|
||||
implementations, not coupling the destruction logic to Spring either.
|
||||
|
||||
TIP: For destroy method inference with XML, you may assign the `destroy-method` attribute
|
||||
of a `<bean>` element a special `(inferred)` value, which instructs Spring to automatically
|
||||
detect a public `close` or `shutdown` method on the bean class for a specific bean definition.
|
||||
You can also set this special `(inferred)` value on the `default-destroy-method` attribute
|
||||
of a `<beans>` element to apply this behavior to an entire set of bean definitions (see
|
||||
xref:core/beans/factory-nature.adoc#beans-factory-lifecycle-default-init-destroy-methods[Default Initialization and Destroy Methods]).
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
For extended shutdown phases, you may implement the `Lifecycle` interface and receive
|
||||
an early stop signal before the destroy methods of any singleton beans are called.
|
||||
You may also implement `SmartLifecycle` for a time-bound stop step where the container
|
||||
will wait for all such stop processing to complete before moving on to destroy methods.
|
||||
====
|
||||
|
||||
|
||||
|
||||
[[beans-factory-lifecycle-default-init-destroy-methods]]
|
||||
=== Default Initialization and Destroy Methods
|
||||
|
||||
When you write initialization and destroy method callbacks that do not use the
|
||||
Spring-specific `InitializingBean` and `DisposableBean` callback interfaces, you
|
||||
typically write methods with names such as `init()`, `initialize()`, `dispose()`, and so
|
||||
on. Ideally, the names of such lifecycle callback methods are standardized across a
|
||||
project so that all developers use the same method names and ensure consistency.
|
||||
typically write methods with names such as `init()`, `initialize()`, `dispose()`,
|
||||
and so on. Ideally, the names of such lifecycle callback methods are standardized across
|
||||
a project so that all developers use the same method names and ensure consistency.
|
||||
|
||||
You can configure the Spring container to "`look`" for named initialization and destroy
|
||||
callback method names on every bean. This means that you, as an application
|
||||
developer, can write your application classes and use an initialization callback called
|
||||
`init()`, without having to configure an `init-method="init"` attribute with each bean
|
||||
definition. The Spring IoC container calls that method when the bean is created (and in
|
||||
accordance with the standard lifecycle callback contract xref:core/beans/factory-nature.adoc#beans-factory-lifecycle[described previously]
|
||||
). This feature also enforces a consistent naming convention for
|
||||
initialization and destroy method callbacks.
|
||||
callback method names on every bean. This means that you, as an application developer,
|
||||
can write your application classes and use an initialization callback called `init()`,
|
||||
without having to configure an `init-method="init"` attribute with each bean definition.
|
||||
The Spring IoC container calls that method when the bean is created (and in accordance
|
||||
with the standard lifecycle callback contract xref:core/beans/factory-nature.adoc#beans-factory-lifecycle[described previously]).
|
||||
This feature also enforces a consistent naming convention for initialization and
|
||||
destroy method callbacks.
|
||||
|
||||
Suppose that your initialization callback methods are named `init()` and your destroy
|
||||
callback methods are named `destroy()`. Your class then resembles the class in the
|
||||
@@ -407,14 +445,15 @@ and closed.
|
||||
[TIP]
|
||||
====
|
||||
Note that the regular `org.springframework.context.Lifecycle` interface is a plain
|
||||
contract for explicit start and stop notifications and does not imply auto-startup at context
|
||||
refresh time. For fine-grained control over auto-startup of a specific bean (including startup phases),
|
||||
consider implementing `org.springframework.context.SmartLifecycle` instead.
|
||||
contract for explicit start and stop notifications and does not imply auto-startup
|
||||
at context refresh time. For fine-grained control over auto-startup and for graceful
|
||||
stopping of a specific bean (including startup and stop phases), consider implementing
|
||||
the extended `org.springframework.context.SmartLifecycle` interface instead.
|
||||
|
||||
Also, please note that stop notifications are not guaranteed to come before destruction.
|
||||
On regular shutdown, all `Lifecycle` beans first receive a stop notification before
|
||||
the general destruction callbacks are being propagated. However, on hot refresh during a
|
||||
context's lifetime or on stopped refresh attempts, only destroy methods are called.
|
||||
the general destruction callbacks are being propagated. However, on hot refresh during
|
||||
a context's lifetime or on stopped refresh attempts, only destroy methods are called.
|
||||
====
|
||||
|
||||
The order of startup and shutdown invocations can be important. If a "`depends-on`"
|
||||
|
||||
@@ -125,8 +125,8 @@ objects regardless of scope, in the case of prototypes, configured destruction
|
||||
lifecycle callbacks are not called. The client code must clean up prototype-scoped
|
||||
objects and release expensive resources that the prototype beans hold. To get
|
||||
the Spring container to release resources held by prototype-scoped beans, try using a
|
||||
custom xref:core/beans/factory-extension.adoc#beans-factory-extension-bpp[bean post-processor], which holds a reference to
|
||||
beans that need to be cleaned up.
|
||||
custom xref:core/beans/factory-extension.adoc#beans-factory-extension-bpp[bean post-processor]
|
||||
which holds a reference to beans that need to be cleaned up.
|
||||
|
||||
In some respects, the Spring container's role in regard to a prototype-scoped bean is a
|
||||
replacement for the Java `new` operator. All lifecycle management past that point must
|
||||
@@ -324,7 +324,6 @@ Kotlin::
|
||||
|
||||
|
||||
|
||||
|
||||
[[beans-factory-scopes-application]]
|
||||
=== Application Scope
|
||||
|
||||
@@ -374,7 +373,6 @@ Kotlin::
|
||||
|
||||
|
||||
|
||||
|
||||
[[beans-factory-scopes-websocket]]
|
||||
=== WebSocket Scope
|
||||
|
||||
@@ -384,7 +382,6 @@ xref:web/websocket/stomp/scope.adoc[WebSocket scope] for more details.
|
||||
|
||||
|
||||
|
||||
|
||||
[[beans-factory-scopes-other-injection]]
|
||||
=== Scoped Beans as Dependencies
|
||||
|
||||
@@ -449,12 +446,13 @@ understand the "`why`" as well as the "`how`" behind it:
|
||||
----
|
||||
<1> The line that defines the proxy.
|
||||
|
||||
To create such a proxy, you insert a child `<aop:scoped-proxy/>` element into a
|
||||
scoped bean definition (see
|
||||
xref:core/beans/factory-scopes.adoc#beans-factory-scopes-other-injection-proxies[Choosing the Type of Proxy to Create]
|
||||
and xref:core/appendix/xsd-schemas.adoc[XML Schema-based configuration]).
|
||||
|
||||
To create such a proxy, you insert a child `<aop:scoped-proxy/>` element into a scoped
|
||||
bean definition (see xref:core/beans/factory-scopes.adoc#beans-factory-scopes-other-injection-proxies[Choosing the Type of Proxy to Create] and
|
||||
xref:core/appendix/xsd-schemas.adoc[XML Schema-based configuration]).
|
||||
Why do definitions of beans scoped at the `request`, `session` and custom-scope
|
||||
levels require the `<aop:scoped-proxy/>` element?
|
||||
levels require the `<aop:scoped-proxy/>` element in common scenarios?
|
||||
Consider the following singleton bean definition and contrast it with
|
||||
what you need to define for the aforementioned scopes (note that the following
|
||||
`userPreferences` bean definition as it stands is incomplete):
|
||||
@@ -513,8 +511,8 @@ the `<aop:scoped-proxy/>` element, a CGLIB-based class proxy is created.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
CGLIB proxies intercept only public method calls! Do not call non-public methods
|
||||
on such a proxy. They are not delegated to the actual scoped target object.
|
||||
CGLIB proxies do not intercept private methods. Attempting to call a private method
|
||||
on such a proxy will not delegate to the actual scoped target object.
|
||||
====
|
||||
|
||||
Alternatively, you can configure the Spring container to create standard JDK
|
||||
|
||||
@@ -180,9 +180,10 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
The resolution mechanism is pretty much identical to constructor-based dependency
|
||||
injection. See xref:core/beans/dependencies/factory-collaborators.adoc#beans-constructor-injection[the relevant section] for more details.
|
||||
injection. See
|
||||
xref:core/beans/dependencies/factory-collaborators.adoc#beans-constructor-injection[the relevant section]
|
||||
for more details.
|
||||
|
||||
|
||||
[[beans-java-lifecycle-callbacks]]
|
||||
@@ -317,8 +318,9 @@ type, making it harder to use for cross-reference calls in other `@Bean` methods
|
||||
intend to refer to the provided resource here).
|
||||
=====
|
||||
|
||||
In the case of `BeanOne` from the example above the preceding note, it would be equally valid to call the `init()`
|
||||
method directly during construction, as the following example shows:
|
||||
In the case of `BeanOne` from the example above the preceding note, it would be
|
||||
equally valid to call the `init()` method directly during construction, as the
|
||||
following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@@ -506,10 +508,10 @@ Kotlin::
|
||||
[[beans-java-bean-aliasing]]
|
||||
== Bean Aliasing
|
||||
|
||||
As discussed in xref:core/beans/definition.adoc#beans-beanname[Naming Beans], it is sometimes desirable to give a single bean
|
||||
multiple names, otherwise known as bean aliasing. The `name` attribute of the `@Bean`
|
||||
annotation accepts a String array for this purpose. The following example shows how to set
|
||||
a number of aliases for a bean:
|
||||
As discussed in xref:core/beans/definition.adoc#beans-beanname[Naming Beans], it is
|
||||
sometimes desirable to give a single bean multiple names, otherwise known as bean aliasing.
|
||||
The `name` attribute of the `@Bean` annotation accepts a String array for this purpose.
|
||||
The following example shows how to set a number of aliases for a bean:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
|
||||
+7
-2
@@ -113,7 +113,8 @@ issue, because no compiler is involved, and you can declare
|
||||
When using `@Configuration` classes, the Java compiler places constraints on
|
||||
the configuration model, in that references to other beans must be valid Java syntax.
|
||||
|
||||
Fortunately, solving this problem is simple. As xref:core/beans/java/bean-annotation.adoc#beans-java-dependencies[we already discussed],
|
||||
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`
|
||||
classes, each depending on beans declared in the others:
|
||||
@@ -204,7 +205,6 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
There is another way to achieve the same result. Remember that `@Configuration` classes are
|
||||
ultimately only another bean in the container: This means that they can take advantage of
|
||||
`@Autowired` and `@Value` injection and other features the same as any other bean.
|
||||
@@ -216,6 +216,11 @@ classes are processed quite early during the initialization of the context, and
|
||||
to be injected this way may lead to unexpected early initialization. Whenever possible, resort to
|
||||
parameter-based injection, as in the preceding example.
|
||||
|
||||
Avoid access to locally defined beans within a `@PostConstruct` method on the same configuration
|
||||
class. This effectively leads to a circular reference since non-static `@Bean` methods semantically
|
||||
require a fully initialized configuration class instance to be called on. With circular references
|
||||
disallowed (e.g. in Spring Boot 2.6+), this may trigger a `BeanCurrentlyInCreationException`.
|
||||
|
||||
Also, be particularly careful with `BeanPostProcessor` and `BeanFactoryPostProcessor` definitions
|
||||
through `@Bean`. Those should usually be declared as `static @Bean` methods, not triggering the
|
||||
instantiation of their containing configuration class. Otherwise, `@Autowired` and `@Value` may not
|
||||
|
||||
+7
-5
@@ -4,7 +4,7 @@
|
||||
Projection lets a collection drive the evaluation of a sub-expression, and the result is
|
||||
a new collection. The syntax for projection is `.![projectionExpression]`. For example,
|
||||
suppose we have a list of inventors but want the list of cities where they were born.
|
||||
Effectively, we want to evaluate 'placeOfBirth.city' for every entry in the inventor
|
||||
Effectively, we want to evaluate `placeOfBirth.city` for every entry in the inventor
|
||||
list. The following example uses projection to do so:
|
||||
|
||||
[tabs]
|
||||
@@ -13,16 +13,18 @@ Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
// returns ['Smiljan', 'Idvor' ]
|
||||
List placesOfBirth = (List)parser.parseExpression("members.![placeOfBirth.city]");
|
||||
// evaluates to ["SmilJan", "Idvor"]
|
||||
List placesOfBirth = parser.parseExpression("members.![placeOfBirth.city]")
|
||||
.getValue(societyContext, List.class);
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
// returns ['Smiljan', 'Idvor' ]
|
||||
val placesOfBirth = parser.parseExpression("members.![placeOfBirth.city]") as List<*>
|
||||
// evaluates to ["SmilJan", "Idvor"]
|
||||
val placesOfBirth = parser.parseExpression("members.![placeOfBirth.city]")
|
||||
.getValue(societyContext) as List<*>
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
+12
-11
@@ -28,13 +28,14 @@ Kotlin::
|
||||
======
|
||||
|
||||
Selection is supported for arrays and anything that implements `java.lang.Iterable` or
|
||||
`java.util.Map`. For a list or array, the selection criteria is evaluated against each
|
||||
individual element. Against a map, the selection criteria is evaluated against each map
|
||||
entry (objects of the Java type `Map.Entry`). Each map entry has its `key` and `value`
|
||||
accessible as properties for use in the selection.
|
||||
`java.util.Map`. For an array or `Iterable`, the selection expression is evaluated
|
||||
against each individual element. Against a map, the selection expression is evaluated
|
||||
against each map entry (objects of the Java type `Map.Entry`). Each map entry has its
|
||||
`key` and `value` accessible as properties for use in the selection.
|
||||
|
||||
The following expression returns a new map that consists of those elements of the
|
||||
original map where the entry's value is less than 27:
|
||||
Given a `Map` stored in a variable named `#map`, the following expression returns a new
|
||||
map that consists of those elements of the original map where the entry's value is less
|
||||
than 27:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@@ -42,21 +43,21 @@ Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
Map newMap = parser.parseExpression("map.?[value<27]").getValue();
|
||||
Map newMap = parser.parseExpression("#map.?[value < 27]").getValue(Map.class);
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
val newMap = parser.parseExpression("map.?[value<27]").getValue()
|
||||
val newMap = parser.parseExpression("#map.?[value < 27]").getValue() as Map
|
||||
----
|
||||
======
|
||||
|
||||
In addition to returning all the selected elements, you can retrieve only the first or
|
||||
the last element. To obtain the first element matching the selection, the syntax is
|
||||
`.^[selectionExpression]`. To obtain the last matching selection, the syntax is
|
||||
`.$[selectionExpression]`.
|
||||
the last element. To obtain the first element matching the selection expression, the
|
||||
syntax is `.^[selectionExpression]`. To obtain the last element matching the selection
|
||||
expression, the syntax is `.$[selectionExpression]`.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -38,5 +38,15 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
If your application or framework manages its own `EvaluationContext`, you may need to
|
||||
manually configure a `StandardTypeLocator` with a specific `ClassLoader` to ensure that
|
||||
the SpEL expression parser is able to reliably locate user types.
|
||||
|
||||
For example, the `StandardBeanExpressionResolver` in the `spring-context` module
|
||||
configures a `StandardTypeLocator` using the bean `ClassLoader` of the corresponding
|
||||
`BeanFactory`.
|
||||
====
|
||||
|
||||
|
||||
|
||||
@@ -268,6 +268,8 @@ Java::
|
||||
@Service
|
||||
public class MyService {
|
||||
|
||||
private final ConversionService conversionService;
|
||||
|
||||
public MyService(ConversionService conversionService) {
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
@@ -211,19 +211,20 @@ 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.
|
||||
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.
|
||||
====
|
||||
|
||||
|
||||
|
||||
@@ -501,8 +501,8 @@ extend from it, your sub-class inherits a `setDataSource(..)` method from the
|
||||
Regardless of which of the above template initialization styles you choose to use (or
|
||||
not), it is seldom necessary to create a new instance of a `JdbcTemplate` class each
|
||||
time you want to run SQL. Once configured, a `JdbcTemplate` instance is thread-safe.
|
||||
If your application accesses multiple
|
||||
databases, you may want multiple `JdbcTemplate` instances, which requires multiple `DataSources` and, subsequently, multiple differently
|
||||
If your application accesses multiple databases, you may want multiple `JdbcTemplate`
|
||||
instances, which requires multiple `DataSources` and, subsequently, multiple differently
|
||||
configured `JdbcTemplate` instances.
|
||||
|
||||
|
||||
@@ -531,11 +531,8 @@ Java::
|
||||
}
|
||||
|
||||
public int countOfActorsByFirstName(String firstName) {
|
||||
|
||||
String sql = "select count(*) from T_ACTOR where first_name = :first_name";
|
||||
|
||||
String sql = "select count(*) from t_actor where first_name = :first_name";
|
||||
SqlParameterSource namedParameters = new MapSqlParameterSource("first_name", firstName);
|
||||
|
||||
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
|
||||
}
|
||||
----
|
||||
@@ -547,7 +544,7 @@ Kotlin::
|
||||
private val namedParameterJdbcTemplate = NamedParameterJdbcTemplate(dataSource)
|
||||
|
||||
fun countOfActorsByFirstName(firstName: String): Int {
|
||||
val sql = "select count(*) from T_ACTOR where first_name = :first_name"
|
||||
val sql = "select count(*) from t_actor where first_name = :first_name"
|
||||
val namedParameters = MapSqlParameterSource("first_name", firstName)
|
||||
return namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Int::class.java)!!
|
||||
}
|
||||
@@ -579,12 +576,9 @@ Java::
|
||||
}
|
||||
|
||||
public int countOfActorsByFirstName(String firstName) {
|
||||
|
||||
String sql = "select count(*) from T_ACTOR where first_name = :first_name";
|
||||
|
||||
String sql = "select count(*) from t_actor where first_name = :first_name";
|
||||
Map<String, String> namedParameters = Collections.singletonMap("first_name", firstName);
|
||||
|
||||
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
|
||||
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
|
||||
}
|
||||
----
|
||||
|
||||
@@ -596,7 +590,7 @@ Kotlin::
|
||||
private val namedParameterJdbcTemplate = NamedParameterJdbcTemplate(dataSource)
|
||||
|
||||
fun countOfActorsByFirstName(firstName: String): Int {
|
||||
val sql = "select count(*) from T_ACTOR where first_name = :first_name"
|
||||
val sql = "select count(*) from t_actor where first_name = :first_name"
|
||||
val namedParameters = mapOf("first_name" to firstName)
|
||||
return namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Int::class.java)!!
|
||||
}
|
||||
@@ -644,7 +638,6 @@ Java::
|
||||
}
|
||||
|
||||
// setters omitted...
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
@@ -673,12 +666,9 @@ Java::
|
||||
}
|
||||
|
||||
public int countOfActors(Actor exampleActor) {
|
||||
|
||||
// notice how the named parameters match the properties of the above 'Actor' class
|
||||
String sql = "select count(*) from T_ACTOR where first_name = :firstName and last_name = :lastName";
|
||||
|
||||
String sql = "select count(*) from t_actor where first_name = :firstName and last_name = :lastName";
|
||||
SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(exampleActor);
|
||||
|
||||
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
|
||||
}
|
||||
----
|
||||
@@ -694,7 +684,7 @@ Kotlin::
|
||||
|
||||
fun countOfActors(exampleActor: Actor): Int {
|
||||
// notice how the named parameters match the properties of the above 'Actor' class
|
||||
val sql = "select count(*) from T_ACTOR where first_name = :firstName and last_name = :lastName"
|
||||
val sql = "select count(*) from t_actor where first_name = :firstName and last_name = :lastName"
|
||||
val namedParameters = BeanPropertySqlParameterSource(exampleActor)
|
||||
return namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Int::class.java)!!
|
||||
}
|
||||
@@ -707,8 +697,8 @@ functionality that is present only in the `JdbcTemplate` class, you can use the
|
||||
`getJdbcOperations()` method to access the wrapped `JdbcTemplate` through the
|
||||
`JdbcOperations` interface.
|
||||
|
||||
See also xref:data-access/jdbc/core.adoc#jdbc-JdbcTemplate-idioms[`JdbcTemplate` Best Practices] for guidelines on using the
|
||||
`NamedParameterJdbcTemplate` class in the context of an application.
|
||||
See also xref:data-access/jdbc/core.adoc#jdbc-JdbcTemplate-idioms[`JdbcTemplate` Best Practices]
|
||||
for guidelines on using the `NamedParameterJdbcTemplate` class in the context of an application.
|
||||
|
||||
|
||||
[[jdbc-SQLExceptionTranslator]]
|
||||
|
||||
@@ -121,18 +121,17 @@ Kotlin::
|
||||
<3> Using the method `setBlobAsBinaryStream` to pass in the contents of the BLOB.
|
||||
======
|
||||
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
If you invoke the `setBlobAsBinaryStream`, `setClobAsAsciiStream`, or
|
||||
`setClobAsCharacterStream` method on the `LobCreator` returned from
|
||||
`DefaultLobHandler.getLobCreator()`, you can optionally specify a negative value for the
|
||||
`contentLength` argument. If the specified content length is negative, the
|
||||
`DefaultLobHandler.getLobCreator()`, you can optionally specify a negative value
|
||||
for the `contentLength` argument. If the specified content length is negative, the
|
||||
`DefaultLobHandler` uses the JDBC 4.0 variants of the set-stream methods without a
|
||||
length parameter. Otherwise, it passes the specified length on to the driver.
|
||||
|
||||
See the documentation for the JDBC driver you use to verify that it supports streaming a
|
||||
LOB without providing the content length.
|
||||
See the documentation for the JDBC driver you use to verify that it supports streaming
|
||||
a LOB without providing the content length.
|
||||
====
|
||||
|
||||
Now it is time to read the LOB data from the database. Again, you use a `JdbcTemplate`
|
||||
@@ -184,15 +183,15 @@ variable list of values. A typical example would be `select * from T_ACTOR where
|
||||
JDBC standard. You cannot declare a variable number of placeholders. You need a number
|
||||
of variations with the desired number of placeholders prepared, or you need to generate
|
||||
the SQL string dynamically once you know how many placeholders are required. The named
|
||||
parameter support provided in the `NamedParameterJdbcTemplate` and `JdbcTemplate` takes
|
||||
the latter approach. You can pass in the values as a `java.util.List` of primitive objects. This
|
||||
list is used to insert the required placeholders and pass in the values during
|
||||
statement execution.
|
||||
parameter support provided in the `NamedParameterJdbcTemplate` takes the latter approach.
|
||||
You can pass in the values as a `java.util.List` (or any `Iterable`) of simple values.
|
||||
This list is used to insert the required placeholders into the actual SQL statement
|
||||
and pass in the values during statement execution.
|
||||
|
||||
NOTE: Be careful when passing in many values. The JDBC standard does not guarantee that you
|
||||
can use more than 100 values for an `in` expression list. Various databases exceed this
|
||||
number, but they usually have a hard limit for how many values are allowed. For example, Oracle's
|
||||
limit is 1000.
|
||||
NOTE: Be careful when passing in many values. The JDBC standard does not guarantee that
|
||||
you can use more than 100 values for an `IN` expression list. Various databases exceed
|
||||
this number, but they usually have a hard limit for how many values are allowed.
|
||||
For example, Oracle's limit is 1000.
|
||||
|
||||
In addition to the primitive values in the value list, you can create a `java.util.List`
|
||||
of object arrays. This list can support multiple expressions being defined for the `in`
|
||||
|
||||
@@ -88,12 +88,6 @@ You can use this option for full JPA capabilities in a Spring-based application
|
||||
This includes web containers such as Tomcat, stand-alone applications, and
|
||||
integration tests with sophisticated persistence requirements.
|
||||
|
||||
NOTE: If you want to specifically configure a Hibernate setup, an immediate alternative
|
||||
is to set up a native Hibernate `LocalSessionFactoryBean` instead of a plain JPA
|
||||
`LocalContainerEntityManagerFactoryBean`, letting it interact with JPA access code
|
||||
as well as native Hibernate access code.
|
||||
See xref:data-access/orm/jpa.adoc#orm-jpa-hibernate[Native Hibernate setup for JPA interaction] for details.
|
||||
|
||||
The `LocalContainerEntityManagerFactoryBean` gives full control over
|
||||
`EntityManagerFactory` configuration and is appropriate for environments where
|
||||
fine-grained customization is required. The `LocalContainerEntityManagerFactoryBean`
|
||||
@@ -187,6 +181,7 @@ and automatic propagation of the weaver to all weaver-aware beans:
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
<context:load-time-weaver/>
|
||||
|
||||
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
|
||||
...
|
||||
</bean>
|
||||
@@ -273,25 +268,25 @@ The actual JPA provider bootstrapping is handed off to the specified executor an
|
||||
running in parallel, to the application bootstrap thread. The exposed `EntityManagerFactory`
|
||||
proxy can be injected into other application components and is even able to respond to
|
||||
`EntityManagerFactoryInfo` configuration inspection. However, once the actual JPA provider
|
||||
is being accessed by other components (for example, calling `createEntityManager`), those calls
|
||||
block until the background bootstrapping has completed. In particular, when you use
|
||||
is being accessed by other components (for example, calling `createEntityManager`), those
|
||||
calls block until the background bootstrapping has completed. In particular, when you use
|
||||
Spring Data JPA, make sure to set up deferred bootstrapping for its repositories as well.
|
||||
|
||||
|
||||
[[orm-jpa-dao]]
|
||||
== Implementing DAOs Based on JPA: `EntityManagerFactory` and `EntityManager`
|
||||
|
||||
NOTE: Although `EntityManagerFactory` instances are thread-safe, `EntityManager` instances are
|
||||
not. The injected JPA `EntityManager` behaves like an `EntityManager` fetched from an
|
||||
NOTE: Although `EntityManagerFactory` instances are thread-safe, `EntityManager` instances
|
||||
are not. The injected JPA `EntityManager` behaves like an `EntityManager` fetched from an
|
||||
application server's JNDI environment, as defined by the JPA specification. It delegates
|
||||
all calls to the current transactional `EntityManager`, if any. Otherwise, it falls back
|
||||
to a newly created `EntityManager` per operation, in effect making its usage thread-safe.
|
||||
|
||||
It is possible to write code against the plain JPA without any Spring dependencies, by
|
||||
using an injected `EntityManagerFactory` or `EntityManager`. Spring can understand the
|
||||
`@PersistenceUnit` and `@PersistenceContext` annotations both at the field and the method level
|
||||
if a `PersistenceAnnotationBeanPostProcessor` is enabled. The following example shows a plain JPA DAO implementation
|
||||
that uses the `@PersistenceUnit` annotation:
|
||||
`@PersistenceUnit` and `@PersistenceContext` annotations both at the field and the method
|
||||
level if a `PersistenceAnnotationBeanPostProcessor` is enabled. The following example
|
||||
shows a plain JPA DAO implementation that uses the `@PersistenceUnit` annotation:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@@ -384,9 +379,9 @@ Consider the following example:
|
||||
----
|
||||
|
||||
The main problem with such a DAO is that it always creates a new `EntityManager` through
|
||||
the factory. You can avoid this by requesting a transactional `EntityManager` (also
|
||||
called a "`shared EntityManager`" because it is a shared, thread-safe proxy for the actual
|
||||
transactional EntityManager) to be injected instead of the factory. The following example shows how to do so:
|
||||
the factory. You can avoid this by requesting a transactional `EntityManager` (also called a
|
||||
"`shared EntityManager`" because it is a shared, thread-safe proxy for the actual transactional
|
||||
EntityManager) to be injected instead of the factory. The following example shows how to do so:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@@ -425,24 +420,24 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
The `@PersistenceContext` annotation has an optional attribute called `type`, which defaults to
|
||||
`PersistenceContextType.TRANSACTION`. You can use this default to receive a shared
|
||||
The `@PersistenceContext` annotation has an optional attribute called `type`, which defaults
|
||||
to `PersistenceContextType.TRANSACTION`. You can use this default to receive a shared
|
||||
`EntityManager` proxy. The alternative, `PersistenceContextType.EXTENDED`, is a completely
|
||||
different affair. This results in a so-called extended `EntityManager`, which is not
|
||||
thread-safe and, hence, must not be used in a concurrently accessed component, such as a
|
||||
Spring-managed singleton bean. Extended `EntityManager` instances are only supposed to be used in
|
||||
stateful components that, for example, reside in a session, with the lifecycle of the
|
||||
Spring-managed singleton bean. Extended `EntityManager` instances are only supposed to be used
|
||||
in stateful components that, for example, reside in a session, with the lifecycle of the
|
||||
`EntityManager` not tied to a current transaction but rather being completely up to the
|
||||
application.
|
||||
|
||||
.Method- and field-level Injection
|
||||
****
|
||||
You can apply annotations that indicate dependency injections (such as `@PersistenceUnit` and
|
||||
`@PersistenceContext`) on field or methods inside a class -- hence the
|
||||
expressions "`method-level injection`" and "`field-level injection`". Field-level
|
||||
annotations are concise and easier to use while method-level annotations allow for further
|
||||
processing of the injected dependency. In both cases, the member visibility (public,
|
||||
protected, or private) does not matter.
|
||||
You can apply annotations that indicate dependency injections (such as `@PersistenceUnit`
|
||||
and `@PersistenceContext`) on field or methods inside a class -- hence the expressions
|
||||
"`method-level injection`" and "`field-level injection`". Field-level annotations are
|
||||
concise and easier to use while method-level annotations allow for further processing of the
|
||||
injected dependency. In both cases, the member visibility (public, protected, or private)
|
||||
does not matter.
|
||||
|
||||
What about class-level annotations?
|
||||
|
||||
@@ -451,21 +446,62 @@ injection.
|
||||
****
|
||||
|
||||
The injected `EntityManager` is Spring-managed (aware of the ongoing transaction).
|
||||
Even though the new DAO implementation uses method-level
|
||||
injection of an `EntityManager` instead of an `EntityManagerFactory`, no change is
|
||||
required in the application context XML, due to annotation usage.
|
||||
Even though the new DAO implementation uses method-level injection of an `EntityManager`
|
||||
instead of an `EntityManagerFactory`, no change is required in the bean definition
|
||||
due to annotation usage.
|
||||
|
||||
The main advantage of this DAO style is that it depends only on the Java Persistence API.
|
||||
No import of any Spring class is required. Moreover, as the JPA annotations are understood,
|
||||
the injections are applied automatically by the Spring container. This is appealing from
|
||||
a non-invasiveness perspective and can feel more natural to JPA developers.
|
||||
|
||||
[[orm-jpa-dao-autowired]]
|
||||
=== Implementing DAOs Based on `@Autowired` (typically with constructor-based injection)
|
||||
|
||||
`@PersistenceUnit` and `@PersistenceContext` can only be declared on methods and fields.
|
||||
What about providing JPA resources via constructors and other `@Autowired` injection points?
|
||||
|
||||
`EntityManagerFactory` can easily be injected via constructors and `@Autowired` fields/methods
|
||||
as long as the target is defined as a bean, e.g. via `LocalContainerEntityManagerFactoryBean`.
|
||||
The injection point matches the original `EntityManagerFactory` definition by type as-is.
|
||||
|
||||
However, an `@PersistenceContext`-style shared `EntityManager` reference is not available for
|
||||
regular dependency injection out of the box. In order to make it available for type-based
|
||||
matching as required by `@Autowired`, consider defining a `SharedEntityManagerBean` as a
|
||||
companion for your `EntityManagerFactory` definition:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
|
||||
...
|
||||
</bean>
|
||||
|
||||
<bean id="em" class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
|
||||
<property name="entityManagerFactory" ref="emf"/>
|
||||
</bean>
|
||||
----
|
||||
|
||||
Alternatively, you may define an `@Bean` method based on `SharedEntityManagerCreator`:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Bean("em")
|
||||
public static EntityManager sharedEntityManager(EntityManagerFactory emf) {
|
||||
return SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
}
|
||||
----
|
||||
|
||||
In case of multiple persistence units, each `EntityManagerFactory` definition needs to be
|
||||
accompanied by a corresponding `EntityManager` bean definition, ideally with qualifiers
|
||||
that match with the distinct `EntityManagerFactory` definition in order to distinguish
|
||||
the persistence units via `@Autowired @Qualifier("...")`.
|
||||
|
||||
|
||||
[[orm-jpa-tx]]
|
||||
== Spring-driven JPA transactions
|
||||
== Spring-driven JPA Transactions
|
||||
|
||||
NOTE: We strongly encourage you to read xref:data-access/transaction/declarative.adoc[Declarative Transaction Management], if you have not
|
||||
already done so, to get more detailed coverage of Spring's declarative transaction support.
|
||||
NOTE: We strongly encourage you to read xref:data-access/transaction/declarative.adoc[Declarative Transaction Management],
|
||||
if you have not already done so, to get more detailed coverage of Spring's declarative transaction support.
|
||||
|
||||
The recommended strategy for JPA is local transactions through JPA's native transaction
|
||||
support. Spring's `JpaTransactionManager` provides many capabilities known from local
|
||||
@@ -478,11 +514,6 @@ to JDBC access code that accesses the same `DataSource`, provided that the regis
|
||||
Spring provides dialects for the EclipseLink and Hibernate JPA implementations.
|
||||
See the xref:data-access/orm/jpa.adoc#orm-jpa-dialect[next section] for details on the `JpaDialect` mechanism.
|
||||
|
||||
NOTE: As an immediate alternative, Spring's native `HibernateTransactionManager` is capable
|
||||
of interacting with JPA access code, adapting to several Hibernate specifics and providing
|
||||
JDBC interaction. This makes particular sense in combination with `LocalSessionFactoryBean`
|
||||
setup. See xref:data-access/orm/jpa.adoc#orm-jpa-hibernate[Native Hibernate Setup for JPA Interaction] for details.
|
||||
|
||||
|
||||
[[orm-jpa-dialect]]
|
||||
== Understanding `JpaDialect` and `JpaVendorAdapter`
|
||||
|
||||
@@ -11,11 +11,13 @@ specification effort to standardize access to SQL databases using reactive patte
|
||||
The Spring Framework's R2DBC abstraction framework consists of two different packages:
|
||||
|
||||
* `core`: The `org.springframework.r2dbc.core` package contains the `DatabaseClient`
|
||||
class plus a variety of related classes. See xref:data-access/r2dbc.adoc#r2dbc-core[Using the R2DBC Core Classes to Control Basic R2DBC Processing and Error Handling].
|
||||
class plus a variety of related classes. See
|
||||
xref:data-access/r2dbc.adoc#r2dbc-core[Using the R2DBC Core Classes to Control Basic R2DBC Processing and Error Handling].
|
||||
|
||||
* `connection`: The `org.springframework.r2dbc.connection` package contains a utility class
|
||||
for easy `ConnectionFactory` access and various simple `ConnectionFactory` implementations
|
||||
that you can use for testing and running unmodified R2DBC. See xref:data-access/r2dbc.adoc#r2dbc-connections[Controlling Database Connections].
|
||||
that you can use for testing and running unmodified R2DBC. See
|
||||
xref:data-access/r2dbc.adoc#r2dbc-connections[Controlling Database Connections].
|
||||
|
||||
|
||||
[[r2dbc-core]]
|
||||
@@ -31,6 +33,7 @@ including error handling. It includes the following topics:
|
||||
* xref:data-access/r2dbc.adoc#r2dbc-DatabaseClient-filter[Statement Filters]
|
||||
* xref:data-access/r2dbc.adoc#r2dbc-auto-generated-keys[Retrieving Auto-generated Keys]
|
||||
|
||||
|
||||
[[r2dbc-DatabaseClient]]
|
||||
=== Using `DatabaseClient`
|
||||
|
||||
@@ -43,8 +46,9 @@ SQL and extract results. The `DatabaseClient` class:
|
||||
* Runs SQL queries
|
||||
* Update statements and stored procedure calls
|
||||
* Performs iteration over `Result` instances
|
||||
* Catches R2DBC exceptions and translates them to the generic, more informative, exception
|
||||
hierarchy defined in the `org.springframework.dao` package. (See xref:data-access/dao.adoc#dao-exceptions[Consistent Exception Hierarchy].)
|
||||
* Catches R2DBC exceptions and translates them to the generic, more informative,
|
||||
exception hierarchy defined in the `org.springframework.dao` package.
|
||||
(See xref:data-access/dao.adoc#dao-exceptions[Consistent Exception Hierarchy].)
|
||||
|
||||
The client has a functional, fluent API using reactive types for declarative composition.
|
||||
|
||||
@@ -250,7 +254,6 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
[[r2dbc-DatabaseClient-mapping-null]]
|
||||
.What about `null`?
|
||||
****
|
||||
@@ -315,10 +318,10 @@ The following example shows parameter binding for a query:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
db.sql("INSERT INTO person (id, name, age) VALUES(:id, :name, :age)")
|
||||
.bind("id", "joe")
|
||||
.bind("name", "Joe")
|
||||
.bind("age", 34);
|
||||
db.sql("INSERT INTO person (id, name, age) VALUES(:id, :name, :age)")
|
||||
.bind("id", "joe")
|
||||
.bind("name", "Joe")
|
||||
.bind("age", 34);
|
||||
----
|
||||
|
||||
.R2DBC Native Bind Markers
|
||||
@@ -327,7 +330,7 @@ R2DBC uses database-native bind markers that depend on the actual database vendo
|
||||
As an example, Postgres uses indexed markers, such as `$1`, `$2`, `$n`.
|
||||
Another example is SQL Server, which uses named bind markers prefixed with `@`.
|
||||
|
||||
This is different from JDBC, which requires `?` as bind markers.
|
||||
This is different from JDBC which requires `?` as bind markers.
|
||||
In JDBC, the actual drivers translate `?` bind markers to database-native
|
||||
markers as part of their statement execution.
|
||||
|
||||
@@ -363,7 +366,7 @@ Java::
|
||||
tuples.add(new Object[] {"Ann", 50});
|
||||
|
||||
client.sql("SELECT id, name, state FROM table WHERE (name, age) IN (:tuples)")
|
||||
.bind("tuples", tuples);
|
||||
.bind("tuples", tuples);
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
@@ -375,7 +378,7 @@ Kotlin::
|
||||
tuples.add(arrayOf("Ann", 50))
|
||||
|
||||
client.sql("SELECT id, name, state FROM table WHERE (name, age) IN (:tuples)")
|
||||
.bind("tuples", tuples)
|
||||
.bind("tuples", tuples)
|
||||
----
|
||||
======
|
||||
|
||||
@@ -390,7 +393,7 @@ Java::
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
client.sql("SELECT id, name, state FROM table WHERE age IN (:ages)")
|
||||
.bind("ages", Arrays.asList(35, 50));
|
||||
.bind("ages", Arrays.asList(35, 50));
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
@@ -402,7 +405,7 @@ Kotlin::
|
||||
tuples.add(arrayOf("Ann", 50))
|
||||
|
||||
client.sql("SELECT id, name, state FROM table WHERE age IN (:ages)")
|
||||
.bind("tuples", arrayOf(35, 50))
|
||||
.bind("tuples", arrayOf(35, 50))
|
||||
----
|
||||
======
|
||||
|
||||
@@ -429,9 +432,9 @@ Java::
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
|
||||
.filter((s, next) -> next.execute(s.returnGeneratedValues("id")))
|
||||
.bind("name", …)
|
||||
.bind("state", …);
|
||||
.filter((s, next) -> next.execute(s.returnGeneratedValues("id")))
|
||||
.bind("name", …)
|
||||
.bind("state", …);
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
@@ -439,9 +442,9 @@ Kotlin::
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
|
||||
.filter { s: Statement, next: ExecuteFunction -> next.execute(s.returnGeneratedValues("id")) }
|
||||
.bind("name", …)
|
||||
.bind("state", …)
|
||||
.filter { s: Statement, next: ExecuteFunction -> next.execute(s.returnGeneratedValues("id")) }
|
||||
.bind("name", …)
|
||||
.bind("state", …)
|
||||
----
|
||||
======
|
||||
|
||||
@@ -455,10 +458,10 @@ Java::
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
|
||||
.filter(statement -> s.returnGeneratedValues("id"));
|
||||
.filter(statement -> s.returnGeneratedValues("id"));
|
||||
|
||||
client.sql("SELECT id, name, state FROM table")
|
||||
.filter(statement -> s.fetchSize(25));
|
||||
.filter(statement -> s.fetchSize(25));
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
@@ -466,10 +469,10 @@ Kotlin::
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
|
||||
.filter { statement -> s.returnGeneratedValues("id") }
|
||||
.filter { statement -> s.returnGeneratedValues("id") }
|
||||
|
||||
client.sql("SELECT id, name, state FROM table")
|
||||
.filter { statement -> s.fetchSize(25) }
|
||||
.filter { statement -> s.fetchSize(25) }
|
||||
----
|
||||
======
|
||||
|
||||
@@ -593,7 +596,7 @@ Java::
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
Mono<Integer> generatedId = client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
|
||||
.filter(statement -> s.returnGeneratedValues("id"))
|
||||
.filter(statement -> s.returnGeneratedValues("id"))
|
||||
.map(row -> row.get("id", Integer.class))
|
||||
.first();
|
||||
|
||||
@@ -605,7 +608,7 @@ Kotlin::
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
val generatedId = client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
|
||||
.filter { statement -> s.returnGeneratedValues("id") }
|
||||
.filter { statement -> s.returnGeneratedValues("id") }
|
||||
.map { row -> row.get("id", Integer.class) }
|
||||
.awaitOne()
|
||||
|
||||
@@ -672,7 +675,6 @@ Kotlin::
|
||||
[[r2dbc-ConnectionFactoryUtils]]
|
||||
=== Using `ConnectionFactoryUtils`
|
||||
|
||||
|
||||
The `ConnectionFactoryUtils` class is a convenient and powerful helper class
|
||||
that provides `static` methods to obtain connections from `ConnectionFactory`
|
||||
and close connections (if necessary).
|
||||
|
||||
@@ -12,19 +12,16 @@ management that delivers the following benefits:
|
||||
than complex transaction APIs, such as JTA.
|
||||
* Excellent integration with Spring's data access abstractions.
|
||||
|
||||
The following sections describe the Spring Framework's transaction features and
|
||||
technologies:
|
||||
The following sections describe the Spring Framework's transaction features and technologies:
|
||||
|
||||
* xref:data-access/transaction/motivation.adoc[Advantages of the Spring Framework's transaction support model]
|
||||
describes why you would use the Spring Framework's transaction abstraction
|
||||
instead of EJB Container-Managed Transactions (CMT) or choosing to drive local
|
||||
transactions through a proprietary API, such as Hibernate.
|
||||
* xref:data-access/transaction/motivation.adoc[Advantages of the Spring Framework's transaction support model]
|
||||
describes why you would use the Spring Framework's transaction abstraction instead of EJB
|
||||
Container-Managed Transactions (CMT) or choosing to drive transactions through a proprietary API.
|
||||
* xref:data-access/transaction/strategies.adoc[Understanding the Spring Framework transaction abstraction]
|
||||
outlines the core classes and describes how to configure and obtain `DataSource`
|
||||
instances from a variety of sources.
|
||||
* xref:data-access/transaction/tx-resource-synchronization.adoc[Synchronizing resources with transactions] describes
|
||||
how the application code ensures that resources are created, reused, and cleaned up
|
||||
properly.
|
||||
outlines the core classes and describes how to configure and obtain `DataSource` instances
|
||||
from a variety of sources.
|
||||
* xref:data-access/transaction/tx-resource-synchronization.adoc[Synchronizing resources with transactions]
|
||||
describes how the application code ensures that resources are created, reused, and cleaned up properly.
|
||||
* xref:data-access/transaction/declarative.adoc[Declarative transaction management] describes support for
|
||||
declarative transaction management.
|
||||
* xref:data-access/transaction/programmatic.adoc[Programmatic transaction management] covers support for
|
||||
|
||||
+1
-33
@@ -13,39 +13,7 @@ javadoc for details.
|
||||
Spring's `JtaTransactionManager` is the standard choice to run on Jakarta EE application
|
||||
servers and is known to work on all common servers. Advanced functionality, such as
|
||||
transaction suspension, works on many servers as well (including GlassFish, JBoss and
|
||||
Geronimo) without any special configuration required. However, for fully supported
|
||||
transaction suspension and further advanced integration, Spring includes special adapters
|
||||
for WebLogic Server and WebSphere. These adapters are discussed in the following
|
||||
sections.
|
||||
|
||||
For standard scenarios, including WebLogic Server and WebSphere, consider using the
|
||||
convenient `<tx:jta-transaction-manager/>` configuration element. When configured,
|
||||
this element automatically detects the underlying server and chooses the best
|
||||
transaction manager available for the platform. This means that you need not explicitly
|
||||
configure server-specific adapter classes (as discussed in the following sections).
|
||||
Rather, they are chosen automatically, with the standard
|
||||
`JtaTransactionManager` as the default fallback.
|
||||
|
||||
|
||||
[[transaction-application-server-integration-websphere]]
|
||||
== IBM WebSphere
|
||||
|
||||
On WebSphere 6.1.0.9 and above, the recommended Spring JTA transaction manager to use is
|
||||
`WebSphereUowTransactionManager`. This special adapter uses IBM's `UOWManager` API,
|
||||
which is available in WebSphere Application Server 6.1.0.9 and later. With this adapter,
|
||||
Spring-driven transaction suspension (suspend and resume as initiated by
|
||||
`PROPAGATION_REQUIRES_NEW`) is officially supported by IBM.
|
||||
|
||||
|
||||
[[transaction-application-server-integration-weblogic]]
|
||||
== Oracle WebLogic Server
|
||||
|
||||
On WebLogic Server 9.0 or above, you would typically use the
|
||||
`WebLogicJtaTransactionManager` instead of the stock `JtaTransactionManager` class. This
|
||||
special WebLogic-specific subclass of the normal `JtaTransactionManager` supports the
|
||||
full power of Spring's transaction definitions in a WebLogic-managed transaction
|
||||
environment, beyond standard JTA semantics. Features include transaction names,
|
||||
per-transaction isolation levels, and proper resuming of transactions in all cases.
|
||||
Geronimo) without any special configuration required.
|
||||
|
||||
|
||||
|
||||
|
||||
+43
-56
@@ -76,7 +76,7 @@ Kotlin::
|
||||
|
||||
Used at the class level as above, the annotation indicates a default for all methods of
|
||||
the declaring class (as well as its subclasses). Alternatively, each method can be
|
||||
annotated individually. See xref:data-access/transaction/declarative/annotations.adoc#transaction-declarative-annotations-method-visibility[null] for
|
||||
annotated individually. See xref:data-access/transaction/declarative/annotations.adoc#transaction-declarative-annotations-method-visibility[method visibility] for
|
||||
further details on which methods Spring considers transactional. Note that a class-level
|
||||
annotation does not apply to ancestor classes up the class hierarchy; in such a scenario,
|
||||
inherited methods need to be locally redeclared in order to participate in a
|
||||
@@ -193,74 +193,63 @@ Kotlin::
|
||||
======
|
||||
|
||||
Note that there are special considerations for the returned `Publisher` with regards to
|
||||
Reactive Streams cancellation signals. See the xref:data-access/transaction/programmatic.adoc#tx-prog-operator-cancel[Cancel Signals] section under
|
||||
"Using the TransactionalOperator" for more details.
|
||||
|
||||
Reactive Streams cancellation signals. See the
|
||||
xref:data-access/transaction/programmatic.adoc#tx-prog-operator-cancel[Cancel Signals]
|
||||
section under "Using the TransactionalOperator" for more details.
|
||||
|
||||
[[transaction-declarative-annotations-method-visibility]]
|
||||
.Method visibility and `@Transactional`
|
||||
.Method visibility and `@Transactional` in proxy mode
|
||||
[NOTE]
|
||||
====
|
||||
When you use transactional proxies with Spring's standard configuration, you should apply
|
||||
the `@Transactional` annotation only to methods with `public` visibility. If you do
|
||||
annotate `protected`, `private`, or package-visible methods with the `@Transactional`
|
||||
annotation, no error is raised, but the annotated method does not exhibit the configured
|
||||
transactional settings. If you need to annotate non-public methods, consider the tip in
|
||||
the following paragraph for class-based proxies or consider using AspectJ compile-time or
|
||||
load-time weaving (described later).
|
||||
The `@Transactional` annotation is typically used on methods with `public` visibility.
|
||||
As of 6.0, `protected` or package-visible methods can also be made transactional for
|
||||
class-based proxies by default. Note that transactional methods in interface-based
|
||||
proxies must always be `public` and defined in the proxied interface. For both kinds
|
||||
of proxies, only external method calls coming in through the proxy are intercepted.
|
||||
|
||||
When using `@EnableTransactionManagement` in a `@Configuration` class, `protected` or
|
||||
package-visible methods can also be made transactional for class-based proxies by
|
||||
registering a custom `transactionAttributeSource` bean like in the following example.
|
||||
Note, however, that transactional methods in interface-based proxies must always be
|
||||
`public` and defined in the proxied interface.
|
||||
If you prefer consistent treatment of method visibility across the different kinds of
|
||||
proxies (which was the default up until 5.3), consider specifying `publicMethodsOnly`:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
/**
|
||||
* Register a custom AnnotationTransactionAttributeSource with the
|
||||
* publicMethodsOnly flag set to false to enable support for
|
||||
* protected and package-private @Transactional methods in
|
||||
* class-based proxies.
|
||||
*
|
||||
* publicMethodsOnly flag set to true to consistently ignore non-public methods.
|
||||
* @see ProxyTransactionManagementConfiguration#transactionAttributeSource()
|
||||
*/
|
||||
@Bean
|
||||
TransactionAttributeSource transactionAttributeSource() {
|
||||
return new AnnotationTransactionAttributeSource(false);
|
||||
return new AnnotationTransactionAttributeSource(true);
|
||||
}
|
||||
----
|
||||
|
||||
The _Spring TestContext Framework_ supports non-private `@Transactional` test methods by
|
||||
default. See xref:testing/testcontext-framework/tx.adoc[Transaction Management] in the testing
|
||||
chapter for examples.
|
||||
The _Spring TestContext Framework_ supports non-private `@Transactional` test methods
|
||||
by default as well. See xref:testing/testcontext-framework/tx.adoc[Transaction Management]
|
||||
in the testing chapter for examples.
|
||||
====
|
||||
|
||||
You can apply the `@Transactional` annotation to an interface definition, a method
|
||||
on an interface, a class definition, or a method on a class. However, the
|
||||
mere presence of the `@Transactional` annotation is not enough to activate the
|
||||
transactional behavior. The `@Transactional` annotation is merely metadata that can
|
||||
be consumed by some runtime infrastructure that is `@Transactional`-aware and that
|
||||
can use the metadata to configure the appropriate beans with transactional behavior.
|
||||
In the preceding example, the `<tx:annotation-driven/>` element switches on the
|
||||
transactional behavior.
|
||||
on an interface, a class definition, or a method on a class. However, the mere presence
|
||||
of the `@Transactional` annotation is not enough to activate the transactional behavior.
|
||||
The `@Transactional` annotation is merely metadata that can be consumed by corresponding
|
||||
runtime infrastructure which uses that metadata to configure the appropriate beans with
|
||||
transactional behavior. In the preceding example, the `<tx:annotation-driven/>` element
|
||||
switches on actual transaction management at runtime.
|
||||
|
||||
TIP: The Spring team recommends that you annotate only concrete classes (and methods of
|
||||
concrete classes) with the `@Transactional` annotation, as opposed to annotating interfaces.
|
||||
You certainly can place the `@Transactional` annotation on an interface (or an interface
|
||||
method), but this works only as you would expect it to if you use interface-based
|
||||
proxies. The fact that Java annotations are not inherited from interfaces means that,
|
||||
if you use class-based proxies (`proxy-target-class="true"`) or the weaving-based
|
||||
aspect (`mode="aspectj"`), the transaction settings are not recognized by the proxying
|
||||
and weaving infrastructure, and the object is not wrapped in a transactional proxy.
|
||||
TIP: The Spring team recommends that you annotate methods of concrete classes with the
|
||||
`@Transactional` annotation, rather than relying on annotated methods in interfaces,
|
||||
even if the latter does work for interface-based and target-class proxies as of 5.0.
|
||||
Since Java annotations are not inherited from interfaces, interface-declared annotations
|
||||
are still not recognized by the weaving infrastructure when using AspectJ mode, so the
|
||||
aspect does not get applied. As a consequence, your transaction annotations may be
|
||||
silently ignored: Your code might appear to "work" until you test a rollback scenario.
|
||||
|
||||
NOTE: In proxy mode (which is the default), only external method calls coming in through
|
||||
the proxy are intercepted. This means that self-invocation (in effect, a method within
|
||||
the target object calling another method of the target object) does not lead to an actual
|
||||
transaction at runtime even if the invoked method is marked with `@Transactional`. Also,
|
||||
the proxy must be fully initialized to provide the expected behavior, so you should not
|
||||
rely on this feature in your initialization code -- for example, in a `@PostConstruct`
|
||||
method.
|
||||
rely on this feature in your initialization code -- e.g. in a `@PostConstruct` method.
|
||||
|
||||
Consider using AspectJ mode (see the `mode` attribute in the following table) if you
|
||||
expect self-invocations to be wrapped with transactions as well. In this case, there is
|
||||
@@ -285,20 +274,20 @@ is modified) to support `@Transactional` runtime behavior on any kind of method.
|
||||
framework (following proxy semantics, as discussed earlier, applying to method calls
|
||||
coming in through the proxy only). The alternative mode (`aspectj`) instead weaves the
|
||||
affected classes with Spring's AspectJ transaction aspect, modifying the target class
|
||||
byte code to apply to any kind of method call. AspectJ weaving requires
|
||||
`spring-aspects.jar` in the classpath as well as having load-time weaving (or compile-time
|
||||
weaving) enabled. (See xref:core/aop/using-aspectj.adoc#aop-aj-ltw-spring[Spring configuration]
|
||||
for details on how to set up load-time weaving.)
|
||||
byte code to apply to any kind of method call. AspectJ weaving requires `spring-aspects.jar`
|
||||
in the classpath as well as having load-time weaving (or compile-time weaving) enabled.
|
||||
(See xref:core/aop/using-aspectj.adoc#aop-aj-ltw-spring[Spring configuration] for details
|
||||
on how to set up load-time weaving.)
|
||||
|
||||
| `proxy-target-class`
|
||||
| `proxyTargetClass`
|
||||
| `false`
|
||||
| Applies to `proxy` mode only. Controls what type of transactional proxies are created
|
||||
for classes annotated with the `@Transactional` annotation. If the
|
||||
`proxy-target-class` attribute is set to `true`, class-based proxies are created.
|
||||
If `proxy-target-class` is `false` or if the attribute is omitted, then standard JDK
|
||||
interface-based proxies are created. (See xref:core/aop/proxying.adoc[Proxying Mechanisms]
|
||||
for a detailed examination of the different proxy types.)
|
||||
for classes annotated with the `@Transactional` annotation. If the `proxy-target-class`
|
||||
attribute is set to `true`, class-based proxies are created. If `proxy-target-class` is
|
||||
`false` or if the attribute is omitted, then standard JDK interface-based proxies are
|
||||
created. (See xref:core/aop/proxying.adoc[Proxying Mechanisms] for a detailed examination
|
||||
of the different proxy types.)
|
||||
|
||||
| `order`
|
||||
| `order`
|
||||
@@ -374,7 +363,6 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
[[transaction-declarative-attransactional-settings]]
|
||||
== `@Transactional` Settings
|
||||
|
||||
@@ -453,10 +441,9 @@ on rollback rule semantics, patterns, and warnings regarding possible unintentio
|
||||
matches for pattern-based rollback rules.
|
||||
|
||||
Currently, you cannot have explicit control over the name of a transaction, where 'name'
|
||||
means the transaction name that appears in a transaction monitor, if applicable
|
||||
(for example, WebLogic's transaction monitor), and in logging output. For declarative
|
||||
transactions, the transaction name is always the fully-qualified class name + `.`
|
||||
+ the method name of the transactionally advised class. For example, if the
|
||||
means the transaction name that appears in a transaction monitor and in logging output.
|
||||
For declarative transactions, the transaction name is always the fully-qualified class
|
||||
name + `.` + the method name of the transactionally advised class. For example, if the
|
||||
`handlePayment(..)` method of the `BusinessService` class started a transaction, the
|
||||
name of the transaction would be: `com.example.BusinessService.handlePayment`.
|
||||
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ As of Spring Framework 5.2, the default configuration also provides support for
|
||||
Vavr's `Try` method to trigger transaction rollbacks when it returns a 'Failure'.
|
||||
This allows you to handle functional-style errors using Try and have the transaction
|
||||
automatically rolled back in case of a failure. For more information on Vavr's Try,
|
||||
refer to the [official Vavr documentation](https://www.vavr.io/vavr-docs/#_try).
|
||||
refer to the https://docs.vavr.io/#_try[official Vavr documentation].
|
||||
|
||||
Here's an example of how to use Vavr's Try with a transactional method:
|
||||
[tabs]
|
||||
|
||||
+8
@@ -59,6 +59,14 @@ status and with an inner transaction's locks released immediately after its comp
|
||||
Such an independent inner transaction can also declare its own isolation level, timeout,
|
||||
and read-only settings and not inherit an outer transaction's characteristics.
|
||||
|
||||
NOTE: The resources attached to the outer transaction will remain bound there while
|
||||
the inner transaction acquires its own resources such as a new database connection.
|
||||
This may lead to exhaustion of the connection pool and potentially to a deadlock if
|
||||
several threads have an active outer transaction and wait to acquire a new connection
|
||||
for their inner transaction, with the pool not being able to hand out any such inner
|
||||
connection anymore. Do not use `PROPAGATION_REQUIRES_NEW` unless your connection pool
|
||||
is appropriately sized, exceeding the number of concurrent threads by at least 1.
|
||||
|
||||
[[tx-propagation-nested]]
|
||||
== Understanding `PROPAGATION_NESTED`
|
||||
|
||||
|
||||
@@ -29,8 +29,6 @@ Brannen, Ramnivas Laddad, Arjen Poutsma, Chris Beams, Tareq Abedrabbo, Andy Clem
|
||||
Syer, Oliver Gierke, Rossen Stoyanchev, Phillip Webb, Rob Winch, Brian Clozel, Stephane
|
||||
Nicoll, Sebastien Deleuze, Jay Bryant, Mark Paluch
|
||||
|
||||
Copyright © 2002 - 2023 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Copies of this document may be made for your own use and for distribution to others,
|
||||
provided that you do not charge any fee for such copies and further provided that each
|
||||
copy contains this Copyright Notice, whether distributed in print or electronically.
|
||||
copy contains the Copyright Notice, whether distributed in print or electronically.
|
||||
|
||||
@@ -98,9 +98,9 @@ through its `key` attribute. You can use xref:core/expressions.adoc[SpEL] to pic
|
||||
arguments of interest (or their nested properties), perform operations, or even
|
||||
invoke arbitrary methods without having to write any code or implement any interface.
|
||||
This is the recommended approach over the
|
||||
xref:integration/cache/annotations.adoc#cache-annotations-cacheable-default-key[default generator], since methods tend to be
|
||||
quite different in signatures as the code base grows. While the default strategy might
|
||||
work for some methods, it rarely works for all methods.
|
||||
xref:integration/cache/annotations.adoc#cache-annotations-cacheable-default-key[default generator],
|
||||
since methods tend to be quite different in signatures as the code base grows. While the
|
||||
default strategy might work for some methods, it rarely works for all methods.
|
||||
|
||||
The following examples use various SpEL declarations (if you are not familiar with SpEL,
|
||||
do yourself a favor and read xref:core/expressions.adoc[Spring Expression Language]):
|
||||
@@ -137,9 +137,8 @@ that specifies both results in an exception.
|
||||
[[cache-annotations-cacheable-default-cache-resolver]]
|
||||
=== Default Cache Resolution
|
||||
|
||||
The caching abstraction uses a simple `CacheResolver` that
|
||||
retrieves the caches defined at the operation level by using the configured
|
||||
`CacheManager`.
|
||||
The caching abstraction uses a simple `CacheResolver` that retrieves the caches
|
||||
defined at the operation level by using the configured `CacheManager`.
|
||||
|
||||
To provide a different default cache resolver, you need to implement the
|
||||
`org.springframework.cache.interceptor.CacheResolver` interface.
|
||||
@@ -160,12 +159,11 @@ For applications that work with several cache managers, you can set the
|
||||
----
|
||||
<1> Specifying `anotherCacheManager`.
|
||||
|
||||
|
||||
You can also replace the `CacheResolver` entirely in a fashion similar to that of
|
||||
replacing xref:integration/cache/annotations.adoc#cache-annotations-cacheable-key[key generation]. The resolution is
|
||||
requested for every cache operation, letting the implementation actually resolve
|
||||
the caches to use based on runtime arguments. The following example shows how to
|
||||
specify a `CacheResolver`:
|
||||
replacing xref:integration/cache/annotations.adoc#cache-annotations-cacheable-key[key generation].
|
||||
The resolution is requested for every cache operation, letting the implementation
|
||||
actually resolve the caches to use based on runtime arguments. The following example
|
||||
shows how to specify a `CacheResolver`:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@@ -174,7 +172,6 @@ specify a `CacheResolver`:
|
||||
----
|
||||
<1> Specifying the `CacheResolver`.
|
||||
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
Since Spring 4.1, the `value` attribute of the cache annotations are no longer
|
||||
@@ -229,7 +226,6 @@ argument `name` has a length shorter than 32:
|
||||
----
|
||||
<1> Setting a condition on `@Cacheable`.
|
||||
|
||||
|
||||
In addition to the `condition` parameter, you can use the `unless` parameter to veto the
|
||||
adding of a value to the cache. Unlike `condition`, `unless` expressions are evaluated
|
||||
after the method has been invoked. To expand on the previous example, perhaps we only
|
||||
@@ -242,7 +238,6 @@ want to cache paperback books, as the following example does:
|
||||
----
|
||||
<1> Using the `unless` attribute to block hardbacks.
|
||||
|
||||
|
||||
The cache abstraction supports `java.util.Optional` return types. If an `Optional` value
|
||||
is _present_, it will be stored in the associated cache. If an `Optional` value is not
|
||||
present, `null` will be stored in the associated cache. `#result` always refers to the
|
||||
@@ -344,7 +339,7 @@ confirm the exclusion.
|
||||
|
||||
|
||||
[[cache-annotations-evict]]
|
||||
== The `@CacheEvict` annotation
|
||||
== The `@CacheEvict` Annotation
|
||||
|
||||
The cache abstraction allows not just population of a cache store but also eviction.
|
||||
This process is useful for removing stale or unused data from the cache. As opposed to
|
||||
@@ -402,7 +397,7 @@ The following example uses two `@CacheEvict` annotations:
|
||||
|
||||
|
||||
[[cache-annotations-config]]
|
||||
== The `@CacheConfig` annotation
|
||||
== The `@CacheConfig` Annotation
|
||||
|
||||
So far, we have seen that caching operations offer many customization options and that
|
||||
you can set these options for each operation. However, some of the customization options
|
||||
@@ -429,10 +424,13 @@ Placing this annotation on the class does not turn on any caching operation.
|
||||
An operation-level customization always overrides a customization set on `@CacheConfig`.
|
||||
Therefore, this gives three levels of customizations for each cache operation:
|
||||
|
||||
* Globally configured, available for `CacheManager`, `KeyGenerator`.
|
||||
* Globally configured, e.g. through `CachingConfigurer`: see next section.
|
||||
* At the class level, using `@CacheConfig`.
|
||||
* At the operation level.
|
||||
|
||||
NOTE: Provider-specific settings are typically available on the `CacheManager` bean,
|
||||
e.g. on `CaffeineCacheManager`. These are effectively also global.
|
||||
|
||||
|
||||
[[cache-annotation-enable]]
|
||||
== Enabling Caching Annotations
|
||||
@@ -451,6 +449,13 @@ To enable caching annotations add the annotation `@EnableCaching` to one of your
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
public class AppConfig {
|
||||
|
||||
@Bean
|
||||
CacheManager cacheManager() {
|
||||
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
|
||||
cacheManager.setCacheSpecification(...);
|
||||
return cacheManager;
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
@@ -465,7 +470,11 @@ Alternatively, for XML configuration you can use the `cache:annotation-driven` e
|
||||
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/cache https://www.springframework.org/schema/cache/spring-cache.xsd">
|
||||
|
||||
<cache:annotation-driven/>
|
||||
<cache:annotation-driven/>
|
||||
|
||||
<bean id="cacheManager" class="org.springframework.cache.caffeine.CaffeineCacheManager">
|
||||
<property name="cacheSpecification" value="..."/>
|
||||
</bean>
|
||||
</beans>
|
||||
----
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ email when someone places an order:
|
||||
|
||||
// Call the collaborators to persist the order...
|
||||
|
||||
// Create a thread safe "copy" of the template message and customize it
|
||||
// Create a thread-safe "copy" of the template message and customize it
|
||||
SimpleMailMessage msg = new SimpleMailMessage(this.templateMessage);
|
||||
msg.setTo(order.getCustomer().getEmailAddress());
|
||||
msg.setText(
|
||||
|
||||
@@ -58,7 +58,7 @@ your `@Configuration` classes, as the following example shows:
|
||||
By default, the infrastructure looks for a bean named `jmsListenerContainerFactory`
|
||||
as the source for the factory to use to create message listener containers. In this
|
||||
case (and ignoring the JMS infrastructure setup), you can invoke the `processOrder`
|
||||
method with a core poll size of three threads and a maximum pool size of ten threads.
|
||||
method with a core pool size of three threads and a maximum pool size of ten threads.
|
||||
|
||||
You can customize the listener container factory to use for each annotation or you can
|
||||
configure an explicit default by implementing the `JmsListenerConfigurer` interface.
|
||||
|
||||
@@ -56,14 +56,13 @@ locally, as the following example shows:
|
||||
----
|
||||
|
||||
The specified `WorkManager` can also point to an environment-specific thread pool --
|
||||
typically through a `SimpleTaskWorkManager` instance's `asyncTaskExecutor` property. Consider
|
||||
defining a shared thread pool for all your `ResourceAdapter` instances if you happen to
|
||||
use multiple adapters.
|
||||
typically through a `SimpleTaskWorkManager` instance's `asyncTaskExecutor` property.
|
||||
Consider defining a shared thread pool for all your `ResourceAdapter` instances
|
||||
if you happen to use multiple adapters.
|
||||
|
||||
In some environments (such as WebLogic 9 or above), you can instead obtain the entire `ResourceAdapter` object
|
||||
from JNDI (by using `<jee:jndi-lookup>`). The Spring-based message
|
||||
listeners can then interact with the server-hosted `ResourceAdapter`, which also use the
|
||||
server's built-in `WorkManager`.
|
||||
In some environments, you can instead obtain the entire `ResourceAdapter` object from JNDI
|
||||
(by using `<jee:jndi-lookup>`). The Spring-based message listeners can then interact with
|
||||
the server-hosted `ResourceAdapter`, which also use the server's built-in `WorkManager`.
|
||||
|
||||
See the javadoc for {api-spring-framework}/jms/listener/endpoint/JmsMessageEndpointManager.html[`JmsMessageEndpointManager`],
|
||||
{api-spring-framework}/jms/listener/endpoint/JmsActivationSpecConfig.html[`JmsActivationSpecConfig`],
|
||||
|
||||
@@ -183,18 +183,21 @@ as the following example shows:
|
||||
|
||||
If you configure a bean with an `MBeanExporter` that is also configured for lazy
|
||||
initialization, the `MBeanExporter` does not break this contract and avoids
|
||||
instantiating the bean. Instead, it registers a proxy with the `MBeanServer` and
|
||||
defers obtaining the bean from the container until the first invocation on the proxy
|
||||
occurs.
|
||||
instantiating the bean. Instead, it registers a proxy with the `MBeanServer` and defers
|
||||
obtaining the bean from the container until the first invocation on the proxy occurs.
|
||||
|
||||
This also affects `FactoryBean` resolution where `MBeanExporter` will regularly
|
||||
introspect the produced object, effectively triggering `FactoryBean.getObject()`.
|
||||
In order to avoid this, mark the corresponding bean definition as lazy-init.
|
||||
|
||||
|
||||
[[jmx-exporting-auto]]
|
||||
== Automatic Registration of MBeans
|
||||
|
||||
Any beans that are exported through the `MBeanExporter` and are already valid MBeans are
|
||||
registered as-is with the `MBeanServer` without further intervention from Spring. You can cause MBeans
|
||||
to be automatically detected by the `MBeanExporter` by setting the `autodetect`
|
||||
property to `true`, as the following example shows:
|
||||
Any beans that are exported through the `MBeanExporter` and are already valid MBeans
|
||||
are registered as-is with the `MBeanServer` without further intervention from Spring.
|
||||
You can cause MBeans to be automatically detected by the `MBeanExporter` by setting
|
||||
the `autodetect` property to `true`, as the following example shows:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
|
||||
@@ -108,7 +108,7 @@ By default, the following `KeyValues` are created:
|
||||
|===
|
||||
|Name | Description
|
||||
|`exception` _(required)_|Name of the exception thrown during the exchange, or `KeyValue#NONE_VALUE`} if no exception happened.
|
||||
|`method` _(required)_|Name of HTTP request method or `"none"` if the request was not received properly.
|
||||
|`method` _(required)_|Name of HTTP request method or `"none"` if not a well-known method.
|
||||
|`outcome` _(required)_|Outcome of the HTTP server exchange.
|
||||
|`status` _(required)_|HTTP response raw status code, or `"UNKNOWN"` if no response was created.
|
||||
|`uri` _(required)_|URI pattern for the matching handler if available, falling back to `REDIRECTION` for 3xx responses, `NOT_FOUND` for 404 responses, `root` for requests with no path info, and `UNKNOWN` for all other requests.
|
||||
@@ -141,7 +141,7 @@ By default, the following `KeyValues` are created:
|
||||
|===
|
||||
|Name | Description
|
||||
|`exception` _(required)_|Name of the exception thrown during the exchange, or `"none"` if no exception happened.
|
||||
|`method` _(required)_|Name of HTTP request method or `"none"` if the request was not received properly.
|
||||
|`method` _(required)_|Name of HTTP request method or `"none"` if not a well-known method.
|
||||
|`outcome` _(required)_|Outcome of the HTTP server exchange.
|
||||
|`status` _(required)_|HTTP response raw status code, or `"UNKNOWN"` if no response was created.
|
||||
|`uri` _(required)_|URI pattern for the matching handler if available, falling back to `REDIRECTION` for 3xx responses, `NOT_FOUND` for 404 responses, `root` for requests with no path info, and `UNKNOWN` for all other requests.
|
||||
@@ -174,7 +174,7 @@ Instrumentation uses the `org.springframework.http.client.observation.ClientRequ
|
||||
[cols="a,a"]
|
||||
|===
|
||||
|Name | Description
|
||||
|`method` _(required)_|Name of HTTP request method or `"none"` if the request could not be created.
|
||||
|`method` _(required)_|Name of HTTP request method or `"none"` if not a well-known method.
|
||||
|`uri` _(required)_|URI template used for HTTP request, or `"none"` if none was provided. Only the path part of the URI is considered.
|
||||
|`client.name` _(required)_|Client name derived from the request URI host.
|
||||
|`status` _(required)_|HTTP response raw status code, or `"IO_ERROR"` in case of `IOException`, or `"CLIENT_ERROR"` if no response was received.
|
||||
@@ -203,7 +203,7 @@ Instrumentation uses the `org.springframework.web.reactive.function.client.Clien
|
||||
[cols="a,a"]
|
||||
|===
|
||||
|Name | Description
|
||||
|`method` _(required)_|Name of HTTP request method or `"none"` if the request could not be created.
|
||||
|`method` _(required)_|Name of HTTP request method or `"none"` if not a well-known method.
|
||||
|`uri` _(required)_|URI template used for HTTP request, or `"none"` if none was provided. Only the path part of the URI is considered.
|
||||
|`client.name` _(required)_|Client name derived from the request URI host.
|
||||
|`status` _(required)_|HTTP response raw status code, or `"IO_ERROR"` in case of `IOException`, or `"CLIENT_ERROR"` if no response was received.
|
||||
|
||||
@@ -97,11 +97,7 @@ preparation and response extraction through callback interfaces.
|
||||
|
||||
The default constructor uses `java.net.HttpURLConnection` to perform requests. You can
|
||||
switch to a different HTTP library with an implementation of `ClientHttpRequestFactory`.
|
||||
There is built-in support for the following:
|
||||
|
||||
* Apache HttpComponents
|
||||
* Netty
|
||||
* OkHttp
|
||||
Currently, there is also built-in support for Apache HttpComponents and OkHttp.
|
||||
|
||||
For example, to switch to Apache HttpComponents, you can use the following:
|
||||
|
||||
|
||||
@@ -5,16 +5,11 @@ The Spring Framework provides abstractions for the asynchronous execution and sc
|
||||
tasks with the `TaskExecutor` and `TaskScheduler` interfaces, respectively. Spring also
|
||||
features implementations of those interfaces that support thread pools or delegation to
|
||||
CommonJ within an application server environment. Ultimately, the use of these
|
||||
implementations behind the common interfaces abstracts away the differences between Java
|
||||
SE 5, Java SE 6, and Jakarta EE environments.
|
||||
implementations behind the common interfaces abstracts away the differences between
|
||||
Java SE and Jakarta EE environments.
|
||||
|
||||
Spring also features integration classes to support scheduling with the `Timer`
|
||||
(part of the JDK since 1.3) and the https://www.quartz-scheduler.org/[Quartz Scheduler].
|
||||
You can set up both of those schedulers by using a `FactoryBean` with optional references to
|
||||
`Timer` or `Trigger` instances, respectively. Furthermore, a convenience class for both
|
||||
the Quartz Scheduler and the `Timer` is available that lets you invoke a method of
|
||||
an existing target object (analogous to the normal `MethodInvokingFactoryBean`
|
||||
operation).
|
||||
Spring also features integration classes to support scheduling with the
|
||||
https://www.quartz-scheduler.org/[Quartz Scheduler].
|
||||
|
||||
|
||||
|
||||
@@ -62,10 +57,10 @@ The variants that Spring provides are as follows:
|
||||
`ConcurrentTaskExecutor` directly. However, if the `ThreadPoolTaskExecutor` is not
|
||||
flexible enough for your needs, `ConcurrentTaskExecutor` is an alternative.
|
||||
* `ThreadPoolTaskExecutor`:
|
||||
This implementation is most commonly used. It exposes bean properties for
|
||||
configuring a `java.util.concurrent.ThreadPoolExecutor` and wraps it in a `TaskExecutor`.
|
||||
If you need to adapt to a different kind of `java.util.concurrent.Executor`, we
|
||||
recommend that you use a `ConcurrentTaskExecutor` instead.
|
||||
This implementation is most commonly used. It exposes bean properties for configuring
|
||||
a `java.util.concurrent.ThreadPoolExecutor` and wraps it in a `TaskExecutor`.
|
||||
If you need to adapt to a different kind of `java.util.concurrent.Executor`,
|
||||
we recommend that you use a `ConcurrentTaskExecutor` instead.
|
||||
* `DefaultManagedTaskExecutor`:
|
||||
This implementation uses a JNDI-obtained `ManagedExecutorService` in a JSR-236
|
||||
compatible runtime environment (such as a Jakarta EE application server),
|
||||
@@ -75,9 +70,9 @@ The variants that Spring provides are as follows:
|
||||
[[scheduling-task-executor-usage]]
|
||||
=== Using a `TaskExecutor`
|
||||
|
||||
Spring's `TaskExecutor` implementations are used as simple JavaBeans. In the following example,
|
||||
we define a bean that uses the `ThreadPoolTaskExecutor` to asynchronously print
|
||||
out a set of messages:
|
||||
Spring's `TaskExecutor` implementations are commonly used with dependency injection.
|
||||
In the following example, we define a bean that uses the `ThreadPoolTaskExecutor`
|
||||
to asynchronously print out a set of messages:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@@ -227,8 +222,8 @@ fixed delay, those methods should be used directly whenever possible. The value
|
||||
`PeriodicTrigger` implementation is that you can use it within components that rely on
|
||||
the `Trigger` abstraction. For example, it may be convenient to allow periodic triggers,
|
||||
cron-based triggers, and even custom trigger implementations to be used interchangeably.
|
||||
Such a component could take advantage of dependency injection so that you can configure such `Triggers`
|
||||
externally and, therefore, easily modify or extend them.
|
||||
Such a component could take advantage of dependency injection so that you can configure
|
||||
such `Triggers` externally and, therefore, easily modify or extend them.
|
||||
|
||||
|
||||
[[scheduling-task-scheduler-implementations]]
|
||||
@@ -238,10 +233,8 @@ As with Spring's `TaskExecutor` abstraction, the primary benefit of the `TaskSch
|
||||
arrangement is that an application's scheduling needs are decoupled from the deployment
|
||||
environment. This abstraction level is particularly relevant when deploying to an
|
||||
application server environment where threads should not be created directly by the
|
||||
application itself. For such scenarios, Spring provides a `TimerManagerTaskScheduler`
|
||||
that delegates to a CommonJ `TimerManager` on WebLogic or WebSphere as well as a more recent
|
||||
`DefaultManagedTaskScheduler` that delegates to a JSR-236 `ManagedScheduledExecutorService`
|
||||
in a Jakarta EE environment. Both are typically configured with a JNDI lookup.
|
||||
application itself. For such scenarios, Spring provides a `DefaultManagedTaskScheduler`
|
||||
that delegates to a JSR-236 `ManagedScheduledExecutorService` in a Jakarta EE environment.
|
||||
|
||||
Whenever external thread management is not a requirement, a simpler alternative is
|
||||
a local `ScheduledExecutorService` setup within the application, which can be adapted
|
||||
@@ -263,8 +256,8 @@ execution.
|
||||
[[scheduling-enable-annotation-support]]
|
||||
=== Enable Scheduling Annotations
|
||||
|
||||
To enable support for `@Scheduled` and `@Async` annotations, you can add `@EnableScheduling` and
|
||||
`@EnableAsync` to one of your `@Configuration` classes, as the following example shows:
|
||||
To enable support for `@Scheduled` and `@Async` annotations, you can add `@EnableScheduling`
|
||||
and `@EnableAsync` to one of your `@Configuration` classes, as the following example shows:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@@ -338,7 +331,7 @@ For example, the previous example can also be written as follows.
|
||||
|
||||
If you need a fixed-rate execution, you can use the `fixedRate` attribute within the
|
||||
annotation. The following method is invoked every five seconds (measured between the
|
||||
successive start times of each invocation).
|
||||
successive start times of each invocation):
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@@ -348,9 +341,9 @@ successive start times of each invocation).
|
||||
}
|
||||
----
|
||||
|
||||
For fixed-delay and fixed-rate tasks, you can specify an initial delay by indicating the
|
||||
amount of time to wait before the first execution of the method, as the following
|
||||
`fixedRate` example shows.
|
||||
For fixed-delay and fixed-rate tasks, you can specify an initial delay by indicating
|
||||
the amount of time to wait before the first execution of the method, as the following
|
||||
`fixedRate` example shows:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@@ -380,6 +373,12 @@ Notice that the methods to be scheduled must have void returns and must not acce
|
||||
arguments. If the method needs to interact with other objects from the application
|
||||
context, those would typically have been provided through dependency injection.
|
||||
|
||||
`@Scheduled` can be used as a repeatable annotation. If several scheduled declarations
|
||||
are found on the same method, each of them will be processed independently, with a
|
||||
separate trigger firing for each of them. As a consequence, such co-located schedules
|
||||
may overlap and execute multiple times in parallel or in immediate succession.
|
||||
Please make sure that your specified cron expressions etc do not accidentally overlap.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
As of Spring Framework 4.3, `@Scheduled` methods are supported on beans of any scope.
|
||||
@@ -413,8 +412,8 @@ to a method that returns `void`, as the following example shows:
|
||||
|
||||
Unlike the methods annotated with the `@Scheduled` annotation, these methods can expect
|
||||
arguments, because they are invoked in the "`normal`" way by callers at runtime rather
|
||||
than from a scheduled task being managed by the container. For example, the following code is
|
||||
a legitimate application of the `@Async` annotation:
|
||||
than from a scheduled task being managed by the container. For example, the following
|
||||
code is a legitimate application of the `@Async` annotation:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@@ -438,15 +437,15 @@ that returns a value:
|
||||
}
|
||||
----
|
||||
|
||||
TIP: `@Async` methods may not only declare a regular `java.util.concurrent.Future` return type
|
||||
but also Spring's `org.springframework.util.concurrent.ListenableFuture` or, as of Spring
|
||||
4.2, JDK 8's `java.util.concurrent.CompletableFuture`, for richer interaction with the
|
||||
asynchronous task and for immediate composition with further processing steps.
|
||||
TIP: `@Async` methods may not only declare a regular `java.util.concurrent.Future` return
|
||||
type but also Spring's `org.springframework.util.concurrent.ListenableFuture` or, as of
|
||||
Spring 4.2, JDK 8's `java.util.concurrent.CompletableFuture`, for richer interaction with
|
||||
the asynchronous task and for immediate composition with further processing steps.
|
||||
|
||||
You can not use `@Async` in conjunction with lifecycle callbacks such as
|
||||
`@PostConstruct`. To asynchronously initialize Spring beans, you currently have to use
|
||||
a separate initializing Spring bean that then invokes the `@Async` annotated method on the
|
||||
target, as the following example shows:
|
||||
You can not use `@Async` in conjunction with lifecycle callbacks such as `@PostConstruct`.
|
||||
To asynchronously initialize Spring beans, you currently have to use a separate
|
||||
initializing Spring bean that then invokes the `@Async` annotated method on the target,
|
||||
as the following example shows:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@@ -500,8 +499,8 @@ used when executing a given method. The following example shows how to do so:
|
||||
----
|
||||
|
||||
In this case, `"otherExecutor"` can be the name of any `Executor` bean in the Spring
|
||||
container, or it may be the name of a qualifier associated with any `Executor` (for example, as
|
||||
specified with the `<qualifier>` element or Spring's `@Qualifier` annotation).
|
||||
container, or it may be the name of a qualifier associated with any `Executor` (for example,
|
||||
as specified with the `<qualifier>` element or Spring's `@Qualifier` annotation).
|
||||
|
||||
|
||||
[[scheduling-annotation-support-exception]]
|
||||
@@ -669,14 +668,15 @@ invoked on that object. The following listing shows a simple example:
|
||||
----
|
||||
|
||||
The scheduler is referenced by the outer element, and each individual
|
||||
task includes the configuration of its trigger metadata. In the preceding example, that
|
||||
metadata defines a periodic trigger with a fixed delay indicating the number of
|
||||
task includes the configuration of its trigger metadata. In the preceding example,
|
||||
that metadata defines a periodic trigger with a fixed delay indicating the number of
|
||||
milliseconds to wait after each task execution has completed. Another option is
|
||||
`fixed-rate`, indicating how often the method should be run regardless of how long
|
||||
any previous execution takes. Additionally, for both `fixed-delay` and `fixed-rate` tasks, you can specify an
|
||||
'initial-delay' parameter, indicating the number of milliseconds to wait
|
||||
before the first execution of the method. For more control, you can instead provide a `cron` attribute
|
||||
to provide a xref:integration/scheduling.adoc#scheduling-cron-expression[cron expression].
|
||||
any previous execution takes. Additionally, for both `fixed-delay` and `fixed-rate`
|
||||
tasks, you can specify an 'initial-delay' parameter, indicating the number of
|
||||
milliseconds to wait before the first execution of the method. For more control,
|
||||
you can instead provide a `cron` attribute to provide a
|
||||
xref:integration/scheduling.adoc#scheduling-cron-expression[cron expression].
|
||||
The following example shows these other options:
|
||||
|
||||
[source,xml,indent=0]
|
||||
@@ -699,9 +699,8 @@ The following example shows these other options:
|
||||
All Spring cron expressions have to conform to the same format, whether you are using them in
|
||||
xref:integration/scheduling.adoc#scheduling-annotation-support-scheduled[`@Scheduled` annotations],
|
||||
xref:integration/scheduling.adoc#scheduling-task-namespace-scheduled-tasks[`task:scheduled-tasks` elements],
|
||||
or someplace else.
|
||||
A well-formed cron expression, such as `* * * * * *`, consists of six space-separated time and date
|
||||
fields, each with its own range of valid values:
|
||||
or someplace else. A well-formed cron expression, such as `* * * * * *`, consists of six
|
||||
space-separated time and date fields, each with its own range of valid values:
|
||||
|
||||
|
||||
....
|
||||
@@ -766,9 +765,10 @@ Here are some examples:
|
||||
[[macros]]
|
||||
=== Macros
|
||||
|
||||
Expressions such as `0 0 * * * *` are hard for humans to parse and are, therefore, hard to fix in case of bugs.
|
||||
To improve readability, Spring supports the following macros, which represent commonly used sequences.
|
||||
You can use these macros instead of the six-digit value, thus: `@Scheduled(cron = "@hourly")`.
|
||||
Expressions such as `0 0 * * * *` are hard for humans to parse and are, therefore,
|
||||
hard to fix in case of bugs. To improve readability, Spring supports the following
|
||||
macros, which represent commonly used sequences. You can use these macros instead
|
||||
of the six-digit value, thus: `@Scheduled(cron = "@hourly")`.
|
||||
|
||||
|===
|
||||
|Macro | Meaning
|
||||
@@ -785,8 +785,8 @@ You can use these macros instead of the six-digit value, thus: `@Scheduled(cron
|
||||
[[scheduling-quartz]]
|
||||
== Using the Quartz Scheduler
|
||||
|
||||
Quartz uses `Trigger`, `Job`, and `JobDetail` objects to realize scheduling of all kinds
|
||||
of jobs. For the basic concepts behind Quartz, see the
|
||||
Quartz uses `Trigger`, `Job`, and `JobDetail` objects to realize scheduling of all
|
||||
kinds of jobs. For the basic concepts behind Quartz, see the
|
||||
https://www.quartz-scheduler.org/[Quartz Web site]. For convenience purposes, Spring
|
||||
offers a couple of classes that simplify using Quartz within Spring-based applications.
|
||||
|
||||
@@ -794,9 +794,9 @@ offers a couple of classes that simplify using Quartz within Spring-based applic
|
||||
[[scheduling-quartz-jobdetail]]
|
||||
=== Using the `JobDetailFactoryBean`
|
||||
|
||||
Quartz `JobDetail` objects contain all the information needed to run a job. Spring provides a
|
||||
`JobDetailFactoryBean`, which provides bean-style properties for XML configuration purposes.
|
||||
Consider the following example:
|
||||
Quartz `JobDetail` objects contain all the information needed to run a job. Spring
|
||||
provides a `JobDetailFactoryBean`, which provides bean-style properties for XML
|
||||
configuration purposes. Consider the following example:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@@ -813,9 +813,9 @@ Consider the following example:
|
||||
The job detail configuration has all the information it needs to run the job (`ExampleJob`).
|
||||
The timeout is specified in the job data map. The job data map is available through the
|
||||
`JobExecutionContext` (passed to you at execution time), but the `JobDetail` also gets
|
||||
its properties from the job data mapped to properties of the job instance. So, in the following example,
|
||||
the `ExampleJob` contains a bean property named `timeout`, and the `JobDetail`
|
||||
has it applied automatically:
|
||||
its properties from the job data mapped to properties of the job instance. So, in the
|
||||
following example, the `ExampleJob` contains a bean property named `timeout`, and the
|
||||
`JobDetail` has it applied automatically:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
|
||||
----
|
||||
@@ -908,8 +908,8 @@ NOTE: By default, jobs will run in a concurrent fashion.
|
||||
[[scheduling-quartz-cron]]
|
||||
=== Wiring up Jobs by Using Triggers and `SchedulerFactoryBean`
|
||||
|
||||
We have created job details and jobs. We have also reviewed the convenience bean that lets
|
||||
you invoke a method on a specific object. Of course, we still need to schedule the
|
||||
We have created job details and jobs. We have also reviewed the convenience bean that
|
||||
lets you invoke a method on a specific object. Of course, we still need to schedule the
|
||||
jobs themselves. This is done by using triggers and a `SchedulerFactoryBean`. Several
|
||||
triggers are available within Quartz, and Spring offers two Quartz `FactoryBean`
|
||||
implementations with convenient defaults: `CronTriggerFactoryBean` and
|
||||
@@ -940,9 +940,9 @@ The following listing uses both a `SimpleTriggerFactoryBean` and a `CronTriggerF
|
||||
</bean>
|
||||
----
|
||||
|
||||
The preceding example sets up two triggers, one running every 50 seconds with a starting delay of 10
|
||||
seconds and one running every morning at 6 AM. To finalize everything, we need to set up the
|
||||
`SchedulerFactoryBean`, as the following example shows:
|
||||
The preceding example sets up two triggers, one running every 50 seconds with a starting
|
||||
delay of 10 seconds and one running every morning at 6 AM. To finalize everything,
|
||||
we need to set up the `SchedulerFactoryBean`, as the following example shows:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
|
||||
@@ -236,6 +236,42 @@ be matched, not only the `GET` method.
|
||||
|
||||
|
||||
|
||||
[[declaration-site-variance]]
|
||||
== Declaration-site variance
|
||||
|
||||
Dealing with generic types in Spring applications written in Kotlin may require, for some use cases, to understand
|
||||
Kotlin https://kotlinlang.org/docs/generics.html#declaration-site-variance[declaration-site variance]
|
||||
which allows to define the variance when declaring a type, which is not possible in Java which supports only use-site
|
||||
variance.
|
||||
|
||||
For example, declaring `List<Foo>` in Kotlin is conceptually equivalent to `java.util.List<? extends Foo>` because
|
||||
`kotlin.collections.List` is declared as
|
||||
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/[`interface List<out E> : kotlin.collections.Collection<E>`].
|
||||
|
||||
This needs to be taken in account by using the `out` Kotlin keyword on generic types when using Java classes,
|
||||
for example when writing a `org.springframework.core.convert.converter.Converter` from a Kotlin type to a Java type.
|
||||
|
||||
[source,kotlin,indent=0]
|
||||
----
|
||||
class ListOfFooConverter : Converter<List<Foo>, CustomJavaList<out Foo>> {
|
||||
// ...
|
||||
}
|
||||
----
|
||||
|
||||
When converting any kind of objects, star projection with `*` can be used instead of `out Any`.
|
||||
[source,kotlin,indent=0]
|
||||
----
|
||||
class ListOfAnyConverter : Converter<List<*>, CustomJavaList<*>> {
|
||||
// ...
|
||||
}
|
||||
----
|
||||
|
||||
NOTE: Spring Framework does not leverage yet declaration-site variance type information for injecting beans,
|
||||
subscribe to https://github.com/spring-projects/spring-framework/issues/22313[spring-framework#22313] to track related
|
||||
progresses.
|
||||
|
||||
|
||||
|
||||
[[testing]]
|
||||
== Testing
|
||||
|
||||
|
||||
@@ -1023,7 +1023,7 @@ Two, create a proxy that will perform the declared RSocket exchanges:
|
||||
RSocketRequester requester = ... ;
|
||||
RSocketServiceProxyFactory factory = RSocketServiceProxyFactory.builder(requester).build();
|
||||
|
||||
RepositoryService service = factory.createClient(RadarService.class);
|
||||
RadarService service = factory.createClient(RadarService.class);
|
||||
----
|
||||
|
||||
|
||||
|
||||
@@ -3,13 +3,4 @@
|
||||
:page-section-summary-toc: 1
|
||||
|
||||
This section covers annotations that you can use when you test Spring applications.
|
||||
It includes the following topics:
|
||||
|
||||
* xref:testing/annotations/integration-standard.adoc[Standard Annotation Support]
|
||||
* xref:testing/annotations/integration-spring.adoc[Spring Testing Annotations]
|
||||
* xref:testing/annotations/integration-junit4.adoc[Spring JUnit 4 Testing Annotations]
|
||||
* xref:testing/annotations/integration-junit-jupiter.adoc[Spring JUnit Jupiter Testing Annotations]
|
||||
* xref:testing/annotations/integration-meta.adoc[Meta-Annotation Support for Testing]
|
||||
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -152,7 +152,7 @@ Kotlin::
|
||||
@Autowired
|
||||
lateinit var accountService: AccountService
|
||||
|
||||
lateinit mockMvc: MockMvc
|
||||
lateinit var mockMvc: MockMvc
|
||||
|
||||
@BeforeEach
|
||||
fun setup(wac: WebApplicationContext) {
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
[[spring-mvc-test-vs-end-to-end-integration-tests]]
|
||||
= MockMvc vs End-to-End Tests
|
||||
|
||||
MockMVc is built on Servlet API mock implementations from the
|
||||
MockMvc is built on Servlet API mock implementations from the
|
||||
`spring-test` module and does not rely on a running container. Therefore, there are
|
||||
some differences when compared to full end-to-end integration tests with an actual
|
||||
client and a live server running.
|
||||
|
||||
+10
-33
@@ -1,38 +1,15 @@
|
||||
[[spring-mvc-test-vs-streaming-response]]
|
||||
= Streaming Responses
|
||||
|
||||
The best way to test streaming responses such as Server-Sent Events is through the
|
||||
<<WebTestClient>> which can be used as a test client to connect to a `MockMvc` instance
|
||||
to perform tests on Spring MVC controllers without a running server. For example:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
WebTestClient client = MockMvcWebTestClient.bindToController(new SseController()).build();
|
||||
|
||||
FluxExchangeResult<Person> exchangeResult = client.get()
|
||||
.uri("/persons")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectHeader().contentType("text/event-stream")
|
||||
.returnResult(Person.class);
|
||||
|
||||
// Use StepVerifier from Project Reactor to test the streaming response
|
||||
|
||||
StepVerifier.create(exchangeResult.getResponseBody())
|
||||
.expectNext(new Person("N0"), new Person("N1"), new Person("N2"))
|
||||
.expectNextCount(4)
|
||||
.consumeNextWith(person -> assertThat(person.getName()).endsWith("7"))
|
||||
.thenCancel()
|
||||
.verify();
|
||||
----
|
||||
======
|
||||
|
||||
`WebTestClient` can also connect to a live server and perform full end-to-end integration
|
||||
tests. This is also supported in Spring Boot where you can
|
||||
{docs-spring-boot}/html/spring-boot-features.html#boot-features-testing-spring-boot-applications-testing-with-running-server[test a running server].
|
||||
You can use `WebTestClient` to test xref:testing/webtestclient.adoc#webtestclient-stream[streaming responses]
|
||||
such as Server-Sent Events. However, `MockMvcWebTestClient` doesn't support infinite
|
||||
streams because there is no way to cancel the server stream from the client side.
|
||||
To test infinite streams, you'll need to
|
||||
xref:testing/webtestclient.adoc#webtestclient-server-config[bind to] a running server,
|
||||
or when using Spring Boot,
|
||||
{docs-spring-boot}/spring-boot-features.html#boot-features-testing-spring-boot-applications-testing-with-running-server[test with a running server].
|
||||
|
||||
`MockMvcWebTestClient` does support asynchronous responses, and even streaming responses.
|
||||
The limitation is that it can't influence the server to stop, and therefore the server
|
||||
must finish writing the response on its own.
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ following features.
|
||||
testing annotations -- as long as the tests are run using a JUnit Platform
|
||||
`TestEngine` that is registered for the current project.
|
||||
* Build-time AOT processing: each unique test `ApplicationContext` in the current project
|
||||
will be xref:core/aot.adoc#refresh[refreshed for AOT processing].
|
||||
will be xref:core/aot.adoc#aot.refresh[refreshed for AOT processing].
|
||||
* Runtime AOT support: when executing in AOT runtime mode, a Spring integration test will
|
||||
use an AOT-optimized `ApplicationContext` that participates transparently with the
|
||||
xref:testing/testcontext-framework/ctx-management/caching.adoc[context cache].
|
||||
@@ -35,7 +35,7 @@ the following options.
|
||||
via {api-spring-framework}/context/annotation/ImportRuntimeHints.html[`@ImportRuntimeHints`].
|
||||
* Annotate a test class with {api-spring-framework}/aot/hint/annotation/Reflective.html[`@Reflective`] or
|
||||
{api-spring-framework}/aot/hint/annotation/RegisterReflectionForBinding.html[`@RegisterReflectionForBinding`].
|
||||
* See xref:core/aot.adoc#hints[Runtime Hints] for details on Spring's core runtime hints
|
||||
* See xref:core/aot.adoc#aot.hints[Runtime Hints] for details on Spring's core runtime hints
|
||||
and annotation support.
|
||||
|
||||
[TIP]
|
||||
|
||||
+2
-2
@@ -35,8 +35,8 @@ in which the test class is defined. A path starting with a slash is treated as a
|
||||
absolute classpath resource (for example: `"/org/example/test.xml"`). A path that
|
||||
references a URL (for example, a path prefixed with `classpath:`, `file:`, or `http:`) is
|
||||
loaded by using the specified resource protocol. Resource location wildcards (such as
|
||||
`**/*.properties`) are not permitted: Each location must evaluate to exactly one
|
||||
`.properties` or `.xml` resource.
|
||||
`{asterisk}{asterisk}/{asterisk}.properties`) are not permitted: Each location must
|
||||
evaluate to exactly one `.properties` or `.xml` resource.
|
||||
|
||||
The following example uses a test properties file:
|
||||
|
||||
|
||||
@@ -75,6 +75,35 @@ To learn more from the source or to make advanced customizations, see:
|
||||
|
||||
|
||||
|
||||
[[webflux-cors-credentialed-requests]]
|
||||
== Credentialed Requests
|
||||
[.small]#xref:web/webmvc-cors.adoc#mvc-cors-credentialed-requests[See equivalent in the Servlet stack]#
|
||||
|
||||
Using CORS with credentialed requests requires enabling `allowedCredentials`. Be aware that
|
||||
this option establishes a high level of trust with the configured domains and also increases
|
||||
the surface of attack of the web application by exposing sensitive user-specific information
|
||||
such as cookies and CSRF tokens.
|
||||
|
||||
Enabling credentials also impacts how the configured `"*"` CORS wildcards are processed:
|
||||
|
||||
* Wildcards are not authorized in `allowOrigins`, but alternatively
|
||||
the `allowOriginPatterns` property may be used to match to a dynamic set of origins.
|
||||
* When set on `allowedHeaders` or `allowedMethods`, the `Access-Control-Allow-Headers`
|
||||
and `Access-Control-Allow-Methods` response headers are handled by copying the related
|
||||
headers and method specified in the CORS preflight request.
|
||||
* When set on `exposedHeaders`, `Access-Control-Expose-Headers` response header is set
|
||||
either to the configured list of headers or to the wildcard character. While the CORS spec
|
||||
does not allow the wildcard character when `Access-Control-Allow-Credentials` is set to
|
||||
`true`, most browsers support it and the response headers are not all available during the
|
||||
CORS processing, so as a consequence the wildcard character is the header value used when
|
||||
specified regardless of the value of the `allowCredentials` property.
|
||||
|
||||
WARNING: While such wildcard configuration can be handy, it is recommended when possible to configure
|
||||
a finite set of values instead to provide a higher level of security.
|
||||
|
||||
|
||||
|
||||
|
||||
[[webflux-cors-controller]]
|
||||
== `@CrossOrigin`
|
||||
[.small]#xref:web/webmvc-cors.adoc#mvc-cors-controller[See equivalent in the Servlet stack]#
|
||||
|
||||
+4
-1
@@ -166,4 +166,7 @@ By default, any argument that is not a simple value type (as determined by
|
||||
and is not resolved by any other argument resolver is treated as if it were annotated
|
||||
with `@ModelAttribute`.
|
||||
|
||||
|
||||
WARNING: When compiling to a native image with GraalVM, the implicit `@ModelAttribute`
|
||||
support described above does not allow proper ahead-of-time inference of related data
|
||||
binding reflection hints. As a consequence, it is recommended to explicitly annotate
|
||||
method parameters with `@ModelAttribute` for use in a GraalVM native image.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user