mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-18 18:22:23 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3591f1e9fb |
@@ -0,0 +1,20 @@
|
||||
name: Await HTTP Resource
|
||||
description: 'Waits for an HTTP resource to be available (a HEAD request succeeds)'
|
||||
inputs:
|
||||
url:
|
||||
description: 'URL of the resource to await'
|
||||
required: true
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Await HTTP resource
|
||||
shell: bash
|
||||
run: |
|
||||
url=${{ inputs.url }}
|
||||
echo "Waiting for $url"
|
||||
until curl --fail --head --silent ${{ inputs.url }} > /dev/null
|
||||
do
|
||||
echo "."
|
||||
sleep 60
|
||||
done
|
||||
echo "$url is available"
|
||||
@@ -1,18 +1,6 @@
|
||||
name: 'Build'
|
||||
description: 'Builds the project, optionally publishing it to a local deployment repository'
|
||||
inputs:
|
||||
commercial-release-repository-url:
|
||||
description: 'URL of the release repository'
|
||||
required: false
|
||||
commercial-repository-password:
|
||||
description: 'Password for authentication with the commercial repository'
|
||||
required: false
|
||||
commercial-repository-username:
|
||||
description: 'Username for authentication with the commercial repository'
|
||||
required: false
|
||||
commercial-snapshot-repository-url:
|
||||
description: 'URL of the snapshot repository'
|
||||
required: false
|
||||
develocity-access-key:
|
||||
description: 'Access key for authentication with ge.spring.io'
|
||||
required: false
|
||||
@@ -58,21 +46,11 @@ runs:
|
||||
id: build
|
||||
if: ${{ inputs.publish == 'false' }}
|
||||
shell: bash
|
||||
env:
|
||||
COMMERCIAL_RELEASE_REPO_URL: ${{ inputs.commercial-release-repository-url }}
|
||||
COMMERCIAL_REPO_PASSWORD: ${{ inputs.commercial-repository-password }}
|
||||
COMMERCIAL_REPO_USERNAME: ${{ inputs.commercial-repository-username }}
|
||||
COMMERCIAL_SNAPSHOT_REPO_URL: ${{ inputs.commercial-snapshot-repository-url }}
|
||||
run: ./gradlew check antora
|
||||
- name: Publish
|
||||
id: publish
|
||||
if: ${{ inputs.publish == 'true' }}
|
||||
shell: bash
|
||||
env:
|
||||
COMMERCIAL_RELEASE_REPO_URL: ${{ inputs.commercial-release-repository-url }}
|
||||
COMMERCIAL_REPO_PASSWORD: ${{ inputs.commercial-repository-password }}
|
||||
COMMERCIAL_REPO_USERNAME: ${{ inputs.commercial-repository-username }}
|
||||
COMMERCIAL_SNAPSHOT_REPO_URL: ${{ inputs.commercial-snapshot-repository-url }}
|
||||
run: ./gradlew -PdeploymentRepository=$(pwd)/deployment-repository build publishAllPublicationsToDeploymentRepository
|
||||
- name: Read Version From gradle.properties
|
||||
id: read-version
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
name: Create GitHub Release
|
||||
description: 'Create the release on GitHub with a changelog'
|
||||
inputs:
|
||||
commercial:
|
||||
description: 'Whether to generate the changelog for the commercial release'
|
||||
required: true
|
||||
latest:
|
||||
description: 'Whether the release is the latest release'
|
||||
required: false
|
||||
default: 'false'
|
||||
milestone:
|
||||
description: 'Name of the GitHub milestone for which a release will be created'
|
||||
required: true
|
||||
@@ -22,13 +15,13 @@ runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Generate Changelog
|
||||
uses: spring-io/github-changelog-generator@f7d7a87a3e7c627ecb8c26cf086c38ac5a939721 #v0.0.14
|
||||
uses: spring-io/github-changelog-generator@86958813a62af8fb223b3fd3b5152035504bcb83 #v0.0.12
|
||||
with:
|
||||
config-file: ${{ inputs.commercial && '.github/actions/create-github-release/changelog-generator-commercial.yml' || '.github/actions/create-github-release/changelog-generator-oss.yml' }}
|
||||
config-file: .github/actions/create-github-release/changelog-generator.yml
|
||||
milestone: ${{ inputs.milestone }}
|
||||
token: ${{ inputs.token }}
|
||||
- name: Create GitHub Release
|
||||
shell: bash
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ inputs.token }}
|
||||
run: gh release create ${{ format('v{0}', inputs.milestone) }} --notes-file changelog.md ${{ inputs.pre-release == 'true' && '--prerelease' || format('--latest={0}', inputs.latest) }}
|
||||
run: gh release create ${{ format('v{0}', inputs.milestone) }} --notes-file changelog.md ${{ inputs.pre-release == 'true' && '--prerelease' || '' }}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
changelog:
|
||||
repository: spring-projects/spring-framework-commercial
|
||||
sections:
|
||||
- title: ":warning: Attention Required"
|
||||
labels:
|
||||
- "for: upgrade-attention"
|
||||
summary:
|
||||
mode: "member-comment"
|
||||
config:
|
||||
prefix: "Attention Required:"
|
||||
- title: ":star: New Features"
|
||||
labels:
|
||||
- "type: enhancement"
|
||||
- title: ":lady_beetle: Bug Fixes"
|
||||
labels:
|
||||
- "type: bug"
|
||||
- "type: regression"
|
||||
- title: ":notebook_with_decorative_cover: Documentation"
|
||||
labels:
|
||||
- "type: documentation"
|
||||
- title: ":hammer: Dependency Upgrades"
|
||||
sort: "title"
|
||||
labels:
|
||||
- "type: dependency-upgrade"
|
||||
contributors:
|
||||
exclude:
|
||||
names:
|
||||
- "bclozel"
|
||||
- "jhoeller"
|
||||
- "rstoyanchev"
|
||||
- "sbrannen"
|
||||
- "sdeleuze"
|
||||
- "snicoll"
|
||||
+2
-7
@@ -1,13 +1,6 @@
|
||||
changelog:
|
||||
repository: spring-projects/spring-framework
|
||||
sections:
|
||||
- title: ":warning: Attention Required"
|
||||
labels:
|
||||
- "for: upgrade-attention"
|
||||
summary:
|
||||
mode: "member-comment"
|
||||
config:
|
||||
prefix: "Attention Required:"
|
||||
- title: ":star: New Features"
|
||||
labels:
|
||||
- "type: enhancement"
|
||||
@@ -27,7 +20,9 @@ changelog:
|
||||
names:
|
||||
- "bclozel"
|
||||
- "jhoeller"
|
||||
- "poutsma"
|
||||
- "rstoyanchev"
|
||||
- "sbrannen"
|
||||
- "sdeleuze"
|
||||
- "simonbasle"
|
||||
- "snicoll"
|
||||
@@ -24,30 +24,32 @@ runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Set Up Java
|
||||
uses: actions/setup-java@v5
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: ${{ inputs.java-early-access == 'true' && 'temurin' || (inputs.java-distribution || 'liberica') }}
|
||||
java-version: |
|
||||
${{ inputs.java-early-access == 'true' && format('{0}-ea', inputs.java-version) || inputs.java-version }}
|
||||
${{ inputs.java-toolchain == 'true' && '25' || '' }}
|
||||
${{ inputs.java-toolchain == 'true' && '17' || '' }}
|
||||
25
|
||||
- name: Set Up Gradle
|
||||
uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # 6.2.0
|
||||
uses: gradle/actions/setup-gradle@ac638b010cf58a27ee6c972d7336334ccaf61c96 # v4.4.1
|
||||
with:
|
||||
cache-provider: basic
|
||||
cache-read-only: false
|
||||
develocity-access-key: ${{ inputs.develocity-access-key }}
|
||||
develocity-token-expiry: 4
|
||||
- name: Configure Gradle Properties
|
||||
shell: bash
|
||||
run: |
|
||||
echo 'systemProp.user.name=spring-builds+github' >> $GRADLE_USER_HOME/gradle.properties
|
||||
echo 'systemProp.org.gradle.internal.launcher.welcomeMessageEnabled=false' >> $GRADLE_USER_HOME/gradle.properties
|
||||
echo 'org.gradle.daemon=false' >> $GRADLE_USER_HOME/gradle.properties
|
||||
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 }} >> $GRADLE_USER_HOME/gradle.properties
|
||||
echo systemProp.org.gradle.java.installations.auto-detect=false >> $GRADLE_USER_HOME/gradle.properties
|
||||
echo systemProp.org.gradle.java.installations.auto-download=false >> $GRADLE_USER_HOME/gradle.properties
|
||||
echo systemProp.org.gradle.java.installations.paths=${{ format('$JAVA_HOME_{0}_X64', inputs.java-version) }} >> $GRADLE_USER_HOME/gradle.properties
|
||||
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
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
name: Build Release
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Build Release
|
||||
shell: bash
|
||||
run: ./gradlew -PdeploymentRepository=$(pwd)/deployment-repository publishAllPublicationsToDeploymentRepository
|
||||
@@ -1,16 +0,0 @@
|
||||
artifactory:
|
||||
artifacts:
|
||||
- pattern: "/**/framework-api-*.zip"
|
||||
properties:
|
||||
zip.deployed: "false"
|
||||
zip.name: "spring-framework"
|
||||
- pattern: "/**/framework-api-*-docs.zip"
|
||||
properties:
|
||||
zip.type: "docs"
|
||||
- pattern: "/**/framework-api-*-schema.zip"
|
||||
properties:
|
||||
zip.type: "schema"
|
||||
maven-central:
|
||||
excludes:
|
||||
- "org/springframework/framework-api/**"
|
||||
- "org/springframework/framework-docs/**"
|
||||
@@ -1,7 +0,0 @@
|
||||
name: Test Release
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Test Release
|
||||
shell: bash
|
||||
run: ./gradlew check
|
||||
@@ -0,0 +1,34 @@
|
||||
name: Sync to Maven Central
|
||||
description: 'Syncs a release to Maven Central and waits for it to be available for use'
|
||||
inputs:
|
||||
central-token-password:
|
||||
description: 'Password for authentication with central.sonatype.com'
|
||||
required: true
|
||||
central-token-username:
|
||||
description: 'Username for authentication with central.sonatype.com'
|
||||
required: true
|
||||
jfrog-cli-config-token:
|
||||
description: 'Config token for the JFrog CLI'
|
||||
required: true
|
||||
spring-framework-version:
|
||||
description: 'Version of Spring Framework that is being synced to Central'
|
||||
required: true
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Set Up JFrog CLI
|
||||
uses: jfrog/setup-jfrog-cli@f748a0599171a192a2668afee8d0497f7c1069df # v4.5.6
|
||||
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/central-publish-action@0cdd90d12e6876341e82860d951e1bcddc1e51b6 # v0.2.0
|
||||
with:
|
||||
token: ${{ inputs.central-token-password }}
|
||||
token-name: ${{ inputs.central-token-username }}
|
||||
- name: Await
|
||||
uses: ./.github/actions/await-http-resource
|
||||
with:
|
||||
url: ${{ format('https://repo.maven.apache.org/maven2/org/springframework/spring-context/{0}/spring-context-{0}.jar', inputs.spring-framework-version) }}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"aql": {
|
||||
"items.find": {
|
||||
"$and": [
|
||||
{
|
||||
"@build.name": "${buildName}",
|
||||
"@build.number": "${buildNumber}",
|
||||
"path": {
|
||||
"$nmatch": "org/springframework/framework-api/*"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"target": "nexus/"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
workflow:
|
||||
generator:
|
||||
project:
|
||||
java:
|
||||
versions:
|
||||
primary: 25
|
||||
workflows:
|
||||
release-train:
|
||||
build:
|
||||
env:
|
||||
COMMERCIAL_REPO_USERNAME: secrets.COMMERCIAL_ARTIFACTORY_USERNAME
|
||||
COMMERCIAL_REPO_PASSWORD: secrets.COMMERCIAL_ARTIFACTORY_PASSWORD
|
||||
COMMERCIAL_RELEASE_REPO_URL: vars.COMMERCIAL_RELEASE_REPO_URL
|
||||
test:
|
||||
env:
|
||||
COMMERCIAL_REPO_USERNAME: secrets.COMMERCIAL_ARTIFACTORY_USERNAME
|
||||
COMMERCIAL_REPO_PASSWORD: secrets.COMMERCIAL_ARTIFACTORY_PASSWORD
|
||||
COMMERCIAL_RELEASE_REPO_URL: vars.COMMERCIAL_RELEASE_REPO_URL
|
||||
@@ -7,15 +7,27 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- '*.x'
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
backport-issue:
|
||||
build:
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Create Backport Issue
|
||||
uses: spring-io/backport-bot@v0.0.3
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
distribution: 'liberica'
|
||||
java-version: 17
|
||||
- name: Download BackportBot
|
||||
run: wget https://github.com/spring-io/backport-bot/releases/download/latest/backport-bot-0.0.1-SNAPSHOT.jar
|
||||
- name: Backport
|
||||
env:
|
||||
GITHUB_EVENT: ${{ toJSON(github.event) }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: java -jar backport-bot-0.0.1-SNAPSHOT.jar --github.accessToken="$GITHUB_TOKEN" --github.event_name "$GITHUB_EVENT_NAME" --github.event "$GITHUB_EVENT"
|
||||
|
||||
@@ -2,76 +2,57 @@ name: Build and Deploy Snapshot
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 7.0.x
|
||||
- main
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
jobs:
|
||||
build-and-deploy-snapshot:
|
||||
name: Build and Deploy Snapshot
|
||||
if: ${{ github.repository == 'spring-projects/spring-framework' || github.repository == 'spring-projects/spring-framework-commercial' }}
|
||||
runs-on: ${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }}
|
||||
if: ${{ github.repository == 'spring-projects/spring-framework' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@v4
|
||||
- name: Build and Publish
|
||||
id: build-and-publish
|
||||
uses: ./.github/actions/build
|
||||
with:
|
||||
commercial-release-repository-url: ${{ vars.COMMERCIAL_RELEASE_REPO_URL }}
|
||||
commercial-repository-password: ${{ secrets.COMMERCIAL_ARTIFACTORY_PASSWORD }}
|
||||
commercial-repository-username: ${{ secrets.COMMERCIAL_ARTIFACTORY_USERNAME }}
|
||||
commercial-snapshot-repository-url: ${{ vars.COMMERCIAL_SNAPSHOT_REPO_URL }}
|
||||
develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
|
||||
publish: true
|
||||
- name: Deploy
|
||||
uses: spring-io/artifactory-deploy-action@aba148f1541e09adcf5735af90029fac9a6d3083 # v0.0.5
|
||||
uses: spring-io/artifactory-deploy-action@dc1913008c0599f0c4b1fdafb6ff3c502b3565ea # v0.0.2
|
||||
with:
|
||||
artifact-properties: |
|
||||
/**/framework-api-*.zip::zip.name=spring-framework,zip.deployed=false
|
||||
/**/framework-api-*-docs.zip::zip.type=docs
|
||||
/**/framework-api-*-schema.zip::zip.type=schema
|
||||
build-name: ${{ vars.COMMERCIAL && format('spring-framework-commercial-{0}', '7.0.x') || format('spring-framework-{0}', '7.0.x') }}
|
||||
build-name: 'spring-framework-7.0.x'
|
||||
folder: 'deployment-repository'
|
||||
project: ${{ vars.COMMERCIAL && 'spring' }}
|
||||
repository: ${{ vars.COMMERCIAL && 'spring-enterprise-maven-dev-local' || 'libs-snapshot-local' }}
|
||||
uri: ${{ vars.COMMERCIAL_DEPLOY_REPO_URL || 'https://repo.spring.io' }}
|
||||
username: ${{ vars.COMMERCIAL && secrets.COMMERCIAL_ARTIFACTORY_USERNAME || secrets.ARTIFACTORY_USERNAME }}
|
||||
password: ${{ vars.COMMERCIAL && secrets.COMMERCIAL_ARTIFACTORY_PASSWORD || secrets.ARTIFACTORY_PASSWORD }}
|
||||
password: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
repository: 'libs-snapshot-local'
|
||||
signing-key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
signing-passphrase: ${{ secrets.GPG_PASSPHRASE }}
|
||||
uri: 'https://repo.spring.io'
|
||||
username: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
- name: Send Notification
|
||||
if: always()
|
||||
uses: ./.github/actions/send-notification
|
||||
with:
|
||||
build-scan-url: ${{ steps.build-and-publish.outputs.build-scan-url }}
|
||||
run-name: ${{ format('{0} | Linux | Java 25', github.ref_name) }}
|
||||
run-name: ${{ format('{0} | Linux | Java 17', github.ref_name) }}
|
||||
status: ${{ job.status }}
|
||||
webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
|
||||
outputs:
|
||||
version: ${{ steps.build-and-publish.outputs.version }}
|
||||
trigger-docs-build:
|
||||
name: Trigger Docs Build
|
||||
needs: build-and-deploy-snapshot
|
||||
if: ${{ !vars.COMMERCIAL }} # remove when commercial support
|
||||
permissions:
|
||||
actions: write
|
||||
runs-on: ${{ vars.UBUNTU_SMALL || 'ubuntu-latest' }}
|
||||
steps:
|
||||
- name: Run Deploy Docs Workflow
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: gh workflow run deploy-docs.yml --repo ${{ github.repository }} -r docs-build -f build-refname=${{ github.ref_name }}
|
||||
verify:
|
||||
name: Verify
|
||||
needs: build-and-deploy-snapshot
|
||||
uses: ./.github/workflows/verify.yml
|
||||
secrets:
|
||||
commercial-repository-password: ${{ secrets.COMMERCIAL_ARTIFACTORY_PASSWORD }}
|
||||
commercial-repository-username: ${{ secrets.COMMERCIAL_ARTIFACTORY_USERNAME }}
|
||||
google-chat-webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
|
||||
opensource-repository-password: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
opensource-repository-username: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
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 }}
|
||||
|
||||
@@ -7,9 +7,10 @@ jobs:
|
||||
name: Build Pull Request
|
||||
if: ${{ github.repository == 'spring-projects/spring-framework' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@v4
|
||||
- name: Build
|
||||
id: build
|
||||
uses: ./.github/actions/build
|
||||
@@ -18,7 +19,7 @@ jobs:
|
||||
uses: ./.github/actions/print-jvm-thread-dumps
|
||||
- name: Upload Build Reports
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: build-reports
|
||||
path: '**/build/reports/'
|
||||
path: '**/build/reports/'
|
||||
|
||||
@@ -2,33 +2,31 @@ name: CI
|
||||
on:
|
||||
schedule:
|
||||
- cron: '30 9 * * *'
|
||||
permissions:
|
||||
contents: read
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
jobs:
|
||||
ci:
|
||||
name: '${{ matrix.os.name}} | Java ${{ matrix.java.version}}'
|
||||
if: ${{ github.repository == 'spring-projects/spring-framework' || github.repository == 'spring-projects/spring-framework-commercial' }}
|
||||
if: ${{ github.repository == 'spring-projects/spring-framework' }}
|
||||
runs-on: ${{ matrix.os.id }}
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- id: ${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }}
|
||||
- id: ubuntu-latest
|
||||
name: Linux
|
||||
java:
|
||||
- version: 17
|
||||
toolchain: true
|
||||
toolchain: false
|
||||
- version: 21
|
||||
toolchain: true
|
||||
- version: 25
|
||||
toolchain: false
|
||||
- version: 26
|
||||
toolchain: true
|
||||
exclude:
|
||||
- os:
|
||||
name: Linux
|
||||
java:
|
||||
version: 25
|
||||
version: 17
|
||||
steps:
|
||||
- name: Prepare Windows runner
|
||||
if: ${{ runner.os == 'Windows' }}
|
||||
@@ -37,15 +35,11 @@ jobs:
|
||||
git config --global core.longPaths true
|
||||
Stop-Service -name Docker
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@v4
|
||||
- name: Build
|
||||
id: build
|
||||
uses: ./.github/actions/build
|
||||
with:
|
||||
commercial-release-repository-url: ${{ vars.COMMERCIAL_RELEASE_REPO_URL }}
|
||||
commercial-repository-password: ${{ secrets.COMMERCIAL_ARTIFACTORY_PASSWORD }}
|
||||
commercial-repository-username: ${{ secrets.COMMERCIAL_ARTIFACTORY_USERNAME }}
|
||||
commercial-snapshot-repository-url: ${{ vars.COMMERCIAL_SNAPSHOT_REPO_URL }}
|
||||
develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
|
||||
java-early-access: ${{ matrix.java.early-access || 'false' }}
|
||||
java-distribution: ${{ matrix.java.distribution }}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
name: Deploy Docs
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
- '*.x'
|
||||
- '!gh-pages'
|
||||
tags:
|
||||
- 'v*'
|
||||
repository_dispatch:
|
||||
types: request-build-reference # legacy
|
||||
workflow_dispatch:
|
||||
permissions:
|
||||
actions: write
|
||||
jobs:
|
||||
build:
|
||||
name: Dispatch docs deployment
|
||||
if: github.repository_owner == 'spring-projects'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
ref: docs-build
|
||||
- name: Dispatch (partial build)
|
||||
if: github.ref_type == 'branch'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: gh workflow run deploy-docs.yml -r $(git rev-parse --abbrev-ref HEAD) -f build-refname=${{ github.ref_name }}
|
||||
- name: Dispatch (full build)
|
||||
if: github.ref_type == 'tag'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: gh workflow run deploy-docs.yml -r $(git rev-parse --abbrev-ref HEAD)
|
||||
@@ -1,39 +0,0 @@
|
||||
name: Release Milestone
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v7.0.0-M[1-9]
|
||||
- v7.0.0-RC[1-9]
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
jobs:
|
||||
trigger-docs-build:
|
||||
name: Trigger Docs Build
|
||||
permissions:
|
||||
actions: write
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
steps:
|
||||
- name: Determine Version
|
||||
id: version
|
||||
run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
|
||||
- name: Run Deploy Docs Workflow
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: gh workflow run deploy-docs.yml --repo ${{ github.repository }} -r docs-build -f build-refname=${{ github.ref_name }}
|
||||
create-github-release:
|
||||
name: Create GitHub Release
|
||||
needs:
|
||||
- trigger-docs-build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@v6
|
||||
- name: Create GitHub Release
|
||||
uses: ./.github/actions/create-github-release
|
||||
with:
|
||||
commercial: ${{ vars.COMMERCIAL }}
|
||||
milestone: ${{ needs.trigger-docs-build.outputs.version }}
|
||||
pre-release: true
|
||||
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
@@ -1,38 +0,0 @@
|
||||
name: Release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v7.0.[0-9]+
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
jobs:
|
||||
trigger-docs-build:
|
||||
name: Trigger Docs Build
|
||||
permissions:
|
||||
actions: write
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
steps:
|
||||
- name: Determine Version
|
||||
id: version
|
||||
run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
|
||||
- name: Run Deploy Docs Workflow
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: gh workflow run deploy-docs.yml --repo ${{ github.repository }} -r docs-build -f build-refname=${{ github.ref_name }}
|
||||
create-github-release:
|
||||
name: Create GitHub Release
|
||||
needs:
|
||||
- trigger-docs-build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@v6
|
||||
- name: Create GitHub Release
|
||||
uses: ./.github/actions/create-github-release
|
||||
with:
|
||||
commercial: ${{ vars.COMMERCIAL }}
|
||||
latest: true
|
||||
milestone: ${{ needs.trigger-docs-build.outputs.version }}
|
||||
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
@@ -0,0 +1,95 @@
|
||||
name: Release Milestone
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v7.0.0-M[1-9]
|
||||
- v7.0.0-RC[1-9]
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
jobs:
|
||||
build-and-stage-release:
|
||||
name: Build and Stage Release
|
||||
if: ${{ github.repository == 'spring-projects/spring-framework' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@v4
|
||||
- name: Build and Publish
|
||||
id: build-and-publish
|
||||
uses: ./.github/actions/build
|
||||
with:
|
||||
develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
|
||||
publish: true
|
||||
- name: Stage Release
|
||||
uses: spring-io/artifactory-deploy-action@26bbe925a75f4f863e1e529e85be2d0093cac116 # v0.0.1
|
||||
with:
|
||||
artifact-properties: |
|
||||
/**/framework-api-*.zip::zip.name=spring-framework,zip.deployed=false
|
||||
/**/framework-api-*-docs.zip::zip.type=docs
|
||||
/**/framework-api-*-schema.zip::zip.type=schema
|
||||
build-name: ${{ format('spring-framework-{0}', steps.build-and-publish.outputs.version)}}
|
||||
folder: 'deployment-repository'
|
||||
password: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
repository: 'libs-staging-local'
|
||||
signing-key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
signing-passphrase: ${{ secrets.GPG_PASSPHRASE }}
|
||||
uri: 'https://repo.spring.io'
|
||||
username: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
outputs:
|
||||
version: ${{ steps.build-and-publish.outputs.version }}
|
||||
verify:
|
||||
name: Verify
|
||||
needs: build-and-stage-release
|
||||
uses: ./.github/workflows/verify.yml
|
||||
secrets:
|
||||
google-chat-webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
|
||||
repository-password: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
repository-username: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
with:
|
||||
staging: true
|
||||
version: ${{ needs.build-and-stage-release.outputs.version }}
|
||||
sync-to-maven-central:
|
||||
name: Sync to Maven Central
|
||||
needs:
|
||||
- build-and-stage-release
|
||||
- verify
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Sync to Maven Central
|
||||
uses: ./.github/actions/sync-to-maven-central
|
||||
with:
|
||||
central-token-password: ${{ secrets.CENTRAL_TOKEN_PASSWORD }}
|
||||
central-token-username: ${{ secrets.CENTRAL_TOKEN_USERNAME }}
|
||||
jfrog-cli-config-token: ${{ secrets.JF_ARTIFACTORY_SPRING }}
|
||||
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@dff217c085c17666e8849ebdbf29c8fe5e3995e6 # v4.5.2
|
||||
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-milestone-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 }}
|
||||
pre-release: true
|
||||
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
@@ -1,93 +0,0 @@
|
||||
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
|
||||
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
|
||||
|
||||
name: "Release Train – Build"
|
||||
run-name: "${{ inputs.callback-ref }} – Build"
|
||||
"on":
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
callback:
|
||||
description: "Repository to which a callback should be made upon completion"
|
||||
required: true
|
||||
type: "string"
|
||||
callback-ref:
|
||||
description: "Ref in the callback repository to which a callback should be made upon completion"
|
||||
required: true
|
||||
type: "string"
|
||||
release-train-maven-repository-url:
|
||||
description: "URL of a Maven repository to be used to resolve artifacts of projects earlier in the train"
|
||||
required: true
|
||||
type: "string"
|
||||
permissions:
|
||||
contents: "read"
|
||||
concurrency:
|
||||
group: "${{ github.workflow }}-${{ github.ref }}"
|
||||
jobs:
|
||||
build-release:
|
||||
name: "Build Release"
|
||||
runs-on: "ubuntu22-2-8"
|
||||
steps:
|
||||
- name: "Prevent Re-runs"
|
||||
id: "prevent-re-runs"
|
||||
run: |-
|
||||
if [ "$GITHUB_RUN_ATTEMPT" -gt 1 ]; then
|
||||
echo "Re-runs are prohibited. Use the 'Release Train – Retry' workflow to retry build failures"
|
||||
exit 1
|
||||
fi
|
||||
- name: "Set up Java"
|
||||
id: "set-up-java"
|
||||
uses: "actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95" # v5.6.0
|
||||
with:
|
||||
distribution: "liberica"
|
||||
java-version: "25"
|
||||
- name: "Check Out Code"
|
||||
id: "check-out-code"
|
||||
uses: "actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0" # v7.0.0
|
||||
- name: "Build Release"
|
||||
id: "build-release"
|
||||
uses: "./.github/actions/release-train-build"
|
||||
env:
|
||||
COMMERCIAL_RELEASE_REPO_URL: "${{ vars.COMMERCIAL_RELEASE_REPO_URL }}"
|
||||
COMMERCIAL_REPO_PASSWORD: "${{ secrets.COMMERCIAL_ARTIFACTORY_PASSWORD }}"
|
||||
COMMERCIAL_REPO_USERNAME: "${{ secrets.COMMERCIAL_ARTIFACTORY_USERNAME }}"
|
||||
RELEASE_TRAIN_MAVEN_REPOSITORY_PASSWORD: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_MAVEN_REPOSITORY_PASSWORD }}"
|
||||
RELEASE_TRAIN_MAVEN_REPOSITORY_URL: "${{ inputs.release-train-maven-repository-url }}"
|
||||
RELEASE_TRAIN_MAVEN_REPOSITORY_USERNAME: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_MAVEN_REPOSITORY_USERNAME }}"
|
||||
- name: "Upload Deployment Repository"
|
||||
id: "upload-deployment-repository"
|
||||
uses: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" # v7.0.1
|
||||
with:
|
||||
name: "deployment-repository"
|
||||
path: "deployment-repository/**"
|
||||
- name: "Upload Deployment Spec"
|
||||
id: "upload-deployment-spec"
|
||||
uses: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" # v7.0.1
|
||||
with:
|
||||
archive: "false"
|
||||
if-no-files-found: "ignore"
|
||||
name: "deployment-spec"
|
||||
path: ".github/actions/release-train-build/deployment-spec.yml"
|
||||
- name: "Save Build System Caches"
|
||||
id: "save-build-system-caches"
|
||||
uses: "actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9" # v6.1.0
|
||||
with:
|
||||
key: "release-train-${{ inputs.callback-ref }}-${{ github.ref_name }}"
|
||||
path: |-
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
- name: "Send Callback"
|
||||
id: "send-callback"
|
||||
if: "${{ !cancelled() }}"
|
||||
env:
|
||||
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
|
||||
run: |-
|
||||
gh workflow run callback \
|
||||
--repo ${{ inputs.callback }} \
|
||||
--ref ${{ inputs.callback-ref }} \
|
||||
--field commit-hash=${{ steps.check-out-code.outputs.commit }} \
|
||||
--field deployment-repository-artifact-identifier=${{ steps.upload-deployment-repository.outputs.artifact-id }} \
|
||||
--field deployment-spec-artifact-identifier=${{ steps.upload-deployment-spec.outputs.artifact-id }} \
|
||||
--field release-branch=${{ github.ref_name }} \
|
||||
--field release-repository=${{ github.repository }} \
|
||||
--field result=${{ job.status == 'success' && 'built' || 'build-failed' }} \
|
||||
--field workflow-run-url=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
@@ -1,55 +0,0 @@
|
||||
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
|
||||
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
|
||||
|
||||
name: "Release Train – Join"
|
||||
run-name: "${{ inputs.release-train }} – Join"
|
||||
"on":
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
deployment-destination:
|
||||
description: "Destination to which the release should be deployed"
|
||||
options:
|
||||
- "Maven Central"
|
||||
- "Spring Enterprise"
|
||||
required: true
|
||||
type: "choice"
|
||||
release-train:
|
||||
description: "Release train"
|
||||
required: true
|
||||
type: "string"
|
||||
release-train-repository:
|
||||
default: "spring-io/release-train"
|
||||
description: "Release train repository"
|
||||
required: true
|
||||
type: "string"
|
||||
permissions:
|
||||
contents: "none"
|
||||
jobs:
|
||||
join-release-train:
|
||||
name: "Join Release Train"
|
||||
runs-on: "ubuntu-latest"
|
||||
steps:
|
||||
- name: "Join Release Train"
|
||||
id: "join-release-train"
|
||||
env:
|
||||
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
|
||||
run: |-
|
||||
run_url=$(
|
||||
gh workflow run join \
|
||||
--repo ${{ inputs.release-train-repository }} \
|
||||
--ref ${{ inputs.release-train }} \
|
||||
--field commit-hash=${{ github.sha }} \
|
||||
--field deployment-destination=${{ inputs.deployment-destination == 'Maven Central' && 'maven-central' || 'spring-enterprise' }} \
|
||||
--field release-branch=${{ github.ref_name }} \
|
||||
--field release-repository=${{ github.repository }}
|
||||
)
|
||||
echo "Dispatched workflow run. Waiting for $run_url to complete."
|
||||
run_id=${run_url##*/}
|
||||
watch_exit_code=0
|
||||
gh run watch $run_id --repo ${{ inputs.release-train-repository }} --exit-status --interval=3 > /dev/null 2>&1 || watch_exit_code=$?
|
||||
if [[ $watch_exit_code -eq 0 ]]; then
|
||||
echo "Workflow run succeeded."
|
||||
else
|
||||
echo "Workflow run failed."
|
||||
fi
|
||||
exit $watch_exit_code
|
||||
@@ -1,46 +0,0 @@
|
||||
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
|
||||
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
|
||||
|
||||
name: "Release Train – Leave"
|
||||
run-name: "${{ inputs.release-train }} – Leave"
|
||||
"on":
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release-train:
|
||||
description: "Release train"
|
||||
required: true
|
||||
type: "string"
|
||||
release-train-repository:
|
||||
default: "spring-io/release-train"
|
||||
description: "Release train repository"
|
||||
required: true
|
||||
type: "string"
|
||||
permissions:
|
||||
contents: "none"
|
||||
jobs:
|
||||
leave:
|
||||
name: "Leave"
|
||||
runs-on: "ubuntu-latest"
|
||||
steps:
|
||||
- name: "Leave"
|
||||
id: "leave"
|
||||
env:
|
||||
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
|
||||
run: |-
|
||||
run_url=$(
|
||||
gh workflow run leave \
|
||||
--repo ${{ inputs.release-train-repository }} \
|
||||
--ref ${{ inputs.release-train }} \
|
||||
--field release-branch=${{ github.ref_name }} \
|
||||
--field release-repository=${{ github.repository }}
|
||||
)
|
||||
echo "Dispatched workflow run. Waiting for $run_url to complete."
|
||||
run_id=${run_url##*/}
|
||||
watch_exit_code=0
|
||||
gh run watch $run_id --repo ${{ inputs.release-train-repository }} --exit-status --interval=3 > /dev/null 2>&1 || watch_exit_code=$?
|
||||
if [[ $watch_exit_code -eq 0 ]]; then
|
||||
echo "Workflow run succeeded."
|
||||
else
|
||||
echo "Workflow run failed."
|
||||
fi
|
||||
exit $watch_exit_code
|
||||
@@ -1,47 +0,0 @@
|
||||
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
|
||||
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
|
||||
|
||||
name: "Release Train – Ready"
|
||||
run-name: "${{ inputs.release-train }} – Ready"
|
||||
"on":
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release-train:
|
||||
description: "Release train"
|
||||
required: true
|
||||
type: "string"
|
||||
release-train-repository:
|
||||
default: "spring-io/release-train"
|
||||
description: "Release train repository"
|
||||
required: true
|
||||
type: "string"
|
||||
permissions:
|
||||
contents: "none"
|
||||
jobs:
|
||||
ready:
|
||||
name: "Ready"
|
||||
runs-on: "ubuntu-latest"
|
||||
steps:
|
||||
- name: "Ready"
|
||||
id: "ready"
|
||||
env:
|
||||
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
|
||||
run: |-
|
||||
run_url=$(
|
||||
gh workflow run ready \
|
||||
--repo ${{ inputs.release-train-repository }} \
|
||||
--ref ${{ inputs.release-train }} \
|
||||
--field commit-hash=${{ github.sha }} \
|
||||
--field release-branch=${{ github.ref_name }} \
|
||||
--field release-repository=${{ github.repository }}
|
||||
)
|
||||
echo "Dispatched workflow run. Waiting for $run_url to complete."
|
||||
run_id=${run_url##*/}
|
||||
watch_exit_code=0
|
||||
gh run watch $run_id --repo ${{ inputs.release-train-repository }} --exit-status --interval=3 > /dev/null 2>&1 || watch_exit_code=$?
|
||||
if [[ $watch_exit_code -eq 0 ]]; then
|
||||
echo "Workflow run succeeded."
|
||||
else
|
||||
echo "Workflow run failed."
|
||||
fi
|
||||
exit $watch_exit_code
|
||||
@@ -1,34 +0,0 @@
|
||||
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
|
||||
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
|
||||
|
||||
name: "Release Train – Retry"
|
||||
run-name: "${{ inputs.release-train }} – Retry"
|
||||
"on":
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release-train:
|
||||
description: "Release train"
|
||||
required: true
|
||||
type: "string"
|
||||
release-train-repository:
|
||||
default: "spring-io/release-train"
|
||||
description: "Release train repository"
|
||||
required: true
|
||||
type: "string"
|
||||
permissions:
|
||||
contents: "none"
|
||||
jobs:
|
||||
trigger-retry:
|
||||
name: "Trigger Retry"
|
||||
runs-on: "ubuntu-latest"
|
||||
steps:
|
||||
- name: "Trigger Retry"
|
||||
id: "trigger-retry"
|
||||
env:
|
||||
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
|
||||
run: |-
|
||||
gh workflow run retry \
|
||||
--repo ${{ inputs.release-train-repository }} \
|
||||
--ref ${{ inputs.release-train }} \
|
||||
--field release-branch=${{ github.ref_name }} \
|
||||
--field release-repository=${{ github.repository }}
|
||||
@@ -1,84 +0,0 @@
|
||||
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
|
||||
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
|
||||
|
||||
name: "Release Train – Test"
|
||||
run-name: "${{ inputs.callback-ref }} – Test"
|
||||
"on":
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
callback:
|
||||
description: "Repository to which a callback should be made upon completion"
|
||||
required: true
|
||||
type: "string"
|
||||
callback-ref:
|
||||
description: "Ref in the callback repository to which a callback should be made upon completion"
|
||||
required: true
|
||||
type: "string"
|
||||
release-train-maven-repository-url:
|
||||
description: "URL of a Maven repository to be used to resolve artifacts of projects earlier in the train"
|
||||
required: true
|
||||
type: "string"
|
||||
permissions:
|
||||
contents: "read"
|
||||
concurrency:
|
||||
group: "${{ github.workflow }}-${{ github.ref }}"
|
||||
jobs:
|
||||
test-release:
|
||||
name: "Test Release"
|
||||
runs-on: "ubuntu22-2-8"
|
||||
steps:
|
||||
- name: "Prevent Re-runs"
|
||||
id: "prevent-re-runs"
|
||||
run: |-
|
||||
if [ "$GITHUB_RUN_ATTEMPT" -gt 1 ]; then
|
||||
echo "Re-runs are prohibited. Use the 'Release Train – Retry' workflow to retry test failures"
|
||||
exit 1
|
||||
fi
|
||||
- name: "Set up Java"
|
||||
id: "set-up-java"
|
||||
uses: "actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95" # v5.6.0
|
||||
with:
|
||||
distribution: "liberica"
|
||||
java-version: "25"
|
||||
- name: "Check Out Code"
|
||||
id: "check-out-code"
|
||||
uses: "actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0" # v7.0.0
|
||||
- name: "Restore Build System Caches"
|
||||
id: "restore-build-system-caches"
|
||||
uses: "actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9" # v6.1.0
|
||||
with:
|
||||
key: "release-train-${{ inputs.callback-ref }}-${{ github.ref_name }}"
|
||||
path: |-
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
- name: "Test Release"
|
||||
id: "test-release"
|
||||
uses: "./.github/actions/release-train-test"
|
||||
env:
|
||||
COMMERCIAL_RELEASE_REPO_URL: "${{ vars.COMMERCIAL_RELEASE_REPO_URL }}"
|
||||
COMMERCIAL_REPO_PASSWORD: "${{ secrets.COMMERCIAL_ARTIFACTORY_PASSWORD }}"
|
||||
COMMERCIAL_REPO_USERNAME: "${{ secrets.COMMERCIAL_ARTIFACTORY_USERNAME }}"
|
||||
RELEASE_TRAIN_MAVEN_REPOSITORY_PASSWORD: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_MAVEN_REPOSITORY_PASSWORD }}"
|
||||
RELEASE_TRAIN_MAVEN_REPOSITORY_URL: "${{ inputs.release-train-maven-repository-url }}"
|
||||
RELEASE_TRAIN_MAVEN_REPOSITORY_USERNAME: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_MAVEN_REPOSITORY_USERNAME }}"
|
||||
- name: "Send Callback"
|
||||
id: "send-callback"
|
||||
if: "${{ !cancelled() }}"
|
||||
env:
|
||||
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
|
||||
run: |-
|
||||
gh workflow run callback \
|
||||
--repo ${{ inputs.callback }} \
|
||||
--ref ${{ inputs.callback-ref }} \
|
||||
--field commit-hash=${{ steps.check-out-code.outputs.commit }} \
|
||||
--field release-branch=${{ github.ref_name }} \
|
||||
--field release-repository=${{ github.repository }} \
|
||||
--field result=${{ job.status == 'success' && 'tested' || 'test-failed' }} \
|
||||
--field workflow-run-url=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
- name: "Upload Build System Reports"
|
||||
id: "upload-build-system-reports"
|
||||
if: "${{ failure() }}"
|
||||
uses: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" # v7.0.1
|
||||
with:
|
||||
name: "build-system-reports"
|
||||
path: "**/build/reports"
|
||||
@@ -0,0 +1,93 @@
|
||||
name: Release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v7.0.[0-9]+
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
jobs:
|
||||
build-and-stage-release:
|
||||
name: Build and Stage Release
|
||||
if: ${{ github.repository == 'spring-projects/spring-framework' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@v4
|
||||
- name: Build and Publish
|
||||
id: build-and-publish
|
||||
uses: ./.github/actions/build
|
||||
with:
|
||||
develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
|
||||
publish: true
|
||||
- name: Stage Release
|
||||
uses: spring-io/artifactory-deploy-action@dc1913008c0599f0c4b1fdafb6ff3c502b3565ea # v0.0.2
|
||||
with:
|
||||
artifact-properties: |
|
||||
/**/framework-api-*.zip::zip.name=spring-framework,zip.deployed=false
|
||||
/**/framework-api-*-docs.zip::zip.type=docs
|
||||
/**/framework-api-*-schema.zip::zip.type=schema
|
||||
build-name: ${{ format('spring-framework-{0}', steps.build-and-publish.outputs.version)}}
|
||||
folder: 'deployment-repository'
|
||||
password: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
repository: 'libs-staging-local'
|
||||
signing-key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
signing-passphrase: ${{ secrets.GPG_PASSPHRASE }}
|
||||
uri: 'https://repo.spring.io'
|
||||
username: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
outputs:
|
||||
version: ${{ steps.build-and-publish.outputs.version }}
|
||||
verify:
|
||||
name: Verify
|
||||
needs: build-and-stage-release
|
||||
uses: ./.github/workflows/verify.yml
|
||||
secrets:
|
||||
google-chat-webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
|
||||
repository-password: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
repository-username: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
with:
|
||||
staging: true
|
||||
version: ${{ needs.build-and-stage-release.outputs.version }}
|
||||
sync-to-maven-central:
|
||||
name: Sync to Maven Central
|
||||
needs:
|
||||
- build-and-stage-release
|
||||
- verify
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Sync to Maven Central
|
||||
uses: ./.github/actions/sync-to-maven-central
|
||||
with:
|
||||
central-token-password: ${{ secrets.CENTRAL_TOKEN_PASSWORD }}
|
||||
central-token-username: ${{ secrets.CENTRAL_TOKEN_USERNAME }}
|
||||
jfrog-cli-config-token: ${{ secrets.JF_ARTIFACTORY_SPRING }}
|
||||
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@dff217c085c17666e8849ebdbf29c8fe5e3995e6 # v4.5.2
|
||||
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 }}
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
branch: [ '6.2.x', '7.0.x', 'main' ]
|
||||
branch: [ '6.2.x', 'main' ]
|
||||
steps:
|
||||
- uses: spring-io/spring-doc-actions/update-antora-spring-ui@5a57bcc6a0da2a1474136cf29571b277850432bc
|
||||
name: Update
|
||||
|
||||
@@ -12,51 +12,42 @@ on:
|
||||
required: true
|
||||
type: string
|
||||
secrets:
|
||||
commercial-repository-password:
|
||||
description: 'Password for authentication with the commercial repository'
|
||||
required: false
|
||||
commercial-repository-username:
|
||||
description: 'Username for authentication with the commercial repository'
|
||||
required: false
|
||||
google-chat-webhook-url:
|
||||
description: 'Google Chat Webhook URL'
|
||||
required: true
|
||||
opensource-repository-password:
|
||||
description: 'Password for authentication with the open-source repository'
|
||||
repository-password:
|
||||
description: 'Password for authentication with the repository'
|
||||
required: false
|
||||
opensource-repository-username:
|
||||
description: 'Username for authentication with the open-source repository'
|
||||
repository-username:
|
||||
description: 'Username for authentication with the repository'
|
||||
required: false
|
||||
token:
|
||||
description: 'Token to use for authentication with GitHub'
|
||||
required: true
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
verify:
|
||||
name: Verify
|
||||
runs-on: ${{ vars.UBUNTU_SMALL || 'ubuntu-latest' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check Out Release Verification Tests
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: 'v0.0.3'
|
||||
ref: 'v0.0.2'
|
||||
repository: spring-projects/spring-framework-release-verification
|
||||
token: ${{ secrets.token }}
|
||||
- name: Check Out Send Notification Action
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: send-notification
|
||||
sparse-checkout: .github/actions/send-notification
|
||||
- name: Set Up Java
|
||||
uses: actions/setup-java@v5
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'liberica'
|
||||
java-version: 17
|
||||
- name: Set Up Gradle
|
||||
uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0
|
||||
uses: gradle/actions/setup-gradle@ac638b010cf58a27ee6c972d7336334ccaf61c96 # v4.4.1
|
||||
with:
|
||||
cache-provider: basic
|
||||
cache-read-only: false
|
||||
- name: Configure Gradle Properties
|
||||
shell: bash
|
||||
@@ -65,17 +56,15 @@ jobs:
|
||||
echo 'org.gradle.daemon=false' >> $HOME/.gradle/gradle.properties
|
||||
- name: Run Release Verification Tests
|
||||
env:
|
||||
RVT_COMMERCIAL_REPOSITORY_PASSWORD: ${{ secrets.commercial-repository-password }}
|
||||
RVT_COMMERCIAL_REPOSITORY_USERNAME: ${{ secrets.commercial-repository-username }}
|
||||
RVT_OSS_REPOSITORY_PASSWORD: ${{ secrets.opensource-repository-password }}
|
||||
RVT_OSS_REPOSITORY_USERNAME: ${{ secrets.opensource-repository-username }}
|
||||
RVT_RELEASE_TYPE: ${{ vars.COMMERCIAL && 'commercial' || 'oss' }}
|
||||
RVT_OSS_REPOSITORY_PASSWORD: ${{ secrets.repository-password }}
|
||||
RVT_OSS_REPOSITORY_USERNAME: ${{ secrets.repository-username }}
|
||||
RVT_RELEASE_TYPE: oss
|
||||
RVT_STAGING: ${{ inputs.staging }}
|
||||
RVT_VERSION: ${{ inputs.version }}
|
||||
run: ./gradlew spring-framework-release-verification-tests:test
|
||||
- name: Upload Build Reports on Failure
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: build-reports
|
||||
path: '**/build/reports/'
|
||||
|
||||
@@ -9,7 +9,6 @@ ivy-cache
|
||||
argfile*
|
||||
activemq-data/
|
||||
classes/
|
||||
.cursor/
|
||||
|
||||
# Log files
|
||||
jxl.log
|
||||
@@ -52,9 +51,6 @@ atlassian-ide-plugin.xml
|
||||
# VS Code
|
||||
.vscode/
|
||||
|
||||
# Claude artifacts
|
||||
.claude/*
|
||||
|
||||
cached-antora-playbook.yml
|
||||
|
||||
node_modules
|
||||
|
||||
+22
-17
@@ -6,7 +6,7 @@ plugins {
|
||||
id 'com.github.bjornvester.xjc' version '1.8.2' apply false
|
||||
id 'com.gradleup.shadow' version "9.2.2" apply false
|
||||
id 'me.champeau.jmh' version '0.7.2' apply false
|
||||
id 'io.spring.nullability' version '0.0.15' apply false
|
||||
id 'io.spring.nullability' version '0.0.8' apply false
|
||||
}
|
||||
|
||||
ext {
|
||||
@@ -18,8 +18,16 @@ description = "Spring Framework"
|
||||
|
||||
configure(allprojects) { project ->
|
||||
apply plugin: "org.springframework.build.localdev"
|
||||
apply plugin: "org.springframework.build.repositories"
|
||||
group = "org.springframework"
|
||||
repositories {
|
||||
mavenCentral()
|
||||
if (version.contains('-')) {
|
||||
maven { url = "https://repo.spring.io/milestone" }
|
||||
}
|
||||
if (version.endsWith('-SNAPSHOT')) {
|
||||
maven { url = "https://repo.spring.io/snapshot" }
|
||||
}
|
||||
}
|
||||
configurations.all {
|
||||
resolutionStrategy {
|
||||
cacheChangingModulesFor 0, "seconds"
|
||||
@@ -53,27 +61,24 @@ configure([rootProject] + javaProjects) { project ->
|
||||
testImplementation("org.junit.platform:junit-platform-suite")
|
||||
testImplementation("org.mockito:mockito-core")
|
||||
testImplementation("org.mockito:mockito-junit-jupiter")
|
||||
testImplementation("io.mockk:mockk") {
|
||||
exclude group: 'junit', module: 'junit'
|
||||
}
|
||||
testImplementation("io.mockk:mockk")
|
||||
testImplementation("org.assertj:assertj-core")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
testRuntimeOnly("org.apache.logging.log4j:log4j-core")
|
||||
}
|
||||
|
||||
ext.javadocLinks = [
|
||||
"https://docs.oracle.com/en/java/javase/17/docs/api/",
|
||||
//"https://jakarta.ee/specifications/platform/11/apidocs/",
|
||||
"https://docs.hibernate.org/orm/7.2/javadocs/",
|
||||
"https://www.quartz-scheduler.org/api/2.3.0/",
|
||||
"https://hc.apache.org/httpcomponents-client-5.6.x/5.6/httpclient5/apidocs/",
|
||||
"https://projectreactor.io/docs/core/release/api/",
|
||||
"https://projectreactor.io/docs/test/release/api/",
|
||||
"https://junit.org/junit4/javadoc/4.13.2/",
|
||||
"https://docs.junit.org/6.0.3/api/",
|
||||
"https://www.reactive-streams.org/reactive-streams-1.0.4-javadoc/",
|
||||
"https://r2dbc.io/spec/1.0.0.RELEASE/api/",
|
||||
"https://jspecify.dev/docs/api/"
|
||||
"https://docs.oracle.com/en/java/javase/17/docs/api/",
|
||||
//"https://jakarta.ee/specifications/platform/11/apidocs/",
|
||||
"https://docs.hibernate.org/orm/5.6/javadocs/",
|
||||
"https://www.quartz-scheduler.org/api/2.3.0/",
|
||||
"https://hc.apache.org/httpcomponents-client-5.5.x/current/httpclient5/apidocs/",
|
||||
"https://projectreactor.io/docs/test/release/api/",
|
||||
"https://junit.org/junit4/javadoc/4.13.2/",
|
||||
"https://docs.junit.org/6.0.1/api/",
|
||||
"https://www.reactive-streams.org/reactive-streams-1.0.3-javadoc/",
|
||||
"https://r2dbc.io/spec/1.0.0.RELEASE/api/",
|
||||
"https://jspecify.dev/docs/api/"
|
||||
] as String[]
|
||||
}
|
||||
|
||||
|
||||
@@ -50,10 +50,6 @@ gradlePlugin {
|
||||
id = "org.springframework.build.multiReleaseJar"
|
||||
implementationClass = "org.springframework.build.multirelease.MultiReleaseJarPlugin"
|
||||
}
|
||||
repositoriesPlugin {
|
||||
id = "org.springframework.build.repositories"
|
||||
implementationClass = "org.springframework.build.RepositoriesPlugin"
|
||||
}
|
||||
optionalDependenciesPlugin {
|
||||
id = "org.springframework.build.optional-dependencies"
|
||||
implementationClass = "org.springframework.build.optional.OptionalDependenciesPlugin"
|
||||
|
||||
@@ -17,9 +17,6 @@
|
||||
package org.springframework.build;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
@@ -38,7 +35,6 @@ import org.gradle.api.plugins.quality.CheckstylePlugin;
|
||||
* {@link Plugin} that applies conventions for checkstyle.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
public class CheckstyleConventions {
|
||||
|
||||
@@ -52,10 +48,9 @@ public class CheckstyleConventions {
|
||||
configureNoHttpPlugin(project);
|
||||
}
|
||||
project.getPlugins().apply(CheckstylePlugin.class);
|
||||
project.getTasks().withType(Checkstyle.class).forEach(checkstyle -> checkstyle.getMaxHeapSize()
|
||||
.set("checkstyleNohttp".equals(checkstyle.getName()) ? "1536m" : "1g"));
|
||||
project.getTasks().withType(Checkstyle.class).forEach(checkstyle -> checkstyle.getMaxHeapSize().set("1g"));
|
||||
CheckstyleExtension checkstyle = project.getExtensions().getByType(CheckstyleExtension.class);
|
||||
checkstyle.setToolVersion("14.1.0");
|
||||
checkstyle.setToolVersion("12.2.0");
|
||||
checkstyle.getConfigDirectory().set(project.getRootProject().file("src/checkstyle"));
|
||||
String version = SpringJavaFormatPlugin.class.getPackage().getImplementationVersion();
|
||||
DependencySet checkstyleDependencies = project.getConfigurations().getByName("checkstyle").getDependencies();
|
||||
@@ -69,9 +64,7 @@ public class CheckstyleConventions {
|
||||
NoHttpExtension noHttp = project.getExtensions().getByType(NoHttpExtension.class);
|
||||
noHttp.setAllowlistFile(project.file("src/nohttp/allowlist.lines"));
|
||||
noHttp.getSource().exclude("**/test-output/**", "**/.settings/**", "**/.classpath",
|
||||
"**/.project", "**/.gradle/**", "**/node_modules/**", "**/spring-jcl/**", "buildSrc/build/**",
|
||||
".claude/**");
|
||||
excludeGitIgnoredPaths(project, noHttp);
|
||||
"**/.project", "**/.gradle/**", "**/node_modules/**", "**/spring-jcl/**", "buildSrc/build/**");
|
||||
List<String> buildFolders = List.of("bin", "build", "out");
|
||||
project.allprojects(subproject -> {
|
||||
Path rootPath = project.getRootDir().toPath();
|
||||
@@ -83,48 +76,4 @@ public class CheckstyleConventions {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Additionally exclude everything matched by the root {@code .gitignore} file,
|
||||
* so that new ignored paths (build output, IDE metadata, local git worktrees,
|
||||
* etc.) are automatically kept out of nohttp scanning without having to
|
||||
* remember to mirror every {@code .gitignore} change here as well.
|
||||
* <p>Negated patterns (lines starting with {@code !}) are not supported and are
|
||||
* simply skipped, since there is no useful Ant-glob equivalent for them here.
|
||||
*/
|
||||
private static void excludeGitIgnoredPaths(Project project, NoHttpExtension noHttp) {
|
||||
File gitignore = project.getRootProject().file(".gitignore");
|
||||
if (!gitignore.exists()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
for (String line : Files.readAllLines(gitignore.toPath())) {
|
||||
String pattern = line.strip();
|
||||
if (pattern.isEmpty() || pattern.startsWith("#") || pattern.startsWith("!")) {
|
||||
continue;
|
||||
}
|
||||
boolean directoryOnly = pattern.endsWith("/");
|
||||
if (directoryOnly) {
|
||||
pattern = pattern.substring(0, pattern.length() - 1);
|
||||
}
|
||||
// A '/' anywhere but a (now removed) trailing position anchors the
|
||||
// pattern to the repository root; otherwise it matches at any depth.
|
||||
boolean anchored = pattern.contains("/");
|
||||
if (pattern.startsWith("/")) {
|
||||
pattern = pattern.substring(1);
|
||||
}
|
||||
String rootPattern = anchored ? pattern : "**/" + pattern;
|
||||
if (directoryOnly) {
|
||||
noHttp.getSource().exclude(rootPattern + "/**");
|
||||
}
|
||||
else {
|
||||
// The pattern may match either a file or a directory, so exclude both.
|
||||
noHttp.getSource().exclude(rootPattern, rootPattern + "/**");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new UncheckedIOException("Failed to read .gitignore for nohttp exclusions", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-present 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;
|
||||
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
|
||||
/**
|
||||
* Plugin that configures the OSS, commercial and release train repositories in the build.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
public class RepositoriesPlugin implements Plugin <Project> {
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
configureOssRepositories(project);
|
||||
configureCommercialRepositories(project);
|
||||
configureReleaseTrainRepository(project);
|
||||
}
|
||||
|
||||
private void configureOssRepositories(Project project) {
|
||||
project.getRepositories().mavenCentral();
|
||||
if (project.getVersion().toString().contains("-")) {
|
||||
project.getRepositories().maven(repository -> {
|
||||
repository.setName("spring-oss-milestone");
|
||||
repository.setUrl("https://repo.spring.io/milestone/");
|
||||
});
|
||||
}
|
||||
if (project.getVersion().toString().endsWith("-SNAPSHOT")) {
|
||||
project.getRepositories().maven(repository -> {
|
||||
repository.setName("spring-oss-snapshot");
|
||||
repository.setUrl("https://repo.spring.io/snapshot/");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void configureCommercialRepositories(Project project) {
|
||||
String releaseRepositoryUrl = getEnv("COMMERCIAL_RELEASE_REPO_URL");
|
||||
if (releaseRepositoryUrl != null) {
|
||||
project.getRepositories().maven((repository) -> {
|
||||
repository.setName("spring-commercial-release");
|
||||
repository.setUrl(releaseRepositoryUrl);
|
||||
repository.credentials((creds) -> {
|
||||
creds.setUsername(System.getenv("COMMERCIAL_REPO_USERNAME"));
|
||||
creds.setPassword(System.getenv("COMMERCIAL_REPO_PASSWORD"));
|
||||
});
|
||||
});
|
||||
}
|
||||
String snapshotRepositoryUrl = getEnv("COMMERCIAL_SNAPSHOT_REPO_URL");
|
||||
if (snapshotRepositoryUrl != null && project.getVersion().toString().endsWith("-SNAPSHOT")) {
|
||||
project.getRepositories().maven((repository) -> {
|
||||
repository.setName("spring-commercial-snapshot");
|
||||
repository.setUrl(snapshotRepositoryUrl);
|
||||
repository.credentials((creds) -> {
|
||||
creds.setUsername(System.getenv("COMMERCIAL_REPO_USERNAME"));
|
||||
creds.setPassword(System.getenv("COMMERCIAL_REPO_PASSWORD"));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void configureReleaseTrainRepository(Project project) {
|
||||
String releaseTrainRepositoryUrl = getEnv("RELEASE_TRAIN_MAVEN_REPOSITORY_URL");
|
||||
if (releaseTrainRepositoryUrl != null) {
|
||||
project.getRepositories().maven(repository -> {
|
||||
repository.setName("spring-release-train");
|
||||
repository.setUrl(releaseTrainRepositoryUrl);
|
||||
repository.credentials((creds) -> {
|
||||
creds.setUsername(System.getenv("RELEASE_TRAIN_MAVEN_REPOSITORY_USERNAME"));
|
||||
creds.setPassword(System.getenv("RELEASE_TRAIN_MAVEN_REPOSITORY_PASSWORD"));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the environment variable's value, or {@code null} if it is unset or blank.
|
||||
*/
|
||||
private static String getEnv(String name) {
|
||||
String value = System.getenv(name);
|
||||
return (value != null && !value.isBlank()) ? value : null;
|
||||
}
|
||||
}
|
||||
@@ -71,7 +71,7 @@ class TestConventions {
|
||||
"junit.platform.discovery.issue.severity.critical", "INFO"
|
||||
));
|
||||
if (project.hasProperty("testGroups")) {
|
||||
test.systemProperty("testGroups", project.findProperty("testGroups"));
|
||||
test.systemProperty("testGroups", project.getProperties().get("testGroups"));
|
||||
}
|
||||
test.jvmArgs(
|
||||
"--add-opens=java.base/java.lang=ALL-UNNAMED",
|
||||
@@ -82,7 +82,7 @@ class TestConventions {
|
||||
|
||||
private void configureByteBuddyAgent(Project project) {
|
||||
if (project.hasProperty("byteBuddyVersion")) {
|
||||
String byteBuddyVersion = (String) project.findProperty("byteBuddyVersion");
|
||||
String byteBuddyVersion = (String) project.getProperties().get("byteBuddyVersion");
|
||||
Configuration byteBuddyAgentConfig = project.getConfigurations().create("byteBuddyAgentConfig");
|
||||
byteBuddyAgentConfig.setTransitive(false);
|
||||
Dependency byteBuddyAgent = project.getDependencies().create("net.bytebuddy:byte-buddy-agent:" + byteBuddyVersion);
|
||||
|
||||
+3
-1
@@ -49,6 +49,7 @@ import static org.springframework.build.architecture.ArchitectureRules.classesSh
|
||||
import static org.springframework.build.architecture.ArchitectureRules.javaClassesShouldNotImportKotlinAnnotations;
|
||||
import static org.springframework.build.architecture.ArchitectureRules.noClassesShouldCallStringToLowerCaseWithoutLocale;
|
||||
import static org.springframework.build.architecture.ArchitectureRules.noClassesShouldCallStringToUpperCaseWithoutLocale;
|
||||
import static org.springframework.build.architecture.ArchitectureRules.packageInfoShouldBeNullMarked;
|
||||
|
||||
/**
|
||||
* {@link Task} that checks for architecture problems.
|
||||
@@ -63,7 +64,8 @@ public abstract class ArchitectureCheck extends DefaultTask {
|
||||
public ArchitectureCheck() {
|
||||
getOutputDirectory().convention(getProject().getLayout().getBuildDirectory().dir(getName()));
|
||||
getProhibitObjectsRequireNonNull().convention(true);
|
||||
getRules().addAll(classesShouldNotImportForbiddenTypes(),
|
||||
getRules().addAll(packageInfoShouldBeNullMarked(),
|
||||
classesShouldNotImportForbiddenTypes(),
|
||||
javaClassesShouldNotImportKotlinAnnotations(),
|
||||
allPackagesShouldBeFreeOfTangles(),
|
||||
noClassesShouldCallStringToLowerCaseWithoutLocale(),
|
||||
|
||||
@@ -46,6 +46,13 @@ abstract class ArchitectureRules {
|
||||
.because("String.toUpperCase(Locale.ROOT) should be used instead");
|
||||
}
|
||||
|
||||
static ArchRule packageInfoShouldBeNullMarked() {
|
||||
return ArchRuleDefinition.classes()
|
||||
.that().haveSimpleName("package-info")
|
||||
.should().beAnnotatedWith("org.jspecify.annotations.NullMarked")
|
||||
.allowEmptyShould(true);
|
||||
}
|
||||
|
||||
static ArchRule classesShouldNotImportForbiddenTypes() {
|
||||
return ArchRuleDefinition.noClasses()
|
||||
.should().dependOnClassesThat()
|
||||
|
||||
@@ -78,7 +78,7 @@ public class ShadowSource extends DefaultTask {
|
||||
}
|
||||
|
||||
@OutputDirectory
|
||||
public DirectoryProperty getOutputDirectory() {
|
||||
DirectoryProperty getOutputDirectory() {
|
||||
return this.outputDirectory;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,12 +15,11 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
rootProject.ext.moduleProjects.each { moduleProject ->
|
||||
javadoc project(moduleProject.path)
|
||||
moduleProjects.each { moduleProject ->
|
||||
javadoc moduleProject
|
||||
}
|
||||
}
|
||||
|
||||
def springAspectsOutput = project(":spring-aspects").sourceSets.main.output
|
||||
javadoc {
|
||||
javadocTool.set(javaToolchains.javadocToolFor({
|
||||
languageVersion = JavaLanguageVersion.of(25)
|
||||
@@ -49,10 +48,10 @@ javadoc {
|
||||
maxMemory = "1024m"
|
||||
doFirst {
|
||||
classpath += files(
|
||||
// ensure the javadoc process can resolve types compiled from .aj sources
|
||||
springAspectsOutput
|
||||
// ensure the javadoc process can resolve types compiled from .aj sources
|
||||
project(":spring-aspects").sourceSets.main.output
|
||||
)
|
||||
classpath += files(rootProject.ext.moduleProjects.collect { it.sourceSets.main.compileClasspath })
|
||||
classpath += files(moduleProjects.collect { it.sourceSets.main.compileClasspath })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +95,7 @@ tasks.register('schemaZip', Zip) {
|
||||
description = "Builds -${archiveClassifier} archive containing all " +
|
||||
"XSDs for deployment at https://springframework.org/schema."
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
rootProject.ext.moduleProjects.each { module ->
|
||||
moduleProjects.each { module ->
|
||||
def Properties schemas = new Properties();
|
||||
|
||||
module.sourceSets.main.resources.find {
|
||||
|
||||
@@ -8,7 +8,7 @@ group = "org.springframework"
|
||||
dependencies {
|
||||
constraints {
|
||||
parent.moduleProjects.sort { "$it.name" }.each {
|
||||
api project(it.path)
|
||||
api it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,8 @@ content:
|
||||
# https://docs.antora.org/antora/latest/playbook/content-refname-matching/
|
||||
# branches: We include snapshots for main, 6.2.x, and 7.0.x to 9.*.x.
|
||||
branches: ['main', '6.2.x', '{7..9}.+({0..9}).x']
|
||||
# tags: include all releases from 6.2.0 to 9.*.*.
|
||||
tags: ['v6.2.+({0..9})', 'v{7..9}.+({0..9}).+({0..9})?(-{RC,M}*)']
|
||||
# tags: We effectively include all releases from 6.0.9 to 9.*.*.
|
||||
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:
|
||||
@@ -38,4 +38,4 @@ runtime:
|
||||
failure_level: warn
|
||||
ui:
|
||||
bundle:
|
||||
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.26/ui-bundle.zip
|
||||
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.25/ui-bundle.zip
|
||||
|
||||
@@ -31,7 +31,7 @@ asciidoc:
|
||||
spring-org: 'spring-projects'
|
||||
spring-github-org: "https://github.com/{spring-org}"
|
||||
spring-framework-github: "https://github.com/{spring-org}/spring-framework"
|
||||
spring-framework-code: '{spring-framework-github}/tree/7.0.x'
|
||||
spring-framework-code: '{spring-framework-github}/tree/main'
|
||||
spring-framework-issues: '{spring-framework-github}/issues'
|
||||
spring-framework-wiki: '{spring-framework-github}/wiki'
|
||||
# Docs
|
||||
|
||||
@@ -15,15 +15,11 @@ apply from: "${rootDir}/gradle/publications.gradle"
|
||||
antora {
|
||||
options = [clean: true, fetch: !project.gradle.startParameter.offline, stacktrace: true]
|
||||
environment = [
|
||||
'BUILD_REFNAME': 'HEAD',
|
||||
'BUILD_VERSION': project.version,
|
||||
'BUILD_REFNAME': 'HEAD',
|
||||
'BUILD_VERSION': project.version,
|
||||
]
|
||||
}
|
||||
|
||||
node {
|
||||
version = '24.15.0'
|
||||
}
|
||||
|
||||
tasks.named("generateAntoraYml") {
|
||||
asciidocAttributes = project.provider( {
|
||||
return ["spring-version": project.version ]
|
||||
@@ -53,8 +49,8 @@ tasks.withType(KotlinCompilationTask.class).configureEach {
|
||||
javaSources.from = []
|
||||
compilerOptions.jvmTarget = JvmTarget.JVM_17
|
||||
compilerOptions.freeCompilerArgs.addAll(
|
||||
"-Xjdk-release=17", // Needed due to https://youtrack.jetbrains.com/issue/KT-49746
|
||||
"-Xannotation-default-target=param-property" // Upcoming default, see https://youtrack.jetbrains.com/issue/KT-73255
|
||||
"-Xjdk-release=17", // Needed due to https://youtrack.jetbrains.com/issue/KT-49746
|
||||
"-Xannotation-default-target=param-property" // Upcoming default, see https://youtrack.jetbrains.com/issue/KT-73255
|
||||
)
|
||||
}
|
||||
|
||||
@@ -74,7 +70,7 @@ dependencies {
|
||||
implementation("com.github.ben-manes.caffeine:caffeine")
|
||||
implementation("com.mchange:c3p0:0.9.5.5")
|
||||
implementation("com.oracle.database.jdbc:ojdbc11")
|
||||
implementation("io.micrometer:context-propagation")
|
||||
implementation("io.micrometer:context-propagation")
|
||||
implementation("io.projectreactor.netty:reactor-netty-http")
|
||||
implementation("jakarta.jms:jakarta.jms-api")
|
||||
implementation("jakarta.servlet:jakarta.servlet-api")
|
||||
@@ -84,14 +80,12 @@ dependencies {
|
||||
implementation("javax.cache:cache-api")
|
||||
implementation("org.apache.activemq:activemq-ra:6.1.2")
|
||||
implementation("org.apache.commons:commons-dbcp2:2.11.0")
|
||||
implementation("org.apache.groovy:groovy-templates")
|
||||
implementation("org.aspectj:aspectjweaver")
|
||||
implementation("org.assertj:assertj-core")
|
||||
implementation("org.eclipse.jetty.websocket:jetty-websocket-jetty-api")
|
||||
implementation("org.freemarker:freemarker")
|
||||
implementation("org.jetbrains.kotlin:kotlin-stdlib")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
|
||||
implementation("org.junit.jupiter:junit-jupiter-api")
|
||||
implementation("tools.jackson.core:jackson-databind")
|
||||
implementation("tools.jackson.dataformat:jackson-dataformat-xml")
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 158 KiB After Width: | Height: | Size: 63 KiB |
@@ -17,7 +17,6 @@
|
||||
class="st5"
|
||||
id="svg5499"
|
||||
version="1.1"
|
||||
font-family="Helvetica, Arial, sans-serif"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="mvc-splitted-contexts.svg"
|
||||
style="font-size:12px;overflow:visible;color-interpolation-filters:sRGB;fill:none;fill-rule:evenodd;stroke-linecap:square;stroke-miterlimit:3"
|
||||
@@ -37,7 +36,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
id="path9164" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -48,7 +47,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
id="path8836" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -59,7 +58,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
id="path8520" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -70,7 +69,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
id="path8216" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -81,7 +80,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
id="path7924" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -92,7 +91,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
id="path7644" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -103,7 +102,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
id="path7375" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -114,7 +113,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
id="path7119" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -125,7 +124,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
id="path6875" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -136,7 +135,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
id="path6643" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -147,7 +146,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
id="path6423" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -158,7 +157,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
id="path6215" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -169,7 +168,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
id="path6019" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -180,7 +179,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
id="path5835" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -191,7 +190,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
id="path5663" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -202,7 +201,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
id="path5503" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -213,7 +212,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
id="path5355" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -224,7 +223,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
id="path5219" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -235,7 +234,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
id="path5095" /></marker><marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
@@ -246,7 +245,7 @@
|
||||
inkscape:stockid="Arrow2Mend"><path
|
||||
transform="scale(0.6) rotate(180) translate(0,0)"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
id="path4983" /></marker><marker
|
||||
inkscape:stockid="Arrow2Mend"
|
||||
orient="auto"
|
||||
@@ -257,7 +256,7 @@
|
||||
inkscape:isstock="true"
|
||||
inkscape:collect="always"><path
|
||||
id="path7394"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
transform="scale(0.6) rotate(180) translate(0,0)" /></marker><marker
|
||||
inkscape:stockid="Arrow2Lend"
|
||||
@@ -268,7 +267,7 @@
|
||||
style="overflow:visible;"
|
||||
inkscape:isstock="true"><path
|
||||
id="path8123"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
transform="scale(1.1) rotate(180) translate(1,0)" /></marker><marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
@@ -280,7 +279,7 @@
|
||||
inkscape:isstock="true"><path
|
||||
id="path8035"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
style="fill-rule:evenodd;stroke:#333333;stroke-width:1pt;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
transform="scale(0.4) rotate(180) translate(10,0)" /></marker><marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
@@ -291,7 +290,7 @@
|
||||
inkscape:isstock="true"><path
|
||||
id="path7959"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
style="fill-rule:evenodd;stroke:#333333;stroke-width:1pt;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
transform="scale(0.4) rotate(180) translate(10,0)" /></marker><marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
@@ -302,7 +301,7 @@
|
||||
inkscape:isstock="true"><path
|
||||
id="path7376"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
style="fill-rule:evenodd;stroke:#333333;stroke-width:1pt;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
transform="scale(0.4) rotate(180) translate(10,0)" /></marker><marker
|
||||
inkscape:stockid="Arrow2Lend"
|
||||
orient="auto"
|
||||
@@ -312,7 +311,7 @@
|
||||
style="overflow:visible;"
|
||||
inkscape:isstock="true"><path
|
||||
id="path7388"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1"
|
||||
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
transform="scale(1.1) rotate(180) translate(1,0)" /></marker><marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
@@ -324,7 +323,7 @@
|
||||
inkscape:isstock="true"><path
|
||||
id="path7370"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
style="fill-rule:evenodd;stroke:#333333;stroke-width:1pt;stroke-opacity:1;fill:#333333;fill-opacity:1"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
|
||||
transform="scale(0.8) rotate(180) translate(12.5,0)" /></marker></defs><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
@@ -353,9 +352,9 @@
|
||||
v:viewMarkup="false" /><style
|
||||
type="text/css"
|
||||
id="style5501"><![CDATA[
|
||||
.st1 {fill:#969696;stroke:#333333;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24}
|
||||
.st2 {fill:#F8F9FA;stroke:#333333;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24}
|
||||
.st3 {fill:#333333;font-family:Helvetica, Arial, sans-serif;font-size:2.50001em;font-weight:bold}
|
||||
.st1 {fill:#969696;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24}
|
||||
.st2 {fill:#dde2cd;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24}
|
||||
.st3 {fill:#000000;font-family:Arial;font-size:2.50001em;font-weight:bold}
|
||||
.st4 {font-size:0.333333em;font-weight:normal}
|
||||
.st5 {fill:none;fill-rule:evenodd;font-size:12;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
|
||||
]]></style><g
|
||||
@@ -364,22 +363,20 @@
|
||||
v:groupContext="foregroundPage"
|
||||
id="g5503"
|
||||
transform="matrix(0.99998201,0,0,1.0824094,-40.812382,-98.908648)"><rect
|
||||
style="fill:#F8F9FA;fill-opacity:1;stroke:#6db33f;stroke-width:2;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
style="fill:#dde2cd;fill-opacity:1;stroke:#000000;stroke-width:1.53790233;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect6599"
|
||||
width="382.68423"
|
||||
height="146.09897"
|
||||
x="87.884865"
|
||||
y="148.26482"
|
||||
rx="8" /><v:userDefs><v:ud
|
||||
y="148.26482" /><v:userDefs><v:ud
|
||||
v:nameU="SchemeName"
|
||||
v:val="VT4(Default)" /></v:userDefs><rect
|
||||
style="fill:none;fill-opacity:1;stroke:#6db33f;stroke-width:2;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:1.53790233;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect5725"
|
||||
width="464.31128"
|
||||
height="374.11411"
|
||||
x="41.684383"
|
||||
y="112.3262"
|
||||
rx="8" /><title
|
||||
y="112.3262" /><title
|
||||
id="title5505">Page-1</title><v:pageProperties
|
||||
v:drawingScale="0.0393701"
|
||||
v:pageScale="0.0393701"
|
||||
@@ -388,16 +385,15 @@
|
||||
v:shadowOffsetY="-8.50394" /><v:layer
|
||||
v:name="Connector"
|
||||
v:index="0" /><rect
|
||||
style="fill:#F8F9FA;fill-opacity:1;fill-rule:evenodd;stroke:#6db33f;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
style="fill:#dde2cd;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.53790233;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect5715"
|
||||
width="322.8194"
|
||||
height="43.63184"
|
||||
x="119.95335"
|
||||
y="-135.66222"
|
||||
transform="scale(1,-1)"
|
||||
rx="8" /><text
|
||||
transform="scale(1,-1)" /><text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:30.7580471px;line-height:125%;font-family:Helvetica, Arial, sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#333333;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:30.7580471px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="168.843"
|
||||
y="124.32391"
|
||||
id="text5717"
|
||||
@@ -405,12 +401,12 @@
|
||||
transform="scale(1.0403984,0.96117025)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5719"
|
||||
x="172.843"
|
||||
x="168.843"
|
||||
y="124.32391"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:23.06853676px;font-family:Helvetica, Arial, sans-serif;-inkscape-font-specification:sans-serif;fill:#333333">DispatcherServlet</tspan></text>
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:23.06853676px;font-family:sans-serif;-inkscape-font-specification:sans-serif">DispatcherServlet</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Helvetica, Arial, sans-serif;letter-spacing:0px;word-spacing:0px;fill:#333333;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="89.770851"
|
||||
y="181.20923"
|
||||
id="text6589"
|
||||
@@ -418,12 +414,12 @@
|
||||
transform="scale(1.0403984,0.96117025)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan6591"
|
||||
x="106.770851"
|
||||
x="89.770851"
|
||||
y="181.20923"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:23.06853676px;font-family:Helvetica, Arial, sans-serif;-inkscape-font-specification:sans-serif;fill:#333333">Servlet WebApplicationContext</tspan></text>
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:23.06853676px;font-family:sans-serif;-inkscape-font-specification:sans-serif">Servlet WebApplicationContext</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Helvetica, Arial, sans-serif;letter-spacing:0px;word-spacing:0px;fill:#333333;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="260.00443"
|
||||
y="198.41273"
|
||||
id="text6593"
|
||||
@@ -433,39 +429,36 @@
|
||||
id="tspan6595"
|
||||
x="260.00443"
|
||||
y="198.41273"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.53426838px;font-family:Helvetica, Arial, sans-serif;-inkscape-font-specification:sans-serif;text-align:center;text-anchor:middle;fill:#555555">(containing controllers, view resolvers,</tspan><tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.53426838px;font-family:sans-serif;-inkscape-font-specification:sans-serif;text-align:center;text-anchor:middle">(containing controllers, view resolvers,</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
x="260.00443"
|
||||
y="212.83057"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.53426838px;font-family:Helvetica, Arial, sans-serif;-inkscape-font-specification:sans-serif;text-align:center;text-anchor:middle;fill:#555555"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.53426838px;font-family:sans-serif;-inkscape-font-specification:sans-serif;text-align:center;text-anchor:middle"
|
||||
id="tspan6597">and other web-related beans)</tspan></text>
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#6db33f;stroke-width:1.5;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.86203903;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect6620"
|
||||
width="82.040657"
|
||||
height="36.72575"
|
||||
x="114.52653"
|
||||
y="-259.43161"
|
||||
transform="scale(1,-1)"
|
||||
rx="4" /><rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#6db33f;stroke-width:1.5;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
transform="scale(1,-1)" /><rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.89166164;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect6622"
|
||||
width="87.843979"
|
||||
height="36.697304"
|
||||
x="223.39864"
|
||||
y="-287.19809"
|
||||
transform="scale(1,-1)"
|
||||
rx="4" /><rect
|
||||
transform="scale(1,-1)" /><rect
|
||||
transform="scale(1,-1)"
|
||||
y="-264.81918"
|
||||
x="117.92834"
|
||||
height="36.72575"
|
||||
width="82.040657"
|
||||
id="rect6614"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#6db33f;stroke-width:1.5;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
rx="4" /><text
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.86203903;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1" /><text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Helvetica, Arial, sans-serif;letter-spacing:0px;word-spacing:0px;fill:#333333;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="121.24728"
|
||||
y="260.14957"
|
||||
id="text6616"
|
||||
@@ -473,20 +466,20 @@
|
||||
transform="scale(1.0403984,0.96117025)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan6618"
|
||||
x="124.24728"
|
||||
x="121.24728"
|
||||
y="260.14957"
|
||||
style="font-size:11.53426838px;fill:#333333">Controllers</tspan></text>
|
||||
style="font-size:11.53426838px">Controllers</tspan></text>
|
||||
<text
|
||||
transform="scale(1.0403984,0.96117025)"
|
||||
sodipodi:linespacing="125%"
|
||||
id="text6624"
|
||||
y="282.70709"
|
||||
x="219.61203"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Helvetica, Arial, sans-serif;letter-spacing:0px;word-spacing:0px;fill:#333333;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
xml:space="preserve"><tspan
|
||||
style="font-size:11.53426838px;fill:#333333"
|
||||
style="font-size:11.53426838px"
|
||||
y="282.70709"
|
||||
x="221.61203"
|
||||
x="219.61203"
|
||||
id="tspan6626"
|
||||
sodipodi:role="line">ViewResolver</tspan></text>
|
||||
<rect
|
||||
@@ -496,10 +489,9 @@
|
||||
height="36.577778"
|
||||
width="114.4539"
|
||||
id="rect6628"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#6db33f;stroke-width:1.5;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
rx="4" /><text
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.0161339;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1" /><text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Helvetica, Arial, sans-serif;letter-spacing:0px;word-spacing:0px;fill:#333333;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="327.51276"
|
||||
y="255.28464"
|
||||
id="text6630"
|
||||
@@ -507,27 +499,26 @@
|
||||
transform="scale(1.0403984,0.96117025)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan6632"
|
||||
x="332.51276"
|
||||
x="327.51276"
|
||||
y="255.28464"
|
||||
style="font-size:11.53426838px;fill:#333333">HandlerMapping</tspan></text>
|
||||
style="font-size:11.53426838px">HandlerMapping</tspan></text>
|
||||
<rect
|
||||
y="338.69724"
|
||||
x="87.803261"
|
||||
height="121.5683"
|
||||
width="382.84744"
|
||||
id="rect6634"
|
||||
style="fill:#F8F9FA;fill-opacity:1;stroke:#6db33f;stroke-width:2;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
rx="8" /><text
|
||||
style="fill:#dde2cd;fill-opacity:1;stroke:#000000;stroke-width:1.53790233;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1" /><text
|
||||
transform="scale(1.0403984,0.96117025)"
|
||||
sodipodi:linespacing="125%"
|
||||
id="text6636"
|
||||
y="376.61673"
|
||||
x="108.61351"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Helvetica, Arial, sans-serif;letter-spacing:0px;word-spacing:0px;fill:#333333;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
xml:space="preserve"><tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:23.06853676px;font-family:Helvetica, Arial, sans-serif;-inkscape-font-specification:sans-serif;fill:#333333"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:23.06853676px;font-family:sans-serif;-inkscape-font-specification:sans-serif"
|
||||
y="376.61673"
|
||||
x="113.61351"
|
||||
x="108.61351"
|
||||
id="tspan6638"
|
||||
sodipodi:role="line">Root WebApplicationContext</tspan></text>
|
||||
<text
|
||||
@@ -536,10 +527,10 @@
|
||||
id="text6640"
|
||||
y="395.35812"
|
||||
x="260.93863"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Helvetica, Arial, sans-serif;letter-spacing:0px;word-spacing:0px;fill:#333333;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
xml:space="preserve"><tspan
|
||||
id="tspan6644"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.53426838px;font-family:Helvetica, Arial, sans-serif;-inkscape-font-specification:sans-serif;text-align:center;text-anchor:middle;fill:#555555"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.53426838px;font-family:sans-serif;-inkscape-font-specification:sans-serif;text-align:center;text-anchor:middle"
|
||||
y="395.35812"
|
||||
x="260.93863"
|
||||
sodipodi:role="line">(containing middle-tier services, datasources, etc.)</tspan></text>
|
||||
@@ -550,47 +541,43 @@
|
||||
height="36.72575"
|
||||
width="82.040657"
|
||||
id="rect6648"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#6db33f;stroke-width:1.5;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
rx="4" /><rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#6db33f;stroke-width:1.5;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.86203903;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1" /><rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.86203903;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect6650"
|
||||
width="82.040657"
|
||||
height="36.72575"
|
||||
x="167.73116"
|
||||
y="-445.45563"
|
||||
transform="scale(1,-1)"
|
||||
rx="4" /><text
|
||||
transform="scale(1,-1)" /><text
|
||||
transform="scale(1.0403984,0.96117025)"
|
||||
sodipodi:linespacing="125%"
|
||||
id="text6652"
|
||||
y="448.55054"
|
||||
x="175.87148"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Helvetica, Arial, sans-serif;letter-spacing:0px;word-spacing:0px;fill:#333333;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
xml:space="preserve"><tspan
|
||||
style="font-size:11.53426838px;fill:#333333"
|
||||
style="font-size:11.53426838px"
|
||||
y="448.55054"
|
||||
x="178.87148"
|
||||
x="175.87148"
|
||||
id="tspan6654"
|
||||
sodipodi:role="line">Services</tspan></text>
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#6db33f;stroke-width:1.5;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.88435173;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect6656"
|
||||
width="86.393044"
|
||||
height="36.704323"
|
||||
x="306.86328"
|
||||
y="-439.60837"
|
||||
transform="scale(1,-1)"
|
||||
rx="4" /><rect
|
||||
transform="scale(1,-1)" /><rect
|
||||
transform="scale(1,-1)"
|
||||
y="-444.99475"
|
||||
x="310.26624"
|
||||
height="36.701977"
|
||||
width="86.876686"
|
||||
id="rect6658"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#6db33f;stroke-width:1.5;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
|
||||
rx="4" /><text
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.88679528;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1" /><text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Helvetica, Arial, sans-serif;letter-spacing:0px;word-spacing:0px;fill:#333333;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="305.30771"
|
||||
y="448.55054"
|
||||
id="text6660"
|
||||
@@ -598,20 +585,20 @@
|
||||
transform="scale(1.0403984,0.96117025)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan6662"
|
||||
x="308.30771"
|
||||
x="305.30771"
|
||||
y="448.55054"
|
||||
style="font-size:11.53426838px;fill:#333333">Repositories</tspan></text>
|
||||
style="font-size:11.53426838px">Repositories</tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#333333;stroke-width:0.76895118px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.76895118px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 265.33234,295.60379 c 0,42.65169 0,42.65169 0,0 z"
|
||||
id="path7643"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
style="fill:#333333;fill-opacity:1;fill-rule:evenodd;stroke:#333333;stroke-width:3.46028023;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow2Mend)"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.46028023;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow2Mend)"
|
||||
d="m 270.43505,294.70585 c 0,39.4721 0,39.87903 0,39.87903"
|
||||
id="path7645"
|
||||
inkscape:connector-curvature="0" /><text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Helvetica, Arial, sans-serif;letter-spacing:0px;word-spacing:0px;fill:#333333;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="352.55331"
|
||||
y="333.03622"
|
||||
id="text4963"
|
||||
@@ -620,6 +607,6 @@
|
||||
sodipodi:role="line"
|
||||
x="352.55331"
|
||||
y="333.03622"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.53426838px;font-family:Helvetica, Arial, sans-serif;-inkscape-font-specification:sans-serif;text-align:center;text-anchor:middle;fill:#555555"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.53426838px;font-family:sans-serif;-inkscape-font-specification:sans-serif;text-align:center;text-anchor:middle"
|
||||
id="tspan4965">Delegates if no bean found</tspan></text>
|
||||
</g></svg>
|
||||
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 30 KiB |
@@ -198,7 +198,6 @@
|
||||
*** xref:web/webmvc/mvc-uri-building.adoc[]
|
||||
*** xref:web/webmvc/mvc-ann-async.adoc[]
|
||||
*** xref:web/webmvc/mvc-range.adoc[]
|
||||
*** xref:web/webmvc/mvc-data-binding.adoc[]
|
||||
*** xref:web/webmvc-cors.adoc[]
|
||||
*** xref:web/webmvc-versioning.adoc[]
|
||||
*** xref:web/webmvc/mvc-ann-rest-exceptions.adoc[]
|
||||
@@ -262,6 +261,7 @@
|
||||
**** xref:web/websocket/stomp/configuration-performance.adoc[]
|
||||
**** xref:web/websocket/stomp/stats.adoc[]
|
||||
**** xref:web/websocket/stomp/testing.adoc[]
|
||||
** xref:web/integration.adoc[]
|
||||
* xref:web-reactive.adoc[]
|
||||
** xref:web/webflux.adoc[]
|
||||
*** xref:web/webflux/new-framework.adoc[]
|
||||
@@ -296,7 +296,6 @@
|
||||
*** xref:web/webflux-functional.adoc[]
|
||||
*** xref:web/webflux/uri-building.adoc[]
|
||||
*** xref:web/webflux/range.adoc[]
|
||||
*** xref:web/webflux/data-binding.adoc[]
|
||||
*** xref:web/webflux-cors.adoc[]
|
||||
*** xref:web/webflux-versioning.adoc[]
|
||||
*** xref:web/webflux/ann-rest-exceptions.adoc[]
|
||||
@@ -332,10 +331,9 @@
|
||||
*** xref:testing/testcontext-framework/application-events.adoc[]
|
||||
*** xref:testing/testcontext-framework/test-execution-events.adoc[]
|
||||
*** xref:testing/testcontext-framework/ctx-management.adoc[]
|
||||
**** xref:testing/testcontext-framework/ctx-management/javaconfig.adoc[]
|
||||
**** xref:testing/testcontext-framework/ctx-management/xml.adoc[]
|
||||
**** xref:testing/testcontext-framework/ctx-management/groovy.adoc[]
|
||||
**** xref:testing/testcontext-framework/ctx-management/default-config.adoc[]
|
||||
**** xref:testing/testcontext-framework/ctx-management/javaconfig.adoc[]
|
||||
**** xref:testing/testcontext-framework/ctx-management/mixed-config.adoc[]
|
||||
**** xref:testing/testcontext-framework/ctx-management/context-customizers.adoc[]
|
||||
**** xref:testing/testcontext-framework/ctx-management/initializers.adoc[]
|
||||
@@ -346,7 +344,6 @@
|
||||
**** xref:testing/testcontext-framework/ctx-management/web.adoc[]
|
||||
**** xref:testing/testcontext-framework/ctx-management/web-mocks.adoc[]
|
||||
**** xref:testing/testcontext-framework/ctx-management/caching.adoc[]
|
||||
**** xref:testing/testcontext-framework/ctx-management/context-pausing.adoc[]
|
||||
**** xref:testing/testcontext-framework/ctx-management/failure-threshold.adoc[]
|
||||
**** xref:testing/testcontext-framework/ctx-management/hierarchies.adoc[]
|
||||
*** xref:testing/testcontext-framework/fixture-di.adoc[]
|
||||
|
||||
@@ -74,17 +74,6 @@ expressions used in XML bean definitions, `@Value`, etc.
|
||||
| The mode to use when compiling expressions for the
|
||||
xref:core/expressions/evaluation.adoc#expressions-compiler-configuration[Spring Expression Language].
|
||||
|
||||
| `spring.expression.maxBigPowerBits`
|
||||
| The default maximum number of bits permitted in the result of a `BigDecimal` or
|
||||
`BigInteger` power operation within a
|
||||
xref:core/expressions/evaluation.adoc#expressions-parser-configuration[Spring Expression Language]
|
||||
expression.
|
||||
|
||||
| `spring.expression.maxOperations`
|
||||
| The default maximum number of operations permitted during
|
||||
xref:core/expressions/evaluation.adoc#expressions-parser-configuration[Spring Expression Language]
|
||||
expression evaluation.
|
||||
|
||||
| `spring.getenv.ignore`
|
||||
| Instructs Spring to ignore operating system environment variables if a Spring
|
||||
`Environment` property -- for example, a placeholder in a configuration String -- isn't
|
||||
@@ -92,11 +81,6 @@ resolvable otherwise. See
|
||||
{spring-framework-api}++/core/env/AbstractEnvironment.html#IGNORE_GETENV_PROPERTY_NAME++[`AbstractEnvironment`]
|
||||
for details.
|
||||
|
||||
| `spring.http.response.flush.enabled`
|
||||
| Configures the Spring MVC `ServletServerHttpResponse` to allow flushing on the `OutputStream`
|
||||
returned by `ServletServerHttpResponse#getBody()`. By default, such flush calls are ignored and
|
||||
only `ServletServerHttpResponse#flush()` will actually flush the response to the network.
|
||||
|
||||
| `spring.jdbc.getParameterType.ignore`
|
||||
| Instructs Spring to ignore `java.sql.ParameterMetaData.getParameterType` completely.
|
||||
See the note in xref:data-access/jdbc/advanced.adoc#jdbc-batch-list[Batch Operations with a List of Objects].
|
||||
@@ -140,20 +124,11 @@ on a test class. See xref:testing/annotations/integration-junit-jupiter.adoc#int
|
||||
| The maximum size of the context cache in the _Spring TestContext Framework_. See
|
||||
xref:testing/testcontext-framework/ctx-management/caching.adoc[Context Caching].
|
||||
|
||||
| `spring.test.context.cache.pause`
|
||||
| The pause mode for the context cache in the _Spring TestContext Framework_. See
|
||||
xref:testing/testcontext-framework/ctx-management/context-pausing.adoc[Context Pausing].
|
||||
|
||||
| `spring.test.context.failure.threshold`
|
||||
| The failure threshold for errors encountered while attempting to load an `ApplicationContext`
|
||||
in the _Spring TestContext Framework_. See
|
||||
xref:testing/testcontext-framework/ctx-management/failure-threshold.adoc[Context Failure Threshold].
|
||||
|
||||
| `spring.test.extension.context.scope`
|
||||
| The default _extension context scope_ used by the `SpringExtension` in `@Nested` test
|
||||
class hierarchies. See
|
||||
xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-springextensionconfig[`@SpringExtensionConfig`].
|
||||
|
||||
| `spring.test.enclosing.configuration`
|
||||
| The default _enclosing configuration inheritance mode_ to use if
|
||||
`@NestedTestConfiguration` is not present on a test class. See
|
||||
|
||||
@@ -38,7 +38,7 @@ In common with most `FactoryBean` implementations provided with Spring, the
|
||||
`ProxyFactoryBean` class is itself a JavaBean. Its properties are used to:
|
||||
|
||||
* Specify the target you want to proxy.
|
||||
* Specify whether to use CGLIB (described later and see also <<aop-pfb-proxy-types,JDK- and CGLIB-based proxies>>).
|
||||
* Specify whether to use CGLIB (described later and see also xref:core/aop-api/pfb.adoc#aop-pfb-proxy-types[JDK- and CGLIB-based proxies]).
|
||||
|
||||
Some key properties are inherited from `org.springframework.aop.framework.ProxyConfig`
|
||||
(the superclass for all AOP proxy factories in Spring). These key properties include
|
||||
@@ -46,7 +46,7 @@ the following:
|
||||
|
||||
* `proxyTargetClass`: `true` if the target class is to be proxied, rather than the
|
||||
target class's interfaces. If this property value is set to `true`, then CGLIB proxies
|
||||
are created (but see also <<aop-pfb-proxy-types,JDK- and CGLIB-based proxies>>).
|
||||
are created (but see also xref:core/aop-api/pfb.adoc#aop-pfb-proxy-types[JDK- and CGLIB-based proxies]).
|
||||
* `optimize`: Controls whether or not aggressive optimizations are applied to proxies
|
||||
created through CGLIB. You should not blithely use this setting unless you fully
|
||||
understand how the relevant AOP proxy handles optimization. This is currently used
|
||||
@@ -64,7 +64,7 @@ the following:
|
||||
Other properties specific to `ProxyFactoryBean` include the following:
|
||||
|
||||
* `proxyInterfaces`: An array of `String` interface names. If this is not supplied, a CGLIB
|
||||
proxy for the target class is used (but see also <<aop-pfb-proxy-types,JDK- and CGLIB-based proxies>>).
|
||||
proxy for the target class is used (but see also xref:core/aop-api/pfb.adoc#aop-pfb-proxy-types[JDK- and CGLIB-based proxies]).
|
||||
* `interceptorNames`: A `String` array of `Advisor`, interceptor, or other advice names to
|
||||
apply. Ordering is significant, on a first come-first served basis. That is to say
|
||||
that the first interceptor in the list is the first to be able to intercept the
|
||||
@@ -76,7 +76,7 @@ factories. You cannot mention bean references here, since doing so results in th
|
||||
+
|
||||
You can append an interceptor name with an asterisk (`*`). Doing so results in the
|
||||
application of all advisor beans with names that start with the part before the asterisk
|
||||
to be applied. You can find an example of using this feature in <<aop-global-advisors,Using "`Global`" Advisors>>.
|
||||
to be applied. You can find an example of using this feature in xref:core/aop-api/pfb.adoc#aop-global-advisors[Using "`Global`" Advisors].
|
||||
|
||||
* singleton: Whether or not the factory should return a single object, no matter how
|
||||
often the `getObject()` method is called. Several `FactoryBean` implementations offer
|
||||
|
||||
@@ -394,7 +394,7 @@ execution-only semantics. You only need to be aware of this difference if you co
|
||||
`@AspectJ` aspects written for Spring and use `proceed` with arguments with the AspectJ
|
||||
compiler and weaver. There is a way to write such aspects that is 100% compatible across
|
||||
both Spring AOP and AspectJ, and this is discussed in the
|
||||
<<aop-ataspectj-advice-proceeding-with-the-call,following section on advice parameters>>.
|
||||
xref:core/aop/ataspectj/advice.adoc#aop-ataspectj-advice-proceeding-with-the-call[following section on advice parameters].
|
||||
====
|
||||
|
||||
The value returned by the around advice is the return value seen by the caller of the
|
||||
@@ -722,12 +722,12 @@ of determining parameter names, an exception will be thrown.
|
||||
|
||||
`AspectJAnnotationParameterNameDiscoverer` :: Uses parameter names that have been explicitly
|
||||
specified by the user via the `argNames` attribute in the corresponding advice or
|
||||
pointcut annotation. See <<aop-ataspectj-advice-params-names-explicit,Explicit Argument Names>> for details.
|
||||
pointcut annotation. See xref:core/aop/ataspectj/advice.adoc#aop-ataspectj-advice-params-names-explicit[Explicit Argument Names] for details.
|
||||
`KotlinReflectionParameterNameDiscoverer` :: Uses Kotlin reflection APIs to determine
|
||||
parameter names. This discoverer is only used if such APIs are present on the classpath.
|
||||
`StandardReflectionParameterNameDiscoverer` :: Uses the standard `java.lang.reflect.Parameter`
|
||||
API to determine parameter names. Requires that code be compiled with the `-parameters`
|
||||
flag for `javac`. Recommended approach.
|
||||
flag for `javac`. Recommended approach on Java 8+.
|
||||
`AspectJAdviceParameterNameDiscoverer` :: Deduces parameter names from the pointcut
|
||||
expression, `returning`, and `throwing` clauses. See the
|
||||
{spring-framework-api}/aop/aspectj/AspectJAdviceParameterNameDiscoverer.html[javadoc]
|
||||
|
||||
@@ -104,7 +104,7 @@ Note that pointcut definitions are generally matched against any intercepted met
|
||||
If a pointcut is strictly meant to be public-only, even in a CGLIB proxy scenario with
|
||||
potential non-public interactions through proxies, it needs to be defined accordingly.
|
||||
|
||||
If your interception needs to include method calls or even constructors within the target
|
||||
If your interception needs include method calls or even constructors within the target
|
||||
class, consider the use of Spring-driven xref:core/aop/using-aspectj.adoc#aop-aj-ltw[native AspectJ weaving] instead
|
||||
of Spring's proxy-based AOP framework. This constitutes a different mode of AOP usage
|
||||
with different characteristics, so be sure to make yourself familiar with weaving
|
||||
|
||||
@@ -10,7 +10,7 @@ of advice parameters.
|
||||
|
||||
To use the aop namespace tags described in this section, you need to import the
|
||||
`spring-aop` schema, as described in xref:core/appendix/xsd-schemas.adoc[XML Schema-based configuration]
|
||||
. See xref:core/appendix/xsd-schemas.adoc#xsd-schemas-aop[the AOP schema]
|
||||
. See xref:core/appendix/xsd-schemas.adoc#aop[the AOP schema]
|
||||
for how to import the tags in the `aop` namespace.
|
||||
|
||||
Within your Spring configurations, all aspect and advisor elements must be placed within
|
||||
@@ -204,7 +204,7 @@ Before advice runs before a matched method execution. It is declared inside an
|
||||
----
|
||||
|
||||
In the example above, `dataAccessOperation` is the `id` of a _named pointcut_ defined at
|
||||
the top (`<aop:config>`) level (see <<aop-schema-pointcuts,Declaring a Pointcut>>).
|
||||
the top (`<aop:config>`) level (see xref:core/aop/schema.adoc#aop-schema-pointcuts[Declaring a Pointcut]).
|
||||
|
||||
NOTE: As we noted in the discussion of the @AspectJ style, using _named pointcuts_ can
|
||||
significantly improve the readability of your code. See xref:core/aop/ataspectj/pointcuts.adoc#aop-common-pointcuts[Sharing Named Pointcut Definitions] for
|
||||
|
||||
@@ -9,12 +9,12 @@ alone.
|
||||
Spring ships with a small AspectJ aspect library, which is available stand-alone in your
|
||||
distribution as `spring-aspects.jar`. You need to add this to your classpath in order
|
||||
to use the aspects in it.
|
||||
<<aop-atconfigurable,Using AspectJ to Dependency Inject Domain Objects with Spring>>
|
||||
and <<aop-ajlib-other,Other Spring aspects for AspectJ>>
|
||||
xref:core/aop/using-aspectj.adoc#aop-atconfigurable[Using AspectJ to Dependency Inject Domain Objects with Spring]
|
||||
and xref:core/aop/using-aspectj.adoc#aop-ajlib-other[Other Spring aspects for AspectJ]
|
||||
discuss the content of this library and how you can use it.
|
||||
<<aop-aj-configure,Configuring AspectJ Aspects by Using Spring IoC>>
|
||||
xref:core/aop/using-aspectj.adoc#aop-aj-configure[Configuring AspectJ Aspects by Using Spring IoC]
|
||||
discusses how to dependency inject AspectJ aspects that are woven using the AspectJ compiler. Finally,
|
||||
<<aop-aj-ltw,Load-time Weaving with AspectJ in the Spring Framework>>
|
||||
xref:core/aop/using-aspectj.adoc#aop-aj-ltw[Load-time Weaving with AspectJ in the Spring Framework]
|
||||
provides an introduction to load-time weaving for Spring applications that use AspectJ.
|
||||
|
||||
|
||||
@@ -177,7 +177,7 @@ types in AspectJ
|
||||
For this to work, the annotated types must be woven with the AspectJ weaver. You can
|
||||
either use a build-time Ant or Maven task to do this (see, for example, the
|
||||
{aspectj-docs-devguide}/antTasks.html[AspectJ Development
|
||||
Environment Guide]) or load-time weaving (see <<aop-aj-ltw,Load-time Weaving with AspectJ in the Spring Framework>>). The
|
||||
Environment Guide]) or load-time weaving (see xref:core/aop/using-aspectj.adoc#aop-aj-ltw[Load-time Weaving with AspectJ in the Spring Framework]). The
|
||||
`AnnotationBeanConfigurerAspect` itself needs to be configured by Spring (in order to obtain
|
||||
a reference to the bean factory that is to be used to configure new objects). You can define
|
||||
the related configuration as follows:
|
||||
@@ -376,7 +376,7 @@ per-`ClassLoader` basis, which is more fine-grained and which can make more
|
||||
sense in a 'single-JVM-multiple-application' environment (such as is found in a typical
|
||||
application server environment).
|
||||
|
||||
Further, <<aop-aj-ltw-environments,in certain environments>>, this support enables
|
||||
Further, xref:core/aop/using-aspectj.adoc#aop-aj-ltw-environments[in certain environments], this support enables
|
||||
load-time weaving without making any modifications to the application server's launch
|
||||
script that is needed to add `-javaagent:path/to/aspectjweaver.jar` or (as we describe
|
||||
later in this section) `-javaagent:path/to/spring-instrument.jar`. Developers configure
|
||||
@@ -400,7 +400,7 @@ tool to that specific area immediately afterwards.
|
||||
NOTE: The example presented here uses XML configuration. You can also configure and
|
||||
use @AspectJ with xref:core/beans/java.adoc[Java configuration]. Specifically, you can use the
|
||||
`@EnableLoadTimeWeaving` annotation as an alternative to `<context:load-time-weaver/>`
|
||||
(see <<aop-aj-ltw-spring,below>> for details).
|
||||
(see xref:core/aop/using-aspectj.adoc#aop-aj-ltw-spring[below] for details).
|
||||
|
||||
The following example shows the profiling aspect, which is not fancy.
|
||||
It is a time-based profiler that uses the @AspectJ-style of aspect declaration:
|
||||
@@ -719,7 +719,7 @@ for AspectJ LTW:
|
||||
* `spring-aop.jar`
|
||||
* `aspectjweaver.jar`
|
||||
|
||||
If you use the <<aop-aj-ltw-environments-generic,Spring-provided agent to enable instrumentation>>
|
||||
If you use the xref:core/aop/using-aspectj.adoc#aop-aj-ltw-environments-generic[Spring-provided agent to enable instrumentation]
|
||||
, you also need:
|
||||
|
||||
* `spring-instrument.jar`
|
||||
@@ -836,7 +836,7 @@ containers.
|
||||
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 <<aop-using-aspectj,described earlier>>.
|
||||
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`.
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ Applying such optimizations early implies the following restrictions:
|
||||
* As we cannot rely on the instance, make sure that the bean type is as precise as
|
||||
possible.
|
||||
|
||||
TIP: See also the <<aot.bestpractices>> section.
|
||||
TIP: See also the xref:core/aot.adoc#aot.bestpractices[] section.
|
||||
|
||||
When these restrictions are in place, it becomes possible to perform ahead-of-time processing at build time and generate additional assets.
|
||||
A Spring AOT processed application typically generates:
|
||||
@@ -378,7 +378,7 @@ The container also supports creating a bean with {spring-framework-api}++/beans/
|
||||
|
||||
. The custom arguments require dynamic introspection of a matching constructor or factory method.
|
||||
Those arguments cannot be detected by AOT, so the necessary reflection hints will have to be provided manually.
|
||||
. Bypassing the instance supplier means that all other optimizations after creation are skipped as well.
|
||||
. By-passing the instance supplier means that all other optimizations after creation are skipped as well.
|
||||
For instance, autowiring on fields and methods will be skipped as they are handled in the instance supplier.
|
||||
|
||||
Rather than having prototype-scoped beans created with custom arguments, we recommend a manual factory pattern where a bean is responsible for the creation of the instance.
|
||||
|
||||
@@ -19,6 +19,10 @@ its behavior changes.
|
||||
| Initialization of `SmartInitializingSingleton` beans.
|
||||
| `beanName` the name of the bean.
|
||||
|
||||
| `spring.context.annotated-bean-reader.create`
|
||||
| Creation of the `AnnotatedBeanDefinitionReader`.
|
||||
|
|
||||
|
||||
| `spring.context.base-packages.scan`
|
||||
| Scanning of base packages.
|
||||
| `packages` array of base packages for scanning.
|
||||
|
||||
@@ -14,11 +14,11 @@ Spring distribution, you should first read the previous section on xref:core/app
|
||||
|
||||
To create new XML configuration extensions:
|
||||
|
||||
. <<xsd-custom-schema,Author>> an XML schema to describe your custom element(s).
|
||||
. <<xsd-custom-namespacehandler,Code>> a custom `NamespaceHandler` implementation.
|
||||
. <<xsd-custom-parser,Code>> one or more `BeanDefinitionParser` implementations
|
||||
. xref:core/appendix/xml-custom.adoc#core.appendix.xsd-custom-schema[Author] an XML schema to describe your custom element(s).
|
||||
. xref:core/appendix/xml-custom.adoc#core.appendix.xsd-custom-namespacehandler[Code] a custom `NamespaceHandler` implementation.
|
||||
. xref:core/appendix/xml-custom.adoc#core.appendix.xsd-custom-parser[Code] one or more `BeanDefinitionParser` implementations
|
||||
(this is where the real work is done).
|
||||
. <<xsd-custom-registration,Register>> your new artifacts with Spring.
|
||||
. xref:core/appendix/xml-custom.adoc#core.appendix.xsd-custom-registration[Register] your new artifacts with Spring.
|
||||
|
||||
For a unified example, we create an
|
||||
XML extension (a custom XML element) that lets us configure objects of the type
|
||||
@@ -553,7 +553,7 @@ Kotlin::
|
||||
|
||||
This works nicely, but it exposes a lot of Spring plumbing to the end user. What we are
|
||||
going to do is write a custom extension that hides away all of this Spring plumbing.
|
||||
If we stick to <<xsd-custom-introduction,the steps described previously>>, we start off
|
||||
If we stick to xref:core/appendix/xml-custom.adoc#core.appendix.xsd-custom-introduction[the steps described previously], we start off
|
||||
by creating the XSD schema to define the structure of our custom tag, as the following
|
||||
listing shows:
|
||||
|
||||
@@ -580,7 +580,7 @@ listing shows:
|
||||
</xsd:schema>
|
||||
----
|
||||
|
||||
Again following <<xsd-custom-introduction,the process described earlier>>,
|
||||
Again following xref:core/appendix/xml-custom.adoc#core.appendix.xsd-custom-introduction[the process described earlier],
|
||||
we then create a custom `NamespaceHandler`:
|
||||
|
||||
[tabs]
|
||||
|
||||
+11
-11
@@ -151,17 +151,17 @@ injected into a `Set<MovieCatalog>` annotated with `@Qualifier("action")`.
|
||||
[TIP]
|
||||
====
|
||||
Letting qualifier values select against target bean names, within the type-matching
|
||||
candidates, does not require a `@Qualifier` annotation at the injection point. If there
|
||||
is no other resolution indicator (such as a qualifier, a primary marker, or a fallback
|
||||
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 (either by bean name or by associated alias).
|
||||
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 (either by bean name or by associated alias).
|
||||
|
||||
Since version 6.1, this requires the `-parameters` Java compiler flag to be present. As
|
||||
of 6.2, the container applies fast shortcut resolution for bean name matches, bypassing
|
||||
the full type matching algorithm when the parameter name matches the bean name and no
|
||||
type, qualifier, primary, or fallback conditions override the match. It is therefore
|
||||
recommendable for your parameter names to match the target bean names.
|
||||
Since version 6.1, this requires the `-parameters` Java compiler flag to be present.
|
||||
As of 6.2, the container applies fast shortcut resolution for bean name matches,
|
||||
bypassing the full type matching algorithm when the parameter name matches the
|
||||
bean name and no type, qualifier or primary conditions override the match. It is
|
||||
therefore recommendable for your parameter names to match the target bean names.
|
||||
====
|
||||
|
||||
As an alternative for injection by name, consider the JSR-250 `@Resource` annotation
|
||||
@@ -274,7 +274,7 @@ Kotlin::
|
||||
Next, you can provide the information for the candidate bean definitions. You can add
|
||||
`<qualifier/>` tags as sub-elements of the `<bean/>` tag and then specify the `type` and
|
||||
`value` to match your custom qualifier annotations. The type is matched against the
|
||||
fully-qualified class name of the annotation. Alternatively, as a convenience if no risk of
|
||||
fully-qualified class name of the annotation. Alternately, as a convenience if no risk of
|
||||
conflicting names exists, you can use the short class name. The following example
|
||||
demonstrates both approaches:
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ An `@Autowired` annotation on such a constructor is not necessary if the target
|
||||
defines only one constructor. However, if several constructors are available and there is
|
||||
no primary or default constructor, at least one of the constructors must be annotated
|
||||
with `@Autowired` in order to instruct the container which one to use. See the discussion
|
||||
on <<beans-autowired-annotation-constructor-resolution,constructor resolution>>
|
||||
on xref:core/beans/annotation-config/autowired.adoc#beans-autowired-annotation-constructor-resolution[constructor resolution]
|
||||
for details.
|
||||
====
|
||||
|
||||
@@ -309,8 +309,7 @@ set of multiple matches for the specific bean type (as returned by the factory m
|
||||
|
||||
Note that the standard `jakarta.annotation.Priority` annotation is not available at the
|
||||
`@Bean` level, since it cannot be declared on methods. Its semantics can be modeled
|
||||
through `@Order` values in combination with `@Primary` or `@Fallback` on a single bean
|
||||
for each type.
|
||||
through `@Order` values in combination with `@Primary` on a single bean for each type.
|
||||
====
|
||||
|
||||
Even typed `Map` instances can be autowired as long as the expected key type is `String`.
|
||||
|
||||
+1
-2
@@ -27,5 +27,4 @@ with the `CustomAutowireConfigurer`
|
||||
|
||||
When multiple beans qualify as autowire candidates, the determination of a "`primary`" is
|
||||
as follows: If exactly one bean definition among the candidates has a `primary`
|
||||
attribute set to `true`, it is selected. For annotation-based configuration, see
|
||||
xref:core/beans/annotation-config/autowired-primary.adoc[Fine-tuning with `@Primary` or `@Fallback`].
|
||||
attribute set to `true`, it is selected.
|
||||
|
||||
@@ -193,7 +193,7 @@ XML configuration file represents a logical layer or module in your architecture
|
||||
|
||||
You can use the `ClassPathXmlApplicationContext` constructor to load bean definitions from
|
||||
XML fragments. This constructor takes multiple `Resource` locations, as was shown in the
|
||||
<<beans-factory-xml,previous section>>. Alternatively,
|
||||
xref:core/beans/basics.adoc#beans-factory-xml[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:
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ supported as a marker for automatic exception translation in your persistence la
|
||||
|
||||
Many of the annotations provided by Spring can be used as meta-annotations in your
|
||||
own code. A meta-annotation is an annotation that can be applied to another annotation.
|
||||
For example, the `@Service` annotation mentioned <<beans-stereotype-annotations,earlier>>
|
||||
For example, the `@Service` annotation mentioned xref:core/beans/classpath-scanning.adoc#beans-stereotype-annotations[earlier]
|
||||
is meta-annotated with `@Component`, as the following example shows:
|
||||
|
||||
[tabs]
|
||||
@@ -483,7 +483,7 @@ When a component is autodetected as part of the scanning process, its bean name
|
||||
generated by the `BeanNameGenerator` strategy known to that scanner.
|
||||
|
||||
By default, the `AnnotationBeanNameGenerator` is used. For Spring
|
||||
<<beans-stereotype-annotations,stereotype annotations>>,
|
||||
xref:core/beans/classpath-scanning.adoc#beans-stereotype-annotations[stereotype annotations],
|
||||
if you supply a name via the annotation's `value` attribute that name will be used as
|
||||
the name in the corresponding bean definition. This convention also applies when the
|
||||
`@jakarta.inject.Named` annotation is used instead of Spring stereotype annotations.
|
||||
@@ -556,11 +556,18 @@ Kotlin::
|
||||
======
|
||||
|
||||
If you do not want to rely on the default bean-naming strategy, you can provide a custom
|
||||
bean-naming strategy. First, implement either the
|
||||
{spring-framework-api}/beans/factory/support/BeanNameGenerator.html[`BeanNameGenerator`] or
|
||||
{spring-framework-api}/context/annotation/ConfigurationBeanNameGenerator.html[`ConfigurationBeanNameGenerator`]
|
||||
bean-naming strategy. First, implement the
|
||||
{spring-framework-api}/beans/factory/support/BeanNameGenerator.html[`BeanNameGenerator`]
|
||||
interface, and be sure to include a default no-arg constructor. Then, provide the fully
|
||||
qualified class name when configuring the scanner, as the following examples show.
|
||||
qualified class name when configuring the scanner, as the following example annotation
|
||||
and bean definition show.
|
||||
|
||||
TIP: If you run into naming conflicts due to multiple autodetected components having the
|
||||
same non-qualified class name (i.e., classes with identical names but residing in
|
||||
different packages), you may need to configure a `BeanNameGenerator` that defaults to the
|
||||
fully qualified class name for the generated bean name. The
|
||||
`FullyQualifiedAnnotationBeanNameGenerator` located in package
|
||||
`org.springframework.context.annotation` can be used for such purposes.
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@@ -595,27 +602,6 @@ Kotlin::
|
||||
</beans>
|
||||
----
|
||||
|
||||
[TIP]
|
||||
====
|
||||
If you run into naming conflicts due to multiple autodetected components having the same
|
||||
non-qualified class name (for example, classes with identical names but residing in
|
||||
different packages), you can configure a `BeanNameGenerator` that defaults to the
|
||||
fully-qualified class name for the generated bean name. The
|
||||
`FullyQualifiedAnnotationBeanNameGenerator` can be used for such purposes.
|
||||
|
||||
As of Spring Framework 7.0, if you encounter naming conflicts among `@Bean` methods in
|
||||
`@Configuration` classes, you can alternatively configure a
|
||||
`ConfigurationBeanNameGenerator` that generates unique bean names for `@Bean` methods.
|
||||
The `FullyQualifiedConfigurationBeanNameGenerator` can be used to generate
|
||||
fully-qualified default bean names for `@Bean` methods without an explicit `name`
|
||||
attribute — for example, `com.example.MyConfig.myBean` for an `@Bean` method named
|
||||
`myBean()` declared in `@Configuration` class `com.example.MyConfig`.
|
||||
|
||||
The `FullyQualifiedAnnotationBeanNameGenerator` and
|
||||
`FullyQualifiedConfigurationBeanNameGenerator` both reside in the
|
||||
`org.springframework.context.annotation` package.
|
||||
====
|
||||
|
||||
As a general rule, consider specifying the name with the annotation whenever other
|
||||
components may be making explicit references to it. On the other hand, the
|
||||
auto-generated names are adequate whenever the container is responsible for wiring.
|
||||
|
||||
@@ -282,7 +282,7 @@ class and the `ApplicationListener` interface. If a bean that implements the
|
||||
Essentially, this is the standard Observer design pattern.
|
||||
|
||||
TIP: As of Spring 4.2, the event infrastructure has been significantly improved and offers
|
||||
an <<context-functionality-events-annotation,annotation-based model>> as well as the
|
||||
an xref:core/beans/context-introduction.adoc#context-functionality-events-annotation[annotation-based model] as well as the
|
||||
ability to publish any arbitrary event (that is, an object that does not necessarily
|
||||
extend from `ApplicationEvent`). When such an object is published, we wrap it in an
|
||||
event for you.
|
||||
@@ -575,7 +575,7 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
NOTE: Do not define such beans to be lazy as the `ApplicationContext` will honor that and will not register the method to listen to events.
|
||||
NOTE: Do not define such beans to be lazy as the `ApplicationContext` will honour that and will not register the method to listen to events.
|
||||
|
||||
The method signature once again declares the event type to which it listens,
|
||||
but, this time, with a flexible name and without implementing a specific listener interface.
|
||||
@@ -698,7 +698,7 @@ Kotlin::
|
||||
======
|
||||
|
||||
NOTE: This feature is not supported for
|
||||
<<context-functionality-events-async,asynchronous listeners>>.
|
||||
xref:core/beans/context-introduction.adoc#context-functionality-events-async[asynchronous listeners].
|
||||
|
||||
The `handleBlockedListEvent()` method publishes a new `ListUpdateEvent` for every
|
||||
`BlockedListEvent` that it handles. If you need to publish several events, you can return
|
||||
|
||||
@@ -27,10 +27,10 @@ The following table describes these properties:
|
||||
| Property| Explained in...
|
||||
|
||||
| Class
|
||||
| <<beans-factory-class,Instantiating Beans>>
|
||||
| xref:core/beans/definition.adoc#beans-factory-class[Instantiating Beans]
|
||||
|
||||
| Name
|
||||
| <<beans-beanname,Naming Beans>>
|
||||
| xref:core/beans/definition.adoc#beans-beanname[Naming Beans]
|
||||
|
||||
| Scope
|
||||
| xref:core/beans/factory-scopes.adoc[Bean Scopes]
|
||||
@@ -205,7 +205,7 @@ If you use XML-based configuration metadata, you specify the type (or class) of
|
||||
that is to be instantiated in the `class` attribute of the `<bean/>` element. This
|
||||
`class` attribute (which, internally, is a `Class` property on a `BeanDefinition`
|
||||
instance) is usually mandatory. (For exceptions, see
|
||||
<<beans-factory-class-instance-factory-method,Instantiation by Using an Instance Factory Method>>
|
||||
xref:core/beans/definition.adoc#beans-factory-class-instance-factory-method[Instantiation by Using an Instance Factory Method]
|
||||
and xref:core/beans/child-bean-definitions.adoc[Bean Definition Inheritance].)
|
||||
You can use the `Class` property in one of two ways:
|
||||
|
||||
@@ -344,7 +344,7 @@ overloads of the `mock` method. Choose the most specific variant of `mock` possi
|
||||
[[beans-factory-class-instance-factory-method]]
|
||||
=== Instantiation by Using an Instance Factory Method
|
||||
|
||||
Similar to instantiation through a <<beans-factory-class-static-factory-method,static factory method>>
|
||||
Similar to instantiation through a xref:core/beans/definition.adoc#beans-factory-class-static-factory-method[static factory method]
|
||||
, instantiation with an instance factory method invokes a non-static
|
||||
method of an existing bean from the container to create a new bean. To use this
|
||||
mechanism, leave the `class` attribute empty and, in the `factory-bean` attribute,
|
||||
@@ -467,8 +467,8 @@ See xref:core/beans/dependencies/factory-properties-detailed.adoc[Dependencies a
|
||||
|
||||
NOTE: In Spring documentation, "factory bean" refers to a bean that is configured in the
|
||||
Spring container and that creates objects through an
|
||||
<<beans-factory-class-instance-factory-method,instance>> or
|
||||
<<beans-factory-class-static-factory-method,static>> factory method. By contrast,
|
||||
xref:core/beans/definition.adoc#beans-factory-class-instance-factory-method[instance] or
|
||||
xref:core/beans/definition.adoc#beans-factory-class-static-factory-method[static] factory method. By contrast,
|
||||
`FactoryBean` (notice the capitalization) refers to a Spring-specific
|
||||
xref:core/beans/factory-extension.adoc#beans-factory-extension-factorybean[`FactoryBean`] implementation class.
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ In the latter scenario, you have several options:
|
||||
* Abandon autowiring in favor of explicit wiring.
|
||||
* Avoid autowiring for a bean definition by setting its `autowire-candidate` attributes
|
||||
to `false`, as described in the
|
||||
<<beans-factory-autowire-candidate,next section>>.
|
||||
xref:core/beans/dependencies/factory-autowire.adoc#beans-factory-autowire-candidate[next section].
|
||||
* Designate a single bean definition as the primary candidate by setting the
|
||||
`primary` attribute of its `<bean/>` element to `true`.
|
||||
* Implement the more fine-grained control available with annotation-based configuration,
|
||||
|
||||
+2
-2
@@ -17,8 +17,8 @@ to test, particularly when the dependencies are on interfaces or abstract base c
|
||||
which allow for stub or mock implementations to be used in unit tests.
|
||||
|
||||
DI exists in two major variants:
|
||||
<<beans-constructor-injection,Constructor-based dependency injection>>
|
||||
and <<beans-setter-injection,Setter-based dependency injection>>.
|
||||
xref:core/beans/dependencies/factory-collaborators.adoc#beans-constructor-injection[Constructor-based dependency injection]
|
||||
and xref:core/beans/dependencies/factory-collaborators.adoc#beans-setter-injection[Setter-based dependency injection].
|
||||
|
||||
|
||||
[[beans-constructor-injection]]
|
||||
|
||||
+1
-1
@@ -110,7 +110,7 @@ You can read more about the motivation for Method Injection in
|
||||
Lookup method injection is the ability of the container to override methods on
|
||||
container-managed beans and return the lookup result for another named bean in the
|
||||
container. The lookup typically involves a prototype bean, as in the scenario described
|
||||
in <<beans-factory-method-injection,the preceding section>>. The Spring Framework
|
||||
in xref:core/beans/dependencies/factory-method-injection.adoc[the preceding section]. The Spring Framework
|
||||
implements this method injection by using bytecode generation from the CGLIB library to
|
||||
dynamically generate a subclass that overrides the method.
|
||||
|
||||
|
||||
+2
-2
@@ -27,7 +27,7 @@ The following example shows various values being set:
|
||||
</bean>
|
||||
----
|
||||
|
||||
The following example uses the <<beans-p-namespace,p-namespace>> for even more succinct
|
||||
The following example uses the xref:core/beans/dependencies/factory-properties-detailed.adoc#beans-p-namespace[p-namespace] for even more succinct
|
||||
XML configuration:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
@@ -539,7 +539,7 @@ three approaches at the same time.
|
||||
== XML Shortcut with the c-namespace
|
||||
|
||||
Similar to the
|
||||
<<beans-p-namespace,XML Shortcut with the p-namespace>>,
|
||||
xref:core/beans/dependencies/factory-properties-detailed.adoc#beans-p-namespace[XML Shortcut with the p-namespace],
|
||||
the c-namespace, introduced in Spring 3.1, allows inlined attributes for configuring
|
||||
the constructor arguments rather then nested `constructor-arg` elements.
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
The {spring-framework-api}/core/env/Environment.html[`Environment`] interface
|
||||
is an abstraction integrated in the container that models two key
|
||||
aspects of the application environment: <<beans-definition-profiles,profiles>>
|
||||
and <<beans-property-source-abstraction,properties>>.
|
||||
aspects of the application environment: xref:core/beans/environment.adoc#beans-definition-profiles[profiles]
|
||||
and xref:core/beans/environment.adoc#beans-property-source-abstraction[properties].
|
||||
|
||||
A profile is a named, logical group of bean definitions to be registered with the
|
||||
container only if the given profile is active. Beans may be assigned to a profile
|
||||
@@ -473,7 +473,7 @@ Kotlin::
|
||||
In addition, you can also declaratively activate profiles through the
|
||||
`spring.profiles.active` property, which may be specified through system environment
|
||||
variables, JVM system properties, servlet context parameters in `web.xml`, or even as an
|
||||
entry in JNDI (see <<beans-property-source-abstraction,`PropertySource` Abstraction>>). In integration tests, active
|
||||
entry in JNDI (see xref:core/beans/environment.adoc#beans-property-source-abstraction[`PropertySource` Abstraction]). In integration tests, active
|
||||
profiles can be declared by using the `@ActiveProfiles` annotation in the `spring-test`
|
||||
module (see xref:testing/testcontext-framework/ctx-management/env-profiles.adoc[context configuration with environment profiles]
|
||||
).
|
||||
@@ -553,7 +553,7 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
If <<beans-definition-profiles-enable,no profile is active>>, the `dataSource` is
|
||||
If xref:#beans-definition-profiles-enable[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.
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ interface. If you write your own `BeanPostProcessor`, you should consider implem
|
||||
the `Ordered` interface, too. For further details, see the javadoc of the
|
||||
{spring-framework-api}/beans/factory/config/BeanPostProcessor.html[`BeanPostProcessor`]
|
||||
and {spring-framework-api}/core/Ordered.html[`Ordered`] interfaces. See also the note on
|
||||
<<beans-factory-programmatically-registering-beanpostprocessors,programmatic registration of `BeanPostProcessor` instances>>.
|
||||
xref:core/beans/factory-extension.adoc#beans-factory-programmatically-registering-beanpostprocessors[programmatic registration of `BeanPostProcessor` instances].
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
@@ -39,7 +39,7 @@ another container, even if both containers are part of the same hierarchy.
|
||||
|
||||
To change the actual bean definition (that is, the blueprint that defines the bean),
|
||||
you instead need to use a `BeanFactoryPostProcessor`, as described in
|
||||
<<beans-factory-extension-factory-postprocessors,Customizing Configuration Metadata with a `BeanFactoryPostProcessor`>>.
|
||||
xref:core/beans/factory-extension.adoc#beans-factory-extension-factory-postprocessors[Customizing Configuration Metadata with a `BeanFactoryPostProcessor`].
|
||||
====
|
||||
|
||||
The `org.springframework.beans.factory.config.BeanPostProcessor` interface consists of
|
||||
@@ -67,13 +67,6 @@ interface, clearly indicating the post-processor nature of that bean. Otherwise,
|
||||
Since a `BeanPostProcessor` needs to be instantiated early in order to apply to the
|
||||
initialization of other beans in the context, this early type detection is critical.
|
||||
|
||||
Furthermore, when registering a `BeanPostProcessor` via an `@Bean` factory method,
|
||||
declare the method as `static` and ideally with no dependencies. Doing so avoids eager
|
||||
initialization of the configuration class and other beans, which would make them
|
||||
ineligible for full post-processing (such as auto-proxying). See the
|
||||
"BeanPostProcessor-returning `@Bean` methods" section in the
|
||||
{spring-framework-api}/context/annotation/Bean.html[`@Bean`] javadoc for details.
|
||||
|
||||
[[beans-factory-programmatically-registering-beanpostprocessors]]
|
||||
.Programmatically registering `BeanPostProcessor` instances
|
||||
NOTE: While the recommended approach for `BeanPostProcessor` registration is through
|
||||
@@ -87,7 +80,7 @@ of execution. Note also that `BeanPostProcessor` instances registered programmat
|
||||
are always processed before those registered through auto-detection, regardless of any
|
||||
explicit ordering.
|
||||
|
||||
.`BeanPostProcessor` instances and early initialization
|
||||
.`BeanPostProcessor` instances and AOP auto-proxying
|
||||
[NOTE]
|
||||
====
|
||||
Classes that implement the `BeanPostProcessor` interface are special and are treated
|
||||
@@ -97,23 +90,17 @@ of the `ApplicationContext`. Next, all `BeanPostProcessor` instances are registe
|
||||
in a sorted fashion and applied to all further beans in the container. Because AOP
|
||||
auto-proxying is implemented as a `BeanPostProcessor` itself, neither `BeanPostProcessor`
|
||||
instances nor the beans they directly reference are eligible for auto-proxying and,
|
||||
thus, do not have aspects woven into them. More generally, any bean that is instantiated
|
||||
during this early phase is not eligible for full post-processing by all
|
||||
`BeanPostProcessor` instances.
|
||||
thus, do not have aspects woven into them.
|
||||
|
||||
For any such bean, you should see a WARN-level log message similar to the following.
|
||||
For any such bean, you should see an informational log message: `Bean someBean is not
|
||||
eligible for getting processed by all BeanPostProcessor interfaces (for example: not
|
||||
eligible for auto-proxying)`.
|
||||
|
||||
[quote]
|
||||
Bean 'someBean' of type [org.example.SomeType] is not eligible for getting processed by
|
||||
all BeanPostProcessors (for example: not eligible for auto-proxying).
|
||||
|
||||
To minimize the number of beans affected, register a `BeanPostProcessor` with a
|
||||
`static` `@Bean` method that has no dependencies (see the note above). If you have
|
||||
beans wired into your `BeanPostProcessor` by using autowiring or `@Resource` (which
|
||||
may fall back to autowiring), Spring might access unexpected beans when searching
|
||||
for type-matching dependency candidates and, therefore, make them ineligible for
|
||||
auto-proxying or other kinds of bean post-processing. For example, if you have a
|
||||
dependency annotated with `@Resource` where the field or setter name does not
|
||||
If you have beans wired into your `BeanPostProcessor` by using autowiring or
|
||||
`@Resource` (which may fall back to autowiring), Spring might access unexpected beans
|
||||
when searching for type-matching dependency candidates and, therefore, make them
|
||||
ineligible for auto-proxying or other kinds of bean post-processing. For example, if you
|
||||
have a dependency annotated with `@Resource` where the field or setter name does not
|
||||
directly correspond to the declared name of a bean and no name attribute is used,
|
||||
Spring accesses other beans for matching them by type.
|
||||
====
|
||||
@@ -148,7 +135,7 @@ Java::
|
||||
}
|
||||
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) {
|
||||
System.out.println("Bean '" + beanName + "' created : " + bean);
|
||||
System.out.println("Bean '" + beanName + "' created : " + bean.toString());
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
@@ -177,48 +164,7 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
You can register the `InstantiationTracingBeanPostProcessor` with Java configuration
|
||||
by using a `static` `@Bean` method (recommended to avoid eager initialization of the
|
||||
configuration class and other beans):
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
|
||||
----
|
||||
@Configuration
|
||||
public class AppConfig {
|
||||
|
||||
@Bean
|
||||
public static InstantiationTracingBeanPostProcessor instantiationTracingBeanPostProcessor() {
|
||||
return new InstantiationTracingBeanPostProcessor();
|
||||
}
|
||||
|
||||
// ... other bean definitions
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",chomp="-packages"]
|
||||
----
|
||||
@Configuration
|
||||
class AppConfig {
|
||||
|
||||
@Bean
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun instantiationTracingBeanPostProcessor() = InstantiationTracingBeanPostProcessor()
|
||||
}
|
||||
|
||||
// ... other bean definitions
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
Alternatively, the `InstantiationTracingBeanPostProcessor` can be registered via the
|
||||
`bean` element with XML configuration:
|
||||
The following `beans` element uses the `InstantiationTracingBeanPostProcessor`:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@@ -246,7 +192,7 @@ Alternatively, the `InstantiationTracingBeanPostProcessor` can be registered via
|
||||
----
|
||||
|
||||
Notice how the `InstantiationTracingBeanPostProcessor` is merely defined. It does not
|
||||
even have a name, and, because it is a bean, it can be dependency-injected as with any
|
||||
even have a name, and, because it is a bean, it can be dependency-injected as you would any
|
||||
other bean. (The preceding configuration also defines a bean that is backed by a Groovy
|
||||
script.)
|
||||
|
||||
@@ -329,7 +275,7 @@ and {spring-framework-api}/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
|
||||
<<beans-factory-extension-bpp,Customizing Beans by Using a `BeanPostProcessor`>>).
|
||||
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
|
||||
@@ -354,23 +300,6 @@ implement the `BeanFactoryPostProcessor` interface. It uses these beans as bean
|
||||
post-processors, at the appropriate time. You can deploy these post-processor beans as
|
||||
you would any other bean.
|
||||
|
||||
When registering a `BeanFactoryPostProcessor` via an `@Bean` factory method in a
|
||||
`@Configuration` class, declare the method as `static` to avoid lifecycle conflicts
|
||||
with annotation processing (such as `@Autowired`, `@Value`, and `@PostConstruct`) in the
|
||||
configuration class. See the "BeanFactoryPostProcessor-returning `@Bean` methods"
|
||||
section in the {spring-framework-api}/context/annotation/Bean.html[`@Bean`] javadoc
|
||||
for details and an example.
|
||||
|
||||
For any non-static `@Bean` factory method with a `BeanFactoryPostProcessor` return type,
|
||||
you should see an INFO-level log message similar to the following.
|
||||
|
||||
[quote]
|
||||
@Bean method MyConfig.myBfpp is non-static and returns an object assignable to Spring's
|
||||
BeanFactoryPostProcessor interface. This will result in a failure to process annotations
|
||||
such as @Autowired, @Resource, and @PostConstruct within the method's declaring
|
||||
@Configuration class. Add the 'static' modifier to this method to avoid these container
|
||||
lifecycle issues; see @Bean javadoc for complete details.
|
||||
|
||||
NOTE: As with ``BeanPostProcessor``s , you typically do not want to configure
|
||||
``BeanFactoryPostProcessor``s for lazy initialization. If no other bean references a
|
||||
`Bean(Factory)PostProcessor`, that post-processor will not get instantiated at all.
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
The Spring Framework provides a number of interfaces you can use to customize the nature
|
||||
of a bean. This section groups them as follows:
|
||||
|
||||
* <<beans-factory-lifecycle,Lifecycle Callbacks>>
|
||||
* <<beans-factory-aware,`ApplicationContextAware` and `BeanNameAware`>>
|
||||
* <<aware-list,Other `Aware` Interfaces>>
|
||||
* xref:core/beans/factory-nature.adoc#beans-factory-lifecycle[Lifecycle Callbacks]
|
||||
* xref:core/beans/factory-nature.adoc#beans-factory-aware[`ApplicationContextAware` and `BeanNameAware`]
|
||||
* xref:core/beans/factory-nature.adoc#aware-list[Other `Aware` Interfaces]
|
||||
|
||||
|
||||
[[beans-factory-lifecycle]]
|
||||
@@ -252,7 +252,7 @@ of a `<bean>` element a special `(inferred)` value, which instructs Spring to au
|
||||
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
|
||||
<<beans-factory-lifecycle-default-init-destroy-methods,Default Initialization and Destroy Methods>>).
|
||||
xref:core/beans/factory-nature.adoc#beans-factory-lifecycle-default-init-destroy-methods[Default Initialization and Destroy Methods]).
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
@@ -276,7 +276,7 @@ callback method names on every bean. This means that you, as an application deve
|
||||
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 <<beans-factory-lifecycle,described previously>>).
|
||||
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.
|
||||
|
||||
@@ -367,8 +367,8 @@ interacts directly with the raw target bean.
|
||||
|
||||
As of Spring 2.5, you have three options for controlling bean lifecycle behavior:
|
||||
|
||||
* The <<beans-factory-lifecycle-initializingbean,`InitializingBean`>> and
|
||||
<<beans-factory-lifecycle-disposablebean,`DisposableBean`>> callback interfaces
|
||||
* The xref:core/beans/factory-nature.adoc#beans-factory-lifecycle-initializingbean[`InitializingBean`] and
|
||||
xref:core/beans/factory-nature.adoc#beans-factory-lifecycle-disposablebean[`DisposableBean`] callback interfaces
|
||||
* Custom `init()` and `destroy()` methods
|
||||
* The xref:core/beans/annotation-config/postconstruct-and-predestroy-annotations.adoc[`@PostConstruct` and `@PreDestroy` annotations]
|
||||
. You can combine these mechanisms to control a given bean.
|
||||
@@ -378,7 +378,7 @@ configured with a different method name, then each configured method is run in t
|
||||
order listed after this note. However, if the same method name is configured -- for example,
|
||||
`init()` for an initialization method -- for more than one of these lifecycle mechanisms,
|
||||
that method is run once, as explained in the
|
||||
<<beans-factory-lifecycle-default-init-destroy-methods,preceding section>>.
|
||||
xref:core/beans/factory-nature.adoc#beans-factory-lifecycle-default-init-destroy-methods[preceding section].
|
||||
|
||||
Multiple lifecycle mechanisms configured for the same bean, with different
|
||||
initialization methods, are called as follows:
|
||||
@@ -669,7 +669,7 @@ init-method.
|
||||
[[aware-list]]
|
||||
== Other `Aware` Interfaces
|
||||
|
||||
Besides `ApplicationContextAware` and `BeanNameAware` (discussed <<beans-factory-aware,earlier>>),
|
||||
Besides `ApplicationContextAware` and `BeanNameAware` (discussed xref:core/beans/factory-nature.adoc#beans-factory-aware[earlier]),
|
||||
Spring offers a wide range of `Aware` callback interfaces that let beans indicate to the container
|
||||
that they require a certain infrastructure dependency. As a general rule, the name indicates the
|
||||
dependency type. The following table summarizes the most important `Aware` interfaces:
|
||||
@@ -681,7 +681,7 @@ dependency type. The following table summarizes the most important `Aware` inter
|
||||
|
||||
| `ApplicationContextAware`
|
||||
| Declaring `ApplicationContext`.
|
||||
| <<beans-factory-aware,`ApplicationContextAware` and `BeanNameAware`>>
|
||||
| xref:core/beans/factory-nature.adoc#beans-factory-aware[`ApplicationContextAware` and `BeanNameAware`]
|
||||
|
||||
| `ApplicationEventPublisherAware`
|
||||
| Event publisher of the enclosing `ApplicationContext`.
|
||||
@@ -697,7 +697,7 @@ dependency type. The following table summarizes the most important `Aware` inter
|
||||
|
||||
| `BeanNameAware`
|
||||
| Name of the declaring bean.
|
||||
| <<beans-factory-aware,`ApplicationContextAware` and `BeanNameAware`>>
|
||||
| xref:core/beans/factory-nature.adoc#beans-factory-aware[`ApplicationContextAware` and `BeanNameAware`]
|
||||
|
||||
| `LoadTimeWeaverAware`
|
||||
| Defined weaver for processing class definition at load time.
|
||||
|
||||
@@ -14,7 +14,7 @@ through configuration instead of having to bake in the scope of an object at the
|
||||
class level. Beans can be defined to be deployed in one of a number of scopes.
|
||||
The Spring Framework supports six scopes, four of which are available only if
|
||||
you use a web-aware `ApplicationContext`. You can also create
|
||||
<<beans-factory-scopes-custom,a custom scope.>>
|
||||
xref:core/beans/factory-scopes.adoc#beans-factory-scopes-custom[a custom scope.]
|
||||
|
||||
The following table describes the supported scopes:
|
||||
|
||||
@@ -24,23 +24,23 @@ The following table describes the supported scopes:
|
||||
|===
|
||||
| Scope| Description
|
||||
|
||||
| <<beans-factory-scopes-singleton,singleton>>
|
||||
| xref:core/beans/factory-scopes.adoc#beans-factory-scopes-singleton[singleton]
|
||||
| (Default) Scopes a single bean definition to a single object instance for each Spring IoC
|
||||
container.
|
||||
|
||||
| <<beans-factory-scopes-prototype,prototype>>
|
||||
| xref:core/beans/factory-scopes.adoc#beans-factory-scopes-prototype[prototype]
|
||||
| Scopes a single bean definition to any number of object instances.
|
||||
|
||||
| <<beans-factory-scopes-request,request>>
|
||||
| xref:core/beans/factory-scopes.adoc#beans-factory-scopes-request[request]
|
||||
| Scopes a single bean definition to the lifecycle of a single HTTP request. That is,
|
||||
each HTTP request has its own instance of a bean created off the back of a single bean
|
||||
definition. Only valid in the context of a web-aware Spring `ApplicationContext`.
|
||||
|
||||
| <<beans-factory-scopes-session,session>>
|
||||
| xref:core/beans/factory-scopes.adoc#beans-factory-scopes-session[session]
|
||||
| Scopes a single bean definition to the lifecycle of an HTTP `Session`. Only valid in
|
||||
the context of a web-aware Spring `ApplicationContext`.
|
||||
|
||||
| <<beans-factory-scopes-application,application>>
|
||||
| xref:core/beans/factory-scopes.adoc#beans-factory-scopes-application[application]
|
||||
| Scopes a single bean definition to the lifecycle of a `ServletContext`. Only valid in
|
||||
the context of a web-aware Spring `ApplicationContext`.
|
||||
|
||||
@@ -53,7 +53,7 @@ NOTE: A thread scope is available but is not registered by default. For more inf
|
||||
see the documentation for
|
||||
{spring-framework-api}/context/support/SimpleThreadScope.html[`SimpleThreadScope`].
|
||||
For instructions on how to register this or any other custom scope, see
|
||||
<<beans-factory-scopes-custom-using,Using a Custom Scope>>.
|
||||
xref:core/beans/factory-scopes.adoc#beans-factory-scopes-custom-using[Using a Custom Scope].
|
||||
|
||||
|
||||
[[beans-factory-scopes-singleton]]
|
||||
@@ -432,7 +432,7 @@ understand the "`why`" as well as the "`how`" behind it:
|
||||
|
||||
To create such a proxy, you insert a child `<aop:scoped-proxy/>` element into a
|
||||
scoped bean definition (see
|
||||
<<beans-factory-scopes-other-injection-proxies,Choosing the Type of Proxy to Create>>
|
||||
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
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
`@Bean` is a method-level annotation and a direct analog of the XML `<bean/>` element.
|
||||
The annotation supports some of the attributes offered by `<bean/>`, such as:
|
||||
|
||||
* xref:core/beans/definition.adoc#beans-beanname[name]
|
||||
* xref:core/beans/factory-nature.adoc#beans-factory-lifecycle-initializingbean[init-method]
|
||||
* xref:core/beans/factory-nature.adoc#beans-factory-lifecycle-disposablebean[destroy-method]
|
||||
* xref:core/beans/dependencies/factory-autowire.adoc[autowiring]
|
||||
* `name`.
|
||||
|
||||
You can use the `@Bean` annotation in a `@Configuration`-annotated or in a
|
||||
`@Component`-annotated class.
|
||||
@@ -17,10 +17,9 @@ You can use the `@Bean` annotation in a `@Configuration`-annotated or in a
|
||||
== Declaring a Bean
|
||||
|
||||
To declare a bean, you can annotate a method with the `@Bean` annotation. You use this
|
||||
method to register a bean definition within an `ApplicationContext` of the type specified
|
||||
by the method's return type. By default, the bean name is the same as the method name
|
||||
(unless a different <<beans-java-customizing-bean-naming,bean name generator>> is
|
||||
configured). The following example shows a `@Bean` method declaration:
|
||||
method to register a bean definition within an `ApplicationContext` of the type
|
||||
specified as the method's return value. By default, the bean name is the same as
|
||||
the method name. The following example shows a `@Bean` method declaration:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@@ -127,7 +126,7 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
However, this limits the visibility for advanced type prediction to the specified
|
||||
However, this limits the visibility for advance type prediction to the specified
|
||||
interface type (`TransferService`). Then, with the full type (`TransferServiceImpl`)
|
||||
known to the container only once the affected singleton bean has been instantiated.
|
||||
Non-lazy singleton beans get instantiated according to their declaration order,
|
||||
@@ -474,15 +473,8 @@ Kotlin::
|
||||
== Customizing Bean Naming
|
||||
|
||||
By default, configuration classes use a `@Bean` method's name as the name of the
|
||||
resulting bean. However, as of Spring Framework 7.0, you can change this default strategy
|
||||
by configuring a custom
|
||||
{spring-framework-api}/context/annotation/ConfigurationBeanNameGenerator.html[`ConfigurationBeanNameGenerator`]
|
||||
when bootstrapping the context or configuring component scanning. For example,
|
||||
{spring-framework-api}/context/annotation/FullyQualifiedConfigurationBeanNameGenerator.html[`FullyQualifiedConfigurationBeanNameGenerator`]
|
||||
can be used to generate fully-qualified default bean names for `@Bean` methods without an
|
||||
explicit `name` attribute. For individual `@Bean` methods, the default or
|
||||
generator-derived name can be overridden with the `name` attribute, as the following
|
||||
example shows:
|
||||
resulting bean. This functionality can be overridden, however, with the `name` attribute,
|
||||
as the following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@@ -513,7 +505,6 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
NOTE: `@Bean("myThing")` is equivalent to `@Bean(name = "myThing")`.
|
||||
|
||||
[[beans-java-bean-aliasing]]
|
||||
== Bean Aliasing
|
||||
|
||||
-13
@@ -610,19 +610,6 @@ Kotlin::
|
||||
See the {spring-framework-api}/context/annotation/Conditional.html[`@Conditional`]
|
||||
javadoc for more detail.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
A `@Conditional` annotation declared on an enclosing `@Configuration` class is only
|
||||
applied to the registration of a nested `@Configuration` class if the nested class is
|
||||
reached through the parser's recursion from its enclosing class, or via `@Import`. If a
|
||||
nested class is discovered independently of its enclosing class — for example, via
|
||||
`@ComponentScan` or by directly registering it against the application context — it is
|
||||
processed using only its own `@Conditional` annotations. Thus, if you wish to ensure that
|
||||
the same `@Conditional` annotations apply in such scenarios, you must redeclare the
|
||||
relevant annotations on the nested class, or extract them into a composed annotation
|
||||
which you apply to both the enclosing class and the nested class.
|
||||
====
|
||||
|
||||
|
||||
[[beans-java-combining]]
|
||||
== Combining Java and XML Configuration
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
[[beans-standard-annotations]]
|
||||
= Using JSR-330 Standard Annotations
|
||||
= Using JSR 330 Standard Annotations
|
||||
|
||||
Spring offers support for JSR-330 standard _Dependency Injection_ annotations which are
|
||||
available in the `jakarta.inject` package. These annotations may optionally be used as
|
||||
alternatives to Spring annotations.
|
||||
|
||||
To use them, you need to have the relevant jar in your classpath. For example, the
|
||||
`jakarta.inject` artifact is available in the standard Maven repository
|
||||
(`https://repo.maven.apache.org/maven2/jakarta/inject/jakarta.inject-api/2.0.0/`),
|
||||
Spring offers support for JSR-330 standard annotations (Dependency Injection). Those
|
||||
annotations are scanned in the same way as the Spring annotations. To use them, you need
|
||||
to have the relevant jars in your classpath.
|
||||
|
||||
[NOTE]
|
||||
=====
|
||||
If you use Maven, you can add the following dependency to your `pom.xml` file.
|
||||
If you use Maven, the `jakarta.inject` artifact is available in the standard Maven
|
||||
repository (
|
||||
https://repo.maven.apache.org/maven2/jakarta/inject/jakarta.inject-api/2.0.0/[https://repo.maven.apache.org/maven2/jakarta/inject/jakarta.inject-api/2.0.0/]).
|
||||
You can add the following dependency to your file pom.xml:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@@ -27,14 +26,13 @@ If you use Maven, you can add the following dependency to your `pom.xml` file.
|
||||
[[beans-inject-named]]
|
||||
== Dependency Injection with `@Inject` and `@Named`
|
||||
|
||||
Instead of using `@Autowired` for dependency injection, you may optionally choose to use
|
||||
`@jakarta.inject.Inject` as follows.
|
||||
Instead of `@Autowired`, you can use `@jakarta.inject.Inject` as follows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",fold="-imports"]
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
import jakarta.inject.Inject;
|
||||
|
||||
@@ -56,7 +54,7 @@ Java::
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",fold="-imports"]
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
import jakarta.inject.Inject
|
||||
|
||||
@@ -74,19 +72,17 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
As with `@Autowired`, you can use `@Inject` at the field level, method level, and
|
||||
constructor-argument level.
|
||||
|
||||
Furthermore, as an alternative to Spring's `ObjectProvider` mechanism, you may choose to
|
||||
declare your injection point as a `jakarta.inject.Provider`, allowing for on-demand
|
||||
access to beans of shorter scopes or lazy access to other beans through a
|
||||
`Provider.get()` call. The following example offers a variant of the preceding example.
|
||||
As with `@Autowired`, you can use `@Inject` at the field level, method level
|
||||
and constructor-argument level. Furthermore, you may declare your injection point as a
|
||||
`Provider`, allowing for on-demand access to beans of shorter scopes or lazy access to
|
||||
other beans through a `Provider.get()` call. The following example offers a variant of the
|
||||
preceding example:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",fold="-imports"]
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Provider;
|
||||
@@ -109,10 +105,9 @@ Java::
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",fold="-imports"]
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
import jakarta.inject.Inject
|
||||
import jakarta.inject.Provider
|
||||
|
||||
class SimpleMovieLister {
|
||||
|
||||
@@ -128,15 +123,14 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
If you would like to use a qualified name for the dependency that should be injected, you
|
||||
may choose to use the `@Named` annotation as an alternative to Spring's `@Qualifier`
|
||||
support, as the following example shows.
|
||||
If you would like to use a qualified name for the dependency that should be injected,
|
||||
you should use the `@Named` annotation, as the following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",fold="-imports"]
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
@@ -156,7 +150,7 @@ Java::
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",fold="-imports"]
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
import jakarta.inject.Inject
|
||||
import jakarta.inject.Named
|
||||
@@ -176,15 +170,12 @@ Kotlin::
|
||||
======
|
||||
|
||||
As with `@Autowired`, `@Inject` can also be used with `java.util.Optional` or
|
||||
`@Nullable`. This is even more applicable here, since `@Inject` does not have a
|
||||
`required` attribute. The following examples show how to use `@Inject` with `Optional`,
|
||||
`@Nullable`, and Kotlin's built-in support for nullable types.
|
||||
`@Nullable`. This is even more applicable here, since `@Inject` does not have
|
||||
a `required` attribute. The following pair of examples show how to use `@Inject` and
|
||||
`@Nullable`:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes",fold="-imports"]
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.Optional;
|
||||
|
||||
public class SimpleMovieLister {
|
||||
|
||||
@Inject
|
||||
@@ -198,11 +189,8 @@ As with `@Autowired`, `@Inject` can also be used with `java.util.Optional` or
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",fold="-imports"]
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
import jakarta.inject.Inject;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public class SimpleMovieLister {
|
||||
|
||||
@Inject
|
||||
@@ -214,10 +202,8 @@ Java::
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",fold="-imports"]
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
import jakarta.inject.Inject
|
||||
|
||||
class SimpleMovieLister {
|
||||
|
||||
@Inject
|
||||
@@ -228,21 +214,21 @@ Kotlin::
|
||||
|
||||
|
||||
[[beans-named]]
|
||||
== `@Named`: Standard Equivalent to the `@Component` Annotation
|
||||
== `@Named` and `@ManagedBean`: Standard Equivalents to the `@Component` Annotation
|
||||
|
||||
Instead of `@Component` or other Spring stereotype annotations, you may optionally choose
|
||||
to use `@jakarta.inject.Named`, as the following example shows.
|
||||
Instead of `@Component`, you can use `@jakarta.inject.Named` or `jakarta.annotation.ManagedBean`,
|
||||
as the following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",fold="-imports"]
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
|
||||
@Named("movieListener")
|
||||
@Named("movieListener") // @ManagedBean("movieListener") could be used as well
|
||||
public class SimpleMovieLister {
|
||||
|
||||
private MovieFinder movieFinder;
|
||||
@@ -258,12 +244,12 @@ Java::
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",fold="-imports"]
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
import jakarta.inject.Inject
|
||||
import jakarta.inject.Named
|
||||
|
||||
@Named("movieListener")
|
||||
@Named("movieListener") // @ManagedBean("movieListener") could be used as well
|
||||
class SimpleMovieLister {
|
||||
|
||||
@Inject
|
||||
@@ -274,15 +260,14 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
It is very common to use `@Component` or other Spring stereotype annotations without
|
||||
specifying an explicit name for the component, and `@Named` can be used in a similar
|
||||
fashion, as the following example shows.
|
||||
It is very common to use `@Component` without specifying a name for the component.
|
||||
`@Named` can be used in a similar fashion, as the following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",fold="-imports"]
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
@@ -303,7 +288,7 @@ Java::
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",fold="-imports"]
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
import jakarta.inject.Inject
|
||||
import jakarta.inject.Named
|
||||
@@ -319,14 +304,14 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
When you use `@Named`, you can use component scanning in the exact same way as when you
|
||||
use Spring annotations, as the following example shows.
|
||||
When you use `@Named` or `@ManagedBean`, you can use component scanning in the
|
||||
exact same way as when you use Spring annotations, as the following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",fold="-imports"]
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Configuration
|
||||
@ComponentScan(basePackages = "org.example")
|
||||
@@ -337,7 +322,7 @@ Java::
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",fold="-imports"]
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Configuration
|
||||
@ComponentScan(basePackages = ["org.example"])
|
||||
@@ -347,106 +332,55 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
NOTE: In contrast to `@Component`, the JSR-330 `@Named` annotation is not composable. You
|
||||
should use Spring's stereotype model for building custom component annotations.
|
||||
|
||||
[TIP]
|
||||
====
|
||||
If you work with legacy systems that still use `@javax.inject.Named` or
|
||||
`@javax.annotation.ManagedBean` for components (note the `javax` package namespace), you
|
||||
can explicitly configure component scanning to include those annotation types, as shown
|
||||
in the following example.
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",fold="-imports"]
|
||||
----
|
||||
@Configuration
|
||||
@ComponentScan(
|
||||
basePackages = "org.example",
|
||||
includeFilters = @Filter({
|
||||
javax.inject.Named.class,
|
||||
javax.annotation.ManagedBean.class
|
||||
})
|
||||
)
|
||||
public class AppConfig {
|
||||
// ...
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",fold="-imports"]
|
||||
----
|
||||
@Configuration
|
||||
@ComponentScan(
|
||||
basePackages = ["org.example"],
|
||||
includeFilters = [Filter([
|
||||
javax.inject.Named::class,
|
||||
javax.annotation.ManagedBean::class
|
||||
])]
|
||||
)
|
||||
class AppConfig {
|
||||
// ...
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
In addition, if you would like for the `value` attributes in `@javax.inject.Named` and
|
||||
`@javax.annotation.ManagedBean` to be used as component names, you need to override the
|
||||
`isStereotypeWithNameValue(...)` method in `AnnotationBeanNameGenerator` to add explicit
|
||||
support for `javax.annotation.ManagedBean` and `javax.inject.Named` and register your
|
||||
custom `AnnotationBeanNameGenerator` via the `nameGenerator` attribute in
|
||||
`@ComponentScan`.
|
||||
====
|
||||
NOTE: In contrast to `@Component`, the JSR-330 `@Named` and the JSR-250 `@ManagedBean`
|
||||
annotations are not composable. You should use Spring's stereotype model for building
|
||||
custom component annotations.
|
||||
|
||||
|
||||
[[beans-standard-annotations-limitations]]
|
||||
== Limitations of JSR-330 Standard Annotations
|
||||
|
||||
When you work with JSR-330 standard annotations, you should know that some significant
|
||||
features are not available, as the following table shows.
|
||||
When you work with standard annotations, you should know that some significant
|
||||
features are not available, as the following table shows:
|
||||
|
||||
[[annotations-comparison]]
|
||||
.Spring component model versus JSR-330 variants
|
||||
.Spring component model elements versus JSR-330 variants
|
||||
|===
|
||||
| Spring | JSR-330 | JSR-330 restrictions / comments
|
||||
| Spring| jakarta.inject.*| jakarta.inject restrictions / comments
|
||||
|
||||
| `@Autowired`
|
||||
| `@Inject`
|
||||
| `@Inject` has no `required` attribute. Can be used with Java's `Optional` instead.
|
||||
| @Autowired
|
||||
| @Inject
|
||||
| `@Inject` has no 'required' attribute. Can be used with Java 8's `Optional` instead.
|
||||
|
||||
| `@Component`
|
||||
| `@Named`
|
||||
| @Component
|
||||
| @Named / @ManagedBean
|
||||
| JSR-330 does not provide a composable model, only a way to identify named components.
|
||||
|
||||
| `@Scope("singleton")`
|
||||
| `@Singleton`
|
||||
| @Scope("singleton")
|
||||
| @Singleton
|
||||
| The JSR-330 default scope is like Spring's `prototype`. However, in order to keep it
|
||||
consistent with Spring's general defaults, a JSR-330 bean declared in the Spring
|
||||
container is a `singleton` by default. In order to use a scope other than `singleton`,
|
||||
you should use Spring's `@Scope` annotation. `jakarta.inject` also provides a
|
||||
`jakarta.inject.Scope` annotation; however, this one is only intended to be used
|
||||
`jakarta.inject.Scope` annotation: however, this one is only intended to be used
|
||||
for creating custom annotations.
|
||||
|
||||
| `@Qualifier`
|
||||
| `@Qualifier` / `@Named`
|
||||
| @Qualifier
|
||||
| @Qualifier / @Named
|
||||
| `jakarta.inject.Qualifier` is just a meta-annotation for building custom qualifiers.
|
||||
Concrete `String` qualifiers (like Spring's `@Qualifier` with a value) can be associated
|
||||
through `jakarta.inject.Named`.
|
||||
|
||||
| `@Value`
|
||||
| @Value
|
||||
| -
|
||||
| no equivalent
|
||||
|
||||
| `@Lazy`
|
||||
| @Lazy
|
||||
| -
|
||||
| no equivalent
|
||||
|
||||
| `ObjectFactory`
|
||||
| `Provider`
|
||||
| ObjectFactory
|
||||
| Provider
|
||||
| `jakarta.inject.Provider` is a direct alternative to Spring's `ObjectFactory`,
|
||||
only with a shorter `get()` method name. It can also be used in combination with
|
||||
Spring's `@Autowired` or with non-annotated constructors and setter methods.
|
||||
|
||||
@@ -8,10 +8,10 @@ Jetty uses pooled byte buffers with a callback to be released, and so on.
|
||||
The `spring-core` module provides a set of abstractions to work with various byte buffer
|
||||
APIs as follows:
|
||||
|
||||
* <<databuffers-factory,`DataBufferFactory`>> abstracts the creation of a data buffer.
|
||||
* <<databuffers-buffer,`DataBuffer`>> represents a byte buffer, which may be
|
||||
<<databuffers-buffer-pooled,pooled>>.
|
||||
* <<databuffers-utils,`DataBufferUtils`>> offers utility methods for data buffers.
|
||||
* xref:core/databuffer-codec.adoc#databuffers-factory[`DataBufferFactory`] abstracts the creation of a data buffer.
|
||||
* xref:core/databuffer-codec.adoc#databuffers-buffer[`DataBuffer`] represents a byte buffer, which may be
|
||||
xref:core/databuffer-codec.adoc#databuffers-buffer-pooled[pooled].
|
||||
* xref:core/databuffer-codec.adoc#databuffers-utils[`DataBufferUtils`] offers utility methods for data buffers.
|
||||
* <<Codecs>> decode or encode data buffer streams into higher level objects.
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ Below is a partial list of benefits:
|
||||
* Read and write with independent positions, i.e. not requiring a call to `flip()` to
|
||||
alternate between read and write.
|
||||
* Capacity expanded on demand as with `java.lang.StringBuilder`.
|
||||
* Pooled buffers and reference counting via <<databuffers-buffer-pooled,`PooledDataBuffer`>>.
|
||||
* Pooled buffers and reference counting via xref:core/databuffer-codec.adoc#databuffers-buffer-pooled[`PooledDataBuffer`].
|
||||
* View a buffer as `java.nio.ByteBuffer`, `InputStream`, or `OutputStream`.
|
||||
* Determine the index, or the last index, for a given byte.
|
||||
|
||||
@@ -101,7 +101,7 @@ xref:web/webflux/reactive-spring.adoc#webflux-codecs[Codecs] in the WebFlux sect
|
||||
== Using `DataBuffer`
|
||||
|
||||
When working with data buffers, special care must be taken to ensure buffers are released
|
||||
since they may be <<databuffers-buffer-pooled,pooled>>. We'll use codecs to illustrate
|
||||
since they may be xref:core/databuffer-codec.adoc#databuffers-buffer-pooled[pooled]. We'll use codecs to illustrate
|
||||
how that works but the concepts apply more generally. Let's see what codecs must do
|
||||
internally to manage data buffers.
|
||||
|
||||
|
||||
@@ -253,175 +253,6 @@ properties. Alternatively, configure custom accessors via
|
||||
`SimpleEvaluationContext.forPropertyAccessors(...)`, potentially disable assignment, and
|
||||
optionally activate method resolution and/or a type converter through the builder.
|
||||
|
||||
[[expressions-evaluation-context-security]]
|
||||
=== Security Considerations
|
||||
|
||||
SpEL is a powerful expression language that can invoke constructors and methods, read and
|
||||
write properties and fields, and reference beans – all backed by reflection. Because of
|
||||
this power, evaluating a SpEL expression obtained from an untrusted source is inherently
|
||||
dangerous and should generally be avoided, since doing so can effectively grant that
|
||||
source the ability to execute arbitrary code within the application, regardless of which
|
||||
`EvaluationContext` implementation is used.
|
||||
|
||||
Throughout this section, a source of a SpEL expression is considered "trusted" only if it
|
||||
is a developer of the application or an administrator responsible for configuring or
|
||||
operating the application. Any other source of a SpEL expression must be treated as
|
||||
untrusted – for example, an expression supplied by an end user of the application or
|
||||
received from an external system.
|
||||
|
||||
[WARNING]
|
||||
====
|
||||
`StandardEvaluationContext` exposes the complete SpEL language and must *never* be used
|
||||
to evaluate an expression obtained from an untrusted source.
|
||||
====
|
||||
|
||||
Although `SimpleEvaluationContext` restricts the SpEL language to a subset of its
|
||||
features, that restriction is provided on a best-effort basis and does not guarantee that
|
||||
expression evaluation is safe. Since an expression can potentially invoke any property,
|
||||
method, or function reachable via the configured root object, property accessors, method
|
||||
resolvers, variables, and functions, care must be taken if you choose to evaluate
|
||||
expressions from an untrusted source. It is therefore the responsibility of the code that
|
||||
configures an `EvaluationContext` – for example, by supplying a root object or by
|
||||
registering property accessors, resolvers, variables, or functions – to ensure that none
|
||||
of the objects reachable via the context expose operations that would be dangerous if
|
||||
invoked by an expression from an untrusted source.
|
||||
|
||||
Furthermore, a property "getter" reachable from an expression is not necessarily a pure,
|
||||
side-effect-free read operation. A JavaBean-style accessor (such as `getName()` or
|
||||
`isActive()`) and a plain accessor method used to support data classes such as Java
|
||||
records and Kotlin data classes (such as `name()`) are indistinguishable from a method
|
||||
that performs an action and happens to return a value (that is, a method which is
|
||||
*accessor-shaped*). For example, the `public boolean delete()` method in `java.io.File`
|
||||
looks like a plain accessor method to SpEL. Specifically, neither
|
||||
`ReflectivePropertyAccessor` nor `DataBindingPropertyAccessor` can determine whether such
|
||||
a method is free of side effects. Moreover, restricting a `SimpleEvaluationContext` to
|
||||
read-only data binding governs only whether *assignment* to a property is permitted: it
|
||||
does not verify that reading a property is side-effect-free. When exposing a root object
|
||||
or other reachable object to an untrusted expression, you must ensure that none of its
|
||||
accessor-shaped methods perform an action that would be unsafe if triggered by that
|
||||
expression.
|
||||
|
||||
[NOTE]
|
||||
.What makes a method "accessor-shaped"?
|
||||
====
|
||||
A method is accessor-shaped if it is `public`, takes no arguments, and returns a value –
|
||||
the same shape that `ReflectivePropertyAccessor` and `DataBindingPropertyAccessor` look
|
||||
for when resolving a property "getter" by name. That shape says nothing about whether
|
||||
invoking the method is actually free of side effects. For example, the following methods
|
||||
are all accessor-shaped, but only some of them are safe to invoke as a property read.
|
||||
|
||||
Side-effect-free (safe to expose as properties):
|
||||
|
||||
* `getName()` and `isActive()`: conventional JavaBean-style accessors.
|
||||
* `name()` and `active()`: plain accessor methods used by data classes such as Java
|
||||
records and Kotlin data classes.
|
||||
|
||||
Side-effecting (unsafe to expose as properties, despite the identical shape):
|
||||
|
||||
* `java.io.File#delete()`: deletes the underlying file and returns whether the deletion
|
||||
succeeded.
|
||||
* `java.util.Queue#poll()`: removes and returns the head element, mutating the queue.
|
||||
* `java.util.concurrent.atomic.AtomicInteger#incrementAndGet()`: increments and returns
|
||||
a counter, mutating it.
|
||||
|
||||
If an untrusted expression can reference `someFile.delete`, `someQueue.poll`, or
|
||||
`someCounter.incrementAndGet` as a property, SpEL invokes the corresponding method just
|
||||
as readily as it would invoke a genuine getter.
|
||||
====
|
||||
|
||||
[[expressions-evaluation-context-object-design]]
|
||||
=== Object Design
|
||||
|
||||
Similar to the design guidance for
|
||||
xref:web/webmvc/mvc-data-binding.adoc#mvc-data-binding-design[web data binding], you
|
||||
should carefully design any object that may be reached from a SpEL expression evaluated
|
||||
against untrusted input. This applies not only to the root object supplied to an
|
||||
`EvaluationContext` but also to every object that such an expression can navigate to from
|
||||
that root object – for example, an object returned by a property, a method, an index
|
||||
operation, a variable, or a function.
|
||||
|
||||
When exposing an object to expressions from an untrusted source, consider the following
|
||||
recommendations.
|
||||
|
||||
Use a dedicated type::
|
||||
Prefer a dedicated type, designed specifically to be evaluated against untrusted
|
||||
expressions, over passing an existing domain or infrastructure type "as is". A
|
||||
dedicated type lets you control exactly which properties and methods are reachable from
|
||||
an expression, rather than exposing the full surface area of a class such as a JPA
|
||||
entity, `java.io.File`, or a JDBC `Connection` – most of which were never designed with
|
||||
SpEL evaluation in mind.
|
||||
|
||||
Prefer immutability::
|
||||
An immutable type – for example, a Java record or a Kotlin data class exposing only
|
||||
`val` properties – rules out property writes and eliminates any concern that a "getter"
|
||||
might mutate state as a side effect, since there is no mutable state to affect.
|
||||
Immutability does not, on its own, rule out an accessor-shaped method with an external
|
||||
side effect (such as a network call or a file system operation), but it removes an
|
||||
entire class of risk.
|
||||
|
||||
Limit scope::
|
||||
Expose only the properties and methods that the expression is expected to use, and
|
||||
nothing more. Because a `PropertyAccessor` cannot restrict access to specific
|
||||
properties or methods on a per-expression basis, every accessor-shaped method reachable
|
||||
on an exposed object is reachable by any expression that can reach that object –
|
||||
regardless of which property or method the application intended the expression to use.
|
||||
|
||||
Audit accessor-shaped methods::
|
||||
Review every accessor-shaped method exposed by a type before making it reachable from
|
||||
an untrusted expression, keeping the <<expressions-evaluation-context-security,
|
||||
security considerations>> discussed above in mind. None of the reachable methods should
|
||||
perform an action that would be unsafe if triggered by that expression.
|
||||
|
||||
[WARNING]
|
||||
====
|
||||
These recommendations apply transitively. If the root object exposes a property or method
|
||||
that returns another object, and an untrusted expression can navigate to it (for example,
|
||||
`rootObject.child.grandchild`), the nested object is just as reachable as the root object
|
||||
itself and must meet the same design requirements. The same is true for an object reached
|
||||
via indexing (for example, `rootObject.items[0]` or `rootObject.items['key']`): whatever
|
||||
is returned by the index operation is just as reachable as any other nested object.
|
||||
====
|
||||
|
||||
[[expressions-evaluation-context-lifecycle]]
|
||||
=== Lifecycle and Reuse
|
||||
|
||||
For performance, the AST nodes that make up a parsed `Expression` may cache the specific
|
||||
`PropertyAccessor`, `IndexAccessor`, `MethodExecutor`, or `ConstructorExecutor` that
|
||||
satisfied a previous evaluation, so that later evaluations of the same node can avoid
|
||||
asking every registered accessor or resolver in turn. Understanding this caching behavior
|
||||
is essential to using `Expression` and `EvaluationContext` correctly, in addition to the
|
||||
<<expressions-evaluation-context-security,security considerations>> discussed previously.
|
||||
|
||||
A parsed `Expression` is designed to be created once and evaluated repeatedly, and doing
|
||||
so is both supported and encouraged. In particular:
|
||||
|
||||
* A parsed `Expression` may be evaluated against different root objects, and against
|
||||
different `EvaluationContext` instances of the *same type and with equivalent
|
||||
configuration* – for example, several `StandardEvaluationContext` instances each
|
||||
registering the same kind of custom `PropertyAccessor`. Changing the accessors or
|
||||
resolvers registered with a context between evaluations of the same expression is
|
||||
atypical and generally not advised, but is expected to work correctly: the registered
|
||||
state of the *current* context is what is consulted, not a snapshot taken during an
|
||||
earlier evaluation.
|
||||
* A parsed `Expression` must *not* be evaluated first against a context with one set of
|
||||
security implications and later against a context with different, typically more
|
||||
restrictive, security implications – for example, first against a
|
||||
`StandardEvaluationContext` and later against a `SimpleEvaluationContext`. Doing so is
|
||||
analogous to executing a database query on behalf of an administrator, caching the
|
||||
resulting administrator-privileged execution plan, and then reusing that cached plan for
|
||||
a lower-privileged user while expecting the lower-privileged user's restrictions to
|
||||
apply: cached state from the first, more permissive evaluation may be reused during the
|
||||
second, and the second context's restrictions cannot be reliably enforced as a result.
|
||||
If the same expression string must be evaluated under contexts with different security
|
||||
implications, parse it into *distinct* `Expression` instances, one per context.
|
||||
|
||||
[WARNING]
|
||||
====
|
||||
Reusing a single parsed `Expression` across `EvaluationContext` instances with different
|
||||
security implications is not a supported usage pattern and must be avoided, regardless of
|
||||
which `EvaluationContext` implementations are involved.
|
||||
====
|
||||
|
||||
[[expressions-type-conversion]]
|
||||
=== Type Conversion
|
||||
|
||||
@@ -488,23 +319,17 @@ Kotlin::
|
||||
|
||||
It is possible to configure the SpEL expression parser by using a parser configuration
|
||||
object (`org.springframework.expression.spel.SpelParserConfiguration`). The configuration
|
||||
object controls the behavior of some of the expression components. To create a
|
||||
`SpelParserConfiguration` instance, favor `SpelParserConfiguration.builder()` over the
|
||||
numerous constructors in `SpelParserConfiguration`, since the builder only requires
|
||||
configuration of the properties that need to deviate from their sensible defaults --
|
||||
or use `SpelParserConfiguration.withDefaults()` if none of those defaults need to be
|
||||
overridden.
|
||||
|
||||
For example, if you index into a collection and the element at the specified index is
|
||||
`null`, SpEL can automatically create the element. This is useful when using expressions
|
||||
made up of a chain of property references. Similarly, if you index into a collection and
|
||||
specify an index that is greater than the current size of the collection, SpEL can
|
||||
automatically grow the collection to accommodate that index. In order to add an element
|
||||
at the specified index, SpEL will try to create the element using the element type's
|
||||
default constructor before setting the specified value. If the element type does not
|
||||
have a default constructor, `null` will be added to the collection. If there is no
|
||||
built-in converter or custom converter that knows how to set the value, `null` will
|
||||
remain in the collection at the specified index. The following example demonstrates how to
|
||||
object controls the behavior of some of the expression components. For example, if you
|
||||
index into a collection and the element at the specified index is `null`, SpEL can
|
||||
automatically create the element. This is useful when using expressions made up of a
|
||||
chain of property references. Similarly, if you index into a collection and specify an
|
||||
index that is greater than the current size of the collection, SpEL can automatically
|
||||
grow the collection to accommodate that index. In order to add an element at the
|
||||
specified index, SpEL will try to create the element using the element type's default
|
||||
constructor before setting the specified value. If the element type does not have a
|
||||
default constructor, `null` will be added to the collection. If there is no built-in
|
||||
converter or custom converter that knows how to set the value, `null` will remain in the
|
||||
collection at the specified index. The following example demonstrates how to
|
||||
automatically grow a `List`.
|
||||
|
||||
[tabs]
|
||||
@@ -517,10 +342,10 @@ Java::
|
||||
public List<String> list;
|
||||
}
|
||||
|
||||
SpelParserConfiguration config = SpelParserConfiguration.builder()
|
||||
.autoGrowNullReferences()
|
||||
.autoGrowCollections()
|
||||
.build();
|
||||
// Turn on:
|
||||
// - auto null reference initialization
|
||||
// - auto collection growing
|
||||
SpelParserConfiguration config = new SpelParserConfiguration(true, true);
|
||||
|
||||
ExpressionParser parser = new SpelExpressionParser(config);
|
||||
|
||||
@@ -542,10 +367,10 @@ Kotlin::
|
||||
var list: List<String>? = null
|
||||
}
|
||||
|
||||
val config = SpelParserConfiguration.builder()
|
||||
.autoGrowNullReferences()
|
||||
.autoGrowCollections()
|
||||
.build()
|
||||
// Turn on:
|
||||
// - auto null reference initialization
|
||||
// - auto collection growing
|
||||
val config = SpelParserConfiguration(true, true)
|
||||
|
||||
val parser = SpelExpressionParser(config)
|
||||
|
||||
@@ -562,8 +387,7 @@ Kotlin::
|
||||
|
||||
By default, a SpEL expression cannot contain more than 10,000 characters; however, the
|
||||
`maxExpressionLength` is configurable. If you create a `SpelExpressionParser`
|
||||
programmatically, you can specify a custom `maxExpressionLength` via
|
||||
`SpelParserConfiguration.builder().maximumExpressionLength(...)` when creating the
|
||||
programmatically, you can specify a custom `maxExpressionLength` when creating the
|
||||
`SpelParserConfiguration` that you provide to the `SpelExpressionParser`. If you wish to
|
||||
set the `maxExpressionLength` used for parsing SpEL expressions within an
|
||||
`ApplicationContext` -- for example, in XML bean definitions, `@Value`, etc. -- you can
|
||||
@@ -571,32 +395,6 @@ set a JVM system property or Spring property named `spring.context.expression.ma
|
||||
to the maximum expression length needed by your application (see
|
||||
xref:appendix.adoc#appendix-spring-properties[Supported Spring Properties]).
|
||||
|
||||
Similarly, the number of operations performed during the evaluation of a SpEL expression
|
||||
cannot exceed 10,000 by default; however, the `maxOperations` value is configurable. If
|
||||
you create a `SpelExpressionParser` programmatically (the recommend approach), you can
|
||||
specify a custom `maxOperations` value via
|
||||
`SpelParserConfiguration.builder().maximumOperations(...)` when creating the
|
||||
`SpelParserConfiguration` that you provide to the `SpelExpressionParser`. If you are not
|
||||
able to configure an explicit value for `maxOperations` via `SpelParserConfiguration`,
|
||||
you can set a JVM system property or Spring property named
|
||||
`spring.expression.maxOperations` to the maximum number of operations required by your
|
||||
application (see xref:appendix.adoc#appendix-spring-properties[Supported Spring
|
||||
Properties]).
|
||||
|
||||
In addition, the result of a `BigDecimal` or `BigInteger` power operation within a SpEL
|
||||
expression cannot exceed 1,000,000 bits by default – approximately equivalent to a
|
||||
decimal number with 300,000 digits. Power operations involving large base values or large
|
||||
exponents can be computationally expensive, and this limit ensures that evaluations
|
||||
remain bounded; however, the `maximumBigPowerBits` value is configurable. If you create a
|
||||
`SpelExpressionParser` programmatically (the recommended approach), you can specify a
|
||||
custom `maximumBigPowerBits` value via
|
||||
`SpelParserConfiguration.builder().maximumBigPowerBits(...)` when creating the
|
||||
`SpelParserConfiguration` that you provide to the `SpelExpressionParser`. To remove this
|
||||
limit entirely, pass `Integer.MAX_VALUE` as the `maximumBigPowerBits` value. If you are
|
||||
not able to configure an explicit value for `maximumBigPowerBits` via
|
||||
`SpelParserConfiguration`, you can set a JVM system property or Spring property named
|
||||
`spring.expression.maxBigPowerBits` to the maximum result size in bits (see
|
||||
xref:appendix.adoc#appendix-spring-properties[Supported Spring Properties]).
|
||||
|
||||
[[expressions-spel-compilation]]
|
||||
== SpEL Compilation
|
||||
@@ -635,9 +433,9 @@ only 3ms using the compiled version of the expression.
|
||||
|
||||
The compiler is not turned on by default, but you can turn it on in either of two
|
||||
different ways. You can turn it on by using the parser configuration process
|
||||
(<<expressions-parser-configuration,discussed earlier>>) or by using a Spring property
|
||||
when SpEL usage is embedded inside another component. This section discusses both of
|
||||
these options.
|
||||
(xref:core/expressions/evaluation.adoc#expressions-parser-configuration[discussed
|
||||
earlier]) or by using a Spring property when SpEL usage is embedded inside another
|
||||
component. This section discusses both of these options.
|
||||
|
||||
The compiler can operate in one of three modes, which are captured in the
|
||||
`org.springframework.expression.spel.SpelCompilerMode` enum. The modes are as follows.
|
||||
@@ -679,10 +477,8 @@ Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
SpelParserConfiguration config = SpelParserConfiguration.builder()
|
||||
.compilerMode(SpelCompilerMode.IMMEDIATE)
|
||||
.compilerClassLoader(getClass().getClassLoader())
|
||||
.build();
|
||||
SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE,
|
||||
this.getClass().getClassLoader());
|
||||
|
||||
SpelExpressionParser parser = new SpelExpressionParser(config);
|
||||
|
||||
@@ -697,10 +493,8 @@ Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
val config = SpelParserConfiguration.builder()
|
||||
.compilerMode(SpelCompilerMode.IMMEDIATE)
|
||||
.compilerClassLoader(javaClass.classLoader)
|
||||
.build()
|
||||
val config = SpelParserConfiguration(SpelCompilerMode.IMMEDIATE,
|
||||
this.javaClass.classLoader)
|
||||
|
||||
val parser = SpelExpressionParser(config)
|
||||
|
||||
@@ -737,7 +531,6 @@ following kinds of expressions cannot be compiled.
|
||||
* Expressions relying on the conversion service
|
||||
* Expressions using custom resolvers
|
||||
* Expressions using overloaded operators
|
||||
* Expressions using `Optional` with the null-safe or Elvis operator
|
||||
* Expressions using array construction syntax
|
||||
* Expressions using selection or projection
|
||||
* Expressions using bean references
|
||||
|
||||
@@ -2,12 +2,4 @@
|
||||
= Language Reference
|
||||
:page-section-summary-toc: 1
|
||||
|
||||
Spring Expression Language (SpEL) expressions are composed of a sequence of tokens such
|
||||
as literals, operators, method invocations, and so forth.
|
||||
|
||||
Whitespace can be used freely between tokens to format and improve the readability of
|
||||
expressions. Specifically, the `\s` (space), `\t` (tab), `\r` (carriage return), and `\n`
|
||||
(newline) characters are all valid separators between tokens. However, whitespace is
|
||||
ignored by the expression parser unless it is part of a string literal.
|
||||
|
||||
The following sections describe the features and syntax of SpEL.
|
||||
This section describes how the Spring Expression Language works.
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
|
||||
The Spring Expression Language supports the following kinds of operators:
|
||||
|
||||
* <<expressions-operators-relational,Relational Operators>>
|
||||
* <<expressions-operators-logical,Logical Operators>>
|
||||
* <<expressions-operators-string,String Operators>>
|
||||
* <<expressions-operators-mathematical,Mathematical Operators>>
|
||||
* <<expressions-assignment,The Assignment Operator>>
|
||||
* <<expressions-operators-overloaded,Overloaded Operators>>
|
||||
* xref:core/expressions/language-ref/operators.adoc#expressions-operators-relational[Relational Operators]
|
||||
* xref:core/expressions/language-ref/operators.adoc#expressions-operators-logical[Logical Operators]
|
||||
* xref:core/expressions/language-ref/operators.adoc#expressions-operators-string[String Operators]
|
||||
* xref:core/expressions/language-ref/operators.adoc#expressions-operators-mathematical[Mathematical Operators]
|
||||
* xref:core/expressions/language-ref/operators.adoc#expressions-assignment[The Assignment Operator]
|
||||
* xref:core/expressions/language-ref/operators.adoc#expressions-operators-overloaded[Overloaded Operators]
|
||||
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ Kotlin::
|
||||
val trueValue = parser.parseExpression("'black' < 'block'").getValue(Boolean::class.java)
|
||||
|
||||
// uses CustomValue:::compareTo
|
||||
val trueValue = parser.parseExpression("new CustomValue(1) < new CustomValue(2)").getValue(Boolean::class.java)
|
||||
val trueValue = parser.parseExpression("new CustomValue(1) < new CustomValue(2)").getValue(Boolean::class.java);
|
||||
----
|
||||
======
|
||||
|
||||
@@ -167,7 +167,7 @@ Kotlin::
|
||||
[CAUTION]
|
||||
====
|
||||
The syntax for the `between` operator is `<input> between {<range_begin>, <range_end>}`,
|
||||
which is effectively a shortcut for `<input> >= <range_begin> && <input> \<= <range_end>`.
|
||||
which is effectively a shortcut for `<input> >= <range_begin> && <input> \<= <range_end>}`.
|
||||
|
||||
Consequently, `1 between {1, 5}` evaluates to `true`, while `1 between {5, 1}` evaluates
|
||||
to `false`.
|
||||
@@ -312,13 +312,13 @@ Kotlin::
|
||||
|
||||
// evaluates to 'a'
|
||||
val ch = parser.parseExpression("'d' - 3")
|
||||
.getValue(Char::class.java)
|
||||
.getValue(Character::class.java);
|
||||
|
||||
// -- Repeat --
|
||||
|
||||
// evaluates to "abcabc"
|
||||
val repeated = parser.parseExpression("'abc' * 2")
|
||||
.getValue(String::class.java)
|
||||
.getValue(String::class.java);
|
||||
----
|
||||
======
|
||||
|
||||
@@ -485,7 +485,7 @@ Kotlin::
|
||||
|
||||
// -- Operator precedence --
|
||||
|
||||
val minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Int::class.java) // -21
|
||||
val minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Int::class.java) // -21
|
||||
----
|
||||
======
|
||||
|
||||
@@ -541,7 +541,32 @@ For example, if we want to overload the `ADD` operator to allow two lists to be
|
||||
concatenated using the `+` sign, we can implement a custom `OperatorOverloader` as
|
||||
follows.
|
||||
|
||||
include-code::./ListConcatenation[]
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
pubic class ListConcatenation implements OperatorOverloader {
|
||||
|
||||
@Override
|
||||
public boolean overridesOperation(Operation operation, Object left, Object right) {
|
||||
return (operation == Operation.ADD &&
|
||||
left instanceof List && right instanceof List);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object operate(Operation operation, Object left, Object right) {
|
||||
if (operation == Operation.ADD &&
|
||||
left instanceof List list1 && right instanceof List list2) {
|
||||
|
||||
List result = new ArrayList(list1);
|
||||
result.addAll(list2);
|
||||
return result;
|
||||
}
|
||||
throw new UnsupportedOperationException(
|
||||
"No overload for operation %s and operands [%s] and [%s]"
|
||||
.formatted(operation, left, right));
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
If we register `ListConcatenation` as the `OperatorOverloader` in a
|
||||
`StandardEvaluationContext`, we can then evaluate expressions like `{1, 2, 3} + {4, 5}`
|
||||
@@ -564,8 +589,8 @@ Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
val context = StandardEvaluationContext()
|
||||
context.operatorOverloader = ListConcatenation()
|
||||
StandardEvaluationContext context = StandardEvaluationContext()
|
||||
context.setOperatorOverloader(ListConcatenation())
|
||||
|
||||
// evaluates to a new list: [1, 2, 3, 4, 5]
|
||||
parser.parseExpression("{1, 2, 3} + {2 + 2, 5}").getValue(context, List::class.java)
|
||||
|
||||
+1
-1
@@ -270,7 +270,7 @@ is applicable for typical implementations of indexed structures.
|
||||
NOTE: `ReflectiveIndexAccessor` also implements `CompilableIndexAccessor` in order to
|
||||
support xref:core/expressions/evaluation.adoc#expressions-spel-compilation[compilation]
|
||||
to bytecode for read access. Note, however, that the configured read-method must be
|
||||
invocable via a `public` class or `public` interface for compilation to succeed.
|
||||
invokable via a `public` class or `public` interface for compilation to succeed.
|
||||
|
||||
The following code listings define a `Color` enum and `FruitMap` type that behaves like a
|
||||
map but does not implement the `java.util.Map` interface. Thus, if you want to index into
|
||||
|
||||
@@ -6,7 +6,7 @@ are set by using the `setVariable()` method in `EvaluationContext` implementatio
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
Variable names must begin with a letter (as defined below), an underscore, or a dollar
|
||||
Variable names must be begin with a letter (as defined below), an underscore, or a dollar
|
||||
sign.
|
||||
|
||||
Variable names must be composed of one or more of the following supported types of
|
||||
|
||||
@@ -25,8 +25,7 @@ public void sendNotification() {
|
||||
|
||||
By default, the method invocation will be retried for any exception thrown: with at most
|
||||
3 retry attempts (`maxRetries = 3`) after an initial failure, and a delay of 1 second
|
||||
between attempts. If all attempts have failed and the retry policy has been exhausted,
|
||||
the last original exception from the target method will be propagated to the caller.
|
||||
between attempts.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
@@ -70,24 +69,17 @@ Or for 4 retry attempts and an exponential back-off strategy with a bit of jitte
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Retryable(
|
||||
includes = MessageDeliveryException.class,
|
||||
maxRetries = 4,
|
||||
delay = 100,
|
||||
jitter = 10,
|
||||
multiplier = 2,
|
||||
maxDelay = 1000)
|
||||
includes = MessageDeliveryException.class,
|
||||
maxRetries = 4,
|
||||
delay = 100,
|
||||
jitter = 10,
|
||||
multiplier = 2,
|
||||
maxDelay = 1000)
|
||||
public void sendNotification() {
|
||||
this.jmsClient.destination("notifications").send(...);
|
||||
}
|
||||
----
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
When `delay` is `0` combined with a positive `jitter`, the delay never grows
|
||||
regardless of any configured `multiplier`, so the full configured `jitter` is
|
||||
applied directly as a random delay in the range from `0` to `min(jitter, maxDelay)`.
|
||||
====
|
||||
|
||||
Last but not least, `@Retryable` also works for reactive methods with a reactive return
|
||||
type, decorating the pipeline with Reactor's retry capabilities:
|
||||
|
||||
@@ -107,160 +99,6 @@ TIP: Several attributes in `@Retryable` have `String` variants that provide prop
|
||||
placeholder and SpEL support, as an alternative to the specifically typed annotation
|
||||
attributes used in the above examples.
|
||||
|
||||
[TIP]
|
||||
====
|
||||
During `@Retryable` processing, Spring publishes a `MethodRetryEvent` for every exception
|
||||
coming out of the target method. This can be used to track/log all original exceptions
|
||||
whereas the caller of the `@Retryable` method will only ever see the last exception.
|
||||
====
|
||||
|
||||
|
||||
[[resilience-annotations-retryable-combining]]
|
||||
=== Combining `@Retryable` with Other Proxy-Based Features
|
||||
|
||||
Spring AOP applies interceptors in a specific order when multiple annotations such as
|
||||
`@Retryable`, `@Transactional`, `@Cacheable`, and `@Async` are present on the same method.
|
||||
The resulting advice chain determines how retries interact with each feature, and
|
||||
understanding that chain is important for using `@Retryable` correctly in combination with
|
||||
other annotations.
|
||||
|
||||
[[resilience-annotations-retryable-combining-transactional]]
|
||||
==== With `@Transactional`
|
||||
|
||||
When `@Transactional` and `@Retryable` are used together, the advice chain is:
|
||||
|
||||
----
|
||||
Retry (OUTER) → Transaction (INNER) → target method
|
||||
----
|
||||
|
||||
Each retry attempt starts a fresh transaction. If the target method throws, the transaction
|
||||
is rolled back and `@Retryable` decides whether to retry. On success, the transaction
|
||||
commits. This is usually the desired behavior for transient failures such as database
|
||||
deadlocks.
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Transactional
|
||||
@Retryable(TransientDataAccessException.class)
|
||||
public void updateRecord() {
|
||||
// Each retry runs in its own transaction
|
||||
}
|
||||
----
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
Because the retry interceptor is outside the transaction interceptor, the current
|
||||
transaction has already been rolled back by the time the retry interceptor receives the
|
||||
exception. The retry interceptor sees the same, unwrapped exception that the target
|
||||
method threw.
|
||||
====
|
||||
|
||||
TIP: See xref:data-access/transaction/declarative/annotations.adoc[Using `@Transactional`]
|
||||
for general details on declarative transaction management.
|
||||
|
||||
[[resilience-annotations-retryable-combining-cacheable]]
|
||||
==== With `@Cacheable`
|
||||
|
||||
When `@Cacheable` and `@Retryable` are used together, the advice chain is:
|
||||
|
||||
----
|
||||
Retry (OUTER) → Cache (INNER) → target method
|
||||
----
|
||||
|
||||
The cache interceptor runs on every attempt. If the cache is populated between attempts
|
||||
(for example, by a concurrent request), subsequent retry attempts will return the cached
|
||||
value without invoking the target method. On success, the cache is populated as normal.
|
||||
|
||||
The same fixed ordering applies to `@CacheEvict` and `@CachePut`, since they share the
|
||||
same underlying cache advisor.
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Cacheable("items")
|
||||
@Retryable
|
||||
public Item loadItem(String id) {
|
||||
// Retry wraps the cache lookup; each attempt checks the cache first
|
||||
}
|
||||
----
|
||||
|
||||
TIP: See xref:integration/cache/annotations.adoc#cache-annotations-cacheable[The `@Cacheable` Annotation]
|
||||
for general details on declarative caching.
|
||||
|
||||
[[resilience-annotations-retryable-combining-async]]
|
||||
==== With `@Async`
|
||||
|
||||
When `@Async` and `@Retryable` are used together, the advice chain is:
|
||||
|
||||
----
|
||||
Async (OUTER) → Retry (INNER) → target method
|
||||
----
|
||||
|
||||
The method is submitted to the async executor once, and all retry attempts run on the
|
||||
same async thread. The caller receives a `CompletableFuture` or `Future` that completes
|
||||
when the last retry attempt finishes (either with a result or a final exception).
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Async
|
||||
@Retryable
|
||||
public CompletableFuture<String> fetchData() {
|
||||
// Retries happen on the async thread, not the calling thread
|
||||
}
|
||||
----
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
Because `@Async` is outermost, the calling thread is never blocked by retry delays.
|
||||
All retry attempts, including any configured delay between them, happen on the async
|
||||
executor thread.
|
||||
====
|
||||
|
||||
TIP: See xref:integration/scheduling.adoc#scheduling-annotation-support-async[The `@Async` annotation]
|
||||
for general details on asynchronous method execution.
|
||||
|
||||
[[resilience-annotations-retryable-combining-order]]
|
||||
==== Adjusting Advice Order
|
||||
|
||||
The `@Async` ordering described above reflects the relative `order` of the
|
||||
`RetryAnnotationBeanPostProcessor` (registered by `@EnableResilientMethods`) and the
|
||||
`AsyncAnnotationBeanPostProcessor` (registered by `@EnableAsync`). Both are plain
|
||||
`Ordered` bean post-processors, so you can change their relative ordering by setting the
|
||||
`order` attribute on `@EnableResilientMethods` and/or `@EnableAsync`.
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Configuration
|
||||
@EnableResilientMethods(order = Ordered.LOWEST_PRECEDENCE) // <1>
|
||||
@EnableAsync(order = Ordered.LOWEST_PRECEDENCE - 1) // <2>
|
||||
class AppConfig {
|
||||
}
|
||||
----
|
||||
<1> Raises the retry post-processor's order so that it runs after the async
|
||||
post-processor.
|
||||
<2> Lowers the async post-processor's order so that it runs before the retry
|
||||
post-processor. As a result, retry becomes the outermost advice and async the
|
||||
innermost, reversing the default order.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
With the reversed order shown above, exceptions thrown during asynchronous execution are
|
||||
not retried: the retry interceptor only sees the `Future` handle, which is returned
|
||||
immediately, rather than the outcome of the asynchronous invocation. Such an arrangement
|
||||
only retries synchronous submission failures (for example, a rejected task submission)
|
||||
and is rarely desirable in practice.
|
||||
====
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
This technique does not apply to `@Transactional` or `@Cacheable`. Their advisors are
|
||||
registered through Spring's shared `InfrastructureAdvisorAutoProxyCreator`, whose own
|
||||
post-processor `order` is fixed at `Ordered.HIGHEST_PRECEDENCE` and is unaffected by the
|
||||
`order` attribute on `@EnableTransactionManagement` or `@EnableCaching` (that attribute
|
||||
only affects ordering relative to other advisors on the same proxy). As a result, retry
|
||||
advice is always applied outside `@Transactional` and `@Cacheable`, regardless of the
|
||||
`order` configured on `@EnableResilientMethods`.
|
||||
====
|
||||
|
||||
|
||||
[[resilience-annotations-concurrencylimit]]
|
||||
== `@ConcurrencyLimit`
|
||||
@@ -336,7 +174,7 @@ configured {spring-framework-api}/core/retry/RetryPolicy.html[`RetryPolicy`].
|
||||
----
|
||||
var retryTemplate = new RetryTemplate(); // <1>
|
||||
|
||||
retryTemplate.invoke(
|
||||
retryTemplate.execute(
|
||||
() -> jmsClient.destination("notifications").send(...));
|
||||
----
|
||||
<1> Implicitly uses `RetryPolicy.withDefaults()`.
|
||||
@@ -345,6 +183,9 @@ By default, a retryable operation will be retried for any exception thrown: with
|
||||
3 retry attempts (`maxRetries = 3`) after an initial failure, and a delay of 1 second
|
||||
between attempts.
|
||||
|
||||
If you only need to customize the number of retry attempts, you can use the
|
||||
`RetryPolicy.withMaxRetries()` factory method as demonstrated below.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
A retryable operation will be executed at least once and retried at most `maxRetries`
|
||||
@@ -355,14 +196,11 @@ For example, if `maxRetries` is set to `4`, the retryable operation will be invo
|
||||
least once and at most 5 times.
|
||||
====
|
||||
|
||||
If you only need to customize the number of retry attempts, you can use the
|
||||
`RetryPolicy.withMaxRetries()` factory method as demonstrated below.
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
var retryTemplate = new RetryTemplate(RetryPolicy.withMaxRetries(4)); // <1>
|
||||
|
||||
retryTemplate.invoke(
|
||||
retryTemplate.execute(
|
||||
() -> jmsClient.destination("notifications").send(...));
|
||||
----
|
||||
<1> Explicitly uses `RetryPolicy.withMaxRetries(4)`.
|
||||
@@ -380,7 +218,7 @@ matched against an exception thrown by a failed operation as well as nested caus
|
||||
|
||||
var retryTemplate = new RetryTemplate(retryPolicy);
|
||||
|
||||
retryTemplate.invoke(
|
||||
retryTemplate.execute(
|
||||
() -> jmsClient.destination("notifications").send(...));
|
||||
----
|
||||
<1> Specify one or more exception types to include.
|
||||
@@ -413,108 +251,21 @@ and an exponential back-off strategy with a bit of jitter.
|
||||
|
||||
var retryTemplate = new RetryTemplate(retryPolicy);
|
||||
|
||||
retryTemplate.invoke(
|
||||
retryTemplate.execute(
|
||||
() -> jmsClient.destination("notifications").send(...));
|
||||
----
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
When `delay` is zero combined with a positive `jitter`, the delay never grows
|
||||
regardless of any configured `multiplier`, so the full configured `jitter` is
|
||||
applied directly as a random delay in the range from zero to `min(jitter, maxDelay)`.
|
||||
====
|
||||
|
||||
[TIP]
|
||||
====
|
||||
A {spring-framework-api}/core/retry/RetryListener.html[`RetryListener`] can be registered
|
||||
with a `RetryTemplate` to react to events published during key retry phases (before a
|
||||
retry attempt, after a retry attempt, etc.), and you can compose multiple listeners via a
|
||||
{spring-framework-api}/core/retry/support/CompositeRetryListener.html[`CompositeRetryListener`].
|
||||
====
|
||||
|
||||
Although the factory methods and builder API for `RetryPolicy` cover most common
|
||||
configuration scenarios, you can implement a custom `RetryPolicy` for complete control
|
||||
over the types of exceptions that should trigger a retry as well as the
|
||||
{spring-framework-api}/util/backoff/BackOff.html[`BackOff`] strategy to use. Note that
|
||||
you can also configure a customized `BackOff` strategy via the `backOff()` method in
|
||||
the `RetryPolicy.Builder`.
|
||||
====
|
||||
|
||||
Note that the examples above apply a pattern similar to `@Retryable` method invocations
|
||||
where the last original exception will be propagated to the caller, using the `invoke`
|
||||
variants on `RetryTemplate` which are available with and without a return value.
|
||||
The callback may throw unchecked exceptions, the last one of which is exposed for
|
||||
direct handling on the caller side:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
try {
|
||||
retryTemplate.invoke(
|
||||
() -> jmsClient.destination("notifications").send(...));
|
||||
}
|
||||
catch (MessageDeliveryException ex) {
|
||||
// coming out of the original JmsClient send method
|
||||
}
|
||||
----
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
try {
|
||||
var result = retryTemplate.invoke(() -> {
|
||||
jmsClient.destination("notifications").send(...);
|
||||
return "result";
|
||||
});
|
||||
}
|
||||
catch (MessageDeliveryException ex) {
|
||||
// coming out of the original JmsClient send method
|
||||
}
|
||||
----
|
||||
|
||||
`RetryTemplate` instances are very light and can be created on the fly,
|
||||
potentially with a specific retry policy to use for a given invocation:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
try {
|
||||
new RetryTemplate(RetryPolicy.withMaxRetries(4)).invoke(
|
||||
() -> jmsClient.destination("notifications").send(...));
|
||||
}
|
||||
catch (MessageDeliveryException ex) {
|
||||
// coming out of the original JmsClient send method
|
||||
}
|
||||
----
|
||||
|
||||
For deeper interaction, you may use RetryTemplate's `execute` method. The caller will
|
||||
have to handle the checked `RetryException` thrown by `RetryTemplate`, exposing the
|
||||
outcome of all attempts:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
try {
|
||||
var result = retryTemplate.execute(() -> {
|
||||
jmsClient.destination("notifications").send(...);
|
||||
return "result";
|
||||
});
|
||||
}
|
||||
catch (RetryException ex) {
|
||||
// ex.getExceptions() / ex.getLastException() ...
|
||||
}
|
||||
----
|
||||
|
||||
A {spring-framework-api}/core/retry/RetryListener.html[`RetryListener`] can be registered
|
||||
with a `RetryTemplate` to react to key retry steps (before or after a retry attempt etc.)
|
||||
or simply to every invocation attempt, being able to track all exceptions coming out of
|
||||
the callback and all retry outcomes (exhaustion, interruption, timeout). This is
|
||||
particularly useful when using `invoke` where no retry state other than the last
|
||||
original exception is exposed otherwise:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
var retryTemplate = new RetryTemplate();
|
||||
retryTemplate.setRetryListener(new RetryListener() {
|
||||
@Override
|
||||
public void onRetryableExecution(RetryPolicy retryPolicy, Retryable<?> retryable, RetryState retryState) {
|
||||
...
|
||||
}
|
||||
});
|
||||
|
||||
retryTemplate.invoke(
|
||||
() -> jmsClient.destination("notifications").send(...));
|
||||
----
|
||||
|
||||
You can also compose multiple listeners via a
|
||||
{spring-framework-api}/core/retry/support/CompositeRetryListener.html[`CompositeRetryListener`].
|
||||
{spring-framework-api}/util/backoff/BackOff.html[`BackOff`] strategy to use. Note that you
|
||||
can also configure a customized `BackOff` strategy via the `backOff()` method in the
|
||||
`RetryPolicy.Builder`.
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
This chapter covers how Spring handles resources and how you can work with resources in
|
||||
Spring. It includes the following topics:
|
||||
|
||||
* <<resources-introduction,Introduction>>
|
||||
* <<resources-resource,The `Resource` Interface>>
|
||||
* <<resources-implementations,Built-in `Resource` Implementations>>
|
||||
* <<resources-resourceloader,The `ResourceLoader` Interface>>
|
||||
* <<resources-resourcepatternresolver,The `ResourcePatternResolver` Interface>>
|
||||
* <<resources-resourceloaderaware,The `ResourceLoaderAware` Interface>>
|
||||
* <<resources-as-dependencies,Resources as Dependencies>>
|
||||
* <<resources-app-ctx,Application Contexts and Resource Paths>>
|
||||
* xref:core/resources.adoc#resources-introduction[Introduction]
|
||||
* xref:core/resources.adoc#resources-resource[The `Resource` Interface]
|
||||
* xref:core/resources.adoc#resources-implementations[Built-in `Resource` Implementations]
|
||||
* xref:core/resources.adoc#resources-resourceloader[The `ResourceLoader` Interface]
|
||||
* xref:core/resources.adoc#resources-resourcepatternresolver[The `ResourcePatternResolver` Interface]
|
||||
* xref:core/resources.adoc#resources-resourceloaderaware[The `ResourceLoaderAware` Interface]
|
||||
* xref:core/resources.adoc#resources-as-dependencies[Resources as Dependencies]
|
||||
* xref:core/resources.adoc#resources-app-ctx[Application Contexts and Resource Paths]
|
||||
|
||||
|
||||
[[resources-introduction]]
|
||||
@@ -126,13 +126,13 @@ For example, a `UrlResource` wraps a URL and uses the wrapped `URL` to do its wo
|
||||
|
||||
Spring includes several built-in `Resource` implementations:
|
||||
|
||||
* <<resources-implementations-urlresource,`UrlResource`>>
|
||||
* <<resources-implementations-classpathresource,`ClassPathResource`>>
|
||||
* <<resources-implementations-filesystemresource,`FileSystemResource`>>
|
||||
* <<resources-implementations-pathresource,`PathResource`>>
|
||||
* <<resources-implementations-servletcontextresource,`ServletContextResource`>>
|
||||
* <<resources-implementations-inputstreamresource,`InputStreamResource`>>
|
||||
* <<resources-implementations-bytearrayresource,`ByteArrayResource`>>
|
||||
* xref:core/resources.adoc#resources-implementations-urlresource[`UrlResource`]
|
||||
* xref:core/resources.adoc#resources-implementations-classpathresource[`ClassPathResource`]
|
||||
* xref:core/resources.adoc#resources-implementations-filesystemresource[`FileSystemResource`]
|
||||
* xref:core/resources.adoc#resources-implementations-pathresource[`PathResource`]
|
||||
* xref:core/resources.adoc#resources-implementations-servletcontextresource[`ServletContextResource`]
|
||||
* xref:core/resources.adoc#resources-implementations-inputstreamresource[`InputStreamResource`]
|
||||
* xref:core/resources.adoc#resources-implementations-bytearrayresource[`ByteArrayResource`]
|
||||
|
||||
For a complete list of `Resource` implementations available in Spring, consult the
|
||||
"All Known Implementing Classes" section of the
|
||||
@@ -350,7 +350,7 @@ objects:
|
||||
|
||||
| file:
|
||||
| `\file:///data/config.xml`
|
||||
| Loaded as a `URL` from the filesystem. See also <<resources-filesystemresource-caveats,`FileSystemResource` Caveats>>.
|
||||
| Loaded as a `URL` from the filesystem. See also xref:core/resources.adoc#resources-filesystemresource-caveats[`FileSystemResource` Caveats].
|
||||
|
||||
| https:
|
||||
| `\https://myserver/logo.png`
|
||||
@@ -384,11 +384,11 @@ for all matching resources from the class path. Note that the resource location
|
||||
expected to be a path without placeholders in this case -- for example,
|
||||
`classpath*:/config/beans.xml`. JAR files or different directories in the class path can
|
||||
contain multiple files with the same path and the same name. See
|
||||
<<resources-app-ctx-wildcards-in-resource-paths,Wildcards in Application Context Constructor Resource Paths>> and its subsections for further details
|
||||
xref:core/resources.adoc#resources-app-ctx-wildcards-in-resource-paths[Wildcards in Application Context Constructor Resource Paths] and its subsections for further details
|
||||
on wildcard support with the `classpath*:` resource prefix.
|
||||
|
||||
A passed-in `ResourceLoader` (for example, one supplied via
|
||||
<<resources-resourceloaderaware,`ResourceLoaderAware`>> semantics) can be checked whether
|
||||
xref:core/resources.adoc#resources-resourceloaderaware[`ResourceLoaderAware`] semantics) can be checked whether
|
||||
it implements this extended interface too.
|
||||
|
||||
`PathMatchingResourcePatternResolver` is a standalone implementation that is usable
|
||||
@@ -452,7 +452,7 @@ For more information, see xref:core/beans/annotation-config/autowired.adoc[Using
|
||||
|
||||
NOTE: To load one or more `Resource` objects for a resource path that contains wildcards
|
||||
or makes use of the special `classpath*:` resource prefix, consider having an instance of
|
||||
<<resources-resourcepatternresolver,`ResourcePatternResolver`>> autowired into your
|
||||
xref:core/resources.adoc#resources-resourcepatternresolver[`ResourcePatternResolver`] autowired into your
|
||||
application components instead of `ResourceLoader`.
|
||||
|
||||
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
= Data Binding
|
||||
|
||||
Data binding is useful for binding user input to a target object where user input is a map
|
||||
with property paths as keys, following <<data-binding-conventions,JavaBeans conventions>>.
|
||||
with property paths as keys, following xref:data-binding-conventions[JavaBeans conventions].
|
||||
`DataBinder` is the main class that supports this, and it provides two ways to bind user
|
||||
input:
|
||||
|
||||
- <<data-binding-constructor-binding,Constructor binding>> - bind user input to a
|
||||
- xref:data-binding-constructor-binding[Constructor binding] - bind user input to a
|
||||
public data constructor, looking up constructor argument values in the user input.
|
||||
- <<data-binding-property-binding,Property binding>> - bind user input to setters,
|
||||
- xref:data-binding-property-binding[Property binding] - bind user input to setters,
|
||||
matching keys from the user input to properties of the target object structure.
|
||||
|
||||
You can apply both constructor and property binding or only one.
|
||||
@@ -32,7 +32,7 @@ WebFlux support a custom name mapping through the `@BindParam` annotation on con
|
||||
parameters or fields if present. If necessary, you can also configure a `NameResolver` on
|
||||
`DataBinder` to customize the argument name to use.
|
||||
|
||||
<<data-binding-conventions,Type conversion>> is applied as needed to convert user input.
|
||||
xref:data-binding-conventions[Type conversion] is applied as needed to convert user input.
|
||||
If the constructor parameter is an object, it is constructed recursively in the same
|
||||
manner, but through a nested property path. That means constructor binding creates both
|
||||
the target object and any objects it contains.
|
||||
@@ -103,7 +103,7 @@ details. The below table shows some examples of these conventions:
|
||||
(This next section is not vitally important to you if you do not plan to work with
|
||||
the `BeanWrapper` directly. If you use only the `DataBinder` and the `BeanFactory`
|
||||
and their default implementations, you should skip ahead to the
|
||||
<<data-binding-conversion,section on `PropertyEditors`>>.)
|
||||
xref:core/validation/data-binding.adoc#data-binding-conversion[section on `PropertyEditors`].)
|
||||
|
||||
The following two example classes use the `BeanWrapper` to get and set
|
||||
properties:
|
||||
@@ -347,10 +347,10 @@ recognized and used as the `PropertyEditor` for `Something`-typed properties.
|
||||
[literal,subs="verbatim,quotes"]
|
||||
----
|
||||
com
|
||||
└── example
|
||||
└── things
|
||||
├── *Something*
|
||||
└── *SomethingEditor* // the PropertyEditor for the Something class
|
||||
chank
|
||||
pop
|
||||
Something
|
||||
SomethingEditor // the PropertyEditor for the Something class
|
||||
----
|
||||
|
||||
Note that you can also use the standard `BeanInfo` JavaBeans mechanism here as well
|
||||
@@ -362,10 +362,10 @@ following example uses the `BeanInfo` mechanism to explicitly register one or mo
|
||||
[literal,subs="verbatim,quotes"]
|
||||
----
|
||||
com
|
||||
└── example
|
||||
└── things
|
||||
├── *Something*
|
||||
└── *SomethingBeanInfo* // the BeanInfo for the Something class
|
||||
chank
|
||||
pop
|
||||
Something
|
||||
SomethingBeanInfo // the BeanInfo for the Something class
|
||||
----
|
||||
|
||||
The following Java source code for the referenced `SomethingBeanInfo` class
|
||||
@@ -447,7 +447,7 @@ where it can be automatically detected and applied.
|
||||
Note that all bean factories and application contexts automatically use a number of
|
||||
built-in property editors, through their use of a `BeanWrapper` to
|
||||
handle property conversions. The standard property editors that the `BeanWrapper`
|
||||
registers are listed in the <<data-binding-conversion,previous section>>.
|
||||
registers are listed in the xref:core/validation/data-binding.adoc#data-binding-conversion[previous section].
|
||||
Additionally, ``ApplicationContext``s also override or add additional editors to handle
|
||||
resource lookups in a manner appropriate to the specific application context type.
|
||||
|
||||
@@ -576,7 +576,7 @@ You can write a corresponding registrar and reuse it in each case.
|
||||
`PropertyEditorRegistry`, an interface that is implemented by the Spring `BeanWrapper`
|
||||
(and `DataBinder`). `PropertyEditorRegistrar` instances are particularly convenient
|
||||
when used in conjunction with `CustomEditorConfigurer` (described
|
||||
<<data-binding-conversion-customeditor-registration,here>>), which exposes a property
|
||||
xref:core/validation/data-binding.adoc#data-binding-conversion-customeditor-registration[here]), which exposes a property
|
||||
called `setPropertyEditorRegistrars(..)`. `PropertyEditorRegistrar` instances added
|
||||
to a `CustomEditorConfigurer` in this fashion can easily be shared with `DataBinder` and
|
||||
Spring MVC controllers. Furthermore, it avoids the need for synchronization on custom
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
|
||||
This part of the appendix lists XML schemas for data access, including the following:
|
||||
|
||||
* <<xsd-schemas-tx,The `tx` Schema>>
|
||||
* <<xsd-schemas-jdbc,The `jdbc` Schema>>
|
||||
* xref:data-access/appendix.adoc#xsd-schemas-tx[The `tx` Schema]
|
||||
* xref:data-access/appendix.adoc#xsd-schemas-jdbc[The `jdbc` Schema]
|
||||
|
||||
[[xsd-schemas-tx]]
|
||||
=== The `tx` Schema
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
|
||||
This section covers:
|
||||
|
||||
* <<jdbc-datasource,Using `DataSource`>>
|
||||
* <<jdbc-DataSourceUtils,Using `DataSourceUtils`>>
|
||||
* <<jdbc-SmartDataSource,Implementing `SmartDataSource`>>
|
||||
* <<jdbc-AbstractDataSource,Extending `AbstractDataSource`>>
|
||||
* <<jdbc-SingleConnectionDataSource,Using `SingleConnectionDataSource`>>
|
||||
* <<jdbc-DriverManagerDataSource,Using `DriverManagerDataSource`>>
|
||||
* <<jdbc-TransactionAwareDataSourceProxy,Using `TransactionAwareDataSourceProxy`>>
|
||||
* <<jdbc-DataSourceTransactionManager,Using `DataSourceTransactionManager` / `JdbcTransactionManager`>>
|
||||
* xref:data-access/jdbc/connections.adoc#jdbc-datasource[Using `DataSource`]
|
||||
* xref:data-access/jdbc/connections.adoc#jdbc-DataSourceUtils[Using `DataSourceUtils`]
|
||||
* xref:data-access/jdbc/connections.adoc#jdbc-SmartDataSource[Implementing `SmartDataSource`]
|
||||
* xref:data-access/jdbc/connections.adoc#jdbc-AbstractDataSource[Extending `AbstractDataSource`]
|
||||
* xref:data-access/jdbc/connections.adoc#jdbc-SingleConnectionDataSource[Using `SingleConnectionDataSource`]
|
||||
* xref:data-access/jdbc/connections.adoc#jdbc-DriverManagerDataSource[Using `DriverManagerDataSource`]
|
||||
* xref:data-access/jdbc/connections.adoc#jdbc-TransactionAwareDataSourceProxy[Using `TransactionAwareDataSourceProxy`]
|
||||
* xref:data-access/jdbc/connections.adoc#jdbc-DataSourceTransactionManager[Using `DataSourceTransactionManager` / `JdbcTransactionManager`]
|
||||
|
||||
|
||||
[[jdbc-datasource]]
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
This section covers how to use the JDBC core classes to control basic JDBC processing,
|
||||
including error handling. It includes the following topics:
|
||||
|
||||
* <<jdbc-JdbcTemplate,Using `JdbcTemplate`>>
|
||||
* <<jdbc-NamedParameterJdbcTemplate,Using `NamedParameterJdbcTemplate`>>
|
||||
* <<jdbc-JdbcClient,Unified JDBC Query/Update Operations: `JdbcClient`>>
|
||||
* <<jdbc-SQLExceptionTranslator,Using `SQLExceptionTranslator`>>
|
||||
* <<jdbc-statements-executing,Running Statements>>
|
||||
* <<jdbc-statements-querying,Running Queries>>
|
||||
* <<jdbc-updates,Updating the Database>>
|
||||
* <<jdbc-auto-generated-keys,Retrieving Auto-generated Keys>>
|
||||
* xref:data-access/jdbc/core.adoc#jdbc-JdbcTemplate[Using `JdbcTemplate`]
|
||||
* xref:data-access/jdbc/core.adoc#jdbc-NamedParameterJdbcTemplate[Using `NamedParameterJdbcTemplate`]
|
||||
* xref:data-access/jdbc/core.adoc#jdbc-JdbcClient[Unified JDBC Query/Update Operations: `JdbcClient`]
|
||||
* xref:data-access/jdbc/core.adoc#jdbc-SQLExceptionTranslator[Using `SQLExceptionTranslator`]
|
||||
* xref:data-access/jdbc/core.adoc#jdbc-statements-executing[Running Statements]
|
||||
* xref:data-access/jdbc/core.adoc#jdbc-statements-querying[Running Queries]
|
||||
* xref:data-access/jdbc/core.adoc#jdbc-updates[Updating the Database]
|
||||
* xref:data-access/jdbc/core.adoc#jdbc-auto-generated-keys[Retrieving Auto-generated Keys]
|
||||
|
||||
|
||||
[[jdbc-JdbcTemplate]]
|
||||
@@ -349,7 +349,7 @@ The `JdbcTemplate` is stateful, in that it maintains a reference to a `DataSourc
|
||||
this state is not conversational state.
|
||||
|
||||
A common practice when using the `JdbcTemplate` class (and the associated
|
||||
<<jdbc-NamedParameterJdbcTemplate,`NamedParameterJdbcTemplate`>> class) is to
|
||||
xref:data-access/jdbc/core.adoc#jdbc-NamedParameterJdbcTemplate[`NamedParameterJdbcTemplate`] class) is to
|
||||
configure a `DataSource` in your Spring configuration file and then dependency-inject
|
||||
that shared `DataSource` bean into your DAO classes. The `JdbcTemplate` is created in
|
||||
the setter for the `DataSource` or in the constructor. This leads to DAOs that resemble the following:
|
||||
@@ -574,7 +574,7 @@ 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 <<jdbc-jdbctemplate-idioms,`JdbcTemplate` Best Practices>>
|
||||
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.
|
||||
|
||||
|
||||
|
||||
@@ -39,9 +39,9 @@ for further details on all supported options.
|
||||
This section covers how to select one of the three embedded databases that Spring
|
||||
supports. It includes the following topics:
|
||||
|
||||
* <<jdbc-embedded-database-using-HSQL,Using HSQL>>
|
||||
* <<jdbc-embedded-database-using-H2,Using H2>>
|
||||
* <<jdbc-embedded-database-using-Derby,Using Derby>>
|
||||
* xref:data-access/jdbc/embedded-database-support.adoc#jdbc-embedded-database-using-HSQL[Using HSQL]
|
||||
* xref:data-access/jdbc/embedded-database-support.adoc#jdbc-embedded-database-using-H2[Using H2]
|
||||
* xref:data-access/jdbc/embedded-database-support.adoc#jdbc-embedded-database-using-Derby[Using Derby]
|
||||
|
||||
[[jdbc-embedded-database-using-HSQL]]
|
||||
=== Using HSQL
|
||||
@@ -143,7 +143,7 @@ can be useful for one-offs when the embedded database does not need to be reused
|
||||
classes. However, if you wish to create an embedded database that is shared within a test suite,
|
||||
consider using the xref:testing/testcontext-framework.adoc[Spring TestContext Framework] and
|
||||
configuring the embedded database as a bean in the Spring `ApplicationContext` as described
|
||||
in <<jdbc-embedded-database,Creating an Embedded Database>>.
|
||||
in xref:data-access/jdbc/embedded-database-support.adoc#jdbc-embedded-database[Creating an Embedded Database].
|
||||
The following listing shows the test template:
|
||||
|
||||
[tabs]
|
||||
|
||||
@@ -10,7 +10,7 @@ procedures and run update, delete, and insert statements.
|
||||
[NOTE]
|
||||
====
|
||||
Many Spring developers believe that the various RDBMS operation classes described below
|
||||
(with the exception of the <<jdbc-StoredProcedure,`StoredProcedure`>> class) can often
|
||||
(with the exception of the xref:data-access/jdbc/object.adoc#jdbc-StoredProcedure[`StoredProcedure`] class) can often
|
||||
be replaced with straight `JdbcTemplate` calls. Often, it is simpler to write a DAO
|
||||
method that calls a method on a `JdbcTemplate` directly (as opposed to
|
||||
encapsulating a query as a full-blown class).
|
||||
@@ -266,7 +266,7 @@ The SQL type is specified using the `java.sql.Types` constants.
|
||||
|
||||
The first line (with the `SqlParameter`) declares an IN parameter. You can use IN parameters
|
||||
both for stored procedure calls and for queries using the `SqlQuery` and its
|
||||
subclasses (covered in <<jdbc-SqlQuery,Understanding `SqlQuery`>>).
|
||||
subclasses (covered in xref:data-access/jdbc/object.adoc#jdbc-SqlQuery[Understanding `SqlQuery`]).
|
||||
|
||||
The second line (with the `SqlOutParameter`) declares an `out` parameter to be used in the
|
||||
stored procedure call. There is also an `SqlInOutParameter` for `InOut` parameters
|
||||
|
||||
@@ -15,7 +15,7 @@ configuration options. You should instantiate the `SimpleJdbcInsert` in the data
|
||||
layer's initialization method. For this example, the initializing method is the
|
||||
`setDataSource` method. You do not need to subclass the `SimpleJdbcInsert` class. Instead,
|
||||
you can create a new instance and set the table name by using the `withTableName` method.
|
||||
Configuration methods for this class follow the `fluent` style that returns the instance
|
||||
Configuration methods for this class follow the `fluid` style that returns the instance
|
||||
of the `SimpleJdbcInsert`, which lets you chain all configuration methods. The following
|
||||
example uses only one configuration method (we show examples of multiple methods later):
|
||||
|
||||
@@ -349,11 +349,11 @@ parameters return the data read from the table.
|
||||
|
||||
You can declare `SimpleJdbcCall` in a manner similar to declaring `SimpleJdbcInsert`. You
|
||||
should instantiate and configure the class in the initialization method of your data-access
|
||||
layer. In contrast to the `StoredProcedure` class, you do not need to create a subclass,
|
||||
and you do not need to declare parameters that can be looked up in the database metadata.
|
||||
The following `SimpleJdbcCall` configuration example uses the preceding stored procedure.
|
||||
The only configuration option (other than the `DataSource`) is the name of the stored
|
||||
procedure.
|
||||
layer. Compared to the `StoredProcedure` class, you need not create a subclass
|
||||
and you need not to declare parameters that can be looked up in the database metadata.
|
||||
The following example of a `SimpleJdbcCall` configuration uses the preceding stored
|
||||
procedure (the only configuration option, in addition to the `DataSource`, is the name
|
||||
of the stored procedure):
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@@ -479,7 +479,7 @@ returned `out` parameters.
|
||||
Earlier in this chapter, we described how parameters are deduced from metadata, but you can declare them
|
||||
explicitly if you wish. You can do so by creating and configuring `SimpleJdbcCall` with
|
||||
the `declareParameters` method, which takes a variable number of `SqlParameter` objects
|
||||
as input. See the <<jdbc-params,next section>> for details on how to define an `SqlParameter`.
|
||||
as input. See the xref:data-access/jdbc/simple.adoc#jdbc-params[next section] for details on how to define an `SqlParameter`.
|
||||
|
||||
NOTE: Explicit declarations are necessary if the database you use is not a Spring-supported
|
||||
database. Currently, Spring supports metadata lookup of stored procedure calls for the
|
||||
|
||||
@@ -35,7 +35,7 @@ JDBC, the `JdbcTemplate` class mentioned in a xref:data-access/jdbc/core.adoc#jd
|
||||
provides connection handling and proper conversion of `SQLException` to the
|
||||
`DataAccessException` hierarchy, including translation of database-specific SQL error
|
||||
codes to meaningful exception classes. For ORM technologies, see the
|
||||
<<orm-exception-translation,next section>> for how to get the same exception
|
||||
xref:data-access/orm/general.adoc#orm-exception-translation[next section] for how to get the same exception
|
||||
translation benefits.
|
||||
|
||||
When it comes to transaction management, the `JdbcTemplate` class hooks in to the Spring
|
||||
|
||||
@@ -14,7 +14,7 @@ As of Spring Framework 7.0, Spring requires Hibernate ORM 7.x for Spring's
|
||||
`HibernateJpaVendorAdapter`.
|
||||
|
||||
The `org.springframework.orm.jpa.hibernate` package supersedes the former `orm.hibernate5`:
|
||||
now for use with Hibernate ORM 7.x, tightly integrated with `HibernateJpaVendorAdapter`
|
||||
now for use with Hibernate ORM 7.1+, tightly integrated with `HibernateJpaVendorAdapter`
|
||||
as well as supporting Hibernate's native `SessionFactory.getCurrentSession()` style.
|
||||
====
|
||||
|
||||
@@ -26,7 +26,7 @@ To avoid tying application objects to hard-coded resource lookups, you can defin
|
||||
resources (such as a JDBC `DataSource` or a Hibernate `SessionFactory`) as beans in the
|
||||
Spring container. Application objects that need to access resources receive references
|
||||
to such predefined instances through bean references, as illustrated in the DAO
|
||||
definition in the <<orm-hibernate-straight,next section>>.
|
||||
definition in the xref:data-access/orm/hibernate.adoc#orm-hibernate-straight[next section].
|
||||
|
||||
The following excerpt from an XML application context definition shows how to set up a
|
||||
JDBC `DataSource` and a Hibernate `SessionFactory` on top of it:
|
||||
|
||||
@@ -14,9 +14,9 @@ the underlying implementation in order to provide additional features.
|
||||
The Spring JPA support offers three ways of setting up the JPA `EntityManagerFactory`
|
||||
that is used by the application to obtain an entity manager.
|
||||
|
||||
* <<orm-jpa-setup-lemfb,Using `LocalEntityManagerFactoryBean`>>
|
||||
* <<orm-jpa-setup-jndi,Obtaining an EntityManagerFactory from JNDI>>
|
||||
* <<orm-jpa-setup-lcemfb,Using `LocalContainerEntityManagerFactoryBean`>>
|
||||
* xref:data-access/orm/jpa.adoc#orm-jpa-setup-lemfb[Using `LocalEntityManagerFactoryBean`]
|
||||
* xref:data-access/orm/jpa.adoc#orm-jpa-setup-jndi[Obtaining an EntityManagerFactory from JNDI]
|
||||
* xref:data-access/orm/jpa.adoc#orm-jpa-setup-lcemfb[Using `LocalContainerEntityManagerFactoryBean`]
|
||||
|
||||
[[orm-jpa-setup-lemfb]]
|
||||
=== Using `LocalEntityManagerFactoryBean`
|
||||
@@ -519,7 +519,7 @@ Spring JPA also lets a configured `JpaTransactionManager` expose a JPA transacti
|
||||
to JDBC access code that accesses the same `DataSource`, provided that the registered
|
||||
`JpaDialect` supports retrieval of the underlying JDBC `Connection`. Spring provides
|
||||
dialects for the EclipseLink and Hibernate JPA implementations. See the
|
||||
<<orm-jpa-dialect,next section>> for details on `JpaDialect`.
|
||||
xref:data-access/orm/jpa.adoc#orm-jpa-dialect[next section] for details on `JpaDialect`.
|
||||
|
||||
For JTA-style lazy retrieval of actual resource connections, Spring provides a
|
||||
corresponding `DataSource` proxy class for the target connection pool: see
|
||||
@@ -621,7 +621,7 @@ seamlessly integrating with `@Bean` style configuration (no `FactoryBean` involv
|
||||
====
|
||||
`LocalSessionFactoryBean` and `LocalSessionFactoryBuilder` support background
|
||||
bootstrapping, just as the JPA `LocalContainerEntityManagerFactoryBean` does.
|
||||
See <<orm-jpa-setup-background,Background Bootstrapping>> for an introduction.
|
||||
See xref:data-access/orm/jpa.adoc#orm-jpa-setup-background[Background Bootstrapping] for an introduction.
|
||||
|
||||
On `LocalSessionFactoryBean`, this is available through the `bootstrapExecutor`
|
||||
property. On the programmatic `LocalSessionFactoryBuilder`, an overloaded
|
||||
|
||||
@@ -17,9 +17,9 @@ stream, or a SAX handler.
|
||||
|
||||
Some of the benefits of using Spring for your O/X mapping needs are:
|
||||
|
||||
* <<oxm-ease-of-configuration,Ease of configuration>>
|
||||
* <<oxm-consistent-interfaces,Consistent Interfaces>>
|
||||
* <<oxm-consistent-exception-hierarchy,Consistent Exception Hierarchy>>
|
||||
* xref:data-access/oxm.adoc#oxm-ease-of-configuration[Ease of configuration]
|
||||
* xref:data-access/oxm.adoc#oxm-consistent-interfaces[Consistent Interfaces]
|
||||
* xref:data-access/oxm.adoc#oxm-consistent-exception-hierarchy[Consistent Exception Hierarchy]
|
||||
|
||||
[[oxm-ease-of-configuration]]
|
||||
=== Ease of configuration
|
||||
@@ -52,7 +52,7 @@ These runtime exceptions wrap the original exception so that no information is l
|
||||
[[oxm-marshaller-unmarshaller]]
|
||||
== `Marshaller` and `Unmarshaller`
|
||||
|
||||
As stated in the <<oxm-introduction,introduction>>, a marshaller serializes an object
|
||||
As stated in the xref:data-access/oxm.adoc#oxm-introduction[introduction], a marshaller serializes an object
|
||||
to XML, and an unmarshaller deserializes XML stream to an object. This section describes
|
||||
the two Spring interfaces used for this purpose.
|
||||
|
||||
@@ -334,8 +334,8 @@ preamble of the XML configuration file. The following example shows how to do so
|
||||
|
||||
The schema makes the following elements available:
|
||||
|
||||
* <<oxm-jaxb2-xsd,`jaxb2-marshaller`>>
|
||||
* <<oxm-jibx-xsd,`jibx-marshaller`>>
|
||||
* xref:data-access/oxm.adoc#oxm-jaxb2-xsd[`jaxb2-marshaller`]
|
||||
* xref:data-access/oxm.adoc#oxm-jibx-xsd[`jibx-marshaller`]
|
||||
|
||||
Each tag is explained in its respective marshaller's section. As an example, though,
|
||||
the configuration of a JAXB2 marshaller might resemble the following:
|
||||
@@ -354,7 +354,7 @@ The JAXB binding compiler translates a W3C XML Schema into one or more Java clas
|
||||
generate a schema from annotated Java classes.
|
||||
|
||||
Spring supports the JAXB 2.0 API as XML marshalling strategies, following the
|
||||
`Marshaller` and `Unmarshaller` interfaces described in <<oxm-marshaller-unmarshaller,`Marshaller` and `Unmarshaller`>>.
|
||||
`Marshaller` and `Unmarshaller` interfaces described in xref:data-access/oxm.adoc#oxm-marshaller-unmarshaller[`Marshaller` and `Unmarshaller`].
|
||||
The corresponding integration classes reside in the `org.springframework.oxm.jaxb`
|
||||
package.
|
||||
|
||||
|
||||
@@ -12,12 +12,12 @@ The Spring Framework's R2DBC abstraction framework consists of two different pac
|
||||
|
||||
* `core`: The `org.springframework.r2dbc.core` package contains the `DatabaseClient`
|
||||
class plus a variety of related classes. See
|
||||
<<r2dbc-core,Using the R2DBC Core Classes to Control Basic R2DBC Processing and Error Handling>>.
|
||||
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
|
||||
<<r2dbc-connections,Controlling Database Connections>>.
|
||||
xref:data-access/r2dbc.adoc#r2dbc-connections[Controlling Database Connections].
|
||||
|
||||
|
||||
[[r2dbc-core]]
|
||||
@@ -26,12 +26,12 @@ that you can use for testing and running unmodified R2DBC. See
|
||||
This section covers how to use the R2DBC core classes to control basic R2DBC processing,
|
||||
including error handling. It includes the following topics:
|
||||
|
||||
* <<r2dbc-DatabaseClient,Using `DatabaseClient`>>
|
||||
* <<r2dbc-DatabaseClient-examples-statement,Executing Statements>>
|
||||
* <<r2dbc-DatabaseClient-examples-query,Querying (`SELECT`)>>
|
||||
* <<r2dbc-DatabaseClient-examples-update,Updating (`INSERT`, `UPDATE`, and `DELETE`) with `DatabaseClient`>>
|
||||
* <<r2dbc-DatabaseClient-filter,Statement Filters>>
|
||||
* <<r2dbc-auto-generated-keys,Retrieving Auto-generated Keys>>
|
||||
* xref:data-access/r2dbc.adoc#r2dbc-DatabaseClient[Using `DatabaseClient`]
|
||||
* xref:data-access/r2dbc.adoc#r2dbc-DatabaseClient-examples-statement[Executing Statements]
|
||||
* xref:data-access/r2dbc.adoc#r2dbc-DatabaseClient-examples-query[Querying (`SELECT`)]
|
||||
* xref:data-access/r2dbc.adoc#r2dbc-DatabaseClient-examples-update[Updating (`INSERT`, `UPDATE`, and `DELETE`) with `DatabaseClient`]
|
||||
* 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`
|
||||
@@ -680,11 +680,11 @@ Kotlin::
|
||||
|
||||
This section covers:
|
||||
|
||||
* <<r2dbc-ConnectionFactory,Using `ConnectionFactory`>>
|
||||
* <<r2dbc-ConnectionFactoryUtils,Using `ConnectionFactoryUtils`>>
|
||||
* <<r2dbc-SingleConnectionFactory,Using `SingleConnectionFactory`>>
|
||||
* <<r2dbc-TransactionAwareConnectionFactoryProxy,Using `TransactionAwareConnectionFactoryProxy`>>
|
||||
* <<r2dbc-R2dbcTransactionManager,Using `R2dbcTransactionManager`>>
|
||||
* xref:data-access/r2dbc.adoc#r2dbc-ConnectionFactory[Using `ConnectionFactory`]
|
||||
* xref:data-access/r2dbc.adoc#r2dbc-ConnectionFactoryUtils[Using `ConnectionFactoryUtils`]
|
||||
* xref:data-access/r2dbc.adoc#r2dbc-SingleConnectionFactory[Using `SingleConnectionFactory`]
|
||||
* xref:data-access/r2dbc.adoc#r2dbc-TransactionAwareConnectionFactoryProxy[Using `TransactionAwareConnectionFactoryProxy`]
|
||||
* xref:data-access/r2dbc.adoc#r2dbc-R2dbcTransactionManager[Using `R2dbcTransactionManager`]
|
||||
|
||||
[[r2dbc-ConnectionFactory]]
|
||||
=== Using `ConnectionFactory`
|
||||
|
||||
+44
-20
@@ -8,7 +8,7 @@ danger of undue coupling, because code that is meant to be used transactionally
|
||||
almost always deployed that way anyway.
|
||||
|
||||
NOTE: The standard `jakarta.transaction.Transactional` annotation is also supported as
|
||||
a drop-in replacement for Spring's own annotation. Please refer to the JTA documentation
|
||||
a drop-in replacement to Spring's own annotation. Please refer to the JTA documentation
|
||||
for more details.
|
||||
|
||||
The ease-of-use afforded by the use of the `@Transactional` annotation is best
|
||||
@@ -77,7 +77,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
|
||||
<<transaction-declarative-annotations-method-visibility,method visibility>>
|
||||
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
|
||||
@@ -89,16 +89,47 @@ annotation in a `@Configuration` class. See the
|
||||
{spring-framework-api}/transaction/annotation/EnableTransactionManagement.html[javadoc]
|
||||
for full details.
|
||||
|
||||
The following example shows the configuration needed to enable annotation-driven transaction management:
|
||||
In XML configuration, the `<tx:annotation-driven/>` tag provides similar convenience:
|
||||
|
||||
include-code::./AppConfig[tag=snippet,indent=0]
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
<!-- from the file 'context.xml' -->
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:aop="http://www.springframework.org/schema/aop"
|
||||
xmlns:tx="http://www.springframework.org/schema/tx"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans
|
||||
https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/tx
|
||||
https://www.springframework.org/schema/tx/spring-tx.xsd
|
||||
http://www.springframework.org/schema/aop
|
||||
https://www.springframework.org/schema/aop/spring-aop.xsd">
|
||||
|
||||
TIP: In programmatic configuration, the `@EnableTransactionManagement` annotation uses any
|
||||
`TransactionManager` bean in the context. In XML configuration, you can omit
|
||||
the `transaction-manager` attribute in the `<tx:annotation-driven/>` tag if the bean
|
||||
name of the `TransactionManager` that you want to wire in has the name `transactionManager`.
|
||||
If the `TransactionManager` bean has any other name, you have to use the
|
||||
`transaction-manager` attribute explicitly, as in the preceding example.
|
||||
<!-- this is the service object that we want to make transactional -->
|
||||
<bean id="fooService" class="x.y.service.DefaultFooService"/>
|
||||
|
||||
<!-- enable the configuration of transactional behavior based on annotations -->
|
||||
<!-- a TransactionManager is still required -->
|
||||
<tx:annotation-driven transaction-manager="txManager"/> <1>
|
||||
|
||||
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
|
||||
<!-- (this dependency is defined somewhere else) -->
|
||||
<property name="dataSource" ref="dataSource"/>
|
||||
</bean>
|
||||
|
||||
<!-- other <bean/> definitions here -->
|
||||
|
||||
</beans>
|
||||
----
|
||||
<1> The line that makes the bean instance transactional.
|
||||
|
||||
TIP: You can omit the `transaction-manager` attribute in the `<tx:annotation-driven/>`
|
||||
tag if the bean name of the `TransactionManager` that you want to wire in has the name
|
||||
`transactionManager`. If the `TransactionManager` bean that you want to dependency-inject
|
||||
has any other name, you have to use the `transaction-manager` attribute, as in the
|
||||
preceding example.
|
||||
|
||||
Reactive transactional methods use reactive return types in contrast to imperative
|
||||
programming arrangements as the following listing shows:
|
||||
@@ -167,11 +198,6 @@ 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.
|
||||
|
||||
TIP: When `@Transactional` is combined with `@Retryable`, the retry advice is applied
|
||||
outermost, so each retry attempt runs in its own transaction. See
|
||||
xref:core/resilience.adoc#resilience-annotations-retryable-combining-transactional[Combining `@Retryable` with `@Transactional`]
|
||||
for details.
|
||||
|
||||
[[transaction-declarative-annotations-method-visibility]]
|
||||
.Method visibility and `@Transactional` in proxy mode
|
||||
[NOTE]
|
||||
@@ -208,10 +234,8 @@ on an interface, a class definition, or a method on a class. However, the mere p
|
||||
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 examples that use programmatic configuration,
|
||||
the `@EnableTransactionManagement` annotation switches on actual transaction management
|
||||
at runtime. Whereas, in the preceding example that uses XML configuration, the
|
||||
`<tx:annotation-driven/>` element switches on actual transaction management at runtime.
|
||||
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 methods of concrete classes with the
|
||||
`@Transactional` annotation, rather than relying on annotated methods in interfaces,
|
||||
@@ -365,7 +389,7 @@ properties of the `@Transactional` annotation:
|
||||
|===
|
||||
| Property| Type| Description
|
||||
|
||||
| <<tx-multiple-tx-mgrs-with-attransactional,value>>
|
||||
| xref:data-access/transaction/declarative/annotations.adoc#tx-multiple-tx-mgrs-with-attransactional[value]
|
||||
| `String`
|
||||
| Optional qualifier that specifies the transaction manager to be used.
|
||||
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ where all the underlying resources have to participate in the service-level tran
|
||||
|
||||
NOTE: By default, a participating transaction joins the characteristics of the outer scope,
|
||||
silently ignoring the local isolation level, timeout value, or read-only flag (if any).
|
||||
Consider switching the `validateExistingTransaction` flag to `true` on your transaction
|
||||
Consider switching the `validateExistingTransactions` flag to `true` on your transaction
|
||||
manager if you want isolation level declarations to be rejected when participating in
|
||||
an existing transaction with a different isolation level. This non-lenient mode also
|
||||
rejects read-only mismatches (that is, an inner read-write transaction that tries to participate
|
||||
|
||||
@@ -179,7 +179,7 @@ infrastructure.
|
||||
|
||||
NOTE: The preceding definition of the `dataSource` bean uses the `<jndi-lookup/>` tag
|
||||
from the `jee` namespace. For more information see
|
||||
xref:integration/appendix.adoc#appendix.xsd-schemas-jee[The JEE Schema].
|
||||
xref:integration/appendix.adoc#xsd-schemas-jee[The JEE Schema].
|
||||
|
||||
NOTE: If you use JTA, your transaction manager definition should look the same, regardless
|
||||
of what data access technology you use, be it JDBC, Hibernate JPA, or any other supported
|
||||
|
||||
@@ -3,22 +3,21 @@
|
||||
:page-aliases: integration/class-data-sharing.adoc
|
||||
:page-aliases: integration/cds.adoc
|
||||
|
||||
The ahead-of-time cache is a JVM feature introduced in Java 24 via
|
||||
The ahead-of-time cache is a JVM feature introduced in Java 24 via the
|
||||
https://openjdk.org/jeps/483[JEP 483] that can help reduce the startup time and memory
|
||||
footprint of Java applications. AOT cache is a natural evolution of
|
||||
https://docs.oracle.com/en/java/javase/17/vm/class-data-sharing.html[Class Data Sharing (CDS)].
|
||||
footprint of Java applications. AOT cache is a natural evolution of https://docs.oracle.com/en/java/javase/17/vm/class-data-sharing.html[Class Data Sharing (CDS)].
|
||||
Spring Framework supports both CDS and AOT cache, and it is recommended that you use the
|
||||
latter if available in the JVM version you are using (Java 24+).
|
||||
later if available in the JVM version your are using (Java 24+).
|
||||
|
||||
To use this feature, an AOT cache should be created for the particular classpath of the
|
||||
application. It is possible to create this cache on the deployed instance, or during a
|
||||
training run performed for example when packaging the application thanks to a hook-point
|
||||
training run performed for example when packaging the application thanks to an hook-point
|
||||
provided by the Spring Framework to ease such use case. Once the cache is available, users
|
||||
should opt in to use it via a JVM flag.
|
||||
|
||||
NOTE: If you are using Spring Boot, it is highly recommended to leverage its
|
||||
{spring-boot-docs-ref}/packaging/efficient.html#packaging.efficient.unpacking[executable JAR unpacking support]
|
||||
which is designed to fulfill the class loading requirements of both the AOT cache and CDS.
|
||||
which is designed to fulfill the class loading requirements of both AOT cache and CDS.
|
||||
|
||||
== Creating the cache
|
||||
|
||||
@@ -30,22 +29,14 @@ invoked; but the lifecycle has not started, and the `ContextRefreshedEvent` has
|
||||
been published.
|
||||
|
||||
To create the cache during the training run, it is possible to specify the `-Dspring.context.exit=onRefresh`
|
||||
JVM flag to start and then exit your Spring application once the
|
||||
JVM flag to start then exit your Spring application once the
|
||||
`ApplicationContext` has refreshed:
|
||||
|
||||
|
||||
--
|
||||
[tabs]
|
||||
======
|
||||
|
||||
AOT cache (Java 25+)::
|
||||
+
|
||||
[source,bash,subs="verbatim,quotes"]
|
||||
----
|
||||
java -XX:AOTCacheOutput=app.aot -Dspring.context.exit=onRefresh -jar application.jar ...
|
||||
----
|
||||
|
||||
AOT cache (Java 24)::
|
||||
AOT cache::
|
||||
+
|
||||
[source,bash,subs="verbatim,quotes"]
|
||||
----
|
||||
@@ -64,12 +55,6 @@ java -XX:ArchiveClassesAtExit=app.jsa -Dspring.context.exit=onRefresh ...
|
||||
======
|
||||
--
|
||||
|
||||
NOTE: With Java 25+, AOT cache stores, among other things, the
|
||||
https://openjdk.org/jeps/515[method profiling information]. Therefore, to benefit of this capability,
|
||||
it is recommended to create an AOT cache for an application that experienced a portion of a
|
||||
production-like workflow instead of using the `-Dspring.context.exit=onRefresh` flag which designed to
|
||||
optimize only the startup of your application.
|
||||
|
||||
== Using the cache
|
||||
|
||||
Once the cache file has been created, you can use it to start your application faster:
|
||||
@@ -97,7 +82,7 @@ java -XX:SharedArchiveFile=app.jsa ...
|
||||
|
||||
Pay attention to the logs and the startup time to check if the AOT cache is used successfully.
|
||||
To figure out how effective the cache is, you can enable class loading logs by adding
|
||||
an extra attribute: `-Xlog:class+load:file=aot-cache.log`. This creates an `aot-cache.log` with
|
||||
an extra attribute: `-Xlog:class+load:file=aot-cache.log`. This creates a `aot-cache.log` with
|
||||
every attempt to load a class and its source. Classes that are loaded from the cache should have
|
||||
a "shared objects file" source, as shown in the following example:
|
||||
|
||||
@@ -110,11 +95,11 @@ a "shared objects file" source, as shown in the following example:
|
||||
[0.151s][info][class,load] org.springframework.context.MessageSource source: shared objects file
|
||||
----
|
||||
|
||||
If the AOT cache cannot be enabled or if you have a large number of classes that are not loaded from
|
||||
If the AOT cache can't be enabled or if you have a large number of classes that are not loaded from
|
||||
the cache, make sure that the following conditions are fulfilled when creating and using the cache:
|
||||
|
||||
- The very same JVM must be used.
|
||||
- The classpath must be specified as a JAR or a list of JARs, and avoid the usage of directories and `*` wildcard characters.
|
||||
- The timestamps of the JARs must be preserved.
|
||||
- When using the cache, the classpath must be the same as the one used to create it, in the same order.
|
||||
Additional JARs or directories can be specified *at the end* (but will not be cached).
|
||||
- When using the cache, the classpath must be the same than the one used to create it, in the same order.
|
||||
Additional JARs or directories can be specified *at the end* (but won't be cached).
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
[[appendix]]
|
||||
= Appendix
|
||||
|
||||
|
||||
|
||||
|
||||
[[appendix.xsd-schemas]]
|
||||
== XML Schemas
|
||||
|
||||
This part of the appendix lists XML schemas related to integration technologies.
|
||||
|
||||
|
||||
|
||||
[[appendix.xsd-schemas-jee]]
|
||||
=== The `jee` Schema
|
||||
|
||||
@@ -167,7 +172,7 @@ different properties with `jee`:
|
||||
|
||||
The `<jee:local-slsb/>` element configures a reference to a local EJB Stateless Session Bean.
|
||||
|
||||
The following example shows how to configure a reference to a local EJB Stateless Session Bean
|
||||
The following example shows how to configures a reference to a local EJB Stateless Session Bean
|
||||
without `jee`:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
@@ -179,7 +184,7 @@ without `jee`:
|
||||
</bean>
|
||||
----
|
||||
|
||||
The following example shows how to configure a reference to a local EJB Stateless Session Bean
|
||||
The following example shows how to configures a reference to a local EJB Stateless Session Bean
|
||||
with `jee`:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
@@ -195,7 +200,7 @@ with `jee`:
|
||||
|
||||
The `<jee:local-slsb/>` element configures a reference to a local EJB Stateless Session Bean.
|
||||
|
||||
The following example shows how to configure a reference to a local EJB Stateless Session Bean
|
||||
The following example shows how to configures a reference to a local EJB Stateless Session Bean
|
||||
and a number of properties without `jee`:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
@@ -210,7 +215,7 @@ and a number of properties without `jee`:
|
||||
</bean>
|
||||
----
|
||||
|
||||
The following example shows how to configure a reference to a local EJB Stateless Session Bean
|
||||
The following example shows how to configures a reference to a local EJB Stateless Session Bean
|
||||
and a number of properties with `jee`:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
@@ -229,7 +234,7 @@ and a number of properties with `jee`:
|
||||
|
||||
The `<jee:remote-slsb/>` element configures a reference to a `remote` EJB Stateless Session Bean.
|
||||
|
||||
The following example shows how to configure a reference to a remote EJB Stateless Session Bean
|
||||
The following example shows how to configures a reference to a remote EJB Stateless Session Bean
|
||||
without `jee`:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
@@ -246,7 +251,7 @@ without `jee`:
|
||||
</bean>
|
||||
----
|
||||
|
||||
The following example shows how to configure a reference to a remote EJB Stateless Session Bean
|
||||
The following example shows how to configures a reference to a remote EJB Stateless Session Bean
|
||||
with `jee`:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
@@ -308,7 +313,7 @@ xref:integration/jmx/naming.adoc#jmx-context-mbeanexport[Configuring Annotation-
|
||||
=== The `cache` Schema
|
||||
|
||||
You can use the `cache` elements to enable support for Spring's `@CacheEvict`, `@CachePut`,
|
||||
and `@Caching` annotations. The `cache` schema also supports declarative XML-based caching. See
|
||||
and `@Caching` annotations. It it also supports declarative XML-based caching. See
|
||||
xref:integration/cache/annotations.adoc#cache-annotation-enable[Enabling Caching Annotations] and
|
||||
xref:integration/cache/declarative-xml.adoc[Declarative XML-based Caching] for details.
|
||||
|
||||
|
||||
@@ -44,11 +44,6 @@ The following example uses `@Cacheable` on the `findBook` method with multiple c
|
||||
public Book findBook(ISBN isbn) {...}
|
||||
----
|
||||
|
||||
TIP: When `@Cacheable` is combined with `@Retryable`, the retry advice is applied
|
||||
outermost, so each retry attempt checks the cache before invoking the method. See
|
||||
xref:core/resilience.adoc#resilience-annotations-retryable-combining-cacheable[Combining `@Retryable` with `@Cacheable`]
|
||||
for details.
|
||||
|
||||
[[cache-annotations-cacheable-default-key]]
|
||||
=== Default Key Generation
|
||||
|
||||
@@ -103,7 +98,7 @@ 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
|
||||
<<cache-annotations-cacheable-default-key,default generator>>,
|
||||
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.
|
||||
|
||||
@@ -165,7 +160,7 @@ 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 <<cache-annotations-cacheable-key,key generation>>.
|
||||
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`:
|
||||
@@ -416,11 +411,6 @@ confirm the exclusion.
|
||||
As of 6.1, `@CachePut` takes `CompletableFuture` and reactive return types into account,
|
||||
performing the put operation whenever the produced object is available.
|
||||
|
||||
TIP: When `@CachePut` is combined with `@Retryable`, the retry advice is applied
|
||||
outermost, so each successful retry attempt updates the cache; a failed attempt does
|
||||
not. See xref:core/resilience.adoc#resilience-annotations-retryable-combining-cacheable[Combining `@Retryable` with `@Cacheable`]
|
||||
for details.
|
||||
|
||||
|
||||
[[cache-annotations-evict]]
|
||||
== The `@CacheEvict` Annotation
|
||||
@@ -466,12 +456,6 @@ and, thus, requires a result.
|
||||
As of 6.1, `@CacheEvict` takes `CompletableFuture` and reactive return types into account,
|
||||
performing an after-invocation evict operation whenever processing has completed.
|
||||
|
||||
TIP: When `@CacheEvict` is combined with `@Retryable`, the retry advice is applied
|
||||
outermost, so eviction runs again on every retry attempt -- and, with
|
||||
`beforeInvocation=true`, before each attempt regardless of its outcome. See
|
||||
xref:core/resilience.adoc#resilience-annotations-retryable-combining-cacheable[Combining `@Retryable` with `@Cacheable`]
|
||||
for details.
|
||||
|
||||
|
||||
[[cache-annotations-caching]]
|
||||
== The `@Caching` Annotation
|
||||
@@ -700,5 +684,5 @@ preceding code:
|
||||
|
||||
Even though `@SlowService` is not a Spring annotation, the container automatically picks
|
||||
up its declaration at runtime and understands its meaning. Note that, as mentioned
|
||||
<<cache-annotation-enable,earlier>>,
|
||||
xref:integration/cache/annotations.adoc#cache-annotation-enable[earlier],
|
||||
annotation-driven behavior needs to be enabled.
|
||||
|
||||
+2
-2
@@ -29,13 +29,13 @@ or eviction contracts.
|
||||
== Ehcache-based Cache
|
||||
|
||||
Ehcache 3.x is fully JSR-107 compliant and no dedicated support is required for it. See
|
||||
<<cache-store-configuration-jsr107,JSR-107 Cache>> for details.
|
||||
xref:integration/cache/store-configuration.adoc#cache-store-configuration-jsr107[JSR-107 Cache] for details.
|
||||
|
||||
|
||||
[[cache-store-configuration-caffeine]]
|
||||
== Caffeine Cache
|
||||
|
||||
Caffeine is a rewrite of Guava's cache, and its implementation is located in the
|
||||
Caffeine is a Java 8 rewrite of Guava's cache, and its implementation is located in the
|
||||
`org.springframework.cache.caffeine` package and provides access to several features
|
||||
of Caffeine.
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user