Compare commits

...

761 Commits

Author SHA1 Message Date
Brian Clozel 9e8cea3ef8 Release v7.0.8 2026-06-08 19:20:41 +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
arno 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
Sébastien Deleuze 739d5ba77b Leverage ResourceHandlerUtils in ScriptTemplateView
This commit apply extra checks to ScriptTemplateView resource handling
with ResourceHandlerUtils, consistently with what is done with static
resource handling.

Closes gh-36458
2026-03-12 19:43:17 +01:00
Sébastien Deleuze 19ab92ef59 Restore ScriptTemplateViewTests
Restore both WebMVC and WebFlux variants that were deleted
by mistake in commit 4db2f8ea1b.

This commit also removes the empty resource loader path, as it is not
needed for the main WEB-INF/ use case that is typically configured
explicitly by the user, and not needed to pass the restored tests.

Closes gh-36456
2026-03-12 19:42:28 +01:00
Sam Brannen 04313f062e Improve documentation for FullyQualifiedConfigurationBeanNameGenerator
This commit documents FullyQualifiedConfigurationBeanNameGenerator in
related Javadoc and in the reference manual.

See gh-33448
Closes gh-36455
2026-03-12 17:03:45 +01:00
Sam Brannen e634ced56b Fix log message in ConfigurationClassBeanDefinitionReader
The log message now properly generates the fully-qualified method name
and includes the resolved bean name as well.

Closes gh-36453
2026-03-12 16:23:45 +01:00
Sam Brannen 1502c2296e Remove redundant method invocation 2026-03-12 16:17:23 +01:00
Brian Clozel cc5c7ba186 Fix enclosing class resolution with ClassFile API
Prior to this commit, the `ClassFile` based implementation of
`AnnotationMetadata` would rely on the `NestHost` class element to get
the enclosing class name for a nested class.
This approach works for bytecode emitted by Java11+, which aligns with
our Java17+ runtime policy. But there are cases where bytecode was not
emitted by a Java11+ compiler, such as Kotlin. In this case, the
`NestHost` class element is absent and we should instead use the
`InnerClasses` information to get it.

This commit makes use of `InnerClasses` to get the enclosing class name,
but still uses `NestHost` as a fallback for anonymous classes.

Fixes gh-36451
2026-03-12 10:47:39 +01:00
Sam Brannen 0269eb80da Fix typo and improve Javadoc for ConfigurationBeanNameGenerator 2026-03-11 17:18:11 +01:00
Sam Brannen 9cedcd65ef Polishing 2026-03-11 16:45:30 +01:00
Sam Brannen 4e8acb9ef2 Polish contribution
See gh-36448
2026-03-11 16:45:30 +01:00
gbouwen dbbbf37ac9 Update exception message in JacksonJsonMessageConverter
Closes gh-36448

Signed-off-by: gbouwen <57719521+gbouwen@users.noreply.github.com>
2026-03-11 16:41:19 +01:00
Juergen Hoeller d8216d719b Upgrade to SnakeYAML 2.6, Protobuf 4.34, H2 2.4.240 2026-03-11 15:53:42 +01:00
Juergen Hoeller 63ced3f3b4 Polishing 2026-03-11 13:52:34 +01:00
Juergen Hoeller 89391fd94c Consistently ignore non-loadable annotation types with ClassFile/ASM
Uses ClassFileAnnotationMetadata name for actual AnnotationMetadata.
Moves JSR-305 dependency to compile-only for all spring-core tests.

Closes gh-36432
2026-03-11 13:52:19 +01:00
rstoyanchev 5168e3a38b Polishing contribution
Closes gh-36400
2026-03-10 19:14:41 +00:00
husseinvr97 d4893fb32f Support defaultHtmlEscape in WebFlux
See gh-36400

Signed-off-by: husseinvr97 <husseinmustafabas05@gmail.com>
2026-03-10 19:14:41 +00:00
Juergen Hoeller 104a2033c5 Upgrade to Reactor 2025.0.4 and Micrometer 1.16.4
Includes Jetty 12.1.7, Hibernate ORM 7.2.6, EclipseLink 5.0.0-RC1, Selenium 4.41, Mockito 5.22, Checkstyle 13.3

Closes gh-36443
Closes gh-36444
2026-03-10 20:09:20 +01:00
Brian Clozel cc1514be85 Fix DefaultResponseErrorHandler method visibility
Prior to this commit, the `setMessageConverters` method would have
private visibility. But `initBodyConvertFunction`, which is `protected`,
relies on the message converters being set in the first place.
While this works with `RestTemplate` because this is done automatically,
the `RestClient` does offer a builder method to configure a
`ResponseErrorHandler` and this makes it impossible to configure
converters in this case.

This commit aligns the method visibility by making it protected.

Closes gh-36434
2026-03-10 18:08:09 +01:00
Brian Clozel 6e9758700a Guard against invalid id/event values in Server Sent Events
Prior to this commit, our implementation of Server Sent Events (SSE),
`SseEmitter` (MVC) and `ServerSentEvent` (WebFlux), would not guard
against invalid characters if the application mistakenly inserts such
characters in the `id` or `event` types.
Both implementations would also behave differently when it comes
to escaping comment multi-line events.

This commit ensures that both implementations handle multi-line comment
events and reject invalid characters in id/event types.
This commit also optimizes `String` concatenation and memory usage
when writing data.

Fixes gh-36440
2026-03-10 17:56:58 +01:00
Sam Brannen 37e8aa76e9 Use link for first reference to @⁠Fallback in @⁠Bean Javadoc
See gh-36439
2026-03-10 17:53:33 +01:00
Sam Brannen 27686dc2e2 Document @Fallback alongside Primary in the reference docs and @Bean Javadoc
Closes gh-36439
2026-03-10 17:13:23 +01:00
Sam Brannen 3b8efbe5a6 Document registration recommendations for BeanPostProcessor and BeanFactoryPostProcessor
Closes gh-34964
2026-03-10 13:07:13 +01:00
Sam Brannen 6f2e59a995 Polishing 2026-03-10 13:07:12 +01:00
Sam Brannen 644a20ae2a Polish @⁠Bean Javadoc and reference docs 2026-03-10 12:24:53 +01:00
rstoyanchev 77bc13dc8c Polishing contribution
See gh-36325
2026-03-09 17:52:12 +00:00
rstoyanchev ab9895b5a9 Polishing contribution
Closes gh-36325
2026-03-09 17:49:25 +00:00
Agil ce5c4f3b4b Refactor calculateHashCode method for clarity
This will:
1. Mathematical Distribution (Collision Reduction)
2. Pipelining and CPU Caching
3. Avoiding "Method Heavy" Expressions

See gh-36325

Signed-off-by: Agil <41694337+AgilAghamirzayev@users.noreply.github.com>
2026-03-09 17:38:15 +00:00
rstoyanchev cd0c26b6db Update Javadoc on binding flag in ModelAttribute
Add clarification and mention primary use case.
2026-03-09 14:25:47 +00:00
조형준 1203ace285 Fix typo in HttpExchangeAdapterDecorator Javadoc
Closes gh-36433

Signed-off-by: jun <ryuu.public@gmail.com>
2026-03-09 14:46:20 +01:00
Sam Brannen 1219943e73 Polishing
See gh-36323
2026-03-09 11:15:19 +01:00
Sébastien Deleuze eeddf9a73a Fix transaction managemement snippet and documentation
Closes gh-36323
2026-03-09 10:29:45 +01:00
jisub-dev b2a4bc8900 Add programmatic configuration code snippet
- Extract code examples to separate Java, Kotlin, and XML files
- Add Kotlin configuration sample alongside Java
- Change "Java Config" terminology to "Programmatic Configuration"
- Use include-code directive for better maintainability

See gh-36323
Signed-off-by: jisub-dev <kimjiseob1209@gmail.com>
2026-03-09 10:12:20 +01:00
Sam Brannen 3a266e65d6 Revise nullability in AnnotationConfigContextLoaderUtils 2026-03-08 14:42:17 +01:00
Sam Brannen 463138acbc Resolve context initializers only once in AbstractTestContextBootstrapper
The internal buildMergedContextConfiguration() method in
AbstractTestContextBootstrapper originally resolved the
ApplicationContextInitializer set only once. However, the changes made
in commit 2244461778 introduced a regression resulting in the
initializers being resolved twice: once for validation and once for
actually building the merged context configuration. In addition, the
resolution for validation does not honor the inheritInitializers flag
in ContextConfigurationAttributes.

To address these issues, buildMergedContextConfiguration() once again
resolves the context initializers once via
ApplicationContextInitializerUtils.resolveInitializerClasses().

See gh-18528
Closes gh-36430
2026-03-08 14:32:06 +01:00
Tran Ngoc Nhan b8a4961b81 Remove unused imports in framework-docs examples
Closes gh-36429

Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>
2026-03-07 15:11:05 +01:00
rstoyanchev 06d4a8fc4b Fix case sensitivity issue in ServletRequestHeadersAdapter
The RequestHeaderOverrideWrapper did not deduplicate in keySet()
across underlying headers and overrides.

A similar change in size() even if it was working correctly,
to align with keySet and make it more efficient.

Closes gh-36418
2026-03-06 16:51:27 +00:00
Sam Brannen 13b0643828 Invoke resolveContextLoader() only once in AbstractTestContextBootstrapper
See commit c2d0d292bd
See gh-35994
Closes gh-36425
2026-03-06 16:05:30 +01:00
Sam Brannen 548b2167b0 Simplify changes made in commit c2d0d292bd
See gh-36390
2026-03-06 13:13:18 +01:00
froggy0m0 de417beff7 Fix typo in ExceptionMapingComparator name
Closes gh-36422

Signed-off-by: 신성현 <froggy0m0a@gmail.com>
2026-03-06 11:57:14 +01:00
rstoyanchev ccc6789cb5 Update docs for path API version resolver
See gh-36398
2026-03-05 17:21:43 +00:00
rstoyanchev f73302a66e Polishing contribution
Closes gh-36398
2026-03-05 17:12:13 +00:00
Martin Mois 348482b5a0 Add usePathSegment with Predicate<RequestPath>
See gh-36398

Signed-off-by: Martin Mois <martin.mois@gmail.com>
2026-03-05 17:12:13 +00:00
rstoyanchev 25bb264bd9 Polishing in ReactorNettyWebSocketClient
Also, remove local variables that should have been removed when
the corresponding, deprecated setters were removed in
2ed281f6a8.

Closes gh-36370
2026-03-05 17:12:13 +00:00
Artem Voronin 9819e1cad5 Propagate max frame length to WebSocket session
This change ensures that maxFramePayloadLength from
WebsocketClientSpec is passed to ReactorNettyWebSocketSession.

Previously, the session used the default 64KB limit regardless
of client configuration, causing TooLongFrameException when
receiving larger frames from servers like Tomcat or Jetty.

See gh-36370

Signed-off-by: Artem Voronin <artem.voronin.dev@gmail.com>
2026-03-05 17:12:13 +00:00
rstoyanchev ce2402a581 Link to Tomcat issue in ServletResponseHeadersAdapter 2026-03-05 17:12:13 +00:00
Sam Brannen c2d0d292bd Re-resolve ContextLoader in logWarningForIgnoredDefaultConfig()
Ideally, we should be able to reuse the ContextLoader that was resolved
in buildDefaultMergedContextConfiguration(); however, since we have
been informed that some implementations of resolveContextLoader()
mutate the state of the supplied ContextConfigurationAttributes to
detect default configuration classes, we need to invoke
resolveContextLoader() on the completeDefaultConfigAttributesList as
well in order not to break custom TestContextBootstrappers that exhibit
such side effects.

Closes gh-36390
2026-03-05 15:28:54 +01:00
Sam Brannen bbe733def7 Address Checkstyle violation 2026-03-05 11:43:06 +01:00
Stéphane Nicoll 03391dfa94 Switch serializable flag to primitive boolean
This commit improves the change made to TypeHint to use a primitive
boolean for the serializable flag.

See gh-36379
2026-03-05 11:23:51 +01:00
Sam Brannen 50c29e64f8 Polish documentation for FrameworkServlet and HttpServletBean 2026-03-05 11:21:44 +01:00
Sam Brannen 22301fd069 Further align synthesized annotation toString() with modern JDKs
Closes gh-36417
2026-03-04 17:39:02 +01:00
Sam Brannen 11ab0b4351 Upgrade to Gradle 9.4
Closes gh-36416
2026-03-04 13:43:07 +01:00
Juergen Hoeller 4734c15d81 Polishing 2026-03-03 23:28:37 +01:00
Sam Brannen 02af3e7a17 Introduce setDefaultCharset() in AbstractResourceBasedMessageSource
Traditionally, AbstractResourceBasedMessageSource has only had a
setDefaultEncoding() method which accepts the name of the default
encoding character set. However, although we have recently made a
concerted effort within the framework to introduce support for
supplying a Charset instead of a character set's name, we had
overlooked this particular scenario.

In light of that, this commit introduces setDefaultCharset(Charset) and
getDefaultCharset() methods in AbstractResourceBasedMessageSource and
makes direct use of the available Charset in
ReloadableResourceBundleMessageSource and ResourceBundleMessageSource.

Furthermore, although technically a regression in behavior, invoking
setDefaultEncoding() on such MessageSource implementations with an
invalid character set name now results in an immediate
UnsupportedCharsetException at configuration time instead of a
NoSuchMessageException at runtime, which will help users to more easily
detect misconfiguration.

Closes gh-36413
2026-03-03 18:08:21 +01:00
cetf 3c6f1b35ba Fix format string argument count
Fixes: gh-36410
Signed-off-by: cetf <cetf9h@163.com>
2026-03-03 12:14:33 +01:00
Sam Brannen 7a420b28df Revise custom @⁠BootstrapWith use case in test
See https://github.com/spring-projects/spring-boot/issues/15077
See gh-35938
2026-03-02 17:07:30 +01:00
Sam Brannen 63723fadb6 Introduce tests for additional @⁠BootstrapWith use cases
See gh-35938
2026-03-02 16:34:39 +01:00
Sam Brannen ab7a2c4e70 Rename configuration.interfaces package to config.interfaces
This commit renames the
org.springframework.test.context.configuration.interfaces package to
org.springframework.test.context.config.interfaces in order to colocate
"config" related tests under the same package.
2026-03-02 16:07:43 +01:00
dependabot[bot] a87b2e0146 Upgrade fast-xml-parser to 5.3.8
Closes gh-36402

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-01 13:13:43 +01:00
Sam Brannen bd40f0e6bb Fix typo 2026-03-01 13:03:40 +01:00
Sam Brannen 04186fdf0e Consistently refer to URLs and URIs in documentation 2026-03-01 13:03:14 +01:00
Tran Ngoc Nhan b2b731b0ba Fix links to UriComponentsBuilder and polish examples
Closes gh-36403

Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>
2026-03-01 12:51:43 +01:00
Juergen Hoeller a3b9098850 Remove prefixed FactoryBean name in ApplicationListenerDetector
Closes gh-36404
2026-03-01 12:08:19 +01:00
Juergen Hoeller 36815f4021 Add support for JPA 4.0 FlushModeType.EXPLICIT
Includes defensive nullable field access in EclipseLinkConnectionHandle.

Closes gh-36401
2026-02-28 19:37:27 +01:00
Sam Brannen 3b1b7ff594 Add Checkstyle suppression 2026-02-28 16:22:57 +01:00
Sam Brannen 95080cabfd Polishing 2026-02-28 16:01:01 +01:00
조형준 017892265e Fix typo in WebMvcConfigurationSupport Javadoc
Closes gh-#36399

Signed-off-by: jun <ryuu.public@gmail.com>
2026-02-28 14:16:14 +01:00
Juergen Hoeller d096a33f13 Add note on test difference against Jackson 3.1.0 2026-02-28 13:36:39 +01:00
Juergen Hoeller b6833ff31f Cancel late-executing tasks within revised closed handling
Closes gh-36362
2026-02-28 13:34:01 +01:00
Stéphane Nicoll 3bc55c77ec Add support for declaring reflection metadata for lambdas
Closes gh-36339
2026-02-27 14:42:46 +01:00
Sam Brannen 824aa137f8 Align implementation of injectDependencies() with injectDependenciesInAotMode() 2026-02-26 18:01:01 +01:00
Sam Brannen 0dc44f79d5 Avoid superfluous MergedContextConfiguration equals() check
See gh-36390
2026-02-26 12:56:38 +01:00
Sam Brannen f481f99516 Avoid unnecessary List and Stream creation
See gh-36390
2026-02-26 12:02:38 +01:00
Sam Brannen 72ed2eaa31 Fix lambda variable names
See gh-36390
2026-02-26 11:57:32 +01:00
Sam Brannen 0676c958f7 Log warning for ignored default context configuration in tests
In Spring Framework 7.1, the TestContext Framework will reliably detect
all default context configuration within the type hierarchy or
enclosing class hierarchy (for @⁠Nested test classes) above a given
test class; however, test suites may encounter failures once we make
that switch in behavior.

In order to help development teams prepare for the switch in 7.1, this
commit logs a warning similar to the following whenever default context
configuration is detected but currently ignored.

  WARN - For test class [org.example.MyTests$NestedTests], the
  following 'default' context configuration classes were detected but
  are currently ignored: org.example.MyTests$Config. In Spring
  Framework 7.1, these classes will no longer be ignored. Please update
  your test configuration accordingly. For details, see:
  https://docs.spring.io/spring-framework/reference/testing/testcontext-framework/ctx-management/default-config.html

See gh-31456
See gh-36392
Closes gh-36390
2026-02-26 11:24:02 +01:00
Sam Brannen 482519d03b Document how to avoid issues w/ ignored default context config in tests
See gh-31456
See gh-36390
Closes gh-36392
2026-02-25 17:36:14 +01:00
Sam Brannen d6b7cea327 Polishing 2026-02-25 17:34:14 +01:00
Sam Brannen 9eea227ab9 Further emphasize @⁠Configuration classes over XML and Groovy in testing chapter
See gh-36393
2026-02-25 15:44:00 +01:00
Sam Brannen 706c98228d Emphasize @⁠Configuration classes over XML and Groovy in testing chapter
Closes gh-36393
2026-02-25 15:26:32 +01:00
rstoyanchev b83cb4a947 Make newRequest methods public
The methods to build a request in RestClientAdapter and WebClientAdapter
are now public to make it easier to create a custom adapter that wraps
the built-in ones and delegates for methods that can be the same.

Closes gh-36374
2026-02-25 12:43:19 +00:00
rstoyanchev 7e79d0e206 Polishing in DefaultRestClientTests 2026-02-25 12:43:19 +00:00
rstoyanchev e58ba2c665 Simplify use of InputStream with RestClient
Closes gh-36380
2026-02-25 12:43:19 +00:00
Sam Brannen 0c9127d111 Use consistent indentation in Gradle build scripts 2026-02-25 11:15:16 +01:00
Sam Brannen c2088517b9 Upgrade to JUnit 6.0.3
Closes gh-36389
2026-02-25 11:05:05 +01:00
Sam Brannen 59b9057e59 Polishing 2026-02-25 10:52:28 +01:00
Sam Brannen 2ec630ef45 Remove obsolete EMPTY_GROUPS constant in InvocableHandlerMethod
See gh-36274
2026-02-25 10:50:45 +01:00
Sam Brannen cf2b59b68b Delete obsolete addCharsetParameter() method
See gh-36318
2026-02-25 10:50:45 +01:00
Sam Brannen f1ca29b05d Move NOTE in "Programmatic Retry Support" to correct location 2026-02-25 10:28:23 +01:00
dependabot[bot] ee6a115a18 Upgrade fast-xml-parser to 5.3.6
Closes gh-36348

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-25 10:08:56 +01:00
Juergen Hoeller 95a31c4e8c Align constant name in ServletServerHttpResponse
See gh-36385
2026-02-25 09:29:22 +01:00
Brian Clozel e0b54e244e Ignore flushes on ServletServerHttpResponse output stream
Prior to this commit, flush calls on the output stream returned by
`ServletServerHttpResponse#getBody` would be delegated to the Servlet
response output stream.
This can cause performance issues when `HttpMessageConverter` and other
web components write and flush multiple times to the response body.
Here, the Servlet container is in a better position to flush to the
network at the optimal time and buffer the response body until then.

This is particularly true for `HttpMessageConverters` when they flush
many times the output stream, sometimes due to the underlying codec
library. Instead of revisiting the entire message converter contract, we
are here ignoring flush calls to that output stream.

This change does not affect the client side, nor the
`ServletServerHttpResponse#flush` calls.

This commit also introduces a new Spring property
`"spring.http.response.flush.enabled"` that reverts this behavior change
if necessary.

Closes gh-36385
2026-02-24 21:44:48 +01:00
Juergen Hoeller f2b64ad09c Polishing 2026-02-24 17:17:31 +01:00
Juergen Hoeller cff48fff2d Reject late-executing tasks after termination waiting
Closes gh-36362
2026-02-24 17:17:21 +01:00
Juergen Hoeller ab8cb8de6e Add support for non-flushing OutputStream
Avoids decoration for streams that are non-closing/flushing already.

Closes gh-36382
2026-02-24 17:17:04 +01:00
Stéphane Nicoll 434da06d1f Migrate to GraalVM metadata format 1.2.0
This commit adapts RuntimeHints to be compatible with v1.2.0 of the
GraalVM metadata format. The changes are as follows:

* Resources and resource bundles are now defined at the same level. This
is transparent to users.
* Serialization is no longer a top-level concept, but rather an
attribute of a type or proxy. The top-level concept has been deprecated
and the relevant methods have been added.

Closes gh-36379
2026-02-24 17:05:50 +01:00
Brian Clozel a37447e07f Remove duplicate flushes in HttpMessageConverter implementations
Prior to this commit, a few implementations of the `HttpMessageConverter`
contract were inheriting from abstract classes. Those classes were
performing extra `OutputStream#flush` on the response body even though
this is the responsibility of the super class. Such abstract classes
do flush already, after delegating to the `writeInternal` method.

This commit ensures that we remove such extra calls as they tend to
waste resources for no added benefit.

Closes gh-36383
2026-02-24 16:49:37 +01:00
Sam Brannen b1cb9c5052 Polish contribution
See gh-36367
2026-02-23 17:24:13 +01:00
Tran Ngoc Nhan 057633edb5 Polish SpEL operator examples in reference docs
Closes gh-36367

Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>
2026-02-23 17:08:22 +01:00
Brian Clozel 7299ff9326 Improve ResourceHttpMessageConverter target type support
This commit updates the target type detection in
`ResourceHttpMessageConverter` to only support target types that are
relevant: `InputStreamResource` for streaming, and types assignable from
`ByteArrayResource` for non-streaming cases.

Closes gh-36368
2026-02-23 11:02:22 +01:00
조형준 53f1656f56 Fix typo in CorsConfiguration Javadoc
Closes gh-36366

Signed-off-by: jun <ryuu.public@gmail.com>
2026-02-22 16:14:45 +01:00
Brian Clozel 2691489381 Fix InvalidMimeTypeException for compatible media types
The `AbstractMessageConverterMethodProcessor` is in charge of handling
controller method return values and to write those as HTTP response
messages. The content negotiation process is an important part.

The `MimeTypeUtils#sortBySpecificity` is in charge of sorting inbound
"Accept" media types by their specificity and reject them if the list
is too large, in order to protect the application from ddos attacks.

Prior to this commit, the content negotiation process would first get
the sorted "Accept" media types, the producible media types as
advertized by message converters - and collect the intersection of both
in a new list (also sorted by specificity). If the "Accept" list is
large enough (but under the limit), the list of compatible media types
could exceed that limit because duplicates could be introduced in that
list: several converters can produce the same content type.

This commit ensures that compatible media types are collected in a set
to avoid duplicates. Without that, exceeding the limit at this point
will throw an `InvalidMimeTypeException` that's not handled by the
processor and result in a server error.

Fixes gh-36300
2026-02-20 18:21:45 +01:00
Tran Ngoc Nhan f103af4982 Remove obsolete space in HandlerMethod.html#assertTargetBean javadoc
Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>
2026-02-20 13:43:11 +00:00
rstoyanchev b9a59853fa Use case-insensitive comparator in HttpHeadersAssert
Closes gh-36349
2026-02-20 13:38:51 +00:00
rstoyanchev d4b2a493f9 Fix duplicate header writing in ResponseBodyEmitterReturnValueHandler
Closes gh-36357
2026-02-20 10:45:22 +00:00
rstoyanchev 188cb9b24d Update Javadoc of RestClient.Builder defaultStatusHandler
See gh-36248
2026-02-19 11:42:59 +00:00
rstoyanchev e3568a3f0a Fix generic return type support in HttpServiceMethod
Closes gh-36326
2026-02-18 17:27:02 +00:00
rstoyanchev e106fc0434 Polishing in RestClientAdapterTests and WebClientAdapterTests 2026-02-18 17:27:02 +00:00
Stéphane Nicoll ca3adf43eb Next development version (v7.0.6-SNAPSHOT) 2026-02-18 14:07:54 +01:00
rstoyanchev 85c18caf25 Update docs on supported versions with baseline notation
Closes gh-36316
2026-02-18 11:33:08 +00:00
Juergen Hoeller 22bd8bd704 Skip serialization of potentially non-serializable cached state
Closes gh-36346
2026-02-17 22:13:57 +01:00
Juergen Hoeller 9b10bb5e08 Polishing 2026-02-17 18:57:09 +01:00
Juergen Hoeller 727ccd04ef Avoid setCharacterEncoding(Charset) call with null value
Includes consistent content length check for functional response.

See gh-36343
2026-02-17 18:56:59 +01:00
Juergen Hoeller a9f447e8d7 Consistent adaptation of HTTP headers on Servlet responses
Includes use of Servlet 6.1 setCharacterEncoding(Charset)

Closes gh-36343
2026-02-17 17:38:50 +01:00
海子 Yang 0841e79e32 Copy methodAnnotations in MethodParameter copy constructor
Signed-off-by: 海子 Yang <i.take.today@gmail.com>
2026-02-17 17:17:22 +01:00
Niravil a1868d3e9e fix TransactionAspectSupport#currentTransactionStatus javadoc
Signed-off-by: Niravil <messerignacius@gmail.com>
2026-02-17 16:26:02 +01:00
rstoyanchev e5aac66157 ServletResponseHeadersAdapter checks contentType property
Issue gh-36334
2026-02-17 11:55:34 +00:00
Juergen Hoeller 8bf85d2596 Polishing 2026-02-17 12:27:00 +01:00
Juergen Hoeller 508b31da4f Optimize Entry hashCode in HttpHeaders (plus related polishing) 2026-02-17 11:55:10 +01:00
Juergen Hoeller d291272736 Use HandlerMethod-determined validation groups
Closes gh-36336
2026-02-17 11:50:07 +01:00
海子 Yang db01f07037 Remove unused EMPTY_GROUPS in InvocableHandlerMethod
Signed-off-by: 海子 Yang <i.take.today@gmail.com>
2026-02-17 11:30:54 +01:00
Brian Clozel b9e190e313 Fix HttpMessageConverters configurers support
This commit fixes the `configureMessageConverters` and
`configureMessageConvertersList` behavior.

`configureMessageConverters` was not executing consumers in their order
of registration (but in the reverse order).
`configureMessageConvertersList` was not executing multiple consumers
and was instead executing the first consumer multiple times.

This commit fixes both issues.

Fixes gh-36332
2026-02-16 18:48:26 +01:00
rstoyanchev d712ec3d49 Minor optimization in Tomcat header adapters
get method skips containsKey and instead checks if the enumeration
has elements, which should give the same behavior other than for
headers without values.

See gh-36334
2026-02-16 17:38:01 +00:00
rstoyanchev 5baa4fdd69 Further optimize Servlet header adapters
- Optimize get method for request headers
- Update keySet methods to use custom extension of AbstractSet
- Drop use of native Tomcat headers, which could be an issue for
request and response wrappers that override header methods.
The performance of Servlet adapters should be similar for the
commonly used methods, and it should be possible to optimize
further for the future in HttpHeaders (e.g. by adding a
set alternative to put), and requesting Servlet API refinements.

Closes gh-36334
2026-02-16 17:37:51 +00:00
rstoyanchev 9273a11a2c Stub Servlet headers adapter methods not needed in HttpHeaders
See gh-36334
2026-02-16 17:37:27 +00:00
rstoyanchev 7ea11baff9 Pass-through handling of Servlet headers
See gh-36334
2026-02-16 17:37:09 +00:00
Sam Brannen 655fd2e2bf Document that SpEL expressions using Optional w/ null-safe & Elvis operators are not compilable
Closes gh-36331
2026-02-15 12:26:54 +01:00
Brian Clozel a31574a0a2 Fix MultipartParser & PartGenerator memory leak
Prior to this commit, the reactive `MultipartParser` and `PartGenerator`
types were leaking memory at runtime in specific cases:

* many HTTP clients must send multipart requests to be parsed and close
  the connection while uploading
* the `PartGenerator` must be configured to write file parts to
  temporary files on disk
* concurrency, upload speed must be important to trigger cases where the
  file system is not fast enough to consume incoming buffers

The `MultipartParser` parses and emits `BodyToken` to its sink
(here, the `PartGenerator`). By definition, Reactor's `FluxSink` when
created with `Flux.create(FluxSink)` will use a "buffer" strategy and
will queue emitted elements if they cannot be consumed.

Here, the cancellation signal does dispose internal states in the
`MultiPartParser` and `PartGenerator` but does not clear the internal
queue in `FluxSink`.

This commit ensures that an operation is registered to release buffers
on the discard event.

Fixes gh-36262
2026-02-13 18:33:47 +01:00
Juergen Hoeller 44f3e7b427 Fix Checkstyle violation
See gh-36317
2026-02-13 16:34:58 +01:00
Juergen Hoeller e8e24e65d2 Detect all common size exceptions from Tomcat and Commons FileUpload 2.x
Closes gh-36317
2026-02-13 16:19:46 +01:00
Sam Brannen 9a002a77b2 Stop referring to "Java-8" features in documentation
See gh-36310
2026-02-13 15:21:31 +01:00
Brian Clozel 97e96895db Optimize MediaType(MediaType, Charset) constructor
Prior to this commit, the `MediaType` and `MimeType` "copy" constructors
would not leverage the fact that the existing instance has been
validated already (types, subtype and parameters have been checked
already for errors) and the entire validation would be performed again.
This would also allocate map instances in the process.

This commit ensures that the already validated information is reused
directly and that we avoid unnessecary operations and allocations for
such constructors.

Closes gh-36318
2026-02-13 14:49:34 +01:00
Juergen Hoeller e98c2fe488 Reuse AnnotatedMethod annotation cache in derived instances
Closes gh-36322
2026-02-13 13:55:24 +01:00
Juergen Hoeller 75705bcbb1 Restore early MessageConsumer creation for temporary queue
Closes gh-36321
2026-02-13 13:55:08 +01:00
rstoyanchev 47a2e7059e Optimal charset handling in AbstractHttpMessageConverter
Closes gh-36320
2026-02-13 12:39:03 +00:00
Sam Brannen 14b6339351 Consistently support @⁠Autowired as a meta-annotation
Prior to this commit, the findAutowiredAnnotation() method in
AutowiredAnnotationBeanPostProcessor fully supported finding
@⁠Autowired as a meta-annotation; however, the isRequired() method in
QualifierAnnotationAutowireCandidateResolver only found @⁠Autowired as
a "directly present" annotation without any support for
meta-annotations.

For consistency and to avoid bugs, this commit revises
QualifierAnnotationAutowireCandidateResolver so that we always support
@⁠Autowired as a meta-annotation.

Closes gh-36315
2026-02-12 16:38:24 +01:00
Juergen Hoeller bc2e89a786 Cache ResponseBody annotation presence per controller class
See gh-36311
2026-02-12 13:44:54 +01:00
Sam Brannen 4abbddf601 Avoid duplicate required attribute lookup for @⁠Autowired annotations
This commit revises AutowiredAnnotationBeanPostProcessor so that
determineRequiredStatus(MergedAnnotation<?>) only looks up the required
attribute once.

Closes gh-36314
2026-02-12 13:19:06 +01:00
Sam Brannen 926bcbd9a6 Stop referring to obsolete ListenableFuture in documentation
ListenableFuture as deprecated for removal in 6.0 and removed in 7.0.

See gh-33808
See gh-33809
See commit cb8ed43be1
Closes gh-36313
2026-02-12 13:19:06 +01:00
Sam Brannen cb8ed43be1 Stop referring to "JDK 8" features in documentation
See gh-36310
2026-02-12 12:27:51 +01:00
Juergen Hoeller 1703388074 Cache ResponseBody annotation presence per controller class
Closes gh-36311
2026-02-12 12:20:14 +01:00
Sam Brannen d84c4a39e2 Stop referring to "Java 8" features in documentation
Closes gh-36310
2026-02-12 12:11:02 +01:00
Sam Brannen 6f03c186b9 Polishing 2026-02-12 11:51:39 +01:00
Brian Clozel e10d37ad54 Next development version (v7.0.5-SNAPSHOT) 2026-02-12 10:39:15 +01:00
Juergen Hoeller 4734837fe4 Upgrade to Netty 4.2.10, Hibernate ORM 7.2.4, Checkstyle 13.2 2026-02-11 22:41:52 +01:00
Juergen Hoeller 4a13655aa5 Polishing 2026-02-11 19:22:27 +01:00
Brian Clozel 5ef9972528 Allow to disable defaults in HttpMessageConverters
The builder for `HttpMessageConverters` allows for auto-detection of
message converters on the classpath and their default registration when
`registerDefaults()` is called. Once called, there is no way to undo
this.

This commit adds a new `disableDefaults()` method to disable the default
registration and take full control over the list of message converters.

Closes gh-36303
2026-02-11 18:37:12 +01:00
Christian Schuster a9b1d6335e avoid unnecessary locking in ConcurrentReferenceHashMap's implementation of computeIfAbsent and computeIfPresent
Signed-off-by: Christian Schuster <christian@dnup.de>
2026-02-11 17:53:26 +01:00
Sam Brannen 98d4046e4b Document that @⁠Validated & @⁠Lazy may be used as meta-annotations
See gh-36305
See gh-36306
2026-02-11 17:37:06 +01:00
Sam Brannen 282fee5c95 Consistently find @⁠Lazy as a meta-annotation at arbitrary depths
Prior to this commit, AnnotationConfigUtils looked up @⁠Lazy as a
meta-annotation at arbitrary depths (e.g., when used as a
meta-meta-annotation); however,
ContextAnnotationAutowireCandidateResolver only found @Lazy as a
"directly present" meta-annotation.

For consistency, this commit revises
ContextAnnotationAutowireCandidateResolver so that it also finds @⁠Lazy
as a meta-annotation at arbitrary depths.

Closes gh-36306
2026-02-11 17:33:15 +01:00
Sam Brannen 0d4737aeec Polishing 2026-02-11 17:26:15 +01:00
Juergen Hoeller 1ef8734881 Cache method annotations in AnnotatedMethod as well
See gh-36307
2026-02-11 17:23:05 +01:00
Juergen Hoeller 6dfa54b8ea Cache method annotations in MethodParameter
Closes gh-36307
2026-02-11 16:41:55 +01:00
Sam Brannen 2d3a2c5cbd Consistently find @⁠Validated as a meta-annotation at arbitrary depths
Prior to this commit, ValidationAnnotationUtils looked up @⁠Validated
as a meta-annotation at arbitrary depths (e.g., when used as a
meta-meta-annotation) in determineValidationGroups() but only as a
"directly present" meta-annotation in determineValidationHints().

For consistency, this commit revises determineValidationHints() so that
it finds @⁠Validated as a meta-annotation at arbitrary depths as well.

Closes gh-36274
2026-02-11 16:02:13 +01:00
Brian Clozel 62e590a437 Use updated message in HttpEntityMethodProcessor
Prior to this commit, the `HttpEntityMethodProcessor` would create a new
`ServletServerHttpRequest` input message to parse the native Servlet
request, but would not reuse it for reading the request body using the
message converters.

In gh-32471, we applied a change that updates HTTP headers accordingly
when request parameters are read. But not reusing the input message
means that we are losing this update when instantiating the resulting
`HttpEntity`.

This commit ensures that `HttpEntityMethodProcessor` uses the input
message it just created when decoding the request body.

Fixes gh-36298
2026-02-11 12:32:15 +01:00
Sam Brannen 7f29df4d79 Make StompBrokerRelayMessageHandler's isPauseable() logic dynamic
Prior to this commit, an attempt to restart the
StompBrokerRelayMessageHandler (SBRMH) resulted in an
IllegalStateException stating that the ReactorNettyTcpClient was still
in the process of "Shutting down." The reason is that a
ReactorNettyTcpClient cannot be restarted after it has been closed, and
that is by design.

To address that issue, this commit introduces an
`internallyManagedTcpClient` flag in SBRMH that is used to track
whether SBRMH is in charge of managing the TcpClient internally or if
the TcpClient was supplied by the user and is therefore managed
externally.

If SBRMH manages the TcpClient internally, isPauseable() returns
`true`, and the `tcpClient` field is set to null (in stopInternal())
when the handler is stopped. Consequently, a new internally managed
TcpClient will be created when the handler is restarted.

If the TcpClient is managed externally, the handler is not considered
to be "pauseable", and a reference to the externally managed TcpClient
is retained after the handler has been stopped. If the
ApplicationContext is subsequently restarted, the externally managed
TcpClient will be reused -- which may or may not work, depending on the
implementation of the TcpClient. Note, however, that this has always
been the behavior of SBRMH with regard to stop/start scenarios for
externally managed TcpClients.

Closes gh-36266
2026-02-11 11:19:36 +01:00
rstoyanchev 8fb119df1e Determine validation groups in HandlerMethod
Closes gh-36274
2026-02-11 09:00:52 +00:00
rstoyanchev 1bb0fdda6b Move validation group determination to ValidationAnnotationUtils
See gh-36274
2026-02-10 16:56:33 +00:00
rstoyanchev 6507367d10 Polishing contribution
Closes gh-36179
2026-02-10 15:28:54 +00:00
Junhwan Kim fee7e6b7e2 Refine SendTo/Subscription header propagation
See gh-36179

Signed-off-by: Junhwan Kim <musoyou1085@gmail.com>
2026-02-10 15:28:54 +00:00
Brian Clozel 35c4973504 Upgrade to Reactor 2025.0.3
Closes gh-36288
2026-02-10 14:06:02 +01:00
Sam Brannen 77b54c7409 Stop setting the relayPort, since we explicitly set the TCP client 2026-02-10 13:21:33 +01:00
Sam Brannen ff9ba15762 Extract test logging into extension 2026-02-10 13:20:43 +01:00
Łukasz Jernaś c33b82f2a1 Log which HandlerInterceptor threw the exception
Closes gh-36281

Signed-off-by: Łukasz Jernaś <lukasz.jernas@allegro.com>
2026-02-10 11:44:26 +01:00
Brian Clozel ec459aefbf Upgrade to Micrometer 1.6.3
Closes gh-36287
2026-02-10 11:42:30 +01:00
rstoyanchev b227701473 Optimize RequestMappingInfo hashcode calculation
Precalculated hashcode makes sense for infos in the registry, but
matched infos created on the fly don't need it.

Closes gh-36279
2026-02-10 10:14:54 +00:00
Tran Ngoc Nhan 7a85ce959b Remove obsolete code in Redirecting to a resource section
See gh-34103
Closes gh-36384

Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>
2026-02-10 11:13:39 +01:00
Brian Clozel e93e55b456 Fix wildcard MIME type support in messaging converters
Prior to this commit, the "application/*+json" wildcard MIME type was
added to the list of supported MIME types in the JSON messaging
converter. This change wasn't fully reflected in the
`AbstractMessageConverter`, because only strict matching of type and
subtybe were considered.

This commit updates the `AbstractMessageConverter` to not only check the
type and subtype, but also check whether the supported MIME type
includes the one given as a parameter.

Fixes gh-36285
2026-02-10 10:41:17 +01:00
rstoyanchev e22b1648bf Cache HandlerMethod with resolved bean if singleton
See gh-36278
2026-02-09 17:16:05 +00:00
rstoyanchev 78967d4bd6 Optimize version match
Closes gh-36276
2026-02-09 16:36:26 +00:00
rstoyanchev 1d2351c87b Optimize single PathPattern match
Closes gh-36275
2026-02-09 16:33:22 +00:00
rstoyanchev 02dc4b6342 Avoid determineValidationGroups not necessary
There is no need to call determineValidationGroups if the method
itself doesn't require method validation.

See gh-36274
2026-02-09 15:52:58 +00:00
Sam Brannen 143828e27c Introduce "Attention Required" section in changelog
Closes gh-36277
2026-02-09 12:39:39 +01:00
Sam Brannen 7998bc9de2 Fix grammar in Javadoc 2026-02-09 12:06:06 +01:00
Manu Sridharan 8a1a9d2ab2 Fix nullability annotation in JdbcTemplate
Signed-off-by: Manu Sridharan <msridhar@gmail.com>
2026-02-08 18:13:57 +01:00
Juergen Hoeller 394a97d7e0 Add setPackagesToScan to LocalEntityManagerFactoryBean (and make setDataSource work)
Includes fix for consistent PersistenceException in case of no unit found for name.
Includes proper tests for Local(Container)EntityManagerFactoryBean with scan setup.

Closes gh-36270
Closes gh-36271
2026-02-08 17:54:02 +01:00
Juergen Hoeller 05ffe8249f Adapt to getAllClassNames SPI (post JPA 4.0 M1)
See gh-35705
2026-02-07 21:01:25 +01:00
Juergen Hoeller 20970a4a37 Repeatedly check status while trying to lock for shutdown
Closes gh-36260
2026-02-06 19:03:54 +01:00
rstoyanchev cb4b87cce2 Use headerFilter on SimpAnnotationMethodMessageHandler
See gh-36179
2026-02-06 17:13:11 +00:00
rstoyanchev 1d470fc375 Exposes headerFilter on SimpAnnotationMethodMessageHandler
Closes gh-36179
2026-02-06 17:08:50 +00:00
rstoyanchev 7fc619acdc Polishing contribution
See gh-36179
2026-02-06 17:08:50 +00:00
김준환 e5f8c5b7ae Add header propagation predicate support to message return value handlers
See gh-36179

Signed-off-by: Junhwan Kim <musoyou1085@gmail.com>
2026-02-06 17:08:50 +00:00
Juergen Hoeller 3666ad9d7f Derive StatelessSession from primary Session in HibernateJpaDialect
This is analogous to LocalSessionFactoryBean's SpringSessionContext.

See gh-36025
2026-02-06 00:18:00 +01:00
Juergen Hoeller 652d509cfc Avoid direct javadoc reference to JPA 4.0 EntityAgent class
See gh-36025
2026-02-05 20:13:20 +01:00
Juergen Hoeller 153c378bb2 Polishing 2026-02-05 20:01:47 +01:00
Juergen Hoeller cdbaa7f3a7 Early support for JPA 4.0 EntityAgent (autowiring a shared proxy)
Includes skipping of isOpen check on close where easily possible.

Closes gh-36025
2026-02-05 20:01:26 +01:00
Brian Clozel d1b0bdb3d5 Revert Servlet TRACE error dispatch workaround
Closes gh-36258
2026-02-04 14:53:07 +01:00
Brian Clozel 59a9d0fcb9 Remove outdated GraalVM substitutions
Closes gh-36257
2026-02-04 14:52:40 +01:00
rstoyanchev db9045ae28 Polishing in CharBufferDecoder 2026-02-04 11:37:52 +00:00
rstoyanchev 7c2159fbe9 Polishing contribution
Closes gh-36225
2026-02-04 11:36:45 +00:00
Đặng Minh Dũng f2d3da3f32 Add ProxyFactoryCustomizer
Allows manipulating the `ProxyFactory` before the proxy is created

See gh-36225

Signed-off-by: Đặng Minh Dũng <dungdm93@live.com>
2026-02-04 11:35:48 +00:00
Yurii Platunov f1c86b1bbc Fix formatting in Web MVC HTTP Message Conversion documentation
Closes gh-36250

Signed-off-by: Yurii Platunov <furachannel16@gmail.com>
2026-02-04 11:30:32 +01:00
Johnny Lim 7fde8a6fa3 Remove obsolete doc for spring.context.annotated-bean-reader.create
See gh-35570
Closes gh-36241

Signed-off-by: Johnny Lim <izeye@naver.com>
2026-02-04 11:06:16 +01:00
Ivars Silamiķelis 033c309d39 Fix JSON syntax in STOMP overview documentation
Closes gh-36229

Signed-off-by: Ivars Silamiķelis <ivars.silamikelis@gmail.com>
2026-02-04 11:04:13 +01:00
Juergen Hoeller ac243fae65 Runtime compatibility with JPA 4.0.0-M1 and Hibernate ORM 8.0.0.Alpha1
Closes gh-35705
2026-02-03 19:13:48 +01:00
Juergen Hoeller ad19c1ac82 Introduce beforeRetry callback with RetryState argument
Closes gh-36245
2026-02-02 18:34:00 +01:00
Sébastien Deleuze 29d125e659 Add resource hint for BeanRegistrar implementing ImportAware
Closes gh-36242
2026-02-02 15:44:22 +01:00
Juergen Hoeller d31bd8139d Remove duplicated Jakarta Validation API declaration 2026-02-02 14:45:22 +01:00
Juergen Hoeller fb10455d3c Upgrade to Groovy 5.0.4, Tomcat 11.0.18, Jetty 12.1.6, Jackson 3.0.4/2.20.2, Hibernate ORM 7.2.3, Checkstyle 13.1 2026-02-02 12:14:40 +01:00
Juergen Hoeller 40350653e1 Upgrade to ASM 9.9.1 and Objenesis 3.5
Includes JavaPoet 0.10.0

Closes gh-36243
2026-02-02 12:13:41 +01:00
Sam Brannen a08a4d6068 Document @⁠SpringExtensionConfig in the reference manual
This commit also updates the Javadoc for the SpringExtension and
@⁠SpringExtensionConfig to point out that the SpringExtension always
uses a test-class scoped ExtensionContext if
@⁠TestInstance(Lifecycle.PER_CLASS) semantics are in effect.

Closes gh-36240
2026-01-31 16:24:38 +01:00
dependabot[bot] 6fd84e4c2f Upgrade fast-xml-parser from 4.5.2 to 5.3.4 in /framework-docs
Closes gh-36234
2026-01-31 15:31:24 +01:00
Juergen Hoeller 516bf1c606 Consistently detect resilience annotations on interfaces
Closes gh-36233
2026-01-30 17:12:22 +01:00
Sam Brannen 32b55e68fc Upgrade to Gradle 9.3.1 2026-01-30 15:48:15 +01:00
Sam Brannen b1f427f137 Fix typo 2026-01-30 13:41:20 +01:00
Sébastien Deleuze f52481493c Use @Nullable Void in TaskCallback#from
See gh-36208
2026-01-30 13:36:29 +01:00
qwding 149397ed10 Optimize NamedParameterUtils#buildValueArray by lazily fetching SqlParameter
This PR optimizes the performance of NamedParameterUtils#buildValueArray by deferring the call to findParameter(declaredParams, paramName, i).

Changes: In the original implementation, findParameter was called for every parameter in the loop, regardless of whether the paramValue retrieved from paramSource was already an instance of SqlParameterValue.

Since findParameter involves iterating through the declaredParams list (or performing lookups), skipping this call when paramValue instanceof SqlParameterValue is true reduces unnecessary CPU cycles and memory access, especially for queries with a large number of parameters or long declaredParams lists.

Signed-off-by: qwding <761945125@qq.com>
2026-01-30 11:59:43 +01:00
Brian Clozel e1d3076570 Fix "remove" implementation in netty headers adapter
Prior to this commit, the `Netty4HeadersAdapter` `MultiValueMapi#remove`
implementation would return an empty list if no value was present. This
is not consistent with other implementations.

This change ensures that `null` is returned for those cases.

Fixes gh-36226
2026-01-29 11:06:46 +01:00
Juergen Hoeller 16f4b23c32 Consistently close streams through try-with-resources
Closes gh-36223
2026-01-28 18:08:06 +01:00
Sam Brannen 54c3d01270 Revise documentation on JSR-330 support in the reference manual
Closes gh-36222
2026-01-28 18:06:39 +01:00
Sam Brannen 799a520c35 Consistently refer to "an SQL" statement, type, etc.
Aside from the three classes I modified, we already do this
consistently across the code base and in the reference manual.
2026-01-28 18:02:34 +01:00
Patrick Strawderman bf5386712c Use Java Files utilities in FileCopyUtils
Use helpers from java.nio.file.Files for some methods in FileCopyUtils.

Additionally, add unit tests for the various file-related methods.

Signed-off-by: Patrick Strawderman <pstrawderman@netflix.com>
2026-01-28 17:46:09 +01:00
Juergen Hoeller 5d33de943b Polishing 2026-01-28 17:09:02 +01:00
Juergen Hoeller 7da65fe4fc Support -1 for undetermined length in SqlBinaryValue/SqlCharacterValue
Closes gh-36219
2026-01-28 17:08:40 +01:00
Sam Brannen 0d612b3b1d Make TaskCallback a @⁠FunctionalInterface 2026-01-28 14:54:52 +01:00
Sam Brannen 4dacc64a30 Fix formatting and spelling 2026-01-28 14:27:27 +01:00
Sam Brannen ba47dd0714 Fix links to JUnit User Guide
Closes gh-36217
2026-01-28 14:16:32 +01:00
rstoyanchev 79386b0e83 Fix unfinished edit from previous commit
See gh-36198
2026-01-28 12:27:09 +00:00
rstoyanchev d1bfe03358 Update docs on trailing slash handling
Closes gh-36198
2026-01-28 12:21:33 +00:00
rstoyanchev 4bc9f68451 Update docs content types for ProblemDetail
Closes gh-36192
2026-01-28 12:21:33 +00:00
Sam Brannen ce0e37536b Fix Javadoc formatting
See gh-36207
2026-01-28 11:00:42 +01:00
Juergen Hoeller b0ac8df13e Consistent delegation to target ValidatorFactory 2026-01-27 20:55:01 +01:00
Juergen Hoeller 80a57c2468 Consistent references to Hibernate ORM 7.x 2026-01-27 20:46:30 +01:00
Juergen Hoeller 02e05b40b0 Polishing 2026-01-27 20:30:46 +01:00
Juergen Hoeller c8c99d0464 Support JtaTransactionManager/SpringBeanContainer on HibernateJpaVendorAdapter
Makes ConfigurableJtaPlatform public and removes outdated WebSphere UOW check.

Closes gh-36216
2026-01-27 20:30:21 +01:00
Juergen Hoeller 867dd0e972 Upgrade to Hibernate ORM 7.2.2 2026-01-27 20:28:18 +01:00
Sébastien Deleuze 2c96f18439 Upgrade nullability plugin to 0.0.11
Closes gh-36188
2026-01-27 14:10:18 +01:00
Patrick Strawderman 96fb5d72eb Use Reader.transferTo in FileCopyUtils
Use the transferTo method in FileCopyUtils#copy(Reader, Writer).

Closes gh-36196

Signed-off-by: Patrick Strawderman <pstrawderman@netflix.com>
2026-01-27 09:38:58 +01:00
Sébastien Deleuze bc01eeb433 Leverage JUnit 6 suspending function support
Closes gh-36215
2026-01-27 08:45:04 +01:00
rstoyanchev 77c24b28b6 Polishing in ReactorClientHttpConnector 2026-01-26 10:51:00 +00:00
Kuyho Chung e809395635 Clarify Kotlin Coroutine definition
Closes gh-36209
Signed-off-by: Kuyho Chung <kchung1995@gmail.com>
2026-01-26 09:54:15 +01:00
Sam Brannen 801035bea7 Upgrade to AssertJ 3.27.7 2026-01-25 17:51:00 +01:00
Sam Brannen 9e4a54bb99 Ensure RetryTaskTests compiles in Eclipse IDE
Prior to this commit, "new RetryTask<>" declarations whose
TaskCallbacks (implemented as lambda expressions) did not actually
throw an exception resulted in a compilation error in Eclipse:

  "Unhandled exception type Exception".

This is due to the fact that RetryTask is declared with "E extends
Exception", and the Eclipse compiler therefore infers that the lambda
expression potentially throws a checked Exception.

To address that, this commit explicitly declares that the affected
lambda expressions throw unchecked RuntimeExceptions:

  "new RetryTask<String, RuntimeException>"
2026-01-25 17:46:14 +01:00
Sam Brannen e56f7c1e53 Polishing 2026-01-25 17:35:17 +01:00
Padraic Slattery 65bdc78a1e Update GitHub upload-artifact action to version 6
Closes gh-36199

Signed-off-by: Padraic Slattery <pgoslatara@gmail.com>
2026-01-25 17:07:09 +01:00
Juergen Hoeller fd3bbb057e Polishing 2026-01-25 10:42:13 +01:00
Juergen Hoeller c47da1247a Add TaskCallback/Callable/Runnable wrapper for retryable tasks
Closes gh-36208
2026-01-25 10:41:25 +01:00
Juergen Hoeller 22cf7958a5 Revise setPersistenceUnitName javadoc
Closes gh-36205
2026-01-25 10:38:24 +01:00
Phillip Webb ab314aadd9 Refine YamlProcessor to allow different empty values
Update `YamlProcessor` to allow any value to be inserted for
empty entries. This update will allow blank strings to be used
instead of `null` which better aligns with the way that
`.properties` files are loaded.

This update also allows allows the specified `emptyValue` to be
inserted instead of a blank String when the YAML contains `null`
or `~` values.

See gh-36197
See gh-19986

Signed-off-by: Phillip Webb <phil.webb@broadcom.com>
2026-01-24 20:23:48 +01:00
Sébastien Deleuze 611811d8fc Anticipate fixing Kotlin 2.3 compilation warning
Closes gh-36203
2026-01-23 18:34:45 +01:00
Phillip Webb 0c9f40b171 Allow YAML processors to create a flattened map with nulls included
Add an additional `getFlattenedMap` method to `YamlProcessor` to allow
the resulting flattened map to include nulls.

This update will allow processor subclasses to tell the difference
between YAML that is defined with an empty object vs missing the key
entirely:

e.g.:

  application:
    name: test
    optional: {}

vs

  application:
    name: test
    optional: {}

Closes gh-36197

Signed-off-by: Phillip Webb <phil.webb@broadcom.com>
2026-01-23 18:07:06 +01:00
Sam Brannen 5b4eda3d20 Clean up warnings in Gradle build 2026-01-22 17:27:24 +01:00
Sam Brannen af8fd8f07d Upgrade to TestNG 7.12 2026-01-22 17:27:00 +01:00
rstoyanchev 345fa51e77 Handle early exception from AsynchronousFileChannel#write
Closes gh-36184
2026-01-21 15:43:03 +01:00
Juergen Hoeller c05ea841bc Declare Callable parameter with nullable type variable
Closes gh-36191
2026-01-21 19:45:18 +01:00
Juergen Hoeller dab28b5dce Upgrade to Selenium 4.40 2026-01-21 15:09:24 +01:00
Juergen Hoeller 1c4b20287f Use correlation id for explicit sendAndReceive response queue
See gh-36162
See gh-36177
2026-01-21 14:46:36 +01:00
Sébastien Deleuze 8fbd147f0b Upgrade nullability plugin to 0.10.0
Closes gh-36188
2026-01-21 10:39:41 +01:00
Juergen Hoeller 1c56ec3f7e Polishing 2026-01-20 19:24:03 +01:00
Juergen Hoeller 65565de1a8 Prevent accidental printStackTrace() usage in main codebase
Closes gh-36185
2026-01-20 19:23:37 +01:00
Juergen Hoeller b3c26255b1 Upgrade to Hibernate ORM 7.2.1 and Protobuf 4.33.4 2026-01-20 19:23:29 +01:00
Sam Brannen 66962875f9 Reinstate test for Jetty in CookieIntegrationTests 2026-01-20 17:11:03 +01:00
Sam Brannen 5490ba94ae Simplify tests 2026-01-20 17:10:07 +01:00
Sam Brannen 17e02804ba Revert changes to FailingBeforeAndAfterMethodsTestNGTests 2026-01-20 15:47:45 +01:00
Sam Brannen 357205723f Simplify tests 2026-01-20 13:54:19 +01:00
Sam Brannen c4c167c0e1 Convert various test classes to @⁠ParameterizedClass
See gh-35833
2026-01-20 13:53:56 +01:00
rstoyanchev 4119ecc25b Refine solution to clear Netty channel attribute
Closes gh-36158
2026-01-20 11:34:18 +00:00
Sam Brannen 9ebfdb8b6a Fix test parameterization in CaffeineReactiveCachingTests
Prior to this commit, test methods in CaffeineReactiveCachingTests
were parameterized twice with the same configuration class.

See gh-31637
See gh-35833
2026-01-20 11:43:22 +01:00
Sébastien Deleuze c4cb55fbde Modernize DispatcherServlet context hierarchy diagram
See gh-36175
2026-01-20 09:41:28 +01:00
Sam Brannen 7917ae53e8 Polishing 2026-01-19 19:16:28 +01:00
Sébastien Deleuze 693ec8219f Introduce RestClient.ResponseSpec#requiredBody
Closes gh-36173
2026-01-19 18:15:34 +01:00
rstoyanchev 9b95482557 ContentRequestMatchers supports HttpEntity multipart
Closes gh-36154
2026-01-19 17:05:16 +00:00
Sam Brannen a029dfc6a8 Improve display names for parameterized invocations
See gh-35833
2026-01-19 17:59:49 +01:00
Sébastien Deleuze a921c487b4 Fix snippet imports
See gh-36175
2026-01-19 17:38:30 +01:00
Sébastien Deleuze 3f01830d11 Refine CORS documentation to match latest changes
Closes gh-36156
2026-01-19 17:27:19 +01:00
Sébastien Deleuze 64d95d7f3e Extract code snippets from mvc-servlet.adoc
Closes gh-36175
2026-01-19 17:24:38 +01:00
Sébastien Deleuze 5a4b1bd1d1 Extract code snippets from logging.adoc
See gh-36175
2026-01-19 17:24:38 +01:00
Sébastien Deleuze a91402dcc6 Extract code snippets from multipart.adoc
See gh-36175
2026-01-19 17:24:38 +01:00
Sébastien Deleuze 0014e0b3c9 Extract code snippets from context-hierarchy.adoc
See gh-36175
2026-01-19 17:24:38 +01:00
Sébastien Deleuze 49da766551 Extract code snippets from exceceptionhandlers.adoc
See gh-36175
2026-01-19 17:24:38 +01:00
Sébastien Deleuze 50bbe6191a Extract code snippets from container-config.adoc
See gh-36175
2026-01-19 17:24:37 +01:00
Shubham Kalloli a8c33c13e2 Upgraded Apache POI and Commons IO to remediate CVEs
Signed-off-by: Shubham Kalloli <shubham.kalloli@est.tech>
2026-01-19 17:06:02 +01:00
Sam Brannen b164db35c1 Revise contribution
See gh-36170
2026-01-18 16:59:30 +01:00
Tran Ngoc Nhan 385e62dbf0 Replace getErrors() with getBindingResult() in examples
DataBinder#getErrors was removed in v4.0.0.M1.

Closes gh-36170

Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>
2026-01-18 16:55:32 +01:00
Sam Brannen 4d4adeb2c6 Polish Javadoc 2026-01-18 16:52:04 +01:00
Juergen Hoeller 7aa862f5ec Make scope fully configurable
Closes gh-36171
2026-01-18 15:55:36 +01:00
Juergen Hoeller 2d2809f45c Consistently throw Task/InvocationRejectedException on access rejected
Closes gh-36167
2026-01-18 15:39:58 +01:00
Michał Pisarski 3f390d55f0 Add overloaded sendAndReceive methods to JmsOperations and JmsTemplate to use explicit response queue
Signed-off-by: Michał Pisarski <pisekfm@o2.pl>
2026-01-17 00:16:15 +01:00
Juergen Hoeller 2b96a61063 Apply transactionIsolationLock in EclipseLinkConnectionHandle as well
Closes gh-36165
2026-01-16 23:12:07 +01:00
Juergen Hoeller c55351aa4a Remove javadoc references to WebLogic API
See gh-36151
2026-01-16 22:50:46 +01:00
Juergen Hoeller 48bedd0af1 Revise DefaultMessageListenerContainer connection recovery
Includes listener validation for consistent back-off behavior.

Closes gh-36143
2026-01-16 22:38:01 +01:00
Juergen Hoeller c1b0e68d6c Bring back WebLogicJtaTransactionManager for WebLogic 15.1.1
Closes gh-36151
2026-01-16 22:37:20 +01:00
rstoyanchev fe2113f5a3 Clear Netty channel attribute
Closes gh-36158
2026-01-16 17:21:27 +00:00
Sam Brannen 2ff2a89f22 Upgrade to Gradle 9.3
Closes gh-36161
2026-01-16 15:41:31 +01:00
Brian Clozel 14641c4299 Next development version (v7.0.4-SNAPSHOT) 2026-01-15 09:26:50 +01:00
Juergen Hoeller 62fd09dfa5 Polishing 2026-01-14 22:05:06 +01:00
Sam Brannen 9df19dedaf Revise wording for PauseMode documentation
See gh-36044
2026-01-14 17:02:22 +01:00
Sam Brannen 01a57a7219 Simplify DefaultContextCache implementation by using entrySet().removeIf() 2026-01-14 13:47:44 +01:00
Sam Brannen b5c2003c51 Fix variable name 2026-01-14 13:44:04 +01:00
Sam Brannen 5f5da06467 Upgrade to JUnit 6.0.2
Closes gh-36149
2026-01-14 13:00:31 +01:00
rstoyanchev 9f19b4074d Exclude DataAccessException and MessagingException in DisconnectedClientHelper
Closes gh-36134
2026-01-14 11:44:43 +00:00
Yanming Zhou a784eb0ab4 Improve DisconnectedClientHelper to better guard ClassNotFoundException
Before this commit, WebClientException is ignored if RestClientException is not present.

Signed-off-by: Yanming Zhou <zhouyanming@gmail.com>
2026-01-14 11:03:09 +00:00
Sam Brannen fa404063ab Avoid unnecessary pausing of application contexts for tests
In commit 9711db787e, we introduced support for disabling test
application context pausing via a Spring property or JVM system
property, as follows.

-Dspring.test.context.cache.pause=never

However, users may actually be interested in keeping the pausing
feature enabled if contexts are not paused unnecessarily.

To address that, this commit introduces a new
PauseMode.ON_CONTEXT_SWITCH enum constant which is now used by default
in the DefaultContextCache.

With this new pause mode, an unused application context will no longer
be paused immediately. Instead, an unused application context will be
paused lazily the first time a different context is retrieved from or
stored in the ContextCache. This effectively means that an unused
context will not be paused at all if the next test class uses the same
context.

Although ON_CONTEXT_SWITCH is the now the default pause mode, users
still have the option to enable context pausing for all usage scenarios
(not only context switches) by setting the Spring property or JVM
system property to ALWAYS (case insensitive) — for example:

-Dspring.test.context.cache.pause=always

This commit also introduces a dedicated "Context Pausing" section in
the reference manual.

See gh-36117
Closes gh-36044
2026-01-14 11:37:37 +01:00
Sam Brannen 948af8bcc6 Fix typo in Javadoc 2026-01-14 11:37:37 +01:00
Juergen Hoeller e3997e3060 Upgrade to Reactor 2025.0.2 and Micrometer 1.16.2
Includes Vavr 0.11, HtmlUnit 4.21, Checkstyle 13.0

Closes gh-36144
Closes gh-36145
2026-01-14 11:29:29 +01:00
Juergen Hoeller 0323218129 Deprecate PagedListHolder and PropertyComparator for removal
Closes gh-36139
2026-01-13 21:20:38 +01:00
Brian Clozel a62699ccfc Further Gradle deprecation fixes
Closes gh-36132
2026-01-13 09:32:08 +01:00
Yejeong, Ham dd6faf62bb Build: Fix deprecated Groovy syntax in spring-aspects
Replace legacy Groovy DSL property assignment (space-separated) with
the standard assignment operator (=) in spring-aspects.gradle.

The legacy syntax is deprecated in Gradle 9 and will be removed in
Gradle 10. This change eliminates deprecation warnings during the
build and ensures future compatibility.

See gh-36132

Signed-off-by: Yejeong, Ham <dev@thelightway.kr>
2026-01-13 09:31:48 +01:00
rstoyanchev cbc2c63abc Polishing contribution
Closes gh-36125
2026-01-12 12:48:49 +00:00
hojeong 2f83e6b4b5 Update reference for API version without a value
See gh-36125

Signed-off-by: hojeong park <parkhj062@gmail.com>
Signed-off-by: hojeong <parkhj062@gmail.com>
2026-01-12 12:24:13 +00:00
Sam Brannen 19c2506476 Do not restart contexts if pausing is disabled
If the PauseMode is set to NEVER, we never pause contexts and therefore
do not need to restart them.

See gh-36117
2026-01-12 12:24:18 +01:00
Brian Clozel ee4780fab6 Build javadoc against HttpClient 5.6.x 2026-01-12 12:11:26 +01:00
Sam Brannen 8e6d44349d Polishing 2026-01-12 11:50:03 +01:00
Sam Brannen ceb6ab3c74 Introduce ContextCacheTestSuite 2026-01-12 11:48:31 +01:00
Sébastien Deleuze 7140ab5bca Simplify TransactionalOperator.executeAndAwait
This commit simplifies TransactionalOperator.executeAndAwait by removing
Optional. It is based on a proposal by @vlsi refined to handle properly
reactive transaction commits (see related commit
217b6e37a6).

Closes gh-36039
2026-01-12 10:52:39 +01:00
rstoyanchev 615c25d4fa Polishing contribution
Closes gh-36126
2026-01-12 09:51:17 +00:00
Juhwan Lee 3df66580f3 Support property placeholders in HTTP service registry
Implement EmbeddedValueResolverAware to resolve ${...} placeholders
in @HttpExchange URL attributes.

See gh-36126
Signed-off-by: Juhwan Lee <jhan0121@gmail.com>
2026-01-12 08:56:35 +00:00
Sam Brannen 9711db787e Introduce Spring property to disable context pausing for tests
Spring Framework 7.0 introduced support for pausing inactive
application contexts between test classes and restarting them once they
are needed again. If pausing and restarting are fast, this feature does
not have a negative impact on test suites.

However, if the pausing or restarting of certain Lifecycle components
in the application context is slow, that can have a negative impact on
the duration of the overall test suite.

In gh-36044, we hope to find a way to avoid unnecessarily pausing an
application context after a test class if the same context is used by
the next test class that is run. That should help reduce the risk of a
negative impact caused by the pause/restart feature; however, for
certain scenarios that may not be enough. In light of that, this commit
introduces a mechanism for completely disabling the pausing feature via
a Spring property or JVM system property, as follows.

-Dspring.test.context.cache.pause=never

See gh-35168
See gh-36044
Closes gh-36117
2026-01-11 11:42:14 +01:00
Yanming Zhou d3a385d222 Retain original requested bean class for SpringContainedBean
Closes GH-36115

Signed-off-by: Yanming Zhou <zhouyanming@gmail.com>
2026-01-09 19:52:22 +01:00
Juergen Hoeller 38a397873e Polishing 2026-01-09 19:33:52 +01:00
Juergen Hoeller 73fa708905 Fix SmartFactoryBean type matching for ResolvableType.NONE
Closes gh-36123
2026-01-09 19:33:32 +01:00
rstoyanchev 23a532ed82 Polishing contribution
Closes gh-36097
2026-01-09 15:06:07 +00:00
Nabil Fawwaz Elqayyim e0db67aa3b Fix double encoding in DefaultApiVersionInserter
Ensure that the DefaultApiVersionInserter does not re-encode existing parts
of the input URI by using the 'encoded' flag in UriComponentsBuilder.

This prevents percent-encoded characters (like %20) from being incorrectly
double-encoded to %2520 during the version insertion process.

See gh-36097

Signed-off-by: Nabil Fawwaz Elqayyim <master@nabilfawwaz.com>
2026-01-09 15:06:07 +00:00
Sam Brannen 1370a833ed Further clarify the meaning of maxAttempts in FixedBackOff and ExponentialBackOff
See gh-36119
2026-01-09 15:37:32 +01:00
Sam Brannen 8d4320e9ef Honor headers in AbstractMessageSendingTemplate.convertAndSend() variant
Prior to this commit, the following method in AbstractMessageSendingTemplate
simply ignored the supplied headers map.

convertAndSend(Object, Map<String, Object>, MessagePostProcessor)

Closes gh-36120
2026-01-09 15:04:47 +01:00
Sam Brannen c53a00a6c4 Polish Javadoc 2026-01-09 15:04:47 +01:00
Sam Brannen 00606d2171 Use default destination name with JmsClient.sendAndReceive() and headers
Prior to this commit, if the JmsClient was configured with a default
destination name (instead of a default Destination), invoking the
sendAndReceive() variant which accepts a map of headers resulted in an
exception stating that a default destination is required.

To address that, this commit overrides the
convertSendAndReceive(Object, Map<String, Object>, Class<T>, MessagePostProcessor)
method in JmsMessagingTemplate to add support for both a default
Destination and a default destination name.

Closes gh-36118
2026-01-09 15:04:47 +01:00
Juergen Hoeller 3e5d8d184a Clarify back-off attempts versus retries for BackOff maxAttempts setting
Closes gh-36119
2026-01-09 14:32:59 +01:00
rstoyanchev b69cbad38d Fall back on the value type in BodyInserters if necessary
Closes gh-36078
2026-01-09 11:52:18 +00:00
rstoyanchev 39106b5811 Restore ApiVersionResolver contract
Instead of making it async and having a sync subinterface variant,
this restores ApiVersionResolver to be as it was with an async
subinterface variant.

ApiVersionStrategy, and the infrastructure invoking it, remains
async first, but also accommodates sync resolvers.

This should provide a better balance with backwards compatibility
while also accommodating async version resolution as the less
common scenario.

See gh-36084
2026-01-09 10:25:44 +00:00
rstoyanchev 2f91baa71c Polishing in ApiVersionResolver 2026-01-09 09:38:53 +00:00
rstoyanchev d8e303f95b Merge branch for PR 36084
Resolve conflicts and refactor merged code in AbstractHandlerMapping

See gh-36084
2026-01-08 17:43:35 +00:00
rstoyanchev 8ff89ffda2 Update contribution
- deprecate sync method on ApiVersionResolver
- add SyncApiVersionResolver
- refactor resolverParseAndValidateApiVersion method

See gh-36084
2026-01-08 17:41:27 +00:00
rstoyanchev dcf5d69d94 Rename async methods
See gh-36084
2026-01-08 17:41:19 +00:00
rstoyanchev 7d33a87278 Polishing contribution
See gh-36084
2026-01-08 17:41:11 +00:00
Jonathan Kaplan b878771dca Update ApiVersionResolver to return Mono String
See gh-36084

Signed-off-by: Jonathan Kaplan <jkaplan@empty-nes.com>
2026-01-08 17:41:01 +00:00
Sam Brannen 06f0f2f317 Polishing 2026-01-08 18:32:57 +01:00
Sam Brannen f16474d585 Reliably resolve overloaded write methods in PropertyDescriptorUtils
Prior to this commit, the algorithm behind determineBasicProperties()
in PropertyDescriptorUtils did not reliably resolve the correct write
method when one candidate write method had a parameter type that was a
subtype of another candidate write method whose parameter type was an
exact match for the resolved read method's return type.

In other words, the algorithm always resolved the candidate write
method with the most specific parameter type (similar to covariant
return types) which is not necessarily the resolved read method's
return type.

To address that, this commit ensures that determineBasicProperties()
always selects an exact match for the write method whenever possible.

As an added bonus, determineBasicProperties() no longer invokes
BasicPropertyDescriptor.getWriteMethod(), which avoids triggering the
resolution algorithm multiple times (when multiple write method
candidates exist), resulting in lazy resolution of the write method the
first time client code invokes getWriteMethod().

Closes gh-36113
2026-01-08 18:13:46 +01:00
Sam Brannen dfb17d84d6 Reintroduce deleted text in Javadoc 2026-01-08 18:11:55 +01:00
Sébastien Deleuze eea9130ea6 Fix context class resolution for nested types
This commit introduces ContextClassRequestBodyAdvice which adds a
"contextClass" hint allowing to resolve generics for Optional,
HttpEntity or ServerSentEvent container types.

Closes gh-36111
2026-01-08 16:53:28 +01:00
Sébastien Deleuze d85ac300bb Update JsonView advices Javadoc
Make the documentation relevant for all Jackson converters.
2026-01-08 16:53:19 +01:00
Sébastien Deleuze d4d10dc75d Enable JacksonSmileEncoderTests#annotEncodeServerSentEvent
This test has been fixed at some (unidentified) point and should be
enabled.
2026-01-08 16:49:11 +01:00
Juergen Hoeller 169465cce1 Align InvocationRejectedException javadoc with TaskRejectedException
See gh-36109
See gh-36114
2026-01-08 16:18:14 +01:00
Juergen Hoeller c4d5e3c57d Add task rejection support to SyncTaskExecutor's concurrency throttle
Closes gh-36114
2026-01-08 15:51:17 +01:00
Juergen Hoeller 38f5f4de8e Add configurable throttle policy to @ConcurrencyLimit annotation
Closes gh-36109
2026-01-08 15:50:57 +01:00
Sébastien Deleuze 644072f7ea Refine the AOT Cache creation section
Put Java 25+ snippet first and refine the note.

Closes gh-36108
2026-01-08 14:50:14 +01:00
Mikhail Polivakha 859940b824 Polishing of the AOT cache documentation
See gh-36108
Signed-off-by: Mikhail Polivakha <mikhailpolivakha@email.com>
2026-01-08 14:49:21 +01:00
Sam Brannen 992ae2ea6b Precompute PropertyDescriptor array in SimpleBeanInfoFactory
Closes gh-36112
2026-01-08 13:34:03 +01:00
Sam Brannen 589a10e6e6 Polish Javadoc formatting 2026-01-08 13:25:15 +01:00
Sam Brannen 79bd2b11da Polishing 2026-01-08 13:23:23 +01:00
Sam Brannen 0624c3a597 Introduce tests for non-generic types for PropertyDescriptorUtils
This commit introduces tests for proper support for non-generic types
in PropertyDescriptorUtils.determineBasicProperties(), effectively to
test the status quo and serve as regression tests.
2026-01-08 13:15:53 +01:00
Sébastien Deleuze bf695c058d Configure Jackson XML codecs when present
Closes gh-35752
2026-01-08 11:52:38 +01:00
Sébastien Deleuze a1204a405a Introduce Jackson XML codecs
See gh-35752
2026-01-08 11:52:38 +01:00
rstoyanchev e0aa116217 Re-initialize API version in each HandlerMapping
Each HandlerMapping may have its own ApiVersionStrategy rules, or may not
have one at all. This change ensures independent decisions.

See gh-36059
2026-01-08 09:32:34 +00:00
Philip Bolting 7fdda1cf0f Fix typo in MVC REST Error Responses documentation
Signed-off-by: Philip Bolting <pb@philipbolting.de>
2026-01-08 08:40:31 +01:00
rstoyanchev 05b98b0a32 Suppress API version errors on ERROR dispatch
While an API version may be important for mapping in an ERROR dispatch,
it is more important to allow the original exception to be handled.

Closes gh-36058
2026-01-07 16:00:40 +00:00
rstoyanchev 46efd930f0 Update docs
On the order of a ControllerAdvice that overrides the handling of
built-in exceptions in a scenario with Boot autoconfig.

Closes gh-35982
2026-01-07 16:00:39 +00:00
rstoyanchev a9027ddf76 Refine handling of API version errors
Among HandlerMapping's some may not expect an API version. This is why
those that do must be careful not to raise API validation errors if
they don't match the request.

Closes gh-36059
2026-01-07 16:00:39 +00:00
rstoyanchev 5551292639 Polishing contribution
Closes gh-35989
2026-01-07 16:00:39 +00:00
Minkyu Park 1240962d68 Optimize single-char wildcard path matching performance
Use lazy evaluation in SingleCharWildcardedPathElement to avoid
unnecessary Character.toLowerCase() calls.
Resolves performance TODO from 2017.

Closes gh-36095

Signed-off-by: Minkyu Park <rb6609@naver.com>
2026-01-07 14:37:54 +01:00
Brian Clozel 9ef4ceb047 Polishing contribution
Closes gh-36083
2026-01-07 14:29:13 +01:00
hayden.rear c7413cbdfc Configure HttpMessageConverters as a list
This commit adds a new `configureMessageConvertersList` method on the
builder to add/remove/move converters in the resulting list before they
are individually post-processed.

This allows to re-introduce a behavior that was missing with the new
contract: the ability to append a converter at the end of the list.

See gh-36083

Signed-off-by: hayden.rear <hayden.rear@gmail.com>
2026-01-07 14:29:05 +01:00
Sam Brannen e94aaab288 Improve exception message for JPA entity scanning 2026-01-07 13:13:27 +01:00
Sébastien Deleuze c5044bfdbc Refine multipart-forms.adoc
See gh-36094
2026-01-07 12:49:25 +01:00
Tran Ngoc Nhan 9abe4b46ca Fix Kotlin example in Multipart Content
Closes gh-36094
Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>
2026-01-07 12:20:59 +01:00
Sébastien Deleuze 192da6229c Fix locale interceptor code snippet
See gh-36105
See gh-36099
2026-01-07 09:52:33 +01:00
Sébastien Deleuze e1402fd252 Upgrade Antora dependencies
Closes gh-36105
2026-01-07 09:51:04 +01:00
Sébastien Deleuze 253eb28458 Update CBOR Jackson codec Javadoc
See gh-20513
2026-01-06 18:26:26 +01:00
Brian Clozel 97cd9dd03b Support HttpComponents 5.6
This commit updates the HttpComponents HttpClient to refer to the parsed
`HttpEntity` for the content-related HTTP response headers such as
encoding and body length.

Closes gh-36100
2026-01-06 16:56:45 +01:00
Sébastien Deleuze 3027d78f40 Modernize the DispatcherServlet Locale documentation
The Java sample for "Locale Interceptor" shows a
`urlHandlerMapping.setUrlMap(Map.of("...` line due the inability to
disable the code chomping Asciidoctor extension with the code include
one. It will be fixed by a subsequent commit or a bug fix in
https://github.com/spring-io/asciidoctor-extensions.

Closes gh-36099
2026-01-06 11:04:05 +01:00
Sébastien Deleuze d752e80356 Add missing line to MVC config interceptors snippet
To ensure consistency with Kotlin and XML snippets.
2026-01-06 11:04:05 +01:00
Sam Brannen e7f019bd3f Introduce tests for PropertyDescriptorUtils with bounded generics
As a follow up to commit 4b07edbaeb, this commit introduces tests for
PropertyDescriptorUtils.determineBasicProperties() using types with
bounded generics.

Note, however, that the following test effectively fails, since
PropertyDescriptorUtils.determineBasicProperties() does not match the
behavior of java.beans.Introspector. Consequently, this test method
currently changes the expected write method type conditionally.

resolvePropertiesWithUnresolvedGenericsInSubclassWithOverloadedSetter()

See gh-36019
2026-01-05 17:12:59 +01:00
Sébastien Deleuze 6939cb90df Add ServerRequest.bindAndAwait Kotlin extension
Closes gh-36092
2026-01-02 18:06:27 +01:00
Sébastien Deleuze 3ddaf51f5d Remove @EnableWebFlux from documentation code snippets
Like what we did for `@EnableWebMvc`, we want to remove `@EnableWebFlux`
from typical code snippets extending WebFluxConfigurer to make them more
Spring Boot friendly.

Closes gh-36091
2026-01-02 18:06:27 +01:00
Sébastien Deleuze 6a28f06b2e Extract some WebFlux functional snippets
See gh-36089
2026-01-02 18:06:27 +01:00
Sébastien Deleuze 5ecece91dc Polishing
See gh-36089
2026-01-02 18:06:26 +01:00
Tran Ngoc Nhan 13d7a6c480 Fix Kotlin snippets in "CORS" and "Functional Endpoints" sections
Closes gh-36089
Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>
2026-01-02 18:05:34 +01:00
Sébastien Deleuze 6e66af15a6 Extract remaining WebMVC configuration snippets
Closes gh-36088
2025-12-31 12:15:12 +01:00
Sébastien Deleuze cd8e38448c Remove the "Other Web Frameworks" documentation
Closes gh-36086
2025-12-31 10:58:44 +01:00
Sébastien Deleuze 93ce99ea06 Polishing 2025-12-31 10:58:44 +01:00
Sébastien Deleuze bf8a1600e5 Add missing override annotations to PropagationContextElement
Closes gh-36087
2025-12-31 10:58:43 +01:00
Sébastien Deleuze 96eee8c8b1 Make nullability flexible in ConverterFactory#getConverter
This commits udpates ConverterFactory#getConverter to accept both
nullable and non-null T in the Converter return value.

Flexible nullability at ConverterFactory type level would have been
ideal, but is not possible due to how Kotlin deals with Class<T> when
T is nullable.

See gh-36063
2025-12-30 16:07:39 +01:00
Tran Ngoc Nhan b8ed4f2ba8 Correct encode Javadoc for UriComponents and UriComponentsBuilder
Closes gh-36040
Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>
2025-12-30 15:26:08 +01:00
Tran Ngoc Nhan 0048f01074 Fix links in client-builder.adoc
Closes gh-36079
Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>
2025-12-30 15:11:52 +01:00
Sébastien Deleuze 0b904a878d Fix indentation in framework-docs.gradle 2025-12-30 15:04:14 +01:00
Sébastien Deleuze 9c78f84915 Modernize the view technologies section
Closes gh-35450
2025-12-30 15:03:34 +01:00
Sébastien Deleuze a55309d478 Null-mark org.springframework.test.web.support package
See gh-36054
2025-12-29 17:09:53 +01:00
Sébastien Deleuze 46fb7c026c Upgrade to nullability plugin 0.0.9
This commit also replaces Arch Unit packageInfoShouldBeNullMarked() rule
by either configuring requireExplicitNullMarking = false when the whole
module does not have JSpecify annotations, or explicit @NullUnmarked
when some have and some don't.

See gh-36054
2025-12-29 17:09:53 +01:00
Manu Sridharan d36244ee56 Update to NullAway 0.12.15 and fix new warnings
Closes gh-36054
Signed-off-by: Manu Sridharan <msridhar@gmail.com>
2025-12-29 17:09:52 +01:00
Sam Brannen 4b07edbaeb Reliably resolve generic read/write methods in PropertyDescriptorUtils
Prior to this commit, the determineBasicProperties() method in
PropertyDescriptorUtils did not reliably resolve read/write methods in
type hierarchies with generics. This utility method is used by
SimpleBeanInfoFactory which is used by BeanUtils and BeanWrapperImpl.
Thus, failure to reliably resolve read/write JavaBeans methods resulted
in bugs in certain scenarios.

For example, BeanUtils.copyProperties() randomly failed to copy certain
properties if the write method for the property could not be resolved.

To address such issues, this commit revises the implementation of
PropertyDescriptorUtils as follows.

1) Read methods with covariant return types are now consistently
   resolved correctly.

2) If multiple ambiguous write methods are discovered, the algorithm now
   checks for an exact match against the resolved generic parameter type
   as a fallback.

Closes gh-36019
2025-12-29 12:18:23 +02:00
Brian Clozel eb3bfc0d65 Document HttpHeaders#toSingleValueMap() case-sensitive behavior
This commit updates the `HttpHeaders` javadoc to better reflect that
`asSingleValueMap()`, `asMultiValueMap()`, and `toSingleValueMap()` all
return case-sentitive map implementations.

Closes gh-36070
2025-12-29 11:02:28 +01:00
木葉 Scarlet d077e043d6 Consistently declare @⁠Nullable on parameter in equals() implementations
Closes gh-36075

Signed-off-by: 木葉 Scarlet <93977077+mukjepscarlet@users.noreply.github.com>
2025-12-29 11:43:49 +02:00
Sam Brannen 7212fbe36a Polishing 2025-12-29 11:43:23 +02:00
Juergen Hoeller 5b6c231788 Use dedicated ApplicationEventFactory interface in EventPublicationInterceptor
See gh-36072
2025-12-25 11:25:54 +01:00
Juergen Hoeller aeb0115605 Introduce MethodRollbackEvent for @Transactional rollbacks
Closes gh-36073
2025-12-24 18:54:39 +01:00
Juergen Hoeller 08104100d3 Introduce generalized MethodFailureEvent for use in EventPublicationInterceptor
Closes gh-36072
2025-12-24 18:50:04 +01:00
Brian Clozel 214cb2c1e1 Polishing contribution
Closes gh-36056
2025-12-23 15:33:20 +01:00
Terry Tao 26e0f550b0 Fix LinkedCaseInsensitiveMap entrySet case-insensitivity
See gh-36056

Signed-off-by: Terry Tao <yueyang.tao@gmail.com>
2025-12-23 15:33:14 +01:00
Brian Clozel 012fb29097 Fix nullness mismatch for Converter/ConverterFactory
In gh-35947, the `Converter` contract was refined to allow for nullable
return values. This created a mismatch with the `ConverterFactory`
contract.

This commit fixes this mismatch by allowing nullable return values in
`Converter` instances created by `ConverterFactory`.

Fixes gh-36063
2025-12-23 11:37:44 +01:00
Juergen Hoeller 92a43c007a Introduce MethodRetryEvent for @Retryable execution
Closes gh-35382
2025-12-23 09:28:23 +01:00
Brian Clozel fc1ff88ede Avoid duplicate flushes in String Http converter
Prior to this commit, the `StringHttpMessageConverter` would perform a
flush after writing the body, via `StreamUtils#copy`. This operation is
not needed as a flush is already performed by the abstract class.

Flush calls by HTTP message converters is being reconsidered altogether
in gh-35427, but we should first remove this extra operation.

Closes gh-36065
2025-12-22 15:34:41 +01:00
Juergen Hoeller fc2e1dd2e5 Avoid redirection for JUnit javadoc link 2025-12-21 22:07:14 +01:00
Juergen Hoeller ffd2e03a9f Upgrade to Log4J 2.25.3, Protobuf 4.33.2, Selenium 4.39 2025-12-21 22:01:11 +01:00
Juergen Hoeller fd6e15bb4d Align execute(TaskCallback) declaration with return value nullability
See gh-36057
2025-12-21 21:44:10 +01:00
Juergen Hoeller 85c6fb0fd0 Add invoke(Runnable) as variant of invoke(Supplier)
See gh-36052
2025-12-21 21:40:50 +01:00
Juergen Hoeller 0b2bb7e751 Declare TaskCallback return value as potentially nullable
Closes gh-36057
2025-12-20 15:21:14 +01:00
Juergen Hoeller faa17abdae Introduce invoke(Supplier) with last original RuntimeException propagated
Closes gh-36052
2025-12-20 15:20:58 +01:00
Brian Clozel 89ca8e6976 Document how to discard response body with WebTestClient
This commit better documents the ways to discard the response body with
`WebTestClient`.

Closes gh-35953
2025-12-19 16:48:57 +01:00
Brian Clozel 7353ab41d2 Fix memory leak in WiretapConnector
Prior to this commit, we found in gh-35953 that using the `WebTestClient`
the following way leaks data buffers:

```
var body = client.get().uri("download")
  .exchange()
  .expectStatus().isOk()
  .returnResult()
  .getResponseBodyContent();
```

Here, the test performs expectations on the response status and headers,
but not on the response body. The WiretapConnector already supports this
case by subscribing to the Flux response body in those cases and
accumulating the entire content as a single byte[].

Here, the `DataBuffer` instances are not decoded by any `Decoder` and
are not released. This results in a memory leak.

This commit ensures that the automatic subscription in
`WiretapConnector` also releases the buffers automatically as the DSL
does not allow at that point to go back to performing body expectations.

Fixes gh-36050
2025-12-19 16:10:33 +01:00
Stéphane Nicoll f1db0ef036 Do not display error location when unknown
Closes gh-36041
2025-12-17 14:33:14 +01:00
Stéphane Nicoll e9a4b93477 Expose compiler warnings in CompilationException
This commit improves TestCompiler to expose both errors and warnings
instead of an opaque message. When compilation fails, both errors and
warnings are displayed.

This is particularly useful when combined with the `-Werror` option
that turns the presence of a warning into an error.

Closes gh-36037
2025-12-17 14:33:14 +01:00
Juergen Hoeller 86e89d53a9 Do not attempt nested PropertyHandler resolution for argument conversion
Includes fix for return type declaration in PropertyAccessor subclasses.

See gh-36024
2025-12-17 14:30:44 +01:00
Juergen Hoeller c813577908 Consistently use DefaultParameterNameDiscoverer.getSharedInstance()
This includes MethodParameter resolving getParameterName() by default now.
initParameterNameDiscovery(null) can be used to suppress such resolution.

Closes gh-36024
2025-12-17 13:39:02 +01:00
Brian Clozel ec6b7730a2 Polishing contribution
Closes gh-36032
2025-12-17 12:35:30 +01:00
jher235 fc29d88778 Use StringBuilder in JdbcTemplate for batch updates
See gh-36032

Signed-off-by: jher235 <tim668666@gmail.com>
2025-12-17 12:35:30 +01:00
Brian Clozel 033df68da6 Fix Javadoc links 2025-12-17 10:21:30 +01:00
Brian Clozel df5b06dabc Fix RfcUriParser parsing for single char fragments
Prior to this commit, the `RfcUriParser` would ignore URI fragments if
their length is < 2. This commit fixes the length check to allow for
single char fragments when parsing URIs.

Fixes gh-36029
2025-12-17 09:51:53 +01:00
Juergen Hoeller 3d8d7ffa1e Upgrade to AspectJ 1.9.25, Tomcat 11.0.15, Netty 4.2.9, Jackson 3.0.3, EclipseLink 5.0.0-B13, Mockito 5.21, Checkstyle 12.3 2025-12-16 13:55:36 +01:00
Juergen Hoeller 6d5a512d90 Suppress serial warning 2025-12-16 13:54:40 +01:00
Sébastien Deleuze d4e4250bb1 Remove JavaScript/Kotlin from ScriptEngine documentation
Closes gh-27919
2025-12-15 18:43:09 +01:00
Sam Brannen 93bc022a46 Extract CopyPropertiesTests as nested test class in BeanUtilsTests 2025-12-15 18:11:35 +01:00
Sam Brannen a4c72c8fcf Polish integration tests 2025-12-15 15:05:05 +01:00
Juergen Hoeller 3d7862abb6 Upgrade to Hibernate ORM 7.2.0.Final 2025-12-15 14:27:39 +01:00
Juergen Hoeller 7fd0c5041d Use default ConstraintValidatorFactory for provider-internal validators
Closes gh-36012
2025-12-15 14:26:58 +01:00
Juergen Hoeller d99ace81c1 Enforce publicly accessible method for externalClassLoader(ClassLoader)
Closes gh-36012
2025-12-14 14:48:33 +01:00
Sam Brannen ed451c107f Polishing 2025-12-13 17:11:05 +01:00
Sam Brannen 452257eb96 Revise contribution
See gh-36022
2025-12-13 17:10:06 +01:00
Tran Ngoc Nhan a7863a0877 Fix typos and grammar in reference manual
Closes gh-36022

Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>
2025-12-13 16:50:39 +01:00
Sam Brannen 1da0e13a2b Polishing 2025-12-12 15:41:23 +01:00
Stéphane Nicoll ee6a156275 Refresh GitHub Actions 2025-12-12 11:51:11 +01:00
Brian Clozel 0b6fe8dcc5 Next development version (v7.0.3-SNAPSHOT) 2025-12-11 17:34:57 +01:00
Juergen Hoeller e2c9dc7138 Revert to previous behavior for 7.0.2 (based on Boot/Data impact)
Preparing the revised behavior for 7.1 instead.

See gh-31456
2025-12-11 15:37:41 +01:00
Sam Brannen 1818161f58 Ensure bottom-up semantics in resolveDefaultContextConfigurationAttributes()
Closes gh-31456
2025-12-11 13:37:32 +01:00
Sam Brannen 8916ee9f81 Set inheritLocations to true in ContextConfigurationAttributes constructor
Closes gh-36000
2025-12-11 13:06:50 +01:00
Brian Clozel d835fe311d Do not send null HTTP header value in JdkClientHttpRequest
Prior to this commit, the `JdkClientHttpRequest` would add all values
from `HttpHeaders` to the native request builder. This could cause
`NullPointerException` being thrown at runtime because the `HttpClient`
does not support that.

This commit replicates a fix that was applied to the
`SimpleClientHttpRequest`, turning null values into empty "".

Fixes gh-35996
2025-12-11 09:45:30 +01:00
rstoyanchev 0eefac21c9 Polishing contribution
Closes gh-35758
2025-12-10 18:17:12 +00:00
Samuel Gulliksson e99791f289 Improve i18n-support for NoResourceFoundException.
Return the requested resource as ErrorResponse.getDetailMessageArguments,
making it usable with message customization and i18n.

See gh-35758

Signed-off-by: Samuel Gulliksson <samuel.gulliksson@gmail.com>
2025-12-10 18:17:12 +00:00
Sam Brannen 658775b914 Avoid unnecessary list creation & processing in AbstractTestContextBootstrapper
Closes gh-35995
2025-12-10 18:56:57 +01:00
Sam Brannen ea7a1d789e Resolve ContextLoader only once in AbstractTestContextBootstrapper
Closes gh-35994
2025-12-10 18:56:50 +01:00
Sam Brannen 4ae471df01 Resolve all default context configuration within @⁠Nested hierarchy
Prior to this commit, if an enclosing test class (such as one annotated
with @⁠SpringBootTest or simply @⁠ExtendWith(SpringExtension.class))
was not annotated with @⁠ContextConfiguration (or @⁠Import with
@⁠SpringBootTest), the ApplicationContext loaded for a @⁠Nested test
class would not use any default context configuration for the enclosing
test class.

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

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

Closes gh-31456
2025-12-10 18:56:42 +01:00
Sam Brannen 75e3f44a7b Update Javadoc due to changes in retry support
See gh-35940
2025-12-10 16:45:43 +01:00
Juergen Hoeller adcd7cb4cb Introduce RetryListener#onRetryableExecution callback with RetryState
Closes gh-35940
2025-12-10 15:09:33 +01:00
Sam Brannen d0be180a69 Make @​Retryable and RetryTemplate timeout tests more robust
See gh-35963
2025-12-10 14:22:42 +01:00
Sam Brannen 3731fed4ca Revise MultiValueMapCollector implementation and tests
See https://github.com/spring-projects/spring-data-commons/issues/3420
Closes gh-35958
2025-12-10 13:35:00 +01:00
u214578 bfc02cda0c Introduce MultiValueMapCollector for use with streams
See https://github.com/spring-projects/spring-data-commons/issues/3420
See gh-35958

Signed-off-by: Florian Hof <florian.hof@sbb.ch>
2025-12-10 13:28:59 +01:00
Sam Brannen f734d04a0d Fix grammar 2025-12-10 13:28:59 +01:00
Sam Brannen ce4c9ebe3c Polishing 2025-12-10 12:31:26 +01:00
Brian Clozel 7c4801304d Upgrade to Reactor 2025.0.1
Closes gh-35985
2025-12-09 22:19:53 +01:00
Brian Clozel 2731183420 Upgrade to Micrometer 1.16.1
Closes gh-35984
2025-12-09 22:18:03 +01:00
Sam Brannen 38cf4ab3fc Make RetryTemplate timeout tests more robust
See gh-35963
2025-12-09 17:03:41 +01:00
Sam Brannen 2137ec70d2 Make @​Retryable timeout tests more robust
See gh-35963
2025-12-09 16:56:53 +01:00
Sam Brannen 61201db704 Improve error message for preemptive timeout in RetryTemplate
The error message in such cases now indicates that the retry process
is being aborted preemptively due to pending sleep time.

For example:

  Retry policy for operation 'myMethod' would exceed timeout (5 ms) due
  to pending sleep time (10 ms); preemptively aborting execution

See gh-35963
2025-12-09 16:46:44 +01:00
Sam Brannen 2643c6212f Delete obsolete test code 2025-12-09 16:39:20 +01:00
Sam Brannen b46328a000 Fix broken assertions in @​Retryable timeout tests
See gh-35963
2025-12-09 16:38:05 +01:00
Sam Brannen cc4c693db7 Log RetryException for @Retryable methods
To improve diagnostics, this commit logs a DEBUG message including the
RetryException thrown by RetryTemplate when it's used behind the scenes
for @Retryable method invocations.

Closes gh-35983
2025-12-09 16:24:00 +01:00
Sam Brannen a206ea8b12 Make @​Retryable timeout tests more robust
See gh-35963
2025-12-09 16:10:43 +01:00
Sam Brannen 9f1d9fe82c Support timeouts in @​Retryable and RetryPolicy
Specifically, this commit introduces:

- timeout and timeoutString attributes in @​Retryable

- a default getTimeout() method in RetryPolicy

- a timeout() method in RetryPolicy.Builder

- an onRetryPolicyTimeout() callback in RetryListener

- support for checking exceeded timeouts in RetryTemplate (also used
  for imperative method invocations with @​Retryable)

- support for checking exceeded timeouts in reactive pipelines with
  @​Retryable

Closes gh-35963
2025-12-09 14:06:35 +01:00
Juergen Hoeller ab33000750 Do not keep target connection after failed settings
Includes aligned setReadOnly exception suppression.

Closes gh-35980
2025-12-09 12:59:27 +01:00
Juergen Hoeller e2ab9cd5da Use composed cache key for different SmartFactoryBean object types
Closes gh-35974
2025-12-09 12:59:19 +01:00
Sam Brannen 103db5429a Use AtomicIntegerAssert 2025-12-09 12:43:18 +01:00
Sam Brannen 68f8139206 Polishing 2025-12-08 17:11:38 +01:00
Sam Brannen a9da900b5b Fix test logic 2025-12-08 17:10:27 +01:00
Brian Clozel e7e4b3559a Polishing contribution
Closes gh-35978
2025-12-08 16:10:20 +01:00
Johnny Lim feb77f924a Fix SubscriberInputStream.resume()
See gh-35978

Signed-off-by: Johnny Lim <izeye@naver.com>
2025-12-08 16:10:05 +01:00
Brian Clozel 12c3dc0cbe Fix compressed HEAD requests handling in JDK client
Prior to this commit, the `JdkClientHttpRequestFactory` would support
decompressing gziped/deflate encoded response bodies but would fail if
the response has no body but has a "Content-Encoding" response header.
This happens as a response to HEAD requests.

This commit ensures that only responses with actual message bodies are
decompressed.

Fixes gh-35966
2025-12-08 15:23:55 +01:00
Juergen Hoeller df27627516 Accept assignable match for covariant return type
See gh-35936
2025-12-08 12:41:43 +01:00
Sam Brannen 939aa84214 Revise type-level nullability in ConvertingComparator
This commit revises the type-level nullability declaration in
ConvertingComparator in order to adapt to recent nullability changes in
Converter.

This commit also revises the workaround in commit 53d9ba879d.

See gh-35947
2025-12-08 11:22:14 +01:00
Brian Clozel 9f77f401ad Complete Propagator.Getter implementation
As of Micrometer Tracing 1.6.0, the `Propagator.Getter` interface
adds a new `getAll` method with a default implementation return a
singleton collection.

This commit adds the missing implementation override in both Servlet and
Reactor web server contexts.

Fixes gh-35965
2025-12-08 11:07:30 +01:00
Sébastien Deleuze f19f1a667c Add support for package-private BeanRegistrar
Closes gh-35803
2025-12-05 17:04:51 +01:00
Sébastien Deleuze 69207c6c16 Introduce BeanFactoryInitializationCode#getClassName
See gh-35803
2025-12-05 17:04:51 +01:00
Sébastien Deleuze f61832ecb8 Fix indentation 2025-12-05 17:04:51 +01:00
Juergen Hoeller 14790493f4 Upgrade to Log4J 2.25.2, Groovy 5.0.3, Jetty 12.1.5, Jetty Reactive HttpClient 4.1.4, Checkstyle 12.2 2025-12-05 17:03:57 +01:00
Juergen Hoeller 53d9ba879d Suppress NullAway warning for nullable Annotation return value 2025-12-05 16:53:24 +01:00
Juergen Hoeller 9d2d9ae0ff Reinstate stopOrder test
See gh-35964
2025-12-05 16:30:00 +01:00
Juergen Hoeller c74af40288 Stop already started Lifecycle beans on cancelled refresh
Closes gh-35964
2025-12-05 16:14:27 +01:00
Juergen Hoeller 196c1dd51c Handle absolute file URLs in getClassPathManifestEntriesFromJar
Closes gh-35682
2025-12-05 16:13:30 +01:00
Sam Brannen 44689b9ec0 Simplify RestTestClientIntegrationTests and remove duplicate field 2025-12-05 16:00:42 +01:00
Sam Brannen b916dc962e Document that annotations which reference types not present are ignored
Add warnings to the class-level Javadoc for MergedAnnotations,
AnnotatedTypeMetadata, AnnotationMetadata, and MethodMetadata to point
out that annotations may be ignored if their attributes reference types
that are not present in the classpath.

Closes gh-35959
2025-12-05 15:50:02 +01:00
Tran Ngoc Nhan 87d0113b25 Correct Reactive Libraries reference link
Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>
2025-12-05 14:09:02 +00:00
rstoyanchev cee185ce3b Append query on redirect in UrlHandlerFilter
Closes gh-35873
2025-12-05 12:35:32 +00:00
rstoyanchev e45727d4fa Polishing in InMemoryWebSessionStore
Closes gh-35866
2025-12-05 12:29:11 +00:00
Mengqi Xu bc088ebdef Allow configuring default maxIdleTime on InMemoryWebSessionStore.
See gh-35866

Signed-off-by: Mengqi Xu <2663479778@qq.com>
2025-12-05 12:28:37 +00:00
Sam Brannen f1cf91c9f9 Polishing 2025-12-05 12:22:24 +01:00
Sébastien Deleuze d36475cb4c Use flexible generic type level nullability in Converter
Allow defining converters that return non-null values by
leveraging flexible generic type level nullability in
org.springframework.core.convert.converter.Converter.

Closes gh-35947
2025-12-05 07:28:52 +01:00
Sébastien Deleuze 8b6d7e0da7 Refine KotlinDetector#hasSerializableAnnotation
Refine KotlinDetector#hasSerializableAnnotation in order to detect
deeply nested generic types.

Closes gh-35960
2025-12-04 15:54:03 +01:00
Sam Brannen 0a86a7e3a8 Use == instead of instanceof for primitive array type checks
Closes gh-35962
2025-12-04 15:06:08 +01:00
rstoyanchev 8edde374bb Lower bad requests to DEBUG level in AbstractHandshakeHandler
Closes gh-35930
2025-12-04 09:07:55 +00:00
rstoyanchev 35b58cd0ee Avoid http - web.utils package cycle
Closes gh-35952
2025-12-04 09:07:55 +00:00
Juergen Hoeller dfc900c7c4 Add @Override to existing resetCaches() methods
See gh-35845
See gh-35840
2025-12-04 07:09:21 +01:00
Juergen Hoeller 07c0213f20 Polishing 2025-12-04 00:12:31 +01:00
Juergen Hoeller 667851c0fa Avoid computeIfAbsent for createMappings which calls back into same map
Closes gh-35944
2025-12-04 00:12:22 +01:00
Juergen Hoeller 96aadc2b12 Add resetCaches() method to general CacheManager interface
Closes gh-35845
See gh-35840
2025-12-04 00:12:13 +01:00
Sam Brannen 2c6ccaea05 Use simple type name for meta-annotation in log message
In contrast to the previous commit, a warning similar to the following
is now logged in such cases.

WARN o.s.c.a.MergedAnnotation -
  Failed to introspect meta-annotation @MyAnnotation on class
  example.Config: java.lang.TypeNotPresentException:
  Type example.OptionalDependency not present

See gh-35927
2025-12-03 17:15:53 +01:00
Sam Brannen 62d09be2ae Log warning if meta-annotation is ignored due to types not present
Prior to this commit, if a meta-annotation could not be loaded because
its attributes referenced types not present in the classpath, the
meta-annotation was silently ignored.

To improve diagnostics for such use cases, this commit introduces WARN
support in IntrospectionFailureLogger and revises AttributeMethods.canLoad()
to log a warning if a meta-annotation is ignored due to an exception
thrown while attempting to load its attributes.

For example, a warning similar to the following is now logged in such
cases.

WARN o.s.c.a.MergedAnnotation -
  Failed to introspect meta-annotation @example.MyAnnotation on class
  example.Config: java.lang.TypeNotPresentException:
  Type example.OptionalDependency not present

This commit also improves log messages in AnnotationTypeMappings.

Closes gh-35927
2025-12-03 17:00:24 +01:00
Stéphane Nicoll 47f65b3dff Use correct product name for IntelliJ IDEA 2025-12-02 16:54:56 +01:00
Sam Brannen c7e24a5c26 Update antora-extensions to 1.14.7
Closes gh-35949
2025-12-02 15:33:56 +01:00
Sam Brannen a493bd959d Fix nullability in TestContextAnnotationUtils.findAnnotationDescriptor() 2025-12-02 15:32:32 +01:00
Juergen Hoeller 92e9543ad4 Select method with resolved return type match (among multiple candidates)
Removes unnecessary array type check for parameters of candidate methods.

Closes gh-35936
2025-12-01 23:25:59 +01:00
ivonaest 68231aa08c Upgrade json-path to 2.10.0
Closes gh-35924
Signed-off-by: ivonaest <ivona.cvija@est.tech>
2025-12-01 15:27:45 +01:00
Juergen Hoeller 71d18ebabc Improve debug log for received message 2025-12-01 15:03:08 +01:00
Juergen Hoeller 3ccb0786db Clear remaining invoker resources when releasing shared Connection
Closes gh-35932
2025-12-01 15:03:00 +01:00
rstoyanchev 15c16d2c3a FragmentsRendering exposes its fragments
Closes gh-35775
2025-12-01 11:17:40 +00:00
John Niang 5dcb85db8d BindingContext constructor uses given ReactiveAdapterRegistry arg
Closes gh-35771

Signed-off-by: John Niang <johnniang@foxmail.com>
2025-12-01 11:07:11 +00:00
rstoyanchev 4ae03ecd40 Use channelId for ReactorNettyWebSocketSession's id
Closes gh-35883
2025-12-01 11:01:04 +00:00
jnizet 1603045c7d Avoid passing null as argument marked as non-nullable
Closes gh-35933
Signed-off-by: jnizet <jb@ninja-squad.com>
2025-12-01 10:29:20 +01:00
Juergen Hoeller ace2b2bac9 Upgrade to Hibernate ORM 7.2.0.CR3 2025-11-30 11:32:33 +01:00
Juergen Hoeller f082c435e1 Polishing 2025-11-30 11:31:10 +01:00
Juergen Hoeller c6810334ca Move extended web data binders to common support packages
This resolves cyclic dependencies between function and annotation-based packages.

See gh-35800
2025-11-30 11:31:04 +01:00
Juergen Hoeller 3b90311c17 Revise ApplicationContext#getId() nullability to non-null
Closes gh-35925
2025-11-28 13:58:11 +01:00
Sam Brannen 946fc65712 Stop linking to 6.1.22-SNAPSHOT reference docs
With this commit, we now include snapshots for main (which currently
correlates to 7.0.x), 6.2.x, and 7.0.x to 9.*.x.

Closes gh-35923

(cherry picked from commit 305a512a55)
2025-11-28 13:00:22 +01:00
Juergen Hoeller fd25e2f468 Use concurrent set for reactive transaction synchronizations
Closes gh-35921
2025-11-28 10:24:21 +01:00
Sébastien Deleuze 46ceedf992 Refine AbstractKotlinSerializationHttpMessageConverter#canWrite
AbstractKotlinSerializationHttpMessageConverter#writeInternal is able to
resolve the ResolvableType from the Object parameter when the provided
one via the ResolvableType parameter is not resolvable, but
AbstractKotlinSerializationHttpMessageConverter#canWrite lacks of
such capability.

This commit refines
AbstractKotlinSerializationHttpMessageConverter#canWrite to resolve the
ResolvableType from the Class<?> parameter when the provided one via the
ResolvableType parameter is not resolvable.

Closes gh-35920
2025-11-27 16:52:45 +01:00
Brian Clozel a4b3111928 Make HttpEntity headers mutables
Since its inception, instantiating an `HttpEntity` makes its
`HttpHeaders` read-only. While immutability is an interesting design
principle, here we shouldn't enforce this.

For example, developers can expect to instantiate a `ResponseEntity`
and still mutate its headers.

Closes gh-35888
2025-11-27 16:37:47 +01:00
Juergen Hoeller 4c4161c8c0 Cache resolved singleton beans in injected Provider instance
Includes alignment for direct Optional injection points, consistently registering an autowiredBeanNames entry for an Optional as well as a non-Optional injection result.

Closes gh-35373
Closes gh-35919
2025-11-27 16:01:57 +01:00
Juergen Hoeller 61d5413c23 Tighten cacheable decision behind @Lazy injection point
Closes gh-35917
2025-11-27 16:00:43 +01:00
Brian Clozel 08e6d762d2 Always buffer response body in RestTestClient
Prior to this commit, `RestTestClient` tests could only perform
expectations on the response without consuming the body. In this case,
the client could leak HTTP connections with the underlying HTTP library
because the response was not entirely read.

This commit ensures that the response is always fully drained before
performing expectations. The client is configured to buffer the response
content, so further body expectations are always possible.

Fixes gh-35784
2025-11-27 15:50:00 +01:00
Sébastien Deleuze 7a19cbb452 Change AbstractSmartHttpMessageConverter canRead/canWrite overrides
Extending AbstractSmartHttpMessageConverter typically requires to
override both Class and ResolvableType variants of canRead. This was not
intended as SmartHttpMessageConverter interface has default methods
doing the conversion from Class parameters to ResolvableType ones, but
AbstractHttpMessageConverter overrides it.

This commit changes AbstractSmartHttpMessageConverter canRead/canWrite
overrides from ResolvableType to Class ones that delegate to the
ResolvableType variants. It also refines
AbstractJacksonHttpMessageConverter accordingly.

Closes gh-35916
2025-11-27 15:38:35 +01:00
Juergen Hoeller 2f1e1d483a Remove early adaptation to JTA 2.1 setReadOnly method
The UserTransaction read-only semantics are still in discussion. If they turn out to be stricter than Spring's read-only hint, we should only apply them when configured with an explicit enforceReadOnly=true flag at the Spring JtaTransactionManager level (similar to the same-named flag in DataSourceTransactionManager).

See gh-35915
See gh-35633
2025-11-27 13:16:45 +01:00
Sam Brannen 24d152cdab Convert sentence to tip in Kotlin testing chapter
This commit also moves the text to a more appropriate section of the
chapter.
2025-11-27 12:37:06 +01:00
Sam Brannen cc530d4df2 Support RuntimeBeanReference(name, type) in AOT-generated code
The RuntimeBeanReference(name, type) constructor was introduced in 7.0;
however, BeanDefinitionPropertyValueCodeGeneratorDelegates in our AOT
infrastructure was not updated to support the new constructor.

This commit revises BeanDefinitionPropertyValueCodeGeneratorDelegates
to ensure that AOT generated code for a RuntimeBeanReference uses the
RuntimeBeanReference(name, type) constructor, thereby retaining the
originally configured bean name.

See gh-35101
Closes gh-35913
2025-11-27 11:37:46 +01:00
Juergen Hoeller 6c3132cb8c Narrow method annotation check in hasQualifier to setter methods
Closes gh-35908
2025-11-26 23:01:55 +01:00
Juergen Hoeller a15274d431 Revise volatile access to singletonInstance field
For defensiveness against a singletonInstance/initialized visibility mismatch, we accept the locking overhead for pre-initialized null values (where we need the initialized field) in favor of a defensive fast path for non-null values (where we only need the singletonInstance field).

Closes gh-35905
2025-11-26 17:20:11 +01:00
Tran Ngoc Nhan 45d4fd3b7e Fix broken Javadoc links to methods
Closes gh-35899
Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>
2025-11-26 17:05:16 +01:00
Sam Brannen 3fd5d69939 Remove JUnit 4 based meta-annotation example
This commit also converts the examples from XML configuration files to
@⁠Configuration classes.

Closes gh-35903
2025-11-26 16:37:04 +01:00
Sébastien Deleuze 8647c44364 Add dynamic ObjectToObjectConverter hints
This commit refines BindingReflectionHintsRegistrar with additional
dynamic hints for application-defined types, main core Java conversion
ones being already covered by ObjectToObjectConverterRuntimeHints.

Closes gh-35847
2025-11-26 16:17:41 +01:00
Sam Brannen 6504177e7b Refer to "Spring Tools" instead of "Spring Tools for Eclipse"
Closes gh-35901
2025-11-26 16:11:41 +01:00
Sam Brannen c1125699bb Document that the SpringExtension requires JUnit Jupiter 6.0 or higher
Closes gh-35900
2025-11-26 15:47:21 +01:00
Juergen Hoeller f58d0f6aae Consistent namespace element declarations 2025-11-26 15:09:05 +01:00
Juergen Hoeller c1b6bfb681 Expose non-existent resources at the end of the sorted result
Closes gh-35895
2025-11-26 15:08:24 +01:00
Juergen Hoeller 9d4abb63d8 Clarify JMS sessionTransacted flag for local versus global transaction
Closes gh-35897
2025-11-26 15:06:22 +01:00
rstoyanchev a9a404266c Data binding from pathvars and headers in fn handlers
Closes gh-35800
2025-11-26 12:30:48 +00:00
rstoyanchev b29f4ed37e Minor refactoring in DefaultServerRequest
Ensure bind logic is shared across DefaultServerRequest and
BuiltServerRequest.

See gh-35800
2025-11-26 12:30:48 +00:00
rstoyanchev 4847ee80b0 Add required type to TypeMismatchException message args
Closes gh-35837
2025-11-26 12:30:48 +00:00
Sam Brannen f4ee120a42 Use current links to JUnit documentation
Closes gh-35892
2025-11-26 13:22:26 +01:00
Sam Brannen e625a28f6d Fix formatting for backticks in Kotlin docs 2025-11-26 13:15:56 +01:00
Sam Brannen 45c1cd9295 Link to Spring Framework Artifacts wiki page
This commit revises the Integration Testing chapter to reference the
"Spring Framework Artifacts" wiki page instead of the nonexistent
"Dependency Management" section of the reference manual.

Closes gh-35890
2025-11-26 12:55:01 +01:00
Sébastien Deleuze 2641b5d783 Polishing
See gh-35861
2025-11-26 10:27:01 +01:00
Sébastien Deleuze dc0c463137 Keep using ZoneId.of("GMT") in HttpHeaders
Otherwise some header values are changed from "GMT" to "Z" and
some tests are broken. We don't want to change the current
runtime behavior, so this commit reverts the related change to
keep using ZoneId.of("GMT") in HttpHeaders.

Closes gh-35861
2025-11-26 10:27:00 +01:00
Vincent Potucek 83bbf16e6c Modernize java.time API usages
See gh-35861
Signed-off-by: Vincent Potucek <vpotucek@me.com>
2025-11-26 10:24:34 +01:00
Juergen Hoeller 3686b89ab5 Revise proxyTargetClass handling in ResilientMethodsConfiguration and ProxyAsyncConfiguration
An annotation-specified proxyTargetClass attribute must only be applied when true, otherwise we need to participate in global defaulting.

Closes gh-35863
2025-11-25 18:03:04 +01:00
Sébastien Deleuze 85ca9f46dd Support reading unresolvable types in AbstractJacksonHttpMessageConverter
Closes gh-35889
2025-11-25 16:00:04 +01:00
Sébastien Deleuze e2287020c3 Polishing
See gh-35889
2025-11-25 16:00:04 +01:00
Sébastien Deleuze 9e18c75ff1 Fix ApiVersionConfigurer Kotlin code sample
Closes gh-35887
2025-11-25 14:51:31 +01:00
Sébastien Deleuze fb9f31d101 Update reference documentation to use Jackson 3
Closes gh-35886
2025-11-25 13:31:39 +01:00
Sébastien Deleuze 4ee0a8ee33 Refine Kotlin Serialization codec type checks
ServerSentEvent and String checks, removed from
KotlinSerializationSupport in Spring Framework 7.0, are reintroduced by
this commit at the right level (KotlinSerializationSupport for
ServerSentEvent and KotlinSerializationString(Decoder|Encoder) for
String).

Closes gh-35885
2025-11-25 13:31:39 +01:00
Sébastien Deleuze f3908832c5 Add WebFlux SSE support with GSON
Closes gh-35884
2025-11-25 13:31:39 +01:00
Sébastien Deleuze c10266e57e Fix handling of ServerSentEvent with Jackson encoder
This commit fixes ServerSentEvent handling with Jackson encoder
when no Accept header is specified.

It also moves the String checks to the JSON codec level, as they do not
make sense for binary formats.

Closes gh-35872
2025-11-25 13:31:38 +01:00
Sébastien Deleuze 1b6b163f24 Polishing 2025-11-25 12:37:45 +01:00
github-actions[bot] 8642a39a03 Update Antora Spring UI to v0.4.25
Closes gh-35876

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-11-24 14:44:34 +01:00
github-actions[bot] f62cc98a97 Update Antora Spring UI to v0.4.22
Closes gh-35860
2025-11-24 10:12:11 +01:00
Tran Ngoc Nhan c599775a9e Refine nullability of Assert#noNullElements
Closes gh-35868
Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>
2025-11-24 09:42:44 +01:00
Brian Clozel 4bc97ad532 Next development version (v7.0.2-SNAPSHOT) 2025-11-20 11:53:27 +01:00
2150 changed files with 41405 additions and 20202 deletions
@@ -1,6 +1,13 @@
changelog:
repository: spring-projects/spring-framework
sections:
- title: ":warning: Attention Required"
labels:
- "for: upgrade-attention"
summary:
mode: "member-comment"
config:
prefix: "Attention Required:"
- title: ":star: New Features"
labels:
- "type: enhancement"
@@ -24,7 +24,7 @@ runs:
using: composite
steps:
- name: Set Up Java
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
distribution: ${{ inputs.java-early-access == 'true' && 'temurin' || (inputs.java-distribution || 'liberica') }}
java-version: |
@@ -32,7 +32,7 @@ runs:
${{ inputs.java-toolchain == 'true' && '17' || '' }}
25
- name: Set Up Gradle
uses: gradle/actions/setup-gradle@ac638b010cf58a27ee6c972d7336334ccaf61c96 # v4.4.1
uses: gradle/actions/setup-gradle@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # v5.0.0
with:
cache-read-only: false
develocity-access-key: ${{ inputs.develocity-access-key }}
@@ -17,14 +17,14 @@ runs:
using: composite
steps:
- name: Set Up JFrog CLI
uses: jfrog/setup-jfrog-cli@f748a0599171a192a2668afee8d0497f7c1069df # v4.5.6
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@0cdd90d12e6876341e82860d951e1bcddc1e51b6 # v0.2.0
uses: spring-io/central-publish-action@0c03960e9b16fdfe70e2443e1d5393cbc3a35622 # v0.3.0
with:
token: ${{ inputs.central-token-password }}
token-name: ${{ inputs.central-token-username }}
+2 -2
View File
@@ -18,9 +18,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Set up Java
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
distribution: 'liberica'
java-version: 17
@@ -2,7 +2,7 @@ name: Build and Deploy Snapshot
on:
push:
branches:
- main
- 7.0.x
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
@@ -13,7 +13,7 @@ jobs:
timeout-minutes: 60
steps:
- name: Check Out Code
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Build and Publish
id: build-and-publish
uses: ./.github/actions/build
@@ -21,7 +21,7 @@ jobs:
develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
publish: true
- name: Deploy
uses: spring-io/artifactory-deploy-action@dc1913008c0599f0c4b1fdafb6ff3c502b3565ea # v0.0.2
uses: spring-io/artifactory-deploy-action@926d7f7cc810569395346bf3a4d91b380b3e355b # v0.0.4
with:
artifact-properties: |
/**/framework-api-*.zip::zip.name=spring-framework,zip.deployed=false
+2 -2
View File
@@ -10,7 +10,7 @@ jobs:
timeout-minutes: 60
steps:
- name: Check Out Code
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Build
id: build
uses: ./.github/actions/build
@@ -19,7 +19,7 @@ jobs:
uses: ./.github/actions/print-jvm-thread-dumps
- name: Upload Build Reports
if: failure()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: build-reports
path: '**/build/reports/'
+1 -1
View File
@@ -35,7 +35,7 @@ jobs:
git config --global core.longPaths true
Stop-Service -name Docker
- name: Check Out Code
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Build
id: build
uses: ./.github/actions/build
+1 -1
View File
@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 1
ref: docs-build
+5 -5
View File
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check Out Code
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Build and Publish
id: build-and-publish
uses: ./.github/actions/build
@@ -21,7 +21,7 @@ jobs:
develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
publish: true
- name: Stage Release
uses: spring-io/artifactory-deploy-action@26bbe925a75f4f863e1e529e85be2d0093cac116 # v0.0.1
uses: spring-io/artifactory-deploy-action@926d7f7cc810569395346bf3a4d91b380b3e355b # v0.0.4
with:
artifact-properties: |
/**/framework-api-*.zip::zip.name=spring-framework,zip.deployed=false
@@ -57,7 +57,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check Out Code
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@v6
- name: Sync to Maven Central
uses: ./.github/actions/sync-to-maven-central
with:
@@ -73,7 +73,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Set up JFrog CLI
uses: jfrog/setup-jfrog-cli@dff217c085c17666e8849ebdbf29c8fe5e3995e6 # v4.5.2
uses: jfrog/setup-jfrog-cli@5b06f730cc5a6f55d78b30753f8583454b08c0aa # v4.8.1
env:
JF_ENV_SPRING: ${{ secrets.JF_ARTIFACTORY_SPRING }}
- name: Promote build
@@ -86,7 +86,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check Out Code
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@v6
- name: Create GitHub Release
uses: ./.github/actions/create-github-release
with:
+5 -5
View File
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check Out Code
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Build and Publish
id: build-and-publish
uses: ./.github/actions/build
@@ -20,7 +20,7 @@ jobs:
develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
publish: true
- name: Stage Release
uses: spring-io/artifactory-deploy-action@dc1913008c0599f0c4b1fdafb6ff3c502b3565ea # v0.0.2
uses: spring-io/artifactory-deploy-action@926d7f7cc810569395346bf3a4d91b380b3e355b # v0.0.4
with:
artifact-properties: |
/**/framework-api-*.zip::zip.name=spring-framework,zip.deployed=false
@@ -56,7 +56,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check Out Code
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@v6
- name: Sync to Maven Central
uses: ./.github/actions/sync-to-maven-central
with:
@@ -72,7 +72,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Set up JFrog CLI
uses: jfrog/setup-jfrog-cli@dff217c085c17666e8849ebdbf29c8fe5e3995e6 # v4.5.2
uses: jfrog/setup-jfrog-cli@5b06f730cc5a6f55d78b30753f8583454b08c0aa # v4.8.1
env:
JF_ENV_SPRING: ${{ secrets.JF_ARTIFACTORY_SPRING }}
- name: Promote build
@@ -85,7 +85,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check Out Code
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@v6
- name: Create GitHub Release
uses: ./.github/actions/create-github-release
with:
@@ -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
+5 -5
View File
@@ -30,23 +30,23 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check Out Release Verification Tests
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
ref: 'v0.0.2'
repository: spring-projects/spring-framework-release-verification
token: ${{ secrets.token }}
- name: Check Out Send Notification Action
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
path: send-notification
sparse-checkout: .github/actions/send-notification
- name: Set Up Java
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
distribution: 'liberica'
java-version: 17
- name: Set Up Gradle
uses: gradle/actions/setup-gradle@ac638b010cf58a27ee6c972d7336334ccaf61c96 # v4.4.1
uses: gradle/actions/setup-gradle@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # v5.0.0
with:
cache-read-only: false
- name: Configure Gradle Properties
@@ -64,7 +64,7 @@ jobs:
run: ./gradlew spring-framework-release-verification-tests:test
- name: Upload Build Reports on Failure
if: failure()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: build-reports
path: '**/build/reports/'
+2 -1
View File
@@ -9,6 +9,7 @@ ivy-cache
argfile*
activemq-data/
classes/
.cursor/
# Log files
jxl.log
@@ -38,7 +39,7 @@ bin
.springBeans
spring-*/src/main/java/META-INF/MANIFEST.MF
# IDEA artifacts and output dirs
# IntelliJ IDEA artifacts and output dirs
*.iml
*.ipr
*.iws
+1 -1
View File
@@ -120,7 +120,7 @@ source code into your IDE.
The wiki pages
[Code Style](https://github.com/spring-projects/spring-framework/wiki/Code-Style) and
[IntelliJ IDEA Editor Settings](https://github.com/spring-projects/spring-framework/wiki/IntelliJ-IDEA-Editor-Settings)
define the source file coding standards we use along with some IDEA editor settings we customize.
define the source file coding standards we use along with some IntelliJ editor settings we customize.
### Reference Docs
+16 -13
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.8' apply false
id 'io.spring.nullability' version '0.0.11' apply false
}
ext {
@@ -61,24 +61,27 @@ 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")
}
ext.javadocLinks = [
"https://docs.oracle.com/en/java/javase/17/docs/api/",
//"https://jakarta.ee/specifications/platform/11/apidocs/",
"https://docs.hibernate.org/orm/5.6/javadocs/",
"https://www.quartz-scheduler.org/api/2.3.0/",
"https://hc.apache.org/httpcomponents-client-5.5.x/current/httpclient5/apidocs/",
"https://projectreactor.io/docs/test/release/api/",
"https://junit.org/junit4/javadoc/4.13.2/",
"https://docs.junit.org/6.0.1/api/",
"https://www.reactive-streams.org/reactive-streams-1.0.3-javadoc/",
"https://r2dbc.io/spec/1.0.0.RELEASE/api/",
"https://jspecify.dev/docs/api/"
"https://docs.oracle.com/en/java/javase/17/docs/api/",
//"https://jakarta.ee/specifications/platform/11/apidocs/",
"https://docs.hibernate.org/orm/7.2/javadocs/",
"https://www.quartz-scheduler.org/api/2.3.0/",
"https://hc.apache.org/httpcomponents-client-5.6.x/5.6/httpclient5/apidocs/",
"https://projectreactor.io/docs/core/release/api/",
"https://projectreactor.io/docs/test/release/api/",
"https://junit.org/junit4/javadoc/4.13.2/",
"https://docs.junit.org/6.0.3/api/",
"https://www.reactive-streams.org/reactive-streams-1.0.4-javadoc/",
"https://r2dbc.io/spec/1.0.0.RELEASE/api/",
"https://jspecify.dev/docs/api/"
] as String[]
}
@@ -50,7 +50,7 @@ public class CheckstyleConventions {
project.getPlugins().apply(CheckstylePlugin.class);
project.getTasks().withType(Checkstyle.class).forEach(checkstyle -> checkstyle.getMaxHeapSize().set("1g"));
CheckstyleExtension checkstyle = project.getExtensions().getByType(CheckstyleExtension.class);
checkstyle.setToolVersion("12.1.2");
checkstyle.setToolVersion("13.4.2");
checkstyle.getConfigDirectory().set(project.getRootProject().file("src/checkstyle"));
String version = SpringJavaFormatPlugin.class.getPackage().getImplementationVersion();
DependencySet checkstyleDependencies = project.getConfigurations().getByName("checkstyle").getDependencies();
@@ -49,7 +49,6 @@ import static org.springframework.build.architecture.ArchitectureRules.classesSh
import static org.springframework.build.architecture.ArchitectureRules.javaClassesShouldNotImportKotlinAnnotations;
import static org.springframework.build.architecture.ArchitectureRules.noClassesShouldCallStringToLowerCaseWithoutLocale;
import static org.springframework.build.architecture.ArchitectureRules.noClassesShouldCallStringToUpperCaseWithoutLocale;
import static org.springframework.build.architecture.ArchitectureRules.packageInfoShouldBeNullMarked;
/**
* {@link Task} that checks for architecture problems.
@@ -64,8 +63,7 @@ public abstract class ArchitectureCheck extends DefaultTask {
public ArchitectureCheck() {
getOutputDirectory().convention(getProject().getLayout().getBuildDirectory().dir(getName()));
getProhibitObjectsRequireNonNull().convention(true);
getRules().addAll(packageInfoShouldBeNullMarked(),
classesShouldNotImportForbiddenTypes(),
getRules().addAll(classesShouldNotImportForbiddenTypes(),
javaClassesShouldNotImportKotlinAnnotations(),
allPackagesShouldBeFreeOfTangles(),
noClassesShouldCallStringToLowerCaseWithoutLocale(),
@@ -46,13 +46,6 @@ abstract class ArchitectureRules {
.because("String.toUpperCase(Locale.ROOT) should be used instead");
}
static ArchRule packageInfoShouldBeNullMarked() {
return ArchRuleDefinition.classes()
.that().haveSimpleName("package-info")
.should().beAnnotatedWith("org.jspecify.annotations.NullMarked")
.allowEmptyShould(true);
}
static ArchRule classesShouldNotImportForbiddenTypes() {
return ArchRuleDefinition.noClasses()
.should().dependOnClassesThat()
@@ -78,7 +78,7 @@ public class ShadowSource extends DefaultTask {
}
@OutputDirectory
DirectoryProperty getOutputDirectory() {
public DirectoryProperty getOutputDirectory() {
return this.outputDirectory;
}
+3 -2
View File
@@ -20,6 +20,7 @@ dependencies {
}
}
def springAspectsOutput = project(":spring-aspects").sourceSets.main.output
javadoc {
javadocTool.set(javaToolchains.javadocToolFor({
languageVersion = JavaLanguageVersion.of(25)
@@ -48,8 +49,8 @@ javadoc {
maxMemory = "1024m"
doFirst {
classpath += files(
// ensure the javadoc process can resolve types compiled from .aj sources
project(":spring-aspects").sourceSets.main.output
// ensure the javadoc process can resolve types compiled from .aj sources
springAspectsOutput
)
classpath += files(moduleProjects.collect { it.sourceSets.main.compileClasspath })
}
+5 -3
View File
@@ -13,8 +13,10 @@ content:
- url: https://github.com/spring-projects/spring-framework
# Refname matching:
# https://docs.antora.org/antora/latest/playbook/content-refname-matching/
branches: ['main', '{6..9}.+({1..9}).x']
tags: ['v{6..9}.+({0..9}).+({0..9})?(-{RC,M}*)', '!(v6.0.{0..8})', '!(v6.0.0-{RC,M}{0..9})']
# 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: 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:
@@ -36,4 +38,4 @@ runtime:
failure_level: warn
ui:
bundle:
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.20/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
+13 -7
View File
@@ -15,11 +15,15 @@ apply from: "${rootDir}/gradle/publications.gradle"
antora {
options = [clean: true, fetch: !project.gradle.startParameter.offline, stacktrace: true]
environment = [
'BUILD_REFNAME': 'HEAD',
'BUILD_VERSION': project.version,
'BUILD_REFNAME': 'HEAD',
'BUILD_VERSION': project.version,
]
}
node {
version = '24.15.0'
}
tasks.named("generateAntoraYml") {
asciidocAttributes = project.provider( {
return ["spring-version": project.version ]
@@ -49,8 +53,8 @@ tasks.withType(KotlinCompilationTask.class).configureEach {
javaSources.from = []
compilerOptions.jvmTarget = JvmTarget.JVM_17
compilerOptions.freeCompilerArgs.addAll(
"-Xjdk-release=17", // Needed due to https://youtrack.jetbrains.com/issue/KT-49746
"-Xannotation-default-target=param-property" // Upcoming default, see https://youtrack.jetbrains.com/issue/KT-73255
"-Xjdk-release=17", // Needed due to https://youtrack.jetbrains.com/issue/KT-49746
"-Xannotation-default-target=param-property" // Upcoming default, see https://youtrack.jetbrains.com/issue/KT-73255
)
}
@@ -70,7 +74,7 @@ dependencies {
implementation("com.github.ben-manes.caffeine:caffeine")
implementation("com.mchange:c3p0:0.9.5.5")
implementation("com.oracle.database.jdbc:ojdbc11")
implementation("io.micrometer:context-propagation")
implementation("io.micrometer:context-propagation")
implementation("io.projectreactor.netty:reactor-netty-http")
implementation("jakarta.jms:jakarta.jms-api")
implementation("jakarta.servlet:jakarta.servlet-api")
@@ -80,12 +84,14 @@ dependencies {
implementation("javax.cache:cache-api")
implementation("org.apache.activemq:activemq-ra:6.1.2")
implementation("org.apache.commons:commons-dbcp2:2.11.0")
implementation("org.apache.groovy:groovy-templates")
implementation("org.aspectj:aspectjweaver")
implementation("org.assertj:assertj-core")
implementation("org.eclipse.jetty.websocket:jetty-websocket-jetty-api")
implementation("org.freemarker:freemarker")
implementation("org.jetbrains.kotlin:kotlin-stdlib")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
implementation("org.junit.jupiter:junit-jupiter-api")
implementation("tools.jackson.core:jackson-databind")
implementation("tools.jackson.dataformat:jackson-dataformat-xml")
Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 158 KiB

@@ -17,6 +17,7 @@
class="st5"
id="svg5499"
version="1.1"
font-family="Helvetica, Arial, sans-serif"
inkscape:version="0.91 r13725"
sodipodi:docname="mvc-splitted-contexts.svg"
style="font-size:12px;overflow:visible;color-interpolation-filters:sRGB;fill:none;fill-rule:evenodd;stroke-linecap:square;stroke-miterlimit:3"
@@ -36,7 +37,7 @@
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
id="path9164" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
@@ -47,7 +48,7 @@
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
id="path8836" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
@@ -58,7 +59,7 @@
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
id="path8520" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
@@ -69,7 +70,7 @@
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
id="path8216" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
@@ -80,7 +81,7 @@
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
id="path7924" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
@@ -91,7 +92,7 @@
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
id="path7644" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
@@ -102,7 +103,7 @@
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
id="path7375" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
@@ -113,7 +114,7 @@
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
id="path7119" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
@@ -124,7 +125,7 @@
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
id="path6875" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
@@ -135,7 +136,7 @@
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
id="path6643" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
@@ -146,7 +147,7 @@
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
id="path6423" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
@@ -157,7 +158,7 @@
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
id="path6215" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
@@ -168,7 +169,7 @@
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
id="path6019" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
@@ -179,7 +180,7 @@
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
id="path5835" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
@@ -190,7 +191,7 @@
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
id="path5663" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
@@ -201,7 +202,7 @@
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
id="path5503" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
@@ -212,7 +213,7 @@
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
id="path5355" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
@@ -223,7 +224,7 @@
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
id="path5219" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
@@ -234,7 +235,7 @@
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
id="path5095" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
@@ -245,7 +246,7 @@
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
id="path4983" /></marker><marker
inkscape:stockid="Arrow2Mend"
orient="auto"
@@ -256,7 +257,7 @@
inkscape:isstock="true"
inkscape:collect="always"><path
id="path7394"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
transform="scale(0.6) rotate(180) translate(0,0)" /></marker><marker
inkscape:stockid="Arrow2Lend"
@@ -267,7 +268,7 @@
style="overflow:visible;"
inkscape:isstock="true"><path
id="path8123"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1;fill:#333333;fill-opacity:1"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
transform="scale(1.1) rotate(180) translate(1,0)" /></marker><marker
inkscape:stockid="Arrow1Mend"
@@ -279,7 +280,7 @@
inkscape:isstock="true"><path
id="path8035"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
style="fill-rule:evenodd;stroke:#333333;stroke-width:1pt;stroke-opacity:1;fill:#333333;fill-opacity:1"
transform="scale(0.4) rotate(180) translate(10,0)" /></marker><marker
inkscape:stockid="Arrow1Mend"
orient="auto"
@@ -290,7 +291,7 @@
inkscape:isstock="true"><path
id="path7959"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
style="fill-rule:evenodd;stroke:#333333;stroke-width:1pt;stroke-opacity:1;fill:#333333;fill-opacity:1"
transform="scale(0.4) rotate(180) translate(10,0)" /></marker><marker
inkscape:stockid="Arrow1Mend"
orient="auto"
@@ -301,7 +302,7 @@
inkscape:isstock="true"><path
id="path7376"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
style="fill-rule:evenodd;stroke:#333333;stroke-width:1pt;stroke-opacity:1;fill:#333333;fill-opacity:1"
transform="scale(0.4) rotate(180) translate(10,0)" /></marker><marker
inkscape:stockid="Arrow2Lend"
orient="auto"
@@ -311,7 +312,7 @@
style="overflow:visible;"
inkscape:isstock="true"><path
id="path7388"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#333333;stroke-opacity:1"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
transform="scale(1.1) rotate(180) translate(1,0)" /></marker><marker
inkscape:stockid="Arrow1Lend"
@@ -323,7 +324,7 @@
inkscape:isstock="true"><path
id="path7370"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
style="fill-rule:evenodd;stroke:#333333;stroke-width:1pt;stroke-opacity:1;fill:#333333;fill-opacity:1"
transform="scale(0.8) rotate(180) translate(12.5,0)" /></marker></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
@@ -352,9 +353,9 @@
v:viewMarkup="false" /><style
type="text/css"
id="style5501"><![CDATA[
.st1 {fill:#969696;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24}
.st2 {fill:#dde2cd;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24}
.st3 {fill:#000000;font-family:Arial;font-size:2.50001em;font-weight:bold}
.st1 {fill:#969696;stroke:#333333;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24}
.st2 {fill:#F8F9FA;stroke:#333333;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24}
.st3 {fill:#333333;font-family:Helvetica, Arial, sans-serif;font-size:2.50001em;font-weight:bold}
.st4 {font-size:0.333333em;font-weight:normal}
.st5 {fill:none;fill-rule:evenodd;font-size:12;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
]]></style><g
@@ -363,20 +364,22 @@
v:groupContext="foregroundPage"
id="g5503"
transform="matrix(0.99998201,0,0,1.0824094,-40.812382,-98.908648)"><rect
style="fill:#dde2cd;fill-opacity:1;stroke:#000000;stroke-width:1.53790233;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
style="fill:#F8F9FA;fill-opacity:1;stroke:#6db33f;stroke-width:2;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
id="rect6599"
width="382.68423"
height="146.09897"
x="87.884865"
y="148.26482" /><v:userDefs><v:ud
y="148.26482"
rx="8" /><v:userDefs><v:ud
v:nameU="SchemeName"
v:val="VT4(Default)" /></v:userDefs><rect
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:1.53790233;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
style="fill:none;fill-opacity:1;stroke:#6db33f;stroke-width:2;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
id="rect5725"
width="464.31128"
height="374.11411"
x="41.684383"
y="112.3262" /><title
y="112.3262"
rx="8" /><title
id="title5505">Page-1</title><v:pageProperties
v:drawingScale="0.0393701"
v:pageScale="0.0393701"
@@ -385,15 +388,16 @@
v:shadowOffsetY="-8.50394" /><v:layer
v:name="Connector"
v:index="0" /><rect
style="fill:#dde2cd;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.53790233;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
style="fill:#F8F9FA;fill-opacity:1;fill-rule:evenodd;stroke:#6db33f;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
id="rect5715"
width="322.8194"
height="43.63184"
x="119.95335"
y="-135.66222"
transform="scale(1,-1)" /><text
transform="scale(1,-1)"
rx="8" /><text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:30.7580471px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:30.7580471px;line-height:125%;font-family:Helvetica, Arial, sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#333333;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="168.843"
y="124.32391"
id="text5717"
@@ -401,12 +405,12 @@
transform="scale(1.0403984,0.96117025)"><tspan
sodipodi:role="line"
id="tspan5719"
x="168.843"
x="172.843"
y="124.32391"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:23.06853676px;font-family:sans-serif;-inkscape-font-specification:sans-serif">DispatcherServlet</tspan></text>
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:23.06853676px;font-family:Helvetica, Arial, sans-serif;-inkscape-font-specification:sans-serif;fill:#333333">DispatcherServlet</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Helvetica, Arial, sans-serif;letter-spacing:0px;word-spacing:0px;fill:#333333;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="89.770851"
y="181.20923"
id="text6589"
@@ -414,12 +418,12 @@
transform="scale(1.0403984,0.96117025)"><tspan
sodipodi:role="line"
id="tspan6591"
x="89.770851"
x="106.770851"
y="181.20923"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:23.06853676px;font-family:sans-serif;-inkscape-font-specification:sans-serif">Servlet WebApplicationContext</tspan></text>
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:23.06853676px;font-family:Helvetica, Arial, sans-serif;-inkscape-font-specification:sans-serif;fill:#333333">Servlet WebApplicationContext</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Helvetica, Arial, sans-serif;letter-spacing:0px;word-spacing:0px;fill:#333333;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="260.00443"
y="198.41273"
id="text6593"
@@ -429,36 +433,39 @@
id="tspan6595"
x="260.00443"
y="198.41273"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.53426838px;font-family:sans-serif;-inkscape-font-specification:sans-serif;text-align:center;text-anchor:middle">(containing controllers, view resolvers,</tspan><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.53426838px;font-family:Helvetica, Arial, sans-serif;-inkscape-font-specification:sans-serif;text-align:center;text-anchor:middle;fill:#555555">(containing controllers, view resolvers,</tspan><tspan
sodipodi:role="line"
x="260.00443"
y="212.83057"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.53426838px;font-family:sans-serif;-inkscape-font-specification:sans-serif;text-align:center;text-anchor:middle"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.53426838px;font-family:Helvetica, Arial, sans-serif;-inkscape-font-specification:sans-serif;text-align:center;text-anchor:middle;fill:#555555"
id="tspan6597">and other web-related beans)</tspan></text>
<rect
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.86203903;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
style="fill:#ffffff;fill-opacity:1;stroke:#6db33f;stroke-width:1.5;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
id="rect6620"
width="82.040657"
height="36.72575"
x="114.52653"
y="-259.43161"
transform="scale(1,-1)" /><rect
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.89166164;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
transform="scale(1,-1)"
rx="4" /><rect
style="fill:#ffffff;fill-opacity:1;stroke:#6db33f;stroke-width:1.5;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
id="rect6622"
width="87.843979"
height="36.697304"
x="223.39864"
y="-287.19809"
transform="scale(1,-1)" /><rect
transform="scale(1,-1)"
rx="4" /><rect
transform="scale(1,-1)"
y="-264.81918"
x="117.92834"
height="36.72575"
width="82.040657"
id="rect6614"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.86203903;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1" /><text
style="fill:#ffffff;fill-opacity:1;stroke:#6db33f;stroke-width:1.5;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
rx="4" /><text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Helvetica, Arial, sans-serif;letter-spacing:0px;word-spacing:0px;fill:#333333;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="121.24728"
y="260.14957"
id="text6616"
@@ -466,20 +473,20 @@
transform="scale(1.0403984,0.96117025)"><tspan
sodipodi:role="line"
id="tspan6618"
x="121.24728"
x="124.24728"
y="260.14957"
style="font-size:11.53426838px">Controllers</tspan></text>
style="font-size:11.53426838px;fill:#333333">Controllers</tspan></text>
<text
transform="scale(1.0403984,0.96117025)"
sodipodi:linespacing="125%"
id="text6624"
y="282.70709"
x="219.61203"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Helvetica, Arial, sans-serif;letter-spacing:0px;word-spacing:0px;fill:#333333;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-size:11.53426838px"
style="font-size:11.53426838px;fill:#333333"
y="282.70709"
x="219.61203"
x="221.61203"
id="tspan6626"
sodipodi:role="line">ViewResolver</tspan></text>
<rect
@@ -489,9 +496,10 @@
height="36.577778"
width="114.4539"
id="rect6628"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.0161339;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1" /><text
style="fill:#ffffff;fill-opacity:1;stroke:#6db33f;stroke-width:1.5;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
rx="4" /><text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Helvetica, Arial, sans-serif;letter-spacing:0px;word-spacing:0px;fill:#333333;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="327.51276"
y="255.28464"
id="text6630"
@@ -499,26 +507,27 @@
transform="scale(1.0403984,0.96117025)"><tspan
sodipodi:role="line"
id="tspan6632"
x="327.51276"
x="332.51276"
y="255.28464"
style="font-size:11.53426838px">HandlerMapping</tspan></text>
style="font-size:11.53426838px;fill:#333333">HandlerMapping</tspan></text>
<rect
y="338.69724"
x="87.803261"
height="121.5683"
width="382.84744"
id="rect6634"
style="fill:#dde2cd;fill-opacity:1;stroke:#000000;stroke-width:1.53790233;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1" /><text
style="fill:#F8F9FA;fill-opacity:1;stroke:#6db33f;stroke-width:2;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
rx="8" /><text
transform="scale(1.0403984,0.96117025)"
sodipodi:linespacing="125%"
id="text6636"
y="376.61673"
x="108.61351"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Helvetica, Arial, sans-serif;letter-spacing:0px;word-spacing:0px;fill:#333333;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:23.06853676px;font-family:sans-serif;-inkscape-font-specification:sans-serif"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:23.06853676px;font-family:Helvetica, Arial, sans-serif;-inkscape-font-specification:sans-serif;fill:#333333"
y="376.61673"
x="108.61351"
x="113.61351"
id="tspan6638"
sodipodi:role="line">Root WebApplicationContext</tspan></text>
<text
@@ -527,10 +536,10 @@
id="text6640"
y="395.35812"
x="260.93863"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Helvetica, Arial, sans-serif;letter-spacing:0px;word-spacing:0px;fill:#333333;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
id="tspan6644"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.53426838px;font-family:sans-serif;-inkscape-font-specification:sans-serif;text-align:center;text-anchor:middle"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.53426838px;font-family:Helvetica, Arial, sans-serif;-inkscape-font-specification:sans-serif;text-align:center;text-anchor:middle;fill:#555555"
y="395.35812"
x="260.93863"
sodipodi:role="line">(containing middle-tier services, datasources, etc.)</tspan></text>
@@ -541,43 +550,47 @@
height="36.72575"
width="82.040657"
id="rect6648"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.86203903;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1" /><rect
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.86203903;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
style="fill:#ffffff;fill-opacity:1;stroke:#6db33f;stroke-width:1.5;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
rx="4" /><rect
style="fill:#ffffff;fill-opacity:1;stroke:#6db33f;stroke-width:1.5;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
id="rect6650"
width="82.040657"
height="36.72575"
x="167.73116"
y="-445.45563"
transform="scale(1,-1)" /><text
transform="scale(1,-1)"
rx="4" /><text
transform="scale(1.0403984,0.96117025)"
sodipodi:linespacing="125%"
id="text6652"
y="448.55054"
x="175.87148"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Helvetica, Arial, sans-serif;letter-spacing:0px;word-spacing:0px;fill:#333333;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-size:11.53426838px"
style="font-size:11.53426838px;fill:#333333"
y="448.55054"
x="175.87148"
x="178.87148"
id="tspan6654"
sodipodi:role="line">Services</tspan></text>
<rect
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.88435173;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
style="fill:#ffffff;fill-opacity:1;stroke:#6db33f;stroke-width:1.5;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
id="rect6656"
width="86.393044"
height="36.704323"
x="306.86328"
y="-439.60837"
transform="scale(1,-1)" /><rect
transform="scale(1,-1)"
rx="4" /><rect
transform="scale(1,-1)"
y="-444.99475"
x="310.26624"
height="36.701977"
width="86.876686"
id="rect6658"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.88679528;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1" /><text
style="fill:#ffffff;fill-opacity:1;stroke:#6db33f;stroke-width:1.5;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
rx="4" /><text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Helvetica, Arial, sans-serif;letter-spacing:0px;word-spacing:0px;fill:#333333;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="305.30771"
y="448.55054"
id="text6660"
@@ -585,20 +598,20 @@
transform="scale(1.0403984,0.96117025)"><tspan
sodipodi:role="line"
id="tspan6662"
x="305.30771"
x="308.30771"
y="448.55054"
style="font-size:11.53426838px">Repositories</tspan></text>
style="font-size:11.53426838px;fill:#333333">Repositories</tspan></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.76895118px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
style="fill:none;fill-rule:evenodd;stroke:#333333;stroke-width:0.76895118px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 265.33234,295.60379 c 0,42.65169 0,42.65169 0,0 z"
id="path7643"
inkscape:connector-curvature="0" /><path
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.46028023;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow2Mend)"
style="fill:#333333;fill-opacity:1;fill-rule:evenodd;stroke:#333333;stroke-width:3.46028023;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow2Mend)"
d="m 270.43505,294.70585 c 0,39.4721 0,39.87903 0,39.87903"
id="path7645"
inkscape:connector-curvature="0" /><text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Helvetica, Arial, sans-serif;letter-spacing:0px;word-spacing:0px;fill:#333333;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="352.55331"
y="333.03622"
id="text4963"
@@ -607,6 +620,6 @@
sodipodi:role="line"
x="352.55331"
y="333.03622"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.53426838px;font-family:sans-serif;-inkscape-font-specification:sans-serif;text-align:center;text-anchor:middle"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.53426838px;font-family:Helvetica, Arial, sans-serif;-inkscape-font-specification:sans-serif;text-align:center;text-anchor:middle;fill:#555555"
id="tspan4965">Delegates if no bean found</tspan></text>
</g></svg>

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 31 KiB

+5 -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[]
@@ -261,7 +262,6 @@
**** xref:web/websocket/stomp/configuration-performance.adoc[]
**** xref:web/websocket/stomp/stats.adoc[]
**** xref:web/websocket/stomp/testing.adoc[]
** xref:web/integration.adoc[]
* xref:web-reactive.adoc[]
** xref:web/webflux.adoc[]
*** xref:web/webflux/new-framework.adoc[]
@@ -296,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[]
@@ -331,9 +332,10 @@
*** xref:testing/testcontext-framework/application-events.adoc[]
*** xref:testing/testcontext-framework/test-execution-events.adoc[]
*** xref:testing/testcontext-framework/ctx-management.adoc[]
**** xref:testing/testcontext-framework/ctx-management/javaconfig.adoc[]
**** xref:testing/testcontext-framework/ctx-management/xml.adoc[]
**** xref:testing/testcontext-framework/ctx-management/groovy.adoc[]
**** xref:testing/testcontext-framework/ctx-management/javaconfig.adoc[]
**** xref:testing/testcontext-framework/ctx-management/default-config.adoc[]
**** xref:testing/testcontext-framework/ctx-management/mixed-config.adoc[]
**** xref:testing/testcontext-framework/ctx-management/context-customizers.adoc[]
**** xref:testing/testcontext-framework/ctx-management/initializers.adoc[]
@@ -344,6 +346,7 @@
**** xref:testing/testcontext-framework/ctx-management/web.adoc[]
**** xref:testing/testcontext-framework/ctx-management/web-mocks.adoc[]
**** xref:testing/testcontext-framework/ctx-management/caching.adoc[]
**** xref:testing/testcontext-framework/ctx-management/context-pausing.adoc[]
**** xref:testing/testcontext-framework/ctx-management/failure-threshold.adoc[]
**** xref:testing/testcontext-framework/ctx-management/hierarchies.adoc[]
*** xref:testing/testcontext-framework/fixture-di.adoc[]
@@ -74,6 +74,11 @@ 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.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
@@ -81,6 +86,11 @@ resolvable otherwise. See
{spring-framework-api}++/core/env/AbstractEnvironment.html#IGNORE_GETENV_PROPERTY_NAME++[`AbstractEnvironment`]
for details.
| `spring.http.response.flush.enabled`
| Configures the Spring MVC `ServletServerHttpResponse` to allow flushing on the `OutputStream`
returned by `ServletServerHttpResponse#getBody()`. By default, such flush calls are ignored and
only `ServletServerHttpResponse#flush()` will actually flush the response to the network.
| `spring.jdbc.getParameterType.ignore`
| Instructs Spring to ignore `java.sql.ParameterMetaData.getParameterType` completely.
See the note in xref:data-access/jdbc/advanced.adoc#jdbc-batch-list[Batch Operations with a List of Objects].
@@ -124,11 +134,20 @@ on a test class. See xref:testing/annotations/integration-junit-jupiter.adoc#int
| The maximum size of the context cache in the _Spring TestContext Framework_. See
xref:testing/testcontext-framework/ctx-management/caching.adoc[Context Caching].
| `spring.test.context.cache.pause`
| The pause mode for the context cache in the _Spring TestContext Framework_. See
xref:testing/testcontext-framework/ctx-management/context-pausing.adoc[Context Pausing].
| `spring.test.context.failure.threshold`
| The failure threshold for errors encountered while attempting to load an `ApplicationContext`
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
@@ -727,7 +727,7 @@ of determining parameter names, an exception will be thrown.
parameter names. This discoverer is only used if such APIs are present on the classpath.
`StandardReflectionParameterNameDiscoverer` :: Uses the standard `java.lang.reflect.Parameter`
API to determine parameter names. Requires that code be compiled with the `-parameters`
flag for `javac`. Recommended approach on Java 8+.
flag for `javac`. Recommended approach.
`AspectJAdviceParameterNameDiscoverer` :: Deduces parameter names from the pointcut
expression, `returning`, and `throwing` clauses. See the
{spring-framework-api}/aop/aspectj/AspectJAdviceParameterNameDiscoverer.html[javadoc]
@@ -104,7 +104,7 @@ Note that pointcut definitions are generally matched against any intercepted met
If a pointcut is strictly meant to be public-only, even in a CGLIB proxy scenario with
potential non-public interactions through proxies, it needs to be defined accordingly.
If your interception needs include method calls or even constructors within the target
If your interception needs to include method calls or even constructors within the target
class, consider the use of Spring-driven xref:core/aop/using-aspectj.adoc#aop-aj-ltw[native AspectJ weaving] instead
of Spring's proxy-based AOP framework. This constitutes a different mode of AOP usage
with different characteristics, so be sure to make yourself familiar with weaving
@@ -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.
@@ -19,10 +19,6 @@ its behavior changes.
| Initialization of `SmartInitializingSingleton` beans.
| `beanName` the name of the bean.
| `spring.context.annotated-bean-reader.create`
| Creation of the `AnnotatedBeanDefinitionReader`.
|
| `spring.context.base-packages.scan`
| Scanning of base packages.
| `packages` array of base packages for scanning.
@@ -151,17 +151,17 @@ injected into a `Set<MovieCatalog>` annotated with `@Qualifier("action")`.
[TIP]
====
Letting qualifier values select against target bean names, within the type-matching
candidates, does not require a `@Qualifier` annotation at the injection point.
If there is no other resolution indicator (such as a qualifier or a primary marker),
for a non-unique dependency situation, Spring matches the injection point name
(that is, the field name or parameter name) against the target bean names and chooses
the same-named candidate, if any (either by bean name or by associated alias).
candidates, does not require a `@Qualifier` annotation at the injection point. If there
is no other resolution indicator (such as a qualifier, a primary marker, or a fallback
marker), for a non-unique dependency situation, Spring matches the injection point name
(that is, the field name or parameter name) against the target bean names and chooses the
same-named candidate, if any (either by bean name or by associated alias).
Since version 6.1, this requires the `-parameters` Java compiler flag to be present.
As of 6.2, the container applies fast shortcut resolution for bean name matches,
bypassing the full type matching algorithm when the parameter name matches the
bean name and no type, qualifier or primary conditions override the match. It is
therefore recommendable for your parameter names to match the target bean names.
Since version 6.1, this requires the `-parameters` Java compiler flag to be present. As
of 6.2, the container applies fast shortcut resolution for bean name matches, bypassing
the full type matching algorithm when the parameter name matches the bean name and no
type, qualifier, primary, or fallback conditions override the match. It is therefore
recommendable for your parameter names to match the target bean names.
====
As an alternative for injection by name, consider the JSR-250 `@Resource` annotation
@@ -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:
@@ -309,7 +309,8 @@ set of multiple matches for the specific bean type (as returned by the factory m
Note that the standard `jakarta.annotation.Priority` annotation is not available at the
`@Bean` level, since it cannot be declared on methods. Its semantics can be modeled
through `@Order` values in combination with `@Primary` on a single bean for each type.
through `@Order` values in combination with `@Primary` or `@Fallback` on a single bean
for each type.
====
Even typed `Map` instances can be autowired as long as the expected key type is `String`.
@@ -27,4 +27,5 @@ with the `CustomAutowireConfigurer`
When multiple beans qualify as autowire candidates, the determination of a "`primary`" is
as follows: If exactly one bean definition among the candidates has a `primary`
attribute set to `true`, it is selected.
attribute set to `true`, it is selected. For annotation-based configuration, see
xref:core/beans/annotation-config/autowired-primary.adoc[Fine-tuning with `@Primary` or `@Fallback`].
@@ -556,18 +556,11 @@ Kotlin::
======
If you do not want to rely on the default bean-naming strategy, you can provide a custom
bean-naming strategy. First, implement the
{spring-framework-api}/beans/factory/support/BeanNameGenerator.html[`BeanNameGenerator`]
bean-naming strategy. First, implement either the
{spring-framework-api}/beans/factory/support/BeanNameGenerator.html[`BeanNameGenerator`] or
{spring-framework-api}/context/annotation/ConfigurationBeanNameGenerator.html[`ConfigurationBeanNameGenerator`]
interface, and be sure to include a default no-arg constructor. Then, provide the fully
qualified class name when configuring the scanner, as the following example annotation
and bean definition show.
TIP: If you run into naming conflicts due to multiple autodetected components having the
same non-qualified class name (i.e., classes with identical names but residing in
different packages), you may need to configure a `BeanNameGenerator` that defaults to the
fully qualified class name for the generated bean name. The
`FullyQualifiedAnnotationBeanNameGenerator` located in package
`org.springframework.context.annotation` can be used for such purposes.
qualified class name when configuring the scanner, as the following examples show.
[tabs]
======
@@ -602,6 +595,27 @@ Kotlin::
</beans>
----
[TIP]
====
If you run into naming conflicts due to multiple autodetected components having the same
non-qualified class name (for example, classes with identical names but residing in
different packages), you can configure a `BeanNameGenerator` that defaults to the
fully-qualified class name for the generated bean name. The
`FullyQualifiedAnnotationBeanNameGenerator` can be used for such purposes.
As of Spring Framework 7.0, if you encounter naming conflicts among `@Bean` methods in
`@Configuration` classes, you can alternatively configure a
`ConfigurationBeanNameGenerator` that generates unique bean names for `@Bean` methods.
The `FullyQualifiedConfigurationBeanNameGenerator` can be used to generate
fully-qualified default bean names for `@Bean` methods without an explicit `name`
attribute — for example, `com.example.MyConfig.myBean` for an `@Bean` method named
`myBean()` declared in `@Configuration` class `com.example.MyConfig`.
The `FullyQualifiedAnnotationBeanNameGenerator` and
`FullyQualifiedConfigurationBeanNameGenerator` both reside in the
`org.springframework.context.annotation` package.
====
As a general rule, consider specifying the name with the annotation whenever other
components may be making explicit references to it. On the other hand, the
auto-generated names are adequate whenever the container is responsible for wiring.
@@ -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.
@@ -50,9 +50,8 @@ XML configuration:
The preceding XML is more succinct. However, typos are discovered at runtime rather than
design time, unless you use an IDE (such as https://www.jetbrains.com/idea/[IntelliJ
IDEA] or the {spring-site-tools}[Spring Tools for Eclipse])
that supports automatic property completion when you create bean definitions. Such IDE
assistance is highly recommended.
IDEA] or the {spring-site-tools}[Spring Tools]) that supports automatic property
completion when you create bean definitions. Such IDE assistance is highly recommended.
You can also configure a `java.util.Properties` instance, as follows:
@@ -67,6 +67,13 @@ interface, clearly indicating the post-processor nature of that bean. Otherwise,
Since a `BeanPostProcessor` needs to be instantiated early in order to apply to the
initialization of other beans in the context, this early type detection is critical.
Furthermore, when registering a `BeanPostProcessor` via an `@Bean` factory method,
declare the method as `static` and ideally with no dependencies. Doing so avoids eager
initialization of the configuration class and other beans, which would make them
ineligible for full post-processing (such as auto-proxying). See the
"BeanPostProcessor-returning `@Bean` methods" section in the
{spring-framework-api}/context/annotation/Bean.html[`@Bean`] javadoc for details.
[[beans-factory-programmatically-registering-beanpostprocessors]]
.Programmatically registering `BeanPostProcessor` instances
NOTE: While the recommended approach for `BeanPostProcessor` registration is through
@@ -80,7 +87,7 @@ of execution. Note also that `BeanPostProcessor` instances registered programmat
are always processed before those registered through auto-detection, regardless of any
explicit ordering.
.`BeanPostProcessor` instances and AOP auto-proxying
.`BeanPostProcessor` instances and early initialization
[NOTE]
====
Classes that implement the `BeanPostProcessor` interface are special and are treated
@@ -90,17 +97,23 @@ of the `ApplicationContext`. Next, all `BeanPostProcessor` instances are registe
in a sorted fashion and applied to all further beans in the container. Because AOP
auto-proxying is implemented as a `BeanPostProcessor` itself, neither `BeanPostProcessor`
instances nor the beans they directly reference are eligible for auto-proxying and,
thus, do not have aspects woven into them.
thus, do not have aspects woven into them. More generally, any bean that is instantiated
during this early phase is not eligible for full post-processing by all
`BeanPostProcessor` instances.
For any such bean, you should see an informational log message: `Bean someBean is not
eligible for getting processed by all BeanPostProcessor interfaces (for example: not
eligible for auto-proxying)`.
For any such bean, you should see a WARN-level log message similar to the following.
If you have beans wired into your `BeanPostProcessor` by using autowiring or
`@Resource` (which may fall back to autowiring), Spring might access unexpected beans
when searching for type-matching dependency candidates and, therefore, make them
ineligible for auto-proxying or other kinds of bean post-processing. For example, if you
have a dependency annotated with `@Resource` where the field or setter name does not
[quote]
Bean 'someBean' of type [org.example.SomeType] is not eligible for getting processed by
all BeanPostProcessors (for example: not eligible for auto-proxying).
To minimize the number of beans affected, register a `BeanPostProcessor` with a
`static` `@Bean` method that has no dependencies (see the note above). If you have
beans wired into your `BeanPostProcessor` by using autowiring or `@Resource` (which
may fall back to autowiring), Spring might access unexpected beans when searching
for type-matching dependency candidates and, therefore, make them ineligible for
auto-proxying or other kinds of bean post-processing. For example, if you have a
dependency annotated with `@Resource` where the field or setter name does not
directly correspond to the declared name of a bean and no name attribute is used,
Spring accesses other beans for matching them by type.
====
@@ -135,7 +148,7 @@ Java::
}
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("Bean '" + beanName + "' created : " + bean.toString());
System.out.println("Bean '" + beanName + "' created : " + bean);
return bean;
}
}
@@ -164,7 +177,48 @@ Kotlin::
----
======
The following `beans` element uses the `InstantiationTracingBeanPostProcessor`:
You can register the `InstantiationTracingBeanPostProcessor` with Java configuration
by using a `static` `@Bean` method (recommended to avoid eager initialization of the
configuration class and other beans):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
----
@Configuration
public class AppConfig {
@Bean
public static InstantiationTracingBeanPostProcessor instantiationTracingBeanPostProcessor() {
return new InstantiationTracingBeanPostProcessor();
}
// ... other bean definitions
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",chomp="-packages"]
----
@Configuration
class AppConfig {
@Bean
companion object {
@JvmStatic
fun instantiationTracingBeanPostProcessor() = InstantiationTracingBeanPostProcessor()
}
// ... other bean definitions
}
----
======
Alternatively, the `InstantiationTracingBeanPostProcessor` can be registered via the
`bean` element with XML configuration:
[source,xml,indent=0,subs="verbatim,quotes"]
----
@@ -192,7 +246,7 @@ The following `beans` element uses the `InstantiationTracingBeanPostProcessor`:
----
Notice how the `InstantiationTracingBeanPostProcessor` is merely defined. It does not
even have a name, and, because it is a bean, it can be dependency-injected as you would any
even have a name, and, because it is a bean, it can be dependency-injected as with any
other bean. (The preceding configuration also defines a bean that is backed by a Groovy
script.)
@@ -300,6 +354,23 @@ implement the `BeanFactoryPostProcessor` interface. It uses these beans as bean
post-processors, at the appropriate time. You can deploy these post-processor beans as
you would any other bean.
When registering a `BeanFactoryPostProcessor` via an `@Bean` factory method in a
`@Configuration` class, declare the method as `static` to avoid lifecycle conflicts
with annotation processing (such as `@Autowired`, `@Value`, and `@PostConstruct`) in the
configuration class. See the "BeanFactoryPostProcessor-returning `@Bean` methods"
section in the {spring-framework-api}/context/annotation/Bean.html[`@Bean`] javadoc
for details and an example.
For any non-static `@Bean` factory method with a `BeanFactoryPostProcessor` return type,
you should see an INFO-level log message similar to the following.
[quote]
@Bean method MyConfig.myBfpp is non-static and returns an object assignable to Spring's
BeanFactoryPostProcessor interface. This will result in a failure to process annotations
such as @Autowired, @Resource, and @PostConstruct within the method's declaring
@Configuration class. Add the 'static' modifier to this method to avoid these container
lifecycle issues; see @Bean javadoc for complete details.
NOTE: As with ``BeanPostProcessor``s , you typically do not want to configure
``BeanFactoryPostProcessor``s for lazy initialization. If no other bean references a
`Bean(Factory)PostProcessor`, that post-processor will not get instantiated at all.
@@ -4,10 +4,10 @@
`@Bean` is a method-level annotation and a direct analog of the XML `<bean/>` element.
The annotation supports some of the attributes offered by `<bean/>`, such as:
* xref:core/beans/definition.adoc#beans-beanname[name]
* xref:core/beans/factory-nature.adoc#beans-factory-lifecycle-initializingbean[init-method]
* xref:core/beans/factory-nature.adoc#beans-factory-lifecycle-disposablebean[destroy-method]
* xref:core/beans/dependencies/factory-autowire.adoc[autowiring]
* `name`.
You can use the `@Bean` annotation in a `@Configuration`-annotated or in a
`@Component`-annotated class.
@@ -17,9 +17,10 @@ You can use the `@Bean` annotation in a `@Configuration`-annotated or in a
== Declaring a Bean
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 as the method's return value. By default, the bean name is the same as
the method name. The following example shows a `@Bean` method declaration:
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
configured). The following example shows a `@Bean` method declaration:
[tabs]
======
@@ -126,7 +127,7 @@ Kotlin::
----
======
However, this limits the visibility for advance type prediction to the specified
However, this limits the visibility for advanced type prediction to the specified
interface type (`TransferService`). Then, with the full type (`TransferServiceImpl`)
known to the container only once the affected singleton bean has been instantiated.
Non-lazy singleton beans get instantiated according to their declaration order,
@@ -473,8 +474,15 @@ Kotlin::
== Customizing Bean Naming
By default, configuration classes use a `@Bean` method's name as the name of the
resulting bean. This functionality can be overridden, however, with the `name` attribute,
as the following example shows:
resulting bean. However, as of Spring Framework 7.0, you can change this default strategy
by configuring a custom
{spring-framework-api}/context/annotation/ConfigurationBeanNameGenerator.html[`ConfigurationBeanNameGenerator`]
when bootstrapping the context or configuring component scanning. For example,
{spring-framework-api}/context/annotation/FullyQualifiedConfigurationBeanNameGenerator.html[`FullyQualifiedConfigurationBeanNameGenerator`]
can be used to generate fully-qualified default bean names for `@Bean` methods without an
explicit `name` attribute. For individual `@Bean` methods, the default or
generator-derived name can be overridden with the `name` attribute, as the following
example shows:
[tabs]
======
@@ -505,6 +513,7 @@ Kotlin::
----
======
NOTE: `@Bean("myThing")` is equivalent to `@Bean(name = "myThing")`.
[[beans-java-bean-aliasing]]
== Bean Aliasing
@@ -338,11 +338,11 @@ In the preceding scenario, using `@Autowired` works well and provides the desire
modularity, but determining exactly where the autowired bean definitions are declared is
still somewhat ambiguous. For example, as a developer looking at `ServiceConfig`, how do
you know exactly where the `@Autowired AccountRepository` bean is declared? It is not
explicit in the code, and this may be just fine. Remember that the
{spring-site-tools}[Spring Tools for Eclipse] provides tooling that
can render graphs showing how everything is wired, which may be all you need. Also,
your Java IDE can easily find all declarations and uses of the `AccountRepository` type
and quickly show you the location of `@Bean` methods that return that type.
explicit in the code, and this may be just fine. Note that the
{spring-site-tools}[Spring Tools] IDE support provides tooling that can render graphs
showing how everything is wired, which may be all you need. Also, your Java IDE can
easily find all declarations and uses of the `AccountRepository` type and quickly show
you the location of `@Bean` methods that return that type.
In cases where this ambiguity is not acceptable and you wish to have direct navigation
from within your IDE from one `@Configuration` class to another, consider autowiring the
@@ -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
@@ -1,16 +1,17 @@
[[beans-standard-annotations]]
= Using JSR 330 Standard Annotations
= Using JSR-330 Standard Annotations
Spring offers support for JSR-330 standard annotations (Dependency Injection). Those
annotations are scanned in the same way as the Spring annotations. To use them, you need
to have the relevant jars in your classpath.
Spring offers support for JSR-330 standard _Dependency Injection_ annotations which are
available in the `jakarta.inject` package. These annotations may optionally be used as
alternatives to Spring annotations.
To use them, you need to have the relevant jar in your classpath. For example, the
`jakarta.inject` artifact is available in the standard Maven repository
(`https://repo.maven.apache.org/maven2/jakarta/inject/jakarta.inject-api/2.0.0/`),
[NOTE]
=====
If you use Maven, the `jakarta.inject` artifact is available in the standard Maven
repository (
https://repo.maven.apache.org/maven2/jakarta/inject/jakarta.inject-api/2.0.0/[https://repo.maven.apache.org/maven2/jakarta/inject/jakarta.inject-api/2.0.0/]).
You can add the following dependency to your file pom.xml:
If you use Maven, you can add the following dependency to your `pom.xml` file.
[source,xml,indent=0,subs="verbatim,quotes"]
----
@@ -26,13 +27,14 @@ You can add the following dependency to your file pom.xml:
[[beans-inject-named]]
== Dependency Injection with `@Inject` and `@Named`
Instead of `@Autowired`, you can use `@jakarta.inject.Inject` as follows:
Instead of using `@Autowired` for dependency injection, you may optionally choose to use
`@jakarta.inject.Inject` as follows.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",fold="-imports"]
----
import jakarta.inject.Inject;
@@ -54,7 +56,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",fold="-imports"]
----
import jakarta.inject.Inject
@@ -72,17 +74,19 @@ Kotlin::
----
======
As with `@Autowired`, you can use `@Inject` at the field level, method level
and constructor-argument level. Furthermore, you may declare your injection point as a
`Provider`, allowing for on-demand access to beans of shorter scopes or lazy access to
other beans through a `Provider.get()` call. The following example offers a variant of the
preceding example:
As with `@Autowired`, you can use `@Inject` at the field level, method level, and
constructor-argument level.
Furthermore, as an alternative to Spring's `ObjectProvider` mechanism, you may choose to
declare your injection point as a `jakarta.inject.Provider`, allowing for on-demand
access to beans of shorter scopes or lazy access to other beans through a
`Provider.get()` call. The following example offers a variant of the preceding example.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",fold="-imports"]
----
import jakarta.inject.Inject;
import jakarta.inject.Provider;
@@ -105,9 +109,10 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",fold="-imports"]
----
import jakarta.inject.Inject
import jakarta.inject.Provider
class SimpleMovieLister {
@@ -123,14 +128,15 @@ Kotlin::
----
======
If you would like to use a qualified name for the dependency that should be injected,
you should use the `@Named` annotation, as the following example shows:
If you would like to use a qualified name for the dependency that should be injected, you
may choose to use the `@Named` annotation as an alternative to Spring's `@Qualifier`
support, as the following example shows.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",fold="-imports"]
----
import jakarta.inject.Inject;
import jakarta.inject.Named;
@@ -150,7 +156,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",fold="-imports"]
----
import jakarta.inject.Inject
import jakarta.inject.Named
@@ -170,12 +176,15 @@ Kotlin::
======
As with `@Autowired`, `@Inject` can also be used with `java.util.Optional` or
`@Nullable`. This is even more applicable here, since `@Inject` does not have
a `required` attribute. The following pair of examples show how to use `@Inject` and
`@Nullable`:
`@Nullable`. This is even more applicable here, since `@Inject` does not have a
`required` attribute. The following examples show how to use `@Inject` with `Optional`,
`@Nullable`, and Kotlin's built-in support for nullable types.
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",fold="-imports"]
----
import jakarta.inject.Inject;
import java.util.Optional;
public class SimpleMovieLister {
@Inject
@@ -189,8 +198,11 @@ a `required` attribute. The following pair of examples show how to use `@Inject`
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",fold="-imports"]
----
import jakarta.inject.Inject;
import org.jspecify.annotations.Nullable;
public class SimpleMovieLister {
@Inject
@@ -202,8 +214,10 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",fold="-imports"]
----
import jakarta.inject.Inject
class SimpleMovieLister {
@Inject
@@ -214,21 +228,21 @@ Kotlin::
[[beans-named]]
== `@Named` and `@ManagedBean`: Standard Equivalents to the `@Component` Annotation
== `@Named`: Standard Equivalent to the `@Component` Annotation
Instead of `@Component`, you can use `@jakarta.inject.Named` or `jakarta.annotation.ManagedBean`,
as the following example shows:
Instead of `@Component` or other Spring stereotype annotations, you may optionally choose
to use `@jakarta.inject.Named`, as the following example shows.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",fold="-imports"]
----
import jakarta.inject.Inject;
import jakarta.inject.Named;
@Named("movieListener") // @ManagedBean("movieListener") could be used as well
@Named("movieListener")
public class SimpleMovieLister {
private MovieFinder movieFinder;
@@ -244,12 +258,12 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",fold="-imports"]
----
import jakarta.inject.Inject
import jakarta.inject.Named
@Named("movieListener") // @ManagedBean("movieListener") could be used as well
@Named("movieListener")
class SimpleMovieLister {
@Inject
@@ -260,14 +274,15 @@ Kotlin::
----
======
It is very common to use `@Component` without specifying a name for the component.
`@Named` can be used in a similar fashion, as the following example shows:
It is very common to use `@Component` or other Spring stereotype annotations without
specifying an explicit name for the component, and `@Named` can be used in a similar
fashion, as the following example shows.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",fold="-imports"]
----
import jakarta.inject.Inject;
import jakarta.inject.Named;
@@ -288,7 +303,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",fold="-imports"]
----
import jakarta.inject.Inject
import jakarta.inject.Named
@@ -304,14 +319,14 @@ Kotlin::
----
======
When you use `@Named` or `@ManagedBean`, you can use component scanning in the
exact same way as when you use Spring annotations, as the following example shows:
When you use `@Named`, you can use component scanning in the exact same way as when you
use Spring annotations, as the following example shows.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",fold="-imports"]
----
@Configuration
@ComponentScan(basePackages = "org.example")
@@ -322,7 +337,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",fold="-imports"]
----
@Configuration
@ComponentScan(basePackages = ["org.example"])
@@ -332,55 +347,106 @@ Kotlin::
----
======
NOTE: In contrast to `@Component`, the JSR-330 `@Named` and the JSR-250 `@ManagedBean`
annotations are not composable. You should use Spring's stereotype model for building
custom component annotations.
NOTE: In contrast to `@Component`, the JSR-330 `@Named` annotation is not composable. You
should use Spring's stereotype model for building custom component annotations.
[TIP]
====
If you work with legacy systems that still use `@javax.inject.Named` or
`@javax.annotation.ManagedBean` for components (note the `javax` package namespace), you
can explicitly configure component scanning to include those annotation types, as shown
in the following example.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",fold="-imports"]
----
@Configuration
@ComponentScan(
basePackages = "org.example",
includeFilters = @Filter({
javax.inject.Named.class,
javax.annotation.ManagedBean.class
})
)
public class AppConfig {
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",fold="-imports"]
----
@Configuration
@ComponentScan(
basePackages = ["org.example"],
includeFilters = [Filter([
javax.inject.Named::class,
javax.annotation.ManagedBean::class
])]
)
class AppConfig {
// ...
}
----
======
In addition, if you would like for the `value` attributes in `@javax.inject.Named` and
`@javax.annotation.ManagedBean` to be used as component names, you need to override the
`isStereotypeWithNameValue(...)` method in `AnnotationBeanNameGenerator` to add explicit
support for `javax.annotation.ManagedBean` and `javax.inject.Named` and register your
custom `AnnotationBeanNameGenerator` via the `nameGenerator` attribute in
`@ComponentScan`.
====
[[beans-standard-annotations-limitations]]
== Limitations of JSR-330 Standard Annotations
When you work with standard annotations, you should know that some significant
features are not available, as the following table shows:
When you work with JSR-330 standard annotations, you should know that some significant
features are not available, as the following table shows.
[[annotations-comparison]]
.Spring component model elements versus JSR-330 variants
.Spring component model versus JSR-330 variants
|===
| Spring| jakarta.inject.*| jakarta.inject restrictions / comments
| Spring | JSR-330 | JSR-330 restrictions / comments
| @Autowired
| @Inject
| `@Inject` has no 'required' attribute. Can be used with Java 8's `Optional` instead.
| `@Autowired`
| `@Inject`
| `@Inject` has no `required` attribute. Can be used with Java's `Optional` instead.
| @Component
| @Named / @ManagedBean
| `@Component`
| `@Named`
| JSR-330 does not provide a composable model, only a way to identify named components.
| @Scope("singleton")
| @Singleton
| `@Scope("singleton")`
| `@Singleton`
| The JSR-330 default scope is like Spring's `prototype`. However, in order to keep it
consistent with Spring's general defaults, a JSR-330 bean declared in the Spring
container is a `singleton` by default. In order to use a scope other than `singleton`,
you should use Spring's `@Scope` annotation. `jakarta.inject` also provides a
`jakarta.inject.Scope` annotation: however, this one is only intended to be used
`jakarta.inject.Scope` annotation; however, this one is only intended to be used
for creating custom annotations.
| @Qualifier
| @Qualifier / @Named
| `@Qualifier`
| `@Qualifier` / `@Named`
| `jakarta.inject.Qualifier` is just a meta-annotation for building custom qualifiers.
Concrete `String` qualifiers (like Spring's `@Qualifier` with a value) can be associated
through `jakarta.inject.Named`.
| @Value
| `@Value`
| -
| no equivalent
| @Lazy
| `@Lazy`
| -
| no equivalent
| ObjectFactory
| Provider
| `ObjectFactory`
| `Provider`
| `jakarta.inject.Provider` is a direct alternative to Spring's `ObjectFactory`,
only with a shorter `get()` method name. It can also be used in combination with
Spring's `@Autowired` or with non-annotated constructors and setter methods.
@@ -7,14 +7,14 @@ similar to the https://jakarta.ee/specifications/expression-language/[Jakarta Ex
Language] but offers additional features, most notably method invocation and basic string
templating functionality.
While there are several other Java expression languages available -- OGNL, MVEL, and JBoss
EL, to name a few -- the Spring Expression Language was created to provide the Spring
community with a single well supported expression language that can be used across all
the products in the Spring portfolio. Its language features are driven by the
requirements of the projects in the Spring portfolio, including tooling requirements
for code completion support within the {spring-site-tools}[Spring Tools for Eclipse].
That said, SpEL is based on a technology-agnostic API that lets other expression language
implementations be integrated, should the need arise.
While there are several other Java expression languages available -- OGNL, MVEL, and
JBoss EL, to name a few -- the Spring Expression Language was created to provide the
Spring community with a single well supported expression language that can be used across
all the products in the Spring portfolio. Its language features are driven by the
requirements of the projects in the Spring portfolio, including tooling requirements for
code completion within the {spring-site-tools}[Spring Tools] IDE support. That said, SpEL
is based on a technology-agnostic API that lets other expression language implementations
be integrated, should the need arise.
While SpEL serves as the foundation for expression evaluation within the Spring
portfolio, it is not directly tied to Spring and can be used independently. To
@@ -395,6 +395,16 @@ 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 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]).
[[expressions-spel-compilation]]
== SpEL Compilation
@@ -531,6 +541,7 @@ following kinds of expressions cannot be compiled.
* Expressions relying on the conversion service
* Expressions using custom resolvers
* Expressions using overloaded operators
* Expressions using `Optional` with the null-safe or Elvis operator
* Expressions using array construction syntax
* Expressions using selection or projection
* Expressions using bean references
@@ -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.
@@ -53,7 +53,7 @@ Kotlin::
val trueValue = parser.parseExpression("'black' < 'block'").getValue(Boolean::class.java)
// uses CustomValue:::compareTo
val trueValue = parser.parseExpression("new CustomValue(1) < new CustomValue(2)").getValue(Boolean::class.java);
val trueValue = parser.parseExpression("new CustomValue(1) < new CustomValue(2)").getValue(Boolean::class.java)
----
======
@@ -167,7 +167,7 @@ Kotlin::
[CAUTION]
====
The syntax for the `between` operator is `<input> between {<range_begin>, <range_end>}`,
which is effectively a shortcut for `<input> >= <range_begin> && <input> \<= <range_end>}`.
which is effectively a shortcut for `<input> >= <range_begin> && <input> \<= <range_end>`.
Consequently, `1 between {1, 5}` evaluates to `true`, while `1 between {5, 1}` evaluates
to `false`.
@@ -312,13 +312,13 @@ Kotlin::
// evaluates to 'a'
val ch = parser.parseExpression("'d' - 3")
.getValue(Character::class.java);
.getValue(Char::class.java)
// -- Repeat --
// evaluates to "abcabc"
val repeated = parser.parseExpression("'abc' * 2")
.getValue(String::class.java);
.getValue(String::class.java)
----
======
@@ -485,7 +485,7 @@ Kotlin::
// -- Operator precedence --
val minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Int::class.java) // -21
val minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Int::class.java) // -21
----
======
@@ -541,32 +541,7 @@ For example, if we want to overload the `ADD` operator to allow two lists to be
concatenated using the `+` sign, we can implement a custom `OperatorOverloader` as
follows.
[source,java,indent=0,subs="verbatim,quotes"]
----
pubic class ListConcatenation implements OperatorOverloader {
@Override
public boolean overridesOperation(Operation operation, Object left, Object right) {
return (operation == Operation.ADD &&
left instanceof List && right instanceof List);
}
@Override
@SuppressWarnings("unchecked")
public Object operate(Operation operation, Object left, Object right) {
if (operation == Operation.ADD &&
left instanceof List list1 && right instanceof List list2) {
List result = new ArrayList(list1);
result.addAll(list2);
return result;
}
throw new UnsupportedOperationException(
"No overload for operation %s and operands [%s] and [%s]"
.formatted(operation, left, right));
}
}
----
include-code::./ListConcatenation[]
If we register `ListConcatenation` as the `OperatorOverloader` in a
`StandardEvaluationContext`, we can then evaluate expressions like `{1, 2, 3} + {4, 5}`
@@ -589,8 +564,8 @@ Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
StandardEvaluationContext context = StandardEvaluationContext()
context.setOperatorOverloader(ListConcatenation())
val context = StandardEvaluationContext()
context.operatorOverloader = ListConcatenation()
// evaluates to a new list: [1, 2, 3, 4, 5]
parser.parseExpression("{1, 2, 3} + {2 + 2, 5}").getValue(context, List::class.java)
@@ -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
@@ -6,7 +6,7 @@ are set by using the `setVariable()` method in `EvaluationContext` implementatio
[NOTE]
====
Variable names must be begin with a letter (as defined below), an underscore, or a dollar
Variable names must begin with a letter (as defined below), an underscore, or a dollar
sign.
Variable names must be composed of one or more of the following supported types of
@@ -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,12 +70,12 @@ 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(...);
}
@@ -99,6 +100,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 +182,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()`.
@@ -183,9 +191,6 @@ By default, a retryable operation will be retried for any exception thrown: with
3 retry attempts (`maxRetries = 3`) after an initial failure, and a delay of 1 second
between attempts.
If you only need to customize the number of retry attempts, you can use the
`RetryPolicy.withMaxRetries()` factory method as demonstrated below.
[NOTE]
====
A retryable operation will be executed at least once and retried at most `maxRetries`
@@ -196,11 +201,14 @@ For example, if `maxRetries` is set to `4`, the retryable operation will be invo
least once and at most 5 times.
====
If you only need to customize the number of retry attempts, you can use the
`RetryPolicy.withMaxRetries()` factory method as demonstrated below.
[source,java,indent=0,subs="verbatim,quotes"]
----
var retryTemplate = new RetryTemplate(RetryPolicy.withMaxRetries(4)); // <1>
retryTemplate.execute(
retryTemplate.invoke(
() -> jmsClient.destination("notifications").send(...));
----
<1> Explicitly uses `RetryPolicy.withMaxRetries(4)`.
@@ -218,7 +226,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 +259,101 @@ 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]
====
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`].
====
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`].
@@ -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
@@ -15,7 +15,7 @@ configuration options. You should instantiate the `SimpleJdbcInsert` in the data
layer's initialization method. For this example, the initializing method is the
`setDataSource` method. You do not need to subclass the `SimpleJdbcInsert` class. Instead,
you can create a new instance and set the table name by using the `withTableName` method.
Configuration methods for this class follow the `fluid` style that returns the instance
Configuration methods for this class follow the `fluent` style that returns the instance
of the `SimpleJdbcInsert`, which lets you chain all configuration methods. The following
example uses only one configuration method (we show examples of multiple methods later):
@@ -349,11 +349,11 @@ parameters return the data read from the table.
You can declare `SimpleJdbcCall` in a manner similar to declaring `SimpleJdbcInsert`. You
should instantiate and configure the class in the initialization method of your data-access
layer. Compared to the `StoredProcedure` class, you need not create a subclass
and you need not to declare parameters that can be looked up in the database metadata.
The following example of a `SimpleJdbcCall` configuration uses the preceding stored
procedure (the only configuration option, in addition to the `DataSource`, is the name
of the stored procedure):
layer. In contrast to the `StoredProcedure` class, you do not need to create a subclass,
and you do not need to declare parameters that can be looked up in the database metadata.
The following `SimpleJdbcCall` configuration example uses the preceding stored procedure.
The only configuration option (other than the `DataSource`) is the name of the stored
procedure.
[tabs]
======
@@ -14,7 +14,7 @@ As of Spring Framework 7.0, Spring requires Hibernate ORM 7.x for Spring's
`HibernateJpaVendorAdapter`.
The `org.springframework.orm.jpa.hibernate` package supersedes the former `orm.hibernate5`:
now for use with Hibernate ORM 7.1+, tightly integrated with `HibernateJpaVendorAdapter`
now for use with Hibernate ORM 7.x, tightly integrated with `HibernateJpaVendorAdapter`
as well as supporting Hibernate's native `SessionFactory.getCurrentSession()` style.
====
@@ -8,7 +8,7 @@ danger of undue coupling, because code that is meant to be used transactionally
almost always deployed that way anyway.
NOTE: The standard `jakarta.transaction.Transactional` annotation is also supported as
a drop-in replacement to Spring's own annotation. Please refer to the JTA documentation
a drop-in replacement for Spring's own annotation. Please refer to the JTA documentation
for more details.
The ease-of-use afforded by the use of the `@Transactional` annotation is best
@@ -89,47 +89,16 @@ annotation in a `@Configuration` class. See the
{spring-framework-api}/transaction/annotation/EnableTransactionManagement.html[javadoc]
for full details.
In XML configuration, the `<tx:annotation-driven/>` tag provides similar convenience:
The following example shows the configuration needed to enable annotation-driven transaction management:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- from the file 'context.xml' -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
include-code::./AppConfig[tag=snippet,indent=0]
<!-- this is the service object that we want to make transactional -->
<bean id="fooService" class="x.y.service.DefaultFooService"/>
<!-- enable the configuration of transactional behavior based on annotations -->
<!-- a TransactionManager is still required -->
<tx:annotation-driven transaction-manager="txManager"/> <1>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- (this dependency is defined somewhere else) -->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- other <bean/> definitions here -->
</beans>
----
<1> The line that makes the bean instance transactional.
TIP: You can omit the `transaction-manager` attribute in the `<tx:annotation-driven/>`
tag if the bean name of the `TransactionManager` that you want to wire in has the name
`transactionManager`. If the `TransactionManager` bean that you want to dependency-inject
has any other name, you have to use the `transaction-manager` attribute, as in the
preceding example.
TIP: In programmatic configuration, the `@EnableTransactionManagement` annotation uses any
`TransactionManager` bean in the context. In XML configuration, you can omit
the `transaction-manager` attribute in the `<tx:annotation-driven/>` tag if the bean
name of the `TransactionManager` that you want to wire in has the name `transactionManager`.
If the `TransactionManager` bean has any other name, you have to use the
`transaction-manager` attribute explicitly, as in the preceding example.
Reactive transactional methods use reactive return types in contrast to imperative
programming arrangements as the following listing shows:
@@ -234,8 +203,10 @@ on an interface, a class definition, or a method on a class. However, the mere p
of the `@Transactional` annotation is not enough to activate the transactional behavior.
The `@Transactional` annotation is merely metadata that can be consumed by corresponding
runtime infrastructure which uses that metadata to configure the appropriate beans with
transactional behavior. In the preceding example, the `<tx:annotation-driven/>` element
switches on actual transaction management at runtime.
transactional behavior. In the preceding examples that use programmatic configuration,
the `@EnableTransactionManagement` annotation switches on actual transaction management
at runtime. Whereas, in the preceding example that uses XML configuration, the
`<tx:annotation-driven/>` element switches on actual transaction management at runtime.
TIP: The Spring team recommends that you annotate methods of concrete classes with the
`@Transactional` annotation, rather than relying on annotated methods in interfaces,
@@ -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
@@ -3,21 +3,22 @@
:page-aliases: integration/class-data-sharing.adoc
:page-aliases: integration/cds.adoc
The ahead-of-time cache is a JVM feature introduced in Java 24 via the
The ahead-of-time cache is a JVM feature introduced in Java 24 via
https://openjdk.org/jeps/483[JEP 483] that can help reduce the startup time and memory
footprint of Java applications. AOT cache is a natural evolution of https://docs.oracle.com/en/java/javase/17/vm/class-data-sharing.html[Class Data Sharing (CDS)].
footprint of Java applications. AOT cache is a natural evolution of
https://docs.oracle.com/en/java/javase/17/vm/class-data-sharing.html[Class Data Sharing (CDS)].
Spring Framework supports both CDS and AOT cache, and it is recommended that you use the
later if available in the JVM version your are using (Java 24+).
latter if available in the JVM version you are using (Java 24+).
To use this feature, an AOT cache should be created for the particular classpath of the
application. It is possible to create this cache on the deployed instance, or during a
training run performed for example when packaging the application thanks to an hook-point
training run performed for example when packaging the application thanks to a hook-point
provided by the Spring Framework to ease such use case. Once the cache is available, users
should opt in to use it via a JVM flag.
NOTE: If you are using Spring Boot, it is highly recommended to leverage its
{spring-boot-docs-ref}/packaging/efficient.html#packaging.efficient.unpacking[executable JAR unpacking support]
which is designed to fulfill the class loading requirements of both AOT cache and CDS.
which is designed to fulfill the class loading requirements of both the AOT cache and CDS.
== Creating the cache
@@ -29,14 +30,22 @@ invoked; but the lifecycle has not started, and the `ContextRefreshedEvent` has
been published.
To create the cache during the training run, it is possible to specify the `-Dspring.context.exit=onRefresh`
JVM flag to start then exit your Spring application once the
JVM flag to start and then exit your Spring application once the
`ApplicationContext` has refreshed:
--
[tabs]
======
AOT cache::
AOT cache (Java 25+)::
+
[source,bash,subs="verbatim,quotes"]
----
java -XX:AOTCacheOutput=app.aot -Dspring.context.exit=onRefresh -jar application.jar ...
----
AOT cache (Java 24)::
+
[source,bash,subs="verbatim,quotes"]
----
@@ -55,6 +64,12 @@ java -XX:ArchiveClassesAtExit=app.jsa -Dspring.context.exit=onRefresh ...
======
--
NOTE: With Java 25+, AOT cache stores, among other things, the
https://openjdk.org/jeps/515[method profiling information]. Therefore, to benefit of this capability,
it is recommended to create an AOT cache for an application that experienced a portion of a
production-like workflow instead of using the `-Dspring.context.exit=onRefresh` flag which designed to
optimize only the startup of your application.
== Using the cache
Once the cache file has been created, you can use it to start your application faster:
@@ -82,7 +97,7 @@ java -XX:SharedArchiveFile=app.jsa ...
Pay attention to the logs and the startup time to check if the AOT cache is used successfully.
To figure out how effective the cache is, you can enable class loading logs by adding
an extra attribute: `-Xlog:class+load:file=aot-cache.log`. This creates a `aot-cache.log` with
an extra attribute: `-Xlog:class+load:file=aot-cache.log`. This creates an `aot-cache.log` with
every attempt to load a class and its source. Classes that are loaded from the cache should have
a "shared objects file" source, as shown in the following example:
@@ -95,11 +110,11 @@ a "shared objects file" source, as shown in the following example:
[0.151s][info][class,load] org.springframework.context.MessageSource source: shared objects file
----
If the AOT cache can't be enabled or if you have a large number of classes that are not loaded from
If the AOT cache cannot be enabled or if you have a large number of classes that are not loaded from
the cache, make sure that the following conditions are fulfilled when creating and using the cache:
- The very same JVM must be used.
- The classpath must be specified as a JAR or a list of JARs, and avoid the usage of directories and `*` wildcard characters.
- The timestamps of the JARs must be preserved.
- When using the cache, the classpath must be the same than the one used to create it, in the same order.
Additional JARs or directories can be specified *at the end* (but won't be cached).
- When using the cache, the classpath must be the same as the one used to create it, in the same order.
Additional JARs or directories can be specified *at the end* (but will not be cached).
@@ -1,16 +1,11 @@
[[appendix]]
= Appendix
[[appendix.xsd-schemas]]
== XML Schemas
This part of the appendix lists XML schemas related to integration technologies.
[[appendix.xsd-schemas-jee]]
=== The `jee` Schema
@@ -172,7 +167,7 @@ different properties with `jee`:
The `<jee:local-slsb/>` element configures a reference to a local EJB Stateless Session Bean.
The following example shows how to configures a reference to a local EJB Stateless Session Bean
The following example shows how to configure a reference to a local EJB Stateless Session Bean
without `jee`:
[source,xml,indent=0,subs="verbatim,quotes"]
@@ -184,7 +179,7 @@ without `jee`:
</bean>
----
The following example shows how to configures a reference to a local EJB Stateless Session Bean
The following example shows how to configure a reference to a local EJB Stateless Session Bean
with `jee`:
[source,xml,indent=0,subs="verbatim,quotes"]
@@ -200,7 +195,7 @@ with `jee`:
The `<jee:local-slsb/>` element configures a reference to a local EJB Stateless Session Bean.
The following example shows how to configures a reference to a local EJB Stateless Session Bean
The following example shows how to configure a reference to a local EJB Stateless Session Bean
and a number of properties without `jee`:
[source,xml,indent=0,subs="verbatim,quotes"]
@@ -215,7 +210,7 @@ and a number of properties without `jee`:
</bean>
----
The following example shows how to configures a reference to a local EJB Stateless Session Bean
The following example shows how to configure a reference to a local EJB Stateless Session Bean
and a number of properties with `jee`:
[source,xml,indent=0,subs="verbatim,quotes"]
@@ -234,7 +229,7 @@ and a number of properties with `jee`:
The `<jee:remote-slsb/>` element configures a reference to a `remote` EJB Stateless Session Bean.
The following example shows how to configures a reference to a remote EJB Stateless Session Bean
The following example shows how to configure a reference to a remote EJB Stateless Session Bean
without `jee`:
[source,xml,indent=0,subs="verbatim,quotes"]
@@ -251,7 +246,7 @@ without `jee`:
</bean>
----
The following example shows how to configures a reference to a remote EJB Stateless Session Bean
The following example shows how to configure a reference to a remote EJB Stateless Session Bean
with `jee`:
[source,xml,indent=0,subs="verbatim,quotes"]
@@ -313,7 +308,7 @@ xref:integration/jmx/naming.adoc#jmx-context-mbeanexport[Configuring Annotation-
=== The `cache` Schema
You can use the `cache` elements to enable support for Spring's `@CacheEvict`, `@CachePut`,
and `@Caching` annotations. It it also supports declarative XML-based caching. See
and `@Caching` annotations. The `cache` schema also supports declarative XML-based caching. See
xref:integration/cache/annotations.adoc#cache-annotation-enable[Enabling Caching Annotations] and
xref:integration/cache/declarative-xml.adoc[Declarative XML-based Caching] for details.
@@ -35,7 +35,7 @@ xref:integration/cache/store-configuration.adoc#cache-store-configuration-jsr107
[[cache-store-configuration-caffeine]]
== Caffeine Cache
Caffeine is a Java 8 rewrite of Guava's cache, and its implementation is located in the
Caffeine is a rewrite of Guava's cache, and its implementation is located in the
`org.springframework.cache.caffeine` package and provides access to several features
of Caffeine.
@@ -26,7 +26,7 @@ behind the scenes for each annotated method, by using a `JmsListenerContainerFac
Such a container is not registered against the application context but can be easily
located for management purposes by using the `JmsListenerEndpointRegistry` bean.
TIP: `@JmsListener` is a repeatable annotation on Java 8, so you can associate
TIP: `@JmsListener` is a repeatable annotation, so you can associate
several JMS destinations with the same method by adding additional `@JmsListener`
declarations to it.
@@ -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].
@@ -466,7 +466,7 @@ synchronous, asynchronous, and streaming scenarios.
* Non-blocking I/O
* Reactive Streams back pressure
* High concurrency with fewer hardware resources
* Functional-style, fluent API that takes advantage of Java 8 lambdas
* Functional-style, fluent API that takes advantage of lambda expressions
* Synchronous and asynchronous interactions
* Streaming up to or streaming down from a server
@@ -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();
@@ -533,8 +533,7 @@ that returns a value:
----
TIP: `@Async` methods may not only declare a regular `java.util.concurrent.Future` return
type but also Spring's `org.springframework.util.concurrent.ListenableFuture` or, as of
Spring 4.2, JDK 8's `java.util.concurrent.CompletableFuture`, for richer interaction with
type but also `java.util.concurrent.CompletableFuture`, for richer interaction with
the asynchronous task and for immediate composition with further processing steps.
You can not use `@Async` in conjunction with lifecycle callbacks such as `@PostConstruct`.
@@ -3,14 +3,16 @@
:page-section-summary-toc: 1
The Spring Framework supports various Kotlin constructs, such as instantiating Kotlin classes
through primary constructors, immutable classes data binding, and function optional parameters
with default values.
through primary constructors, data binding for immutable classes, and optional parameters
with default values for functions.
Kotlin parameter names are recognized through a dedicated `KotlinReflectionParameterNameDiscoverer`,
which allows finding interface method parameter names without requiring the Java 8 `-parameters`
compiler flag to be enabled during compilation. (For completeness, we nevertheless recommend
running the Kotlin compiler with its `-java-parameters` flag for standard Java parameter exposure.)
which allows finding interface method parameter names without requiring the Java `-parameters`
compiler flag to be enabled during compilation.
TIP: For completeness, we nevertheless recommend running the Kotlin compiler with its
`-java-parameters` flag for standard Java parameter exposure.
You can declare configuration classes as
{kotlin-docs}/nested-classes.html[top level or nested but not inner],
since the later requires a reference to the outer class.
since the latter requires a reference to the outer class.
@@ -1,8 +1,8 @@
[[coroutines]]
= Coroutines
Kotlin {kotlin-docs}/coroutines-overview.html[Coroutines] are Kotlin
lightweight threads allowing to write non-blocking code in an imperative way. On language side,
Kotlin {kotlin-docs}/coroutines-overview.html[Coroutines] are instances of
suspendable computations allowing to write non-blocking code in an imperative way. On language side,
suspending functions provides an abstraction for asynchronous operations while on library side
{kotlin-github-org}/kotlinx.coroutines[kotlinx.coroutines] provides functions like
{kotlin-coroutines-api}/kotlinx-coroutines-core/kotlinx.coroutines/async.html[`async { }`]
@@ -319,9 +319,17 @@ progresses.
== Testing
This section addresses testing with the combination of Kotlin and Spring Framework.
The recommended testing framework is https://junit.org/junit5/[JUnit] along with
The recommended testing framework is https://junit.org/[JUnit] along with
https://mockk.io/[Mockk] for mocking.
[TIP]
====
Kotlin lets you specify meaningful test function names between backticks (```).
For a concrete example, see the `+++`Find all users on HTML page`()+++` test function later
in this section.
====
NOTE: If you are using Spring Boot, see
{spring-boot-docs-ref}/features/kotlin.html#features.kotlin.testing[this related documentation].
@@ -352,7 +360,6 @@ file with a `spring.test.constructor.autowire.mode = all` property.
[[per_class-lifecycle]]
=== `PER_CLASS` Lifecycle
Kotlin lets you specify meaningful test function names between backticks (```).
With JUnit Jupiter, Kotlin test classes can use the `@TestInstance(TestInstance.Lifecycle.PER_CLASS)`
annotation to enable single instantiation of test classes, which allows the use of `@BeforeAll`
and `@AfterAll` annotations on non-static methods, which is a good fit for Kotlin.
@@ -237,8 +237,8 @@ Java::
[source,java,indent=0,subs="verbatim,quotes"]
----
RSocketStrategies strategies = RSocketStrategies.builder()
.encoders(encoders -> encoders.add(new Jackson2CborEncoder()))
.decoders(decoders -> decoders.add(new Jackson2CborDecoder()))
.encoders(encoders -> encoders.add(new JacksonCborEncoder()))
.decoders(decoders -> decoders.add(new JacksonCborDecoder()))
.build();
RSocketRequester requester = RSocketRequester.builder()
@@ -251,8 +251,8 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
val strategies = RSocketStrategies.builder()
.encoders { it.add(Jackson2CborEncoder()) }
.decoders { it.add(Jackson2CborDecoder()) }
.encoders { it.add(JacksonCborEncoder()) }
.decoders { it.add(JacksonCborDecoder()) }
.build()
val requester = RSocketRequester.builder()
@@ -681,8 +681,8 @@ Java::
@Bean
public RSocketStrategies rsocketStrategies() {
return RSocketStrategies.builder()
.encoders(encoders -> encoders.add(new Jackson2CborEncoder()))
.decoders(decoders -> decoders.add(new Jackson2CborDecoder()))
.encoders(encoders -> encoders.add(new JacksonCborEncoder()))
.decoders(decoders -> decoders.add(new JacksonCborDecoder()))
.routeMatcher(new PathPatternRouteMatcher())
.build();
}
@@ -703,8 +703,8 @@ Kotlin::
@Bean
fun rsocketStrategies() = RSocketStrategies.builder()
.encoders { it.add(Jackson2CborEncoder()) }
.decoders { it.add(Jackson2CborDecoder()) }
.encoders { it.add(JacksonCborEncoder()) }
.decoders { it.add(JacksonCborDecoder()) }
.routeMatcher(PathPatternRouteMatcher())
.build()
}
@@ -3,8 +3,9 @@
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`]
@@ -14,6 +15,56 @@ and JUnit Jupiter (that is, the programming model in JUnit):
* xref:testing/annotations/integration-spring/annotation-disabledinaotmode.adoc[`@DisabledInAotMode`]
[[integration-testing-annotations-springextensionconfig]]
== `@SpringExtensionConfig`
`@SpringExtensionConfig` is a type-level annotation that can be used to configure the
behavior of the `SpringExtension`.
As of Spring Framework 7.0, the `SpringExtension` is configured to use a test-method
scoped `ExtensionContext`, which enables consistent dependency injection into fields and
constructors from the `ApplicationContext` for the current test method in a `@Nested`
test class hierarchy. However, if a third-party `TestExecutionListener` is not compatible
with the semantics associated with a test-method scoped extension context — or if a
developer wishes to switch to test-class scoped semantics — the `SpringExtension` can be
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 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.
In addition,
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.
====
[[integration-testing-annotations-junit-jupiter-springjunitconfig]]
== `@SpringJUnitConfig`
@@ -187,7 +238,7 @@ default mode may be set via the
xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism.
The default mode may also be configured as a
https://junit.org/junit5/docs/current/user-guide/#running-tests-config-params[JUnit Platform configuration parameter].
https://docs.junit.org/current/running-tests/configuration-parameters.html[JUnit Platform configuration parameter].
If the `spring.test.constructor.autowire.mode` property is not set, test class
constructors will not be automatically autowired.
@@ -2,11 +2,12 @@
= Meta-Annotation Support for Testing
You can use most test-related annotations as
xref:core/beans/classpath-scanning.adoc#beans-meta-annotations[meta-annotations] to create custom composed
annotations and reduce configuration duplication across a test suite.
xref:core/beans/classpath-scanning.adoc#beans-meta-annotations[meta-annotations] to
create custom composed annotations and reduce configuration duplication across a test
suite.
You can use each of the following as a meta-annotation in conjunction with the
xref:testing/testcontext-framework.adoc[TestContext framework].
For example, you can use each of the following as a meta-annotation in conjunction with
the xref:testing/testcontext-framework.adoc[TestContext framework].
* `@BootstrapWith`
* `@ContextConfiguration`
@@ -37,111 +38,7 @@ xref:testing/testcontext-framework.adoc[TestContext framework].
* `@EnabledIf` _(only supported on JUnit Jupiter)_
* `@DisabledIf` _(only supported on JUnit Jupiter)_
Consider the following example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
@RunWith(SpringRunner.class)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
public class OrderRepositoryTests { }
@RunWith(SpringRunner.class)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
public class UserRepositoryTests { }
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@RunWith(SpringRunner::class)
@ContextConfiguration("/app-config.xml", "/test-data-access-config.xml")
@ActiveProfiles("dev")
@Transactional
class OrderRepositoryTests { }
@RunWith(SpringRunner::class)
@ContextConfiguration("/app-config.xml", "/test-data-access-config.xml")
@ActiveProfiles("dev")
@Transactional
class UserRepositoryTests { }
----
======
If we discover that we are repeating the preceding configuration across our JUnit 4-based
test suite, we can reduce the duplication by introducing a custom composed annotation
that centralizes the common test configuration for Spring, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
public @interface TransactionalDevTestConfig { }
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@Target(AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
@ContextConfiguration("/app-config.xml", "/test-data-access-config.xml")
@ActiveProfiles("dev")
@Transactional
annotation class TransactionalDevTestConfig { }
----
======
Then we can use our custom `@TransactionalDevTestConfig` annotation to simplify the
configuration of individual JUnit 4 based test classes, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
@RunWith(SpringRunner.class)
@TransactionalDevTestConfig
public class OrderRepositoryTests { }
@RunWith(SpringRunner.class)
@TransactionalDevTestConfig
public class UserRepositoryTests { }
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@RunWith(SpringRunner::class)
@TransactionalDevTestConfig
class OrderRepositoryTests
@RunWith(SpringRunner::class)
@TransactionalDevTestConfig
class UserRepositoryTests
----
======
If we write tests that use JUnit Jupiter, we can reduce code duplication even further,
since annotations in JUnit Jupiter can also be used as meta-annotations. Consider the
following example:
Consider the following test classes that use the `SpringExtension` with JUnit Jupiter:
[tabs]
======
@@ -150,13 +47,13 @@ Java::
[source,java,indent=0,subs="verbatim,quotes"]
----
@ExtendWith(SpringExtension.class)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ContextConfiguration(classes = {AppConfig.class, TestDataAccessConfig.class})
@ActiveProfiles("dev")
@Transactional
class OrderRepositoryTests { }
@ExtendWith(SpringExtension.class)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ContextConfiguration(classes = {AppConfig.class, TestDataAccessConfig.class})
@ActiveProfiles("dev")
@Transactional
class UserRepositoryTests { }
@@ -167,23 +64,22 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@ExtendWith(SpringExtension::class)
@ContextConfiguration("/app-config.xml", "/test-data-access-config.xml")
@ContextConfiguration(classes = [AppConfig::class, TestDataAccessConfig::class])
@ActiveProfiles("dev")
@Transactional
class OrderRepositoryTests { }
@ExtendWith(SpringExtension::class)
@ContextConfiguration("/app-config.xml", "/test-data-access-config.xml")
@ContextConfiguration(classes = [AppConfig::class, TestDataAccessConfig::class])
@ActiveProfiles("dev")
@Transactional
class UserRepositoryTests { }
----
======
If we discover that we are repeating the preceding configuration across our JUnit
Jupiter-based test suite, we can reduce the duplication by introducing a custom composed
annotation that centralizes the common test configuration for Spring and JUnit Jupiter,
as follows:
If we discover that we are repeating the preceding configuration across our test suite,
we can reduce the duplication by introducing a custom composed annotation that
centralizes the common test configuration for Spring and JUnit Jupiter, as follows:
[tabs]
======
@@ -194,7 +90,7 @@ Java::
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(SpringExtension.class)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ContextConfiguration(classes = {AppConfig.class, TestDataAccessConfig.class})
@ActiveProfiles("dev")
@Transactional
public @interface TransactionalDevTestConfig { }
@@ -207,7 +103,7 @@ Kotlin::
@Target(AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
@ExtendWith(SpringExtension::class)
@ContextConfiguration("/app-config.xml", "/test-data-access-config.xml")
@ContextConfiguration(classes = [AppConfig::class, TestDataAccessConfig::class])
@ActiveProfiles("dev")
@Transactional
annotation class TransactionalDevTestConfig { }
@@ -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
@@ -4,8 +4,7 @@
`@AfterTransaction` indicates that the annotated `void` method should be run after a
transaction is ended, for test methods that have been configured to run within a
transaction by using Spring's `@Transactional` annotation. `@AfterTransaction` methods
are not required to be `public` and may be declared on Java 8-based interface default
methods.
are not required to be `public` and may be declared on interface default methods.
[tabs]
======
@@ -4,8 +4,7 @@
`@BeforeTransaction` indicates that the annotated `void` method should be run before a
transaction is started, for test methods that have been configured to run within a
transaction by using Spring's `@Transactional` annotation. `@BeforeTransaction` methods
are not required to be `public` and may be declared on Java 8-based interface default
methods.
are not required to be `public` and may be declared on interface default methods.
The following example shows how to use the `@BeforeTransaction` annotation:
@@ -140,6 +140,21 @@ Java::
}
----
<1> Replace the bean with type `CustomService` with a Mockito mock.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@SpringJUnitConfig(TestConfig::class)
class BeanOverrideTests {
@MockitoBean // <1>
lateinit var customService: CustomService
// tests...
}
----
<1> Replace the bean with type `CustomService` with a Mockito mock.
======
In the example above, we are creating a mock for `CustomService`. If more than one bean
@@ -168,6 +183,22 @@ Java::
}
----
<1> Replace the bean named `service` with a Mockito mock.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@SpringJUnitConfig(TestConfig::class)
class BeanOverrideTests {
@MockitoBean("service") // <1>
lateinit var customService: CustomService
// tests...
}
----
<1> Replace the bean named `service` with a Mockito mock.
======
The following `@SharedMocks` annotation registers two mocks by-type and one mock by-name.
@@ -187,6 +218,19 @@ Java::
----
<1> Register `OrderService` and `UserService` mocks by-type.
<2> Register `PrintingService` mock by-name.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@MockitoBean(types = [OrderService::class, UserService::class]) // <1>
@MockitoBean(name = "ps1", types = [PrintingService::class]) // <2>
annotation class SharedMocks
----
<1> Register `OrderService` and `UserService` mocks by-type.
<2> Register `PrintingService` mock by-name.
======
The following demonstrates how `@SharedMocks` can be used on a test class.
@@ -217,6 +261,34 @@ Java::
----
<1> Register common mocks via the custom `@SharedMocks` annotation.
<2> Optionally inject mocks to _stub_ or _verify_ them.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@SpringJUnitConfig(TestConfig::class)
@SharedMocks // <1>
class BeanOverrideTests {
@Autowired
lateinit var orderService: OrderService // <2>
@Autowired
lateinit var userService: UserService // <2>
@Autowired
lateinit var ps1: PrintingService // <2>
// Inject other components that rely on the mocks.
@Test
fun testThatDependsOnMocks() {
// ...
}
}
----
<1> Register common mocks via the custom `@SharedMocks` annotation.
<2> Optionally inject mocks to _stub_ or _verify_ them.
======
TIP: The mocks can also be injected into `@Configuration` classes or other test-related
@@ -246,6 +318,21 @@ Java::
}
----
<1> Wrap the bean with type `CustomService` with a Mockito spy.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@SpringJUnitConfig(TestConfig::class)
class BeanOverrideTests {
@MockitoSpyBean // <1>
lateinit var customService: CustomService
// tests...
}
----
<1> Wrap the bean with type `CustomService` with a Mockito spy.
======
In the example above, we are wrapping the bean with type `CustomService`. If more than
@@ -271,6 +358,21 @@ Java::
}
----
<1> Wrap the bean named `service` with a Mockito spy.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@SpringJUnitConfig(TestConfig::class)
class BeanOverrideTests {
@MockitoSpyBean("service") // <1>
lateinit var customService: CustomService
// tests...
}
----
<1> Wrap the bean named `service` with a Mockito spy.
======
The following `@SharedSpies` annotation registers two spies by-type and one spy by-name.
@@ -290,6 +392,19 @@ Java::
----
<1> Register `OrderService` and `UserService` spies by-type.
<2> Register `PrintingService` spy by-name.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@MockitoSpyBean(types = [OrderService::class, UserService::class]) // <1>
@MockitoSpyBean(name = "ps1", types = [PrintingService::class]) // <2>
annotation class SharedSpies
----
<1> Register `OrderService` and `UserService` spies by-type.
<2> Register `PrintingService` spy by-name.
======
The following demonstrates how `@SharedSpies` can be used on a test class.
@@ -320,6 +435,34 @@ Java::
----
<1> Register common spies via the custom `@SharedSpies` annotation.
<2> Optionally inject spies to _stub_ or _verify_ them.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@SpringJUnitConfig(TestConfig::class)
@SharedSpies // <1>
class BeanOverrideTests {
@Autowired
lateinit var orderService: OrderService // <2>
@Autowired
lateinit var userService: UserService // <2>
@Autowired
lateinit var ps1: PrintingService // <2>
// Inject other components that rely on the spies.
@Test
fun testThatDependsOnMocks() {
// ...
}
}
----
<1> Register common spies via the custom `@SharedSpies` annotation.
<2> Optionally inject spies to _stub_ or _verify_ them.
======
TIP: The spies can also be injected into `@Configuration` classes or other test-related
@@ -3,7 +3,7 @@
`@SqlGroup` is a container annotation that aggregates several `@Sql` annotations. You can
use `@SqlGroup` natively to declare several nested `@Sql` annotations, or you can use it
in conjunction with Java 8's support for repeatable annotations, where `@Sql` can be
in conjunction with Java's support for repeatable annotations, where `@Sql` can be
declared several times on the same class or method, implicitly generating this container
annotation. The following example shows how to declare an SQL group:
@@ -73,6 +73,27 @@ Java::
----
<1> Mark a field for overriding the bean with type `CustomService`.
<2> The result of this static method will be used as the instance and injected into the field.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
class OverrideBeanTests {
@TestBean // <1>
lateinit var customService: CustomService
// test case body...
companion object {
@JvmStatic
fun customService(): CustomService { // <2>
return MyFakeCustomService()
}
}
}
----
<1> Mark a field for overriding the bean with type `CustomService`.
<2> The result of this static method will be used as the instance and injected into the field.
======
In the example above, we are overriding the bean with type `CustomService`. If more than
@@ -102,6 +123,28 @@ Java::
<1> Mark a field for overriding the bean with name `service`, and specify that the
factory method is named `createCustomService`.
<2> The result of this static method will be used as the instance and injected into the field.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
class OverrideBeanTests {
@TestBean(name = "service", methodName = "createCustomService") // <1>
lateinit var customService: CustomService
// test case body...
companion object {
@JvmStatic
fun createCustomService(): CustomService { // <2>
return MyFakeCustomService()
}
}
}
----
<1> Mark a field for overriding the bean with name `service`, and specify that the
factory method is named `createCustomService`.
<2> The result of this static method will be used as the instance and injected into the field.
======
[TIP]
@@ -9,7 +9,6 @@ and can be used anywhere in the Spring Framework.
* `@Qualifier`
* `@Value`
* `@Resource` (jakarta.annotation) if JSR-250 is present
* `@ManagedBean` (jakarta.annotation) if JSR-250 is present
* `@Inject` (jakarta.inject) if JSR-330 is present
* `@Named` (jakarta.inject) if JSR-330 is present
* `@PersistenceContext` (jakarta.persistence) if JPA is present
@@ -5,24 +5,25 @@ It is important to be able to perform some integration testing without requiring
deployment to your application server or connecting to other enterprise infrastructure.
Doing so lets you test things such as:
* The correct wiring of your Spring IoC container contexts.
* Data access using JDBC or an ORM tool. This can include such things as the correctness
of SQL statements, Hibernate queries, JPA entity mappings, and so forth.
* The correct wiring of your Spring components.
* Data access using JDBC or an ORM tool.
** This can include such things as the correctness of SQL statements, Hibernate queries,
JPA entity mappings, and so forth.
The Spring Framework provides first-class support for integration testing in the
`spring-test` module. The name of the actual JAR file might include the release version
and might also be in the long `org.springframework.test` form, depending on where you get
it from (see the xref:core/beans/dependencies.adoc[section on Dependency Management]
for an explanation). This library includes the `org.springframework.test` package, which
`spring-test` module. The name of the actual JAR file might include the release version,
depending on where you get it from (see the
{spring-framework-wiki}/Spring-Framework-Artifacts[Spring Framework Artifacts] wiki page
for details). This library includes the `org.springframework.test` package, which
contains valuable classes for integration testing with a Spring container. This testing
does not rely on an application server or other deployment environment. Such tests are
slower to run than unit tests but much faster than the equivalent Selenium tests or
remote tests that rely on deployment to an application server.
Unit and integration testing support is provided in the form of the annotation-driven
xref:testing/testcontext-framework.adoc[Spring TestContext Framework]. The TestContext framework is
agnostic of the actual testing framework in use, which allows instrumentation of tests
in various environments, including JUnit, TestNG, and others.
xref:testing/testcontext-framework.adoc[Spring TestContext Framework]. The TestContext
framework is agnostic of the actual testing framework in use, which allows
instrumentation of tests in various environments, including JUnit, TestNG, and others.
The following section provides an overview of the high-level goals of Spring's
integration support, and the rest of this chapter then focuses on dedicated topics:
@@ -3,7 +3,7 @@
In the previous sections, we have seen how to use MockMvc in conjunction with the raw
HtmlUnit APIs. In this section, we use additional abstractions within the Selenium
https://docs.seleniumhq.org/projects/webdriver/[WebDriver] to make things even easier.
https://www.selenium.dev/documentation/webdriver/[WebDriver] to make things even easier.
[[mockmvc-server-htmlunit-webdriver-why]]
== Why WebDriver and MockMvc?
@@ -12,8 +12,8 @@ We can already use HtmlUnit and MockMvc, so why would we want to use WebDriver?
Selenium WebDriver provides a very elegant API that lets us easily organize our code. To
better show how it works, we explore an example in this section.
NOTE: Despite being a part of https://docs.seleniumhq.org/[Selenium], WebDriver does not
require a Selenium Server to run your tests.
NOTE: Despite being a part of https://www.selenium.dev/documentation/[Selenium],
WebDriver does not require a Selenium Server to run your tests.
Suppose we need to ensure that a message is created properly. The tests involve finding
the HTML form input elements, filling them out, and making various assertions.
@@ -308,7 +308,7 @@ interested. These are of type `WebElement`. WebDriver's
https://github.com/SeleniumHQ/selenium/wiki/PageFactory[`PageFactory`] lets us remove a
lot of code from the HtmlUnit version of `CreateMessagePage` by automatically resolving
each `WebElement`. The
https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/PageFactory.html#initElements-org.openqa.selenium.WebDriver-java.lang.Class-[`PageFactory#initElements(WebDriver,Class<T>)`]
https://www.selenium.dev/selenium/docs/api/java/org/openqa/selenium/support/PageFactory.html#initElements-org.openqa.selenium.WebDriver-java.lang.Class-[`PageFactory#initElements(WebDriver,Class<T>)`]
method automatically resolves each `WebElement` by using the field name and looking it up
by the `id` or `name` of the element within the HTML page.
<3> We can use the
@@ -352,7 +352,7 @@ interested. These are of type `WebElement`. WebDriver's
https://github.com/SeleniumHQ/selenium/wiki/PageFactory[`PageFactory`] lets us remove a
lot of code from the HtmlUnit version of `CreateMessagePage` by automatically resolving
each `WebElement`. The
https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/PageFactory.html#initElements-org.openqa.selenium.WebDriver-java.lang.Class-[`PageFactory#initElements(WebDriver,Class<T>)`]
https://www.selenium.dev/selenium/docs/api/java/org/openqa/selenium/support/PageFactory.html#initElements-org.openqa.selenium.WebDriver-java.lang.Class-[`PageFactory#initElements(WebDriver,Class<T>)`]
method automatically resolves each `WebElement` by using the field name and looking it up
by the `id` or `name` of the element within the HTML page.
<3> We can use the
@@ -11,7 +11,7 @@ https://testng.org/[TestNG] ::
testing, distributed testing, and other features. Supported in the
xref:testing/testcontext-framework.adoc[Spring TestContext Framework].
{assertj-docs}[AssertJ] ::
"Fluent assertions for Java", including support for Java 8 lambdas, streams, and
"Fluent assertions for Java", including support for lambda expressions, streams, and
numerous other features. Supported in Spring's
xref:testing/mockmvc/assertj.adoc[MockMvc testing support].
https://en.wikipedia.org/wiki/Mock_Object[Mock Objects] ::
@@ -10,7 +10,7 @@ applications without a running server via MockMvc.
[[resttestclient-setup]]
[[resttestclient.setup]]
== Setup
To set up a `RestTestClient` you need to choose a server setup to bind to. This can be one
@@ -18,7 +18,7 @@ of several MockMvc setup choices, or a connection to a live server.
[[resttestclient-controller-config]]
[[resttestclient.controller-config]]
=== Bind to Controller
This setup allows you to test specific controller(s) via mock request and response objects,
@@ -42,55 +42,17 @@ Kotlin::
----
======
[[resttestclient-context-config]]
[[resttestclient.context-config]]
=== Bind to `ApplicationContext`
This setup allows you to load Spring configuration with Spring MVC
infrastructure and controller declarations and use it to handle requests via mock request
and response objects, without a running server.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
@SpringJUnitConfig(WebConfig.class) // <1>
class MyTests {
include-code::./RestClientContextTests[indent=0]
RestTestClient client;
@BeforeEach
void setUp(ApplicationContext context) { // <2>
client = RestTestClient.bindToApplicationContext(context).build(); // <3>
}
}
----
<1> Specify the configuration to load
<2> Inject the configuration
<3> Create the `RestTestClient`
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@SpringJUnitConfig(WebConfig::class) // <1>
class MyTests {
lateinit var client: RestTestClient
@BeforeEach
fun setUp(context: ApplicationContext) { // <2>
client = RestTestClient.bindToApplicationContext(context).build() // <3>
}
}
----
<1> Specify the configuration to load
<2> Inject the configuration
<3> Create the `RestTestClient`
======
[[resttestclient-fn-config]]
[[resttestclient.fn-config]]
=== Bind to Router Function
This setup allows you to test xref:web/webmvc-functional.adoc[functional endpoints] via
@@ -115,7 +77,7 @@ Kotlin::
----
======
[[resttestclient-server-config]]
[[resttestclient.server-config]]
=== Bind to Server
This setup connects to a running server to perform full, end-to-end HTTP tests:
@@ -139,7 +101,7 @@ Kotlin::
[[resttestclient-client-config]]
[[resttestclient.client-config]]
=== Client Config
In addition to the server setup options described earlier, you can also configure client
@@ -170,7 +132,7 @@ Kotlin::
[[resttestclient-tests]]
[[resttestclient.tests]]
== Writing Tests
xref:integration/rest-clients.adoc#rest-restclient[`RestClient`] and `RestTestClient` have
@@ -182,70 +144,22 @@ provides two alternative ways to verify the response:
[[resttestclient-workflow]]
[[resttestclient.workflow]]
=== Built-in Assertions
To use the built-in assertions, remain in the workflow after the call to `exchange()`, and
use one of the expectation methods. For example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
client.get().uri("/persons/1")
.accept(MediaType.APPLICATION_JSON)
.exchange()
.expectStatus().isOk()
.expectHeader().contentType(MediaType.APPLICATION_JSON);
----
include-code::./RestClientWorkflowTests[tag=test,indent=0]
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
client.get().uri("/persons/1")
.accept(MediaType.APPLICATION_JSON)
.exchange()
.expectStatus().isOk()
.expectHeader().contentType(MediaType.APPLICATION_JSON)
----
======
If you would like for all expectations to be asserted even if one of them fails, you can
use `expectAll(..)` instead of multiple chained `expect*(..)` calls. This feature is
similar to the _soft assertions_ support in AssertJ and the `assertAll()` support in
JUnit Jupiter.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
client.get().uri("/persons/1")
.accept(MediaType.APPLICATION_JSON)
.exchange()
.expectAll(
spec -> spec.expectStatus().isOk(),
spec -> spec.expectHeader().contentType(MediaType.APPLICATION_JSON)
);
----
include-code::./RestClientWorkflowTests[tag=soft-assertions,indent=0]
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
client.get().uri("/persons/1")
.accept(MediaType.APPLICATION_JSON)
.exchange()
.expectAll(
{ spec -> spec.expectStatus().isOk() },
{ spec -> spec.expectHeader().contentType(MediaType.APPLICATION_JSON) }
)
----
======
You can then choose to decode the response body through one of the following:
@@ -256,124 +170,34 @@ You can then choose to decode the response body through one of the following:
If the built-in assertions are insufficient, you can consume the object instead and
perform any other assertions:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
client.get().uri("/persons/1")
.exchange()
.expectStatus().isOk()
.expectBody(Person.class)
.consumeWith(result -> {
// custom assertions (for example, AssertJ)...
});
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
client.get().uri("/persons/1")
.exchange()
.expectStatus().isOk()
.expectBody<Person>()
.consumeWith {
// custom assertions (for example, AssertJ)...
}
----
======
include-code::./RestClientWorkflowTests[tag=consume,indent=0]
Or you can exit the workflow and obtain a `EntityExchangeResult`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
EntityExchangeResult<Person> result = client.get().uri("/persons/1")
.exchange()
.expectStatus().isOk()
.expectBody(Person.class)
.returnResult();
----
include-code::./RestClientWorkflowTests[tag=result,indent=0]
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
val result = client.get().uri("/persons/1")
.exchange()
.expectStatus().isOk
.expectBody<Person>()
.returnResult()
----
======
TIP: When you need to decode to a target type with generics, look for the overloaded methods
that accept {spring-framework-api}/core/ParameterizedTypeReference.html[`ParameterizedTypeReference`]
instead of `Class<T>`.
[[resttestclient-no-content]]
[[resttestclient.no-content]]
==== No Content
If the response is not expected to have content, you can assert that as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
client.post().uri("/persons")
.body(person)
.exchange()
.expectStatus().isCreated()
.expectBody().isEmpty();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
client.post().uri("/persons")
.body(person)
.exchange()
.expectStatus().isCreated()
.expectBody().isEmpty()
----
======
include-code::./NoContentTests[tag=emptyBody,indent=0]
If you want to ignore the response content, the following releases the content without any assertions:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
client.get().uri("/persons/123")
.exchange()
.expectStatus().isNotFound()
.expectBody(Void.class);
----
include-code::./NoContentTests[tag=ignoreBody,indent=0]
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
client.get().uri("/persons/123")
.exchange()
.expectStatus().isNotFound
.expectBody<Unit>()
----
======
NOTE: Consuming the response body (for example, with `expectBody`) is required if your tests are running with
leak detection for pooled buffers. Without that, the tool will report buffers being leaked.
[[resttestclient-json]]
[[resttestclient.json]]
==== JSON Content
You can use `expectBody()` without a target type to perform assertions on the raw
@@ -381,126 +205,25 @@ content rather than through higher level Object(s).
To verify the full JSON content with https://jsonassert.skyscreamer.org[JSONAssert]:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
client.get().uri("/persons/1")
.exchange()
.expectStatus().isOk()
.expectBody()
.json("{\"name\":\"Jane\"}")
----
include-code::./JsonTests[tag=jsonBody,indent=0]
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
client.get().uri("/persons/1")
.exchange()
.expectStatus().isOk()
.expectBody()
.json("{\"name\":\"Jane\"}")
----
======
To verify JSON content with https://github.com/jayway/JsonPath[JSONPath]:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
client.get().uri("/persons")
.exchange()
.expectStatus().isOk()
.expectBody()
.jsonPath("$[0].name").isEqualTo("Jane")
.jsonPath("$[1].name").isEqualTo("Jason");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
client.get().uri("/persons")
.exchange()
.expectStatus().isOk()
.expectBody()
.jsonPath("$[0].name").isEqualTo("Jane")
.jsonPath("$[1].name").isEqualTo("Jason")
----
======
include-code::./JsonTests[tag=jsonPath,indent=0]
[[resttestclient-assertj]]
[[resttestclient.assertj]]
=== AssertJ Integration
`RestTestClientResponse` is the main entry point for the AssertJ integration.
It is an `AssertProvider` that wraps the `ResponseSpec` of an exchange in order to enable
use of `assertThat()` statements. For example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
ResponseSpec spec = client.get().uri("/persons").exchange();
include-code::./AssertJTests[tag=withSpec,indent=0]
RestTestClientResponse response = RestTestClientResponse.from(spec);
assertThat(response).hasStatusOk();
assertThat(response).hasContentTypeCompatibleWith(MediaType.TEXT_PLAIN);
// ...
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
val spec = client.get().uri("/persons").exchange()
val response = RestTestClientResponse.from(spec)
assertThat(response).hasStatusOk()
assertThat(response).hasContentTypeCompatibleWith(MediaType.TEXT_PLAIN)
// ...
----
======
You can also use the built-in workflow first, and then obtain an `ExchangeResult` to wrap
and continue with AssertJ. For example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
ExchangeResult result = client.get().uri("/persons").exchange()
. // ...
.returnResult();
RestTestClientResponse response = RestTestClientResponse.from(result);
assertThat(response).hasStatusOk();
assertThat(response).hasContentTypeCompatibleWith(MediaType.TEXT_PLAIN);
// ...
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
val result = client.get().uri("/persons").exchange()
. // ...
.returnResult()
val response = RestTestClientResponse.from(spec)
assertThat(response).hasStatusOk()
assertThat(response).hasContentTypeCompatibleWith(MediaType.TEXT_PLAIN)
// ...
----
======
include-code::./AssertJTests[tag=withResult,indent=0]
@@ -19,6 +19,6 @@ meta-annotation. If a bootstrapper is not explicitly configured by using
`WebTestContextBootstrapper` is used, depending on the presence of `@WebAppConfiguration`.
Since the `TestContextBootstrapper` SPI is likely to change in the future (to accommodate
new requirements), we strongly encourage implementers not to implement this interface
new requirements), we strongly encourage implementors not to implement this interface
directly but rather to extend `AbstractTestContextBootstrapper` or one of its concrete
subclasses instead.
@@ -49,7 +49,6 @@ Kotlin::
<1> Injecting the `ApplicationContext`.
======
Similarly, if your test is configured to load a `WebApplicationContext`, you can inject
the web application context into your test, as follows:
@@ -87,7 +86,6 @@ Kotlin::
<2> Injecting the `WebApplicationContext`.
======
Dependency injection by using `@Autowired` is provided by the
`DependencyInjectionTestExecutionListener`, which is configured by default
(see xref:testing/testcontext-framework/fixture-di.adoc[Dependency Injection of Test Fixtures]).
@@ -96,22 +94,24 @@ Dependency injection by using `@Autowired` is provided by the
Test classes that use the TestContext framework do not need to extend any particular
class or implement a specific interface to configure their application context. Instead,
configuration is achieved by declaring the `@ContextConfiguration` annotation at the
class level. If your test class does not explicitly declare application context resource
locations or component classes, the configured `ContextLoader` determines how to load a
context from a default location or default configuration classes. In addition to context
resource locations and component classes, an application context can also be configured
through application context initializers.
class level. If your test class does not explicitly declare component classes or resource
locations, the configured `ContextLoader` determines how to load a context from _default_
configuration classes or a _default_ location. In addition to component classes and
context resource locations, an application context can also be configured through
xref:testing/testcontext-framework/ctx-management/context-customizers.adoc[context customizers]
or xref:testing/testcontext-framework/ctx-management/initializers.adoc[context initializers].
The following sections explain how to use Spring's `@ContextConfiguration` annotation to
configure a test `ApplicationContext` by using XML configuration files, Groovy scripts,
component classes (typically `@Configuration` classes), or context initializers.
Alternatively, you can implement and configure your own custom `SmartContextLoader` for
advanced use cases.
The following sections explain how to use `@ContextConfiguration` and related annotations
to configure a test `ApplicationContext` by using component classes (typically
`@Configuration` classes), XML configuration files, Groovy scripts, context customizers,
or context initializers. Alternatively, you can implement and configure your own custom
`SmartContextLoader` for advanced use cases.
* xref:testing/testcontext-framework/ctx-management/xml.adoc[Context Configuration with XML resources]
* xref:testing/testcontext-framework/ctx-management/groovy.adoc[Context Configuration with Groovy Scripts]
* xref:testing/testcontext-framework/ctx-management/javaconfig.adoc[Context Configuration with Component Classes]
* xref:testing/testcontext-framework/ctx-management/mixed-config.adoc[Mixing XML, Groovy Scripts, and Component Classes]
* xref:testing/testcontext-framework/ctx-management/xml.adoc[Context Configuration with XML Resources]
* xref:testing/testcontext-framework/ctx-management/groovy.adoc[Context Configuration with Groovy Scripts]
* xref:testing/testcontext-framework/ctx-management/default-config.adoc[Default Context Configuration]
* xref:testing/testcontext-framework/ctx-management/mixed-config.adoc[Mixing Component Classes, XML, and Groovy Scripts]
* xref:testing/testcontext-framework/ctx-management/context-customizers.adoc[Context Configuration with Context Customizers]
* xref:testing/testcontext-framework/ctx-management/initializers.adoc[Context Configuration with Context Initializers]
* xref:testing/testcontext-framework/ctx-management/inheritance.adoc[Context Configuration Inheritance]
@@ -62,17 +62,6 @@ script by setting a JVM system property named `spring.test.context.cache.maxSize
alternative, you can set the same property via the
xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism.
As of Spring Framework 7.0, an application context stored in the context cache will be
_paused_ when it is no longer actively in use and automatically _restarted_ the next time
the context is retrieved from the cache. Specifically, the latter will restart all
auto-startup beans in the application context, effectively restoring the lifecycle state.
This ensures that background processes within the context are not actively running while
the context is not used by tests. For example, JMS listener containers, scheduled tasks,
and any other components in the context that implement `Lifecycle` or `SmartLifecycle`
will be in a "stopped" state until the context is used again by a test. Note, however,
that `SmartLifecycle` components can opt out of pausing by returning `false` from
`SmartLifecycle#isPauseable()`.
Since having a large number of application contexts loaded within a given test suite can
cause the suite to take an unnecessarily long time to run, it is often beneficial to
know exactly how many contexts have been loaded and cached. To view the statistics for
@@ -0,0 +1,45 @@
[[testcontext-ctx-management-pausing]]
= Context Pausing
As of Spring Framework 7.0, an `ApplicationContext` stored in the context cache (see
xref:testing/testcontext-framework/ctx-management/caching.adoc[Context Caching]) may be
_paused_ when it is no longer actively in use and automatically _restarted_ the next time
the context is retrieved from the cache. Specifically, the latter will restart all
auto-startup beans in the application context, effectively restoring the lifecycle state.
This ensures that background processes within the context are not actively running while
the context is not used by tests. For example, JMS listener containers, scheduled tasks,
and any other components in the context that implement `Lifecycle` or `SmartLifecycle`
will be in a "stopped" state until the context is used again by a test. Note, however,
that `SmartLifecycle` components can opt out of pausing by returning `false` from
`SmartLifecycle#isPauseable()`.
You can control whether inactive application contexts should be paused by setting the
`PauseMode` to one of the following supported values.
`ALWAYS` :: Always pause inactive application contexts.
`ON_CONTEXT_SWITCH` :: Only pause inactive application contexts if the next context
retrieved from the context cache is a different context.
`NEVER` :: Never pause inactive application contexts, effectively disabling the pausing
feature of the context cache.
The `PauseMode` defaults to `ON_CONTEXT_SWITCH`, but it can be changed from the command
line or a build script by setting a JVM system property named
`spring.test.context.cache.pause` to one of the supported values (case insensitive). As
an alternative, you can set the property via the
xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism.
For example, if you want inactive application contexts to always be paused, you can
switch from the default `ON_CONTEXT_SWITCH` mode to `ALWAYS` by setting the
`spring.test.context.cache.pause` system property to `always`.
```shell
-Dspring.test.context.cache.pause=always
```
Similarly, if you encounter issues with `Lifecycle` components that cannot or should not
opt out of pausing, or if you discover that your test suite runs more slowly due to the
pausing and restarting of application contexts, you can disable the pausing feature by
setting the `spring.test.context.cache.pause` system property to `never`.
```shell
-Dspring.test.context.cache.pause=never
```
@@ -0,0 +1,53 @@
[[testcontext-ctx-management-default-config]]
= Default Context Configuration
As explained in the sections on
xref:testing/testcontext-framework/ctx-management/javaconfig.adoc[component classes],
xref:testing/testcontext-framework/ctx-management/xml.adoc[XML resources], and
xref:testing/testcontext-framework/ctx-management/groovy.adoc[Groovy scripts], the
TestContext framework will attempt to locate _default_ context configuration if you do
not explicitly specify `@Configuration` classes, XML configuration files, or Groovy
scripts from which the test's `ApplicationContext` should be loaded.
However, due to a bug in the detection algorithm, default context configuration for a
superclass or enclosing class is currently ignored if the type hierarchy or enclosing
class hierarchy (for `@Nested` test classes) does not declare `@ContextConfiguration`.
Beginning with Spring Framework 7.1, the TestContext framework will reliably detect
**all** _default_ context configuration within a type hierarchy or enclosing class
hierarchy above a given test class in such scenarios. Consequently, test suites may
encounter issues after the upgrade to 7.1. For example, if a static nested
`@Configuration` class in a superclass or enclosing class is ignored due to the
aforementioned bug, after the bug has been fixed in 7.1 that `@Configuration` class will
no longer be ignored, which may lead to unexpected beans in the resulting
`ApplicationContext` our outright failures in tests.
In the interim, the TestContext framework logs a warning whenever it encounters _default_
context configuration that is currently ignored — for example, a `@Configuration` class
or XML configuration file. The remainder of this section provides guidance on how to
address such issues if you encounter warnings in your test suite.
[TIP]
====
Annotating the affected subclass or `@Nested` class with `@ContextConfiguration` allows
you to take matters into your own hands and specify which classes in the hierarchy are
actually intended to contribute context configuration.
====
If you do not want static nested `@Configuration` classes to be processed, you can:
- Remove the `@Configuration` declaration.
- Apply `@ContextConfiguration` only where you actually want such classes to be processed.
- Move the static nested `@Configuration` classes to standalone top-level classes so that
they cannot be accidentally interpreted as _default_ configuration classes.
Similarly, if you encounter issues with _default_ XML configuration files or Groovy
scripts being detected and you do not want them to be processed, you can:
- Apply `@ContextConfiguration` only where you actually want such resources to be
processed.
- Rename the resource files to something that does not match the default naming
convention (such as `*-context.xml` for XML configuration) so that they cannot be
accidentally interpreted as _default_ configuration files.
- Move the affected resource files to a different package or filesystem location within
your project.
@@ -543,3 +543,85 @@ Kotlin::
----
======
The following example demonstrates how to implement and register a custom
`SystemPropertyOverrideActiveProfilesResolver` that allows the `spring.profiles.active`
property (when configured as a JVM system property) to override profiles configured via
`@ActiveProfiles`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
// profiles resolved programmatically via a custom resolver that
// allows "spring.profiles.active" to override @ActiveProfiles
@ActiveProfiles(
resolver = SystemPropertyOverrideActiveProfilesResolver.class,
inheritProfiles = false)
class TransferServiceTest extends AbstractIntegrationTest {
// test body
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
// profiles resolved programmatically via a custom resolver that
// allows "spring.profiles.active" to override @ActiveProfiles
@ActiveProfiles(
resolver = SystemPropertyOverrideActiveProfilesResolver::class,
inheritProfiles = false)
class TransferServiceTest : AbstractIntegrationTest() {
// test body
}
----
======
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",fold="-imports"]
----
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.test.context.support.DefaultActiveProfilesResolver;
import org.springframework.util.StringUtils;
public class SystemPropertyOverrideActiveProfilesResolver extends DefaultActiveProfilesResolver {
@Override
public String[] resolve(Class<?> testClass) {
String profiles = System.getProperty(AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME);
if (StringUtils.hasText(profiles)) {
return StringUtils.commaDelimitedListToStringArray(
StringUtils.trimAllWhitespace(profiles));
}
return super.resolve(testClass);
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",fold="-imports"]
----
import org.springframework.core.env.AbstractEnvironment
import org.springframework.test.context.support.DefaultActiveProfilesResolver
import org.springframework.util.StringUtils
class SystemPropertyOverrideActiveProfilesResolver : DefaultActiveProfilesResolver() {
override fun resolve(testClass: Class<*>): Array<String> {
val profiles = System.getProperty(AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME)
if (StringUtils.hasText(profiles)) {
return StringUtils.commaDelimitedListToStringArray(
StringUtils.trimAllWhitespace(profiles)
)
}
return super.resolve(testClass)
}
}
----
======
@@ -1,33 +1,32 @@
[[testcontext-ctx-management-mixed-config]]
= Mixing XML, Groovy Scripts, and Component Classes
= Mixing Component Classes, XML, and Groovy Scripts
It may sometimes be desirable to mix XML configuration files, Groovy scripts, and
component classes (typically `@Configuration` classes) to configure an
`ApplicationContext` for your tests. For example, if you use XML configuration in
production, you may decide that you want to use `@Configuration` classes to configure
It may sometimes be desirable to mix component classes (typically `@Configuration`
classes), XML configuration files, or Groovy scripts to configure an `ApplicationContext`
for your tests. For example, if you use XML configuration in production for legacy
reasons, you may decide that you want to use `@Configuration` classes to configure
specific Spring-managed components for your tests, or vice versa.
Furthermore, some third-party frameworks (such as Spring Boot) provide first-class
support for loading an `ApplicationContext` from different types of resources
simultaneously (for example, XML configuration files, Groovy scripts, and
`@Configuration` classes). The Spring Framework, historically, has not supported this for
standard deployments. Consequently, most of the `SmartContextLoader` implementations that
the Spring Framework delivers in the `spring-test` module support only one resource type
for each test context. However, this does not mean that you cannot use both. One
exception to the general rule is that the `GenericGroovyXmlContextLoader` and
simultaneously (for example, `@Configuration` classes, XML configuration files, and
Groovy scripts). The Spring Framework, historically, has not supported this for standard
deployments. Consequently, most of the `SmartContextLoader` implementations that the
Spring Framework delivers in the `spring-test` module support only one resource type for
each test context. However, this does not mean that you cannot use a mixture of resource
types. One exception to the general rule is that the `GenericGroovyXmlContextLoader` and
`GenericGroovyXmlWebContextLoader` support both XML configuration files and Groovy
scripts simultaneously. Furthermore, third-party frameworks may choose to support the
declaration of both `locations` and `classes` through `@ContextConfiguration`, and, with
declaration of both `classes` and `locations` through `@ContextConfiguration`, and, with
the standard testing support in the TestContext framework, you have the following options.
If you want to use resource locations (for example, XML or Groovy) and `@Configuration`
classes to configure your tests, you must pick one as the entry point, and that one must
include or import the other. For example, in XML or Groovy scripts, you can include
`@Configuration` classes by using component scanning or defining them as normal Spring
beans, whereas, in a `@Configuration` class, you can use `@ImportResource` to import XML
configuration files or Groovy scripts. Note that this behavior is semantically equivalent
If you want to use `@Configuration` classes and resource locations (for example, XML or
Groovy) to configure your tests, you must pick one as the entry point, and that one must
import or include the other. For example, in a `@Configuration` class, you can use
`@ImportResource` to import XML configuration files or Groovy scripts; whereas, in XML or
Groovy scripts, you can include `@Configuration` classes by using component scanning or
defining them as normal Spring beans. Note that this behavior is semantically equivalent
to how you configure your application in production: In production configuration, you
define either a set of XML or Groovy resource locations or a set of `@Configuration`
classes from which your production `ApplicationContext` is loaded, but you still have the
freedom to include or import the other type of configuration.
define either a set of `@Configuration` classes or a set of XML or Groovy resource
locations from which your production `ApplicationContext` is loaded, but you still have
the freedom to import or include the other type of configuration.
@@ -1,5 +1,5 @@
[[testcontext-ctx-management-xml]]
= Context Configuration with XML resources
= Context Configuration with XML Resources
To load an `ApplicationContext` for your tests by using XML configuration files, annotate
your test class with `@ContextConfiguration` and configure the `locations` attribute with
@@ -8,12 +8,15 @@ in JUnit and TestNG.
[[testcontext-junit-jupiter-extension]]
== SpringExtension for JUnit Jupiter
The Spring TestContext Framework offers full integration with the JUnit Jupiter testing
framework, originally introduced in JUnit 5. By annotating test classes with
`@ExtendWith(SpringExtension.class)`, you can implement standard JUnit Jupiter-based unit
and integration tests and simultaneously reap the benefits of the TestContext framework,
such as support for loading application contexts, dependency injection of test instances,
transactional test method execution, and so on.
The `SpringExtension` integrates the Spring TestContext Framework into the JUnit Jupiter
testing framework.
NOTE: As of Spring Framework 7.0, the `SpringExtension` requires JUnit Jupiter 6.0 or higher.
By annotating test classes with `@ExtendWith(SpringExtension.class)`, you can implement
standard JUnit Jupiter-based unit and integration tests and simultaneously reap the
benefits of the TestContext framework, such as support for loading application contexts,
dependency injection of test instances, transactional test method execution, and so on.
Furthermore, thanks to the rich extension API in JUnit Jupiter, Spring provides the
following features above and beyond the feature set that Spring supports for JUnit 4 and
@@ -22,7 +25,7 @@ TestNG:
* Dependency injection for test constructors, test methods, and test lifecycle callback
methods. See xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit-jupiter-di[Dependency
Injection with the `SpringExtension`] for further details.
* Powerful support for link:https://junit.org/junit5/docs/current/user-guide/#extensions-conditions[conditional
* Powerful support for link:https://docs.junit.org/current/extensions/conditional-test-execution.html[conditional
test execution] based on SpEL expressions, environment variables, system properties,
and so on. See the documentation for `@EnabledIf` and `@DisabledIf` in
xref:testing/annotations/integration-junit-jupiter.adoc[Spring JUnit Jupiter Testing Annotations]
@@ -160,7 +163,7 @@ for further details.
=== Dependency Injection with the `SpringExtension`
The `SpringExtension` implements the
link:https://junit.org/junit5/docs/current/user-guide/#extensions-parameter-resolution[`ParameterResolver`]
link:https://docs.junit.org/current/extensions/parameter-resolution.html[`ParameterResolver`]
extension API from JUnit Jupiter, which lets Spring provide dependency injection for test
constructors, test methods, and test lifecycle callback methods.
@@ -389,6 +392,16 @@ any of its subclasses and nested classes. Thus, you may annotate a top-level tes
with `@NestedTestConfiguration`, and that will apply to all of its nested test classes
recursively.
[NOTE]
====
As of Spring Framework 7.0, the `SpringExtension` uses a test-method scoped
`ExtensionContext` within `@Nested` test class hierarchies by default. However, the
`SpringExtension` can be configured to use a test-class scoped `ExtensionContext` — for
example via `@SpringExtensionConfig` or the `spring.test.extension.context.scope` Spring
property (see
xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-springextensionconfig[`@SpringExtensionConfig`]).
====
[TIP]
====
If you are developing a component that integrates with the Spring TestContext Framework
@@ -362,7 +362,7 @@ of `PlatformTransactionManager` within the test's `ApplicationContext`, you can
qualifier by using `@Transactional("myTxMgr")` or `@Transactional(transactionManager =
"myTxMgr")`, or `TransactionManagementConfigurer` can be implemented by an
`@Configuration` class. Consult the
{spring-framework-api}/test/context/transaction/TestContextTransactionUtils.html#retrieveTransactionManager-org.springframework.test.context.TestContext-java.lang.String-[javadoc
{spring-framework-api}/test/context/transaction/TestContextTransactionUtils.html#retrieveTransactionManager(org.springframework.test.context.TestContext,java.lang.String)[javadoc
for `TestContextTransactionUtils.retrieveTransactionManager()`] for details on the
algorithm used to look up a transaction manager in the test's `ApplicationContext`.
@@ -75,7 +75,7 @@ infrastructure and controller declarations and use it to handle requests via moc
and response objects, without a running server.
For WebFlux, use the following where the Spring `ApplicationContext` is passed to
{spring-framework-api}/web/server/adapter/WebHttpHandlerBuilder.html#applicationContext-org.springframework.context.ApplicationContext-[WebHttpHandlerBuilder]
{spring-framework-api}/web/server/adapter/WebHttpHandlerBuilder.html#applicationContext(org.springframework.context.ApplicationContext)[WebHttpHandlerBuilder]
to create the xref:web/webflux/reactive-spring.adoc#webflux-web-handler-api[WebHandler chain] to handle
requests:
@@ -1,186 +0,0 @@
[[web-integration]]
= Other Web Frameworks
This chapter details Spring's integration with third-party web frameworks.
One of the core value propositions of the Spring Framework is that of enabling
_choice_. In a general sense, Spring does not force you to use or buy into any
particular architecture, technology, or methodology (although it certainly recommends
some over others). This freedom to pick and choose the architecture, technology, or
methodology that is most relevant to a developer and their development team is
arguably most evident in the web area, where Spring provides its own web frameworks
(xref:web/webmvc.adoc#mvc[Spring MVC] and xref:web/webflux.adoc#webflux[Spring WebFlux])
while, at the same time, supporting integration with a number of popular third-party
web frameworks.
[[web-integration-common]]
== Common Configuration
Before diving into the integration specifics of each supported web framework, let us
first take a look at common Spring configuration that is not specific to any one web
framework. (This section is equally applicable to Spring's own web framework variants.)
One of the concepts (for want of a better word) espoused by Spring's lightweight
application model is that of a layered architecture. Remember that in a "classic"
layered architecture, the web layer is but one of many layers. It serves as one of the
entry points into a server-side application, and it delegates to service objects
(facades) that are defined in a service layer to satisfy business-specific (and
presentation-technology agnostic) use cases. In Spring, these service objects, any other
business-specific objects, data-access objects, and others exist in a distinct "business
context", which contains no web or presentation layer objects (presentation objects,
such as Spring MVC controllers, are typically configured in a distinct "presentation
context"). This section details how you can configure a Spring container (a
`WebApplicationContext`) that contains all of the 'business beans' in your application.
Moving on to specifics, all you need to do is declare a
{spring-framework-api}/web/context/ContextLoaderListener.html[`ContextLoaderListener`]
in the standard Jakarta EE servlet `web.xml` file of your web application and add a
`contextConfigLocation` `<context-param/>` section (in the same file) that defines which
set of Spring XML configuration files to load.
Consider the following `<listener/>` configuration:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
----
Further consider the following `<context-param/>` configuration:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext*.xml</param-value>
</context-param>
----
If you do not specify the `contextConfigLocation` context parameter, the
`ContextLoaderListener` looks for a file called `/WEB-INF/applicationContext.xml` to
load. Once the context files are loaded, Spring creates a
{spring-framework-api}/web/context/WebApplicationContext.html[`WebApplicationContext`]
object based on the bean definitions and stores it in the `ServletContext` of the web
application.
All Java web frameworks are built on top of the Servlet API, so you can use the
following code snippet to get access to this "business context" `ApplicationContext`
created by the `ContextLoaderListener`.
The following example shows how to get the `WebApplicationContext`:
[source,java,indent=0,subs="verbatim,quotes"]
----
WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext);
----
The
{spring-framework-api}/web/context/support/WebApplicationContextUtils.html[`WebApplicationContextUtils`]
class is for convenience, so you need not remember the name of the `ServletContext`
attribute. Its `getWebApplicationContext()` method returns `null` if an object
does not exist under the `WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE`
key. Rather than risk getting `NullPointerExceptions` in your application, it is better
to use the `getRequiredWebApplicationContext()` method. This method throws an exception
when the `ApplicationContext` is missing.
Once you have a reference to the `WebApplicationContext`, you can retrieve beans by their
name or type. Most developers retrieve beans by name and then cast them to one of their
implemented interfaces.
Fortunately, most of the frameworks in this section have simpler ways of looking up beans.
Not only do they make it easy to get beans from a Spring container, but they also let you
use dependency injection on their controllers. Each web framework section has more detail
on its specific integration strategies.
[[jsf]]
== JSF
JavaServer Faces (JSF) is the JCP's standard component-based, event-driven web
user interface framework. It is an official part of the Jakarta EE umbrella but also
individually usable, for example, through embedding Mojarra or MyFaces within Tomcat.
Please note that recent versions of JSF became closely tied to CDI infrastructure
in application servers, with some new JSF functionality only working in such an
environment. Spring's JSF support is not actively evolved anymore and primarily
exists for migration purposes when modernizing older JSF-based applications.
The key element in Spring's JSF integration is the JSF `ELResolver` mechanism.
[[jsf-springbeanfaceselresolver]]
=== Spring Bean Resolver
`SpringBeanFacesELResolver` is a JSF compliant `ELResolver` implementation,
integrating with the standard Unified EL as used by JSF and JSP. It delegates to
Spring's "business context" `WebApplicationContext` first and then to the
default resolver of the underlying JSF implementation.
Configuration-wise, you can define `SpringBeanFacesELResolver` in your JSF
`faces-context.xml` file, as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<faces-config>
<application>
<el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
...
</application>
</faces-config>
----
[[jsf-facescontextutils]]
=== Using `FacesContextUtils`
A custom `ELResolver` works well when mapping your properties to beans in
`faces-config.xml`, but, at times, you may need to explicitly grab a bean.
The {spring-framework-api}/web/jsf/FacesContextUtils.html[`FacesContextUtils`]
class makes this easy. It is similar to `WebApplicationContextUtils`, except that
it takes a `FacesContext` parameter rather than a `ServletContext` parameter.
The following example shows how to use `FacesContextUtils`:
[source,java,indent=0,subs="verbatim,quotes"]
----
ApplicationContext ctx = FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance());
----
[[struts]]
== Apache Struts
Invented by Craig McClanahan, https://struts.apache.org[Struts] is an open-source project
hosted by the Apache Software Foundation. Struts 1.x greatly simplified the
JSP/Servlet programming paradigm and won over many developers who were using proprietary
frameworks. It simplified the programming model; it was open source; and it had a large
community, which let the project grow and become popular among Java web developers.
As a successor to the original Struts 1.x, check out Struts 2.x or more recent versions
as well as the Struts-provided
https://struts.apache.org/plugins/spring/[Spring Plugin] for built-in Spring integration.
[[tapestry]]
== Apache Tapestry
https://tapestry.apache.org/[Tapestry] is a "Component oriented framework for creating
dynamic, robust, highly scalable web applications in Java."
While Spring has its own xref:web/webmvc.adoc#mvc[powerful web layer], there are a number of unique
advantages to building an enterprise Java application by using a combination of Tapestry
for the web user interface and the Spring container for the lower layers.
For more information, see Tapestry's dedicated
https://tapestry.apache.org/integrating-with-spring-framework.html[integration module for Spring].
[[web-integration-resources]]
== Further Resources
The following links go to further resources about the various web frameworks described in
this chapter.
* The https://www.oracle.com/java/technologies/javaserverfaces.html[JSF] homepage
* The https://struts.apache.org/[Struts] homepage
* The https://tapestry.apache.org/[Tapestry] homepage
@@ -39,12 +39,12 @@ required CORS response headers set.
In order to enable cross-origin requests (that is, the `Origin` header is present and
differs from the host of the request), you need to have some explicitly declared CORS
configuration. If no matching CORS configuration is found, preflight requests are
rejected. No CORS headers are added to the responses of simple and actual CORS requests
and, consequently, browsers reject them.
configuration. If no matching CORS configuration is found, no CORS headers are added to
the responses to preflight, simple and actual CORS requests and, consequently, browsers
reject them.
Each `HandlerMapping` can be
{spring-framework-api}/web/reactive/handler/AbstractHandlerMapping.html#setCorsConfigurations-java.util.Map-[configured]
{spring-framework-api}/web/reactive/handler/AbstractHandlerMapping.html#setCorsConfigurations(java.util.Map)[configured]
individually with URL pattern-based `CorsConfiguration` mappings. In most cases, applications
use the WebFlux Java configuration to declare such mappings, which results in a single,
global map passed to all `HandlerMapping` implementations.
@@ -57,7 +57,7 @@ class- or method-level `@CrossOrigin` annotations (other handlers can implement
The rules for combining global and local configuration are generally additive -- for example,
all global and all local origins. For those attributes where only a single value can be
accepted, such as `allowCredentials` and `maxAge`, the local overrides the global value. See
{spring-framework-api}/web/cors/CorsConfiguration.html#combine-org.springframework.web.cors.CorsConfiguration-[`CorsConfiguration#combine(CorsConfiguration)`]
{spring-framework-api}/web/cors/CorsConfiguration.html#combine(org.springframework.web.cors.CorsConfiguration)[`CorsConfiguration#combine(CorsConfiguration)`]
for more details.
[TIP]
@@ -305,7 +305,6 @@ Java::
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
@@ -328,7 +327,6 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
override fun addCorsMappings(registry: CorsRegistry) {
@@ -13,9 +13,9 @@ the same xref:web/webflux/reactive-spring.adoc[Reactive Core] foundation.
== Overview
[.small]#xref:web/webmvc-functional.adoc#webmvc-fn-overview[See equivalent in the Servlet stack]#
In WebFlux.fn, an HTTP request is handled with a `HandlerFunction`: a function that takes
In WebFlux.fn, an HTTP request is handled with a `HandlerFunction`: a function that takes a
`ServerRequest` and returns a delayed `ServerResponse` (i.e. `Mono<ServerResponse>`).
Both the request and the response object have immutable contracts that offer JDK 8-friendly
Both the request and the response object have immutable contracts that offer convenient
access to the HTTP request and response.
`HandlerFunction` is the equivalent of the body of a `@RequestMapping` method in the
annotation-based programming model.
@@ -117,13 +117,13 @@ Most applications can run through the WebFlux Java configuration, see xref:web/w
== HandlerFunction
[.small]#xref:web/webmvc-functional.adoc#webmvc-fn-handler-functions[See equivalent in the Servlet stack]#
`ServerRequest` and `ServerResponse` are immutable interfaces that offer JDK 8-friendly
`ServerRequest` and `ServerResponse` are immutable interfaces that offer convenient
access to the HTTP request and response.
Both request and response provide {reactive-streams-site}[Reactive Streams] back pressure
against the body streams.
The request body is represented with a Reactor `Flux` or `Mono`.
The response body is represented with any Reactive Streams `Publisher`, including `Flux` and `Mono`.
For more on that, see xref:web-reactive.adoc#webflux-reactive-libraries[Reactive Libraries].
For more on that, see xref:web/webflux-reactive-libraries.adoc[Reactive Libraries].
[[webflux-fn-request]]
=== ServerRequest
@@ -235,87 +235,14 @@ val map = request.awaitMultipartData()
The following example shows how to access multipart data, one at a time, in streaming fashion:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
Flux<PartEvent> allPartEvents = request.bodyToFlux(PartEvent.class);
allPartsEvents.windowUntil(PartEvent::isLast)
.concatMap(p -> p.switchOnFirst((signal, partEvents) -> {
if (signal.hasValue()) {
PartEvent event = signal.get();
if (event instanceof FormPartEvent formEvent) {
String value = formEvent.value();
// handle form field
}
else if (event instanceof FilePartEvent fileEvent) {
String filename = fileEvent.filename();
Flux<DataBuffer> contents = partEvents.map(PartEvent::content);
// handle file upload
}
else {
return Mono.error(new RuntimeException("Unexpected event: " + event));
}
}
else {
return partEvents; // either complete or error signal
}
}));
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
val parts = request.bodyToFlux<PartEvent>()
allPartsEvents.windowUntil(PartEvent::isLast)
.concatMap {
it.switchOnFirst { signal, partEvents ->
if (signal.hasValue()) {
val event = signal.get()
if (event is FormPartEvent) {
val value: String = event.value();
// handle form field
} else if (event is FilePartEvent) {
val filename: String = event.filename();
val contents: Flux<DataBuffer> = partEvents.map(PartEvent::content);
// handle file upload
} else {
return Mono.error(RuntimeException("Unexpected event: " + event));
}
} else {
return partEvents; // either complete or error signal
}
}
}
}
----
======
include-code::./PartEventHandler[tag=snippet,indent=0]
NOTE: The body contents of the `PartEvent` objects must be completely consumed, relayed, or released to avoid memory leaks.
The following shows how to bind request parameters, including an optional `DataBinder` customization:
[tabs]
======
Java::
+
[source,java]
----
Pet pet = request.bind(Pet.class, dataBinder -> dataBinder.setAllowedFields("name"));
----
Kotlin::
+
[source,kotlin]
----
val pet = request.bind(Pet::class.java, {dataBinder -> dataBinder.setAllowedFields("name")})
----
======
The following shows how to bind request parameters, URI variables, or headers via `DataBinder`,
and also shows how to customize the `DataBinder`:
include-code::./RequestHandler[tag=snippet,indent=0]
[[webflux-fn-response]]
=== ServerResponse
@@ -325,24 +252,7 @@ a `build` method to create it. You can use the builder to set the response statu
headers, or to provide a body. The following example creates a 200 (OK) response with JSON
content:
[tabs]
======
Java::
+
[source,java]
----
Mono<Person> person = ...
ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(person, Person.class);
----
Kotlin::
+
[source,kotlin]
----
val person: Person = ...
ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValue(person)
----
======
include-code::./ResponseHandler[tag=snippet,indent=0]
The following example shows how to build a 201 (CREATED) response with a `Location` header and no body:
@@ -353,7 +263,7 @@ Java::
[source,java]
----
URI location = ...
ServerResponse.created(location).build();
return ServerResponse.created(location).build();
----
Kotlin::
@@ -361,7 +271,7 @@ Kotlin::
[source,kotlin]
----
val location: URI = ...
ServerResponse.created(location).build()
return ServerResponse.created(location).build()
----
======
@@ -374,14 +284,14 @@ Java::
+
[source,java]
----
ServerResponse.ok().hint(Jackson2CodecSupport.JSON_VIEW_HINT, MyJacksonView.class).body(...);
return ServerResponse.ok().hint(JacksonCodecSupport.JSON_VIEW_HINT, MyJacksonView.class).body(...);
----
Kotlin::
+
[source,kotlin]
----
ServerResponse.ok().hint(Jackson2CodecSupport.JSON_VIEW_HINT, MyJacksonView::class.java).body(...)
return ServerResponse.ok().hint(JacksonCodecSupport.JSON_VIEW_HINT, MyJacksonView::class.java).body(...)
----
======
@@ -416,87 +326,7 @@ Therefore, it is useful to group related handler functions together into a handl
has a similar role as `@Controller` in an annotation-based application.
For example, the following class exposes a reactive `Person` repository:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.server.ServerResponse.ok;
public class PersonHandler {
private final PersonRepository repository;
public PersonHandler(PersonRepository repository) {
this.repository = repository;
}
public Mono<ServerResponse> listPeople(ServerRequest request) { // <1>
Flux<Person> people = repository.allPeople();
return ok().contentType(APPLICATION_JSON).body(people, Person.class);
}
public Mono<ServerResponse> createPerson(ServerRequest request) { // <2>
Mono<Person> person = request.bodyToMono(Person.class);
return ok().build(repository.savePerson(person));
}
public Mono<ServerResponse> getPerson(ServerRequest request) { // <3>
int personId = Integer.valueOf(request.pathVariable("id"));
return repository.getPerson(personId)
.flatMap(person -> ok().contentType(APPLICATION_JSON).bodyValue(person))
.switchIfEmpty(ServerResponse.notFound().build());
}
}
----
<1> `listPeople` is a handler function that returns all `Person` objects found in the repository as
JSON.
<2> `createPerson` is a handler function that stores a new `Person` contained in the request body.
Note that `PersonRepository.savePerson(Person)` returns `Mono<Void>`: an empty `Mono` that emits
a completion signal when the person has been read from the request and stored. So we use the
`build(Publisher<Void>)` method to send a response when that completion signal is received (that is,
when the `Person` has been saved).
<3> `getPerson` is a handler function that returns a single person, identified by the `id` path
variable. We retrieve that `Person` from the repository and create a JSON response, if it is
found. If it is not found, we use `switchIfEmpty(Mono<T>)` to return a 404 Not Found response.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
class PersonHandler(private val repository: PersonRepository) {
suspend fun listPeople(request: ServerRequest): ServerResponse { // <1>
val people: Flow<Person> = repository.allPeople()
return ok().contentType(APPLICATION_JSON).bodyAndAwait(people);
}
suspend fun createPerson(request: ServerRequest): ServerResponse { // <2>
val person = request.awaitBody<Person>()
repository.savePerson(person)
return ok().buildAndAwait()
}
suspend fun getPerson(request: ServerRequest): ServerResponse { // <3>
val personId = request.pathVariable("id").toInt()
return repository.getPerson(personId)?.let { ok().contentType(APPLICATION_JSON).bodyValueAndAwait(it) }
?: ServerResponse.notFound().buildAndAwait()
}
}
----
<1> `listPeople` is a handler function that returns all `Person` objects found in the repository as
JSON.
<2> `createPerson` is a handler function that stores a new `Person` contained in the request body.
Note that `PersonRepository.savePerson(Person)` is a suspending function with no return type.
<3> `getPerson` is a handler function that returns a single person, identified by the `id` path
variable. We retrieve that `Person` from the repository and create a JSON response, if it is
found. If it is not found, we return a 404 Not Found response.
======
--
include-code::./PersonHandler[tag=snippet,indent=0]
[[webflux-fn-handler-validation]]
=== Validation
@@ -505,66 +335,7 @@ A functional endpoint can use Spring's xref:web/webmvc/mvc-config/validation.ado
apply validation to the request body. For example, given a custom Spring
xref:web/webmvc/mvc-config/validation.adoc[Validator] implementation for a `Person`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
public class PersonHandler {
private final Validator validator = new PersonValidator(); // <1>
// ...
public Mono<ServerResponse> createPerson(ServerRequest request) {
Mono<Person> person = request.bodyToMono(Person.class).doOnNext(this::validate); // <2>
return ok().build(repository.savePerson(person));
}
private void validate(Person person) {
Errors errors = new BeanPropertyBindingResult(person, "person");
validator.validate(person, errors);
if (errors.hasErrors()) {
throw new ServerWebInputException(errors.toString()); // <3>
}
}
}
----
<1> Create `Validator` instance.
<2> Apply validation.
<3> Raise exception for a 400 response.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
class PersonHandler(private val repository: PersonRepository) {
private val validator = PersonValidator() // <1>
// ...
suspend fun createPerson(request: ServerRequest): ServerResponse {
val person = request.awaitBody<Person>()
validate(person) // <2>
repository.savePerson(person)
return ok().buildAndAwait()
}
private fun validate(person: Person) {
val errors: Errors = BeanPropertyBindingResult(person, "person");
validator.validate(person, errors);
if (errors.hasErrors()) {
throw ServerWebInputException(errors.toString()) // <3>
}
}
}
----
<1> Create `Validator` instance.
<2> Apply validation.
<3> Raise exception for a 400 response.
======
include-code::./PersonHandler[tag=snippet,indent=0]
Handlers can also use the standard bean validation API (JSR-303) by creating and injecting
a global `Validator` instance based on `LocalValidatorFactoryBean`.
@@ -602,28 +373,7 @@ path, headers, xref:#api-version[API version], and more.
The following example uses an `Accept` header, request predicate:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
RouterFunction<ServerResponse> route = RouterFunctions.route()
.GET("/hello-world", accept(MediaType.TEXT_PLAIN),
request -> ServerResponse.ok().bodyValue("Hello World")).build();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
val route = coRouter {
GET("/hello-world", accept(TEXT_PLAIN)) {
ServerResponse.ok().bodyValueAndAwait("Hello World")
}
}
----
======
include-code::./RouterConfiguration[tag=snippet,indent=0]
You can compose multiple request predicates together by using:
@@ -658,61 +408,7 @@ There are also other ways to compose multiple router functions together:
The following example shows the composition of four routes:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.server.RequestPredicates.*;
PersonRepository repository = ...
PersonHandler handler = new PersonHandler(repository);
RouterFunction<ServerResponse> otherRoute = ...
RouterFunction<ServerResponse> route = route()
.GET("/person/{id}", accept(APPLICATION_JSON), handler::getPerson) // <1>
.GET("/person", accept(APPLICATION_JSON), handler::listPeople) // <2>
.POST("/person", handler::createPerson) // <3>
.add(otherRoute) // <4>
.build();
----
<1> pass:q[`GET /person/{id}`] with an `Accept` header that matches JSON is routed to
`PersonHandler.getPerson`
<2> `GET /person` with an `Accept` header that matches JSON is routed to
`PersonHandler.listPeople`
<3> `POST /person` with no additional predicates is mapped to
`PersonHandler.createPerson`, and
<4> `otherRoute` is a router function that is created elsewhere, and added to the route built.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
import org.springframework.http.MediaType.APPLICATION_JSON
val repository: PersonRepository = ...
val handler = PersonHandler(repository);
val otherRoute: RouterFunction<ServerResponse> = coRouter { }
val route = coRouter {
GET("/person/{id}", accept(APPLICATION_JSON), handler::getPerson) // <1>
GET("/person", accept(APPLICATION_JSON), handler::listPeople) // <2>
POST("/person", handler::createPerson) // <3>
}.and(otherRoute) // <4>
----
<1> pass:q[`GET /person/{id}`] with an `Accept` header that matches JSON is routed to
`PersonHandler.getPerson`
<2> `GET /person` with an `Accept` header that matches JSON is routed to
`PersonHandler.listPeople`
<3> `POST /person` with no additional predicates is mapped to
`PersonHandler.createPerson`, and
<4> `otherRoute` is a router function that is created elsewhere, and added to the route built.
======
include-code::./RouterConfiguration[tag=snippet,indent=0]
[[nested-routes]]
=== Nested Routes
@@ -810,7 +506,7 @@ Java::
----
RouterFunction<ServerResponse> route = RouterFunctions.route()
.GET("/hello-world", version("1.2"),
request -> ServerResponse.ok().body("Hello World")).build();
request -> ServerResponse.ok().bodyValue("Hello World")).build();
----
Kotlin::
@@ -943,7 +639,6 @@ Java::
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Bean
@@ -980,7 +675,6 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
@Bean
@@ -1048,9 +742,9 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
val route = router {
"/person".nest {
("/person" and accept(APPLICATION_JSON)).nest {
GET("/{id}", handler::getPerson)
GET("", handler::listPeople)
GET(handler::listPeople)
before { // <1>
ServerRequest.from(it)
.header("X-RequestHeader", "Value").build()
@@ -1077,54 +771,7 @@ Now we can add a simple security filter to our route, assuming that we have a `S
can determine whether a particular path is allowed.
The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
SecurityManager securityManager = ...
RouterFunction<ServerResponse> route = route()
.path("/person", b1 -> b1
.nest(accept(APPLICATION_JSON), b2 -> b2
.GET("/{id}", handler::getPerson)
.GET(handler::listPeople))
.POST(handler::createPerson))
.filter((request, next) -> {
if (securityManager.allowAccessTo(request.path())) {
return next.handle(request);
}
else {
return ServerResponse.status(UNAUTHORIZED).build();
}
})
.build();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
val securityManager: SecurityManager = ...
val route = router {
("/person" and accept(APPLICATION_JSON)).nest {
GET("/{id}", handler::getPerson)
GET("", handler::listPeople)
POST(handler::createPerson)
filter { request, next ->
if (securityManager.allowAccessTo(request.path())) {
next(request)
}
else {
status(UNAUTHORIZED).build();
}
}
}
}
----
======
include-code::./RouterConfiguration[tag=snippet,indent=0]
The preceding example demonstrates that invoking the `next.handle(ServerRequest)` is optional.
We only let the handler function be run when access is allowed.
@@ -49,10 +49,9 @@ This strategy resolves the API version from a request. The WebFlux config provid
options to resolve from a header, query parameter, media type parameter,
or from the URL path. You can also use a custom `ApiVersionResolver`.
NOTE: The path resolver always resolves the version from the specified path segment, or
raises `InvalidApiVersionException` otherwise, and therefore it cannot yield to other
resolvers.
The path resolver selects the version from a path segment specified by index, or
raises `InvalidApiVersionException`, and therefore never results in `null` (no version)
unless it is configured with a `Predicate<RequestPath>` to determine if a path is versioned.
@@ -56,8 +56,7 @@ Java::
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
public class WebConfiguration implements WebFluxConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
@@ -80,8 +79,7 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
class WebConfiguration : WebFluxConfigurer {
override fun configureViewResolvers(registry: ViewResolverRegistry) {
registry.freeMarker()
@@ -119,8 +117,7 @@ Java::
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
public class WebConfiguration implements WebFluxConfigurer {
// ...
@@ -142,8 +139,7 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
class WebConfiguration : WebFluxConfigurer {
// ...
@@ -210,13 +206,8 @@ The following table shows the templating libraries that we have tested on differ
[%header]
|===
|Scripting Library |Scripting Engine
|https://handlebarsjs.com/[Handlebars] |https://openjdk.java.net/projects/nashorn/[Nashorn]
|https://mustache.github.io/[Mustache] |https://openjdk.java.net/projects/nashorn/[Nashorn]
|https://react.dev/[React] |https://openjdk.java.net/projects/nashorn/[Nashorn]
|https://ejs.co/[EJS] |https://openjdk.java.net/projects/nashorn/[Nashorn]
|https://docs.ruby-lang.org/en/master/ERB.html[ERB] |https://www.jruby.org[JRuby]
|https://docs.python.org/2/library/string.html#template-strings[String templates] |https://www.jython.org/[Jython]
|https://github.com/sdeleuze/kotlin-script-templating[Kotlin Script templating] |{kotlin-site}[Kotlin]
|===
TIP: The basic rule for integrating any other script engine is that it must implement the
@@ -228,17 +219,8 @@ TIP: The basic rule for integrating any other script engine is that it must impl
You need to have the script engine on your classpath, the details of which vary by script engine:
* The https://openjdk.java.net/projects/nashorn/[Nashorn] JavaScript engine is provided with
Java 8+. Using the latest update release available is highly recommended.
* https://www.jruby.org[JRuby] should be added as a dependency for Ruby support.
* https://www.jython.org[Jython] should be added as a dependency for Python support.
* `org.jetbrains.kotlin:kotlin-script-util` dependency and a `META-INF/services/javax.script.ScriptEngineFactory`
file containing a `org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmLocalScriptEngineFactory`
line should be added for Kotlin script support. See
https://github.com/sdeleuze/kotlin-script-templating[this example] for more detail.
You need to have the script templating library. One way to do that for JavaScript is
through https://www.webjars.org/[WebJars].
[[webflux-view-script-integrate]]
=== Script Templates
@@ -246,7 +228,7 @@ through https://www.webjars.org/[WebJars].
You can declare a `ScriptTemplateConfigurer` bean to specify the script engine to use,
the script files to load, what function to call to render templates, and so on.
The following example uses Mustache templates and the Nashorn JavaScript engine:
The following example uses the Jython Python engine:
[tabs]
======
@@ -255,8 +237,7 @@ Java::
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
public class WebConfiguration implements WebFluxConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
@@ -266,9 +247,8 @@ Java::
@Bean
public ScriptTemplateConfigurer configurer() {
ScriptTemplateConfigurer configurer = new ScriptTemplateConfigurer();
configurer.setEngineName("nashorn");
configurer.setScripts("mustache.js");
configurer.setRenderObject("Mustache");
configurer.setEngineName("jython");
configurer.setScripts("render.py");
configurer.setRenderFunction("render");
return configurer;
}
@@ -280,8 +260,7 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
class WebConfiguration : WebFluxConfigurer {
override fun configureViewResolvers(registry: ViewResolverRegistry) {
registry.scriptTemplate()
@@ -289,9 +268,8 @@ Kotlin::
@Bean
fun configurer() = ScriptTemplateConfigurer().apply {
engineName = "nashorn"
setScripts("mustache.js")
renderObject = "Mustache"
engineName = "jython"
setScripts("render.py")
renderFunction = "render"
}
}
@@ -305,94 +283,7 @@ The `render` function is called with the following parameters:
* `RenderingContext renderingContext`: The
{spring-framework-api}/web/servlet/view/script/RenderingContext.html[`RenderingContext`]
that gives access to the application context, the locale, the template loader, and the
URL (since 5.0)
`Mustache.render()` is natively compatible with this signature, so you can call it directly.
If your templating technology requires some customization, you can provide a script that
implements a custom render function. For example, https://handlebarsjs.com[Handlerbars]
needs to compile templates before using them and requires a
https://en.wikipedia.org/wiki/Polyfill[polyfill] in order to emulate some
browser facilities not available in the server-side script engine.
The following example shows how to set a custom render function:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.scriptTemplate();
}
@Bean
public ScriptTemplateConfigurer configurer() {
ScriptTemplateConfigurer configurer = new ScriptTemplateConfigurer();
configurer.setEngineName("nashorn");
configurer.setScripts("polyfill.js", "handlebars.js", "render.js");
configurer.setRenderFunction("render");
configurer.setSharedEngine(false);
return configurer;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
override fun configureViewResolvers(registry: ViewResolverRegistry) {
registry.scriptTemplate()
}
@Bean
fun configurer() = ScriptTemplateConfigurer().apply {
engineName = "nashorn"
setScripts("polyfill.js", "handlebars.js", "render.js")
renderFunction = "render"
isSharedEngine = false
}
}
----
======
NOTE: Setting the `sharedEngine` property to `false` is required when using non-thread-safe
script engines with templating libraries not designed for concurrency, such as Handlebars or
React running on Nashorn. In that case, Java SE 8 update 60 is required, due to
https://bugs.openjdk.java.net/browse/JDK-8076099[this bug], but it is generally
recommended to use a recent Java SE patch release in any case.
`polyfill.js` defines only the `window` object needed by Handlebars to run properly,
as the following snippet shows:
[source,javascript,indent=0,subs="verbatim,quotes"]
----
var window = {};
----
This basic `render.js` implementation compiles the template before using it. A production
ready implementation should also store and reused cached templates or pre-compiled templates.
This can be done on the script side, as well as any customization you need (managing
template engine configuration for example).
The following example shows how compile a template:
[source,javascript,indent=0,subs="verbatim,quotes"]
----
function render(template, model) {
var compiledTemplate = Handlebars.compile(template);
return compiledTemplate(model);
}
----
URL
Check out the Spring Framework unit tests,
{spring-framework-code}/spring-webflux/src/test/java/org/springframework/web/reactive/result/view/script[Java], and
@@ -482,7 +373,7 @@ purposes, it is useful to be able to alternate between rendering a model with an
or as other formats (such as JSON or XML), depending on the content type requested by the client.
To support doing so, Spring WebFlux provides the `HttpMessageWriterView`, which you can use to
plug in any of the available xref:web/webflux/reactive-spring.adoc#webflux-codecs[Codecs] from
`spring-web`, such as `Jackson2JsonEncoder`, `Jackson2SmileEncoder`, or `Jaxb2XmlEncoder`.
`spring-web`, such as `JacksonJsonEncoder`, `JacksonSmileEncoder`, or `Jaxb2XmlEncoder`.
Unlike other view technologies, `HttpMessageWriterView` does not require a `ViewResolver` but is
instead xref:web/webflux/config.adoc#webflux-config-view-resolvers[configured] as a default view.
@@ -18,8 +18,8 @@ You can also use `WebClient.builder()` with further options:
* `exchangeStrategies`: HTTP message reader/writer customizations.
* `clientConnector`: HTTP client library settings.
* `apiVersionInserter`: to insert API version values in the request
* `observationRegistry`: the registry to use for enabling xref:integration/observability.adoc#http-client.webclient[Observability support].
* `observationConvention`: xref:integration/observability.adoc#config[an optional, custom convention to extract metadata] for recorded observations.
* `observationRegistry`: the registry to use for enabling xref:integration/observability.adoc#observability.http-client.webclient[Observability support].
* `observationConvention`: xref:integration/observability.adoc#observability.config[an optional, custom convention to extract metadata] for recorded observations.
For example:
@@ -32,9 +32,9 @@ any `@RequestMapping` method to render an RFC 9457 response. This is processed a
- The `status` property of `ProblemDetail` determines the HTTP status.
- The `instance` property of `ProblemDetail` is set from the current URL path, if not
already set.
- For content negotiation, the Jackson `HttpMessageConverter` prefers
"application/problem+json" over "application/json" when rendering a `ProblemDetail`,
and also falls back on it if no compatible media type is found.
- The Jackson JSON and XML message converters use "application/problem+json" or
"application/problem+xml" respectively as the producible media types for `ProblemDetail`
to ensure they are favored for content negotiation.
To enable RFC 9457 responses for Spring WebFlux exceptions and for any
`ErrorResponseException`, extend `ResponseEntityExceptionHandler` and declare it as an
@@ -66,6 +66,13 @@ from an existing `ProblemDetail`. This could be done centrally, for example, fro
`@ControllerAdvice` such as `ResponseEntityExceptionHandler` that re-creates the
`ProblemDetail` of an exception into a subclass with the additional non-standard fields.
TIP: In Spring Boot, the `spring.webflux.problemdetails.enabled` property autoconfigures
a `ResponseEntityExceptionHandler` that handles built-in exceptions with problem details.
In that case, you may prefer to create another `@ControllerAdvice` instead of extending
`ResponseEntityExceptionHandler` if you want to take over the handling of a specific
built-in exception. You'll need to ensure your handler is ordered ahead of the one
configured by Spring Boot whose order is 0.
[[webflux-ann-rest-exceptions-i18n]]
== Customization and i18n
@@ -135,6 +142,10 @@ Message codes and arguments for each error are also resolved via `MessageSource`
| `+{0}+` the list of global errors, `+{1}+` the list of field errors.
Message codes and arguments for each error are also resolved via `MessageSource`.
| `NoResourceFoundException`
| (default)
| `+{0}+` the request path (or portion of) used to find a resource
|===
NOTE: Unlike other exceptions, the message arguments for
@@ -251,7 +251,8 @@ Kotlin::
======
TIP: If you need to have a `LocalValidatorFactoryBean` injected somewhere, create a bean and
mark it with `@Primary` in order to avoid conflict with the one declared in the MVC config.
mark it with `@Primary`, or mark the one declared in the MVC config with `@Fallback`, in
order to avoid conflict.
[[webflux-config-content-negotiation]]
@@ -334,19 +335,8 @@ Kotlin::
`ServerCodecConfigurer` provides a set of default readers and writers. You can use it to add
more readers and writers, customize the default ones, or replace the default ones completely.
For Jackson JSON and XML, consider using
{spring-framework-api}/http/converter/json/Jackson2ObjectMapperBuilder.html[`Jackson2ObjectMapperBuilder`],
which customizes Jackson's default properties with the following ones:
* {jackson-docs}/jackson-databind/javadoc/2.6/com/fasterxml/jackson/databind/DeserializationFeature.html#FAIL_ON_UNKNOWN_PROPERTIES[`DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES`] is disabled.
* {jackson-docs}/jackson-databind/javadoc/2.6/com/fasterxml/jackson/databind/MapperFeature.html#DEFAULT_VIEW_INCLUSION[`MapperFeature.DEFAULT_VIEW_INCLUSION`] is disabled.
It also automatically registers the following well-known modules if they are detected on the classpath:
* {jackson-github-org}/jackson-datatype-jsr310[`jackson-datatype-jsr310`]: Support for Java 8 Date and Time API types.
* {jackson-github-org}/jackson-datatype-jdk8[`jackson-datatype-jdk8`]: Support for other Java 8 types, such as `Optional`.
* {jackson-github-org}/jackson-module-kotlin[`jackson-module-kotlin`]: Support for Kotlin classes and data classes.
For Jackson, consider using a Jackson format-specific builder like `JsonMapper.Builder` to configure Jackson's default
properties.
[[webflux-config-view-resolvers]]
== View Resolvers
@@ -489,7 +479,7 @@ Java::
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.freeMarker();
Jackson2JsonEncoder encoder = new Jackson2JsonEncoder();
JacksonJsonEncoder encoder = new JacksonJsonEncoder();
registry.defaultViews(new HttpMessageWriterView(encoder));
}
@@ -508,7 +498,7 @@ Kotlin::
override fun configureViewResolvers(registry: ViewResolverRegistry) {
registry.freeMarker()
val encoder = Jackson2JsonEncoder()
val encoder = JacksonJsonEncoder()
registry.defaultViews(HttpMessageWriterView(encoder))
}
@@ -701,7 +691,7 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim"]
----
@Configuration
class WebConfiguration : WebMvcConfigurer {
class WebConfiguration : WebFluxConfigurer {
override fun configureApiVersioning(configurer: ApiVersionConfigurer) {
configurer.useRequestHeader("API-Version")
@@ -107,7 +107,4 @@ Kotlin::
[[webflux-ann-initbinder-model-design]]
== Model Design
[.small]#xref:web/webmvc/mvc-controller/ann-initbinder.adoc#mvc-ann-initbinder-model-design[See equivalent in the Servlet stack]#
include::partial$web/web-data-binding-model-design.adoc[]
NOTE: For more guidance on model design, please see xref:web/webflux/data-binding.adoc[Data Binding].
@@ -5,7 +5,7 @@
The following table shows the supported controller method arguments.
Reactive types (Reactor, RxJava, xref:web-reactive.adoc#webflux-reactive-libraries[or other]) are
Reactive types (Reactor, RxJava, xref:web/webflux-reactive-libraries.adoc[or other]) are
supported on arguments that require blocking I/O (for example, reading the request body) to
be resolved. This is marked in the Description column. Reactive types are not expected
on arguments that do not require blocking.
@@ -114,6 +114,6 @@ and others) and is equivalent to `required=false`.
| Any other argument
| If a method argument is not matched to any of the above, it is, by default, resolved as
a `@RequestParam` if it is a simple type, as determined by
{spring-framework-api}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty],
{spring-framework-api}/beans/BeanUtils.html#isSimpleProperty(java.lang.Class)[BeanUtils#isSimpleProperty],
or as a `@ModelAttribute`, otherwise.
|===
@@ -205,7 +205,7 @@ controller method xref:web/webmvc/mvc-controller/ann-validation.adoc[Validation]
TIP: Using `@ModelAttribute` is optional. By default, any argument that is not a simple
value type as determined by
{spring-framework-api}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty]
{spring-framework-api}/beans/BeanUtils.html#isSimpleProperty(java.lang.Class)[BeanUtils#isSimpleProperty]
_AND_ that is not resolved by any other argument resolver is treated as an implicit `@ModelAttribute`.
WARNING: When compiling to a native image with GraalVM, the implicit `@ModelAttribute`
@@ -41,8 +41,8 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
class MyForm(
val name: String,
val file: FilePart)
private val name: String,
private val file: FilePart)
@Controller
class FileUploadController {
@@ -103,8 +103,8 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@PostMapping("/")
fun handle(@RequestPart("meta-data") Part metadata, // <1>
@RequestPart("file-data") FilePart file): String { // <2>
fun handle(@RequestPart("meta-data") metadata: Part, // <1>
@RequestPart("file-data") file: FilePart): String { // <2>
// ...
}
----
@@ -169,8 +169,8 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@PostMapping("/")
fun handle(@Valid @RequestPart("meta-data") metadata: MetaData): String {
// ...
fun handle(@Valid @RequestPart("meta-data") metadata: Mono<MetaData>): String {
// use one of the onError* operators...
}
----
======
@@ -202,7 +202,7 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@PostMapping("/")
fun handle(@RequestBody parts: MultiValueMap<String, Part>): String { // <1>
fun handle(@RequestBody parts: Mono<MultiValueMap<String, Part>>): String { // <1>
// ...
}
----
@@ -227,87 +227,7 @@ when uploading. If the file is large enough to be split across multiple buffers,
For example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
@PostMapping("/")
public void handle(@RequestBody Flux<PartEvent> allPartsEvents) { <1>
allPartsEvents.windowUntil(PartEvent::isLast) <2>
.concatMap(p -> p.switchOnFirst((signal, partEvents) -> { <3>
if (signal.hasValue()) {
PartEvent event = signal.get();
if (event instanceof FormPartEvent formEvent) { <4>
String value = formEvent.value();
// handle form field
}
else if (event instanceof FilePartEvent fileEvent) { <5>
String filename = fileEvent.filename();
Flux<DataBuffer> contents = partEvents.map(PartEvent::content); <6>
// handle file upload
}
else {
return Mono.error(new RuntimeException("Unexpected event: " + event));
}
}
else {
return partEvents; // either complete or error signal
}
}));
}
----
<1> Using `@RequestBody`.
<2> The final `PartEvent` for a particular part will have `isLast()` set to `true`, and can be
followed by additional events belonging to subsequent parts.
This makes the `isLast` property suitable as a predicate for the `Flux::windowUntil` operator, to
split events from all parts into windows that each belong to a single part.
<3> The `Flux::switchOnFirst` operator allows you to see whether you are handling a form field or
file upload.
<4> Handling the form field.
<5> Handling the file upload.
<6> The body contents must be completely consumed, relayed, or released to avoid memory leaks.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@PostMapping("/")
fun handle(@RequestBody allPartsEvents: Flux<PartEvent>) = { // <1>
allPartsEvents.windowUntil(PartEvent::isLast) <2>
.concatMap {
it.switchOnFirst { signal, partEvents -> <3>
if (signal.hasValue()) {
val event = signal.get()
if (event is FormPartEvent) { <4>
val value: String = event.value();
// handle form field
} else if (event is FilePartEvent) { <5>
val filename: String = event.filename();
val contents: Flux<DataBuffer> = partEvents.map(PartEvent::content); <6>
// handle file upload
} else {
return Mono.error(RuntimeException("Unexpected event: " + event));
}
} else {
return partEvents; // either complete or error signal
}
}
}
}
----
<1> Using `@RequestBody`.
<2> The final `PartEvent` for a particular part will have `isLast()` set to `true`, and can be
followed by additional events belonging to subsequent parts.
This makes the `isLast` property suitable as a predicate for the `Flux::windowUntil` operator, to
split events from all parts into windows that each belong to a single part.
<3> The `Flux::switchOnFirst` operator allows you to see whether you are handling a form field or
file upload.
<4> Handling the form field.
<5> Handling the file upload.
<6> The body contents must be completely consumed, relayed, or released to avoid memory leaks.
======
include-code::./PartEventController[tag=snippet,indent=0]
Received part events can also be relayed to another service by using the `WebClient`.
See xref:web/webflux-webclient/client-body.adoc#webflux-client-body-multipart[Multipart Data].

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