Compare commits

..
267 Commits
Author SHA1 Message Date
Brian Clozel 2e6e620ee1 Merge branch '7.0.x' 2026-09-18 17:30:55 +02:00
Sagar Chanchal cb9ce4d9e2 Fix out-of-bounds read for truncated percent-escape in opaque host
The opaque-host percent-escape validation guard reads
input.codePointAt(i + 2) after only checking 'input.length() - i < 2',
so an input such as 'foo://%4' throws StringIndexOutOfBoundsException
instead of reporting a validation error.

Fix the bounds guard to require two code points after '%' and check
ASCII hex digits rather than ASCII digits, matching the URL spec, where
invalid percent-escapes in opaque hosts are validation errors, not
failures.

Signed-off-by: Sagar Chanchal <Sagarr2112@gmail.com>
2026-09-18 17:26:03 +02:00
Brian Clozel f6e53b76ae Polishing contribution
See gh-37272
2026-09-18 17:21:08 +02:00
Eymen f9f9186478 Expose matched PathPattern as MVC request attribute
Signed-off-by: Eymen <eymenonar123@gmail.com>
2026-09-18 17:21:08 +02:00
Brian Clozel 12cf9b7d54 Build against Hibernate 7.4 Javadoc 2026-09-18 17:15:57 +02:00
Sam Brannen 96cbca1db5 Merge branch '7.0.x' 2026-09-18 16:48:36 +02:00
Sam Brannen 2aa36fea64 Polish contribution
See gh-37005
2026-09-18 16:48:27 +02:00
Sam Brannen 6f4021ad87 Merge branch '7.0.x' 2026-09-18 16:37:49 +02:00
flinter fc10d200bf Document combining @⁠Retryable with proxy-based features
Add a "Combining @⁠Retryable with Other Proxy-Based Features" section
to the resilience reference documentation, covering interaction with
@⁠Transactional, @⁠Cacheable, and @⁠Async, as well as advice order
customization between @⁠Retryable and @⁠Async via
@⁠EnableResilientMethods(order) and @⁠EnableAsync(order).

Also document the advice chain semantics in @⁠Retryable Javadoc, add
cross-reference TIP blocks in the @⁠Async, @⁠Cacheable, and
@⁠Transactional reference sections, add a @⁠Cacheable combination test
to RetryInterceptorTests, and add RetryableTransactionTests in
spring-tx for the @⁠Transactional combination.

See gh-35584
Closes gh-37005

Signed-off-by: jhan0121 <jhan0121@gmail.com>
2026-09-18 16:37:25 +02:00
Sam Brannen bd1616b0aa Merge branch '7.0.x' 2026-09-18 15:54:08 +02:00
guanchengang b49252ed66 Avoid useless queue ops in ConcurrentLruCache.clear()
ConcurrentLruCache.clear() previously drained write operations before
cleaning up the cache. This could re-enqueue nodes that clear() was
about to remove, causing useless evictionQueue operations. Now clear()
iterates the cache values directly, removes and marks nodes as removed
first, and drains write operations afterward. Since AddTask fails
silently after a node is marked removed, this avoids the no-op work
and improves performance.

See gh-37287

Closes gh-37292

Signed-off-by: Chengang Guan <guanchengang@qq.com>
2026-09-18 15:53:18 +02:00
머랭 71965c1c85 Align DispatcherServlet bean detection logs
RequestToViewNameTranslator and FlashMapManager logged the simple class
name at TRACE and the object at DEBUG. Swap them to match the multipart
and locale resolver logs. This follows Spring's logging guidance to keep
DEBUG output compact.

See: https://github.com/spring-projects/spring-framework/wiki/Logging

Closes gh-37296

Signed-off-by: cookie-meringue <daehyeon3351@gmail.com>
2026-09-18 15:08:00 +02:00
cookie-meringue 3d023b3ef5 Remove unchecked cast in DispatcherServlet
Match the attribute snapshot parameter type to its sole caller.
This removes the unchecked cast and warning suppression.

Signed-off-by: cookie-meringue <daehyeon3351@gmail.com>
2026-09-18 14:43:51 +02:00
Yanming Zhou 00291a5172 Polish PathPattern to refine null-safety
Signed-off-by: Yanming Zhou <zhouyanming@gmail.com>
2026-09-18 14:30:08 +02:00
Yanming Zhou c1ba31c40f Use "isEmpty" to check if a collection is empty
Signed-off-by: Yanming Zhou <zhouyanming@gmail.com>
2026-09-18 14:14:36 +02:00
Brian Clozel 4cdd314850 Merge branch '7.0.x' 2026-09-18 14:09:23 +02:00
Brian Clozel 5d32f6719a Fix flaky test in RetryTemplateTests
`RetryTemplateTests` can, under load, break the build because of a flaky
test: "retryableWithTimeoutExceededAfterSecondRetry".
This usually happens when many cores are busy and the timeout check
runs before each retry attempt.
This commit extends the timeout to avoid such cases and failures.
2026-09-18 14:05:38 +02:00
Sam Brannen 884975b7eb Polish AOT generated resources support
This commit fixes Javadoc typos, grammar, and incorrect/swapped
references; avoids redundant self-validation in GeneratedResource's
createOrValidate(); adds missing Javadoc; and adds/renames tests
accordingly.

See gh-35862
2026-09-18 13:25:21 +02:00
Brian Clozel e8eb2b6751 Add Java 27 to CI testing matrix 2026-09-17 18:28:22 +02:00
Stéphane Nicoll 496ed729a0 Add support for AOT generated resources
This commit updates the AOT infrastructure to handle generated resources
in a similar fashion than generated classes: naming conventions, feature
prefixes, and uniqueness are applied.

The new abstraction also provides a more explicit contract that guides
users to either create the resource or create it if it does not exist
and validate its content if it does.

As part of this change ClassNameGenerator has been renamed to
NameGenerator as it is responsible to generate names for both classes
and resources.

Closes gh-35862
2026-09-17 18:22:26 +02:00
Brian Clozel 7ca362a003 Merge branch '7.0.x' 2026-09-17 16:52:42 +02:00
Brian Clozel 8107a2b561 Polishing contribution
See gh-37285
2026-09-17 16:48:59 +02:00
seonghun lee 94afeedad6 Enforce disk usage limit when spilling part to disk
Previously, PartGenerator only enforced maxDiskUsagePerPart for body
buffers that arrived after a part had switched to file storage. The
content accumulated in memory that triggered the switch was written
to disk without any check against maxDiskUsagePerPart. As a result,
a part whose last body buffer caused the in-memory overflow was
accepted in full, even when its total size exceeded the configured
disk usage limit.

This commit checks the accumulated byte count against
maxDiskUsagePerPart before switching to file storage, and emits a
DataBufferLimitException, consistent with the existing check for
subsequent buffers.

Closes gh-35099

Signed-off-by: seonghun lee <harrisleesh@gmail.com>
2026-09-17 16:48:59 +02:00
Brian Clozel c3360ba7da Switch to Micrometer SNAPSHOTs
See gh-37288
2026-09-17 16:07:26 +02:00
Brian Clozel 3222c1b3a8 Deprecate PropertyAccessorUtils formally
Now that all `PropertyAccessorUtils` has been removed and replaced by
`PropertyPath` and local private methods, we can officially deprecate
this utility class and remove it in the future.

Closes gh-37275
2026-09-17 15:37:40 +02:00
Brian Clozel cd110ad14e Use PropertyPath instead of utility methods
Prior to this commit, `DataBinder` and the property accessor
hierarchy relied on `PropertyAccessorUtils` and several
independent scanners for property paths.
This commit migrates all of them to `PropertyPath`, so
there is exactly one parser deciding what a well-formed property
path is, used identically for policy checks and for actual
navigation.

This removes long standing protected methods like
A`getPropertyAccessorForPropertyPathi` and `getFinalPath` from
`bstractNestablePropertyAccessor`. The path is now parsed exactly
once per public entry point, via the new `resolvePropertyPath`, which
returns a `ResolvedProperty`. Then, property navigation walks
the parsed segment list rather than re-scanning partial strings.
Any subclass overriding the removed method will need to adapt.

This removal initially conflicted with gh-37252 (maxNestedPathDepth
support). The public configuration remains but the actual behavior
changed; it is replaced with `PropertyPath.Options` which enforces
limit right after parsing, before property navigation begins.
The exception thrown changes from `InvalidPropertyException` to
`InvalidPropertyPathException`.

This commmit also reverts the `map[']` / `map["]` quoting behavior
from gh-36765 as it is incompatible with the new grammar.

See gh-37275
2026-09-17 15:37:40 +02:00
Brian Clozel 0d079ea435 Extract bean property path support in PropertyPath
Prior to this commit, bean property path support would be duplicated in
`AbstractNestablePropertyAccessor` and `PropertyAccessorUtils`. This
means they both supported the parsing, validation and extraction of path
segments. Implementations were not always in sync and could cause
issues.

This commit introduces a new `PropertyPath` type that holds the
canonical form of the property path and the parsed path segments for
property access. This implements an efficient parser that rejects
invalid property paths early if they don't match the new grammar.

`PropertyPath.parse(String, Options)` additionally accepts a maximum
nesting depth, rejecting an excessively deep path immediately after
parsing and before any navigation of an object graph begins.
This moves the implementation introduced in gh-37252, but keeps the
public configuration in place.

`InvalidPropertyPathException` is introduced to report a syntactically
invalid path, as distinct from a syntactically valid path that
happens not to resolve against a particular target object (see
`NotReadablePropertyException` and `NotWritablePropertyException`).
It extends `PropertyAccessException`, but not `InvalidPropertyException`.
Malformed paths should be collected into
`PropertyBatchUpdateException` alongside other per-property failures.

See gh-37275
2026-09-17 15:37:40 +02:00
Sam Brannen e2cc271e0c Merge branch '7.0.x' 2026-09-17 12:52:59 +02:00
Sam Brannen bb7ea37f1b Drain pending writes before evicting in ConcurrentLruCache.clear()
Prior to this commit, clear() polled the eviction queue to remove
entries and only afterward drained the pending write operations queue.

Consequently, a put() whose AddTask had not yet been linked into the
eviction queue -- for example, because it lost the race to self-drain
while clear() held the eviction lock -- would only be applied by that
trailing drain, linking the entry into the eviction queue right after
clear() had already finished removing everything it could see.

The practical effect was that an entry already fully added to the cache
could still be present immediately after clear() returned, with no
further concurrent activity required at that point.

To address that, this commit revises clear() so that it drains the
pending write operations queue before polling the eviction queue, so
any write that was already queued gets cleaned up along with everything
else. However, a put() that is genuinely concurrent with an in-progress
clear() call can still survive, which is consistent with the cache's
weak-consistency design.

Thanks to @guanchengang for raising gh-37286, which prompted this fix.

Closes gh-37287
2026-09-17 12:45:42 +02:00
Sam Brannen 8ab525f6f0 Add a configurable limit for maximum nested property path depth
AbstractNestablePropertyAccessor resolves a nested property path
recursively, one recursive call per path segment, and there was
previously no limit on the nesting depth. Consequently, a sufficiently
deeply nested property path -- for example, against a self-referential
type -- could exhaust the current thread's call stack, resulting in a
StackOverflowError which lacks useful diagnostics for developers
attempting to assess what went wrong.

Note that the existing autoGrowCollectionLimit bounds array and
collection growth, not path depth.

The same is true for constructor binding via DataBinder.construct(),
which constructs a nested constructor argument recursively through a
nested property path. Path segments are constrained to declared
constructor parameters there, but a self-referential type nonetheless
permits an arbitrarily deep path.

With this commit, each property accessor tracks the number of nested
properties traversed to reach the object that it wraps, and an
InvalidPropertyException is thrown once the configured (or default)
maxNestedPathDepth limit is exceeded, with a message that reports the
configured limit. The limit applies regardless of autoGrowNestedPaths,
since resolving an existing deep object graph recurses in the same
manner as auto-growing one. Tracking the depth per property accessor
rather than threading it through the recursion allows the recursion to
dispatch through the protected
getPropertyAccessorForPropertyPath(String) method, which subclasses may
override, and avoids deriving the depth from the nested path, which
would require rescanning an ever longer path prefix at each level.

Constructor binding likewise tracks the nesting depth while
constructing nested objects as well as indexed and mapped elements, and
throws the same InvalidPropertyException once the limit is exceeded.

The maxNestedPathDepth (which defaults to 100) can be configured on a
per-use-case basis via ConfigurablePropertyAccessor or DataBinder,
which applies it to constructor binding directly and supplies it to the
property accessor via its binding result. In contrast to the auto-grow
collection limit, which is unlimited on a plain accessor, the nesting
depth is bounded by default even for programmatic property access,
since a large array or collection can be perfectly legitimate whereas a
deeply nested property path effectively never is.

Specifying zero for the maxNestedPathDepth disables support for nested
property paths altogether while continuing to allow simple, indexed,
and mapped property access, which is a reasonable way to constrain data
binding for a target object that is not intended to be traversed (such
as a flat DTO). However, negative values for maxNestedPathDepth are
always rejected.

Closes gh-37252
2026-09-16 17:52:04 +02:00
rstoyanchev 33988a4621 Refine lost connection checks in DefaultHandlerExceptionResolver
This commit adds additional "disconnected client" checks for
HttpMessageNotReadableException and HttpMessageNotWriteableException,
both of which wrap I/O errors and could be due to a lost connection.

Closes gh-37151
2026-09-15 16:26:37 +01:00
Brian Clozel 5b424c0431 Merge branch '7.0.x' 2026-09-15 10:06:25 +02:00
Brian Clozel 9e0d1c734e Upgrade to artifactory-deploy-action 0.0.5 2026-09-15 10:06:11 +02:00
Sam Brannen 6504e75669 Merge branch '7.0.x' 2026-09-14 18:17:15 +02:00
Sam Brannen 3178df92bd Consistently use while (true) instead of for (;;) across the codebase 2026-09-14 18:15:02 +02:00
Sam Brannen 566887d573 Merge branch '7.0.x' 2026-09-14 18:07:12 +02:00
김준형 c1aa1b7405 Prevent double size decrement in ConcurrentLruCache
markAsRemoved() transitions a node to the removed state and decrements
the current size, but it did not check whether the node had already
been removed. The eviction path and an explicit removal can process
the same node in sequence: when a write drain runs a queued AddTask
whose eviction polls a node that a concurrent remove(K) has already
taken out of the cache, the eviction decrements the size, and the
queued RemovalTask for the same node decrements it again. The sibling
transition markForRemoval() guards against invalid transitions; this one
did not.

Each extra decrement makes currentSize permanently smaller than the
number of cached entries, so eviction stops triggering and the cache
exceeds its capacity for good, silently. A bounded two-thread stress
run accumulates the drift reliably: before the change the cache
stabilized far above its capacity in 20 out of 20 runs.

markAsRemoved() now returns without decrementing when the entry is
already in the removed state, mirroring the guard in markForRemoval().
The removed state is terminal, so each node is counted down exactly
once. The new test races explicit removals against eviction and then
verifies that the cache converges back to its capacity; it also
asserts that the racing thread ran and terminated cleanly.

Closes gh-37268

Signed-off-by: junhyeong9812 <pickjog@gmail.com>
2026-09-14 18:06:47 +02:00
Sam Brannen 0148c4ccde Merge branch '7.0.x' 2026-09-14 17:39:11 +02:00
Sam Brannen 8c1b366bda Polish contribution
See gh-37261
2026-09-14 17:38:39 +02:00
guanchengang d571c4097d Lazily handle setClientInfo/setNetworkTimeout in LazyConnectionDataSourceProxy
This commit extends LazyConnectionInvocationHandler to cache early
calls to:

- setClientInfo(String, String)
- setNetworkTimeout(Executor, int)

These methods now defer physical connection acquisition until Statement
creation, consistent with existing lazy behavior for autoCommit,
readOnly, transactionIsolation, catalog, and schema.

We also accept and lazily cache calls to setNetworkTimeout() even when
the provided Executor is null. Since some JDBC driver implementations
completely ignore the Executor parameter (or fall back to a default
executor), we cannot meaningfully validate or handle a null Executor
before the physical connection is obtained.

getClientInfo() and getClientInfo(String) remain non-lazy (triggering
immediate connection fetch), because they are read operations whose
values cannot be reliably cached due to driver defaults, pooled
connection remnants, or external session modifications.

setClientInfo(Properties) also remains non-lazy. The reason is that JDBC
driver implementations are inconsistent. Some treat it as overwrite,
others as append/merge. To guarantee behavior identical to non-lazy
execution across all drivers, we choose not to cache or replay it,
avoiding any risk of semantic mismatch.

See gh-37258
Closes gh-37261

Signed-off-by: Chengang Guan <guanchengang@qq.com>
2026-09-14 17:31:37 +02:00
Sam Brannen dabd63770f Add dedicated unit test for getBean(String, ParameterizedTypeReference)
See gh-34687
See gh-37047
2026-09-14 16:59:18 +02:00
Juergen Hoeller fe2617582a Upgrade to Groovy 5.1.2, Hibernate ORM 7.4.8, Jackson 3.1.6/2.21.6, Woodstox 7.2.2 2026-09-14 16:27:52 +02:00
Juergen Hoeller c4376c04b1 Merge branch '7.0.x'
# Conflicts:
#	framework-platform/framework-platform.gradle
2026-09-14 16:22:05 +02:00
Yanming Zhou 74a6c1c328 Fix BeanFactory.getBean(String, ParameterizedTypeReference) to respect AOP proxy
Before this commit, the implementation uses `ResolvableType::isInstance` which doesn't take JDK proxy into account, it fails if `proxyTargetClass = false`:

```
Bean named 'userDao' is expected to be of type 'org.springframework.cache.config.ExpressionCachingIntegrationTests$BaseDao<org.springframework.cache.config.ExpressionCachingIntegrationTests$User>' but was actually of type 'org.springframework.cache.config.$Proxy53'
org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'userDao' is expected to be of type 'org.springframework.cache.config.ExpressionCachingIntegrationTests$BaseDao<org.springframework.cache.config.ExpressionCachingIntegrationTests$User>' but was actually of type 'org.springframework.cache.config.$Proxy53'
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:212)
	at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1312)
	at org.springframework.cache.config.ExpressionCachingIntegrationTests.expressionIsCacheBasedOnActualMethod(ExpressionCachingIntegrationTests.java:42)
```

See gh-34687
Closes gh-37047

Signed-off-by: Yanming Zhou <zhouyanming@gmail.com>
2026-09-14 15:57:07 +02:00
Juergen Hoeller 803517c0cb Upgrade to Tomcat 11.0.25, Jetty 12.1.13, Netty 4.2.18, Protobuf 4.36.1 2026-09-14 15:55:16 +02:00
Juergen Hoeller 8a511726cd Consistent JPA/Hibernate transaction interoperability
Closes gh-37273
2026-09-14 15:54:43 +02:00
Sam Brannen 05a1075b69 Merge branch '7.0.x' 2026-09-14 15:02:41 +02:00
Hyunwoo Jung 4898ed3ad8 Fix message supplier coverage in AssertTests
Closes gh-37255

Signed-off-by: Hyunwoo Jung <hyunwoojung@kakao.com>
2026-09-14 15:01:52 +02:00
Sam Brannen 9f8a476f94 Merge branch '7.0.x' 2026-09-14 14:51:44 +02:00
Sam Brannen f768641c08 Polish contribution
See gh-37254
2026-09-14 14:50:45 +02:00
Hyunwoo Jung 01a23e32b5 Fix CollectionToCollectionConverterTests
Closes gh-37254

Signed-off-by: Hyunwoo Jung <hyunwoojung@kakao.com>
2026-09-14 14:41:06 +02:00
Sam Brannen afdbebee70 Merge branch '7.0.x' 2026-09-14 14:39:51 +02:00
Hyunwoo Jung 9d1156f1fd Avoid redundant filtering in FilteredMap.size()
Since keySet() already applies the filter, size() evaluated the
predicate twice for every accepted key.

This commit uses delegate.keySet() instead, avoiding the second
evaluation as well as a FilteredSet and FilteredIterator allocation.

Closes gh-37256

Signed-off-by: Hyunwoo Jung <hyunwoojung@kakao.com>
2026-09-14 14:31:42 +02:00
Sébastien Deleuze a099debca9 Upgrade to Kotlin 2.4.20
Closes gh-37271
2026-09-11 18:03:52 +02:00
Brian Clozel c1d4a76692 Merge branch '7.0.x' 2026-09-10 16:59:22 +02:00
Brian Clozel bcd78a2ccc Add implementation note in DefaultAsyncServerResponse
See gh-37257
2026-09-10 16:59:06 +02:00
Sam Brannen 8d50a5b724 Merge branch '7.0.x' 2026-09-10 16:27:35 +02:00
Sam Brannen b96592e4af Guard CharSequence-based logging methods in LogAccessor
Prior to this commit, LogAccessor's CharSequence-based logging methods
delegated directly to the corresponding method on the underlying
commons-logging Log instance without first checking whether the target
level was enabled. This differed from the Supplier-based overloads,
which have checked isXxxEnabled() before delegating since Spring
Framework 5.2.9.

That asymmetry was harmless as long as spring-jcl supplied the
underlying Log implementation, since its SLF4J adapter itself checked
the level before rendering the message. However, since Spring Framework
7 replaced spring-jcl with Apache commons-logging, whose SLF4J adapters
call String.valueOf(message) unconditionally, any CharSequence argument
-- most notably a LogMessage supplied via LogMessage.format(...) or
LogMessage.of(...) -- is now rendered eagerly, even when the
corresponding level is disabled. Since LogMessage exists specifically
to defer that work, and the idiom is used extensively throughout the
framework and its portfolio projects, this leads to unnecessary
computation and allocation whenever logging is disabled.

To address this, this commit adds the same isXxxEnabled() guard to all
twelve CharSequence-based methods in LogAccessor, matching the
existing Supplier-based overloads and making LogAccessor's laziness
guarantee independent of the underlying Log implementation.

This commit also introduces LogAccessorTests, which verifies that a
lazily rendering LogMessage passed to one of the CharSequence-based
methods is only rendered when the corresponding level is enabled.

See gh-25741
Closes gh-37266
2026-09-10 16:17:34 +02:00
Sam Brannen 9a396c8ed4 Improve Javadoc for LogAccessor 2026-09-10 16:08:23 +02:00
Brian Clozel b40ed77e94 Follow up changes in Servlet multipart support
Apply similar changes to the Servlet multipart message converter.

See gh-37264
2026-09-10 14:41:13 +02:00
Brian Clozel d1402d3c7f Merge branch '7.0.x' 2026-09-10 14:34:01 +02:00
Brian Clozel 1ea92339e2 Emit empty multipart body for all cases
Prior to this commit, gh-30953 fixed a case where multipart file parts
were not emitted properly when the body itself is empty.
There are other cases like this, depending on the order and slicing of
data buffers received by the parser. Here, a buffer containing the
entire boundary would not cause an empty file part to be emitted and
instead switch to the next header.

This commit ensures that empty file parts are always emitted as they
should.

Fixes gh-37264
2026-09-10 14:23:34 +02:00
Artyom Tsvirko adbc8ceeab Ignore invalid SSE retry field
Per the SSE specification, a "retry" field whose value is not made up
solely of ASCII digits must be ignored. ServerSentEventHttpMessageReader
passed the value straight to Long.parseLong, so "retry:none", an empty
"retry:", or a value too large for a long raised NumberFormatException
and terminated the event stream. A client cannot control what a server
sends, so an unusable reconnection hint would kill an otherwise healthy
subscription.

Signed-off-by: Artyom Tsvirko <36863599+lArtiquel@users.noreply.github.com>
2026-09-09 15:08:23 +02:00
Brian Clozel 60b9f4cd3a Merge branch '7.0.x' 2026-09-09 14:56:35 +02:00
junhyeong9812 d3d8e05fa9 Complete empty Uni instances from the Mutiny reactive adapter
The Mutiny Uni adapter registers its empty-value supplier as
Uni.createFrom().nothing(), which returns a Uni that never signals an
item, a failure, or completion. Every sibling registration supplies an
empty value that completes immediately: Mono.empty(), Maybe.empty(),
Completable.complete(), and CompletableDeferred(null); the Multi
registration uses Multi.createFrom().empty() as well.

ReactiveAdapter.toPublisher(null) substitutes that empty value whenever
a null source needs to be adapted, for example when a WebFlux handler
method with a Uni return type returns null. With a never-completing
empty value the resulting Publisher emits no signal at all, so the
response is never written and the request hangs until a timeout,
whereas the same handler declared with Mono completes empty. The
adapter also becomes asymmetric with its own fromPublisher function,
which adapts an empty Publisher to a Uni that completes with a null
item.

The supplier now uses Uni.createFrom().nullItem(), whose conversion to
a Publisher completes without emitting an item, matching the sibling
adapters and the round-trip through fromPublisher. The descriptor is
shared by the Mutiny 1 and Mutiny 2 registrations, so both paths are
covered.

Signed-off-by: junhyeong9812 <pickjog@gmail.com>
2026-09-09 14:47:10 +02:00
Brian Clozel aa655718a8 Switch to Reactor 2026.0.0 SNAPSHOTs
See gh-37263
2026-09-09 11:38:48 +02:00
Brian Clozel 06f2fba5ed Merge branch '7.0.x' 2026-09-09 10:40:54 +02:00
Brian Clozel 280861e7ee Add missing proxy hints for Hibernate 8's MutationOrSelectionQuery
Prior to this commit, Hibernate 8 types extending
`MutationOrSelectionQuery` would fail proxying at runtime in native
images because reflection hints were not registered at build time.

While the `MutationOrSelectionQueryImpl` case can be solved with an
additional proxy hint, `NativeMutationOrSelectionQueryImpl` is
impossible to solve that way due to multi interface mismatch.
This was found in gh-36878 and handled with a fallback proxy.

In this case, native image will throw a
`MissingReflectionRegistrationError` - but obviously we cannot depend on
this type in JVM applications. `MissingReflectionRegistrationError`
extends `LinkageError`, which we will use along
IllegalArgumentException` to detect that the proxying operation failed
and that we should use the fallback.

This commit also register a proxy hint for the said fallback.

Fixes gh-37251
2026-09-09 10:35:25 +02:00
Tran Ngoc Nhan e8f5e31219 Remove redundant whitespace
Closes gh-37262

Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>
2026-09-09 10:18:37 +02:00
Sam Brannen 572850bdcf Merge branch '7.0.x' 2026-09-08 13:30:50 +02:00
MoonFruitandSam Brannen e2fae069dc Avoid exception in ConversionService.canConvert() for Enum targets
Prior to this commit, ConversionService#canConvert(Class, Class)
threw an IllegalArgumentException when invoked with Enum.class as the
target type (i.e., `canConvert(String.class, Enum.class)`), because
ConverterFactory#getConverter() in StringToEnumConverterFactory and
IntegerToEnumConverterFactory eagerly resolved the concrete enum type.

To address that, StringToEnumConverterFactory and
IntegerToEnumConverterFactory now implement ConditionalConverter so
that matches() can reject non-concrete-enum targets before
getConverter() is ever invoked.

Closes gh-34532

Signed-off-by: MoonFruit <dkmoonfruit@gmail.com>

Co-authored-by: Sam Brannen <104798+sbrannen@users.noreply.github.com>
2026-09-08 13:29:30 +02:00
Sam Brannen 1bc5bb0f90 Make canonical SpelParserConfiguration constructor package-private
The 9-arg canonical constructor for SpelParserConfiguration was
recently introduced to support the new maximumNestingDepth property in
7.1. However, this feature has not yet been released, and in the
interim we introduced a builder API which supersedes the use of those
constructors.

Since no released version has ever exposed this constructor publicly,
this commit converts it to package-private in favor of exclusively
using the builder to construct instances which need to override the
default value for maximumNestingDepth.

See gh-36723
See gh-37187
See gh-37190
2026-09-08 12:59:07 +02:00
Brian Clozel 30a07ed551 Merge branch '7.0.x' 2026-09-08 11:59:09 +02:00
Brian Clozel 3c6b001349 Reuse existing async timeout in DefaultAsyncServerResponse
Prior to this commit, calling `DefaultAsyncServerResponse.writeAsync()`
would  unconditionally create a new `AsyncWebRequest` and install it
on the `WebAsyncManager`, even when one is already present for the
current request.
The functional web framework can do such a thing when returning a
`ServerResponse.async(future)` from a `HandlerFunction`; the
`HandlerFunctionAdapter` does install an async web request already.

This means that the async timeout configured at the application level
would be ignored and instead falling back to the Servlet container
default.

This commit makes the `DefaultAsyncServerResponse` skip async web
request creation it there is an existing one.

Fixes gh-37257
2026-09-08 11:48:40 +02:00
Brian Clozel 4c8c6409a2 Polishing contribution
See gh-37202
2026-09-07 11:57:05 +02:00
Sagar Chanchal 8f4fcb6cbc Emit multipart parts with empty bodies in PartGenerator
Parts were emitted only from PartListener.onBody(buffer, last=true), so a
part with an empty body (for example a blank form field, or a trailing
empty part) was silently dropped from the resulting MultiValueMap, and
was indistinguishable from an absent field.

This carries over the fix from the reactive DefaultPartHttpMessageReader
(spring-framework#30953): State gains an onComplete() callback that emits
the part, also when it has an empty body. It is invoked when a new part
begins, and when parsing completes for the final part.

Signed-off-by: Sagar Chanchal <Sagarr2112@gmail.com>
2026-09-07 11:42:44 +02:00
Sam Brannen 2028c54d01 Polish TableMetaDataContextTests
See gh-37014
2026-09-07 10:48:19 +02:00
김준형 e06482ad51 Reject overlapping declared and generated key columns in SimpleJdbcInsert
When a column was declared via usingColumns() and also listed in
usingGeneratedKeyColumns(), TableMetaDataContext.reconcileColumnsToUse
accepted the declared list as-is: the generated key column was rendered
into the INSERT statement and counted against the parameter values,
even though the database is expected to generate its value.

Such an overlap is a configuration error, so it is now rejected at
compile time with an InvalidDataAccessApiUsageException naming the
offending columns in their declared spelling, consistent with the
existing validation in AbstractJdbcInsert.compile(). Matching is
case-insensitive, mirroring the normalization used for auto-discovered
columns; the auto-discovery path itself is unchanged and continues to
exclude generated key columns silently.

The tests cover the rejection, its message, a case-insensitive variant,
and the untouched non-overlapping declared path.

Closes gh-37014

Signed-off-by: junhyeong9812 <pickjog@gmail.com>
2026-09-07 10:34:38 +02:00
Sam Brannen 99f2ccc2ec Merge branch '7.0.x' 2026-09-07 10:18:58 +02:00
Hyunwoo Jung 4acd6d6a7e Fix missing assertion in DefaultClientResponseTests
Closes gh-37248

Signed-off-by: Hyunwoo Jung <hyunwoojung@kakao.com>
2026-09-07 10:15:52 +02:00
Brian Clozel 1aedfa5d5a Merge branch '7.0.x' 2026-09-07 10:00:25 +02:00
Brian Clozel 26174aa9a0 Update Jar metadata to make Gradle build reproducible
This commit makes use of the Java specification version in the Jar
metadata to make build easier to reproduce on a different environment.

Closes gh-37250
2026-09-07 09:59:12 +02:00
Brian Clozel 93528f096a Merge branch '7.0.x' 2026-09-07 09:35:59 +02:00
Brian Clozel 486db005a1 Add missing Hibernate reflection hints
This commit adds the missing reflection hints for Hibernate 8 support:
`PersistenceUnitInfoDescriptor` and `StatelessSession`.

Fixes gh-37247
Fixes gh-37249
2026-09-07 09:34:38 +02:00
Sam Brannen e74054be0a Merge branch '7.0.x' 2026-09-05 14:52:07 +02:00
junhyeong9812 6e260bc78e Sort duplicate key codes in SQLErrorCodes
Every error code setter in SQLErrorCodes sorts its array with
StringUtils.sortStringArray, and CustomSQLErrorCodesTranslation does
the same, because SQLErrorCodeSQLExceptionTranslator looks the codes
up with Arrays.binarySearch. setDuplicateKeyCodes was the only setter
that stored the supplied array as-is.

With an unsorted list of duplicate key codes, the binary search finds
or misses a code depending on where the values happen to sit: for
codes it misses, the translator silently falls through to the SQLState
fallback and reports a DataIntegrityViolationException, or fails to
translate at all, instead of the configured DuplicateKeyException. The
default sql-error-codes.xml is not affected since its lists are
already sorted; the mismatch surfaces for custom configurations, for
example codes of different digit lengths listed in numeric order.

setDuplicateKeyCodes now sorts the array like all sibling setters. The
new test covers an unsorted custom list whose codes previously hit or
missed depending on their position.

Closes gh-37235

Signed-off-by: junhyeong9812 <pickjog@gmail.com>
2026-09-05 14:50:26 +02:00
Sam Brannen 42fe29218b Merge branch '7.0.x' 2026-09-05 13:42:41 +02:00
Sam Brannen 35d8c4d06f Align synthesized annotation toString() with JDK for NaN/Infinity
Closes gh-37244
2026-09-05 13:37:06 +02:00
Sam Brannen c1d241928c Rename maxAttemptsReached() to maxElapsedTimeReached() and organize tests 2026-09-05 13:08:12 +02:00
dxbjavid e92bf76055 validate samesite attribute in ResponseCookie
Signed-off-by: dxbjavid <dxbjavid@gmail.com>
2026-09-04 18:27:48 +02:00
Sunghyun Shin 19fdc0b77c Migrate responseBodyAdvice test to Jackson 3 converter
Migrate RequestMappingHandlerAdapterTests#responseBodyAdvice from the
deprecated MappingJackson2HttpMessageConverter to
JacksonJsonHttpMessageConverter.

The test advice now implements ResponseBodyAdvice directly and returns a
map body that is written by the selected converter.

This maintains the test coverage for gh-22638, verifying that a
ControllerAdvice implementing both ResponseBodyAdvice and
RequestBodyAdvice is not registered twice.

Signed-off-by: Sunghyun Shin <froggy0m0a@gmail.com>
2026-09-04 18:21:37 +02:00
Clayton Walker 82018e1510 Fix configuration-cache compatibility with ArchRule task
Signed-off-by: Clayton Walker <clayton.m.walker@gmail.com>
2026-09-04 17:42:06 +02:00
Brian Clozel 9bedb06b9b Merge branch '7.0.x' 2026-09-04 17:34:11 +02:00
junhyeong9812 4a803961bc Generate compilable code for non-finite floating-point values
PrimitiveDelegate generated code via "$LF" for Float and "(double) $L"
for Double, which emit the value's toString() verbatim. For NaN and
infinities this produced non-compilable source such as "NaNF" or
"(double) Infinity", causing the generated AOT sources to fail to
compile.

Detect NaN (via isNaN, since NaN is never equal to itself) and the
positive/negative infinities, emitting the corresponding constant
field references (Float.NaN, Double.POSITIVE_INFINITY, etc.) through
the "$T" placeholder. Finite values keep their existing handling.

Signed-off-by: junhyeong9812 <pickjog@gmail.com>
2026-09-04 17:23:41 +02:00
Brian Clozel 54b3a8c868 Merge branch '7.0.x' 2026-09-04 17:17:23 +02:00
junhyeong9812 2b5229ff8f Fix reserveMethodNames to reserve each supplied name
GeneratedClass.reserveMethodNames(String...) passed the entire varargs
array to MethodName.of() inside the per-name loop instead of the current
element. Since MethodName.of(String...) joins all parts into a single
camel-case name, reserving two or more names (for example "apply" and
"test") produced "applyTest", and the per-element check
Assert.state(generatedName.equals(reservedMethodName)) failed with an
IllegalStateException. Single-name calls worked only by accident.

Reserve each supplied name individually by passing the loop variable.

Signed-off-by: junhyeong9812 <pickjog@gmail.com>
2026-09-04 17:14:25 +02:00
Brian Clozel 59df1a7031 Merge branch '7.0.x' 2026-09-04 17:09:36 +02:00
Tran Ngoc Nhan 85c8bb674c Handle zero readTimeout in JdkClientHttpRequestFactory
Closes gh-37232

Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>
2026-09-04 17:05:12 +02:00
Tran Ngoc Nhan c21ea9e249 Add Validation section examples
Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>
2026-09-04 16:57:58 +02:00
Brian Clozel acbae80205 Merge branch '7.0.x' 2026-09-04 16:43:22 +02:00
kodacme 2d478f7d9b Log broker availability events as String messages
Prior to this commit, AbstractBrokerMessageHandler logged the
BrokerAvailabilityEvent object directly at INFO level. With a structured
JSON logging layout, the event could be serialized as an object rather
than via toString(), causing the layout to traverse the event source
(a SimpleBrokerMessageHandler) object graph. That graph contains a
cyclic reference through the client inbound channel executor's thread
factory, which fails serialization at the maximum nesting depth.

This commit logs the event's toString() representation instead, keeping
the same operational signal while preventing structured logging layouts
from traversing framework internals.

Signed-off-by: kodacme <kodac.saito@kodac.me>
2026-09-04 16:37:30 +02:00
Brian Clozel 60e5abff7f Enforce "data: " prefix for outgoing SSE data payloads
Prior to this commit, SSE support in Spring would write payloads with
the "data:" prefix (without space). While this is OK with the standard,
this makes it harder for implementations to support reading and writing
payloads with Spring (the round trip use case).

This commit introduces a breaking change and now enforces "data: " in
all variants. This has the potential of breaking some low level test
suites with text/plain or custom media types, but this should overall
make the situation better for developers.

Closes gh-37242
2026-09-04 16:21:13 +02:00
Sam Brannen 21bb726934 Suppress removal warnings for Derby DB
See gh-36045
2026-09-04 15:47:03 +02:00
Patrick Strawderman 658c263cf6 Use immutable map for static cache in TypeDescriptor
Switch to Map.of for the static commonTypesCache field for immutability.

Signed-off-by: Patrick Strawderman <pstrawderman@netflix.com>
2026-09-04 15:38:40 +02:00
JunHwan 34b9815215 Polish DateTimeFormatterRegistrar to refine null-safety
Narrow the scope of the @⁠SuppressWarnings("NullAway") annotation in
DateTimeFormatterRegistrar from the class level to a single, new
getFactory(Type) accessor.

The `factories` map is a private, final EnumMap that is fully populated
for every `Type` in the constructor and never mutated afterward, so the
suppression only needs to cover that one lookup instead of masking
unrelated issues across the whole class.

Closes gh-37225

Signed-off-by: Junhwan Choi <devjunsday@gmail.com>C
2026-09-04 15:32:23 +02:00
Sam Brannen 21ee87448c Merge branch '7.0.x' 2026-09-04 15:28:48 +02:00
Hyunwoo Jung 8c151f5887 Move BackOff tests to correct package
Closes gh-37241

Signed-off-by: Hyunwoo Jung <hyunwoojung@kakao.com>
2026-09-04 15:27:27 +02:00
Sam Brannen c0519b9bbf Merge branch '7.0.x' 2026-09-04 15:14:36 +02:00
Sam Brannen ea2a26206c Polish contribution
See gh-36914
2026-09-04 15:13:35 +02:00
junhyeong9812 2b276311eb Honor sourceStart offset in AbstractXMLStreamReader#getTextCharacters
Prior to this commit, AbstractXMLStreamReader.getTextCharacters(int
sourceStart, char[], int, int) capped the copy length with
Math.min(length, source.length), ignoring sourceStart. When sourceStart
> 0 and sourceStart + length exceeds the text length, System.arraycopy
read past the end of the source array and threw
ArrayIndexOutOfBoundsException, contrary to the
XMLStreamReader#getTextCharacters contract (copy up to length
characters starting at sourceStart and return the number copied).

To address that, this commit caps the length by the number of
characters remaining from sourceStart.

Closes gh-36914

Signed-off-by: junhyeong9812 <pickjog@gmail.com>
2026-09-04 15:13:35 +02:00
Sam Brannen b5a358019f Polishing 2026-09-04 15:13:35 +02:00
Brian Clozel c39d48a261 Merge branch '7.0.x' 2026-09-04 15:12:07 +02:00
Sebastien Tardif e0144da4fe Replace thread-unsafe SimpleDateFormat with DateTimeFormatter
The static SimpleDateFormat instance in
AbstractMockHttpServletRequestBuilder is shared across all
instances. SimpleDateFormat.format() mutates internal Calendar
state and is not thread-safe, which can produce corrupt date
strings or ArrayIndexOutOfBoundsException when tests run in
parallel.

Replace with DateTimeFormatter which is immutable and thread-safe.

Signed-off-by: Sebastien Tardif <SebTardif@ncf.ca>
2026-09-04 15:01:05 +02:00
Raphael Schweikert 130b0ec50c Parse RFC 9651-like date headers
RFC 9651 specifies @«timestamp» as a new format for date headers.
The Deprecation header as specified in RFC 9745, for example, makes use of it.
Make sure this can be parsed using `HttpHeaders#getFirstDate` and `HttpHeaders#getFirstZonedDateTime`

Signed-off-by: Raphael Schweikert <any@sabberworm.com>
2026-09-04 14:28:29 +02:00
Brian Clozel 815911b39c Upgrade XJC Gradle plugin 2026-09-04 14:17:11 +02:00
Hyunwoo Jung d130601a74 Avoid using Project objects as dependency notation
Gradle 9.6 deprecates passing a Project instance as dependency notation,
which currently causes the build to emit deprecation warnings and will
become an error in Gradle 10.

This commit updates KotlinConventions and RuntimeHintsAgentPlugin to use
DependencyFactory#createProjectDependency instead.

Signed-off-by: Hyunwoo Jung <hyunwoojung@kakao.com>
2026-09-04 14:17:11 +02:00
Sam Brannen 4f0b8e4205 Merge branch '7.0.x' 2026-09-04 14:15:54 +02:00
Tran Ngoc Nhan 8ced135f49 Move SimpleMessageConverterTests to correct package
Closes gh-37165

Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>
2026-09-04 14:14:07 +02:00
Sam Brannen 42cffc2a55 Merge branch '7.0.x' 2026-09-04 13:55:03 +02:00
Tran Ngoc Nhan 5b33c7e2ce Add missing closing parenthesis in WebFlux config reference example
Closes gh-37163

Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>
2026-09-04 13:53:15 +02:00
Mateo Maza d798317a2b Add WebFlux OpenTelemetry observation convention
Closes gh-37131

Signed-off-by: Mateo Maza <mateomaza.github@gmail.com>
2026-09-04 10:21:44 +02:00
Brian Clozel d550ab1313 Count in memory buffered data against limit in PartGeenrator
The new PartGenerator for parsing multipart requests supports buffering
the content in memory and switching to a file after a configured size.
More specifically, when a multipart part exceeds maxInMemorySize and
InMemoryState switches it over to FileState, the bytes that were
already buffered in memory are flushed to the temp file.

Prior to this commit, this was done via FileState.writeBuffer(), meaning
that the in memory buffered data would not be counted against the
configured limit for writing to a file.
This commit fixes this by writing buffered data with FileState.onBody().

Fixes gh-37238
2026-09-04 09:24:15 +02:00
Brian Clozel 136dddb67d Merge branch '7.0.x' 2026-09-03 17:38:29 +02:00
Brian Clozel 6316c06a97 Polishing contribution
See gh-36919
2026-09-03 17:36:26 +02:00
junhyeong9812 ad83d5ebd9 Render parameter type names in ClassFileMethodMetadata
ClassFileMethodMetadata's toString() formatted method parameter types
as packageName() + "." + displayName(). Since ClassDesc.packageName()
is empty for primitive, array and default-package types, these rendered
with a leading dot (for example ".int" and ".String[]") and reference
arrays lost their package. The return type already uses
ClassFileAnnotationMetadata.resolveTypeName(); apply it to the
parameters as well.

Signed-off-by: junhyeong9812 <pickjog@gmail.com>
2026-09-03 17:23:18 +02:00
froggy0m0 0de56c41df Remove stray TODO in exception handler tests
Closes gh-37003

Signed-off-by: Sunghyun Shin <79225728+froggy0m0@users.noreply.github.com>
2026-09-03 13:55:58 +02:00
Yanming Zhou 29602e95b6 Polish DefaultListableBeanFactoryTests
Fix that scope is not overridden and asserted.

Closes gh-36941

Signed-off-by: Yanming Zhou <zhouyanming@gmail.com>
2026-09-03 13:53:47 +02:00
Yanming Zhou 94458c05e0 Polish method parameter name
The parameter type is `TransactionManager` not `PlatformTransactionManager`.

Closes gh-36702

Signed-off-by: Yanming Zhou <zhouyanming@gmail.com>
2026-09-03 13:45:46 +02:00
Sam Brannen 351a6d8f61 Merge branch '7.0.x' 2026-09-03 13:23:27 +02:00
Sam Brannen 34ed7a5e22 Test correct scenarios in tests 2026-09-03 13:20:29 +02:00
Sam Brannen 4a92dd6ed1 Reorder tests 2026-09-03 13:19:21 +02:00
Sam Brannen 7cdb326623 Declare redirectedUrl argument as @⁠Nullable
MockMvcResultMatchers.forwardedUrl() already accepts a @⁠Nullable
expected value to assert that no forwarding occurred, but its
counterpart redirectedUrl() previously did not, even though the
underlying assertEquals() comparison is null-safe and behaves the same
way for redirects.

To address that, this commit adds the same @⁠Nullable declaration to
redirectedUrl() and documents the null semantics in the Javadoc for
both methods.

Closes gh-37230
2026-09-03 13:19:04 +02:00
Sam Brannen ff9192fa8f Merge branch '7.0.x' 2026-09-03 11:49:10 +02:00
Chengang Guan f2fe79e56d Use ArrayList instead of LinkedList in CompositeRetryListener
CompositeRetryListener currently uses LinkedList to store registered
listeners. The primary operation on this list is iteration (traversing
all listeners on every retry lifecycle event). ArrayList provides
better iteration performance due to better cache locality and lower
memory overhead.

Closes gh-37231

Signed-off-by: Chengang Guan <guanchengang@qq.com>
2026-09-03 11:45:36 +02:00
Hyunwoo Jung ea42275dcf Fix typos in Javadoc
Closes gh-37229

Signed-off-by: Hyunwoo Jung <hyunwoojung@kakao.com>
2026-09-03 11:45:00 +02:00
Sam Brannen 3be70836ce Merge branch '7.0.x' 2026-09-03 11:43:00 +02:00
Sam Brannen 7a0612dd4f Polishing
See gh-36789
2026-09-03 11:42:07 +02:00
Sam Brannen ee7a0d48c5 Polish contribution
This commit introduces additional unit tests for CallMetaDataContext's
function return parameter matching in reconcileParameters(), verifying
that the declared return parameter is correctly resolved regardless of
whether it is declared before or after an additional OUT parameter.

See gh-37206
2026-09-03 11:34:44 +02:00
junhyeong9812 ec6b925191 Normalize function return parameter lookup in CallMetaDataContext
CallMetaDataContext.reconcileParameters() keys the map of declared
parameters by lowerCase(provider.parameterNameToUse(name)), but the
branch that matches the return parameter reported by the database
metadata did not apply the same rule. It looked up the function return
name as declared (original case) and fell back to the first declared
OUT parameter name with a plain toLowerCase(), without the provider
transformation that strips the '@' prefix on SQL Server and Sybase.

The first lookup therefore always missed on Oracle, so the fallback
silently used whichever OUT parameter was declared first. Declaring an
additional OUT parameter before the return parameter of a function made
that parameter double as the return slot: the declared return parameter
was dropped from the call parameters, the wrong parameter was bound at
position 1, and executeFunction() returned the value of the other out
parameter. On SQL Server, a procedure compiled with withReturnValue()
and an '@'-prefixed OUT parameter declared before the return parameter
failed with InvalidDataAccessApiUsageException because neither lookup
could find the declared parameter.

The return parameter branch now looks up the metadata-derived name
first and normalizes both the function return name and the first OUT
parameter fallback with the same rule as the declared parameter map.
Tests cover both declaration orders for an Oracle function and for a
SQL Server procedure with a return value.

Closes gh-37206

Signed-off-by: junhyeong9812 <pickjog@gmail.com>
2026-09-02 17:28:04 +02:00
Sam Brannen 751aa19671 Upgrade to backport-bot v0.0.3 2026-09-02 17:28:04 +02:00
Sam Brannen 1911647ab5 Eagerly reject property paths with unbalanced brackets
As a follow up to 1b56f58999, this commit introduces
hasUnbalancedBrackets() in PropertyAccessorUtils, which
AbstractNestablePropertyAccessor uses to reject property paths with
unbalanced '[' or ']' brackets by throwing a
NotReadablePropertyException with an informative message, thereby
improving diagnostics for users.

See gh-36999
2026-09-02 17:25:16 +02:00
Brian Clozel 2ce9563b3c Merge branch '7.0.x' 2026-09-02 14:45:26 +02:00
Brian Clozel c07da25bdf Update recommendations for web data binding
Most of the documentation updates were already done in gh-36803, this
completes the section with some information on the property path syntax
supported by allowFields/disallowFields.

Closes gh-36789
2026-09-02 14:43:51 +02:00
rstoyanchev e2235b702a Merge branch '7.0.x' 2026-09-02 10:00:49 +01:00
rstoyanchev 7da197de66 More updates for Data Binding doc restructuring
Closes gh-37228
2026-09-02 10:00:14 +01:00
Brian Clozel 558e8955f0 Merge branch '7.0.x' 2026-09-01 11:20:33 +02:00
Brian Clozel 04b3bdc1b3 Fix reconnection attempt count in ReactorNettyTcpClient
Prior to this commit, the reconnection attempt count provided to the
reconnect strategy would not give the updated, incremented value but
instead the previous one.

This commit fixes this and ensures the value is incremented before it's
given to the strategy.

Fixes gh-37223
2026-09-01 11:18:53 +02:00
Brian Clozel 46913bca91 Revert "Remove unnessary args after Kotlin 2.4 upgrade"
This reverts commit ce00aac748.
2026-09-01 09:54:16 +02:00
Brian Clozel 5ab70bb036 Merge branch '7.0.x' 2026-08-31 20:24:39 +02:00
Brian Clozel f3764f6d62 Fix Gradle metadata for source and javadoc elements
This commit fixes the published Gradle metadata to list proper entries
for `javadocElements` and `sourceElements`.

Fixes gh-37209
2026-08-31 20:23:18 +02:00
Brian Clozel ce00aac748 Remove unnessary args after Kotlin 2.4 upgrade
See gh-37074
2026-08-31 18:46:06 +02:00
Brian Clozel 990d1a8370 Fix Derby warnings
See gh-36045
2026-08-31 18:45:41 +02:00
Brian Clozel 5df0c2dc0c Merge branch '7.0.x' 2026-08-31 18:39:19 +02:00
Brian Clozel 41db0fe5a3 Preserve original headers and cookies when mutating client response
Prior to this commit, the `DefaultClientResponseBuilder` would assume
that an original client HTTP response, when mutated, would not be reused
nor read anymore. While this is the advised use case, there was some
inconsistency with the builder API here when mutating: some data like
the response status would copied, but the HTTP headers and cookies would
refer directly to the previous entries, making all changes visible to
the previous response instance.

This commit ensures that deep copies are performed when mutating a
client response with the builder API.

Fixes gh-37086
2026-08-31 18:36:09 +02:00
Juergen Hoeller 772d361cf2 Consistently check ultimate singleton target
See gh-37207
2026-08-31 18:25:58 +02:00
Juergen Hoeller 5c4bf226bb Merge branch '7.0.x' 2026-08-31 17:31:45 +02:00
Juergen Hoeller 8a64fe9c45 Fix accidental bypass of registerStoredProcedureParameter
Closes gh-37221
2026-08-31 17:30:56 +02:00
Artyom Tsvirko 2028eb3694 Handle MIME type parameter names case-insensitively
MIME type parameter names are case-insensitive, and MimeType already
stores them in a LinkedCaseInsensitiveMap. Several code paths, however,
still compared them with case-sensitive String.equals().

As a result, MimeType.hashCode() disagreed with MimeType.equals() for
parameter names that differ only in case, breaking the equals/hashCode
contract: text/plain;FOO=bar and text/plain;foo=bar are equal but hash
differently, so one is not found in a hash-based collection holding the
other. MimeType.compareTo() had the same blind spot for the charset
parameter.

MediaType was affected in two further ways: an out-of-range quality
value escaped validation when spelled Q=, and removeQualityValue() left
a Q= parameter in place.

Signed-off-by: Artyom Tsvirko <36863599+lArtiquel@users.noreply.github.com>
2026-08-31 17:30:39 +02:00
Sam Brannen ee19b96ee9 Merge branch '7.0.x' 2026-08-31 17:17:01 +02:00
kogun dcc24a2325 Avoid int overflow in expiration calculations in MockMvc and FlashMap
FlashMap.startExpirationPeriod(int) and MockMvcWebConnection's cookie
handling both multiplied an int number of seconds by 1000 without
widening to long. Above 2_147_483 seconds (about 24.9 days) the
multiplication overflows to a negative offset, so the computed
expiration time lands in the past.

For FlashMap, a flash map configured through
AbstractFlashMapManager.setFlashMapTimeout(int) with a large timeout is
then treated as expired immediately. For MockMvcWebConnection, a cookie
with a large max-age is removed from the CookieManager instead of being
stored.

This applies the same widening already used for this pattern in
gh-25613.

Closes gh-37208

Signed-off-by: kogun <akogun@gmail.com>
2026-08-31 17:05:20 +02:00
junhyeong9812andYash bc66622342 Reject MIME type parameters differing only in case
MIME type parameter names are case-insensitive, but MimeTypeParser
accumulates parameters in a case-sensitive LinkedHashMap. As a result,
duplicate parameters differing only in case (such as "charset" and
"CHARSET") were not rejected and were silently collapsed to the last
value by the case-insensitive parameter map of MimeType.

Accumulate parameters in a LinkedCaseInsensitiveMap so that duplicates
differing only in case map to the same key and are rejected consistently
with exact duplicates.

Signed-off-by: junhyeong9812 <pickjog@gmail.com>
Co-authored-by: Yash <190389954+yashsiwacha@users.noreply.github.com>
2026-08-31 16:23:20 +02:00
Sam Brannen dd110d4604 Revise contribution
See gh-37219
2026-08-31 15:17:13 +02:00
Yanming Zhou 513ca9d9e5 Polish ScheduledAnnotationBeanPostProcessor to refine null-safety
Closes gh-37219

Signed-off-by: Yanming Zhou <zhouyanming@gmail.com>
2026-08-31 15:10:57 +02:00
Sam Brannen 4b5afaff53 Revise "Close contexts when clearing test context cache"
This commit picks up where 97067f9c9c left off by removing the four
trailing collection-clearing calls in clear(), which are now redundant
since the removal loop already empties contextMap, hierarchyMap,
contextUsageMap, and unusedContexts as an invariant. This commit also
adds regression tests in LruContextCacheTests for closing a context
hierarchy via clear() and reset(), asserting bottom-up close order with
Mockito.inOrder(), and confirming that getParentContextCount() and
getContextUsageCount() return to zero.

See gh-36825
2026-08-31 13:21:18 +02:00
Will-thom 97067f9c9c Close contexts when clearing test context cache
This updates the TestContext Framework cache so that clearing the
cache also closes cached ConfigurableApplicationContext instances
instead of only dropping the internal references. The implementation
reuses the existing removal path, preserving the hierarchy-aware close
behavior already used by cache eviction/removal.

The ContextCache contract now documents the close behavior for
clear(), and LruContextCacheTests covers both clear() and reset(),
since reset() delegates to clear().

See gh-26196
Closes gh-36825

Signed-off-by: Will-thom <116388885+Will-thom@users.noreply.github.com>
2026-08-31 13:07:54 +02:00
Brian Clozel 5524fc6a55 Merge branch '7.0.x' 2026-08-31 12:58:00 +02:00
Brian Clozel b823aed17c Upgrade to Nullability plugin 0.0.15
See gh-37188
2026-08-31 12:57:32 +02:00
Manu Sridharan fce57adc31 Update to NullAway 0.14.0 and fix new warnings
See gh-37188

Signed-off-by: Manu Sridharan <msridhar@gmail.com>
2026-08-31 12:57:32 +02:00
Juergen Hoeller 49d955650a Upgrade to Groovy 5.1.1 and Hibernate ORM 7.4.7 2026-08-31 11:08:44 +02:00
Juergen Hoeller b020aca2eb Merge branch '7.0.x' 2026-08-31 11:02:55 +02:00
Juergen Hoeller 8e783e2ec9 Upgrade to Checkstyle 14.1 2026-08-31 10:58:06 +02:00
Juergen Hoeller 178eb17191 Remove lock around transform step
Closes gh-37199
2026-08-31 10:52:35 +02:00
Juergen Hoeller 354ade9f40 Merge branch '7.0.x' 2026-08-29 20:02:43 +02:00
Juergen Hoeller 3656241ff1 Use singleton target as cache key for destruction purposes
Closes gh-37207
2026-08-29 20:01:40 +02:00
Juergen Hoeller 133a372f91 Reduce lock-guarded boolean field to thread-local
Closes gh-37199
2026-08-29 20:00:24 +02:00
Sam Brannen 1b56f58999 Use depth-aware bracket parsing in PropertyAccessorUtils
Previously, canonicalPropertyName() located the end of a [key]
expression via a naive indexOf("]") search, while
AbstractNestablePropertyAccessor.getPropertyNameKeyEnd() -- used during
actual property resolution -- tracked bracket nesting depth. This meant
the two methods could disagree on the canonical form of a property
path whose map key itself contains bracket characters (e.g.,
map['key[0]']).

Similarly, getNestedPropertySeparatorIndex() tracked whether a dot
separator occurs inside a [key] expression using a simple boolean
toggle that flips on both '[' and ']', which produces an incorrect
result when a key contains an odd net count of inner bracket
characters.

To address those inconsistencies, this commit extracts the private
getPropertyNameKeyEnd() method from AbstractNestablePropertyAccessor to
a package-private static utility method in PropertyAccessorUtils, so
that canonicalPropertyName() can reuse the same depth-aware bracket
matching, and getNestedPropertySeparatorIndex() has been reworked to
track bracket nesting depth instead of toggling a boolean flag.

Closes gh-36999
2026-08-28 19:11:20 +02:00
Brian Clozel 3eb93a4873 Merge branch '7.0.x' 2026-08-28 18:36:12 +02:00
Gimin Kim 74a4b1a694 Preserve async API version resolver order
Prior to this commit, the `DefaultApiVersionStrategy` reactive variant
would attempt to resolve the API version with the first resolver that
replies with a version. This contradicts the API that registers
resolvers in order.
This commit ensures that each resolver is called in order.

Signed-off-by: Gimin Kim <138752849+Gimini-3@users.noreply.github.com>
2026-08-28 18:33:41 +02:00
jungh8n 09548c0874 Upgrade HtmlUnit and Selenium dependencies
HtmlUnit 5 moved its Cookie type to org.htmlunit.http, so
Spring Test's HtmlUnit integration now uses the new API.

Constraint: Keep HtmlUnit and htmlunit3-driver versions compatible.
Rejected: Upgrade HtmlUnit alone | driver requires HtmlUnit 5.4.0.
Confidence: high
Scope-risk: narrow
Directive: Keep HtmlUnit and the driver version aligned.
Tested: JAVA_HOME=/opt/homebrew/opt/openjdk@25 ./gradlew build
Not-tested: None
Signed-off-by: jungh8n <jh981113@naver.com>
2026-08-28 18:20:04 +02:00
Brian Clozel 491859b5ce Merge branch '7.0.x' 2026-08-28 18:15:06 +02:00
Hyunsik Kang 6e5cf0ce45 Do not release body buffers already handed to the sink
BodyState.flush() emits every queued buffer and only clears the queue
afterwards, so a cancellation arriving while it emits makes dispose()
release buffers whose ownership has already been transferred to the sink.
Such a buffer is then released twice: once by the parser, and once by the
downstream consumer or the discard hook. With Netty, body buffers are
slices of the inbound buffer, so the second release frees the inbound
buffer prematurely, which surfaces as

  IllegalReferenceCountException: refCnt: 0, decrement: 1
    io.netty.handler.codec.http.DefaultHttpContent.release
    reactor.netty.channel.FluxReceive.drainReceiver

when reactor-netty releases its own share right after onNext.

Remove each buffer from the queue before emitting it, mirroring what
enqueue() already does, so that dispose() only ever releases buffers the
parser still owns.

Signed-off-by: Hyunsik Kang <cj848@hanmail.net>
2026-08-28 16:26:54 +02:00
Hyunsik Kang a99f4dd43c Release queued body token buffers on multipart cancel
When a multipart subscriber cancels while MultipartParser has already
emitted body tokens beyond the downstream demand, those tokens are held
in the Flux.create sink queue (and in downstream operator queues such
as windowUntil). On cancellation, Reactor discards the queued tokens,
but BodyToken is not a DataBuffer, so the buffers inside the discarded
tokens are never released and Netty reports "LEAK: ByteBuf.release()
was not called before it's garbage-collected".

Register a doOnDiscard hook for BodyToken in MultipartParser.parse() so
that a discarded body token releases its buffer, both in the sink queue
and in any downstream operator queue that supports discarding.

Closes gh-37115

Signed-off-by: Hyunsik Kang <cj848@hanmail.net>
2026-08-28 16:26:47 +02:00
Brian Clozel 5d6a56fe4a Polishing CacheControl behavior
This commit builds on the previous commit and ensures that
"must-understand" is only used with "no-store". This check is performed
at runtime as a staged interface/builder would be a major breaking
change for a behavior that is highlighted as "SHOULD" in the
specification.

This commit also performs similar runtime checks for:
* cache-public + cache-private
* cache-public + no-store

See gh-36918
2026-08-28 15:49:57 +02:00
heka1024 d9e240b37d Add Cache-Control must-understand directive
Signed-off-by: heka1024 <heka1024@gmail.com>
2026-08-28 15:10:37 +02:00
Yanming Zhou 2588fb078e Polish ConcurrentLruCache to refine null-safety
Signed-off-by: Yanming Zhou <zhouyanming@gmail.com>
2026-08-28 14:53:40 +02:00
Istvan Verhas 752193fca9 Refactor JettyDataBuffer with new JettyVirtualDataBuffer
This commits simplifies the Jetty buffer support by consolidating
the shared logic and delegating operations to the parent class
thanks to the new `JettyVirtualDataBuffer`.

Signed-off-by: Istvan Verhas <vi@mocker.guru>
2026-08-28 14:42:41 +02:00
junhyung8795 17e0daf8a1 Use computeIfAbsent in CommandLineArgs.addOptionArg
Signed-off-by: junhyung8795 <junhyung8795@naver.com>
2026-08-27 17:00:41 +02:00
Brian Clozel d883602919 Remove mentions of the Derby database support
See gh-36045
2026-08-27 16:57:58 +02:00
Philippe Marschall 1b354f2705 Deprecate Derby support
Deprecate Derby support since Apache Derby is retired since 2023.

Signed-off-by: Philippe Marschall <philippe.marschall@gmail.com>
2026-08-27 16:50:58 +02:00
Brian Clozel 7daf1013aa Merge branch '7.0.x' 2026-08-27 14:37:23 +02:00
Brian Clozel 3170dd5714 Fix mock servlet request behavior with session ids
Prior to this commit,
`MockHttpServletRequest.isRequestedSessionIdValid()` would return `true`
by default and could only be changed manually with a setter. This does
not align with the Servlet spec because of 1) its default value and 2)
it does not react to `changeSessionId()` calls.

This commit fixes that behavior while still allowing "manual" booleans
being set here.

Fixes gh-36631
2026-08-27 14:34:49 +02:00
Sam Brannen 6e9534df4d Limit bracket depth in PropertyEditorRegistrySupport
Previously, PropertyEditorRegistrySupport.addStrippedPropertyPaths()
recursively enumerated every combination of stripped/retained [key]
segments in a property path, producing 2^n - 1 variants for a path
with n bracket pairs.

To address that, this commit limits the recursion at a depth of 8,
preserving existing behavior for realistic property paths while
bounding the work done for paths with an unusually large number of
bracket segments.

Closes gh-37020
2026-08-26 16:36:40 +02:00
Sam Brannen a00fb1b5ae Avoid redundant object construction in DataBinder.createMap()
Previously, createMap() invoked createIndexedValue() – and therefore
createObject() for non-simple value types – once per matching parameter
name rather than once per distinct map key, causing redundant nested
object construction for map entries whose value type has multiple
constructor parameters.

To address that, this commit aligns createMap() with createList() and
createArray() by skipping construction for keys that have already been
resolved.

Closes gh-37019
2026-08-26 13:34:57 +02:00
rstoyanchev b28569119f Merge branch '7.0.x' 2026-08-24 17:21:41 +01:00
rstoyanchev 495fd6b3a5 Polishing contribution
See gh-37099
2026-08-24 17:18:17 +01:00
Garvit Joshi 8d4208f030 Allow null contextPath in ServerHttpRequest.Builder
The builder method required a non-null contextPath while the underlying
field, MutatedServerHttpRequest constructor, and RequestPath.parse all
accept null and treat it the same as an empty string. Relax the method
parameter to @Nullable so callers can clear the context path directly.

Closes gh-37099

Signed-off-by: Garvit Joshi <garvitjoshi9@gmail.com>
2026-08-24 17:18:17 +01:00
rstoyanchev 82cf15c60f ProtobufJsonEncoder actually supports streaming
Closes gh-37158
2026-08-24 16:48:48 +01:00
Sam Brannen 79a75a5762 Polish contribution
See gh-36935
2026-08-24 12:36:28 +02:00
seonwoojung ac95b96c21 Suppress CGLIB validation WARN for lifecycle callbacks
When CglibAopProxy validates the target class, it logs a WARN-level
message for each public final method that implements an interface,
suggesting to use interface-based JDK proxies instead. For final
methods inherited from Spring's configuration callback interfaces
(InitializingBean, DisposableBean, Aware sub-interfaces, Closeable,
AutoCloseable) that recommendation is misleading: those methods are
container-driven, are not advised by typical application pointcuts, and
the user usually cannot make them non-final.

The validation now only emits the WARN-level message when at least one
user-defined interface declares the method. Methods inherited
exclusively from configuration callback interfaces fall back to the
existing DEBUG diagnostic.

In addition, the isConfigurationCallbackInterface() method has been
extracted from ProxyProcessorSupport into a static package-private
method in AopProxyUtils with the same signature, and
ProxyProcessorSupport and CglibAopProxy now delegate to the new shared
static utility in AopProxyUtils.

See gh-35365
Closes gh-36935

Signed-off-by: seonwoo_jung <laborlawseon@kap.kr>
Signed-off-by: seonwooj0810 <seonwooj0810@gmail.com>
2026-08-24 12:18:48 +02:00
Sam Brannen 91eb42645e Deprecate SpelParserConfiguration constructors in favor of the builder API
Since we now have an official builder API for SpelParserConfiguration
(introduced in 7.0.10), this commit follows through on the plan stated
in that commit's Javadoc and formally deprecates all 9 overloaded
constructors in SpelParserConfiguration, thereby encouraging users to
benefit from the simplicity of the builder API -- or
SpelParserConfiguration.withDefaults() for the common case -- instead
of having to migrate to the latest-and-greatest full constructor every
time a new configuration property is introduced.

The no-arg constructor points users to withDefaults(), and all other
constructors -- including the canonical 9-parameter constructor --
point to the builder API. Builder.build() has been annotated with
@SuppressWarnings("deprecation"), since it is the sole legitimate
internal caller of the now-deprecated canonical constructor.

The SpelParserConfigurationTests.LegacyConstructorTests nested class
(and its sibling builderAppliesSameDefaultsAsNoArgConstructor() test
method) are annotated with @SuppressWarnings("deprecation"), since they
exist specifically to provide regression coverage for the deprecated
constructors. IndexingTests.MaxAutoGrowSizeTests and
SpelParserTests.MaxNestingDepthTests, on the other hand, were both
introduced before the builder API existed and had no such need for the
legacy constructors, so they have been converted to use the builder API
instead, avoiding the need for any deprecation suppression there.

See gh-37187
Closes gh-37190
2026-08-22 13:28:57 +02:00
Sam Brannen aee39843f8 Merge branch '7.0.x' 2026-08-22 12:09:56 +02:00
Sam Brannen 37c8f41633 Introduce a builder for SpelParserConfiguration
Prior to this commit, SpelParserConfiguration exposed 8 overloaded
constructors that accumulated over time as new configuration options
were introduced (auto-grow support since 3.0, maximumExpressionLength
in 5.2.25, maximumOperations in 6.2.19, and maximumBigPowerBits in
7.0.9), culminating in an 8-parameter constructor. This made call sites
hard to read due to unlabeled sequences of booleans and ints, and it
forced users who wanted to override a single setting to also supply
every other value explicitly.

To address that, this commit introduces a builder API in
SpelParserConfiguration, following the pattern already established by
SimpleEvaluationContext's builder API.

Specifically, SpelParserConfiguration.builder() returns a Builder that
is pre-populated with the same defaults as the no-arg constructor,
including the SpringProperties-driven overrides for the default
compiler mode, maximum operations, and maximum big-power bits -- the
latter two are only resolved lazily in build(), so that overriding them
via the builder never triggers an unnecessary SpringProperties lookup.
Each property has a dedicated, named setter (compilerMode(),
compilerClassLoader(), maximumAutoGrowSize(),
maximumExpressionLength(), maximumOperations(), maximumBigPowerBits()),
and the two auto-grow flags are exposed as simple no-arg opt-ins
(autoGrowNullReferences(), autoGrowCollections()) since they both
default to false. build() delegates to the existing canonical
constructor, so validation and defaults remain centralized in one
place.

In addition, a new SpelParserConfiguration.withDefaults() factory
method has been introduced as shorthand for
SpelParserConfiguration.builder().build(), for the common case where
none of the builder's defaults need to be overridden.

As the one deliberate exception to matching the no-arg constructor's
defaults, the builder defaults maximumAutoGrowSize to 256 -- aligned
with DataBinder.DEFAULT_AUTO_GROW_COLLECTION_LIMIT -- rather than the
constructors' Integer.MAX_VALUE. The constructors keep their legacy
default for backward compatibility, but the builder is a new, opt-in
API that is not bound by that compatibility contract.

This change is purely additive: none of the existing constructors have
been modified or deprecated. Deprecating those constructors in favor of
the builder is being deferred to 7.1, since new deprecations should not
be introduced in a patch release. In the meantime, the Javadoc for the
constructors and for the SPRING_EXPRESSION_*_PROPERTY_NAME constants
has been updated to favor the builder (or a specific Builder setter)
instead of the constructors, and the class-level Javadoc now states
that the constructors are planned to be deprecated in favor of the
builder as of Spring Framework 7.1.

SpelExpressionParser's no-arg constructor, ExpressionState's two
convenience constructors, and StandardBeanExpressionResolver's
ClassLoader-based constructor have all been switched from the
SpelParserConfiguration constructors to the builder (or
withDefaults()). This is behaviorally identical in every case:
autoGrowCollections remains false at each of those call sites, and
maximumAutoGrowSize -- the only property whose default differs between
the constructors and the builder -- has no effect when
autoGrowCollections is false.

Tests have been added in a new SpelParserConfigurationTests class to
verify that the builder's defaults match the no-arg constructor (with
the one intentional maximumAutoGrowSize exception called out above),
that custom values are applied correctly, and that invalid values are
rejected. The nested LegacyConstructorTests class provides regression
coverage for each of the legacy constructors, consolidating their usage
in tests to a single class -- which will keep any future deprecation
warnings confined to this class -- and documents that, unlike the
builder, the canonical constructor does not (yet) reject a negative
maximumAutoGrowSize. The remaining incidental usages of the
SpelParserConfiguration constructors throughout EvaluationTests,
IndexingTests, SpelCompilationCoverageTests, SpelReproTests, and
SpelCompilerTests have been converted to use the builder.

Furthermore, the reference documentation has been updated to recommend
the builder and withDefaults() over the constructors, both in prose and
in the Java/Kotlin examples.

Closes gh-37187
2026-08-22 11:41:12 +02:00
Sam Brannen 9dabfe98e8 Account for all array objects when checking array size in SpEL
Prior to this commit, ConstructorReference.createArray() enforced the
MAX_ARRAY_ELEMENTS threshold for multi-dimensional arrays by checking
only the product of all dimension sizes, which is equivalent to the
total number of leaf-level elements. However, Array.newInstance()
allocates a distinct array object at every nesting level, not just at
the leaf level. For dimensions [d0, d1, ..., dk-1], the total number
of array objects created is 1 + d0 + d0*d1 + ... + d0*d1*...*d(k-2).
As a result, an expression such as new int[262143][1][1]...[1], whose
trailing dimensions are all 1, kept the leaf-element product just
under the threshold while still causing tens of millions of array
objects to be allocated.

To address that, this commit introduces a second running total,
totalArrayObjects, alongside the existing leaf-element product in the
multi-dimensional array construction loop. Both totals are checked
against MAX_ARRAY_ELEMENTS on every iteration, so array constructions
that fan out into an excessive number of array objects are now
rejected even when the leaf-element count remains within bounds.

Note that SimpleEvaluationContext does not permit array construction
in SpEL expressions at all, so this fix effectively only changes
behavior for expressions evaluated via StandardEvaluationContext.

Tests have been added to ArrayConstructorTests to verify that the new
check rejects array constructions with an excessive number of array
objects and that array constructions just under the threshold remain
unaffected.

Closes gh-36998
2026-08-21 15:21:13 +02:00
Sam Brannen fb240829b3 Align SpEL's default max auto-grow size with Spring data binding
Prior to this commit, the SpelParserConfiguration constructors that
omit an explicit maximumAutoGrowSize left collection auto-growing
effectively unbounded, defaulting to Integer.MAX_VALUE. That default
was inconsistent with the auto-grow limit applied elsewhere in the
framework for data binding (see
DataBinder.DEFAULT_AUTO_GROW_COLLECTION_LIMIT).

To address that, this commit introduces a new
SpelParserConfiguration.DEFAULT_MAX_AUTO_GROW_SIZE constant (set to 256
to match DataBinder.DEFAULT_AUTO_GROW_COLLECTION_LIMIT) and switches
the constructors that previously hard-coded Integer.MAX_VALUE to use
this new default instead. Constructors that accept an explicit
maximumAutoGrowSize are unaffected.

In addition, SpelParserConfiguration now enforces that a user-supplied
maximumAutoGrowSize is not a negative value, consistent with the
preconditions already enforced for maximumExpressionLength,
maximumOperations, maximumBigPowerBits, and maximumNestingDepth. A
value of 0 remains supported (effectively disabling collection
auto-growing) and is now documented as such in the Javadoc.

The Spring Framework reference documentation has also been updated to
describe the new default, and tests have been added to IndexingTests to
verify the default, the ability to override it, and the new
precondition.

Closes gh-36995
2026-08-21 14:11:04 +02:00
Sam Brannen 68d438c9ff Polishing
See gh-36723
2026-08-21 13:23:54 +02:00
Sam Brannen 8473ec3e25 Add a configurable limit for maximum nesting depth in SpEL expressions
This commit introduces support for limiting the structural nesting
depth of a SpEL expression during parsing. Without such a limit, an
expression with deeply nested constructs (for example, inline lists or
maps, parenthesized expressions, ternary or Elvis expressions, or
chained unary operators) can cause SpEL's recursive-descent parser to
throw a StackOverflowError which lacks useful diagnostics for
developer's attempting to assess what went wrong.

With this commit, a nesting-depth counter is now tracked around the
parser's primary recursive entry point (eatExpression()) as well as
around chained unary operators (eatUnaryExpression()), ensuring that
independent, sibling uses of assignment, Elvis, and ternary expressions
do not inadvertently accumulate depth and trip the limit.

If the configured (or default) nesting-depth limit is exceeded during
parsing, a SpelParseException is thrown instead, with a message that
reports the configured limit.

The limit can be configured on a per-use-case basis via
SpelParserConfiguration and defaults to 1000.

Closes gh-36723
2026-08-21 13:15:14 +02:00
Brian Clozel 89047909ea Merge branch '7.0.x' 2026-08-20 18:26:02 +02:00
Brian Clozel df72838ce1 Merge commit 'v7.1.0-M1~1' 2026-08-20 18:18:13 +02:00
Brian Clozel 0e9a1d72f5 Merge commit 'v7.0.9~1' into 7.0.x 2026-08-20 18:17:01 +02:00
Sam Brannen 6ee3ef6af5 Avoid unnecessarily synthesizing meta-annotations with attributes
In commit 622fc3edf7, I introduced a check in
TypeMappedAnnotation#isSynthesizable() intended to force synthesis when
an attribute value needs to be resolved from a different level of a
multi-level annotation hierarchy whose root annotation does not
redeclare the target attribute itself.

That check tested if `resolvedMirrors.length > 0` for a
meta-annotation; however, resolvedMirrors is always sized according to
the number of attributes declared by the mapped annotation type,
regardless of whether any of those attributes actually participate in
mirroring or an @⁠AliasFor override. As a result, the check effectively
synthesized any meta-annotation that declares at least one attribute,
which reintroduced the unnecessary-synthesis behavior that commit
d6768ccc18 had fixed, merely narrowed to meta-annotations with
attributes.

This commit replaces that overly broad check with a precise one in
AnnotationTypeMapping#computeSynthesizableFlag(), which now also
considers whether any attribute's value must be resolved from a
different annotation in the meta-annotation hierarchy (tracked via
annotationValueSource). This correctly identifies the original
multi-level hierarchy scenario without over-matching on ordinary
meta-annotations that have nothing to merge or override.

See gh-28704
See gh-28716
Closes gh-37135
2026-08-20 16:32:05 +02:00
greg taube 59a784cb7e Avoid unnecessary allocations for cached annotation mappings
This commit defers creation of the visited annotation types set until a
cache miss occurs, which avoids allocating a HashSet for every cached
annotation mapping lookup while preserving recursive annotation
handling during mapping creation.

Closes gh-37141

Signed-off-by: GT <gregjotau@gmail.com>
2026-08-20 16:27:18 +02:00
김준형 af466ccf63 Fix OptionalToObjectConverter applicability check
OptionalToObjectConverter.matches() used
TypeDescriptor.getElementTypeDescriptor(), which returns null for an
Optional (element types are only resolved for arrays, streams and
collections). ConversionUtils.canConvertElements() then treats a null
source element type as "maybe" and returns true unconditionally, so
ConversionService.canConvert(Optional<X>, target) reported true even
when X is not convertible to the target -- a violation of the
canConvert contract, since the subsequent conversion fails.

To address that, this commit resolves the Optional's element type from
its generic and checks it against the target, mirroring
ObjectToOptionalConverter. A raw or otherwise unresolved element type
remains permissive.

Closes gh-36913

Signed-off-by: junhyeong9812 <pickjog@gmail.com>
2026-08-20 16:19:37 +02:00
Sam Brannen bcfa6c3c4f Merge branch '7.0.x' 2026-08-20 15:56:20 +02:00
Sam Brannen 0acdf80830 Derive additional nohttp excludes from .gitignore
Excluding a path from nohttp scanning has so far required mirroring it
by hand in CheckstyleConventions, in addition to any existing
`.gitignore` entry. However, that extra step is easy to forget, as
happened when the .claude folder was added to `.gitignore` (49d2a202da)
but not to the nohttp excludes (48971139c0), only surfacing later as
an OutOfMemoryError that required a separate heap size increase
(90ad7f947d).

To address that, this commit introduces excludeGitIgnoredPaths() in
CheckstyleConventions, which parses the root `.gitignore` file and
translates its patterns into additional nohttp excludes, so that newly
ignored paths are picked up automatically. Note, however, that the
existing hand-maintained excludes are left in place for entries that
are specific to nohttp and are not otherwise ignored by git.

Closes gh-37164
2026-08-20 15:50:55 +02:00
Sam Brannen 74b6a5ba3e Merge branch '7.0.x' 2026-08-20 14:57:50 +02:00
Sam Brannen 15d7a3b327 Use in-document <<id,text>> syntax for same-page links in reference docs
Prior to this commit, some same-page links in our Antora-based docs
were written using the `xref:` macro, which Antora resolves as a
cross-page reference. However, a same-page target can be misread as a
page reference and break the site build (as nearly happened in
gh-37152), whereas `<<id,text>>` only ever resolves against the current
page's own anchors.

To address that inconsistency and avoid potential future bugs (broken
links), this commit converts all such same-page `xref:` links to
`<<id,text>>`, leaving genuine cross-page `xref:` links are unaffected.

Closes gh-37161
2026-08-20 14:53:32 +02:00
Brian Clozel 2730d77f82 Polishing contribution
Closes gh-34993
2026-08-20 14:13:55 +02:00
Mario Daniel Ruiz Saavedra 4a64537ac6 Add QUERY HTTP method
Signed-off-by: Mario Daniel Ruiz Saavedra <desiderantes93@gmail.com>
2026-08-20 14:13:55 +02:00
Sam Brannen 555ac3768d Merge branch '7.0.x' 2026-08-20 12:45:02 +02:00
Sam Brannen 90ad7f947d Increase heap size for checkstyleNohttp task
On a developer machine, the nohttp check scans the whole project
directory, and things like local git worktrees can add enough extra
content on disk to push the task past its previous 1g heap limit,
causing an OutOfMemoryError. This goes beyond what was addressed by
excluding the .claude folder from nohttp scanning in 48971139c0.

This commit raises the heap size for checkstyleNohttp specifically
to 1536m, leaving the limit for other Checkstyle tasks unchanged so
as not to increase memory pressure on CI.
2026-08-20 12:43:02 +02:00
Sam Brannen 49d2a202da Add .claude folder contents to .gitignore
If we later wish to share certain settings, we can introduce
exclusions like: !.claude/commands/
2026-08-20 12:43:02 +02:00
Sam Brannen e9e00e3257 Merge branch '7.0.x' 2026-08-20 10:59:06 +02:00
Sam Brannen df10251539 Upgrade to Gradle 9.7.1
Closes gh-37160
2026-08-20 10:57:33 +02:00
Sam Brannen 526c706d1c Merge branch '7.0.x' 2026-08-19 18:41:13 +02:00
Gabriel Gerhardt 507406c3cd Fix broken internal xref links in reference docs
Closes gh-37152

Signed-off-by: Gabriel Gerhardt <gabrielgerhardt27@gmail.com>
Signed-off-by: gabrielgerhardt <gabrielgerhardt27@gmail.com>
2026-08-19 18:39:12 +02:00
junhyeong9812 e8e293a706 Reject write methods not starting with "set" in Property
Prior to this commit, Property.resolveName() located the "set" prefix
of a write method with String.indexOf(), which matches the token
anywhere in the method name. A write method that merely contains "set"
(for example "offsetX" or "upset") was silently accepted and resolved
to a meaningless property name derived from whatever follows the token,
while only names with no "set" token at all were rejected.

To address that, this commit matches the "set" prefix only at the start
of the method name via startsWith(), so that an
IllegalArgumentException is consistently thrown for any write method
candidate that is not a setter.

See gh-36911
Closes gh-37139

Signed-off-by: junhyeong9812 <pickjog@gmail.com>
2026-08-19 18:26:46 +02:00
김준형 f067d40f0a Fix Property name resolution for record-style accessors
Property.resolveName() located the get/is accessor prefix with
String.indexOf, which matches the prefix anywhere in the method name.
A plain accessor whose name embeds such a prefix (for example
budget()) had the wrong portion stripped and resolved to an empty or
wrong property name, which in turn caused the backing field's
annotations to be silently dropped.

Match the get/is prefix only at the start of the method name and do
not strip it when the method is a plain accessor for a data class,
that is, a non-static no-arg method referring to an instance field of
the same name. This supports Java records, Kotlin data classes, and
custom Java data classes alike, without relying on java.lang.Record.

As a consequence, a getter backed by a field of the exact same name
(for example isUrgent()) now resolves to the field name.

Closes gh-36911

Signed-off-by: junhyeong9812 <pickjog@gmail.com>
2026-08-19 18:19:04 +02:00
Sam Brannen fd95ab16ba Use verified property names when constructing Property instances in SpEL
ReflectivePropertyAccessor's canRead(), read(), and canWrite() methods
previously constructed org.springframework.core.convert.Property
instances without an explicit name, forcing Property#resolveName() to
re-derive the property name from the accessor method via prefix
matching. That heuristic incorrectly resolves record-style and other
prefix-less accessor methods whose names embed or start with "get"/"is"
(for example, budget(), issue(), or island()), and it also normalizes
acronym-style JavaBean properties inconsistently (for example, getURL()
resolves to "uRL" rather than "URL").

By the time these three methods construct a Property, they have already
located the accessor method by searching for exactly the requested
property name, so the resolved name is already known and verified. This
commit passes that name through explicitly via the 4-arg Property
constructor, bypassing Property#resolveName() entirely at these call
sites.

This commit also introduces tests in PropertyAccessTests to cover the
following scenarios:

- A genuine record accessor whose component name embeds or starts with
  a "get"/"is" prefix
- The same scenario on a hand-written, non-record "data class"
- The read() call site exercised directly, since it is otherwise
  unreachable once canRead() has warmed the cache
- A boolean isXxx() getter, as a plain regression check
- An acronym-style property with a decoy field to prove that the
  correct field (and its annotations) is now resolved for both reads
  and writes

See gh-36911
Closes gh-37123
2026-08-19 17:45:47 +02:00
Sam Brannen ba13cae6cc Make nullability contracts in JMS SimpleMessageConverter explicit
TextMessage.getText() and ObjectMessage.getObject() may both return
null per the JMS specification when the message body was never set, but
SimpleMessageConverter.fromMessage() and its parent MessageConverter
interface currently declare a non-null return type despite residing in
an @⁠NullMarked package.

To address that, this commit updates MessageConverter.fromMessage(),
SimpleMessageConverter, and the protected
extractStringFromMessage()/extractSerializableFromMessage() methods to
declare @⁠Nullable accordingly and propagate the resulting nullability
through MessagingMessageConverter and
AbstractAdaptableMessageListener/MessageListenerAdapter, raising a
clear MessageConversionException where a non-null payload is required
by the Message<T> contract.

Closes gh-37148
2026-08-19 17:26:45 +02:00
rstoyanchev ee3b666a5c Merge branch '7.0.x' 2026-08-19 14:19:30 +03:00
rstoyanchev 6dcdf19169 Polishing in Protobuf decoders
See gh-37147
2026-08-19 10:55:12 +03:00
rstoyanchev 76239d083b Make getMessageBuilder in Protobuf decoders protected
Closes gh-37147
2026-08-19 10:52:35 +03:00
rstoyanchev 40ea92621d Correct supported media types in ProtobufJsonEncoder
Closes gh-37154
2026-08-19 10:44:15 +03:00
rstoyanchev b0149b842b Polishing in Protobuf encoding support
See gh-37154
2026-08-19 10:44:15 +03:00
Hyunwoo Jung a165094470 Fix RestClient API usage in documentation
Closes gh-37137

Signed-off-by: Hyunwoo Jung <hyunwoojung@kakao.com>
2026-08-18 18:49:06 +02:00
Sam Brannen 04e5c92162 Polish MockCookieTests
See gh-37134
See gh-37136
2026-08-18 18:24:54 +02:00
Tran Ngoc Nhan b20d31a5cd Update MockCookie#parse(String) validation to align with Javadoc
See gh-37134
Closes gh-37136

Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>
2026-08-18 18:24:38 +02:00
Sam Brannen daa8c10031 Polish contribution
See gh-36556
2026-08-18 18:17:57 +02:00
Vedran Pavic 7da47f8c47 Simplify programmatic scheduling of cron tasks with time zone
This commit adds an overloaded `addCronTask()` method to `ScheduledTaskRegistrar`
that allows simpler scheduling of cron tasks with non-default time zones.

Closes gh-36556

Signed-off-by: Vedran Pavic <vedran@vedranpavic.com>
2026-08-18 18:14:01 +02:00
Sam Brannen a947f9bf79 Merge branch '7.0.x' 2026-08-18 17:14:31 +02:00
Sam Brannen a4720ccf77 Guard all AsynchronousFileChannel#write call sites in DataBufferUtils
Prior to this commit, only the very first AsynchronousFileChannel#write
call in DataBufferUtils$WriteCompletionHandler#hookOnNext(DataBuffer)
was guarded against exceptions escaping synchronously, and even then
only via `catch (RuntimeException ex)`, per the original fix for
gh-36184.

While widening that guard to match the read side's `catch (Throwable
ex)` combined with `Exceptions.throwIfFatal(ex)` (see gh-37143), we
discovered that completed(Integer, Attachment) contains two more direct
`this.channel.write(...)` calls -- for continuing a partial write and
for advancing to the next ByteBuffer within the same DataBuffer's
iterator -- neither of which was guarded at all. Since completed() is
invoked by the channel's own completion callback, typically on a
different thread than the one that issued the original write, a
synchronous exception escaping either of those calls has no path back
to the FluxSink, and the resulting Flux hangs indefinitely, exactly as
described in gh-37143, for any write that receives a partial OS write
or spans multiple ByteBuffers.

To address that, this commit extracts a private write(ByteBuffer, long,
Attachment) helper that wraps the channel.write(...) call with a
try/catch block, routing any non-fatal Throwable -- via
Exceptions.throwIfFatal() -- to the existing failed(Throwable,
Attachment) handler. All three call sites (hookOnNext() and both
branches in completed()) now go through this helper, ensuring the
Flux always terminates with a proper error signal instead of hanging
silently, regardless of which write attempt fails or which thread it
fails on.

See gh-36184
See gh-37143
Closes gh-37145
2026-08-18 17:13:56 +02:00
Sam Brannen a13056f6af Merge branch '7.0.x' 2026-08-18 16:44:50 +02:00
Sam Brannen 15ef2b21f0 Handle synchronous exceptions from AsynchronousFileChannel#read
Prior to this commit, DataBufferUtils$ReadCompletionHandler#read()
invoked AsynchronousFileChannel#read(ByteBuffer, long, Attachment,
CompletionHandler) without guarding against exceptions thrown directly
by that call. Although that method is documented to report failures
asynchronously via the supplied CompletionHandler, some platform-
specific implementations can instead throw synchronously – for
example, on Windows with JDK 25, when the JDK rejects a ByteBuffer
backed by a closeable shared memory Arena, as produced by Netty 4.2's
off-heap buffer allocation.

When such an exception is thrown from a recursive read() invocation
triggered from completed() – which happens once a resource requires
more than a single chunk – the exception has no path back to the
FluxSink: it escapes on whatever thread invoked the CompletionHandler,
and the resulting Flux never signals onError or onComplete. In
practice, this surfaced as an indefinite hang when serving a Resource
whose HTTP response is not a ZeroCopyHttpOutputMessage, since
ResourceHttpMessageWriter falls back to ResourceEncoder, which reads
the resource via DataBufferUtils.

To address that, this commit wraps the channel.read(...) call in a
try/catch block and routes any non-fatal Throwable to the existing
failed(Throwable, Attachment) handler, via Exceptions.throwIfFatal(),
mirroring the equivalent fix already applied to the write side for
gh-36184. This ensures the allocated DataBuffer is released and the
Flux always terminates with a proper error signal instead of leaking a
buffer or hanging silently.

See gh-36184
Closes gh-37143
2026-08-18 16:32:34 +02:00
Sam Brannen 48971139c0 Exclude the .claude folder from nohttp scanning
Without this exclusion, the Gradle build will fail (due to an
OutOfMemoryError) for temporary git work trees residing in the .claude
folder.
2026-08-18 16:32:24 +02:00
Brian Clozel d823822dd6 Merge branch '7.0.x' 2026-08-18 09:25:15 +02:00
Brian Clozel 94beae1779 Next development version (7.0.10-SNAPSHOT) 2026-08-18 09:24:40 +02:00
Brian Clozel a7b1b59cbd Upgrade to Reactor 2025.0.7
Closes gh-37103
2026-08-14 09:12:18 +02:00
Brian Clozel 996e3d3f18 Upgrade to Micrometer 1.16.7
Closes gh-37104
2026-08-14 09:12:18 +02:00
rstoyanchev 73f5ddddcd Refactor maxInMemory limit handling for async XML parsing
The limit was previously enforced in XmlEventDecoder, because it is
what parses incoming buffers. However, the actual caching is in
Jaxb2Decoder, which holds on to XML events, but has no good way to
estimate their size.

After this commit XmlEventDecoder no longer enforces memory limits
for async parsing. It releases each buffer immediately anyway.

Instead XmlEventDecoder is only responsible to update the number
of bytes received via a new ReceivedByteTracker type while
Jaxb2XmlDecoder uses the same to perform limit and reset the
count depending on when it is aggregating XML events.

Closes gh-37031
2026-08-14 09:11:50 +02:00
rstoyanchev 675f25de72 Leading slash handling in UrlHandlerFilter
Closes gh-37030
2026-08-14 09:11:50 +02:00
rstoyanchev 692dbc9160 Apply ResourceHandlerUtils checks in XsltView
Closes gh-37029
2026-08-14 09:11:50 +02:00
rstoyanchev 8647e90bc7 Consistent maxPartSize check in PartEventHttpMessageReader
Closes gh-37028
2026-08-14 09:11:50 +02:00
rstoyanchev b9379e33d5 Check viewName for special prefixes in UrlFilenameViewController
Closes gh-37027
2026-08-14 09:11:50 +02:00
rstoyanchev a784dbe286 Ensure Payload release on early error in createHeaders
Closes gh-37026
2026-08-14 09:11:50 +02:00
rstoyanchev 3b492f3908 Return sameSite cookie value in Jetty response
Closes gh-37025
2026-08-14 09:11:50 +02:00
rstoyanchev 07cbd482a0 Add preflight handling in RouterFunctionWebHandler
Request predicates support preflight request matching based on the "would be"
request (e.g. target HTTP method) so the actual handler is not meant to be
invoked. That's the case only with a DispatcherHandler setup.

Closes gh-37024
2026-08-14 09:11:50 +02:00
rstoyanchev dadd474d21 Update exception messages in HandshakeWebSocketService
Closes gh-37023
2026-08-14 09:11:50 +02:00
Sam Brannen 0d08f8dfaf Disable SpEL expression compilation by default in SimpleEvaluationContext
Prior to this commit, SpEL expression compilation could be silently
activated in a SimpleEvaluationContext via the
`spring.expression.compiler.mode` Spring/system property or
SpelParserConfiguration. Once an expression is compiled, the evaluation
guards enforced during interpreted evaluation are no longer applied,
which is at odds with the restricted intent of SimpleEvaluationContext.

To address that, this commit introduces a mechanism analogous to
isAssignmentEnabled() which disables compilation by default in
SimpleEvaluationContext. Specifically:

- A new isCompilationSupported() default method has been introduced in
  the EvaluationContext API, which returns true by default.

- SimpleEvaluationContext overrides isCompilationSupported() to return
  false by default. However, compilation can be opted into explicitly
  via the new withCompilationSupported() method in the
  SimpleEvaluationContext.Builder.

- SpelExpression.checkCompile() now consults isCompilationSupported()
  before triggering new compilation, ensuring that evaluation within an
  EvaluationContext never produces a compiled form of the expression if
  the context's isCompilationSupported() method returns false.

- All eight getValue() variants in SpelExpression now consult
  isCompilationSupported() before executing a compiled expression,
  ensuring that a compiled expression produced via a different
  EvaluationContext is not silently reused if the caller inadvertently
  switches to an EvaluationContext that does not support compilation.

Closes gh-37035
2026-08-14 09:11:50 +02:00
Sam Brannen baae93f20a Limit result size of BigDecimal/BigInteger power operations in SpEL
This commit introduces a configurable limit on the estimated result size
of BigDecimal and BigInteger power operations within SpEL expressions.
The estimated result size in bits is computed as the product of the base
value's bit length and the exponent. If this limit is exceeded, a
SpelEvaluationException is thrown.

The limit defaults to 1,000,000 bits, which is approximately equivalent
to a decimal number with 300,000 digits, and can be configured either
on a per-use-case basis via the new maximumBigPowerBits constructor
argument in SpelParserConfiguration or globally as a JVM system
property or Spring property named `spring.expression.maxBigPowerBits`.
Parsers intended for trusted internal expressions may supply
Integer.MAX_VALUE to remove the limit entirely.

Closes ch-37034
2026-08-14 09:11:50 +02:00
Sam Brannen d186b381b9 Check list index after auto-grow in AbstractNestablePropertyAccessor
Prior to this commit, the List branch in
AbstractNestablePropertyAccessor's getPropertyValue() method called
list.get(index) unconditionally after invoking
growCollectionIfNecessary(), which implicitly relied on the list
throwing an IndexOutOfBoundsException for out-of-range access. Such an
exception is caught downstream and wrapped as an
InvalidPropertyException; however, any List implementation whose get()
method allocates elements on demand rather than throwing an
IndexOutOfBoundsException could bypass that check.

This behavior was also inconsistent with the Collection/Iterable branch
in the same method, which already performs an explicit `index >=
collection.size()` bounds check before attempting element access.

To address that, this commit aligns the List branch with the
Collection/Iterable branch by adding an explicit `index < 0 || index >=
list.size()` check immediately after the auto-grow attempt. If the
index remains out of bounds after growCollectionIfNecessary() runs –
for example, because growth was capped by autoGrowCollectionLimit or
auto-growing was disabled – an InvalidPropertyException is now thrown
rather than delegating to list.get() which may or may not throw an
exception.

Closes gh-37036
2026-08-14 09:11:50 +02:00
Sébastien Deleuze ac0f8be0d8 Ensure consistent EscapedErrors field error escaping
Closes gh-37055
2026-08-14 09:11:50 +02:00
Sébastien Deleuze 6e3dc633f0 Reject backslashes in SpringTemplateLoader template names
Closes gh-37054
2026-08-14 09:11:50 +02:00
Brian Clozel 35921cc01f Centralize Server Sent Event utility methods
Prior to this commit, many classes would support writing Server Sent
Events in some way to the response output stream. This has lead to some
code duplication.

This commit refactors the duplicated code in a shared `SseUtils` class.

Closes gh-37065
2026-08-14 09:11:50 +02:00
Brian Clozel 062032373e Ensure parsing/tostring symmetry in ContentDisposition
Prior to this commit, building a "Content-Disposition" header to a
String and then parsing it back would not always result in the original
header.

This commit ensures that ContentDisposition guarantees this and honors
the "equals" contract.

Fixes gh-37064
2026-08-14 09:11:50 +02:00
Brian Clozel 1994e0ebd0 Escape SSE view fragments
Prior to this commit, the MVC and WebFlux view fragments rendering would
only partially escape rendered view fragments before sending then as SSE
events. This could in some cases break the SSE stream with invalid data.

This commit ensures that the rendered views are properly escaped before
they are sent as SSE events.

Fixes gh-37061
2026-08-14 09:11:50 +02:00
Brian Clozel dc7fc89aec Switch to INTERNAL-SNAPSHOT versions
See gh-37103
See gh-37104
2026-08-14 09:11:49 +02:00
Brian Clozel f2a7f13d13 Prepare 7.0.x-internal branch 2026-08-14 09:11:49 +02:00
437 changed files with 11209 additions and 2443 deletions
+1 -1
View File
@@ -16,6 +16,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Create Backport Issue
uses: spring-io/backport-bot@v0.0.2
uses: spring-io/backport-bot@v0.0.3
with:
token: ${{ secrets.GITHUB_TOKEN }}
@@ -2,7 +2,7 @@ name: Build and Deploy Snapshot
on:
push:
branches:
- 'main-internal'
- 'main'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
@@ -22,10 +22,10 @@ jobs:
commercial-repository-password: ${{ secrets.COMMERCIAL_ARTIFACTORY_PASSWORD }}
commercial-repository-username: ${{ secrets.COMMERCIAL_ARTIFACTORY_USERNAME }}
commercial-snapshot-repository-url: ${{ vars.COMMERCIAL_SNAPSHOT_REPO_URL }}
# develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
publish: true
- name: Deploy
uses: spring-io/artifactory-deploy-action@926d7f7cc810569395346bf3a4d91b380b3e355b # v0.0.4
uses: spring-io/artifactory-deploy-action@aba148f1541e09adcf5735af90029fac9a6d3083 # v0.0.5
with:
artifact-properties: |
/**/framework-api-*.zip::zip.name=spring-framework,zip.deployed=false
+1 -1
View File
@@ -22,7 +22,7 @@ jobs:
toolchain: true
- version: 25
toolchain: false
- version: 26
- version: 27
toolchain: true
exclude:
- os:
+3
View File
@@ -52,6 +52,9 @@ atlassian-ide-plugin.xml
# VS Code
.vscode/
# Claude artifacts
.claude/*
cached-antora-playbook.yml
node_modules
+3 -3
View File
@@ -3,10 +3,10 @@ plugins {
// kotlinVersion is managed in gradle.properties
id 'org.jetbrains.kotlin.plugin.serialization' version "${kotlinVersion}" apply false
id 'org.jetbrains.dokka'
id 'com.github.bjornvester.xjc' version '1.8.2' apply false
id 'com.github.bjornvester.xjc' version '1.9.1' apply false
id 'com.gradleup.shadow' version "9.2.2" apply false
id 'me.champeau.jmh' version '0.7.2' apply false
id 'io.spring.nullability' version '0.0.14' apply false
id 'io.spring.nullability' version '0.0.15' apply false
}
ext {
@@ -64,7 +64,7 @@ configure([rootProject] + javaProjects) { project ->
ext.javadocLinks = [
"https://docs.oracle.com/en/java/javase/17/docs/api/",
//"https://jakarta.ee/specifications/platform/11/apidocs/",
"https://docs.hibernate.org/orm/7.2/javadocs/",
"https://docs.hibernate.org/orm/7.4/javadocs/",
"https://www.quartz-scheduler.org/api/2.3.0/",
"https://hc.apache.org/httpcomponents-client-5.6.x/5.6/httpclient5/apidocs/",
"https://projectreactor.io/docs/core/release/api/",
@@ -17,6 +17,9 @@
package org.springframework.build;
import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
@@ -35,6 +38,7 @@ import org.gradle.api.plugins.quality.CheckstylePlugin;
* {@link Plugin} that applies conventions for checkstyle.
*
* @author Brian Clozel
* @author Sam Brannen
*/
public class CheckstyleConventions {
@@ -48,9 +52,10 @@ public class CheckstyleConventions {
configureNoHttpPlugin(project);
}
project.getPlugins().apply(CheckstylePlugin.class);
project.getTasks().withType(Checkstyle.class).forEach(checkstyle -> checkstyle.getMaxHeapSize().set("1g"));
project.getTasks().withType(Checkstyle.class).forEach(checkstyle -> checkstyle.getMaxHeapSize()
.set("checkstyleNohttp".equals(checkstyle.getName()) ? "1536m" : "1g"));
CheckstyleExtension checkstyle = project.getExtensions().getByType(CheckstyleExtension.class);
checkstyle.setToolVersion("13.10.0");
checkstyle.setToolVersion("14.1.0");
checkstyle.getConfigDirectory().set(project.getRootProject().file("src/checkstyle"));
String version = SpringJavaFormatPlugin.class.getPackage().getImplementationVersion();
DependencySet checkstyleDependencies = project.getConfigurations().getByName("checkstyle").getDependencies();
@@ -64,7 +69,9 @@ public class CheckstyleConventions {
NoHttpExtension noHttp = project.getExtensions().getByType(NoHttpExtension.class);
noHttp.setAllowlistFile(project.file("src/nohttp/allowlist.lines"));
noHttp.getSource().exclude("**/test-output/**", "**/.settings/**", "**/.classpath",
"**/.project", "**/.gradle/**", "**/node_modules/**", "**/spring-jcl/**", "buildSrc/build/**");
"**/.project", "**/.gradle/**", "**/node_modules/**", "**/spring-jcl/**", "buildSrc/build/**",
".claude/**");
excludeGitIgnoredPaths(project, noHttp);
List<String> buildFolders = List.of("bin", "build", "out");
project.allprojects(subproject -> {
Path rootPath = project.getRootDir().toPath();
@@ -76,4 +83,48 @@ public class CheckstyleConventions {
});
}
/**
* Additionally exclude everything matched by the root {@code .gitignore} file,
* so that new ignored paths (build output, IDE metadata, local git worktrees,
* etc.) are automatically kept out of nohttp scanning without having to
* remember to mirror every {@code .gitignore} change here as well.
* <p>Negated patterns (lines starting with {@code !}) are not supported and are
* simply skipped, since there is no useful Ant-glob equivalent for them here.
*/
private static void excludeGitIgnoredPaths(Project project, NoHttpExtension noHttp) {
File gitignore = project.getRootProject().file(".gitignore");
if (!gitignore.exists()) {
return;
}
try {
for (String line : Files.readAllLines(gitignore.toPath())) {
String pattern = line.strip();
if (pattern.isEmpty() || pattern.startsWith("#") || pattern.startsWith("!")) {
continue;
}
boolean directoryOnly = pattern.endsWith("/");
if (directoryOnly) {
pattern = pattern.substring(0, pattern.length() - 1);
}
// A '/' anywhere but a (now removed) trailing position anchors the
// pattern to the repository root; otherwise it matches at any depth.
boolean anchored = pattern.contains("/");
if (pattern.startsWith("/")) {
pattern = pattern.substring(1);
}
String rootPattern = anchored ? pattern : "**/" + pattern;
if (directoryOnly) {
noHttp.getSource().exclude(rootPattern + "/**");
}
else {
// The pattern may match either a file or a directory, so exclude both.
noHttp.getSource().exclude(rootPattern, rootPattern + "/**");
}
}
}
catch (IOException ex) {
throw new UncheckedIOException("Failed to read .gitignore for nohttp exclusions", ex);
}
}
}
@@ -37,7 +37,8 @@ public class KotlinConventions {
if (project.getLayout().getProjectDirectory().dir("src/main/kotlin").getAsFile().exists()) {
project.getPlugins().apply(DokkaPlugin.class);
project.getExtensions().configure(DokkaExtension.class, dokka -> configure(project, dokka));
project.project(":framework-api").getDependencies().add("dokka", project);
project.project(":framework-api").getDependencies()
.add("dokka", project.getDependencyFactory().createProjectDependency());
}
});
}
@@ -18,7 +18,6 @@ package org.springframework.build.architecture;
import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.core.importer.ClassFileImporter;
import com.tngtech.archunit.lang.ArchRule;
import com.tngtech.archunit.lang.EvaluationResult;
import java.io.File;
import java.io.IOException;
@@ -44,12 +43,6 @@ import org.gradle.api.tasks.PathSensitivity;
import org.gradle.api.tasks.SkipWhenEmpty;
import org.gradle.api.tasks.TaskAction;
import static org.springframework.build.architecture.ArchitectureRules.allPackagesShouldBeFreeOfTangles;
import static org.springframework.build.architecture.ArchitectureRules.classesShouldNotImportForbiddenTypes;
import static org.springframework.build.architecture.ArchitectureRules.javaClassesShouldNotImportKotlinAnnotations;
import static org.springframework.build.architecture.ArchitectureRules.noClassesShouldCallStringToLowerCaseWithoutLocale;
import static org.springframework.build.architecture.ArchitectureRules.noClassesShouldCallStringToUpperCaseWithoutLocale;
/**
* {@link Task} that checks for architecture problems.
*
@@ -63,12 +56,11 @@ public abstract class ArchitectureCheck extends DefaultTask {
public ArchitectureCheck() {
getOutputDirectory().convention(getProject().getLayout().getBuildDirectory().dir(getName()));
getProhibitObjectsRequireNonNull().convention(true);
getRules().addAll(classesShouldNotImportForbiddenTypes(),
javaClassesShouldNotImportKotlinAnnotations(),
allPackagesShouldBeFreeOfTangles(),
noClassesShouldCallStringToLowerCaseWithoutLocale(),
noClassesShouldCallStringToUpperCaseWithoutLocale());
getRuleDescriptions().set(getRules().map((rules) -> rules.stream().map(ArchRule::getDescription).toList()));
getRules().addAll(ArchitectureRules.CLASSES_SHOULD_NOT_IMPORT_FORBIDDEN_TYPES,
ArchitectureRules.JAVA_CLASSES_SHOULD_NOT_IMPORT_KOTLIN_ANNOTATIONS,
ArchitectureRules.ALL_PACKAGES_SHOULD_BE_FREE_OF_TANGLES,
ArchitectureRules.NO_CLASSES_SHOULD_CALL_STRING_TO_LOWER_CASE_WITHOUT_LOCALE,
ArchitectureRules.NO_CLASSES_SHOULD_CALL_STRING_TO_UPPER_CASE_WITHOUT_LOCALE);
}
@TaskAction
@@ -77,6 +69,7 @@ public abstract class ArchitectureCheck extends DefaultTask {
.importPaths(this.classes.getFiles().stream().map(File::toPath).toList());
List<EvaluationResult> violations = getRules().get()
.stream()
.map(ArchitectureRules::archRule)
.map((rule) -> rule.evaluate(javaClasses))
.filter(EvaluationResult::hasViolation)
.toList();
@@ -122,14 +115,9 @@ public abstract class ArchitectureCheck extends DefaultTask {
@OutputDirectory
public abstract DirectoryProperty getOutputDirectory();
@Internal
public abstract ListProperty<ArchRule> getRules();
@Input
public abstract ListProperty<ArchitectureRules> getRules();
@Internal
public abstract Property<Boolean> getProhibitObjectsRequireNonNull();
@Input
// The rules themselves can't be an input as they aren't serializable so we use
// their descriptions instead
abstract ListProperty<String> getRuleDescriptions();
}
@@ -25,7 +25,24 @@ import com.tngtech.archunit.library.dependencies.SliceIdentifier;
import com.tngtech.archunit.library.dependencies.SlicesRuleDefinition;
import java.util.List;
abstract class ArchitectureRules {
public enum ArchitectureRules {
ALL_PACKAGES_SHOULD_BE_FREE_OF_TANGLES(allPackagesShouldBeFreeOfTangles()),
NO_CLASSES_SHOULD_CALL_STRING_TO_LOWER_CASE_WITHOUT_LOCALE(noClassesShouldCallStringToLowerCaseWithoutLocale()),
NO_CLASSES_SHOULD_CALL_STRING_TO_UPPER_CASE_WITHOUT_LOCALE(noClassesShouldCallStringToUpperCaseWithoutLocale()),
CLASSES_SHOULD_NOT_IMPORT_FORBIDDEN_TYPES(classesShouldNotImportForbiddenTypes()),
JAVA_CLASSES_SHOULD_NOT_IMPORT_KOTLIN_ANNOTATIONS(javaClassesShouldNotImportKotlinAnnotations())
;
private final ArchRule archRule;
public ArchRule archRule() {
return this.archRule;
}
private ArchitectureRules(ArchRule archRule) {
this.archRule = archRule;
}
static ArchRule allPackagesShouldBeFreeOfTangles() {
return SlicesRuleDefinition.slices()
@@ -61,10 +78,10 @@ abstract class ArchitectureRules {
static ArchRule javaClassesShouldNotImportKotlinAnnotations() {
return ArchRuleDefinition.noClasses()
.that(new DescribedPredicate<JavaClass>("is not a Kotlin class") {
@Override
public boolean test(JavaClass javaClass) {
return javaClass.getSourceCodeLocation()
.getSourceFileName().endsWith(".java");
@Override
public boolean test(JavaClass javaClass) {
return javaClass.getSourceCodeLocation()
.getSourceFileName().endsWith(".java");
}
}
)
@@ -65,7 +65,8 @@ public class RuntimeHintsAgentPlugin implements Plugin<Project> {
test.getJvmArgumentProviders().add(createRuntimeHintsAgentArgumentProvider(project, agentExtension));
});
project.getTasks().named("check", task -> task.dependsOn(agentTest));
project.getDependencies().add(CONFIGURATION_NAME, project.project(":spring-core-test"));
project.getDependencies().add(CONFIGURATION_NAME,
project.getDependencyFactory().createProjectDependency(":spring-core-test"));
});
}
@@ -38,7 +38,7 @@ In common with most `FactoryBean` implementations provided with Spring, the
`ProxyFactoryBean` class is itself a JavaBean. Its properties are used to:
* Specify the target you want to proxy.
* Specify whether to use CGLIB (described later and see also xref:core/aop-api/pfb.adoc#aop-pfb-proxy-types[JDK- and CGLIB-based proxies]).
* Specify whether to use CGLIB (described later and see also <<aop-pfb-proxy-types,JDK- and CGLIB-based proxies>>).
Some key properties are inherited from `org.springframework.aop.framework.ProxyConfig`
(the superclass for all AOP proxy factories in Spring). These key properties include
@@ -46,7 +46,7 @@ the following:
* `proxyTargetClass`: `true` if the target class is to be proxied, rather than the
target class's interfaces. If this property value is set to `true`, then CGLIB proxies
are created (but see also xref:core/aop-api/pfb.adoc#aop-pfb-proxy-types[JDK- and CGLIB-based proxies]).
are created (but see also <<aop-pfb-proxy-types,JDK- and CGLIB-based proxies>>).
* `optimize`: Controls whether or not aggressive optimizations are applied to proxies
created through CGLIB. You should not blithely use this setting unless you fully
understand how the relevant AOP proxy handles optimization. This is currently used
@@ -64,7 +64,7 @@ the following:
Other properties specific to `ProxyFactoryBean` include the following:
* `proxyInterfaces`: An array of `String` interface names. If this is not supplied, a CGLIB
proxy for the target class is used (but see also xref:core/aop-api/pfb.adoc#aop-pfb-proxy-types[JDK- and CGLIB-based proxies]).
proxy for the target class is used (but see also <<aop-pfb-proxy-types,JDK- and CGLIB-based proxies>>).
* `interceptorNames`: A `String` array of `Advisor`, interceptor, or other advice names to
apply. Ordering is significant, on a first come-first served basis. That is to say
that the first interceptor in the list is the first to be able to intercept the
@@ -76,7 +76,7 @@ factories. You cannot mention bean references here, since doing so results in th
+
You can append an interceptor name with an asterisk (`*`). Doing so results in the
application of all advisor beans with names that start with the part before the asterisk
to be applied. You can find an example of using this feature in xref:core/aop-api/pfb.adoc#aop-global-advisors[Using "`Global`" Advisors].
to be applied. You can find an example of using this feature in <<aop-global-advisors,Using "`Global`" Advisors>>.
* singleton: Whether or not the factory should return a single object, no matter how
often the `getObject()` method is called. Several `FactoryBean` implementations offer
@@ -394,7 +394,7 @@ execution-only semantics. You only need to be aware of this difference if you co
`@AspectJ` aspects written for Spring and use `proceed` with arguments with the AspectJ
compiler and weaver. There is a way to write such aspects that is 100% compatible across
both Spring AOP and AspectJ, and this is discussed in the
xref:core/aop/ataspectj/advice.adoc#aop-ataspectj-advice-proceeding-with-the-call[following section on advice parameters].
<<aop-ataspectj-advice-proceeding-with-the-call,following section on advice parameters>>.
====
The value returned by the around advice is the return value seen by the caller of the
@@ -722,7 +722,7 @@ of determining parameter names, an exception will be thrown.
`AspectJAnnotationParameterNameDiscoverer` :: Uses parameter names that have been explicitly
specified by the user via the `argNames` attribute in the corresponding advice or
pointcut annotation. See xref:core/aop/ataspectj/advice.adoc#aop-ataspectj-advice-params-names-explicit[Explicit Argument Names] for details.
pointcut annotation. See <<aop-ataspectj-advice-params-names-explicit,Explicit Argument Names>> for details.
`KotlinReflectionParameterNameDiscoverer` :: Uses Kotlin reflection APIs to determine
parameter names. This discoverer is only used if such APIs are present on the classpath.
`StandardReflectionParameterNameDiscoverer` :: Uses the standard `java.lang.reflect.Parameter`
@@ -10,7 +10,7 @@ of advice parameters.
To use the aop namespace tags described in this section, you need to import the
`spring-aop` schema, as described in xref:core/appendix/xsd-schemas.adoc[XML Schema-based configuration]
. See xref:core/appendix/xsd-schemas.adoc#aop[the AOP schema]
. See xref:core/appendix/xsd-schemas.adoc#xsd-schemas-aop[the AOP schema]
for how to import the tags in the `aop` namespace.
Within your Spring configurations, all aspect and advisor elements must be placed within
@@ -204,7 +204,7 @@ Before advice runs before a matched method execution. It is declared inside an
----
In the example above, `dataAccessOperation` is the `id` of a _named pointcut_ defined at
the top (`<aop:config>`) level (see xref:core/aop/schema.adoc#aop-schema-pointcuts[Declaring a Pointcut]).
the top (`<aop:config>`) level (see <<aop-schema-pointcuts,Declaring a Pointcut>>).
NOTE: As we noted in the discussion of the @AspectJ style, using _named pointcuts_ can
significantly improve the readability of your code. See xref:core/aop/ataspectj/pointcuts.adoc#aop-common-pointcuts[Sharing Named Pointcut Definitions] for
@@ -9,12 +9,12 @@ alone.
Spring ships with a small AspectJ aspect library, which is available stand-alone in your
distribution as `spring-aspects.jar`. You need to add this to your classpath in order
to use the aspects in it.
xref:core/aop/using-aspectj.adoc#aop-atconfigurable[Using AspectJ to Dependency Inject Domain Objects with Spring]
and xref:core/aop/using-aspectj.adoc#aop-ajlib-other[Other Spring aspects for AspectJ]
<<aop-atconfigurable,Using AspectJ to Dependency Inject Domain Objects with Spring>>
and <<aop-ajlib-other,Other Spring aspects for AspectJ>>
discuss the content of this library and how you can use it.
xref:core/aop/using-aspectj.adoc#aop-aj-configure[Configuring AspectJ Aspects by Using Spring IoC]
<<aop-aj-configure,Configuring AspectJ Aspects by Using Spring IoC>>
discusses how to dependency inject AspectJ aspects that are woven using the AspectJ compiler. Finally,
xref:core/aop/using-aspectj.adoc#aop-aj-ltw[Load-time Weaving with AspectJ in the Spring Framework]
<<aop-aj-ltw,Load-time Weaving with AspectJ in the Spring Framework>>
provides an introduction to load-time weaving for Spring applications that use AspectJ.
@@ -177,7 +177,7 @@ types in AspectJ
For this to work, the annotated types must be woven with the AspectJ weaver. You can
either use a build-time Ant or Maven task to do this (see, for example, the
{aspectj-docs-devguide}/antTasks.html[AspectJ Development
Environment Guide]) or load-time weaving (see xref:core/aop/using-aspectj.adoc#aop-aj-ltw[Load-time Weaving with AspectJ in the Spring Framework]). The
Environment Guide]) or load-time weaving (see <<aop-aj-ltw,Load-time Weaving with AspectJ in the Spring Framework>>). The
`AnnotationBeanConfigurerAspect` itself needs to be configured by Spring (in order to obtain
a reference to the bean factory that is to be used to configure new objects). You can define
the related configuration as follows:
@@ -376,7 +376,7 @@ per-`ClassLoader` basis, which is more fine-grained and which can make more
sense in a 'single-JVM-multiple-application' environment (such as is found in a typical
application server environment).
Further, xref:core/aop/using-aspectj.adoc#aop-aj-ltw-environments[in certain environments], this support enables
Further, <<aop-aj-ltw-environments,in certain environments>>, this support enables
load-time weaving without making any modifications to the application server's launch
script that is needed to add `-javaagent:path/to/aspectjweaver.jar` or (as we describe
later in this section) `-javaagent:path/to/spring-instrument.jar`. Developers configure
@@ -400,7 +400,7 @@ tool to that specific area immediately afterwards.
NOTE: The example presented here uses XML configuration. You can also configure and
use @AspectJ with xref:core/beans/java.adoc[Java configuration]. Specifically, you can use the
`@EnableLoadTimeWeaving` annotation as an alternative to `<context:load-time-weaver/>`
(see xref:core/aop/using-aspectj.adoc#aop-aj-ltw-spring[below] for details).
(see <<aop-aj-ltw-spring,below>> for details).
The following example shows the profiling aspect, which is not fancy.
It is a time-based profiler that uses the @AspectJ-style of aspect declaration:
@@ -719,7 +719,7 @@ for AspectJ LTW:
* `spring-aop.jar`
* `aspectjweaver.jar`
If you use the xref:core/aop/using-aspectj.adoc#aop-aj-ltw-environments-generic[Spring-provided agent to enable instrumentation]
If you use the <<aop-aj-ltw-environments-generic,Spring-provided agent to enable instrumentation>>
, you also need:
* `spring-instrument.jar`
@@ -836,7 +836,7 @@ containers.
Tomcat and JBoss/WildFly provide a general app `ClassLoader` that is capable of local
instrumentation. Spring's native LTW may leverage those ClassLoader implementations
to provide AspectJ weaving.
You can simply enable load-time weaving, as xref:core/aop/using-aspectj.adoc[described earlier].
You can simply enable load-time weaving, as <<aop-using-aspectj,described earlier>>.
Specifically, you do not need to modify the JVM launch script to add
`-javaagent:path/to/spring-instrument.jar`.
@@ -24,7 +24,7 @@ Applying such optimizations early implies the following restrictions:
* As we cannot rely on the instance, make sure that the bean type is as precise as
possible.
TIP: See also the xref:core/aot.adoc#aot.bestpractices[] section.
TIP: See also the <<aot.bestpractices>> section.
When these restrictions are in place, it becomes possible to perform ahead-of-time processing at build time and generate additional assets.
A Spring AOT processed application typically generates:
@@ -14,11 +14,11 @@ Spring distribution, you should first read the previous section on xref:core/app
To create new XML configuration extensions:
. xref:core/appendix/xml-custom.adoc#core.appendix.xsd-custom-schema[Author] an XML schema to describe your custom element(s).
. xref:core/appendix/xml-custom.adoc#core.appendix.xsd-custom-namespacehandler[Code] a custom `NamespaceHandler` implementation.
. xref:core/appendix/xml-custom.adoc#core.appendix.xsd-custom-parser[Code] one or more `BeanDefinitionParser` implementations
. <<xsd-custom-schema,Author>> an XML schema to describe your custom element(s).
. <<xsd-custom-namespacehandler,Code>> a custom `NamespaceHandler` implementation.
. <<xsd-custom-parser,Code>> one or more `BeanDefinitionParser` implementations
(this is where the real work is done).
. xref:core/appendix/xml-custom.adoc#core.appendix.xsd-custom-registration[Register] your new artifacts with Spring.
. <<xsd-custom-registration,Register>> your new artifacts with Spring.
For a unified example, we create an
XML extension (a custom XML element) that lets us configure objects of the type
@@ -553,7 +553,7 @@ Kotlin::
This works nicely, but it exposes a lot of Spring plumbing to the end user. What we are
going to do is write a custom extension that hides away all of this Spring plumbing.
If we stick to xref:core/appendix/xml-custom.adoc#core.appendix.xsd-custom-introduction[the steps described previously], we start off
If we stick to <<xsd-custom-introduction,the steps described previously>>, we start off
by creating the XSD schema to define the structure of our custom tag, as the following
listing shows:
@@ -580,7 +580,7 @@ listing shows:
</xsd:schema>
----
Again following xref:core/appendix/xml-custom.adoc#core.appendix.xsd-custom-introduction[the process described earlier],
Again following <<xsd-custom-introduction,the process described earlier>>,
we then create a custom `NamespaceHandler`:
[tabs]
@@ -43,7 +43,7 @@ An `@Autowired` annotation on such a constructor is not necessary if the target
defines only one constructor. However, if several constructors are available and there is
no primary or default constructor, at least one of the constructors must be annotated
with `@Autowired` in order to instruct the container which one to use. See the discussion
on xref:core/beans/annotation-config/autowired.adoc#beans-autowired-annotation-constructor-resolution[constructor resolution]
on <<beans-autowired-annotation-constructor-resolution,constructor resolution>>
for details.
====
@@ -193,7 +193,7 @@ XML configuration file represents a logical layer or module in your architecture
You can use the `ClassPathXmlApplicationContext` constructor to load bean definitions from
XML fragments. This constructor takes multiple `Resource` locations, as was shown in the
xref:core/beans/basics.adoc#beans-factory-xml[previous section]. Alternatively,
<<beans-factory-xml,previous section>>. Alternatively,
use one or more occurrences of the `<import/>` element to load bean definitions from
another file or files. The following example shows how to do so:
@@ -52,7 +52,7 @@ supported as a marker for automatic exception translation in your persistence la
Many of the annotations provided by Spring can be used as meta-annotations in your
own code. A meta-annotation is an annotation that can be applied to another annotation.
For example, the `@Service` annotation mentioned xref:core/beans/classpath-scanning.adoc#beans-stereotype-annotations[earlier]
For example, the `@Service` annotation mentioned <<beans-stereotype-annotations,earlier>>
is meta-annotated with `@Component`, as the following example shows:
[tabs]
@@ -483,7 +483,7 @@ When a component is autodetected as part of the scanning process, its bean name
generated by the `BeanNameGenerator` strategy known to that scanner.
By default, the `AnnotationBeanNameGenerator` is used. For Spring
xref:core/beans/classpath-scanning.adoc#beans-stereotype-annotations[stereotype annotations],
<<beans-stereotype-annotations,stereotype annotations>>,
if you supply a name via the annotation's `value` attribute that name will be used as
the name in the corresponding bean definition. This convention also applies when the
`@jakarta.inject.Named` annotation is used instead of Spring stereotype annotations.
@@ -282,7 +282,7 @@ class and the `ApplicationListener` interface. If a bean that implements the
Essentially, this is the standard Observer design pattern.
TIP: As of Spring 4.2, the event infrastructure has been significantly improved and offers
an xref:core/beans/context-introduction.adoc#context-functionality-events-annotation[annotation-based model] as well as the
an <<context-functionality-events-annotation,annotation-based model>> as well as the
ability to publish any arbitrary event (that is, an object that does not necessarily
extend from `ApplicationEvent`). When such an object is published, we wrap it in an
event for you.
@@ -698,7 +698,7 @@ Kotlin::
======
NOTE: This feature is not supported for
xref:core/beans/context-introduction.adoc#context-functionality-events-async[asynchronous listeners].
<<context-functionality-events-async,asynchronous listeners>>.
The `handleBlockedListEvent()` method publishes a new `ListUpdateEvent` for every
`BlockedListEvent` that it handles. If you need to publish several events, you can return
@@ -27,10 +27,10 @@ The following table describes these properties:
| Property| Explained in...
| Class
| xref:core/beans/definition.adoc#beans-factory-class[Instantiating Beans]
| <<beans-factory-class,Instantiating Beans>>
| Name
| xref:core/beans/definition.adoc#beans-beanname[Naming Beans]
| <<beans-beanname,Naming Beans>>
| Scope
| xref:core/beans/factory-scopes.adoc[Bean Scopes]
@@ -205,7 +205,7 @@ If you use XML-based configuration metadata, you specify the type (or class) of
that is to be instantiated in the `class` attribute of the `<bean/>` element. This
`class` attribute (which, internally, is a `Class` property on a `BeanDefinition`
instance) is usually mandatory. (For exceptions, see
xref:core/beans/definition.adoc#beans-factory-class-instance-factory-method[Instantiation by Using an Instance Factory Method]
<<beans-factory-class-instance-factory-method,Instantiation by Using an Instance Factory Method>>
and xref:core/beans/child-bean-definitions.adoc[Bean Definition Inheritance].)
You can use the `Class` property in one of two ways:
@@ -344,7 +344,7 @@ overloads of the `mock` method. Choose the most specific variant of `mock` possi
[[beans-factory-class-instance-factory-method]]
=== Instantiation by Using an Instance Factory Method
Similar to instantiation through a xref:core/beans/definition.adoc#beans-factory-class-static-factory-method[static factory method]
Similar to instantiation through a <<beans-factory-class-static-factory-method,static factory method>>
, instantiation with an instance factory method invokes a non-static
method of an existing bean from the container to create a new bean. To use this
mechanism, leave the `class` attribute empty and, in the `factory-bean` attribute,
@@ -467,8 +467,8 @@ See xref:core/beans/dependencies/factory-properties-detailed.adoc[Dependencies a
NOTE: In Spring documentation, "factory bean" refers to a bean that is configured in the
Spring container and that creates objects through an
xref:core/beans/definition.adoc#beans-factory-class-instance-factory-method[instance] or
xref:core/beans/definition.adoc#beans-factory-class-static-factory-method[static] factory method. By contrast,
<<beans-factory-class-instance-factory-method,instance>> or
<<beans-factory-class-static-factory-method,static>> factory method. By contrast,
`FactoryBean` (notice the capitalization) refers to a Spring-specific
xref:core/beans/factory-extension.adoc#beans-factory-extension-factorybean[`FactoryBean`] implementation class.
@@ -91,7 +91,7 @@ In the latter scenario, you have several options:
* Abandon autowiring in favor of explicit wiring.
* Avoid autowiring for a bean definition by setting its `autowire-candidate` attributes
to `false`, as described in the
xref:core/beans/dependencies/factory-autowire.adoc#beans-factory-autowire-candidate[next section].
<<beans-factory-autowire-candidate,next section>>.
* Designate a single bean definition as the primary candidate by setting the
`primary` attribute of its `<bean/>` element to `true`.
* Implement the more fine-grained control available with annotation-based configuration,
@@ -17,8 +17,8 @@ to test, particularly when the dependencies are on interfaces or abstract base c
which allow for stub or mock implementations to be used in unit tests.
DI exists in two major variants:
xref:core/beans/dependencies/factory-collaborators.adoc#beans-constructor-injection[Constructor-based dependency injection]
and xref:core/beans/dependencies/factory-collaborators.adoc#beans-setter-injection[Setter-based dependency injection].
<<beans-constructor-injection,Constructor-based dependency injection>>
and <<beans-setter-injection,Setter-based dependency injection>>.
[[beans-constructor-injection]]
@@ -110,7 +110,7 @@ You can read more about the motivation for Method Injection in
Lookup method injection is the ability of the container to override methods on
container-managed beans and return the lookup result for another named bean in the
container. The lookup typically involves a prototype bean, as in the scenario described
in xref:core/beans/dependencies/factory-method-injection.adoc[the preceding section]. The Spring Framework
in <<beans-factory-method-injection,the preceding section>>. The Spring Framework
implements this method injection by using bytecode generation from the CGLIB library to
dynamically generate a subclass that overrides the method.
@@ -27,7 +27,7 @@ The following example shows various values being set:
</bean>
----
The following example uses the xref:core/beans/dependencies/factory-properties-detailed.adoc#beans-p-namespace[p-namespace] for even more succinct
The following example uses the <<beans-p-namespace,p-namespace>> for even more succinct
XML configuration:
[source,xml,indent=0,subs="verbatim,quotes"]
@@ -539,7 +539,7 @@ three approaches at the same time.
== XML Shortcut with the c-namespace
Similar to the
xref:core/beans/dependencies/factory-properties-detailed.adoc#beans-p-namespace[XML Shortcut with the p-namespace],
<<beans-p-namespace,XML Shortcut with the p-namespace>>,
the c-namespace, introduced in Spring 3.1, allows inlined attributes for configuring
the constructor arguments rather then nested `constructor-arg` elements.
@@ -3,8 +3,8 @@
The {spring-framework-api}/core/env/Environment.html[`Environment`] interface
is an abstraction integrated in the container that models two key
aspects of the application environment: xref:core/beans/environment.adoc#beans-definition-profiles[profiles]
and xref:core/beans/environment.adoc#beans-property-source-abstraction[properties].
aspects of the application environment: <<beans-definition-profiles,profiles>>
and <<beans-property-source-abstraction,properties>>.
A profile is a named, logical group of bean definitions to be registered with the
container only if the given profile is active. Beans may be assigned to a profile
@@ -473,7 +473,7 @@ Kotlin::
In addition, you can also declaratively activate profiles through the
`spring.profiles.active` property, which may be specified through system environment
variables, JVM system properties, servlet context parameters in `web.xml`, or even as an
entry in JNDI (see xref:core/beans/environment.adoc#beans-property-source-abstraction[`PropertySource` Abstraction]). In integration tests, active
entry in JNDI (see <<beans-property-source-abstraction,`PropertySource` Abstraction>>). In integration tests, active
profiles can be declared by using the `@ActiveProfiles` annotation in the `spring-test`
module (see xref:testing/testcontext-framework/ctx-management/env-profiles.adoc[context configuration with environment profiles]
).
@@ -553,7 +553,7 @@ Kotlin::
----
======
If xref:#beans-definition-profiles-enable[no profile is active], the `dataSource` is
If <<beans-definition-profiles-enable,no profile is active>>, the `dataSource` is
created. You can see this as a way to provide a default definition for one or more
beans. If any profile is enabled, the default profile does not apply.
@@ -23,7 +23,7 @@ interface. If you write your own `BeanPostProcessor`, you should consider implem
the `Ordered` interface, too. For further details, see the javadoc of the
{spring-framework-api}/beans/factory/config/BeanPostProcessor.html[`BeanPostProcessor`]
and {spring-framework-api}/core/Ordered.html[`Ordered`] interfaces. See also the note on
xref:core/beans/factory-extension.adoc#beans-factory-programmatically-registering-beanpostprocessors[programmatic registration of `BeanPostProcessor` instances].
<<beans-factory-programmatically-registering-beanpostprocessors,programmatic registration of `BeanPostProcessor` instances>>.
[NOTE]
====
@@ -39,7 +39,7 @@ another container, even if both containers are part of the same hierarchy.
To change the actual bean definition (that is, the blueprint that defines the bean),
you instead need to use a `BeanFactoryPostProcessor`, as described in
xref:core/beans/factory-extension.adoc#beans-factory-extension-factory-postprocessors[Customizing Configuration Metadata with a `BeanFactoryPostProcessor`].
<<beans-factory-extension-factory-postprocessors,Customizing Configuration Metadata with a `BeanFactoryPostProcessor`>>.
====
The `org.springframework.beans.factory.config.BeanPostProcessor` interface consists of
@@ -329,7 +329,7 @@ and {spring-framework-api}/core/Ordered.html[`Ordered`] interfaces for more deta
If you want to change the actual bean instances (that is, the objects that are created
from the configuration metadata), then you instead need to use a `BeanPostProcessor`
(described earlier in
xref:core/beans/factory-extension.adoc#beans-factory-extension-bpp[Customizing Beans by Using a `BeanPostProcessor`]).
<<beans-factory-extension-bpp,Customizing Beans by Using a `BeanPostProcessor`>>).
While it is technically possible to work with bean instances within a `BeanFactoryPostProcessor`
(for example, by using `BeanFactory.getBean()`), doing so causes premature bean instantiation,
violating the standard container lifecycle. This may cause negative side effects, such as
@@ -4,9 +4,9 @@
The Spring Framework provides a number of interfaces you can use to customize the nature
of a bean. This section groups them as follows:
* xref:core/beans/factory-nature.adoc#beans-factory-lifecycle[Lifecycle Callbacks]
* xref:core/beans/factory-nature.adoc#beans-factory-aware[`ApplicationContextAware` and `BeanNameAware`]
* xref:core/beans/factory-nature.adoc#aware-list[Other `Aware` Interfaces]
* <<beans-factory-lifecycle,Lifecycle Callbacks>>
* <<beans-factory-aware,`ApplicationContextAware` and `BeanNameAware`>>
* <<aware-list,Other `Aware` Interfaces>>
[[beans-factory-lifecycle]]
@@ -252,7 +252,7 @@ of a `<bean>` element a special `(inferred)` value, which instructs Spring to au
detect a public `close` or `shutdown` method on the bean class for a specific bean definition.
You can also set this special `(inferred)` value on the `default-destroy-method` attribute
of a `<beans>` element to apply this behavior to an entire set of bean definitions (see
xref:core/beans/factory-nature.adoc#beans-factory-lifecycle-default-init-destroy-methods[Default Initialization and Destroy Methods]).
<<beans-factory-lifecycle-default-init-destroy-methods,Default Initialization and Destroy Methods>>).
[NOTE]
====
@@ -276,7 +276,7 @@ callback method names on every bean. This means that you, as an application deve
can write your application classes and use an initialization callback called `init()`,
without having to configure an `init-method="init"` attribute with each bean definition.
The Spring IoC container calls that method when the bean is created (and in accordance
with the standard lifecycle callback contract xref:core/beans/factory-nature.adoc#beans-factory-lifecycle[described previously]).
with the standard lifecycle callback contract <<beans-factory-lifecycle,described previously>>).
This feature also enforces a consistent naming convention for initialization and
destroy method callbacks.
@@ -367,8 +367,8 @@ interacts directly with the raw target bean.
As of Spring 2.5, you have three options for controlling bean lifecycle behavior:
* The xref:core/beans/factory-nature.adoc#beans-factory-lifecycle-initializingbean[`InitializingBean`] and
xref:core/beans/factory-nature.adoc#beans-factory-lifecycle-disposablebean[`DisposableBean`] callback interfaces
* The <<beans-factory-lifecycle-initializingbean,`InitializingBean`>> and
<<beans-factory-lifecycle-disposablebean,`DisposableBean`>> callback interfaces
* Custom `init()` and `destroy()` methods
* The xref:core/beans/annotation-config/postconstruct-and-predestroy-annotations.adoc[`@PostConstruct` and `@PreDestroy` annotations]
. You can combine these mechanisms to control a given bean.
@@ -378,7 +378,7 @@ configured with a different method name, then each configured method is run in t
order listed after this note. However, if the same method name is configured -- for example,
`init()` for an initialization method -- for more than one of these lifecycle mechanisms,
that method is run once, as explained in the
xref:core/beans/factory-nature.adoc#beans-factory-lifecycle-default-init-destroy-methods[preceding section].
<<beans-factory-lifecycle-default-init-destroy-methods,preceding section>>.
Multiple lifecycle mechanisms configured for the same bean, with different
initialization methods, are called as follows:
@@ -669,7 +669,7 @@ init-method.
[[aware-list]]
== Other `Aware` Interfaces
Besides `ApplicationContextAware` and `BeanNameAware` (discussed xref:core/beans/factory-nature.adoc#beans-factory-aware[earlier]),
Besides `ApplicationContextAware` and `BeanNameAware` (discussed <<beans-factory-aware,earlier>>),
Spring offers a wide range of `Aware` callback interfaces that let beans indicate to the container
that they require a certain infrastructure dependency. As a general rule, the name indicates the
dependency type. The following table summarizes the most important `Aware` interfaces:
@@ -681,7 +681,7 @@ dependency type. The following table summarizes the most important `Aware` inter
| `ApplicationContextAware`
| Declaring `ApplicationContext`.
| xref:core/beans/factory-nature.adoc#beans-factory-aware[`ApplicationContextAware` and `BeanNameAware`]
| <<beans-factory-aware,`ApplicationContextAware` and `BeanNameAware`>>
| `ApplicationEventPublisherAware`
| Event publisher of the enclosing `ApplicationContext`.
@@ -697,7 +697,7 @@ dependency type. The following table summarizes the most important `Aware` inter
| `BeanNameAware`
| Name of the declaring bean.
| xref:core/beans/factory-nature.adoc#beans-factory-aware[`ApplicationContextAware` and `BeanNameAware`]
| <<beans-factory-aware,`ApplicationContextAware` and `BeanNameAware`>>
| `LoadTimeWeaverAware`
| Defined weaver for processing class definition at load time.
@@ -14,7 +14,7 @@ through configuration instead of having to bake in the scope of an object at the
class level. Beans can be defined to be deployed in one of a number of scopes.
The Spring Framework supports six scopes, four of which are available only if
you use a web-aware `ApplicationContext`. You can also create
xref:core/beans/factory-scopes.adoc#beans-factory-scopes-custom[a custom scope.]
<<beans-factory-scopes-custom,a custom scope.>>
The following table describes the supported scopes:
@@ -24,23 +24,23 @@ The following table describes the supported scopes:
|===
| Scope| Description
| xref:core/beans/factory-scopes.adoc#beans-factory-scopes-singleton[singleton]
| <<beans-factory-scopes-singleton,singleton>>
| (Default) Scopes a single bean definition to a single object instance for each Spring IoC
container.
| xref:core/beans/factory-scopes.adoc#beans-factory-scopes-prototype[prototype]
| <<beans-factory-scopes-prototype,prototype>>
| Scopes a single bean definition to any number of object instances.
| xref:core/beans/factory-scopes.adoc#beans-factory-scopes-request[request]
| <<beans-factory-scopes-request,request>>
| Scopes a single bean definition to the lifecycle of a single HTTP request. That is,
each HTTP request has its own instance of a bean created off the back of a single bean
definition. Only valid in the context of a web-aware Spring `ApplicationContext`.
| xref:core/beans/factory-scopes.adoc#beans-factory-scopes-session[session]
| <<beans-factory-scopes-session,session>>
| Scopes a single bean definition to the lifecycle of an HTTP `Session`. Only valid in
the context of a web-aware Spring `ApplicationContext`.
| xref:core/beans/factory-scopes.adoc#beans-factory-scopes-application[application]
| <<beans-factory-scopes-application,application>>
| Scopes a single bean definition to the lifecycle of a `ServletContext`. Only valid in
the context of a web-aware Spring `ApplicationContext`.
@@ -53,7 +53,7 @@ NOTE: A thread scope is available but is not registered by default. For more inf
see the documentation for
{spring-framework-api}/context/support/SimpleThreadScope.html[`SimpleThreadScope`].
For instructions on how to register this or any other custom scope, see
xref:core/beans/factory-scopes.adoc#beans-factory-scopes-custom-using[Using a Custom Scope].
<<beans-factory-scopes-custom-using,Using a Custom Scope>>.
[[beans-factory-scopes-singleton]]
@@ -432,7 +432,7 @@ understand the "`why`" as well as the "`how`" behind it:
To create such a proxy, you insert a child `<aop:scoped-proxy/>` element into a
scoped bean definition (see
xref:core/beans/factory-scopes.adoc#beans-factory-scopes-other-injection-proxies[Choosing the Type of Proxy to Create]
<<beans-factory-scopes-other-injection-proxies,Choosing the Type of Proxy to Create>>
and xref:core/appendix/xsd-schemas.adoc[XML Schema-based configuration]).
Why do definitions of beans scoped at the `request`, `session` and custom-scope
@@ -19,7 +19,7 @@ You can use the `@Bean` annotation in a `@Configuration`-annotated or in a
To declare a bean, you can annotate a method with the `@Bean` annotation. You use this
method to register a bean definition within an `ApplicationContext` of the type specified
by the method's return type. By default, the bean name is the same as the method name
(unless a different xref:#beans-java-customizing-bean-naming[bean name generator] is
(unless a different <<beans-java-customizing-bean-naming,bean name generator>> is
configured). The following example shows a `@Bean` method declaration:
[tabs]
@@ -8,10 +8,10 @@ Jetty uses pooled byte buffers with a callback to be released, and so on.
The `spring-core` module provides a set of abstractions to work with various byte buffer
APIs as follows:
* xref:core/databuffer-codec.adoc#databuffers-factory[`DataBufferFactory`] abstracts the creation of a data buffer.
* xref:core/databuffer-codec.adoc#databuffers-buffer[`DataBuffer`] represents a byte buffer, which may be
xref:core/databuffer-codec.adoc#databuffers-buffer-pooled[pooled].
* xref:core/databuffer-codec.adoc#databuffers-utils[`DataBufferUtils`] offers utility methods for data buffers.
* <<databuffers-factory,`DataBufferFactory`>> abstracts the creation of a data buffer.
* <<databuffers-buffer,`DataBuffer`>> represents a byte buffer, which may be
<<databuffers-buffer-pooled,pooled>>.
* <<databuffers-utils,`DataBufferUtils`>> offers utility methods for data buffers.
* <<Codecs>> decode or encode data buffer streams into higher level objects.
@@ -41,7 +41,7 @@ Below is a partial list of benefits:
* Read and write with independent positions, i.e. not requiring a call to `flip()` to
alternate between read and write.
* Capacity expanded on demand as with `java.lang.StringBuilder`.
* Pooled buffers and reference counting via xref:core/databuffer-codec.adoc#databuffers-buffer-pooled[`PooledDataBuffer`].
* Pooled buffers and reference counting via <<databuffers-buffer-pooled,`PooledDataBuffer`>>.
* View a buffer as `java.nio.ByteBuffer`, `InputStream`, or `OutputStream`.
* Determine the index, or the last index, for a given byte.
@@ -101,7 +101,7 @@ xref:web/webflux/reactive-spring.adoc#webflux-codecs[Codecs] in the WebFlux sect
== Using `DataBuffer`
When working with data buffers, special care must be taken to ensure buffers are released
since they may be xref:core/databuffer-codec.adoc#databuffers-buffer-pooled[pooled]. We'll use codecs to illustrate
since they may be <<databuffers-buffer-pooled,pooled>>. We'll use codecs to illustrate
how that works but the concepts apply more generally. Let's see what codecs must do
internally to manage data buffers.
@@ -488,17 +488,23 @@ Kotlin::
It is possible to configure the SpEL expression parser by using a parser configuration
object (`org.springframework.expression.spel.SpelParserConfiguration`). The configuration
object controls the behavior of some of the expression components. For example, if you
index into a collection and the element at the specified index is `null`, SpEL can
automatically create the element. This is useful when using expressions made up of a
chain of property references. Similarly, if you index into a collection and specify an
index that is greater than the current size of the collection, SpEL can automatically
grow the collection to accommodate that index. In order to add an element at the
specified index, SpEL will try to create the element using the element type's default
constructor before setting the specified value. If the element type does not have a
default constructor, `null` will be added to the collection. If there is no built-in
converter or custom converter that knows how to set the value, `null` will remain in the
collection at the specified index. The following example demonstrates how to
object controls the behavior of some of the expression components. To create a
`SpelParserConfiguration` instance, favor `SpelParserConfiguration.builder()` over the
numerous constructors in `SpelParserConfiguration`, since the builder only requires
configuration of the properties that need to deviate from their sensible defaults --
or use `SpelParserConfiguration.withDefaults()` if none of those defaults need to be
overridden.
For example, if you index into a collection and the element at the specified index is
`null`, SpEL can automatically create the element. This is useful when using expressions
made up of a chain of property references. Similarly, if you index into a collection and
specify an index that is greater than the current size of the collection, SpEL can
automatically grow the collection to accommodate that index. In order to add an element
at the specified index, SpEL will try to create the element using the element type's
default constructor before setting the specified value. If the element type does not
have a default constructor, `null` will be added to the collection. If there is no
built-in converter or custom converter that knows how to set the value, `null` will
remain in the collection at the specified index. The following example demonstrates how to
automatically grow a `List`.
[tabs]
@@ -511,10 +517,10 @@ Java::
public List<String> list;
}
// Turn on:
// - auto null reference initialization
// - auto collection growing
SpelParserConfiguration config = new SpelParserConfiguration(true, true);
SpelParserConfiguration config = SpelParserConfiguration.builder()
.autoGrowNullReferences()
.autoGrowCollections()
.build();
ExpressionParser parser = new SpelExpressionParser(config);
@@ -536,10 +542,10 @@ Kotlin::
var list: List<String>? = null
}
// Turn on:
// - auto null reference initialization
// - auto collection growing
val config = SpelParserConfiguration(true, true)
val config = SpelParserConfiguration.builder()
.autoGrowNullReferences()
.autoGrowCollections()
.build()
val parser = SpelExpressionParser(config)
@@ -554,9 +560,18 @@ Kotlin::
----
======
When collection auto-growing is enabled, a collection cannot automatically grow beyond
256 elements by default; however, the `maximumAutoGrowSize` value is configurable. This
default is aligned with `DataBinder.DEFAULT_AUTO_GROW_COLLECTION_LIMIT`, for consistency
with the auto-grow limit used for data binding in Spring MVC and Spring WebFlux. If you
create a `SpelExpressionParser` programmatically, you can specify a custom
`maximumAutoGrowSize` when creating the `SpelParserConfiguration` that you provide to the
`SpelExpressionParser`.
By default, a SpEL expression cannot contain more than 10,000 characters; however, the
`maxExpressionLength` is configurable. If you create a `SpelExpressionParser`
programmatically, you can specify a custom `maxExpressionLength` when creating the
programmatically, you can specify a custom `maxExpressionLength` via
`SpelParserConfiguration.builder().maximumExpressionLength(...)` when creating the
`SpelParserConfiguration` that you provide to the `SpelExpressionParser`. If you wish to
set the `maxExpressionLength` used for parsing SpEL expressions within an
`ApplicationContext` -- for example, in XML bean definitions, `@Value`, etc. -- you can
@@ -567,12 +582,14 @@ xref:appendix.adoc#appendix-spring-properties[Supported Spring Properties]).
Similarly, the number of operations performed during the evaluation of a SpEL expression
cannot exceed 10,000 by default; however, the `maxOperations` value is configurable. If
you create a `SpelExpressionParser` programmatically (the recommend approach), you can
specify a custom `maxOperations` value when creating the `SpelParserConfiguration` that
you provide to the `SpelExpressionParser`. If you are not able to configure an explicit
value for `maxOperations` via `SpelParserConfiguration`, you can set a JVM system
property or Spring property named `spring.expression.maxOperations` to the maximum number
of operations required by your application (see
xref:appendix.adoc#appendix-spring-properties[Supported Spring Properties]).
specify a custom `maxOperations` value via
`SpelParserConfiguration.builder().maximumOperations(...)` when creating the
`SpelParserConfiguration` that you provide to the `SpelExpressionParser`. If you are not
able to configure an explicit value for `maxOperations` via `SpelParserConfiguration`,
you can set a JVM system property or Spring property named
`spring.expression.maxOperations` to the maximum number of operations required by your
application (see xref:appendix.adoc#appendix-spring-properties[Supported Spring
Properties]).
In addition, the result of a `BigDecimal` or `BigInteger` power operation within a SpEL
expression cannot exceed 1,000,000 bits by default approximately equivalent to a
@@ -580,13 +597,40 @@ decimal number with 300,000 digits. Power operations involving large base values
exponents can be computationally expensive, and this limit ensures that evaluations
remain bounded; however, the `maximumBigPowerBits` value is configurable. If you create a
`SpelExpressionParser` programmatically (the recommended approach), you can specify a
custom `maximumBigPowerBits` value when creating the `SpelParserConfiguration` that you
provide to the `SpelExpressionParser`. To remove this limit entirely, pass
`Integer.MAX_VALUE` as the `maximumBigPowerBits` value. If you are not able to configure
an explicit value for `maximumBigPowerBits` via `SpelParserConfiguration`, you can set a
JVM system property or Spring property named `spring.expression.maxBigPowerBits` to the
maximum result size in bits (see xref:appendix.adoc#appendix-spring-properties[Supported
Spring Properties]).
custom `maximumBigPowerBits` value via
`SpelParserConfiguration.builder().maximumBigPowerBits(...)` when creating the
`SpelParserConfiguration` that you provide to the `SpelExpressionParser`. To remove this
limit entirely, pass `Integer.MAX_VALUE` as the `maximumBigPowerBits` value. If you are
not able to configure an explicit value for `maximumBigPowerBits` via
`SpelParserConfiguration`, you can set a JVM system property or Spring property named
`spring.expression.maxBigPowerBits` to the maximum result size in bits (see
xref:appendix.adoc#appendix-spring-properties[Supported Spring Properties]).
Likewise, the structural nesting depth of a SpEL expression -- for example, the depth of
nested inline lists or maps, parenthesized expressions, ternary or Elvis expressions, or
chained unary operators -- cannot exceed 1,000 by default; however, the
`maximumNestingDepth` value is configurable. If you create a `SpelExpressionParser`
programmatically, you can specify a custom `maximumNestingDepth` value via
`SpelParserConfiguration.builder().maximumNestingDepth(...)` when creating the
`SpelParserConfiguration` that you provide to the `SpelExpressionParser`. Unlike
`maxExpressionLength` and `maxOperations`, there is currently no JVM system property or
Spring property available for configuring `maximumNestingDepth` globally.
[NOTE]
====
Without such a limit, a sufficiently deeply nested expression can drive SpEL's
recursive-descent parser to exhaust the current thread's call stack, resulting in a
`StackOverflowError` instead of a descriptive exception.
The `maximumNestingDepth` limit improves diagnostics for that common case by converting
it into a clear `SpelParseException`; however, it is not a guaranteed defense against
`StackOverflowError` under every possible JVM thread stack size configuration, since the
amount of stack space consumed per level of nesting depends on the JVM, its JIT
compilation state, and the platform. Applications and frameworks that evaluate SpEL
expressions from an untrusted source should not rely on `maximumNestingDepth` alone and
should instead heed the <<expressions-evaluation-context-security,security
considerations>> discussed previously in this chapter.
====
[[expressions-spel-compilation]]
== SpEL Compilation
@@ -625,9 +669,9 @@ only 3ms using the compiled version of the expression.
The compiler is not turned on by default, but you can turn it on in either of two
different ways. You can turn it on by using the parser configuration process
(xref:core/expressions/evaluation.adoc#expressions-parser-configuration[discussed
earlier]) or by using a Spring property when SpEL usage is embedded inside another
component. This section discusses both of these options.
(<<expressions-parser-configuration,discussed earlier>>) or by using a Spring property
when SpEL usage is embedded inside another component. This section discusses both of
these options.
The compiler can operate in one of three modes, which are captured in the
`org.springframework.expression.spel.SpelCompilerMode` enum. The modes are as follows.
@@ -669,8 +713,10 @@ Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE,
this.getClass().getClassLoader());
SpelParserConfiguration config = SpelParserConfiguration.builder()
.compilerMode(SpelCompilerMode.IMMEDIATE)
.compilerClassLoader(getClass().getClassLoader())
.build();
SpelExpressionParser parser = new SpelExpressionParser(config);
@@ -685,8 +731,10 @@ Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
val config = SpelParserConfiguration(SpelCompilerMode.IMMEDIATE,
this.javaClass.classLoader)
val config = SpelParserConfiguration.builder()
.compilerMode(SpelCompilerMode.IMMEDIATE)
.compilerClassLoader(javaClass.classLoader)
.build()
val parser = SpelExpressionParser(config)
@@ -3,12 +3,12 @@
The Spring Expression Language supports the following kinds of operators:
* xref:core/expressions/language-ref/operators.adoc#expressions-operators-relational[Relational Operators]
* xref:core/expressions/language-ref/operators.adoc#expressions-operators-logical[Logical Operators]
* xref:core/expressions/language-ref/operators.adoc#expressions-operators-string[String Operators]
* xref:core/expressions/language-ref/operators.adoc#expressions-operators-mathematical[Mathematical Operators]
* xref:core/expressions/language-ref/operators.adoc#expressions-assignment[The Assignment Operator]
* xref:core/expressions/language-ref/operators.adoc#expressions-operators-overloaded[Overloaded Operators]
* <<expressions-operators-relational,Relational Operators>>
* <<expressions-operators-logical,Logical Operators>>
* <<expressions-operators-string,String Operators>>
* <<expressions-operators-mathematical,Mathematical Operators>>
* <<expressions-assignment,The Assignment Operator>>
* <<expressions-operators-overloaded,Overloaded Operators>>
@@ -115,6 +115,153 @@ whereas the caller of the `@Retryable` method will only ever see the last except
====
[[resilience-annotations-retryable-combining]]
=== Combining `@Retryable` with Other Proxy-Based Features
Spring AOP applies interceptors in a specific order when multiple annotations such as
`@Retryable`, `@Transactional`, `@Cacheable`, and `@Async` are present on the same method.
The resulting advice chain determines how retries interact with each feature, and
understanding that chain is important for using `@Retryable` correctly in combination with
other annotations.
[[resilience-annotations-retryable-combining-transactional]]
==== With `@Transactional`
When `@Transactional` and `@Retryable` are used together, the advice chain is:
----
Retry (OUTER) → Transaction (INNER) → target method
----
Each retry attempt starts a fresh transaction. If the target method throws, the transaction
is rolled back and `@Retryable` decides whether to retry. On success, the transaction
commits. This is usually the desired behavior for transient failures such as database
deadlocks.
[source,java,indent=0,subs="verbatim,quotes"]
----
@Transactional
@Retryable(TransientDataAccessException.class)
public void updateRecord() {
// Each retry runs in its own transaction
}
----
[NOTE]
====
Because the retry interceptor is outside the transaction interceptor, the current
transaction has already been rolled back by the time the retry interceptor receives the
exception. The retry interceptor sees the same, unwrapped exception that the target
method threw.
====
TIP: See xref:data-access/transaction/declarative/annotations.adoc[Using `@Transactional`]
for general details on declarative transaction management.
[[resilience-annotations-retryable-combining-cacheable]]
==== With `@Cacheable`
When `@Cacheable` and `@Retryable` are used together, the advice chain is:
----
Retry (OUTER) → Cache (INNER) → target method
----
The cache interceptor runs on every attempt. If the cache is populated between attempts
(for example, by a concurrent request), subsequent retry attempts will return the cached
value without invoking the target method. On success, the cache is populated as normal.
The same fixed ordering applies to `@CacheEvict` and `@CachePut`, since they share the
same underlying cache advisor.
[source,java,indent=0,subs="verbatim,quotes"]
----
@Cacheable("items")
@Retryable
public Item loadItem(String id) {
// Retry wraps the cache lookup; each attempt checks the cache first
}
----
TIP: See xref:integration/cache/annotations.adoc#cache-annotations-cacheable[The `@Cacheable` Annotation]
for general details on declarative caching.
[[resilience-annotations-retryable-combining-async]]
==== With `@Async`
When `@Async` and `@Retryable` are used together, the advice chain is:
----
Async (OUTER) → Retry (INNER) → target method
----
The method is submitted to the async executor once, and all retry attempts run on the
same async thread. The caller receives a `CompletableFuture` or `Future` that completes
when the last retry attempt finishes (either with a result or a final exception).
[source,java,indent=0,subs="verbatim,quotes"]
----
@Async
@Retryable
public CompletableFuture<String> fetchData() {
// Retries happen on the async thread, not the calling thread
}
----
[NOTE]
====
Because `@Async` is outermost, the calling thread is never blocked by retry delays.
All retry attempts, including any configured delay between them, happen on the async
executor thread.
====
TIP: See xref:integration/scheduling.adoc#scheduling-annotation-support-async[The `@Async` annotation]
for general details on asynchronous method execution.
[[resilience-annotations-retryable-combining-order]]
==== Adjusting Advice Order
The `@Async` ordering described above reflects the relative `order` of the
`RetryAnnotationBeanPostProcessor` (registered by `@EnableResilientMethods`) and the
`AsyncAnnotationBeanPostProcessor` (registered by `@EnableAsync`). Both are plain
`Ordered` bean post-processors, so you can change their relative ordering by setting the
`order` attribute on `@EnableResilientMethods` and/or `@EnableAsync`.
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableResilientMethods(order = Ordered.LOWEST_PRECEDENCE) // <1>
@EnableAsync(order = Ordered.LOWEST_PRECEDENCE - 1) // <2>
class AppConfig {
}
----
<1> Raises the retry post-processor's order so that it runs after the async
post-processor.
<2> Lowers the async post-processor's order so that it runs before the retry
post-processor. As a result, retry becomes the outermost advice and async the
innermost, reversing the default order.
[NOTE]
====
With the reversed order shown above, exceptions thrown during asynchronous execution are
not retried: the retry interceptor only sees the `Future` handle, which is returned
immediately, rather than the outcome of the asynchronous invocation. Such an arrangement
only retries synchronous submission failures (for example, a rejected task submission)
and is rarely desirable in practice.
====
[NOTE]
====
This technique does not apply to `@Transactional` or `@Cacheable`. Their advisors are
registered through Spring's shared `InfrastructureAdvisorAutoProxyCreator`, whose own
post-processor `order` is fixed at `Ordered.HIGHEST_PRECEDENCE` and is unaffected by the
`order` attribute on `@EnableTransactionManagement` or `@EnableCaching` (that attribute
only affects ordering relative to other advisors on the same proxy). As a result, retry
advice is always applied outside `@Transactional` and `@Cacheable`, regardless of the
`order` configured on `@EnableResilientMethods`.
====
[[resilience-annotations-concurrencylimit]]
== `@ConcurrencyLimit`
@@ -4,14 +4,14 @@
This chapter covers how Spring handles resources and how you can work with resources in
Spring. It includes the following topics:
* xref:core/resources.adoc#resources-introduction[Introduction]
* xref:core/resources.adoc#resources-resource[The `Resource` Interface]
* xref:core/resources.adoc#resources-implementations[Built-in `Resource` Implementations]
* xref:core/resources.adoc#resources-resourceloader[The `ResourceLoader` Interface]
* xref:core/resources.adoc#resources-resourcepatternresolver[The `ResourcePatternResolver` Interface]
* xref:core/resources.adoc#resources-resourceloaderaware[The `ResourceLoaderAware` Interface]
* xref:core/resources.adoc#resources-as-dependencies[Resources as Dependencies]
* xref:core/resources.adoc#resources-app-ctx[Application Contexts and Resource Paths]
* <<resources-introduction,Introduction>>
* <<resources-resource,The `Resource` Interface>>
* <<resources-implementations,Built-in `Resource` Implementations>>
* <<resources-resourceloader,The `ResourceLoader` Interface>>
* <<resources-resourcepatternresolver,The `ResourcePatternResolver` Interface>>
* <<resources-resourceloaderaware,The `ResourceLoaderAware` Interface>>
* <<resources-as-dependencies,Resources as Dependencies>>
* <<resources-app-ctx,Application Contexts and Resource Paths>>
[[resources-introduction]]
@@ -126,13 +126,13 @@ For example, a `UrlResource` wraps a URL and uses the wrapped `URL` to do its wo
Spring includes several built-in `Resource` implementations:
* xref:core/resources.adoc#resources-implementations-urlresource[`UrlResource`]
* xref:core/resources.adoc#resources-implementations-classpathresource[`ClassPathResource`]
* xref:core/resources.adoc#resources-implementations-filesystemresource[`FileSystemResource`]
* xref:core/resources.adoc#resources-implementations-pathresource[`PathResource`]
* xref:core/resources.adoc#resources-implementations-servletcontextresource[`ServletContextResource`]
* xref:core/resources.adoc#resources-implementations-inputstreamresource[`InputStreamResource`]
* xref:core/resources.adoc#resources-implementations-bytearrayresource[`ByteArrayResource`]
* <<resources-implementations-urlresource,`UrlResource`>>
* <<resources-implementations-classpathresource,`ClassPathResource`>>
* <<resources-implementations-filesystemresource,`FileSystemResource`>>
* <<resources-implementations-pathresource,`PathResource`>>
* <<resources-implementations-servletcontextresource,`ServletContextResource`>>
* <<resources-implementations-inputstreamresource,`InputStreamResource`>>
* <<resources-implementations-bytearrayresource,`ByteArrayResource`>>
For a complete list of `Resource` implementations available in Spring, consult the
"All Known Implementing Classes" section of the
@@ -350,7 +350,7 @@ objects:
| file:
| `\file:///data/config.xml`
| Loaded as a `URL` from the filesystem. See also xref:core/resources.adoc#resources-filesystemresource-caveats[`FileSystemResource` Caveats].
| Loaded as a `URL` from the filesystem. See also <<resources-filesystemresource-caveats,`FileSystemResource` Caveats>>.
| https:
| `\https://myserver/logo.png`
@@ -384,11 +384,11 @@ for all matching resources from the class path. Note that the resource location
expected to be a path without placeholders in this case -- for example,
`classpath*:/config/beans.xml`. JAR files or different directories in the class path can
contain multiple files with the same path and the same name. See
xref:core/resources.adoc#resources-app-ctx-wildcards-in-resource-paths[Wildcards in Application Context Constructor Resource Paths] and its subsections for further details
<<resources-app-ctx-wildcards-in-resource-paths,Wildcards in Application Context Constructor Resource Paths>> and its subsections for further details
on wildcard support with the `classpath*:` resource prefix.
A passed-in `ResourceLoader` (for example, one supplied via
xref:core/resources.adoc#resources-resourceloaderaware[`ResourceLoaderAware`] semantics) can be checked whether
<<resources-resourceloaderaware,`ResourceLoaderAware`>> semantics) can be checked whether
it implements this extended interface too.
`PathMatchingResourcePatternResolver` is a standalone implementation that is usable
@@ -452,7 +452,7 @@ For more information, see xref:core/beans/annotation-config/autowired.adoc[Using
NOTE: To load one or more `Resource` objects for a resource path that contains wildcards
or makes use of the special `classpath*:` resource prefix, consider having an instance of
xref:core/resources.adoc#resources-resourcepatternresolver[`ResourcePatternResolver`] autowired into your
<<resources-resourcepatternresolver,`ResourcePatternResolver`>> autowired into your
application components instead of `ResourceLoader`.
@@ -2,13 +2,13 @@
= Data Binding
Data binding is useful for binding user input to a target object where user input is a map
with property paths as keys, following xref:data-binding-conventions[JavaBeans conventions].
with property paths as keys, following <<data-binding-conventions,JavaBeans conventions>>.
`DataBinder` is the main class that supports this, and it provides two ways to bind user
input:
- xref:data-binding-constructor-binding[Constructor binding] - bind user input to a
- <<data-binding-constructor-binding,Constructor binding>> - bind user input to a
public data constructor, looking up constructor argument values in the user input.
- xref:data-binding-property-binding[Property binding] - bind user input to setters,
- <<data-binding-property-binding,Property binding>> - bind user input to setters,
matching keys from the user input to properties of the target object structure.
You can apply both constructor and property binding or only one.
@@ -32,7 +32,7 @@ WebFlux support a custom name mapping through the `@BindParam` annotation on con
parameters or fields if present. If necessary, you can also configure a `NameResolver` on
`DataBinder` to customize the argument name to use.
xref:data-binding-conventions[Type conversion] is applied as needed to convert user input.
<<data-binding-conventions,Type conversion>> is applied as needed to convert user input.
If the constructor parameter is an object, it is constructed recursively in the same
manner, but through a nested property path. That means constructor binding creates both
the target object and any objects it contains.
@@ -61,7 +61,8 @@ corresponding implementation (`BeanWrapperImpl`). As quoted from the javadoc, th
`BeanWrapper` offers functionality to set and get property values (individually or in
bulk), get property descriptors, and query properties to determine if they are
readable or writable. Also, the `BeanWrapper` offers support for nested properties,
enabling the setting of properties on sub-properties to an unlimited depth. The
enabling the setting of properties on sub-properties up to a
<<data-binding-nested-path-depth,configurable maximum nesting depth>>. The
`BeanWrapper` also supports the ability to add standard JavaBeans `PropertyChangeListeners`
and `VetoableChangeListeners`, without the need for supporting code in the target class.
Last but not least, the `BeanWrapper` provides support for setting indexed properties.
@@ -103,7 +104,7 @@ details. The below table shows some examples of these conventions:
(This next section is not vitally important to you if you do not plan to work with
the `BeanWrapper` directly. If you use only the `DataBinder` and the `BeanFactory`
and their default implementations, you should skip ahead to the
xref:core/validation/data-binding.adoc#data-binding-conversion[section on `PropertyEditors`].)
<<data-binding-conversion,section on `PropertyEditors`>>.)
The following two example classes use the `BeanWrapper` to get and set
properties:
@@ -236,6 +237,48 @@ Kotlin::
======
[[data-binding-nested-path-depth]]
=== Maximum Nesting Depth for Nested Property Paths
A nested property path such as `managingDirector.salary` is resolved recursively, one
level per nested property. The nesting depth of a property path therefore corresponds to
the number of intermediate properties that must be traversed in order to reach the final
property -- for example, `address.country.name` has a nesting depth of 2, since the
`address` and `country` properties must be traversed in order to reach the `name`
property.
The nesting depth of a property path cannot exceed 100 by default; however, the
`maxNestedPathDepth` value is configurable. You can specify a custom value via
`setMaxNestedPathDepth(...)` on a `ConfigurablePropertyAccessor` such as
`BeanWrapperImpl`, or on a `DataBinder` -- and therefore also on a `WebDataBinder`, for
example within an `@InitBinder` method. If a property path exceeds the configured limit,
an `InvalidPropertyException` is thrown. Specify `0` to disable support for nested
property paths altogether, while continuing to allow simple, indexed, and mapped property
access -- for example, `name`, `accounts[2]`, or `accounts[KEY]`.
Note that this limit applies to property binding as well as to
<<data-binding-constructor-binding,constructor binding>> via `DataBinder.construct`,
since a constructor parameter which is itself an object is constructed recursively
through a nested property path.
[NOTE]
====
Without such a limit, a sufficiently deeply nested property path can drive the recursive
resolution of nested property paths to exhaust the current thread's call stack, resulting
in a `StackOverflowError` instead of a descriptive exception.
The `maxNestedPathDepth` limit improves diagnostics for that common case by converting
it into a clear `InvalidPropertyException`; however, it is not a guaranteed defense against
`StackOverflowError` under every possible JVM thread stack size configuration, since the
amount of stack space consumed per level of nesting depends on the JVM, its JIT
compilation state, and the platform.
When binding untrusted input, you should additionally constrain binding to the expected
input as described in
xref:web/webmvc/mvc-data-binding.adoc#mvc-data-binding-design[Model Design].
====
[[data-binding-conversion]]
== ``PropertyEditor``s
@@ -447,7 +490,7 @@ where it can be automatically detected and applied.
Note that all bean factories and application contexts automatically use a number of
built-in property editors, through their use of a `BeanWrapper` to
handle property conversions. The standard property editors that the `BeanWrapper`
registers are listed in the xref:core/validation/data-binding.adoc#data-binding-conversion[previous section].
registers are listed in the <<data-binding-conversion,previous section>>.
Additionally, ``ApplicationContext``s also override or add additional editors to handle
resource lookups in a manner appropriate to the specific application context type.
@@ -576,7 +619,7 @@ You can write a corresponding registrar and reuse it in each case.
`PropertyEditorRegistry`, an interface that is implemented by the Spring `BeanWrapper`
(and `DataBinder`). `PropertyEditorRegistrar` instances are particularly convenient
when used in conjunction with `CustomEditorConfigurer` (described
xref:core/validation/data-binding.adoc#data-binding-conversion-customeditor-registration[here]), which exposes a property
<<data-binding-conversion-customeditor-registration,here>>), which exposes a property
called `setPropertyEditorRegistrars(..)`. `PropertyEditorRegistrar` instances added
to a `CustomEditorConfigurer` in this fashion can easily be shared with `DataBinder` and
Spring MVC controllers. Furthermore, it avoids the need for synchronization on custom
@@ -7,8 +7,8 @@
This part of the appendix lists XML schemas for data access, including the following:
* xref:data-access/appendix.adoc#xsd-schemas-tx[The `tx` Schema]
* xref:data-access/appendix.adoc#xsd-schemas-jdbc[The `jdbc` Schema]
* <<xsd-schemas-tx,The `tx` Schema>>
* <<xsd-schemas-jdbc,The `jdbc` Schema>>
[[xsd-schemas-tx]]
=== The `tx` Schema
@@ -3,14 +3,14 @@
This section covers:
* xref:data-access/jdbc/connections.adoc#jdbc-datasource[Using `DataSource`]
* xref:data-access/jdbc/connections.adoc#jdbc-DataSourceUtils[Using `DataSourceUtils`]
* xref:data-access/jdbc/connections.adoc#jdbc-SmartDataSource[Implementing `SmartDataSource`]
* xref:data-access/jdbc/connections.adoc#jdbc-AbstractDataSource[Extending `AbstractDataSource`]
* xref:data-access/jdbc/connections.adoc#jdbc-SingleConnectionDataSource[Using `SingleConnectionDataSource`]
* xref:data-access/jdbc/connections.adoc#jdbc-DriverManagerDataSource[Using `DriverManagerDataSource`]
* xref:data-access/jdbc/connections.adoc#jdbc-TransactionAwareDataSourceProxy[Using `TransactionAwareDataSourceProxy`]
* xref:data-access/jdbc/connections.adoc#jdbc-DataSourceTransactionManager[Using `DataSourceTransactionManager` / `JdbcTransactionManager`]
* <<jdbc-datasource,Using `DataSource`>>
* <<jdbc-DataSourceUtils,Using `DataSourceUtils`>>
* <<jdbc-SmartDataSource,Implementing `SmartDataSource`>>
* <<jdbc-AbstractDataSource,Extending `AbstractDataSource`>>
* <<jdbc-SingleConnectionDataSource,Using `SingleConnectionDataSource`>>
* <<jdbc-DriverManagerDataSource,Using `DriverManagerDataSource`>>
* <<jdbc-TransactionAwareDataSourceProxy,Using `TransactionAwareDataSourceProxy`>>
* <<jdbc-DataSourceTransactionManager,Using `DataSourceTransactionManager` / `JdbcTransactionManager`>>
[[jdbc-datasource]]
@@ -4,14 +4,14 @@
This section covers how to use the JDBC core classes to control basic JDBC processing,
including error handling. It includes the following topics:
* xref:data-access/jdbc/core.adoc#jdbc-JdbcTemplate[Using `JdbcTemplate`]
* xref:data-access/jdbc/core.adoc#jdbc-NamedParameterJdbcTemplate[Using `NamedParameterJdbcTemplate`]
* xref:data-access/jdbc/core.adoc#jdbc-JdbcClient[Unified JDBC Query/Update Operations: `JdbcClient`]
* xref:data-access/jdbc/core.adoc#jdbc-SQLExceptionTranslator[Using `SQLExceptionTranslator`]
* xref:data-access/jdbc/core.adoc#jdbc-statements-executing[Running Statements]
* xref:data-access/jdbc/core.adoc#jdbc-statements-querying[Running Queries]
* xref:data-access/jdbc/core.adoc#jdbc-updates[Updating the Database]
* xref:data-access/jdbc/core.adoc#jdbc-auto-generated-keys[Retrieving Auto-generated Keys]
* <<jdbc-JdbcTemplate,Using `JdbcTemplate`>>
* <<jdbc-NamedParameterJdbcTemplate,Using `NamedParameterJdbcTemplate`>>
* <<jdbc-JdbcClient,Unified JDBC Query/Update Operations: `JdbcClient`>>
* <<jdbc-SQLExceptionTranslator,Using `SQLExceptionTranslator`>>
* <<jdbc-statements-executing,Running Statements>>
* <<jdbc-statements-querying,Running Queries>>
* <<jdbc-updates,Updating the Database>>
* <<jdbc-auto-generated-keys,Retrieving Auto-generated Keys>>
[[jdbc-JdbcTemplate]]
@@ -349,7 +349,7 @@ The `JdbcTemplate` is stateful, in that it maintains a reference to a `DataSourc
this state is not conversational state.
A common practice when using the `JdbcTemplate` class (and the associated
xref:data-access/jdbc/core.adoc#jdbc-NamedParameterJdbcTemplate[`NamedParameterJdbcTemplate`] class) is to
<<jdbc-NamedParameterJdbcTemplate,`NamedParameterJdbcTemplate`>> class) is to
configure a `DataSource` in your Spring configuration file and then dependency-inject
that shared `DataSource` bean into your DAO classes. The `JdbcTemplate` is created in
the setter for the `DataSource` or in the constructor. This leads to DAOs that resemble the following:
@@ -574,7 +574,7 @@ functionality that is present only in the `JdbcTemplate` class, you can use the
`getJdbcOperations()` method to access the wrapped `JdbcTemplate` through the
`JdbcOperations` interface.
See also xref:data-access/jdbc/core.adoc#jdbc-jdbctemplate-idioms[`JdbcTemplate` Best Practices]
See also <<jdbc-jdbctemplate-idioms,`JdbcTemplate` Best Practices>>
for guidelines on using the `NamedParameterJdbcTemplate` class in the context of an application.
@@ -3,9 +3,8 @@
The `org.springframework.jdbc.datasource.embedded` package provides support for embedded
Java database engines. Support for https://www.hsqldb.org[HSQL],
https://www.h2database.com[H2], and https://db.apache.org/derby[Derby] is provided
natively. You can also use an extensible API to plug in new embedded database types and
`DataSource` implementations.
and https://www.h2database.com[H2] is provided natively.
You can also use an extensible API to plug in new embedded database types and `DataSource` implementations.
[[jdbc-why-embedded-database]]
@@ -39,9 +38,8 @@ for further details on all supported options.
This section covers how to select one of the three embedded databases that Spring
supports. It includes the following topics:
* xref:data-access/jdbc/embedded-database-support.adoc#jdbc-embedded-database-using-HSQL[Using HSQL]
* xref:data-access/jdbc/embedded-database-support.adoc#jdbc-embedded-database-using-H2[Using H2]
* xref:data-access/jdbc/embedded-database-support.adoc#jdbc-embedded-database-using-Derby[Using Derby]
* <<jdbc-embedded-database-using-HSQL,Using HSQL>>
* <<jdbc-embedded-database-using-H2,Using H2>>
[[jdbc-embedded-database-using-HSQL]]
=== Using HSQL
@@ -58,13 +56,6 @@ Spring supports the H2 database. To enable H2, set the `type` attribute of the
`embedded-database` tag to `H2`. If you use the builder API, call the
`setType(EmbeddedDatabaseType)` method with `EmbeddedDatabaseType.H2`.
[[jdbc-embedded-database-using-Derby]]
=== Using Derby
Spring supports Apache Derby 10.5 and above. To enable Derby, set the `type`
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
@@ -143,7 +134,7 @@ can be useful for one-offs when the embedded database does not need to be reused
classes. However, if you wish to create an embedded database that is shared within a test suite,
consider using the xref:testing/testcontext-framework.adoc[Spring TestContext Framework] and
configuring the embedded database as a bean in the Spring `ApplicationContext` as described
in xref:data-access/jdbc/embedded-database-support.adoc#jdbc-embedded-database[Creating an Embedded Database].
in <<jdbc-embedded-database,Creating an Embedded Database>>.
The following listing shows the test template:
[tabs]
@@ -10,7 +10,7 @@ procedures and run update, delete, and insert statements.
[NOTE]
====
Many Spring developers believe that the various RDBMS operation classes described below
(with the exception of the xref:data-access/jdbc/object.adoc#jdbc-StoredProcedure[`StoredProcedure`] class) can often
(with the exception of the <<jdbc-StoredProcedure,`StoredProcedure`>> class) can often
be replaced with straight `JdbcTemplate` calls. Often, it is simpler to write a DAO
method that calls a method on a `JdbcTemplate` directly (as opposed to
encapsulating a query as a full-blown class).
@@ -266,7 +266,7 @@ The SQL type is specified using the `java.sql.Types` constants.
The first line (with the `SqlParameter`) declares an IN parameter. You can use IN parameters
both for stored procedure calls and for queries using the `SqlQuery` and its
subclasses (covered in xref:data-access/jdbc/object.adoc#jdbc-SqlQuery[Understanding `SqlQuery`]).
subclasses (covered in <<jdbc-SqlQuery,Understanding `SqlQuery`>>).
The second line (with the `SqlOutParameter`) declares an `out` parameter to be used in the
stored procedure call. There is also an `SqlInOutParameter` for `InOut` parameters
@@ -17,7 +17,7 @@ xref:data-access/jdbc/simple.adoc[Simplifying JDBC Operations with the `SimpleJd
for easy `DataSource` access and various simple `DataSource` implementations that you can
use for testing and running unmodified JDBC code outside of a Jakarta EE container. A subpackage
named `org.springframework.jdbc.datasource.embedded` provides support for creating
embedded databases by using Java database engines, such as HSQL, H2, and Derby. See
embedded databases by using Java database engines, such as HSQL and H2. See
xref:data-access/jdbc/connections.adoc[Controlling Database Connections] and
xref:data-access/jdbc/embedded-database-support.adoc[Embedded Database Support].
@@ -479,11 +479,11 @@ returned `out` parameters.
Earlier in this chapter, we described how parameters are deduced from metadata, but you can declare them
explicitly if you wish. You can do so by creating and configuring `SimpleJdbcCall` with
the `declareParameters` method, which takes a variable number of `SqlParameter` objects
as input. See the xref:data-access/jdbc/simple.adoc#jdbc-params[next section] for details on how to define an `SqlParameter`.
as input. See the <<jdbc-params,next section>> for details on how to define an `SqlParameter`.
NOTE: Explicit declarations are necessary if the database you use is not a Spring-supported
database. Currently, Spring supports metadata lookup of stored procedure calls for the
following databases: Apache Derby, DB2, MySQL, Microsoft SQL Server, Oracle, and Sybase.
following databases: DB2, MySQL, Microsoft SQL Server, Oracle, and Sybase.
We also support metadata lookup of stored functions for MySQL, Microsoft SQL Server,
and Oracle.
@@ -35,7 +35,7 @@ JDBC, the `JdbcTemplate` class mentioned in a xref:data-access/jdbc/core.adoc#jd
provides connection handling and proper conversion of `SQLException` to the
`DataAccessException` hierarchy, including translation of database-specific SQL error
codes to meaningful exception classes. For ORM technologies, see the
xref:data-access/orm/general.adoc#orm-exception-translation[next section] for how to get the same exception
<<orm-exception-translation,next section>> for how to get the same exception
translation benefits.
When it comes to transaction management, the `JdbcTemplate` class hooks in to the Spring
@@ -26,7 +26,7 @@ To avoid tying application objects to hard-coded resource lookups, you can defin
resources (such as a JDBC `DataSource` or a Hibernate `SessionFactory`) as beans in the
Spring container. Application objects that need to access resources receive references
to such predefined instances through bean references, as illustrated in the DAO
definition in the xref:data-access/orm/hibernate.adoc#orm-hibernate-straight[next section].
definition in the <<orm-hibernate-straight,next section>>.
The following excerpt from an XML application context definition shows how to set up a
JDBC `DataSource` and a Hibernate `SessionFactory` on top of it:
@@ -14,9 +14,9 @@ the underlying implementation in order to provide additional features.
The Spring JPA support offers three ways of setting up the JPA `EntityManagerFactory`
that is used by the application to obtain an entity manager.
* xref:data-access/orm/jpa.adoc#orm-jpa-setup-lemfb[Using `LocalEntityManagerFactoryBean`]
* xref:data-access/orm/jpa.adoc#orm-jpa-setup-jndi[Obtaining an EntityManagerFactory from JNDI]
* xref:data-access/orm/jpa.adoc#orm-jpa-setup-lcemfb[Using `LocalContainerEntityManagerFactoryBean`]
* <<orm-jpa-setup-lemfb,Using `LocalEntityManagerFactoryBean`>>
* <<orm-jpa-setup-jndi,Obtaining an EntityManagerFactory from JNDI>>
* <<orm-jpa-setup-lcemfb,Using `LocalContainerEntityManagerFactoryBean`>>
[[orm-jpa-setup-lemfb]]
=== Using `LocalEntityManagerFactoryBean`
@@ -519,7 +519,7 @@ Spring JPA also lets a configured `JpaTransactionManager` expose a JPA transacti
to JDBC access code that accesses the same `DataSource`, provided that the registered
`JpaDialect` supports retrieval of the underlying JDBC `Connection`. Spring provides
dialects for the EclipseLink and Hibernate JPA implementations. See the
xref:data-access/orm/jpa.adoc#orm-jpa-dialect[next section] for details on `JpaDialect`.
<<orm-jpa-dialect,next section>> for details on `JpaDialect`.
For JTA-style lazy retrieval of actual resource connections, Spring provides a
corresponding `DataSource` proxy class for the target connection pool: see
@@ -621,7 +621,7 @@ seamlessly integrating with `@Bean` style configuration (no `FactoryBean` involv
====
`LocalSessionFactoryBean` and `LocalSessionFactoryBuilder` support background
bootstrapping, just as the JPA `LocalContainerEntityManagerFactoryBean` does.
See xref:data-access/orm/jpa.adoc#orm-jpa-setup-background[Background Bootstrapping] for an introduction.
See <<orm-jpa-setup-background,Background Bootstrapping>> for an introduction.
On `LocalSessionFactoryBean`, this is available through the `bootstrapExecutor`
property. On the programmatic `LocalSessionFactoryBuilder`, an overloaded
@@ -17,9 +17,9 @@ stream, or a SAX handler.
Some of the benefits of using Spring for your O/X mapping needs are:
* xref:data-access/oxm.adoc#oxm-ease-of-configuration[Ease of configuration]
* xref:data-access/oxm.adoc#oxm-consistent-interfaces[Consistent Interfaces]
* xref:data-access/oxm.adoc#oxm-consistent-exception-hierarchy[Consistent Exception Hierarchy]
* <<oxm-ease-of-configuration,Ease of configuration>>
* <<oxm-consistent-interfaces,Consistent Interfaces>>
* <<oxm-consistent-exception-hierarchy,Consistent Exception Hierarchy>>
[[oxm-ease-of-configuration]]
=== Ease of configuration
@@ -52,7 +52,7 @@ These runtime exceptions wrap the original exception so that no information is l
[[oxm-marshaller-unmarshaller]]
== `Marshaller` and `Unmarshaller`
As stated in the xref:data-access/oxm.adoc#oxm-introduction[introduction], a marshaller serializes an object
As stated in the <<oxm-introduction,introduction>>, a marshaller serializes an object
to XML, and an unmarshaller deserializes XML stream to an object. This section describes
the two Spring interfaces used for this purpose.
@@ -334,8 +334,8 @@ preamble of the XML configuration file. The following example shows how to do so
The schema makes the following elements available:
* xref:data-access/oxm.adoc#oxm-jaxb2-xsd[`jaxb2-marshaller`]
* xref:data-access/oxm.adoc#oxm-jibx-xsd[`jibx-marshaller`]
* <<oxm-jaxb2-xsd,`jaxb2-marshaller`>>
* <<oxm-jibx-xsd,`jibx-marshaller`>>
Each tag is explained in its respective marshaller's section. As an example, though,
the configuration of a JAXB2 marshaller might resemble the following:
@@ -354,7 +354,7 @@ The JAXB binding compiler translates a W3C XML Schema into one or more Java clas
generate a schema from annotated Java classes.
Spring supports the JAXB 2.0 API as XML marshalling strategies, following the
`Marshaller` and `Unmarshaller` interfaces described in xref:data-access/oxm.adoc#oxm-marshaller-unmarshaller[`Marshaller` and `Unmarshaller`].
`Marshaller` and `Unmarshaller` interfaces described in <<oxm-marshaller-unmarshaller,`Marshaller` and `Unmarshaller`>>.
The corresponding integration classes reside in the `org.springframework.oxm.jaxb`
package.
@@ -12,12 +12,12 @@ The Spring Framework's R2DBC abstraction framework consists of two different pac
* `core`: The `org.springframework.r2dbc.core` package contains the `DatabaseClient`
class plus a variety of related classes. See
xref:data-access/r2dbc.adoc#r2dbc-core[Using the R2DBC Core Classes to Control Basic R2DBC Processing and Error Handling].
<<r2dbc-core,Using the R2DBC Core Classes to Control Basic R2DBC Processing and Error Handling>>.
* `connection`: The `org.springframework.r2dbc.connection` package contains a utility class
for easy `ConnectionFactory` access and various simple `ConnectionFactory` implementations
that you can use for testing and running unmodified R2DBC. See
xref:data-access/r2dbc.adoc#r2dbc-connections[Controlling Database Connections].
<<r2dbc-connections,Controlling Database Connections>>.
[[r2dbc-core]]
@@ -26,12 +26,12 @@ xref:data-access/r2dbc.adoc#r2dbc-connections[Controlling Database Connections].
This section covers how to use the R2DBC core classes to control basic R2DBC processing,
including error handling. It includes the following topics:
* xref:data-access/r2dbc.adoc#r2dbc-DatabaseClient[Using `DatabaseClient`]
* xref:data-access/r2dbc.adoc#r2dbc-DatabaseClient-examples-statement[Executing Statements]
* xref:data-access/r2dbc.adoc#r2dbc-DatabaseClient-examples-query[Querying (`SELECT`)]
* xref:data-access/r2dbc.adoc#r2dbc-DatabaseClient-examples-update[Updating (`INSERT`, `UPDATE`, and `DELETE`) with `DatabaseClient`]
* xref:data-access/r2dbc.adoc#r2dbc-DatabaseClient-filter[Statement Filters]
* xref:data-access/r2dbc.adoc#r2dbc-auto-generated-keys[Retrieving Auto-generated Keys]
* <<r2dbc-DatabaseClient,Using `DatabaseClient`>>
* <<r2dbc-DatabaseClient-examples-statement,Executing Statements>>
* <<r2dbc-DatabaseClient-examples-query,Querying (`SELECT`)>>
* <<r2dbc-DatabaseClient-examples-update,Updating (`INSERT`, `UPDATE`, and `DELETE`) with `DatabaseClient`>>
* <<r2dbc-DatabaseClient-filter,Statement Filters>>
* <<r2dbc-auto-generated-keys,Retrieving Auto-generated Keys>>
[[r2dbc-DatabaseClient]]
=== Using `DatabaseClient`
@@ -680,11 +680,11 @@ Kotlin::
This section covers:
* xref:data-access/r2dbc.adoc#r2dbc-ConnectionFactory[Using `ConnectionFactory`]
* xref:data-access/r2dbc.adoc#r2dbc-ConnectionFactoryUtils[Using `ConnectionFactoryUtils`]
* xref:data-access/r2dbc.adoc#r2dbc-SingleConnectionFactory[Using `SingleConnectionFactory`]
* xref:data-access/r2dbc.adoc#r2dbc-TransactionAwareConnectionFactoryProxy[Using `TransactionAwareConnectionFactoryProxy`]
* xref:data-access/r2dbc.adoc#r2dbc-R2dbcTransactionManager[Using `R2dbcTransactionManager`]
* <<r2dbc-ConnectionFactory,Using `ConnectionFactory`>>
* <<r2dbc-ConnectionFactoryUtils,Using `ConnectionFactoryUtils`>>
* <<r2dbc-SingleConnectionFactory,Using `SingleConnectionFactory`>>
* <<r2dbc-TransactionAwareConnectionFactoryProxy,Using `TransactionAwareConnectionFactoryProxy`>>
* <<r2dbc-R2dbcTransactionManager,Using `R2dbcTransactionManager`>>
[[r2dbc-ConnectionFactory]]
=== Using `ConnectionFactory`
@@ -77,7 +77,7 @@ Kotlin::
Used at the class level as above, the annotation indicates a default for all methods
of the declaring class (as well as its subclasses). Alternatively, each method can be
annotated individually. See
xref:data-access/transaction/declarative/annotations.adoc#transaction-declarative-annotations-method-visibility[method visibility]
<<transaction-declarative-annotations-method-visibility,method visibility>>
for further details on which methods Spring considers transactional. Note that a class-level
annotation does not apply to ancestor classes up the class hierarchy; in such a scenario,
inherited methods need to be locally redeclared in order to participate in a
@@ -167,6 +167,11 @@ Reactive Streams cancellation signals. See the
xref:data-access/transaction/programmatic.adoc#tx-prog-operator-cancel[Cancel Signals]
section under "Using the TransactionalOperator" for more details.
TIP: When `@Transactional` is combined with `@Retryable`, the retry advice is applied
outermost, so each retry attempt runs in its own transaction. See
xref:core/resilience.adoc#resilience-annotations-retryable-combining-transactional[Combining `@Retryable` with `@Transactional`]
for details.
[[transaction-declarative-annotations-method-visibility]]
.Method visibility and `@Transactional` in proxy mode
[NOTE]
@@ -360,7 +365,7 @@ properties of the `@Transactional` annotation:
|===
| Property| Type| Description
| xref:data-access/transaction/declarative/annotations.adoc#tx-multiple-tx-mgrs-with-attransactional[value]
| <<tx-multiple-tx-mgrs-with-attransactional,value>>
| `String`
| Optional qualifier that specifies the transaction manager to be used.
@@ -179,7 +179,7 @@ infrastructure.
NOTE: The preceding definition of the `dataSource` bean uses the `<jndi-lookup/>` tag
from the `jee` namespace. For more information see
xref:integration/appendix.adoc#xsd-schemas-jee[The JEE Schema].
xref:integration/appendix.adoc#appendix.xsd-schemas-jee[The JEE Schema].
NOTE: If you use JTA, your transaction manager definition should look the same, regardless
of what data access technology you use, be it JDBC, Hibernate JPA, or any other supported
@@ -44,6 +44,11 @@ The following example uses `@Cacheable` on the `findBook` method with multiple c
public Book findBook(ISBN isbn) {...}
----
TIP: When `@Cacheable` is combined with `@Retryable`, the retry advice is applied
outermost, so each retry attempt checks the cache before invoking the method. See
xref:core/resilience.adoc#resilience-annotations-retryable-combining-cacheable[Combining `@Retryable` with `@Cacheable`]
for details.
[[cache-annotations-cacheable-default-key]]
=== Default Key Generation
@@ -98,7 +103,7 @@ through its `key` attribute. You can use xref:core/expressions.adoc[SpEL] to pic
arguments of interest (or their nested properties), perform operations, or even
invoke arbitrary methods without having to write any code or implement any interface.
This is the recommended approach over the
xref:integration/cache/annotations.adoc#cache-annotations-cacheable-default-key[default generator],
<<cache-annotations-cacheable-default-key,default generator>>,
since methods tend to be quite different in signatures as the code base grows. While the
default strategy might work for some methods, it rarely works for all methods.
@@ -160,7 +165,7 @@ For applications that work with several cache managers, you can set the
<1> Specifying `anotherCacheManager`.
You can also replace the `CacheResolver` entirely in a fashion similar to that of
replacing xref:integration/cache/annotations.adoc#cache-annotations-cacheable-key[key generation].
replacing <<cache-annotations-cacheable-key,key generation>>.
The resolution is requested for every cache operation, letting the implementation
actually resolve the caches to use based on runtime arguments. The following example
shows how to specify a `CacheResolver`:
@@ -411,6 +416,11 @@ confirm the exclusion.
As of 6.1, `@CachePut` takes `CompletableFuture` and reactive return types into account,
performing the put operation whenever the produced object is available.
TIP: When `@CachePut` is combined with `@Retryable`, the retry advice is applied
outermost, so each successful retry attempt updates the cache; a failed attempt does
not. See xref:core/resilience.adoc#resilience-annotations-retryable-combining-cacheable[Combining `@Retryable` with `@Cacheable`]
for details.
[[cache-annotations-evict]]
== The `@CacheEvict` Annotation
@@ -456,6 +466,12 @@ and, thus, requires a result.
As of 6.1, `@CacheEvict` takes `CompletableFuture` and reactive return types into account,
performing an after-invocation evict operation whenever processing has completed.
TIP: When `@CacheEvict` is combined with `@Retryable`, the retry advice is applied
outermost, so eviction runs again on every retry attempt -- and, with
`beforeInvocation=true`, before each attempt regardless of its outcome. See
xref:core/resilience.adoc#resilience-annotations-retryable-combining-cacheable[Combining `@Retryable` with `@Cacheable`]
for details.
[[cache-annotations-caching]]
== The `@Caching` Annotation
@@ -684,5 +700,5 @@ preceding code:
Even though `@SlowService` is not a Spring annotation, the container automatically picks
up its declaration at runtime and understands its meaning. Note that, as mentioned
xref:integration/cache/annotations.adoc#cache-annotation-enable[earlier],
<<cache-annotation-enable,earlier>>,
annotation-driven behavior needs to be enabled.
@@ -29,7 +29,7 @@ or eviction contracts.
== Ehcache-based Cache
Ehcache 3.x is fully JSR-107 compliant and no dedicated support is required for it. See
xref:integration/cache/store-configuration.adoc#cache-store-configuration-jsr107[JSR-107 Cache] for details.
<<cache-store-configuration-jsr107,JSR-107 Cache>> for details.
[[cache-store-configuration-caffeine]]
@@ -25,7 +25,7 @@ See xref:integration/jms/annotated.adoc#jms-annotated-support[Enable Listener En
In a fashion similar to a Message-Driven Bean (MDB) in the EJB world, the Message-Driven
POJO (MDP) acts as a receiver for JMS messages. The one restriction (but see
xref:integration/jms/receiving.adoc#jms-receiving-async-message-listener-adapter[Using `MessageListenerAdapter`])
<<jms-receiving-async-message-listener-adapter,Using `MessageListenerAdapter`>>)
on an MDP is that it must implement the `jakarta.jms.MessageListener` interface.
Note that, if your POJO receives messages on multiple threads, it is important to
ensure that your implementation is thread-safe.
@@ -206,8 +206,8 @@ boilerplate JMS infrastructure concerns to the framework.
There are two standard JMS message listener containers packaged with Spring, each with
its specialized feature set.
* xref:integration/jms/using.adoc#jms-mdp-simple[`SimpleMessageListenerContainer`]
* xref:integration/jms/using.adoc#jms-mdp-default[`DefaultMessageListenerContainer`]
* <<jms-mdp-simple,`SimpleMessageListenerContainer`>>
* <<jms-mdp-default,`DefaultMessageListenerContainer`>>
[[jms-mdp-simple]]
=== Using `SimpleMessageListenerContainer`
@@ -258,7 +258,7 @@ a simple `BackOff` implementation retries every five seconds. You can specify
a custom `BackOff` implementation for more fine-grained recovery options. See
{spring-framework-api}/util/backoff/ExponentialBackOff.html[`ExponentialBackOff`] for an example.
NOTE: Like its sibling (xref:integration/jms/using.adoc#jms-mdp-simple[`SimpleMessageListenerContainer`]),
NOTE: Like its sibling (<<jms-mdp-simple,`SimpleMessageListenerContainer`>>),
`DefaultMessageListenerContainer` supports native JMS transactions and allows for
customizing the acknowledgment mode. If feasible for your scenario, This is strongly
recommended over externally managed transactions -- that is, if you can live with
@@ -33,7 +33,7 @@ the export happens or disable automatic registration by setting the `autoStartup
[[jmx-exporting-mbeanserver]]
== Creating an MBeanServer
The configuration shown in the xref:integration/jmx/exporting.adoc[preceding section] assumes that the
The configuration shown in the <<jmx-exporting,preceding section>> assumes that the
application is running in an environment that has one (and only one) `MBeanServer`
already running. In this case, Spring tries to locate the running `MBeanServer` and
register your beans with that server (if any). This behavior is useful when your
@@ -108,7 +108,7 @@ In the preceding example, you can see that the `AnnotationTestBean` class is ann
with `@ManagedResource` and that this `@ManagedResource` annotation is configured
with a set of attributes. These attributes can be used to configure various aspects
of the MBean that is generated by the `MBeanExporter` and are explained in greater
detail later in xref:integration/jmx/interface.adoc#jmx-interface-metadata-types[Spring JMX Annotations].
detail later in <<jmx-interface-metadata-types,Spring JMX Annotations>>.
Both the `age` and `name` properties are annotated with `@ManagedAttribute`,
but, in the case of the `age` property, only the getter method is annotated.
@@ -305,7 +305,7 @@ it. The only downside with this approach is that the name of the `AnnotationTest
has business meaning. You can address this issue by configuring an `ObjectNamingStrategy`
as explained in xref:integration/jmx/naming.adoc[Controlling `ObjectName` Instances for
Your Beans]. You can also see an example which uses the `MetadataNamingStrategy` in
xref:integration/jmx/interface.adoc#jmx-interface-metadata[Using Source-level Metadata: Java Annotations].
<<jmx-interface-metadata,Using Source-level Metadata: Java Annotations>>.
@@ -131,7 +131,7 @@ If necessary, you can provide a reference to a particular MBean `server`, and th
`defaultDomain` attribute (a property of `AnnotationMBeanExporter`) accepts an alternate
value for the generated MBean `ObjectName` domains. This is used in place of the
fully qualified package name as described in the previous section on
xref:integration/jmx/naming.adoc#jmx-naming-metadata[MetadataNamingStrategy], as the following example shows:
<<jmx-naming-metadata,MetadataNamingStrategy>>, as the following example shows:
include-code::./CustomJmxConfiguration[tag=snippet,indent=0]
@@ -19,26 +19,26 @@ You can learn more about {spring-boot-docs-ref}/actuator/observability.html[conf
== List of produced Observations
Spring Framework instruments various features for observability.
As outlined xref:integration/observability.adoc[at the beginning of this section], observations can generate timer Metrics and/or Traces depending on the configuration.
As outlined <<observability,at the beginning of this section>>, observations can generate timer Metrics and/or Traces depending on the configuration.
.Observations produced by Spring Framework
[%autowidth]
|===
|Observation name |Description
|xref:integration/observability.adoc#observability.http-client[`"http.client.requests"`]
|<<observability.http-client,`"http.client.requests"`>>
|Time spent for HTTP client exchanges
|xref:integration/observability.adoc#observability.http-server[`"http.server.requests"`]
|<<observability.http-server,`"http.server.requests"`>>
|Processing time for HTTP server exchanges at the Framework level
|xref:integration/observability.adoc#observability.jms.publish[`"jms.message.publish"`]
|<<observability.jms.publish,`"jms.message.publish"`>>
|Time spent sending a JMS message to a destination by a message producer.
|xref:integration/observability.adoc#observability.jms.process[`"jms.message.process"`]
|<<observability.jms.process,`"jms.message.process"`>>
|Processing time for a JMS message that was previously received by a message consumer.
|xref:integration/observability.adoc#observability.tasks-scheduled[`"tasks.scheduled.execution"`]
|<<observability.tasks-scheduled,`"tasks.scheduled.execution"`>>
|Processing time for an execution of a `@Scheduled` task
|===
@@ -3,10 +3,10 @@
The Spring Framework provides the following choices for making calls to REST endpoints:
* xref:integration/rest-clients.adoc#rest-restclient[`RestClient`] -- synchronous client with a fluent API
* xref:integration/rest-clients.adoc#rest-webclient[`WebClient`] -- non-blocking, reactive client with fluent API
* xref:integration/rest-clients.adoc#rest-resttemplate[`RestTemplate`] -- synchronous client with template method API, now deprecated in favor of `RestClient`
* xref:integration/rest-clients.adoc#rest-http-service-client[HTTP Service Clients] -- annotated interface backed by generated proxy
* <<rest-restclient,`RestClient`>> -- synchronous client with a fluent API
* <<rest-webclient,`WebClient`>> -- non-blocking, reactive client with fluent API
* <<rest-resttemplate,`RestTemplate`>> -- synchronous client with template method API, now deprecated in favor of `RestClient`
* <<rest-http-service-client,HTTP Service Clients>> -- annotated interface backed by generated proxy
[[rest-restclient]]
@@ -15,6 +15,7 @@ The Spring Framework provides the following choices for making calls to REST end
`RestClient` is a synchronous HTTP client that provides a fluent API to perform requests.
It serves as an abstraction over HTTP libraries, and handles conversion of HTTP request and response content to and from higher level Java objects.
[[rest-restclient.create]]
=== Create a `RestClient`
`RestClient` has static `create` shortcut methods.
@@ -32,48 +33,7 @@ Once created, a `RestClient` is safe to use in multiple threads.
The below shows how to create or build a `RestClient`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim"]
----
RestClient defaultClient = RestClient.create();
RestClient customClient = RestClient.builder()
.requestFactory(new HttpComponentsClientHttpRequestFactory())
.messageConverters(converters -> converters.add(new MyCustomMessageConverter()))
.baseUrl("https://example.com")
.defaultUriVariables(Map.of("variable", "foo"))
.defaultHeader("My-Header", "Foo")
.defaultCookie("My-Cookie", "Bar")
.defaultVersion("1.2")
.apiVersionInserter(ApiVersionInserter.fromHeader("API-Version").build())
.requestInterceptor(myCustomInterceptor)
.requestInitializer(myCustomInitializer)
.build();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
----
val defaultClient = RestClient.create()
val customClient = RestClient.builder()
.requestFactory(HttpComponentsClientHttpRequestFactory())
.messageConverters { converters -> converters.add(MyCustomMessageConverter()) }
.baseUrl("https://example.com")
.defaultUriVariables(mapOf("variable" to "foo"))
.defaultHeader("My-Header", "Foo")
.defaultCookie("My-Cookie", "Bar")
.defaultVersion("1.2")
.apiVersionInserter(ApiVersionInserter.fromHeader("API-Version").build())
.requestInterceptor(myCustomInterceptor)
.requestInitializer(myCustomInitializer)
.build()
----
======
include-code::./RestClientCreation[tag=snippet,indent=0]
=== Use the `RestClient`
@@ -390,17 +350,7 @@ xref:web/webmvc/message-converters.adoc#message-converters[See the supported HTT
To serialize only a subset of the object properties, you can specify a {baeldung-blog}/jackson-json-view-annotation[Jackson JSON View], as the following example shows:
[source,java,indent=0,subs="verbatim"]
----
MappingJacksonValue value = new MappingJacksonValue(new User("eric", "7!jd#h23"));
value.setSerializationView(User.WithoutPasswordView.class);
ResponseEntity<Void> response = restClient.post() // or RestTemplate.postForEntity
.contentType(APPLICATION_JSON)
.body(value)
.retrieve()
.toBodilessEntity();
----
include-code::./../restmessageconversion/RestClientMessageConversion[tag=jsonview,indent=0]
==== URL encoded Forms
@@ -410,17 +360,7 @@ or a target type.
For example:
[source,java,indent=0,subs="verbatim"]
----
MultiValueMap<String, String> form = new LinkedMultiValueMap<>();
form.add("project", "Spring Framework");
form.add("module", "spring-web");
ResponseEntity<Void> response = this.restClient.post()
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.body(form)
.retrieve()
.toBodilessEntity();
----
include-code::./../restmessageconversion/RestClientMessageConversion[tag=urlencodedform,indent=0]
==== Multipart
@@ -428,24 +368,7 @@ For example:
To send multipart data, you need to provide a `MultiValueMap<String, Object>` whose values may be an `Object` for part content, a `Resource` for a file part, or an `HttpEntity` for part content with headers.
For example:
[source,java,indent=0,subs="verbatim"]
----
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>();
parts.add("fieldPart", "fieldValue");
parts.add("filePart", new FileSystemResource("...logo.png"));
parts.add("jsonPart", new Person("Jason"));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_XML);
parts.add("xmlPart", new HttpEntity<>(myBean, headers));
ResponseEntity<Void> response = this.restClient.post()
.contentType(MediaType.MULTIPART_FORM_DATA)
.body(parts)
.retrieve()
.toBodilessEntity();
----
include-code::./../restmessageconversion/RestClientMessageConversion[tag=multipartrequest,indent=0]
In most cases, you do not have to specify the `Content-Type` for each part.
The content type is determined automatically based on the `HttpMessageConverter` chosen to serialize it or, in the case of a `Resource`, based on the file extension.
@@ -461,48 +384,7 @@ To decode a multipart response body, use a `ParameterizedTypeReference<MultiValu
The decoded map contains `Part` instances where `FormFieldPart` represents form field values
and `FilePart` represents file parts with a `filename()` and a `transferTo()` method.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim"]
----
MultiValueMap<String, Part> result = this.restClient.get()
.uri("https://example.com/upload")
.accept(MediaType.MULTIPART_FORM_DATA)
.retrieve()
.body(new ParameterizedTypeReference<>() {});
Part field = result.getFirst("fieldPart");
if (field instanceof FormFieldPart formField) {
String fieldValue = formField.value();
}
Part file = result.getFirst("filePart");
if (file instanceof FilePart filePart) {
filePart.transferTo(Path.of("/tmp/" + filePart.filename()));
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
----
val result = this.restClient.get()
.uri("https://example.com/upload")
.accept(MediaType.MULTIPART_FORM_DATA)
.retrieve()
.body(object : ParameterizedTypeReference<MultiValueMap<String, Part>>() {})
val field = result?.getFirst("fieldPart")
if (field is FormFieldPart) {
val fieldValue = field.value()
}
val file = result?.getFirst("filePart")
if (file is FilePart) {
file.transferTo(Path.of("/tmp/" + file.filename()))
}
----
======
include-code::./../restmessageconversion/RestClientMessageConversion[tag=multipartresponse,indent=0]
[[rest-request-factories]]
@@ -553,7 +435,7 @@ The `RestTemplate` provides a high-level API over HTTP client libraries in the f
It exposes the following groups of overloaded methods:
WARNING: As of Spring Framework 7.0, `RestTemplate` is deprecated in favor of `RestClient` and will be removed in a future version,
please use the xref:integration/rest-clients.adoc#migrating-to-restclient["Migrating to RestClient"] guide.
please use the <<migrating-to-restclient,"Migrating to RestClient">> guide.
For asynchronous and streaming scenarios, consider the reactive xref:web/webflux-webclient.adoc[WebClient].
[[rest-overview-of-resttemplate-methods-tbl]]
@@ -633,7 +515,7 @@ You can consider the following steps:
2. Once all client requests go through `RestClient` instances, you can now work on replicating your existing
`RestTemplate` instance creations by using `RestClient.Builder`. Because `RestTemplate` and `RestClient`
share the same infrastructure, you can reuse custom `ClientHttpRequestFactory` or `ClientHttpRequestInterceptor`
in your setup. See xref:integration/rest-clients.adoc#rest-restclient[the `RestClient` builder API].
in your setup. See <<rest-restclient,the `RestClient` builder API>>.
If no other library is available on the classpath, `RestClient` will choose the `JdkClientHttpRequestFactory`
powered by the modern JDK `HttpClient`, whereas `RestTemplate` would pick the `SimpleClientHttpRequestFactory` that
@@ -947,7 +829,7 @@ The following table shows `RestClient` equivalents for `RestTemplate` methods.
`RestClient` and `RestTemplate` instances share the same behavior when it comes to throwing exceptions
(with the `RestClientException` type being at the top of the hierarchy).
When `RestTemplate` consistently throws `HttpClientErrorException` for "4xx" response statues,
`RestClient` allows for more flexibility with custom xref:integration/rest-clients.adoc#rest-http-service-client-exceptions["status handlers"].
`RestClient` allows for more flexibility with custom <<rest-http-service-client-exceptions,"status handlers">>.
[[rest-http-service-client]]
@@ -183,7 +183,7 @@ default). The following listing shows the available methods for `Trigger` implem
Spring provides two implementations of the `Trigger` interface. The most interesting one
is the `CronTrigger`. It enables the scheduling of tasks based on
xref:integration/scheduling.adoc#scheduling-cron-expression[cron expressions].
<<scheduling-cron-expression,cron expressions>>.
For example, the following task is scheduled to run 15 minutes past each hour but only
during the 9-to-5 "business hours" on weekdays:
@@ -335,7 +335,7 @@ of time to wait before the intended execution of the method:
----
If simple periodic scheduling is not expressive enough, you can provide a
xref:integration/scheduling.adoc#scheduling-cron-expression[cron expression].
<<scheduling-cron-expression,cron expression>>.
The following example runs only on weekdays:
[source,java,indent=0]
@@ -573,12 +573,17 @@ for asynchronous execution in the first place, not externally re-declared to be
However, you can manually set up Spring's `AsyncExecutionInterceptor` with Spring AOP,
in combination with a custom pointcut.
TIP: When `@Async` is combined with `@Retryable`, the async advice is applied outermost, so
all retry attempts run on the async executor thread. See
xref:core/resilience.adoc#resilience-annotations-retryable-combining-async[Combining `@Retryable` with `@Async`]
for details.
[[scheduling-annotation-support-qualification]]
=== Executor Qualification with `@Async`
By default, when specifying `@Async` on a method, the executor that is used is the
one xref:integration/scheduling.adoc#scheduling-enable-annotation-support[configured when enabling async support],
one <<scheduling-enable-annotation-support,configured when enabling async support>>,
i.e. the "`annotation-driven`" element if you are using XML or your `AsyncConfigurer`
implementation, if any. However, you can use the `value` attribute of the `@Async`
annotation when you need to indicate that an executor other than the default should be
@@ -658,7 +663,7 @@ The following creates a `ThreadPoolTaskExecutor` instance:
<task:executor id="executor" pool-size="10"/>
----
As with the scheduler shown in the xref:integration/scheduling.adoc#scheduling-task-namespace-scheduler[previous section],
As with the scheduler shown in the <<scheduling-task-namespace-scheduler,previous section>>,
the value provided for the `id` attribute is used as the prefix for thread names within
the pool. As far as the pool size is concerned, the `executor` element supports more
configuration options than the `scheduler` element. For one thing, the thread pool for
@@ -770,7 +775,7 @@ any previous execution takes. Additionally, for both `fixed-delay` and `fixed-ra
tasks, you can specify an 'initial-delay' parameter, indicating the number of
milliseconds to wait before the first execution of the method. For more control,
you can instead provide a `cron` attribute to provide a
xref:integration/scheduling.adoc#scheduling-cron-expression[cron expression].
<<scheduling-cron-expression,cron expression>>.
The following example shows these other options:
[source,xml,indent=0]
@@ -791,8 +796,8 @@ The following example shows these other options:
== Cron Expressions
All Spring cron expressions have to conform to the same format, whether you are using them in
xref:integration/scheduling.adoc#scheduling-annotation-support-scheduled[`@Scheduled` annotations],
xref:integration/scheduling.adoc#scheduling-task-namespace-scheduled-tasks[`task:scheduled-tasks` elements],
<<scheduling-annotation-support-scheduled,`@Scheduled` annotations>>,
<<scheduling-task-namespace-scheduled-tasks,`task:scheduled-tasks` elements>>,
or someplace else. A well-formed cron expression, such as `* * * * * *`, consists of six
space-separated time and date fields, each with its own range of valid values:
+21 -21
View File
@@ -131,11 +131,11 @@ demonstrate its API and protocol features.
The `spring-messaging` module contains the following:
* xref:rsocket.adoc#rsocket-requester[RSocketRequester] -- fluent API to make requests
* <<rsocket-requester,RSocketRequester>> -- fluent API to make requests
through an `io.rsocket.RSocket` with data and metadata encoding/decoding.
* xref:rsocket.adoc#rsocket-annot-responders[Annotated Responders] -- `@MessageMapping`
* <<rsocket-annot-responders,Annotated Responders>> -- `@MessageMapping`
and `@RSocketExchange` annotated handler methods for responding.
* xref:rsocket.adoc#rsocket-interface[RSocket Interface] -- RSocket service declaration
* <<rsocket-interface,RSocket Interface>> -- RSocket service declaration
as Java interface with `@RSocketExchange` methods, for use as requester or responder.
The `spring-web` module contains `Encoder` and `Decoder` implementations such as Jackson
@@ -217,7 +217,7 @@ metadata, the default mime type is
metadata value and mime type pairs per request. Typically both don't need to be changed.
Data and metadata in the `SETUP` frame is optional. On the server side,
xref:rsocket.adoc#rsocket-annot-connectmapping[@ConnectMapping] methods can be used to
<<rsocket-annot-connectmapping,@ConnectMapping>> methods can be used to
handle the start of a connection and the content of the `SETUP` frame. Metadata may be
used for connection level security.
@@ -355,7 +355,7 @@ annotation such as `@RSocketClientResponder` vs the default `@Controller`. This
is necessary in scenarios with client and server, or multiple clients in the same
application.
See also xref:rsocket.adoc#rsocket-annot-responders[Annotated Responders], for more on the programming model.
See also <<rsocket-annot-responders,Annotated Responders>>, for more on the programming model.
[[rsocket-requester-client-advanced]]
==== Advanced
@@ -396,7 +396,7 @@ Kotlin::
To make requests from a server to connected clients is a matter of obtaining the
requester for the connected client from the server.
In xref:rsocket.adoc#rsocket-annot-responders[Annotated Responders], `@ConnectMapping` and `@MessageMapping` methods support an
In <<rsocket-annot-responders,Annotated Responders>>, `@ConnectMapping` and `@MessageMapping` methods support an
`RSocketRequester` argument. Use it to access the requester for the connection. Keep in
mind that `@ConnectMapping` methods are essentially handlers of the `SETUP` frame which
must be handled before requests can begin. Therefore, requests at the very start must be
@@ -442,8 +442,8 @@ Kotlin::
[[rsocket-requester-requests]]
=== Requests
Once you have a xref:rsocket.adoc#rsocket-requester-client[client] or
xref:rsocket.adoc#rsocket-requester-server[server] requester, you can make requests as follows:
Once you have a <<rsocket-requester-client,client>> or
<<rsocket-requester-server,server>> requester, you can make requests as follows:
[tabs]
======
@@ -647,7 +647,7 @@ Kotlin::
`RSocketMessageHandler` supports
{rsocket-protocol-extensions}/CompositeMetadata.md[composite] and
{rsocket-protocol-extensions}/Routing.md[routing] metadata by default. You can set its
xref:rsocket.adoc#rsocket-metadata-extractor[MetadataExtractor] if you need to switch to a
<<rsocket-metadata-extractor,MetadataExtractor>> if you need to switch to a
different mime type or register additional metadata mime types.
You'll need to set the `Encoder` and `Decoder` instances required for metadata and data
@@ -716,13 +716,13 @@ Kotlin::
Annotated responders on the client side need to be configured in the
`RSocketRequester.Builder`. For details, see
xref:rsocket.adoc#rsocket-requester-client-responder[Client Responders].
<<rsocket-requester-client-responder,Client Responders>>.
[[rsocket-annot-messagemapping]]
=== @MessageMapping
Once xref:rsocket.adoc#rsocket-annot-responders-server[server] or
xref:rsocket.adoc#rsocket-annot-responders-client[client] responder configuration is in place,
Once <<rsocket-annot-responders-server,server>> or
<<rsocket-annot-responders-client,client>> responder configuration is in place,
`@MessageMapping` methods can be used as follows:
[tabs]
@@ -780,10 +780,10 @@ use the following method arguments:
pass:q[`@MessageMapping("find.radar.{id}")`].
| `@Header`
| Metadata value registered for extraction as described in xref:rsocket.adoc#rsocket-metadata-extractor[MetadataExtractor].
| Metadata value registered for extraction as described in <<rsocket-metadata-extractor,MetadataExtractor>>.
| `@Headers Map<String, Object>`
| All metadata values registered for extraction as described in xref:rsocket.adoc#rsocket-metadata-extractor[MetadataExtractor].
| All metadata values registered for extraction as described in <<rsocket-metadata-extractor,MetadataExtractor>>.
|===
@@ -846,7 +846,7 @@ interaction type(s):
As an alternative to `@MessageMapping`, you can also handle requests with
`@RSocketExchange` methods. Such methods are declared on an
xref:rsocket-interface[RSocket Interface] and can be used as a requester via
<<rsocket-interface,RSocket Interface>> and can be used as a requester via
`RSocketServiceProxyFactory` or implemented by a responder.
For example, to handle requests as a responder:
@@ -897,8 +897,8 @@ former needs to remain suitable for requester and responder use. For example, wh
`@MessageMapping` can be declared to handle any number of routes and each route can
be a pattern, `@RSocketExchange` must be declared with a single, concrete route. There are
also small differences in the supported method parameters related to metadata, see
xref:rsocket-annot-messagemapping[@MessageMapping] and
xref:rsocket-interface[RSocket Interface] for a list of supported parameters.
<<rsocket-annot-messagemapping,@MessageMapping>> and
<<rsocket-interface,RSocket Interface>> for a list of supported parameters.
`@RSocketExchange` can be used at the type level to specify a common prefix for all routes
for a given RSocket service interface.
@@ -911,7 +911,7 @@ any subsequent metadata push notifications through the `METADATA_PUSH` frame, i.
`metadataPush(Payload)` in `io.rsocket.RSocket`.
`@ConnectMapping` methods support the same arguments as
xref:rsocket.adoc#rsocket-annot-messagemapping[@MessageMapping] but based on metadata and data from the `SETUP` and
<<rsocket-annot-messagemapping,@MessageMapping>> but based on metadata and data from the `SETUP` and
`METADATA_PUSH` frames. `@ConnectMapping` can have a pattern to narrow handling to
specific connections that have a route in the metadata, or if no patterns are declared
then all connections match.
@@ -920,7 +920,7 @@ then all connections match.
`Mono<Void>` as the return value. If handling returns an error for a new
connection then the connection is rejected. Handling must not be held up to make
requests to the `RSocketRequester` for the connection. See
xref:rsocket.adoc#rsocket-requester-server[Server Requester] for details.
<<rsocket-requester-server,Server Requester>> for details.
[[rsocket-metadata-extractor]]
@@ -1036,7 +1036,7 @@ Kotlin::
The Spring Framework lets you define an RSocket service as a Java interface with
`@RSocketExchange` methods. You can pass such an interface to `RSocketServiceProxyFactory`
to create a proxy which performs requests through an
xref:rsocket.adoc#rsocket-requester[RSocketRequester]. You can also implement the
<<rsocket-requester,RSocketRequester>>. You can also implement the
interface as a responder that handles requests.
Start by creating the interface with `@RSocketExchange` methods:
@@ -1064,7 +1064,7 @@ Now you can create a proxy that performs requests when methods are called:
----
You can also implement the interface to handle requests as a responder.
See xref:rsocket.adoc#rsocket-annot-rsocketexchange[Annotated Responders].
See <<rsocket-annot-rsocketexchange,Annotated Responders>>.
[[rsocket-interface-method-parameters]]
=== Method Parameters
@@ -5,13 +5,13 @@ The following annotations are supported when used in conjunction with the
xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit-jupiter-extension[`SpringExtension`]
and the JUnit Jupiter testing framework:
* xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-springextensionconfig[`@SpringExtensionConfig`]
* xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-junit-jupiter-springjunitconfig[`@SpringJUnitConfig`]
* xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-junit-jupiter-springjunitwebconfig[`@SpringJUnitWebConfig`]
* xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-testconstructor[`@TestConstructor`]
* xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-nestedtestconfiguration[`@NestedTestConfiguration`]
* xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-junit-jupiter-enabledif[`@EnabledIf`]
* xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-junit-jupiter-disabledif[`@DisabledIf`]
* <<integration-testing-annotations-springextensionconfig,`@SpringExtensionConfig`>>
* <<integration-testing-annotations-junit-jupiter-springjunitconfig,`@SpringJUnitConfig`>>
* <<integration-testing-annotations-junit-jupiter-springjunitwebconfig,`@SpringJUnitWebConfig`>>
* <<integration-testing-annotations-testconstructor,`@TestConstructor`>>
* <<integration-testing-annotations-nestedtestconfiguration,`@NestedTestConfiguration`>>
* <<integration-testing-annotations-junit-jupiter-enabledif,`@EnabledIf`>>
* <<integration-testing-annotations-junit-jupiter-disabledif,`@DisabledIf`>>
* xref:testing/annotations/integration-spring/annotation-disabledinaotmode.adoc[`@DisabledInAotMode`]
@@ -59,7 +59,7 @@ Consequently, there is no need to declare this annotation on a test class that d
contain `@Nested` test classes.
In addition,
xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-nestedtestconfiguration[`@NestedTestConfiguration`]
<<integration-testing-annotations-nestedtestconfiguration,`@NestedTestConfiguration`>>
does not apply to this annotation. `@SpringExtensionConfig` will always be detected
within a `@Nested` test class hierarchy, effectively disregarding any
`@NestedTestConfiguration(OVERRIDE)` declarations.
@@ -291,7 +291,7 @@ following annotations.
* xref:testing/annotations/integration-spring/annotation-sql.adoc[`@Sql`]
* xref:testing/annotations/integration-spring/annotation-sqlconfig.adoc[`@SqlConfig`]
* xref:testing/annotations/integration-spring/annotation-sqlmergemode.adoc[`@SqlMergeMode`]
* xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-testconstructor[`@TestConstructor`]
* <<integration-testing-annotations-testconstructor,`@TestConstructor`>>
NOTE: The use of `@NestedTestConfiguration` typically only makes sense in conjunction
with `@Nested` test classes in JUnit Jupiter; however, there may be other testing
@@ -13,10 +13,10 @@ xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit4-runne
xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit4-rules[Spring's JUnit 4 rules], or
xref:testing/testcontext-framework/support-classes.adoc#testcontext-support-classes-junit4[Spring's JUnit 4 support classes]:
* xref:testing/annotations/integration-junit4.adoc#integration-testing-annotations-junit4-ifprofilevalue[`@IfProfileValue`]
* xref:testing/annotations/integration-junit4.adoc#integration-testing-annotations-junit4-profilevaluesourceconfiguration[`@ProfileValueSourceConfiguration`]
* xref:testing/annotations/integration-junit4.adoc#integration-testing-annotations-junit4-timed[`@Timed`]
* xref:testing/annotations/integration-junit4.adoc#integration-testing-annotations-junit4-repeat[`@Repeat`]
* <<integration-testing-annotations-junit4-ifprofilevalue,`@IfProfileValue`>>
* <<integration-testing-annotations-junit4-profilevaluesourceconfiguration,`@ProfileValueSourceConfiguration`>>
* <<integration-testing-annotations-junit4-timed,`@Timed`>>
* <<integration-testing-annotations-junit4-repeat,`@Repeat`>>
[[integration-testing-annotations-junit4-ifprofilevalue]]
@@ -41,10 +41,10 @@ integration support, and the rest of this chapter then focuses on dedicated topi
Spring's integration testing support has the following primary goals:
* To manage xref:testing/integration.adoc#testing-ctx-management[Spring IoC container caching] between tests.
* To provide xref:testing/integration.adoc#testing-fixture-di[Dependency Injection of test fixture instances].
* To provide xref:testing/integration.adoc#testing-tx[transaction management] appropriate to integration testing.
* To supply xref:testing/integration.adoc#testing-support-classes[Spring-specific base classes] that assist
* To manage <<testing-ctx-management,Spring IoC container caching>> between tests.
* To provide <<testing-fixture-di,Dependency Injection of test fixture instances>>.
* To provide <<testing-tx,transaction management>> appropriate to integration testing.
* To supply <<testing-support-classes,Spring-specific base classes>> that assist
developers in writing integration tests.
The next few sections describe each goal and provide links to implementation and
@@ -139,8 +139,8 @@ xref:integration/rest-clients.adoc#rest-restclient[`RestClient`] and `RestTestCl
the same API up to the point of the call to `exchange()`. After that, `RestTestClient`
provides two alternative ways to verify the response:
1. xref:resttestclient-workflow[Built-in Assertions] extend the request workflow with a chain of expectations
2. xref:resttestclient-assertj[AssertJ Integration] to verify the response via `assertThat()` statements
1. <<resttestclient.workflow,Built-in Assertions>> extend the request workflow with a chain of expectations
2. <<resttestclient.assertj,AssertJ Integration>> to verify the response via `assertThat()` statements
TIP: See the xref:integration/rest-clients.adoc#rest-message-conversion[HTTP Message Conversion]
section for examples on how to prepare a request with any content, including form data and multipart data.
@@ -167,7 +167,7 @@ include-code::./RestClientWorkflowTests[tag=soft-assertions,indent=0]
You can then choose to decode the response body through one of the following:
* `expectBody(Class<T>)`: Decode to single object.
* `expectBody()`: Decode to `byte[]` for xref:testing/resttestclient.adoc#resttestclient-json[JSON Content] or an empty body.
* `expectBody()`: Decode to `byte[]` for <<resttestclient.json,JSON Content>> or an empty body.
If the built-in assertions are insufficient, you can consume the object instead and
@@ -58,7 +58,7 @@ implementations to the list of default factories in the same manner through thei
If a custom `ContextCustomizerFactory` is registered via `@ContextCustomizerFactories`, it
will be _merged_ with the default factories that have been registered using the aforementioned
xref:testing/testcontext-framework/ctx-management/context-customizers.adoc#testcontext-context-customizers-automatic-discovery[automatic discovery mechanism].
<<testcontext-context-customizers-automatic-discovery,automatic discovery mechanism>>.
The merging algorithm ensures that duplicates are removed from the list and that locally
declared factories are appended to the list of default factories when merged.
@@ -2,7 +2,7 @@
= Context Configuration with Groovy Scripts
To load an `ApplicationContext` for your tests by using Groovy scripts that use the
xref:core/beans/basics.adoc#beans-factory-groovy[Groovy Bean Definition DSL], you can annotate
xref:languages/groovy.adoc#beans-factory-groovy[Groovy Bean Definition DSL], you can annotate
your test class with `@ContextConfiguration` and configure the `locations` or `value`
attribute with an array that contains the resource locations of Groovy scripts. Resource
lookup semantics for Groovy scripts are the same as those described for
@@ -105,7 +105,7 @@ by default.
====
Method-level `@Sql` declarations override class-level declarations by default, but this
behavior may be configured per test class or per test method via `@SqlMergeMode`. See
xref:testing/testcontext-framework/executing-sql.adoc#testcontext-executing-sql-declaratively-script-merging[Merging and Overriding Configuration with `@SqlMergeMode`]
<<testcontext-executing-sql-declaratively-script-merging,Merging and Overriding Configuration with `@SqlMergeMode`>>
for further details.
However, this does not apply to class-level declarations configured for the
@@ -23,8 +23,8 @@ following features above and beyond the feature set that Spring supports for JUn
TestNG:
* Dependency injection for test constructors, test methods, and test lifecycle callback
methods. See xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit-jupiter-di[Dependency
Injection with the `SpringExtension`] for further details.
methods. See <<testcontext-junit-jupiter-di,Dependency Injection with the
`SpringExtension`>> for further details.
* Powerful support for link:https://docs.junit.org/current/extensions/conditional-test-execution.html[conditional
test execution] based on SpEL expressions, environment variables, system properties,
and so on. See the documentation for `@EnabledIf` and `@DisabledIf` in
@@ -503,7 +503,7 @@ Kotlin::
====
JUnit 4 is officially in maintenance mode, and JUnit 4 support in Spring is deprecated
since Spring Framework 7.0 in favor of the
xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit-jupiter-extension[`SpringExtension`]
<<testcontext-junit-jupiter-extension,`SpringExtension`>>
and JUnit Jupiter.
====
@@ -516,7 +516,7 @@ loading application contexts, dependency injection of test instances, transactio
method execution, and so on. If you want to use the Spring TestContext Framework with an
alternative runner (such as JUnit 4's `Parameterized` runner) or third-party runners
(such as the `MockitoJUnitRunner`), you can, optionally, use
xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit4-rules[Spring's support for JUnit rules]
<<testcontext-junit4-rules,Spring's support for JUnit rules>>
instead.
The following code listing shows the minimal requirements for configuring a test class to
@@ -566,7 +566,7 @@ be configured through `@ContextConfiguration`.
====
JUnit 4 is officially in maintenance mode, and JUnit 4 support in Spring is deprecated
since Spring Framework 7.0 in favor of the
xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit-jupiter-extension[`SpringExtension`]
<<testcontext-junit-jupiter-extension,`SpringExtension`>>
and JUnit Jupiter.
====
@@ -643,7 +643,7 @@ Kotlin::
====
JUnit 4 is officially in maintenance mode, and JUnit 4 support in Spring is deprecated
since Spring Framework 7.0 in favor of the
xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit-jupiter-extension[`SpringExtension`]
<<testcontext-junit-jupiter-extension,`SpringExtension`>>
and JUnit Jupiter.
====
@@ -679,7 +679,7 @@ Furthermore, `AbstractTransactionalJUnit4SpringContextTests` provides an
TIP: These classes are a convenience for extension. If you do not want your test classes
to be tied to a Spring-specific class hierarchy, you can configure your own custom test
classes by using `@RunWith(SpringRunner.class)` or
xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit4-rules[Spring's JUnit rules].
<<testcontext-junit4-rules,Spring's JUnit rules>>.
[[testcontext-support-classes-testng]]
@@ -99,7 +99,7 @@ manner through their own `spring.factories` files.
== Ordering `TestExecutionListener` Implementations
When the TestContext framework discovers default `TestExecutionListener` implementations
through the xref:testing/testcontext-framework/tel-config.adoc#testcontext-tel-config-automatic-discovery[aforementioned]
through the <<testcontext-tel-config-automatic-discovery,aforementioned>>
`SpringFactoriesLoader` mechanism, the instantiated listeners are sorted by using
Spring's `AnnotationAwareOrderComparator`, which honors Spring's `Ordered` interface and
`@Order` annotation for ordering. `AbstractTestExecutionListener` and all default
@@ -167,7 +167,7 @@ introduced in Spring Framework 4.1, and `DirtiesContextBeforeModesTestExecutionL
was introduced in Spring Framework 4.2. Furthermore, third-party frameworks like Spring
Boot and Spring Security register their own default `TestExecutionListener`
implementations by using the aforementioned
xref:testing/testcontext-framework/tel-config.adoc#testcontext-tel-config-automatic-discovery[automatic discovery mechanism].
<<testcontext-tel-config-automatic-discovery,automatic discovery mechanism>>.
To avoid having to be aware of and re-declare all default listeners, you can set the
`mergeMode` attribute of `@TestExecutionListeners` to `MergeMode.MERGE_WITH_DEFAULTS`.
@@ -175,7 +175,7 @@ To avoid having to be aware of and re-declare all default listeners, you can set
default listeners. The merging algorithm ensures that duplicates are removed from the
list and that the resulting set of merged listeners is sorted according to the semantics
of `AnnotationAwareOrderComparator`, as described in
xref:testing/testcontext-framework/tel-config.adoc#testcontext-tel-config-ordering[Ordering `TestExecutionListener` Implementations].
<<testcontext-tel-config-ordering,Ordering `TestExecutionListener` Implementations>>.
If a listener implements `Ordered` or is annotated with `@Order`, it can influence the
position in which it is merged with the defaults. Otherwise, locally declared listeners
are appended to the list of default listeners when merged.
@@ -195,7 +195,7 @@ Kotlin::
----
======
As explained in xref:testing/testcontext-framework/tx.adoc#testcontext-tx-rollback-and-commit-behavior[Transaction Rollback and Commit Behavior],
As explained in <<testcontext-tx-rollback-and-commit-behavior,Transaction Rollback and Commit Behavior>>,
there is no need to clean up the database after the `createUser()` method runs,
since any changes made to the database are automatically rolled back by the
`TransactionalTestExecutionListener`.
@@ -598,7 +598,7 @@ Kotlin::
.Testing ORM entity lifecycle callbacks
[NOTE]
=====
Similar to the note about avoiding xref:testing/testcontext-framework/tx.adoc#testcontext-tx-false-positives[false positives]
Similar to the note about avoiding <<testcontext-tx-false-positives,false positives>>
when testing ORM code, if your application makes use of entity lifecycle callbacks (also
known as entity listeners), make sure to flush the underlying unit of work within test
methods that run that code. Failing to _flush_ or _clear_ the underlying unit of work can
@@ -4,7 +4,7 @@
Dependency injection should make your code less dependent on the container than it would
be with traditional J2EE / Java EE development. The POJOs that make up your application
should be testable in JUnit or TestNG tests, with objects instantiated by using the `new`
operator, without Spring or any other container. You can use xref:testing/unit.adoc#mock-objects[mock objects]
operator, without Spring or any other container. You can use <<mock-objects,mock objects>>
(in conjunction with other valuable testing techniques) to test your code in isolation.
If you follow the architecture recommendations for Spring, the resulting clean layering
and componentization of your codebase facilitate easier unit testing. For example,
@@ -24,9 +24,9 @@ are described in this chapter.
Spring includes a number of packages dedicated to mocking:
* xref:testing/unit.adoc#mock-objects-env[Environment]
* xref:testing/unit.adoc#mock-objects-servlet[Servlet API]
* xref:testing/unit.adoc#mock-objects-web-reactive[Spring Web Reactive]
* <<mock-objects-env,Environment>>
* <<mock-objects-servlet,Servlet API>>
* <<mock-objects-web-reactive,Spring Web Reactive>>
[[mock-objects-env]]
=== Environment
@@ -81,8 +81,8 @@ end-to-end tests with a running server.
Spring includes a number of classes that can help with unit testing. They fall into two
categories:
* xref:testing/unit.adoc#unit-testing-utilities[General Testing Utilities]
* xref:testing/unit.adoc#unit-testing-spring-mvc[Spring MVC Testing Utilities]
* <<unit-testing-utilities,General Testing Utilities>>
* <<unit-testing-spring-mvc,Spring MVC Testing Utilities>>
[[unit-testing-utilities]]
=== General Testing Utilities
@@ -144,7 +144,7 @@ that deal with Spring MVC `ModelAndView` objects.
.Unit testing Spring MVC Controllers
TIP: To unit test your Spring MVC `Controller` classes as POJOs, use `ModelAndViewAssert`
combined with `MockHttpServletRequest`, `MockHttpSession`, and so on from Spring's
xref:testing/unit.adoc#mock-objects-servlet[Servlet API mocks]. For thorough integration
<<mock-objects-servlet,Servlet API mocks>>. For thorough integration
testing of your Spring MVC and REST `Controller` classes in conjunction with your
`WebApplicationContext` configuration for Spring MVC, use
xref:testing/mockmvc.adoc[MockMvc] instead.
@@ -279,8 +279,8 @@ xref:web/webflux-webclient.adoc[WebClient] and `WebTestClient` have
the same API up to the point of the call to `exchange()`. After that, `WebTestClient`
provides two alternative ways to verify the response:
1. xref:webtestclient-workflow[Built-in Assertions] extend the request workflow with a chain of expectations
2. xref:webtestclient-assertj[AssertJ Integration] to verify the response via `assertThat()` statements
1. <<webtestclient-workflow,Built-in Assertions>> extend the request workflow with a chain of expectations
2. <<webtestclient-assertj,AssertJ Integration>> to verify the response via `assertThat()` statements
TIP: See the xref:web/webflux-webclient/client-body.adoc[WebClient] documentation for
examples on how to prepare a request with any content including form data,
@@ -356,7 +356,7 @@ You can then choose to decode the response body through one of the following:
* `expectBody(Class<T>)`: Decode to single object.
* `expectBodyList(Class<T>)`: Decode and collect objects to `List<T>`.
* `expectBody()`: Decode to `byte[]` for xref:testing/webtestclient.adoc#webtestclient-json[JSON Content] or an empty body.
* `expectBody()`: Decode to `byte[]` for <<webtestclient-json,JSON Content>> or an empty body.
And perform assertions on the resulting higher level Object(s):
@@ -110,7 +110,7 @@ through one of the built-in xref:web/webflux/reactive-spring.adoc#webflux-httpha
* `RouterFunctions.toHttpHandler(RouterFunction)`
* `RouterFunctions.toHttpHandler(RouterFunction, HandlerStrategies)`
Most applications can run through the WebFlux Java configuration, see xref:web/webflux-functional.adoc#webflux-fn-running[Running a Server].
Most applications can run through the WebFlux Java configuration, see <<webflux-fn-running,Running a Server>>.
[[webflux-fn-handler-functions]]
@@ -369,7 +369,7 @@ parameter, though which additional constraints can be expressed.
You can write your own `RequestPredicate`, but the `RequestPredicates` utility class
offers built-in options for common needs for matching based on the HTTP method, request
path, headers, xref:#api-version[API version], and more.
path, headers, <<api-version,API version>>, and more.
The following example uses an `Accept` header, request predicate:
@@ -496,7 +496,7 @@ Router functions support matching by API version.
First, enable API versioning in the
xref:web/webflux/config.adoc#webflux-config-api-version[WebFlux Config], and then you can
use the `version` xref:#webflux-fn-predicates[predicate] as follows:
use the `version` <<webflux-fn-predicates,predicate>> as follows:
[tabs]
======
@@ -30,9 +30,9 @@ for testing in `WebTestClient`.
This is the central strategy for API versioning that holds all configured preferences
related to versioning. It does the following:
- Resolves versions from the requests via xref:#webflux-versioning-resolver[ApiVersionResolver]
- Parses raw version values into `Comparable<?>` with xref:#webflux-versioning-parser[ApiVersionParser]
- xref:#webflux-versioning-validation[Validates] request versions
- Resolves versions from the requests via <<webflux-versioning-resolver,ApiVersionResolver>>
- Parses raw version values into `Comparable<?>` with <<webflux-versioning-parser,ApiVersionParser>>
- <<webflux-versioning-validation,Validates>> request versions
`ApiVersionStrategy` helps to map requests to `@RequestMapping` controller methods,
and is initialized by the WebFlux config. Typically, applications do not interact
@@ -22,8 +22,8 @@ This section describes the HTTP caching related options available in Spring WebF
configuring settings related to the `Cache-Control` header and is accepted as an argument
in a number of places:
* xref:web/webflux/caching.adoc#webflux-caching-etag-lastmodified[Controllers]
* xref:web/webflux/caching.adoc#webflux-caching-static-resources[Static Resources]
* <<webflux-caching-etag-lastmodified,Controllers>>
* <<webflux-caching-static-resources,Static Resources>>
While {rfc-site}/rfc7234#section-5.2.2[RFC 7234] describes all possible
directives for the `Cache-Control` response header, the `CacheControl` type takes a
@@ -12,7 +12,7 @@ in xref:web/webflux/dispatcher-handler.adoc#webflux-special-bean-types[Special B
For more advanced customizations, not available in the configuration API, you can
gain full control over the configuration through the
xref:web/webflux/config.adoc#webflux-config-advanced-java[Advanced Configuration Mode].
<<webflux-config-advanced-java,Advanced Configuration Mode>>.
[[webflux-config-enable]]
@@ -45,7 +45,7 @@ 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
<<webflux-config-customize,the WebFlux config API section>> and in
{spring-boot-docs-ref}/web/reactive.html#web.reactive.webflux.auto-configuration[the dedicated Spring Boot documentation].
The preceding example registers a number of Spring WebFlux
@@ -454,7 +454,7 @@ Kotlin::
override fun configureViewResolvers(registry: ViewResolverRegistry) {
val resolver: ViewResolver = ...
registry.viewResolver(resolver
registry.viewResolver(resolver)
}
}
----
@@ -659,7 +659,7 @@ whether to decode the request path nor whether to remove semicolon content for
path matching purposes.
Spring WebFlux also does not support suffix pattern matching, unlike in Spring MVC, where we
are also xref:web/webmvc/mvc-controller/ann-requestmapping.adoc#mvc-ann-requestmapping-suffix-pattern-match[recommend] moving away from
are also xref:web/webmvc/mvc-controller/ann-requestmapping.adoc#mvc-ann-requestmapping-rfd[recommend] moving away from
reliance on it.
====
@@ -713,7 +713,7 @@ to contain the version. The path segment must be declared as a URI variable, e.g
"/\{version}", "/api/\{version}", etc. where the actual name is not important.
As the version is typically at the start of the path, consider configuring it externally
as a common path prefix for all handlers through the
xref:web/webflux/config.adoc#webflux-config-path-matching[Path Matching] options.
<<webflux-config-path-matching,Path Matching>> options.
By default, the version is parsed with `SemanticVersionParser`, but you can also configure
a custom xref:web/webflux-versioning.adoc#webflux-versioning-parser[ApiVersionParser].
@@ -107,4 +107,6 @@ Kotlin::
[[webflux-ann-initbinder-model-design]]
NOTE: For more guidance on model design, please see xref:web/webflux/data-binding.adoc[Data Binding].
== Model Design
Please see xref:web/webflux/data-binding.adoc[Data Binding] for more guidance on model object design.
@@ -49,7 +49,7 @@ recommended either to use an object tailored specifically for web binding, or to
constructor binding only. If property binding must still be used, then _allowedFields_
patterns should be set to limit which properties can be set. For further details on this
and example configuration, see
xref:web/webflux/controller/ann-initbinder.adoc#webflux-ann-initbinder-model-design[model design].
xref:web/webflux/data-binding.adoc#webflux-data-binding-design[model design].
When using constructor binding, you can customize request parameter names through an
`@BindParam` annotation. For example:
@@ -24,7 +24,7 @@ There are also HTTP method specific shortcut variants of `@RequestMapping`:
* `@DeleteMapping`
* `@PatchMapping`
The preceding annotations are xref:web/webflux/controller/ann-requestmapping.adoc#webflux-ann-requestmapping-composed[Custom Annotations] that are provided
The preceding annotations are <<webflux-ann-requestmapping-composed,Custom Annotations>> that are provided
because, arguably, most controller methods should be mapped to a specific HTTP method versus
using `@RequestMapping`, which, by default, matches to all HTTP methods. At the same time, a
`@RequestMapping` is still needed at the class level to express shared mappings.
@@ -65,64 +65,4 @@ The exception contains a list of ``ParameterValidationResult``s that group valid
by method parameter. You can either iterate over those, or provide a visitor with callback
methods by controller method parameter type:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
HandlerMethodValidationException ex = ... ;
ex.visitResults(new HandlerMethodValidationException.Visitor() {
@Override
public void requestHeader(RequestHeader requestHeader, ParameterValidationResult result) {
// ...
}
@Override
public void requestParam(@Nullable RequestParam requestParam, ParameterValidationResult result) {
// ...
}
@Override
public void modelAttribute(@Nullable ModelAttribute modelAttribute, ParameterErrors errors) {
// ...
@Override
public void other(ParameterValidationResult result) {
// ...
}
});
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
// HandlerMethodValidationException
val ex
ex.visitResults(object : HandlerMethodValidationException.Visitor {
override fun requestHeader(requestHeader: RequestHeader, result: ParameterValidationResult) {
// ...
}
override fun requestParam(requestParam: RequestParam?, result: ParameterValidationResult) {
// ...
}
override fun modelAttribute(modelAttribute: ModelAttribute?, errors: ParameterErrors) {
// ...
}
// ...
override fun other(result: ParameterValidationResult) {
// ...
}
})
----
======
include-code::./HandlerMethodValidationExceptionVisitor[tag=snippet,indent=0]
@@ -20,7 +20,7 @@ Spring configuration in a WebFlux application typically contains:
* `DispatcherHandler` with the bean name `webHandler`
* `WebFilter` and `WebExceptionHandler` beans
* xref:web/webflux/dispatcher-handler.adoc#webflux-special-bean-types[`DispatcherHandler` special beans]
* <<webflux-special-bean-types,`DispatcherHandler` special beans>>
* Others
The configuration is given to `WebHttpHandlerBuilder` to build the processing chain,
@@ -86,7 +86,7 @@ in the Web Handler API).
| `HandlerResultHandler`
| Process the result from the handler invocation and finalize the response.
See xref:web/webflux/dispatcher-handler.adoc#webflux-resulthandling[Result Handling].
See <<webflux-resulthandling,Result Handling>>.
|===
@@ -97,9 +97,9 @@ in the Web Handler API).
Applications can declare the infrastructure beans (listed under
xref:web/webflux/reactive-spring.adoc#webflux-web-handler-api-special-beans[Web Handler API] and
xref:web/webflux/dispatcher-handler.adoc#webflux-special-bean-types[`DispatcherHandler`])
<<webflux-special-bean-types,`DispatcherHandler`>>)
that are required to process requests. However, in most cases, the
xref:web/webflux/dispatcher-handler.adoc#webflux-framework-config[WebFlux Config]
<<webflux-framework-config,WebFlux Config>>
is the best starting point. It declares the required beans and provides a higher-level
configuration callback API to customize it.
@@ -127,7 +127,7 @@ The return value from the invocation of a handler, through a `HandlerAdapter`, i
as a `HandlerResult`, along with some additional context, and passed to the first
`HandlerResultHandler` that claims support for it. The following table shows the available
`HandlerResultHandler` implementations, all of which are declared in the
xref:web/webflux/dispatcher-handler.adoc#webflux-framework-config[WebFlux Config]:
<<webflux-framework-config,WebFlux Config>>:
[cols="1,2,1", options="header"]
|===
@@ -151,7 +151,7 @@ xref:web/webflux/dispatcher-handler.adoc#webflux-framework-config[WebFlux Config
{spring-framework-api}/web/reactive/result/view/Rendering.html[Rendering],
or any other `Object` is treated as a model attribute.
See also xref:web/webflux/dispatcher-handler.adoc#webflux-viewresolution[View Resolution].
See also <<webflux-viewresolution,View Resolution>>.
| `Integer.MAX_VALUE`
|===
@@ -183,7 +183,7 @@ xref:web/webflux/reactive-spring.adoc#webflux-exception-handler[Exceptions] in t
View resolution enables rendering to a browser with an HTML template and a model without
tying you to a specific view technology. In Spring WebFlux, view resolution is
supported through a dedicated xref:web/webflux/dispatcher-handler.adoc#webflux-resulthandling[HandlerResultHandler]
supported through a dedicated <<webflux-resulthandling,HandlerResultHandler>>
that uses `ViewResolver` instances to map a String (representing a logical view name) to
a `View` instance. The `View` is then used to render the response.
@@ -167,7 +167,7 @@ unsure what benefits to look for, start by learning about how non-blocking I/O w
Spring WebFlux is supported on Tomcat, Jetty, Servlet containers, as well as on
non-Servlet runtimes such as Netty. All servers are adapted to a low-level,
xref:web/webflux/reactive-spring.adoc#webflux-httphandler[common API] so that higher-level
xref:web/webflux/new-framework.adoc#webflux-programming-models[programming models] can be supported across servers.
<<webflux-programming-models,programming models>> can be supported across servers.
Spring WebFlux does not have built-in support to start or stop a server. However, it is
easy to xref:web/webflux/reactive-spring.adoc#webflux-web-handler-api[assemble] an application from Spring configuration and
@@ -269,7 +269,7 @@ of their own.
=== Configuring
The Spring Framework does not provide support for starting and stopping
xref:web/webflux/new-framework.adoc#webflux-server-choice[servers]. To configure the threading model for a server,
<<webflux-server-choice,servers>>. To configure the threading model for a server,
you need to use server-specific configuration APIs, or, if you use Spring Boot,
check the Spring Boot configuration options for each server. You can
xref:web/webflux-webclient/client-builder.adoc[configure] the `WebClient` directly.
@@ -5,10 +5,10 @@ The `spring-web` module contains the following foundational support for reactive
applications:
* For server request processing there are two levels of support.
** xref:web/webflux/reactive-spring.adoc#webflux-httphandler[HttpHandler]: Basic contract for HTTP request handling with
** <<webflux-httphandler,HttpHandler>>: Basic contract for HTTP request handling with
non-blocking I/O and Reactive Streams back pressure, along with adapters for Reactor Netty,
Tomcat, Jetty, and any Servlet container.
** xref:web/webflux/reactive-spring.adoc#webflux-web-handler-api[`WebHandler` API]: Slightly higher level, general-purpose web API for
** <<webflux-web-handler-api,`WebHandler` API>>: Slightly higher level, general-purpose web API for
request handling, on top of which concrete programming models such as annotated
controllers and functional endpoints are built.
* For the client side, there is a basic `ClientHttpConnector` contract to perform HTTP
@@ -18,7 +18,7 @@ https://github.com/jetty-project/jetty-reactive-httpclient[Jetty HttpClient]
and https://hc.apache.org/[Apache HttpComponents].
The higher level xref:web/webflux-webclient.adoc[WebClient] used in applications
builds on this basic contract.
* For client and server, xref:web/webflux/reactive-spring.adoc#webflux-codecs[codecs] for serialization and
* For client and server, <<webflux-codecs,codecs>> for serialization and
deserialization of HTTP request and response content.
@@ -188,14 +188,14 @@ to adapt `HttpHandler` to a `Servlet` via `ServletHttpHandlerAdapter`.
== `WebHandler` API
The `org.springframework.web.server` package builds on the
xref:web/webflux/reactive-spring.adoc#webflux-httphandler[`HttpHandler`] contract
<<webflux-httphandler,`HttpHandler`>> contract
to provide a general-purpose web API for processing requests through a chain of multiple
{spring-framework-api}/web/server/WebExceptionHandler.html[`WebExceptionHandler`], multiple
{spring-framework-api}/web/server/WebFilter.html[`WebFilter`], and a single
{spring-framework-api}/web/server/WebHandler.html[`WebHandler`] component. The chain can
be put together with `WebHttpHandlerBuilder` by simply pointing to a Spring
`ApplicationContext` where components are
xref:web/webflux/reactive-spring.adoc#webflux-web-handler-api-special-beans[auto-detected], and/or by registering components
<<webflux-web-handler-api-special-beans,auto-detected>>, and/or by registering components
with the builder.
While `HttpHandler` has a simple goal to abstract the use of different HTTP servers, the
@@ -223,13 +223,13 @@ Spring ApplicationContext, or that can be registered directly with it:
| `WebExceptionHandler`
| 0..N
| Provide handling for exceptions from the chain of `WebFilter` instances and the target
`WebHandler`. For more details, see xref:web/webflux/reactive-spring.adoc#webflux-exception-handler[Exceptions].
`WebHandler`. For more details, see <<webflux-exception-handler,Exceptions>>.
| <any>
| `WebFilter`
| 0..N
| Apply interception style logic to before and after the rest of the filter chain and
the target `WebHandler`. For more details, see xref:web/webflux/reactive-spring.adoc#webflux-filters[Filters].
the target `WebHandler`. For more details, see <<webflux-filters,Filters>>.
| `webHandler`
| `WebHandler`
@@ -287,7 +287,7 @@ Kotlin::
The `DefaultServerWebExchange` uses the configured `HttpMessageReader` to parse form data
(`application/x-www-form-urlencoded`) into a `MultiValueMap`. By default,
`FormHttpMessageReader` is configured for use by the `ServerCodecConfigurer` bean
(see the xref:web/webflux/reactive-spring.adoc#webflux-web-handler-api[Web Handler API]).
(see the <<webflux-web-handler-api,Web Handler API>>).
[[webflux-multipart]]
@@ -321,7 +321,7 @@ dependencies.
Alternatively, the `SynchronossPartHttpMessageReader` can be used, which is based on the
https://github.com/synchronoss/nio-multipart[Synchronoss NIO Multipart] library.
Both are configured through the `ServerCodecConfigurer` bean
(see the xref:web/webflux/reactive-spring.adoc#webflux-web-handler-api[Web Handler API]).
(see the <<webflux-web-handler-api,Web Handler API>>).
To parse multipart data in streaming fashion, you can use the `Flux<PartEvent>` returned from the
`PartEventHttpMessageReader` instead of using `@RequestPart`, as that implies `Map`-like access
@@ -342,7 +342,7 @@ include::partial$web/forwarded-headers.adoc[]
from the standard `"Forwarded"` or `"X-Forwarded"` headers, and also removes those headers
to eliminate further impact. If you declare it as a bean with the name
`forwardedHeaderTransformer`, it will be
xref:web/webflux/reactive-spring.adoc#webflux-web-handler-api-special-beans[detected] and used.
<<webflux-web-handler-api-special-beans,detected>> and used.
[[webflux-forwarded-headers-security]]
=== Security Considerations
@@ -363,7 +363,7 @@ forwarded headers from the request without using them.
== Filters
[.small]#xref:web/webmvc/filters.adoc[See equivalent in the Servlet stack]#
In the xref:web/webflux/reactive-spring.adoc#webflux-web-handler-api[`WebHandler` API], you can use a `WebFilter` to apply interception-style
In the <<webflux-web-handler-api,`WebHandler` API>>, you can use a `WebFilter` to apply interception-style
logic before and after the rest of the processing chain of filters and the target
`WebHandler`. When using the xref:web/webflux/dispatcher-handler.adoc#webflux-framework-config[WebFlux Config], registering a `WebFilter` is as simple
as declaring it as a Spring bean and (optionally) expressing precedence by using `@Order` on
@@ -408,7 +408,7 @@ not map when trailing slash handling applies; use `@RequestMapping` (no path att
== Exceptions
[.small]#xref:web/webmvc/mvc-servlet/exceptionhandlers.adoc#mvc-ann-customer-servlet-container-error-page[See equivalent in the Servlet stack]#
In the xref:web/webflux/reactive-spring.adoc#webflux-web-handler-api[`WebHandler` API], you can use a `WebExceptionHandler` to handle
In the <<webflux-web-handler-api,`WebHandler` API>>, you can use a `WebExceptionHandler` to handle
exceptions from the chain of `WebFilter` instances and the target `WebHandler`. When using the
xref:web/webflux/dispatcher-handler.adoc#webflux-framework-config[WebFlux Config], registering a `WebExceptionHandler` is as simple as declaring it as a
Spring bean and (optionally) expressing precedence by using `@Order` on the bean declaration or
@@ -515,8 +515,8 @@ encode a `Mono<List<String>>`.
On the server side where form content often needs to be accessed from multiple places,
`ServerWebExchange` provides a dedicated `getFormData()` method that parses the content
through `FormHttpMessageReader` and then caches the result for repeated access.
See xref:web/webflux/reactive-spring.adoc#webflux-form-data[Form Data] in the
xref:web/webflux/reactive-spring.adoc#webflux-web-handler-api[`WebHandler` API] section.
See <<webflux-form-data,Form Data>> in the
<<webflux-web-handler-api,`WebHandler` API>> section.
Once `getFormData()` is used, the original raw content can no longer be read from the
request body. For this reason, applications are expected to go through `ServerWebExchange`
@@ -537,8 +537,8 @@ For more information about the `DefaultPartHttpMessageReader`, refer to the
On the server side where multipart form content may need to be accessed from multiple
places, `ServerWebExchange` provides a dedicated `getMultipartData()` method that parses
the content through `MultipartHttpMessageReader` and then caches the result for repeated access.
See xref:web/webflux/reactive-spring.adoc#webflux-multipart[Multipart Data] in the
xref:web/webflux/reactive-spring.adoc#webflux-web-handler-api[`WebHandler` API] section.
See <<webflux-multipart,Multipart Data>> in the
<<webflux-web-handler-api,`WebHandler` API>> section.
Once `getMultipartData()` is used, the original raw content can no longer be read from the
request body. For this reason applications have to consistently use `getMultipartData()`
@@ -590,7 +590,7 @@ set all codecs, see xref:web/webflux/config.adoc#webflux-config-message-codecs[H
all codecs can be changed in
xref:web/webflux-webclient/client-builder.adoc#webflux-client-builder-maxinmemorysize[WebClient.Builder].
For xref:web/webflux/reactive-spring.adoc#webflux-codecs-multipart[Multipart parsing] the `maxInMemorySize` property limits
For <<webflux-codecs-multipart,Multipart parsing>> the `maxInMemorySize` property limits
the size of non-file parts. For file parts, it determines the threshold at which the part
is written to disk. For file parts written to disk, there is an additional
`maxDiskUsagePerPart` property to limit the amount of disk space per part. There is also
@@ -737,8 +737,8 @@ or specific behaviors that are not supported by the default codecs.
Some configuration options expressed by developers are enforced on default codecs.
Custom codecs might want to get a chance to align with those preferences,
like xref:web/webflux/reactive-spring.adoc#webflux-codecs-limits[enforcing buffering limits]
or xref:web/webflux/reactive-spring.adoc#webflux-logging-sensitive-data[logging sensitive data].
like <<webflux-codecs-limits,enforcing buffering limits>>
or <<webflux-logging-sensitive-data,logging sensitive data>>.
The following example shows how to do so for client-side requests:
@@ -109,7 +109,7 @@ Kotlin::
If you register the `RouterFunction` as a bean, for instance by exposing it in a
`@Configuration` class, it will be auto-detected by the servlet, as explained in
xref:web/webmvc-functional.adoc#webmvc-fn-running[Running a Server].
<<webmvc-fn-running,Running a Server>>.
[[webmvc-fn-handler-functions]]
@@ -568,7 +568,7 @@ parameter, through which additional constraints can be expressed.
You can write your own `RequestPredicate`, but the `RequestPredicates` utility class
offers built-in options for common needs for matching based on the HTTP method, request
path, headers, xref:#api-version[API version], and more.
path, headers, <<api-version,API version>>, and more.
The following example uses an `Accept` header, request predicate:
@@ -778,7 +778,7 @@ Router functions support matching by API version.
First, enable API versioning in the
xref:web/webmvc/mvc-config/api-version.adoc[MVC Config], and then you can use the
`version` xref:#webmvc-fn-predicates[predicate] as follows:
`version` <<webmvc-fn-predicates,predicate>> as follows:
[tabs]
======
@@ -1,6 +1,6 @@
[[test]]
= Testing
[.small]#xref:web-reactive.adoc#webflux-test[See equivalent in the Reactive stack]#
[.small]#xref:web/webflux-test.adoc[See equivalent in the Reactive stack]#
This section summarizes the options available in `spring-test` for Spring MVC applications.
@@ -29,9 +29,9 @@ for testing in MockMvc and `WebTestClient`.
This is the central strategy for API versioning that holds all configured preferences
related to versioning. It does the following:
- Resolves versions from the requests via xref:#mvc-versioning-resolver[ApiVersionResolver]
- Parses raw version values into `Comparable<?>` with an xref:#mvc-versioning-parser[ApiVersionParser]
- xref:#mvc-versioning-validation[Validates] request versions
- Resolves versions from the requests via <<mvc-versioning-resolver,ApiVersionResolver>>
- Parses raw version values into `Comparable<?>` with an <<mvc-versioning-parser,ApiVersionParser>>
- <<mvc-versioning-validation,Validates>> request versions
- Sends deprecation hints in the responses
`ApiVersionStrategy` helps to map requests to `@RequestMapping` controller methods,
@@ -164,7 +164,7 @@ following example shows:
=== The `input` Tag
This tag renders an HTML `input` element with the bound value and `type='text'` by default.
For an example of this tag, see xref:web/webmvc-view/mvc-jsp.adoc#mvc-view-jsp-formtaglib-formtag[The Form Tag]. You can also use
For an example of this tag, see <<mvc-view-jsp-formtaglib-formtag,The Form Tag>>. You can also use
HTML5-specific types, such as `email`, `tel`, `date`, and others.
[[mvc-view-jsp-formtaglib-checkboxtag]]
@@ -354,7 +354,7 @@ but with different values, as the following example shows:
This tag renders multiple HTML `input` elements with the `type` set to `radio`.
As with the xref:web/webmvc-view/mvc-jsp.adoc#mvc-view-jsp-formtaglib-checkboxestag[`checkboxes` tag], you might want to
As with the <<mvc-view-jsp-formtaglib-checkboxestag,`checkboxes` tag>>, you might want to
pass in the available options as a runtime variable. For this usage, you can use the
`radiobuttons` tag. You pass in an `Array`, a `List`, or a `Map` that contains the
available options in the `items` property. If you use a `Map`, the map entry key is
@@ -8,11 +8,11 @@ before and after the rest of the processing chain of filters and the target `Ser
The `spring-web` module has a number of built-in `Filter` implementations:
* xref:web/webmvc/filters.adoc#filters-http-put[Form Data]
* xref:web/webmvc/filters.adoc#filters-forwarded-headers[Forwarded Headers]
* xref:web/webmvc/filters.adoc#filters-shallow-etag[Shallow ETag]
* xref:web/webmvc/filters.adoc#filters-cors[CORS]
* xref:web/webmvc/filters.adoc#filters.url-handler[URL Handler]
* <<filters-http-put,Form Data>>
* <<filters-forwarded-headers,Forwarded Headers>>
* <<filters-shallow-etag,Shallow ETag>>
* <<filters-cors,CORS>>
* <<filters.url-handler,URL Handler>>
There are also base class implementations for use in Spring applications:
@@ -2,25 +2,25 @@
= Asynchronous Requests
Spring MVC has an extensive integration with Servlet asynchronous request
xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-processing[processing]:
<<mvc-ann-async-processing,processing>>:
* xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-deferredresult[`DeferredResult`],
xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-callable[`Callable`], and
xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-webasynctask[`WebAsyncTask`] return values
* <<mvc-ann-async-deferredresult,`DeferredResult`>>,
<<mvc-ann-async-callable,`Callable`>>, and
<<mvc-ann-async-webasynctask,`WebAsyncTask`>> return values
in controller methods provide support for a single asynchronous return value.
* Controllers can xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-http-streaming[stream] multiple values, including
xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-sse[SSE] and
xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-output-stream[raw data].
* Controllers can <<mvc-ann-async-http-streaming,stream>> multiple values, including
<<mvc-ann-async-sse,SSE>> and
<<mvc-ann-async-output-stream,raw data>>.
* Controllers can use reactive clients and return
xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-reactive-types[reactive types] for response handling.
<<mvc-ann-async-reactive-types,reactive types>> for response handling.
For an overview of how this differs from Spring WebFlux, see the xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-vs-webflux[Async Spring MVC compared to WebFlux] section below.
For an overview of how this differs from Spring WebFlux, see the <<mvc-ann-async-vs-webflux,Async Spring MVC compared to WebFlux>> section below.
[[mvc-ann-async-deferredresult]]
== `DeferredResult`
Once the asynchronous request processing feature is xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-configuration[enabled]
Once the asynchronous request processing feature is <<mvc-ann-async-configuration,enabled>>
in the Servlet container, controller methods can wrap any supported controller method
return value with `DeferredResult`, as the following example shows:
@@ -94,13 +94,13 @@ Kotlin::
======
The return value can then be obtained by running the given task through the
xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-configuration-spring-mvc[configured] `AsyncTaskExecutor`.
<<mvc-ann-async-configuration-spring-mvc,configured>> `AsyncTaskExecutor`.
[[mvc-ann-async-webasynctask]]
== `WebAsyncTask`
`WebAsyncTask` is comparable to using xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-callable[Callable]
`WebAsyncTask` is comparable to using <<mvc-ann-async-callable,Callable>>
but allows customizing additional settings such a request timeout value, and the
`AsyncTaskExecutor` to execute the `java.util.concurrent.Callable` with instead
of the defaults set up globally for Spring MVC. Below is an example of using `WebAsyncTask`:
@@ -228,7 +228,7 @@ handling is built into all framework contracts and is intrinsically supported th
stages of request processing.
From a programming model perspective, both Spring MVC and Spring WebFlux support
asynchronous and xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-reactive-types[Reactive Types] as return values in controller methods.
asynchronous and <<mvc-ann-async-reactive-types,Reactive Types>> as return values in controller methods.
Spring MVC even supports streaming, including reactive back pressure. However, individual
writes to the response remain blocking (and are performed on a separate thread), unlike WebFlux,
which relies on non-blocking I/O and does not need an extra thread for each write.
@@ -239,7 +239,7 @@ nor does it have any explicit support for asynchronous and reactive types as mod
Spring WebFlux does support all that.
Finally, from a configuration perspective the asynchronous request processing feature must be
xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-configuration[enabled at the Servlet container level].
<<mvc-ann-async-configuration,enabled at the Servlet container level>>.
[[mvc-ann-async-http-streaming]]
@@ -368,7 +368,7 @@ xref:web/websocket.adoc[WebSocket messaging] with
xref:web/websocket/fallback.adoc[SockJS fallback] transports (including SSE) that target
a wide range of browsers.
See also xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-objects[previous section] for notes on exception handling.
See also <<mvc-ann-async-objects,previous section>> for notes on exception handling.
[[mvc-ann-async-output-stream]]
=== Raw Data
@@ -436,7 +436,7 @@ TIP: Spring MVC supports Reactor and RxJava through the
For streaming to the response, reactive back pressure is supported, but writes to the
response are still blocking and are run on a separate thread through the
xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-configuration-spring-mvc[configured]
<<mvc-ann-async-configuration-spring-mvc,configured>>
`AsyncTaskExecutor`, to avoid blocking the upstream source such as a `Flux` returned
from `WebClient`.
@@ -455,7 +455,7 @@ GraphQL Java https://www.graphql-java.com/documentation/concerns/#context-object
and others.
If Micrometer Context Propagation is present on the classpath, when a controller method
returns a xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-reactive-types[reactive type] such as `Flux` or `Mono`, all
returns a <<mvc-ann-async-reactive-types,reactive type>> such as `Flux` or `Mono`, all
`ThreadLocal` values, for which there is a registered `io.micrometer.ThreadLocalAccessor`,
are written to the Reactor `Context` as key-value pairs, using the key assigned by the
`ThreadLocalAccessor`.
@@ -491,8 +491,8 @@ Micrometer Context Propagation library.
[.small]#xref:web/webflux/reactive-spring.adoc#webflux-codecs-streaming[See equivalent in the Reactive stack]#
The Servlet API does not provide any notification when a remote client goes away.
Therefore, while streaming to the response, whether through xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-sse[SseEmitter]
or xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-reactive-types[reactive types], it is important to send data periodically,
Therefore, while streaming to the response, whether through <<mvc-ann-async-sse,SseEmitter>>
or <<mvc-ann-async-reactive-types,reactive types>>, it is important to send data periodically,
since the write fails if the client has disconnected. The send could take the form of an
empty (comment-only) SSE event or any other data that the other side would have to interpret
as a heartbeat and ignore.
@@ -535,7 +535,7 @@ You can configure the following:
* The default timeout value for async requests depends
on the underlying Servlet container, unless it is set explicitly.
* `AsyncTaskExecutor` to use for blocking writes when streaming with
xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-reactive-types[Reactive Types] and for
<<mvc-ann-async-reactive-types,Reactive Types>> and for
executing `Callable` instances returned from controller methods.
The one used by default is not suitable for production under load.
* `DeferredResultProcessingInterceptor` implementations and `CallableProcessingInterceptor` implementations.
@@ -24,8 +24,8 @@ in a number of places:
* {spring-framework-api}/web/servlet/mvc/WebContentInterceptor.html[`WebContentInterceptor`]
* {spring-framework-api}/web/servlet/support/WebContentGenerator.html[`WebContentGenerator`]
* xref:web/webmvc/mvc-caching.adoc#mvc-caching-etag-lastmodified[Controllers]
* xref:web/webmvc/mvc-caching.adoc#mvc-caching-static-resources[Static Resources]
* <<mvc-caching-etag-lastmodified,Controllers>>
* <<mvc-caching-static-resources,Static Resources>>
While {rfc-site}/rfc7234#section-5.2.2[RFC 7234] describes all possible
directives for the `Cache-Control` response header, the `CacheControl` type takes a
@@ -10,8 +10,7 @@ By default, only the `Accept` header is checked.
If you must use URL-based content type resolution, consider using the query parameter
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
xref:web/webmvc/mvc-controller/ann-requestmapping.adoc#mvc-ann-requestmapping-rfd[Suffix Match and RFD] for
more details.
You can customize requested content type resolution, as the following example shows:
@@ -107,4 +107,6 @@ Kotlin::
[[mvc-ann-initbinder-model-design]]
NOTE: For more guidance on model design, please see xref:web/webmvc/mvc-data-binding.adoc[Data Binding].
== Model Design
Please see xref:web/webmvc/mvc-data-binding.adoc[Data Binding] for guidance on safe model object design.
@@ -84,7 +84,7 @@ recommended either to use an object tailored specifically for web binding, or to
constructor binding only. If property binding must still be used, then _allowedFields_
patterns should be set to limit which properties can be set. For further details on this
and example configuration, see
xref:web/webmvc/mvc-controller/ann-initbinder.adoc#mvc-ann-initbinder-model-design[model design].
xref:web/webmvc/mvc-data-binding.adoc#mvc-data-binding-design[model design].
When using constructor binding, you can customize request parameter names through an
`@BindParam` annotation. For example:
@@ -25,7 +25,7 @@ There are also HTTP method specific shortcut variants of `@RequestMapping`:
* `@PatchMapping`
The shortcuts are
xref:web/webmvc/mvc-controller/ann-requestmapping.adoc#mvc-ann-requestmapping-composed[Custom Annotations]
<<mvc-ann-requestmapping-composed,Custom Annotations>>
that are provided because, arguably, most controller methods should be mapped to a specific
HTTP method versus using `@RequestMapping`, which, by default, matches to all HTTP methods.
A `@RequestMapping` is still needed at the class level to express shared mappings.
@@ -405,10 +405,9 @@ Kotlin::
<1> Testing whether `myHeader` equals `myValue`.
======
TIP: You can match `Content-Type` and `Accept` with the headers condition, but it is better to use
xref:web/webmvc/mvc-controller/ann-requestmapping.adoc#mvc-ann-requestmapping-consumes[consumes]
and xref:web/webmvc/mvc-controller/ann-requestmapping.adoc#mvc-ann-requestmapping-produces[produces]
instead.
TIP: You can match `Content-Type` and `Accept` with the headers condition, but it is
better to use <<mvc-ann-requestmapping-consumes,consumes>> and
<<mvc-ann-requestmapping-produces,produces>> instead.
[[mvc-ann-requestmapping-version]]
@@ -521,7 +520,7 @@ is not necessary in the common case.
[[mvc-ann-requestmapping-composed]]
== Custom Annotations
[.small]#xref:web/webmvc/mvc-controller/ann-requestmapping.adoc#mvc-ann-requestmapping-head-options[See equivalent in the Reactive stack]#
[.small]#<<mvc-ann-requestmapping-head-options,See equivalent in the Reactive stack>>#
Spring MVC supports the use of xref:core/beans/classpath-scanning.adoc#beans-meta-annotations[composed annotations]
for request mapping. Those are annotations that are themselves meta-annotated with
@@ -648,4 +647,4 @@ xref:web/webmvc/mvc-controller/ann-methods/arguments.adoc[@RequestMapping].
`@HttpExchange` also supports a `headers()` parameter which accepts `"name=value"`-like
pairs like in `@RequestMapping(headers={})` on the client side. On the server side,
this extends to the full syntax that
xref:#mvc-ann-requestmapping-params-and-headers[`@RequestMapping`] supports.
<<mvc-ann-requestmapping-params-and-headers,`@RequestMapping`>> supports.
@@ -65,64 +65,4 @@ The exception contains a list of ``ParameterValidationResult``s that group valid
by method parameter. You can either iterate over those, or provide a visitor with callback
methods by controller method parameter type:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
HandlerMethodValidationException ex = ... ;
ex.visitResults(new HandlerMethodValidationException.Visitor() {
@Override
public void requestHeader(RequestHeader requestHeader, ParameterValidationResult result) {
// ...
}
@Override
public void requestParam(@Nullable RequestParam requestParam, ParameterValidationResult result) {
// ...
}
@Override
public void modelAttribute(@Nullable ModelAttribute modelAttribute, ParameterErrors errors) {
// ...
@Override
public void other(ParameterValidationResult result) {
// ...
}
});
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
// HandlerMethodValidationException
val ex
ex.visitResults(object : HandlerMethodValidationException.Visitor {
override fun requestHeader(requestHeader: RequestHeader, result: ParameterValidationResult) {
// ...
}
override fun requestParam(requestParam: RequestParam?, result: ParameterValidationResult) {
// ...
}
override fun modelAttribute(modelAttribute: ModelAttribute?, errors: ParameterErrors) {
// ...
}
// ...
override fun other(result: ParameterValidationResult) {
// ...
}
})
----
======
include-code::./HandlerMethodValidationExceptionVisitor[tag=snippet,indent=0]
@@ -19,11 +19,11 @@ Locale resolvers and interceptors are defined in the
context in the normal way. The following selection of locale resolvers is included in
Spring.
* xref:web/webmvc/mvc-servlet/localeresolver.adoc#mvc-timezone[Time Zone]
* xref:web/webmvc/mvc-servlet/localeresolver.adoc#mvc-localeresolver-acceptheader[Header Resolver]
* xref:web/webmvc/mvc-servlet/localeresolver.adoc#mvc-localeresolver-cookie[Cookie Resolver]
* xref:web/webmvc/mvc-servlet/localeresolver.adoc#mvc-localeresolver-session[Session Resolver]
* xref:web/webmvc/mvc-servlet/localeresolver.adoc#mvc-localeresolver-interceptor[Locale Interceptor]
* <<mvc-timezone,Time Zone>>
* <<mvc-localeresolver-acceptheader,Header Resolver>>
* <<mvc-localeresolver-cookie,Cookie Resolver>>
* <<mvc-localeresolver-session,Session Resolver>>
* <<mvc-localeresolver-interceptor,Locale Interceptor>>
[[mvc-timezone]]
@@ -41,7 +41,7 @@ The following table provides more details on the `ViewResolver` hierarchy:
| `ContentNegotiatingViewResolver`
| Implementation of the `ViewResolver` interface that resolves a view based on the
request file name or `Accept` header. See xref:web/webmvc/mvc-servlet/viewresolver.adoc#mvc-multiple-representations[Content Negotiation].
request file name or `Accept` header. See <<mvc-multiple-representations,Content Negotiation>>.
| `BeanNameViewResolver`
| Implementation of the `ViewResolver` interface that interprets a view name as a
@@ -52,7 +52,7 @@ the steps of the WebSocket handshake, including validating the client origin,
negotiating a sub-protocol, and other details. An application may also need to use this
option if it needs to configure a custom `RequestUpgradeStrategy` in order to
adapt to a WebSocket server engine and version that is not yet supported
(see xref:web/websocket/server.adoc#websocket-server-deployment[Deployment] for more on this subject).
(see <<websocket-server-deployment,Deployment>> for more on this subject).
Both the Java configuration and XML namespace make it possible to configure a custom
`HandshakeHandler`.
@@ -5,9 +5,9 @@ Applications can use annotated `@Controller` classes to handle messages from cli
Such classes can declare `@MessageMapping`, `@SubscribeMapping`, and `@ExceptionHandler`
methods, as described in the following topics:
* xref:web/websocket/stomp/handle-annotations.adoc#websocket-stomp-message-mapping[`@MessageMapping`]
* xref:web/websocket/stomp/handle-annotations.adoc#websocket-stomp-subscribe-mapping[`@SubscribeMapping`]
* xref:web/websocket/stomp/handle-annotations.adoc#websocket-stomp-exception-handler[`@MessageExceptionHandler`]
* <<websocket-stomp-message-mapping,`@MessageMapping`>>
* <<websocket-stomp-subscribe-mapping,`@SubscribeMapping`>>
* <<websocket-stomp-exception-handler,`@MessageExceptionHandler`>>
[[websocket-stomp-message-mapping]]
@@ -102,7 +102,7 @@ See xref:web/websocket/stomp/handle-send.adoc[Sending Messages].
`@SubscribeMapping` is similar to `@MessageMapping` but narrows the mapping to
subscription messages only. It supports the same
xref:web/websocket/stomp/handle-annotations.adoc#websocket-stomp-message-mapping[method arguments] as `@MessageMapping`. However
<<websocket-stomp-message-mapping,method arguments>> as `@MessageMapping`. However
for the return value, by default, a message is sent directly to the client (through
`clientOutboundChannel`, in response to the subscription) and not to the broker (through
`brokerChannel`, as a broadcast to matching subscriptions). Adding `@SendTo` or
@@ -173,7 +173,7 @@ The following example declares an exception through a method argument:
`@MessageExceptionHandler` methods support flexible method signatures and support
the same method argument types and return values as
xref:web/websocket/stomp/handle-annotations.adoc#websocket-stomp-message-mapping[`@MessageMapping`] methods.
<<websocket-stomp-message-mapping,`@MessageMapping`>> methods.
Typically, `@MessageExceptionHandler` methods apply within the `@Controller` class
(or class hierarchy) in which they are declared. If you want such methods to apply

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