Compare commits

..
83 Commits
Author SHA1 Message Date
Spring Builds 5356a1b1ac Release v6.1.10 2024-06-19 15:29:59 +00:00
Brian Clozel f7307c9e07 Avoid recording RestClient observations twice
Prior to this commit, the fix for gh-32575 introduced cases where the
client observation would be stopped twice.

This commit ensures that `RestClient` observations are stopped only once
when the response is closed, or before throwing an unhanlded exception.

Fixes gh-33068
2024-06-19 12:46:21 +02:00
Brian Clozel 098c4b1dd7 Use Sonatype S01 token in release pipeline 2024-06-18 19:25:20 +02:00
Juergen Hoeller 65dbfd09b4 Defensive PersistenceExceptionTranslator bean retrieval on shutdown
Closes gh-33067
2024-06-18 18:31:15 +02:00
Sam Brannen 203fa75196 Support all "connection reset" phrases in DisconnectedClientHelper
Prior to this commit, the isClientDisconnectedException() method in
DisconnectedClientHelper checked whether the message of the ultimate
exception in an exception chain contained one of the phrases "broken
pipe" or "connection reset by peer". However, that failed to match if
the exception message contained "Connection reset", which is the case
for the SocketException thrown by throwConnectionReset() in
sun.nio.ch.SocketChannelImpl.

This commit therefore replaces the "connection reset by peer" phrase
with "connection reset" in order to support all exception messages
containing "connection reset".

Closes gh-33064
2024-06-18 16:43:06 +02:00
Juergen Hoeller 9a56a8877f Polishing 2024-06-17 18:42:37 +02:00
Juergen Hoeller e79a9a5bff Correct and consistent event class names in constructor javadoc
Closes gh-33032
2024-06-17 18:42:30 +02:00
Juergen Hoeller 2c3c3831c1 Consistently ignore bridge method on generated subclass for visibility purposes
Closes gh-33030
2024-06-17 18:42:20 +02:00
Arjen Poutsma c38e9896c7 Remove use of ServletException in ModelFactory
This commit changes the use of HttpSessionRequiredException in
ModelFactory::initModel to an IllegalStateException, because the former
extends ServletException and cannot be used in WebFlux.

Closes gh-33043
2024-06-17 14:19:19 +02:00
Stéphane Nicoll 3e0849a566 Fix typo
Closes gh-33050
2024-06-17 13:39:18 +02:00
github-actions[bot] f3a605b92c Update Antora Spring UI to v0.4.16 2024-06-17 10:06:47 +00:00
Stéphane Nicoll 6fdff201e9 Fix property name in Container Extension Points section
Closes gh-33037
2024-06-16 16:47:36 +02:00
Stéphane Nicoll c42778a05b Merge pull request #33036 from tafjwr
* pr/33036:
  Polish "Fix typo in comment"
  Fix typo in comment

Closes gh-33036
2024-06-16 09:59:15 +02:00
Stéphane Nicoll f140df881d Polish "Fix typo in comment"
See gh-33036
2024-06-16 09:57:24 +02:00
tafjwr 12cf654c98 Fix typo in comment
See gh-33036
2024-06-16 09:56:58 +02:00
Juergen Hoeller 089e4e69f1 Do not attempt to load pre-enhanced class for reloadable classes
Closes gh-33024
2024-06-14 22:07:46 +02:00
Stéphane Nicoll 77755ff2fa Remove repo-specific code of conduct
This will allow us to inherit the org's CoC instead.

Closes gh-33031
2024-06-14 18:58:49 +02:00
Stéphane Nicoll d0aa7ad524 Fix invalid character in Javadoc of BeanFactory 2024-06-13 14:06:19 +02:00
Spring Builds 072fc80cdb Next development version (v6.1.10-SNAPSHOT) 2024-06-13 10:27:57 +00:00
Arjen Poutsma 6f32ff489a Use null stream in ReactorNettyClientResponse if no body is available
Closes gh-32805
2024-06-13 11:10:50 +02:00
Juergen Hoeller 24c8dfea1f Remove duplicated javadoc paragraph 2024-06-12 14:23:26 +02:00
Juergen Hoeller cdfe5816c8 Upgrade to Netty 4.1.111 and Awaitility 4.2.1 2024-06-12 14:23:14 +02:00
Juergen Hoeller 0ff200b2f1 Trigger cancellation on context close for non-managed objects only
Specifically for prototype/scoped beans and FactoryBean-exposed objects.

Closes gh-33009
2024-06-12 13:31:00 +02:00
Stéphane Nicoll 261dac87cc Upgrade to Reactor 2023.0.7
Closes gh-33007
2024-06-11 14:42:08 +02:00
Juergen Hoeller fce2f49e46 Polishing (aligned with main) 2024-06-11 09:07:09 +02:00
Juergen Hoeller eca2b9657e Documentation notes for @Bean overriding and bean name matching
Closes gh-32825
2024-06-11 09:06:33 +02:00
Juergen Hoeller e48f37d956 Upgrade to Micrometer 1.12.7
Closes gh-33001
2024-06-11 09:05:48 +02:00
Juergen Hoeller ddc397dd05 Upgrade to spring-javaformat-checkstyle 0.0.42 2024-06-11 09:04:30 +02:00
Stéphane Nicoll 6b7f0bd4b6 Fix typo 2024-06-11 06:47:42 +02:00
Sébastien Deleuze c97a895f09 Add support for double backslashes to StringUtils#cleanPath
Closes gh-32962
2024-06-10 22:25:31 +02:00
rstoyanchev 3b13f2ed38 Update code-of-conduct email 2024-06-10 10:48:06 +01:00
Stéphane Nicoll a0eebca0cf Merge pull request #32993 from hlmg
* pr/32993:
  Fix typo

Closes gh-32993
2024-06-10 10:02:36 +02:00
hlmg c6c64e6fe7 Fix typo
See gh-32993
2024-06-10 10:00:37 +02:00
Brian Clozel 0ca393c0dc Restrict memory allocation in ContentCachingRequestWrapper
Prior to this commit, the `ContentCachingRequestWrapper` could allocate
a `FastByteArrayOutputStream` block that was larger than the content
cache limit given as a consturctor argument. This was due to an
optimization applied in gh-31834 for allocating the right content cache
size when the request size is known.

Fixes gh-32987
2024-06-10 09:46:01 +02:00
Brian Clozel 6681394886 Stop observations for async requests in Servlet filter
Prior to this commit, the `ServerHttpObservationFilter` would support
async dispatches and would do the following:

1. start the observation
2. call the filter chain
3. if async has started, do nothing
4. if not in async mode, stop the observation

This behavior would effectively rely on Async implementations to
complete and dispatch the request back to the container for an async
dispatch. This is what Spring web frameworks do and guarantee.

Some implementations complete the async request but do not dispatch
back; as a result, observations could leak as they are never stopped.

This commit changes the support of async requests. The filter now
opts-out of async dispatches - the filter will not be called for those
anymore. Instead, if the application started async mode during the
initial container dispatch, the filter will register an AsyncListener to
be notified of the outcome of the async handling.

Fixes gh-32730
2024-06-07 19:01:57 +02:00
Sébastien Deleuze 172987c874 Ignore checkpointOnRefresh after restore
Closes gh-32978
2024-06-07 18:41:37 +02:00
Thomas Deblock 47a5ebfde6 Support canEncode() for JAXBElement in Jaxb2XmlEncoder
Commit d7970e4ab8 introduced support for JAXBElement in
Jaxb2XmlEncoder's encodeValue() method; however, canEncode() still
returned false for a JAXBElement element type.

This commit revises canEncode() so that it returns true for an element
type that is assignable from JAXBElement.

See gh-30552
See gh-32972
Closes gh-32977
2024-06-07 14:13:59 +02:00
Sébastien Deleuze 43a113f067 Polishing
See gh-32983
2024-06-07 13:45:09 +02:00
Sébastien Deleuze dc250e1cc1 Remove outdated copyright from index.adoc
Closes gh-32983
2024-06-07 13:35:21 +02:00
Sébastien Deleuze fe74fcfded Exclude node_modules from NoHttp checks
Closes gh-32980
2024-06-07 11:43:45 +02:00
Juergen Hoeller 2451bd62b0 Polishing 2024-06-06 20:43:31 +02:00
Juergen Hoeller 624d6dd167 Expose actual result value for @CacheEvict condition
Closes gh-32960
2024-06-06 20:43:04 +02:00
Juergen Hoeller 0ea96b4806 Skip ajc-compiled aspects for ajc-compiled target classes
Includes defensive ignoring of incompatible aspect types.

Closes gh-32970
2024-06-06 20:42:07 +02:00
Sébastien Deleuze c28a0d5627 Add missing hints for Hibernate @TenantId
Closes gh-32967
2024-06-06 10:04:35 +02:00
Juergen Hoeller 61d045ce52 Polishing 2024-06-06 08:54:37 +02:00
Juergen Hoeller c0bef2c693 Lazily start resources on demand (if necessary outside of lifecycle)
See gh-32945
2024-06-06 08:54:32 +02:00
Stéphane Nicoll c3a0eaa95e Merge pull request #32966 from ypyf
* pr/32966:
  Use HttpStatusCode consistently in reference guide

Closes gh-32966
2024-06-06 08:08:45 +02:00
ypyf e12d1259d1 Use HttpStatusCode consistently in reference guide
See gh-32966
2024-06-06 08:06:33 +02:00
Brian Clozel 404c4d9d92 Support @Valid on container elements for handler arguments
Prior to this commit, #31870 added support for constraint annotations on
container elements for handler method argument validation. Supporting
this use case:

```
public void addNames(List<@NotEmpty String> names)
```

This commit does the same for `@Valid` annotation:

```
public void addPeople(List<@Valid Person> people)
```

Fixes gh-32964
2024-06-05 20:02:46 +02:00
Juergen Hoeller 7785f94c4c Revise and align Reactor client lifecycle management
Closes gh-32945
2024-06-05 16:32:40 +02:00
Juergen Hoeller 4f6f2c0d41 Revert to separate get/put steps against method cache for concurrency
Closes gh-32958
2024-06-05 16:32:27 +02:00
Stéphane Nicoll e6da2a86fc Merge pull request #32957 from soglad
* pr/32957:
  Fix entity name in MappingSqlQuery example of reference guide

Closes gh-32957
2024-06-05 10:45:01 +02:00
Tony Wen 301087e510 Fix entity name in MappingSqlQuery example of reference guide
See gh-32957
2024-06-05 10:43:14 +02:00
Juergen Hoeller 6c054f88ea Defensively handle UncheckedIOException cause (for NullAway compliance) 2024-06-05 00:01:52 +02:00
Juergen Hoeller f58c7d80cc Upgrade to SLF4J 2.0.13, Jetty 12.0.10, Netty 4.1.110, JRuby 9.4.7 2024-06-04 23:44:54 +02:00
Juergen Hoeller e5be10d53d Consistently throw IOException from ReactorNettyClientResponse
Aligned with ReactorNettyClientRequest.

See gh-32952
2024-06-04 23:43:10 +02:00
Juergen Hoeller 524da905db Consistently throw IOException from ReactorNettyClientRequest
This commit renames ReactorNettyClientRequestFactoryTests.

Closes gh-32952
2024-06-04 22:59:29 +02:00
Juergen Hoeller 4323c60513 Common context lifecycle management for ReactorResourceFactory
This commit moves ReactorResourceFactoryTests to same package.

Closes gh-32945
2024-06-04 22:59:18 +02:00
Sébastien Deleuze f6b608eecb Consistently support Hibernate annotation hint inference on methods
See gh-32842
2024-06-04 16:46:25 +02:00
Sébastien Deleuze 4da1511ed3 Infer hints for Hibernate generators
This commit updates
PersistenceManagedTypesBeanRegistrationAotProcessor
in order to infer hints for Hibernate annotations meta
annotated with `@ValueGenerationType` (like `@CreationTimestamp`)
and `@IdGeneratorType`.

`@GenericGenerator` is not supported as it is deprecated as of
Hibernate 6.5.

Closes gh-32842
2024-06-04 16:46:25 +02:00
Stéphane Nicoll 7102c33661 Add section about using complex data structures with AOT
Closes gh-32273
2024-06-04 15:51:40 +02:00
Sébastien Deleuze 43409b00d0 Refine KotlinDetector.isKotlinType documentation
This commit documents changes in lambda detection
as of Kotlin 2.0.

Closes gh-32905
2024-06-03 18:58:13 +02:00
Sébastien Deleuze d55abc6cf9 Fix RegisterReflectionForBinding Javadoc
Closes gh-32947
2024-06-03 18:26:24 +02:00
Sébastien Deleuze 31806f3a6b Fix MethodValidationPostProcessor refdoc
Closes gh-32929
2024-06-03 18:20:52 +02:00
Juergen Hoeller 8a84241c1e Polishing 2024-06-03 12:46:31 +02:00
Juergen Hoeller 624be6d4e6 Report bean creation failure in sortAdvisors as AopConfigException
Closes gh-32230
2024-06-03 12:46:14 +02:00
Juergen Hoeller b08883b65c Avoid NoSuchMethodException for annotation attribute checks
Closes gh-32921
2024-06-03 12:45:11 +02:00
Cong-Xin Cynthia Qiu 542ba3517f Fix typo in Jakarta validation documentation
Closes gh-32928
2024-05-31 12:22:02 +02:00
Juergen Hoeller 8c6a7799be Upgrade to Checkstyle 10.17 2024-05-28 18:46:22 +02:00
Juergen Hoeller 557dbba585 Remove superfluous addToClassHierarchy call for Enum types
Closes gh-32906
2024-05-28 18:45:05 +02:00
Sam Brannen e9de426eb5 Support compilation of map indexing with primitive in SpEL
Prior to this commit, the Spring Expression Language (SpEL) failed to
compile an expression that indexed into a Map using a primitive literal
(boolean, int, long, float, or double).

This commit adds support for compilation of such expressions by
ensuring that primitive literals are boxed into their corresponding
wrapper types in the compiled bytecode.

Closes gh-32903
2024-05-28 10:19:15 +02:00
Sam Brannen cda577d1aa Support compilation of array and list indexing with Integer in SpEL
Prior to this commit, the Spring Expression Language (SpEL) failed to
compile an expression that indexed into any array or list using an
Integer.

This commit adds support for compilation of such expressions by
ensuring that an Integer is unboxed into an int in the compiled
bytecode.

See gh-32694
Closes gh-32908
2024-05-27 17:06:48 +02:00
Sam Brannen 8feb842df5 Upgrade to AssertJ 3.26.0
See https://github.com/assertj/assertj/issues/3322
2024-05-27 16:43:22 +02:00
Sam Brannen ea2931f24a Use Develocity Gradle plugin API to avoid deprecation warning
Prior to this commit, the Gradle build issued the following warning.

- The deprecated "gradleEnterprise.buildScan.value" API has been
  replaced by "develocity.buildScan.value"
2024-05-24 15:41:58 +02:00
Juergen Hoeller 26d1c38d84 Polishing 2024-05-24 13:05:49 +02:00
Juergen Hoeller 345daaabbc Detect original generic method for CGLIB bridge method
Closes gh-32888
2024-05-24 11:49:10 +02:00
Juergen Hoeller 6c08d93992 Polishing 2024-05-23 17:07:55 +02:00
Juergen Hoeller 6d7cd9c7dc Defensive handling of incompatible advice methods
This covers AspectJ transaction and caching aspects when encountered by Spring AOP.

Closes gh-32882
See gh-32793
2024-05-23 17:07:51 +02:00
Attacktive 73eb6f0660 Complete a Kotlin code snippet in the refdoc
Closes gh-32877
2024-05-23 16:03:34 +02:00
Stéphane Nicoll 31f298b929 Merge pull request #32874 from Seungpang
* pr/32874:
  Polish
  Document using ThreadLocal#remove instead of ThreadLocal#set(null)

Closes gh-32874
2024-05-23 08:14:00 +02:00
Stéphane Nicoll c01aab5850 Polish
See gh-32874
2024-05-23 08:12:30 +02:00
Seungrae 61ef5a8930 Document using ThreadLocal#remove instead of ThreadLocal#set(null)
See gh-32874
2024-05-23 08:10:54 +02:00
Spring Builds 3b53ee7038 Next development version (v6.1.9-SNAPSHOT) 2024-05-22 16:45:49 +00:00
1534 changed files with 10894 additions and 49344 deletions
@@ -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 $java_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:
-43
View File
@@ -1,43 +0,0 @@
name: Build Pull Request
on: pull_request
permissions:
contents: read
jobs:
build:
name: Build pull request
runs-on: ubuntu-latest
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 code
uses: actions/checkout@v4
- name: Validate Gradle wrapper
uses: gradle/wrapper-validation-action@699bb18358f12c5b78b37bb0111d3a0e2276e0e2
- name: Set up Gradle
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
- 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:
-44
View File
@@ -1,44 +0,0 @@
= Contributor Code of Conduct
As contributors and maintainers of this project, and in the interest of fostering an open
and welcoming community, we pledge to respect all people who contribute through reporting
issues, posting feature requests, updating documentation, submitting pull requests or
patches, and other activities.
We are committed to making participation in this project a harassment-free experience for
everyone, regardless of level of experience, gender, gender identity and expression,
sexual orientation, disability, personal appearance, body size, race, ethnicity, age,
religion, or nationality.
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery
* Personal attacks
* Trolling or insulting/derogatory comments
* Public or private harassment
* Publishing other's private information, such as physical or electronic addresses,
without explicit permission
* Other unethical or unprofessional conduct
Project maintainers have the right and responsibility to remove, edit, or reject comments,
commits, code, wiki edits, issues, and other contributions that are not aligned to this
Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors
that they deem inappropriate, threatening, offensive, or harmful.
By adopting this Code of Conduct, project maintainers commit themselves to fairly and
consistently applying these principles to every aspect of managing this project. Project
maintainers who do not follow or enforce the Code of Conduct may be permanently removed
from the project team.
This Code of Conduct applies both within project spaces and in public spaces when an
individual is representing the project or its community.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by
contacting a project maintainer at spring-code-of-conduct@pivotal.io . All complaints will
be reviewed and investigated and will result in a response that is deemed necessary and
appropriate to the circumstances. Maintainers are obligated to maintain confidentiality
with regard to the reporter of an incident.
This Code of Conduct is adapted from the
https://contributor-covenant.org[Contributor Covenant], version 1.3.0, available at
https://contributor-covenant.org/version/1/3/0/[contributor-covenant.org/version/1/3/0/]
+1 -1
View File
@@ -18,7 +18,7 @@ First off, thank you for taking the time to contribute! :+1: :tada:
This project is governed by the [Spring Code of Conduct](CODE_OF_CONDUCT.adoc).
By participating you are expected to uphold this code.
Please report unacceptable behavior to spring-code-of-conduct@pivotal.io.
Please report unacceptable behavior to spring-code-of-conduct@spring.io.
### How to Contribute
+2 -2
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".
@@ -6,7 +6,7 @@ Spring provides everything required beyond the Java programming language for cre
## Code of Conduct
This project is governed by the [Spring Code of Conduct](CODE_OF_CONDUCT.adoc). By participating, you are expected to uphold this code of conduct. Please report unacceptable behavior to spring-code-of-conduct@pivotal.io.
This project is governed by the [Spring Code of Conduct](CODE_OF_CONDUCT.adoc). By participating, you are expected to uphold this code of conduct. Please report unacceptable behavior to spring-code-of-conduct@spring.io.
## Access to Binaries
+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 'org.unbroken-dome.xjc' version '2.0.0' apply false
id 'com.github.ben-manes.versions' version '0.51.0'
id 'com.github.johnrengelman.shadow' version '8.1.1' apply false
id 'de.undercouch.download' version '5.4.0'
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 -1
View File
@@ -1,2 +1,2 @@
org.gradle.caching=true
javaFormatVersion=0.0.41
javaFormatVersion=0.0.42
@@ -50,7 +50,7 @@ public class CheckstyleConventions {
project.getPlugins().apply(CheckstylePlugin.class);
project.getTasks().withType(Checkstyle.class).forEach(checkstyle -> checkstyle.getMaxHeapSize().set("1g"));
CheckstyleExtension checkstyle = project.getExtensions().getByType(CheckstyleExtension.class);
checkstyle.setToolVersion("10.16.0");
checkstyle.setToolVersion("10.17.0");
checkstyle.getConfigDirectory().set(project.getRootProject().file("src/checkstyle"));
String version = SpringJavaFormatPlugin.class.getPackage().getImplementationVersion();
DependencySet checkstyleDependencies = project.getConfigurations().getByName("checkstyle").getDependencies();
@@ -64,7 +64,7 @@ public class CheckstyleConventions {
NoHttpExtension noHttp = project.getExtensions().getByType(NoHttpExtension.class);
noHttp.setAllowlistFile(project.file("src/nohttp/allowlist.lines"));
noHttp.getSource().exclude("**/test-output/**", "**/.settings/**",
"**/.classpath", "**/.project", "**/.gradle/**");
"**/.classpath", "**/.project", "**/.gradle/**", "**/node_modules/**");
List<String> buildFolders = List.of("bin", "build", "out");
project.allprojects(subproject -> {
Path rootPath = project.getRootDir().toPath();
+2 -2
View File
@@ -4,7 +4,7 @@ NOTE: CI is being migrated to GitHub Actions.
The Spring Framework uses https://concourse-ci.org/[Concourse] for its CI build and other automated tasks.
The Spring team has a dedicated Concourse instance available at https://ci.spring.io with a build pipeline
for https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-6.2.x[Spring Framework 6.2.x].
for https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-6.1.x[Spring Framework 6.1.x].
=== Setting up your development environment
@@ -53,7 +53,7 @@ The pipeline can be deployed using the following command:
[source]
----
$ fly -t spring set-pipeline -p spring-framework-6.2.x -c ci/pipeline.yml -l ci/parameters.yml
$ fly -t spring set-pipeline -p spring-framework-6.1.x -c ci/pipeline.yml -l ci/parameters.yml
----
NOTE: This assumes that you have credhub integration configured with the appropriate secrets.
+2 -2
View File
@@ -3,8 +3,8 @@ github-repo-name: "spring-projects/spring-framework"
sonatype-staging-profile: "org.springframework"
docker-hub-organization: "springci"
artifactory-server: "https://repo.spring.io"
branch: "main"
milestone: "6.2.x"
branch: "6.1.x"
milestone: "6.1.x"
build-name: "spring-framework"
pipeline-name: "spring-framework"
concourse-url: "https://ci.spring.io"
+2 -2
View File
@@ -7,8 +7,8 @@ anchors:
gradle-enterprise-task-params: &gradle-enterprise-task-params
DEVELOCITY_ACCESS_KEY: ((gradle_enterprise_secret_access_key))
sonatype-task-params: &sonatype-task-params
SONATYPE_USERNAME: ((sonatype-username))
SONATYPE_PASSWORD: ((sonatype-password))
SONATYPE_USERNAME: ((s01-user-token))
SONATYPE_PASSWORD: ((s01-user-token-password))
SONATYPE_URL: ((sonatype-url))
SONATYPE_STAGING_PROFILE: ((sonatype-staging-profile))
artifactory-task-params: &artifactory-task-params
+1 -1
View File
@@ -36,4 +36,4 @@ runtime:
failure_level: warn
ui:
bundle:
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.15/ui-bundle.zip
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.16/ui-bundle.zip
-2
View File
@@ -20,8 +20,6 @@ asciidoc:
fold: 'all'
table-stripes: 'odd'
include-java: 'example$docs-src/main/java/org/springframework/docs'
include-kotlin: 'example$docs-src/main/kotlin/org/springframework/docs'
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"
-18
View File
@@ -43,28 +43,10 @@ repositories {
dependencies {
api(project(":spring-context"))
api(project(":spring-jdbc"))
api(project(":spring-jms"))
api(project(":spring-web"))
api(project(":spring-webmvc"))
api(project(":spring-context-support"))
api(project(":spring-aspects"))
api(project(":spring-websocket"))
api("org.jetbrains.kotlin:kotlin-stdlib")
api("jakarta.jms:jakarta.jms-api")
api("jakarta.servlet:jakarta.servlet-api")
api("org.apache.commons:commons-dbcp2:2.11.0")
api("com.mchange:c3p0:0.9.5.5")
api("com.fasterxml.jackson.core:jackson-databind")
api("com.fasterxml.jackson.module:jackson-module-parameter-names")
api("jakarta.validation:jakarta.validation-api")
api("org.aspectj:aspectjweaver")
api("io.projectreactor.netty:reactor-netty-http")
api("org.eclipse.jetty.websocket:jetty-websocket-jetty-api")
api("javax.cache:cache-api")
api("jakarta.resource:jakarta.resource-api")
api("org.apache.activemq:activemq-ra:6.1.2")
implementation(project(":spring-core-test"))
implementation("org.assertj:assertj-core")
-3
View File
@@ -134,7 +134,6 @@
**** 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[]
@@ -172,8 +171,6 @@
***** 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[]
@@ -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.
@@ -221,8 +221,8 @@ NOTE: `ThreadLocal` instances come with serious issues (potentially resulting in
incorrectly using them in multi-threaded and multi-classloader environments. You
should always consider wrapping a `ThreadLocal` in some other class and never directly use
the `ThreadLocal` itself (except in the wrapper class). Also, you should
always remember to correctly set and unset (where the latter simply involves a call to
`ThreadLocal.set(null)`) the resource local to the thread. Unsetting should be done in
always remember to correctly set and unset (where the latter involves a call to
`ThreadLocal.remove()`) the resource local to the thread. Unsetting should be done in
any case, since not unsetting it might result in problematic behavior. Spring's
`ThreadLocal` support does this for you and should always be considered in favor of using
`ThreadLocal` instances without other proper handling code.
@@ -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`.
@@ -326,6 +326,19 @@ However, this is not a best practice and flagging the preferred constructor with
In case you are working on a code base that you cannot modify, you can set the {spring-framework-api}/beans/factory/support/AbstractBeanDefinition.html#PREFERRED_CONSTRUCTORS_ATTRIBUTE[`preferredConstructors` attribute] on the related bean definition to indicate which constructor should be used.
[[aot.bestpractices.comlext-data-structure]]
=== Avoid Complex Data Structure for Constructor Parameters and Properties
When crafting a `RootBeanDefinition` programmatically, you are not constrained in terms of types that you can use.
For instance, you may have a custom `record` with several properties that your bean takes as a constructor argument.
While this works fine with the regular runtime, AOT does not know how to generate the code of your custom data structure.
A good rule of thumb is to keep in mind that bean definitions are an abstraction on top of several models.
Rather than using such structure, decomposing to simple types or referring to a bean that is built as such is recommended.
As a last resort, you can implement your own `org.springframework.aot.generate.ValueCodeGenerator$Delegate`.
To use it, register its fully qualified name in `META-INF/spring/aot.factories` using the `Delegate` as the key.
[[aot.bestpractices.custom-arguments]]
=== Avoid Creating Bean with Custom Arguments
@@ -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]
@@ -154,17 +153,15 @@ Letting qualifier values select against target bean names, within the type-match
candidates, does not require a `@Qualifier` annotation at the injection point.
If there is no other resolution indicator (such as a qualifier or a primary marker),
for a non-unique dependency situation, Spring matches the injection point name
(that is, the field name or parameter name) against the target bean names and chooses the
same-named candidate, if any.
(that is, the field name or parameter name) against the target bean names and chooses
the same-named candidate, if any (either by bean name or by associated alias).
Since version 6.1, this requires the `-parameters` Java compiler flag to be present.
====
That said, if you intend to express annotation-driven injection by name, do not
primarily use `@Autowired`, even if it is capable of selecting by bean name among
type-matching candidates. Instead, use the JSR-250 `@Resource` annotation, which is
semantically defined to identify a specific target component by its unique name, with
the declared type being irrelevant for the matching process. `@Autowired` has rather
As an alternative for injection by name, consider the JSR-250 `@Resource` annotation
which is semantically defined to identify a specific target component by its unique name,
with the declared type being irrelevant for the matching process. `@Autowired` has rather
different semantics: After selecting candidate beans by type, the specified `String`
qualifier value is considered within those type-selected candidates only (for example,
matching an `account` qualifier against beans marked with the same qualifier label).
@@ -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.
@@ -73,6 +73,8 @@ runtime (concurrently with live access to the factory) is not officially support
lead to concurrent access exceptions, inconsistent state in the bean container, or both.
====
[[beans-definition-overriding]]
== Overriding Beans
@@ -81,15 +83,22 @@ already allocated. While bean overriding is possible, it makes the configuration
to read and this feature will be deprecated in a future release.
To disable bean overriding altogether, you can set the `allowBeanDefinitionOverriding`
to `false` on the `ApplicationContext` before it is refreshed. In such 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 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`.
NOTE: We acknowledge that overriding beans in a test is convenient, and there is
explicit support for this. For more details please refer to xref:testing/testcontext-framework/bean-overriding.adoc[this section].
.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
`@Bean` method matches that bean class. This simply means that the container will call
the `@Bean` factory method in favor of any pre-declared constructor on the bean class.
****
[[beans-beanname]]
== Naming Beans
@@ -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>
----
@@ -439,7 +439,7 @@ dataSource.url=jdbc:mysql:mydb
----
This example file can be used with a container definition that contains a bean called
`dataSource` that has `driver` and `url` properties.
`dataSource` that has `driverClassName` and `url` properties.
Compound property names are also supported, as long as every component of the path
except the final property being overridden is already non-null (presumably initialized
@@ -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,7 +516,7 @@ 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
@@ -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.
@@ -299,7 +299,7 @@ Java::
public class AppConfig {
@Bean
public MethodValidationPostProcessor validationPostProcessor() {
public static MethodValidationPostProcessor validationPostProcessor() {
return new MethodValidationPostProcessor();
}
}
@@ -341,7 +341,7 @@ xref:web/webflux/ann-rest-exceptions.adoc[Error Responses] sections.
=== Method Validation Exceptions
By default, `jakarta.validation.ConstraintViolationException` is raised with the set of
``ConstraintViolation``s returned by `jakarata.validation.Validator`. As an alternative,
``ConstraintViolation``s returned by `jakarta.validation.Validator`. As an alternative,
you can have `MethodValidationException` raised instead with ``ConstraintViolation``s
adapted to `MessageSourceResolvable` errors. To enable set the following flag:
@@ -357,7 +357,7 @@ Java::
public class AppConfig {
@Bean
public MethodValidationPostProcessor validationPostProcessor() {
public static MethodValidationPostProcessor validationPostProcessor() {
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
processor.setAdaptConstraintViolations(true);
return processor;
@@ -438,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
`"student.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
@@ -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]
======
@@ -112,7 +112,7 @@ Java::
this.actorMappingQuery = new ActorMappingQuery(dataSource);
}
public Customer getCustomer(Long id) {
public Actor getActor(Long id) {
return actorMappingQuery.findObject(id);
}
----
@@ -123,11 +123,11 @@ Kotlin::
----
private val actorMappingQuery = ActorMappingQuery(dataSource)
fun getCustomer(id: Long) = actorMappingQuery.findObject(id)
fun getActor(id: Long) = actorMappingQuery.findObject(id)
----
======
The method in the preceding example retrieves the customer with the `id` that is passed in as the
The method in the preceding example retrieves the actor with the `id` that is passed in as the
only parameter. Since we want only one object to be returned, we call the `findObject` convenience
method with the `id` as the parameter. If we had instead a query that returned a
list of objects and took additional parameters, we would use one of the `execute`
@@ -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`
@@ -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]
+1 -3
View File
@@ -29,8 +29,6 @@ Brannen, Ramnivas Laddad, Arjen Poutsma, Chris Beams, Tareq Abedrabbo, Andy Clem
Syer, Oliver Gierke, Rossen Stoyanchev, Phillip Webb, Rob Winch, Brian Clozel, Stephane
Nicoll, Sebastien Deleuze, Jay Bryant, Mark Paluch
Copyright © 2002 - 2024 VMware, Inc. All Rights Reserved.
Copies of this document may be made for your own use and for distribution to others,
provided that you do not charge any fee for such copies and further provided that each
copy contains this Copyright Notice, whether distributed in print or electronically.
copy contains the Copyright Notice, whether distributed in print or electronically.
@@ -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
public 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,12 +37,28 @@ 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
case (and ignoring the JMS infrastructure setup), you can invoke the `processOrder`
method with a core poll size of three threads and a maximum pool size of ten threads.
method with a core pool size of three threads and a maximum pool size of ten threads.
You can customize the listener container factory to use for each annotation or you can
configure an explicit default by implementing the `JmsListenerConfigurer` interface.
@@ -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
@@ -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`.
@@ -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.
@@ -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
@@ -199,9 +199,9 @@ 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,51 +0,0 @@
[[spring-testing-annotation-beanoverriding-mockitobean]]
= `@MockitoBean` and `@MockitoSpyBean`
`@MockitoBean` and `@MockitoSpyBean` are used on test class fields 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 definitions to
override, but note that `@Qualifier` annotations are also taken into account for the
purpose of matching. Users can also make things entirely explicit by specifying a bean
`name` in the annotation.
Each annotation also defines Mockito-specific attributes to fine-tune the mocking details.
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].
It requires that at most one matching candidate definition exists if a bean name
is specified, or exactly one if no bean name is specified.
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.
It requires that exactly one candidate definition exists.
The following example shows how to configure the bean name via `@MockitoBean` and
`@MockitoSpyBean`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
class OverrideBeanTests {
@MockitoBean(name = "service1") // <1>
private CustomService mockService;
@MockitoSpyBean(name = "service2") // <2>
private CustomService spyService; // <3>
// test case body...
}
----
<1> Mark `mockService` as a Mockito mock override of bean `service1` in this test class.
<2> Mark `spyService` as a Mockito spy override of bean `service2` in this test class.
<3> The fields will be injected with the Mockito mock and spy, respectively.
======
@@ -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,44 +0,0 @@
[[spring-testing-annotation-beanoverriding-testbean]]
= `@TestBean`
`@TestBean` is used on a test class field to override a specific bean in the test's
`ApplicationContext` with an instance provided by a conventionally named static factory
method.
By default, the associated factory method name is derived from the annotated field's name,
but the annotation allows for a specific method name to be provided.
The `@TestBean` annotation uses the `REPLACE_DEFINITION`
xref:testing/testcontext-framework/bean-overriding.adoc#testcontext-bean-overriding-custom[strategy for test bean overriding].
By default, the annotated field's type is used to search for candidate definitions to override.
In that case it is required that exactly one definition matches, but note that `@Qualifier`
annotations are also taken into account for the purpose of matching.
Users can also make things entirely explicit by specifying a bean `name` in the annotation.
The following example shows how to fully configure the `@TestBean` annotation, with
explicit values equivalent to the defaults:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
class OverrideBeanTests {
@TestBean(name = "service", methodName = "serviceTestOverride") // <1>
private CustomService service;
// test case body...
private static CustomService serviceTestOverride() { // <2>
return new MyFakeCustomService();
}
}
----
<1> Mark a field for bean overriding in this test class.
<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.
@@ -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
@@ -8,7 +8,8 @@ to use the raw HtmlUnit libraries.
== 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:
@@ -166,7 +166,7 @@ following sections to make this pattern much easier to implement.
== MockMvc and WebDriver Setup
To use Selenium WebDriver with the Spring MVC Test framework, make sure that your project
includes a test dependency on `org.seleniumhq.selenium:selenium-htmlunit3-driver`.
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:
@@ -87,7 +87,7 @@ 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]
@@ -1,71 +0,0 @@
[[testcontext-bean-overriding]]
= Bean Overriding in Tests
Bean overriding in tests refers to the ability to override specific beans in the
`ApplicationContext` for a test class, by annotating one or more fields in the test class.
NOTE: This feature is intended as a less risky alternative to the practice of registering
a bean via `@Bean` with the `DefaultListableBeanFactory`
`setAllowBeanDefinitionOverriding` flag set to `true`.
The Spring TestContext framework provides two sets of annotations for bean overriding.
* xref:testing/annotations/integration-spring/annotation-testbean.adoc[`@TestBean`]
* xref:testing/annotations/integration-spring/annotation-mockitobean.adoc[`@MockitoBean` and `@MockitoSpyBean`]
The former relies purely on Spring, while the latter set relies on the
https://site.mockito.org/[Mockito] third-party library.
[[testcontext-bean-overriding-custom]]
== Custom Bean Override Support
The three annotations mentioned above build upon the `@BeanOverride` meta-annotation and
associated infrastructure, which allows one to define custom bean overriding variants.
To create custom bean override support, the following is needed:
* An annotation meta-annotated with `@BeanOverride` that defines the
`BeanOverrideProcessor` to use
* A custom `BeanOverrideProcessor` implementation
* One or more concrete `OverrideMetadata` implementations provided by the processor
The Spring TestContext framework includes implementations of the following APIs that
support bean overriding and are responsible for setting up the rest of the infrastructure.
* a `BeanFactoryPostProcessor`
* a `ContextCustomizerFactory`
* a `TestExecutionListener`
The `spring-test` module registers implementations of the latter two
(`BeanOverrideContextCustomizerFactory` and `BeanOverrideTestExecutionListener`) in its
{spring-framework-code}/spring-test/src/main/resources/META-INF/spring.factories[`META-INF/spring.factories`
properties file].
The bean overriding infrastructure searches in test classes for any field meta-annotated
with `@BeanOverride` and instantiates the corresponding `BeanOverrideProcessor` which is
responsible for registering appropriate `OverrideMetadata`.
The internal `BeanOverrideBeanFactoryPostProcessor` then uses that information to alter
the test's `ApplicationContext` by registering and replacing bean definitions as defined
by the corresponding `BeanOverrideStrategy`:
* `REPLACE_DEFINITION`: Replaces the bean definition. Throws an exception if a
corresponding bean definition does not exist.
* `REPLACE_OR_CREATE_DEFINITION`: Replaces the bean definition if it exists. Creates a
new bean definition if a corresponding bean definition does not exist.
* `WRAP_BEAN`: Retrieves the original bean instance and wraps it.
[NOTE]
====
In contrast to Spring's autowiring mechanism (for example, resolution of an `@Autowired`
field), the bean overriding infrastructure in the TestContext framework has limited
heuristics it can perform to locate a bean. Either the `BeanOverrideProcessor` can compute
the name of the bean to override, or it can be unambiguously selected given the type of
the annotated field and its qualifying annotations.
Typically, the bean is selected by type by the `BeanOverrideFactoryPostProcessor`.
Alternatively, the user can directly provide the bean name in the custom annotation.
Some `BeanOverrideProcessor`s could also internally compute a bean name based on a
convention or another advanced method.
====
@@ -1,58 +1,43 @@
[[testcontext-ctx-management-dynamic-property-sources]]
= Context Configuration with Dynamic Property Sources
The Spring TestContext Framework provides support for _dynamic_ properties via the
`@DynamicPropertySource` annotation and the `DynamicPropertyRegistry`.
[NOTE]
====
The `@DynamicPropertySource` annotation and its supporting infrastructure were originally
designed to allow properties from {testcontainers-site}[Testcontainers] based tests to be
exposed easily to Spring integration tests. However, this feature may be used with any
form of external resource whose lifecycle is managed outside the test's
`ApplicationContext` or with beans whose lifecycle is managed by the test's
`ApplicationContext`.
====
In contrast to the
xref:testing/testcontext-framework/ctx-management/property-sources.adoc[`@TestPropertySource`]
annotation that is applied at the class level, `@DynamicPropertySource` can be applied to
`static` methods in integration test classes or to `@Bean` methods in test
`@Configuration` classes in order to add properties with dynamic values to the set of
As of Spring Framework 5.2.5, the TestContext framework provides support for _dynamic_
properties via the `@DynamicPropertySource` annotation. This annotation can be used in
integration tests that need to add properties with dynamic values to the set of
`PropertySources` in the `Environment` for the `ApplicationContext` loaded for the
integration test.
A `DynamicPropertyRegistry` is used to add _name-value_ pairs to the `Environment`.
Values are dynamic and provided via a `Supplier` which is only invoked when the property
is resolved. Typically, method references are used to supply values.
[NOTE]
====
The `@DynamicPropertySource` annotation and its supporting infrastructure were
originally designed to allow properties from
{testcontainers-site}[Testcontainers] based tests to be exposed easily to
Spring integration tests. However, this feature may also be used with any form of
external resource whose lifecycle is maintained outside the test's `ApplicationContext`.
====
Methods in integration test classes that are annotated with `@DynamicPropertySource` must
be `static` and must accept a single `DynamicPropertyRegistry` argument.
`@Bean` methods annotated with `@DynamicPropertySource` may either accept an argument of
type `DynamicPropertyRegistry` or access a `DynamicPropertyRegistry` instance autowired
into their enclosing `@Configuration` class. Note, however, that `@Bean` methods which
interact with a `DynamicPropertyRegistry` are not required to be annotated with
`@DynamicPropertySource` unless they need to enforce eager initialization of the bean
within the context. See the class-level javadoc for `DynamicPropertyRegistry` for details.
In contrast to the xref:testing/testcontext-framework/ctx-management/property-sources.adoc[`@TestPropertySource`]
annotation that is applied at the class level, `@DynamicPropertySource` must be applied
to a `static` method that accepts a single `DynamicPropertyRegistry` argument which is
used to add _name-value_ pairs to the `Environment`. Values are dynamic and provided via
a `Supplier` which is only invoked when the property is resolved. Typically, method
references are used to supply values, as can be seen in the following example which uses
the Testcontainers project to manage a Redis container outside of the Spring
`ApplicationContext`. The IP address and port of the managed Redis container are made
available to components within the test's `ApplicationContext` via the `redis.host` and
`redis.port` properties. These properties can be accessed via Spring's `Environment`
abstraction or injected directly into Spring-managed components for example, via
`@Value("${redis.host}")` and `@Value("${redis.port}")`, respectively.
[TIP]
====
If you use `@DynamicPropertySource` in a base class and discover that tests in subclasses
fail because the dynamic properties change between subclasses, you may need to annotate
your base class with
xref:testing/annotations/integration-spring/annotation-dirtiescontext.adoc[`@DirtiesContext`]
to ensure that each subclass gets its own `ApplicationContext` with the correct dynamic
your base class with xref:testing/annotations/integration-spring/annotation-dirtiescontext.adoc[`@DirtiesContext`] to
ensure that each subclass gets its own `ApplicationContext` with the correct dynamic
properties.
====
The following example uses the Testcontainers project to manage a Redis container outside
of the Spring `ApplicationContext`. The IP address and port of the managed Redis
container are made available to components within the test's `ApplicationContext` via the
`redis.host` and `redis.port` properties. These properties can be accessed via Spring's
`Environment` abstraction or injected directly into Spring-managed components for
example, via `@Value("${redis.host}")` and `@Value("${redis.port}")`, respectively.
[tabs]
======
Java::
@@ -107,55 +92,7 @@ Kotlin::
----
======
The following example demonstrates how to use `DynamicPropertyRegistry` and
`@DynamicPropertySource` with a `@Bean` method. The `api.url` property can be accessed
via Spring's `Environment` abstraction or injected directly into other Spring-managed
components for example, via `@Value("${api.url}")`. The value of the `api.url` property
will be dynamically retrieved from the `ApiServer` bean.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
class TestConfig {
@Bean
@DynamicPropertySource
ApiServer apiServer(DynamicPropertyRegistry registry) {
ApiServer apiServer = new ApiServer();
registry.add("api.url", apiServer::getUrl);
return apiServer;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
class TestConfig {
@Bean
@DynamicPropertySource
fun apiServer(registry: DynamicPropertyRegistry): ApiServer {
val apiServer = ApiServer()
registry.add("api.url", apiServer::getUrl)
return apiServer
}
}
----
======
NOTE: The use of `@DynamicPropertySource` on the `@Bean` method is optional and results
in the `ApiServer` bean being eagerly initialized so that other beans in the context can
be given access to the dynamic properties sourced from the `ApiServer` bean when those
other beans are initialized.
[[testcontext-ctx-management-dynamic-property-sources-precedence]]
[[precedence]]
== Precedence
Dynamic properties have higher precedence than those loaded from `@TestPropertySource`,
@@ -184,6 +184,7 @@ meta-present `@TestPropertySource` annotations. In other words, `locations` and
meta-annotation.
====
[[default-properties-file-detection]]
== Default Properties File Detection
@@ -194,7 +195,7 @@ if the annotated test class is `com.example.MyTest`, the corresponding default p
file is `classpath:com/example/MyTest.properties`. If the default cannot be detected, an
`IllegalStateException` is thrown.
[[testcontext-ctx-management-property-sources-precedence]]
[[precedence]]
== Precedence
Test properties have higher precedence than those defined in the operating system's
@@ -16,8 +16,6 @@ by default, exactly in the following order:
Micrometer's `ObservationRegistry`.
* `DirtiesContextTestExecutionListener`: Handles the `@DirtiesContext` annotation for
"`after`" modes.
* `CommonCachesTestExecutionListener`: Clears resource caches in the test's
`ApplicationContext` if necessary.
* `TransactionalTestExecutionListener`: Provides transactional test execution with
default rollback semantics.
* `SqlScriptsTestExecutionListener`: Runs SQL scripts configured by using the `@Sql`
@@ -803,7 +803,7 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
ClassPathResource index = new ClassPathResource("static/index.html");
List<String> extensions = List.of("js", "css", "ico", "png", "jpg", "gif");
List<String> extensions = Arrays.asList("js", "css", "ico", "png", "jpg", "gif");
RequestPredicate spaPredicate = path("/api/**").or(path("/error")).or(pathExtension(extensions::contains)).negate();
RouterFunction<ServerResponse> redirectToIndex = route()
.resource(spaPredicate, index)
@@ -97,8 +97,8 @@ Java::
Mono<Person> result = client.get()
.uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
.retrieve()
.onStatus(HttpStatus::is4xxClientError, response -> ...)
.onStatus(HttpStatus::is5xxServerError, response -> ...)
.onStatus(HttpStatusCode::is4xxClientError, response -> ...)
.onStatus(HttpStatusCode::is5xxServerError, response -> ...)
.bodyToMono(Person.class);
----
@@ -109,8 +109,8 @@ Kotlin::
val result = client.get()
.uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
.retrieve()
.onStatus(HttpStatus::is4xxClientError) { ... }
.onStatus(HttpStatus::is5xxServerError) { ... }
.onStatus(HttpStatusCode::is4xxClientError) { ... }
.onStatus(HttpStatusCode::is5xxServerError) { ... }
.awaitBody<Person>()
----
======
@@ -44,10 +44,6 @@ has an `@ExceptionHandler` method that handles any `ErrorResponse` exception, wh
includes all built-in web exceptions. You can add more exception handling methods, and
use a protected method to map any exception to a `ProblemDetail`.
You can register `ErrorResponse` interceptors through the
xref:web/webflux/config.adoc[WebFlux Config] with a `WebFluxConfigurer`. Use that to intercept
any RFC 7807 response and take some action.
[[webflux-ann-rest-exceptions-non-standard]]
@@ -44,11 +44,6 @@ Kotlin::
----
======
NOTE: When using Spring Boot, you may want to use `@Configuration` classes of type `WebFluxConfigurer` but without
`@EnableWebFlux` to keep Spring Boot WebFlux customizations. See more details in
xref:#webflux-config-customize[the WebFlux config API section] and in
{spring-boot-docs}/web.html#web.reactive.webflux.auto-configuration[the dedicated Spring Boot documentation].
The preceding example registers a number of Spring WebFlux
xref:web/webflux/dispatcher-handler.adoc#webflux-special-bean-types[infrastructure beans] and adapts to dependencies
available on the classpath -- for JSON, XML, and others.
@@ -69,6 +64,7 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
// Implement configuration methods...
@@ -80,6 +76,7 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
// Implement configuration methods...
@@ -105,6 +102,7 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
@@ -120,6 +118,7 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
override fun addFormatters(registry: FormatterRegistry) {
@@ -141,6 +140,7 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
@@ -157,6 +157,7 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
override fun addFormatters(registry: FormatterRegistry) {
@@ -193,6 +194,7 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
@@ -208,6 +210,7 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
override fun getValidator(): Validator {
@@ -276,6 +279,7 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
@@ -290,6 +294,7 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
override fun configureContentTypeResolver(builder: RequestedContentTypeResolverBuilder) {
@@ -314,6 +319,7 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
@@ -328,10 +334,11 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
override fun configureHttpMessageCodecs(configurer: ServerCodecConfigurer) {
// ...
configurer.defaultCodecs().maxInMemorySize(512 * 1024)
}
}
----
@@ -369,6 +376,7 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
@@ -383,6 +391,7 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
override fun configureViewResolvers(registry: ViewResolverRegistry) {
@@ -403,6 +412,7 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@@ -427,6 +437,7 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
override fun configureViewResolvers(registry: ViewResolverRegistry) {
@@ -452,6 +463,7 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@@ -468,6 +480,7 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
override fun configureViewResolvers(registry: ViewResolverRegistry) {
@@ -490,6 +503,7 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@@ -510,6 +524,7 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
@@ -550,6 +565,7 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
@@ -567,6 +583,7 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
override fun addResourceHandlers(registry: ResourceHandlerRegistry) {
@@ -599,6 +616,7 @@ Java::
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
@@ -617,6 +635,7 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
override fun addResourceHandlers(registry: ResourceHandlerRegistry) {
@@ -676,6 +695,7 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
@@ -691,6 +711,7 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
@Override
@@ -735,6 +756,7 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
@@ -750,6 +772,7 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
@Override
@@ -787,6 +810,7 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
@@ -803,6 +827,7 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
@Override
@@ -7,8 +7,43 @@
`@ExceptionHandler` methods to handle exceptions from controller methods. The following
example includes such a handler method:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Controller
public class SimpleController {
// ...
@ExceptionHandler // <1>
public ResponseEntity<String> handle(IOException ex) {
// ...
}
}
----
<1> Declaring an `@ExceptionHandler`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Controller
class SimpleController {
// ...
@ExceptionHandler // <1>
fun handle(ex: IOException): ResponseEntity<String> {
// ...
}
}
----
<1> Declaring an `@ExceptionHandler`.
======
include-code::./SimpleController[indent=0]
The exception can match against a top-level exception being propagated (that is, a direct
@@ -30,22 +65,6 @@ Support for `@ExceptionHandler` methods in Spring WebFlux is provided by the
`HandlerAdapter` for `@RequestMapping` methods. See xref:web/webflux/dispatcher-handler.adoc[`DispatcherHandler`]
for more detail.
[[webflux-ann-exceptionhandler-media]]
== Media Type Mapping
[.small]#xref:web/webmvc/mvc-controller/ann-exceptionhandler.adoc#mvc-ann-exceptionhandler-media[See equivalent in the Servlet stack]#
In addition to exception types, `@ExceptionHandler` methods can also declare producible media types.
This allows to refine error responses depending on the media types requested by HTTP clients, typically in the "Accept" HTTP request header.
Applications can declare producible media types directly on annotations, for the same exception type:
include-code::./MediaTypeController[tag=mediatype,indent=0]
Here, methods handle the same exception type but will not be rejected as duplicates.
Instead, API clients requesting "application/json" will receive a JSON error, and browsers will get an HTML error view.
Each `@ExceptionHandler` annotation can declare several producible media types,
the content negotiation during the error handling phase will decide which content type will be used.
[[webflux-ann-exceptionhandler-args]]
@@ -20,7 +20,7 @@ Java::
----
@Configuration
@ComponentScan("org.example.web") // <1>
public class WebConfiguration {
public class WebConfig {
// ...
}
@@ -33,7 +33,7 @@ Kotlin::
----
@Configuration
@ComponentScan("org.example.web") // <1>
class WebConfiguration {
class WebConfig {
// ...
}
@@ -781,7 +781,7 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
ClassPathResource index = new ClassPathResource("static/index.html");
List<String> extensions = List.of("js", "css", "ico", "png", "jpg", "gif");
List<String> extensions = Arrays.asList("js", "css", "ico", "png", "jpg", "gif");
RequestPredicate spaPredicate = path("/api/**").or(path("/error")).or(pathExtension(extensions::contains)).negate();
RouterFunction<ServerResponse> redirectToIndex = route()
.resource(spaPredicate, index)
@@ -445,13 +445,6 @@ directly. For example:
}
----
The following `ThreadLocalAccessor` implementations are provided out of the box:
* `LocaleContextThreadLocalAccessor` -- propagates `LocaleContext` via `LocaleContextHolder`
* `RequestAttributesThreadLocalAccessor` -- propagates `RequestAttributes` via `RequestContextHolder`
The above are not registered automatically. You need to register them via `ContextRegistry.getInstance()` on startup.
For more details, see the
https://micrometer.io/docs/contextPropagation[documentation] of the Micrometer Context
Propagation library.
@@ -44,10 +44,6 @@ has an `@ExceptionHandler` method that handles any `ErrorResponse` exception, wh
includes all built-in web exceptions. You can add more exception handling methods, and
use a protected method to map any exception to a `ProblemDetail`.
You can register `ErrorResponse` interceptors through the
xref:web/webmvc/mvc-config.adoc[MVC Config] with a `WebMvcConfigurer`. Use that to intercept
any RFC 7807 response and take some action.
[[mvc-ann-rest-exceptions-non-standard]]
@@ -187,7 +183,7 @@ Message codes and arguments for each error are also resolved via `MessageSource`
|===
NOTE: Unlike other exceptions, the message arguments for
`MethodArgumentValidException` and `HandlerMethodValidationException` are based on a list of
`MethodArgumentValidException` and `HandlerMethodValidationException` are baed on a list of
`MessageSourceResolvable` errors that can also be customized through a
xref:core/beans/context-introduction.adoc#context-functionality-messagesource[MessageSource]
resource bundle. See
@@ -12,7 +12,30 @@ For advanced mode, you can remove `@EnableWebMvc` and extend directly from
`DelegatingWebMvcConfiguration` instead of implementing `WebMvcConfigurer`,
as the following example shows:
include-code::./WebConfiguration[tag=snippet,indent=0]
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
public class WebConfig extends DelegatingWebMvcConfiguration {
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
class WebConfig : DelegatingWebMvcConfiguration() {
// ...
}
----
======
You can keep existing methods in `WebConfig`, but you can now also override bean declarations
from the base class, and you can still have any number of other `WebMvcConfigurer` implementations on
@@ -5,7 +5,39 @@ The MVC namespace does not have an advanced mode. If you need to customize a pro
a bean that you cannot change otherwise, you can use the `BeanPostProcessor` lifecycle
hook of the Spring `ApplicationContext`, as the following example shows:
include-code::./MyPostProcessor[tag=snippet,indent=0]
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Component
public class MyPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String name) throws BeansException {
// ...
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Component
class MyPostProcessor : BeanPostProcessor {
override fun postProcessBeforeInitialization(bean: Any, name: String): Any {
// ...
}
}
----
======
Note that you need to declare `MyPostProcessor` as a bean, either explicitly in XML or
by letting it be detected through a `<component-scan/>` declaration.
@@ -13,9 +13,59 @@ strategy over path extensions. See
xref:web/webmvc/mvc-controller/ann-requestmapping.adoc#mvc-ann-requestmapping-suffix-pattern-match[Suffix Match] and xref:web/webmvc/mvc-controller/ann-requestmapping.adoc#mvc-ann-requestmapping-rfd[Suffix Match and RFD] for
more details.
You can customize requested content type resolution, as the following example shows:
In Java configuration, you can customize requested content type resolution, as the
following example shows:
include-code::./WebConfiguration[tag=snippet,indent=0]
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.mediaType("json", MediaType.APPLICATION_JSON);
configurer.mediaType("xml", MediaType.APPLICATION_XML);
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebMvc
class WebConfig : WebMvcConfigurer {
override fun configureContentNegotiation(configurer: ContentNegotiationConfigurer) {
configurer.mediaType("json", MediaType.APPLICATION_JSON)
configurer.mediaType("xml", MediaType.APPLICATION_XML)
}
}
----
======
The following example shows how to achieve the same configuration in XML:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager"/>
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="mediaTypes">
<value>
json=application/json
xml=application/xml
</value>
</property>
</bean>
----
@@ -6,16 +6,119 @@
By default, formatters for various number and date types are installed, along with support
for customization via `@NumberFormat` and `@DateTimeFormat` on fields.
To register custom formatters and converters, use the following:
To register custom formatters and converters in Java config, use the following:
include-code::./WebConfiguration[tag=snippet,indent=0]
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
// ...
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebMvc
class WebConfig : WebMvcConfigurer {
override fun addFormatters(registry: FormatterRegistry) {
// ...
}
}
----
======
To do the same in XML config, use the following:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:annotation-driven conversion-service="conversionService"/>
<bean id="conversionService"
class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="org.example.MyConverter"/>
</set>
</property>
<property name="formatters">
<set>
<bean class="org.example.MyFormatter"/>
<bean class="org.example.MyAnnotationFormatterFactory"/>
</set>
</property>
<property name="formatterRegistrars">
<set>
<bean class="org.example.MyFormatterRegistrar"/>
</set>
</property>
</bean>
</beans>
----
By default Spring MVC considers the request Locale when parsing and formatting date
values. This works for forms where dates are represented as Strings with "input" form
fields. For "date" and "time" form fields, however, browsers use a fixed format defined
in the HTML spec. For such cases date and time formatting can be customized as follows:
include-code::./DateTimeWebConfiguration[tag=snippet,indent=0]
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
registrar.setUseIsoFormat(true);
registrar.registerFormatters(registry);
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebMvc
class WebConfig : WebMvcConfigurer {
override fun addFormatters(registry: FormatterRegistry) {
val registrar = DateTimeFormatterRegistrar()
registrar.setUseIsoFormat(true)
registrar.registerFormatters(registry)
}
}
----
======
NOTE: See xref:core/validation/format.adoc#format-FormatterRegistrar-SPI[the `FormatterRegistrar` SPI]
and the `FormattingConversionServiceFactoryBean` for more information on when to use
@@ -6,7 +6,33 @@
In Java configuration, you can implement the `WebMvcConfigurer` interface, as the
following example shows:
include-code::./WebConfiguration[tag=snippet,indent=0]
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
// Implement configuration methods...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebMvc
class WebConfig : WebMvcConfigurer {
// Implement configuration methods...
}
----
======
In XML, you can check attributes and sub-elements of `<mvc:annotation-driven/>`. You can
view the https://schema.spring.io/mvc/spring-mvc.xsd[Spring MVC XML schema] or use
@@ -15,7 +15,44 @@ lower than that of the `DefaultServletHttpRequestHandler`, which is `Integer.MAX
The following example shows how to enable the feature by using the default setup:
include-code::./WebConfiguration[tag=snippet,indent=0]
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebMvc
class WebConfig : WebMvcConfigurer {
override fun configureDefaultServletHandling(configurer: DefaultServletHandlerConfigurer) {
configurer.enable()
}
}
----
======
The following example shows how to achieve the same configuration in XML:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<mvc:default-servlet-handler/>
----
The caveat to overriding the `/` Servlet mapping is that the `RequestDispatcher` for the
default Servlet must be retrieved by name rather than by path. The
@@ -26,4 +63,45 @@ If the default Servlet has been custom-configured with a different name, or if a
different Servlet container is being used where the default Servlet name is unknown,
then you must explicitly provide the default Servlet's name, as the following example shows:
include-code::./CustomDefaultServletConfiguration[tag=snippet,indent=0]
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable("myCustomDefaultServlet");
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebMvc
class WebConfig : WebMvcConfigurer {
override fun configureDefaultServletHandling(configurer: DefaultServletHandlerConfigurer) {
configurer.enable("myCustomDefaultServlet")
}
}
----
======
The following example shows how to achieve the same configuration in XML:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<mvc:default-servlet-handler default-servlet-name="myCustomDefaultServlet"/>
----
@@ -3,11 +3,50 @@
[.small]#xref:web/webflux/config.adoc#webflux-config-enable[See equivalent in the Reactive stack]#
You can use the `@EnableWebMvc` annotation to enable MVC configuration with programmatic configuration, or `<mvc:annotation-driven>` with XML configuration, as the following example shows:
In Java configuration, you can use the `@EnableWebMvc` annotation to enable MVC
configuration, as the following example shows:
include-code::./WebConfiguration[tag=snippet,indent=0]
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebMvc
public class WebConfig {
}
----
NOTE: When using Spring Boot, you may want to use `@Configuration` classes of type `WebMvcConfigurer` but without `@EnableWebMvc` to keep Spring Boot MVC customizations. See more details in xref:web/webmvc/mvc-config/customize.adoc[the MVC Config API section] and in {spring-boot-docs}/web.html#web.servlet.spring-mvc.auto-configuration[the dedicated Spring Boot documentation].
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebMvc
class WebConfig
----
======
In XML configuration, you can use the `<mvc:annotation-driven>` element to enable MVC
configuration, as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:annotation-driven/>
</beans>
----
The preceding example registers a number of Spring MVC
xref:web/webmvc/mvc-servlet/special-bean-types.adoc[infrastructure beans] and adapts to dependencies
@@ -1,9 +1,56 @@
[[mvc-config-interceptors]]
= Interceptors
You can register interceptors to apply to incoming requests, as the following example shows:
In Java configuration, you can register interceptors to apply to incoming requests, as
the following example shows:
include-code::./WebConfiguration[tag=snippet,indent=0]
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LocaleChangeInterceptor());
registry.addInterceptor(new ThemeChangeInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**");
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Configuration
@EnableWebMvc
class WebConfig : WebMvcConfigurer {
override fun addInterceptors(registry: InterceptorRegistry) {
registry.addInterceptor(LocaleChangeInterceptor())
registry.addInterceptor(ThemeChangeInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**")
}
}
----
======
The following example shows how to achieve the same configuration in XML:
[source,xml,indent=0,subs="verbatim"]
----
<mvc:interceptors>
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"/>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/admin/**"/>
<bean class="org.springframework.web.servlet.theme.ThemeChangeInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
----
WARNING: Interceptors are not ideally suited as a security layer due to the potential for
a mismatch with annotated controller path matching. Generally, we recommend using Spring
@@ -15,10 +15,48 @@ Boot application, prefer to use the {spring-boot-docs}/web.html#web.servlet.spri
mechanism. Or alternatively, use `extendMessageConverters` to modify message converters
at the end.
The following example adds XML and Jackson JSON converters with a customized `ObjectMapper`
instead of the default ones:
The following example adds XML and Jackson JSON converters with a customized
`ObjectMapper` instead of the default ones:
include-code::./WebConfiguration[tag=snippet,indent=0]
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebMvc
public class WebConfiguration implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder()
.indentOutput(true)
.dateFormat(new SimpleDateFormat("yyyy-MM-dd"))
.modulesToInstall(new ParameterNamesModule());
converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
converters.add(new MappingJackson2XmlHttpMessageConverter(builder.createXmlMapper(true).build()));
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebMvc
class WebConfiguration : WebMvcConfigurer {
override fun configureMessageConverters(converters: MutableList<HttpMessageConverter<*>>) {
val builder = Jackson2ObjectMapperBuilder()
.indentOutput(true)
.dateFormat(SimpleDateFormat("yyyy-MM-dd"))
.modulesToInstall(ParameterNamesModule())
converters.add(MappingJackson2HttpMessageConverter(builder.build()))
converters.add(MappingJackson2XmlHttpMessageConverter(builder.createXmlMapper(true).build()))
----
======
In the preceding example,
{spring-framework-api}/http/converter/json/Jackson2ObjectMapperBuilder.html[`Jackson2ObjectMapperBuilder`]
@@ -38,7 +76,7 @@ It also automatically registers the following well-known modules if they are det
* {jackson-github-org}/jackson-datatype-joda[jackson-datatype-joda]: Support for Joda-Time types.
* {jackson-github-org}/jackson-datatype-jsr310[jackson-datatype-jsr310]: Support for Java 8 Date and Time API types.
* {jackson-github-org}/jackson-datatype-jdk8[jackson-datatype-jdk8]: Support for other Java 8 types, such as `Optional`.
* {jackson-github-org}/jackson-module-kotlin[jackson-module-kotlin]: Support for Kotlin classes and data classes.
* {jackson-github-org}/jackson-module-kotlin[`jackson-module-kotlin`]: Support for Kotlin classes and data classes.
NOTE: Enabling indentation with Jackson XML support requires
https://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.codehaus.woodstox%22%20AND%20a%3A%22woodstox-core-asl%22[`woodstox-core-asl`]
@@ -48,3 +86,29 @@ Other interesting Jackson modules are available:
* https://github.com/zalando/jackson-datatype-money[jackson-datatype-money]: Support for `javax.money` types (unofficial module).
* {jackson-github-org}/jackson-datatype-hibernate[jackson-datatype-hibernate]: Support for Hibernate-specific types and properties (including lazy-loading aspects).
The following example shows how to achieve the same configuration in XML:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper" ref="objectMapper"/>
</bean>
<bean class="org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter">
<property name="objectMapper" ref="xmlMapper"/>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<bean id="objectMapper" class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean"
p:indentOutput="true"
p:simpleDateFormat="yyyy-MM-dd"
p:modulesToInstall="com.fasterxml.jackson.module.paramnames.ParameterNamesModule"/>
<bean id="xmlMapper" parent="objectMapper" p:createXmlMapper="true"/>
----
@@ -7,6 +7,61 @@ You can customize options related to path matching and treatment of the URL.
For details on the individual options, see the
{spring-framework-api}/web/servlet/config/annotation/PathMatchConfigurer.html[`PathMatchConfigurer`] javadoc.
The following example shows how to customize path matching:
The following example shows how to customize path matching in Java configuration:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.addPathPrefix("/api", HandlerTypePredicate.forAnnotation(RestController.class));
}
private PathPatternParser patternParser() {
// ...
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebMvc
class WebConfig : WebMvcConfigurer {
override fun configurePathMatch(configurer: PathMatchConfigurer) {
configurer.addPathPrefix("/api", HandlerTypePredicate.forAnnotation(RestController::class.java))
}
fun patternParser(): PathPatternParser {
//...
}
}
----
======
The following example shows how to customize path matching in XML configuration:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<mvc:annotation-driven>
<mvc:path-matching
path-helper="pathHelper"
path-matcher="pathMatcher"/>
</mvc:annotation-driven>
<bean id="pathHelper" class="org.example.app.MyPathHelper"/>
<bean id="pathMatcher" class="org.example.app.MyPathMatcher"/>
----
include-code::./WebConfiguration[tag=snippet,indent=0]

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