Compare commits

...
622 Commits
Author SHA1 Message Date
Sam Brannen c116f3bb26 Merge branch '7.0.x' 2026-09-19 19:42:02 +02:00
Sam Brannen 48b59dfaec Polish contribution
See gh-37305
2026-09-19 19:39:48 +02:00
Sam Brannen f8cdd80a71 Merge branch '7.0.x' 2026-09-19 19:37:25 +02:00
Tran Ngoc Nhan b5454b1a69 Add closing braces to examples
Closes gh-37305

Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>
2026-09-19 19:36:24 +02:00
Sam Brannen 495f8b3f6c Polishing 2026-09-19 19:32:08 +02:00
Brian Clozel b6d7f1d586 Avoid duplicate date-related converters registration
Prior to this commit, `DefaultFormattingConversionService` would
register converters with both `DateTimeFormatterRegistrar` and
`DateFormatterRegistrar`, the former also registering the legace date
converters that the latter contributes.

While we cannot change the behavior for `DateTimeFormatterRegistrar` or
`DateFormatterRegistrar` because of their public contract, we can update
the `DefaultFormattingConversionService` to not use
`DateFormatterRegistrar` and register manually the annotation support
that it contributes.

Closes gh-36951
2026-09-19 12:12:10 +02:00
Juergen Hoeller b9fa062918 Merge branch '7.0.x' 2026-09-18 21:00:53 +02:00
Juergen Hoeller 83ecf67455 Match manifest-specified jar names with pre-encoded escape sequence
Closes gh-37280
2026-09-18 20:58:46 +02:00
Juergen Hoeller 2a15cd498a Invert findColumn fallback to try common underscore naming first
Closes gh-37297
2026-09-18 20:58:37 +02:00
Yanming Zhou 94e6e0ce68 Add batch update support to JdbcClient
Introduce a fluent batch() operation on JdbcClient.StatementSpec that
accumulates several sets of parameters - bound as positional or named
parameters in the same fashion as a single update, separated by add() -
and executes them as a single JDBC batch through update(). Batch updates
previously required dropping down to (NamedParameter)JdbcTemplate.

Closes gh-37216
Co-authored-by: Jiří Krokviak <j.krokviak@gmail.com>
Signed-off-by: Jiří Krokviak <j.krokviak@gmail.com>
Signed-off-by: Yanming Zhou <zhouyanming@gmail.com>
2026-09-18 20:27:24 +02:00
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 14927a959f Upgrade to Reactor 2026.0.0-M1
Closes gh-37107
2026-08-14 09:21:18 +02:00
Brian Clozel efbae85324 Upgrade to Micrometer 1.18.0-M1 and Tracing 1.8.0-M1
Closes gh-37108
2026-08-14 09:21:18 +02:00
rstoyanchev e12f0761f3 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:19:58 +02:00
rstoyanchev 30e3a5719e Leading slash handling in UrlHandlerFilter
Closes gh-37030
2026-08-14 09:19:58 +02:00
rstoyanchev d31f7a5a80 Apply ResourceHandlerUtils checks in XsltView
Closes gh-37029
2026-08-14 09:19:58 +02:00
rstoyanchev 1c77e241e6 Consistent maxPartSize check in PartEventHttpMessageReader
Closes gh-37028
2026-08-14 09:19:58 +02:00
rstoyanchev 9312e25e24 Check viewName for special prefixes in UrlFilenameViewController
Closes gh-37027
2026-08-14 09:19:58 +02:00
rstoyanchev d5dbc9b310 Ensure Payload release on early error in createHeaders
Closes gh-37026
2026-08-14 09:19:58 +02:00
rstoyanchev a69fe71630 Return sameSite cookie value in Jetty response
Closes gh-37025
2026-08-14 09:19:58 +02:00
rstoyanchev d6f5356db1 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:19:58 +02:00
rstoyanchev b4d9b514f5 Update exception messages in HandshakeWebSocketService
Closes gh-37023
2026-08-14 09:19:58 +02:00
Sam Brannen ea3e61fd2f 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:19:58 +02:00
Sam Brannen 8a92c19e4d 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:19:58 +02:00
Sam Brannen ee1874ac52 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:19:58 +02:00
Sébastien Deleuze 8cb1151375 Ensure consistent EscapedErrors field error escaping
Closes gh-37055
2026-08-14 09:19:58 +02:00
Sébastien Deleuze bb65d819a4 Reject backslashes in SpringTemplateLoader template names
Closes gh-37054
2026-08-14 09:19:58 +02:00
Brian Clozel 5abe6d3e5f 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:19:58 +02:00
Brian Clozel 999f428987 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:19:58 +02:00
Brian Clozel fd50270b8d 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:19:58 +02:00
Brian Clozel b10179ffdf Switch to INTERNAL-SNAPSHOTs 2026-08-14 09:19:58 +02:00
Brian Clozel f8974ad620 Prepare main-internal branch 2026-08-14 09:19:58 +02:00
Sam Brannen c7712052ce Merge branch '7.0.x' 2026-08-13 19:01:47 +02:00
Sam Brannen e78d3df566 Revert "Update MockCookie#parse(String) validation to align with Javadoc"
This reverts commit 8b894933ae due to
code freeze on main.

See gh-37134
2026-08-13 17:15:34 +02:00
Sam Brannen 951c1f306a Revert "Polish MockCookieTests"
This reverts commit 27a85be4e0 due to
code freeze on main.

See gh-37134
2026-08-13 17:15:10 +02:00
Sam Brannen 27a85be4e0 Polish MockCookieTests
See gh-37134
2026-08-13 12:41:41 +02:00
Tran Ngoc Nhan 8b894933ae Update MockCookie#parse(String) validation to align with Javadoc
Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>
2026-08-13 12:38:02 +02:00
Juergen Hoeller 68e6acd37e Merge branch '7.0.x' 2026-08-12 00:09:51 +02:00
Juergen Hoeller 88b383a153 Upgrade to Jackson 3.1.5 and 2.21.5 2026-08-11 23:55:26 +02:00
Juergen Hoeller 00b9063e7d Polishing 2026-08-11 23:48:56 +02:00
Juergen Hoeller cc751e61a2 Merge branch '7.0.x'
# Conflicts:
#	framework-platform/framework-platform.gradle
2026-08-11 23:23:40 +02:00
Sam Brannen 69bf83ad71 Merge branch '7.0.x' 2026-08-09 17:47:04 +03:00
Sam Brannen da4b31c82b Merge branch '7.0.x' 2026-08-07 10:54:27 +03:00
rstoyanchev b6cb9a5f87 Merge branch '7.0.x' 2026-08-07 10:32:13 +03:00
Juergen Hoeller 7f01cd0a5b Introduce enforceReadOnly flag for JTA 2.1 read-only mode
Closes gh-35915
2026-08-06 20:18:13 +02:00
Brian Clozel 51c4539bb6 Stop using deprecated HttpMessageConverterExtractor in StatusHandler
Prior to this commit, `HttpMessageConverterExtractor` was deprecated
with `RestTemplate` and related types. `StatusHandler` was still using
it and causing a deprecation warning.

This commit extracts the relevant implementation from
`DefaultRestClient` and promotes it as a shared static method in
`RestClientUtils`.

Fixes gh-37010
2026-08-05 17:54:02 +02:00
samlightfoot 595c246cce Skip logging operators in DefaultExchangeFunction when possible
Prior to this commit, `DefaultExchangeFunction.exchange` added
logging operations within `doOnRequest`/`doOnCancel` operators
unconditionally, which costs two subscriber wrappers per request
even though the log message construction itself is already
guarded lazily. This commit gates the operators on `isDebugEnabled()`,
checked per exchange so runtime log level changes are still honored.

Signed-off-by: samlightfoot <samueldlightfoot@gmail.com>
2026-08-05 16:56:13 +02:00
Brian Clozel 4b5c92703c Merge branch '7.0.x' 2026-08-05 10:44:06 +02:00
Brian Clozel c17b4ad787 Merge branch '7.0.x' 2026-08-05 10:09:11 +02:00
Brian Clozel e8729d0438 Switch to SNAPSHOT dependencies
See gh-37107
See gh-37108
2026-08-03 15:17:34 +02:00
Brian Clozel d2d7fd36da Merge branch '7.0.x' 2026-08-03 11:36:43 +02:00
Sam Brannen 0abf59feee Merge branch '7.0.x' 2026-08-03 12:35:10 +03:00
Sam Brannen 11da74d51b Merge branch '7.0.x' 2026-08-03 11:35:45 +03:00
Brian Clozel eceebb3077 Merge branch '7.0.x' 2026-07-31 18:52:03 +02:00
Brian Clozel f3e202e1b5 Merge branch '7.0.x' 2026-07-31 18:26:09 +02:00
Brian Clozel 17002a26cc Merge branch '7.0.x'
# Conflicts:
#	.github/workflows/build-and-deploy-snapshot.yml
#	.github/workflows/release-milestone.yml
#	.github/workflows/release.yml
2026-07-31 18:15:39 +02:00
Juergen Hoeller 0376dd9a78 Merge branch '7.0.x' 2026-07-31 16:28:17 +02:00
Sam Brannen 7c2fdcc1fb Merge branch '7.0.x' 2026-07-30 16:25:12 +03:00
Sam Brannen abe33703b4 Update Javadoc for PropertyDescriptorUtils.determineBasicProperties()
See gh-37081
2026-07-30 15:16:19 +03:00
Arnab Nandy badddeb0dc Ignore static get/is accessor methods in PropertyDescriptorUtils
Prior to this commit, PropertyDescriptorUtils.determineBasicProperties()
incorrectly recognized static `get` and `is` accessor methods as
JavaBean read methods, in contrast to the standard
java.beans.Introspector, which has always excluded static methods from
property discovery. This regression was introduced in Spring Framework
6.0 when determineBasicProperties() replaced the delegation to
java.beans.Introspector for the fast property-discovery path used by
SimpleBeanInfoFactory. As a result, an unrelated static method such as
a singleton accessor could be exposed as a bean property, and
reflective access to such a property (for example, via BeanWrapperImpl)
could lead to a StackOverflowError if the property's value recursively
exposed the same static accessor.

To address that, this commit adds Modifier.isStatic(...) checks to the
`get` and `is` branches in determineBasicProperties(), mirroring the
equivalent check already present in
CachedIntrospectionResults.isPlainAccessor(). Static `set` methods
continue to be supported as write methods, consistent with the existing
behavior in ExtendedBeanInfo.

See gh-37068
Closes gh-37081

Signed-off-by: Arnab Nandy <arnab_nandy7@yahoo.com>
2026-07-30 14:12:08 +02:00
rstoyanchev 50f923ace4 Restore MatchableHandlerMapping
See gh-36481
2026-07-30 12:07:17 +03:00
Sam Brannen 317eae88d0 Merge branch '7.0.x' 2026-07-29 22:07:54 +03:00
Sam Brannen 2aaeef7190 Merge branch '7.0.x' 2026-07-29 21:38:13 +03:00
rstoyanchev 8bc5e11ec3 Remove HandlerMappingIntrospector
Closes gh-36481
2026-07-29 18:03:09 +03:00
rstoyanchev f53674d582 Replace HandlerMappingIntrospector with DefaultPreFlightRequestHandler
See gh-36481
2026-07-29 18:03:08 +03:00
rstoyanchev 67d54dccd2 Polishing contribution
See gh-36816
2026-07-29 16:17:54 +03:00
jhan0121 4bcae5305d Deprecate setDisallowedFields in DataBinder
Closes gh-36816
Signed-off-by: Juhwan Lee <jhan0121@gmail.com>
2026-07-29 16:17:09 +03:00
rstoyanchev 3f632382d6 Add WebClientResponseException.PreconditionFailed
See gh-36807
2026-07-29 15:55:04 +03:00
rstoyanchev cde75754bc Polishing in HttpClientErrorException
See gh-36807
2026-07-29 15:50:39 +03:00
Dominik Kovács dddd237449 Add HttpClientErrorException.PreconditionFailed
Closes gh-36807

Signed-off-by: Dominik Kovács <dominik.kovacs28@gmail.com>
2026-07-29 15:50:38 +03:00
rstoyanchev 56f7cc2dab Merge branch '7.0.x' 2026-07-29 15:18:45 +03:00
Juergen Hoeller 91c6851f29 Merge branch '7.0.x'
# Conflicts:
#	framework-platform/framework-platform.gradle
2026-07-29 12:17:09 +02:00
Juergen Hoeller 16d9965fe3 Consistently enforce non-null instance in AbstractFactoryBean
Closes gh-37091
2026-07-27 19:47:12 +02:00
rstoyanchev 1d1aac3674 Merge branch '7.0.x' 2026-07-27 12:58:34 +03:00
Sam Brannen d5acf5bceb Merge branch '7.0.x' 2026-07-26 10:59:55 +03:00
Sam Brannen 4c192bf58f Merge branch '7.0.x' 2026-07-26 10:21:32 +03:00
Sam Brannen cb6226c98a Merge branch '7.0.x' 2026-07-25 11:06:31 +03:00
rstoyanchev ffcf37468f Update documentation on forwarded headers
See gh-37072
2026-07-24 22:31:52 +03:00
Brian Clozel e24f5f2ca7 Merge branch '7.0.x' 2026-07-24 15:15:52 +02:00
Brian Clozel 079992021c Improve MimeType parser for RFC compliance
Prior to this commit, the `MimeType` class would compare raw parameter
values for the equals/hashcode contract. This went against the RFC which
states that quoted and unquoted parameter values are equivalent.

This commit rewrote the entire `MimeType` parser in `MimeTypeUtils`
as a state parser to improve robustness and performance.
The `MimeType` equals, compareTo and hascode contracts now unquote
parameter values before comparing them.

This change also optimizes the `tokenize` function that splits many
comma-separated MIME types into a list. Now that this method isn't used
anywhere else, it is also deprecated as of 7.1. This method was
initially made public to be reused within Spring Framework and has no
particular use in Spring applications in general.

Finally, this also makes `MediaType` and `MimeType` leverage the
`MimeType` LRU cache as much as possible, including when parsing
`Accept:` HTTP headers.

Closes gh-36729
2026-07-24 14:29:36 +02:00
rstoyanchev 9bdeadcfbd Deprecate historic forwarded header behavior
See gh-37072
2026-07-23 12:15:24 +03:00
rstoyanchev 31c37d4f2c Require choice between Forwarded and X-Forwarded headers
This commit introduces a constructor argument to select whether
to use the standard "Forwarded" header or the "X-Forwarded-*"
alternative headers. A separate property enables support for
X-Forwarded-Prefix.

Closes gh-37072
2026-07-23 12:15:24 +03:00
rstoyanchev 68862530fb Parse the standard "Forwarded" header directly
This commit introduces manual parsing of the standard "Forwarded"
header instead of using regular expressions.

Closes gh-36964
2026-07-23 12:15:24 +03:00
rstoyanchev 7b71df72e5 Add ForwardedInfo to ForwardedHeaderUtils
This commit adds two new methods in ForwardedHeaderUtils, one to parse
the standard "Forwarded" header only, and another to parse the
"X-Forwarded-*" alternative headers. As those are single parse methods,
a ForwardedInfo container type is necessary to return the results.

See gh-36964
2026-07-23 12:15:24 +03:00
Brian Clozel 224522244f Merge branch '7.0.x' 2026-07-22 10:54:58 +02:00
Tran Ngoc Nhan 1502ab0b20 Correct HandlerMethodValidationExceptionTests package
Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>
2026-07-21 15:54:53 +03:00
Sam Brannen 304f8eb27e Merge branch '7.0.x' 2026-07-21 12:00:08 +03:00
Sébastien Deleuze 3dfb4ef754 Upgrade Kotlin Coroutines to 1.11.0
Closes gh-37076
2026-07-20 15:30:59 +02:00
Sébastien Deleuze 08a4844288 Upgrade Kotlin to 2.4.10
Closes gh-37074
2026-07-20 15:30:58 +02:00
Sam Brannen 38e1bf5970 Merge branch '7.0.x' 2026-07-20 11:07:00 +03:00
Brian Clozel 5ac20a8104 Reinstate invalid resource location checks
This checks was removed previously because the location was considered
as invalid in #36695, but they were later reinstated in #36692.

This commit also reinstates the check that prevents static resource
resolution in those locations.

Closes gh-37063
2026-07-17 13:41:38 +02:00
Brian Clozel 12d71c9a9b Merge branch '7.0.x' 2026-07-16 19:21:35 +02:00
Brian Clozel 0791d9a6e3 Merge branch '7.0.x' 2026-07-16 19:10:36 +02:00
Sébastien Deleuze 734c7ed4a7 Merge branch '7.0.x' 2026-07-16 17:32:26 +02:00
Juergen Hoeller 40f7d56ed4 Register original bean name as alias if not taken already
Closes gh-37038
2026-07-16 10:45:16 +02:00
Juergen Hoeller c4c0a84f83 Merge branch '7.0.x' 2026-07-16 10:20:53 +02:00
Sam Brannen 99b991b6f3 Upgrade to JUnit 6.1.2
Closes gh-36815
2026-07-13 10:16:37 +02:00
Brian Clozel 9024d5ffbd Merge branch '7.0.x' 2026-07-12 11:55:38 +02:00
Sébastien Deleuze 6dd2aa3988 Merge branch '7.0.x' 2026-07-08 20:46:34 +02:00
Sébastien Deleuze 28bf619887 Merge branch '7.0.x' 2026-07-08 17:23:42 +02:00
Sam Brannen 1700fad16d Update due to deprecation warnings 2026-07-06 15:01:43 +02:00
Sam Brannen d71643c347 Remove unused code 2026-07-06 14:55:47 +02:00
Juergen Hoeller 257687d44b Upgrade to Aalto 1.4, Gson 2.14, Woodstox 7.2.1 2026-07-06 12:52:52 +02:00
Juergen Hoeller 9a82d107c0 Merge branch '7.0.x'
# Conflicts:
#	framework-platform/framework-platform.gradle
2026-07-06 12:32:43 +02:00
Sam Brannen 45b4d00d9f Merge branch '7.0.x' 2026-07-06 11:03:12 +02:00
Sam Brannen 4bcb6cc081 Merge branch '7.0.x' 2026-07-02 12:08:26 +02:00
Sébastien Deleuze df0ec74107 Merge branch '7.0.x' 2026-07-01 15:41:32 +02:00
Sam Brannen c5d7908a78 Find transitive interface annotations in findAllLocalMergedAnnotations()
This commit picks up where ce718cf699 left off by ensuring that
findAllLocalMergedAnnotations() also finds annotations declared on
transitive interfaces — that is, on interfaces of the directly
implemented interfaces of the root declaring class.

The previous implementation filtered annotations from a single
TYPE_HIERARCHY search by checking whether the annotation's source was
either the root declaring class or one of its directly declared
interfaces. However, this excluded annotations inherited through an
interface chain such as First -> Second -> Third, where the annotation
is declared on Third but not on Second.

This commit replaces that approach with two targeted searches whose
results are combined:

- DIRECT on the root declaring class, to capture annotations declared
  directly on the class (including via composed/meta-annotations)
- TYPE_HIERARCHY on each directly implemented interface, which
  naturally traverses the full super-interface chain of each interface

A corresponding test for this scenario has also been added.

Closes gh-36975
2026-06-30 16:44:53 +02:00
Sam Brannen ce718cf699 Always find interface annotations in findAllLocalMergedAnnotations()
Prior to this commit, AnnotationDescriptor's
findAllLocalMergedAnnotations() filtered results using
MergedAnnotationPredicates.firstRunOf(
MergedAnnotation::getAggregateIndex), which retains only annotations
that share the same aggregate index as the first annotation
encountered. Since annotations on the root declaring class have
aggregate index 0 and annotations on interfaces have higher indices,
interface annotations were silently excluded whenever the root
declaring class itself declared the annotation.

This commit fixes the issue by replacing the aggregate-index-based
filter with a source-based filter that explicitly includes annotations
from the root declaring class and from each directly implemented
interface. The Javadoc for findAllLocalMergedAnnotations() has also
been updated to document the ordering guarantee: annotations from the
root declaring class appear first, followed by annotations from
implemented interfaces in declaration order.

Closes gh-36975
2026-06-28 14:35:18 +02:00
Sam Brannen efaa53b488 Upgrade to JUnit 6.1.1
Closes gh-36815
2026-06-28 13:55:12 +02:00
Sam Brannen bb34bf6dc6 Merge branch '7.0.x' 2026-06-27 18:09:31 +02:00
Sam Brannen 8f539b6e23 Merge branch '7.0.x' 2026-06-27 17:14:07 +02:00
Sam Brannen 62eea96151 Merge branch '7.0.x' 2026-06-27 16:19:38 +02:00
Sam Brannen 3cf19aabcd Merge branch '7.0.x' 2026-06-27 15:20:15 +02:00
Juergen Hoeller fc0ad3d367 Merge branch '7.0.x' 2026-06-26 18:21:51 +02:00
Sam Brannen 69839889c4 Merge branch '7.0.x' 2026-06-26 17:41:35 +02:00
rstoyanchev 2b7ec43571 Merge branch '7.0.x' 2026-06-25 16:10:55 +01:00
Sam Brannen 787f9e1cbb Merge branch '7.0.x' 2026-06-25 13:57:56 +02:00
rstoyanchev ed0919b92e Merge branch '7.0.x' 2026-06-25 12:50:49 +01:00
Sam Brannen 7e9da44e18 Merge branch '7.0.x' 2026-06-25 13:33:10 +02:00
Sam Brannen a077324670 Polish contribution
See gh-36956
2026-06-23 12:19:28 +02:00
Yanming Zhou c35e17b038 Refactor NumberToDataSizeConverter to use DataSize.ofBytes(long) directly
Prior to this commit, a `Number` was converted to a `String` and then
the string was parsed/matched using regular expressions back into a
suitable `long` which was inefficient and also prevented valid data
size values such as 10.0.

To address those issues, this commit refactors
NumberToDataSizeConverter to use DataSize.ofBytes(long) directly, first
checking that the supplied Number does not have a fractional part.

Closes gh-36956

Signed-off-by: Yanming Zhou <zhouyanming@gmail.com>
2026-06-23 12:19:27 +02:00
Juergen Hoeller 0cdb0cfae4 Upgrade to Jackson 3.1.4 / 2.21.4, Hibernate ORM 7.4.2, Hibernate Validator 9.1.1 2026-06-23 12:07:35 +02:00
Juergen Hoeller bd405756ab Avoid "NullAway.Init" suppression in favor of explicit field handling
Closes gh-36961
2026-06-23 11:55:58 +02:00
Juergen Hoeller 9130ded96f Resolve against type variable from same declaration if possible
Closes gh-36890
2026-06-22 22:09:52 +02:00
Juergen Hoeller 0dc2d03093 Merge branch '7.0.x'
# Conflicts:
#	spring-context/src/main/java/org/springframework/validation/DataBinder.java
2026-06-22 21:55:43 +02:00
rstoyanchev 8a2e4a9e0a Merge branch '7.0.x' 2026-06-22 14:08:46 +01:00
rstoyanchev 0fbe714bd1 Merge branch '7.0.x' 2026-06-22 13:50:10 +01:00
Sam Brannen 9c64be98e4 Merge branch '7.0.x' 2026-06-19 16:32:04 +02:00
rstoyanchev 8e6b6c5ac3 Refine error handling in MultipartParser
Closes gh-36947
2026-06-17 15:54:48 +01:00
Sam Brannen 7b31e0c2dc Polish contribution
See gh-36938
2026-06-17 15:49:10 +02:00
junhyeong9812 233e7b91f9 Throw ClassNotFoundException for missing class resource in ThrowawayClassLoader
Prior to this commit, ThrowawayClassLoader.loadClass fell back to
loadClassFromResource(), which returns null when no class resource is
available. Returning null from loadClass violates the ClassLoader
contract and leads to a NullPointerException in callers such as
PreComputeFieldFeature.

To address that, this commit rethrows the original
ClassNotFoundException when the resource fallback yields no class.

Closes gh-36938

Signed-off-by: junhyeong9812 <pickjog@gmail.com>
2026-06-17 15:47:27 +02:00
Sam Brannen 077bfaf095 Polish contribution
See gh-36830
2026-06-17 14:15:04 +02:00
YeongJae Min 175f551d91 Add DataSize converters to DefaultConversionService
Spring Boot already provides equivalent converters, and DataSize itself
already exposes parsing support via DataSize.parse(...).

This commit makes that conversion available through Spring Framework's
default conversion service.

- new StringToDataSizeConverter
- new NumberToDataSizeConverter
- both converters are registered with the DefaultConversionService
- new tests for string, number, empty, and invalid inputs

This intentionally does not move Spring Boot's @⁠DataSizeUnit support
into Spring Framework.

See gh-28910
Closes gh-36830

Signed-off-by: YeongJae Min <whereismysejong@naver.com>
2026-06-17 14:15:04 +02:00
rstoyanchev 2bc0ee7ec1 Polishing in PartGenerator 2026-06-17 13:05:10 +01:00
rstoyanchev 65fe0f1d2f PartGenerator disposes of resources in current State
Closes gh-36942
2026-06-17 13:05:10 +01:00
rstoyanchev d5dee4ef1c Close OutputStream after part created in PartGenerator
Closes gh-36945
2026-06-17 13:05:10 +01:00
rstoyanchev 97db213d41 Minor refactoring in MultipartParser
Move the nested InternalParser class up, merging it with the top-level
MultipartParser, make the constructor private, and expose a static
parse method.
2026-06-17 13:05:10 +01:00
leestana01 7565c51dc5 Restore thread interrupt flag in DefaultMvcResult
awaitAsyncDispatch() catches InterruptedException, returns false, and
discards the interruption. Catching InterruptedException without
rethrowing should restore the interrupt status (as is done across the
framework's main sources), so re-assert it before returning false.

Closes gh-36876

Signed-off-by: leestana01 <leestana01@naver.com>
2026-06-17 13:32:36 +02:00
junhyeong9812 03d80feed0 Close class resource InputStream in ThrowawayClassLoader
Prior to this commit, ThrowawayClassLoader#loadClassFromResource opened
an InputStream via getResourceAsStream(...) but never closed it. The
stream leaked on both the success path (after defineClass) and the
IOException path, as the surrounding try-block had neither a finally
nor a try-with-resources clause.

This commit adapts the existing inputStream variable as a
try-with-resources resource so that it is closed on every path, leaving
the loading logic unchanged.

Closes gh-36933

Signed-off-by: junhyeong9812 <pickjog@gmail.com>
2026-06-17 13:12:23 +02:00
Sam Brannen b611fcf114 Use double division to calculate applied jitter in ExponentialBackOff
In order to avoid the staircase scaling effect that results from our
current use of integer division, this commit revises applyJitter(long)
in ExponentialBackOffExecution to use floating-point (double) division
to calculate the applied jitter.

Closes gh-36943
2026-06-17 13:02:49 +02:00
Sam Brannen 472e610c4a Merge branch '7.0.x' 2026-06-17 12:43:48 +02:00
Sam Brannen 2723847917 Merge branch '7.0.x' 2026-06-17 11:59:46 +02:00
Sam Brannen 94db4f7f7a Merge branch '7.0.x' 2026-06-17 11:56:51 +02:00
rstoyanchev 8cfe90c4c0 Polishing in MultipartHttpMessageConverter 2026-06-17 10:14:01 +01:00
Sam Brannen 30287d789c Merge branch '7.0.x' 2026-06-15 15:44:04 +02:00
Sam Brannen c26029c26b Use "instanceof pattern matching" in WebSocketExtension.equals() 2026-06-15 15:25:59 +02:00
Sam Brannen 0bfe82b315 Polishing 2026-06-15 15:25:06 +02:00
Yanming Zhou cdc3c52640 Replace isAssignableFrom() with isInstance() where feasible
Closes gh-36899

Signed-off-by: Yanming Zhou <zhouyanming@gmail.com>
2026-06-15 15:16:18 +02:00
Juergen Hoeller 0c60266986 Merge branch '7.0.x' 2026-06-09 22:42:03 +02:00
Brian Clozel 99f6f1a77a Merge branch '7.0.x' 2026-06-08 22:19:46 +02:00
Brian Clozel 1bd3ad2050 Merge branch '7.0.x' 2026-06-08 19:51:16 +02:00
Sam Brannen 2b08e6e1d3 Merge branch '7.0.x' 2026-06-08 18:29:20 +02:00
Brian Clozel 4e39c28b92 Merge branch '7.0.x' 2026-06-08 14:49:45 +02:00
Brian Clozel 381fa10477 Merge branch '7.0.x' 2026-06-08 10:41:12 +02:00
rstoyanchev 14f4deef3a Merge branch '7.0.x' 2026-06-05 14:36:23 +01:00
Sam Brannen e23971efb9 Merge branch '7.0.x' 2026-06-05 15:15:19 +02:00
Sam Brannen 2469aae672 Merge branch '7.0.x' 2026-06-05 14:57:29 +02:00
cookie-meringue 83e29382b3 Optimize ClassNameReader.getClassName via direct ASM API
getClassName now calls ClassReader.getClassName() directly instead
of routing through the visitor-based getClassInfo. Previously, it
allocated a List and a ClassVisitor and decoded super_class and
every interface name only to discard all but the first element.

The method is on the hot path of every CGLIB proxy class definition,
so this change significantly lowers its per-call processing cost.

Closes gh-36814

Signed-off-by: cookie-meringue <daehyeon3351@gmail.com>
2026-06-04 14:20:49 +02:00
Brian Clozel ee5c82a2f8 Merge branch '7.0.x' 2026-06-04 12:27:18 +02:00
rstoyanchev ae7891e797 Revise disconnected client error handling in WebFlux
A disconnected client error does not necessarily prevent us from setting
the status of the WebFlux ServerHttpResponse, which is only gated by a
committed flag and does not necessarily reflect the connection state.

This is why we need to check if we have a disconnected client error
first and handle it accordingly. We still set the response to 500
in case the disconnect client error is to a remote host in which
case it will propagate to the client.

Closes gh-36811
2026-06-04 11:10:58 +01:00
rstoyanchev 98ed1dfcf8 Revise disconnected client error handling in Spring MVC
DisconnectedClientHelper identifies lost connection issues, but it's
not always easy to know if it is the connection to the client or to
another remote host. DisconnectedClientHelper does recognize and
filter out common client exceptions, but there is a possibility for
other similar custom exceptions.

DefaultHandlerExceptionResolver now attempts to set the status to
500, which won't impact a client that has gone away, but it will
set the status correct on the off chance that the exception is
actually a server side issue.

Closes gh-34481
2026-06-04 11:10:58 +01:00
Brian Clozel 9b8a851969 Merge branch '7.0.x' 2026-06-04 10:49:36 +02:00
seungchan 4c6194a2ad Simplify BUFFER_COUNT in ConcurrentLruCache to a constant
The detectNumberOfBuffers() method attempted to scale the read
buffer count based on the number of available processors, but used
Math.min(4, nextPowerOfTwo) which effectively caps the result at 4
regardless of CPU count. For systems with fewer than 3 processors,
the buffer count would be reduced below 4, but this edge case adds
complexity without measurable benefit.

Simplify BUFFER_COUNT to a constant value of 4, removing the
unnecessary CPU-detection logic.

Closes gh-36872

Signed-off-by: seungchan <s24041@gsm.hs.kr>
2026-06-04 09:30:36 +02:00
Juergen Hoeller 2fc99eb12f Merge branch '7.0.x' 2026-06-03 23:04:51 +02:00
Brian Clozel 783388e9f8 Merge branch '7.0.x' 2026-06-03 11:23:03 +02:00
Juergen Hoeller de4de02a1d Merge branch '7.0.x'
# Conflicts:
#	framework-platform/framework-platform.gradle
#	spring-context/src/main/java/org/springframework/validation/DataBinder.java
2026-06-02 17:33:07 +02:00
Juergen Hoeller e6ce2a3c36 Expose autoGrowCollectionLimit in ConfigurablePropertyAccessor interface
See gh-36862
2026-06-02 17:20:52 +02:00
Matthias Kurz 481a5743b3 Apply auto-grow limit to direct field binding
DataBinder applies its auto-grow collection limit to bean
property access, but direct field access left DirectFieldAccessor
at its default limit.

Pass DataBinder's configured limit into DirectFieldBindingResult
and apply it to the DirectFieldAccessor.

Closes gh-36861

Signed-off-by: Matthias Kurz <m.kurz@irregular.at>
2026-06-02 16:58:47 +02:00
wushiyuanmaimob 85a8868bae Preserve generic type info in awaitEntity()
awaitEntity() used T::class.java which erases generic type
information (e.g. List<Foo> becomes just List). Use the
reified toEntity<T>() extension instead, which preserves
full generic type via ParameterizedTypeReference.

Closes gh-36834
Signed-off-by: wushiyuanmaimob <wushiyuanwork@outlook.com>
2026-06-02 12:26:41 +02:00
rstoyanchev dd72b9eb4b Merge branch '7.0.x' 2026-06-01 10:53:55 +01:00
Juergen Hoeller 2e65c1dfe6 Merge branch '7.0.x' 2026-05-29 13:31:01 +02:00
Sam Brannen ca72cd66e3 Merge branch '7.0.x' 2026-05-28 10:59:38 +02:00
Juergen Hoeller a3f3ba5685 Merge branch '7.0.x' 2026-05-27 18:59:32 +02:00
rstoyanchev 79f4f76bd2 Merge branch '7.0.x' 2026-05-27 17:19:22 +01:00
Juergen Hoeller 744d136cf7 Upgrade to Hibernate ORM 7.4
Closes gh-36519
2026-05-27 16:44:26 +02:00
Juergen Hoeller 00ca23859e Merge branch '7.0.x'
# Conflicts:
#	framework-platform/framework-platform.gradle
2026-05-27 16:35:51 +02:00
Sam Brannen facc7c5371 Merge branch '7.0.x' 2026-05-27 12:16:46 +02:00
Sam Brannen bd1e7e16a9 Merge branch '7.0.x' 2026-05-27 12:05:04 +02:00
Brian Clozel 25e8395df8 Reject duplicate MIME type parameters
Prior to this commit, MIME type parsing in Spring would allow duplicate
parameters like "text/plain; dupe=1; dupe=2", effectively retaining the
latest value and ignoring the first.

RFC 6838 4.3 states that this should be treated as an error and this
commit ensures that this is the case.

Closes gh-36841
2026-05-26 17:53:28 +02:00
Sam Brannen 148b7fd8f3 Merge branch '7.0.x' 2026-05-26 16:40:06 +02:00
Brian Clozel 68338aa818 Use ASCII chars in Content-Disposition filename parameter
Prior to this commit, gh-36328 avoided using RFC 2047 encoding for the
"filename" parameter and use ISO-8859-1 only. This change unfortunately
caused issues because some implementations might try and detect the
encoding automatically.

This commit restricts the filename parameter to ASCII encoding only by:
* transliterating characters to the closes ASCII character
("é"->"e", "ä"->"ae"...)
* falling back to "_" for other chacacters with non latin alphabet or
  emojis

Fixes gh-36805
2026-05-22 21:33:40 +02:00
Sam Brannen fbbc0f487c Use Constants API introduced in JUnit 6.1
See gh-36815
2026-05-21 12:48:37 +02:00
Sam Brannen 611c390417 Use EngineTestKit in ParallelExecutionSpringExtensionTests 2026-05-21 12:27:33 +02:00
Sam Brannen cf3196e8de Merge branch '7.0.x' 2026-05-20 17:49:42 +02:00
Sam Brannen 80e9e4f6f2 Make ParallelExecutionSpringExtensionTests more robust
... due to changes in JUnit 6.1.0.

See gh-36815
2026-05-20 17:32:01 +02:00
Sam Brannen 91655be0db Merge branch '7.0.x' 2026-05-20 16:53:56 +02:00
Sam Brannen 3b030e0431 Upgrade to JUnit 6.1
Closes gh-36815
2026-05-20 16:34:26 +02:00
Sam Brannen 9870ce1844 Only update ObservationThreadLocalAccessor when test has an active ApplicationContext
Prior to this commit,
MicrometerObservationRegistryTestExecutionListener always attempted to
load the test's ApplicationContext in order to update the
ObservationThreadLocalAccessor in its beforeTestMethod() callback, even
if there was no active ApplicationContext.

To avoid unnecessarily loading an ApplicationContext or attempting to
load an ApplicationContext that cannot be loaded (for example, due to a
context-load failure), this commit applies a hasApplicationContext()
check in beforeTestMethod().

Since the MicrometerObservationRegistryTestExecutionListener is
registered after the DependencyInjectionTestExecutionListener (at least
by default), an active ApplicationContext should be present unless
dependency injection from the context failed or the context failed to
load.

Closes gh-36817
2026-05-20 14:52:20 +02:00
Sam Brannen d87c03a6be Reset mocks only when a test has an ApplicationContext
Prior to this commit and the previous commit,
MockitoResetTestExecutionListener always attempted to load the
ApplicationContext to reset mocks in its beforeTestMethod() and
afterTestMethod() callbacks, even if there was no active
ApplicationContext.

The reason this was noticed is that the @BeforeMethod(alwaysRun = true)
and @AfterMethod(alwaysRun = true) lifecycle methods in
AbstractTestNGSpringContextTests are always invoked, even if a previous
lifecycle configuration method failed (for example, due to a
context-load failure).

However, with JUnit Jupiter and the SpringExtension the
beforeTestMethod() and afterTestMethod() callbacks in the
TestExecutionListener API are not invoked if there was a previous
lifecycle failure.

Consequently, the reported drawbacks only exist when using Spring's
TestNG base support classes

This commit picks up where the previous commit left off by applying the
same hasApplicationContext() check in beforeTestMethod().

This commit also introduces unit and integration tests for both Jupiter
and TestNG support.

Closes gh-36782
2026-05-20 14:04:09 +02:00
seregamorph 1d91982f83 Reset mocks after test only when the test has an ApplicationContext
See gh-36782

Signed-off-by: seregamorph <serega.morph@gmail.com>
2026-05-20 14:03:55 +02:00
Sam Brannen 0c25d817bd Merge branch '7.0.x' 2026-05-17 15:33:24 +02:00
Sam Brannen 17ed3e8370 Merge branch '7.0.x' 2026-05-17 13:56:47 +02:00
rstoyanchev 2f458f9093 Merge branch '7.0.x' 2026-05-15 13:51:31 +01:00
rstoyanchev 16e0ed0463 Merge branch '7.0.x' 2026-05-14 16:56:21 +01:00
rstoyanchev e24448118c Merge branch '7.0.x' 2026-05-14 16:42:29 +01:00
Juergen Hoeller a0ec6656b4 Merge branch '7.0.x' 2026-05-13 19:46:39 +02:00
Juergen Hoeller 5ab4c5c5d9 Add support for JPA 4.0 @PersistenceAgent injection
Closes gh-36264
2026-05-13 19:41:21 +02:00
Juergen Hoeller c3baa01535 Merge branch '7.0.x' 2026-05-12 16:21:43 +02:00
Juergen Hoeller c0b749479a Merge branch '7.0.x' 2026-05-12 13:10:34 +02:00
Juergen Hoeller 5bf58cfe05 Merge branch '7.0.x'
# Conflicts:
#	framework-platform/framework-platform.gradle
2026-05-11 13:09:38 +02:00
Vinod Kumar 238a24cb6d Polish collection usage in HttpHeadersTests
Signed-off-by: Vinod Kumar <codingkiddo@gmail.com>
2026-05-11 09:34:28 +02:00
Sam Brannen 9a54f75d6d Merge branch '7.0.x' 2026-05-09 16:40:53 +02:00
Juergen Hoeller 9db16e4c15 Merge branch '7.0.x'
# Conflicts:
#	framework-platform/framework-platform.gradle
2026-05-08 16:02:00 +02:00
Sam Brannen ec8f0ca6b2 Merge branch '7.0.x' 2026-05-06 13:54:03 +02:00
Sam Brannen 1a0bd12c34 Merge branch '7.0.x' 2026-05-06 13:26:10 +02:00
Brian Clozel e6590aa1a8 Merge branch '7.0.x'
Closes gh-36753
2026-05-05 11:59:37 +02:00
Sam Brannen 051b09694e Merge branch '7.0.x' 2026-05-04 17:21:22 +02:00
Sam Brannen 39ff8e46ab Use String#replace instead of String#replaceAll in tests
See gh-36678
2026-05-03 14:36:23 +02:00
shenjianeng 27bdf24482 Use String#replace instead of String#replaceAll where appropriate
Avoid using String#replaceAll when the pattern is not a regular
expression.

Using java.lang.String#replace(CharSequence, CharSequence) will
improve performance.

Closes gh-36678

Signed-off-by: shenjianeng <ishenjianeng@qq.com>
2026-05-03 14:34:01 +02:00
Sam Brannen d9ecf945cc Merge branch '7.0.x' 2026-05-02 18:39:59 +02:00
Sam Brannen b35da5b140 Merge branch '7.0.x' 2026-05-02 18:35:24 +02:00
rstoyanchev 367a62018d Merge branch '7.0.x' 2026-05-01 21:48:29 +01:00
Juergen Hoeller 1b26f5d1e6 Adapt bean overriding test for deferred BeanRegistrar processing in 7.1
See gh-36648
See gh-21497
2026-04-30 14:33:55 +02:00
Juergen Hoeller 6ff2d187cf Merge branch '7.0.x'
# Conflicts:
#	spring-context/src/test/java/org/springframework/context/support/GenericApplicationContextTests.java
2026-04-30 14:21:49 +02:00
Juergen Hoeller 72cf389754 Merge branch '7.0.x' 2026-04-29 21:53:07 +02:00
rstoyanchev 3184eb3acc Merge branch '7.0.x' 2026-04-29 12:12:31 +01:00
Brian Clozel 9e4c127eda Merge branch '7.0.x' 2026-04-28 23:36:56 +02:00
Sébastien Deleuze 1f642b973b Merge branch '7.0.x' 2026-04-28 11:16:42 +02:00
Brian Clozel 80c9efd638 Merge branch '7.0.x' 2026-04-28 09:29:26 +02:00
Sam Brannen 0e1a2b4f87 Merge branch '7.0.x' 2026-04-27 14:01:51 +03:00
Brian Clozel 5e6e223686 Merge branch '7.0.x' 2026-04-23 16:24:27 +02:00
Brian Clozel 7a5844f1ce Reject unsafe resource handling locations
As of gh-36692, Spring logs a WARN message when an unsafe resource
handling location is configured. This change now rejects entirely such
setups by failing before the application starts up.

Closes gh-36695
2026-04-23 11:29:47 +02:00
Brian Clozel 0d14aa5856 Merge branch '7.0.x' 2026-04-23 10:55:18 +02:00
Juergen Hoeller c6096ff6e5 Upgrade to Hibernate ORM 7.3.2 and Woodstox 7.1.1 2026-04-21 20:37:08 +02:00
Juergen Hoeller 6ff758391c Merge branch '7.0.x' 2026-04-21 20:36:27 +02:00
Brian Clozel ca9b26eb8f Merge branch '7.0.x' 2026-04-21 18:52:41 +02:00
Brian Clozel 279409acce Merge branch '7.0.x' 2026-04-21 17:47:25 +02:00
Sam Brannen 4f1b4b36bf Merge branch '7.0.x' 2026-04-17 17:25:53 +02:00
Juergen Hoeller 680f4ee482 Merge branch '7.0.x'
# Conflicts:
#	framework-platform/framework-platform.gradle
2026-04-16 23:30:21 +02:00
rstoyanchev 59cd577fd1 Merge branch '7.0.x' 2026-04-16 21:15:42 +01:00
Sébastien Deleuze 9f92183710 Upgrade to Kotlin Serialization 1.11.0
Closes gh-36657
2026-04-16 16:36:56 +02:00
Sam Brannen 680854d1f3 Merge branch '7.0.x' 2026-04-16 16:35:25 +02:00
Sam Brannen 1a7161c85e Merge branch '7.0.x' 2026-04-16 15:12:44 +02:00
Sam Brannen 8bce51267a Merge branch '7.0.x' 2026-04-16 14:42:00 +02:00
Sam Brannen d169eb547e Polishing 2026-04-15 14:30:03 +02:00
Sam Brannen 9c8535f5e4 Compile SpEL expressions that use Optional with null-safe & Elvis operators
In Spring Framework 7.0, we introduced support for using `Optional`
with the null-safe and Elvis operators in SpEL expressions; however,
such expressions were previously not compilable.

To address that, this commit introduces a new
insertOptionalUnwrapIfNecessary() method in CodeFlow which effectively
inserts byte code instructions for `myOptional.orElse(null)`, and the
Elvis, Indexer, MethodReference, and PropertyOrFieldReference
implementations have been modified to track the need to unwrap an
`Optional` in compiled mode and delegate to
insertOptionalUnwrapIfNecessary() accordingly.

See gh-20433
See gh-36331
Closes gh-36330
2026-04-15 14:09:57 +02:00
Sam Brannen 829add3aa9 Update Javadoc for HttpMethod.valueOf() on main
See gh-36642
See gh-36652
2026-04-14 16:30:56 +02:00
Sam Brannen 20e57608ba Merge branch '7.0.x' 2026-04-14 16:18:23 +02:00
Sam Brannen 6275f46a66 Merge branch '7.0.x' 2026-04-13 17:42:59 +02:00
Sam Brannen 9a17b5c453 Merge branch '7.0.x' 2026-04-13 12:59:04 +02:00
Sam Brannen 1787d3e885 Merge branch '7.0.x' 2026-04-12 17:17:27 +02:00
Sam Brannen 1f5af8f364 Merge branch '7.0.x' 2026-04-12 16:46:39 +02:00
Sam Brannen a4b33b98df Merge branch '7.0.x' 2026-04-12 14:29:42 +02:00
Sam Brannen 59f9cf8645 Polish SpEL internals 2026-04-12 14:20:50 +02:00
Sébastien Deleuze fb34264169 Merge branch '7.0.x' 2026-04-10 16:10:45 +02:00
Sam Brannen c2cf5e065d Perform case-insensitive lookup in HttpMethod.valueOf()
Prior to this commit, the implementation of HttpMethod.valueOf()
aligned with the semantics of Enum#valueOf() which requires an exact
match for the enum constant name.

However, since HttpMethod is no longer an enum, that restriction is no
longer necessary. Consequently, this commit revises the implementation
of valueOf() to perform a case-insensitive lookup for predefined
constants.

In other words, HttpMethod.valueOf("GET") and HttpMethod.valueOf("get")
now both resolve to HttpMethod.GET.

Closes gh-36518
2026-04-10 15:04:42 +02:00
Sam Brannen e0e78257d6 Merge branch '7.0.x' 2026-04-09 13:07:19 +02:00
Sam Brannen 227ddf817d Merge branch '7.0.x' 2026-04-09 12:57:01 +02:00
Sam Brannen 07aa952bed Merge branch '7.0.x' 2026-04-09 12:16:02 +02:00
Brian Clozel 598f0b64f0 Merge branch '7.0.x' 2026-04-09 11:02:07 +02:00
Sam Brannen d4e3d6be58 Merge branch '7.0.x' 2026-04-09 10:36:15 +02:00
Juergen Hoeller 8fe0eec5bf Merge branch '7.0.x' 2026-04-08 16:11:34 +02:00
Sam Brannen f78264d158 Polish contribution
See gh-36626
2026-04-08 16:08:09 +02:00
Junseo Bae 4b0101a9dc Defensively copy sentDate in SimpleMailMessage
Use defensive Date copies for sentDate to avoid shared mutable state.

Apply consistent handling in setSentDate, getSentDate, the copy constructor, and copyTo.

Add regression tests for mutation safety and copy isolation.

Closes gh-36626

Signed-off-by: Junseo Bae <ferrater1013@gmail.com>
2026-04-08 16:07:33 +02:00
Sam Brannen e940a38014 Merge branch '7.0.x' 2026-04-08 14:19:16 +02:00
Juergen Hoeller 623bdfb677 Merge branch '7.0.x'
# Conflicts:
#	framework-platform/framework-platform.gradle
2026-04-08 13:43:25 +02:00
Sam Brannen 8566e7bf55 Favor Class#getTypeName over ClassUtils#getQualifiedName where feasible 2026-04-08 13:27:52 +02:00
Sam Brannen c17f25f939 Fall back to type name in ClassUtils.getCanonicalName()
See gh-36607
2026-04-08 12:39:40 +02:00
Sam Brannen 9da22ecb46 Merge branch '7.0.x' 2026-04-08 12:35:54 +02:00
Sam Brannen f602967dbc Consistently supply List to MergedAnnotations.of() 2026-04-08 11:59:24 +02:00
Yanming Zhou f7d3556b8c Polish DisconnectedClientHelper
Use `CollectionUtils::newLinkedHashSet` instead of `LinkedHashSet::new` to avoid resizing.

Closes gh-36618

Signed-off-by: Yanming Zhou <zhouyanming@gmail.com>
2026-04-08 11:58:36 +02:00
Sam Brannen 813e113ea9 Merge branch '7.0.x' 2026-04-08 11:40:52 +02:00
Sam Brannen 306a1f6c99 Merge branch '7.0.x' 2026-04-08 11:14:48 +02:00
Sam Brannen 62c6d67615 Align StandardMethodMetadata with ASM/ClassFile support for getReturnTypeName()
We currently have three implementations of MethodMetadata:

- StandardMethodMetadata (Java reflection)
- SimpleMethodMetadata (ASM)
- ClassFileMethodMetadata (ClassFile API)

The ASM and ClassFile variants return a string equivalent to
Class#getTypeName(); whereas, StandardMethodMetadata currently returns a
binary name using Class#getName() (for example, `[I` instead of `int[]`).

In order to align with the ASM and ClassFile variants and provide
consistent results for all MethodMetadata implementations, this commit
revises StandardMethodMetadata.getReturnTypeName() to use
Class#getTypeName().

Closes gh-36619
2026-04-08 10:27:39 +02:00
Sam Brannen c4c0aca69b Polishing 2026-04-08 10:11:23 +02:00
Sam Brannen 6f08c0b473 Merge branch '7.0.x' 2026-04-07 18:17:00 +02:00
daguimu d37d7abb17 Reject unbalanced parentheses in profile expressions
ProfilesParser.parseTokens() silently accepts unbalanced parentheses
in profile expressions such as "dev)" or "(dev", treating them as
valid. This can lead to unexpected behavior where malformed @Profile
annotations are silently interpreted instead of being rejected.

This commit tightens the validation in parseTokens() to reject:
- Unmatched closing parenthesis at the top level
- Unmatched opening parenthesis when tokens are exhausted

Also fixes an existing test that inadvertently relied on this lenient
behavior by using "spring&framework)" instead of "(spring&framework)".

Closes gh-36550

Signed-off-by: daguimu <daguimu.geek@gmail.com>
2026-04-07 15:38:50 +02:00
Sébastien Deleuze 2a1678f246 Merge branch '7.0.x' 2026-04-07 15:11:54 +02:00
Brian Clozel 3806315e31 Merge branch '7.0.x' 2026-04-07 11:53:44 +02:00
Sam Brannen 6062363738 Use canonical names in error messages in annotation processing
Prior to this commit, we invoked `Class.getName()` when building error
messages during annotation processing, resulting in exceptions like the
following which use binary names for nested types and arrays.

  Attribute 'chars' in annotation
  org.springframework.core.annotation.AnnotationUtilsTests$CharsContainer
  should be compatible with [C but a [I value was returned

This commit switches to canonical names in error messages in annotation
processing, resulting in improved such errors messages such as the
following.

  Attribute 'chars' in annotation
  org.springframework.core.annotation.AnnotationUtilsTests.CharsContainer
  should be compatible with char[] but a int[] value was returned

In addition, this commit introduces a new getCanonicalName(Class) method
in ClassUtils, which has effectively been extracted from the following
classes where this functionality was previously duplicated.

- AttributeMethods
- SynthesizedMergedAnnotationInvocationHandler
- TypeDescriptor
- DefaultRetryPolicy
- ReflectiveIndexAccessor

Closes gh-36607
2026-04-06 17:34:54 +02:00
Sam Brannen 31d9fe5f41 Merge branch '7.0.x' 2026-04-06 16:56:26 +02:00
Sébastien Deleuze 2ee4c3a363 Provide bean conditional registration capabilities in BeanRegistrarDsl
Closes gh-36601
2026-04-05 18:49:07 +02:00
Sam Brannen 82b179f238 Merge branch '7.0.x' 2026-04-04 17:21:16 +02:00
Sam Brannen f993e9710d Align with JDK by throwing TypeNotPresentException in MergedAnnotations
Prior to this commit, we used ClassUtils.resolveClassName() in
TypeMappedAnnotation.adapt(...) which throws an IllegalStateException
or IllegalArgumentException if a type referenced by an annotation
attribute cannot be loaded. However, if such an error occurs while
using the JDK's reflection APIs, a TypeNotPresentException is thrown
instead.

In order to align with the standard behavior of the JDK, this commit
modifies TypeMappedAnnotation.adapt(...) to use ClassUtils.forName()
and throw a TypeNotPresentException in such scenarios.

This commit also makes similar changes in
MergedAnnotationReadingVisitor and ClassFileAnnotationDelegate.

Closes gh-36593
2026-04-03 16:44:28 +02:00
Sam Brannen 822001c6a4 Merge branch '7.0.x' 2026-04-03 15:42:28 +02:00
Sam Brannen f2d4d59f5a Merge branch '7.0.x' 2026-04-03 15:39:58 +02:00
Sam Brannen 4709f68446 Merge branch '7.0.x' 2026-04-02 18:40:08 +02:00
Sam Brannen afba74c516 Merge branch '7.0.x' 2026-04-02 17:21:10 +02:00
Sam Brannen fd50c0841c Merge branch '7.0.x' 2026-04-02 16:55:02 +02:00
Brian Clozel cbe8b148b1 Merge branch '7.0.x' 2026-04-02 14:48:56 +02:00
Stéphane Nicoll 2086508924 Polish
See gh-36581
2026-04-02 14:41:26 +02:00
Juergen Hoeller 759b173b1a Merge branch '7.0.x' 2026-04-02 14:31:56 +02:00
Sam Brannen 7590c4c92e Fix Javadoc link
See gh-36581
2026-04-02 13:32:32 +02:00
Sam Brannen 596c0df826 Merge branch '7.0.x' 2026-04-02 12:45:50 +02:00
Stéphane Nicoll d5b6f4a7ee Polish BeanRegistrar Javadoc and add tests for non-invocation semantics
Revise the BeanRegistrar Javadoc to document the two distinct usage
modes: @Configuration/@Import and programmatic GenericApplicationContext
setup.

Clarify that implementations are not Spring components (requiring a
no-arg constructor and no dependency injection), and detail the ordering
guarantees for each mode.

Add missing tests

Signed-off-by: Stéphane Nicoll <stephane.nicoll@broadcom.com>
2026-04-02 11:18:58 +02:00
Sam Brannen 5dc4a3a7a0 Merge branch '7.0.x' 2026-04-02 11:06:39 +02:00
Brian Clozel 21f8b6d2f3 Merge branch '7.0.x' 2026-04-02 10:19:55 +02:00
Juergen Hoeller 6ebaeba1c2 Merge branch '7.0.x' 2026-04-01 21:17:00 +02:00
Juergen Hoeller 3bf31e45dd Replace DeferredBeanRegistrar with implicit ordering semantics
GenericApplicationContext-registered BeanRegistrars are invoked after other programmatic bean definitions. Configuration-imported BeanRegistrars participate in configuration class order and in particular in Boot's auto-configuration ordering.

Closes gh-21497
2026-04-01 20:59:45 +02:00
Brian Clozel 5e16e25109 Merge branch '7.0.x' 2026-04-01 11:06:24 +02:00
Sam Brannen 6b5f0e92b1 Merge branch '7.0.x' 2026-04-01 10:15:08 +02:00
Brian Clozel 2c973d3034 Mention when RestTemplate will be removed
`RestTemplate` is deprecated, this commit amends the JavaDoc to mention
its scheduled removal for the next major version, Spring Framework 8.0.

See gh-36574
2026-03-31 22:03:36 +02:00
Brian Clozel 94e2f49e9f Read multipart requests from RestTestClient
Prior to this commit, the `RestTestClient` MockMvc integration would
support transforming client requests into MockMvc requests.
`RestTestClient` can serialize `MultiValueMap` request bodies as
multipart requests. In this case, the `MockMvcClientHttpRequestFactory`
would only read the body as byte stream and would not turn this into a
proper `MockMultipartHttpServletRequestBuilder`.

This commit uses the new `MultipartHttpMessageConverter` to parse the
request payload as `MockPart` instances to be added to the MockMvc
requests.

Closes gh-35569
2026-03-31 19:13:06 +02:00
Yanming ZhouandSam Brannen 8b62ea13a1 Remove deprecated methodIdentification() method in CacheAspectSupport
The Javadoc said it's used for logging, but it's not used anywhere.

Closes gh-36560

Signed-off-by: Yanming Zhou <zhouyanming@gmail.com>
Signed-off-by: Sam Brannen <104798+sbrannen@users.noreply.github.com>
Co-authored-by: Sam Brannen <104798+sbrannen@users.noreply.github.com>
2026-03-31 17:56:47 +02:00
Sam Brannen 76ad7c9cc0 Merge branch '7.0.x' 2026-03-31 17:50:15 +02:00
rstoyanchev 8586095370 Merge branch '7.0.x' 2026-03-31 16:22:16 +01:00
Sébastien Deleuze 32970fe10d Fix WebClient context propagation in Kotlin Coroutines
Prior to this commit, thread-local variables like Trace ID were not
automatically propagated into the Reactor Context when making requests
using Kotlin coroutine WebClient extensions like `awaitExchange`.

This commit updates `CoroutineContext.toReactorContext()` to capture
thread-local values via Micrometer Context Propagation when available,
ensuring observations and traces are properly reused.

Closes gh-36182
2026-03-31 14:45:55 +02:00
Brian Clozel 83c2afb643 Deprecate RestTemplate and related types
As announced in "the state of HTTP clients in Spring" blog post
(https://spring.io/blog/2025/09/30/the-state-of-http-clients-in-spring),
the deprecation timeline for `RestTemplate` was announced last November
and docs were updated accordingly.

This commit `@Deprecate` `RestTemplate` and related types for removal to
send a stronger signal to our community.
The actual removal is scheduled for Spring Framework 8.0 (not yet
scheduled).

Closes gh-36574
2026-03-31 12:28:44 +02:00
rstoyanchev 7473cd5fbc Merge branch '7.0.x' 2026-03-31 11:00:51 +01:00
Brian Clozel 8de3683ccb Merge branch '7.0.x' 2026-03-31 11:47:41 +02:00
Brian Clozel 208f62ba07 Merge branch '7.0.x' 2026-03-31 11:02:57 +02:00
Sébastien Deleuze a0d51c8f7d Upgrade to Dokka 2.2.0
Closes gh-36570
2026-03-31 09:40:46 +02:00
Sébastien Deleuze f4cefb6ec5 Upgrade to Jackson 3.1 and 2.21
This commit raises the Jackson baseline and upgrades related
dependencies to Jackson 3.1 and 2.21 which are the new LTS.

Closes gh-36130
2026-03-31 09:21:48 +02:00
Sam Brannen e7fdbb8339 Merge branch '7.0.x' 2026-03-30 17:01:56 +02:00
Sam Brannen a64b001f6a Use AtomicBoolean as a "plain" holder in BeanOverrideUtils
Since the AtomicBoolean cannot be accessed by another thread, there is
no need to use compareAndSet().
2026-03-30 13:44:12 +02:00
Sam Brannen c68470d0fd Simplify Bean Override support in the SpringExtension
See gh-36096
2026-03-30 13:22:12 +02:00
Sam Brannen 182e6b744a Polishing 2026-03-30 13:22:12 +02:00
rstoyanchev d46e96f1ca Correct ApiVersionConfigurer method signature
Closes gh-36551
2026-03-30 12:07:06 +01:00
Sam Brannen 51a5489438 Polishing 2026-03-29 17:34:32 +02:00
Sam Brannen f9523a785b Support @⁠MockitoBean and @⁠MockitoSpyBean on test constructor parameters
Prior to this commit, @⁠MockitoBean and @⁠MockitoSpyBean could be
declared on fields or at the type level (on test classes and test
interfaces), but not on constructor parameters. Consequently, a test
class could not use constructor injection for bean overrides.

To address that, this commit introduces support for @⁠MockitoBean and
@⁠MockitoSpyBean on constructor parameters in JUnit Jupiter test
classes. Specifically, the Bean Override infrastructure has been
overhauled to support constructor parameters as declaration sites and
injection points alongside fields, and the SpringExtension now
recognizes composed @⁠BeanOverride annotations on constructor
parameters in supportsParameter() and resolves them properly in
resolveParameter(). Note, however, that this support has not been
introduced for @⁠TestBean.

For example, the following which uses field injection:

   @⁠SpringJUnitConfig(TestConfig.class)
   class BeanOverrideTests {

      @⁠MockitoBean
      CustomService customService;

      // tests...
   }

Can now be rewritten to use constructor injection:

   @⁠SpringJUnitConfig(TestConfig.class)
   class BeanOverrideTests {

      private final CustomService customService;

      BeanOverrideTests(@⁠MockitoBean CustomService customService) {
         this.customService = customService;
      }

      // tests...
   }

With Kotlin this can be achieved even more succinctly via a compact
constructor declaration:

   @⁠SpringJUnitConfig(TestConfig::class)
   class BeanOverrideTests(@⁠MockitoBean val customService: CustomService) {

      // tests...
   }

Of course, if one is a fan of so-called "test records", that can also
be achieved succinctly with a Java record:

   @⁠SpringJUnitConfig(TestConfig.class)
   record BeanOverrideTests(@⁠MockitoBean CustomService customService) {

      // tests...
   }

Closes gh-36096
2026-03-29 17:17:16 +02:00
Juergen Hoeller 955f9d3ea9 Merge branch '7.0.x'
# Conflicts:
#	spring-beans/src/main/java/org/springframework/beans/factory/support/BeanRegistryAdapter.java
#	spring-context/src/main/java/org/springframework/context/annotation/Import.java
2026-03-28 20:41:57 +01:00
Juergen Hoeller 7502b92392 Introduce DeferredBeanRegistrar and BeanRegistry#containsBean methods
Closes gh-21497
2026-03-28 20:27:23 +01:00
Juergen Hoeller 99e7543a7f Merge branch '7.0.x' 2026-03-28 11:13:56 +01:00
Sam Brannen 82a9d66079 Merge branch '7.0.x' 2026-03-27 11:26:11 +01:00
Sam Brannen 5708b73ea9 Introduce ResolvableType.forParameter() factory method
Prior to this commit, one could invoke
ResolvableType.forMethodParameter(MethodParameter.forParameter(parameter))
to create a ResolvableType for a Parameter; however, that's slightly
cumbersome.

To address that, this commit introduces ResolvableType.forParameter(),
analogous to existing convenience factory methods in ResolvableType.

Closes gh-36545
2026-03-26 16:43:50 +01:00
Sam Brannen 5e975f51dd Remove redundant Assert.notNull() checks in ResolvableType
Since equivalent Assert.notNull() checks are already performed by
subsequent code (constructors and factory methods), there is no need
to perform the exact same assertion twice in such use cases.

Closes gh-36544
2026-03-26 16:40:17 +01:00
Sam Brannen ccf0cae18c Polishing 2026-03-26 15:22:04 +01:00
Brian Clozel de0dfe5d93 Fix Javadoc format error
See gh-33263
2026-03-26 15:19:20 +01:00
Brian Clozel aac521c116 Add integration tests and docs for multipart support
This commit adds integration tests and reference documentation
 for multipart support in `RestClient` and `RestTestClient`.

Closes gh-35569
Closes gh-33263
2026-03-26 15:10:01 +01:00
Brian Clozel ab8de8ec4b Support Map payloads in FormHttpMessageConverter
Prior to this commit, the `FormHttpMessageConverter` would only support
`MultiValueMap` payloads for reading and writing. While this can be
useful when web forms contain multiple values under the same key, this
prevent developers from using a common `Map` type and factory methods
like `Map.of(...)`.

This commit relaxes constraints in `FormHttpMessageConverter` and
ensures that `Map` types are supported for reading and writing URL
encoded forms.
Note that when reading forms to a `Map`, only the first value for each
key will be considered and other values will be dropped if they exist.

Closes gh-36408
2026-03-26 15:09:44 +01:00
Brian Clozel abc3cfc7be Move multipart support to dedicated converter
Prior to this commit, gh-36255 introduced the new
`MultipartHttpMessageConverter`, focusing on multipart message
conversion in a separate converter. The `FormHttpMessageConverter` did
conflate URL encoded forms and multipart messages in the same converter.

With the introduction of the new converter and related types in the same
package (with `Part`, `FormFieldPart` and `FilePart`), we can now
revisit this arrangement.

This commit restricts the `FormHttpMessageConverter` to URL encoded
forms only and as a result, changes its implementation to only consider
`MultiValueMap<String, String>` types for reading and writing HTTP
messages. Because type erasure, this converter is now a
`SmartHttpMessageConverter` to get better type information with
`ResolvableType`.

As a result, the `AllEncompassingFormHttpMessageConverter` is formally
deprecated and replaced by the `MultipartHttpMessageConverter`, by
setting part converters explicitly in its constructor.

Closes gh-36256
2026-03-26 15:08:58 +01:00
Brian Clozel 44302aca93 Add MultipartHttpMessageConverter
Prior to this commit, the `FormHttpMessageConverter` would write, but
not read, multipart HTTP messages. Reading multipart messages is
typicaly performed by Servlet containers with Spring's
`MultipartResolver` infrastructure.

This commit introduces a new `MultipartHttpMessageConverter` that copies
the existing feature for writing multipart messages, borrowed from
`FormHttpMessageConverter`. This also introduces a new `MultipartParser`
class that is an imperative port of the reactive variant, but keeping it
based on the `DataBuffer` abstraction. This will allow us to maintain
both side by side more easily.

This change also adds new `Part`, `FilePart` and `FormFieldPart` types
that will be used when converting multipart messages to
`MultiValueMap<String, Part>` maps.

Closes gh-36255
2026-03-26 15:08:22 +01:00
Brian Clozel ac86dc1264 Move multipart test files to a common location
This commit moves "*.multipart" test files to a common location
in order to share these resources with another test suite.

Closes gh-36253
2026-03-26 15:08:01 +01:00
Brian Clozel 051219c3c9 Introduce HttpMessageConverter#canWriteRepeatedly
The `AbstractHttpMessageConverter#supportsRepeatableWrites`
contract is a protected method that message converters can override.
This method tells whether the current converter can write several
times the payload given as a parameter. This is mainly useful on the
client side, where we need to know if we can send the same HTTP
message again, after receiving an HTTP redirect status.

Because this method is protected, this limits our ability to call
it from a different package; this is needed for gh-33263.

This commit promotes this method to the main `HttpMessageConverter`
interface and deprecates the former.

Closes gh-36252
2026-03-26 15:07:48 +01:00
Sam Brannen d8916a326a Merge branch '7.0.x' 2026-03-26 14:29:16 +01:00
Brian Clozel 3be51b1c77 Merge branch '7.0.x' 2026-03-25 21:53:09 +01:00
Sam Brannen a3118f276f Remove @⁠ContextConfiguration from MockitoBeanNestedTests
See gh-31456
2026-03-25 17:46:48 +01:00
Sam Brannen 3d70074089 Introduce support for custom parameter names in ParameterResolutionDelegate
The resolveDependency() utility method in ParameterResolutionDelegate
resolves a dependency using the name of the parameter as a fallback
qualifier. That suffices for most use cases; however, there are times
when a custom parameter name should be used instead.

For example, for our Bean Override support in the Spring TestContext
Framework, an annotation such as @⁠MockitoBean("myBean") specifies an
explicit name that should be used instead of name of the annotated
parameter.

Furthermore, introducing support for custom parameter names will
greatly simplify the logic in SpringExtension that will be required to
implement #36096.

To address those issues, this commit introduces an overloaded variant
of resolveDependency() which accepts a custom parameter name.
Internally, a custom DependencyDescriptor has been implemented to
transparently support this use case.

See gh-36096
Closes gh-36534
2026-03-25 13:15:15 +01:00
Sam Brannen bcc9e27dd0 Polishing 2026-03-25 12:41:05 +01:00
Sam Brannen 0f4ee906de Extract BeanOverrideUtils from BeanOverrideHandler
Prior to this commit, BeanOverrideHandler contained a large amount of
logic solely related to search algorithms for finding handlers.
Consequently, BeanOverrideHandler took on more responsibility than it
ideally should. In addition, we will soon increase the complexity of
those search algorithms, and we will need to make another utility
method public for use outside the bean.override package.

To address those issues, this commit extracts the search utilities from
BeanOverrideHandler into a new BeanOverrideUtils class.

Closes gh-36533
2026-03-25 11:58:49 +01:00
Juergen Hoeller 5f9dd9e135 Merge branch '7.0.x' 2026-03-24 23:50:32 +01:00
Juergen Hoeller 5335dbf802 Merge branch '7.0.x' 2026-03-24 18:08:10 +01:00
Brian Clozel 4c1b4f33a8 Skip Jaxb auto-detection in HttpMessageConverters for servers
Prior to this commit, `HttpMessageConverters` would consider the JAXB
message converters when building `HttpMessageConverters` instances.
We noticed that, on the server side, the Jakarta JAXB dependency is very
common on the classpath and often brought transitively. At runtime, this
converter can use significant CPU resources when checking the
`canRead`/`canWrite` methods. This can happen when content types aren't
strictly called out on controller endpoints.

This commit changes the auto-detection mechanism in
`HttpMessageConverters` to not consider the JAXB message converter for
server use cases.
For client use cases, we keep considering this converter as the runtime
cost there is lower.

Closes gh-36302
2026-03-24 17:23:49 +01:00
Brian Clozel f1a60f1664 Merge branch '7.0.x' 2026-03-24 13:19:49 +01:00
Sam Brannen d66e6571c1 Merge branch '7.0.x' 2026-03-24 11:07:55 +01:00
Brian Clozel dc8b9fff57 Merge branch '7.0.x' 2026-03-24 10:26:04 +01:00
Yanming Zhou 0eba6f0da3 Add typesafe method to get generic bean by name with type reference
Fix GH-34687

Signed-off-by: Yanming Zhou <zhouyanming@gmail.com>
2026-03-24 08:02:58 +01:00
Juergen Hoeller a5579614d9 Merge branch '7.0.x'
# Conflicts:
#	framework-platform/framework-platform.gradle
2026-03-23 20:14:26 +01:00
Juergen Hoeller eca6d91532 Refine nullable declaration of internal type array constant 2026-03-23 17:11:32 +01:00
Juergen Hoeller 354c315f55 Upgrade to Hibernate ORM 7.3
Closes gh-36519
2026-03-23 17:11:21 +01:00
Brian Clozel a302ad88f3 Merge branch '7.0.x' 2026-03-23 14:35:05 +01:00
Sam Brannen 447e1ee0cd Merge branch '7.0.x' 2026-03-23 11:50:36 +01:00
Sam Brannen bef2f4488f Merge branch '7.0.x' 2026-03-23 11:36:48 +01:00
Sam Brannen d5adfd7980 Merge branch '7.0.x' 2026-03-22 18:04:53 +01:00
Sam Brannen 3777a9fcbd Merge branch '7.0.x' 2026-03-22 17:33:01 +01:00
Sam Brannen 7c834224a2 Merge branch '7.0.x' 2026-03-22 16:56:20 +01:00
Paul Ngo (Do Not Bug) c1e90d51ca GenericTypeResolver.resolveType should resolve TypeVariable with nested ParameterizedType (#36480)
Signed-off-by: anaconda875 <hflbtmax@gmail.com>
2026-03-21 13:28:12 +01:00
Juergen Hoeller a0dba60fa6 Merge branch '7.0.x' 2026-03-21 12:45:25 +01:00
Brian Clozel 63e01cf6b3 Merge branch '7.0.x' 2026-03-20 15:56:10 +01:00
Sam Brannen 5d45036c09 Merge branch '7.0.x' 2026-03-20 11:13:01 +01:00
Sam Brannen b31f78de50 Merge branch '7.0.x' 2026-03-19 14:16:58 +01:00
Sam Brannen 967fd099f9 Merge branch '7.0.x' 2026-03-19 14:02:36 +01:00
rstoyanchev e655edafec Merge branch '7.0.x' 2026-03-19 09:12:49 +00:00
Sam Brannen 6ec2455e24 Merge branch '7.0.x' 2026-03-18 18:39:17 +01:00
Sam Brannen 003d8b2f80 Merge branch '7.0.x' 2026-03-18 18:19:31 +01:00
Sam Brannen b846a29b17 Polishing 2026-03-18 17:42:18 +01:00
박준형 8b994be381 Remove unnecessary space in contributing guide title
Closes gh-36491

Signed-off-by: junhyung8795 <junhyung8795@naver.com>
2026-03-18 15:43:45 +01:00
Brian Clozel b246fc881b Merge branch '7.0.x' 2026-03-18 12:19:22 +01:00
rstoyanchev 5785923c0e Lower log level of cache miss in HandlerMappingIntrospector
See gh-36309
2026-03-17 19:00:34 +00:00
rstoyanchev 66607cb145 Add PreFlightRequestFilter
Closes gh-36482
2026-03-17 18:54:29 +00:00
Brian Clozel 4637da1f36 Merge branch '7.0.x' 2026-03-17 18:12:40 +01:00
Brian Clozel 22e4d84993 Add support for "application/jsonl" JSON lines
Prior to this commit, Spring web frameworks were using the
"application/x-ndjson" media type for streaming JSON payloads delimited
with newlines.

The "application/jsonl" media type seems to gain popularity in the
broader ecosystem and could supersede NDJSON in the future. This commit
adds support for JSON Lines as an alternative.

Closes gh-36485
2026-03-17 12:18:23 +01:00
Sébastien Deleuze e2b9b19970 Upgrade to Kotlin 2.3.20
Closes gh-36484
2026-03-17 11:59:45 +01:00
Brian Clozel 75f964657f Merge branch '7.0.x' 2026-03-17 09:20:16 +01:00
Sam Brannen 23b73168a0 Merge branch '7.0.x' 2026-03-16 13:39:30 +01:00
Juergen Hoeller 391dd90e84 Support "classpath*:" prefix for resource bundle basename
Closes gh-36292
See gh-36415
2026-03-16 12:23:35 +01:00
Juergen Hoeller 1345760087 Support "classpath*:" prefix for ResourceLoader#getResource
Introduces a general-purpose consumeContent method on Resource and EncodedResource with special behavior for multi-content resources. Regular getInputStream/getReader calls will expose the merged content of all same-named resources in the classpath.

Closes gh-36415
2026-03-16 12:23:16 +01:00
Brian Clozel 51fccf226f Merge branch '7.0.x' 2026-03-16 11:16:48 +01:00
Sam Brannen a14a20b61b Merge branch '7.0.x' 2026-03-15 17:06:18 +01:00
Sam Brannen fadbd0fa31 Partially revert forward merge of "Branch for 7.0.x maintenance" 2026-03-15 14:45:09 +01:00
Sam Brannen 40ef084e7f Merge branch '7.0.x' 2026-03-15 14:40:47 +01:00
Sam Brannen a07033f3fb Resolve all default context configuration within test class hierarchies
Prior to this commit, if a superclass or enclosing test class (such as
one annotated with @⁠SpringBootTest or simply
@⁠ExtendWith(SpringExtension.class)) was not annotated with
@⁠ContextConfiguration (or @⁠Import with @⁠SpringBootTest), the
ApplicationContext loaded for a subclass or @⁠Nested test class would
not use any default context configuration for the superclass or
enclosing test class.

Effectively, a default XML configuration file or static nested
@⁠Configuration class for the superclass or enclosing test class was
not discovered by the AbstractTestContextBootstrapper when attempting
to build the MergedContextConfiguration (application context cache key).

To address that, this commit introduces a new
resolveDefaultContextConfigurationAttributes() method in
ContextLoaderUtils which is responsible for creating instances of
ContextConfigurationAttributes for all superclasses and enclosing
classes. This effectively enables AbstractTestContextBootstrapper to
delegate to the resolved SmartContextLoader to properly detect a
default XML configuration file or static nested @⁠Configuration class
even if such classes are not annotated with @⁠ContextConfiguration.

Closes gh-31456
2026-03-13 15:39:05 +01:00
Sam Brannen 293a9c0ee3 Allow local @⁠BootstrapWith annotation to override a meta-annotation
This commit revises the resolveExplicitTestContextBootstrapper()
algorithm in BootstrapUtils to allow a local @⁠BootstrapWith annotation
to override a meta-annotation within the same composed annotation.

Closes gh-35938
2026-03-13 15:24:38 +01:00
Sam Brannen 32d1b83f62 Override Servlet 6.1's doPatch() method in FrameworkServlet
See gh-12640
See gh-14975
See gh-36258
Closes gh-36247
2026-03-13 15:17:04 +01:00
Brian Clozel 227bc196b1 Consider 7.0.x branch for Antora UI upgrades 2026-03-13 15:02:25 +01:00
Brian Clozel 2fcef050e0 Build 7.1.0-SNAPSHOT 2026-03-13 14:50:12 +01:00
814 changed files with 23535 additions and 6588 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:
- '7.0.x-internal'
- 'main'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
@@ -22,16 +22,16 @@ 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
/**/framework-api-*-docs.zip::zip.type=docs
/**/framework-api-*-schema.zip::zip.type=schema
build-name: ${{ vars.COMMERCIAL && format('spring-framework-commercial-{0}', '7.0.x') || format('spring-framework-{0}', '7.0.x') }}
build-name: ${{ vars.COMMERCIAL && format('spring-framework-commercial-{0}', '7.1.x') || format('spring-framework-{0}', '7.1.x') }}
folder: 'deployment-repository'
project: ${{ vars.COMMERCIAL && 'spring' }}
repository: ${{ vars.COMMERCIAL && 'spring-enterprise-maven-dev-local' || 'libs-snapshot-local' }}
+1 -1
View File
@@ -22,7 +22,7 @@ jobs:
toolchain: true
- version: 25
toolchain: false
- version: 26
- version: 27
toolchain: true
exclude:
- os:
+2 -2
View File
@@ -2,8 +2,8 @@ name: Release Milestone
on:
push:
tags:
- v7.0.0-M[1-9]
- v7.0.0-RC[1-9]
- v7.1.0-M[1-9]
- v7.1.0-RC[1-9]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
+1 -1
View File
@@ -2,7 +2,7 @@ name: Release
on:
push:
tags:
- v7.0.[0-9]+
- v7.1.[0-9]+
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
+3
View File
@@ -52,6 +52,9 @@ atlassian-ide-plugin.xml
# VS Code
.vscode/
# Claude artifacts
.claude/*
cached-antora-playbook.yml
node_modules
+1 -1
View File
@@ -1,4 +1,4 @@
# Contributing to the Spring Framework
# Contributing to the Spring Framework
First off, thank you for taking the time to contribute! :+1: :tada:
+4 -4
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,13 +64,13 @@ 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/",
"https://projectreactor.io/docs/test/release/api/",
"https://junit.org/junit4/javadoc/4.13.2/",
"https://docs.junit.org/6.0.3/api/",
"https://docs.junit.org/6.1.2/api/",
"https://www.reactive-streams.org/reactive-streams-1.0.4-javadoc/",
"https://r2dbc.io/spec/1.0.0.RELEASE/api/",
"https://jspecify.dev/docs/api/"
+1 -1
View File
@@ -20,7 +20,7 @@ ext {
dependencies {
checkstyle "io.spring.javaformat:spring-javaformat-checkstyle:${javaFormatVersion}"
implementation "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}"
implementation "org.jetbrains.dokka:dokka-gradle-plugin:2.1.0"
implementation "org.jetbrains.dokka:dokka-gradle-plugin:2.2.0"
implementation "com.tngtech.archunit:archunit:1.4.1"
implementation "org.gradle:test-retry-gradle-plugin:1.6.2"
implementation "io.spring.javaformat:spring-javaformat-gradle-plugin:${javaFormatVersion}"
@@ -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());
}
});
}
@@ -54,7 +55,7 @@ public class KotlinConventions {
"-Xjsr305=strict", // For dependencies using JSR 305
"-opt-in=kotlin.RequiresOptIn",
"-Xjdk-release=17", // Needed due to https://youtrack.jetbrains.com/issue/KT-49746
"-Xannotation-default-target=param-property" // Upcoming default, see https://youtrack.jetbrains.com/issue/KT-73255
"-Xannotation-default-target=param-property" // Preferred behavior, default with Kotlin language version set to 2.4+, see https://youtrack.jetbrains.com/issue/KT-73255
);
});
}
@@ -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"));
});
}
+1 -1
View File
@@ -31,7 +31,7 @@ asciidoc:
spring-org: 'spring-projects'
spring-github-org: "https://github.com/{spring-org}"
spring-framework-github: "https://github.com/{spring-org}/spring-framework"
spring-framework-code: '{spring-framework-github}/tree/7.0.x'
spring-framework-code: '{spring-framework-github}/tree/main'
spring-framework-issues: '{spring-framework-github}/issues'
spring-framework-wiki: '{spring-framework-github}/wiki'
# Docs
@@ -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)
@@ -723,7 +771,6 @@ following kinds of expressions cannot be compiled.
* Expressions relying on the conversion service
* Expressions using custom resolvers
* Expressions using overloaded operators
* Expressions using `Optional` with the null-safe or Elvis operator
* Expressions using array construction syntax
* Expressions using selection or projection
* Expressions using bean references
@@ -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]]
@@ -174,7 +174,7 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
val actors = jdbcTemplate.query("select first_name, last_name from t_actor") { rs, _ ->
Actor(rs.getString("first_name"), rs.getString("last_name"))
Actor(rs.getString("first_name"), rs.getString("last_name")) }
----
======
@@ -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:
@@ -557,8 +557,6 @@ Kotlin::
// some JDBC-backed DAO class...
private val namedParameterJdbcTemplate = NamedParameterJdbcTemplate(dataSource)
private val namedParameterJdbcTemplate = NamedParameterJdbcTemplate(dataSource)
fun countOfActors(exampleActor: Actor): Int {
// notice how the named parameters match the properties of the above 'Actor' class
val sql = "select count(*) from t_actor where first_name = :firstName and last_name = :lastName"
@@ -574,7 +572,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.
@@ -680,6 +678,21 @@ provides `firstName` and `lastName` properties, such as the `Actor` class from a
.update();
----
For batch updates, accumulate the batch entries in a fluent fashion through `batch()`,
binding the parameters for each entry as for a single update either as positional
parameters or as named parameters and separating consecutive entries with `add()`.
The accumulated entries are executed as a single JDBC batch by `update()`, which
also completes the final entry implicitly:
[source,java,indent=0,subs="verbatim,quotes"]
----
this.jdbcClient.sql("insert into t_actor (first_name, last_name) values (:firstName, :lastName)")
.batch()
.param("firstName", "Leonor").param("lastName", "Watling").add()
.param("firstName", "Christian").param("lastName", "Bale")
.update();
----
The automatic `Actor` class mapping for parameters as well as the query results above is
provided through implicit `SimplePropertySqlParameterSource` and `SimplePropertyRowMapper`
strategies which are also available for direct use. They can serve as a common replacement
@@ -687,9 +700,9 @@ for `BeanPropertySqlParameterSource` and `BeanPropertyRowMapper`/`DataClassRowMa
also with `JdbcTemplate` and `NamedParameterJdbcTemplate` themselves.
NOTE: `JdbcClient` is a flexible but simplified facade for JDBC query/update statements.
Advanced capabilities such as batch inserts and stored procedure calls typically require
extra customization: consider Spring's `SimpleJdbcInsert` and `SimpleJdbcCall` classes or
plain direct `JdbcTemplate` usage for any such capabilities not available in `JdbcClient`.
Advanced capabilities such as stored procedure calls typically require extra customization:
consider Spring's `SimpleJdbcInsert` and `SimpleJdbcCall` classes or plain direct
`JdbcTemplate` usage for any such capabilities not available in `JdbcClient`.
[[jdbc-SQLExceptionTranslator]]
@@ -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]
@@ -32,7 +32,7 @@ example writes notifications to the console:
}
public boolean isNotificationEnabled(Notification notification) {
return AttributeChangeNotification.class.isAssignableFrom(notification.getClass());
return (notification instanceof AttributeChangeNotification);
}
}
@@ -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,47 +350,42 @@ 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
URL encoded forms, using the `"application/x-www-form-urlencoded"` media type, are useful for sending String key/values over the wire.
This is supported by the `FormHttpMessageConverter`, if the application uses a `MultiValueMap<String, String>` as source instance
or a target type.
For example:
include-code::./../restmessageconversion/RestClientMessageConversion[tag=urlencodedform,indent=0]
==== Multipart
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));
// send using RestClient.post or RestTemplate.postForEntity
----
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.
If necessary, you can explicitly provide the `MediaType` with an `HttpEntity` wrapper.
Once the `MultiValueMap` is ready, you can use it as the body of a `POST` request, using `RestClient.post().body(parts)` (or `RestTemplate.postForObject`).
The `Content-Type` is set to `multipart/form-data` by the `MultipartHttpMessageConverter`.
As seen in the previous section, `MultiValueMap` types can also be used for URL encoded forms.
It is preferable to explicitly set the media type in the `Content-Type` or `Accept` HTTP request headers to ensure that the expected
message converter is used.
`RestClient` can also receive multipart responses.
To decode a multipart response body, use a `ParameterizedTypeReference<MultiValueMap<String, Part>>`.
The decoded map contains `Part` instances where `FormFieldPart` represents form field values
and `FilePart` represents file parts with a `filename()` and a `transferTo()` method.
include-code::./../restmessageconversion/RestClientMessageConversion[tag=multipartresponse,indent=0]
If the `MultiValueMap` contains at least one non-`String` value, the `Content-Type` is set to `multipart/form-data` by the `FormHttpMessageConverter`.
If the `MultiValueMap` has `String` values, the `Content-Type` defaults to `application/x-www-form-urlencoded`.
If necessary the `Content-Type` may also be set explicitly.
[[rest-request-factories]]
=== Client Request Factories
@@ -480,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]]
@@ -560,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
@@ -874,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]]
@@ -131,7 +131,7 @@ listing shows the `TaskScheduler` interface definition:
ScheduledFuture scheduleWithFixedDelay(Runnable task, Instant startTime, Duration delay);
ScheduledFuture scheduleWithFixedDelay(Runnable task, Duration delay);
}
----
The simplest method is the one named `schedule` that takes only a `Runnable` and an `Instant`.
@@ -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]
@@ -468,14 +468,14 @@ seconds:
----
@Scheduled(initialDelay = 0, fixedRate = 5000)
public Mono<Void> reactiveSomething() {
AtomicInteger countdown = new AtomicInteger(2);
AtomicInteger countDown = new AtomicInteger(2);
return Mono.defer(() -> {
if (countDown.get() == 0 || countDown.decrementAndGet() == 0) {
return Mono.fromRunnable(() -> System.out.println("Message"));
}
return Mono.error(new IllegalStateException("Cannot deliver message"));
})
});
}
----
@@ -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]]
@@ -12,18 +12,20 @@ The annotations can be applied in the following ways.
* On a non-static field in a test class or any of its superclasses.
* On a non-static field in an enclosing class for a `@Nested` test class or in any class
in the type hierarchy or enclosing class hierarchy above the `@Nested` test class.
* On a parameter in the constructor for a test class.
* At the type level on a test class or any superclass or implemented interface in the
type hierarchy above the test class.
* At the type level on an enclosing class for a `@Nested` test class or on any class or
interface in the type hierarchy or enclosing class hierarchy above the `@Nested` test
class.
When `@MockitoBean` or `@MockitoSpyBean` is declared on a field, the bean to mock or spy
is inferred from the type of the annotated field. If multiple candidates exist in the
`ApplicationContext`, a `@Qualifier` annotation can be declared on the field to help
disambiguate. In the absence of a `@Qualifier` annotation, the name of the annotated
field will be used as a _fallback qualifier_. Alternatively, you can explicitly specify a
bean name to mock or spy by setting the `value` or `name` attribute in the annotation.
When `@MockitoBean` or `@MockitoSpyBean` is declared on a field or constructor parameter,
the bean to mock or spy is inferred from the type of the annotated field or parameter. If
multiple candidates exist in the `ApplicationContext`, a `@Qualifier` annotation can be
declared on the field or parameter to help disambiguate. In the absence of a `@Qualifier`
annotation, the name of the annotated field or parameter will be used as a _fallback
qualifier_. Alternatively, you can explicitly specify a bean name to mock or spy by
setting the `value` or `name` attribute in the annotation.
When `@MockitoBean` or `@MockitoSpyBean` is declared at the type level, the type of bean
(or beans) to mock or spy must be supplied via the `types` attribute in the annotation
@@ -211,6 +213,82 @@ Kotlin::
<1> Replace the bean named `service` with a Mockito mock.
======
The following example shows how to use `@MockitoBean` on a constructor parameter for a
by-type lookup.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
@SpringJUnitConfig(TestConfig.class)
class BeanOverrideTests {
private final CustomService customService;
BeanOverrideTests(@MockitoBean CustomService customService) { // <1>
this.customService = customService;
}
// tests...
}
----
<1> Replace the bean with type `CustomService` with a Mockito mock and inject it into
the constructor.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@SpringJUnitConfig(TestConfig::class)
class BeanOverrideTests(@MockitoBean val customService: CustomService) { // <1>
// tests...
}
----
<1> Replace the bean with type `CustomService` with a Mockito mock and inject it into
the constructor.
======
The following example shows how to use `@MockitoBean` on a constructor parameter for a
by-name lookup.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
@SpringJUnitConfig(TestConfig.class)
class BeanOverrideTests {
private final CustomService customService;
BeanOverrideTests(@MockitoBean("service") CustomService customService) { // <1>
this.customService = customService;
}
// tests...
}
----
<1> Replace the bean named `service` with a Mockito mock and inject it into the
constructor.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@SpringJUnitConfig(TestConfig::class)
class BeanOverrideTests(@MockitoBean("service") val customService: CustomService) { // <1>
// tests...
}
----
<1> Replace the bean named `service` with a Mockito mock and inject it into the
constructor.
======
The following `@SharedMocks` annotation registers two mocks by-type and one mock by-name.
[tabs]
@@ -385,6 +463,80 @@ Kotlin::
<1> Wrap the bean named `service` with a Mockito spy.
======
The following example shows how to use `@MockitoSpyBean` on a constructor parameter for
a by-type lookup.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
@SpringJUnitConfig(TestConfig.class)
class BeanOverrideTests {
private final CustomService customService;
BeanOverrideTests(@MockitoSpyBean CustomService customService) { // <1>
this.customService = customService;
}
// tests...
}
----
<1> Wrap the bean with type `CustomService` with a Mockito spy and inject it into the
constructor.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@SpringJUnitConfig(TestConfig::class)
class BeanOverrideTests(@MockitoSpyBean val customService: CustomService) { // <1>
// tests...
}
----
<1> Wrap the bean with type `CustomService` with a Mockito spy and inject it into the
constructor.
======
The following example shows how to use `@MockitoSpyBean` on a constructor parameter for
a by-name lookup.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
@SpringJUnitConfig(TestConfig.class)
class BeanOverrideTests {
private final CustomService customService;
BeanOverrideTests(@MockitoSpyBean("service") CustomService customService) { // <1>
this.customService = customService;
}
// tests...
}
----
<1> Wrap the bean named `service` with a Mockito spy and inject it into the constructor.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@SpringJUnitConfig(TestConfig::class)
class BeanOverrideTests(@MockitoSpyBean("service") val customService: CustomService) { // <1>
// tests...
}
----
<1> Wrap the bean named `service` with a Mockito spy and inject it into the constructor.
======
The following `@SharedSpies` annotation registers two spies by-type and one spy by-name.
[tabs]
@@ -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,11 @@ 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.
@@ -164,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
@@ -213,6 +216,16 @@ To verify JSON content with https://github.com/jayway/JsonPath[JSONPath]:
include-code::./JsonTests[tag=jsonPath,indent=0]
[[resttestclient.multipart]]
==== Multipart Content
When testing endpoints that return multipart responses, you can decode the body to a
`MultiValueMap<String, Part>` and assert individual parts using the `FormFieldPart`
and `FilePart` subtypes.
include-code::./MultipartTests[tag=multipart,indent=0]
[[resttestclient.assertj]]
=== AssertJ Integration
@@ -2,8 +2,9 @@
= Bean Overriding in Tests
Bean overriding in tests refers to the ability to override specific beans in the
`ApplicationContext` for a test class, by annotating the test class or one or more
non-static fields in the test class.
`ApplicationContext` for a test class, by annotating the test class, one or more
non-static fields in the test class, or one or more parameters in the constructor for the
test class.
NOTE: This feature is intended as a less risky alternative to the practice of registering
a bean via `@Bean` with the `DefaultListableBeanFactory`
@@ -42,9 +43,9 @@ The `spring-test` module registers implementations of the latter two
{spring-framework-code}/spring-test/src/main/resources/META-INF/spring.factories[`META-INF/spring.factories`
properties file].
The bean overriding infrastructure searches for annotations on test classes as well as
annotations on non-static fields in test classes that are meta-annotated with
`@BeanOverride` and instantiates the corresponding `BeanOverrideProcessor` which is
The bean overriding infrastructure searches for annotations on test classes, non-static
fields in test classes, and parameters in test class constructors that are meta-annotated
with `@BeanOverride`, and instantiates the corresponding `BeanOverrideProcessor` which is
responsible for creating an appropriate `BeanOverrideHandler`.
The internal `BeanOverrideBeanFactoryPostProcessor` then uses bean override handlers to
@@ -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
@@ -179,6 +179,10 @@ If a specific parameter in a constructor for a JUnit Jupiter test class is of ty
`ApplicationContext` (or a sub-type thereof) or is annotated or meta-annotated with
`@Autowired`, `@Qualifier`, or `@Value`, Spring injects the value for that specific
parameter with the corresponding bean or value from the test's `ApplicationContext`.
Similarly, if a specific parameter is annotated with `@MockitoBean` or `@MockitoSpyBean`,
Spring will inject a Mockito mock or spy, respectively &mdash; see
xref:testing/annotations/integration-spring/annotation-mockitobean.adoc[`@MockitoBean` and `@MockitoSpyBean`]
for details.
Spring can also be configured to autowire all arguments for a test class constructor if
the constructor is considered to be _autowirable_. A constructor is considered to be
@@ -499,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.
====
@@ -512,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
@@ -562,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.
====
@@ -639,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.
====
@@ -675,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):
@@ -580,8 +580,8 @@ Kotlin::
[[webtestclient-stream]]
==== Streaming Responses
To test potentially infinite streams such as `"text/event-stream"` or
`"application/x-ndjson"`, start by verifying the response status and headers, and then
To test potentially infinite streams such as `"text/event-stream"`,
`"application/jsonl"` or `"application/x-ndjson"`, start by verifying the response status and headers, and then
obtain a `FluxExchangeResult`:
[tabs]
@@ -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
@@ -352,9 +352,9 @@ outside. A proxy at the edge of trust must remove forwarded headers including bo
standard `"Forwarded"` and `"X-Forwarded"` headers, regardless of which one they use,
to protect applications which may check both.
When creating `ForwardedHeaderTransformer` you can specify whether to use the
standard `"Forwarded"` or `"X-Forwarded"` headers. A separate property on the transformer
lets you turn use of `"X-Forwarded-Prefix"` on and off.
When creating `ForwardedHeaderTransformer` you need to specify whether it should use the
standard `"Forwarded"` or `"X-Forwarded"` headers. If needed `"X-Forwarded-Prefix"`
must be enabled separately through a property on the transformer.
`ForwardedHeaderTransformer` can be configured in `removeOnly` mode, in which case it removes
forwarded headers from the request without using them.
@@ -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
@@ -490,8 +490,8 @@ The `JacksonJsonEncoder` works as follows:
* For a multi-value publisher with `application/json`, by default collect the values with
`Flux#collectToList()` and then serialize the resulting collection.
* For a multi-value publisher with a streaming media type such as
`application/x-ndjson` or `application/stream+x-jackson-smile`, encode, write, and
flush each value individually using a
`application/jsonl`, `application/x-ndjson` or `application/stream+x-jackson-smile`,
encode, write, and flush each value individually using a
https://en.wikipedia.org/wiki/JSON_streaming[line-delimited JSON] format. Other
streaming media types may be registered with the encoder.
* For SSE the `JacksonJsonEncoder` is invoked per event and the output is flushed to ensure
@@ -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
@@ -603,7 +603,7 @@ To configure all three in WebFlux, you'll need to supply a pre-configured instan
[.small]#xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-http-streaming[See equivalent in the Servlet stack]#
When streaming to the HTTP response (for example, `text/event-stream`,
`application/x-ndjson`), it is important to send data periodically, in order to
`application/jsonl`, `application/x-ndjson`), it is important to send data periodically, in order to
reliably detect a disconnected client sooner rather than later. Such a send could be a
comment-only, empty SSE event or any other "no-op" data that would effectively serve as
a heartbeat.
@@ -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:
@@ -66,9 +66,9 @@ outside. A proxy at the edge of trust must remove forwarded headers including bo
standard `"Forwarded"` and `"X-Forwarded"` headers, regardless of which one they use,
to protect applications which may check both.
When creating `ForwardedHeaderFilter` you can specify whether to use the
standard `"Forwarded"` or `"X-Forwarded"` headers. A separate property on the filter
lets you turn use of `"X-Forwarded-Prefix"` on and off.
When creating `ForwardedHeaderFilter` you need to specify whether it should use the
standard `"Forwarded"` or `"X-Forwarded"` headers. If needed `"X-Forwarded-Prefix"`
must be enabled separately through a property on the filter.
`ForwardedHeaderFilter` can be configured in `removeOnly` mode, in which case it removes
forwarded headers from the request without using them.
@@ -23,13 +23,17 @@ For all converters, a default media type is used, but you can override it by set
By default, this converter supports all text media types(`text/{asterisk}`) and writes with a `Content-Type` of `text/plain`.
| `FormHttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write form data from the HTTP request and response.
| An `HttpMessageConverter` implementation that can read and write URL encoded forms.
By default, this converter reads and writes the `application/x-www-form-urlencoded` media type.
Form data is read from and written into a `MultiValueMap<String, String>`.
The converter can also write (but not read) multipart data read from a `MultiValueMap<String, Object>`.
By default, `multipart/form-data` is supported.
Additional multipart subtypes can be supported for writing form data.
Consult the javadoc for `FormHttpMessageConverter` for further details.
`Map<String, String>` is also supported, but multiple values under the same key will be ignored.
| `MultipartHttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write multipart messages.
`MultiValueMap<String, Object>` can be written to multipart messages, converting each part independently using
the configured message converters. Multipart messages can be read into `MultiValueMap<String, Part>`, each value
being a `Part` or one of its subtypes (`FormFieldPart` and `FilePart`).
By default, `multipart/form-data` is supported. Additional multipart subtypes can be supported for writing form data.
| `ByteArrayHttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write byte arrays from the HTTP request and response.
@@ -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
@@ -423,8 +423,8 @@ Reactive return values are handled as follows:
* A single-value promise is adapted to, similar to using `DeferredResult`. Examples
include `CompletionStage` (JDK), `Mono` (Reactor), and `Single` (RxJava).
* A multi-value stream with a streaming media type (such as `application/x-ndjson`
or `text/event-stream`) is adapted to, similar to using `ResponseBodyEmitter` or
* A multi-value stream with a streaming media type (such as `application/jsonl`,
`application/x-ndjson` or `text/event-stream`) is adapted to, similar to using `ResponseBodyEmitter` or
`SseEmitter`. Examples include `Flux` (Reactor) or `Observable` (RxJava).
Applications can also return `Flux<ServerSentEvent>` or `Observable<ServerSentEvent>`.
* A multi-value stream with any other media type (such as `application/json`) is adapted
@@ -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

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