Compare commits

..

1 Commits

Author SHA1 Message Date
Stéphane Nicoll 34764252dc Release v6.1.12 2024-08-14 12:38:39 +02:00
1979 changed files with 15443 additions and 73751 deletions
@@ -7,10 +7,6 @@ inputs:
token:
description: Token to use for authentication with GitHub
required: true
pre-release:
description: Whether the release is a pre-release (a milestone or release candidate)
required: false
default: 'false'
runs:
using: composite
steps:
@@ -24,4 +20,4 @@ runs:
env:
GITHUB_TOKEN: ${{ inputs.token }}
shell: bash
run: gh release create ${{ format('v{0}', inputs.milestone) }} --notes-file changelog.md ${{ inputs.pre-release == 'true' && '--prerelease' || '' }}
run: gh release create ${{ format('v{0}', inputs.milestone) }} --notes-file changelog.md
@@ -1,17 +0,0 @@
name: Print JVM thread dumps
description: Prints a thread dump for all running JVMs
runs:
using: composite
steps:
- if: ${{ runner.os == 'Linux' }}
shell: bash
run: |
for jvm_pid in $(jps -q -J-XX:+PerfDisableSharedMem); do
jcmd $jvm_pid Thread.print
done
- if: ${{ runner.os == 'Windows' }}
shell: powershell
run: |
foreach ($jvm_pid in $(jps -q -J-XX:+PerfDisableSharedMem)) {
jcmd $jvm_pid Thread.print
}
@@ -2,7 +2,7 @@ name: Build and Deploy Snapshot
on:
push:
branches:
- main
- 6.1.x
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
@@ -26,7 +26,7 @@ jobs:
uri: 'https://repo.spring.io'
username: ${{ secrets.ARTIFACTORY_USERNAME }}
password: ${{ secrets.ARTIFACTORY_PASSWORD }}
build-name: 'spring-framework-6.2.x'
build-name: 'spring-framework-6.1.x'
repository: 'libs-snapshot-local'
folder: 'deployment-repository'
signing-key: ${{ secrets.GPG_PRIVATE_KEY }}
-38
View File
@@ -1,38 +0,0 @@
name: Build Pull Request
on: pull_request
permissions:
contents: read
jobs:
build:
name: Build Pull Request
runs-on: ubuntu-latest
timeout-minutes: 60
if: ${{ github.repository == 'spring-projects/spring-framework' }}
steps:
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'liberica'
- name: Check Out
uses: actions/checkout@v4
- name: Validate Gradle Wrapper
uses: gradle/actions/wrapper-validation@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3.5.0
- name: Set Up Gradle
uses: gradle/actions/setup-gradle@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3.5.0
- name: Build
env:
CI: 'true'
GRADLE_ENTERPRISE_URL: 'https://ge.spring.io'
run: ./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --no-parallel --continue build
- name: Print JVM Thread Dumps When Cancelled
uses: ./.github/actions/print-jvm-thread-dumps
if: cancelled()
- name: Upload Build Reports
uses: actions/upload-artifact@v4
if: failure()
with:
name: build-reports
path: '**/build/reports/'
+3 -2
View File
@@ -1,7 +1,8 @@
name: CI
on:
schedule:
- cron: '30 9 * * *'
push:
branches:
- 6.1.x
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
-79
View File
@@ -1,79 +0,0 @@
name: Release Milestone
on:
push:
tags:
- v6.2.0-M[1-9]
- v6.2.0-RC[1-9]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
build-and-stage-release:
if: ${{ github.repository == 'spring-projects/spring-framework' }}
name: Build and Stage Release
runs-on: ubuntu-latest
steps:
- name: Check Out Code
uses: actions/checkout@v4
- name: Build and Publish
id: build-and-publish
uses: ./.github/actions/build
with:
develocity-access-key: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
publish: true
- name: Stage Release
uses: spring-io/artifactory-deploy-action@26bbe925a75f4f863e1e529e85be2d0093cac116 # v0.0.1
with:
uri: 'https://repo.spring.io'
username: ${{ secrets.ARTIFACTORY_USERNAME }}
password: ${{ secrets.ARTIFACTORY_PASSWORD }}
build-name: ${{ format('spring-framework-{0}', steps.build-and-publish.outputs.version)}}
repository: 'libs-staging-local'
folder: 'deployment-repository'
signing-key: ${{ secrets.GPG_PRIVATE_KEY }}
signing-passphrase: ${{ secrets.GPG_PASSPHRASE }}
artifact-properties: |
/**/framework-api-*.zip::zip.name=spring-framework,zip.deployed=false
/**/framework-api-*-docs.zip::zip.type=docs
/**/framework-api-*-schema.zip::zip.type=schema
outputs:
version: ${{ steps.build-and-publish.outputs.version }}
verify:
name: Verify
needs: build-and-stage-release
uses: ./.github/workflows/verify.yml
with:
staging: true
version: ${{ needs.build-and-stage-release.outputs.version }}
secrets:
google-chat-webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
repository-password: ${{ secrets.ARTIFACTORY_PASSWORD }}
repository-username: ${{ secrets.ARTIFACTORY_USERNAME }}
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
promote-release:
name: Promote Release
needs:
- build-and-stage-release
- verify
runs-on: ubuntu-latest
steps:
- name: Set up JFrog CLI
uses: jfrog/setup-jfrog-cli@105617d23456a69a92485207c4f28ae12297581d # v4.2.1
env:
JF_ENV_SPRING: ${{ secrets.JF_ARTIFACTORY_SPRING }}
- name: Promote build
run: jfrog rt build-promote ${{ format('spring-framework-{0}', needs.build-and-stage-release.outputs.version)}} ${{ github.run_number }} libs-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 }}
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
pre-release: true
+1 -1
View File
@@ -2,7 +2,7 @@ name: Release
on:
push:
tags:
- v6.2.[0-9]+
- v6.1.[0-9]+
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
@@ -1,35 +0,0 @@
name: Update Antora UI Spring
on:
schedule:
- cron: '0 10 * * *' # Once per day at 10am UTC
workflow_dispatch:
permissions:
pull-requests: write
issues: write
contents: write
jobs:
update-antora-ui-spring:
runs-on: ubuntu-latest
name: Update on Supported Branches
strategy:
matrix:
branch: [ '6.0.x', '6.1.x', 'main' ]
steps:
- uses: spring-io/spring-doc-actions/update-antora-spring-ui@5a57bcc6a0da2a1474136cf29571b277850432bc
name: Update
with:
docs-branch: ${{ matrix.branch }}
token: ${{ secrets.GITHUB_TOKEN }}
antora-file-path: 'framework-docs/antora-playbook.yml'
update-antora-ui-spring-docs-build:
runs-on: ubuntu-latest
name: Update on docs-build
steps:
- uses: spring-io/spring-doc-actions/update-antora-spring-ui@5a57bcc6a0da2a1474136cf29571b277850432bc
name: Update
with:
docs-branch: 'docs-build'
token: ${{ secrets.GITHUB_TOKEN }}
+1 -1
View File
@@ -1,4 +1,4 @@
# <img src="framework-docs/src/docs/spring-framework.png" width="80" height="80"> Spring Framework [![Build Status](https://github.com/spring-projects/spring-framework/actions/workflows/build-and-deploy-snapshot.yml/badge.svg?branch=main)](https://github.com/spring-projects/spring-framework/actions/workflows/build-and-deploy-snapshot.yml?query=branch%3Amain) [![Revved up by Develocity](https://img.shields.io/badge/Revved%20up%20by-Develocity-06A0CE?logo=Gradle&labelColor=02303A)](https://ge.spring.io/scans?search.rootProjectNames=spring)
# <img src="framework-docs/src/docs/spring-framework.png" width="80" height="80"> Spring Framework [![Build Status](https://github.com/spring-projects/spring-framework/actions/workflows/build-and-deploy-snapshot.yml/badge.svg?branch=6.1.x)](https://github.com/spring-projects/spring-framework/actions/workflows/build-and-deploy-snapshot.yml?query=branch%3A6.1.x) [![Revved up by Develocity](https://img.shields.io/badge/Revved%20up%20by-Develocity-06A0CE?logo=Gradle&labelColor=02303A)](https://ge.spring.io/scans?search.rootProjectNames=spring)
This is the home of the Spring Framework: the foundation for all [Spring projects](https://spring.io/projects). Collectively the Spring Framework and the family of Spring projects are often referred to simply as "Spring".
+1 -2
View File
@@ -2,14 +2,13 @@ plugins {
id 'io.freefair.aspectj' version '8.4' apply false
// kotlinVersion is managed in gradle.properties
id 'org.jetbrains.kotlin.plugin.serialization' version "${kotlinVersion}" apply false
id 'org.jetbrains.dokka' version '1.9.20'
id 'org.jetbrains.dokka' version '1.8.20'
id 'com.github.ben-manes.versions' version '0.51.0'
id 'com.github.bjornvester.xjc' version '1.8.2' apply false
id 'de.undercouch.download' version '5.4.0'
id 'io.github.goooler.shadow' version '8.1.8' apply false
id 'me.champeau.jmh' version '0.7.2' apply false
id 'me.champeau.mrjar' version '0.1.1'
id "net.ltgt.errorprone" version "3.1.0" apply false
}
ext {
-1
View File
@@ -21,7 +21,6 @@ asciidoc:
table-stripes: 'odd'
include-java: 'example$docs-src/main/java/org/springframework/docs'
include-kotlin: 'example$docs-src/main/kotlin/org/springframework/docs'
include-xml: 'example$docs-src/main/resources/org/springframework/docs'
spring-site: 'https://spring.io'
spring-site-blog: '{spring-site}/blog'
spring-site-cve: "{spring-site}/security"
-16
View File
@@ -42,31 +42,15 @@ repositories {
}
dependencies {
api(project(":spring-aspects"))
api(project(":spring-context"))
api(project(":spring-context-support"))
api(project(":spring-jdbc"))
api(project(":spring-jms"))
api(project(":spring-test"))
api(project(":spring-web"))
api(project(":spring-webflux"))
api(project(":spring-webmvc"))
api(project(":spring-websocket"))
api("com.fasterxml.jackson.core:jackson-databind")
api("com.fasterxml.jackson.module:jackson-module-parameter-names")
api("com.mchange:c3p0:0.9.5.5")
api("com.oracle.database.jdbc:ojdbc11")
api("io.projectreactor.netty:reactor-netty-http")
api("jakarta.jms:jakarta.jms-api")
api("jakarta.servlet:jakarta.servlet-api")
api("jakarta.resource:jakarta.resource-api")
api("jakarta.validation:jakarta.validation-api")
api("javax.cache:cache-api")
api("org.apache.activemq:activemq-ra:6.1.2")
api("org.apache.commons:commons-dbcp2:2.11.0")
api("org.aspectj:aspectjweaver")
api("org.eclipse.jetty.websocket:jetty-websocket-jetty-api")
api("org.jetbrains.kotlin:kotlin-stdlib")
implementation(project(":spring-core-test"))
+81 -93
View File
@@ -106,6 +106,87 @@
*** xref:core/appendix/xsd-schemas.adoc[]
*** xref:core/appendix/xml-custom.adoc[]
*** xref:core/appendix/application-startup-steps.adoc[]
* xref:testing.adoc[]
** xref:testing/introduction.adoc[]
** xref:testing/unit.adoc[]
** xref:testing/integration.adoc[]
** xref:testing/support-jdbc.adoc[]
** xref:testing/testcontext-framework.adoc[]
*** xref:testing/testcontext-framework/key-abstractions.adoc[]
*** xref:testing/testcontext-framework/bootstrapping.adoc[]
*** xref:testing/testcontext-framework/tel-config.adoc[]
*** 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/xml.adoc[]
**** xref:testing/testcontext-framework/ctx-management/groovy.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[]
**** xref:testing/testcontext-framework/ctx-management/inheritance.adoc[]
**** xref:testing/testcontext-framework/ctx-management/env-profiles.adoc[]
**** xref:testing/testcontext-framework/ctx-management/property-sources.adoc[]
**** xref:testing/testcontext-framework/ctx-management/dynamic-property-sources.adoc[]
**** 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/failure-threshold.adoc[]
**** xref:testing/testcontext-framework/ctx-management/hierarchies.adoc[]
*** xref:testing/testcontext-framework/fixture-di.adoc[]
*** xref:testing/testcontext-framework/web-scoped-beans.adoc[]
*** xref:testing/testcontext-framework/tx.adoc[]
*** xref:testing/testcontext-framework/executing-sql.adoc[]
*** xref:testing/testcontext-framework/parallel-test-execution.adoc[]
*** xref:testing/testcontext-framework/support-classes.adoc[]
*** xref:testing/testcontext-framework/aot.adoc[]
** xref:testing/webtestclient.adoc[]
** xref:testing/spring-mvc-test-framework.adoc[]
*** xref:testing/spring-mvc-test-framework/server.adoc[]
*** xref:testing/spring-mvc-test-framework/server-static-imports.adoc[]
*** xref:testing/spring-mvc-test-framework/server-setup-options.adoc[]
*** xref:testing/spring-mvc-test-framework/server-setup-steps.adoc[]
*** xref:testing/spring-mvc-test-framework/server-performing-requests.adoc[]
*** xref:testing/spring-mvc-test-framework/server-defining-expectations.adoc[]
*** xref:testing/spring-mvc-test-framework/async-requests.adoc[]
*** xref:testing/spring-mvc-test-framework/vs-streaming-response.adoc[]
*** xref:testing/spring-mvc-test-framework/server-filters.adoc[]
*** xref:testing/spring-mvc-test-framework/vs-end-to-end-integration-tests.adoc[]
*** xref:testing/spring-mvc-test-framework/server-resources.adoc[]
*** xref:testing/spring-mvc-test-framework/server-htmlunit.adoc[]
**** xref:testing/spring-mvc-test-framework/server-htmlunit/why.adoc[]
**** xref:testing/spring-mvc-test-framework/server-htmlunit/mah.adoc[]
**** xref:testing/spring-mvc-test-framework/server-htmlunit/webdriver.adoc[]
**** xref:testing/spring-mvc-test-framework/server-htmlunit/geb.adoc[]
** xref:testing/spring-mvc-test-client.adoc[]
** xref:testing/appendix.adoc[]
*** xref:testing/annotations.adoc[]
**** xref:testing/annotations/integration-standard.adoc[]
**** xref:testing/annotations/integration-spring.adoc[]
***** xref:testing/annotations/integration-spring/annotation-bootstrapwith.adoc[]
***** xref:testing/annotations/integration-spring/annotation-contextconfiguration.adoc[]
***** xref:testing/annotations/integration-spring/annotation-webappconfiguration.adoc[]
***** xref:testing/annotations/integration-spring/annotation-contexthierarchy.adoc[]
***** xref:testing/annotations/integration-spring/annotation-contextcustomizerfactories.adoc[]
***** xref:testing/annotations/integration-spring/annotation-activeprofiles.adoc[]
***** xref:testing/annotations/integration-spring/annotation-testpropertysource.adoc[]
***** xref:testing/annotations/integration-spring/annotation-dynamicpropertysource.adoc[]
***** xref:testing/annotations/integration-spring/annotation-dirtiescontext.adoc[]
***** xref:testing/annotations/integration-spring/annotation-testexecutionlisteners.adoc[]
***** xref:testing/annotations/integration-spring/annotation-recordapplicationevents.adoc[]
***** xref:testing/annotations/integration-spring/annotation-commit.adoc[]
***** xref:testing/annotations/integration-spring/annotation-rollback.adoc[]
***** xref:testing/annotations/integration-spring/annotation-beforetransaction.adoc[]
***** xref:testing/annotations/integration-spring/annotation-aftertransaction.adoc[]
***** xref:testing/annotations/integration-spring/annotation-sql.adoc[]
***** xref:testing/annotations/integration-spring/annotation-sqlconfig.adoc[]
***** xref:testing/annotations/integration-spring/annotation-sqlmergemode.adoc[]
***** xref:testing/annotations/integration-spring/annotation-sqlgroup.adoc[]
***** xref:testing/annotations/integration-spring/annotation-disabledinaotmode.adoc[]
**** xref:testing/annotations/integration-junit4.adoc[]
**** xref:testing/annotations/integration-junit-jupiter.adoc[]
**** xref:testing/annotations/integration-meta.adoc[]
*** xref:testing/resources.adoc[]
* xref:data-access.adoc[]
** xref:data-access/transaction.adoc[]
*** xref:data-access/transaction/motivation.adoc[]
@@ -164,7 +245,6 @@
**** xref:web/webmvc/mvc-servlet/multipart.adoc[]
**** xref:web/webmvc/mvc-servlet/logging.adoc[]
*** xref:web/webmvc/filters.adoc[]
*** xref:web/webmvc/message-converters.adoc[]
*** xref:web/webmvc/mvc-controller.adoc[]
**** xref:web/webmvc/mvc-controller/ann.adoc[]
**** xref:web/webmvc/mvc-controller/ann-requestmapping.adoc[]
@@ -205,7 +285,6 @@
**** xref:web/webmvc-view/mvc-freemarker.adoc[]
**** xref:web/webmvc-view/mvc-groovymarkup.adoc[]
**** xref:web/webmvc-view/mvc-script.adoc[]
**** xref:web/webmvc-view/mvc-fragments.adoc[]
**** xref:web/webmvc-view/mvc-jsp.adoc[]
**** xref:web/webmvc-view/mvc-feeds.adoc[]
**** xref:web/webmvc-view/mvc-document.adoc[]
@@ -313,97 +392,6 @@
** xref:web/webflux-test.adoc[]
** xref:rsocket.adoc[]
** xref:web/webflux-reactive-libraries.adoc[]
* xref:testing.adoc[]
** xref:testing/introduction.adoc[]
** xref:testing/unit.adoc[]
** xref:testing/integration.adoc[]
** xref:testing/support-jdbc.adoc[]
** xref:testing/testcontext-framework.adoc[]
*** xref:testing/testcontext-framework/key-abstractions.adoc[]
*** xref:testing/testcontext-framework/bootstrapping.adoc[]
*** xref:testing/testcontext-framework/tel-config.adoc[]
*** 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/xml.adoc[]
**** xref:testing/testcontext-framework/ctx-management/groovy.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[]
**** xref:testing/testcontext-framework/ctx-management/inheritance.adoc[]
**** xref:testing/testcontext-framework/ctx-management/env-profiles.adoc[]
**** xref:testing/testcontext-framework/ctx-management/property-sources.adoc[]
**** xref:testing/testcontext-framework/ctx-management/dynamic-property-sources.adoc[]
**** 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/failure-threshold.adoc[]
**** xref:testing/testcontext-framework/ctx-management/hierarchies.adoc[]
*** xref:testing/testcontext-framework/fixture-di.adoc[]
*** xref:testing/testcontext-framework/bean-overriding.adoc[]
*** xref:testing/testcontext-framework/web-scoped-beans.adoc[]
*** xref:testing/testcontext-framework/tx.adoc[]
*** xref:testing/testcontext-framework/executing-sql.adoc[]
*** xref:testing/testcontext-framework/parallel-test-execution.adoc[]
*** xref:testing/testcontext-framework/support-classes.adoc[]
*** xref:testing/testcontext-framework/aot.adoc[]
** xref:testing/webtestclient.adoc[]
** xref:testing/mockmvc.adoc[]
*** xref:testing/mockmvc/overview.adoc[]
*** xref:testing/mockmvc/setup-options.adoc[]
*** xref:testing/mockmvc/hamcrest.adoc[]
**** xref:testing/mockmvc/hamcrest/static-imports.adoc[]
**** xref:testing/mockmvc/hamcrest/setup.adoc[]
**** xref:testing/mockmvc/hamcrest/setup-steps.adoc[]
**** xref:testing/mockmvc/hamcrest/requests.adoc[]
**** xref:testing/mockmvc/hamcrest/expectations.adoc[]
**** xref:testing/mockmvc/hamcrest/async-requests.adoc[]
**** xref:testing/mockmvc/hamcrest/vs-streaming-response.adoc[]
**** xref:testing/mockmvc/hamcrest/filters.adoc[]
*** xref:testing/mockmvc/assertj.adoc[]
**** xref:testing/mockmvc/assertj/setup.adoc[]
**** xref:testing/mockmvc/assertj/requests.adoc[]
**** xref:testing/mockmvc/assertj/assertions.adoc[]
**** xref:testing/mockmvc/assertj/integration.adoc[]
*** xref:testing/mockmvc/htmlunit.adoc[]
**** xref:testing/mockmvc/htmlunit/why.adoc[]
**** xref:testing/mockmvc/htmlunit/mah.adoc[]
**** xref:testing/mockmvc/htmlunit/webdriver.adoc[]
**** xref:testing/mockmvc/htmlunit/geb.adoc[]
*** xref:testing/mockmvc/vs-end-to-end-integration-tests.adoc[]
*** xref:testing/mockmvc/resources.adoc[]
** xref:testing/spring-mvc-test-client.adoc[]
** xref:testing/appendix.adoc[]
*** xref:testing/annotations.adoc[]
**** xref:testing/annotations/integration-standard.adoc[]
**** xref:testing/annotations/integration-spring.adoc[]
***** xref:testing/annotations/integration-spring/annotation-bootstrapwith.adoc[]
***** xref:testing/annotations/integration-spring/annotation-contextconfiguration.adoc[]
***** xref:testing/annotations/integration-spring/annotation-webappconfiguration.adoc[]
***** xref:testing/annotations/integration-spring/annotation-contexthierarchy.adoc[]
***** xref:testing/annotations/integration-spring/annotation-contextcustomizerfactories.adoc[]
***** xref:testing/annotations/integration-spring/annotation-activeprofiles.adoc[]
***** xref:testing/annotations/integration-spring/annotation-testpropertysource.adoc[]
***** xref:testing/annotations/integration-spring/annotation-dynamicpropertysource.adoc[]
***** xref:testing/annotations/integration-spring/annotation-testbean.adoc[]
***** xref:testing/annotations/integration-spring/annotation-mockitobean.adoc[]
***** xref:testing/annotations/integration-spring/annotation-dirtiescontext.adoc[]
***** xref:testing/annotations/integration-spring/annotation-testexecutionlisteners.adoc[]
***** xref:testing/annotations/integration-spring/annotation-recordapplicationevents.adoc[]
***** xref:testing/annotations/integration-spring/annotation-commit.adoc[]
***** xref:testing/annotations/integration-spring/annotation-rollback.adoc[]
***** xref:testing/annotations/integration-spring/annotation-beforetransaction.adoc[]
***** xref:testing/annotations/integration-spring/annotation-aftertransaction.adoc[]
***** xref:testing/annotations/integration-spring/annotation-sql.adoc[]
***** xref:testing/annotations/integration-spring/annotation-sqlconfig.adoc[]
***** xref:testing/annotations/integration-spring/annotation-sqlmergemode.adoc[]
***** xref:testing/annotations/integration-spring/annotation-sqlgroup.adoc[]
***** xref:testing/annotations/integration-spring/annotation-disabledinaotmode.adoc[]
**** xref:testing/annotations/integration-junit4.adoc[]
**** xref:testing/annotations/integration-junit-jupiter.adoc[]
**** xref:testing/annotations/integration-meta.adoc[]
*** xref:testing/resources.adoc[]
* xref:integration.adoc[]
** xref:integration/rest-clients.adoc[]
** xref:integration/jms.adoc[]
@@ -205,7 +205,7 @@ Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val person = factory.getBean("person") as Person
val person = factory.getBean("person") as Person;
----
======
@@ -128,7 +128,18 @@ the resulting pointcut is effectively the union of the specified patterns.)
The following example shows how to use `JdkRegexpMethodPointcut`:
include-code::./JdkRegexpConfiguration[tag=snippet,indent=0]
[source,xml,indent=0,subs="verbatim"]
----
<bean id="settersAndAbsquatulatePointcut"
class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="patterns">
<list>
<value>.*set.*</value>
<value>.*absquatulate</value>
</list>
</property>
</bean>
----
Spring provides a convenience class named `RegexpMethodPointcutAdvisor`, which lets us
also reference an `Advice` (remember that an `Advice` can be an interceptor, before advice,
@@ -136,7 +147,21 @@ throws advice, and others). Behind the scenes, Spring uses a `JdkRegexpMethodPoi
Using `RegexpMethodPointcutAdvisor` simplifies wiring, as the one bean encapsulates both
pointcut and advice, as the following example shows:
include-code::./RegexpConfiguration[tag=snippet,indent=0]
[source,xml,indent=0,subs="verbatim"]
----
<bean id="settersAndAbsquatulateAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref bean="beanNameOfAopAllianceInterceptor"/>
</property>
<property name="patterns">
<list>
<value>.*set.*</value>
<value>.*absquatulate</value>
</list>
</property>
</bean>
----
You can use `RegexpMethodPointcutAdvisor` with any `Advice` type.
@@ -923,7 +923,7 @@ Each of the distinct advice types of a particular aspect is conceptually meant t
to the join point directly. As a consequence, an `@AfterThrowing` advice method is not
supposed to receive an exception from an accompanying `@After`/`@AfterReturning` method.
Advice methods defined in the same `@Aspect` class that
As of Spring Framework 5.2.7, advice methods defined in the same `@Aspect` class that
need to run at the same join point are assigned precedence based on their advice type in
the following order, from highest to lowest precedence: `@Around`, `@Before`, `@After`,
`@AfterReturning`, `@AfterThrowing`. Note, however, that an `@After` advice method will
@@ -8,8 +8,54 @@ determines that a bean is advised by one or more aspects, it automatically gener
a proxy for that bean to intercept method invocations and ensures that advice is run
as needed.
The @AspectJ support can be enabled with programmatic or XML configuration. In either
case, you also need to ensure that AspectJ's `org.aspectj:aspectjweaver` library is on the
classpath of your application (version 1.9 or later).
The @AspectJ support can be enabled with XML- or Java-style configuration. In either
case, you also need to ensure that AspectJ's `aspectjweaver.jar` library is on the
classpath of your application (version 1.9 or later). This library is available in the
`lib` directory of an AspectJ distribution or from the Maven Central repository.
[[aop-enable-aspectj-java]]
== Enabling @AspectJ Support with Java Configuration
To enable @AspectJ support with Java `@Configuration`, add the `@EnableAspectJAutoProxy`
annotation, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Configuration
@EnableAspectJAutoProxy
class AppConfig
----
======
[[aop-enable-aspectj-xml]]
== Enabling @AspectJ Support with XML Configuration
To enable @AspectJ support with XML-based configuration, use the `aop:aspectj-autoproxy`
element, as the following example shows:
[source,xml,indent=0,subs="verbatim"]
----
<aop:aspectj-autoproxy/>
----
This assumes that you use schema support as described in
xref:core/appendix/xsd-schemas.adoc[XML Schema-based configuration].
See xref:core/appendix/xsd-schemas.adoc#aop[the AOP schema] for how to
import the tags in the `aop` namespace.
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
@@ -9,12 +9,43 @@ minimal steps required for a not-very-useful aspect.
The first of the two examples shows a regular bean definition in the application context
that points to a bean class that is annotated with `@Aspect`:
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
[source,xml,indent=0,subs="verbatim"]
----
<bean id="myAspect" class="com.xyz.NotVeryUsefulAspect">
<!-- configure properties of the aspect here -->
</bean>
----
The second of the two examples shows the `NotVeryUsefulAspect` class definition, which is
annotated with `@Aspect`:
include-code::./NotVeryUsefulAspect[tag=snippet,indent=0]
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages",fold="none"]
----
package com.xyz;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class NotVeryUsefulAspect {
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages",fold="none"]
----
package com.xyz
import org.aspectj.lang.annotation.Aspect
@Aspect
class NotVeryUsefulAspect
----
======
Aspects (classes annotated with `@Aspect`) can have methods and fields, the same as any
other class. They can also contain pointcut, advice, and introduction (inter-type)
@@ -16,9 +16,93 @@ aspect.
Because we want to retry the operation, we need to use around advice so that we can
call `proceed` multiple times. The following listing shows the basic aspect implementation:
include-code::./ConcurrentOperationExecutor[tag=snippet,indent=0]
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Aspect
public class ConcurrentOperationExecutor implements Ordered {
`@Around("com.xyz.CommonPointcuts.businessService()")` references the `businessService` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-common-pointcuts[Sharing Named Pointcut Definitions].
private static final int DEFAULT_MAX_RETRIES = 2;
private int maxRetries = DEFAULT_MAX_RETRIES;
private int order = 1;
public void setMaxRetries(int maxRetries) {
this.maxRetries = maxRetries;
}
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
@Around("com.xyz.CommonPointcuts.businessService()") // <1>
public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
int numAttempts = 0;
PessimisticLockingFailureException lockFailureException;
do {
numAttempts++;
try {
return pjp.proceed();
}
catch(PessimisticLockingFailureException ex) {
lockFailureException = ex;
}
} while(numAttempts <= this.maxRetries);
throw lockFailureException;
}
}
----
<1> References the `businessService` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-common-pointcuts[Sharing Named Pointcut Definitions].
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Aspect
class ConcurrentOperationExecutor : Ordered {
private val DEFAULT_MAX_RETRIES = 2
private var maxRetries = DEFAULT_MAX_RETRIES
private var order = 1
fun setMaxRetries(maxRetries: Int) {
this.maxRetries = maxRetries
}
override fun getOrder(): Int {
return this.order
}
fun setOrder(order: Int) {
this.order = order
}
@Around("com.xyz.CommonPointcuts.businessService()") // <1>
fun doConcurrentOperation(pjp: ProceedingJoinPoint): Any? {
var numAttempts = 0
var lockFailureException: PessimisticLockingFailureException
do {
numAttempts++
try {
return pjp.proceed()
} catch (ex: PessimisticLockingFailureException) {
lockFailureException = ex
}
} while (numAttempts <= this.maxRetries)
throw lockFailureException
}
}
----
<1> References the `businessService` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-common-pointcuts[Sharing Named Pointcut Definitions].
======
Note that the aspect implements the `Ordered` interface so that we can set the precedence of
the aspect higher than the transaction advice (we want a fresh transaction each time we
@@ -30,15 +114,70 @@ we have exhausted all of our retry attempts.
The corresponding Spring configuration follows:
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
[source,xml,indent=0,subs="verbatim"]
----
<aop:aspectj-autoproxy/>
<bean id="concurrentOperationExecutor"
class="com.xyz.service.impl.ConcurrentOperationExecutor">
<property name="maxRetries" value="3"/>
<property name="order" value="100"/>
</bean>
----
To refine the aspect so that it retries only idempotent operations, we might define the following
`Idempotent` annotation:
include-code::./service/Idempotent[tag=snippet,indent=0]
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Retention(RetentionPolicy.RUNTIME)
// marker annotation
public @interface Idempotent {
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Retention(AnnotationRetention.RUNTIME)
// marker annotation
annotation class Idempotent
----
======
We can then use the annotation to annotate the implementation of service operations. The change
to the aspect to retry only idempotent operations involves refining the pointcut
expression so that only `@Idempotent` operations match, as follows:
include-code::./service/SampleService[tag=snippet,indent=0]
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Around("execution(* com.xyz..service.*.*(..)) && " +
"@annotation(com.xyz.service.Idempotent)")
public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Around("execution(* com.xyz..service.*.*(..)) && " +
"@annotation(com.xyz.service.Idempotent)")
fun doConcurrentOperation(pjp: ProceedingJoinPoint): Any? {
// ...
}
----
======
@@ -72,7 +72,7 @@ Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
val usageTracked = context.getBean<UsageTracked>("myService")
val usageTracked = context.getBean("myService", UsageTracked.class)
----
======
@@ -66,7 +66,7 @@ Kotlin::
When used as a marker interface in this way, Spring configures new instances of the
annotated type (`Account`, in this case) by using a bean definition (typically
prototype-scoped) with the same name as the fully-qualified type name
(`com.xyz.domain.Account`). Since the default name for a bean defined via XML is the
(`com.xyz.domain.Account`). Since the default name for a bean is the
fully-qualified name of its type, a convenient way to declare the prototype definition
is to omit the `id` attribute, as the following example shows:
@@ -177,10 +177,41 @@ 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 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:
a reference to the bean factory that is to be used to configure new objects). If you
use Java-based configuration, you can add `@EnableSpringConfigured` to any
`@Configuration` class, as follows:
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Configuration
@EnableSpringConfigured
public class AppConfig {
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Configuration
@EnableSpringConfigured
class AppConfig {
}
----
======
If you prefer XML based configuration, the Spring
xref:core/appendix/xsd-schemas.adoc#context[`context` namespace]
defines a convenient `context:spring-configured` element, which you can use as follows:
[source,xml,indent=0,subs="verbatim"]
----
<context:spring-configured/>
----
Instances of `@Configurable` objects created before the aspect has been configured
result in a message being issued to the debug log and no configuration of the
@@ -752,9 +783,52 @@ adding one line. (Note that you almost certainly need to use an
`ApplicationContext` as your Spring container -- typically, a `BeanFactory` is not
enough because the LTW support uses `BeanFactoryPostProcessors`.)
To enable the Spring Framework's LTW support, you need to configure a `LoadTimeWeaver` as follows:
To enable the Spring Framework's LTW support, you need to configure a `LoadTimeWeaver`,
which typically is done by using the `@EnableLoadTimeWeaving` annotation, as follows:
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Configuration
@EnableLoadTimeWeaving
public class AppConfig {
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Configuration
@EnableLoadTimeWeaving
class AppConfig {
}
----
======
Alternatively, if you prefer XML-based configuration, use the
`<context:load-time-weaver/>` element. Note that the element is defined in the
`context` namespace. The following example shows how to use `<context:load-time-weaver/>`:
[source,xml,indent=0,subs="verbatim"]
----
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:load-time-weaver/>
</beans>
----
The preceding configuration automatically defines and registers a number of LTW-specific
infrastructure beans, such as a `LoadTimeWeaver` and an `AspectJWeavingEnabler`, for you.
@@ -790,12 +864,63 @@ Note that the table lists only the `LoadTimeWeavers` that are autodetected when
use the `DefaultContextLoadTimeWeaver`. You can specify exactly which `LoadTimeWeaver`
implementation to use.
To configure a specific `LoadTimeWeaver`, implement the
`LoadTimeWeavingConfigurer` interface and override the `getLoadTimeWeaver()` method
(or use the XML equivalent).
To specify a specific `LoadTimeWeaver` with Java configuration, implement the
`LoadTimeWeavingConfigurer` interface and override the `getLoadTimeWeaver()` method.
The following example specifies a `ReflectiveLoadTimeWeaver`:
include-code::./CustomWeaverConfiguration[tag=snippet,indent=0]
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Configuration
@EnableLoadTimeWeaving
public class AppConfig implements LoadTimeWeavingConfigurer {
@Override
public LoadTimeWeaver getLoadTimeWeaver() {
return new ReflectiveLoadTimeWeaver();
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Configuration
@EnableLoadTimeWeaving
class AppConfig : LoadTimeWeavingConfigurer {
override fun getLoadTimeWeaver(): LoadTimeWeaver {
return ReflectiveLoadTimeWeaver()
}
}
----
======
If you use XML-based configuration, you can specify the fully qualified class name
as the value of the `weaver-class` attribute on the `<context:load-time-weaver/>`
element. Again, the following example specifies a `ReflectiveLoadTimeWeaver`:
[source,xml,indent=0,subs="verbatim"]
----
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:load-time-weaver
weaver-class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver"/>
</beans>
----
The `LoadTimeWeaver` that is defined and registered by the configuration can be later
retrieved from the Spring container by using the well known name, `loadTimeWeaver`.
+26 -31
View File
@@ -509,45 +509,40 @@ It is also possible to register an implementation statically by adding an entry
{spring-framework-api}/aot/hint/annotation/Reflective.html[`@Reflective`] provides an idiomatic way to flag the need for reflection on an annotated element.
For instance, `@EventListener` is meta-annotated with `@Reflective` since the underlying implementation invokes the annotated method using reflection.
Out-of-the-box, only Spring beans are considered but you can opt-in for scanning using `@ReflectiveScan`.
In the example below, all types of the package `com.example.app` and their subpackages are considered:
include-code::./MyConfiguration[]
Scanning happens during AOT processing and the types in the target packages do not need to have a class-level annotation to be considered.
This performs a "deep scan" and the presence of `@Reflective`, either directly or as a meta-annotation, is checked on types, fields, constructors, methods, and enclosed elements.
By default, `@Reflective` registers an invocation hint for the annotated element.
This can be tuned by specifying a custom `ReflectiveProcessor` implementation via the `@Reflective` annotation.
By default, only Spring beans are considered, and an invocation hint is registered for the annotated element.
This can be tuned by specifying a custom `ReflectiveProcessor` implementation via the
`@Reflective` annotation.
Library authors can reuse this annotation for their own purposes.
An example of such customization is covered in the next section.
If components other than Spring beans need to be processed, a `BeanFactoryInitializationAotProcessor` can detect the relevant types and use `ReflectiveRuntimeHintsRegistrar` to process them.
[[aot.hints.register-reflection]]
=== `@RegisterReflection`
[[aot.hints.register-reflection-for-binding]]
=== `@RegisterReflectionForBinding`
{spring-framework-api}/aot/hint/annotation/RegisterReflection.html[`@RegisterReflection`] is a specialization of `@Reflective` that provides a declarative way of registering reflection for arbitrary types.
NOTE: As a specialization of `@Reflective`, this is also detected if you're using `@ReflectiveScan`.
In the following example, public constructors and public methods can be invoked via reflection on `AccountService`:
include-code::./MyConfiguration[tag=snippet,indent=0]
`@RegisterReflection` can be applied to any target type at the class level, but it can also be applied directly to a method to better indicate where the hints are actually required.
`@RegisterReflection` can be used as a meta-annotation to provide more specific needs.
{spring-framework-api}/aot/hint/annotation/RegisterReflectionForBinding.html[`@RegisterReflectionForBinding`] is such composed annotation and registers the need for serializing arbitrary types.
{spring-framework-api}/aot/hint/annotation/RegisterReflectionForBinding.html[`@RegisterReflectionForBinding`] is a specialization of `@Reflective` that registers the need for serializing arbitrary types.
A typical use case is the use of DTOs that the container cannot infer, such as using a web client within a method body.
The following example registers `Order` for serialization.
`@RegisterReflectionForBinding` can be applied to any Spring bean at the class level, but it can also be applied directly to a method, field, or constructor to better indicate where the hints are actually required.
The following example registers `Account` for serialization.
include-code::./OrderService[tag=snippet,indent=0]
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Component
public class OrderService {
This registers hints for constructors, fields, properties, and record components of `Order`.
Hints are also registered for types transitively used on properties and record components.
In other words, if `Order` exposes others types, hints are registered for those as well.
@RegisterReflectionForBinding(Account.class)
public void process(Order order) {
// ...
}
}
----
======
[[aot.hints.testing]]
=== Testing Runtime Hints
@@ -579,7 +574,7 @@ If you forgot to contribute a hint, the test will fail and provide some details
[source,txt,indent=0,subs="verbatim,quotes"]
----
org.springframework.docs.core.aot.hints.testing.SampleReflection performReflection
INFO: Spring version: 6.2.0
INFO: Spring version:6.0.0-SNAPSHOT
Missing <"ReflectionHints"> for invocation <java.lang.Class#forName>
with arguments ["org.springframework.core.SpringVersion",
@@ -1,5 +1,5 @@
[[beans-autowired-annotation-primary]]
= Fine-tuning Annotation-based Autowiring with `@Primary` or `@Fallback`
= Fine-tuning Annotation-based Autowiring with `@Primary`
Because autowiring by type may lead to multiple candidates, it is often necessary to have
more control over the selection process. One way to accomplish this is with Spring's
@@ -50,51 +50,8 @@ Kotlin::
----
======
Alternatively, as of 6.2, there is a `@Fallback` annotation for demarcating
any beans other than the regular ones to be injected. If only one regular
bean is left, it is effectively primary as well:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
public class MovieConfiguration {
@Bean
public MovieCatalog firstMovieCatalog() { ... }
@Bean
@Fallback
public MovieCatalog secondMovieCatalog() { ... }
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
class MovieConfiguration {
@Bean
fun firstMovieCatalog(): MovieCatalog { ... }
@Bean
@Fallback
fun secondMovieCatalog(): MovieCatalog { ... }
// ...
}
----
======
With both variants of the preceding configuration, the following
`MovieRecommender` is autowired with the `firstMovieCatalog`:
With the preceding configuration, the following `MovieRecommender` is autowired with the
`firstMovieCatalog`:
[tabs]
======
@@ -1,13 +1,12 @@
[[beans-autowired-annotation-qualifiers]]
= Fine-tuning Annotation-based Autowiring with Qualifiers
`@Primary` and `@Fallback` are effective ways to use autowiring by type with several
instances when one primary (or non-fallback) candidate can be determined.
When you need more control over the selection process, you can use Spring's `@Qualifier`
annotation. You can associate qualifier values with specific arguments, narrowing the set
of type matches so that a specific bean is chosen for each argument. In the simplest case,
this can be a plain descriptive value, as shown in the following example:
`@Primary` is an effective way to use autowiring by type with several instances when one
primary candidate can be determined. When you need more control over the selection process,
you can use Spring's `@Qualifier` annotation. You can associate qualifier values
with specific arguments, narrowing the set of type matches so that a specific bean is
chosen for each argument. In the simplest case, this can be a plain descriptive value, as
shown in the following example:
--
[tabs]
@@ -158,28 +157,24 @@ for a non-unique dependency situation, Spring matches the injection point name
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 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
which is semantically defined to identify a specific target component by its unique name,
with the declared type being irrelevant for the matching process. `@Autowired` has rather
different semantics: after selecting candidate beans by type, the specified `String`
different semantics: After selecting candidate beans by type, the specified `String`
qualifier value is considered within those type-selected candidates only (for example,
matching an `account` qualifier against beans marked with the same qualifier label).
For beans that are themselves defined as a collection, `Map`, or array type, `@Resource`
is a fine solution, referring to the specific collection or array bean by unique name.
That said, you can match collection, `Map`, and array types through Spring's
That said, as of 4.3, you can match collection, `Map`, and array types through Spring's
`@Autowired` type matching algorithm as well, as long as the element type information
is preserved in `@Bean` return type signatures or collection inheritance hierarchies.
In this case, you can use qualifier values to select among same-typed collections,
as outlined in the previous paragraph.
`@Autowired` also considers self references for injection (that is, references
As of 4.3, `@Autowired` also considers self references for injection (that is, references
back to the bean that is currently injected). Note that self injection is a fallback.
Regular dependencies on other components always have precedence. In that sense, self
references do not participate in regular candidate selection and are therefore in
@@ -421,9 +421,9 @@ through Java 8's `java.util.Optional`, as the following example shows:
}
----
You can also use a `@Nullable` annotation (of any kind in any package -- for example,
`javax.annotation.Nullable` from JSR-305) or just leverage Kotlin built-in null-safety
support:
As of Spring Framework 5.0, you can also use a `@Nullable` annotation (of any kind
in any package -- for example, `javax.annotation.Nullable` from JSR-305) or just leverage
Kotlin built-in null-safety support:
[tabs]
======
@@ -101,8 +101,8 @@ NOTE: When configuring a `PropertySourcesPlaceholderConfigurer` using JavaConfig
Using the above configuration ensures Spring initialization failure if any `${}`
placeholder could not be resolved. It is also possible to use methods like
`setPlaceholderPrefix`, `setPlaceholderSuffix`, `setValueSeparator`, or
`setEscapeCharacter` to customize placeholders.
`setPlaceholderPrefix`, `setPlaceholderSuffix`, or `setValueSeparator` to customize
placeholders.
NOTE: Spring Boot configures by default a `PropertySourcesPlaceholderConfigurer` bean that
will get properties from `application.properties` and `application.yml` files.
@@ -751,7 +751,7 @@ and bean definition show.
TIP: If you run into naming conflicts due to multiple autodetected components having the
same non-qualified class name (i.e., classes with identical names but residing in
different packages), you may need to configure a `BeanNameGenerator` that defaults to the
fully qualified class name for the generated bean name. The
fully qualified class name for the generated bean name. As of Spring Framework 5.2.3, the
`FullyQualifiedAnnotationBeanNameGenerator` located in package
`org.springframework.context.annotation` can be used for such purposes.
@@ -78,20 +78,19 @@ lead to concurrent access exceptions, inconsistent state in the bean container,
[[beans-definition-overriding]]
== Overriding Beans
Bean overriding occurs when a bean is registered using an identifier that is already
allocated. While bean overriding is possible, it makes the configuration harder to read.
WARNING: Bean overriding will be deprecated in a future release.
Bean overriding is happening when a bean is registered using an identifier that is
already allocated. While bean overriding is possible, it makes the configuration harder
to read and this feature will be deprecated in a future release.
To disable bean overriding altogether, you can set the `allowBeanDefinitionOverriding`
flag to `false` on the `ApplicationContext` before it is refreshed. In such a setup, an
flag to `false` on the `ApplicationContext` before it is refreshed. In such setup, an
exception is thrown if bean overriding is used.
By default, the container logs every attempt to override a bean at `INFO` level so that
you can adapt your configuration accordingly. While not recommended, you can silence
those logs by setting the `allowBeanDefinitionOverriding` flag to `true`.
By default, the container logs every bean overriding at `INFO` level so that you can
adapt your configuration accordingly. While not recommended, you can silence those logs
by setting the `allowBeanDefinitionOverriding` flag to `true`.
.Java Configuration
.Java-configuration
****
If you use Java Configuration, a corresponding `@Bean` method always silently overrides
a scanned bean class with the same component name as long as the return type of the
@@ -99,10 +98,6 @@ a scanned bean class with the same component name as long as the return type of
the `@Bean` factory method in favor of any pre-declared constructor on the bean class.
****
NOTE: We acknowledge that overriding beans in test scenarios is convenient, and there is
explicit support for this as of Spring Framework 6.2. Please refer to
xref:testing/testcontext-framework/bean-overriding.adoc[this section] for more details.
[[beans-beanname]]
@@ -10,25 +10,33 @@ pre-instantiation of a singleton bean by marking the bean definition as being
lazy-initialized. A lazy-initialized bean tells the IoC container to create a bean
instance when it is first requested, rather than at startup.
This behavior is controlled by the `@Lazy` annotation or in XML the `lazy-init` attribute on the `<bean/>` element, as
the following example shows:
In XML, this behavior is controlled by the `lazy-init` attribute on the `<bean/>`
element, as the following example shows:
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>
<bean name="not.lazy" class="com.something.AnotherBean"/>
----
When the preceding configuration is consumed by an `ApplicationContext`, the `lazy` bean
is not eagerly pre-instantiated when the `ApplicationContext` starts,
whereas the `notLazy` one is eagerly pre-instantiated.
whereas the `not.lazy` bean is eagerly pre-instantiated.
However, when a lazy-initialized bean is a dependency of a singleton bean that is
not lazy-initialized, the `ApplicationContext` creates the lazy-initialized bean at
startup, because it must satisfy the singleton's dependencies. The lazy-initialized bean
is injected into a singleton bean elsewhere that is not lazy-initialized.
You can also control lazy-initialization for a set of beans by using the `@Lazy` annotation on your `@Configuration`
annotated class or in XML using the `default-lazy-init` attribute on the `<beans/>` element, as the following example
shows:
You can also control lazy-initialization at the container level by using the
`default-lazy-init` attribute on the `<beans/>` element, as the following example shows:
include-code::./LazyConfiguration[tag=snippet,indent=0]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<beans default-lazy-init="true">
<!-- no beans will be pre-instantiated... -->
</beans>
----
@@ -503,47 +503,10 @@ way, navigating `@Configuration` classes and their dependencies becomes no diffe
than the usual process of navigating interface-based code.
--
[[beans-java-startup]]
== Influencing the Startup of `@Bean`-defined Singletons
If you want to influence the startup creation order of certain singleton beans, consider
declaring some of them as `@Lazy` for creation on first access instead of on startup.
`@DependsOn` forces certain other beans to be initialized first, making sure that
the specified beans are created before the current bean, beyond what the latter's
direct dependencies imply.
[[beans-java-startup-background]]
=== Background Initialization
As of 6.2, there is a background initialization option: `@Bean(bootstrap=BACKGROUND)`
allows for singling out specific beans for background initialization, covering the
entire bean creation step for each such bean on context startup.
Dependent beans with non-lazy injection points automatically wait for the bean instance
to be completed. All regular background initializations are forced to complete at the end
of context startup. Only beans additionally marked as `@Lazy` are allowed to be completed
later (up until the first actual access).
Background initialization typically goes together with `@Lazy` (or `ObjectProvider`)
injection points in dependent beans. Otherwise, the main bootstrap thread is going to
block when an actual background-initialized bean instance needs to be injected early.
This form of concurrent startup applies to individual beans: if such a bean depends on
other beans, they need to have been initialized already, either simply through being
declared earlier or through `@DependsOn` which enforces initialization in the main
bootstrap thread before background initialization for the affected bean is triggered.
[NOTE]
====
A `bootstrapExecutor` bean of type `Executor` must be declared for background
bootstrapping to be actually active. Otherwise, the background markers will be ignored at
runtime.
The bootstrap executor may be a bounded executor just for startup purposes or a shared
thread pool which serves for other purposes as well.
====
TIP: If you want to influence the startup creation order of certain beans, consider
declaring some of them as `@Lazy` (for creation on first access instead of on startup)
or as `@DependsOn` certain other beans (making sure that specific other beans are
created before the current bean, beyond what the latter's direct dependencies imply).
[[beans-java-conditional]]
@@ -1,34 +1,219 @@
[[expressions-beandef]]
= Expressions in Bean Definitions
You can use SpEL expressions with configuration metadata for defining bean instances. In both
cases, the syntax to define the expression is of the form `#{ <expression string> }`.
You can use SpEL expressions with XML-based or annotation-based configuration metadata for
defining `BeanDefinition` instances. In both cases, the syntax to define the expression is of the
form `#{ <expression string> }`.
[[expressions-beandef-xml-based]]
== XML Configuration
A property or constructor argument value can be set by using expressions, as the following
example shows:
[source,xml,indent=0,subs="verbatim"]
----
<bean id="numberGuess" class="org.spring.samples.NumberGuess">
<property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/>
<!-- other properties -->
</bean>
----
All beans in the application context are available as predefined variables with their
common bean name. This includes standard context beans such as `environment` (of type
`org.springframework.core.env.Environment`) as well as `systemProperties` and
`systemEnvironment` (of type `Map<String, Object>`) for access to the runtime environment.
To specify a default value, you can place the `@Value` annotation on fields, methods,
and method or constructor parameters (or XML equivalent).
The following example shows access to the `systemProperties` bean as a SpEL variable:
The following example sets the default value of a field:
[source,xml,indent=0,subs="verbatim"]
----
<bean id="taxCalculator" class="org.spring.samples.TaxCalculator">
<property name="defaultLocale" value="#{ systemProperties['user.region'] }"/>
include-code::./FieldValueTestBean[tag=snippet,indent=0]
<!-- other properties -->
</bean>
----
Note that you do not have to prefix the predefined variable with the `#` symbol here.
You can also refer to other bean properties by name, as the following example shows:
[source,xml,indent=0,subs="verbatim"]
----
<bean id="numberGuess" class="org.spring.samples.NumberGuess">
<property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/>
<!-- other properties -->
</bean>
<bean id="shapeGuess" class="org.spring.samples.ShapeGuess">
<property name="initialShapeSeed" value="#{ numberGuess.randomNumber }"/>
<!-- other properties -->
</bean>
----
[[expressions-beandef-annotation-based]]
== Annotation Configuration
To specify a default value, you can place the `@Value` annotation on fields, methods,
and method or constructor parameters.
The following example sets the default value of a field:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class FieldValueTestBean {
@Value("#{ systemProperties['user.region'] }")
private String defaultLocale;
public void setDefaultLocale(String defaultLocale) {
this.defaultLocale = defaultLocale;
}
public String getDefaultLocale() {
return this.defaultLocale;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class FieldValueTestBean {
@Value("#{ systemProperties['user.region'] }")
var defaultLocale: String? = null
}
----
======
The following example shows the equivalent but on a property setter method:
include-code::./PropertyValueTestBean[tag=snippet,indent=0]
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class PropertyValueTestBean {
private String defaultLocale;
@Value("#{ systemProperties['user.region'] }")
public void setDefaultLocale(String defaultLocale) {
this.defaultLocale = defaultLocale;
}
public String getDefaultLocale() {
return this.defaultLocale;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class PropertyValueTestBean {
@Value("#{ systemProperties['user.region'] }")
var defaultLocale: String? = null
}
----
======
Autowired methods and constructors can also use the `@Value` annotation, as the following
examples show:
include-code::./SimpleMovieLister[tag=snippet,indent=0]
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class SimpleMovieLister {
private MovieFinder movieFinder;
private String defaultLocale;
@Autowired
public void configure(MovieFinder movieFinder,
@Value("#{ systemProperties['user.region'] }") String defaultLocale) {
this.movieFinder = movieFinder;
this.defaultLocale = defaultLocale;
}
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class SimpleMovieLister {
private lateinit var movieFinder: MovieFinder
private lateinit var defaultLocale: String
@Autowired
fun configure(movieFinder: MovieFinder,
@Value("#{ systemProperties['user.region'] }") defaultLocale: String) {
this.movieFinder = movieFinder
this.defaultLocale = defaultLocale
}
// ...
}
----
======
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class MovieRecommender {
private String defaultLocale;
private CustomerPreferenceDao customerPreferenceDao;
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao,
@Value("#{systemProperties['user.country']}") String defaultLocale) {
this.customerPreferenceDao = customerPreferenceDao;
this.defaultLocale = defaultLocale;
}
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class MovieRecommender(private val customerPreferenceDao: CustomerPreferenceDao,
@Value("#{systemProperties['user.country']}") private val defaultLocale: String) {
// ...
}
----
======
include-code::./MovieRecommender[tag=snippet,indent=0]
You can also refer to other bean properties by name, as the following example shows:
include-code::./ShapeGuess[tag=snippet,indent=0]
@@ -516,11 +516,10 @@ following kinds of expressions cannot be compiled.
* Expressions involving assignment
* Expressions relying on the conversion service
* Expressions using custom resolvers
* Expressions using custom resolvers or accessors
* Expressions using overloaded operators
* Expressions using array construction syntax
* Expressions using selection or projection
* Expressions using bean references
Compilation of additional kinds of expressions may be supported in the future.
@@ -1,8 +1,8 @@
[[expressions-bean-references]]
= Bean References
If the evaluation context has been configured with a bean resolver, you can look up beans
from an expression by using the `@` symbol as a prefix. The following example shows how
If the evaluation context has been configured with a bean resolver, you can
look up beans from an expression by using the `@` symbol. The following example shows how
to do so:
[tabs]
@@ -15,9 +15,8 @@ Java::
StandardEvaluationContext context = new StandardEvaluationContext();
context.setBeanResolver(new MyBeanResolver());
// This will end up calling resolve(context, "someBean") on MyBeanResolver
// during evaluation.
Object bean = parser.parseExpression("@someBean").getValue(context);
// This will end up calling resolve(context,"something") on MyBeanResolver during evaluation
Object bean = parser.parseExpression("@something").getValue(context);
----
Kotlin::
@@ -28,20 +27,13 @@ Kotlin::
val context = StandardEvaluationContext()
context.setBeanResolver(MyBeanResolver())
// This will end up calling resolve(context, "someBean") on MyBeanResolver
// during evaluation.
val bean = parser.parseExpression("@someBean").getValue(context)
// This will end up calling resolve(context,"something") on MyBeanResolver during evaluation
val bean = parser.parseExpression("@something").getValue(context)
----
======
[NOTE]
====
If a bean name contains a dot (`.`) or other special characters, you must provide the
name of the bean as a _string literal_ for example, `@'order.service'`.
====
To access a factory bean itself, you should instead prefix the bean name with an `&`
symbol. The following example shows how to do so:
To access a factory bean itself, you should instead prefix the bean name with an `&` symbol.
The following example shows how to do so:
[tabs]
======
@@ -53,9 +45,8 @@ Java::
StandardEvaluationContext context = new StandardEvaluationContext();
context.setBeanResolver(new MyBeanResolver());
// This will end up calling resolve(context, "&someFactoryBean") on
// MyBeanResolver during evaluation.
Object factoryBean = parser.parseExpression("&someFactoryBean").getValue(context);
// This will end up calling resolve(context,"&foo") on MyBeanResolver during evaluation
Object bean = parser.parseExpression("&foo").getValue(context);
----
Kotlin::
@@ -66,8 +57,9 @@ Kotlin::
val context = StandardEvaluationContext()
context.setBeanResolver(MyBeanResolver())
// This will end up calling resolve(context, "&someFactoryBean") on
// MyBeanResolver during evaluation.
val factoryBean = parser.parseExpression("&someFactoryBean").getValue(context)
// This will end up calling resolve(context,"&foo") on MyBeanResolver during evaluation
val bean = parser.parseExpression("&foo").getValue(context)
----
======
@@ -1,7 +1,7 @@
[[expressions-operator-safe-navigation]]
= Safe Navigation Operator
The safe navigation operator (`?.`) is used to avoid a `NullPointerException` and comes
The safe navigation operator (`?`) is used to avoid a `NullPointerException` and comes
from the https://www.groovy-lang.org/operators.html#_safe_navigation_operator[Groovy]
language. Typically, when you have a reference to an object, you might need to verify
that it is not `null` before accessing methods or properties of the object. To avoid
@@ -81,65 +81,6 @@ For example, the expression `#calculator?.max(4, 2)` evaluates to `null` if the
`max(int, int)` method will be invoked on the `#calculator`.
====
[[expressions-operator-safe-navigation-indexing]]
== Safe Index Access
Since Spring Framework 6.2, the Spring Expression Language supports safe navigation for
indexing into the following types of structures.
* xref:core/expressions/language-ref/properties-arrays.adoc#expressions-indexing-arrays-and-collections[arrays and collections]
* xref:core/expressions/language-ref/properties-arrays.adoc#expressions-indexing-strings[strings]
* xref:core/expressions/language-ref/properties-arrays.adoc#expressions-indexing-maps[maps]
* xref:core/expressions/language-ref/properties-arrays.adoc#expressions-indexing-objects[objects]
* xref:core/expressions/language-ref/properties-arrays.adoc#expressions-indexing-custom[custom]
The following example shows how to use the safe navigation operator for indexing into
a list (`?.[]`).
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
ExpressionParser parser = new SpelExpressionParser();
IEEE society = new IEEE();
EvaluationContext context = new StandardEvaluationContext(society);
// evaluates to Inventor("Nikola Tesla")
Inventor inventor = parser.parseExpression("members?.[0]") // <1>
.getValue(context, Inventor.class);
society.members = null;
// evaluates to null - does not throw an exception
inventor = parser.parseExpression("members?.[0]") // <2>
.getValue(context, Inventor.class);
----
<1> Use null-safe index operator on a non-null `members` list
<2> Use null-safe index operator on a null `members` list
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val parser = SpelExpressionParser()
val society = IEEE()
val context = StandardEvaluationContext(society)
// evaluates to Inventor("Nikola Tesla")
var inventor = parser.parseExpression("members?.[0]") // <1>
.getValue(context, Inventor::class.java)
society.members = null
// evaluates to null - does not throw an exception
inventor = parser.parseExpression("members?.[0]") // <2>
.getValue(context, Inventor::class.java)
----
<1> Use null-safe index operator on a non-null `members` list
<2> Use null-safe index operator on a null `members` list
======
[[expressions-operator-safe-navigation-selection-and-projection]]
== Safe Collection Selection and Projection
@@ -1,25 +1,11 @@
[[expressions-properties-arrays]]
= Properties, Arrays, Lists, Maps, and Indexers
The Spring Expression Language provides support for navigating object graphs and indexing
into various structures.
NOTE: Numerical index values are zero-based, such as when accessing the n^th^ element of
an array in Java.
TIP: See the xref:core/expressions/language-ref/operator-safe-navigation.adoc[Safe Navigation Operator]
section for details on how to navigate object graphs and index into various structures
using the null-safe operator.
[[expressions-property-navigation]]
== Property Navigation
You can navigate property references within an object graph by using a period to indicate
a nested property value. The instances of the `Inventor` class, `pupin` and `tesla`, were
populated with data listed in the
xref:core/expressions/example-classes.adoc[Classes used in the examples] section. To
navigate _down_ the object graph and get Tesla's year of birth and Pupin's city of birth,
we use the following expressions:
Navigating with property references is easy. To do so, use a period to indicate a nested
property value. The instances of the `Inventor` class, `pupin` and `tesla`, were
populated with data listed in the xref:core/expressions/example-classes.adoc[Classes used in the examples]
section. To navigate "down" the object graph and get Tesla's year of birth and
Pupin's city of birth, we use the following expressions:
[tabs]
======
@@ -30,7 +16,6 @@ Java::
// evaluates to 1856
int year = (Integer) parser.parseExpression("birthdate.year + 1900").getValue(context);
// evaluates to "Smiljan"
String city = (String) parser.parseExpression("placeOfBirth.city").getValue(context);
----
@@ -41,7 +26,6 @@ Kotlin::
// evaluates to 1856
val year = parser.parseExpression("birthdate.year + 1900").getValue(context) as Int
// evaluates to "Smiljan"
val city = parser.parseExpression("placeOfBirth.city").getValue(context) as String
----
======
@@ -55,20 +39,8 @@ method invocations -- for example, `getPlaceOfBirth().getCity()` instead of
`placeOfBirth.city`.
====
[[expressions-indexing-arrays-and-collections]]
== Indexing into Arrays and Collections
The n^th^ element of an array or collection (for example, a `Set` or `List`) can be
obtained by using square bracket notation, as the following example shows.
[NOTE]
====
If the indexed collection is a `java.util.List`, the n^th^ element will be accessed
directly via `list.get(n)`.
For any other type of `Collection`, the n^th^ element will be accessed by iterating over
the collection using its `Iterator` and returning the n^th^ element encountered.
====
The contents of arrays and lists are obtained by using square bracket notation, as the
following example shows:
[tabs]
======
@@ -91,8 +63,7 @@ Java::
String name = parser.parseExpression("members[0].name").getValue(
context, ieee, String.class);
// List and Array Indexing
// List and Array navigation
// evaluates to "Wireless communication"
String invention = parser.parseExpression("members[0].inventions[6]").getValue(
context, ieee, String.class);
@@ -117,22 +88,16 @@ Kotlin::
val name = parser.parseExpression("members[0].name").getValue(
context, ieee, String::class.java)
// List and Array Indexing
// List and Array navigation
// evaluates to "Wireless communication"
val invention = parser.parseExpression("members[0].inventions[6]").getValue(
context, ieee, String::class.java)
----
======
[[expressions-indexing-strings]]
== Indexing into Strings
The n^th^ character of a string can be obtained by specifying the index within square
brackets, as demonstrated in the following example.
NOTE: The n^th^ character of a string will evaluate to a `java.lang.String`, not a
`java.lang.Character`.
The contents of maps are obtained by specifying the literal key value within the
brackets. In the following example, because keys for the `officers` map are strings, we can specify
string literals:
[tabs]
======
@@ -140,225 +105,38 @@ Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
// evaluates to "T" (8th letter of "Nikola Tesla")
String character = parser.parseExpression("members[0].name[7]")
.getValue(societyContext, String.class);
----
// Officer's Dictionary
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
// evaluates to "T" (8th letter of "Nikola Tesla")
val character = parser.parseExpression("members[0].name[7]")
.getValue(societyContext, String::class.java)
----
======
[[expressions-indexing-maps]]
== Indexing into Maps
The contents of maps are obtained by specifying the key value within square brackets. In
the following example, because keys for the `officers` map are strings, we can specify
string literals such as `'president'`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
// Officer's Map
// evaluates to Inventor("Pupin")
Inventor pupin = parser.parseExpression("officers['president']")
.getValue(societyContext, Inventor.class);
Inventor pupin = parser.parseExpression("officers['president']").getValue(
societyContext, Inventor.class);
// evaluates to "Idvor"
String city = parser.parseExpression("officers['president'].placeOfBirth.city")
.getValue(societyContext, String.class);
String countryExpression = "officers['advisors'][0].placeOfBirth.country";
String city = parser.parseExpression("officers['president'].placeOfBirth.city").getValue(
societyContext, String.class);
// setting values
parser.parseExpression(countryExpression)
.setValue(societyContext, "Croatia");
// evaluates to "Croatia"
String country = parser.parseExpression(countryExpression)
.getValue(societyContext, String.class);
parser.parseExpression("officers['advisors'][0].placeOfBirth.country").setValue(
societyContext, "Croatia");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
// Officer's Map
// Officer's Dictionary
// evaluates to Inventor("Pupin")
val pupin = parser.parseExpression("officers['president']")
.getValue(societyContext, Inventor::class.java)
val pupin = parser.parseExpression("officers['president']").getValue(
societyContext, Inventor::class.java)
// evaluates to "Idvor"
val city = parser.parseExpression("officers['president'].placeOfBirth.city")
.getValue(societyContext, String::class.java)
val countryExpression = "officers['advisors'][0].placeOfBirth.country"
val city = parser.parseExpression("officers['president'].placeOfBirth.city").getValue(
societyContext, String::class.java)
// setting values
parser.parseExpression(countryExpression)
.setValue(societyContext, "Croatia")
// evaluates to "Croatia"
val country = parser.parseExpression(countryExpression)
.getValue(societyContext, String::class.java)
parser.parseExpression("officers['advisors'][0].placeOfBirth.country").setValue(
societyContext, "Croatia")
----
======
[[expressions-indexing-objects]]
== Indexing into Objects
A property of an object can be obtained by specifying the name of the property within
square brackets. This is analogous to accessing the value of a map based on its key. The
following example demonstrates how to _index_ into an object to retrieve a specific
property.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
// Create an inventor to use as the root context object.
Inventor tesla = new Inventor("Nikola Tesla");
// evaluates to "Nikola Tesla"
String name = parser.parseExpression("#root['name']")
.getValue(context, tesla, String.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
// Create an inventor to use as the root context object.
val tesla = Inventor("Nikola Tesla")
// evaluates to "Nikola Tesla"
val name = parser.parseExpression("#root['name']")
.getValue(context, tesla, String::class.java)
----
======
[[expressions-indexing-custom]]
== Indexing into Custom Structures
Since Spring Framework 6.2, the Spring Expression Language supports indexing into custom
structures by allowing developers to implement and register an `IndexAccessor` with the
`EvaluationContext`. If you would like to support
xref:core/expressions/evaluation.adoc#expressions-spel-compilation[compilation] of
expressions that rely on a custom index accessor, that index accessor must implement the
`CompilableIndexAccessor` SPI.
To support common use cases, Spring provides a built-in `ReflectiveIndexAccessor` which
is a flexible `IndexAccessor` that uses reflection to read from and optionally write to
an indexed structure of a target object. The indexed structure can be accessed through a
`public` read-method (when being read) or a `public` write-method (when being written).
The relationship between the read-method and write-method is based on a convention that
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
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
a `FruitMap` within a SpEL expression, you will need to register an `IndexAccessor`.
[source,java,indent=0,subs="verbatim,quotes"]
----
package example;
public enum Color {
RED, ORANGE, YELLOW
}
----
[source,java,indent=0,subs="verbatim,quotes"]
----
public class FruitMap {
private final Map<Color, String> map = new HashMap<>();
public FruitMap() {
this.map.put(Color.RED, "cherry");
this.map.put(Color.ORANGE, "orange");
this.map.put(Color.YELLOW, "banana");
}
public String getFruit(Color color) {
return this.map.get(color);
}
public void setFruit(Color color, String fruit) {
this.map.put(color, fruit);
}
}
----
A read-only `IndexAccessor` for `FruitMap` can be created via `new
ReflectiveIndexAccessor(FruitMap.class, Color.class, "getFruit")`. With that accessor
registered and a `FruitMap` registered as a variable named `#fruitMap`, the SpEL
expression `#fruitMap[T(example.Color).RED]` will evaluate to `"cherry"`.
A read-write `IndexAccessor` for `FruitMap` can be created via `new
ReflectiveIndexAccessor(FruitMap.class, Color.class, "getFruit", "setFruit")`. With that
accessor registered and a `FruitMap` registered as a variable named `#fruitMap`, the SpEL
expression `#fruitMap[T(example.Color).RED] = 'strawberry'` can be used to change the
fruit mapping for the color red from `"cherry"` to `"strawberry"`.
The following example demonstrates how to register a `ReflectiveIndexAccessor` to index
into a `FruitMap` and then index into the `FruitMap` within a SpEL expression.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
// Create a ReflectiveIndexAccessor for FruitMap
IndexAccessor fruitMapAccessor = new ReflectiveIndexAccessor(
FruitMap.class, Color.class, "getFruit", "setFruit");
// Register the IndexAccessor for FruitMap
context.addIndexAccessor(fruitMapAccessor);
// Register the fruitMap variable
context.setVariable("fruitMap", new FruitMap());
// evaluates to "cherry"
String fruit = parser.parseExpression("#fruitMap[T(example.Color).RED]")
.getValue(context, String.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
// Create a ReflectiveIndexAccessor for FruitMap
val fruitMapAccessor = ReflectiveIndexAccessor(
FruitMap::class.java, Color::class.java, "getFruit", "setFruit")
// Register the IndexAccessor for FruitMap
context.addIndexAccessor(fruitMapAccessor)
// Register the fruitMap variable
context.setVariable("fruitMap", FruitMap())
// evaluates to "cherry"
val fruit = parser.parseExpression("#fruitMap[T(example.Color).RED]")
.getValue(context, String::class.java)
----
======
@@ -3,7 +3,7 @@
Expression templates allow mixing literal text with one or more evaluation blocks.
Each evaluation block is delimited with prefix and suffix characters that you can
define. A common choice is to use `+#{ }+` as the delimiters, as the following example
define. A common choice is to use `#{ }` as the delimiters, as the following example
shows:
[tabs]
@@ -32,7 +32,7 @@ Kotlin::
======
The string is evaluated by concatenating the literal text `'random number is '` with the
result of evaluating the expression inside the `+#{ }+` delimiters (in this case, the
result of evaluating the expression inside the `#{ }` delimiters (in this case, the
result of calling that `random()` method). The second argument to the `parseExpression()`
method is of the type `ParserContext`. The `ParserContext` interface is used to influence
how the expression is parsed in order to support the expression templating functionality.
@@ -1,7 +1,7 @@
[[spring-jcl]]
= Logging
Spring comes with its own Commons Logging bridge implemented
Since Spring Framework 5.0, Spring comes with its own Commons Logging bridge implemented
in the `spring-jcl` module. The implementation checks for the presence of the Log4j 2.x
API and the SLF4J 1.7 API in the classpath and uses the first one of those found as the
logging implementation, falling back to the Java platform's core logging facilities (also
@@ -27,20 +27,15 @@ The target class should have a single public constructor or a single non-public
with arguments. If there are multiple constructors, then a default constructor if present
is used.
By default, argument values are looked up via constructor parameter names. Spring MVC and
WebFlux support a custom name mapping through the `@BindParam` annotation on constructor
parameters or fields if present. If necessary, you can also configure a `NameResolver` on
`DataBinder` to customize the argument name to use.
By default, constructor parameter names are used to look up argument values, but you can
configure a `NameResolver`. Spring MVC and WebFlux both rely to allow customizing the name
of the value to bind through an `@BindParam` annotation on constructor parameters.
xref:beans-beans-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.
Constructor binding supports `List`, `Map`, and array arguments either converted from
a single string, e.g. comma-separated list, or based on indexed keys such as
`accounts[2].name` or `account[KEY].name`.
Binding and conversion errors are reflected in the `BindingResult` of the `DataBinder`.
If the target is created successfully, then `target` is set to the created instance
after the call to `construct`.
@@ -95,12 +90,13 @@ details. The below table shows some examples of these conventions:
| Indicates the nested property `name` of the property `account` that corresponds to
(for example) the `getAccount().setName()` or `getAccount().getName()` methods.
| `accounts[2]`
| `account[2]`
| Indicates the _third_ element of the indexed property `account`. Indexed properties
can be of type `array`, `list`, or other naturally ordered collection.
| `accounts[KEY]`
| Indicates the value of the map entry indexed by the `KEY` value.
| `account[COMPANYNAME]`
| Indicates the value of the map entry indexed by the `COMPANYNAME` key of the `account` `Map`
property.
|===
(This next section is not vitally important to you if you do not plan to work with
@@ -287,7 +287,32 @@ As the preceding example shows, a `ConstraintValidator` implementation can have
You can integrate the method validation feature of Bean Validation into a
Spring context through a `MethodValidationPostProcessor` bean definition:
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
@Configuration
public class AppConfig {
@Bean
public static MethodValidationPostProcessor validationPostProcessor() {
return new MethodValidationPostProcessor();
}
}
----
XML::
+
[source,xml,indent=0,subs="verbatim,quotes",role="secondary"]
----
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>
----
======
To be eligible for Spring-driven method validation, target classes need to be annotated
with Spring's `@Validated` annotation, which can optionally also declare the validation
@@ -320,7 +345,36 @@ By default, `jakarta.validation.ConstraintViolationException` is raised with the
you can have `MethodValidationException` raised instead with ``ConstraintViolation``s
adapted to `MessageSourceResolvable` errors. To enable set the following flag:
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
@Configuration
public class AppConfig {
@Bean
public static MethodValidationPostProcessor validationPostProcessor() {
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
processor.setAdaptConstraintViolations(true);
return processor;
}
}
----
XML::
+
[source,xml,indent=0,subs="verbatim,quotes",role="secondary"]
----
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor">
<property name="adaptConstraintViolations" value="true"/>
</bean>
----
======
`MethodValidationException` contains a list of ``ParameterValidationResult``s which
group errors by method parameter, and each exposes a `MethodParameter`, the argument
@@ -384,7 +438,7 @@ A `ConstraintViolation` on `Person.name()` is adapted to a `FieldError` with the
To customize the default message, you can add properties to
xref:core/beans/context-introduction.adoc#context-functionality-messagesource[MessageSource]
resource bundles using any of the above errors codes and message arguments. Note also that the
message argument `"name"` is itself a `MessageSourceResolvable` with error codes
message argument `"name"` is itself a `MessagreSourceResolvable` with error codes
`"person.name"` and `"name"` and can customized too. For example:
Properties::
@@ -11,9 +11,106 @@ formatters manually with the help of:
* `org.springframework.format.datetime.standard.DateTimeFormatterRegistrar`
* `org.springframework.format.datetime.DateFormatterRegistrar`
For example, the following configuration registers a global `yyyyMMdd` format:
For example, the following Java configuration registers a global `yyyyMMdd` format:
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
public class AppConfig {
@Bean
public FormattingConversionService conversionService() {
// Use the DefaultFormattingConversionService but do not register defaults
DefaultFormattingConversionService conversionService =
new DefaultFormattingConversionService(false);
// Ensure @NumberFormat is still supported
conversionService.addFormatterForFieldAnnotation(
new NumberFormatAnnotationFormatterFactory());
// Register JSR-310 date conversion with a specific global format
DateTimeFormatterRegistrar dateTimeRegistrar = new DateTimeFormatterRegistrar();
dateTimeRegistrar.setDateFormatter(DateTimeFormatter.ofPattern("yyyyMMdd"));
dateTimeRegistrar.registerFormatters(conversionService);
// Register date conversion with a specific global format
DateFormatterRegistrar dateRegistrar = new DateFormatterRegistrar();
dateRegistrar.setFormatter(new DateFormatter("yyyyMMdd"));
dateRegistrar.registerFormatters(conversionService);
return conversionService;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
class AppConfig {
@Bean
fun conversionService(): FormattingConversionService {
// Use the DefaultFormattingConversionService but do not register defaults
return DefaultFormattingConversionService(false).apply {
// Ensure @NumberFormat is still supported
addFormatterForFieldAnnotation(NumberFormatAnnotationFormatterFactory())
// Register JSR-310 date conversion with a specific global format
val dateTimeRegistrar = DateTimeFormatterRegistrar()
dateTimeRegistrar.setDateFormatter(DateTimeFormatter.ofPattern("yyyyMMdd"))
dateTimeRegistrar.registerFormatters(this)
// Register date conversion with a specific global format
val dateRegistrar = DateFormatterRegistrar()
dateRegistrar.setFormatter(DateFormatter("yyyyMMdd"))
dateRegistrar.registerFormatters(this)
}
}
}
----
======
If you prefer XML-based configuration, you can use a
`FormattingConversionServiceFactoryBean`. The following example shows how to do so:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="registerDefaultFormatters" value="false" />
<property name="formatters">
<set>
<bean class="org.springframework.format.number.NumberFormatAnnotationFormatterFactory" />
</set>
</property>
<property name="formatterRegistrars">
<set>
<bean class="org.springframework.format.datetime.standard.DateTimeFormatterRegistrar">
<property name="dateFormatter">
<bean class="org.springframework.format.datetime.standard.DateTimeFormatterFactoryBean">
<property name="pattern" value="yyyyMMdd"/>
</bean>
</property>
</bean>
</set>
</property>
</bean>
</beans>
----
Note there are extra considerations when configuring date and time formats in web
applications. Please see
@@ -74,8 +74,7 @@ The `format` subpackages provide several `Formatter` implementations as a conven
The `number` package provides `NumberStyleFormatter`, `CurrencyStyleFormatter`, and
`PercentStyleFormatter` to format `Number` objects that use a `java.text.NumberFormat`.
The `datetime` package provides a `DateFormatter` to format `java.util.Date` objects with
a `java.text.DateFormat`, as well as a `DurationFormatter` to format `Duration` objects
in different styles defined in the `@DurationFormat.Style` enum (see <<format-annotations-api>>).
a `java.text.DateFormat`.
The following `DateFormatter` is an example `Formatter` implementation:
@@ -281,8 +280,7 @@ Kotlin::
A portable format annotation API exists in the `org.springframework.format.annotation`
package. You can use `@NumberFormat` to format `Number` fields such as `Double` and
`Long`, `@DurationFormat` to format `Duration` fields in ISO8601 and simplified styles,
and `@DateTimeFormat` to format `java.util.Date`, `java.util.Calendar`, `Long`
`Long`, and `@DateTimeFormat` to format `java.util.Date`, `java.util.Calendar`, `Long`
(for millisecond timestamps) as well as JSR-310 `java.time`.
The following example uses `@DateTimeFormat` to format a `java.util.Date` as an ISO Date
@@ -46,9 +46,47 @@ To configure a `DriverManagerDataSource`:
for the correct value.)
. Provide a username and a password to connect to the database.
The following example shows how to configure a `DriverManagerDataSource`:
The following example shows how to configure a `DriverManagerDataSource` in Java:
include-code::./DriverManagerDataSourceConfiguration[tag=snippet,indent=0]
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
dataSource.setUrl("jdbc:hsqldb:hsql://localhost:");
dataSource.setUsername("sa");
dataSource.setPassword("");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val dataSource = DriverManagerDataSource().apply {
setDriverClassName("org.hsqldb.jdbcDriver")
url = "jdbc:hsqldb:hsql://localhost:"
username = "sa"
password = ""
}
----
======
The following example shows the corresponding XML configuration:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<context:property-placeholder location="jdbc.properties"/>
----
The next two examples show the basic connectivity and configuration for DBCP and C3P0.
To learn about more options that help control the pooling features, see the product
@@ -56,11 +94,32 @@ documentation for the respective connection pooling implementations.
The following example shows DBCP configuration:
include-code::./BasicDataSourceConfiguration[tag=snippet,indent=0]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<context:property-placeholder location="jdbc.properties"/>
----
The following example shows C3P0 configuration:
include-code::./ComboPooledDataSourceConfiguration[tag=snippet,indent=0]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${jdbc.driverClassName}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<context:property-placeholder location="jdbc.properties"/>
----
[[jdbc-DataSourceUtils]]
== Using `DataSourceUtils`
@@ -339,7 +339,7 @@ Kotlin::
More sophisticated stored procedure support is xref:data-access/jdbc/object.adoc#jdbc-StoredProcedure[covered later].
[[jdbc-jdbctemplate-idioms]]
[[jdbc-JdbcTemplate-idioms]]
=== `JdbcTemplate` Best Practices
Instances of the `JdbcTemplate` class are thread-safe, once configured. This is
@@ -352,23 +352,147 @@ A common practice when using the `JdbcTemplate` class (and the associated
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:
the setter for the `DataSource`. This leads to DAOs that resemble the following:
include-code::./JdbcCorporateEventDao[tag=snippet,indent=0]
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class JdbcCorporateEventDao implements CorporateEventDao {
The following example shows the corresponding configuration:
private JdbcTemplate jdbcTemplate;
include-code::./JdbcCorporateEventDaoConfiguration[tag=snippet,indent=0]
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
// JDBC-backed implementations of the methods on the CorporateEventDao follow...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class JdbcCorporateEventDao(dataSource: DataSource) : CorporateEventDao {
private val jdbcTemplate = JdbcTemplate(dataSource)
// JDBC-backed implementations of the methods on the CorporateEventDao follow...
}
----
======
--
The following example shows the corresponding XML configuration:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<bean id="corporateEventDao" class="com.example.JdbcCorporateEventDao">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<context:property-placeholder location="jdbc.properties"/>
</beans>
----
An alternative to explicit configuration is to use component-scanning and annotation
support for dependency injection. In this case, you can annotate the class with `@Repository`
(which makes it a candidate for component-scanning). The following example shows how to do so:
(which makes it a candidate for component-scanning) and annotate the `DataSource` setter
method with `@Autowired`. The following example shows how to do so:
include-code::./JdbcCorporateEventRepository[tag=snippet,indent=0]
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Repository // <1>
public class JdbcCorporateEventDao implements CorporateEventDao {
The following example shows the corresponding configuration:
private JdbcTemplate jdbcTemplate;
include-code::./JdbcCorporateEventRepositoryConfiguration[tag=snippet,indent=0]
@Autowired // <2>
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource); // <3>
}
// JDBC-backed implementations of the methods on the CorporateEventDao follow...
}
----
<1> Annotate the class with `@Repository`.
<2> Annotate the `DataSource` setter method with `@Autowired`.
<3> Create a new `JdbcTemplate` with the `DataSource`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Repository // <1>
class JdbcCorporateEventDao(dataSource: DataSource) : CorporateEventDao { // <2>
private val jdbcTemplate = JdbcTemplate(dataSource) // <3>
// JDBC-backed implementations of the methods on the CorporateEventDao follow...
}
----
<1> Annotate the class with `@Repository`.
<2> Constructor injection of the `DataSource`.
<3> Create a new `JdbcTemplate` with the `DataSource`.
======
--
The following example shows the corresponding XML configuration:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- Scans within the base package of the application for @Component classes to configure as beans -->
<context:component-scan base-package="org.springframework.docs.test" />
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<context:property-placeholder location="jdbc.properties"/>
</beans>
----
If you use Spring's `JdbcDaoSupport` class and your various JDBC-backed DAO classes
extend from it, your sub-class inherits a `setDataSource(..)` method from the
@@ -574,7 +698,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 xref:data-access/jdbc/core.adoc#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.
@@ -16,22 +16,124 @@ lightweight nature. Benefits include ease of configuration, quick startup time,
testability, and the ability to rapidly evolve your SQL during development.
[[jdbc-embedded-database]]
== Creating an Embedded Database
[[jdbc-embedded-database-xml]]
== Creating an Embedded Database by Using Spring XML
You can expose an embedded database instance as a bean as the following example shows:
If you want to expose an embedded database instance as a bean in a Spring
`ApplicationContext`, you can use the `embedded-database` tag in the `spring-jdbc` namespace:
include-code::./JdbcEmbeddedDatabaseConfiguration[tag=snippet,indent=0]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<jdbc:embedded-database id="dataSource" generate-name="true">
<jdbc:script location="classpath:schema.sql"/>
<jdbc:script location="classpath:test-data.sql"/>
</jdbc:embedded-database>
----
The preceding configuration creates an embedded H2 database that is populated with SQL from
The preceding configuration creates an embedded HSQL database that is populated with SQL from
the `schema.sql` and `test-data.sql` resources in the root of the classpath. In addition, as
a best practice, the embedded database is assigned a uniquely generated name. The
embedded database is made available to the Spring container as a bean of type
`javax.sql.DataSource` that can then be injected into data access objects as needed.
[[jdbc-embedded-database-java]]
== Creating an Embedded Database Programmatically
The `EmbeddedDatabaseBuilder` class provides a fluent API for constructing an embedded
database programmatically. You can use this when you need to create an embedded database in a
stand-alone environment or in a stand-alone integration test, as in the following example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
EmbeddedDatabase db = new EmbeddedDatabaseBuilder()
.generateUniqueName(true)
.setType(H2)
.setScriptEncoding("UTF-8")
.ignoreFailedDrops(true)
.addScript("schema.sql")
.addScripts("user_data.sql", "country_data.sql")
.build();
// perform actions against the db (EmbeddedDatabase extends javax.sql.DataSource)
db.shutdown()
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val db = EmbeddedDatabaseBuilder()
.generateUniqueName(true)
.setType(H2)
.setScriptEncoding("UTF-8")
.ignoreFailedDrops(true)
.addScript("schema.sql")
.addScripts("user_data.sql", "country_data.sql")
.build()
// perform actions against the db (EmbeddedDatabase extends javax.sql.DataSource)
db.shutdown()
----
======
See the {spring-framework-api}/jdbc/datasource/embedded/EmbeddedDatabaseBuilder.html[javadoc for `EmbeddedDatabaseBuilder`]
for further details on all supported options.
You can also use the `EmbeddedDatabaseBuilder` to create an embedded database by using Java
configuration, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
public class DataSourceConfig {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.generateUniqueName(true)
.setType(H2)
.setScriptEncoding("UTF-8")
.ignoreFailedDrops(true)
.addScript("schema.sql")
.addScripts("user_data.sql", "country_data.sql")
.build();
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
class DataSourceConfig {
@Bean
fun dataSource(): DataSource {
return EmbeddedDatabaseBuilder()
.generateUniqueName(true)
.setType(H2)
.setScriptEncoding("UTF-8")
.ignoreFailedDrops(true)
.addScript("schema.sql")
.addScripts("user_data.sql", "country_data.sql")
.build()
}
}
----
======
[[jdbc-embedded-database-types]]
== Selecting the Embedded Database Type
@@ -66,74 +168,6 @@ attribute of the `embedded-database` tag to `DERBY`. If you use the builder API,
call the `setType(EmbeddedDatabaseType)` method with `EmbeddedDatabaseType.DERBY`.
[[jdbc-embedded-database-types-custom]]
== Customizing the Embedded Database Type
While each supported type comes with default connection settings, it is possible
to customize them if necessary. The following example uses H2 with a custom driver:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
public class DataSourceConfig {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setDatabaseConfigurer(EmbeddedDatabaseConfigurers
.customizeConfigurer(H2, this::customize))
.addScript("schema.sql")
.build();
}
private EmbeddedDatabaseConfigurer customize(EmbeddedDatabaseConfigurer defaultConfigurer) {
return new EmbeddedDatabaseConfigurerDelegate(defaultConfigurer) {
@Override
public void configureConnectionProperties(ConnectionProperties properties, String databaseName) {
super.configureConnectionProperties(properties, databaseName);
properties.setDriverClass(CustomDriver.class);
}
};
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
class DataSourceConfig {
@Bean
fun dataSource(): DataSource {
return EmbeddedDatabaseBuilder()
.setDatabaseConfigurer(EmbeddedDatabaseConfigurers
.customizeConfigurer(EmbeddedDatabaseType.H2) { this.customize(it) })
.addScript("schema.sql")
.build()
}
private fun customize(defaultConfigurer: EmbeddedDatabaseConfigurer): EmbeddedDatabaseConfigurer {
return object : EmbeddedDatabaseConfigurerDelegate(defaultConfigurer) {
override fun configureConnectionProperties(
properties: ConnectionProperties,
databaseName: String
) {
super.configureConnectionProperties(properties, databaseName)
properties.setDriverClass(CustomDriver::class.java)
}
}
}
}
----
======
[[jdbc-embedded-database-dao-testing]]
== Testing Data Access Logic with an Embedded Database
@@ -143,8 +177,8 @@ 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 xref:data-access/jdbc/embedded-database-support.adoc#jdbc-embedded-database[Creating an Embedded Database].
The following listing shows the test template:
in xref:data-access/jdbc/embedded-database-support.adoc#jdbc-embedded-database-xml[Creating an Embedded Database by Using Spring XML] and xref:data-access/jdbc/embedded-database-support.adoc#jdbc-embedded-database-java[Creating an Embedded Database Programmatically]. The following listing
shows the test template:
[tabs]
======
@@ -87,8 +87,8 @@ On `LocalSessionFactoryBean`, this is available through the `bootstrapExecutor`
property. On the programmatic `LocalSessionFactoryBuilder`, there is an overloaded
`buildSessionFactory` method that takes a bootstrap executor argument.
Such a native Hibernate setup can also expose a JPA `EntityManagerFactory` for standard
JPA interaction next to native Hibernate access.
As of Spring Framework 5.1, such a native Hibernate setup can also expose a JPA
`EntityManagerFactory` for standard JPA interaction next to native Hibernate access.
See xref:data-access/orm/jpa.adoc#orm-jpa-hibernate[Native Hibernate Setup for JPA] for details.
====
@@ -272,13 +272,6 @@ is being accessed by other components (for example, calling `createEntityManager
calls block until the background bootstrapping has completed. In particular, when you use
Spring Data JPA, make sure to set up deferred bootstrapping for its repositories as well.
As of 6.2, JPA initialization is enforced before context refresh completion, waiting for
asynchronous bootstrapping to complete by then. This makes the availability of the fully
initialized database infrastructure predictable and allows for custom post-initialization
logic in `ContextRefreshedEvent` listeners etc. Putting such application-level database
initialization into `@PostConstruct` methods or the like is not recommended; this is
better placed in `Lifecycle.start` (if applicable) or a `ContextRefreshedEvent` listener.
[[orm-jpa-dao]]
== Implementing DAOs Based on JPA: `EntityManagerFactory` and `EntityManager`
@@ -364,28 +364,6 @@ Or you may pass in a parameter object with bean properties or record components:
.bindProperties(new Person("joe", "Joe", 34);
----
Alternatively, you can use positional parameters for binding values to statements.
Indices are zero based.
[source,java]
----
db.sql("INSERT INTO person (id, name, age) VALUES(:id, :name, :age)")
.bind(0, "joe")
.bind(1, "Joe")
.bind(2, 34);
----
In case your application is binding to many parameters, the same can be achieved with a single call:
[source,java]
----
List<?> values = List.of("joe", "Joe", 34);
db.sql("INSERT INTO person (id, name, age) VALUES(:id, :name, :age)")
.bindValues(values);
----
.R2DBC Native Bind Markers
****
R2DBC uses database-native bind markers that depend on the actual database vendor.
@@ -442,32 +442,12 @@ xref:data-access/transaction/declarative/rolling-back.adoc#transaction-declarati
for further details on rollback rule semantics, patterns, and warnings
regarding possible unintentional matches for pattern-based rollback rules.
[NOTE]
====
As of 6.2, you can globally change the default rollback behavior for example, through
`@EnableTransactionManagement(rollbackOn=ALL_EXCEPTIONS)`, leading to a rollback
for all exceptions raised within a transaction, including any checked exception.
For further customizations, `AnnotationTransactionAttributeSource` provides an
`addDefaultRollbackRule(RollbackRuleAttribute)` method for custom default rules.
Note that transaction-specific rollback rules override the default behavior but
retain the chosen default for unspecified exceptions. This is the case for
Spring's `@Transactional` as well as JTA's `jakarta.transaction.Transactional`
annotation.
Unless you rely on EJB-style business exceptions with commit behavior, it is
advisable to switch to `ALL_EXCEPTIONS` for consistent rollback semantics even
in case of a (potentially accidental) checked exception. Also, it is advisable
to make that switch for Kotlin-based applications where there is no enforcement
of checked exceptions at all.
====
Currently, you cannot have explicit control over the name of a transaction, where 'name'
means the transaction name that appears in a transaction monitor and in logging output.
For declarative transactions, the transaction name is always the fully-qualified class
name of the transactionally advised class + `.` + the method name. For example, if the
name + `.` + the method name of the transactionally advised class. For example, if the
`handlePayment(..)` method of the `BusinessService` class started a transaction, the
name of the transaction would be `com.example.BusinessService.handlePayment`.
name of the transaction would be: `com.example.BusinessService.handlePayment`.
[[tx-multiple-tx-mgrs-with-attransactional]]
== Multiple Transaction Managers with `@Transactional`
@@ -551,32 +531,12 @@ transaction managers, differentiated by the `order`, `account`, and `reactive-ac
qualifiers. The default `<tx:annotation-driven>` target bean name, `transactionManager`,
is still used if no specifically qualified `TransactionManager` bean is found.
[TIP]
====
If all transactional methods on the same class share the same qualifier, consider
declaring a type-level `org.springframework.beans.factory.annotation.Qualifier`
annotation instead. If its value matches the qualifier value (or bean name) of a
specific transaction manager, that transaction manager is going to be used for
transaction definitions without a specific qualifier on `@Transactional` itself.
Such a type-level qualifier can be declared on the concrete class, applying to
transaction definitions from a base class as well. This effectively overrides
the default transaction manager choice for any unqualified base class methods.
Last but not least, such a type-level bean qualifier can serve multiple purposes,
e.g. with a value of "order" it can be used for autowiring purposes (identifying
the order repository) as well as transaction manager selection, as long as the
target beans for autowiring as well as the associated transaction manager
definitions declare the same qualifier value. Such a qualifier value only needs
to be unique within a set of type-matching beans, not having to serve as an ID.
====
[[tx-custom-attributes]]
== Custom Composed Annotations
If you find you repeatedly use the same attributes with `@Transactional` on many different methods,
xref:core/beans/classpath-scanning.adoc#beans-meta-annotations[Spring's meta-annotation support]
lets you define custom composed annotations for your specific use cases. For example, consider the
If you find you repeatedly use the same attributes with `@Transactional` on many different
methods, xref:core/beans/classpath-scanning.adoc#beans-meta-annotations[Spring's meta-annotation support] lets you
define custom composed annotations for your specific use cases. For example, consider the
following annotation definitions:
[tabs]
@@ -19,8 +19,8 @@ marks a transaction for rollback only in the case of runtime, unchecked exceptio
That is, when the thrown exception is an instance or subclass of `RuntimeException`.
(`Error` instances also, by default, result in a rollback).
The default configuration also provides support for Vavr's `Try` method to trigger
transaction rollbacks when it returns a 'Failure'.
As of Spring Framework 5.2, the default configuration also provides support for
Vavr's `Try` method to trigger transaction rollbacks when it returns a 'Failure'.
This allows you to handle functional-style errors using Try and have the transaction
automatically rolled back in case of a failure. For more information on Vavr's Try,
refer to the {vavr-docs}/#_try[official Vavr documentation].
@@ -45,9 +45,9 @@ exists in the current call stack. The implication in this latter case is that, a
Jakarta EE transaction contexts, a `TransactionStatus` is associated with a thread of
execution.
Spring also provides a transaction management abstraction for reactive applications that
make use of reactive types or Kotlin Coroutines. The following listing shows the
transaction strategy defined by
As of Spring Framework 5.2, Spring also provides a transaction management abstraction for
reactive applications that make use of reactive types or Kotlin Coroutines. The following
listing shows the transaction strategy defined by
`org.springframework.transaction.ReactiveTransactionManager`:
[source,java,indent=0,subs="verbatim,quotes"]
@@ -518,9 +518,41 @@ disable it by removing only one configuration line rather than all the annotatio
your code).
To enable caching annotations add the annotation `@EnableCaching` to one of your
`@Configuration` classes or use the `cache:annotation-driven` element with XML:
`@Configuration` classes:
include-code::./CacheConfiguration[tag=snippet,indent=0]
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableCaching
class AppConfig {
@Bean
CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCacheSpecification(...);
return cacheManager;
}
}
----
Alternatively, for XML configuration you can use the `cache:annotation-driven` element:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/cache https://www.springframework.org/schema/cache/spring-cache.xsd">
<cache:annotation-driven/>
<bean id="cacheManager" class="org.springframework.cache.caffeine.CaffeineCacheManager">
<property name="cacheSpecification" value="..."/>
</bean>
</beans>
----
Both the `cache:annotation-driven` element and the `@EnableCaching` annotation let you
specify various options that influence the way the caching behavior is added to the
@@ -13,7 +13,18 @@ The JDK-based `Cache` implementation resides under
`org.springframework.cache.concurrent` package. It lets you use `ConcurrentHashMap`
as a backing `Cache` store. The following example shows how to configure two caches:
include-code::./CacheConfiguration[tag=snippet,indent=0]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- simple cache manager -->
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="default"/>
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="books"/>
</set>
</property>
</bean>
----
The preceding snippet uses the `SimpleCacheManager` to create a `CacheManager` for the
two nested `ConcurrentMapCache` instances named `default` and `books`. Note that the
@@ -41,12 +52,26 @@ of Caffeine.
The following example configures a `CacheManager` that creates the cache on demand:
include-code::./CacheConfiguration[tag=snippet,indent=0]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="cacheManager"
class="org.springframework.cache.caffeine.CaffeineCacheManager"/>
----
You can also provide the caches to use explicitly. In that case, only those
are made available by the manager. The following example shows how to do so:
include-code::./CustomCacheConfiguration[tag=snippet,indent=0]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="cacheManager" class="org.springframework.cache.caffeine.CaffeineCacheManager">
<property name="cacheNames">
<set>
<value>default</value>
<value>books</value>
</set>
</property>
</bean>
----
The Caffeine `CacheManager` also supports custom `Caffeine` and `CacheLoader`.
See the https://github.com/ben-manes/caffeine/wiki[Caffeine documentation]
@@ -72,7 +97,15 @@ implementation is located in the `org.springframework.cache.jcache` package.
Again, to use it, you need to declare the appropriate `CacheManager`.
The following example shows how to do so:
include-code::./CacheConfiguration[tag=snippet,indent=0]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="cacheManager"
class="org.springframework.cache.jcache.JCacheCacheManager"
p:cache-manager-ref="jCacheManager"/>
<!-- JSR-107 cache manager setup -->
<bean id="jCacheManager" .../>
----
[[cache-store-configuration-noop]]
@@ -86,7 +119,18 @@ cache declarations (which can prove tedious), you can wire in a simple dummy cac
performs no caching -- that is, it forces the cached methods to be invoked every time.
The following example shows how to do so:
include-code::./CacheConfiguration[tag=snippet,indent=0]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="cacheManager" class="org.springframework.cache.support.CompositeCacheManager">
<property name="cacheManagers">
<list>
<ref bean="jdkCache"/>
<ref bean="gemfireCache"/>
</list>
</property>
<property name="fallbackToNoOpCache" value="true"/>
</bean>
----
The `CompositeCacheManager` in the preceding chains multiple `CacheManager` instances and,
through the `fallbackToNoOpCache` flag, adds a no-op cache for all the definitions not
@@ -41,7 +41,14 @@ JavaMail features, such as MIME message support to the `MailSender` interface
Assume that we have a business interface called `OrderManager`, as the following example shows:
include-code::./OrderManager[tag=snippet,indent=0]
[source,java,indent=0,subs="verbatim,quotes"]
----
public interface OrderManager {
void placeOrder(Order order);
}
----
Further assume that we have a requirement stating that an email message with an
order number needs to be generated and sent to a customer who placed the relevant order.
@@ -53,11 +60,70 @@ order number needs to be generated and sent to a customer who placed the relevan
The following example shows how to use `MailSender` and `SimpleMailMessage` to send an
email when someone places an order:
include-code::./SimpleOrderManager[tag=snippet,indent=0]
[source,java,indent=0,subs="verbatim,quotes"]
----
import org.springframework.mail.MailException;
import org.springframework.mail.MailSender;
import org.springframework.mail.SimpleMailMessage;
public class SimpleOrderManager implements OrderManager {
private MailSender mailSender;
private SimpleMailMessage templateMessage;
public void setMailSender(MailSender mailSender) {
this.mailSender = mailSender;
}
public void setTemplateMessage(SimpleMailMessage templateMessage) {
this.templateMessage = templateMessage;
}
public void placeOrder(Order order) {
// Do the business calculations...
// Call the collaborators to persist the order...
// Create a thread-safe "copy" of the template message and customize it
SimpleMailMessage msg = new SimpleMailMessage(this.templateMessage);
msg.setTo(order.getCustomer().getEmailAddress());
msg.setText(
"Dear " + order.getCustomer().getFirstName()
+ order.getCustomer().getLastName()
+ ", thank you for placing order. Your order number is "
+ order.getOrderNumber());
try {
this.mailSender.send(msg);
}
catch (MailException ex) {
// simply log it and go on...
System.err.println(ex.getMessage());
}
}
}
----
The following example shows the bean definitions for the preceding code:
include-code::./MailConfiguration[tag=snippet,indent=0]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host" value="mail.mycompany.example"/>
</bean>
<!-- this is a template message that we can pre-load with default state -->
<bean id="templateMessage" class="org.springframework.mail.SimpleMailMessage">
<property name="from" value="customerservice@mycompany.example"/>
<property name="subject" value="Your order"/>
</bean>
<bean id="orderManager" class="com.mycompany.businessapp.support.SimpleOrderManager">
<property name="mailSender" ref="mailSender"/>
<property name="templateMessage" ref="templateMessage"/>
</bean>
----
[[mail-usage-mime]]
@@ -37,7 +37,23 @@ declarations to it.
To enable support for `@JmsListener` annotations, you can add `@EnableJms` to one of
your `@Configuration` classes, as the following example shows:
include-code::./JmsConfiguration[tag=snippet,indent=0]
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableJms
public class AppConfig {
@Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory());
factory.setDestinationResolver(destinationResolver());
factory.setSessionTransacted(true);
factory.setConcurrency("3-10");
return factory;
}
}
----
By default, the infrastructure looks for a bean named `jmsListenerContainerFactory`
as the source for the factory to use to create message listener containers. In this
@@ -51,6 +67,22 @@ container factory. See the javadoc of classes that implement
{spring-framework-api}/jms/annotation/JmsListenerConfigurer.html[`JmsListenerConfigurer`]
for details and examples.
If you prefer xref:integration/jms/namespace.adoc[XML configuration], you can use the `<jms:annotation-driven>`
element, as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<jms:annotation-driven/>
<bean id="jmsListenerContainerFactory"
class="org.springframework.jms.config.DefaultJmsListenerContainerFactory">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destinationResolver" ref="destinationResolver"/>
<property name="sessionTransacted" value="true"/>
<property name="concurrency" value="3-10"/>
</bean>
----
[[jms-annotated-programmatic-registration]]
== Programmatic Endpoint Registration
@@ -7,13 +7,62 @@ automatically determine the `ActivationSpec` class name from the provider's
`ResourceAdapter` class name. Therefore, it is typically possible to provide
Spring's generic `JmsActivationSpecConfig`, as the following example shows:
include-code::./JmsConfiguration[tag=snippet,indent=0]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean class="org.springframework.jms.listener.endpoint.JmsMessageEndpointManager">
<property name="resourceAdapter" ref="resourceAdapter"/>
<property name="activationSpecConfig">
<bean class="org.springframework.jms.listener.endpoint.JmsActivationSpecConfig">
<property name="destinationName" value="myQueue"/>
</bean>
</property>
<property name="messageListener" ref="myMessageListener"/>
</bean>
----
Alternatively, you can set up a `JmsMessageEndpointManager` with a given
`ActivationSpec` object. The `ActivationSpec` object may also come from a JNDI lookup
(using `<jee:jndi-lookup>`). The following example shows how to do so:
include-code::./AlternativeJmsConfiguration[tag=snippet,indent=0]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean class="org.springframework.jms.listener.endpoint.JmsMessageEndpointManager">
<property name="resourceAdapter" ref="resourceAdapter"/>
<property name="activationSpec">
<bean class="org.apache.activemq.ra.ActiveMQActivationSpec">
<property name="destination" value="myQueue"/>
<property name="destinationType" value="jakarta.jms.Queue"/>
</bean>
</property>
<property name="messageListener" ref="myMessageListener"/>
</bean>
----
Using Spring's `ResourceAdapterFactoryBean`, you can configure the target `ResourceAdapter`
locally, as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="resourceAdapter" class="org.springframework.jca.support.ResourceAdapterFactoryBean">
<property name="resourceAdapter">
<bean class="org.apache.activemq.ra.ActiveMQResourceAdapter">
<property name="serverUrl" value="tcp://localhost:61616"/>
</bean>
</property>
<property name="workManager">
<bean class="org.springframework.jca.work.SimpleTaskWorkManager"/>
</property>
</bean>
----
The specified `WorkManager` can also point to an environment-specific thread pool --
typically through a `SimpleTaskWorkManager` instance's `asyncTaskExecutor` property.
Consider defining a shared thread pool for all your `ResourceAdapter` instances
if you happen to use multiple adapters.
In some environments, you can instead obtain the entire `ResourceAdapter` object from JNDI
(by using `<jee:jndi-lookup>`). The Spring-based message listeners can then interact with
the server-hosted `ResourceAdapter`, which also use the server's built-in `WorkManager`.
See the javadoc for {spring-framework-api}/jms/listener/endpoint/JmsMessageEndpointManager.html[`JmsMessageEndpointManager`],
{spring-framework-api}/jms/listener/endpoint/JmsActivationSpecConfig.html[`JmsActivationSpecConfig`],
@@ -31,7 +31,30 @@ on multiple threads, it is important to ensure that your implementation is threa
The following example shows a simple implementation of an MDP:
include-code::./ExampleListener[tag=snippet,indent=0]
[source,java,indent=0,subs="verbatim,quotes"]
----
import jakarta.jms.JMSException;
import jakarta.jms.Message;
import jakarta.jms.MessageListener;
import jakarta.jms.TextMessage;
public class ExampleListener implements MessageListener {
public void onMessage(Message message) {
if (message instanceof TextMessage textMessage) {
try {
System.out.println(textMessage.getText());
}
catch (JMSException ex) {
throw new RuntimeException(ex);
}
}
else {
throw new IllegalArgumentException("Message must be of type TextMessage");
}
}
}
----
Once you have implemented your `MessageListener`, it is time to create a message listener
container.
@@ -39,7 +62,18 @@ container.
The following example shows how to define and configure one of the message listener
containers that ships with Spring (in this case, `DefaultMessageListenerContainer`):
include-code::./JmsConfiguration[tag=snippet,indent=0]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- this is the Message Driven POJO (MDP) -->
<bean id="messageListener" class="jmsexample.ExampleListener"/>
<!-- and this is the message listener container -->
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="destination"/>
<property name="messageListener" ref="messageListener"/>
</bean>
----
See the Spring javadoc of the various message listener containers (all of which implement
{spring-framework-api}/jms/listener/MessageListenerContainer.html[MessageListenerContainer])
@@ -89,7 +123,19 @@ messaging support. In a nutshell, it lets you expose almost any class as an MDP
Consider the following interface definition:
include-code::./MessageDelegate[tag=snippet,indent=0]
[source,java,indent=0,subs="verbatim,quotes"]
----
public interface MessageDelegate {
void handleMessage(String message);
void handleMessage(Map message);
void handleMessage(byte[] message);
void handleMessage(Serializable message);
}
----
Notice that, although the interface extends neither the `MessageListener` nor the
`SessionAwareMessageListener` interface, you can still use it as an MDP by using the
@@ -99,13 +145,33 @@ receive and handle.
Now consider the following implementation of the `MessageDelegate` interface:
include-code::./DefaultMessageDelegate[tag=snippet,indent=0]
[source,java,indent=0,subs="verbatim,quotes"]
----
public class DefaultMessageDelegate implements MessageDelegate {
// implementation elided for clarity...
}
----
In particular, note how the preceding implementation of the `MessageDelegate` interface (the
`DefaultMessageDelegate` class) has no JMS dependencies at all. It truly is a
POJO that we can make into an MDP through the following configuration:
include-code::./JmsConfiguration[tag=snippet,indent=0]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- this is the Message Driven POJO (MDP) -->
<bean id="messageListener" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
<constructor-arg>
<bean class="jmsexample.DefaultMessageDelegate"/>
</constructor-arg>
</bean>
<!-- and this is the message listener container... -->
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="destination"/>
<property name="messageListener" ref="messageListener"/>
</bean>
----
The next example shows another MDP that can handle only receiving JMS
`TextMessage` messages. Notice how the message handling method is actually called
@@ -115,15 +181,38 @@ also how the `receive(..)` method is strongly typed to receive and respond only
`TextMessage` messages.
The following listing shows the definition of the `TextMessageDelegate` interface:
include-code::./TextMessageDelegate[tag=snippet,indent=0]
[source,java,indent=0,subs="verbatim,quotes"]
----
public interface TextMessageDelegate {
void receive(TextMessage message);
}
----
The following listing shows a class that implements the `TextMessageDelegate` interface:
include-code::./DefaultTextMessageDelegate[tag=snippet,indent=0]
[source,java,indent=0,subs="verbatim,quotes"]
----
public class DefaultTextMessageDelegate implements TextMessageDelegate {
// implementation elided for clarity...
}
----
The configuration of the attendant `MessageListenerAdapter` would then be as follows:
include-code::./MessageListenerConfiguration[tag=snippet,indent=0]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="messageListener" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
<constructor-arg>
<bean class="jmsexample.DefaultTextMessageDelegate"/>
</constructor-arg>
<property name="defaultListenerMethod" value="receive"/>
<!-- we don't want automatic message context extraction -->
<property name="messageConverter">
<null/>
</property>
</bean>
----
Note that, if the `messageListener` receives a JMS `Message` of a type
other than `TextMessage`, an `IllegalStateException` is thrown (and subsequently
@@ -131,9 +220,21 @@ swallowed). Another of the capabilities of the `MessageListenerAdapter` class is
ability to automatically send back a response `Message` if a handler method returns a
non-void value. Consider the following interface and class:
include-code::./ResponsiveTextMessageDelegate[tag=snippet,indent=0]
[source,java,indent=0,subs="verbatim,quotes"]
----
public interface ResponsiveTextMessageDelegate {
include-code::./DefaultResponsiveTextMessageDelegate[tag=snippet,indent=0]
// notice the return type...
String receive(TextMessage message);
}
----
[source,java,indent=0,subs="verbatim,quotes"]
----
public class DefaultResponsiveTextMessageDelegate implements ResponsiveTextMessageDelegate {
// implementation elided for clarity...
}
----
If you use the `DefaultResponsiveTextMessageDelegate` in conjunction with a
`MessageListenerAdapter`, any non-null value that is returned from the execution of
@@ -163,7 +264,15 @@ has committed but message processing failed to commit.
Consider the following bean definition:
include-code::./JmsConfiguration[tag=snippet,indent=0]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="destination"/>
<property name="messageListener" ref="messageListener"/>
<property name="sessionTransacted" value="true"/>
</bean>
----
To participate in an externally managed transaction, you need to configure a
transaction manager and use a listener container that supports externally managed
@@ -179,9 +288,24 @@ semantics, at the expense of XA transaction log overhead).
The following bean definition creates a transaction manager:
include-code::./ExternalTxJmsConfiguration[tag=transactionManagerSnippet,indent=0]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
----
Then we need to add it to our earlier container configuration. The container
takes care of the rest. The following example shows how to do so:
include-code::./ExternalTxJmsConfiguration[tag=jmsContainerSnippet,indent=0]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="destination"/>
<property name="messageListener" ref="messageListener"/>
<property name="transactionManager" ref="transactionManager"/> <1>
</bean>
----
<1> Our transaction manager.
@@ -5,13 +5,63 @@ The core class in Spring's JMX framework is the `MBeanExporter`. This class is
responsible for taking your Spring beans and registering them with a JMX `MBeanServer`.
For example, consider the following class:
include-code::./JmxTestBean[tag=snippet,indent=0]
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages",chomp="-packages"]
----
package org.springframework.jmx;
public class JmxTestBean implements IJmxTestBean {
private String name;
private int age;
private boolean isSuperman;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public int add(int x, int y) {
return x + y;
}
public void dontExposeMe() {
throw new RuntimeException();
}
}
----
To expose the properties and methods of this bean as attributes and operations of an
MBean, you can configure an instance of the `MBeanExporter` class in your
configuration file and pass in the bean, as the following example shows:
include-code::./JmxConfiguration[tag=snippet,indent=0]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<beans>
<!-- this bean must not be lazily initialized if the exporting is to happen -->
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
<property name="beans">
<map>
<entry key="bean:name=testBean1" value-ref="testBean"/>
</map>
</property>
</bean>
<bean id="testBean" class="org.springframework.jmx.JmxTestBean">
<property name="name" value="TEST"/>
<property name="age" value="100"/>
</bean>
</beans>
----
The pertinent bean definition from the preceding configuration snippet is the `exporter`
bean. The `beans` property tells the `MBeanExporter` exactly which of your beans must be
@@ -122,10 +122,25 @@ your management interfaces, a convenience subclass of `MBeanExporter` is availab
`namingStrategy`, `assembler`, and `attributeSource` configuration,
since it always uses standard Java annotation-based metadata (autodetection is
always enabled as well). In fact, rather than defining an `MBeanExporter` bean, an even
simpler syntax is supported by the `@EnableMBeanExport` `@Configuration` annotation or the `<context:mbean-export/>`
element as the following example shows:
simpler syntax is supported by the `@EnableMBeanExport` `@Configuration` annotation,
as the following example shows:
include-code::./JmxConfiguration[tag=snippet,indent=0]
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableMBeanExport
public class AppConfig {
}
----
If you prefer XML-based configuration, the `<context:mbean-export/>` element serves the
same purpose and is shown in the following listing:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<context:mbean-export/>
----
If necessary, you can provide a reference to a particular MBean `server`, and the
`defaultDomain` attribute (a property of `AnnotationMBeanExporter`) accepts an alternate
@@ -133,7 +148,21 @@ value for the generated MBean `ObjectName` domains. This is used in place of the
fully qualified package name as described in the previous section on
xref:integration/jmx/naming.adoc#jmx-naming-metadata[MetadataNamingStrategy], as the following example shows:
include-code::./CustomJmxConfiguration[tag=snippet,indent=0]
[source,java,indent=0,subs="verbatim,quotes"]
----
@EnableMBeanExport(server="myMBeanServer", defaultDomain="myDomain")
@Configuration
ContextConfiguration {
}
----
The following example shows the XML equivalent of the preceding annotation-based example:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<context:mbean-export server="myMBeanServer" default-domain="myDomain"/>
----
CAUTION: Do not use interface-based AOP proxies in combination with autodetection of JMX
annotations in your bean classes. Interface-based proxies "`hide`" the target class, which
@@ -366,7 +366,68 @@ val result = restClient.get()
[[rest-message-conversion]]
=== HTTP Message Conversion
xref:web/webmvc/message-converters.adoc#message-converters[See the supported HTTP message converters in the dedicated section].
[.small]#xref:web/webflux/reactive-spring.adoc#webflux-codecs[See equivalent in the Reactive stack]#
The `spring-web` module contains the `HttpMessageConverter` interface for reading and writing the body of HTTP requests and responses through `InputStream` and `OutputStream`.
`HttpMessageConverter` instances are used on the client side (for example, in the `RestClient`) and on the server side (for example, in Spring MVC REST controllers).
Concrete implementations for the main media (MIME) types are provided in the framework and are, by default, registered with the `RestClient` and `RestTemplate` on the client side and with `RequestMappingHandlerAdapter` on the server side (see xref:web/webmvc/mvc-config/message-converters.adoc[Configuring Message Converters]).
Several implementations of `HttpMessageConverter` are described below.
Refer to the {spring-framework-api}/http/converter/HttpMessageConverter.html[`HttpMessageConverter` Javadoc] for the complete list.
For all converters, a default media type is used, but you can override it by setting the `supportedMediaTypes` property.
[[rest-message-converters-tbl]]
.HttpMessageConverter Implementations
[cols="1,3"]
|===
| MessageConverter | Description
| `StringHttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write `String` instances from the HTTP request and response.
By default, this converter supports all text media types(`text/{asterisk}`) and writes with a `Content-Type` of `text/plain`.
| `FormHttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write form data from the HTTP request and response.
By default, this converter reads and writes the `application/x-www-form-urlencoded` media type.
Form data is read from and written into a `MultiValueMap<String, String>`.
The converter can also write (but not read) multipart data read from a `MultiValueMap<String, Object>`.
By default, `multipart/form-data` is supported.
Additional multipart subtypes can be supported for writing form data.
Consult the javadoc for `FormHttpMessageConverter` for further details.
| `ByteArrayHttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write byte arrays from the HTTP request and response.
By default, this converter supports all media types (`{asterisk}/{asterisk}`) and writes with a `Content-Type` of `application/octet-stream`.
You can override this by setting the `supportedMediaTypes` property and overriding `getContentType(byte[])`.
| `MarshallingHttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write XML by using Spring's `Marshaller` and `Unmarshaller` abstractions from the `org.springframework.oxm` package.
This converter requires a `Marshaller` and `Unmarshaller` before it can be used.
You can inject these through constructor or bean properties.
By default, this converter supports `text/xml` and `application/xml`.
| `MappingJackson2HttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write JSON by using Jackson's `ObjectMapper`.
You can customize JSON mapping as needed through the use of Jackson's provided annotations.
When you need further control (for cases where custom JSON serializers/deserializers need to be provided for specific types), you can inject a custom `ObjectMapper` through the `ObjectMapper` property.
By default, this converter supports `application/json`.
| `MappingJackson2XmlHttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write XML by using {jackson-github-org}/jackson-dataformat-xml[Jackson XML] extension's `XmlMapper`.
You can customize XML mapping as needed through the use of JAXB or Jackson's provided annotations.
When you need further control (for cases where custom XML serializers/deserializers need to be provided for specific types), you can inject a custom `XmlMapper` through the `ObjectMapper` property.
By default, this converter supports `application/xml`.
| `SourceHttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write `javax.xml.transform.Source` from the HTTP request and response.
Only `DOMSource`, `SAXSource`, and `StreamSource` are supported.
By default, this converter supports `text/xml` and `application/xml`.
|===
By default, `RestClient` and `RestTemplate` register all built-in message converters, depending on the availability of underlying libraries on the classpath.
You can also set the message converters to use explicitly, by using the `messageConverters()` method on the `RestClient` builder, or via the `messageConverters` property of `RestTemplate`.
==== Jackson JSON Views
@@ -938,8 +999,7 @@ method parameters:
| `@RequestHeader`
| Add a request header or multiple headers. The argument may be a `Map<String, ?>` or
`MultiValueMap<String, ?>` with multiple headers, a `Collection<?>` of values, or an
individual value. Type conversion is supported for non-String values. This overrides
the annotation's `headers` attribute.
individual value. Type conversion is supported for non-String values.
| `@PathVariable`
| Add a variable for expand a placeholder in the request URL. The argument may be a
@@ -947,8 +1007,7 @@ method parameters:
is supported for non-String values.
| `@RequestAttribute`
| Provide an `Object` to add as a request attribute. Only supported by `RestClient`
and `WebClient`.
| Provide an `Object` to add as a request attribute. Only supported by `WebClient`.
| `@RequestBody`
| Provide the body of the request either as an Object to be serialized, or a
@@ -980,11 +1039,6 @@ method parameters:
|===
Method parameters cannot be `null` unless the `required` attribute (where available on a
parameter annotation) is set to `false`, or the parameter is marked optional as determined by
{spring-framework-api}/core/MethodParameter.html#isOptional()[`MethodParameter#isOptional`].
[[rest-http-interface-return-values]]
=== Return Values
@@ -79,7 +79,38 @@ Spring's `TaskExecutor` implementations are commonly used with dependency inject
In the following example, we define a bean that uses the `ThreadPoolTaskExecutor`
to asynchronously print out a set of messages:
include-code::./TaskExecutorExample[tag=snippet,indent=0]
[source,java,indent=0,subs="verbatim,quotes"]
----
import org.springframework.core.task.TaskExecutor;
public class TaskExecutorExample {
private class MessagePrinterTask implements Runnable {
private String message;
public MessagePrinterTask(String message) {
this.message = message;
}
public void run() {
System.out.println(message);
}
}
private TaskExecutor taskExecutor;
public TaskExecutorExample(TaskExecutor taskExecutor) {
this.taskExecutor = taskExecutor;
}
public void printMessages() {
for(int i = 0; i < 25; i++) {
taskExecutor.execute(new MessagePrinterTask("Message" + i));
}
}
}
----
As you can see, rather than retrieving a thread from the pool and executing it yourself,
you add your `Runnable` to the queue. Then the `TaskExecutor` uses its internal rules to
@@ -87,7 +118,19 @@ decide when the task gets run.
To configure the rules that the `TaskExecutor` uses, we expose simple bean properties:
include-code::./TaskExecutorConfiguration[tag=snippet,indent=0]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="5"/>
<property name="maxPoolSize" value="10"/>
<property name="queueCapacity" value="25"/>
</bean>
<bean id="taskExecutorExample" class="TaskExecutorExample">
<constructor-arg ref="taskExecutor"/>
</bean>
----
[[scheduling-task-scheduler]]
@@ -226,10 +269,16 @@ execution.
=== Enable Scheduling Annotations
To enable support for `@Scheduled` and `@Async` annotations, you can add `@EnableScheduling`
and `@EnableAsync` to one of your `@Configuration` classes, or `<task:annotation-driven>` element,
as the following example shows:
and `@EnableAsync` to one of your `@Configuration` classes, as the following example shows:
include-code::./SchedulingConfiguration[tag=snippet,indent=0]
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableAsync
@EnableScheduling
public class AppConfig {
}
----
You can pick and choose the relevant annotations for your application. For example,
if you need only support for `@Scheduled`, you can omit `@EnableAsync`. For more
@@ -239,6 +288,16 @@ interface, the `AsyncConfigurer` interface, or both. See the
and {spring-framework-api}/scheduling/annotation/AsyncConfigurer.html[`AsyncConfigurer`]
javadoc for full details.
If you prefer XML configuration, you can use the `<task:annotation-driven>` element,
as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<task:annotation-driven executor="myExecutor" scheduler="myScheduler"/>
<task:executor id="myExecutor" pool-size="5"/>
<task:scheduler id="myScheduler" pool-size="10"/>
----
Note that, with the preceding XML, an executor reference is provided for handling those
tasks that correspond to methods with the `@Async` annotation, and the scheduler
reference is provided for managing those methods annotated with `@Scheduled`.
@@ -209,7 +209,7 @@ class UserHandler(builder: WebClient.Builder) {
== Transactions
Transactions on Coroutines are supported via the programmatic variant of the Reactive
transaction management.
transaction management provided as of Spring Framework 5.2.
For suspending functions, a `TransactionalOperator.executeAndAwait` extension is provided.
@@ -10,7 +10,7 @@ Spring requires Java 17+.
Spring supports a wide range of application scenarios. In a large enterprise, applications
often exist for a long time and have to run on a JDK and application server whose upgrade
cycle is beyond the developer's control. Others may run as a single jar with the server embedded,
cycle is beyond developer control. Others may run as a single jar with the server embedded,
possibly in a cloud environment. Yet others may be standalone applications (such as batch
or integration workloads) that do not need a server.
@@ -1115,9 +1115,7 @@ method parameters:
| `@Payload`
| Set the input payload(s) for the request. This can be a concrete value, or any producer
of values that can be adapted to a Reactive Streams `Publisher` via
`ReactiveAdapterRegistry`. A payload must be provided unless the `required` attribute
is set to `false`, or the parameter is marked optional as determined by
{spring-framework-api}/core/MethodParameter.html#isOptional()[`MethodParameter#isOptional`].
`ReactiveAdapterRegistry`
| `Object`, if followed by `MimeType`
| The value for a metadata entry in the input payload. This can be any `Object` as long
@@ -165,8 +165,8 @@ for further details.
[[integration-testing-annotations-testconstructor]]
== `@TestConstructor`
`@TestConstructor` is an annotation that can be applied to a test class to configure how
the parameters of a test class constructor are autowired from components in the test's
`@TestConstructor` is a type-level annotation that is used to configure how the parameters
of a test class constructor are autowired from components in the test's
`ApplicationContext`.
If `@TestConstructor` is not present or meta-present on a test class, the default _test
@@ -183,24 +183,25 @@ The default _test constructor autowire mode_ can be changed by setting the
default mode may be set via the
xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism.
The default mode may also be configured as a
As of Spring Framework 5.3, the default mode may also be configured as a
https://junit.org/junit5/docs/current/user-guide/#running-tests-config-params[JUnit Platform configuration parameter].
If the `spring.test.constructor.autowire.mode` property is not set, test class
constructors will not be automatically autowired.
=====
NOTE: `@TestConstructor` is only supported in conjunction with the `SpringExtension` for
use with JUnit Jupiter. Note that the `SpringExtension` is often automatically registered
for you for example, when using annotations such as `@SpringJUnitConfig` and
`@SpringJUnitWebConfig` or various test-related annotations from Spring Boot Test.
NOTE: As of Spring Framework 5.2, `@TestConstructor` is only supported in conjunction
with the `SpringExtension` for use with JUnit Jupiter. Note that the `SpringExtension` is
often automatically registered for you for example, when using annotations such as
`@SpringJUnitConfig` and `@SpringJUnitWebConfig` or various test-related annotations from
Spring Boot Test.
[[integration-testing-annotations-nestedtestconfiguration]]
== `@NestedTestConfiguration`
`@NestedTestConfiguration` is an annotation that can be applied to a test class to
configure how Spring test configuration annotations are processed within enclosing class
hierarchies for inner test classes.
`@NestedTestConfiguration` is a type-level annotation that is used to configure how
Spring test configuration annotations are processed within enclosing class hierarchies
for inner test classes.
If `@NestedTestConfiguration` is not present or meta-present on a test class, in its
supertype hierarchy, or in its enclosing class hierarchy, the default _enclosing
@@ -13,10 +13,10 @@ xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit4-runne
[[integration-testing-annotations-junit4-ifprofilevalue]]
== `@IfProfileValue`
`@IfProfileValue` indicates that the annotated test class or test method is enabled for a
specific testing environment. If the configured `ProfileValueSource` returns a matching
`value` for the provided `name`, the test is enabled. Otherwise, the test is disabled
and, effectively, ignored.
`@IfProfileValue` indicates that the annotated test is enabled for a specific testing
environment. If the configured `ProfileValueSource` returns a matching `value` for the
provided `name`, the test is enabled. Otherwise, the test is disabled and, effectively,
ignored.
You can apply `@IfProfileValue` at the class level, the method level, or both.
Class-level usage of `@IfProfileValue` takes precedence over method-level usage for any
@@ -90,12 +90,11 @@ Kotlin::
[[integration-testing-annotations-junit4-profilevaluesourceconfiguration]]
== `@ProfileValueSourceConfiguration`
`@ProfileValueSourceConfiguration` is an annotation that can be applied to a test class
to specify what type of `ProfileValueSource` to use when retrieving profile values
configured through the `@IfProfileValue` annotation. If
`@ProfileValueSourceConfiguration` is not declared for a test, `SystemProfileValueSource`
is used by default. The following example shows how to use
`@ProfileValueSourceConfiguration`:
`@ProfileValueSourceConfiguration` is a class-level annotation that specifies what type
of `ProfileValueSource` to use when retrieving profile values configured through the
`@IfProfileValue` annotation. If `@ProfileValueSourceConfiguration` is not declared for a
test, `SystemProfileValueSource` is used by default. The following example shows how to
use `@ProfileValueSourceConfiguration`:
[tabs]
======
@@ -16,8 +16,6 @@ Spring's testing annotations include the following:
* xref:testing/annotations/integration-spring/annotation-activeprofiles.adoc[`@ActiveProfiles`]
* xref:testing/annotations/integration-spring/annotation-testpropertysource.adoc[`@TestPropertySource`]
* xref:testing/annotations/integration-spring/annotation-dynamicpropertysource.adoc[`@DynamicPropertySource`]
* xref:testing/annotations/integration-spring/annotation-testbean.adoc[`@TestBean`]
* xref:testing/annotations/integration-spring/annotation-mockitobean.adoc[`@MockitoBean` and `@MockitoSpyBean`]
* xref:testing/annotations/integration-spring/annotation-dirtiescontext.adoc[`@DirtiesContext`]
* xref:testing/annotations/integration-spring/annotation-testexecutionlisteners.adoc[`@TestExecutionListeners`]
* xref:testing/annotations/integration-spring/annotation-recordapplicationevents.adoc[`@RecordApplicationEvents`]
@@ -1,8 +1,8 @@
[[spring-testing-annotation-activeprofiles]]
= `@ActiveProfiles`
`@ActiveProfiles` is an annotation that can be applied to a test class to declare which
bean definition profiles should be active when loading an `ApplicationContext` for an
`@ActiveProfiles` is a class-level annotation that is used to declare which bean
definition profiles should be active when loading an `ApplicationContext` for an
integration test.
The following example indicates that the `dev` profile should be active:
@@ -2,8 +2,8 @@
= `@BootstrapWith`
:page-section-summary-toc: 1
`@BootstrapWith` is an annotation that can be applied to a test class to configure how
the Spring TestContext Framework is bootstrapped. Specifically, you can use
`@BootstrapWith` to specify a custom `TestContextBootstrapper`. See the section on
`@BootstrapWith` is a class-level annotation that you can use to configure how the Spring
TestContext Framework is bootstrapped. Specifically, you can use `@BootstrapWith` to
specify a custom `TestContextBootstrapper`. See the section on
xref:testing/testcontext-framework/bootstrapping.adoc[bootstrapping the TestContext framework] for further details.
@@ -1,10 +1,10 @@
[[spring-testing-annotation-contextconfiguration]]
= `@ContextConfiguration`
`@ContextConfiguration` is an annotation that can be applied to a test class to configure
metadata that is used to determine how to load and configure an `ApplicationContext` for
integration tests. Specifically, `@ContextConfiguration` declares the application context
resource `locations` or the component `classes` used to load the context.
`@ContextConfiguration` defines class-level metadata that is used to determine how to
load and configure an `ApplicationContext` for integration tests. Specifically,
`@ContextConfiguration` declares the application context resource `locations` or the
component `classes` used to load the context.
Resource locations are typically XML configuration files or Groovy scripts located in the
classpath, while component classes are typically `@Configuration` classes. However,
@@ -1,10 +1,10 @@
[[spring-testing-annotation-contextcustomizerfactories]]
= `@ContextCustomizerFactories`
`@ContextCustomizerFactories` is an annotation that can be applied to a test class to
register `ContextCustomizerFactory` implementations for the particular test class, its
subclasses, and its nested classes. If you wish to register a factory globally, you
should register it via the automatic discovery mechanism described in
`@ContextCustomizerFactories` is used to register `ContextCustomizerFactory`
implementations for a particular test class, its subclasses, and its nested classes. If
you wish to register a factory globally, you should register it via the automatic
discovery mechanism described in
xref:testing/testcontext-framework/ctx-management/context-customizers.adoc[`ContextCustomizerFactory` Configuration].
The following example shows how to register two `ContextCustomizerFactory` implementations:
@@ -1,12 +1,12 @@
[[spring-testing-annotation-contexthierarchy]]
= `@ContextHierarchy`
`@ContextHierarchy` is an annotation that can be applied to a test class to define a
hierarchy of `ApplicationContext` instances for integration tests. `@ContextHierarchy`
should be declared with a list of one or more `@ContextConfiguration` instances, each of
which defines a level in the context hierarchy. The following examples demonstrate the
use of `@ContextHierarchy` within a single test class (`@ContextHierarchy` can also be
used within a test class hierarchy):
`@ContextHierarchy` is a class-level annotation that is used to define a hierarchy of
`ApplicationContext` instances for integration tests. `@ContextHierarchy` should be
declared with a list of one or more `@ContextConfiguration` instances, each of which
defines a level in the context hierarchy. The following examples demonstrate the use of
`@ContextHierarchy` within a single test class (`@ContextHierarchy` can also be used
within a test class hierarchy):
[tabs]
======
@@ -10,9 +10,9 @@ rebuilt for any subsequent test that requires a context with the same configurat
metadata.
You can use `@DirtiesContext` as both a class-level and a method-level annotation within
the same test class or test class hierarchy. In such scenarios, the `ApplicationContext`
is marked as dirty before or after any such annotated method as well as before or after
the current test class, depending on the configured `methodMode` and `classMode`. When
the same class or class hierarchy. In such scenarios, the `ApplicationContext` is marked
as dirty before or after any such annotated method as well as before or after the current
test class, depending on the configured `methodMode` and `classMode`. When
`@DirtiesContext` is declared at both the class level and the method level, the
configured modes from both annotations will be honored. For example, if the class mode is
set to `BEFORE_EACH_TEST_METHOD` and the method mode is set to `AFTER_METHOD`, the
@@ -1,7 +1,7 @@
[[spring-testing-annotation-disabledinaotmode]]
= `@DisabledInAotMode`
`@DisabledInAotMode` signals that the annotated test class is disabled in Spring AOT
`@DisabledInAotMode` signals that an annotated test class is disabled in Spring AOT
(ahead-of-time) mode, which means that the `ApplicationContext` for the test class will
not be processed for AOT optimizations at build time.
@@ -1,12 +1,12 @@
[[spring-testing-annotation-dynamicpropertysource]]
= `@DynamicPropertySource`
`@DynamicPropertySource` is an annotation that can be applied to methods in integration
test classes that need to register _dynamic_ properties to be added to the set of
`PropertySources` in the `Environment` for an `ApplicationContext` loaded for an
integration test. Dynamic properties are useful when you do not know the value of the
properties upfront for example, if the properties are managed by an external resource
such as for a container managed by the {testcontainers-site}[Testcontainers] project.
`@DynamicPropertySource` is a method-level annotation that you can use to register
_dynamic_ properties to be added to the set of `PropertySources` in the `Environment` for
an `ApplicationContext` loaded for an integration test. Dynamic properties are useful
when you do not know the value of the properties upfront for example, if the properties
are managed by an external resource such as for a container managed by the
{testcontainers-site}[Testcontainers] project.
The following example demonstrates how to register a dynamic property:
@@ -1,124 +0,0 @@
[[spring-testing-annotation-beanoverriding-mockitobean]]
= `@MockitoBean` and `@MockitoSpyBean`
`@MockitoBean` and `@MockitoSpyBean` are used on fields in test classes to override beans
in the test's `ApplicationContext` with a Mockito mock or spy, respectively. In the
latter case, the original bean definition is not replaced, but instead an early instance
of the bean is captured and wrapped by the spy.
By default, the annotated field's type is used to search for candidate bean definitions
to override. If multiple candidates match, `@Qualifier` can be provided to narrow the
candidate to override. Alternatively, a candidate whose bean definition name matches the
name of the field will match.
To use a by-name override rather than a by-type override, specify the `name` attribute
of the annotation.
[WARNING]
====
Qualifiers, including the name of the field, are used to determine if a separate
`ApplicationContext` needs to be created. If you are using this feature to mock or spy
the same bean in several tests, make sure to name the field consistently to avoid
creating unnecessary contexts.
====
Each annotation also defines Mockito-specific attributes to fine-tune the mocking details.
During the test class lifecycle, Mockito is set up via the `Mockito#mockitoSession()`
mechanism. Notably, it enables `STRICT_STUBS` mode by default. This can be changed on
individual test classes with the `@MockitoBeanSettings` annotation.
The `@MockitoBean` annotation uses the `REPLACE_OR_CREATE_DEFINITION`
xref:testing/testcontext-framework/bean-overriding.adoc#testcontext-bean-overriding-custom[strategy for test bean overriding].
If no existing bean definition matches, a new bean definition is created on the fly.
The `@MockitoSpyBean` annotation uses the `WRAP_BEAN`
xref:testing/testcontext-framework/bean-overriding.adoc#testcontext-bean-overriding-custom[strategy],
and the original instance is wrapped in a Mockito spy. This strategy requires that
exactly one candidate bean definition exists.
The following example shows how to use the default behavior of the `@MockitoBean` annotation:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
class OverrideBeanTests {
@MockitoBean // <1>
private CustomService customService;
// test case body...
}
----
<1> Replace the bean with type `CustomService` with a Mockito `mock`.
======
In the example above, we are creating a mock for `CustomService`. If more than one bean
of that type exists, the bean named `customService` is considered. Otherwise, the test
will fail, and you will need to provide a qualifier of some sort to identify which of the
`CustomService` beans you want to override. If no such bean exists, a bean definition
will be created with an auto-generated bean name.
The following example uses a by-name lookup, rather than a by-type lookup:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
class OverrideBeanTests {
@MockitoBean(name = "service") // <1>
private CustomService customService;
// test case body...
}
----
<1> Replace the bean named `service` with a Mockito `mock`.
======
If no bean definition named `service` exists, one is created.
The following example shows how to use the default behavior of the `@MockitoSpyBean` annotation:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
class OverrideBeanTests {
@MockitoSpyBean // <1>
private CustomService customService;
// test case body...
}
----
<1> Wrap the bean with type `CustomService` with a Mockito `spy`.
======
In the example above, we are wrapping the bean with type `CustomService`. If more than
one bean of that type exists, the bean named `customService` is considered. Otherwise,
the test will fail, and you will need to provide a qualifier of some sort to identify
which of the `CustomService` beans you want to spy.
The following example uses a by-name lookup, rather than a by-type lookup:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
class OverrideBeanTests {
@MockitoSpyBean(name = "service") // <1>
private CustomService customService;
// test case body...
}
----
<1> Wrap the bean named `service` with a Mockito `spy`.
======
@@ -2,9 +2,9 @@
= `@RecordApplicationEvents`
:page-section-summary-toc: 1
`@RecordApplicationEvents` is an annotation that can be applied to a test class to
instruct the _Spring TestContext Framework_ to record all application events that are
published in the `ApplicationContext` during the execution of a single test.
`@RecordApplicationEvents` is a class-level annotation that is used to instruct the
_Spring TestContext Framework_ to record all application events that are published in the
`ApplicationContext` during the execution of a single test.
The recorded events can be accessed via the `ApplicationEvents` API within tests.
@@ -1,89 +0,0 @@
[[spring-testing-annotation-beanoverriding-testbean]]
= `@TestBean`
`@TestBean` is used on a field in a test class to override a specific bean in the test's
`ApplicationContext` with an instance provided by a factory method.
The associated factory method name is derived from the annotated field's name, or the
bean name if specified. The factory method must be `static`, accept no arguments, and
have a return type compatible with the type of the bean to override. To make things more
explicit, or if you'd rather use a different name, the annotation allows for a specific
method name to be provided.
By default, the annotated field's type is used to search for candidate bean definitions
to override. If multiple candidates match, `@Qualifier` can be provided to narrow the
candidate to override. Alternatively, a candidate whose bean definition name matches the
name of the field will match.
To use a by-name override rather than a by-type override, specify the `name` attribute
of the annotation.
[WARNING]
====
Qualifiers, including the name of the field, are used to determine if a separate
`ApplicationContext` needs to be created. If you are using this feature to override the
same bean in several tests, make sure to name the field consistently to avoid creating
unnecessary contexts.
====
The following example shows how to use the default behavior of the `@TestBean` annotation:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
class OverrideBeanTests {
@TestBean // <1>
private CustomService customService;
// test case body...
private static CustomService customService() { // <2>
return new MyFakeCustomService();
}
}
----
<1> Mark a field for overriding the bean with type `CustomService`.
<2> The result of this static method will be used as the instance and injected into the field.
======
In the example above, we are overriding the bean with type `CustomService`. If more than
one bean of that type exists, the bean named `customService` is considered. Otherwise,
the test will fail, and you will need to provide a qualifier of some sort to identify
which of the `CustomService` beans you want to override.
The following example uses a by-name lookup, rather than a by-type lookup:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
class OverrideBeanTests {
@TestBean(name = "service", methodName = "createCustomService") // <1>
private CustomService customService;
// test case body...
private static CustomService createCustomService() { // <2>
return new MyFakeCustomService();
}
}
----
<1> Mark a field for overriding the bean with name `service`, and specify that the
factory method is named `createCustomService`.
<2> The result of this static method will be used as the instance and injected into the field.
======
[NOTE]
====
Spring searches for the factory method to invoke in the test class, in the test class
hierarchy, and in the enclosing class hierarchy for a `@Nested` test class.
Alternatively, a factory method in an external class can be referenced via its
fully-qualified method name following the syntax `<fully-qualified class name>#<method name>`
for example, `methodName = "org.example.TestUtils#createCustomService"`.
====
@@ -1,7 +1,7 @@
[[spring-testing-annotation-testexecutionlisteners]]
= `@TestExecutionListeners`
`@TestExecutionListeners` is used to register listeners for the annotated test class, its
`@TestExecutionListeners` is used to register listeners for a particular test class, its
subclasses, and its nested classes. If you wish to register a listener globally, you
should register it via the automatic discovery mechanism described in
xref:testing/testcontext-framework/tel-config.adoc[`TestExecutionListener` Configuration].
@@ -1,8 +1,8 @@
[[spring-testing-annotation-testpropertysource]]
= `@TestPropertySource`
`@TestPropertySource` is an annotation that can be applied to a test class to configure
the locations of properties files and inlined properties to be added to the set of
`@TestPropertySource` is a class-level annotation that you can use to configure the
locations of properties files and inlined properties to be added to the set of
`PropertySources` in the `Environment` for an `ApplicationContext` loaded for an
integration test.
@@ -1,10 +1,10 @@
[[spring-testing-annotation-webappconfiguration]]
= `@WebAppConfiguration`
`@WebAppConfiguration` is an annotation that can be applied to a test class to declare
that the `ApplicationContext` loaded for an integration test should be a
`WebApplicationContext`. The mere presence of `@WebAppConfiguration` on a test class
ensures that a `WebApplicationContext` is loaded for the test, using the default value of
`@WebAppConfiguration` is a class-level annotation that you can use to declare that the
`ApplicationContext` loaded for an integration test should be a `WebApplicationContext`.
The mere presence of `@WebAppConfiguration` on a test class ensures that a
`WebApplicationContext` is loaded for the test, using the default value of
`"file:src/main/webapp"` for the path to the root of the web application (that is, the
resource base path). The resource base path is used behind the scenes to create a
`MockServletContext`, which serves as the `ServletContext` for the test's
@@ -30,7 +30,7 @@ integration support, and the rest of this chapter then focuses on dedicated topi
* xref:testing/support-jdbc.adoc[JDBC Testing Support]
* xref:testing/testcontext-framework.adoc[Spring TestContext Framework]
* xref:testing/webtestclient.adoc[WebTestClient]
* xref:testing/mockmvc.adoc[MockMvc]
* xref:testing/spring-mvc-test-framework.adoc[MockMvc]
* xref:testing/spring-mvc-test-client.adoc[Testing Client Applications]
* xref:testing/annotations.adoc[Annotations]
@@ -1,16 +0,0 @@
[[mockmvc]]
= MockMvc
:page-section-summary-toc: 1
MockMvc provides support for testing Spring MVC applications. It performs full Spring MVC
request handling but via mock request and response objects instead of a running server.
MockMvc can be used on its own to perform requests and verify responses responses using
Hamcrest, or through `MockMvcTester` that provides a fluent API using AssertJ. Finally,
it can also be used through the xref:testing/webtestclient.adoc[WebTestClient] where
MockMvc is plugged in as the server to handle requests with. The advantage of
`WebTestClient` is the option to work with higher level objects instead of raw data as
well as the ability to switch to full, end-to-end HTTP tests against a live server and
use the same test API.
@@ -1,16 +0,0 @@
[[mockmvc-tester]]
= AssertJ Integration
:page-section-summary-toc: 1
The AssertJ integration builds on top of plain `MockMvc` with several differences:
* There is no need to use static imports as both the requests and assertions can be
crafted using a fluent API.
* Unresolved exceptions are handled consistently so that your tests do not need to
throw (or catch) `Exception`.
* By default, the result to assert is complete whether the processing is asynchronous
or not. In other words, there is no need for special handling for Async requests.
`MockMvcTester` is the entry point for the AssertJ support. It allows to craft the
request and return a result that is AssertJ compatible so that it can be wrapped in
a standard `assertThat()` method.
@@ -1,49 +0,0 @@
[[mockmvc-tester-assertions]]
= Defining Expectations
Assertions work the same way as any AssertJ assertions. The support provides dedicated
assert objects for the various pieces of the `MvcTestResult`, as shown in the following
example:
include-code::./HotelControllerTests[tag=get,indent=0]
If a request fails, the exchange does not throw the exception. Rather, you can assert
that the result of the exchange has failed:
include-code::./HotelControllerTests[tag=failure,indent=0]
The request could also fail unexpectedly, that is the exception thrown by the handler
has not been handled and is thrown as is. You can still use `.hasFailed()` and
`.failure()` but any attempt to access part of the result will throw an exception as
the exchange hasn't completed.
[[mockmvc-tester-assertions-json]]
== JSON Support
The AssertJ support for `MvcTestResult` provides JSON support via `bodyJson()`.
If https://github.com/jayway/JsonPath[JSONPath] is available, you can apply an expression
on the JSON document. The returned value provides convenient methods to return a dedicated
assert object for the various supported JSON data types:
include-code::./FamilyControllerTests[tag=extract-asmap,indent=0]
You can also convert the raw content to any of your data types as long as the message
converter is configured properly:
include-code::./FamilyControllerTests[tag=extract-convert,indent=0]
Converting to a target `Class` provides a generic assert object. For more complex types,
you may want to use `AssertFactory` instead that returns a dedicated assert type, if
possible:
include-code::./FamilyControllerTests[tag=extract-convert-assert-factory,indent=0]
https://jsonassert.skyscreamer.org[JSONAssert] is also supported. The body of the
response can be matched against a `Resource` or a content. If the content ends with
`.json ` we look for a file matching that name on the classpath:
include-code::./FamilyControllerTests[tag=assert-file,indent=0]
If you prefer to use another library, you can provide an implementation of
{spring-framework-api}/test/json/JsonComparator.html[`JsonComparator`].
@@ -1,23 +0,0 @@
[[mockmvc-tester-integration]]
= MockMvc integration
If you want to use the AssertJ support but have invested in the original `MockMvc`
API, `MockMvcTester` offers several ways to integrate with it.
If you have your own `RequestBuilder` implementation, you can trigger the processing
of the request using `perform`. The example below showcases how the query can be
crafted with the original API:
include-code::./HotelControllerTests[tag=perform,indent=0]
Similarly, if you have crafted custom matchers that you use with the `.andExpect` feature
of `MockMvc` you can use them via `.matches`. In the example below, we rewrite the
preceding example to assert the status with the `ResultMatcher` implementation that
`MockMvc` provides:
include-code::./HotelControllerTests[tag=matches,indent=0]
`MockMvc` also defines a `ResultHandler` contract that lets you execute arbitrary actions
on `MvcResult`. If you have implemented this contract you can invoke it using `.apply`.
@@ -1,83 +0,0 @@
[[mockmvc-tester-requests]]
= Performing Requests
This section shows how to use `MockMvcTester` to perform requests and its integration
with AssertJ to verify responses.
`MockMvcTester` provides a fluent API to compose the request that reuses the same
`MockHttpServletRequestBuilder` as the Hamcrest support, except that there is no need
to import a static method. The builder that is returned is AssertJ-aware so that
wrapping it in the regular `assertThat()` factory method triggers the exchange and
provides access to a dedicated Assert object for `MvcTestResult`.
Here is a simple example that performs a `POST` on `/hotels/42` and configures the
request to specify an `Accept` header:
include-code::./HotelControllerTests[tag=post,indent=0]
AssertJ often consists of multiple `assertThat()` statements to validate the different
parts of the exchange. Rather than having a single statement as in the case above, you
can use `.exchange()` to return a `MvcTestResult` that can be used in multiple
`assertThat` statements:
include-code::./HotelControllerTests[tag=post-exchange,indent=0]
You can specify query parameters in URI template style, as the following example shows:
include-code::./HotelControllerTests[tag=query-parameters,indent=0]
You can also add Servlet request parameters that represent either query or form
parameters, as the following example shows:
include-code::./HotelControllerTests[tag=parameters,indent=0]
If application code relies on Servlet request parameters and does not check the query
string explicitly (as is most often the case), it does not matter which option you use.
Keep in mind, however, that query parameters provided with the URI template are decoded
while request parameters provided through the `param(...)` method are expected to already
be decoded.
[[mockmvc-tester-requests-async]]
== Async
If the processing of the request is done asynchronously, `exchange()` waits for
the completion of the request so that the result to assert is effectively immutable.
The default timeout is 10 seconds but it can be controlled on a request-by-request
basis as shown in the following example:
include-code::./AsyncControllerTests[tag=duration,indent=0]
If you prefer to get the raw result and manage the lifecycle of the asynchronous
request yourself, use `asyncExchange` rather than `exchange`.
[[mockmvc-tester-requests-multipart]]
== Multipart
You can perform file upload requests that internally use
`MockMultipartHttpServletRequest` so that there is no actual parsing of a multipart
request. Rather, you have to set it up to be similar to the following example:
include-code::./MultipartControllerTests[tag=snippet,indent=0]
[[mockmvc-tester-requests-paths]]
== Using Servlet and Context Paths
In most cases, it is preferable to leave the context path and the Servlet path out of the
request URI. If you must test with the full request URI, be sure to set the `contextPath`
and `servletPath` accordingly so that request mappings work, as the following example
shows:
include-code::./HotelControllerTests[tag=context-servlet-paths,indent=0]
In the preceding example, it would be cumbersome to set the `contextPath` and
`servletPath` with every performed request. Instead, you can set up default request
properties, as the following example shows:
include-code::./HotelControllerTests[tag=default-customizations,indent=0]
The preceding properties affect every request performed through the `mockMvc` instance.
If the same property is also specified on a given request, it overrides the default
value. That is why the HTTP method and URI in the default request do not matter, since
they must be specified on every request.
@@ -1,30 +0,0 @@
[[mockmvc-tester-setup]]
= Configuring MockMvcTester
`MockMvcTester` can be setup in one of two ways. One is to point directly to the
controllers you want to test and programmatically configure Spring MVC infrastructure.
The second is to point to Spring configuration with Spring MVC and controller
infrastructure in it.
TIP: For a comparison of those two modes, check xref:testing/mockmvc/setup-options.adoc[Setup Options].
To set up `MockMvcTester` for testing a specific controller, use the following:
include-code::./AccountControllerStandaloneTests[tag=snippet,indent=0]
To set up `MockMvcTester` through Spring configuration, use the following:
include-code::./AccountControllerIntegrationTests[tag=snippet,indent=0]
`MockMvcTester` can convert the JSON response body, or the result of a JSONPath expression,
to one of your domain object as long as the relevant `HttpMessageConverter` is registered.
If you use Jackson to serialize content to JSON, the following example registers the
converter:
include-code::./converter/AccountControllerIntegrationTests[tag=snippet,indent=0]
NOTE: The above assumes the converter has been registered as a Bean.
Finally, if you have a `MockMvc` instance handy, you can create a `MockMvcTester` by
providing the `MockMvc` instance to use using the `create` factory method.
@@ -1,7 +0,0 @@
[[mockmvc-server]]
= Hamcrest Integration
:page-section-summary-toc: 1
Plain `MockMvc` provides an API to build the request using a builder-style approach
that can be initiated with static imports. Hamcrest is used to define expectations and
it provides many out-of-the-box options for common needs.
@@ -1,100 +0,0 @@
[[mockmvc-setup]]
= Configuring MockMvc
MockMvc can be setup in one of two ways. One is to point directly to the controllers you
want to test and programmatically configure Spring MVC infrastructure. The second is to
point to Spring configuration with Spring MVC and controller infrastructure in it.
TIP: For a comparison of those two modes, check xref:testing/mockmvc/setup-options.adoc[Setup Options].
To set up MockMvc for testing a specific controller, use the following:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
class MyWebTests {
MockMvc mockMvc;
@BeforeEach
void setup() {
this.mockMvc = MockMvcBuilders.standaloneSetup(new AccountController()).build();
}
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class MyWebTests {
lateinit var mockMvc : MockMvc
@BeforeEach
fun setup() {
mockMvc = MockMvcBuilders.standaloneSetup(AccountController()).build()
}
// ...
}
----
======
Or you can also use this setup when testing through the
xref:testing/webtestclient.adoc#webtestclient-controller-config[WebTestClient] which delegates to the same builder
as shown above.
To set up MockMvc through Spring configuration, use the following:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@SpringJUnitWebConfig(locations = "my-servlet-context.xml")
class MyWebTests {
MockMvc mockMvc;
@BeforeEach
void setup(WebApplicationContext wac) {
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@SpringJUnitWebConfig(locations = ["my-servlet-context.xml"])
class MyWebTests {
lateinit var mockMvc: MockMvc
@BeforeEach
fun setup(wac: WebApplicationContext) {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build()
}
// ...
}
----
======
Or you can also use this setup when testing through the
xref:testing/webtestclient.adoc#webtestclient-context-config[WebTestClient] which delegates to the same builder
as shown above.
@@ -1,29 +0,0 @@
[[mockmvc-server-setup-options]]
= Setup Options
MockMvc can be setup in one of two ways. One is to point directly to the controllers you
want to test and programmatically configure Spring MVC infrastructure. The second is to
point to Spring configuration with Spring MVC and controller infrastructure in it.
Which setup option should you use?
The use of an `ApplicationContext` loads your actual Spring MVC configuration, resulting
The `WebApplicationContext`-based test loads your actual Spring MVC configuration,
resulting in a more complete integration test. Since the TestContext framework caches
the loaded Spring configuration, it helps keep tests running fast, even as you introduce
more tests in your test suite using the same configuration. Furthermore, you can
override services used by your controller using `@MockitoBean` to remain focused on
testing the web layer.
The standalone test, on the other hand, is a little closer to a unit test. It tests one
controller at a time. You can manually inject the controller with mock dependencies, and
it does not involve loading Spring configuration. Such tests are more focused on style
and make it easier to see which controller is being tested, whether any specific Spring
MVC configuration is required to work, and so on. The standalone setup is also a very
convenient way to write ad-hoc tests to verify specific behavior or to debug an issue.
As with most "`integration versus unit testing`" debates, there is no right or wrong
answer. However, using standalone tests does imply the need for additional integration
tests to verify your Spring MVC configuration. Alternatively, you can write all your
tests with a `WebApplicationContext`, so that they always test against your actual Spring
MVC configuration.
@@ -0,0 +1,15 @@
[[spring-mvc-test-framework]]
= MockMvc
:page-section-summary-toc: 1
The Spring MVC Test framework, also known as MockMvc, provides support for testing Spring
MVC applications. It performs full Spring MVC request handling but via mock request and
response objects instead of a running server.
MockMvc can be used on its own to perform requests and verify responses. It can also be
used through the xref:testing/webtestclient.adoc[WebTestClient] where MockMvc is plugged in as the server to handle
requests with. The advantage of `WebTestClient` is the option to work with higher level
objects instead of raw data as well as the ability to switch to full, end-to-end HTTP
tests against a live server and use the same test API.
@@ -1,4 +1,4 @@
[[mockmvc-async-requests]]
[[spring-mvc-test-async-requests]]
= Async Requests
This section shows how to use MockMvc on its own to test asynchronous request handling.
@@ -1,4 +1,4 @@
[[mockmvc-server-defining-expectations]]
[[spring-mvc-test-server-defining-expectations]]
= Defining Expectations
You can define expectations by appending one or more `andExpect(..)` calls after
@@ -1,4 +1,4 @@
[[mockmvc-server-filters]]
[[spring-mvc-test-server-filters]]
= Filter Registrations
:page-section-summary-toc: 1
@@ -1,8 +1,8 @@
[[mockmvc-server-htmlunit]]
[[spring-mvc-test-server-htmlunit]]
= HtmlUnit Integration
:page-section-summary-toc: 1
Spring provides integration between xref:testing/mockmvc/overview.adoc[MockMvc] and
Spring provides integration between xref:testing/spring-mvc-test-framework/server.adoc[MockMvc] and
https://htmlunit.sourceforge.io/[HtmlUnit]. This simplifies performing end-to-end testing
when using HTML-based views. This integration lets you:
@@ -1,18 +1,18 @@
[[mockmvc-server-htmlunit-geb]]
[[spring-mvc-test-server-htmlunit-geb]]
= MockMvc and Geb
In the previous section, we saw how to use MockMvc with WebDriver. In this section, we
use https://www.gebish.org/[Geb] to make our tests even Groovy-er.
[[mockmvc-server-htmlunit-geb-why]]
[[spring-mvc-test-server-htmlunit-geb-why]]
== Why Geb and MockMvc?
Geb is backed by WebDriver, so it offers many of the
xref:testing/mockmvc/htmlunit/webdriver.adoc#spring-mvc-test-server-htmlunit-webdriver-why[same benefits] that we get from
xref:testing/spring-mvc-test-framework/server-htmlunit/webdriver.adoc#spring-mvc-test-server-htmlunit-webdriver-why[same benefits] that we get from
WebDriver. However, Geb makes things even easier by taking care of some of the
boilerplate code for us.
[[mockmvc-server-htmlunit-geb-setup]]
[[spring-mvc-test-server-htmlunit-geb-setup]]
== MockMvc and Geb Setup
We can easily initialize a Geb `Browser` with a Selenium WebDriver that uses MockMvc, as
@@ -28,14 +28,14 @@ def setup() {
----
NOTE: This is a simple example of using `MockMvcHtmlUnitDriverBuilder`. For more advanced
usage, see xref:testing/mockmvc/htmlunit/webdriver.adoc#spring-mvc-test-server-htmlunit-webdriver-advanced-builder[Advanced `MockMvcHtmlUnitDriverBuilder`].
usage, see xref:testing/spring-mvc-test-framework/server-htmlunit/webdriver.adoc#spring-mvc-test-server-htmlunit-webdriver-advanced-builder[Advanced `MockMvcHtmlUnitDriverBuilder`].
This ensures that any URL referencing `localhost` as the server is directed to our
`MockMvc` instance without the need for a real HTTP connection. Any other URL is
requested by using a network connection as normal. This lets us easily test the use of
CDNs.
[[mockmvc-server-htmlunit-geb-usage]]
[[spring-mvc-test-server-htmlunit-geb-usage]]
== MockMvc and Geb Usage
Now we can use Geb as we normally would but without the need to deploy our application to
@@ -62,7 +62,7 @@ forwarded to the current page object. This removes a lot of the boilerplate code
needed when using WebDriver directly.
As with direct WebDriver usage, this improves on the design of our
xref:testing/mockmvc/htmlunit/mah.adoc#spring-mvc-test-server-htmlunit-mah-usage[HtmlUnit test] by using the Page Object
xref:testing/spring-mvc-test-framework/server-htmlunit/mah.adoc#spring-mvc-test-server-htmlunit-mah-usage[HtmlUnit test] by using the Page Object
Pattern. As mentioned previously, we can use the Page Object Pattern with HtmlUnit and
WebDriver, but it is even easier with Geb. Consider our new Groovy-based
`CreateMessagePage` implementation:
@@ -1,14 +1,15 @@
[[mockmvc-server-htmlunit-mah]]
[[spring-mvc-test-server-htmlunit-mah]]
= MockMvc and HtmlUnit
This section describes how to integrate MockMvc and HtmlUnit. Use this option if you want
to use the raw HtmlUnit libraries.
[[mockmvc-server-htmlunit-mah-setup]]
[[spring-mvc-test-server-htmlunit-mah-setup]]
== MockMvc and HtmlUnit Setup
First, make sure that you have included a test dependency on
`org.htmlunit:htmlunit`.
`net.sourceforge.htmlunit:htmlunit`. In order to use HtmlUnit with Apache HttpComponents
4.5+, you need to use HtmlUnit 2.18 or higher.
We can easily create an HtmlUnit `WebClient` that integrates with MockMvc by using the
`MockMvcWebClientBuilder`, as follows:
@@ -45,14 +46,14 @@ Kotlin::
======
NOTE: This is a simple example of using `MockMvcWebClientBuilder`. For advanced usage,
see xref:testing/mockmvc/htmlunit/mah.adoc#spring-mvc-test-server-htmlunit-mah-advanced-builder[Advanced `MockMvcWebClientBuilder`].
see xref:testing/spring-mvc-test-framework/server-htmlunit/mah.adoc#spring-mvc-test-server-htmlunit-mah-advanced-builder[Advanced `MockMvcWebClientBuilder`].
This ensures that any URL that references `localhost` as the server is directed to our
`MockMvc` instance without the need for a real HTTP connection. Any other URL is
requested by using a network connection, as normal. This lets us easily test the use of
CDNs.
[[mockmvc-server-htmlunit-mah-usage]]
[[spring-mvc-test-server-htmlunit-mah-usage]]
== MockMvc and HtmlUnit Usage
Now we can use HtmlUnit as we normally would but without the need to deploy our
@@ -77,7 +78,7 @@ Kotlin::
======
NOTE: The default context path is `""`. Alternatively, we can specify the context path,
as described in xref:testing/mockmvc/htmlunit/mah.adoc#spring-mvc-test-server-htmlunit-mah-advanced-builder[Advanced `MockMvcWebClientBuilder`].
as described in xref:testing/spring-mvc-test-framework/server-htmlunit/mah.adoc#spring-mvc-test-server-htmlunit-mah-advanced-builder[Advanced `MockMvcWebClientBuilder`].
Once we have a reference to the `HtmlPage`, we can then fill out the form and submit it
to create a message, as the following example shows:
@@ -144,7 +145,7 @@ Kotlin::
======
The preceding code improves on our
xref:testing/mockmvc/htmlunit/why.adoc#spring-mvc-test-server-htmlunit-mock-mvc-test[MockMvc test] in a number of ways.
xref:testing/spring-mvc-test-framework/server-htmlunit/why.adoc#spring-mvc-test-server-htmlunit-mock-mvc-test[MockMvc test] in a number of ways.
First, we no longer have to explicitly verify our form and then create a request that
looks like the form. Instead, we request the form, fill it out, and submit it, thereby
significantly reducing the overhead.
@@ -156,7 +157,7 @@ the behavior of JavaScript within our pages.
See the https://htmlunit.sourceforge.io/gettingStarted.html[HtmlUnit documentation] for
additional information about using HtmlUnit.
[[mockmvc-server-htmlunit-mah-advanced-builder]]
[[spring-mvc-test-server-htmlunit-mah-advanced-builder]]
== Advanced `MockMvcWebClientBuilder`
In the examples so far, we have used `MockMvcWebClientBuilder` in the simplest way
@@ -275,5 +276,5 @@ This is more verbose, but, by building the `WebClient` with a `MockMvc` instance
the full power of MockMvc at our fingertips.
TIP: For additional information on creating a `MockMvc` instance, see
xref:testing/mockmvc/hamcrest/setup.adoc[Configuring MockMvc].
xref:testing/spring-mvc-test-framework/server-setup-options.adoc[Setup Choices].
@@ -1,11 +1,11 @@
[[mockmvc-server-htmlunit-webdriver]]
[[spring-mvc-test-server-htmlunit-webdriver]]
= MockMvc and WebDriver
In the previous sections, we have seen how to use MockMvc in conjunction with the raw
HtmlUnit APIs. In this section, we use additional abstractions within the Selenium
https://docs.seleniumhq.org/projects/webdriver/[WebDriver] to make things even easier.
[[mockmvc-server-htmlunit-webdriver-why]]
[[spring-mvc-test-server-htmlunit-webdriver-why]]
== Why WebDriver and MockMvc?
We can already use HtmlUnit and MockMvc, so why would we want to use WebDriver? The
@@ -162,11 +162,11 @@ https://github.com/SeleniumHQ/selenium/wiki/PageObjects[Page Object Pattern]. Wh
can certainly do this with HtmlUnit, WebDriver provides some tools that we explore in the
following sections to make this pattern much easier to implement.
[[mockmvc-server-htmlunit-webdriver-setup]]
[[spring-mvc-test-server-htmlunit-webdriver-setup]]
== MockMvc and WebDriver Setup
To use Selenium WebDriver with `MockMvc`, make sure that your project includes a test
dependency on `org.seleniumhq.selenium:selenium-htmlunit3-driver`.
To use Selenium WebDriver with the Spring MVC Test framework, make sure that your project
includes a test dependency on `org.seleniumhq.selenium:selenium-htmlunit-driver`.
We can easily create a Selenium WebDriver that integrates with MockMvc by using the
`MockMvcHtmlUnitDriverBuilder` as the following example shows:
@@ -203,14 +203,14 @@ Kotlin::
======
NOTE: This is a simple example of using `MockMvcHtmlUnitDriverBuilder`. For more advanced
usage, see xref:testing/mockmvc/htmlunit/webdriver.adoc#spring-mvc-test-server-htmlunit-webdriver-advanced-builder[Advanced `MockMvcHtmlUnitDriverBuilder`].
usage, see xref:testing/spring-mvc-test-framework/server-htmlunit/webdriver.adoc#spring-mvc-test-server-htmlunit-webdriver-advanced-builder[Advanced `MockMvcHtmlUnitDriverBuilder`].
The preceding example ensures that any URL that references `localhost` as the server is
directed to our `MockMvc` instance without the need for a real HTTP connection. Any other
URL is requested by using a network connection, as normal. This lets us easily test the
use of CDNs.
[[mockmvc-server-htmlunit-webdriver-usage]]
[[spring-mvc-test-server-htmlunit-webdriver-usage]]
== MockMvc and WebDriver Usage
Now we can use WebDriver as we normally would but without the need to deploy our
@@ -259,9 +259,9 @@ Kotlin::
======
--
This improves on the design of our xref:testing/mockmvc/htmlunit/mah.adoc#spring-mvc-test-server-htmlunit-mah-usage[HtmlUnit test]
This improves on the design of our xref:testing/spring-mvc-test-framework/server-htmlunit/mah.adoc#spring-mvc-test-server-htmlunit-mah-usage[HtmlUnit test]
by leveraging the Page Object Pattern. As we mentioned in
xref:testing/mockmvc/htmlunit/webdriver.adoc#spring-mvc-test-server-htmlunit-webdriver-why[Why WebDriver and MockMvc?], we can use the Page Object Pattern
xref:testing/spring-mvc-test-framework/server-htmlunit/webdriver.adoc#spring-mvc-test-server-htmlunit-webdriver-why[Why WebDriver and MockMvc?], we can use the Page Object Pattern
with HtmlUnit, but it is much easier with WebDriver. Consider the following
`CreateMessagePage` implementation:
@@ -451,7 +451,7 @@ Kotlin::
For additional information on using WebDriver, see the Selenium
https://github.com/SeleniumHQ/selenium/wiki/Getting-Started[WebDriver documentation].
[[mockmvc-server-htmlunit-webdriver-advanced-builder]]
[[spring-mvc-test-server-htmlunit-webdriver-advanced-builder]]
== Advanced `MockMvcHtmlUnitDriverBuilder`
In the examples so far, we have used `MockMvcHtmlUnitDriverBuilder` in the simplest way
@@ -570,5 +570,5 @@ This is more verbose, but, by building the `WebDriver` with a `MockMvc` instance
the full power of MockMvc at our fingertips.
TIP: For additional information on creating a `MockMvc` instance, see
xref:testing/mockmvc/hamcrest/setup.adoc[Configuring MockMvc].
xref:testing/spring-mvc-test-framework/server-setup-options.adoc[Setup Choices].
@@ -1,4 +1,4 @@
[[mockmvc-server-htmlunit-why]]
[[spring-mvc-test-server-htmlunit-why]]
= Why HtmlUnit Integration?
The most obvious question that comes to mind is "`Why do I need this?`" The answer is
@@ -87,14 +87,14 @@ Kotlin::
This test has some obvious drawbacks. If we update our controller to use the parameter
`message` instead of `text`, our form test continues to pass, even though the HTML form
is out of sync with the controller. To resolve this we can combine our two tests, as
is out of synch with the controller. To resolve this we can combine our two tests, as
follows:
[tabs]
======
Java::
+
[[mockmvc-server-htmlunit-mock-mvc-test]]
[[spring-mvc-test-server-htmlunit-mock-mvc-test]]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
String summaryParamName = "summary";
@@ -151,7 +151,7 @@ the input to a user for creating a message. In addition, our form view can poten
use additional resources that impact the behavior of the page, such as JavaScript
validation.
[[mockmvc-server-htmlunit-why-integration]]
[[spring-mvc-test-server-htmlunit-why-integration]]
== Integration Testing to the Rescue?
To resolve the issues mentioned earlier, we could perform end-to-end integration testing,
@@ -181,23 +181,23 @@ and without side effects. We can then implement a small number of true end-to-en
integration tests that validate simple workflows to ensure that everything works together
properly.
[[mockmvc-server-htmlunit-why-mockmvc]]
[[spring-mvc-test-server-htmlunit-why-mockmvc]]
== Enter HtmlUnit Integration
So how can we achieve a balance between testing the interactions of our pages and still
retain good performance within our test suite? The answer is: "`By integrating MockMvc
with HtmlUnit.`"
[[mockmvc-server-htmlunit-options]]
[[spring-mvc-test-server-htmlunit-options]]
== HtmlUnit Integration Options
You have a number of options when you want to integrate MockMvc with HtmlUnit:
* xref:testing/mockmvc/htmlunit/mah.adoc[MockMvc and HtmlUnit]: Use this option if you
* xref:testing/spring-mvc-test-framework/server-htmlunit/mah.adoc[MockMvc and HtmlUnit]: Use this option if you
want to use the raw HtmlUnit libraries.
* xref:testing/mockmvc/htmlunit/webdriver.adoc[MockMvc and WebDriver]: Use this option to
* xref:testing/spring-mvc-test-framework/server-htmlunit/webdriver.adoc[MockMvc and WebDriver]: Use this option to
ease development and reuse code between integration and end-to-end testing.
* xref:testing/mockmvc/htmlunit/geb.adoc[MockMvc and Geb]: Use this option if you want to
* xref:testing/spring-mvc-test-framework/server-htmlunit/geb.adoc[MockMvc and Geb]: Use this option if you want to
use Groovy for testing, ease development, and reuse code between integration and
end-to-end testing.
@@ -1,4 +1,4 @@
[[mockmvc-server-performing-requests]]
[[spring-mvc-test-server-performing-requests]]
= Performing Requests
This section shows how to use MockMvc on its own to perform requests and verify responses.

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