Compare commits

..
424 Commits
Author SHA1 Message Date
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
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
Brian Clozel 9e0d1c734e Upgrade to artifactory-deploy-action 0.0.5 2026-09-15 10:06:11 +02:00
Sam Brannen 3178df92bd Consistently use while (true) instead of for (;;) across the codebase 2026-09-14 18:15:02 +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 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
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
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 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
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
Brian Clozel bcd78a2ccc Add implementation note in DefaultAsyncServerResponse
See gh-37257
2026-09-10 16:59:06 +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 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
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 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
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
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
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 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 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
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 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
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
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
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
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
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 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
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
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
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
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
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
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 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
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 7da197de66 More updates for Data Binding doc restructuring
Closes gh-37228
2026-09-02 10:00:14 +01: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 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 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 8a64fe9c45 Fix accidental bypass of registerStoredProcedureParameter
Closes gh-37221
2026-08-31 17:30:56 +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
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 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 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
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
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 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
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 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
Brian Clozel 0e9a1d72f5 Merge commit 'v7.0.9~1' into 7.0.x 2026-08-20 18:17:01 +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 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
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 df10251539 Upgrade to Gradle 9.7.1
Closes gh-37160
2026-08-20 10:57:33 +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
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
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 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 94beae1779 Next development version (7.0.10-SNAPSHOT) 2026-08-18 09:24:40 +02:00
Brian Clozel a7b1b59cbd Upgrade to Reactor 2025.0.7
Closes gh-37103
2026-08-14 09:12:18 +02:00
Brian Clozel 996e3d3f18 Upgrade to Micrometer 1.16.7
Closes gh-37104
2026-08-14 09:12:18 +02:00
rstoyanchev 73f5ddddcd Refactor maxInMemory limit handling for async XML parsing
The limit was previously enforced in XmlEventDecoder, because it is
what parses incoming buffers. However, the actual caching is in
Jaxb2Decoder, which holds on to XML events, but has no good way to
estimate their size.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Fixes gh-37061
2026-08-14 09:11:50 +02:00
Brian Clozel dc7fc89aec Switch to INTERNAL-SNAPSHOT versions
See gh-37103
See gh-37104
2026-08-14 09:11:49 +02:00
Brian Clozel f2a7f13d13 Prepare 7.0.x-internal branch 2026-08-14 09:11:49 +02:00
Sam Brannen f8a2bdad87 Clarify Bean Overrides and Spring AOP Proxies documentation
A user reported confusion between two distinct uses of "wraps" in the
AOP proxy documentation for Bean Overrides: the sense in which a
Mockito spy wraps the original bean instance it was created from, and
the sense in which a Spring AOP proxy wraps the spy in the actual
object graph.

To address that, this commit adds two small diagrams to the general
"Bean Overrides and Spring AOP Proxies" section, illustrating, from a
caller's perspective, the shape of the bean for the
REPLACE/REPLACE_OR_CREATE strategy (no proxy at all) versus the WRAP
strategy (an AOP proxy still created, now wrapping the override
instance instead of the original bean). Both diagrams use the same
generic "override instance" label, since the section is not specific to
Mockito; a Mockito spy created by @⁠MockitoSpyBean is mentioned only as
an example.

The accompanying text is revised to reserve "wraps" for the AOP proxy
relationship and to explicitly call out that a Mockito spy's
relationship to its original bean instance is a separate concern from
AOP proxy nesting.

The @⁠MockitoSpyBean-specific strategy paragraph in the
@⁠MockitoBean/@⁠MockitoSpyBean documentation has also been revised
similarly, and now points to the new diagram.

See gh-37121
2026-08-13 18:57:57 +02:00
Juergen Hoeller 9aeda49273 Polishing 2026-08-12 00:08:59 +02:00
Juergen Hoeller abd323d428 Polishing 2026-08-11 23:10:15 +02:00
Juergen Hoeller 176bc2a133 Upgrade to Groovy 5.0.8, Jetty 12.1.12, Netty 4.2.17, Hibernate ORM 7.2.24, Checkstyle 13.10 2026-08-11 23:08:52 +02:00
Sam Brannen 8df51ad6cc Document AOP proxy semantics for Bean Overrides in tests
This commit documents how the Bean Override support in the TestContext
framework interacts with Spring AOP proxies created for annotations
such as @⁠Transactional, @⁠Cacheable, and @⁠Retryable.

The new "Bean Overrides and Spring AOP Proxies" section in the general
Bean Overriding documentation explains that overrides using the WRAP
strategy (such as @⁠MockitoSpyBean) end up as the target of any AOP
proxy subsequently created for the original bean; whereas, overrides
using the REPLACE or REPLACE_OR_CREATE strategy (such as @⁠TestBean,
@⁠MockitoBean) bypass the container's bean post-processing entirely and
therefore carry no AOP advice at all.

The new "@⁠MockitoSpyBean and Spring AOP Proxies" section documents the
resulting stubbing and verification semantics. Verification via
Mockito's verify() API works transparently regardless of whether it is
invoked on the proxy or on the spy. Stubbing via doReturn(...)/doThrow(...)
is safe for stateless advice such as @⁠Retryable, but can silently
corrupt the spy's configured answers for stateful or memoizing advice
such as @⁠Cacheable, since the invocation used to declare a stub is
intercepted by Mockito before it reaches the spy and returns an empty
value that such advice may then cache.
AopTestUtils.getUltimateTargetObject(...) is documented as the way to
stub directly against the spy in that case.

The same section also documents how to disable the AOP advice for a
test altogether while leaving it in place in production code – for
example, binding a @⁠Retryable attribute to a property placeholder
overridden via @⁠TestPropertySource, or replacing the CacheManager with
a NoOpCacheManager via @⁠TestBean.

Brief cross-referencing notes have also been added to the @⁠TestBean
documentation and to the existing AopTestUtils description in the
"General Testing Utilities" section, to avoid duplicating the
explanation across pages.

In addition, the Javadoc for @⁠MockitoSpyBean now contains a concise
WARNING summarizing these AOP proxy implications and linking to the new
reference documentation section for details.

Closes gh-37121
2026-08-09 17:46:28 +03:00
Sam Brannen 957df686c4 Upgrade to Gradle 9.7
Closes gh-36952
2026-08-07 10:43:29 +03:00
rstoyanchev 11bb7b54e5 Ignore an empty port
Closes gh-37117
2026-08-07 10:31:29 +03:00
rstoyanchev 70ca103b22 Polishing in UriComponentsTests
See gh-37117
2026-08-07 10:25:12 +03:00
junhyeong9812 7f1966f5f5 Reset TwoByteMatcher partial match on mismatching byte
DataBufferUtils.TwoByteMatcher inherited AbstractNestedMatcher.match(byte)
without providing the mismatch fallback that its siblings implement
(KnuthMorrisPrattMatcher backtracks via its suffix-prefix table, and
SingleByteMatcher is stateless). As a result, once the first delimiter
byte had matched, the match counter stayed at 1 across any number of
intervening non-matching bytes, so a later occurrence of the second
delimiter byte falsely completed the match.

For a two-byte delimiter such as \r\n this made the matcher report a
match across non-contiguous bytes. CompositeMatcher prefers the longest
delimiter that matches at a position, so the false \r\n match was chosen
over a real single \n, causing StringDecoder to strip two bytes and drop
the character preceding a lone \n whenever a line contained a stray \r.

TwoByteMatcher now overrides match(byte) to reset the counter to 0 when
the incoming byte is not the expected next delimiter byte before
delegating to super.match(), mirroring KnuthMorrisPrattMatcher. A
genuine contiguous delimiter is unaffected.

Signed-off-by: junhyeong9812 <pickjog@gmail.com>
2026-08-05 10:36:05 +02:00
Boris Perović b556766e1a Avoid retaining class files in annotation metadata
ClassFileAnnotationDelegate passed the raw java.lang.classfile
Annotation to MergedAnnotation.of() as the annotation source. That
annotation holds a Utf8Entry, so retaining the metadata of an
annotated class also retained its class file byte[], the parsed
constant pool, and the class model.

Store the declaring class name instead, as the ASM variant does.

See gh-37111

Signed-off-by: Boris Perović <boris.perovic@sysdig.com>
2026-08-05 10:07:44 +02:00
Brian Clozel 0c281fd1ce Fix build SNAPSHOT workflow
This commit ensures that workflow dispatches to docs build only happen
on OSS branches.
This also upgrades the verification project to the latest version.

See gh-37097
2026-08-03 11:36:22 +02:00
Sam Brannen 63f0894621 Improve wording
See gh-37102
2026-08-03 12:34:54 +03:00
Sam Brannen 0c966029e2 Document object design guidelines for SpEL expressions
Property accessors resolved via SpEL's ReflectivePropertyAccessor and
DataBindingPropertyAccessor may be JavaBean-style accessors or plain
accessor methods used to support data classes such as Java records and
Kotlin data classes. However, neither accessor can determine, via
reflection, whether such a method is a side-effect-free read or an
action that happens to return a value. For example, File.delete() is a
public method that returns a boolean and therefore looks like a
plain "property".

To better inform users, this commit updates the Javadoc for
ReflectivePropertyAccessor, DataBindingPropertyAccessor, and
SimpleEvaluationContext (as well as in the SpEL reference
documentation) to clarify that restricting a SimpleEvaluationContext to
read-only data binding governs only whether assignment to a property is
permitted and does not guarantee that reading a property is free of
side effects. The reference documentation's "Security Considerations"
section now also defines what makes a method "accessor-shaped", with
concrete examples of safe versus side-effecting methods that share that
shape (for example, File.delete(), Queue.poll(), and
AtomicInteger.incrementAndGet()).

Building on that clarification, this commit introduces a new "Object
Design" section to the SpEL reference documentation, analogous to the
"Model Design" guidance for web data binding. This new section
recommends that any object reachable from an untrusted SpEL expression
(not only the root object) be a purpose-built, immutable type with a
deliberately limited surface area, and that its accessor-shaped methods
be audited for unsafe side effects. The new section also notes that
reachability is transitive through both property navigation and
indexing (for example, rootObject.child.grandchild or
rootObject.items[0]).

In any case, it remains the responsibility of the code that exposes a
root object or other reachable object to an expression from an
untrusted source to ensure that none of its accessor-shaped methods
perform an unsafe action.

Closes gh-37102
2026-08-03 11:35:05 +03:00
Brian Clozel 05619b7450 Fix docs build trigger
See gh-37097
2026-07-31 18:51:39 +02:00
Brian Clozel f3ba3c9e1f Fix URL fetching from environment in Gradle build plugin
See gh-37097
2026-07-31 18:25:11 +02:00
Brian Clozel 89e62e7e31 Add release train infrastructure
Closes gh-37097
2026-07-31 18:08:03 +02:00
Brian Clozel e4d5ec9c0d Update GitHub workflows and build infrastructure
See gh-37097
2026-07-31 18:05:15 +02:00
Juergen Hoeller f5564e7e31 Consistently use default constants within builder
See gh-36983
2026-07-31 16:27:18 +02:00
Juergen Hoeller 6d04ea9e84 Check existing database transaction against UnitOfWork
See gh-37085
2026-07-31 16:24:09 +02:00
Sam Brannen 0f5bd82c5d Centralize SpEL security documentation in the reference manual
The Javadoc for EvaluationContext, StandardEvaluationContext, and
SimpleEvaluationContext previously repeated the same detailed
explanation of trusted sources and best-effort restrictions in four
places, making it hard to maintain and to digest.

This commit condenses each class-level warning to a succinct summary
that links to the new "Security Considerations" section of the SpEL
reference documentation, which remains the single, detailed source of
truth introduced in 9b42a40a2a.

See gh-36997
2026-07-30 16:23:44 +03:00
Sam Brannen a894818c9e Document lifecycle and reuse contract for SpEL expressions and contexts
This commit adds a "Lifecycle and Reuse" section to the SpEL reference
documentation, immediately following the security considerations
introduced for gh-36997, explaining that AST nodes within a parsed
Expression may cache resolved PropertyAccessor, IndexAccessor,
MethodExecutor, and ConstructorExecutor instances for performance.

We also now document that reusing a parsed Expression across
EvaluationContext instances of the same type and with equivalent
configuration is supported (even if atypical), including when accessors
or resolvers registered with a context change between evaluations, but
that reusing a parsed Expression across contexts with different
security implications (for example, first against a
StandardEvaluationContext and later against a SimpleEvaluationContext)
is not supported, since cached state from a more permissive evaluation
may be reused during a more restrictive one.

The Javadoc for Expression, SpelExpression, EvaluationContext,
StandardEvaluationContext, SimpleEvaluationContext, PropertyAccessor,
IndexAccessor, MethodExecutor, and ConstructorExecutor has also been
updated to make these contracts discoverable via the API as well.

Closes gh-36968
2026-07-29 22:05:15 +03:00
Sam Brannen 9b42a40a2a Document security implications of evaluating untrusted SpEL expressions
This commit clarifies in the Javadoc for EvaluationContext,
StandardEvaluationContext, and SimpleEvaluationContext (as well as in
the SpEL reference documentation) that StandardEvaluationContext must
never be used to evaluate expressions from an untrusted source, and
that SimpleEvaluationContext's restricted language and feature subset
is only a best-effort measure. The updated documentation also defines a
"trusted" source as a developer or administrator of the application and
points out that it is the responsibility of the code that configures an
EvaluationContext to ensure that no object reachable via the context
exposes dangerous operations.

Closes gh-36997
2026-07-29 21:21:11 +03:00
rstoyanchev 719311f09b Correct since tags
See gh-37090
2026-07-29 15:18:22 +03:00
Juergen Hoeller 28a78170b5 Upgrade to Tomcat 11.0.24, Jetty 12.1.11, Hibernate ORM 7.2.23, Hibernate Validator 9.1.3, Checkstyle 13.9 2026-07-29 12:14:26 +02:00
Juergen Hoeller 8fa7d88a0c Avoid getConnection lock for existing database transaction
Closes gh-37085
2026-07-29 11:30:17 +02:00
rstoyanchev b1d025d2c6 Update documentation on forwarded headers
See gh-37090
2026-07-27 12:46:43 +03:00
rstoyanchev 181a5d3403 Allow 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 to control support for
X-Forwarded-Prefix.

Closes gh-37090
2026-07-27 12:46:43 +03:00
Sam Brannen ae4214aa95 Do not reuse cached PropertyAccessor in Indexer without checking EvaluationContext
Prior to this commit, the SpEL Indexer's PropertyAccessorValueRef could
reuse a cached PropertyAccessor for reads and writes even after that
accessor had been removed from the current EvaluationContext, leading
to stale property access if the EvaluationContext changes between
evaluations of the same expression.

This commit aligns PropertyAccessorValueRef with the analogous logic
in PropertyOrFieldReference and Indexer's IndexAccessorValueRef by
verifying that the cached PropertyAccessor is still registered in the
current EvaluationContext before reusing it in getValue() and
setValue().

Closes gh-36986
2026-07-26 10:56:44 +03:00
Sam Brannen 4f086322d0 Do not reuse cached ConstructorExecutor without ConstructorResolvers
Prior to this commit, a SpEL ConstructorReference could reuse a cached
ConstructorExecutor even when the current EvaluationContext no longer
had any registered ConstructorResolvers, leading to inconsistent
behavior if the EvaluationContext changes between evaluations of the
same expression.

This commit aligns ConstructorReference with the analogous logic in
PropertyOrFieldReference by discarding the cached ConstructorExecutor
whenever there are no ConstructorResolvers registered in the current
EvaluationContext, ensuring that constructor resolution consistently
fails with a CONSTRUCTOR_NOT_FOUND exception in that scenario.

Closes gh-36985
2026-07-25 16:05:23 +03:00
Alexis SEGURA e255ccca7d Update Javadoc for active profile ordering
Following gh-26004, the registration order of active profiles in
@⁠ActiveProfiles is preserved; however, the Javadoc in
MergedContextConfiguration and WebMergedContextConfiguration still
described the old sorting behavior, which is misleading since profile
order is part of the context cache key (equals/hashCode).

This commit updates the Javadoc to document the status quo.

Closes gh-36950

Signed-off-by: Alexis SEGURA <alex.segura06@gmail.com>
2026-07-25 11:04:11 +03:00
Brian Clozel b5de644cbb Do not deploy org.springframework:framework-docs on Central
Fixes gh-36879
2026-07-24 15:15:22 +02:00
Brian Clozel 7de2b24d81 Fix primitive array annotation attributes on Java 24 class reading
Prior to this commit, `ClassFileAnnotationDelegate#parseArrayValue`
would only consider `int[]`, `double[]` and `long[]` array
annotation attributes; other primitive array types like `byte[]`
would use a generic path that would use boxed types.

This commit ensures that a comprehensive pass is made for all
primitive typed arrays. Because the `AnnotationValue` hierarchy
is sealed, we can now maje sure that the implementation is
exhaustive.

Closes gh-37083
2026-07-22 10:50:07 +02:00
Sam Brannen b90624472e Polish Javadoc for ProtobufDecoder 2026-07-21 11:57:20 +03:00
BAE JAE HYEON 1600e2479b Fix Javadoc in ProtobufDecoder's MessageSizeReader API
Previously, the @⁠return tag in MessageSizeReader.readMessageSize() was
followed by a redundant "return", which rendered as "Returns: return
the message size..." in the published API documentation.

Closes gh-37079

Signed-off-by: BAE JAE HYEON <roblery128@gmail.com>
2026-07-21 11:53:24 +03:00
Manu Sridharan 233725c8f5 Add @⁠Nullable annotations when treating Map.remove() as returning @⁠Nullable
Closes gh-37067

Signed-off-by: Manu Sridharan <msridhar@gmail.com>
2026-07-20 11:02:55 +03:00
Brian Clozel 8fb2f72282 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

Closes gh-37062
2026-07-16 19:21:16 +02:00
samlightfoot a2feb9ffe6 Release Jackson BufferRecycler to its pool in encoders
Both encoders acquire a pooled BufferRecycler via
factory._getBufferRecycler() but never return it. Jackson 3 changed the
default pool from a ThreadLocal to a per-factory ConcurrentDequePool that
only refills on an explicit releaseToPool(), so the gap leaves the pool
empty and every encode allocates a fresh recycler and buffers.

Hold the recycler in a local and release it once the generator and byte
builder are done: in encodeValue's finally, and in the streaming path's
doAfterTerminate after generator.close() and byteBuilder.release().
releaseToPool() is idempotent, so sharing the recycler with the generator
cannot double-release.

Closes gh-37059

Signed-off-by: samlightfoot <samueldlightfoot@gmail.com>
2026-07-16 19:00:14 +02:00
Dmitry Sulman 27aa9e46c5 Add awaitEntityWithRetry extension to WebClient.ResponseSpec
See #36771
Closes #36808

Signed-off-by: Dmitry Sulman <dmitry.sulman@gmail.com>
2026-07-16 17:25:29 +02:00
Juergen Hoeller 56d706d591 Skip concurrency limit tests when common pool parallelism is too low 2026-07-16 10:17:29 +02:00
junhyeong9812 bbfe6a0473 Make immediate-cancel task termination test deterministic
The taskTerminationTimeoutWithImmediateCancel test submitted a task and
immediately closed the executor, then asserted that the future was
cancelled. The cancellation flag is set by close() on the calling thread,
while it is checked at the start of the task on a separate worker thread.
With no ordering guarantee between the two, a quickly scheduled worker
could pass the cancellation check before close() set the flag, complete
the trivial task normally, and leave the future uncancelled, making the
test fail intermittently under load.

Override doExecute to capture the task-tracking wrapper instead of
running it on a background thread, then run it on the test thread after
close() has set the cancellation flag. This exercises the same
cancellation path deterministically, with no reliance on thread
scheduling.

Signed-off-by: junhyeong9812 <pickjog@gmail.com>
2026-07-10 20:30:19 +02:00
Sébastien Deleuze 13a43e76cb Fix Javadoc error in RetryPolicy
See gh-36983
2026-07-08 20:46:17 +02:00
Sébastien Deleuze 16e82cd693 Refine JdbcTemplate nullability contribution
Closes gh-37012
2026-07-08 16:52:17 +02:00
Chris 516a2ca511 Make batchArgs contents nullable in batchUpdate
Updated batchUpdate method signatures to allow nullable Object arrays.

See gh-37012

Signed-off-by: Chris <christian@vonrueti.ch>
2026-07-08 15:26:55 +02:00
Yanming Zhou 9726c7ed5f Let composite/filtered collections accept null elements
Closes gh-36923

Signed-off-by: Yanming Zhou <zhouyanming@gmail.com>
2026-07-08 15:17:17 +02:00
Sébastien Deleuze 7e0818f4e9 Align domainToAscii with current WhatWG spec
The WhatWG URL Standard changed its "domain to ASCII" algorithm when
beStrict is false and the domain is an ASCII string, it now returns
the domain lowercased regardless of Unicode ToASCII's outcome, for web
compatibility. Invalid or ambiguous "xn--" (ACE) labels are no longer
rejected or validated; they are lowercased and accepted, matching
browsers and the web-platform-tests URL cases.

This supersedes the earlier spec revision that only lowercased ASCII
domains whose labels did not start with "xn--". Drop the now-obsolete
"xn--" label detection (which was dead code anyway due to a typo) and
unconditionally lowercase ASCII domains.

Closes gh-37018
2026-07-08 15:11:42 +02:00
Sébastien Deleuze ed13afa0d4 Ensure consistent ButtonTag value attribute processing
Closes gh-37017
2026-07-08 15:08:51 +02:00
Juergen Hoeller c08e5e9c1c Upgrade to Log4J 2.26.1, Groovy 5.0.7, Tomcat 11.0.23, Jetty 12.1.10, EclipseLink 5.0.1, Hibernate ORM 7.2.22, Hibernate Validator 9.1.2, Caffeine 3.2.4, Protobuf 4.35.1, Checkstyle 13.7 2026-07-06 12:30:41 +02:00
Juergen Hoeller 97e9ddb2d7 Add constant for default timeout value
Closes gh-36983
2026-07-06 11:57:27 +02:00
Sam Brannen f0e69a702c Ensure SpEL's InlineList is immutable in compiled mode
Prior to this commit, SpEL's InlineList was cached as a mutable list in
compiled mode (i.e., in a static field in the generated byte code).

To address that, this commit modifies InlineList's generateClinitCode()
method so that it wraps both top-level and nested inline lists using
Collections.unmodifiableList(), analogous to what we already do in
createList().

Closes gh-37001
2026-07-06 10:52:54 +02:00
junhyeong9812 d1470bbb25 Register native configuration file when only lambda hints are present
Prior to this commit, FileNativeConfigurationWriter did not write reachability-metadata.json
when a RuntimeHints instance contained only lambda hints, because
NativeConfigurationWriter.hasAnyHint() omitted
ReflectionHints.lambdaHints() from its checks. As a result, lambda
metadata emitted by RuntimeHintsWriter was silently dropped.

This commit addresses that by including lambda hints in hasAnyHint() so
the configuration file is written whenever lambda hints are present.

Closes gh-36989

Signed-off-by: junhyeong9812 <pickjog@gmail.com>
2026-07-02 12:06:24 +02:00
Sébastien Deleuze 347f23a73f Upgrade nullability plugin to 0.0.14
Nullaway 0.13.7 is now the default.

See gh-36850
2026-07-01 15:39:44 +02:00
Manu Sridharan 99b3e205d1 Update to NullAway 0.13.6
Add missing nullness annotation in
UriComponents.VarArgsTemplateVariables to fix new warnings.

Closes gh-36850

Signed-off-by: Manu Sridharan <msridhar@gmail.com>
2026-07-01 15:39:30 +02:00
Sam Brannen 78f05d8f8e Address deprecation warnings
This commit addresses warnings across the code base related to:

- internal and public deprecations in Spring Framework
- deprecated Locale constructors
- deprecated URL constructors
- deprecated Thread#getId method
2026-06-27 18:08:53 +02:00
Sam Brannen 996b337f37 Upgrade to io.spring.develocity.conventions 0.0.25 2026-06-27 17:09:30 +02:00
Sam Brannen 66ffc04fe5 Avoid Gradle deprecation warnings
After the upgrade to Gradle 9.6.0/9.6.1, the Gradle build started
emitting warnings due to use of deprecated APIs.

For example, Project.getProperties() is now annotated as @⁠Deprecated
in Gradle 9.6 and will be removed in Gradle 10.0.

To avoid the warnings, this commit modifies:

- TestConventions to use `project.findProperty(...)` instead of
  `project.getProperties().get(...)`

- framework-api.gradle to use `rootProject.ext.moduleProjects` instead
  of simply `moduleProjects`

- framework-api.gradle and framework-bom.gradle to use
  `project(<projectX>)` instead of simply `<projectX>`

- ide.gradle so that it no longer uses the deprecated `javaRuntimeName`

See gh-36952
2026-06-27 17:02:40 +02:00
Sam Brannen ade96d275d Upgrade to Gradle 9.6.1
Closes gh-36952
2026-06-27 16:16:59 +02:00
Sam Brannen 072fa3f43d Polishing 2026-06-27 16:11:42 +02:00
Sam Brannen 78dcdab3fc Polishing
See gh-36972
2026-06-27 15:19:42 +02:00
junhyeong9812 872b1addeb Write native configuration files as UTF-8
Prior to this commit, FileNativeConfigurationWriter wrote native-image
configuration files using a plain FileWriter, which encodes with the
JVM platform default charset. On a non-UTF-8 platform (for example a
Windows JVM, where the default charset is not UTF-8 prior to JDK 18)
non-ASCII characters in resource patterns or bundle names were written
with the wrong encoding, while GraalVM expects the configuration files
to be UTF-8.

This commit specifies StandardCharsets.UTF_8 explicitly so the files
are always written as UTF-8, consistent with the UTF-8 usage already
present in the aot.generate package.

Closes gh-36972

Signed-off-by: junhyeong9812 <pickjog@gmail.com>
2026-06-27 15:10:30 +02:00
Juergen Hoeller 98d552f15f Add javadoc note on trusted broker setups for Spring JMS 2026-06-26 18:21:00 +02:00
Sam Brannen 4d6e88dc98 Fix off-by-one error in MimeTypeUtils.parseMimeType()
Due to changes made in commit 41cd6879bd, MimeTypeUtils now raises a
StringIndexOutOfBoundsException instead of an InvalidMimeTypeException
when parsing certain invalid mime types -- for example, for a value
wrapped in double quotes which does not contain a ";" character.

To address that minor regression, this commit replaces
`mimeType.charAt(nextIndex - 1) != '\\'` with
`(nextIndex == 0 || mimeType.charAt(nextIndex - 1) != '\\')` to avoid
invoking `String#charAt` with a negative value.

See gh-36730
Closes gh-36971
2026-06-26 17:40:53 +02:00
rstoyanchev 0044c4c8a3 Improve remoteAddress check in TransportHandlingSockJsService
Closes gh-36904
2026-06-25 16:10:34 +01:00
Sam Brannen b00f691655 Preserve parameter order in DefaultServerRequest's ServletParametersMap
Prior to this commit, DefaultServerRequest's ServletParametersMap lost
the original parameter order when entrySet() was invoked.

To address that, this commit revises ServletParametersMap.entrySet() so
that it stores the results in a LinkedHashSet, thereby retaining the
original order.

Closes gh-36966
2026-06-25 13:57:34 +02:00
rstoyanchev 6ac642e301 Remove closeLock from ConcurrentWebSocketSessionDecorator#checkSessionLimits
Closes gh-36909
2026-06-25 12:50:32 +01:00
rstoyanchev 11bdb43aad Polishing in ConcurrentWebSocketSessionDecorator
See gh-36909
2026-06-25 12:50:32 +01:00
Sam Brannen 4074155d76 Polish contribution
See gh-36948
2026-06-25 13:23:49 +02:00
junhyeong9812 1277279527 Ignore DOCTYPE inside a multi-line comment body
XmlValidationModeDetector peeks at the start of an XML document to
choose between DTD- and XSD-based validation, skipping any DOCTYPE that
appears inside an XML comment.

Prior to this commit, consumeCommentTokens() short-circuited a line
with no start or end comment marker by returning it unchanged, even
while already inside a multi-line comment. Such a body line was then
treated as content, so a literal "DOCTYPE" word in the comment body
caused an XSD document to be misdetected as DTD-based.

This commit honors the "in comment" parse state in that early return so
a comment body line is treated as empty content, completing the fix for
gh-27915 which only covered comment markers on the same line.

Closes gh-36948

Signed-off-by: junhyeong9812 <pickjog@gmail.com>
2026-06-25 13:07:57 +02:00
Juergen Hoeller d0331a049a Refine various javadoc notes 2026-06-22 21:52:57 +02:00
Juergen Hoeller 7de9c8d58a Consistently handle input format mismatch 2026-06-22 21:52:18 +02:00
Juergen Hoeller bae022a0fc Perform nextKey within synchronization
Closes gh-36959
2026-06-22 21:51:40 +02:00
Juergen Hoeller 325c3e1ec7 Add support for custom ObjectInputFilter
Closes gh-36958
2026-06-22 21:51:11 +02:00
Juergen Hoeller 67e5ea9509 Revise resource bundle caching for common locales
Closes gh-36957
2026-06-22 21:50:56 +02:00
rstoyanchev 41b8f13e91 Fix checkStyleNoHttp errors
See gh-36893
2026-06-22 14:08:25 +01:00
rstoyanchev 167afd91be Fix delegation in LifecyclePrintWriter
Closes gh-36885
2026-06-22 13:12:50 +01:00
rstoyanchev 12f9a5c2a5 Apply ResourceHandlerUtils to Groovy markup templates
Closes gh-36902
2026-06-22 11:57:12 +01:00
rstoyanchev 1a89363a5c Restore host header initialization in StompBrokerRelayMessageHandler
Closes gh-36907
2026-06-22 11:35:38 +01:00
rstoyanchev 34a307d4a6 Improve requestUri handling in ReactorUriHelper
Closes gh-36893
2026-06-22 11:12:13 +01:00
rstoyanchev 0e4842062b Polishing in ReactorUriHelper
See gh-36893
2026-06-22 11:06:43 +01:00
rstoyanchev 76089feeb4 Refine error handling in JettyWebSocketSession
Closes gh-36891
2026-06-22 10:42:51 +01:00
rstoyanchev ad5bd67b31 Improve ambiguous preflight checks in AbstractHandlerMethodMapping
Closes gh-36903
2026-06-22 10:42:51 +01:00
rstoyanchev 0ee636af7f Improve Javadoc of UrlFilenameViewController
Closes gh-36906
2026-06-22 10:42:51 +01:00
rstoyanchev c64f2ee104 Disable assignment in DefaultSubscriptionRegistry
Closes gh-36924
2026-06-22 10:42:51 +01:00
rstoyanchev 5059bbd58e Improve parameter-based content negotiation
Closes gh-36925
2026-06-22 10:42:51 +01:00
rstoyanchev cc0ca1b6a5 Polishing in MappingContentNegotiationStrategy hierarchy and tests
See gh-36925
2026-06-22 10:42:51 +01:00
Sam Brannen ee81785afc Upgrade to Gradle 9.6
Closes gh-36952
2026-06-19 16:31:38 +02:00
Sam Brannen 846a6a8f7c Document behavior for 0 delay combined with jitter
Closes gh-36946
2026-06-17 12:41:03 +02:00
Sam Brannen 0d706f8da6 Polish contribution
See gh-36932
2026-06-17 12:38:31 +02:00
junhyeong9812 924849f55b Avoid divide-by-zero in ExponentialBackOff jitter
When an ExponentialBackOff is configured with an initialInterval of 0
and a positive jitter, the first nextBackOff() evaluated (jitter *
(interval / initialInterval)) performs integer division by zero
and throws an ArithmeticException.

Both initialInterval = 0 and jitter > 0 are individually accepted
configurations -- with jitter = 0, an initialInterval of 0 already
yields a delay of 0 -- so the combination should not throw.

This commit addresses that by guarding the division so that no jitter
scaling is applied when initialInterval is 0, leaving the behavior for
positive intervals unchanged.

Closes gh-36932

Signed-off-by: junhyeong9812 <pickjog@gmail.com>
2026-06-17 12:09:30 +02:00
gianmarco 136f78ebd0 Fix Javadoc errors in AotContextLoaderTests
Closes gh-36937

Signed-off-by: gianmarco <gianmarco.schifone02@gmail.com>
2026-06-17 11:58:20 +02:00
Yanming Zhou 0fc724b348 Make inner classes in tests static where feasible
Closes gh-36939

Signed-off-by: Yanming Zhou <zhouyanming@gmail.com>
2026-06-17 11:49:12 +02:00
Sam Brannen 292959e2ca Improve nullability for getSession(*) in MockHttpServletRequest
Closes gh-36926
2026-06-15 15:35:17 +02:00
Sam Brannen 5383520388 Sync changes in MockHttpServletRequest to spring-web test fixture 2026-06-15 15:34:32 +02:00
Juergen Hoeller 0b5a9ea30a Adapt query proxy if necessary (for compatibility with Hibernate 8.0)
Closes gh-36878
2026-06-09 22:40:22 +02:00
Brian Clozel 298e1db625 Skip web fonts in module javadocs
Prior to this commit, module javadoc packages like
"spring-core-7.0.8-javadoc.jar" would contain 4MB of web fonts in the
`resource-files/fonts/` folder. This increases significantly (often
doubles) the size of the javadoc JAR for little value.

With this commit, Web fonts are packaged with the aggregated Javadocs
for the entire Spring Framework project, but are skipped for inidividual
modules.

Closes gh-36889
2026-06-08 22:15:27 +02:00
Brian Clozel 3f7f75abdb Next development version (v7.0.9-SNAPSHOT) 2026-06-08 19:50:42 +02:00
Sam Brannen 2c18c33ce0 Track operations during SpEL expression evaluation
This commit introduces support for tracking operations during SpEL
expression evaluation. If the maximum number of operations is exceeded,
a SpelEvaluationException is thrown.

The limit can be configured either on a per-use-case basis via
SpelParserConfiguration supplied to the SpelExpressionParser or
globally as a JVM system property or Spring property named
`spring.expression.maxOperations`.

Closes gh-36801
2026-06-08 15:13:44 +02:00
Sam Brannen 83667f808c Ensure getters have non-void return types in SpEL
Closes gh-36800
2026-06-08 15:13:44 +02:00
Sam Brannen 7a8917b137 Improve additional error messages in SpEL
This commit picks up where 987d6cca6d left off.

See gh-36756
2026-06-08 15:13:44 +02:00
Sam Brannen 7baa86536f Further improve pattern caching in SpEL
See gh-36755
2026-06-08 15:13:44 +02:00
Sam Brannen 12b44f2545 Avoid too many character access attempts in AntPathMatcher
Closes gh-36799
2026-06-08 15:13:44 +02:00
Sébastien Deleuze e8f10244e3 Ensure consistent JSP tag attribute processing
Closes gh-36797
2026-06-08 15:13:44 +02:00
Sébastien Deleuze a1826b725c Refine JavaScriptUtils#javaScriptEscape
Closes gh-36795
2026-06-08 15:13:43 +02:00
Sébastien Deleuze 7add5243b9 Prevent special prefixes in default view name resolution
This commit updates the default view name generation logic in
both Spring WebMVC and Spring WebFlux to prevent "redirect:"
and "forward:" (for MVC) prefixes from the incoming request path.

Closes gh-36793
2026-06-08 15:13:43 +02:00
Sébastien Deleuze 9bec52b1ec Add trusted packages to MappingJackson2MessageConverter
This commit introduces trusted packages, specified via the related
setter for untrusted use cases. It allows explicit configuration
of which Java packages are allowed to be deserialized.

Closes gh-36791
2026-06-08 15:13:43 +02:00
Sébastien Deleuze fe68e337b2 Add trusted packages to JacksonJsonMessageConverter
This commit introduces trusted packages, specified via the related
setter for untrusted use cases. It allows explicit configuration
of which Java packages are allowed to be deserialized.

See gh-36791
2026-06-08 15:13:43 +02:00
rstoyanchev 322eef2942 RfcParser rejects invalid IPv6 host
Fix gh-36787
2026-06-08 15:13:43 +02:00
Brian Clozel ba0a9b8923 Upgrade to Reactor 2025.0.6
Closes gh-36884
2026-06-08 14:49:19 +02:00
Brian Clozel 8a192eab1f Upgrade to Micrometer 1.16.6
Closes gh-36883
2026-06-08 10:40:01 +02:00
rstoyanchev 45812cb9d0 Prefer Jackson XML codecs when present
Closes gh-36776
2026-06-05 14:36:05 +01:00
rstoyanchev 95bd3f7c67 Exclude Jackson XML from String encoding/decoding
Closes gh-36775
2026-06-05 14:36:05 +01:00
Sam Brannen 6ca66afc7b Polish contribution
See gh-36871
2026-06-05 15:13:25 +02:00
zhaomeng 1b32d8d41d Include zone ID in CronTrigger's equals() and hashCode() implementations
CronTrigger carries an optional ZoneId since 5.3 that affects
nextExecution; however, prior to this commit, equals() and hashCode()
only considered the cron expression.

This commit ensures that CronTrigger instances with the same cron
expression but different time zones are no longer considered equal.

Closes gh-36871

Signed-off-by: zhaomeng <zhaomeng1.vendor@sensetime.com>
2026-06-05 15:09:14 +02:00
Sam Brannen b4a378186f Fix additional links to Selenium documentation
See gh-36875
2026-06-05 14:56:51 +02:00
leestana01 220fcaa1e3 Fix broken links to Selenium documentation
The links to docs.seleniumhq.org no longer resolve, since the host was
retired.

This commit changes those links to use the current Selenium
documentation at selenium.dev.

Closes gh-36875

Signed-off-by: leestana01 <leestana01@naver.com>
2026-06-05 14:52:15 +02:00
Brian Clozel 80534f9df6 Polishing contribution
This commit adds further fixes in the same area, since there were
similar bugs in the WriteCompletionHandler:
* databuffers were not always emitted when fully read in the onNext hook
* on completion, the iterator was closed too early, before it was fully
  read
* on completion, writing the next bytebuffers from the iterator would
  always reuse the first one and not update the attachment

Closes gh-36714
2026-06-04 12:18:01 +02:00
KimDaehyeon d8bc54d2e7 Fix data loss in DataBufferUtils synchronous write
Prior to this commit, WritableByteChannelSubscriber.hookOnNext() called
iterator.next() exactly once. If a DataBuffer consisted of multiple NIO
ByteBuffers (e.g., NettyDataBuffer wrapping a CompositeByteBuf), only
the first buffer was written to the channel, and the remaining buffers
were silently ignored and lost.

This commit adds the missing while (iterator.hasNext()) outer loop to
ensure all fragmented buffers exposed by the iterator are completely
and safely written to the synchronous channel.

See gh-36714

Signed-off-by: KimDaehyeon <daehyeon3351@gmail.com>
2026-06-04 12:17:57 +02:00
Brian Clozel 6467fca05b Polishing contribution
This fixes a potential regression introduced by the previous commit.
Because the current value was not updated after the temporal was rolled
forward, there were new cases where entire days would be skipped.

Closes gh-36865
2026-06-04 10:22:23 +02:00
arnoandCursor cddc671a8c Fix CronExpression day skip on midnight DST gap
After rollForward, BitsCronField always searched for the next
matching bit from zero. When daylight saving creates a gap at
the start of a period (e.g. Africa/Cairo), the temporal lands on
a non-zero field value and matching from zero could advance an
entire period too far, skipping the calendar day.

Search from the actual field value in the new period instead,
falling back to zero only when no bit matches in that period.

See gh-36865

Signed-off-by: arno <me@zmovo.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 10:22:17 +02:00
Juergen Hoeller 2e653246b5 Fix concurrency issue against shared cookie field in setLocaleContext
Closes gh-36869
2026-06-03 23:03:39 +02:00
Brian Clozel 86a68a77c4 Support multi-line comments in Server Sent Events
Prior to this commit, comments sent with Server Sent Events could break
the wire format when sent over the network when comments contained line
breaks.
While comments are mainly used for sending keepalive messages, they can
also be used for sending debug data. This commit ensures that line
breaks are properly handled in comments.

Fixes gh-36866
2026-06-03 11:13:32 +02:00
Juergen Hoeller fe27ad24d5 Upgrade to Netty 4.2.15 and Hibernate ORM 7.2.17 2026-06-02 17:30:17 +02:00
Juergen Hoeller ecc847c493 Fix applicability note on setAutoGrowCollectionLimit
Closes gh-36863
2026-06-02 17:28:33 +02:00
daguimu c2191e3ce2 Fix fragment parsing for relative URI in RfcUriParser
When parsing a relative URI such as `path2#foo`, RfcUriParser's
SCHEME_OR_PATH state advanced to FRAGMENT without moving the component
index past the `#` character, so the captured fragment included the
entire input (`path2#foo`) instead of just `foo`. As a result,
`toUriString()` produced `path2#path2#foo`.

Update the SCHEME_OR_PATH `#` transition to advance the component
index to `i + 1`, matching the sibling `?` -> QUERY transition in the
same state and every other `-> FRAGMENT` transition in the parser.
URIs with a `/` in the path are unaffected because they leave
SCHEME_OR_PATH for PATH on the first slash; the WhatWG parser already
handled this case correctly.

Closes gh-36762

Signed-off-by: daguimu <daguimu.geek@gmail.com>
2026-06-01 10:52:37 +01:00
Juergen Hoeller 175c1f9437 Runtime compatibility with JPA 4.0 M4
Closes gh-36784
2026-05-29 13:28:46 +02:00
Juergen Hoeller 0b77c3df45 Detect back-off exhaustion at invoker level as well
See gh-36809
2026-05-29 13:23:30 +02:00
Sam Brannen 6985d00fce Update antora-extensions to 1.14.12
Closes gh-36851
2026-05-28 10:57:11 +02:00
Juergen Hoeller 1e45ad8fa6 Restore back-off on listener setup failure
Supports exponential back-off as well now.

Closes gh-36809
2026-05-27 18:58:37 +02:00
rstoyanchev 6fd45c118d Polishing contribution
Closes gh-36650
2026-05-27 17:19:00 +01:00
Max Guiking f9b75b02da Fix last flag check in JettyWebSocketSession
See gh-36650

Signed-off-by: Max Guiking <mguiking@een.com>
2026-05-27 17:19:00 +01:00
Patrick Strawderman 84efa46e09 DefaultPathContainer uses immutable map for SEPARATORS
Closes gh-36821

Signed-off-by: Patrick Strawderman <pstrawderman@netflix.com>
2026-05-27 17:18:59 +01:00
shenjianeng b3dfba8719 Refactor map initialization using CollectionUtils
Closes gh-36763

Signed-off-by: shenjianeng <ishenjianeng@qq.com>
2026-05-27 17:17:02 +01:00
Juergen Hoeller 2782a2701c Upgrade to Netty 4.2.14 and Hibernate ORM 7.2.16 2026-05-27 16:33:15 +02:00
Juergen Hoeller af2b96192d Force initialization of configuration class in mainline thread
Closes gh-36844
2026-05-27 16:32:37 +02:00
Juergen Hoeller 121c0ac285 Remove scanned class only when conflicting with imported class
Closes gh-36835
2026-05-27 16:31:13 +02:00
Juergen Hoeller 3e585830d7 Fix MethodParameter nestingLevel documentation
Closes gh-36826
2026-05-27 16:29:51 +02:00
Sam Brannen b95caa8331 Upgrade Antora dependencies 2026-05-27 12:15:25 +02:00
Sam Brannen c17939ed5c Polish contribution
See gh-36831
2026-05-27 12:02:50 +02:00
Dennis-Mircea Ciupitu f3bfe27445 Document @⁠Conditional gating of nested @⁠Configuration classes
Closes gh-36831

Signed-off-by: Dennis-Mircea Ciupitu <dennis.mircea.ciupitu@gmail.com>
2026-05-27 12:02:50 +02:00
Sam Brannen 7651d5841f Pin Node.js version to 24.15.0
Prior to this commit, the `antora` Gradle task silently failed to build
the reference documentation, since Antora uses the latest LTS release
for Node.js by default, and the latest LTS apparently does not work for
us.
2026-05-27 12:01:09 +02:00
Sam Brannen 6e122d3aaa Polish contribution
See gh-36833
2026-05-26 16:39:30 +02:00
seonwoo_jung f7be796c1c Expose ClassLoader from DefaultDeserializer
Add a public accessor for the ClassLoader configured on a
DefaultDeserializer instance so that callers no longer need to read the
private field via reflection in order to forward it to a
ConfigurableObjectInputStream subclass.

See gh-36827
Closes gh-36833

Signed-off-by: seonwoo_jung <laborlawseon@kap.kr>
2026-05-26 16:35:37 +02:00
Sam Brannen 1e843fd3ec Polish contribution
See gh-36777
2026-05-20 17:45:24 +02:00
Kai Zander 4bb20ea8db Allow specifying charset to use in ExchangeFilterFunctions#basicAuthentication
Prior to this commit, it was not possible to specify the character set
to use with ExchangeFilterFunctions#basicAuthentication.

To address that, this commit introduces a new

Closes gh-36777

Signed-off-by: Kai Zander <61500114+kzander91@users.noreply.github.com>
2026-05-20 17:45:24 +02:00
dependabot[bot] ac24766510 Upgrade fast-xml-parser to 5.7.0
Closes gh-36691
2026-05-20 16:51:05 +02:00
Sam Brannen d5e85bd95e Remove obsolete code 2026-05-17 15:30:04 +02:00
Sam Brannen c3c96b9f32 Upgrade to Gradle 9.5.1
Closes gh-36744
2026-05-17 13:47:00 +02:00
rstoyanchev 4d98ff5dcf Polishing in data binding docs 2026-05-15 13:50:57 +01:00
rstoyanchev 48262b8efe Further polishing in docs on web binding
See gh-36803
2026-05-14 16:55:53 +01:00
rstoyanchev ca32ac5e7f Polishing in docs
See gh-36803
2026-05-14 16:41:59 +01:00
rstoyanchev 63ccc9d9cc Restructure web binding documentation and refine guidance
Closes gh-36803
2026-05-14 16:36:27 +01:00
Juergen Hoeller 8fe1de4595 Polishing 2026-05-13 19:46:07 +02:00
Juergen Hoeller c048074436 Restrict SpringVersion.getVersion() to "major.minor.patch" format
Closes gh-36785
2026-05-12 16:20:47 +02:00
Juergen Hoeller b7882d703c Expose package-info classes through PersistenceUnitInfo#getAllClassNames()
Closes gh-36784
2026-05-12 13:09:51 +02:00
Juergen Hoeller ca8a1ea375 Upgrade to Log4J 2.26, Groovy 5.0.6, Tomcat 11.0.22, Jetty 12.1.9, Hibernate ORM 7.2.14, Checkstyle 13.4.2 2026-05-11 13:08:34 +02:00
Yanming Zhou cfb8dc6cc8 Fix typos for validateExistingTransaction
Closes gh-36767

Signed-off-by: Yanming Zhou <zhouyanming@gmail.com>
2026-05-09 16:39:50 +02:00
Juergen Hoeller 665c9ad7df Upgrade to Netty 4.2.13 and Hibernate ORM 7.2.13 2026-05-08 16:00:14 +02:00
Juergen Hoeller d3152c11c7 Consistently expose map key quotes
Closes gh-36765
2026-05-08 15:59:57 +02:00
Juergen Hoeller 856e1d5dc8 Avoid ResolvableType#forType contention for implicit cache cleanup
Closes gh-36745
2026-05-08 15:59:46 +02:00
Sam Brannen 987d6cca6d Fix error message for invalid regex in SpEL
Closes gh-36756
2026-05-06 13:53:37 +02:00
shenjianeng ec21dc0f91 Refactor regex pattern caching using computeIfAbsent
Closes gh-36755

Signed-off-by: shenjianeng <ishenjianeng@qq.com>
2026-05-06 13:25:07 +02:00
Brian Clozel 628261d5bb Do not warn against Root Servlet context location
Closes gh-36692
2026-05-05 11:50:28 +02:00
Kai Zander 715d33f001 Remove control character from log message in TransportHandlingSockJsService
Closes gh-36731

Signed-off-by: Kai Zander <61500114+kzander91@users.noreply.github.com>
2026-05-04 17:20:20 +02:00
Sam Brannen bb5142164e Upgrade to Gradle 9.5
Closes gh-36744
2026-05-02 18:38:09 +02:00
박건영(Parkgunyoung) 25b8d64cd0 Fix typo in HttpServiceProxyFactory example
Closes gh-36736

Signed-off-by: 박건영(Parkgunyoung) <parkky3563@gmail.com>
2026-05-02 18:34:00 +02:00
rstoyanchev d72da90d3a Avoid race in InMemoryWebSession
Closes gh-36742
2026-05-01 21:47:37 +01:00
rstoyanchev bff9899905 Switch to JdkIdGenerator in AbstractWebSocketSession
Closes gh-36740
2026-05-01 21:47:26 +01:00
Brian Clozel 41cd6879bd Fix parsing failure for MIME types with quoted pairs
Prior to this commit, MIME types with parameter values that contain a
quoted pair would sometimes fail and parse an incomplete parameter
value.

This commit ensures that the quoted section of the parameter value is
correctly handled.

Fixes gh-36730
2026-05-01 21:47:16 +01:00
Brian Clozel 3a91d90c28 Resolve URL path for versioned webjar directories
Prior to this commit, the `resolveUrlPath` implementation for the
`LiteWebJarsResourceResolver` would always delegate to the resource
chain once the versioned webjar folder has been resolved.

While this aligns with the `ResourceResolver` contract and the fact that
the resource chain does not resolve directories, here the WebJar locator
does support such use cases and we shouldn't get in the way here.

This commit falls back to the resolved versioned WebJar path if no
resource could be resolved and the path ends with "/".

Fixes gh-36726
2026-05-01 21:47:06 +01:00
Juergen Hoeller 08c5280843 Consistent wrapping of BeanCreationExceptions from instance suppliers
Includes tests for circular references and bean definition overrides.

Closes gh-36725
See gh-36648
2026-04-30 14:19:25 +02:00
Juergen Hoeller cd5fee5347 Polishing 2026-04-29 21:52:10 +02:00
Juergen Hoeller 916cb64581 Detect custom deserialized NullValue instances
Closes gh-36727
2026-04-29 21:52:04 +02:00
rstoyanchev b3ef834ae6 Reliably detect broadcast messages
Closes gh-36662
2026-04-29 12:11:16 +01:00
rstoyanchev a9d344b3ed TransportHandlingSockJsService checks remoteAddress
Closes gh-36681
2026-04-29 12:11:16 +01:00
cuitianhao 39a746d30f Set host header consistently in STOMP relay CONNECT frames
StompBrokerRelayMessageHandler only set the host header in CONNECT
frames when virtualHost was explicitly configured. Per STOMP 1.2, the
host header is required on CONNECT frames.

Fall back to relayHost (the TCP connection target) when virtualHost is
not configured, ensuring the host header is always present in both
system session and client session CONNECT frames.

Closes gh-36673

Signed-off-by: cuitianhao <54015884+tianhaocui@users.noreply.github.com>
2026-04-29 12:11:16 +01:00
Brian Clozel 952198b2ee Fix PartGenerator token request while creating tmp file
Prior to this commit, the `PartGenerator` would allow requesting
additional part tokens while in the `CreateFileState`. This is invalid
as any new token emitted would be rejected and would fail the entire
process.
This would only happen if the tmp file creation is slow enough for a new
token to be parsed and emitted.

This commit ensures that no new part token is requested while creating
the temporary file.
This change also fixes lifecycle issues and ensures that buffer
resources are cleaned in case of errors.

Fixes gh-36694
2026-04-28 23:21:05 +02:00
Dmitry Sulman 8d93670430 Support Micrometer context propagation in Kotlin Flow
See gh-36427
Closes gh-36667
Signed-off-by: Dmitry Sulman <dmitry.sulman@gmail.com>
2026-04-28 11:10:55 +02:00
Sébastien Deleuze 29a7402adf 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-04-28 11:10:55 +02:00
Sigurd Gerke 3dfd6838c0 Fix a regression on value class parameter handling
This commit fixes a regression introduced by gh-36449 for
nullable value class with an non-null value.

Closes gh-36665
Signed-off-by: Sigurd Gerke <sigurd.gerke@onedata.de>
2026-04-28 11:10:55 +02:00
Brian Clozel 32170e5847 Avoid cache collisions in CachingResourceResolver
Prior to this commit, there could be cache collisions in the
`CachingResourceResolver` because the cache key generation was
incomplete. This commit expands the cache key generation to avoid such
cases.

Fixes gh-36713
2026-04-28 09:28:00 +02:00
Yanming Zhou bfb88cfc1c Remove unnecessary invocations of toString()
Closes gh-36709

Signed-off-by: Yanming Zhou <zhouyanming@gmail.com>
2026-04-27 11:27:29 +03:00
Yanming Zhou c4dfb24116 Update copyright headers in remaining source files
Closes gh-36703

Signed-off-by: Yanming Zhou <zhouyanming@gmail.com>
2026-04-27 11:26:35 +03:00
Brian Clozel 4fb0244b5a Enfoce single version removal in content versioning
Prior to this commit, content based version strategies would remove all
instances of the version string in the request path when trying to
resolve the original resource with the chain.
This can cause issues in rare cases where there is a collision between
the content version and some other version string in the request path.

Because this strategy is based on the contents of the file itself, we
should only remove the last instance of the version string and then
attempt to resolve the original file.

Fixes gh-36698
2026-04-23 15:04:02 +02:00
Brian Clozel 0725bf4941 Warn against unsafe static resource locations
Prior to this commit, `ResourceHandlerUtils` would perform resource
location checks to ensure that the configured location is valid. This
commit also ensures that we log a WARN message if the application
chooses a well-known unsafe location like "classpath:" or the root
Servlet context for serving static resources.

Closes gh-36692
2026-04-23 10:45:15 +02:00
Juergen Hoeller 8965d9bccf Polishing 2026-04-21 20:35:25 +02:00
Juergen Hoeller af7a5716e8 Consistently ignore exceptions for Xerces-specific properties
Closes gh-36682
2026-04-21 20:34:29 +02:00
Brian Clozel 83e7aa1131 Update HttpComponents Javadoc URI
Fixes gh-36685
2026-04-21 18:51:58 +02:00
박동윤 (Park Dong-Yun) 865cf3c117 Avoid redundant URI object creation in WebClientUtils
Prior to this commit, WebClientUtils.getRequestDescription()
created a new URI object on every invocation. Since the URI
constructor includes validation and parsing, which is already
performed by the parameter URI object, this was unnecessarily
expensive for a logging utility.

This commit reuses the original URI's string representation
to avoid redundant parsing.
This commit also strips userInfo, query, and fragment from the
log description even if URIs contain only userInfo or
fragment (without a query).

Closes gh-36641

Signed-off-by: 박동윤 (Park Dong-Yun) <ehddbs7458@gmail.com>
2026-04-21 16:52:33 +02:00
Stéphane Nicoll be70e1c5a7 Next development version (v7.0.8-SNAPSHOT) 2026-04-17 08:33:14 +02:00
Juergen Hoeller 91852546d9 Upgrade to Reactor 2025.0.5 and Micrometer 1.16.5
Includes Hibernate ORM 7.2.11

Closes gh-36658
Closes gh-36659
2026-04-16 23:28:46 +02:00
Juergen Hoeller 802fa4d65e Refine RetryListener example
See gh-36452
2026-04-16 23:27:59 +02:00
rstoyanchev 7052da4532 Add doOnDiscard in MultipartHttpMessageReader
Closes gh-36563
2026-04-16 21:15:09 +01:00
Sam Brannen 63817ce202 Add missing tests for WebRequestDataBinder
See gh-36625
2026-04-16 16:33:43 +02:00
Sam Brannen 61bd79017f Polish WebRequestDataBinderTests 2026-04-16 16:31:57 +02:00
Sam Brannen ab6637c670 Completely extract ServletRequestParameterPropertyValuesTests
This aligns with changes to ServletRequestParameterPropertyValuesTests
on 6.2.x.
2026-04-16 15:11:39 +02:00
Sam Brannen c9b88b4ebd Extract ServletRequestParameterPropertyValuesTests 2026-04-16 14:38:17 +02:00
Sam Brannen 68c575ab14 Revise "Skip binding entirely when field is not allowed"
This commit reverts the changes made to WebDataBinder's doBind()
implementation in e4d03f6625 and instead implements the skipping logic
directly in checkFieldDefaults(), checkFieldMarkers(), and
adaptEmptyArrayIndices() by preemptively checking if the corresponding
field is allowed.

This commit also improves the Javadoc and adds missing tests.

Fixes gh-36625
2026-04-16 14:33:50 +02:00
Sam Brannen cb320468db Further clarify semantics of HttpMethod.valueOf()
See gh-36652
2026-04-14 16:17:20 +02:00
angry2.k df828458fa Clarify semantics of HttpMethod.valueOf()
HttpMethod.valueOf() performs a case-sensitive lookup of predefined
HttpMethod constants. For example, valueOf("GET") resolves to
HttpMethod.GET, whereas valueOf("get") results in a newly created
HttpMethod instance.
Update the Javadoc to explicitly document this behavior.

See gh-36642
Closes gh-36652

Signed-off-by: angry-2k <edkev@kakao.com>
2026-04-14 16:15:43 +02:00
Sam Brannen ff3f2937ab Restructure SpelCompilationCoverageTests using @⁠Nested test classes 2026-04-13 17:42:18 +02:00
Sam Brannen d7450ce8e4 Polish SpelCompilationCoverageTests 2026-04-13 12:58:36 +02:00
Sam Brannen 28f78f435e Introduce missing tests for immediate SpEL compilation for Elvis operator 2026-04-12 17:11:07 +02:00
Sam Brannen a3705632c1 Polish SpEL Ternary and Elvis operators 2026-04-12 17:09:28 +02:00
Sam Brannen 84221064c8 Polish SpEl Ternary operator and compilation tests 2026-04-12 16:41:49 +02:00
Sam Brannen 21f3b964fe Improve SpEL tests for Elvis and Ternary operators 2026-04-12 14:28:33 +02:00
Sébastien Deleuze efb85d68ef Apply nullable value class fix to InvocableHandlerMethod
See gh-36449
2026-04-10 16:03:06 +02:00
T45K b3f01a7f92 Fix nullable value class handling in CoroutinesUtils
Closes gh-36449
Signed-off-by: T45K <tasktas9@gmail.com>
2026-04-10 16:03:06 +02:00
Sébastien Deleuze 22bcac1eb5 Refine Kotlin serialization contribution
This commit moves the JSON specific code to
KotlinSerializationJsonDecoder, uses switchOnFirst operator to keep the
existing behavior and derives the list serializer from the element one.

Closes gh-36597
2026-04-10 16:02:51 +02:00
Dmitry Sulman 546ae15a44 Support JSON list deserialization in KotlinSerializationStringDecoder
See gh-36597
Signed-off-by: Dmitry Sulman <dmitry.sulman@gmail.com>
2026-04-10 16:02:51 +02:00
xxxxxxjun 78c32aa7d2 Fix typo in FormTag Javadoc and TLD
Closes gh-36610

Signed-off-by: xxxxxxjun <ryuu.public@gmail.com>
2026-04-09 13:06:31 +02:00
Sam Brannen 99b78adce3 Revise documentation for @⁠ActiveProfiles and ActiveProfilesResolver
See gh-36269
See gh-36600
2026-04-09 12:52:18 +02:00
Mohak-Nagaraju 644731c9f6 Document that spring.profiles.active is ignored by @⁠ActiveProfiles
The Spring TestContext Framework does not honor the
`spring.profiles.active` system property when determining
active profiles for a test class if @⁠ActiveProfiles is
used.

This commit documents that behavior in the @⁠ActiveProfiles
and DefaultActiveProfilesResolver Javadoc, as well as in
the reference manual. A SystemPropertyActiveProfilesResolver
example is also added showing how to allow
`spring.profiles.active` to override @⁠ActiveProfiles.

See gh-36269
Closes gh-36600

Signed-off-by: Mohak Nagaraju <98132980+Mohak-Nagaraju@users.noreply.github.com>
2026-04-09 12:15:33 +02:00
Brian Clozel e4d03f6625 Skip binding entirely when field is not allowed
Prior to this commit, fields that are not allowed for binding were
always skipped and would not be bound. But the field and default marker
support (with the "_" and "!" prefixes) would be still considered and
could trigger collection instantiation/autogrow.

While this does not cause unwanted binding, this allocates memory for no
reason. This commit revisits the binding algorithm to only consider
default and field marker support if the regular field is allowed.

Fixes gh-36625
2026-04-09 10:58:57 +02:00
Sam Brannen 6251b2c0c9 Support @⁠Sql with DataSource wrapped in a TransactionAwareDataSourceProxy
Prior to this commit, SqlScriptsTestExecutionListener unwrapped data
sources wrapped in an InfrastructureProxy or a scoped proxy, but it did
not unwrap a data source wrapped in a TransactionAwareDataSourceProxy.
Consequently, the sameDataSource() check failed in the latter case,
preventing execution of @⁠Sql scripts.

To address that, this commit revises sameDataSource() to unwrap a
TransactionAwareDataSourceProxy as well, analogous to the
implementations of setDataSource() in DataSourceTransactionManager,
JpaTransactionManager, and HibernateTransactionManager.

Closes gh-36611
2026-04-09 10:35:22 +02:00
Juergen Hoeller 3d211b71b1 Revise target bean exception with consistent message formatting 2026-04-08 15:28:29 +02:00
Sam Brannen 18b8f871aa Prevent mockk from transitively pulling in JUnit 4 2026-04-08 14:13:59 +02:00
Juergen Hoeller d7ddfe8d2e Upgrade to Log4J 2.25.4, Groovy 5.0.5, Hibernate ORM 7.2.10, Mockito 5.23, Checkstyle 13.4 2026-04-08 13:41:57 +02:00
Juergen Hoeller 1687d90a8c Polishing 2026-04-08 13:41:32 +02:00
Juergen Hoeller b8f1005897 Improve SpringValidatorAdapter and MethodValidationAdapter performance
Closes gh-36621
2026-04-08 13:39:20 +02:00
Juergen Hoeller 0150c4ba06 Avoid MessageFormat rendering for exception messages in binding errors
Closes gh-36609
2026-04-08 13:39:10 +02:00
Sam Brannen b560c7b85d Improve Javadoc for MergedAnnotations 2026-04-08 12:35:24 +02:00
Sam Brannen 0f05a2e153 Polish ClassFileMethodMetadata 2026-04-08 11:38:07 +02:00
Sam Brannen d4cc273c31 Avoid recursion in ClassFileAnnotationMetadata.resolveTypeName()
See gh-36577
2026-04-08 11:13:51 +02:00
Sam Brannen f3b6c222f9 Use ClassLoader for method or field in MergedAnnotation
Prior to this commit, the `return` keyword was missing in
TypeMappedAnnotation's getClassLoader() implementation, which prevented
the ClassLoader of the Member (Method or Field) from being used.

This commit fixes that by adding the missing `return` keyword and adds
a test using a custom ClassLoader to verify the correct behavior.

Closes gh-36606
2026-04-07 18:16:09 +02:00
Sam Brannen 8d390f4e5a Polish SpEL documentation 2026-04-07 17:54:43 +02:00
Manu Sridharan f5a8c321c0 Address nullness warning from future version of NullAway
Closes gh-36548
Signed-off-by: Manu Sridharan <msridhar@gmail.com>
2026-04-07 15:10:35 +02:00
Brian Clozel 14466eef4c Document valid SpEL separators for tokenizer 2026-04-07 11:52:47 +02:00
Sam Brannen 9d365906b5 Fix typo 2026-04-06 16:55:32 +02:00
Sam Brannen 97e10a5948 Fix flaky SpEL tests
This commit fixes SpEL related tests that failed if the test methods
were executed in a different order than in the Gradle build.
2026-04-06 16:55:32 +02:00
Sam Brannen bfbfe4a572 Fix BridgeMethodResolverTests.findBridgedMethodInHierarchy() in Eclipse 2026-04-06 16:54:34 +02:00
Sam Brannen 38464a15dc Retain source declaration order in AnnotatedTypeMetadata on Java 24+
Prior to this commit, ClassFileAnnotationDelegate created
MergedAnnotations from a HashSet, which resulted in a non-deterministic
iteration order and lost the original source declaration order of the
annotations.

To address that, this commit revises ClassFileAnnotationDelegate to
create MergedAnnotations from a List.

In addition, this commit updates all related tests to use the
containsExactly() assertion instead of containsExactlyInAnyOrder() to
ensure we consistently adhere to the "source declaration order"
requirement.

Closes gh-36598
2026-04-04 17:19:25 +02:00
Sam Brannen c04b502866 Polishing 2026-04-04 17:17:38 +02:00
xxxxxxjun f2c0ad484e Fix typo in HttpRange error message
Closes gh-36590

Signed-off-by: xxxxxxjun <ryuu.public@gmail.com>
2026-04-03 15:41:14 +02:00
Sam Brannen d10460d775 Track class loading exceptions in MergedAnnotation.asMap()
Spring Framework 5.2 introduced a regression in our annotation
processing support when the MergedAnnotations API was introduced.
Consequently, prior to this commit, our "annotation attributes as a Map
or AnnotationAttributes instance" support no longer stored exceptions
thrown while attempting to load a type referenced by an annotation
attribute. Instead, the exception was thrown immediately.

To address that, this commit revises our MergedAnnotation.asMap()
support so that it now tracks such exceptions in the map instead of
immediately throwing them. This allows map functionality such as
contains(attributeName), keySet(), etc. to continue to function
properly. In addition, the internal getRequiredAttribute() method in
AnnotationAttributes once again properly throws the original exception
wrapped in an IllegalArgumentException whenever a caller invokes one of
the convenience methods such as getClass() and getClassArray().

Note that this affects both asMap() variants as well as
asAnnotationAttributes().

In addition, this commit reverts the fix applied in 00fbd91cca since
it is no longer necessary.

See gh-36524
Closes gh-36586
2026-04-03 15:38:36 +02:00
Sam Brannen 1ead8bf1ab Polishing 2026-04-02 18:39:37 +02:00
Sam Brannen 7b087d1a6c Disable flaky reactorNettyAttributes() test in WebClientIntegrationTests
See gh-36589
2026-04-02 17:20:38 +02:00
Sam Brannen 00fbd91cca Skip annotations that cannot be processed in AnnotationBeanNameGenerator
Prior to this commit, AnnotationBeanNameGenerator failed when searching
for a convention-based bean name, if an annotation referenced a
non-existent class.

To address that, this commit introduces a try-catch block around each
invocation of MergedAnnotation.asAnnotationAttributes() and skips
processing of the current MergedAnnotation if an exception occurs,
which is likely due to a type referenced from an annotation attribute
not being present in the classpath.

See gh-31203
Closes gh-36524
2026-04-02 16:51:17 +02:00
Brian Clozel d87ebbff78 Polishing
See gh-36584
2026-04-02 14:48:09 +02:00
Juergen Hoeller e977bd7bf0 Enforce strict implementation of Resource#isReadable() contract
Closes gh-36584
2026-04-02 14:30:51 +02:00
Sam Brannen 24c7d31ba7 Polishing 2026-04-02 12:43:28 +02:00
Sam Brannen b6fc3a1b6f Enforce use of AssertJ assumptions via Checkstyle
Closes gh-36582
2026-04-02 12:43:12 +02:00
Sam Brannen d767806977 Ignore .cursor directory 2026-04-02 12:11:58 +02:00
Sam Brannen c5b9c3d604 Add 2D array tests, use AssertJ assumptions, and polish
See gh-36577
2026-04-02 11:05:59 +02:00
Brian Clozel 40ef6f6872 Fix method and annotation metadata for ClassFile variant
Prior to this commit, the ClassFile variant for  annotation and method
metadata would report incorrect metadata for:
* the method return type names in case of primitives and array types
* `toString` values for methods
* `equals` and `hashcode` information for methods

This commit expands the test suite and ensures that the ASM and
ClassFile variants are aligned.

Fixes gh-36577

Signed-off-by: Brian Clozel <brian.clozel@broadcom.com>
2026-04-02 10:18:13 +02:00
Junseo Bae b01fdb0140 Fix ClassFileMethodMetadata return type names for primitives and arrays
Return correct names for primitive and array types
Introduce resolveTypeName helper for ClassDesc handling
Add ClassFileMethodMetadataTests (java24Test)
Extend AbstractMethodMetadataTests with void return case

See gh-36577

Signed-off-by: Junseo Bae <ferrater1013@gmail.com>
2026-04-02 10:17:21 +02:00
Juergen Hoeller 3f01e3fe11 Polishing 2026-04-01 21:15:58 +02:00
Juergen Hoeller e78af58af3 Fix Writer lifecycle for writeInternal(Object, Type, Writer)
Closes gh-36565
2026-04-01 21:15:50 +02:00
Brian Clozel aebf7c97f7 Register overrides in HttpMessageConverters when defaults are off
Prior to this commit, all `with**Converter` methods in
`HttpMessageConverters` builders would only apply if the
`registerDefaults()` option is on. While the original intent is to allow
overrides for auto-detected defaults, this creates an unnecessary
discrepancy with `addCustomConverter` which is always effective.

Allowing sich registrations when defaults are off should not introduce
invalid states and only reflects the intent of the developer. This
commit now allows such cases.

Fixes gh-36579
2026-04-01 10:49:55 +02:00
Sam Brannen 7b8d8693dd Fix AnnotationMetadata tests in Eclipse IDE
Since commit 89391fd94c, the getClassAttributeWhenUnknownClass() methods
in SimpleAnnotationMetadataTests and DefaultAnnotationMetadataTests
have failed in Eclipse, since the JSR-305 JAR is always on the test
runtime classpath in Eclipse.

To address that, this commit introduces a FilteringClassLoader in both
test classes which mimics the test runtime behavior in the Gradle build.

See gh-36432
2026-04-01 10:13:06 +02:00
Sam Brannen 22adccd190 Deprecate methodIdentification() in CacheAspectSupport for removal
CacheAspectSupport currently contains a protected methodIdentification()
method which is not used internally within the framework, and it appears
that it was accidentally copied from TransactionAspectSupport when the
caching support was first introduced.

In light of that, this commit deprecates it for removal in 7.1.

Closes gh-36576
2026-03-31 17:49:21 +02:00
rstoyanchev d4d49fd546 Make base classes for client requests public
AbstractBufferingClientHttpRequest and AbstractStreamingClientHttpRequest

Closes gh-36501
2026-03-31 14:11:17 +01:00
KimDaehyeon 730f824cad Fix Javadoc in HandlerMethodReturnValueHandlerComposite
Closes gh-36555

Signed-off-by: KimDaehyeon <daehyeon3351@gmail.com>
2026-03-31 11:19:52 +01:00
rstoyanchev 2dcd56bbaf Polishing contribution
Closes gh-36514
2026-03-31 11:00:22 +01:00
Vishnutheep B 00be6d3afd Add null check when setting API version to preserve default value
See gh-36514

Signed-off-by: Vishnutheep B <vishnutheep@gmail.com>
2026-03-31 11:00:22 +01:00
rstoyanchev ebe3b45743 Consistently log request details in ExchangeFunctions
Closes gh-36502
2026-03-31 11:00:22 +01:00
Brian Clozel 9c4b246c8c Use RestClient in integration tests
Now that `RestClient` offers a better alternative, this commit revisits
our integration tests to use `RestClient` instead of `RestTemplate`.

Closes gh-36573
2026-03-31 11:37:43 +02:00
Brian Clozel f850b051b5 Add MockRestServiceServer#createServer variant for RestClient
Prior to this commit, `MockRestServiceServer` would provide
`createServer` shortcuts for `RestTemplate` and `RestGatewaySupport`.

Now that `RestClient` offers a modern client variant, we should provide
the same for `RestClient`.

Closes gh-36572
2026-03-31 11:20:44 +02:00
Brian Clozel c9c137ec51 Create and use RestClientXhrTransport
Prior to this commit, the `SockJsClient` would use the
`RestTemplateXhrTransport`. `RestClient` is a modern replacement for
RestTemplate and should be used instead.

This commit introduces the `RestClientXhrTransport` variant as an
immediate replacement.

Closes gh-36566
2026-03-31 11:02:33 +02:00
Sam Brannen b7a1157e66 Correct Javadoc for MergedAnnotation.asAnnotationAttributes()
Closes gh-36567
2026-03-30 16:59:57 +02:00
Sam Brannen 27bc60e077 Polishing 2026-03-30 16:59:24 +02:00
Juergen Hoeller 0abf97ff90 Polishing 2026-03-28 20:38:03 +01:00
Juergen Hoeller f0f447a73d Upgrade to Netty 4.2.12 2026-03-28 11:13:04 +01:00
Juergen Hoeller b81a63fb5c Polishing 2026-03-28 11:12:28 +01:00
Juergen Hoeller 529a6fc932 Add documentation notes on error handling with sync=true
See gh-36531
2026-03-28 11:11:52 +01:00
Juergen Hoeller e964ced1ad Make ApplicationListenerMethodAdapter#getTargetMethod() public
Closes gh-36558
2026-03-28 11:11:46 +01:00
Junseo Bae ff30329861 Fix incorrect method name in TypeDescriptor.array() Javadoc
Closes gh-36549

Signed-off-by: Junseo Bae <ferrater1013@gmail.com>
2026-03-27 11:25:40 +01:00
Sam Brannen 6d3d3d11c4 Introduce Kotlin examples for Bean Overrides (@⁠MockitoBean, etc.)
This commit introduces Kotlin examples in the reference manual for
@⁠MockitoBean, @⁠MockitoSpyBean, and @⁠TestBean.

Closes gh-36541
2026-03-26 14:28:36 +01:00
Brian Clozel 9140ed6fa9 Fix SSE flushing in SseServerResponse
Prior to this commit, `SseServerResponse.send()` would flush the
output stream returned by `getBody()`. Since gh-36385,
`ServletServerHttpResponse` wraps this stream with a non-flushing
decorator to avoid performance issues with `HttpMessageConverter`
implementations that flush excessively. As a result, SSE events were
no longer flushed to the client.

This commit changes `send()` to call `outputMessage.flush()` instead
of `body.flush()`, which properly delegates to the servlet response
`flushBuffer()` and is not affected by the non-flushing wrapper.

Fixes gh-36537
2026-03-25 21:45:24 +01:00
Juergen Hoeller 6cea397bd8 Consistent handling of early setCatalog/setSchema/setHoldability calls
Closes gh-36527
Closes gh-36528
2026-03-24 23:40:43 +01:00
Juergen Hoeller 484599ecbb Upgrade to EclipseLink 5.0.0 GA 2026-03-24 18:06:33 +01:00
Juergen Hoeller 1ff369d1ce Document RetryTemplate#invoke variants in reference manual
Closes gh-36452
2026-03-24 18:06:24 +01:00
Brian Clozel ede46647e8 Link observability section to Micrometer Observation Handler docs
This commit links the Spring Framework Observability section in the
reference documentation to the Micrometer docs describing how
Observations are handled as metrics.

Closes gh-34994
2026-03-24 13:18:52 +01:00
Sam Brannen 7f7f7c5f5b Invoke assertIsMock() before configuring mocks 2026-03-24 11:07:38 +01:00
Brian Clozel d1950f598a Fix response stream handling in RestClient
Prior to this commit, the `DefaultRestClient` implementation would
look at the type of the returned value to decide whether the HTTP
response stream needs to be closed before returning.
As of gh-36380, this checks for `InputStream` and `InputStreamResource`
return types, effectively not considering `ResponseEntity<InputStream>`
and variants.

This commit ensures that `ResponseEntity` types are unwrapped before
checking for streaming types.

Fixes gh-36492
2026-03-24 10:18:32 +01:00
Juergen Hoeller a01dd1cd79 Upgrade to Tomcat 11.0.20, Hibernate ORM 7.2.7, Protobuf 4.34.1 2026-03-23 20:13:03 +01:00
Juergen Hoeller 86924d067b Add registerManagedResource variant with bean key argument
Closes gh-36520
2026-03-23 20:12:40 +01:00
Juergen Hoeller f0472ea1c0 Polishing (includes nullable declaration for internal type array) 2026-03-23 20:11:54 +01:00
elgun.shukurov 4b211643bb Handle blank Accept-Language header in AcceptHeaderLocaleResolver
When the Accept-Language header is present but blank, defaultLocale
was ignored and fell back to Locale.getDefault() (JVM system locale).
A blank header represents the same intent as an absent header.

Closes gh-36513

Signed-off-by: elgun.shukurov <elgun.sukurov@kapitalbank.az>
2026-03-23 14:28:27 +01:00
Junseo Bae f92e810831 Fix incorrect cross reference in AbstractEnvironment Javadoc
Closes gh-36516

Signed-off-by: Junseo Bae <ferrater1013@gmail.com>
2026-03-23 11:49:39 +01:00
Sam Brannen 9fba394b8a Sync changes to antora-playbook.yml from docs-build branch 2026-03-23 11:36:26 +01:00
Sam Brannen 0005ec09c2 Fix link to SpringProperties
See gh-36460
2026-03-22 18:04:33 +01:00
Sam Brannen 0e800538c0 Fix warning in ConcurrentOperationExecutor.kt
Prior to this commit, the following warning was emitted in the build.

w: file:///<path>/spring-framework/framework-docs/src/main/kotlin/org/springframework/docs/core/aop/ataspectj/aopataspectjexample/ConcurrentOperationExecutor.kt:57:29 Unnecessary non-null assertion (!!) on a non-null receiver of type 'PessimisticLockingFailureException'.
2026-03-22 17:31:14 +01:00
Sam Brannen 43b01382ed Support global ExtensionContext scope configuration for SpringExtension
Prior to this commit, the extension context scope used by the
SpringExtension defaulted to test-method scoped and could only be
changed on a per-class basis via the @⁠SpringExtensionConfig
annotation. However, having to annotate each affected test class is
cumbersome for developers who wish to use test-class scoped extension
context semantics across their code base.

To address that, this commit introduces support for configuring the
global default extension context scope for the SpringExtension via
Spring properties or JUnit properties.

Specifically, this commit introduces a
`spring.test.extension.context.scope` property that can be set via the
SpringProperties mechanism or via a JUnit Platform configuration
parameter.

See gh-35697
Closes gh-36460
2026-03-22 16:48:41 +01:00
Juergen Hoeller 3a35c4dbd0 Consistent IOException spelling in class/method names 2026-03-21 12:13:39 +01:00
Juergen Hoeller 4c854d564c Avoid endless re-interruption loop on shutdown
Closes gh-36506
2026-03-21 12:04:29 +01:00
Juergen Hoeller de562deab0 Recognize error code 149 as deadlock loser (MySQL Galera)
Closes gh-36499
2026-03-21 12:04:00 +01:00
Brian Clozel 0b2e3900b9 Share codings resolution in resource resolvers
Prior to this commit, different resource resolvers would resolve
accepted codings from the HTTP request sent by the client. This would be
done with different implementations, which could lead to resolution
errors and desynchronizations.

This commit now introduced a new shared method in
`EncodedResourceResolver` (Servlet and Reactive) to perform a consisten
resolution.

Fixes gh-36507
2026-03-20 15:47:09 +01:00
Sam Brannen e1e4d52b61 Clean up AssertJ usage after migration
- Avoid the use of assertThat(Arrays.equals(...))
- Convert assertThat(Boolean.FALSE).isEqualTo(x) to assertThat(x).isEqualTo(Boolean.FALSE)
- Convert assertThat(Boolean.TRUE).isEqualTo(x) to assertThat(x).isEqualTo(Boolean.TRUE)
- Convert assertThat(x.equals(y)).isTrue() to assertThat(x).isEqualTo(y)
- Convert assertThat(x.equals(y)).isFalse() to assertThat(x).isNotEqualTo(y)
- Remove unnecessary parentheses in assertThat() arguments
- Convert assertThat(x instanceof X).isFalse() to assertThat(x).isNotInstanceOf()
- Convert assertThat(x instanceof X).isTrue() to assertThat(x).isInstanceOf()
- Convert assertThat(!x).isTrue() to assertThat(x).isFalse()
- Inline conditions in assertThat() statements

Closes gh-36504
2026-03-20 11:07:42 +01:00
Sam Brannen 8ca0262e2f Avoid the use of assertThat(Arrays.equals(...))
See gh-36504
2026-03-20 10:54:57 +01:00
Sam Brannen 29ea530525 Convert assertThat(Boolean.FALSE).isEqualTo(x) to assertThat(x).isEqualTo(Boolean.FALSE)
See gh-36504
2026-03-20 10:54:54 +01:00
Sam Brannen ac10d079df Convert assertThat(Boolean.TRUE).isEqualTo(x) to assertThat(x).isEqualTo(Boolean.TRUE)
See gh-36504
2026-03-20 10:54:46 +01:00
Sam Brannen 87c4acd7ac Convert assertThat(x.equals(y)).isTrue() to assertThat(x).isEqualTo(y)
Search:

  assertThat\((?!\s*Arrays\.)((?:[^()]+|\([^()]*\))+)\.equals\(\s*((?:[^()]+|\([^()]*\))+)\s*\)\)(.*)\.isTrue\(\)

Replace:

  assertThat($1)$3.isEqualTo($2)

See gh-36504
2026-03-20 10:54:42 +01:00
Sam Brannen 2ce21f7654 Convert assertThat(x.equals(y)).isFalse() to assertThat(x).isNotEqualTo(y)
Search:

  assertThat\((?!\s*Arrays\.)(.+?)\.equals\((.+?)\)\)(.*?)\.isFalse\(\)

Replace:

  assertThat($1)$3.isNotEqualTo($2)

See gh-36504
2026-03-20 10:54:37 +01:00
Sam Brannen 9e6504b05a Remove unnecessary parentheses in assertThat() arguments
Search:  assertThat\(\(\s*((?:[^()]+|\([^()]*\))+)\s*\)\)(\s*\..+)

Replace: assertThat($1)$2

See gh-36504
2026-03-20 10:54:31 +01:00
Sam Brannen 399f779c43 Convert assertThat(x instanceof X).isFalse() to assertThat(x).isNotInstanceOf()
Search:  assertThat\((.+?) instanceof (.+?)\)(.*?)\.isFalse\(\)

Replace: assertThat($1)$3.isNotInstanceOf($2.class)

See gh-36504
2026-03-20 10:54:27 +01:00
Sam Brannen a40ec44cb7 Convert assertThat(x instanceof X).isTrue() to assertThat(x).isInstanceOf()
Search:  assertThat\((.+?) instanceof (.+?)\)(.*?)\.isTrue\(\)

Replace: assertThat($1)$3.isInstanceOf($2.class)

See gh-36504
2026-03-20 10:54:23 +01:00
Sam Brannen 4357bbd31b Convert assertThat(!x).isTrue() to assertThat(x).isFalse()
Search:  assertThat\(\!(.+?)\)(.*?)\.isTrue\(\)

Replace: assertThat($1)$2.isFalse()

See gh-36504
2026-03-20 10:54:13 +01:00
Sam Brannen 4548f1ec66 Inline conditions in assertThat() statements
Search: assertThat\(conditions?[0-9]?\)

See gh-36504
2026-03-20 10:53:54 +01:00
Sam Brannen 9e71dc6029 Upgrade to Gradle 9.4.1
Closes gh-36500
2026-03-19 14:05:14 +01:00
Sam Brannen 0daa5ce631 Polishing
(cherry-picked from b846a29b17)
2026-03-19 14:02:11 +01:00
rstoyanchev 8817536e5a Lower log level of cache miss in HandlerMappingIntrospector
See gh-36309
2026-03-19 09:10:43 +00:00
rstoyanchev cdbaee11c1 Add PreFlightRequestFilter
Closes gh-36482
2026-03-19 09:10:04 +00:00
Sam Brannen 1256307c83 Enable SpringJUnit5 Checkstyle rule
See gh-36496
Closes gh-36496
2026-03-18 18:38:43 +01:00
Sam Brannen 4c14abf0cd Remove obsolete "test" prefix from test method names
Although this commit also changes the visibility of some test methods
to package-private, the remainder of that task will be addressed in
conjunction with gh-36496.

Closes gh-36495
2026-03-18 18:16:15 +01:00
Tobias Fasching a81367f686 Use ISO-8859-1 for fallback filename
Updates the Content-Disposition header creation logic to use only
ISO-8859-1 characters for the fallback 'filename' parameter instead of
RFC 2047 encoded strings. Non-compatible characters are replaced with '_'.

This does not remove the ability to parse RFC 2047 encoded filenames.

Closes gh-36328

Signed-off-by: Tobias Fasching <tobias.fasching@outlook.com>
2026-03-18 12:09:36 +01:00
Brian Clozel a21706c58a Copy WS handshake headers to store in session
Prior to this commit, the `StandardWebSocketUpgradeStrategy` would get
the HTTP headers from the handshake request and store them in the
WebSocket session for the entire duration of the session.
As of gh-36334, Spring MVC manages HTTP directly with a native API
instead of copying them. This improves performance but also uncovered
this bug: we cannot keep a reference to HTTP headers once the HTTP
exchange is finished, because such resources can be recycled and reused.

This commit ensures that the handshake headers are copied into the
session info to keep them around for the entire duration of the session.
Without that, Tomcat will raise an `IllegalStateException` at runtime.

This was already done for WebFlux in SPR-17250, but the latest header
management changes in Framework uncovered this issue for the Standard
WebSocket container case.

Fixes gh-36486
2026-03-17 18:09:57 +01:00
github-actions[bot] de913642fb Update Antora Spring UI to v0.4.26 2026-03-17 09:19:50 +01:00
jun c2c6130da5 Fix typo in StompSession Javadoc
Closes gh-36469

Signed-off-by: jun <ryuu.public@gmail.com>
2026-03-16 13:38:19 +01:00
Brian Clozel df817364b2 Fix WebTestClient list assertions
Prior to this commit, the `WebTestClient` could only accept non-nullable
types in its `expectBodyList<T>` Kotlin extension function.
This commit relaxes this requirements to allow for more assertions.

Fixes gh-36476
2026-03-16 11:14:55 +01:00
Brian Clozel bc07a451dc Fix ParameterizedTypeReference nullness
This commit ensures that `ParameterizedTypeReference<T>` can accept
nullable types. This is especially useful for Kotlin extension functions
and assertion contracts.

Fixes gh-36477
2026-03-16 11:12:06 +01:00
Sam Brannen 5eb0e99d58 Fix common typos and grammatical mistakes
Closes gh-36471
2026-03-15 17:05:29 +01:00
Sam Brannen 17699103dc Consistently use American English spelling
Since the Spring Framework uses American English spelling, this commit
updates Javadoc and the reference manual to ensure consistency in that
regard. However, there are two exceptions to this rule that arise due
to their use within a technical context.

- We use "cancelled/cancelling" instead of "canceled/canceling" in
  numerous places (including error messages).
- We use "implementor" instead of "implementer".

Closes gh-36470
2026-03-15 17:04:23 +01:00
Sam Brannen 9b366b28b7 Remove obsolete, custom Eclipse configuration for spring-oxm
See gh-36195
2026-03-15 14:32:56 +01:00
Sam Brannen 0f990e44a8 Polish contribution
See gh-36195
2026-03-15 14:32:56 +01:00
Clayton Walker e102bc4933 Improve XJC configuration for spring-oxm in the Gradle build
Closes gh-36195

Signed-off-by: Clayton Walker <clayton.m.walker@gmail.com>
2026-03-15 14:32:44 +01:00
Brian Clozel be1bf91ce8 Branch for 7.0.x maintenance 2026-03-13 10:22:01 +01:00
Brian Clozel 877825b4ab Next development version (v7.0.7-SNAPSHOT) 2026-03-13 09:33:52 +01:00
1633 changed files with 24961 additions and 14723 deletions
@@ -1,20 +0,0 @@
name: Await HTTP Resource
description: 'Waits for an HTTP resource to be available (a HEAD request succeeds)'
inputs:
url:
description: 'URL of the resource to await'
required: true
runs:
using: composite
steps:
- name: Await HTTP resource
shell: bash
run: |
url=${{ inputs.url }}
echo "Waiting for $url"
until curl --fail --head --silent ${{ inputs.url }} > /dev/null
do
echo "."
sleep 60
done
echo "$url is available"
+22
View File
@@ -1,6 +1,18 @@
name: 'Build'
description: 'Builds the project, optionally publishing it to a local deployment repository'
inputs:
commercial-release-repository-url:
description: 'URL of the release repository'
required: false
commercial-repository-password:
description: 'Password for authentication with the commercial repository'
required: false
commercial-repository-username:
description: 'Username for authentication with the commercial repository'
required: false
commercial-snapshot-repository-url:
description: 'URL of the snapshot repository'
required: false
develocity-access-key:
description: 'Access key for authentication with ge.spring.io'
required: false
@@ -46,11 +58,21 @@ runs:
id: build
if: ${{ inputs.publish == 'false' }}
shell: bash
env:
COMMERCIAL_RELEASE_REPO_URL: ${{ inputs.commercial-release-repository-url }}
COMMERCIAL_REPO_PASSWORD: ${{ inputs.commercial-repository-password }}
COMMERCIAL_REPO_USERNAME: ${{ inputs.commercial-repository-username }}
COMMERCIAL_SNAPSHOT_REPO_URL: ${{ inputs.commercial-snapshot-repository-url }}
run: ./gradlew check antora
- name: Publish
id: publish
if: ${{ inputs.publish == 'true' }}
shell: bash
env:
COMMERCIAL_RELEASE_REPO_URL: ${{ inputs.commercial-release-repository-url }}
COMMERCIAL_REPO_PASSWORD: ${{ inputs.commercial-repository-password }}
COMMERCIAL_REPO_USERNAME: ${{ inputs.commercial-repository-username }}
COMMERCIAL_SNAPSHOT_REPO_URL: ${{ inputs.commercial-snapshot-repository-url }}
run: ./gradlew -PdeploymentRepository=$(pwd)/deployment-repository build publishAllPublicationsToDeploymentRepository
- name: Read Version From gradle.properties
id: read-version
@@ -1,6 +1,13 @@
name: Create GitHub Release
description: 'Create the release on GitHub with a changelog'
inputs:
commercial:
description: 'Whether to generate the changelog for the commercial release'
required: true
latest:
description: 'Whether the release is the latest release'
required: false
default: 'false'
milestone:
description: 'Name of the GitHub milestone for which a release will be created'
required: true
@@ -15,13 +22,13 @@ runs:
using: composite
steps:
- name: Generate Changelog
uses: spring-io/github-changelog-generator@86958813a62af8fb223b3fd3b5152035504bcb83 #v0.0.12
uses: spring-io/github-changelog-generator@f7d7a87a3e7c627ecb8c26cf086c38ac5a939721 #v0.0.14
with:
config-file: .github/actions/create-github-release/changelog-generator.yml
config-file: ${{ inputs.commercial && '.github/actions/create-github-release/changelog-generator-commercial.yml' || '.github/actions/create-github-release/changelog-generator-oss.yml' }}
milestone: ${{ inputs.milestone }}
token: ${{ inputs.token }}
- name: Create GitHub Release
shell: bash
env:
GITHUB_TOKEN: ${{ inputs.token }}
run: gh release create ${{ format('v{0}', inputs.milestone) }} --notes-file changelog.md ${{ inputs.pre-release == 'true' && '--prerelease' || '' }}
run: gh release create ${{ format('v{0}', inputs.milestone) }} --notes-file changelog.md ${{ inputs.pre-release == 'true' && '--prerelease' || format('--latest={0}', inputs.latest) }}
@@ -0,0 +1,33 @@
changelog:
repository: spring-projects/spring-framework-commercial
sections:
- title: ":warning: Attention Required"
labels:
- "for: upgrade-attention"
summary:
mode: "member-comment"
config:
prefix: "Attention Required:"
- title: ":star: New Features"
labels:
- "type: enhancement"
- title: ":lady_beetle: Bug Fixes"
labels:
- "type: bug"
- "type: regression"
- title: ":notebook_with_decorative_cover: Documentation"
labels:
- "type: documentation"
- title: ":hammer: Dependency Upgrades"
sort: "title"
labels:
- "type: dependency-upgrade"
contributors:
exclude:
names:
- "bclozel"
- "jhoeller"
- "rstoyanchev"
- "sbrannen"
- "sdeleuze"
- "snicoll"
@@ -27,9 +27,7 @@ changelog:
names:
- "bclozel"
- "jhoeller"
- "poutsma"
- "rstoyanchev"
- "sbrannen"
- "sdeleuze"
- "simonbasle"
- "snicoll"
+10 -12
View File
@@ -29,27 +29,25 @@ runs:
distribution: ${{ inputs.java-early-access == 'true' && 'temurin' || (inputs.java-distribution || 'liberica') }}
java-version: |
${{ inputs.java-early-access == 'true' && format('{0}-ea', inputs.java-version) || inputs.java-version }}
${{ inputs.java-toolchain == 'true' && '17' || '' }}
25
${{ inputs.java-toolchain == 'true' && '25' || '' }}
- name: Set Up Gradle
uses: gradle/actions/setup-gradle@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # v5.0.0
uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # 6.2.0
with:
cache-provider: basic
cache-read-only: false
develocity-access-key: ${{ inputs.develocity-access-key }}
develocity-token-expiry: 4
- name: Configure Gradle Properties
shell: bash
run: |
mkdir -p $HOME/.gradle
echo 'systemProp.user.name=spring-builds+github' >> $HOME/.gradle/gradle.properties
echo 'systemProp.org.gradle.internal.launcher.welcomeMessageEnabled=false' >> $HOME/.gradle/gradle.properties
echo 'org.gradle.daemon=false' >> $HOME/.gradle/gradle.properties
echo 'org.gradle.daemon=4' >> $HOME/.gradle/gradle.properties
echo 'systemProp.user.name=spring-builds+github' >> $GRADLE_USER_HOME/gradle.properties
echo 'systemProp.org.gradle.internal.launcher.welcomeMessageEnabled=false' >> $GRADLE_USER_HOME/gradle.properties
echo 'org.gradle.daemon=false' >> $GRADLE_USER_HOME/gradle.properties
- name: Configure Toolchain Properties
if: ${{ inputs.java-toolchain == 'true' }}
shell: bash
run: |
echo toolchainVersion=${{ inputs.java-version }} >> $HOME/.gradle/gradle.properties
echo systemProp.org.gradle.java.installations.auto-detect=false >> $HOME/.gradle/gradle.properties
echo systemProp.org.gradle.java.installations.auto-download=false >> $HOME/.gradle/gradle.properties
echo systemProp.org.gradle.java.installations.paths=${{ format('$JAVA_HOME_{0}_X64', inputs.java-version) }} >> $HOME/.gradle/gradle.properties
echo toolchainVersion=${{ inputs.java-version }} >> $GRADLE_USER_HOME/gradle.properties
echo systemProp.org.gradle.java.installations.auto-detect=false >> $GRADLE_USER_HOME/gradle.properties
echo systemProp.org.gradle.java.installations.auto-download=false >> $GRADLE_USER_HOME/gradle.properties
echo systemProp.org.gradle.java.installations.paths=${{ format('$JAVA_HOME_{0}_X64', inputs.java-version) }} >> $GRADLE_USER_HOME/gradle.properties
@@ -0,0 +1,7 @@
name: Build Release
runs:
using: composite
steps:
- name: Build Release
shell: bash
run: ./gradlew -PdeploymentRepository=$(pwd)/deployment-repository publishAllPublicationsToDeploymentRepository
@@ -0,0 +1,16 @@
artifactory:
artifacts:
- pattern: "/**/framework-api-*.zip"
properties:
zip.deployed: "false"
zip.name: "spring-framework"
- pattern: "/**/framework-api-*-docs.zip"
properties:
zip.type: "docs"
- pattern: "/**/framework-api-*-schema.zip"
properties:
zip.type: "schema"
maven-central:
excludes:
- "org/springframework/framework-api/**"
- "org/springframework/framework-docs/**"
@@ -0,0 +1,7 @@
name: Test Release
runs:
using: composite
steps:
- name: Test Release
shell: bash
run: ./gradlew check
@@ -1,34 +0,0 @@
name: Sync to Maven Central
description: 'Syncs a release to Maven Central and waits for it to be available for use'
inputs:
central-token-password:
description: 'Password for authentication with central.sonatype.com'
required: true
central-token-username:
description: 'Username for authentication with central.sonatype.com'
required: true
jfrog-cli-config-token:
description: 'Config token for the JFrog CLI'
required: true
spring-framework-version:
description: 'Version of Spring Framework that is being synced to Central'
required: true
runs:
using: composite
steps:
- name: Set Up JFrog CLI
uses: jfrog/setup-jfrog-cli@5b06f730cc5a6f55d78b30753f8583454b08c0aa # v4.8.1
env:
JF_ENV_SPRING: ${{ inputs.jfrog-cli-config-token }}
- name: Download Release Artifacts
shell: bash
run: jf rt download --spec ${{ format('{0}/artifacts.spec', github.action_path) }} --spec-vars 'buildName=${{ format('spring-framework-{0}', inputs.spring-framework-version) }};buildNumber=${{ github.run_number }}'
- name: Sync
uses: spring-io/central-publish-action@0c03960e9b16fdfe70e2443e1d5393cbc3a35622 # v0.3.0
with:
token: ${{ inputs.central-token-password }}
token-name: ${{ inputs.central-token-username }}
- name: Await
uses: ./.github/actions/await-http-resource
with:
url: ${{ format('https://repo.maven.apache.org/maven2/org/springframework/spring-context/{0}/spring-context-{0}.jar', inputs.spring-framework-version) }}
@@ -1,20 +0,0 @@
{
"files": [
{
"aql": {
"items.find": {
"$and": [
{
"@build.name": "${buildName}",
"@build.number": "${buildNumber}",
"path": {
"$nmatch": "org/springframework/framework-api/*"
}
}
]
}
},
"target": "nexus/"
}
]
}
+18
View File
@@ -0,0 +1,18 @@
workflow:
generator:
project:
java:
versions:
primary: 25
workflows:
release-train:
build:
env:
COMMERCIAL_REPO_USERNAME: secrets.COMMERCIAL_ARTIFACTORY_USERNAME
COMMERCIAL_REPO_PASSWORD: secrets.COMMERCIAL_ARTIFACTORY_PASSWORD
COMMERCIAL_RELEASE_REPO_URL: vars.COMMERCIAL_RELEASE_REPO_URL
test:
env:
COMMERCIAL_REPO_USERNAME: secrets.COMMERCIAL_ARTIFACTORY_USERNAME
COMMERCIAL_REPO_PASSWORD: secrets.COMMERCIAL_ARTIFACTORY_PASSWORD
COMMERCIAL_RELEASE_REPO_URL: vars.COMMERCIAL_RELEASE_REPO_URL
+4 -16
View File
@@ -7,27 +7,15 @@ on:
push:
branches:
- '*.x'
permissions:
contents: read
jobs:
build:
backport-issue:
permissions:
contents: read
issues: write
pull-requests: write
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v6
- name: Set up Java
uses: actions/setup-java@v5
- name: Create Backport Issue
uses: spring-io/backport-bot@v0.0.3
with:
distribution: 'liberica'
java-version: 17
- name: Download BackportBot
run: wget https://github.com/spring-io/backport-bot/releases/download/latest/backport-bot-0.0.1-SNAPSHOT.jar
- name: Backport
env:
GITHUB_EVENT: ${{ toJSON(github.event) }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: java -jar backport-bot-0.0.1-SNAPSHOT.jar --github.accessToken="$GITHUB_TOKEN" --github.event_name "$GITHUB_EVENT_NAME" --github.event "$GITHUB_EVENT"
token: ${{ secrets.GITHUB_TOKEN }}
+32 -13
View File
@@ -2,57 +2,76 @@ name: Build and Deploy Snapshot
on:
push:
branches:
- main
- 7.0.x
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
build-and-deploy-snapshot:
name: Build and Deploy Snapshot
if: ${{ github.repository == 'spring-projects/spring-framework' }}
runs-on: ubuntu-latest
if: ${{ github.repository == 'spring-projects/spring-framework' || github.repository == 'spring-projects/spring-framework-commercial' }}
runs-on: ${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }}
timeout-minutes: 60
steps:
- name: Check Out Code
uses: actions/checkout@v6
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Build and Publish
id: build-and-publish
uses: ./.github/actions/build
with:
commercial-release-repository-url: ${{ vars.COMMERCIAL_RELEASE_REPO_URL }}
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 }}
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: 'spring-framework-7.0.x'
build-name: ${{ vars.COMMERCIAL && format('spring-framework-commercial-{0}', '7.0.x') || format('spring-framework-{0}', '7.0.x') }}
folder: 'deployment-repository'
password: ${{ secrets.ARTIFACTORY_PASSWORD }}
repository: 'libs-snapshot-local'
project: ${{ vars.COMMERCIAL && 'spring' }}
repository: ${{ vars.COMMERCIAL && 'spring-enterprise-maven-dev-local' || 'libs-snapshot-local' }}
uri: ${{ vars.COMMERCIAL_DEPLOY_REPO_URL || 'https://repo.spring.io' }}
username: ${{ vars.COMMERCIAL && secrets.COMMERCIAL_ARTIFACTORY_USERNAME || secrets.ARTIFACTORY_USERNAME }}
password: ${{ vars.COMMERCIAL && secrets.COMMERCIAL_ARTIFACTORY_PASSWORD || secrets.ARTIFACTORY_PASSWORD }}
signing-key: ${{ secrets.GPG_PRIVATE_KEY }}
signing-passphrase: ${{ secrets.GPG_PASSPHRASE }}
uri: 'https://repo.spring.io'
username: ${{ secrets.ARTIFACTORY_USERNAME }}
- name: Send Notification
if: always()
uses: ./.github/actions/send-notification
with:
build-scan-url: ${{ steps.build-and-publish.outputs.build-scan-url }}
run-name: ${{ format('{0} | Linux | Java 17', github.ref_name) }}
run-name: ${{ format('{0} | Linux | Java 25', github.ref_name) }}
status: ${{ job.status }}
webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
outputs:
version: ${{ steps.build-and-publish.outputs.version }}
trigger-docs-build:
name: Trigger Docs Build
needs: build-and-deploy-snapshot
if: ${{ !vars.COMMERCIAL }} # remove when commercial support
permissions:
actions: write
runs-on: ${{ vars.UBUNTU_SMALL || 'ubuntu-latest' }}
steps:
- name: Run Deploy Docs Workflow
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh workflow run deploy-docs.yml --repo ${{ github.repository }} -r docs-build -f build-refname=${{ github.ref_name }}
verify:
name: Verify
needs: build-and-deploy-snapshot
uses: ./.github/workflows/verify.yml
secrets:
commercial-repository-password: ${{ secrets.COMMERCIAL_ARTIFACTORY_PASSWORD }}
commercial-repository-username: ${{ secrets.COMMERCIAL_ARTIFACTORY_USERNAME }}
google-chat-webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
repository-password: ${{ secrets.ARTIFACTORY_PASSWORD }}
repository-username: ${{ secrets.ARTIFACTORY_USERNAME }}
opensource-repository-password: ${{ secrets.ARTIFACTORY_PASSWORD }}
opensource-repository-username: ${{ secrets.ARTIFACTORY_USERNAME }}
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
with:
version: ${{ needs.build-and-deploy-snapshot.outputs.version }}
+3 -4
View File
@@ -7,10 +7,9 @@ jobs:
name: Build Pull Request
if: ${{ github.repository == 'spring-projects/spring-framework' }}
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- name: Check Out Code
uses: actions/checkout@v6
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Build
id: build
uses: ./.github/actions/build
@@ -19,7 +18,7 @@ jobs:
uses: ./.github/actions/print-jvm-thread-dumps
- name: Upload Build Reports
if: failure()
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
name: build-reports
path: '**/build/reports/'
path: '**/build/reports/'
+13 -7
View File
@@ -2,31 +2,33 @@ name: CI
on:
schedule:
- cron: '30 9 * * *'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
permissions:
contents: read
jobs:
ci:
name: '${{ matrix.os.name}} | Java ${{ matrix.java.version}}'
if: ${{ github.repository == 'spring-projects/spring-framework' }}
if: ${{ github.repository == 'spring-projects/spring-framework' || github.repository == 'spring-projects/spring-framework-commercial' }}
runs-on: ${{ matrix.os.id }}
timeout-minutes: 60
strategy:
matrix:
os:
- id: ubuntu-latest
- id: ${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }}
name: Linux
java:
- version: 17
toolchain: false
toolchain: true
- version: 21
toolchain: true
- version: 25
toolchain: false
- version: 26
toolchain: true
exclude:
- os:
name: Linux
java:
version: 17
version: 25
steps:
- name: Prepare Windows runner
if: ${{ runner.os == 'Windows' }}
@@ -35,11 +37,15 @@ jobs:
git config --global core.longPaths true
Stop-Service -name Docker
- name: Check Out Code
uses: actions/checkout@v6
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Build
id: build
uses: ./.github/actions/build
with:
commercial-release-repository-url: ${{ vars.COMMERCIAL_RELEASE_REPO_URL }}
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 }}
java-early-access: ${{ matrix.java.early-access || 'false' }}
java-distribution: ${{ matrix.java.distribution }}
-35
View File
@@ -1,35 +0,0 @@
name: Deploy Docs
on:
push:
branches:
- 'main'
- '*.x'
- '!gh-pages'
tags:
- 'v*'
repository_dispatch:
types: request-build-reference # legacy
workflow_dispatch:
permissions:
actions: write
jobs:
build:
name: Dispatch docs deployment
if: github.repository_owner == 'spring-projects'
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v6
with:
fetch-depth: 1
ref: docs-build
- name: Dispatch (partial build)
if: github.ref_type == 'branch'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh workflow run deploy-docs.yml -r $(git rev-parse --abbrev-ref HEAD) -f build-refname=${{ github.ref_name }}
- name: Dispatch (full build)
if: github.ref_type == 'tag'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh workflow run deploy-docs.yml -r $(git rev-parse --abbrev-ref HEAD)
+39
View File
@@ -0,0 +1,39 @@
name: Release Milestone
on:
push:
tags:
- v7.0.0-M[1-9]
- v7.0.0-RC[1-9]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
trigger-docs-build:
name: Trigger Docs Build
permissions:
actions: write
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- name: Determine Version
id: version
run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
- name: Run Deploy Docs Workflow
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh workflow run deploy-docs.yml --repo ${{ github.repository }} -r docs-build -f build-refname=${{ github.ref_name }}
create-github-release:
name: Create GitHub Release
needs:
- trigger-docs-build
runs-on: ubuntu-latest
steps:
- name: Check Out Code
uses: actions/checkout@v6
- name: Create GitHub Release
uses: ./.github/actions/create-github-release
with:
commercial: ${{ vars.COMMERCIAL }}
milestone: ${{ needs.trigger-docs-build.outputs.version }}
pre-release: true
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
+38
View File
@@ -0,0 +1,38 @@
name: Release
on:
push:
tags:
- v7.0.[0-9]+
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
trigger-docs-build:
name: Trigger Docs Build
permissions:
actions: write
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- name: Determine Version
id: version
run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
- name: Run Deploy Docs Workflow
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh workflow run deploy-docs.yml --repo ${{ github.repository }} -r docs-build -f build-refname=${{ github.ref_name }}
create-github-release:
name: Create GitHub Release
needs:
- trigger-docs-build
runs-on: ubuntu-latest
steps:
- name: Check Out Code
uses: actions/checkout@v6
- name: Create GitHub Release
uses: ./.github/actions/create-github-release
with:
commercial: ${{ vars.COMMERCIAL }}
latest: true
milestone: ${{ needs.trigger-docs-build.outputs.version }}
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
-95
View File
@@ -1,95 +0,0 @@
name: Release Milestone
on:
push:
tags:
- v7.0.0-M[1-9]
- v7.0.0-RC[1-9]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
build-and-stage-release:
name: Build and Stage Release
if: ${{ github.repository == 'spring-projects/spring-framework' }}
runs-on: ubuntu-latest
steps:
- name: Check Out Code
uses: actions/checkout@v6
- name: Build and Publish
id: build-and-publish
uses: ./.github/actions/build
with:
develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
publish: true
- name: Stage Release
uses: spring-io/artifactory-deploy-action@926d7f7cc810569395346bf3a4d91b380b3e355b # v0.0.4
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: ${{ format('spring-framework-{0}', steps.build-and-publish.outputs.version)}}
folder: 'deployment-repository'
password: ${{ secrets.ARTIFACTORY_PASSWORD }}
repository: 'libs-staging-local'
signing-key: ${{ secrets.GPG_PRIVATE_KEY }}
signing-passphrase: ${{ secrets.GPG_PASSPHRASE }}
uri: 'https://repo.spring.io'
username: ${{ secrets.ARTIFACTORY_USERNAME }}
outputs:
version: ${{ steps.build-and-publish.outputs.version }}
verify:
name: Verify
needs: build-and-stage-release
uses: ./.github/workflows/verify.yml
secrets:
google-chat-webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
repository-password: ${{ secrets.ARTIFACTORY_PASSWORD }}
repository-username: ${{ secrets.ARTIFACTORY_USERNAME }}
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
with:
staging: true
version: ${{ needs.build-and-stage-release.outputs.version }}
sync-to-maven-central:
name: Sync to Maven Central
needs:
- build-and-stage-release
- verify
runs-on: ubuntu-latest
steps:
- name: Check Out Code
uses: actions/checkout@v6
- name: Sync to Maven Central
uses: ./.github/actions/sync-to-maven-central
with:
central-token-password: ${{ secrets.CENTRAL_TOKEN_PASSWORD }}
central-token-username: ${{ secrets.CENTRAL_TOKEN_USERNAME }}
jfrog-cli-config-token: ${{ secrets.JF_ARTIFACTORY_SPRING }}
spring-framework-version: ${{ needs.build-and-stage-release.outputs.version }}
promote-release:
name: Promote Release
needs:
- build-and-stage-release
- sync-to-maven-central
runs-on: ubuntu-latest
steps:
- name: Set up JFrog CLI
uses: jfrog/setup-jfrog-cli@5b06f730cc5a6f55d78b30753f8583454b08c0aa # v4.8.1
env:
JF_ENV_SPRING: ${{ secrets.JF_ARTIFACTORY_SPRING }}
- name: Promote build
run: jfrog rt build-promote ${{ format('spring-framework-{0}', needs.build-and-stage-release.outputs.version)}} ${{ github.run_number }} libs-milestone-local
create-github-release:
name: Create GitHub Release
needs:
- build-and-stage-release
- promote-release
runs-on: ubuntu-latest
steps:
- name: Check Out Code
uses: actions/checkout@v6
- name: Create GitHub Release
uses: ./.github/actions/create-github-release
with:
milestone: ${{ needs.build-and-stage-release.outputs.version }}
pre-release: true
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
+93
View File
@@ -0,0 +1,93 @@
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
name: "Release Train Build"
run-name: "${{ inputs.callback-ref }} Build"
"on":
workflow_dispatch:
inputs:
callback:
description: "Repository to which a callback should be made upon completion"
required: true
type: "string"
callback-ref:
description: "Ref in the callback repository to which a callback should be made upon completion"
required: true
type: "string"
release-train-maven-repository-url:
description: "URL of a Maven repository to be used to resolve artifacts of projects earlier in the train"
required: true
type: "string"
permissions:
contents: "read"
concurrency:
group: "${{ github.workflow }}-${{ github.ref }}"
jobs:
build-release:
name: "Build Release"
runs-on: "ubuntu22-2-8"
steps:
- name: "Prevent Re-runs"
id: "prevent-re-runs"
run: |-
if [ "$GITHUB_RUN_ATTEMPT" -gt 1 ]; then
echo "Re-runs are prohibited. Use the 'Release Train  Retry' workflow to retry build failures"
exit 1
fi
- name: "Set up Java"
id: "set-up-java"
uses: "actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95" # v5.6.0
with:
distribution: "liberica"
java-version: "25"
- name: "Check Out Code"
id: "check-out-code"
uses: "actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0" # v7.0.0
- name: "Build Release"
id: "build-release"
uses: "./.github/actions/release-train-build"
env:
COMMERCIAL_RELEASE_REPO_URL: "${{ vars.COMMERCIAL_RELEASE_REPO_URL }}"
COMMERCIAL_REPO_PASSWORD: "${{ secrets.COMMERCIAL_ARTIFACTORY_PASSWORD }}"
COMMERCIAL_REPO_USERNAME: "${{ secrets.COMMERCIAL_ARTIFACTORY_USERNAME }}"
RELEASE_TRAIN_MAVEN_REPOSITORY_PASSWORD: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_MAVEN_REPOSITORY_PASSWORD }}"
RELEASE_TRAIN_MAVEN_REPOSITORY_URL: "${{ inputs.release-train-maven-repository-url }}"
RELEASE_TRAIN_MAVEN_REPOSITORY_USERNAME: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_MAVEN_REPOSITORY_USERNAME }}"
- name: "Upload Deployment Repository"
id: "upload-deployment-repository"
uses: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" # v7.0.1
with:
name: "deployment-repository"
path: "deployment-repository/**"
- name: "Upload Deployment Spec"
id: "upload-deployment-spec"
uses: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" # v7.0.1
with:
archive: "false"
if-no-files-found: "ignore"
name: "deployment-spec"
path: ".github/actions/release-train-build/deployment-spec.yml"
- name: "Save Build System Caches"
id: "save-build-system-caches"
uses: "actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9" # v6.1.0
with:
key: "release-train-${{ inputs.callback-ref }}-${{ github.ref_name }}"
path: |-
~/.gradle/caches
~/.gradle/wrapper
- name: "Send Callback"
id: "send-callback"
if: "${{ !cancelled() }}"
env:
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
run: |-
gh workflow run callback \
--repo ${{ inputs.callback }} \
--ref ${{ inputs.callback-ref }} \
--field commit-hash=${{ steps.check-out-code.outputs.commit }} \
--field deployment-repository-artifact-identifier=${{ steps.upload-deployment-repository.outputs.artifact-id }} \
--field deployment-spec-artifact-identifier=${{ steps.upload-deployment-spec.outputs.artifact-id }} \
--field release-branch=${{ github.ref_name }} \
--field release-repository=${{ github.repository }} \
--field result=${{ job.status == 'success' && 'built' || 'build-failed' }} \
--field workflow-run-url=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+55
View File
@@ -0,0 +1,55 @@
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
name: "Release Train Join"
run-name: "${{ inputs.release-train }} Join"
"on":
workflow_dispatch:
inputs:
deployment-destination:
description: "Destination to which the release should be deployed"
options:
- "Maven Central"
- "Spring Enterprise"
required: true
type: "choice"
release-train:
description: "Release train"
required: true
type: "string"
release-train-repository:
default: "spring-io/release-train"
description: "Release train repository"
required: true
type: "string"
permissions:
contents: "none"
jobs:
join-release-train:
name: "Join Release Train"
runs-on: "ubuntu-latest"
steps:
- name: "Join Release Train"
id: "join-release-train"
env:
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
run: |-
run_url=$(
gh workflow run join \
--repo ${{ inputs.release-train-repository }} \
--ref ${{ inputs.release-train }} \
--field commit-hash=${{ github.sha }} \
--field deployment-destination=${{ inputs.deployment-destination == 'Maven Central' && 'maven-central' || 'spring-enterprise' }} \
--field release-branch=${{ github.ref_name }} \
--field release-repository=${{ github.repository }}
)
echo "Dispatched workflow run. Waiting for $run_url to complete."
run_id=${run_url##*/}
watch_exit_code=0
gh run watch $run_id --repo ${{ inputs.release-train-repository }} --exit-status --interval=3 > /dev/null 2>&1 || watch_exit_code=$?
if [[ $watch_exit_code -eq 0 ]]; then
echo "Workflow run succeeded."
else
echo "Workflow run failed."
fi
exit $watch_exit_code
+46
View File
@@ -0,0 +1,46 @@
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
name: "Release Train Leave"
run-name: "${{ inputs.release-train }} Leave"
"on":
workflow_dispatch:
inputs:
release-train:
description: "Release train"
required: true
type: "string"
release-train-repository:
default: "spring-io/release-train"
description: "Release train repository"
required: true
type: "string"
permissions:
contents: "none"
jobs:
leave:
name: "Leave"
runs-on: "ubuntu-latest"
steps:
- name: "Leave"
id: "leave"
env:
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
run: |-
run_url=$(
gh workflow run leave \
--repo ${{ inputs.release-train-repository }} \
--ref ${{ inputs.release-train }} \
--field release-branch=${{ github.ref_name }} \
--field release-repository=${{ github.repository }}
)
echo "Dispatched workflow run. Waiting for $run_url to complete."
run_id=${run_url##*/}
watch_exit_code=0
gh run watch $run_id --repo ${{ inputs.release-train-repository }} --exit-status --interval=3 > /dev/null 2>&1 || watch_exit_code=$?
if [[ $watch_exit_code -eq 0 ]]; then
echo "Workflow run succeeded."
else
echo "Workflow run failed."
fi
exit $watch_exit_code
+47
View File
@@ -0,0 +1,47 @@
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
name: "Release Train Ready"
run-name: "${{ inputs.release-train }} Ready"
"on":
workflow_dispatch:
inputs:
release-train:
description: "Release train"
required: true
type: "string"
release-train-repository:
default: "spring-io/release-train"
description: "Release train repository"
required: true
type: "string"
permissions:
contents: "none"
jobs:
ready:
name: "Ready"
runs-on: "ubuntu-latest"
steps:
- name: "Ready"
id: "ready"
env:
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
run: |-
run_url=$(
gh workflow run ready \
--repo ${{ inputs.release-train-repository }} \
--ref ${{ inputs.release-train }} \
--field commit-hash=${{ github.sha }} \
--field release-branch=${{ github.ref_name }} \
--field release-repository=${{ github.repository }}
)
echo "Dispatched workflow run. Waiting for $run_url to complete."
run_id=${run_url##*/}
watch_exit_code=0
gh run watch $run_id --repo ${{ inputs.release-train-repository }} --exit-status --interval=3 > /dev/null 2>&1 || watch_exit_code=$?
if [[ $watch_exit_code -eq 0 ]]; then
echo "Workflow run succeeded."
else
echo "Workflow run failed."
fi
exit $watch_exit_code
+34
View File
@@ -0,0 +1,34 @@
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
name: "Release Train Retry"
run-name: "${{ inputs.release-train }} Retry"
"on":
workflow_dispatch:
inputs:
release-train:
description: "Release train"
required: true
type: "string"
release-train-repository:
default: "spring-io/release-train"
description: "Release train repository"
required: true
type: "string"
permissions:
contents: "none"
jobs:
trigger-retry:
name: "Trigger Retry"
runs-on: "ubuntu-latest"
steps:
- name: "Trigger Retry"
id: "trigger-retry"
env:
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
run: |-
gh workflow run retry \
--repo ${{ inputs.release-train-repository }} \
--ref ${{ inputs.release-train }} \
--field release-branch=${{ github.ref_name }} \
--field release-repository=${{ github.repository }}
+84
View File
@@ -0,0 +1,84 @@
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
name: "Release Train Test"
run-name: "${{ inputs.callback-ref }} Test"
"on":
workflow_dispatch:
inputs:
callback:
description: "Repository to which a callback should be made upon completion"
required: true
type: "string"
callback-ref:
description: "Ref in the callback repository to which a callback should be made upon completion"
required: true
type: "string"
release-train-maven-repository-url:
description: "URL of a Maven repository to be used to resolve artifacts of projects earlier in the train"
required: true
type: "string"
permissions:
contents: "read"
concurrency:
group: "${{ github.workflow }}-${{ github.ref }}"
jobs:
test-release:
name: "Test Release"
runs-on: "ubuntu22-2-8"
steps:
- name: "Prevent Re-runs"
id: "prevent-re-runs"
run: |-
if [ "$GITHUB_RUN_ATTEMPT" -gt 1 ]; then
echo "Re-runs are prohibited. Use the 'Release Train  Retry' workflow to retry test failures"
exit 1
fi
- name: "Set up Java"
id: "set-up-java"
uses: "actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95" # v5.6.0
with:
distribution: "liberica"
java-version: "25"
- name: "Check Out Code"
id: "check-out-code"
uses: "actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0" # v7.0.0
- name: "Restore Build System Caches"
id: "restore-build-system-caches"
uses: "actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9" # v6.1.0
with:
key: "release-train-${{ inputs.callback-ref }}-${{ github.ref_name }}"
path: |-
~/.gradle/caches
~/.gradle/wrapper
- name: "Test Release"
id: "test-release"
uses: "./.github/actions/release-train-test"
env:
COMMERCIAL_RELEASE_REPO_URL: "${{ vars.COMMERCIAL_RELEASE_REPO_URL }}"
COMMERCIAL_REPO_PASSWORD: "${{ secrets.COMMERCIAL_ARTIFACTORY_PASSWORD }}"
COMMERCIAL_REPO_USERNAME: "${{ secrets.COMMERCIAL_ARTIFACTORY_USERNAME }}"
RELEASE_TRAIN_MAVEN_REPOSITORY_PASSWORD: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_MAVEN_REPOSITORY_PASSWORD }}"
RELEASE_TRAIN_MAVEN_REPOSITORY_URL: "${{ inputs.release-train-maven-repository-url }}"
RELEASE_TRAIN_MAVEN_REPOSITORY_USERNAME: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_MAVEN_REPOSITORY_USERNAME }}"
- name: "Send Callback"
id: "send-callback"
if: "${{ !cancelled() }}"
env:
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
run: |-
gh workflow run callback \
--repo ${{ inputs.callback }} \
--ref ${{ inputs.callback-ref }} \
--field commit-hash=${{ steps.check-out-code.outputs.commit }} \
--field release-branch=${{ github.ref_name }} \
--field release-repository=${{ github.repository }} \
--field result=${{ job.status == 'success' && 'tested' || 'test-failed' }} \
--field workflow-run-url=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
- name: "Upload Build System Reports"
id: "upload-build-system-reports"
if: "${{ failure() }}"
uses: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" # v7.0.1
with:
name: "build-system-reports"
path: "**/build/reports"
-93
View File
@@ -1,93 +0,0 @@
name: Release
on:
push:
tags:
- v7.0.[0-9]+
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
build-and-stage-release:
name: Build and Stage Release
if: ${{ github.repository == 'spring-projects/spring-framework' }}
runs-on: ubuntu-latest
steps:
- name: Check Out Code
uses: actions/checkout@v6
- name: Build and Publish
id: build-and-publish
uses: ./.github/actions/build
with:
develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
publish: true
- name: Stage Release
uses: spring-io/artifactory-deploy-action@926d7f7cc810569395346bf3a4d91b380b3e355b # v0.0.4
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: ${{ format('spring-framework-{0}', steps.build-and-publish.outputs.version)}}
folder: 'deployment-repository'
password: ${{ secrets.ARTIFACTORY_PASSWORD }}
repository: 'libs-staging-local'
signing-key: ${{ secrets.GPG_PRIVATE_KEY }}
signing-passphrase: ${{ secrets.GPG_PASSPHRASE }}
uri: 'https://repo.spring.io'
username: ${{ secrets.ARTIFACTORY_USERNAME }}
outputs:
version: ${{ steps.build-and-publish.outputs.version }}
verify:
name: Verify
needs: build-and-stage-release
uses: ./.github/workflows/verify.yml
secrets:
google-chat-webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
repository-password: ${{ secrets.ARTIFACTORY_PASSWORD }}
repository-username: ${{ secrets.ARTIFACTORY_USERNAME }}
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
with:
staging: true
version: ${{ needs.build-and-stage-release.outputs.version }}
sync-to-maven-central:
name: Sync to Maven Central
needs:
- build-and-stage-release
- verify
runs-on: ubuntu-latest
steps:
- name: Check Out Code
uses: actions/checkout@v6
- name: Sync to Maven Central
uses: ./.github/actions/sync-to-maven-central
with:
central-token-password: ${{ secrets.CENTRAL_TOKEN_PASSWORD }}
central-token-username: ${{ secrets.CENTRAL_TOKEN_USERNAME }}
jfrog-cli-config-token: ${{ secrets.JF_ARTIFACTORY_SPRING }}
spring-framework-version: ${{ needs.build-and-stage-release.outputs.version }}
promote-release:
name: Promote Release
needs:
- build-and-stage-release
- sync-to-maven-central
runs-on: ubuntu-latest
steps:
- name: Set up JFrog CLI
uses: jfrog/setup-jfrog-cli@5b06f730cc5a6f55d78b30753f8583454b08c0aa # v4.8.1
env:
JF_ENV_SPRING: ${{ secrets.JF_ARTIFACTORY_SPRING }}
- name: Promote build
run: jfrog rt build-promote ${{ format('spring-framework-{0}', needs.build-and-stage-release.outputs.version)}} ${{ github.run_number }} libs-release-local
create-github-release:
name: Create GitHub Release
needs:
- build-and-stage-release
- promote-release
runs-on: ubuntu-latest
steps:
- name: Check Out Code
uses: actions/checkout@v6
- name: Create GitHub Release
uses: ./.github/actions/create-github-release
with:
milestone: ${{ needs.build-and-stage-release.outputs.version }}
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
branch: [ '6.2.x', 'main' ]
branch: [ '6.2.x', '7.0.x', 'main' ]
steps:
- uses: spring-io/spring-doc-actions/update-antora-spring-ui@5a57bcc6a0da2a1474136cf29571b277850432bc
name: Update
+24 -13
View File
@@ -12,31 +12,39 @@ on:
required: true
type: string
secrets:
commercial-repository-password:
description: 'Password for authentication with the commercial repository'
required: false
commercial-repository-username:
description: 'Username for authentication with the commercial repository'
required: false
google-chat-webhook-url:
description: 'Google Chat Webhook URL'
required: true
repository-password:
description: 'Password for authentication with the repository'
opensource-repository-password:
description: 'Password for authentication with the open-source repository'
required: false
repository-username:
description: 'Username for authentication with the repository'
opensource-repository-username:
description: 'Username for authentication with the open-source repository'
required: false
token:
description: 'Token to use for authentication with GitHub'
required: true
permissions:
contents: read
jobs:
verify:
name: Verify
runs-on: ubuntu-latest
runs-on: ${{ vars.UBUNTU_SMALL || 'ubuntu-latest' }}
steps:
- name: Check Out Release Verification Tests
uses: actions/checkout@v6
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: 'v0.0.2'
ref: 'v0.0.3'
repository: spring-projects/spring-framework-release-verification
token: ${{ secrets.token }}
- name: Check Out Send Notification Action
uses: actions/checkout@v6
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
path: send-notification
sparse-checkout: .github/actions/send-notification
@@ -46,8 +54,9 @@ jobs:
distribution: 'liberica'
java-version: 17
- name: Set Up Gradle
uses: gradle/actions/setup-gradle@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # v5.0.0
uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0
with:
cache-provider: basic
cache-read-only: false
- name: Configure Gradle Properties
shell: bash
@@ -56,15 +65,17 @@ jobs:
echo 'org.gradle.daemon=false' >> $HOME/.gradle/gradle.properties
- name: Run Release Verification Tests
env:
RVT_OSS_REPOSITORY_PASSWORD: ${{ secrets.repository-password }}
RVT_OSS_REPOSITORY_USERNAME: ${{ secrets.repository-username }}
RVT_RELEASE_TYPE: oss
RVT_COMMERCIAL_REPOSITORY_PASSWORD: ${{ secrets.commercial-repository-password }}
RVT_COMMERCIAL_REPOSITORY_USERNAME: ${{ secrets.commercial-repository-username }}
RVT_OSS_REPOSITORY_PASSWORD: ${{ secrets.opensource-repository-password }}
RVT_OSS_REPOSITORY_USERNAME: ${{ secrets.opensource-repository-username }}
RVT_RELEASE_TYPE: ${{ vars.COMMERCIAL && 'commercial' || 'oss' }}
RVT_STAGING: ${{ inputs.staging }}
RVT_VERSION: ${{ inputs.version }}
run: ./gradlew spring-framework-release-verification-tests:test
- name: Upload Build Reports on Failure
if: failure()
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
name: build-reports
path: '**/build/reports/'
+4
View File
@@ -9,6 +9,7 @@ ivy-cache
argfile*
activemq-data/
classes/
.cursor/
# Log files
jxl.log
@@ -51,6 +52,9 @@ atlassian-ide-plugin.xml
# VS Code
.vscode/
# Claude artifacts
.claude/*
cached-antora-playbook.yml
node_modules
+6 -12
View File
@@ -6,7 +6,7 @@ plugins {
id 'com.github.bjornvester.xjc' version '1.8.2' 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.11' apply false
id 'io.spring.nullability' version '0.0.15' apply false
}
ext {
@@ -18,16 +18,8 @@ description = "Spring Framework"
configure(allprojects) { project ->
apply plugin: "org.springframework.build.localdev"
apply plugin: "org.springframework.build.repositories"
group = "org.springframework"
repositories {
mavenCentral()
if (version.contains('-')) {
maven { url = "https://repo.spring.io/milestone" }
}
if (version.endsWith('-SNAPSHOT')) {
maven { url = "https://repo.spring.io/snapshot" }
}
}
configurations.all {
resolutionStrategy {
cacheChangingModulesFor 0, "seconds"
@@ -61,7 +53,9 @@ configure([rootProject] + javaProjects) { project ->
testImplementation("org.junit.platform:junit-platform-suite")
testImplementation("org.mockito:mockito-core")
testImplementation("org.mockito:mockito-junit-jupiter")
testImplementation("io.mockk:mockk")
testImplementation("io.mockk:mockk") {
exclude group: 'junit', module: 'junit'
}
testImplementation("org.assertj:assertj-core")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
testRuntimeOnly("org.apache.logging.log4j:log4j-core")
@@ -72,7 +66,7 @@ configure([rootProject] + javaProjects) { project ->
//"https://jakarta.ee/specifications/platform/11/apidocs/",
"https://docs.hibernate.org/orm/7.2/javadocs/",
"https://www.quartz-scheduler.org/api/2.3.0/",
"https://hc.apache.org/httpcomponents-client-5.6.x/current/httpclient5/apidocs/",
"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/",
+4
View File
@@ -50,6 +50,10 @@ gradlePlugin {
id = "org.springframework.build.multiReleaseJar"
implementationClass = "org.springframework.build.multirelease.MultiReleaseJarPlugin"
}
repositoriesPlugin {
id = "org.springframework.build.repositories"
implementationClass = "org.springframework.build.RepositoriesPlugin"
}
optionalDependenciesPlugin {
id = "org.springframework.build.optional-dependencies"
implementationClass = "org.springframework.build.optional.OptionalDependenciesPlugin"
@@ -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.3.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);
}
}
}
@@ -0,0 +1,98 @@
/*
* Copyright 2002-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.build;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
/**
* Plugin that configures the OSS, commercial and release train repositories in the build.
*
* @author Brian Clozel
*/
public class RepositoriesPlugin implements Plugin <Project> {
@Override
public void apply(Project project) {
configureOssRepositories(project);
configureCommercialRepositories(project);
configureReleaseTrainRepository(project);
}
private void configureOssRepositories(Project project) {
project.getRepositories().mavenCentral();
if (project.getVersion().toString().contains("-")) {
project.getRepositories().maven(repository -> {
repository.setName("spring-oss-milestone");
repository.setUrl("https://repo.spring.io/milestone/");
});
}
if (project.getVersion().toString().endsWith("-SNAPSHOT")) {
project.getRepositories().maven(repository -> {
repository.setName("spring-oss-snapshot");
repository.setUrl("https://repo.spring.io/snapshot/");
});
}
}
private void configureCommercialRepositories(Project project) {
String releaseRepositoryUrl = getEnv("COMMERCIAL_RELEASE_REPO_URL");
if (releaseRepositoryUrl != null) {
project.getRepositories().maven((repository) -> {
repository.setName("spring-commercial-release");
repository.setUrl(releaseRepositoryUrl);
repository.credentials((creds) -> {
creds.setUsername(System.getenv("COMMERCIAL_REPO_USERNAME"));
creds.setPassword(System.getenv("COMMERCIAL_REPO_PASSWORD"));
});
});
}
String snapshotRepositoryUrl = getEnv("COMMERCIAL_SNAPSHOT_REPO_URL");
if (snapshotRepositoryUrl != null && project.getVersion().toString().endsWith("-SNAPSHOT")) {
project.getRepositories().maven((repository) -> {
repository.setName("spring-commercial-snapshot");
repository.setUrl(snapshotRepositoryUrl);
repository.credentials((creds) -> {
creds.setUsername(System.getenv("COMMERCIAL_REPO_USERNAME"));
creds.setPassword(System.getenv("COMMERCIAL_REPO_PASSWORD"));
});
});
}
}
private void configureReleaseTrainRepository(Project project) {
String releaseTrainRepositoryUrl = getEnv("RELEASE_TRAIN_MAVEN_REPOSITORY_URL");
if (releaseTrainRepositoryUrl != null) {
project.getRepositories().maven(repository -> {
repository.setName("spring-release-train");
repository.setUrl(releaseTrainRepositoryUrl);
repository.credentials((creds) -> {
creds.setUsername(System.getenv("RELEASE_TRAIN_MAVEN_REPOSITORY_USERNAME"));
creds.setPassword(System.getenv("RELEASE_TRAIN_MAVEN_REPOSITORY_PASSWORD"));
});
});
}
}
/**
* Returns the environment variable's value, or {@code null} if it is unset or blank.
*/
private static String getEnv(String name) {
String value = System.getenv(name);
return (value != null && !value.isBlank()) ? value : null;
}
}
@@ -71,7 +71,7 @@ class TestConventions {
"junit.platform.discovery.issue.severity.critical", "INFO"
));
if (project.hasProperty("testGroups")) {
test.systemProperty("testGroups", project.getProperties().get("testGroups"));
test.systemProperty("testGroups", project.findProperty("testGroups"));
}
test.jvmArgs(
"--add-opens=java.base/java.lang=ALL-UNNAMED",
@@ -82,7 +82,7 @@ class TestConventions {
private void configureByteBuddyAgent(Project project) {
if (project.hasProperty("byteBuddyVersion")) {
String byteBuddyVersion = (String) project.getProperties().get("byteBuddyVersion");
String byteBuddyVersion = (String) project.findProperty("byteBuddyVersion");
Configuration byteBuddyAgentConfig = project.getConfigurations().create("byteBuddyAgentConfig");
byteBuddyAgentConfig.setTransitive(false);
Dependency byteBuddyAgent = project.getDependencies().create("net.bytebuddy:byte-buddy-agent:" + byteBuddyVersion);
+4 -4
View File
@@ -15,8 +15,8 @@ repositories {
}
dependencies {
moduleProjects.each { moduleProject ->
javadoc moduleProject
rootProject.ext.moduleProjects.each { moduleProject ->
javadoc project(moduleProject.path)
}
}
@@ -52,7 +52,7 @@ javadoc {
// ensure the javadoc process can resolve types compiled from .aj sources
springAspectsOutput
)
classpath += files(moduleProjects.collect { it.sourceSets.main.compileClasspath })
classpath += files(rootProject.ext.moduleProjects.collect { it.sourceSets.main.compileClasspath })
}
}
@@ -96,7 +96,7 @@ tasks.register('schemaZip', Zip) {
description = "Builds -${archiveClassifier} archive containing all " +
"XSDs for deployment at https://springframework.org/schema."
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
moduleProjects.each { module ->
rootProject.ext.moduleProjects.each { module ->
def Properties schemas = new Properties();
module.sourceSets.main.resources.find {
+1 -1
View File
@@ -8,7 +8,7 @@ group = "org.springframework"
dependencies {
constraints {
parent.moduleProjects.sort { "$it.name" }.each {
api it
api project(it.path)
}
}
}
+3 -3
View File
@@ -15,8 +15,8 @@ content:
# https://docs.antora.org/antora/latest/playbook/content-refname-matching/
# branches: We include snapshots for main, 6.2.x, and 7.0.x to 9.*.x.
branches: ['main', '6.2.x', '{7..9}.+({0..9}).x']
# tags: We effectively include all releases from 6.0.9 to 9.*.*.
tags: ['v{6..9}.+({0..9}).+({0..9})?(-{RC,M}*)', '!(v6.0.{0..8})', '!(v6.0.0-{RC,M}{0..9})']
# tags: include all releases from 6.2.0 to 9.*.*.
tags: ['v6.2.+({0..9})', 'v{7..9}.+({0..9}).+({0..9})?(-{RC,M}*)']
start_path: framework-docs
asciidoc:
extensions:
@@ -38,4 +38,4 @@ runtime:
failure_level: warn
ui:
bundle:
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.25/ui-bundle.zip
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.26/ui-bundle.zip
+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/main'
spring-framework-code: '{spring-framework-github}/tree/7.0.x'
spring-framework-issues: '{spring-framework-github}/issues'
spring-framework-wiki: '{spring-framework-github}/wiki'
# Docs
+4
View File
@@ -20,6 +20,10 @@ antora {
]
}
node {
version = '24.15.0'
}
tasks.named("generateAntoraYml") {
asciidocAttributes = project.provider( {
return ["spring-version": project.version ]
+2
View File
@@ -198,6 +198,7 @@
*** xref:web/webmvc/mvc-uri-building.adoc[]
*** xref:web/webmvc/mvc-ann-async.adoc[]
*** xref:web/webmvc/mvc-range.adoc[]
*** xref:web/webmvc/mvc-data-binding.adoc[]
*** xref:web/webmvc-cors.adoc[]
*** xref:web/webmvc-versioning.adoc[]
*** xref:web/webmvc/mvc-ann-rest-exceptions.adoc[]
@@ -295,6 +296,7 @@
*** xref:web/webflux-functional.adoc[]
*** xref:web/webflux/uri-building.adoc[]
*** xref:web/webflux/range.adoc[]
*** xref:web/webflux/data-binding.adoc[]
*** xref:web/webflux-cors.adoc[]
*** xref:web/webflux-versioning.adoc[]
*** xref:web/webflux/ann-rest-exceptions.adoc[]
@@ -74,6 +74,17 @@ expressions used in XML bean definitions, `@Value`, etc.
| The mode to use when compiling expressions for the
xref:core/expressions/evaluation.adoc#expressions-compiler-configuration[Spring Expression Language].
| `spring.expression.maxBigPowerBits`
| The default maximum number of bits permitted in the result of a `BigDecimal` or
`BigInteger` power operation within a
xref:core/expressions/evaluation.adoc#expressions-parser-configuration[Spring Expression Language]
expression.
| `spring.expression.maxOperations`
| The default maximum number of operations permitted during
xref:core/expressions/evaluation.adoc#expressions-parser-configuration[Spring Expression Language]
expression evaluation.
| `spring.getenv.ignore`
| Instructs Spring to ignore operating system environment variables if a Spring
`Environment` property -- for example, a placeholder in a configuration String -- isn't
@@ -138,6 +149,11 @@ xref:testing/testcontext-framework/ctx-management/context-pausing.adoc[Context P
in the _Spring TestContext Framework_. See
xref:testing/testcontext-framework/ctx-management/failure-threshold.adoc[Context Failure Threshold].
| `spring.test.extension.context.scope`
| The default _extension context scope_ used by the `SpringExtension` in `@Nested` test
class hierarchies. See
xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-springextensionconfig[`@SpringExtensionConfig`].
| `spring.test.enclosing.configuration`
| The default _enclosing configuration inheritance mode_ to use if
`@NestedTestConfiguration` is not present on a test class. See
@@ -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:
@@ -378,7 +378,7 @@ The container also supports creating a bean with {spring-framework-api}++/beans/
. The custom arguments require dynamic introspection of a matching constructor or factory method.
Those arguments cannot be detected by AOT, so the necessary reflection hints will have to be provided manually.
. By-passing the instance supplier means that all other optimizations after creation are skipped as well.
. Bypassing the instance supplier means that all other optimizations after creation are skipped as well.
For instance, autowiring on fields and methods will be skipped as they are handled in the instance supplier.
Rather than having prototype-scoped beans created with custom arguments, we recommend a manual factory pattern where a bean is responsible for the creation of the instance.
@@ -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]
@@ -274,7 +274,7 @@ Kotlin::
Next, you can provide the information for the candidate bean definitions. You can add
`<qualifier/>` tags as sub-elements of the `<bean/>` tag and then specify the `type` and
`value` to match your custom qualifier annotations. The type is matched against the
fully-qualified class name of the annotation. Alternately, as a convenience if no risk of
fully-qualified class name of the annotation. Alternatively, as a convenience if no risk of
conflicting names exists, you can use the short class name. The following example
demonstrates both approaches:
@@ -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.
@@ -575,7 +575,7 @@ Kotlin::
----
======
NOTE: Do not define such beans to be lazy as the `ApplicationContext` will honour that and will not register the method to listen to events.
NOTE: Do not define such beans to be lazy as the `ApplicationContext` will honor that and will not register the method to listen to events.
The method signature once again declares the event type to which it listens,
but, this time, with a flexible name and without implementing a specific listener interface.
@@ -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]
@@ -610,6 +610,19 @@ Kotlin::
See the {spring-framework-api}/context/annotation/Conditional.html[`@Conditional`]
javadoc for more detail.
[NOTE]
====
A `@Conditional` annotation declared on an enclosing `@Configuration` class is only
applied to the registration of a nested `@Configuration` class if the nested class is
reached through the parser's recursion from its enclosing class, or via `@Import`. If a
nested class is discovered independently of its enclosing class — for example, via
`@ComponentScan` or by directly registering it against the application context — it is
processed using only its own `@Conditional` annotations. Thus, if you wish to ensure that
the same `@Conditional` annotations apply in such scenarios, you must redeclare the
relevant annotations on the nested class, or extract them into a composed annotation
which you apply to both the enclosing class and the nested class.
====
[[beans-java-combining]]
== Combining Java and XML Configuration
@@ -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.
@@ -253,6 +253,175 @@ properties. Alternatively, configure custom accessors via
`SimpleEvaluationContext.forPropertyAccessors(...)`, potentially disable assignment, and
optionally activate method resolution and/or a type converter through the builder.
[[expressions-evaluation-context-security]]
=== Security Considerations
SpEL is a powerful expression language that can invoke constructors and methods, read and
write properties and fields, and reference beans all backed by reflection. Because of
this power, evaluating a SpEL expression obtained from an untrusted source is inherently
dangerous and should generally be avoided, since doing so can effectively grant that
source the ability to execute arbitrary code within the application, regardless of which
`EvaluationContext` implementation is used.
Throughout this section, a source of a SpEL expression is considered "trusted" only if it
is a developer of the application or an administrator responsible for configuring or
operating the application. Any other source of a SpEL expression must be treated as
untrusted for example, an expression supplied by an end user of the application or
received from an external system.
[WARNING]
====
`StandardEvaluationContext` exposes the complete SpEL language and must *never* be used
to evaluate an expression obtained from an untrusted source.
====
Although `SimpleEvaluationContext` restricts the SpEL language to a subset of its
features, that restriction is provided on a best-effort basis and does not guarantee that
expression evaluation is safe. Since an expression can potentially invoke any property,
method, or function reachable via the configured root object, property accessors, method
resolvers, variables, and functions, care must be taken if you choose to evaluate
expressions from an untrusted source. It is therefore the responsibility of the code that
configures an `EvaluationContext` for example, by supplying a root object or by
registering property accessors, resolvers, variables, or functions to ensure that none
of the objects reachable via the context expose operations that would be dangerous if
invoked by an expression from an untrusted source.
Furthermore, a property "getter" reachable from an expression is not necessarily a pure,
side-effect-free read operation. A JavaBean-style accessor (such as `getName()` or
`isActive()`) and a plain accessor method used to support data classes such as Java
records and Kotlin data classes (such as `name()`) are indistinguishable from a method
that performs an action and happens to return a value (that is, a method which is
*accessor-shaped*). For example, the `public boolean delete()` method in `java.io.File`
looks like a plain accessor method to SpEL. Specifically, neither
`ReflectivePropertyAccessor` nor `DataBindingPropertyAccessor` can determine whether such
a method is free of side effects. Moreover, restricting a `SimpleEvaluationContext` to
read-only data binding governs only whether *assignment* to a property is permitted: it
does not verify that reading a property is side-effect-free. When exposing a root object
or other reachable object to an untrusted expression, you must ensure that none of its
accessor-shaped methods perform an action that would be unsafe if triggered by that
expression.
[NOTE]
.What makes a method "accessor-shaped"?
====
A method is accessor-shaped if it is `public`, takes no arguments, and returns a value
the same shape that `ReflectivePropertyAccessor` and `DataBindingPropertyAccessor` look
for when resolving a property "getter" by name. That shape says nothing about whether
invoking the method is actually free of side effects. For example, the following methods
are all accessor-shaped, but only some of them are safe to invoke as a property read.
Side-effect-free (safe to expose as properties):
* `getName()` and `isActive()`: conventional JavaBean-style accessors.
* `name()` and `active()`: plain accessor methods used by data classes such as Java
records and Kotlin data classes.
Side-effecting (unsafe to expose as properties, despite the identical shape):
* `java.io.File#delete()`: deletes the underlying file and returns whether the deletion
succeeded.
* `java.util.Queue#poll()`: removes and returns the head element, mutating the queue.
* `java.util.concurrent.atomic.AtomicInteger#incrementAndGet()`: increments and returns
a counter, mutating it.
If an untrusted expression can reference `someFile.delete`, `someQueue.poll`, or
`someCounter.incrementAndGet` as a property, SpEL invokes the corresponding method just
as readily as it would invoke a genuine getter.
====
[[expressions-evaluation-context-object-design]]
=== Object Design
Similar to the design guidance for
xref:web/webmvc/mvc-data-binding.adoc#mvc-data-binding-design[web data binding], you
should carefully design any object that may be reached from a SpEL expression evaluated
against untrusted input. This applies not only to the root object supplied to an
`EvaluationContext` but also to every object that such an expression can navigate to from
that root object for example, an object returned by a property, a method, an index
operation, a variable, or a function.
When exposing an object to expressions from an untrusted source, consider the following
recommendations.
Use a dedicated type::
Prefer a dedicated type, designed specifically to be evaluated against untrusted
expressions, over passing an existing domain or infrastructure type "as is". A
dedicated type lets you control exactly which properties and methods are reachable from
an expression, rather than exposing the full surface area of a class such as a JPA
entity, `java.io.File`, or a JDBC `Connection` most of which were never designed with
SpEL evaluation in mind.
Prefer immutability::
An immutable type for example, a Java record or a Kotlin data class exposing only
`val` properties rules out property writes and eliminates any concern that a "getter"
might mutate state as a side effect, since there is no mutable state to affect.
Immutability does not, on its own, rule out an accessor-shaped method with an external
side effect (such as a network call or a file system operation), but it removes an
entire class of risk.
Limit scope::
Expose only the properties and methods that the expression is expected to use, and
nothing more. Because a `PropertyAccessor` cannot restrict access to specific
properties or methods on a per-expression basis, every accessor-shaped method reachable
on an exposed object is reachable by any expression that can reach that object
regardless of which property or method the application intended the expression to use.
Audit accessor-shaped methods::
Review every accessor-shaped method exposed by a type before making it reachable from
an untrusted expression, keeping the <<expressions-evaluation-context-security,
security considerations>> discussed above in mind. None of the reachable methods should
perform an action that would be unsafe if triggered by that expression.
[WARNING]
====
These recommendations apply transitively. If the root object exposes a property or method
that returns another object, and an untrusted expression can navigate to it (for example,
`rootObject.child.grandchild`), the nested object is just as reachable as the root object
itself and must meet the same design requirements. The same is true for an object reached
via indexing (for example, `rootObject.items[0]` or `rootObject.items['key']`): whatever
is returned by the index operation is just as reachable as any other nested object.
====
[[expressions-evaluation-context-lifecycle]]
=== Lifecycle and Reuse
For performance, the AST nodes that make up a parsed `Expression` may cache the specific
`PropertyAccessor`, `IndexAccessor`, `MethodExecutor`, or `ConstructorExecutor` that
satisfied a previous evaluation, so that later evaluations of the same node can avoid
asking every registered accessor or resolver in turn. Understanding this caching behavior
is essential to using `Expression` and `EvaluationContext` correctly, in addition to the
<<expressions-evaluation-context-security,security considerations>> discussed previously.
A parsed `Expression` is designed to be created once and evaluated repeatedly, and doing
so is both supported and encouraged. In particular:
* A parsed `Expression` may be evaluated against different root objects, and against
different `EvaluationContext` instances of the *same type and with equivalent
configuration* for example, several `StandardEvaluationContext` instances each
registering the same kind of custom `PropertyAccessor`. Changing the accessors or
resolvers registered with a context between evaluations of the same expression is
atypical and generally not advised, but is expected to work correctly: the registered
state of the *current* context is what is consulted, not a snapshot taken during an
earlier evaluation.
* A parsed `Expression` must *not* be evaluated first against a context with one set of
security implications and later against a context with different, typically more
restrictive, security implications for example, first against a
`StandardEvaluationContext` and later against a `SimpleEvaluationContext`. Doing so is
analogous to executing a database query on behalf of an administrator, caching the
resulting administrator-privileged execution plan, and then reusing that cached plan for
a lower-privileged user while expecting the lower-privileged user's restrictions to
apply: cached state from the first, more permissive evaluation may be reused during the
second, and the second context's restrictions cannot be reliably enforced as a result.
If the same expression string must be evaluated under contexts with different security
implications, parse it into *distinct* `Expression` instances, one per context.
[WARNING]
====
Reusing a single parsed `Expression` across `EvaluationContext` instances with different
security implications is not a supported usage pattern and must be avoided, regardless of
which `EvaluationContext` implementations are involved.
====
[[expressions-type-conversion]]
=== Type Conversion
@@ -319,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]
@@ -342,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);
@@ -367,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)
@@ -387,7 +562,8 @@ Kotlin::
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
@@ -395,6 +571,32 @@ set a JVM system property or Spring property named `spring.context.expression.ma
to the maximum expression length needed by your application (see
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 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
decimal number with 300,000 digits. Power operations involving large base values or large
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 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]).
[[expressions-spel-compilation]]
== SpEL Compilation
@@ -433,9 +635,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.
@@ -477,8 +679,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);
@@ -493,8 +697,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)
@@ -2,4 +2,12 @@
= Language Reference
:page-section-summary-toc: 1
This section describes how the Spring Expression Language works.
Spring Expression Language (SpEL) expressions are composed of a sequence of tokens such
as literals, operators, method invocations, and so forth.
Whitespace can be used freely between tokens to format and improve the readability of
expressions. Specifically, the `\s` (space), `\t` (tab), `\r` (carriage return), and `\n`
(newline) characters are all valid separators between tokens. However, whitespace is
ignored by the expression parser unless it is part of a string literal.
The following sections describe the features and syntax of SpEL.
@@ -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>>
@@ -270,7 +270,7 @@ is applicable for typical implementations of indexed structures.
NOTE: `ReflectiveIndexAccessor` also implements `CompilableIndexAccessor` in order to
support xref:core/expressions/evaluation.adoc#expressions-spel-compilation[compilation]
to bytecode for read access. Note, however, that the configured read-method must be
invokable via a `public` class or `public` interface for compilation to succeed.
invocable via a `public` class or `public` interface for compilation to succeed.
The following code listings define a `Color` enum and `FruitMap` type that behaves like a
map but does not implement the `java.util.Map` interface. Thus, if you want to index into
@@ -25,7 +25,8 @@ public void sendNotification() {
By default, the method invocation will be retried for any exception thrown: with at most
3 retry attempts (`maxRetries = 3`) after an initial failure, and a delay of 1 second
between attempts.
between attempts. If all attempts have failed and the retry policy has been exhausted,
the last original exception from the target method will be propagated to the caller.
[NOTE]
====
@@ -69,17 +70,24 @@ Or for 4 retry attempts and an exponential back-off strategy with a bit of jitte
[source,java,indent=0,subs="verbatim,quotes"]
----
@Retryable(
includes = MessageDeliveryException.class,
maxRetries = 4,
delay = 100,
jitter = 10,
multiplier = 2,
maxDelay = 1000)
includes = MessageDeliveryException.class,
maxRetries = 4,
delay = 100,
jitter = 10,
multiplier = 2,
maxDelay = 1000)
public void sendNotification() {
this.jmsClient.destination("notifications").send(...);
}
----
[NOTE]
====
When `delay` is `0` combined with a positive `jitter`, the delay never grows
regardless of any configured `multiplier`, so the full configured `jitter` is
applied directly as a random delay in the range from `0` to `min(jitter, maxDelay)`.
====
Last but not least, `@Retryable` also works for reactive methods with a reactive return
type, decorating the pipeline with Reactor's retry capabilities:
@@ -99,6 +107,13 @@ TIP: Several attributes in `@Retryable` have `String` variants that provide prop
placeholder and SpEL support, as an alternative to the specifically typed annotation
attributes used in the above examples.
[TIP]
====
During `@Retryable` processing, Spring publishes a `MethodRetryEvent` for every exception
coming out of the target method. This can be used to track/log all original exceptions
whereas the caller of the `@Retryable` method will only ever see the last exception.
====
[[resilience-annotations-concurrencylimit]]
== `@ConcurrencyLimit`
@@ -174,7 +189,7 @@ configured {spring-framework-api}/core/retry/RetryPolicy.html[`RetryPolicy`].
----
var retryTemplate = new RetryTemplate(); // <1>
retryTemplate.execute(
retryTemplate.invoke(
() -> jmsClient.destination("notifications").send(...));
----
<1> Implicitly uses `RetryPolicy.withDefaults()`.
@@ -200,7 +215,7 @@ If you only need to customize the number of retry attempts, you can use the
----
var retryTemplate = new RetryTemplate(RetryPolicy.withMaxRetries(4)); // <1>
retryTemplate.execute(
retryTemplate.invoke(
() -> jmsClient.destination("notifications").send(...));
----
<1> Explicitly uses `RetryPolicy.withMaxRetries(4)`.
@@ -218,7 +233,7 @@ matched against an exception thrown by a failed operation as well as nested caus
var retryTemplate = new RetryTemplate(retryPolicy);
retryTemplate.execute(
retryTemplate.invoke(
() -> jmsClient.destination("notifications").send(...));
----
<1> Specify one or more exception types to include.
@@ -251,21 +266,108 @@ and an exponential back-off strategy with a bit of jitter.
var retryTemplate = new RetryTemplate(retryPolicy);
retryTemplate.execute(
retryTemplate.invoke(
() -> jmsClient.destination("notifications").send(...));
----
[TIP]
[NOTE]
====
A {spring-framework-api}/core/retry/RetryListener.html[`RetryListener`] can be registered
with a `RetryTemplate` to react to events published during key retry phases (before a
retry attempt, after a retry attempt, etc.), and you can compose multiple listeners via a
{spring-framework-api}/core/retry/support/CompositeRetryListener.html[`CompositeRetryListener`].
When `delay` is zero combined with a positive `jitter`, the delay never grows
regardless of any configured `multiplier`, so the full configured `jitter` is
applied directly as a random delay in the range from zero to `min(jitter, maxDelay)`.
====
[TIP]
====
Although the factory methods and builder API for `RetryPolicy` cover most common
configuration scenarios, you can implement a custom `RetryPolicy` for complete control
over the types of exceptions that should trigger a retry as well as the
{spring-framework-api}/util/backoff/BackOff.html[`BackOff`] strategy to use. Note that you
can also configure a customized `BackOff` strategy via the `backOff()` method in the
`RetryPolicy.Builder`.
{spring-framework-api}/util/backoff/BackOff.html[`BackOff`] strategy to use. Note that
you can also configure a customized `BackOff` strategy via the `backOff()` method in
the `RetryPolicy.Builder`.
====
Note that the examples above apply a pattern similar to `@Retryable` method invocations
where the last original exception will be propagated to the caller, using the `invoke`
variants on `RetryTemplate` which are available with and without a return value.
The callback may throw unchecked exceptions, the last one of which is exposed for
direct handling on the caller side:
[source,java,indent=0,subs="verbatim,quotes"]
----
try {
retryTemplate.invoke(
() -> jmsClient.destination("notifications").send(...));
}
catch (MessageDeliveryException ex) {
// coming out of the original JmsClient send method
}
----
[source,java,indent=0,subs="verbatim,quotes"]
----
try {
var result = retryTemplate.invoke(() -> {
jmsClient.destination("notifications").send(...);
return "result";
});
}
catch (MessageDeliveryException ex) {
// coming out of the original JmsClient send method
}
----
`RetryTemplate` instances are very light and can be created on the fly,
potentially with a specific retry policy to use for a given invocation:
[source,java,indent=0,subs="verbatim,quotes"]
----
try {
new RetryTemplate(RetryPolicy.withMaxRetries(4)).invoke(
() -> jmsClient.destination("notifications").send(...));
}
catch (MessageDeliveryException ex) {
// coming out of the original JmsClient send method
}
----
For deeper interaction, you may use RetryTemplate's `execute` method. The caller will
have to handle the checked `RetryException` thrown by `RetryTemplate`, exposing the
outcome of all attempts:
[source,java,indent=0,subs="verbatim,quotes"]
----
try {
var result = retryTemplate.execute(() -> {
jmsClient.destination("notifications").send(...);
return "result";
});
}
catch (RetryException ex) {
// ex.getExceptions() / ex.getLastException() ...
}
----
A {spring-framework-api}/core/retry/RetryListener.html[`RetryListener`] can be registered
with a `RetryTemplate` to react to key retry steps (before or after a retry attempt etc.)
or simply to every invocation attempt, being able to track all exceptions coming out of
the callback and all retry outcomes (exhaustion, interruption, timeout). This is
particularly useful when using `invoke` where no retry state other than the last
original exception is exposed otherwise:
[source,java,indent=0,subs="verbatim,quotes"]
----
var retryTemplate = new RetryTemplate();
retryTemplate.setRetryListener(new RetryListener() {
@Override
public void onRetryableExecution(RetryPolicy retryPolicy, Retryable<?> retryable, RetryState retryState) {
...
}
});
retryTemplate.invoke(
() -> jmsClient.destination("notifications").send(...));
----
You can also compose multiple listeners via a
{spring-framework-api}/core/retry/support/CompositeRetryListener.html[`CompositeRetryListener`].
@@ -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.
@@ -103,7 +103,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:
@@ -347,10 +347,10 @@ recognized and used as the `PropertyEditor` for `Something`-typed properties.
[literal,subs="verbatim,quotes"]
----
com
chank
pop
Something
SomethingEditor // the PropertyEditor for the Something class
└── example
└── things
├── *Something*
└── *SomethingEditor* // the PropertyEditor for the Something class
----
Note that you can also use the standard `BeanInfo` JavaBeans mechanism here as well
@@ -362,10 +362,10 @@ following example uses the `BeanInfo` mechanism to explicitly register one or mo
[literal,subs="verbatim,quotes"]
----
com
chank
pop
Something
SomethingBeanInfo // the BeanInfo for the Something class
└── example
└── things
├── *Something*
└── *SomethingBeanInfo* // the BeanInfo for the Something class
----
The following Java source code for the referenced `SomethingBeanInfo` class
@@ -447,7 +447,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 +576,7 @@ You can write a corresponding registrar and reuse it in each case.
`PropertyEditorRegistry`, an interface that is implemented by the Spring `BeanWrapper`
(and `DataBinder`). `PropertyEditorRegistrar` instances are particularly convenient
when used in conjunction with `CustomEditorConfigurer` (described
xref:core/validation/data-binding.adoc#data-binding-conversion-customeditor-registration[here]), which exposes a property
<<data-binding-conversion-customeditor-registration,here>>), which exposes a property
called `setPropertyEditorRegistrars(..)`. `PropertyEditorRegistrar` instances added
to a `CustomEditorConfigurer` in this fashion can easily be shared with `DataBinder` and
Spring MVC controllers. Furthermore, it avoids the need for synchronization on custom
@@ -7,8 +7,8 @@
This part of the appendix lists XML schemas for data access, including the following:
* xref:data-access/appendix.adoc#xsd-schemas-tx[The `tx` Schema]
* xref:data-access/appendix.adoc#xsd-schemas-jdbc[The `jdbc` Schema]
* <<xsd-schemas-tx,The `tx` Schema>>
* <<xsd-schemas-jdbc,The `jdbc` Schema>>
[[xsd-schemas-tx]]
=== The `tx` Schema
@@ -3,14 +3,14 @@
This section covers:
* xref:data-access/jdbc/connections.adoc#jdbc-datasource[Using `DataSource`]
* xref:data-access/jdbc/connections.adoc#jdbc-DataSourceUtils[Using `DataSourceUtils`]
* xref:data-access/jdbc/connections.adoc#jdbc-SmartDataSource[Implementing `SmartDataSource`]
* xref:data-access/jdbc/connections.adoc#jdbc-AbstractDataSource[Extending `AbstractDataSource`]
* xref:data-access/jdbc/connections.adoc#jdbc-SingleConnectionDataSource[Using `SingleConnectionDataSource`]
* xref:data-access/jdbc/connections.adoc#jdbc-DriverManagerDataSource[Using `DriverManagerDataSource`]
* xref:data-access/jdbc/connections.adoc#jdbc-TransactionAwareDataSourceProxy[Using `TransactionAwareDataSourceProxy`]
* xref:data-access/jdbc/connections.adoc#jdbc-DataSourceTransactionManager[Using `DataSourceTransactionManager` / `JdbcTransactionManager`]
* <<jdbc-datasource,Using `DataSource`>>
* <<jdbc-DataSourceUtils,Using `DataSourceUtils`>>
* <<jdbc-SmartDataSource,Implementing `SmartDataSource`>>
* <<jdbc-AbstractDataSource,Extending `AbstractDataSource`>>
* <<jdbc-SingleConnectionDataSource,Using `SingleConnectionDataSource`>>
* <<jdbc-DriverManagerDataSource,Using `DriverManagerDataSource`>>
* <<jdbc-TransactionAwareDataSourceProxy,Using `TransactionAwareDataSourceProxy`>>
* <<jdbc-DataSourceTransactionManager,Using `DataSourceTransactionManager` / `JdbcTransactionManager`>>
[[jdbc-datasource]]
@@ -4,14 +4,14 @@
This section covers how to use the JDBC core classes to control basic JDBC processing,
including error handling. It includes the following topics:
* xref:data-access/jdbc/core.adoc#jdbc-JdbcTemplate[Using `JdbcTemplate`]
* xref:data-access/jdbc/core.adoc#jdbc-NamedParameterJdbcTemplate[Using `NamedParameterJdbcTemplate`]
* xref:data-access/jdbc/core.adoc#jdbc-JdbcClient[Unified JDBC Query/Update Operations: `JdbcClient`]
* xref:data-access/jdbc/core.adoc#jdbc-SQLExceptionTranslator[Using `SQLExceptionTranslator`]
* xref:data-access/jdbc/core.adoc#jdbc-statements-executing[Running Statements]
* xref:data-access/jdbc/core.adoc#jdbc-statements-querying[Running Queries]
* xref:data-access/jdbc/core.adoc#jdbc-updates[Updating the Database]
* xref:data-access/jdbc/core.adoc#jdbc-auto-generated-keys[Retrieving Auto-generated Keys]
* <<jdbc-JdbcTemplate,Using `JdbcTemplate`>>
* <<jdbc-NamedParameterJdbcTemplate,Using `NamedParameterJdbcTemplate`>>
* <<jdbc-JdbcClient,Unified JDBC Query/Update Operations: `JdbcClient`>>
* <<jdbc-SQLExceptionTranslator,Using `SQLExceptionTranslator`>>
* <<jdbc-statements-executing,Running Statements>>
* <<jdbc-statements-querying,Running Queries>>
* <<jdbc-updates,Updating the Database>>
* <<jdbc-auto-generated-keys,Retrieving Auto-generated Keys>>
[[jdbc-JdbcTemplate]]
@@ -349,7 +349,7 @@ The `JdbcTemplate` is stateful, in that it maintains a reference to a `DataSourc
this state is not conversational state.
A common practice when using the `JdbcTemplate` class (and the associated
xref:data-access/jdbc/core.adoc#jdbc-NamedParameterJdbcTemplate[`NamedParameterJdbcTemplate`] class) is to
<<jdbc-NamedParameterJdbcTemplate,`NamedParameterJdbcTemplate`>> class) is to
configure a `DataSource` in your Spring configuration file and then dependency-inject
that shared `DataSource` bean into your DAO classes. The `JdbcTemplate` is created in
the setter for the `DataSource` or in the constructor. This leads to DAOs that resemble the following:
@@ -574,7 +574,7 @@ functionality that is present only in the `JdbcTemplate` class, you can use the
`getJdbcOperations()` method to access the wrapped `JdbcTemplate` through the
`JdbcOperations` interface.
See also xref:data-access/jdbc/core.adoc#jdbc-jdbctemplate-idioms[`JdbcTemplate` Best Practices]
See also <<jdbc-jdbctemplate-idioms,`JdbcTemplate` Best Practices>>
for guidelines on using the `NamedParameterJdbcTemplate` class in the context of an application.
@@ -39,9 +39,9 @@ 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-Derby,Using Derby>>
[[jdbc-embedded-database-using-HSQL]]
=== Using HSQL
@@ -143,7 +143,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
@@ -479,7 +479,7 @@ 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
@@ -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
@@ -360,7 +360,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.
@@ -22,7 +22,7 @@ where all the underlying resources have to participate in the service-level tran
NOTE: By default, a participating transaction joins the characteristics of the outer scope,
silently ignoring the local isolation level, timeout value, or read-only flag (if any).
Consider switching the `validateExistingTransactions` flag to `true` on your transaction
Consider switching the `validateExistingTransaction` flag to `true` on your transaction
manager if you want isolation level declarations to be rejected when participating in
an existing transaction with a different isolation level. This non-lenient mode also
rejects read-only mismatches (that is, an inner read-write transaction that tries to participate
@@ -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
@@ -98,7 +98,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 +160,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`:
@@ -684,5 +684,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]
@@ -1,10 +1,15 @@
[[observability]]
= Observability Support
With https://docs.micrometer.io/micrometer/reference/concepts.html[Micrometer], developers can instrument libraries and applications for metrics (timers, gauges, counters)
that collect statistics about their runtime behavior. Metrics can help you to track error rates, usage patterns, performance, and more.
https://docs.micrometer.io/tracing/reference/[Micrometer can also produce traces], giving you a holistic view of an entire system, crossing application boundaries; you can zoom in on particular user requests and follow their entire completion across applications.
Micrometer defines an {micrometer-docs}/observation.html[Observation concept that enables both Metrics and Traces] in applications.
Metrics support offers a way to create timers, gauges, or counters for collecting statistics about the runtime behavior of your application.
Metrics can help you to track error rates, usage patterns, performance, and more.
Traces provide a holistic view of an entire system, crossing application boundaries; you can zoom in on particular user requests and follow their entire completion across applications.
Each observation will produce:
* https://docs.micrometer.io/micrometer/reference/observation/components.html#micrometer-observation-default-meter-handler[several metrics - a timer, a long task timer and many counters]
* a https://docs.micrometer.io/tracing/reference/glossary.html[span for the current trace]
Spring Framework instruments various parts of its own codebase to publish observations if an `ObservationRegistry` is configured.
You can learn more about {spring-boot-docs-ref}/actuator/observability.html[configuring the observability infrastructure in Spring Boot].
@@ -14,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]]
@@ -480,7 +480,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 +560,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 +874,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]]
@@ -1179,7 +1179,7 @@ built-in decorators to suppress 404 exceptions and return a `ResponseEntity` wit
[source,java,indent=0,subs="verbatim,quotes"]
----
// For RestClient
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(restCqlientAdapter)
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(restClientAdapter)
.exchangeAdapterDecorator(NotFoundRestClientAdapterDecorator::new)
.build();
@@ -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]
@@ -578,7 +578,7 @@ in combination with a custom pointcut.
=== 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 +658,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 +770,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 +791,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
@@ -3,15 +3,15 @@
The following annotations are supported when used in conjunction with the
xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit-jupiter-extension[`SpringExtension`]
and JUnit Jupiter (that is, the programming model in JUnit):
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`]
@@ -30,30 +30,39 @@ developer wishes to switch to test-class scoped semantics — the `SpringExtensi
configured to use a test-class scoped `ExtensionContext` by annotating a top-level test
class with `@SpringExtensionConfig(useTestClassScopedExtensionContext = true)`.
Alternatively, you can change the global default by setting the
`spring.test.extension.context.scope` property to `test_class`. The property is resolved
first via the xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism
which also supports JVM system properties — for example,
`-Dspring.test.extension.context.scope=test_class`. If the Spring property has not been
set, the `SpringExtension` will attempt to resolve the property as a
https://docs.junit.org/current/running-tests/configuration-parameters.html[JUnit Platform configuration parameter]
as a fallback mechanism. If the property has not been set via either of those mechanisms,
the `SpringExtension` will use a test-method scoped extension context by default. Note,
however, that a `@SpringExtensionConfig` declaration always takes precedence over this
property.
[TIP]
====
If your top-level test class is configured to use JUnit Jupiter's
`@TestInstance(Lifecycle.PER_CLASS)` semantics, the `SpringExtension` will always use a
test-class scoped `ExtensionContext`, and there is no need to declare
`@SpringExtensionConfig(useTestClassScopedExtensionContext = true)`.
If a test class uses JUnit Jupiter's `@TestInstance(Lifecycle.PER_CLASS)` semantics, the
`SpringExtension` will always use a test-class scoped `ExtensionContext`, and
configuration via `@SpringExtensionConfig(useTestClassScopedExtensionContext = true)` or
the `spring.test.extension.context.scope` property will have no effect for that test
class.
====
[NOTE]
====
This annotation is currently only applicable to `@Nested` test class hierarchies and
should be applied to the top-level enclosing class of a `@Nested` test class hierarchy.
Consequently, there is no need to declare this annotation on a test class that does not
contain `@Nested` test classes.
====
[NOTE]
====
xref:testing/annotations/integration-junit-jupiter.adoc#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.
In addition,
<<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.
====
[[integration-testing-annotations-junit-jupiter-springjunitconfig]]
@@ -282,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]]
@@ -72,6 +72,13 @@ bean definition profiles programmatically by implementing a custom
xref:testing/testcontext-framework/ctx-management/env-profiles.adoc#testcontext-ctx-management-env-profiles-ActiveProfilesResolver[`ActiveProfilesResolver`]
and registering it by using the `resolver` attribute of `@ActiveProfiles`.
NOTE: When `@ActiveProfiles` is declared on a test class, the `spring.profiles.active`
property (whether configured as a JVM system property or environment variable) is not
taken into account by the TestContext Framework when determining active profiles. If
you need to allow `spring.profiles.active` to override the profiles configured via
`@ActiveProfiles`, you can implement a custom `ActiveProfilesResolver` as described in
xref:testing/testcontext-framework/ctx-management/env-profiles.adoc[Context Configuration with Environment Profiles].
See xref:testing/testcontext-framework/ctx-management/env-profiles.adoc[Context Configuration with Environment Profiles],
xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit-jupiter-nested-test-configuration[`@Nested` test class configuration], and the
{spring-framework-api}/test/context/ActiveProfiles.html[`@ActiveProfiles`] javadoc for

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