Compare commits

...

432 Commits

Author SHA1 Message Date
Spring Builds 6c03531c86 Release v6.2.0-M1 2024-04-11 08:55:35 +00:00
Brian Clozel 54dab45151 Merge branch '6.1.x' 2024-04-11 08:51:13 +02:00
Stéphane Nicoll 2e3a923225 Polish 2024-04-11 08:45:26 +02:00
Juergen Hoeller 77c6f160fe Merge branch '6.1.x'
# Conflicts:
#	spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java
#	spring-web/src/test/java/org/springframework/web/method/annotation/RequestParamMethodArgumentResolverTests.java
#	spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestParamMethodArgumentResolverTests.java
2024-04-10 18:29:48 +02:00
Simon Baslé 2d33aac350 Improve Bean Overriding support, testing and documentation
This commit improves on the bean overriding feature in several ways:
the API is simplified and polished (metadata and processor contracts,
 etc...).

The commit also reworks infrastructure classes (context customizer,
test execution listener, BeanOverrideBeanFactoryPostProcessor, etc...).
Parsing of annotations is now fully stateless.

In order to avoid OverrideMetadata in bean definition and to make a
first step towards AOT support, the BeanOverrideBeanFactoryPostProcessor
now delegates to a BeanOverrideRegistrar to track classes to parse,
the metadata-related state as well as for the field injection methods
for tests.

Lastly, this commit increases the test coverage for the provided
annotations and adds integration tests and fixes a few `@TestBean`
issues.
2024-04-10 18:16:43 +02:00
Sébastien Deleuze 711ddd1ac6 Merge branch '6.1.x' 2024-04-10 17:55:09 +02:00
Brian Clozel 35a0d24dc3 Merge branch '6.1.x' 2024-04-10 16:44:20 +02:00
Sam Brannen ae3dc0d4b1 Merge SpEL IndexAccessor feature into main
This set of commits introduces a new IndexAccessor SPI for the Spring
Expression Language (SpEL) which allows third parties to customize the
SpEL Indexer.

A custom IndexAccessor implementation can be registered in a
StandardEvaluationContext.

For an example, see the JacksonArrayNodeIndexAccessor in IndexingTests
in the spring-expression module.

See gh-26409
Closes gh-26478
2024-04-10 16:07:33 +02:00
Sam Brannen 1c9cff668c Honor IndexAccessor#canWrite() and cache accessors
See gh-26409
See gh-26478
2024-04-10 15:54:34 +02:00
Sam Brannen 15511890bd Revise Javadoc for IndexAccessor
See gh-26409
See gh-26478
2024-04-10 15:54:34 +02:00
Sam Brannen 22bfe7da5a Introduce proper error handling for IndexAccessor support in SpEL
See gh-26409
See gh-26478
2024-04-10 15:54:34 +02:00
Sam Brannen 5c6b82a947 Change IndexAccessor#read()'s return type to TypedValue
Prior to this commit, the read() method in the IndexAccessor SPI
declared a return type of ValueRef which introduced a package cycle.

This commit addresses this by aligning with the PropertyAccess SPI and
returning TypedValue from IndexAccessor's read() method. This commit
also reworks the internals of Indexer based on a new, local
IndexAccessorValueRef implementation.

See gh-26409
See gh-26478
2024-04-10 15:54:34 +02:00
Sam Brannen b7c3833732 Revise null-safety contracts in IndexAccessor SPI
When indexing into an object, the target object can never be null.

See gh-26409
See gh-26478
2024-04-10 15:54:34 +02:00
Sam Brannen d91277095a Initial review and polish of IndexAccessor support in SpEL
See gh-26409
See gh-26478
2024-04-10 15:54:34 +02:00
ljmn3211 50a0000ed7 Introduce IndexAccessor SPI to customize the SpEL Indexer
See gh-26409
See gh-26478
2024-04-10 15:14:50 +02:00
Sébastien Deleuze d6e9562aeb Revert "Prevent compilation warnings with @Nullable"
This reverts commit dc242df533.
2024-04-10 14:39:47 +02:00
Juergen Hoeller f6089afd0e Use ClassLoaderAwareGeneratorStrategy with UndeclaredThrowableStrategy delegate
See gh-32469
2024-04-10 13:23:24 +02:00
Mikaël Francoeur 561583842f Consistent handling of undeclared checked exceptions in CGLIB proxies (#32469)
Co-authored-by: hengyunabc <hengyunabc@gmail.com>
Co-authored-by: Mikaël Francoeur <mikael.francoeur@ticketmaster.com>
2024-04-10 12:11:33 +02:00
Sam Brannen bed3689001 Polishing 2024-04-10 10:56:41 +02:00
Stéphane Nicoll 3d85ec2354 Merge pull request #32610 from alidandach
* pr/32610:
  Polish "Use try-with-resources when applicable"
  Use try-with-resources when applicable

Closes gh-32610
2024-04-10 09:52:25 +02:00
Stéphane Nicoll 330dcf15bc Polish "Use try-with-resources when applicable"
See gh-32610
2024-04-10 09:49:35 +02:00
ali dandach 82b51479d5 Use try-with-resources when applicable
See gh-32610
2024-04-10 09:49:17 +02:00
Stéphane Nicoll 2d408685ce Merge branch '6.1.x' 2024-04-09 19:53:43 +02:00
Stéphane Nicoll cc7815145f Upgrade to Reactor 2024.0.0-M1
Closes gh-32591
2024-04-09 19:51:42 +02:00
rstoyanchev a1586ff0a0 Merge branch '6.1.x' 2024-04-09 18:25:11 +01:00
Sam Brannen 0637b699cd Merge branch '6.1.x' 2024-04-09 19:01:38 +02:00
Brian Clozel c3aa0cd815 Merge branch '6.1.x' 2024-04-09 18:36:53 +02:00
Juergen Hoeller a9fffa844f Merge branch '6.1.x' 2024-04-09 16:15:19 +02:00
Sébastien Deleuze bb04c6b1f3 Merge branch '6.1.x' 2024-04-09 15:22:58 +02:00
Stéphane Nicoll cb8b7a455a Start building against Reactor 2024.0.0-M1 snapshots
See gh-32591
2024-04-09 13:54:10 +02:00
Stéphane Nicoll 9dbb269bb9 Merge branch '6.1.x' 2024-04-09 13:40:03 +02:00
Stéphane Nicoll f3aa61c5bb Upgrade to Micrometer 1.13.0-RC1
Closes gh-32595
2024-04-09 13:39:49 +02:00
Sébastien Deleuze dc242df533 Prevent compilation warnings with @Nullable
This commit replaces `@Nonnull(when = When.MAYBE)` meta-annotation in
`org.springframework.lang.Nullable` by `@CheckForNull` in order to
prevent `unknown enum constant When.MAYBE` compilation warnings.

Closes gh-27183
2024-04-09 13:35:49 +02:00
Sébastien Deleuze c05b4bcd71 Add missing dependency and remove warnings
See gh-22171
2024-04-09 12:50:36 +02:00
Sébastien Deleuze 246f291f67 Use code includes and tabs in WebSocket documentation
See gh-22171
2024-04-09 12:21:36 +02:00
Juergen Hoeller 4eb93da31d Merge branch '6.1.x'
# Conflicts:
#	spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.java
2024-04-08 22:41:02 +02:00
Sébastien Deleuze cabd1f53df Polishing 2024-04-08 18:03:19 +02:00
Sébastien Deleuze a471aa6a9a Use code includes and tabs in Regexp Pointcuts documentation
See gh-22171
2024-04-08 18:03:10 +02:00
Sébastien Deleuze 515295e205 Use code includes and tabs in AOP documentation
See gh-22171
2024-04-08 16:54:45 +02:00
Sam Brannen 1455a9fe22 Polishing 2024-04-08 16:37:47 +02:00
Stéphane Nicoll 36636b000b Consistent newline at the end of files
See gh-22171
2024-04-08 13:34:44 +02:00
Sébastien Deleuze 94050b3638 Use code includes and tabs in SpEL documentation
See gh-22171
2024-04-08 11:53:56 +02:00
Stéphane Nicoll 62db2680f5 Merge branch '6.1.x' 2024-04-08 11:11:15 +02:00
Sébastien Deleuze 5c8043dcea Use code includes and tabs in date/time format documentation
See gh-22171
2024-04-08 10:32:16 +02:00
Juergen Hoeller 13df59cef4 Merge branch '6.1.x' 2024-04-08 09:52:42 +02:00
Sébastien Deleuze 78f08ed1dd Upgrade to webjars-locator-lite 0.0.4
See gh-27619
2024-04-08 09:33:35 +02:00
Juergen Hoeller 03dd8e20e2 Merge branch '6.1.x' 2024-04-05 18:05:37 +02:00
Arjen Poutsma dcab1f2237 Merge branch '6.1.x' 2024-04-05 16:13:58 +02:00
Stéphane Nicoll 3e93ca02cc Merge branch '6.1.x' 2024-04-05 15:58:08 +02:00
Stéphane Nicoll 583dc04986 Merge branch '6.1.x' 2024-04-05 15:57:39 +02:00
Stéphane Nicoll 749d7671a0 Stop linking to Websphere's Javadoc
Closes gh-32581
2024-04-05 15:54:41 +02:00
Arjen Poutsma 08bc959c9b Merge branch '6.1.x' 2024-04-05 15:43:49 +02:00
Sébastien Deleuze 4e6532f639 Merge branch '6.1.x' 2024-04-05 15:00:48 +02:00
Sébastien Deleuze 0c42874629 Merge branch '6.1.x' 2024-04-05 14:33:20 +02:00
Stéphane Nicoll 5253af1a04 Merge branch '6.1.x' 2024-04-04 14:43:38 +02:00
Stéphane Nicoll e3281a527c Merge branch '6.1.x' 2024-04-02 18:01:59 +02:00
Stéphane Nicoll a96f22d5e2 Merge branch '6.1.x' 2024-04-02 15:48:04 +02:00
Stéphane Nicoll 7a74e45946 Make use of bean definition overriding more visible
This commit makes the use of bean definition overriding more visible and
prepare for a deprecation of the feature in the next major release.

As of this commit, use of bean definition overriding logs at INFO level.
The previous log level can be restored by setting the
allowBeanDefinitionOverriding flag explicitly on the BeanFactory (or
via the related ApplicationContext).

A number of tests that are using bean overriding on purpose have been
updated to set this flag, which will make them easier to find once we
actually deprecate the feature.

Closes gh-31288
2024-04-02 14:05:12 +02:00
Sébastien Deleuze 8af3eb1aea Polishing
See gh-22171
2024-04-02 13:33:29 +02:00
Sébastien Deleuze d26265420a Avoid removal warnings in tests
See gh-27619
2024-04-02 13:19:34 +02:00
Sébastien Deleuze bede619e7d Refine @EnableWebFlux documentation
This commit refines the documentation related to `@EnableWebFlux` in
order to make it more relevant for modern Boot applications.

See gh-22171
2024-04-02 11:48:12 +02:00
Sébastien Deleuze bf2a72b043 Upgrade webjars-locator-lite to 0.0.3
See gh-27619
2024-04-02 10:22:31 +02:00
Sébastien Deleuze 67edcde0a2 Introduce support for webjars-locator-lite
This commit introduces support for org.webjars:webjars-locator-lite
via a new LiteWebJarsResourceResolver in Spring MVC and WebFlux, and
deprecates WebJarsResourceResolver which is performing a classpath
scanning that slows down application startup.

Closes gh-27619
2024-04-02 10:16:51 +02:00
Stéphane Nicoll 81bc586e15 Merge branch '6.1.x' 2024-04-02 09:34:39 +02:00
Stéphane Nicoll 6f95af978e Merge branch '6.1.x' 2024-04-01 12:14:56 +02:00
Sam Brannen d360def92e Polishing 2024-03-30 16:06:06 +01:00
Łukasz Jernaś 86b87d7d85 Prevent package summaries from being truncated due to incorrect sentence detection
Javadoc doesn't seem to like having `e.g.` as it thinks the sentence
ends there, which is usually incorrect and results in broken descriptions.

This commit rewords the doc strings slightly to avoid problematic parts.

Closes gh-32532
2024-03-30 15:44:41 +01:00
Sébastien Deleuze 186e70cd04 Use code includes and tabs in MVC Config documentation
Additional fixes.

See gh-22171
2024-03-29 18:24:50 +01:00
Sébastien Deleuze 91bb7d8daf Use code includes and tabs in MVC Config documentation
This commit also refines the documentation related to
`@EnableWebMvc` in order to make it more relevant for modern Boot
applications.

See gh-22171
2024-03-29 17:58:01 +01:00
Sébastien Deleuze db1010f9c9 Add CBOR support to AllEncompassingFormHttpMessageConverter
Closes gh-32428
2024-03-28 14:59:22 +01:00
Sébastien Deleuze cf87441a26 Remove unnecessary method.isAccessible() invocation
Closes gh-32548
2024-03-28 11:50:29 +01:00
Sébastien Deleuze dc982d0d51 Merge branch '6.1.x' 2024-03-28 11:14:42 +01:00
Sébastien Deleuze 996e66abdb Perform NullAway build-time checks in spring-test
Closes gh-32475
2024-03-26 18:14:56 +01:00
Sébastien Deleuze 96d9081190 Merge branch '6.1.x' 2024-03-26 18:10:25 +01:00
Stéphane Nicoll bf5b22d64f Merge branch '6.1.x' 2024-03-26 17:45:25 +01:00
Sébastien Deleuze 8b51b36729 Perform NullAway build-time checks in more modules
This commit enables null-safety build-time checks in
all remaining modules except spring-test.

See gh-32475
2024-03-26 15:53:01 +01:00
Sébastien Deleuze 2fea3d7921 Merge branch '6.1.x' 2024-03-26 15:41:46 +01:00
Sam Brannen f47352ff04 Polish PropertyOrFieldReference 2024-03-26 13:21:46 +01:00
Stéphane Nicoll c1ed504ac1 Avoid classpath scanning in test
This commit updates SpringConfiguratorTests to not rely on classpath
scanning as it could have side effect. In this particular case, the
configuration class that sources the scan is detected again, leading
to bean overriding.

Irrespective of that, adding more code in that package may have side
effect as they could be scanned as well.

Closes gh-32535
2024-03-26 10:41:00 +01:00
Sébastien Deleuze 5b660da52d Perform NullAway build-time checks in more modules
This commit enables null-safety build-time checks in:
 - spring-jdbc
 - spring-r2dbc
 - spring-orm
 - spring-beans
 - spring-aop

See gh-32475
2024-03-26 09:59:30 +01:00
Sébastien Deleuze 3b4f8dbb8e Merge branch '6.1.x' 2024-03-26 09:47:09 +01:00
Sam Brannen 4b0a048570 Polish SpEL internals and remove duplicate code 2024-03-25 17:47:14 +01:00
Sam Brannen 57632f9f08 Fix wording in SpEL's PropertyAccessor Javadoc
The documentation now properly refers to "property accessors" instead
of "resolvers".
2024-03-25 14:26:40 +01:00
Sébastien Deleuze 7c009ccc1f Perform NullAway build-time checks in spring-messaging
See gh-32475
2024-03-25 12:30:03 +01:00
Sébastien Deleuze f421f38e24 Merge branch '6.1.x' 2024-03-25 12:28:53 +01:00
Sébastien Deleuze 88e26a9570 Merge branch '6.1.x' 2024-03-25 11:28:43 +01:00
Sébastien Deleuze 2fc78dfb69 Perform NullAway build-time checks in spring-jms
See gh-32475
2024-03-25 11:27:04 +01:00
Sébastien Deleuze e52ee01ec8 Perform NullAway build-time checks in spring-web
Also in spring-websocket.

See gh-32475
2024-03-25 11:11:09 +01:00
Sébastien Deleuze 2b1eb488fc Merge branch '6.1.x' 2024-03-25 11:04:49 +01:00
Sam Brannen 5bec072dcb Polish SpEL internals 2024-03-24 17:07:42 +01:00
Stéphane Nicoll 8736ca038a Merge branch '6.1.x' 2024-03-24 09:23:10 +01:00
Stéphane Nicoll aa9f0b3cb2 Merge pull request #32524 from izeye
* pr/32524:
  Fix Javadoc since to ChannelRegistration.executor()

Closes gh-32524
2024-03-24 08:29:45 +01:00
Johnny Lim 56b14dc54a Fix Javadoc since to ChannelRegistration.executor()
See gh-32524
2024-03-24 08:29:22 +01:00
Sam Brannen 2a74fe5ddb Introduce null-safe index operator in SpEL
This set of commits introduces support for a null-safe operator in the
Spring Expression Language (SpEL), including support for compiling
expressions that use the null-safe index operator.

Note, however, that compilation is not supported for null-safe indexing
into a String or any kind of Collection (other than a List).

See gh-21468
Closes gh-29847
2024-03-23 15:03:51 +01:00
Sam Brannen 218a148898 Document null-safe index operator in SpEL
See gh-29847
2024-03-23 14:56:01 +01:00
Sam Brannen 38c473fd05 Support compilation of null-safe index operations in SpEL
See gh-29847
2024-03-23 13:48:20 +01:00
Sam Brannen d2bd0d5716 Retain null-safe syntax in AST representation of SpEL indexers
Prior to this commit, SpEL's CompoundExpression omitted the null-safe
syntax in AST string representations of indexing operations.

To address this, this commit implements isNullSafe() in Indexer.

See gh-29847
2024-03-23 12:46:03 +01:00
Sam Brannen 4d433174eb Revise null-safe index operator support in SpEL
See gh-29847
2024-03-23 12:46:03 +01:00
Grigory Stepanov 9f4d46fe33 Introduce null-safe index operator in SpEL
See gh-29847
2024-03-23 12:38:10 +01:00
Sam Brannen 2a1abb5553 Simplify compilation of array indexing in SpEL's Indexer 2024-03-23 12:35:25 +01:00
Sam Brannen 7edd4e8e22 Clean up warnings in Gradle build 2024-03-23 09:28:49 +01:00
Felix 05b15812bb Apply instanceof pattern matching in RootBeanDefinition
Closes gh-32520
2024-03-23 09:09:25 +01:00
Sébastien Deleuze 0e7aba4179 Perform NullAway build-time checks in spring-webmvc
See gh-32475
2024-03-22 17:49:03 +01:00
Sébastien Deleuze cf8810042a Merge branch '6.1.x' 2024-03-22 17:46:35 +01:00
Sam Brannen 88bc3f270e Merge branch '6.1.x' 2024-03-22 17:18:38 +01:00
Sam Brannen b695dbc2bf Merge branch '6.1.x' 2024-03-22 16:37:55 +01:00
Stéphane Nicoll cf4ac96688 Merge branch '6.1.x' 2024-03-22 16:33:17 +01:00
Sébastien Deleuze 0a50854e1a Perform NullAway build-time checks in spring-webflux
See gh-32475
2024-03-22 16:09:18 +01:00
Sébastien Deleuze 637aa9f71f Merge branch '6.1.x' 2024-03-22 16:06:25 +01:00
Sébastien Deleuze b1b9ee0ccf Merge branch '6.1.x' 2024-03-21 17:56:14 +01:00
Sam Brannen a30c06b883 Polishing and consistent use of exception assertions 2024-03-21 16:36:14 +01:00
Andrea Mauro 458c30cb63 Resolve property-dependent parameter names for exception messages
Prior to this commit when a required parameter defined as a property or
expression placeholder was missing, the exception thrown would refer to
the placeholder instead of the resolved name.

This change covers messaging handlers and web controllers, both blocking
and reactive. It also fixes the error message when handling null values
for non-required parameters, as well as in cases that need conversion.

See gh-32323
Closes gh-32462
2024-03-20 17:40:38 +01:00
Sam Brannen 5698191ba0 Rename listener to CommonCachesTestExecutionListener
See gh-30954
2024-03-20 16:37:22 +01:00
Sam Brannen 7d197972e0 Polishing 2024-03-20 13:05:30 +01:00
Sam Brannen 1732844137 Polishing 2024-03-20 12:55:35 +01:00
Sam Brannen 7f40b49f4d Improve names of classes generated by the SpEL compiler
Prior to this commit, the SpEL compiler generated classes in a package
named "spel" with names following the pattern "Ex#", where # was an
index starting with 2.

This resulted in class names such as:

- spel.Ex2
- spel.Ex3

This commit improves the names of classes created by the SpEL compiler
by generating classes in a package named
"org.springframework.expression.spel.generated" with names following
the pattern "CompiledExpression#####", where ##### is a 0-padded
counter starting with 00001.

This results in class names such as:

- org.springframework.expression.spel.generated.CompiledExpression00001
- org.springframework.expression.spel.generated.CompiledExpression00002

This commit also moves the saveGeneratedClassFile() method from
SpelCompilationCoverageTests to SpelCompiler and enhances it to:

- Save classes in a "build/generated-classes" directory.
- Convert package names to directories.
- Create missing parent directories.
- Use logging instead of System.out.println().

Running a test with saveGeneratedClassFile() enabled now logs something
similar to the following.

DEBUG o.s.e.s.s.SpelCompiler - Saving compiled SpEL expression [(#root.empty ? 0 : #root.size)] to [/Users/<username>/spring-framework/spring-expression/build/generated-classes/org/springframework/expression/spel/generated/CompiledExpression00001.class]

Closes gh-32497
2024-03-20 12:34:01 +01:00
Stéphane Nicoll 2f070e59e6 Merge branch '6.1.x' 2024-03-20 10:16:26 +01:00
Sébastien Deleuze f648fd7c3b Perform NullAway build-time checks in spring-expression
See gh-32475
2024-03-20 10:10:00 +01:00
Sébastien Deleuze 1ccd5512c5 Merge branch '6.1.x' 2024-03-20 10:08:30 +01:00
Stéphane Nicoll 5a86ab99df Merge branch '6.1.x' 2024-03-20 10:01:56 +01:00
Stéphane Nicoll 29d307e211 Clear ApplicationContext cache after a test class completes if necessary
The ApplicationContext has a resource cache that it uses while the
context is being refreshed. Once the refresh phase completes, the
cache is cleared as its content is no longer in use. If beans are
lazily initialized, there is a case where the cache might be filled
again as processing is deferred past the refresh phase.

For those cases, the cache is not cleared. This can be a problem when
running in this mode with a large set of test configurations as the TCF
caches the related contexts.

This commit includes an additional TestExecutionListener to the TCF that
clears the resource cache if necessary once a test class completes. It
is ordered so that it runs after `@DirtiesContext` support as marking
the context as dirty will remove it from the cache and make that extra
step unnecessary.

Closes gh-30954
2024-03-20 09:42:41 +01:00
Sébastien Deleuze 4c7735016b Refine null-safety with NullAway build-time checks
This commit introduces null-safety checks for spring-core at build-time
in order to validate the consistency of Spring null-safety annotations
and generate errors when inconsistencies are detected during a build
(similar to what is done with Checkstyle).

In order to make that possible, this commit also introduces a new
org.springframework.lang.Contract annotation inspired from
org.jetbrains.annotations.Contract, which allows to specify semantics
of methods like Assert#notNull in order to prevent artificial
additional null checks in Spring Framework code base.

This commit only checks org.springframework.core package, follow-up
commits will also extend the analysis to other modules, after related
null-safety refinements.

See gh-32475
2024-03-19 16:30:32 +01:00
Sam Brannen 0a715bcab3 Merge branch '6.1.x' 2024-03-19 15:52:12 +01:00
Sébastien Deleuze 8d23ec4747 Merge branch '6.1.x' 2024-03-19 13:36:39 +01:00
Juergen Hoeller 14bc0d6469 Fix typo in condition check
See gh-21190
2024-03-19 13:17:33 +01:00
Sam Brannen 3bd342ee7d Polish bean override support in the TestContext framework 2024-03-19 12:41:06 +01:00
Sébastien Deleuze 871860798e Merge branch '6.1.x' 2024-03-19 11:05:22 +01:00
Juergen Hoeller e58ea0d945 Merge branch '6.1.x'
# Conflicts:
#	spring-core/src/main/java/org/springframework/core/CoroutinesUtils.java
2024-03-19 10:06:48 +01:00
Sam Brannen 8f56b4a8ae Fix logic error hidden by auto-boxing 2024-03-18 18:19:34 +01:00
Sam Brannen 2ca10e20ca Simplify implementation of BeanOverrideParser
See gh-29917
2024-03-18 18:13:36 +01:00
Sam Brannen 0ef5a4090b Support non-public BeanOverrideProcessors in the TestContext framework
Closes gh-32485
2024-03-18 18:02:51 +01:00
Juergen Hoeller 6df27644eb Merge branch '6.1.x' 2024-03-18 16:04:26 +01:00
Juergen Hoeller 2f2c4188e5 Nullability refinements
See gh-32475
2024-03-18 15:49:20 +01:00
Sam Brannen ccebaa6b0c Merge branch '6.1.x' 2024-03-18 15:18:06 +01:00
Brian Clozel ae5cff1705 Merge branch '6.1.x' 2024-03-18 14:37:21 +01:00
Stéphane Nicoll 3e102e5d8e Merge branch '6.1.x' 2024-03-18 14:11:02 +01:00
Brian Clozel 9cd4d32195 Merge branch '6.1.x' 2024-03-18 12:18:29 +01:00
Stéphane Nicoll 73a1c2d509 Polish 2024-03-18 08:59:22 +01:00
Juergen Hoeller 2b56ca08d4 Restore canonical name representation for 6.2
See gh-32405
2024-03-17 21:03:02 +01:00
Juergen Hoeller b89cf2b16a Merge branch '6.1.x' 2024-03-17 21:00:00 +01:00
Johnny Lim 99e1b0d117 Make SimpleAliasRegistry.aliasNames final 2024-03-17 13:33:31 +01:00
Juergen Hoeller 89d563097f Merge branch '6.1.x'
# Conflicts:
#	spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java
2024-03-16 23:24:51 +01:00
Brian Clozel 10e8039de8 Merge branch '6.1.x' 2024-03-15 22:48:12 +01:00
Juergen Hoeller 2086cda296 Merge branch '6.1.x' 2024-03-15 21:30:55 +01:00
Juergen Hoeller 66235fa8c8 Consistent TaskDecorator and ErrorHandler support in schedulers
Closes gh-23755
Closes gh-32460
2024-03-15 21:07:59 +01:00
Sam Brannen cf31d088e2 Polish AssertJ support for MockMvc
See gh-21178
2024-03-15 17:15:32 +01:00
Stéphane Nicoll 4a74e1fc2d Add AssertJ support for MockMvc
Closes gh-21178
2024-03-15 13:32:49 +01:00
Stéphane Nicoll e7d7cb8641 Add AssertJ support for MockMvc
This commit adds a new way to use MockMvc by returning a MvcResult that
is AssertJ compatible. Compared to the existing MockMvc infrastructure,
this new model has the following advantages:

* Can be created from a MockMvc instance
* Dedicated builder methods for easier setup
* Assertions use familiar AssertJ syntax with discoverable assertions
through a DSL
* Improved exception message

See gh-21178

Co-authored-by: Brian Clozel <brian.clozel@broadcom.com>
2024-03-15 13:30:32 +01:00
Stéphane Nicoll 555d4a6004 Add AssertJ support for cookies
This commit adds AssertJ compatible assertions for cookies

See gh-21178

Co-authored-by: Brian Clozel <brian.clozel@broadcom.com>
2024-03-15 13:28:12 +01:00
Stéphane Nicoll 1cdbcc58f3 Add AssertJ support for the Model
This commit adds AssertJ compatible assertions for the Model that is
generated from an HTTP request.

See gh-21178
2024-03-15 13:28:12 +01:00
Stéphane Nicoll b46e528922 Add AssertJ support for the HTTP handler
This commit adds AssertJ compatible assertions for the component that
produces the result from the request.

See gh-21178
2024-03-15 13:28:12 +01:00
Stéphane Nicoll e97ae434fb Add AssertJ support for HTTP requests and responses
This commit adds AssertJ compatible assertions for HTTP request and
responses, including headers, media type, and response body. The latter
delegate to the recently included JSON assert support.

See gh-21178
2024-03-15 13:28:11 +01:00
Stéphane Nicoll 76f45c4289 Add support for JSON assertions using JSON path
This commit moves JSON path AssertJ support from Spring Boot.

See gh-21178

Co-authored-by: Brian Clozel <brian.clozel@broadcom.com>
2024-03-15 13:28:11 +01:00
Stéphane Nicoll 97ebc43ea9 Add support for JSON assertions using JSON compare
This commit moves JSON content AssertJ support from Spring Boot.

See gh-21178

Co-authored-by: Brian Clozel <brian.clozel@broadcom.com>
2024-03-15 13:28:11 +01:00
Stéphane Nicoll 214a54dd6f Run CI on main only once a day 2024-03-15 08:40:09 +01:00
Stéphane Nicoll 875037c431 Merge branch '6.1.x' 2024-03-15 08:28:24 +01:00
Stéphane Nicoll 35e8ae2a84 Merge branch '6.1.x' 2024-03-14 17:47:51 +01:00
Stéphane Nicoll 26cab0616e Merge branch '6.1.x' 2024-03-14 17:43:32 +01:00
Sam Brannen e1b1435a00 Stop referring to JDK 1.5 plus polishing 2024-03-14 16:00:51 +01:00
Stéphane Nicoll 8e2134c6a7 Merge branch '6.1.x' 2024-03-14 15:29:18 +01:00
Stéphane Nicoll 7c7a59bdd2 Merge branch '6.1.x' 2024-03-14 15:17:47 +01:00
Stéphane Nicoll acd69d38e9 Merge branch '6.1.x' 2024-03-14 15:05:46 +01:00
Sam Brannen 4a3daa7812 Polishing 2024-03-14 14:20:59 +01:00
Sam Brannen 27d5213176 Polish Javadoc for @⁠TestBean support 2024-03-14 13:44:27 +01:00
Stéphane Nicoll 19dfc781f5 Build Pull Requests on CI using GitHub Actions
Closes gh-32443
2024-03-14 11:10:41 +01:00
rstoyanchev 1d2b55e670 Merge branch '6.1.x' 2024-03-14 08:46:46 +00:00
Juergen Hoeller 7f7c0d59f6 Merge branch '6.1.x' 2024-03-13 18:07:21 +01:00
Stéphane Nicoll 7d4db434a5 Merge branch '6.1.x' 2024-03-13 17:28:23 +01:00
Sam Brannen 986c4fd926 Revise use of Objects.requireNonNull()
Historically, we have rarely intentionally thrown a
NullPointerException in the Spring Framework. Instead, we prefer to
throw either an IllegalArgumentException or IllegalStateException
instead of a NullPointerException.

However, changes to the code in recent times have introduced the use of
Objects.requireNonNull(Object) which throws a NullPointerException
without an explicit error message.

The latter ends up providing less context than a NullPointerException
thrown by the JVM (since Java 14) due to actually de-referencing a
null-pointer. See https://openjdk.org/jeps/358.

In light of that, this commit revises our current use of
Objects.requireNonNull(Object) by removing it or replacing it with
Assert.notNull().

However, we still use Objects.requireNonNull(T, String) in a few places
where we are required to throw a NullPointerException in order to
comply with a third-party contract such as Reactive Streams.

Closes gh-32430
2024-03-13 16:00:22 +01:00
Sam Brannen d6422d368a Revise @⁠TestBean support
See gh-29917
2024-03-13 16:00:22 +01:00
Sam Brannen 21ed8aad74 Add missing test 2024-03-13 15:59:28 +01:00
Sam Brannen 7211db9262 Polishing 2024-03-13 15:59:28 +01:00
Juergen Hoeller 3b87c87a33 Merge branch '6.1.x' 2024-03-12 20:11:21 +01:00
Sébastien Deleuze 460ffbc0f6 Use code includes and tabs in mvc-controller/ann.adoc
See gh-22171
2024-03-12 19:18:42 +01:00
Sébastien Deleuze 8a67018e61 Use code includes and tabs in embedded-database-support.adoc
See gh-22171
2024-03-12 17:46:38 +01:00
Sébastien Deleuze 68ae1e5700 Fix a link in MockitoResetTestExecutionListener javadoc 2024-03-12 17:43:10 +01:00
Brian Clozel 4b732d62c2 Deprecate HttpHeaders.writableHttpHeaders
Prior to this commit, gh-21783 introduced `ReadOnlyHttpHeaders` to avoid
parsing media types multiple times during the lifetime of an HTTP
exchange: such values are cached and the headers map is made read-only.
This also added a new `HttpHeaders.writableHttpHeaders` method to unwrap
the read-only variant when needed.

It turns out this method sends the wrong signal to the community
because:
* the underlying map might be unmodifiable even if this is not an
  instance of ReadOnlyHttpHeaders
* developers were assuming that modifying the collection that backs the
  read-only instance would work around the cached values for
  Content-Type and Accept headers

This commit adds more documentation to highlight the desired behavior
for cached values by the read-only variant, and deprecates the
`writableHttpHeaders` method as `ReadOnlyHttpHeaders` is package private
and we should not surface that concept anyway.
Instead, this commit unwraps the read-only variant if needed when a new
HttpHeaders instance is created.

Closes gh-32116
2024-03-12 14:04:09 +01:00
rstoyanchev 670fc9bb4e Merge branch '6.1.x' 2024-03-12 11:43:43 +00:00
Sébastien Deleuze 7a65aad09c Merge branch '6.1.x' 2024-03-12 12:24:30 +01:00
Sébastien Deleuze b1bf8c5242 Prevent NPE when using pathExtension predicate
This commit ensures pathExtension predicate is skipped when the value
is null, in order to provide a more predictable behavior, and allow
a better compatibility with collections not supporting null elements
like the ones created by List#of.

Closes gh-32404
2024-03-12 12:11:50 +01:00
rstoyanchev 6767f7010c Polishing contribution
Closes gh-29958
2024-03-12 10:43:32 +00:00
rstoyanchev 8af1d8e842 Remove applyAttributes flag from contribution
See gh-29958
2024-03-12 10:43:31 +00:00
PhilKes 052b6357c8 Apply attributes to the underlying HTTP request
See gh-29958
2024-03-12 10:43:31 +00:00
rstoyanchev 282ee02419 Polishing contribution
Closes gh-31924
2024-03-12 10:43:31 +00:00
Anton Ždanov 667e30f580 Set UTF-8 as default multipart charset to ContentRequestMatchers
See gh-31924
2024-03-12 10:43:31 +00:00
rstoyanchev f9883d8bd6 Polishing contribution
Closes gh-31970
2024-03-12 10:43:31 +00:00
rstoyanchev 73ee86c666 Split messages only if configured to do so
See gh-31970
2024-03-12 10:43:31 +00:00
injae-kim 76d00d78db Support splitting STOMP messages in WebSocketStompClient
See gh-31970
2024-03-12 10:43:31 +00:00
Brian Clozel bf014ef18b Merge branch '6.1.x' 2024-03-12 09:52:22 +01:00
Stéphane Nicoll 13679bb906 Reject use of component scan with REGISTER_BEAN condition
This commit introduce a change of behaviour when component scan is used
with conditions. Previously, any condition in the REGISTER_BEAN phase
were ignored and the scan was applied regardless of the outcome of
those conditions. This is because REGISTER_BEAN condition evaluation
happens later in the bean factory preparation.

Rather than ignoring those conditions, this commit fails fast when it
detects such use case. Code will have to be adapted accordingly.

Closes gh-23206
2024-03-12 08:39:53 +01:00
Stéphane Nicoll 6fc1f72704 Merge branch '6.1.x' 2024-03-12 08:21:08 +01:00
Sam Brannen 04e69bdb26 Polish contribution
Closes gh-32412
2024-03-11 17:04:53 +01:00
Mikaël Francoeur 3e48031601 Reject null return value from MethodReplacer for primitive return type
This commit throws an exception instead of silently converting a null
return value from a MethodReplacer to a primitive 0/false value.

See gh-32412
2024-03-11 17:04:53 +01:00
Sam Brannen f285971cb3 Polishing 2024-03-11 17:04:53 +01:00
Stéphane Nicoll 89f6e8ec49 Enable test now that the related issue is resolved
See gh-20765
2024-03-11 16:45:30 +01:00
Stéphane Nicoll 53ed15e4a7 Remove disabled test of declined issue
See gh-19689
2024-03-11 16:44:50 +01:00
Brian Clozel 9af4f5cf17 Remove deprecated API in WebContentGenerator
Closes gh-31492
2024-03-11 15:35:12 +01:00
Stéphane Nicoll 92d1ebefbb Upgrade to HtmUnit 3.11.0
This commit upgrades to a major new release of HtmlUnit. This is a
breaking change as HtmlUnit 3 moves to a `org.htmlunit` package and
calling code needs to be restructured.

Our use of Selenium has been adapted accordingly, moving to Selenium
3, using the new org.seleniumhq.selenium:htmlunit3-driver integration.

Closes gh-30392
2024-03-11 14:36:46 +01:00
Sam Brannen 71245f9655 Relocate bean override infrastructure to the proper package in the TCF 2024-03-11 14:21:04 +01:00
Sam Brannen f30a67c9d0 Remove duplicate dependency 2024-03-11 13:59:23 +01:00
Sébastien Deleuze 0435e9c49e Merge branch '6.1.x' 2024-03-11 13:40:23 +01:00
Sam Brannen 38c831f15f Relocate findPublicDeclaringClass() to CodeFlow
This commit moves findPublicDeclaringClass() from ReflectionHelper to
CodeFlow, since findPublicDeclaringClass() is only used for bytecode
generation and therefore not for reflection-based invocations.
2024-03-11 11:33:52 +01:00
Sam Brannen c4e0f96ef7 Polishing 2024-03-11 11:28:59 +01:00
Sébastien Deleuze 5ee11fb1b3 Polish Yaml support
Closes gh-32345
2024-03-11 09:27:25 +01:00
Hyoungjune 6a8f0d6d7d Add web support for Yaml via Jackson
This commit adds support for application/yaml in MediaType and leverages
jackson-dataformat-yaml in order to support Yaml in RestTemplate,
RestClient and Spring MVC.

See gh-32345
2024-03-11 09:22:51 +01:00
Yanming Zhou 246e4977a2 Polishing Optional usage 2024-03-11 09:13:49 +01:00
Stéphane Nicoll 4e5723ca9e Merge pull request #32411 from quaff
* pr/32411:
  Polish contribution
  Polish Bean Overriding in Tests section of the reference guide

Closes gh-32411
2024-03-11 08:57:57 +01:00
Stéphane Nicoll ffb1caaff4 Polish contribution
See gh-32411
2024-03-11 08:56:59 +01:00
Yanming Zhou 918c5f1f33 Polish Bean Overriding in Tests section of the reference guide
See gh-32411
2024-03-11 08:43:40 +01:00
Sam Brannen b431594021 Make SpEL's OptimalPropertyAccessor private
It was never necessary for SpEL's OptimalPropertyAccessor to be public.

Instead, clients should interact with OptimalPropertyAccessor
exclusively via the CompilablePropertyAccessor API.

In light of that, this commit makes SpEL's OptimalPropertyAccessor
private.

Closes gh-32410
2024-03-10 20:38:19 +01:00
Sam Brannen fc885224f0 Merge branch '6.1.x' 2024-03-10 17:41:49 +01:00
ZeroCyan 5dc1190930 Fix typo in web documentation
Fixed a small typo.

Closes gh-32407
2024-03-10 16:50:00 +01:00
Sam Brannen 6f5d3a4b12 Polish Bean Override support in the TestContext framework 2024-03-10 16:05:55 +01:00
Sam Brannen 4c246b7c96 Consistently use canonical annotation names in string representations 2024-03-10 15:54:53 +01:00
Sam Brannen 5345a13918 Polish contribution
See gh-32405
2024-03-10 12:36:08 +01:00
Patrick Strawderman 7fa2a28970 Avoid cloning empty Annotation array in TypeDescriptor
Rework AnnotatedElementAdapter to avoid cloning the underlying
Annotation array if it is empty when getAnnotations() is called.

Additionally, make the class static and add a factory method that
returns a singleton instance for null or empty Annotation arrays.

Closes gh-32405
2024-03-10 12:26:49 +01:00
Stéphane Nicoll 8172d7adfe Polish 2024-03-09 17:27:44 +01:00
Sam Brannen 6b64e9992a Merge branch '6.1.x' 2024-03-09 16:31:28 +01:00
Sam Brannen c188f22378 Polishing 2024-03-09 16:08:17 +01:00
Stéphane Nicoll e53ed3e3c6 Merge branch '6.1.x' 2024-03-09 16:03:04 +01:00
Sam Brannen f4c1ad7ae6 Polishing
See gh-29857
2024-03-09 15:38:52 +01:00
Sam Brannen 20be9e150c Polishing 2024-03-09 14:28:01 +01:00
Sam Brannen 65d77624d1 Support SpEL compilation for public methods in private subtypes
Commit c79436f832 ensured that methods are invoked via a public
interface or public superclass when compiling Spring Expression
Language (SpEL) expressions involving method references or property
access (see MethodReference, PropertyOrFieldReference, and
collaborating support classes). However, compilation of expressions
that access properties by indexing into an object by property name is
still not properly supported in all scenarios.

To address those remaining use cases, this commit ensures that methods
are invoked via a public interface or public superclass when accessing
a property by indexing into an object by the property name – for
example, `person['name']` instead of `person.name`.

In addition, SpEL's Indexer now properly relies on the
CompilablePropertyAccessor abstraction instead of hard-coding support
for only OptimalPropertyAccessor. This greatly reduces the complexity
of the Indexer and simultaneously allows the Indexer to potentially
support other CompilablePropertyAccessor implementations.

Closes gh-29857
2024-03-09 14:01:00 +01:00
Sam Brannen 107f47cfcf Add tests for status quo for SpEL compiler 2024-03-09 13:46:39 +01:00
Juergen Hoeller fad544e077 Merge branch '6.1.x'
# Conflicts:
#	spring-core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java
2024-03-08 19:51:05 +01:00
Juergen Hoeller 19fec0633f Local root directory and jar caching in PathMatchingResourcePatternResolver
Closes gh-21190
2024-03-08 19:12:14 +01:00
Simon Baslé 729dc0b671 Polishing javadoc 2024-03-08 18:07:51 +01:00
rstoyanchev 38d5c0fed6 Add RFC-7807 response interception
Closes gh-31822
2024-03-08 16:56:38 +00:00
Sam Brannen 3577e3b758 Polish SpEL internals and tests 2024-03-08 17:49:58 +01:00
Simon Baslé e1bbdf0913 Add support for bean overriding in tests
This commit introduces two sets of annotations (`@TestBean` on one side
and `MockitoBean`/`MockitoSpyBean` on the other side), as well as an
extension mecanism based on the `@BeanOverride` meta-annotation.

Extension implementors are expected to only provide an annotation,
a BeanOverrideProcessor implementation and an OverrideMetadata subclass.

Closes gh-29917.
2024-03-08 16:52:39 +01:00
Sam Brannen 90867e7e62 Merge branch '6.1.x' 2024-03-08 15:50:29 +01:00
Sébastien Deleuze 5cac2ca11a Fix code snippet compilation error
See gh-22171
2024-03-08 12:28:37 +01:00
Sébastien Deleuze db3c7a157e Use consistently snippet tags
See gh-22171
2024-03-08 12:15:51 +01:00
Sébastien Deleuze d2e55a2038 Use code includes and tabs in jdbc/core.adoc
See gh-22171
2024-03-08 12:15:51 +01:00
Sam Brannen ae6c64abc5 Fix Javadoc errors 2024-03-08 11:59:53 +01:00
Brian Clozel c1bd9243ee Merge branch '6.1.x' 2024-03-08 11:36:54 +01:00
Sébastien Deleuze e66d965fed Merge branch '6.1.x' 2024-03-08 09:25:17 +01:00
Sébastien Deleuze aa4282d7f8 Polishing
See gh-22171
2024-03-07 18:16:57 +01:00
Sébastien Deleuze 34f0c27b5e Fix build link 2024-03-07 17:42:49 +01:00
Sébastien Deleuze 722199fe6d Use code includes and tabs in connections.adoc
See gh-22171
2024-03-07 17:40:11 +01:00
Sébastien Deleuze 9cb8766977 Add support for Kotlin and XML code includes
See gh-22171
2024-03-07 17:37:48 +01:00
rstoyanchev 8d24e22e33 Merge branch '6.1.x' 2024-03-07 16:06:18 +00:00
Sam Brannen c79436f832 Support SpEL compilation for public methods in private subtypes
Although the Spring Expression Language (SpEL) generally does a good
job of locating the public declaring class or interface on which to
invoke a method in a compiled expression, prior to this commit there
were still a few unsupported use cases.

To address those remaining use cases, this commit ensures that methods
are invoked via a public interface or public superclass whenever
possible when compiling SpEL expressions.

See gh-29857
2024-03-07 16:20:50 +01:00
rstoyanchev 1ffffef85e Merge branch '6.1.x' 2024-03-07 14:48:41 +00:00
Sébastien Deleuze f0039833c8 Merge branch '6.1.x' 2024-03-07 14:31:26 +01:00
Sam Brannen ac1176af53 Use Map#computeIfAbsent in SpEL support classes
Closes gh-32385
2024-03-07 13:55:35 +01:00
Sam Brannen dcbc2ef134 Polishing 2024-03-07 13:55:35 +01:00
Juergen Hoeller 4ec4e93ece Document when the JPA infrastructure is ready for use
Closes gh-26153
See gh-21868
2024-03-07 13:35:58 +01:00
Simon Baslé 7f0ab22c47 Merge branch '6.1.x' 2024-03-07 12:10:07 +01:00
Sébastien Deleuze 92f4e883b3 Merge branch '6.1.x' 2024-03-07 11:11:46 +01:00
Juergen Hoeller dedc6a7e9b Enforce JPA/Hibernate initialization before context refresh completion
Closes gh-21868
2024-03-07 11:03:08 +01:00
Juergen Hoeller c9e85ec297 Introduce callback for singleton availability
Closes gh-21362
2024-03-07 09:57:29 +01:00
rstoyanchev 1a8d64f9d8 Merge branch '6.1.x' 2024-03-06 18:17:40 +00:00
Juergen Hoeller 13221ac0ef Merge branch '6.1.x'
# Conflicts:
#	spring-tx/src/test/java/org/springframework/transaction/annotation/EnableTransactionManagementTests.java
#	spring-tx/src/test/java/org/springframework/transaction/interceptor/TransactionInterceptorTests.java
2024-03-06 18:09:32 +01:00
Sam Brannen 40da093f58 Polishing 2024-03-06 15:42:14 +01:00
Sam Brannen b32a2cadfb Update class-level Javadoc for ParameterNameDiscoverer 2024-03-06 14:49:46 +01:00
Sam Brannen d87465f9e9 Use ELContext instead of VariableResolver in JspPropertyAccessor
The JSP VariableResolver API has been deprecated since JSP 2.1 in favor
of the newer ELContext API.

This commit therefore refactors JspPropertyAccessor to use the
ELContext API.

Closes gh-32383
2024-03-06 14:31:21 +01:00
Juergen Hoeller 14a461e795 Consider type-level qualifier annotations for transaction manager selection
Closes gh-24291
2024-03-06 13:36:33 +01:00
Sam Brannen 6461eec582 Revise contribution
Closes gh-32380
2024-03-06 11:49:21 +01:00
Yanming Zhou be136d79ee Improve Javadoc for NamedParameterUtils.substituteNamedParameters()
This commit documents that an empty list is not guaranteed to be
supported by the database. For example, MySQL and PostgreSQL do not
support `foo in ()`.

See gh-32380
2024-03-06 11:49:15 +01:00
Sam Brannen 9eea768205 Polish SpEL internals 2024-03-06 11:31:10 +01:00
Simon Baslé 19b5f11734 Merge branch '6.1.x' 2024-03-05 18:33:48 +01:00
Juergen Hoeller 6f7f032ced Merge branch '6.1.x' 2024-03-05 18:24:01 +01:00
Juergen Hoeller 7d4c8a403e Introduce configurable default rollback rules
Includes rollbackOn annotation attribute on @EnableTransactionManagement and addDefaultRollbackRule method on AnnotationTransactionAttributeSource, as well as publicMethodsOnly as instance-level flag (also on AnnotationCacheOperationSource).

Closes gh-23473
2024-03-05 18:08:08 +01:00
ali dandach eb01cc0d9d Use String#isEmpty where feasible
This commit replaces checks for empty strings ("".equals(...)) with the
String#isEmpty method.

Closes gh-32377
2024-03-05 17:38:29 +01:00
Juergen Hoeller 30e75e4a09 Enable efficient pointcut checks for composite sources as well
Closes gh-20072
2024-03-05 13:34:48 +01:00
Sam Brannen 1fa6ac30b5 Remove unused lastReadInvokerPair field in ReflectivePropertyAccessor 2024-03-05 12:55:30 +01:00
rstoyanchev ddab971fca Merge branch '6.1.x' 2024-03-05 11:42:17 +00:00
Sam Brannen b44c31e997 Polishing 2024-03-05 12:01:55 +01:00
Sam Brannen 1ea593e777 Convert PropertyCacheKey to a record 2024-03-05 12:01:55 +01:00
Sam Brannen 70a545e13a Use appropriate variable names in ReflectivePropertyAccessor 2024-03-05 11:52:30 +01:00
Sam Brannen c29c67839b Cache parameterTypes in ClassUtils.getInterfaceMethodIfPossible 2024-03-05 11:48:01 +01:00
Sébastien Deleuze f6dfbac582 Merge branch '6.1.x' 2024-03-05 11:01:29 +01:00
Arjen Poutsma 6d9aba88d0 Fix typo
See gh-32360
2024-03-05 10:48:06 +01:00
Sébastien Deleuze db826551a6 Upgrade to Dokka 1.9.20
Closes gh-32374
2024-03-05 10:31:29 +01:00
Arjen Poutsma 646bd7f893 Document StringUtils::uriDecode limitations
Closes gh-32360
2024-03-05 10:03:15 +01:00
ali dandach 1cb2dfa459 enh: use isEmpty() instead of length() 2024-03-04 23:55:33 +01:00
Juergen Hoeller 79c54098ab Merge branch '6.1.x' 2024-03-04 23:49:55 +01:00
Juergen Hoeller dc6c96de0a Merge branch '6.1.x'
# Conflicts:
#	spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/AbstractFallbackJCacheOperationSource.java
#	spring-context/src/main/java/org/springframework/cache/interceptor/AbstractFallbackCacheOperationSource.java
#	spring-tx/src/main/java/org/springframework/transaction/interceptor/AbstractFallbackTransactionAttributeSource.java
2024-03-04 22:50:22 +01:00
Juergen Hoeller b5ca646431 Leniently tolerate late bean retrieval during destroySingletons()
Closes gh-22526
Closes gh-29730
2024-03-04 17:21:02 +01:00
Sébastien Deleuze 138e7a0e07 Use ServletResponse#getContentType in ServletServerHttpResponse
This commit updates ServletServerHttpResponse.ServletResponseHttpHeaders
in order to use ServletResponse#getContentType instead of
ServletResponse#getHeader.

It allows to have a consistent behavior between Tomcat (which sets only
the former) and Undertow/Jetty (which set both).

Closes gh-32339
2024-03-04 15:32:32 +01:00
Sébastien Deleuze 4ce2559980 Merge branch '6.1.x' 2024-03-04 14:43:18 +01:00
rstoyanchev 1c5bb732b9 Merge branch '6.1.x' 2024-03-04 13:36:30 +00:00
Juergen Hoeller 3d7ef3ebfc Avoid storage of null marker per method for proxy decision purposes
Includes missing isCandidateClass support on JCacheOperationSource.

Closes gh-20072
2024-03-04 12:55:33 +01:00
Sébastien Deleuze 219004ef13 Merge branch '6.1.x' 2024-03-03 22:26:37 +01:00
rstoyanchev 0758c8b14c Merge branch '6.1.x' 2024-03-03 20:53:36 +00:00
Sam Brannen 4a6dbb17f4 Merge branch '6.1.x' 2024-03-03 14:51:01 +01:00
Sam Brannen a29c84f663 Merge branch '6.1.x' 2024-03-02 18:08:27 +01:00
Juergen Hoeller 390fe0fe78 Add support for resolving multiple bounds in type variables
Closes gh-22902
See gh-32327
2024-03-02 11:30:17 +01:00
Stéphane Nicoll ac1a030c35 Make PlaceholderResolutionException extend from IllegalArgumentException
To smooth upgrade from 6.1.x, this commit makes sure that code that used
to catch an IAE to ignore a faulty placeholder resolution still works.

See gh-9628
2024-03-02 08:28:38 +01:00
Sam Brannen fdbefad59c Improve documentation for SpEL indexing support
Prior to this commit, the reference manual only documented indexing
support for arrays, lists, and maps.

This commit improves the overall documentation for SpEL's property
navigation and indexing support and introduces additional documentation
for indexing into Strings and Objects.

Closes gh-32355
2024-03-01 18:08:29 +01:00
Sébastien Deleuze bdcd10e228 Merge branch '6.1.x' 2024-03-01 16:53:27 +01:00
Juergen Hoeller 861ef88d9f Expose savepoint callbacks on TransactionSynchronization
Closes gh-30509
2024-03-01 16:48:51 +01:00
Sam Brannen 193424c465 Polish "Background Initialization" documentation 2024-03-01 16:14:12 +01:00
Sam Brannen c76d681dc4 Merge branch '6.1.x' 2024-03-01 14:41:49 +01:00
Sébastien Deleuze 4149d5cb2b Merge branch '6.1.x' 2024-03-01 11:55:08 +01:00
Juergen Hoeller 89a10d5c9b Reference documentation for @Bean(bootstrap=BACKGROUND)
See gh-13410
2024-02-29 17:51:31 +01:00
Juergen Hoeller 4ac521607e Reference documentation for @Fallback
See gh-26241
2024-02-29 17:51:12 +01:00
Stéphane Nicoll 57c10a1fac Merge branch '6.1.x' 2024-02-29 17:23:08 +01:00
Arjen Poutsma d81619addd Merge branch '6.1.x' 2024-02-29 15:55:29 +01:00
Yanming Zhou 246ebd24bf Add missing method BeanDefinitionBuilder.setFallback() 2024-02-29 13:34:55 +01:00
Yanming Zhou 9a1e5eb8d7 Add test for @Fallback with BeanFactory.getBean(Class) 2024-02-29 13:33:43 +01:00
Juergen Hoeller 4ff0f86ae2 Merge branch '6.1.x' 2024-02-28 21:46:15 +01:00
Juergen Hoeller e68487a481 Merge branch '6.1.x' 2024-02-28 19:27:07 +01:00
Juergen Hoeller cfb29db278 Initial support for Servlet 6.1
Closes gh-31159
2024-02-28 18:54:36 +01:00
Sébastien Deleuze 570c5b34e6 Merge branch '6.1.x' 2024-02-28 18:35:29 +01:00
Juergen Hoeller fbc265b72b Add DataSource configuration/exposure to LocalEntityManagerFactoryBean
Closes gh-32344
2024-02-28 17:35:46 +01:00
Juergen Hoeller 5acee7b22e Initial support for JPA 3.2
Includes proxy support for Query#getSingleResultOrNull() and EntityManagerFactory#getName() invocations.

Closes gh-31157
2024-02-28 17:35:37 +01:00
Sébastien Deleuze 7c5bcbcba4 Merge branch '6.1.x' 2024-02-28 17:24:08 +01:00
Juergen Hoeller 3477738bed Consistently pick lowest superclass level to replace
See gh-28676
2024-02-28 15:49:05 +01:00
Juergen Hoeller fa5d246a1b Replace all exposed superclasses in final step after traversal
See gh-28676
2024-02-28 15:39:53 +01:00
Juergen Hoeller aeb77cf4e1 Restore correct threadWithLock check without isInfoEnabled()
See gh-23501
2024-02-28 11:47:39 +01:00
Sam Brannen 18dc7d73d6 Merge branch '6.1.x' 2024-02-28 10:42:31 +01:00
Juergen Hoeller 17b2087198 Introduce background bootstrapping for individual singleton beans
Closes gh-13410
Closes gh-19487
See gh-23501
2024-02-27 22:33:18 +01:00
Sébastien Deleuze 9e7c64270b Merge branch '6.1.x' 2024-02-27 15:52:13 +01:00
Sam Brannen 69dabe9df5 Merge branch '6.1.x' 2024-02-26 18:12:55 +01:00
Juergen Hoeller 4d206f79d0 Merge branch '6.1.x'
# Conflicts:
#	spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java
2024-02-26 13:42:00 +01:00
Juergen Hoeller 5c589833d7 Revise idleReceivesPerTaskLimit for surplus tasks on default/simple executor
Closes gh-32260
2024-02-25 19:00:56 +01:00
Juergen Hoeller 9198774f13 Detect wildcard without bounds returned from VariableResolver as well
See gh-32327
See gh-20727
2024-02-25 16:32:11 +01:00
Sam Brannen 430824038e Merge branch '6.1.x' 2024-02-25 16:28:13 +01:00
Juergen Hoeller 260404b7f2 Consistently detect wildcard without bounds as unresolvable
Closes gh-32327
See gh-20727
2024-02-24 20:25:56 +01:00
Juergen Hoeller 567547b63c Skip shortcut resolution for non-standard dependency descriptors
Closes gh-32326
See gh-28122
2024-02-24 18:05:23 +01:00
Sam Brannen 98efa1df5a Merge branch '6.1.x' 2024-02-24 16:42:47 +01:00
Juergen Hoeller 87ea3bd31a Merge branch '6.1.x' 2024-02-24 08:35:10 +01:00
Sam Brannen f59c4023e9 Polishing 2024-02-23 14:46:12 +01:00
Juergen Hoeller 58b8330e8d Consistent documentation of defaults and related methods
See gh-32308
2024-02-23 14:41:37 +01:00
Sam Brannen c2d2e99c2f Polishing 2024-02-23 13:18:09 +01:00
Sam Brannen eab1a3dc6b Fix BridgeMethodResolverTests.isBridgeMethodFor() in Eclipse IDE 2024-02-23 12:57:14 +01:00
Sam Brannen 4b5e96578d Extract runningInEclipse() into IdeUtils test fixture 2024-02-23 12:57:06 +01:00
Sam Brannen b0d08fe2d4 Spring cleaning: avoid deprecation warnings 2024-02-23 12:38:40 +01:00
Sam Brannen 122372c580 Spring cleaning: update copyright headers 2024-02-23 12:21:22 +01:00
Sam Brannen 9c610d9a70 Spring cleaning: remove unnecessary semicolon 2024-02-23 12:21:22 +01:00
Sam Brannen 5ae6c0e8ca Spring cleaning: avoid unnecessary super() invocations 2024-02-23 12:21:22 +01:00
Sam Brannen eb8492d597 Spring cleaning: avoid use of Iterator for Iterable 2024-02-23 12:21:12 +01:00
Sam Brannen c98bebd6d3 Spring cleaning: add missing @⁠Override annotations 2024-02-23 12:20:11 +01:00
Sam Brannen d0ffc16efc Spring cleaning: avoid unnecessary static imports 2024-02-23 12:20:11 +01:00
Sam Brannen 4bd1485ce4 Spring cleaning: use method references 2024-02-23 12:20:11 +01:00
Sam Brannen 4339c8eac2 Spring cleaning: use diamond operator 2024-02-23 12:20:11 +01:00
Sam Brannen 233b59f144 Polish Javadoc 2024-02-23 12:20:11 +01:00
Sam Brannen 3c00637c88 Fix @⁠inheritDoc usage 2024-02-23 12:08:29 +01:00
Juergen Hoeller 524588ef93 Avoid transaction listener execution without transaction management setup
Closes gh-32319
2024-02-23 11:53:05 +01:00
yuhangbin bed4d684e6 Polishing 2024-02-23 08:37:46 +01:00
Juergen Hoeller 229214a9b0 Merge branch '6.1.x'
# Conflicts:
#	spring-jdbc/src/main/java/org/springframework/jdbc/JdbcUpdateAffectedIncorrectNumberOfRowsException.java
2024-02-22 17:01:04 +01:00
Sébastien Deleuze 906109e129 Merge branch '6.1.x' 2024-02-22 16:37:58 +01:00
cboy 00edba07ff Polishing Javadoc (#32313) 2024-02-22 15:21:33 +01:00
Arjen Poutsma aee03c5201 Use composite collections in attribute merging
This commit introduces composite collections (i.e. Collection, Set, Map)
and uses these composites in request predicates, where before new
collections were instantiated.

Closes gh-32245
2024-02-22 13:11:20 +01:00
Arjen Poutsma 89d746ddf8 Avoid async dispatch if completed in AsyncServerResponse
This commit checks whether the CompletableFuture<ServerResponse> passed
to AsyncServerResponse.async has been completed, and if so returns a
CompletedAsyncServerResponse that simply delegates to the completed
response, instead of the DefaultAsyncServerResponse that uses async
dispatch.

Closes gh-32223
2024-02-22 11:29:51 +01:00
Juergen Hoeller 3ddc512108 Add missing @Override annotations 2024-02-22 11:21:51 +01:00
Yanming Zhou 76eb5b8c19 Replace redundant javadoc with {@inheritDoc} for AbstractBeanDefinition 2024-02-22 10:59:35 +01:00
Juergen Hoeller 491ebb4ec4 Merge branch '6.1.x'
# Conflicts:
#	spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClass.java
2024-02-21 23:01:09 +01:00
Juergen Hoeller f811f0dc18 Clarify primary/fallback autowiring for arrays/collections/maps
Closes gh-32301
2024-02-21 20:24:57 +01:00
Juergen Hoeller f5397d6426 Consistent processing of overridden bean methods
Closes gh-28286
2024-02-21 18:36:03 +01:00
Brian Clozel 2a1b30dbed Merge branch '6.1.x' 2024-02-21 18:24:45 +01:00
Sam Brannen 734fc476ee Polish SpEL's Indexer and test 2024-02-21 17:44:09 +01:00
Sam Brannen 5a2b127a21 SpEL's StringIndexingValueRef.isWritable() should return false 2024-02-21 17:36:46 +01:00
Sam Brannen bfed6a3bc5 Clean up warnings in Gradle build 2024-02-21 16:22:38 +01:00
Sam Brannen 644887e094 Polish (Linked)HashSet usage
See gh-32291
2024-02-21 16:22:31 +01:00
Sam Brannen b9c304b890 Polish contribution
See gh-32291
2024-02-21 16:19:53 +01:00
Patrick Strawderman e1a32d4ba9 Avoid resizing of fixed-size HashSet/LinkedHashSet variants
Add helpers to CollectionUtils for building HashSets and LinkedHashSets
that can hold an expected number of elements without needing to
resize/rehash.

Closes gh-32291
2024-02-21 16:19:53 +01:00
Stéphane Nicoll 6383a0d7ca Merge branch '6.1.x' 2024-02-21 15:55:55 +01:00
Sam Brannen 3fb170058f Polish contribution
See gh-32294
2024-02-21 14:09:09 +01:00
Patrick Strawderman f9fe8efb2e Use ConcurrentHashMap.newKeySet
In places where a ConcurrentHashMap was used as a set by wrapping it
with Collections.newSetFromMap, switch to just using the set returned
by ConcurrentHashMap.newKeySet directly.

Closes gh-32294
2024-02-21 14:08:38 +01:00
Juergen Hoeller ff9c7141c5 Replace superclass exposure in case of late configuration class skipping
Closes gh-28676
2024-02-21 11:10:27 +01:00
Stéphane Nicoll 871f705bca Remove ComponentScan duplicate condition
Closes gh-27077
2024-02-21 10:09:31 +01:00
Juergen Hoeller 22b41c33ba Preserve existing imported class over scanned configuration class
Closes gh-24643
2024-02-20 20:43:41 +01:00
Juergen Hoeller 266953195c Avoid enhancement of configuration class in case of existing instance
Closes gh-25738
2024-02-20 20:43:09 +01:00
Juergen Hoeller 889c4e0ff5 Reject multiple primary candidates in ancestor factory as well
Closes gh-26612
2024-02-20 16:24:03 +01:00
Juergen Hoeller ad2e95be4b Merge branch '6.1.x' 2024-02-20 15:43:24 +01:00
Juergen Hoeller 63ca8d5d17 Consider defaultCandidate flag in case of no annotations as well
See gh-26528
2024-02-20 15:30:38 +01:00
Juergen Hoeller bc01e3116f Ignore fallback bean for shortcut resolution
See gh-26241
See gh-28122
2024-02-20 13:51:37 +01:00
Juergen Hoeller 480051a21c Introduce fallback flag and annotation (as companion to primary)
Closes gh-26241
2024-02-20 13:37:41 +01:00
Arjen Poutsma c077805761 Set correct capacity in DefaultDataBuffer::setNativeBuffer
Closes gh-30984
2024-02-20 12:36:55 +01:00
Arjen Poutsma ceb7afdca2 Merge pull request #32009 from injae-kim:fix-defaultDataBuffer-getNativeBuffer
* gh-32009:
  Polishing external contribution
  Set correct limit in DefaultDataBuffer::getNativeBuffer
2024-02-20 12:20:22 +01:00
Arjen Poutsma 70004e9ad0 Polishing external contribution
Change position and limit on duplicate, rather than source.

See gh-30967
Closes gh-32009
2024-02-20 12:12:35 +01:00
injae-kim 969d0bd08b Set correct limit in DefaultDataBuffer::getNativeBuffer
Closes gh-30967
2024-02-20 12:12:35 +01:00
Juergen Hoeller a8fb16b47c Introduce defaultCandidate flag (for plain type vs. qualified match)
Closes gh-26528
2024-02-20 12:00:47 +01:00
Arjen Poutsma bc2257aaff Invoke defaultRequest earlier in RestClient and WebClient
Closes gh-32053
2024-02-20 11:21:24 +01:00
Stéphane Nicoll 218957f0e8 Merge branch '6.1.x' 2024-02-20 10:19:41 +01:00
Juergen Hoeller a001319f1f Add additional shortcut for qualifier value matching target bean name
Closes gh-17677
See gh-28122
2024-02-19 22:39:37 +01:00
Juergen Hoeller 874e61a0c6 Test for async event publication before listener initialized
Closes gh-20904
See gh-23501
See gh-25799
2024-02-19 18:18:09 +01:00
Juergen Hoeller 4a02893c31 Avoid early singleton inference outside of original creation thread
See gh-23501
2024-02-19 17:44:30 +01:00
Juergen Hoeller 902e5707a8 Revise singleton registry for lenient locking (fallback instead of deadlock)
Closes gh-23501
2024-02-19 15:49:33 +01:00
Stéphane Nicoll f529386ce2 Merge pull request #31781 from rstoyanchev
* pr/31781:
  Write form data without charset parameter

Closes gh-31781
2024-02-19 15:23:59 +01:00
rstoyanchev b8715811f8 Write form data without charset parameter
This commit updates the content-type for form data to not include the
charset, unless it is different from the default charset.

Some servers don't handle charset parameter and the spec is unclear.

See gh-31781
2024-02-19 15:21:54 +01:00
Stéphane Nicoll 08998376cc Merge branch '6.1.x' 2024-02-19 10:52:17 +01:00
Stéphane Nicoll 6adda31328 Upgrade CI image to Java 22+36 RC 2024-02-19 10:51:16 +01:00
Sam Brannen c6e6a3e44d Link to section in reference manual 2024-02-18 18:19:59 +01:00
Sam Brannen bc2895a30b Support backticks for quoted identifiers in spring-r2dbc
NamedParameterUtils in spring-r2dbc now supports MySQL-style backticks
for quoted identifiers for consistency with spring-jdbc.

See gh-31944
Closes gh-32285
2024-02-17 16:05:40 +01:00
Sam Brannen d86af57e0f Polishing 2024-02-17 16:00:58 +01:00
Sam Brannen d4cde29f75 Clean up TODOs in SpEL 2024-02-17 15:33:05 +01:00
Sam Brannen 5f1e25a61f Polishing 2024-02-17 15:32:43 +01:00
Juergen Hoeller 0ddfe3977a Merge branch '6.1.x' 2024-02-16 22:50:04 +01:00
Juergen Hoeller c6146ea2db Introduce shortcut for declared dependency name matching target bean name
Closes gh-28122
2024-02-16 21:28:04 +01:00
Sam Brannen eefdee7983 Fix syntax in Selection/Projection examples 2024-02-16 18:44:28 +01:00
Sam Brannen ab48ac36e9 Deprecate local variable support in SpEL's internal ExpressionState
Since the Spring Expression Language does not actually support local
variables in expressions, this commit deprecates all public APIs
related to local variables in ExpressionState (namely, the two
enterScope(...) variants that accept local variable data,
setLocalVariable(), and lookupLocalVariable()).

In addition, we no longer invoke `state.enterScope("index", ...)` in
the Projection and Selection AST nodes since the $index local variable
was never accessible within expressions anyway.

See gh-23202
Closes gh-32004
2024-02-16 18:29:00 +01:00
Sam Brannen c1f0faade7 Polish ExpressionState 2024-02-16 18:25:14 +01:00
rstoyanchev cd2a3fdd8d Remove deprecations in HandlerResult
See gh-30608
2024-02-16 16:16:08 +00:00
Sam Brannen 71dfebbfe5 Polishing 2024-02-16 16:45:18 +01:00
Sam Brannen a85bf3185e Remove APIs deprecated for removal in 6.2
This commit removes the following obsolete and deprecated APIs.

- org.springframework.util.Base64Utils

- org.springframework.cache.jcache.interceptor.JCacheOperationSourcePointcut

- org.springframework.http.client.AbstractClientHttpResponse

- org.springframework.http.client.ClientHttpResponse.getRawStatusCode()

- org.springframework.http.client.observation.ClientHttpObservationDocumentation.HighCardinalityKeyNames.CLIENT_NAME

- org.springframework.web.reactive.function.client.ClientHttpObservationDocumentation.HighCardinalityKeyNames.CLIENT_NAME

- org.springframework.web.filter.reactive.ServerWebExchangeContextFilter.get(Context)

- org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler.handleBindException(...)

- org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver.handleBindException(...)

Closes gh-30608
2024-02-16 16:45:18 +01:00
Sam Brannen 6d5bf6d9b3 Ensure alias resolution in SimpleAliasRegistry depends on registration order
Closes gh-32024
2024-02-16 15:22:27 +01:00
Sam Brannen 6b482df6fa Clean up warning in Gradle build 2024-02-16 15:04:41 +01:00
Sam Brannen ea4e7df9ca Consistently declare ignoreUnresolvablePlaceholders as last argument 2024-02-16 15:00:32 +01:00
Sam Brannen 7c07c43201 Polishing 2024-02-16 15:00:06 +01:00
Juergen Hoeller 6791ea94a0 Change executor phase to MAX_VALUE/2 and reduce timeout to 10 seconds
Closes gh-32152
2024-02-16 14:16:32 +01:00
Juergen Hoeller 7ee8e66c7f Avoid internal lifecycle synchronization in favor of lifecycle lock
Closes gh-32284
2024-02-16 12:42:39 +01:00
Juergen Hoeller 34372ee32b Introduce lock-based lifecycle waiting and default virtual threads flag
Closes gh-32252
2024-02-16 12:42:27 +01:00
Sam Brannen b6df5a677e Polishing 2024-02-16 11:41:22 +01:00
Sam Brannen fc9a118406 Polish SimpleCommandLinePropertySource support 2024-02-16 11:41:22 +01:00
Sam Brannen 87e6d1b7da Update Eclipse template to 6.2 2024-02-16 11:41:22 +01:00
Juergen Hoeller ea3573176a Avoid infinite recursion for self-referencing generic type
Closes gh-32282
See gh-30079
2024-02-16 11:15:02 +01:00
Stéphane Nicoll bcf235cc66 Merge branch '6.1.x' 2024-02-16 08:36:26 +01:00
Juergen Hoeller 7ad197e8e1 Deprecate LobHandler and SqlXmlHandler abstractions
Including SqlLobValue and related support classes.

Closes gh-32179
2024-02-15 17:26:49 +01:00
Juergen Hoeller 7e67da8a26 Support for matching partial generics
Closes gh-20727
2024-02-15 16:51:09 +01:00
Juergen Hoeller d4e8daaede Add javadoc note on recursive resolution as of 6.2
See gh-30079
2024-02-15 16:50:53 +01:00
Yanming Zhou e788aeb25b Improve GenericTypeResolver to resolve type variable recursively
Fix GH-24963
2024-02-15 16:28:45 +01:00
Stéphane Nicoll 4b2e40153a Merge pull request #32243 from ttddyy
* pr/32243:
  Polish "Add ThreadLocalAccessor for LocaleContext and RequestAttributes"
  Add ThreadLocalAccessor for LocaleContext and RequestAttributes

Closes gh-32243
2024-02-15 15:58:06 +01:00
Stéphane Nicoll aef4b21f19 Polish "Add ThreadLocalAccessor for LocaleContext and RequestAttributes"
See gh-32243
2024-02-15 15:57:43 +01:00
Tadaya Tsuyukubo 5bd1c1fddb Add ThreadLocalAccessor for LocaleContext and RequestAttributes
Add `ThreadLocalAccessor` implementations:
- `LocaleThreadLocalAccessor`
- `RequestAttributesThreadLocalAccessor`

See gh-32243
2024-02-15 15:49:55 +01:00
Stéphane Nicoll 5d22aa9770 Add support for escaping prefix and separator in property placeholders
Closes gh-9628
Closes gh-26268
2024-02-15 15:28:36 +01:00
Stéphane Nicoll e3aa5b6b11 Use new implementation in PropertyPlaceholderHelper
This commit removes the previous implementation in favor of the new
PlaceholderParser. The only noticeable side effect is that the exception
is no longer an IllegalArgumentException, but rather the dedicated
PlaceholderResolutionException.

See gh-9628
2024-02-15 15:27:13 +01:00
Stéphane Nicoll 00e05e603d Add new property placeholder implementation
This commit provides a rewrite of the parser for properties containing
potentially placeholders.

Assuming a source where `firstName` = `John` and `lastName` = `Smith`,
the "${firstName}-${lastName}" property is evaluated as "John-Smith".

Compared with the existing implementation in PropertyPlaceholderHelper,
the new implementation offers the following extra features:

1. Placeholder can be escaped using a configurable escape character.
When a placeholder is escaped it is rendered as is. This does apply to
any nested placeholder that wouldn't be escaped. For instance,
"\${firstName}" is evaluated as "${firstName}".
2. The default separator can also be escaped the same way. When the
separator is escaped, the left and right parts are not considered as
the key and the default value respectively. Rather the two parts
combined, including the separator (but not the escape separator) are
used for resolution. For instance, ${java\:comp/env/test} is looking
for a "java:comp/env/test" property.
3. Placeholders are resolved lazily. Previously, all nested placeholders
were resolved before considering if a separator was present. This
implementation only attempts the resolution of the default value if the
key does not provide a value.
4. Failure to resolve a placeholder are more rich, with a dedicated
PlaceholderResolutionException that contains the resolution chain.

See gh-9628
See gh-26268
2024-02-15 15:27:13 +01:00
Stéphane Nicoll 0fbfecd392 Merge pull request #31513 from bclozel
* pr/31513:
  Support "--" end of options in SimpleCommandLineArgsParser

Closes gh-31513
2024-02-15 15:09:39 +01:00
Brian Clozel b4131ce131 Support "--" end of options in SimpleCommandLineArgsParser
Prior to this commit, the `SimpleCommandLineArgsParser` would reject
"--" arguments as invalid. As reported by the community, the POSIX
utility conventions (Guideline 10) state that

> The first -- argument that is not an option-argument should be
> accepted as a delimiter indicating the end of options.
> Any following arguments should be treated as operands, even if they
> begin with the '-' character.

This commit updates `SimpleCommandLineArgsParser` to not reject "--"
arguments and instead to consider remaining arguments as non-optional.

See gh-31513
2024-02-15 15:06:17 +01:00
Stéphane Nicoll 0f707706f1 Stop setting a thread name prefix for externally defined Executors
This commit updates the WebSocket message broker configuration to stop
setting a thread name prefix for externally defined Executors.

This used to apply to:

* clientInboundChannel with a thread name prefix of
"clientInboundChannel-".
* clientOutboundChannel with a thread name prefix of
"clientOutboundChannel-".
* brokerChannel with a thread name prefix of "brokerChannel-".

Closes gh-32132
2024-02-15 14:59:42 +01:00
Stéphane Nicoll f526b23fd7 Harmonize WebSocket message broker to use Executor
This commit harmonizes the configuration of the WebSocket message
broker to use Executor rather than TaskExecutor as only the former
is enforced. This lets custom configuration to use a wider range
of implementations.

Closes gh-32129
2024-02-15 14:59:42 +01:00
Stéphane Nicoll 2fc8b13dd5 Add support for MySQL backticks
This commit makes sure that content within backticks are skipped
when parsing a SQL statement using NamedParameterUtils. This harmonizes
the current behavior of ignoring special characters that are wrapped
in backticks.

Closes gh-31944
2024-02-15 14:41:30 +01:00
Stéphane Nicoll e73bbd4ad3 Configure jsonpath MappingProvider in WebTestClient
This commit improves jsonpath support in WebTestClient by detecting
a suitable json encoder/decoder that can be applied to assert more
complex data structure.

Closes gh-31653
2024-02-15 14:41:30 +01:00
Stéphane Nicoll 9f8038963f Allow JsonPathExpectationsHelper to use a custom configuration
This commit improves JsonPathExpectationsHelper to allow a custom json
path configuration to be defined. The primary objective of a custom
configuration is to specify a custom mapper that can deserialize complex
object structure.

As part of this commit, it is now possible to invoke a Matcher against
a type that holds generic information, using a regular
ParameterizedTypeReference.

Given that the existing constructor takes a vararg of Object, this
commit also deprecates this constructor in favor of formatting the
expression String upfront.

Closes gh-31651
2024-02-15 13:57:40 +01:00
Stéphane Nicoll 4f8eca1350 Order methods that take a type as a lambda consistently
Closes gh-32062
2024-02-15 13:57:17 +01:00
Stéphane Nicoll 18ea43c905 Allow customizations of embedded database connections
This commit allows EmbeddedDatabaseConfigurer instances to be further
customized if necessary. EmbeddedDatabaseBuilder has a way now to set
a DatabaseConfigurer rather than just a type to provide full control,
or customize an existing supported database type using the new
EmbeddedDatabaseConfigurers#customizeConfigurer callback.

Closes gh-21160
2024-02-15 13:46:49 +01:00
Stéphane Nicoll bcccba50c1 Merge branch '6.1.x' 2024-02-15 13:07:47 +01:00
Stéphane Nicoll 8288db496d Initiate Spring Framework 6.2.0 snapshots 2024-02-15 13:03:54 +01:00
1193 changed files with 34325 additions and 7754 deletions
@@ -0,0 +1,17 @@
name: Print JVM thread dumps
description: Prints a thread dump for all running JVMs
runs:
using: composite
steps:
- if: ${{ runner.os == 'Linux' }}
shell: bash
run: |
for jvm_pid in $(jps -q -J-XX:+PerfDisableSharedMem); do
jcmd $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:
- 6.1.x
- main
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
+43
View File
@@ -0,0 +1,43 @@
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/'
+2 -3
View File
@@ -1,8 +1,7 @@
name: CI
on:
push:
branches:
- 6.1.x
schedule:
- cron: '30 9 * * *'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
+1 -1
View File
@@ -1,4 +1,4 @@
# <img src="framework-docs/src/docs/spring-framework.png" width="80" height="80"> Spring Framework [![Build Status](https://github.com/spring-projects/spring-framework/actions/workflows/build-and-deploy-snapshot.yml/badge.svg?branch=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)
# <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)
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".
+2 -1
View File
@@ -2,13 +2,14 @@ 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.8.20'
id 'org.jetbrains.dokka' version '1.9.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 {
+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.1.x[Spring Framework 6.1.x].
for https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-6.2.x[Spring Framework 6.2.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.1.x -c ci/pipeline.yml -l ci/parameters.yml
$ fly -t spring set-pipeline -p spring-framework-6.2.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: "6.1.x"
milestone: "6.1.x"
branch: "main"
milestone: "6.2.x"
build-name: "spring-framework"
pipeline-name: "spring-framework"
concourse-url: "https://ci.spring.io"
+2
View File
@@ -20,6 +20,8 @@ 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"
+16 -1
View File
@@ -30,7 +30,7 @@ antora {
'@asciidoctor/tabs': '1.0.0-beta.3',
'@opendevise/antora-release-line-extension': '1.0.0',
'@springio/antora-extensions': '1.8.2',
'@springio/asciidoctor-extensions': '1.0.0-alpha.9'
'@springio/asciidoctor-extensions': '1.0.0-alpha.10'
]
}
@@ -60,10 +60,25 @@ 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")
implementation(project(":spring-core-test"))
implementation("org.assertj:assertj-core")
+1
View File
@@ -183,6 +183,7 @@
***** xref:testing/annotations/integration-spring/annotation-sqlmergemode.adoc[]
***** xref:testing/annotations/integration-spring/annotation-sqlgroup.adoc[]
***** xref:testing/annotations/integration-spring/annotation-disabledinaotmode.adoc[]
***** xref:testing/annotations/integration-spring/annotation-beanoverriding.adoc[]
**** xref:testing/annotations/integration-junit4.adoc[]
**** xref:testing/annotations/integration-junit-jupiter.adoc[]
**** xref:testing/annotations/integration-meta.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,18 +128,7 @@ the resulting pointcut is effectively the union of the specified patterns.)
The following example shows how to use `JdkRegexpMethodPointcut`:
[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>
----
include-code::./JdkRegexpConfiguration[tag=snippet,indent=0]
Spring provides a convenience class named `RegexpMethodPointcutAdvisor`, which lets us
also reference an `Advice` (remember that an `Advice` can be an interceptor, before advice,
@@ -147,21 +136,7 @@ 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:
[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>
----
include-code::./RegexpConfiguration[tag=snippet,indent=0]
You can use `RegexpMethodPointcutAdvisor` with any `Advice` type.
@@ -8,54 +8,8 @@ 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 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.
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).
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
@@ -9,43 +9,12 @@ 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`:
[source,xml,indent=0,subs="verbatim"]
----
<bean id="myAspect" class="com.xyz.NotVeryUsefulAspect">
<!-- configure properties of the aspect here -->
</bean>
----
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
The second of the two examples shows the `NotVeryUsefulAspect` class definition, which is
annotated with `@Aspect`:
[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
----
======
include-code::./NotVeryUsefulAspect[tag=snippet,indent=0]
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,93 +16,9 @@ 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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Aspect
public class ConcurrentOperationExecutor implements Ordered {
include-code::./ConcurrentOperationExecutor[tag=snippet,indent=0]
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].
======
`@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].
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
@@ -114,70 +30,15 @@ we have exhausted all of our retry attempts.
The corresponding Spring configuration follows:
[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>
----
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
To refine the aspect so that it retries only idempotent operations, we might define the following
`Idempotent` annotation:
[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
----
======
include-code::./service/Idempotent[tag=snippet,indent=0]
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:
[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? {
// ...
}
----
======
include-code::./service/SampleService[tag=snippet,indent=0]
@@ -72,7 +72,7 @@ Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
val usageTracked = context.getBean("myService", UsageTracked.class)
val usageTracked = context.getBean<UsageTracked>("myService")
----
======
@@ -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 is the
(`com.xyz.domain.Account`). Since the default name for a bean defined via XML 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,41 +177,10 @@ 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). If you
use Java-based configuration, you can add `@EnableSpringConfigured` to any
`@Configuration` class, as follows:
a reference to the bean factory that is to be used to configure new objects). You can define
the related configuration as follows:
[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/>
----
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
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
@@ -783,52 +752,9 @@ 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`,
which typically is done by using the `@EnableLoadTimeWeaving` annotation, as follows:
To enable the Spring Framework's LTW support, you need to configure a `LoadTimeWeaver` as follows:
[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>
----
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
The preceding configuration automatically defines and registers a number of LTW-specific
infrastructure beans, such as a `LoadTimeWeaver` and an `AspectJWeavingEnabler`, for you.
@@ -864,63 +790,12 @@ 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 specify a specific `LoadTimeWeaver` with Java configuration, implement the
`LoadTimeWeavingConfigurer` interface and override the `getLoadTimeWeaver()` method.
To configure a specific `LoadTimeWeaver`, implement the
`LoadTimeWeavingConfigurer` interface and override the `getLoadTimeWeaver()` method
(or use the XML equivalent).
The following example specifies a `ReflectiveLoadTimeWeaver`:
[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>
----
include-code::./CustomWeaverConfiguration[tag=snippet,indent=0]
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`.
@@ -1,5 +1,5 @@
[[beans-autowired-annotation-primary]]
= Fine-tuning Annotation-based Autowiring with `@Primary`
= Fine-tuning Annotation-based Autowiring with `@Primary` or `@Fallback`
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,8 +50,51 @@ Kotlin::
----
======
With the preceding configuration, the following `MovieRecommender` is autowired with the
`firstMovieCatalog`:
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`:
[tabs]
======
@@ -1,12 +1,13 @@
[[beans-autowired-annotation-qualifiers]]
= Fine-tuning Annotation-based Autowiring with Qualifiers
`@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:
`@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:
--
[tabs]
@@ -101,8 +101,8 @@ NOTE: When configuring a `PropertySourcesPlaceholderConfigurer` using JavaConfig
Using the above configuration ensures Spring initialization failure if any `${}`
placeholder could not be resolved. It is also possible to use methods like
`setPlaceholderPrefix`, `setPlaceholderSuffix`, or `setValueSeparator` to customize
placeholders.
`setPlaceholderPrefix`, `setPlaceholderSuffix`, `setValueSeparator`, or
`setEscapeCharacter` to customize placeholders.
NOTE: Spring Boot configures by default a `PropertySourcesPlaceholderConfigurer` bean that
will get properties from `application.properties` and `application.yml` files.
@@ -73,7 +73,23 @@ 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
Bean overriding is happening when a bean is registered using an identifier that is
already allocated. While bean overriding is possible, it makes the configuration harder
to read and this feature will be deprecated in a future release.
To disable bean overriding altogether, you can set the `allowBeanDefinitionOverriding`
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/annotations/integration-spring/annotation-beanoverriding.adoc[this section].
[[beans-beanname]]
== Naming Beans
@@ -503,10 +503,47 @@ way, navigating `@Configuration` classes and their dependencies becomes no diffe
than the usual process of navigating interface-based code.
--
TIP: If you want to influence the startup creation order of certain beans, consider
declaring some of them as `@Lazy` (for creation on first access instead of on startup)
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-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.
====
[[beans-java-conditional]]
@@ -1,219 +1,34 @@
[[expressions-beandef]]
= Expressions in Bean Definitions
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>
----
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> }`.
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.
The following example shows access to the `systemProperties` bean as a SpEL variable:
[source,xml,indent=0,subs="verbatim"]
----
<bean id="taxCalculator" class="org.spring.samples.TaxCalculator">
<property name="defaultLocale" value="#{ systemProperties['user.region'] }"/>
<!-- 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.
and method or constructor parameters (or XML equivalent).
The following example sets the default value of a field:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class FieldValueTestBean {
include-code::./FieldValueTestBean[tag=snippet,indent=0]
@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
}
----
======
Note that you do not have to prefix the predefined variable with the `#` symbol here.
The following example shows the equivalent but on a property setter method:
[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
}
----
======
include-code::./PropertyValueTestBean[tag=snippet,indent=0]
Autowired methods and constructors can also use the `@Value` annotation, as the following
examples show:
[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::./SimpleMovieLister[tag=snippet,indent=0]
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]
@@ -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,6 +81,64 @@ 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]
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,11 +1,25 @@
[[expressions-properties-arrays]]
= Properties, Arrays, Lists, Maps, and Indexers
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:
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:
[tabs]
======
@@ -16,6 +30,7 @@ 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);
----
@@ -26,6 +41,7 @@ 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
----
======
@@ -39,8 +55,20 @@ method invocations -- for example, `getPlaceOfBirth().getCity()` instead of
`placeOfBirth.city`.
====
The contents of arrays and lists are obtained by using square bracket notation, as the
following example shows:
[[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.
====
[tabs]
======
@@ -63,7 +91,8 @@ Java::
String name = parser.parseExpression("members[0].name").getValue(
context, ieee, String.class);
// List and Array navigation
// List and Array Indexing
// evaluates to "Wireless communication"
String invention = parser.parseExpression("members[0].inventions[6]").getValue(
context, ieee, String.class);
@@ -88,16 +117,22 @@ Kotlin::
val name = parser.parseExpression("members[0].name").getValue(
context, ieee, String::class.java)
// List and Array navigation
// List and Array Indexing
// evaluates to "Wireless communication"
val invention = parser.parseExpression("members[0].inventions[6]").getValue(
context, ieee, String::class.java)
----
======
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:
[[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`.
[tabs]
======
@@ -105,38 +140,113 @@ Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
// Officer's Dictionary
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);
// setting values
parser.parseExpression("officers['advisors'][0].placeOfBirth.country").setValue(
societyContext, "Croatia");
// evaluates to "T" (8th letter of "Nikola Tesla")
String character = parser.parseExpression("members[0].name[7]")
.getValue(societyContext, String.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
// Officer's Dictionary
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)
// setting values
parser.parseExpression("officers['advisors'][0].placeOfBirth.country").setValue(
societyContext, "Croatia")
// 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);
// evaluates to "Idvor"
String city = parser.parseExpression("officers['president'].placeOfBirth.city")
.getValue(societyContext, String.class);
String countryExpression = "officers['advisors'][0].placeOfBirth.country";
// setting values
parser.parseExpression(countryExpression)
.setValue(societyContext, "Croatia");
// evaluates to "Croatia"
String country = parser.parseExpression(countryExpression)
.getValue(societyContext, String.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
// Officer's Map
// evaluates to Inventor("Pupin")
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"
// setting values
parser.parseExpression(countryExpression)
.setValue(societyContext, "Croatia")
// evaluates to "Croatia"
val country = parser.parseExpression(countryExpression)
.getValue(societyContext, String::class.java)
----
======
[[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)
----
======
@@ -11,106 +11,9 @@ formatters manually with the help of:
* `org.springframework.format.datetime.standard.DateTimeFormatterRegistrar`
* `org.springframework.format.datetime.DateFormatterRegistrar`
For example, the following Java configuration registers a global `yyyyMMdd` format:
For example, the following configuration registers a global `yyyyMMdd` format:
[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>
----
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
Note there are extra considerations when configuring date and time formats in web
applications. Please see
@@ -46,47 +46,9 @@ 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` in Java:
The following example shows how to configure a `DriverManagerDataSource`:
[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"/>
----
include-code::./DriverManagerDataSourceConfiguration[tag=snippet,indent=0]
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
@@ -94,32 +56,11 @@ documentation for the respective connection pooling implementations.
The following example shows DBCP configuration:
[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"/>
----
include-code::./BasicDataSourceConfiguration[tag=snippet,indent=0]
The following example shows C3P0 configuration:
[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"/>
----
include-code::./ComboPooledDataSourceConfiguration[tag=snippet,indent=0]
[[jdbc-DataSourceUtils]]
== Using `DataSourceUtils`
@@ -387,112 +387,19 @@ Kotlin::
======
--
The following example shows the corresponding XML configuration:
The following example shows the corresponding 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>
----
include-code::./JdbcCorporateEventDaoConfiguration[tag=snippet,indent=0]
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) and annotate the `DataSource` setter
method with `@Autowired`. The following example shows how to do so:
(which makes it a candidate for component-scanning). The following example shows how to do so:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Repository // <1>
public class JdbcCorporateEventDao implements CorporateEventDao {
include-code::./JdbcCorporateEventRepository[tag=snippet,indent=0]
private JdbcTemplate jdbcTemplate;
The following example shows the corresponding configuration:
@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>
----
include-code::./JdbcCorporateEventRepositoryConfiguration[tag=snippet,indent=0]
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
@@ -16,124 +16,22 @@ lightweight nature. Benefits include ease of configuration, quick startup time,
testability, and the ability to rapidly evolve your SQL during development.
[[jdbc-embedded-database-xml]]
== Creating an Embedded Database by Using Spring XML
[[jdbc-embedded-database]]
== Creating an Embedded Database
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:
You can expose an embedded database instance as a bean as the following example shows:
[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>
----
include-code::./JdbcEmbeddedDatabaseConfiguration[tag=snippet,indent=0]
The preceding configuration creates an embedded HSQL database that is populated with SQL from
The preceding configuration creates an embedded H2 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
@@ -168,6 +66,74 @@ 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
@@ -177,8 +143,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-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:
in xref:data-access/jdbc/embedded-database-support.adoc#jdbc-embedded-database[Creating an Embedded Database].
The following listing shows the test template:
[tabs]
======
@@ -272,6 +272,13 @@ 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`
@@ -436,16 +436,36 @@ properties of the `@Transactional` annotation:
| Optional array of exception name patterns that must not cause rollback.
|===
TIP: See xref:data-access/transaction/declarative/rolling-back.adoc#transaction-declarative-rollback-rules[Rollback rules] for further details
on rollback rule semantics, patterns, and warnings regarding possible unintentional
matches for pattern-based rollback rules.
TIP: See xref:data-access/transaction/declarative/rolling-back.adoc#transaction-declarative-rollback-rules[Rollback rules]
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 + `.` + the method name of the transactionally advised class. For example, if the
name of the transactionally advised class + `.` + the method name. 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`
@@ -529,12 +549,32 @@ 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]
@@ -28,4 +28,6 @@ Spring's testing annotations include the following:
* xref:testing/annotations/integration-spring/annotation-sqlmergemode.adoc[`@SqlMergeMode`]
* xref:testing/annotations/integration-spring/annotation-sqlgroup.adoc[`@SqlGroup`]
* xref:testing/annotations/integration-spring/annotation-disabledinaotmode.adoc[`@DisabledInAotMode`]
* xref:testing/annotations/integration-spring/annotation-beanoverriding.adoc#spring-testing-annotation-beanoverriding-testbean[`@TestBean`]
* xref:testing/annotations/integration-spring/annotation-beanoverriding.adoc#spring-testing-annotation-beanoverriding-mockitobean[`@MockitoBean` and `@MockitoSpyBean`]
@@ -0,0 +1,134 @@
[[spring-testing-annotation-beanoverriding]]
= Bean Overriding in Tests
Bean Overriding in Tests refers to the ability to override specific beans in the Context
for a test class, by annotating one or more fields in said test class.
NOTE: This is intended as a less risky alternative to the practice of registering a bean via
`@Bean` with the `DefaultListableBeanFactory` `setAllowBeanDefinitionOverriding` set to
`true`.
The Spring Testing Framework provides two sets of annotations presented below. One relies
purely on Spring, while the second set relies on the Mockito third party library.
[[spring-testing-annotation-beanoverriding-testbean]]
== `@TestBean`
`@TestBean` is used on a test class field to override a specific bean with an instance
provided by a conventionally named static method.
By default, the bean name and the associated static method name are derived from the
annotated field's name, but the annotation allows for specific values to be provided.
The `@TestBean` annotation uses the `REPLACE_DEFINITION`
xref:#spring-testing-annotation-beanoverriding-extending[strategy for test bean overriding].
The following example shows how to fully configure the `@TestBean` annotation, with
explicit values equivalent to the default:
[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: The method to invoke is searched in the test class and any enclosing class it might
have, as well as its hierarchy. This typically allows nested test class to rely on the
method to use in the root test class.
[[spring-testing-annotation-beanoverriding-mockitobean]]
== `@MockitoBean` and `@MockitoSpyBean`
`@MockitoBean` and `@MockitoSpyBean` are used on a test class field to override a bean
with a mocking and spying instance, respectively. In the later case, the original bean
definition is not replaced but instead an early instance is captured and wrapped by the
spy.
By default, the name of the bean to override is derived from the annotated field's name,
but both annotations allows for a specific `name` to be provided. Each annotation also
defines Mockito-specific attributes to fine-tune the mocking details.
The `@MockitoBean` annotation uses the `CREATE_OR_REPLACE_DEFINITION`
xref:#spring-testing-annotation-beanoverriding-extending[strategy for test bean overriding].
The `@MockitoSpyBean` annotation uses the `WRAP_EARLY_BEAN`
xref:#spring-testing-annotation-beanoverriding-extending[strategy] and the original instance
is wrapped in a Mockito spy.
The following example shows how to configure the bean name for both `@MockitoBean` and
`@MockitoSpyBean` annotations:
[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> Both fields will be injected with the Mockito values (the mock and the spy respectively)
======
[[spring-testing-annotation-beanoverriding-extending]]
== Extending bean override with a custom annotation
The three annotations introduced above build upon the `@BeanOverride` meta-annotation
and associated infrastructure, which allows to define custom bean overriding variants.
To create an extension, the following is needed:
- An annotation meta-annotated with `@BeanOverride` that defines the
`BeanOverrideProcessor` to use.
- The `BeanOverrideProcessor` implementation itself.
- One or more concrete `OverrideMetadata` implementations provided by the processor.
The Spring TestContext Framework includes infrastructure classes that support bean
overriding: a `BeanFactoryPostProcessor`, a `TestExecutionListener` and a
`ContextCustomizerFactory`.
The later two are automatically registered via the Spring TestContext Framework
`spring.factories` file, and are responsible for setting up the rest of the infrastructure.
The test classes are parsed looking for any field meta-annotated with `@BeanOverride`,
instantiating the relevant `BeanOverrideProcessor` in order to register an
`OverrideMetadata`.
Then the `BeanOverrideBeanFactoryPostProcessor` will use that information to alter the
context, registering and replacing bean definitions as defined by each metadata
`BeanOverrideStrategy`:
- `REPLACE_DEFINITION`: replaces the bean definition. If it is not present in the
context, an exception is thrown.
- `CREATE_OR_REPLACE_DEFINITION`: replaces the bean definition if the bean definition
does not exist, or create one if it is not.
- `WRAP_BEAN`: get the original instance early so that it can be wrapped.
NOTE: The Bean Overriding infrastructure does not include any bean resolution step,
unlike an `@Autowired`-annotated field for instance. As such, the name of the bean to
override must be somehow provided to or computed by the `BeanOverrideProcessor`.
Typically, the user provides the name one way or the other.
@@ -8,8 +8,7 @@ to use the raw HtmlUnit libraries.
== MockMvc and HtmlUnit Setup
First, make sure that you have included a test dependency on
`net.sourceforge.htmlunit:htmlunit`. In order to use HtmlUnit with Apache HttpComponents
4.5+, you need to use HtmlUnit 2.18 or higher.
`org.htmlunit:htmlunit`.
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-htmlunit-driver`.
includes a test dependency on `org.seleniumhq.selenium:selenium-htmlunit3-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 synch with the controller. To resolve this we can combine our two tests, as
is out of sync with the controller. To resolve this we can combine our two tests, as
follows:
[tabs]
@@ -16,6 +16,8 @@ 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 = Arrays.asList("js", "css", "ico", "png", "jpg", "gif");
List<String> extensions = List.of("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)
@@ -44,6 +44,10 @@ 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,6 +44,11 @@ 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.
@@ -64,7 +69,6 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
// Implement configuration methods...
@@ -76,7 +80,6 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
// Implement configuration methods...
@@ -102,7 +105,6 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
@@ -118,7 +120,6 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
override fun addFormatters(registry: FormatterRegistry) {
@@ -140,7 +141,6 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
@@ -157,7 +157,6 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
override fun addFormatters(registry: FormatterRegistry) {
@@ -194,7 +193,6 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
@@ -210,7 +208,6 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
override fun getValidator(): Validator {
@@ -279,7 +276,6 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
@@ -294,7 +290,6 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
override fun configureContentTypeResolver(builder: RequestedContentTypeResolverBuilder) {
@@ -319,7 +314,6 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
@@ -334,7 +328,6 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
override fun configureHttpMessageCodecs(configurer: ServerCodecConfigurer) {
@@ -376,7 +369,6 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
@@ -391,7 +383,6 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
override fun configureViewResolvers(registry: ViewResolverRegistry) {
@@ -412,7 +403,6 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@@ -437,7 +427,6 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
override fun configureViewResolvers(registry: ViewResolverRegistry) {
@@ -463,7 +452,6 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@@ -480,7 +468,6 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
override fun configureViewResolvers(registry: ViewResolverRegistry) {
@@ -503,7 +490,6 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@@ -524,7 +510,6 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
@@ -565,7 +550,6 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
@@ -583,7 +567,6 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
override fun addResourceHandlers(registry: ResourceHandlerRegistry) {
@@ -616,7 +599,6 @@ Java::
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
@@ -635,7 +617,6 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
override fun addResourceHandlers(registry: ResourceHandlerRegistry) {
@@ -695,7 +676,6 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
@@ -711,7 +691,6 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
@Override
@@ -756,7 +735,6 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
@@ -772,7 +750,6 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
@Override
@@ -810,7 +787,6 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
@@ -827,7 +803,6 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
@Override
@@ -20,7 +20,7 @@ Java::
----
@Configuration
@ComponentScan("org.example.web") // <1>
public class WebConfig {
public class WebConfiguration {
// ...
}
@@ -33,7 +33,7 @@ Kotlin::
----
@Configuration
@ComponentScan("org.example.web") // <1>
class WebConfig {
class WebConfiguration {
// ...
}
@@ -781,7 +781,7 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
ClassPathResource index = new ClassPathResource("static/index.html");
List<String> extensions = Arrays.asList("js", "css", "ico", "png", "jpg", "gif");
List<String> extensions = List.of("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,6 +445,13 @@ 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,6 +44,10 @@ 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]]
@@ -183,7 +187,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 baed on a list of
`MethodArgumentValidException` and `HandlerMethodValidationException` are based 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,30 +12,7 @@ For advanced mode, you can remove `@EnableWebMvc` and extend directly from
`DelegatingWebMvcConfiguration` instead of implementing `WebMvcConfigurer`,
as the following example shows:
[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() {
// ...
}
----
======
include-code::./WebConfiguration[tag=snippet,indent=0]
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,39 +5,7 @@ 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:
[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 {
// ...
}
}
----
======
include-code::./MyPostProcessor[tag=snippet,indent=0]
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,59 +13,9 @@ 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.
In Java configuration, you can customize requested content type resolution, as the
following example shows:
You can customize requested content type resolution, as the following example shows:
[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>
----
include-code::./WebConfiguration[tag=snippet,indent=0]
@@ -6,119 +6,16 @@
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 in Java config, use the following:
To register custom formatters and converters, use the following:
[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>
----
include-code::./WebConfiguration[tag=snippet,indent=0]
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:
[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)
}
}
----
======
include-code::./DateTimeWebConfiguration[tag=snippet,indent=0]
NOTE: See xref:core/validation/format.adoc#format-FormatterRegistrar-SPI[the `FormatterRegistrar` SPI]
and the `FormattingConversionServiceFactoryBean` for more information on when to use
@@ -6,33 +6,7 @@
In Java configuration, you can implement the `WebMvcConfigurer` interface, as the
following example shows:
[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...
}
----
======
include-code::./WebConfiguration[tag=snippet,indent=0]
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,44 +15,7 @@ lower than that of the `DefaultServletHttpRequestHandler`, which is `Integer.MAX
The following example shows how to enable the feature by using the default setup:
[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/>
----
include-code::./WebConfiguration[tag=snippet,indent=0]
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
@@ -63,45 +26,4 @@ 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:
[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"/>
----
include-code::./CustomDefaultServletConfiguration[tag=snippet,indent=0]
@@ -3,50 +3,11 @@
[.small]#xref:web/webflux/config.adoc#webflux-config-enable[See equivalent in the Reactive stack]#
In Java configuration, you can use the `@EnableWebMvc` annotation to enable MVC
configuration, as the following example shows:
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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebMvc
public class WebConfig {
}
----
include-code::./WebConfiguration[tag=snippet,indent=0]
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>
----
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].
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,56 +1,9 @@
[[mvc-config-interceptors]]
= Interceptors
In Java configuration, you can register interceptors to apply to incoming requests, as
the following example shows:
You can register interceptors to apply to incoming requests, as the following example shows:
[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>
----
include-code::./WebConfiguration[tag=snippet,indent=0]
NOTE: Interceptors are not ideally suited as a security layer due to the potential
for a mismatch with annotated controller path matching, which can also match trailing
@@ -15,48 +15,10 @@ 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:
[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()))
----
======
include-code::./WebConfiguration[tag=snippet,indent=0]
In the preceding example,
{spring-framework-api}/http/converter/json/Jackson2ObjectMapperBuilder.html[`Jackson2ObjectMapperBuilder`]
@@ -76,7 +38,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`]
@@ -86,29 +48,3 @@ 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,61 +7,6 @@ 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 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"/>
----
The following example shows how to customize path matching:
include-code::./WebConfiguration[tag=snippet,indent=0]
@@ -13,52 +13,9 @@ expiration to ensure maximum use of the browser cache and a reduction in HTTP re
made by the browser. The `Last-Modified` information is deduced from `Resource#lastModified`
so that HTTP conditional requests are supported with `"Last-Modified"` headers.
The following listing shows how to do so with Java configuration:
The following listing shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**")
.addResourceLocations("/public", "classpath:/static/")
.setCacheControl(CacheControl.maxAge(Duration.ofDays(365)));
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Configuration
@EnableWebMvc
class WebConfig : WebMvcConfigurer {
override fun addResourceHandlers(registry: ResourceHandlerRegistry) {
registry.addResourceHandler("/resources/**")
.addResourceLocations("/public", "classpath:/static/")
.setCacheControl(CacheControl.maxAge(Duration.ofDays(365)))
}
}
----
======
The following example shows how to achieve the same configuration in XML:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<mvc:resources mapping="/resources/**"
location="/public, classpath:/static/"
cache-period="31556926" />
----
include-code::./WebConfiguration[tag=snippet,indent=0]
See also
xref:web/webmvc/mvc-caching.adoc#mvc-caching-static-resources[HTTP caching support for static resources].
@@ -73,60 +30,9 @@ computed from the content, a fixed application version, or other. A
`ContentVersionStrategy` (MD5 hash) is a good choice -- with some notable exceptions, such as
JavaScript resources used with a module loader.
The following example shows how to use `VersionResourceResolver` in Java configuration:
The following example shows how to use `VersionResourceResolver`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**")
.addResourceLocations("/public/")
.resourceChain(true)
.addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"));
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Configuration
@EnableWebMvc
class WebConfig : WebMvcConfigurer {
override fun addResourceHandlers(registry: ResourceHandlerRegistry) {
registry.addResourceHandler("/resources/**")
.addResourceLocations("/public/")
.resourceChain(true)
.addResolver(VersionResourceResolver().addContentVersionStrategy("/**"))
}
}
----
======
The following example shows how to achieve the same configuration in XML:
[source,xml,indent=0,subs="verbatim"]
----
<mvc:resources mapping="/resources/**" location="/public/">
<mvc:resource-chain resource-cache="true">
<mvc:resolvers>
<mvc:version-resolver>
<mvc:content-version-strategy patterns="/**"/>
</mvc:version-resolver>
</mvc:resolvers>
</mvc:resource-chain>
</mvc:resources>
----
include-code::./VersionedConfiguration[tag=snippet,indent=0]
You can then use `ResourceUrlProvider` to rewrite URLs and apply the full chain of resolvers and
transformers -- for example, to insert versions. The MVC configuration provides a `ResourceUrlProvider`
@@ -8,93 +8,15 @@ on the classpath (for example, Hibernate Validator), the `LocalValidatorFactoryB
registered as a global xref:core/validation/validator.adoc[Validator] for use with `@Valid` and
`@Validated` on controller method arguments.
In Java configuration, you can customize the global `Validator` instance, as the
You can customize the global `Validator` instance, as the
following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public Validator getValidator() {
// ...
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebMvc
class WebConfig : WebMvcConfigurer {
override fun getValidator(): Validator {
// ...
}
}
----
======
The following example shows how to achieve the same configuration in XML:
[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 validator="globalValidator"/>
</beans>
----
include-code::./WebConfiguration[tag=snippet,indent=0]
Note that you can also register `Validator` implementations locally, as the following
example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Controller
public class MyController {
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.addValidators(new FooValidator());
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Controller
class MyController {
@InitBinder
protected fun initBinder(binder: WebDataBinder) {
binder.addValidators(FooValidator())
}
}
----
======
include-code::./MyController[tag=snippet,indent=0]
TIP: If you need to have a `LocalValidatorFactoryBean` injected somewhere, create a bean and
mark it with `@Primary` in order to avoid conflict with the one declared in the MVC configuration.
@@ -5,47 +5,9 @@ This is a shortcut for defining a `ParameterizableViewController` that immediate
forwards to a view when invoked. You can use it in static cases when there is no Java controller
logic to run before the view generates the response.
The following example of Java configuration forwards a request for `/` to a view called `home`:
The following example forwards a request for `/` to a view called `home`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("home");
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebMvc
class WebConfig : WebMvcConfigurer {
override fun addViewControllers(registry: ViewControllerRegistry) {
registry.addViewController("/").setViewName("home")
}
}
----
======
The following example achieves the same thing as the preceding example, but with XML, by
using the `<mvc:view-controller>` element:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<mvc:view-controller path="/" view-name="home"/>
----
include-code::./WebConfiguration[tag=snippet,indent=0]
If an `@RequestMapping` method is mapped to a URL for any HTTP method then a view
controller cannot be used to handle the same URL. This is because a match by URL to an
@@ -5,127 +5,12 @@
The MVC configuration simplifies the registration of view resolvers.
The following Java configuration example configures content negotiation view
resolution by using JSP and Jackson as a default `View` for JSON rendering:
The following example configures content negotiation view resolution by using JSP and Jackson as a
default `View` for JSON rendering:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.enableContentNegotiation(new MappingJackson2JsonView());
registry.jsp();
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebMvc
class WebConfig : WebMvcConfigurer {
override fun configureViewResolvers(registry: ViewResolverRegistry) {
registry.enableContentNegotiation(MappingJackson2JsonView())
registry.jsp()
}
}
----
======
The following example shows how to achieve the same configuration in XML:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<mvc:view-resolvers>
<mvc:content-negotiation>
<mvc:default-views>
<bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/>
</mvc:default-views>
</mvc:content-negotiation>
<mvc:jsp/>
</mvc:view-resolvers>
----
include-code::./WebConfiguration[tag=snippet,indent=0]
Note, however, that FreeMarker, Groovy Markup, and script templates also require
configuration of the underlying view technology.
The MVC namespace provides dedicated elements. The following example works with FreeMarker:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<mvc:view-resolvers>
<mvc:content-negotiation>
<mvc:default-views>
<bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/>
</mvc:default-views>
</mvc:content-negotiation>
<mvc:freemarker cache="false"/>
</mvc:view-resolvers>
<mvc:freemarker-configurer>
<mvc:template-loader-path location="/freemarker"/>
</mvc:freemarker-configurer>
----
In Java configuration, you can add the respective `Configurer` bean,
as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.enableContentNegotiation(new MappingJackson2JsonView());
registry.freeMarker().cache(false);
}
@Bean
public FreeMarkerConfigurer freeMarkerConfigurer() {
FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
configurer.setTemplateLoaderPath("/freemarker");
return configurer;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebMvc
class WebConfig : WebMvcConfigurer {
override fun configureViewResolvers(registry: ViewResolverRegistry) {
registry.enableContentNegotiation(MappingJackson2JsonView())
registry.freeMarker().cache(false)
}
@Bean
fun freeMarkerConfigurer() = FreeMarkerConfigurer().apply {
setTemplateLoaderPath("/freemarker")
}
}
----
======
configuration of the underlying view technology. The following example works with FreeMarker:
include-code::./FreeMarkerConfiguration[tag=snippet,indent=0]
@@ -12,54 +12,7 @@ annotated class, indicating its role as a web component.
To enable auto-detection of such `@Controller` beans, you can add component scanning to
your Java configuration, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@ComponentScan("org.example.web")
public class WebConfig {
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@ComponentScan("org.example.web")
class WebConfig {
// ...
}
----
======
The following example shows the XML configuration equivalent of the preceding example:
[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:p="http://www.springframework.org/schema/p"
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:component-scan base-package="org.example.web"/>
<!-- ... -->
</beans>
----
include-code::./WebConfiguration[tag=snippet,indent=0]
`@RestController` is a xref:core/beans/classpath-scanning.adoc#beans-meta-annotations[composed annotation] that is
itself meta-annotated with `@Controller` and `@ResponseBody` to indicate a controller whose
@@ -82,49 +82,9 @@ https://sockjs.github.io/sockjs-protocol/sockjs-protocol-0.3.3.html[narrated tes
[[websocket-fallback-sockjs-enable]]
== Enabling SockJS
You can enable SockJS through Java configuration, as the following example shows:
You can enable SockJS through configuration, as the following example shows:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myHandler(), "/myHandler").withSockJS();
}
@Bean
public WebSocketHandler myHandler() {
return new MyHandler();
}
}
----
The following example shows the XML configuration equivalent of the preceding example:
[source,xml,indent=0,subs="verbatim,quotes,attributes"]
----
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
https://www.springframework.org/schema/websocket/spring-websocket.xsd">
<websocket:handlers>
<websocket:mapping path="/myHandler" handler="myHandler"/>
<websocket:sockjs/>
</websocket:handlers>
<bean id="myHandler" class="org.springframework.samples.MyHandler"/>
</beans>
----
include-code::./WebSocketConfiguration[tag=snippet,indent=0]
The preceding example is for use in Spring MVC applications and should be included in the
configuration of a xref:web/webmvc/mvc-servlet.adoc[`DispatcherServlet`]. However, Spring's WebSocket
@@ -16,69 +16,12 @@ Creating a WebSocket server is as simple as implementing `WebSocketHandler` or,
likely, extending either `TextWebSocketHandler` or `BinaryWebSocketHandler`. The following
example uses `TextWebSocketHandler`:
[source,java,indent=0,subs="verbatim,quotes"]
----
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.TextMessage;
include-code::./MyHandler[tag=snippet,indent=0]
public class MyHandler extends TextWebSocketHandler {
@Override
public void handleTextMessage(WebSocketSession session, TextMessage message) {
// ...
}
}
----
There is dedicated WebSocket Java configuration and XML namespace support for mapping the preceding
There is dedicated WebSocket programmatic configuration and XML namespace support for mapping the preceding
WebSocket handler to a specific URL, as the following example shows:
[source,java,indent=0,subs="verbatim,quotes"]
----
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myHandler(), "/myHandler");
}
@Bean
public WebSocketHandler myHandler() {
return new MyHandler();
}
}
----
The following example shows the XML configuration equivalent of the preceding example:
[source,xml,indent=0,subs="verbatim,quotes,attributes"]
----
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
https://www.springframework.org/schema/websocket/spring-websocket.xsd">
<websocket:handlers>
<websocket:mapping path="/myHandler" handler="myHandler"/>
</websocket:handlers>
<bean id="myHandler" class="org.springframework.samples.MyHandler"/>
</beans>
----
include-code::./WebSocketConfiguration[tag=snippet,indent=0]
The preceding example is for use in Spring MVC applications and should be included
in the configuration of a xref:web/webmvc/mvc-servlet.adoc[`DispatcherServlet`]. However, Spring's
@@ -104,45 +47,7 @@ You can use such an interceptor to preclude the handshake or to make any attribu
available to the `WebSocketSession`. The following example uses a built-in interceptor
to pass HTTP session attributes to the WebSocket session:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new MyHandler(), "/myHandler")
.addInterceptors(new HttpSessionHandshakeInterceptor());
}
}
----
The following example shows the XML configuration equivalent of the preceding example:
[source,xml,indent=0,subs="verbatim,quotes,attributes"]
----
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
https://www.springframework.org/schema/websocket/spring-websocket.xsd">
<websocket:handlers>
<websocket:mapping path="/myHandler" handler="myHandler"/>
<websocket:handshake-interceptors>
<bean class="org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor"/>
</websocket:handshake-interceptors>
</websocket:handlers>
<bean id="myHandler" class="org.springframework.samples.MyHandler"/>
</beans>
----
include-code::./WebSocketConfiguration[tag=snippet,indent=0]
A more advanced option is to extend the `DefaultHandshakeHandler` that performs
the steps of the WebSocket handshake, including validating the client origin,
@@ -236,69 +141,17 @@ You can configure of the underlying WebSocket server such as input message buffe
idle timeout, and more.
For Jakarta WebSocket servers, you can add a `ServletServerContainerFactoryBean` to your
Java configuration. For example:
configuration. For example:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Bean
public ServletServerContainerFactoryBean createWebSocketContainer() {
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
container.setMaxTextMessageBufferSize(8192);
container.setMaxBinaryMessageBufferSize(8192);
return container;
}
----
Or to your XML configuration:
[source,xml,indent=0,subs="verbatim,quotes,attributes"]
----
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
https://www.springframework.org/schema/websocket/spring-websocket.xsd">
<bean class="org.springframework...ServletServerContainerFactoryBean">
<property name="maxTextMessageBufferSize" value="8192"/>
<property name="maxBinaryMessageBufferSize" value="8192"/>
</bean>
</beans>
----
include-code::./WebSocketConfiguration[tag=snippet,indent=0]
NOTE: For client Jakarta WebSocket configuration, use
ContainerProvider.getWebSocketContainer() in Java configuration, or
ContainerProvider.getWebSocketContainer() in programmatic configuration, or
`WebSocketContainerFactoryBean` in XML.
For Jetty, you can supply a `Consumer` callback to configure the WebSocket server:
For Jetty, you can supply a callback to configure the WebSocket server:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(echoWebSocketHandler(), "/echo").setHandshakeHandler(handshakeHandler());
}
@Bean
public DefaultHandshakeHandler handshakeHandler() {
JettyRequestUpgradeStrategy strategy = new JettyRequestUpgradeStrategy();
strategy.addWebSocketConfigurer(configurable -> {
policy.setInputBufferSize(8192);
policy.setIdleTimeout(600000);
});
return new DefaultHandshakeHandler(strategy);
}
}
----
include-code::./JettyWebSocketConfiguration[tag=snippet,indent=0]
TIP: When using STOMP over WebSocket, you will also need to configure
xref:web/websocket/stomp/server-config.adoc[STOMP WebSocket transport]
@@ -331,51 +184,4 @@ The three possible behaviors are:
You can configure WebSocket and SockJS allowed origins, as the following example shows:
[source,java,indent=0,subs="verbatim,quotes"]
----
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myHandler(), "/myHandler").setAllowedOrigins("https://mydomain.com");
}
@Bean
public WebSocketHandler myHandler() {
return new MyHandler();
}
}
----
The following example shows the XML configuration equivalent of the preceding example:
[source,xml,indent=0,subs="verbatim,quotes,attributes"]
----
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
https://www.springframework.org/schema/websocket/spring-websocket.xsd">
<websocket:handlers allowed-origins="https://mydomain.com">
<websocket:mapping path="/myHandler" handler="myHandler" />
</websocket:handlers>
<bean id="myHandler" class="org.springframework.samples.MyHandler"/>
</beans>
----
include-code::./WebSocketConfiguration[tag=snippet,indent=0]
@@ -40,29 +40,7 @@ the user header on the CONNECT `Message`. Spring notes and saves the authenticat
user and associate it with subsequent STOMP messages on the same session. The following
example shows how to register a custom authentication interceptor:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableWebSocketMessageBroker
public class MyConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(new ChannelInterceptor() {
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor =
MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
if (StompCommand.CONNECT.equals(accessor.getCommand())) {
Authentication user = ... ; // access authentication header(s)
accessor.setUser(user);
}
return message;
}
});
}
}
----
include-code::./WebSocketConfiguration[tag=snippet,indent=0]
Also, note that, when you use Spring Security's authorization for messages, at present,
you need to ensure that the authentication `ChannelInterceptor` config is ordered
@@ -105,5 +105,20 @@ it handle ERROR frames in addition to the `handleException` callback for
exceptions from the handling of messages and `handleTransportError` for
transport-level errors including `ConnectionLostException`.
You can use the `inboundMessageSizeLimit` and `outboundMessageSizeLimit` properties of
`WebSocketStompClient` to limit the maximum size of inbound and outbound WebSocket
messages. When an outbound STOMP message exceeds the limit, it is split into partial frames,
which the receiver would have to reassemble. By default, there is no size limit for outbound
messages. When an inbound STOMP message size exceeds the configured limit, a
`StompConversionException` is thrown. The default size limit for inbound messages is `64KB`.
[source,java,indent=0,subs="verbatim,quotes"]
----
WebSocketClient webSocketClient = new StandardWebSocketClient();
WebSocketStompClient stompClient = new WebSocketStompClient(webSocketClient);
stompClient.setInboundMessageSizeLimit(64 * 1024); // 64KB
stompClient.setOutboundMessageSizeLimit(64 * 1024); // 64KB
----
@@ -62,42 +62,7 @@ documentation of the XML schema for important additional details.
The following example shows a possible configuration:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureWebSocketTransport(WebSocketTransportRegistration registration) {
registration.setSendTimeLimit(15 * 1000).setSendBufferSizeLimit(512 * 1024);
}
// ...
}
----
The following example shows the XML configuration equivalent of the preceding example:
[source,xml,indent=0,subs="verbatim,quotes,attributes"]
----
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
https://www.springframework.org/schema/websocket/spring-websocket.xsd">
<websocket:message-broker>
<websocket:transport send-timeout="15000" send-buffer-size="524288" />
<!-- ... -->
</websocket:message-broker>
</beans>
----
include-code::./WebSocketConfiguration[tag=snippet,indent=0]
You can also use the WebSocket transport configuration shown earlier to configure the
maximum allowed size for incoming STOMP messages. In theory, a WebSocket
@@ -115,42 +80,7 @@ minimum.
The following example shows one possible configuration:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureWebSocketTransport(WebSocketTransportRegistration registration) {
registration.setMessageSizeLimit(128 * 1024);
}
// ...
}
----
The following example shows the XML configuration equivalent of the preceding example:
[source,xml,indent=0,subs="verbatim,quotes,attributes"]
----
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
https://www.springframework.org/schema/websocket/spring-websocket.xsd">
<websocket:message-broker>
<websocket:transport message-size="131072" />
<!-- ... -->
</websocket:message-broker>
</beans>
----
include-code::./MessageSizeLimitWebSocketConfiguration[tag=snippet,indent=0]
An important point about scaling involves using multiple application instances.
Currently, you cannot do that with the simple broker.
@@ -6,65 +6,14 @@ When messages are routed to `@MessageMapping` methods, they are matched with
This is a good convention in web applications and similar to HTTP URLs. However, if
you are more used to messaging conventions, you can switch to using dot (`.`) as the separator.
The following example shows how to do so in Java configuration:
The following example shows how to do so:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
// ...
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setPathMatcher(new AntPathMatcher("."));
registry.enableStompBrokerRelay("/queue", "/topic");
registry.setApplicationDestinationPrefixes("/app");
}
}
----
The following example shows the XML configuration equivalent of the preceding example:
[source,xml,indent=0,subs="verbatim,quotes,attributes"]
----
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
https://www.springframework.org/schema/websocket/spring-websocket.xsd">
<websocket:message-broker application-destination-prefix="/app" path-matcher="pathMatcher">
<websocket:stomp-endpoint path="/stomp"/>
<websocket:stomp-broker-relay prefix="/topic,/queue" />
</websocket:message-broker>
<bean id="pathMatcher" class="org.springframework.util.AntPathMatcher">
<constructor-arg index="0" value="."/>
</bean>
</beans>
----
include-code::./WebSocketConfiguration[tag=snippet,indent=0]
After that, a controller can use a dot (`.`) as the separator in `@MessageMapping` methods,
as the following example shows:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Controller
@MessageMapping("red")
public class RedController {
@MessageMapping("blue.{green}")
public void handleGreen(@DestinationVariable String green) {
// ...
}
}
----
include-code::./RedController[tag=snippet,indent=0]
The client can now send a message to `/app/red.blue.green123`.
@@ -5,56 +5,7 @@ STOMP over WebSocket support is available in the `spring-messaging` and
`spring-websocket` modules. Once you have those dependencies, you can expose a STOMP
endpoint over WebSocket, as the following example shows:
[source,java,indent=0,subs="verbatim,quotes"]
----
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/portfolio"); // <1>
}
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.setApplicationDestinationPrefixes("/app"); // <2>
config.enableSimpleBroker("/topic", "/queue"); // <3>
}
}
----
<1> `/portfolio` is the HTTP URL for the endpoint to which a WebSocket (or SockJS)
client needs to connect for the WebSocket handshake.
<2> STOMP messages whose destination header begins with `/app` are routed to
`@MessageMapping` methods in `@Controller` classes.
<3> Use the built-in message broker for subscriptions and broadcasting and
route messages whose destination header begins with `/topic` or `/queue` to the broker.
The following example shows the XML configuration equivalent of the preceding example:
[source,xml,indent=0,subs="verbatim,quotes,attributes"]
----
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
https://www.springframework.org/schema/websocket/spring-websocket.xsd">
<websocket:message-broker application-destination-prefix="/app">
<websocket:stomp-endpoint path="/portfolio" />
<websocket:simple-broker prefix="/topic, /queue"/>
</websocket:message-broker>
</beans>
----
include-code::./WebSocketConfiguration[tag=snippet,indent=0]
NOTE: For the built-in simple broker, the `/topic` and `/queue` prefixes do not have any special
meaning. They are merely a convention to differentiate between pub-sub versus point-to-point
@@ -36,27 +36,7 @@ connectivity is lost, to the same host and port. If you wish to supply multiple
on each attempt to connect, you can configure a supplier of addresses, instead of a
fixed host and port. The following example shows how to do that:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
// ...
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableStompBrokerRelay("/queue/", "/topic/").setTcpClient(createTcpClient());
registry.setApplicationDestinationPrefixes("/app");
}
private ReactorNettyTcpClient<byte[]> createTcpClient() {
return new ReactorNettyTcpClient<>(
client -> client.addressSupplier(() -> ... ),
new StompReactorNettyCodec());
}
}
----
include-code::./WebSocketConfiguration[tag=snippet,indent=0]
You can also configure the STOMP broker relay with a `virtualHost` property.
The value of this property is set as the `host` header of every `CONNECT` frame
@@ -15,48 +15,7 @@ and run it with STOMP support enabled. Then you can enable the STOMP broker rela
The following example configuration enables a full-featured broker:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/portfolio").withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableStompBrokerRelay("/topic", "/queue");
registry.setApplicationDestinationPrefixes("/app");
}
}
----
The following example shows the XML configuration equivalent of the preceding example:
[source,xml,indent=0,subs="verbatim,quotes,attributes"]
----
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
https://www.springframework.org/schema/websocket/spring-websocket.xsd">
<websocket:message-broker application-destination-prefix="/app">
<websocket:stomp-endpoint path="/portfolio" />
<websocket:sockjs/>
</websocket:stomp-endpoint>
<websocket:stomp-broker-relay prefix="/topic,/queue" />
</websocket:message-broker>
</beans>
----
include-code::./WebSocketConfiguration[tag=snippet,indent=0]
The STOMP broker relay in the preceding configuration is a Spring
{spring-framework-api}/messaging/MessageHandler.html[`MessageHandler`]
@@ -17,29 +17,4 @@ declared in the built-in WebSocket configuration, however, you'll' need `@Lazy`
a cycle between the built-in WebSocket configuration and your
`WebSocketMessageBrokerConfigurer`. For example:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
private TaskScheduler messageBrokerTaskScheduler;
@Autowired
public void setMessageBrokerTaskScheduler(@Lazy TaskScheduler taskScheduler) {
this.messageBrokerTaskScheduler = taskScheduler;
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/queue/", "/topic/")
.setHeartbeatValue(new long[] {10000, 20000})
.setTaskScheduler(this.messageBrokerTaskScheduler);
// ...
}
}
----
include-code::./WebSocketConfiguration[tag=snippet,indent=0]
@@ -6,35 +6,12 @@ of a STOMP connection but not for every client message. Applications can also re
`ChannelInterceptor` to intercept any message and in any part of the processing chain.
The following example shows how to intercept inbound messages from clients:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(new MyChannelInterceptor());
}
}
----
include-code::./WebSocketConfiguration[tag=snippet,indent=0]
A custom `ChannelInterceptor` can use `StompHeaderAccessor` or `SimpMessageHeaderAccessor`
to access information about the message, as the following example shows:
[source,java,indent=0,subs="verbatim,quotes"]
----
public class MyChannelInterceptor implements ChannelInterceptor {
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
StompCommand command = accessor.getStompCommand();
// ...
return message;
}
}
----
include-code::./MyChannelInterceptor[tag=snippet,indent=0]
Applications can also implement `ExecutorChannelInterceptor`, which is a sub-interface
of `ChannelInterceptor` with callbacks in the thread in which the messages are handled.
@@ -60,33 +60,9 @@ to broadcast to subscribed clients.
We can trace the flow through a simple example. Consider the following example, which sets up a server:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
include-code::./WebSocketConfiguration[tag=snippet,indent=0]
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/portfolio");
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app");
registry.enableSimpleBroker("/topic");
}
}
@Controller
public class GreetingController {
@MessageMapping("/greeting")
public String handle(String greeting) {
return "[" + getTimestamp() + ": " + greeting;
}
}
----
include-code::./GreetingController[tag=snippet,indent=0]
The preceding example supports the following flow:
@@ -8,40 +8,7 @@ not match the exact order of publication.
To enable ordered publishing, set the `setPreservePublishOrder` flag as follows:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableWebSocketMessageBroker
public class MyConfig implements WebSocketMessageBrokerConfigurer {
@Override
protected void configureMessageBroker(MessageBrokerRegistry registry) {
// ...
registry.setPreservePublishOrder(true);
}
}
----
The following example shows the XML configuration equivalent of the preceding example:
[source,xml,indent=0,subs="verbatim,quotes,attributes"]
----
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
https://www.springframework.org/schema/websocket/spring-websocket.xsd">
<websocket:message-broker preserve-publish-order="true">
<!-- ... -->
</websocket:message-broker>
</beans>
----
include-code::./PublishOrderWebSocketConfiguration[tag=snippet,indent=0]
When the flag is set, messages within the same client session are published to the
`clientOutboundChannel` one at a time, so that the order of publication is guaranteed.
@@ -54,15 +21,4 @@ of handling may not match the exact order in which they were received.
To enable ordered publishing, set the `setPreserveReceiveOrder` flag as follows:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableWebSocketMessageBroker
public class MyConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.setPreserveReceiveOrder(true);
}
}
----
include-code::./ReceiveOrderWebSocketConfiguration[tag=snippet,indent=0]
@@ -10,43 +10,9 @@ under the WebSocket section.
For Jetty WebSocket servers, customize the `JettyRequestUpgradeStrategy` as follows:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/portfolio").setHandshakeHandler(handshakeHandler());
}
@Bean
public DefaultHandshakeHandler handshakeHandler() {
JettyRequestUpgradeStrategy strategy = new JettyRequestUpgradeStrategy();
strategy.addWebSocketConfigurer(configurable -> {
policy.setInputBufferSize(4 * 8192);
policy.setIdleTimeout(600000);
});
return new DefaultHandshakeHandler(strategy);
}
}
----
include-code::./JettyWebSocketConfiguration[tag=snippet,indent=0]
In addition to WebSocket server properties, there are also STOMP WebSocket transport properties
to customize as follows:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureWebSocketTransport(WebSocketTransportRegistration registry) {
registry.setMessageSizeLimit(4 * 8192);
registry.setTimeToFirstMessage(30000);
}
}
----
include-code::./WebSocketConfiguration[tag=snippet,indent=0]
@@ -0,0 +1,27 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.docs.core.aop.aopajltwspring;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableLoadTimeWeaving;
// tag::snippet[]
@Configuration
@EnableLoadTimeWeaving
public class ApplicationConfiguration {
}
// end::snippet[]
@@ -0,0 +1,35 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.docs.core.aop.aopajltwspring;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableLoadTimeWeaving;
import org.springframework.context.annotation.LoadTimeWeavingConfigurer;
import org.springframework.instrument.classloading.LoadTimeWeaver;
import org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver;
// tag::snippet[]
@Configuration
@EnableLoadTimeWeaving
public class CustomWeaverConfiguration implements LoadTimeWeavingConfigurer {
@Override
public LoadTimeWeaver getLoadTimeWeaver() {
return new ReflectiveLoadTimeWeaver();
}
}
// end::snippet[]
@@ -0,0 +1,27 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.docs.core.aop.aopatconfigurable;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.aspectj.EnableSpringConfigured;
// tag::snippet[]
@Configuration
@EnableSpringConfigured
public class ApplicationConfiguration {
}
// end::snippet[]
@@ -0,0 +1,27 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.docs.core.aop.ataspectj.aopaspectjsupport;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
// tag::snippet[]
@Configuration
@EnableAspectJAutoProxy
public class ApplicationConfiguration {
}
// end::snippet[]
@@ -0,0 +1,31 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.docs.core.aop.ataspectj.aopataspectj;
import org.springframework.context.annotation.Bean;
// tag::snippet[]
public class ApplicationConfiguration {
@Bean
public NotVeryUsefulAspect myAspect() {
NotVeryUsefulAspect myAspect = new NotVeryUsefulAspect();
// Configure properties of the aspect here
return myAspect;
}
}
// end::snippet[]
@@ -0,0 +1,24 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.docs.core.aop.ataspectj.aopataspectj;
import org.aspectj.lang.annotation.Aspect;
// tag::snippet[]
@Aspect
public class NotVeryUsefulAspect {
}
// end::snippet[]
@@ -0,0 +1,37 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.docs.core.aop.ataspectj.aopataspectjexample;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
// tag::snippet[]
@Configuration
@EnableAspectJAutoProxy
public class ApplicationConfiguration {
@Bean
public ConcurrentOperationExecutor concurrentOperationExecutor() {
ConcurrentOperationExecutor executor = new ConcurrentOperationExecutor();
executor.setMaxRetries(3);
executor.setOrder(100);
return executor;
}
}
// end::snippet[]
@@ -0,0 +1,63 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.docs.core.aop.ataspectj.aopataspectjexample;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.Ordered;
import org.springframework.dao.PessimisticLockingFailureException;
// tag::snippet[]
@Aspect
public class ConcurrentOperationExecutor implements Ordered {
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()")
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;
}
}
// end::snippet[]
@@ -0,0 +1,27 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.docs.core.aop.ataspectj.aopataspectjexample.service;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
// tag::snippet[]
@Retention(RetentionPolicy.RUNTIME)
// marker annotation
public @interface Idempotent {
}
// end::snippet[]
@@ -0,0 +1,36 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.docs.core.aop.ataspectj.aopataspectjexample.service;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.springframework.stereotype.Service;
@Service
public class SampleService {
// tag::snippet[]
@Around("execution(* com.xyz..service.*.*(..)) && " +
"@annotation(com.xyz.service.Idempotent)")
public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
// ...
return pjp.proceed(pjp.getArgs());
}
// end::snippet[]
}
@@ -0,0 +1,34 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.docs.core.aopapi.aopapipointcutsregex;
import org.springframework.aop.support.JdkRegexpMethodPointcut;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
// tag::snippet[]
@Configuration
public class JdkRegexpConfiguration {
@Bean
public JdkRegexpMethodPointcut settersAndAbsquatulatePointcut() {
JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
pointcut.setPatterns(".*set.*", ".*absquatulate");
return pointcut;
}
}
// end::snippet[]
@@ -0,0 +1,38 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.docs.core.aopapi.aopapipointcutsregex;
import org.aopalliance.aop.Advice;
import org.springframework.aop.support.RegexpMethodPointcutAdvisor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
// tag::snippet[]
@Configuration
public class RegexpConfiguration {
@Bean
public RegexpMethodPointcutAdvisor settersAndAbsquatulateAdvisor(Advice beanNameOfAopAllianceInterceptor) {
RegexpMethodPointcutAdvisor advisor = new RegexpMethodPointcutAdvisor();
advisor.setAdvice(beanNameOfAopAllianceInterceptor);
advisor.setPatterns(".*set.*", ".*absquatulate");
return advisor;
}
}
// end::snippet[]
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,15 +14,7 @@
* limitations under the License.
*/
package org.springframework.aop.framework;
package org.springframework.docs.core.expressions.expressionsbeandef;
public class ClassWithConstructor {
public ClassWithConstructor(Object object) {
}
public void method() {
}
public class CustomerPreferenceDao {
}
@@ -0,0 +1,35 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.docs.core.expressions.expressionsbeandef;
import org.springframework.beans.factory.annotation.Value;
// tag::snippet[]
public class FieldValueTestBean {
@Value("#{ systemProperties['user.region'] }")
private String defaultLocale;
public void setDefaultLocale(String defaultLocale) {
this.defaultLocale = defaultLocale;
}
public String getDefaultLocale() {
return this.defaultLocale;
}
}
// end::snippet[]
@@ -0,0 +1,20 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.docs.core.expressions.expressionsbeandef;
public class MovieFinder {
}
@@ -0,0 +1,36 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.docs.core.expressions.expressionsbeandef;
import org.springframework.beans.factory.annotation.Value;
// tag::snippet[]
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;
}
// ...
}
// end::snippet[]
@@ -0,0 +1,35 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.docs.core.expressions.expressionsbeandef;
import org.springframework.beans.factory.annotation.Value;
// tag::snippet[]
public class NumberGuess {
private double randomNumber;
@Value("#{ T(java.lang.Math).random() * 100.0 }")
public void setRandomNumber(double randomNumber) {
this.randomNumber = randomNumber;
}
public double getRandomNumber() {
return randomNumber;
}
}
// end::snippet[]
@@ -0,0 +1,35 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.docs.core.expressions.expressionsbeandef;
import org.springframework.beans.factory.annotation.Value;
// tag::snippet[]
public class PropertyValueTestBean {
private String defaultLocale;
@Value("#{ systemProperties['user.region'] }")
public void setDefaultLocale(String defaultLocale) {
this.defaultLocale = defaultLocale;
}
public String getDefaultLocale() {
return this.defaultLocale;
}
}
// end::snippet[]
@@ -0,0 +1,35 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.docs.core.expressions.expressionsbeandef;
import org.springframework.beans.factory.annotation.Value;
// tag::snippet[]
public class ShapeGuess {
private double initialShapeSeed;
@Value("#{ numberGuess.randomNumber }")
public void setInitialShapeSeed(double initialShapeSeed) {
this.initialShapeSeed = initialShapeSeed;
}
public double getInitialShapeSeed() {
return initialShapeSeed;
}
}
// end::snippet[]
@@ -0,0 +1,37 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.docs.core.expressions.expressionsbeandef;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
// tag::snippet[]
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;
}
// ...
}
// end::snippet[]
@@ -0,0 +1,58 @@
/*
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.docs.core.validation.formatconfiguringformattingglobaldatetimeformat;
import java.time.format.DateTimeFormatter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.datetime.DateFormatter;
import org.springframework.format.datetime.DateFormatterRegistrar;
import org.springframework.format.datetime.standard.DateTimeFormatterRegistrar;
import org.springframework.format.number.NumberFormatAnnotationFormatterFactory;
import org.springframework.format.support.DefaultFormattingConversionService;
import org.springframework.format.support.FormattingConversionService;
// tag::snippet[]
@Configuration
public class ApplicationConfiguration {
@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;
}
}
// end::snippet[]
@@ -0,0 +1,20 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.docs.dataaccess.jdbc.jdbcJdbcTemplateidioms;
public interface CorporateEventDao {
}
@@ -0,0 +1,20 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.docs.dataaccess.jdbc.jdbcJdbcTemplateidioms;
public interface CorporateEventRepository {
}
@@ -0,0 +1,32 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.docs.dataaccess.jdbc.jdbcJdbcTemplateidioms;
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
public class JdbcCorporateEventDao implements CorporateEventDao {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
// JDBC-backed implementations of the methods on the CorporateEventDao follow...
}
@@ -0,0 +1,30 @@
package org.springframework.docs.dataaccess.jdbc.jdbcJdbcTemplateidioms;
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class JdbcCorporateEventDaoConfiguration {
// tag::snippet[]
@Bean
JdbcCorporateEventDao corporateEventDao(DataSource dataSource) {
return new JdbcCorporateEventDao();
}
@Bean(destroyMethod = "close")
BasicDataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
dataSource.setUrl("jdbc:hsqldb:hsql://localhost:");
dataSource.setUsername("sa");
dataSource.setPassword("");
return dataSource;
}
// end::snippet[]
}
@@ -0,0 +1,37 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.docs.dataaccess.jdbc.jdbcJdbcTemplateidioms;
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
// tag::snippet[]
@Repository
public class JdbcCorporateEventRepository implements CorporateEventRepository {
private JdbcTemplate jdbcTemplate;
// Implicitly autowire the DataSource constructor parameter
public JdbcCorporateEventRepository(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
// JDBC-backed implementations of the methods on the CorporateEventRepository follow...
}
// end::snippet[]

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