Compare commits

...

446 Commits

Author SHA1 Message Date
Sam Brannen 2b08e6e1d3 Merge branch '7.0.x' 2026-06-08 18:29:20 +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 4e39c28b92 Merge branch '7.0.x' 2026-06-08 14:49:45 +02:00
Brian Clozel ba0a9b8923 Upgrade to Reactor 2025.0.6
Closes gh-36884
2026-06-08 14:49:19 +02:00
Brian Clozel 381fa10477 Merge branch '7.0.x' 2026-06-08 10:41:12 +02:00
Brian Clozel 8a192eab1f Upgrade to Micrometer 1.16.6
Closes gh-36883
2026-06-08 10:40:01 +02:00
rstoyanchev 14f4deef3a Merge branch '7.0.x' 2026-06-05 14:36:23 +01: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 e23971efb9 Merge branch '7.0.x' 2026-06-05 15:15:19 +02: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 2469aae672 Merge branch '7.0.x' 2026-06-05 14:57:29 +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
cookie-meringue 83e29382b3 Optimize ClassNameReader.getClassName via direct ASM API
getClassName now calls ClassReader.getClassName() directly instead
of routing through the visitor-based getClassInfo. Previously, it
allocated a List and a ClassVisitor and decoded super_class and
every interface name only to discard all but the first element.

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

Closes gh-36814

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

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

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

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

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

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

Closes gh-36872

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

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

Closes gh-36861

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Signed-off-by: seregamorph <serega.morph@gmail.com>
2026-05-20 14:03:55 +02:00
Sam Brannen 0c25d817bd Merge branch '7.0.x' 2026-05-17 15:33:24 +02:00
Sam Brannen d5e85bd95e Remove obsolete code 2026-05-17 15:30:04 +02:00
Sam Brannen 17ed3e8370 Merge branch '7.0.x' 2026-05-17 13:56:47 +02:00
Sam Brannen c3c96b9f32 Upgrade to Gradle 9.5.1
Closes gh-36744
2026-05-17 13:47:00 +02:00
rstoyanchev 2f458f9093 Merge branch '7.0.x' 2026-05-15 13:51:31 +01:00
rstoyanchev 4d98ff5dcf Polishing in data binding docs 2026-05-15 13:50:57 +01:00
rstoyanchev 16e0ed0463 Merge branch '7.0.x' 2026-05-14 16:56:21 +01:00
rstoyanchev 48262b8efe Further polishing in docs on web binding
See gh-36803
2026-05-14 16:55:53 +01:00
rstoyanchev e24448118c Merge branch '7.0.x' 2026-05-14 16:42:29 +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 a0ec6656b4 Merge branch '7.0.x' 2026-05-13 19:46:39 +02:00
Juergen Hoeller 8fe1de4595 Polishing 2026-05-13 19:46:07 +02:00
Juergen Hoeller 5ab4c5c5d9 Add support for JPA 4.0 @PersistenceAgent injection
Closes gh-36264
2026-05-13 19:41:21 +02:00
Juergen Hoeller c3baa01535 Merge branch '7.0.x' 2026-05-12 16:21:43 +02:00
Juergen Hoeller c048074436 Restrict SpringVersion.getVersion() to "major.minor.patch" format
Closes gh-36785
2026-05-12 16:20:47 +02:00
Juergen Hoeller c0b749479a Merge branch '7.0.x' 2026-05-12 13:10:34 +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 5bf58cfe05 Merge branch '7.0.x'
# Conflicts:
#	framework-platform/framework-platform.gradle
2026-05-11 13:09:38 +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
Vinod Kumar 238a24cb6d Polish collection usage in HttpHeadersTests
Signed-off-by: Vinod Kumar <codingkiddo@gmail.com>
2026-05-11 09:34:28 +02:00
Sam Brannen 9a54f75d6d Merge branch '7.0.x' 2026-05-09 16:40:53 +02:00
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 9db16e4c15 Merge branch '7.0.x'
# Conflicts:
#	framework-platform/framework-platform.gradle
2026-05-08 16:02:00 +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 ec8f0ca6b2 Merge branch '7.0.x' 2026-05-06 13:54:03 +02:00
Sam Brannen 987d6cca6d Fix error message for invalid regex in SpEL
Closes gh-36756
2026-05-06 13:53:37 +02:00
Sam Brannen 1a0bd12c34 Merge branch '7.0.x' 2026-05-06 13:26:10 +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 e6590aa1a8 Merge branch '7.0.x'
Closes gh-36753
2026-05-05 11:59:37 +02:00
Brian Clozel 628261d5bb Do not warn against Root Servlet context location
Closes gh-36692
2026-05-05 11:50:28 +02:00
Sam Brannen 051b09694e Merge branch '7.0.x' 2026-05-04 17:21:22 +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 39ff8e46ab Use String#replace instead of String#replaceAll in tests
See gh-36678
2026-05-03 14:36:23 +02:00
shenjianeng 27bdf24482 Use String#replace instead of String#replaceAll where appropriate
Avoid using String#replaceAll when the pattern is not a regular
expression.

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

Closes gh-36678

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

Closes gh-36695
2026-04-23 11:29:47 +02:00
Brian Clozel 0d14aa5856 Merge branch '7.0.x' 2026-04-23 10:55:18 +02:00
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 c6096ff6e5 Upgrade to Hibernate ORM 7.3.2 and Woodstox 7.1.1 2026-04-21 20:37:08 +02:00
Juergen Hoeller 6ff758391c Merge branch '7.0.x' 2026-04-21 20:36:27 +02:00
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 ca9b26eb8f Merge branch '7.0.x' 2026-04-21 18:52:41 +02:00
Brian Clozel 83e7aa1131 Update HttpComponents Javadoc URI
Fixes gh-36685
2026-04-21 18:51:58 +02:00
Brian Clozel 279409acce Merge branch '7.0.x' 2026-04-21 17:47:25 +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
Sam Brannen 4f1b4b36bf Merge branch '7.0.x' 2026-04-17 17:25:53 +02:00
Stéphane Nicoll be70e1c5a7 Next development version (v7.0.8-SNAPSHOT) 2026-04-17 08:33:14 +02:00
Juergen Hoeller 680f4ee482 Merge branch '7.0.x'
# Conflicts:
#	framework-platform/framework-platform.gradle
2026-04-16 23:30:21 +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 59cd577fd1 Merge branch '7.0.x' 2026-04-16 21:15:42 +01:00
rstoyanchev 7052da4532 Add doOnDiscard in MultipartHttpMessageReader
Closes gh-36563
2026-04-16 21:15:09 +01:00
Sébastien Deleuze 9f92183710 Upgrade to Kotlin Serialization 1.11.0
Closes gh-36657
2026-04-16 16:36:56 +02:00
Sam Brannen 680854d1f3 Merge branch '7.0.x' 2026-04-16 16:35:25 +02:00
Sam Brannen 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 1a7161c85e Merge branch '7.0.x' 2026-04-16 15:12:44 +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 8bce51267a Merge branch '7.0.x' 2026-04-16 14:42:00 +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 d169eb547e Polishing 2026-04-15 14:30:03 +02:00
Sam Brannen 9c8535f5e4 Compile SpEL expressions that use Optional with null-safe & Elvis operators
In Spring Framework 7.0, we introduced support for using `Optional`
with the null-safe and Elvis operators in SpEL expressions; however,
such expressions were previously not compilable.

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

See gh-20433
See gh-36331
Closes gh-36330
2026-04-15 14:09:57 +02:00
Sam Brannen 829add3aa9 Update Javadoc for HttpMethod.valueOf() on main
See gh-36642
See gh-36652
2026-04-14 16:30:56 +02:00
Sam Brannen 20e57608ba Merge branch '7.0.x' 2026-04-14 16:18:23 +02:00
Sam Brannen 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 6275f46a66 Merge branch '7.0.x' 2026-04-13 17:42:59 +02:00
Sam Brannen ff3f2937ab Restructure SpelCompilationCoverageTests using @⁠Nested test classes 2026-04-13 17:42:18 +02:00
Sam Brannen 9a17b5c453 Merge branch '7.0.x' 2026-04-13 12:59:04 +02:00
Sam Brannen d7450ce8e4 Polish SpelCompilationCoverageTests 2026-04-13 12:58:36 +02:00
Sam Brannen 1787d3e885 Merge branch '7.0.x' 2026-04-12 17:17:27 +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 1f5af8f364 Merge branch '7.0.x' 2026-04-12 16:46:39 +02:00
Sam Brannen 84221064c8 Polish SpEl Ternary operator and compilation tests 2026-04-12 16:41:49 +02:00
Sam Brannen a4b33b98df Merge branch '7.0.x' 2026-04-12 14:29:42 +02:00
Sam Brannen 21f3b964fe Improve SpEL tests for Elvis and Ternary operators 2026-04-12 14:28:33 +02:00
Sam Brannen 59f9cf8645 Polish SpEL internals 2026-04-12 14:20:50 +02:00
Sébastien Deleuze fb34264169 Merge branch '7.0.x' 2026-04-10 16:10:45 +02:00
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
Sam Brannen c2cf5e065d Perform case-insensitive lookup in HttpMethod.valueOf()
Prior to this commit, the implementation of HttpMethod.valueOf()
aligned with the semantics of Enum#valueOf() which requires an exact
match for the enum constant name.

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

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

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

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

Add regression tests for mutation safety and copy isolation.

Closes gh-36626

Signed-off-by: Junseo Bae <ferrater1013@gmail.com>
2026-04-08 16:07:33 +02:00
Juergen Hoeller 3d211b71b1 Revise target bean exception with consistent message formatting 2026-04-08 15:28:29 +02:00
Sam Brannen e940a38014 Merge branch '7.0.x' 2026-04-08 14:19:16 +02:00
Sam Brannen 18b8f871aa Prevent mockk from transitively pulling in JUnit 4 2026-04-08 14:13:59 +02:00
Juergen Hoeller 623bdfb677 Merge branch '7.0.x'
# Conflicts:
#	framework-platform/framework-platform.gradle
2026-04-08 13:43:25 +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 8566e7bf55 Favor Class#getTypeName over ClassUtils#getQualifiedName where feasible 2026-04-08 13:27:52 +02:00
Sam Brannen c17f25f939 Fall back to type name in ClassUtils.getCanonicalName()
See gh-36607
2026-04-08 12:39:40 +02:00
Sam Brannen 9da22ecb46 Merge branch '7.0.x' 2026-04-08 12:35:54 +02:00
Sam Brannen b560c7b85d Improve Javadoc for MergedAnnotations 2026-04-08 12:35:24 +02:00
Sam Brannen f602967dbc Consistently supply List to MergedAnnotations.of() 2026-04-08 11:59:24 +02:00
Yanming Zhou f7d3556b8c Polish DisconnectedClientHelper
Use `CollectionUtils::newLinkedHashSet` instead of `LinkedHashSet::new` to avoid resizing.

Closes gh-36618

Signed-off-by: Yanming Zhou <zhouyanming@gmail.com>
2026-04-08 11:58:36 +02:00
Sam Brannen 813e113ea9 Merge branch '7.0.x' 2026-04-08 11:40:52 +02:00
Sam Brannen 0f05a2e153 Polish ClassFileMethodMetadata 2026-04-08 11:38:07 +02:00
Sam Brannen 306a1f6c99 Merge branch '7.0.x' 2026-04-08 11:14:48 +02:00
Sam Brannen d4cc273c31 Avoid recursion in ClassFileAnnotationMetadata.resolveTypeName()
See gh-36577
2026-04-08 11:13:51 +02:00
Sam Brannen 62c6d67615 Align StandardMethodMetadata with ASM/ClassFile support for getReturnTypeName()
We currently have three implementations of MethodMetadata:

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

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

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

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

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

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

Closes gh-36550

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

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

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

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

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

- AttributeMethods
- SynthesizedMergedAnnotationInvocationHandler
- TypeDescriptor
- DefaultRetryPolicy
- ReflectiveIndexAccessor

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

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

This commit also makes similar changes in
MergedAnnotationReadingVisitor and ClassFileAnnotationDelegate.

Closes gh-36593
2026-04-03 16:44:28 +02:00
Sam Brannen 822001c6a4 Merge branch '7.0.x' 2026-04-03 15:42:28 +02:00
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 f2d4d59f5a Merge branch '7.0.x' 2026-04-03 15:39:58 +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 4709f68446 Merge branch '7.0.x' 2026-04-02 18:40:08 +02:00
Sam Brannen 1ead8bf1ab Polishing 2026-04-02 18:39:37 +02:00
Sam Brannen afba74c516 Merge branch '7.0.x' 2026-04-02 17:21:10 +02:00
Sam Brannen 7b087d1a6c Disable flaky reactorNettyAttributes() test in WebClientIntegrationTests
See gh-36589
2026-04-02 17:20:38 +02:00
Sam Brannen fd50c0841c Merge branch '7.0.x' 2026-04-02 16:55:02 +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 cbe8b148b1 Merge branch '7.0.x' 2026-04-02 14:48:56 +02:00
Brian Clozel d87ebbff78 Polishing
See gh-36584
2026-04-02 14:48:09 +02:00
Stéphane Nicoll 2086508924 Polish
See gh-36581
2026-04-02 14:41:26 +02:00
Juergen Hoeller 759b173b1a Merge branch '7.0.x' 2026-04-02 14:31:56 +02:00
Juergen Hoeller e977bd7bf0 Enforce strict implementation of Resource#isReadable() contract
Closes gh-36584
2026-04-02 14:30:51 +02:00
Sam Brannen 7590c4c92e Fix Javadoc link
See gh-36581
2026-04-02 13:32:32 +02:00
Sam Brannen 596c0df826 Merge branch '7.0.x' 2026-04-02 12:45:50 +02:00
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
Stéphane Nicoll d5b6f4a7ee Polish BeanRegistrar Javadoc and add tests for non-invocation semantics
Revise the BeanRegistrar Javadoc to document the two distinct usage
modes: @Configuration/@Import and programmatic GenericApplicationContext
setup.

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

Add missing tests

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

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

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

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

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

Closes gh-36560

Signed-off-by: Yanming Zhou <zhouyanming@gmail.com>
Signed-off-by: Sam Brannen <104798+sbrannen@users.noreply.github.com>
Co-authored-by: Sam Brannen <104798+sbrannen@users.noreply.github.com>
2026-03-31 17:56:47 +02:00
Sam Brannen 76ad7c9cc0 Merge branch '7.0.x' 2026-03-31 17:50:15 +02:00
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 8586095370 Merge branch '7.0.x' 2026-03-31 16:22:16 +01:00
rstoyanchev d4d49fd546 Make base classes for client requests public
AbstractBufferingClientHttpRequest and AbstractStreamingClientHttpRequest

Closes gh-36501
2026-03-31 14:11:17 +01:00
Sébastien Deleuze 32970fe10d Fix WebClient context propagation in Kotlin Coroutines
Prior to this commit, thread-local variables like Trace ID were not
automatically propagated into the Reactor Context when making requests
using Kotlin coroutine WebClient extensions like `awaitExchange`.

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

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

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

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

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

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

For example, the following which uses field injection:

   @⁠SpringJUnitConfig(TestConfig.class)
   class BeanOverrideTests {

      @⁠MockitoBean
      CustomService customService;

      // tests...
   }

Can now be rewritten to use constructor injection:

   @⁠SpringJUnitConfig(TestConfig.class)
   class BeanOverrideTests {

      private final CustomService customService;

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

      // tests...
   }

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

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

      // tests...
   }

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

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

      // tests...
   }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Closes gh-36302
2026-03-24 17:23:49 +01:00
Brian Clozel f1a60f1664 Merge branch '7.0.x' 2026-03-24 13:19:49 +01:00
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 d66e6571c1 Merge branch '7.0.x' 2026-03-24 11:07:55 +01:00
Sam Brannen 7f7f7c5f5b Invoke assertIsMock() before configuring mocks 2026-03-24 11:07:38 +01:00
Brian Clozel dc8b9fff57 Merge branch '7.0.x' 2026-03-24 10:26:04 +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
Yanming Zhou 0eba6f0da3 Add typesafe method to get generic bean by name with type reference
Fix GH-34687

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

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

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

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

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

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

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

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

Closes gh-35938
2026-03-13 15:24:38 +01:00
Sam Brannen 32d1b83f62 Override Servlet 6.1's doPatch() method in FrameworkServlet
See gh-12640
See gh-14975
See gh-36258
Closes gh-36247
2026-03-13 15:17:04 +01:00
Brian Clozel 227bc196b1 Consider 7.0.x branch for Antora UI upgrades 2026-03-13 15:02:25 +01:00
Brian Clozel 2fcef050e0 Build 7.1.0-SNAPSHOT 2026-03-13 14:50:12 +01:00
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
1508 changed files with 25856 additions and 14195 deletions
@@ -27,7 +27,7 @@ jobs:
/**/framework-api-*.zip::zip.name=spring-framework,zip.deployed=false
/**/framework-api-*-docs.zip::zip.type=docs
/**/framework-api-*-schema.zip::zip.type=schema
build-name: 'spring-framework-7.0.x'
build-name: 'spring-framework-7.1.x'
folder: 'deployment-repository'
password: ${{ secrets.ARTIFACTORY_PASSWORD }}
repository: 'libs-snapshot-local'
+2 -2
View File
@@ -2,8 +2,8 @@ name: Release Milestone
on:
push:
tags:
- v7.0.0-M[1-9]
- v7.0.0-RC[1-9]
- v7.1.0-M[1-9]
- v7.1.0-RC[1-9]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
+1 -1
View File
@@ -2,7 +2,7 @@ name: Release
on:
push:
tags:
- v7.0.[0-9]+
- v7.1.[0-9]+
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
@@ -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
+1
View File
@@ -9,6 +9,7 @@ ivy-cache
argfile*
activemq-data/
classes/
.cursor/
# Log files
jxl.log
+1 -1
View File
@@ -1,4 +1,4 @@
# Contributing to the Spring Framework
# Contributing to the Spring Framework
First off, thank you for taking the time to contribute! :+1: :tada:
+5 -3
View File
@@ -61,7 +61,9 @@ configure([rootProject] + javaProjects) { project ->
testImplementation("org.junit.platform:junit-platform-suite")
testImplementation("org.mockito:mockito-core")
testImplementation("org.mockito:mockito-junit-jupiter")
testImplementation("io.mockk:mockk")
testImplementation("io.mockk:mockk") {
exclude group: 'junit', module: 'junit'
}
testImplementation("org.assertj:assertj-core")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
testRuntimeOnly("org.apache.logging.log4j:log4j-core")
@@ -72,11 +74,11 @@ configure([rootProject] + javaProjects) { project ->
//"https://jakarta.ee/specifications/platform/11/apidocs/",
"https://docs.hibernate.org/orm/7.2/javadocs/",
"https://www.quartz-scheduler.org/api/2.3.0/",
"https://hc.apache.org/httpcomponents-client-5.6.x/current/httpclient5/apidocs/",
"https://hc.apache.org/httpcomponents-client-5.6.x/5.6/httpclient5/apidocs/",
"https://projectreactor.io/docs/core/release/api/",
"https://projectreactor.io/docs/test/release/api/",
"https://junit.org/junit4/javadoc/4.13.2/",
"https://docs.junit.org/6.0.3/api/",
"https://docs.junit.org/6.1.0/api/",
"https://www.reactive-streams.org/reactive-streams-1.0.4-javadoc/",
"https://r2dbc.io/spec/1.0.0.RELEASE/api/",
"https://jspecify.dev/docs/api/"
+1 -1
View File
@@ -20,7 +20,7 @@ ext {
dependencies {
checkstyle "io.spring.javaformat:spring-javaformat-checkstyle:${javaFormatVersion}"
implementation "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}"
implementation "org.jetbrains.dokka:dokka-gradle-plugin:2.1.0"
implementation "org.jetbrains.dokka:dokka-gradle-plugin:2.2.0"
implementation "com.tngtech.archunit:archunit:1.4.1"
implementation "org.gradle:test-retry-gradle-plugin:1.6.2"
implementation "io.spring.javaformat:spring-javaformat-gradle-plugin:${javaFormatVersion}"
@@ -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("13.3.0");
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();
+3 -3
View File
@@ -15,8 +15,8 @@ content:
# https://docs.antora.org/antora/latest/playbook/content-refname-matching/
# branches: We include snapshots for main, 6.2.x, and 7.0.x to 9.*.x.
branches: ['main', '6.2.x', '{7..9}.+({0..9}).x']
# tags: We effectively include all releases from 6.0.9 to 9.*.*.
tags: ['v{6..9}.+({0..9}).+({0..9})?(-{RC,M}*)', '!(v6.0.{0..8})', '!(v6.0.0-{RC,M}{0..9})']
# tags: include all releases from 6.2.0 to 9.*.*.
tags: ['v6.2.+({0..9})', 'v{7..9}.+({0..9}).+({0..9})?(-{RC,M}*)']
start_path: framework-docs
asciidoc:
extensions:
@@ -38,4 +38,4 @@ runtime:
failure_level: warn
ui:
bundle:
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.25/ui-bundle.zip
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.26/ui-bundle.zip
+4
View File
@@ -20,6 +20,10 @@ antora {
]
}
node {
version = '24.15.0'
}
tasks.named("generateAntoraYml") {
asciidocAttributes = project.provider( {
return ["spring-version": project.version ]
+2
View File
@@ -198,6 +198,7 @@
*** xref:web/webmvc/mvc-uri-building.adoc[]
*** xref:web/webmvc/mvc-ann-async.adoc[]
*** xref:web/webmvc/mvc-range.adoc[]
*** xref:web/webmvc/mvc-data-binding.adoc[]
*** xref:web/webmvc-cors.adoc[]
*** xref:web/webmvc-versioning.adoc[]
*** xref:web/webmvc/mvc-ann-rest-exceptions.adoc[]
@@ -295,6 +296,7 @@
*** xref:web/webflux-functional.adoc[]
*** xref:web/webflux/uri-building.adoc[]
*** xref:web/webflux/range.adoc[]
*** xref:web/webflux/data-binding.adoc[]
*** xref:web/webflux-cors.adoc[]
*** xref:web/webflux-versioning.adoc[]
*** xref:web/webflux/ann-rest-exceptions.adoc[]
@@ -74,6 +74,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
@@ -138,6 +143,11 @@ xref:testing/testcontext-framework/ctx-management/context-pausing.adoc[Context P
in the _Spring TestContext Framework_. See
xref:testing/testcontext-framework/ctx-management/failure-threshold.adoc[Context Failure Threshold].
| `spring.test.extension.context.scope`
| The default _extension context scope_ used by the `SpringExtension` in `@Nested` test
class hierarchies. See
xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-springextensionconfig[`@SpringExtensionConfig`].
| `spring.test.enclosing.configuration`
| The default _enclosing configuration inheritance mode_ to use if
`@NestedTestConfiguration` is not present on a test class. See
@@ -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.
@@ -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:
@@ -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.
@@ -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
@@ -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,7 +541,6 @@ following kinds of expressions cannot be compiled.
* Expressions relying on the conversion service
* Expressions using custom resolvers
* Expressions using overloaded operators
* Expressions using `Optional` with the null-safe or Elvis operator
* Expressions using array construction syntax
* Expressions using selection or projection
* Expressions using bean references
@@ -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.
@@ -270,7 +270,7 @@ is applicable for typical implementations of indexed structures.
NOTE: `ReflectiveIndexAccessor` also implements `CompilableIndexAccessor` in order to
support xref:core/expressions/evaluation.adoc#expressions-spel-compilation[compilation]
to bytecode for read access. Note, however, that the configured read-method must be
invokable via a `public` class or `public` interface for compilation to succeed.
invocable via a `public` class or `public` interface for compilation to succeed.
The following code listings define a `Color` enum and `FruitMap` type that behaves like a
map but does not implement the `java.util.Map` interface. Thus, if you want to index into
@@ -25,7 +25,8 @@ public void sendNotification() {
By default, the method invocation will be retried for any exception thrown: with at most
3 retry attempts (`maxRetries = 3`) after an initial failure, and a delay of 1 second
between attempts.
between attempts. If all attempts have failed and the retry policy has been exhausted,
the last original exception from the target method will be propagated to the caller.
[NOTE]
====
@@ -69,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()`.
@@ -200,7 +208,7 @@ If you only need to customize the number of retry attempts, you can use the
----
var retryTemplate = new RetryTemplate(RetryPolicy.withMaxRetries(4)); // <1>
retryTemplate.execute(
retryTemplate.invoke(
() -> jmsClient.destination("notifications").send(...));
----
<1> Explicitly uses `RetryPolicy.withMaxRetries(4)`.
@@ -218,7 +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
@@ -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
@@ -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].
@@ -402,6 +402,27 @@ To serialize only a subset of the object properties, you can specify a {baeldung
.toBodilessEntity();
----
==== URL encoded Forms
URL encoded forms, using the `"application/x-www-form-urlencoded"` media type, are useful for sending String key/values over the wire.
This is supported by the `FormHttpMessageConverter`, if the application uses a `MultiValueMap<String, String>` as source instance
or a target type.
For example:
[source,java,indent=0,subs="verbatim"]
----
MultiValueMap<String, String> form = new LinkedMultiValueMap<>();
form.add("project", "Spring Framework");
form.add("module", "spring-web");
ResponseEntity<Void> response = this.restClient.post()
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.body(form)
.retrieve()
.toBodilessEntity();
----
==== Multipart
To send multipart data, you need to provide a `MultiValueMap<String, Object>` whose values may be an `Object` for part content, a `Resource` for a file part, or an `HttpEntity` for part content with headers.
@@ -419,18 +440,70 @@ For example:
headers.setContentType(MediaType.APPLICATION_XML);
parts.add("xmlPart", new HttpEntity<>(myBean, headers));
// send using RestClient.post or RestTemplate.postForEntity
ResponseEntity<Void> response = this.restClient.post()
.contentType(MediaType.MULTIPART_FORM_DATA)
.body(parts)
.retrieve()
.toBodilessEntity();
----
In most cases, you do not have to specify the `Content-Type` for each part.
The content type is determined automatically based on the `HttpMessageConverter` chosen to serialize it or, in the case of a `Resource`, based on the file extension.
If necessary, you can explicitly provide the `MediaType` with an `HttpEntity` wrapper.
Once the `MultiValueMap` is ready, you can use it as the body of a `POST` request, using `RestClient.post().body(parts)` (or `RestTemplate.postForObject`).
The `Content-Type` is set to `multipart/form-data` by the `MultipartHttpMessageConverter`.
As seen in the previous section, `MultiValueMap` types can also be used for URL encoded forms.
It is preferable to explicitly set the media type in the `Content-Type` or `Accept` HTTP request headers to ensure that the expected
message converter is used.
`RestClient` can also receive multipart responses.
To decode a multipart response body, use a `ParameterizedTypeReference<MultiValueMap<String, Part>>`.
The decoded map contains `Part` instances where `FormFieldPart` represents form field values
and `FilePart` represents file parts with a `filename()` and a `transferTo()` method.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim"]
----
MultiValueMap<String, Part> result = this.restClient.get()
.uri("https://example.com/upload")
.accept(MediaType.MULTIPART_FORM_DATA)
.retrieve()
.body(new ParameterizedTypeReference<>() {});
Part field = result.getFirst("fieldPart");
if (field instanceof FormFieldPart formField) {
String fieldValue = formField.value();
}
Part file = result.getFirst("filePart");
if (file instanceof FilePart filePart) {
filePart.transferTo(Path.of("/tmp/" + filePart.filename()));
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
----
val result = this.restClient.get()
.uri("https://example.com/upload")
.accept(MediaType.MULTIPART_FORM_DATA)
.retrieve()
.body(object : ParameterizedTypeReference<MultiValueMap<String, Part>>() {})
val field = result?.getFirst("fieldPart")
if (field is FormFieldPart) {
val fieldValue = field.value()
}
val file = result?.getFirst("filePart")
if (file is FilePart) {
file.transferTo(Path.of("/tmp/" + file.filename()))
}
----
======
If the `MultiValueMap` contains at least one non-`String` value, the `Content-Type` is set to `multipart/form-data` by the `FormHttpMessageConverter`.
If the `MultiValueMap` has `String` values, the `Content-Type` defaults to `application/x-www-form-urlencoded`.
If necessary the `Content-Type` may also be set explicitly.
[[rest-request-factories]]
=== Client Request Factories
@@ -1179,7 +1252,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();
@@ -3,7 +3,7 @@
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`]
@@ -30,30 +30,39 @@ developer wishes to switch to test-class scoped semantics — the `SpringExtensi
configured to use a test-class scoped `ExtensionContext` by annotating a top-level test
class with `@SpringExtensionConfig(useTestClassScopedExtensionContext = true)`.
Alternatively, you can change the global default by setting the
`spring.test.extension.context.scope` property to `test_class`. The property is resolved
first via the xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism
which also supports JVM system properties — for example,
`-Dspring.test.extension.context.scope=test_class`. If the Spring property has not been
set, the `SpringExtension` will attempt to resolve the property as a
https://docs.junit.org/current/running-tests/configuration-parameters.html[JUnit Platform configuration parameter]
as a fallback mechanism. If the property has not been set via either of those mechanisms,
the `SpringExtension` will use a test-method scoped extension context by default. Note,
however, that a `@SpringExtensionConfig` declaration always takes precedence over this
property.
[TIP]
====
If your top-level test class is configured to use JUnit Jupiter's
`@TestInstance(Lifecycle.PER_CLASS)` semantics, the `SpringExtension` will always use a
test-class scoped `ExtensionContext`, and there is no need to declare
`@SpringExtensionConfig(useTestClassScopedExtensionContext = true)`.
If a test class uses JUnit Jupiter's `@TestInstance(Lifecycle.PER_CLASS)` semantics, the
`SpringExtension` will always use a test-class scoped `ExtensionContext`, and
configuration via `@SpringExtensionConfig(useTestClassScopedExtensionContext = true)` or
the `spring.test.extension.context.scope` property will have no effect for that test
class.
====
[NOTE]
====
This annotation is currently only applicable to `@Nested` test class hierarchies and
should be applied to the top-level enclosing class of a `@Nested` test class hierarchy.
Consequently, there is no need to declare this annotation on a test class that does not
contain `@Nested` test classes.
====
[NOTE]
====
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.
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]]
@@ -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
@@ -12,18 +12,20 @@ The annotations can be applied in the following ways.
* On a non-static field in a test class or any of its superclasses.
* On a non-static field in an enclosing class for a `@Nested` test class or in any class
in the type hierarchy or enclosing class hierarchy above the `@Nested` test class.
* On a parameter in the constructor for a test class.
* At the type level on a test class or any superclass or implemented interface in the
type hierarchy above the test class.
* At the type level on an enclosing class for a `@Nested` test class or on any class or
interface in the type hierarchy or enclosing class hierarchy above the `@Nested` test
class.
When `@MockitoBean` or `@MockitoSpyBean` is declared on a field, the bean to mock or spy
is inferred from the type of the annotated field. If multiple candidates exist in the
`ApplicationContext`, a `@Qualifier` annotation can be declared on the field to help
disambiguate. In the absence of a `@Qualifier` annotation, the name of the annotated
field will be used as a _fallback qualifier_. Alternatively, you can explicitly specify a
bean name to mock or spy by setting the `value` or `name` attribute in the annotation.
When `@MockitoBean` or `@MockitoSpyBean` is declared on a field or constructor parameter,
the bean to mock or spy is inferred from the type of the annotated field or parameter. If
multiple candidates exist in the `ApplicationContext`, a `@Qualifier` annotation can be
declared on the field or parameter to help disambiguate. In the absence of a `@Qualifier`
annotation, the name of the annotated field or parameter will be used as a _fallback
qualifier_. Alternatively, you can explicitly specify a bean name to mock or spy by
setting the `value` or `name` attribute in the annotation.
When `@MockitoBean` or `@MockitoSpyBean` is declared at the type level, the type of bean
(or beans) to mock or spy must be supplied via the `types` attribute in the annotation
@@ -140,6 +142,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 +185,98 @@ 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 example shows how to use `@MockitoBean` on a constructor parameter for a
by-type lookup.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
@SpringJUnitConfig(TestConfig.class)
class BeanOverrideTests {
private final CustomService customService;
BeanOverrideTests(@MockitoBean CustomService customService) { // <1>
this.customService = customService;
}
// tests...
}
----
<1> Replace the bean with type `CustomService` with a Mockito mock and inject it into
the constructor.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@SpringJUnitConfig(TestConfig::class)
class BeanOverrideTests(@MockitoBean val customService: CustomService) { // <1>
// tests...
}
----
<1> Replace the bean with type `CustomService` with a Mockito mock and inject it into
the constructor.
======
The following example shows how to use `@MockitoBean` on a constructor parameter for a
by-name lookup.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
@SpringJUnitConfig(TestConfig.class)
class BeanOverrideTests {
private final CustomService customService;
BeanOverrideTests(@MockitoBean("service") CustomService customService) { // <1>
this.customService = customService;
}
// tests...
}
----
<1> Replace the bean named `service` with a Mockito mock and inject it into the
constructor.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@SpringJUnitConfig(TestConfig::class)
class BeanOverrideTests(@MockitoBean("service") val customService: CustomService) { // <1>
// tests...
}
----
<1> Replace the bean named `service` with a Mockito mock and inject it into the
constructor.
======
The following `@SharedMocks` annotation registers two mocks by-type and one mock by-name.
@@ -187,6 +296,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 +339,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 +396,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 +436,95 @@ 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 example shows how to use `@MockitoSpyBean` on a constructor parameter for
a by-type lookup.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
@SpringJUnitConfig(TestConfig.class)
class BeanOverrideTests {
private final CustomService customService;
BeanOverrideTests(@MockitoSpyBean CustomService customService) { // <1>
this.customService = customService;
}
// tests...
}
----
<1> Wrap the bean with type `CustomService` with a Mockito spy and inject it into the
constructor.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@SpringJUnitConfig(TestConfig::class)
class BeanOverrideTests(@MockitoSpyBean val customService: CustomService) { // <1>
// tests...
}
----
<1> Wrap the bean with type `CustomService` with a Mockito spy and inject it into the
constructor.
======
The following example shows how to use `@MockitoSpyBean` on a constructor parameter for
a by-name lookup.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
@SpringJUnitConfig(TestConfig.class)
class BeanOverrideTests {
private final CustomService customService;
BeanOverrideTests(@MockitoSpyBean("service") CustomService customService) { // <1>
this.customService = customService;
}
// tests...
}
----
<1> Wrap the bean named `service` with a Mockito spy and inject it into the constructor.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@SpringJUnitConfig(TestConfig::class)
class BeanOverrideTests(@MockitoSpyBean("service") val customService: CustomService) { // <1>
// tests...
}
----
<1> Wrap the bean named `service` with a Mockito spy and inject it into the constructor.
======
The following `@SharedSpies` annotation registers two spies by-type and one spy by-name.
@@ -290,6 +544,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 +587,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
@@ -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]
@@ -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
@@ -142,6 +142,9 @@ provides two alternative ways to verify the response:
1. xref:resttestclient-workflow[Built-in Assertions] extend the request workflow with a chain of expectations
2. xref:resttestclient-assertj[AssertJ Integration] to verify the response via `assertThat()` statements
TIP: See the xref:integration/rest-clients.adoc#rest-message-conversion[HTTP Message Conversion]
section for examples on how to prepare a request with any content, including form data and multipart data.
[[resttestclient.workflow]]
@@ -213,6 +216,16 @@ To verify JSON content with https://github.com/jayway/JsonPath[JSONPath]:
include-code::./JsonTests[tag=jsonPath,indent=0]
[[resttestclient.multipart]]
==== Multipart Content
When testing endpoints that return multipart responses, you can decode the body to a
`MultiValueMap<String, Part>` and assert individual parts using the `FormFieldPart`
and `FilePart` subtypes.
include-code::./MultipartTests[tag=multipart,indent=0]
[[resttestclient.assertj]]
=== AssertJ Integration
@@ -2,8 +2,9 @@
= Bean Overriding in Tests
Bean overriding in tests refers to the ability to override specific beans in the
`ApplicationContext` for a test class, by annotating the test class or one or more
non-static fields in the test class.
`ApplicationContext` for a test class, by annotating the test class, one or more
non-static fields in the test class, or one or more parameters in the constructor for the
test class.
NOTE: This feature is intended as a less risky alternative to the practice of registering
a bean via `@Bean` with the `DefaultListableBeanFactory`
@@ -42,9 +43,9 @@ The `spring-test` module registers implementations of the latter two
{spring-framework-code}/spring-test/src/main/resources/META-INF/spring.factories[`META-INF/spring.factories`
properties file].
The bean overriding infrastructure searches for annotations on test classes as well as
annotations on non-static fields in test classes that are meta-annotated with
`@BeanOverride` and instantiates the corresponding `BeanOverrideProcessor` which is
The bean overriding infrastructure searches for annotations on test classes, non-static
fields in test classes, and parameters in test class constructors that are meta-annotated
with `@BeanOverride`, and instantiates the corresponding `BeanOverrideProcessor` which is
responsible for creating an appropriate `BeanOverrideHandler`.
The internal `BeanOverrideBeanFactoryPostProcessor` then uses bean override handlers to
@@ -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.
@@ -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)
}
}
----
======
@@ -179,6 +179,10 @@ If a specific parameter in a constructor for a JUnit Jupiter test class is of ty
`ApplicationContext` (or a sub-type thereof) or is annotated or meta-annotated with
`@Autowired`, `@Qualifier`, or `@Value`, Spring injects the value for that specific
parameter with the corresponding bean or value from the test's `ApplicationContext`.
Similarly, if a specific parameter is annotated with `@MockitoBean` or `@MockitoSpyBean`,
Spring will inject a Mockito mock or spy, respectively &mdash; see
xref:testing/annotations/integration-spring/annotation-mockitobean.adoc[`@MockitoBean` and `@MockitoSpyBean`]
for details.
Spring can also be configured to autowire all arguments for a test class constructor if
the constructor is considered to be _autowirable_. A constructor is considered to be
@@ -396,11 +400,10 @@ recursively.
====
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`.
See the documentation for
xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-springextensionconfig[`@SpringExtensionConfig`]
for details.
`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]
@@ -580,8 +580,8 @@ Kotlin::
[[webtestclient-stream]]
==== Streaming Responses
To test potentially infinite streams such as `"text/event-stream"` or
`"application/x-ndjson"`, start by verifying the response status and headers, and then
To test potentially infinite streams such as `"text/event-stream"`,
`"application/jsonl"` or `"application/x-ndjson"`, start by verifying the response status and headers, and then
obtain a `FluxExchangeResult`:
[tabs]
@@ -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].
@@ -0,0 +1,36 @@
[[webflux-data-binding]]
= Data Binding
:page-section-summary-toc: 1
[.small]#xref:web/webmvc/mvc-data-binding.adoc[See equivalent in the Servlet stack]#
Data binding is a mechanism that binds string parameters onto an object graph with type conversion.
It is a core mechanism of the Spring Framework that helps with application configuration.
In web applications it makes it easy to access query parameters and form data through richly typed objects rather than through maps of string values.
To learn more about the data binding mechanism, including constructor and setter binding, property name syntax, type conversion,
and more, see xref:core/validation/data-binding.adoc[Data binding] in the Core Technologies section.
For annotated controllers, data binding applies to a
xref:web/webflux/controller/ann-methods/modelattrib-method-args.adoc[@ModelAttribute] method argument.
For functional endpoints, use the `bind` method of xref:web/webflux-functional.adoc#webflux-fn-request[ServerRequest].
TIP: For browser applications with annotated controllers, you can use
xref:web/webflux/controller/ann-modelattrib-methods.adoc[@ModelAttribute methods]
to initialize additional model attributes for use in rendered views.
Each request uses a separate `WebDataBinder` instance.
For annotated controllers, this instance can be customized through
xref:web/webflux/controller/ann-initbinder.adoc[@InitBinder methods] within a controller, or
across controllers through xref:web/webmvc/mvc-controller/ann-advice.adoc[Controller Advice].
For functional endpoints, use overloaded `ServerRequest.bind` methods.
[[webflux-data-binding-design]]
== Model Design
[.small]#xref:web/webmvc/mvc-data-binding.adoc#mvc-data-binding-design[See equivalent in the Servlet stack]#
include::partial$web/web-data-binding-model-design.adoc[]
@@ -485,8 +485,8 @@ The `JacksonJsonEncoder` works as follows:
* For a multi-value publisher with `application/json`, by default collect the values with
`Flux#collectToList()` and then serialize the resulting collection.
* For a multi-value publisher with a streaming media type such as
`application/x-ndjson` or `application/stream+x-jackson-smile`, encode, write, and
flush each value individually using a
`application/jsonl`, `application/x-ndjson` or `application/stream+x-jackson-smile`,
encode, write, and flush each value individually using a
https://en.wikipedia.org/wiki/JSON_streaming[line-delimited JSON] format. Other
streaming media types may be registered with the encoder.
* For SSE the `JacksonJsonEncoder` is invoked per event and the output is flushed to ensure
@@ -598,7 +598,7 @@ To configure all three in WebFlux, you'll need to supply a pre-configured instan
[.small]#xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-http-streaming[See equivalent in the Servlet stack]#
When streaming to the HTTP response (for example, `text/event-stream`,
`application/x-ndjson`), it is important to send data periodically, in order to
`application/jsonl`, `application/x-ndjson`), it is important to send data periodically, in order to
reliably detect a disconnected client sooner rather than later. Such a send could be a
comment-only, empty SSE event or any other "no-op" data that would effectively serve as
a heartbeat.
@@ -182,7 +182,7 @@ The parameters to any of the above macros have consistent meanings:
For strictly sorted maps, you can use a `SortedMap` (such as a `TreeMap`) with a
suitable `Comparator` and, for arbitrary Maps that should return values in insertion
order, use a `LinkedHashMap` or a `LinkedMap` from `commons-collections`.
* `separator`: Where multiple options are available as discreet elements (radio buttons
* `separator`: Where multiple options are available as discrete elements (radio buttons
or checkboxes), the sequence of characters used to separate each one in the list
(such as `<br>`).
* `attributes`: An additional string of arbitrary tags or text to be included within
@@ -23,13 +23,17 @@ For all converters, a default media type is used, but you can override it by set
By default, this converter supports all text media types(`text/{asterisk}`) and writes with a `Content-Type` of `text/plain`.
| `FormHttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write form data from the HTTP request and response.
| An `HttpMessageConverter` implementation that can read and write URL encoded forms.
By default, this converter reads and writes the `application/x-www-form-urlencoded` media type.
Form data is read from and written into a `MultiValueMap<String, String>`.
The converter can also write (but not read) multipart data read from a `MultiValueMap<String, Object>`.
By default, `multipart/form-data` is supported.
Additional multipart subtypes can be supported for writing form data.
Consult the javadoc for `FormHttpMessageConverter` for further details.
`Map<String, String>` is also supported, but multiple values under the same key will be ignored.
| `MultipartHttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write multipart messages.
`MultiValueMap<String, Object>` can be written to multipart messages, converting each part independently using
the configured message converters. Multipart messages can be read into `MultiValueMap<String, Part>`, each value
being a `Part` or one of its subtypes (`FormFieldPart` and `FilePart`).
By default, `multipart/form-data` is supported. Additional multipart subtypes can be supported for writing form data.
| `ByteArrayHttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write byte arrays from the HTTP request and response.
@@ -423,8 +423,8 @@ Reactive return values are handled as follows:
* A single-value promise is adapted to, similar to using `DeferredResult`. Examples
include `CompletionStage` (JDK), `Mono` (Reactor), and `Single` (RxJava).
* A multi-value stream with a streaming media type (such as `application/x-ndjson`
or `text/event-stream`) is adapted to, similar to using `ResponseBodyEmitter` or
* A multi-value stream with a streaming media type (such as `application/jsonl`,
`application/x-ndjson` or `text/event-stream`) is adapted to, similar to using `ResponseBodyEmitter` or
`SseEmitter`. Examples include `Flux` (Reactor) or `Observable` (RxJava).
Applications can also return `Flux<ServerSentEvent>` or `Observable<ServerSentEvent>`.
* A multi-value stream with any other media type (such as `application/json`) is adapted
@@ -188,7 +188,7 @@ the content negotiation during the error handling phase will decide which conten
| `View`
| A `View` instance to use for rendering together with the implicit model -- determined
through command objects and `@ModelAttribute` methods. The handler method may also
programmatically enrich the model by declaring a `Model` argument (descried earlier).
programmatically enrich the model by declaring a `Model` argument (described earlier).
| `java.util.Map`, `org.springframework.ui.Model`
| Attributes to be added to the implicit model with the view name implicitly determined
@@ -107,7 +107,4 @@ Kotlin::
[[mvc-ann-initbinder-model-design]]
== Model Design
[.small]#xref:web/webflux/controller/ann-initbinder.adoc#webflux-ann-initbinder-model-design[See equivalent in the Reactive stack]#
include::partial$web/web-data-binding-model-design.adoc[]
NOTE: For more guidance on model design, please see xref:web/webmvc/mvc-data-binding.adoc[Data Binding].
@@ -0,0 +1,34 @@
[[mvc-data-binding]]
= Data Binding
:page-section-summary-toc: 1
[.small]#xref:web/webflux/data-binding.adoc[See equivalent in the Reactive stack]#
Data binding is a mechanism that binds string parameters onto an object graph with type conversion.
It is a core mechanism of the Spring Framework that helps with application configuration.
In web applications it makes it easy to access query parameters and form data through richly typed objects rather than through maps of string values.
To learn more about the data binding mechanism, including constructor and setter binding, property name syntax, type conversion,
and more, see xref:core/validation/data-binding.adoc[Data binding] in the Core Technologies section.
For annotated controllers, data binding applies to a
xref:web/webmvc/mvc-controller/ann-methods/modelattrib-method-args.adoc[@ModelAttribute] method argument.
For functional endpoints, use the `bind` method of xref:web/webmvc-functional.adoc#webmvc-fn-request[ServerRequest].
TIP: For browser applications with annotated controllers, you can use
xref:web/webmvc/mvc-controller/ann-modelattrib-methods.adoc[@ModelAttribute methods]
to initialize additional model attributes for use in rendered views.
Each request uses a separate `WebDataBinder` instance.
For annotated controllers, this instance can be customized through
xref:web/webmvc/mvc-controller/ann-initbinder.adoc[@InitBinder methods] within a controller, or
across controllers through xref:web/webmvc/mvc-controller/ann-advice.adoc[Controller Advice].
For functional endpoints, use overloaded `ServerRequest.bind` methods.
[[mvc-data-binding-design]]
== Model Design
[.small]#xref:web/webflux/data-binding.adoc#webflux-data-binding-design[See equivalent in the Reactive stack]#
include::partial$web/web-data-binding-model-design.adoc[]
@@ -257,7 +257,7 @@ An `XhrTransport`, by definition, supports both `xhr-streaming` and `xhr-polling
from a client perspective, there is no difference other than in the URL used to connect
to the server. At present there are two implementations:
* `RestTemplateXhrTransport` uses Spring's `RestTemplate` for HTTP requests.
* `RestClientXhrTransport` uses Spring's `RestClient` for HTTP requests.
* `JettyXhrTransport` uses Jetty's `HttpClient` for HTTP requests.
The following example shows how to create a SockJS client and connect to a SockJS endpoint:
@@ -266,7 +266,7 @@ The following example shows how to create a SockJS client and connect to a SockJ
----
List<Transport> transports = new ArrayList<>(2);
transports.add(new WebSocketTransport(new StandardWebSocketClient()));
transports.add(new RestTemplateXhrTransport());
transports.add(new RestClientXhrTransport());
SockJsClient sockJsClient = new SockJsClient(transports);
sockJsClient.doHandshake(new MyWebSocketHandler(), "ws://example.com:8080/sockjs");
@@ -1,91 +1,53 @@
xref:core/validation/data-binding.adoc[Data binding] for web requests involves
binding request parameters to a model object. By default, request parameters can be bound
to any public property of the model object, which means malicious clients can provide
extra values for properties that exist in the model object graph, but are not expected to
be set. This is why model object design requires careful consideration.
Data binding involves binding untrusted input onto application objects.
For security reasons, it's crucial to ensure that input is properly constrained to expected fields only.
This section provides guidance for safe binding.
TIP: The model object, and its nested object graph is also sometimes referred to as a
_command object_, _form-backing object_, or _POJO_ (Plain Old Java Object).
First, prefer **immutable object design** for web binding purposes.
It is safe because a constructor naturally constrains binding to expected inputs.
You can use a Java record or a class with a primary constructor, and either can have further nested objects.
See xref:core/validation/data-binding.adoc#data-binding-constructor-binding[Constructor Binding] for details.
A good practice is to use a _dedicated model object_ rather than exposing your domain
model such as JPA or Hibernate entities for web data binding. For example, on a form to
change an email address, create a `ChangeEmailForm` model object that declares only
the properties required for the input:
Another option for safe binding is to use **dedicated objects** designed for the expected input.
Such objects, even if mutable, are safe because they constrain binding to the expected inputs.
[source,java,indent=0,subs="verbatim,quotes"]
----
public class ChangeEmailForm {
private String oldEmailAddress;
private String newEmailAddress;
public void setOldEmailAddress(String oldEmailAddress) {
this.oldEmailAddress = oldEmailAddress;
}
public String getOldEmailAddress() {
return this.oldEmailAddress;
}
public void setNewEmailAddress(String newEmailAddress) {
this.newEmailAddress = newEmailAddress;
}
public String getNewEmailAddress() {
return this.newEmailAddress;
}
}
----
Another good practice is to apply
xref:core/validation/data-binding.adoc#data-binding-constructor-binding[constructor binding],
which uses only the request parameters it needs for constructor arguments, and any other
input is ignored. This is in contrast to property binding which by default binds every
request parameter for which there is a matching property.
If neither a dedicated model object nor constructor binding is sufficient, and you must
use property binding, we strongly recommend registering `allowedFields` patterns (case
sensitive) on `WebDataBinder` in order to prevent unexpected properties from being set.
Domain objects such as JPA or Hibernate entities are generally not safe for web binding
as they likely contain more properties than the expected inputs.
For such cases, it's crucial to declare the properties to expose for binding.
For example:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Controller
public class ChangeEmailController {
public class PersonController {
@InitBinder
void initBinder(WebDataBinder binder) {
binder.setAllowedFields("oldEmailAddress", "newEmailAddress");
// See Javadoc for supported pattern syntax
binder.setAllowedFields("firstName", "lastName", "*Address");
}
// @RequestMapping methods, etc.
}
----
You can also register `disallowedFields` patterns (case insensitive). However,
"allowed" configuration is preferred over "disallowed" as it is more explicit and less
prone to mistakes.
NOTE: It is also possible to configure `disallowedFields`, but that's fragile, and
due to be https://github.com/spring-projects/spring-framework/issues/36802[deprecated] in Spring Framework 7.1.
It is easy to overlook fields or introduce additional fields over time that should also be excluded.
By default, constructor and property binding are both used. If you want to use
constructor binding only, you can set the `declarativeBinding` flag on `WebDataBinder`
through an `@InitBinder` method either locally within a controller or globally through an
`@ControllerAdvice`. Turning this flag on ensures that only constructor binding is used
and that property binding is not used unless `allowedFields` patterns are configured.
For example:
By default, `DataBinder` applies both constructor and setter binding.
This is fine with immutable objects and dedicated objects, but for domain objects, you must
remember to set `allowedFields`. To ensure data binding is only used in declarative style where
expected inputs are explicitly declared, you can set `declarativeBinding` on `DataBinder`.
That applies constructor binding always, and setter binding conditionally if `allowedFields` is set.
The following shows how to set this flag globally, or
you can also narrow it through attributes on `ControllerAdvice`:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Controller
public class MyController {
@ControllerAdvice
public class ControllerConfig {
@InitBinder
void initBinder(WebDataBinder binder) {
binder.setDeclarativeBinding(true);
}
// @RequestMapping methods, etc.
}
----
+5 -5
View File
@@ -1,11 +1,11 @@
{
"dependencies": {
"antora": "3.2.0-alpha.11",
"antora": "3.2.0-alpha.12",
"@antora/atlas-extension": "1.0.0-alpha.5",
"@antora/collector-extension": "1.0.2",
"@antora/collector-extension": "1.0.3",
"@asciidoctor/tabs": "1.0.0-beta.6",
"@springio/antora-extensions": "1.14.7",
"fast-xml-parser": "5.3.8",
"@springio/asciidoctor-extensions": "1.0.0-alpha.17"
"@springio/antora-extensions": "1.14.12",
"fast-xml-parser": "5.7.0",
"@springio/asciidoctor-extensions": "1.0.0-alpha.18"
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -0,0 +1,54 @@
/*
* Copyright 2025-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.docs.testing.resttestclient.multipart;
import org.junit.jupiter.api.Test;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.MediaType;
import org.springframework.http.converter.multipart.FilePart;
import org.springframework.http.converter.multipart.FormFieldPart;
import org.springframework.http.converter.multipart.Part;
import org.springframework.test.web.servlet.client.RestTestClient;
import org.springframework.util.MultiValueMap;
import static org.assertj.core.api.Assertions.assertThat;
public class MultipartTests {
RestTestClient client;
@Test
void multipart() {
// tag::multipart[]
client.get().uri("/upload")
.accept(MediaType.MULTIPART_FORM_DATA)
.exchange()
.expectStatus().isOk()
.expectBody(new ParameterizedTypeReference<MultiValueMap<String, Part>>() {})
.value(result -> {
Part field = result.getFirst("fieldPart");
assertThat(field).isInstanceOfSatisfying(FormFieldPart.class,
formField -> assertThat(formField.value()).isEqualTo("fieldValue"));
Part file = result.getFirst("filePart");
assertThat(file).isInstanceOfSatisfying(FilePart.class,
filePart -> assertThat(filePart.filename()).isEqualTo("logo.png"));
});
// end::multipart[]
}
}
@@ -29,7 +29,7 @@ public class ExceptionController {
// tag::narrow[]
@ExceptionHandler({FileSystemException.class, RemoteException.class})
public ResponseEntity<String> handleIoException(IOException ex) {
public ResponseEntity<String> handleIOException(IOException ex) {
return ResponseEntity.internalServerError().body(ex.getMessage());
}
// end::narrow[]
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -54,6 +54,6 @@ class ConcurrentOperationExecutor : Ordered {
lockFailureException = ex
}
} while (numAttempts <= this.maxRetries)
throw lockFailureException!!
throw lockFailureException
}
} // end::snippet[]
@@ -27,7 +27,7 @@ class ExceptionController {
// tag::narrow[]
@ExceptionHandler(FileSystemException::class, RemoteException::class)
fun handleIoException(ex: IOException): ResponseEntity<String> {
fun handleIOException(ex: IOException): ResponseEntity<String> {
return ResponseEntity.internalServerError().body(ex.message)
}
// end::narrow[]
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2025 the original author or authors.
* Copyright 2002-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
+20 -21
View File
@@ -7,31 +7,31 @@ javaPlatform {
}
dependencies {
api(platform("com.fasterxml.jackson:jackson-bom:2.20.2"))
api(platform("io.micrometer:micrometer-bom:1.16.4"))
api(platform("io.netty:netty-bom:4.2.10.Final"))
api(platform("io.projectreactor:reactor-bom:2025.0.4"))
api(platform("com.fasterxml.jackson:jackson-bom:2.21.2"))
api(platform("io.micrometer:micrometer-bom:1.16.6"))
api(platform("io.netty:netty-bom:4.2.15.Final"))
api(platform("io.projectreactor:reactor-bom:2025.0.6"))
api(platform("io.rsocket:rsocket-bom:1.1.5"))
api(platform("org.apache.groovy:groovy-bom:5.0.4"))
api(platform("org.apache.logging.log4j:log4j-bom:2.25.3"))
api(platform("org.apache.groovy:groovy-bom:5.0.6"))
api(platform("org.apache.logging.log4j:log4j-bom:2.26.0"))
api(platform("org.assertj:assertj-bom:3.27.7"))
api(platform("org.eclipse.jetty:jetty-bom:12.1.7"))
api(platform("org.eclipse.jetty.ee11:jetty-ee11-bom:12.1.7"))
api(platform("org.eclipse.jetty:jetty-bom:12.1.9"))
api(platform("org.eclipse.jetty.ee11:jetty-ee11-bom:12.1.9"))
api(platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.10.2"))
api(platform("org.jetbrains.kotlinx:kotlinx-serialization-bom:1.9.0"))
api(platform("org.junit:junit-bom:6.0.3"))
api(platform("org.mockito:mockito-bom:5.22.0"))
api(platform("tools.jackson:jackson-bom:3.0.4"))
api(platform("org.jetbrains.kotlinx:kotlinx-serialization-bom:1.11.0"))
api(platform("org.junit:junit-bom:6.1.0"))
api(platform("org.mockito:mockito-bom:5.23.0"))
api(platform("tools.jackson:jackson-bom:3.1.1"))
constraints {
api("com.fasterxml:aalto-xml:1.3.4")
api("com.fasterxml.woodstox:woodstox-core:6.7.0")
api("com.fasterxml.woodstox:woodstox-core:7.1.1")
api("com.github.ben-manes.caffeine:caffeine:3.2.3")
api("com.github.librepdf:openpdf:1.3.43")
api("com.google.code.findbugs:findbugs:3.0.1")
api("com.google.code.findbugs:jsr305:3.0.2")
api("com.google.code.gson:gson:2.13.2")
api("com.google.protobuf:protobuf-java-util:4.34.0")
api("com.google.protobuf:protobuf-java-util:4.34.1")
api("com.h2database:h2:2.4.240")
api("com.jayway.jsonpath:json-path:2.10.0")
api("com.networknt:json-schema-validator:1.5.3")
@@ -96,10 +96,10 @@ dependencies {
api("org.apache.httpcomponents.client5:httpclient5:5.6")
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.4.2")
api("org.apache.poi:poi-ooxml:5.5.1")
api("org.apache.tomcat.embed:tomcat-embed-core:11.0.18")
api("org.apache.tomcat.embed:tomcat-embed-websocket:11.0.18")
api("org.apache.tomcat:tomcat-util:11.0.18")
api("org.apache.tomcat:tomcat-websocket:11.0.18")
api("org.apache.tomcat.embed:tomcat-embed-core:11.0.22")
api("org.apache.tomcat.embed:tomcat-embed-websocket:11.0.22")
api("org.apache.tomcat:tomcat-util:11.0.22")
api("org.apache.tomcat:tomcat-websocket:11.0.22")
api("org.aspectj:aspectjrt:1.9.25")
api("org.aspectj:aspectjtools:1.9.25")
api("org.aspectj:aspectjweaver:1.9.25")
@@ -111,7 +111,7 @@ dependencies {
api("org.easymock:easymock:5.6.0")
api("org.eclipse.angus:angus-mail:2.0.3")
api("org.eclipse.jetty:jetty-reactive-httpclient:4.1.4")
api("org.eclipse.persistence:org.eclipse.persistence.jpa:5.0.0-RC1")
api("org.eclipse.persistence:org.eclipse.persistence.jpa:5.0.0")
api("org.eclipse:yasson:3.0.4")
api("org.ehcache:ehcache:3.10.8")
api("org.ehcache:jcache:1.0.1")
@@ -120,7 +120,7 @@ dependencies {
api("org.glassfish:jakarta.el:4.0.2")
api("org.graalvm.sdk:graal-sdk:22.3.1")
api("org.hamcrest:hamcrest:3.0")
api("org.hibernate.orm:hibernate-core:7.2.6.Final")
api("org.hibernate.orm:hibernate-core:7.4.0.Final")
api("org.hibernate.validator:hibernate-validator:9.1.0.Final")
api("org.hsqldb:hsqldb:2.7.4")
api("org.htmlunit:htmlunit:4.21.0")
@@ -138,7 +138,6 @@ dependencies {
api("org.seleniumhq.selenium:selenium-java:4.41.0")
api("org.skyscreamer:jsonassert:1.5.3")
api("org.testng:testng:7.12.0")
api("org.webjars:underscorejs:1.8.3")
api("org.webjars:webjars-locator-lite:1.1.0")
api("org.xmlunit:xmlunit-assertj:2.10.4")
api("org.xmlunit:xmlunit-matchers:2.10.4")
+2 -2
View File
@@ -1,10 +1,10 @@
version=7.0.6-SNAPSHOT
version=7.1.0-SNAPSHOT
org.gradle.caching=true
org.gradle.jvmargs=-Xmx2048m
org.gradle.parallel=true
kotlinVersion=2.2.21
kotlinVersion=2.3.20
byteBuddyVersion=1.17.6
kotlin.jvm.target.validation.mode=ignore
-13
View File
@@ -84,19 +84,6 @@ eclipse.classpath.file.whenMerged {
}
}
// Due to an apparent bug in Gradle, even though we exclude the "main" classpath
// entries for sources generated by XJC in spring-oxm.gradle, the Gradle eclipse
// plugin still includes them in the generated .classpath file. So, we have to
// manually remove those lingering "main" entries.
if (project.name == "spring-oxm") {
eclipse.classpath.file.whenMerged { classpath ->
classpath.entries.removeAll {
it.path =~ /build\/generated\/sources\/xjc\/.+/ &&
it.entryAttributes.get("gradle_scope") == "main"
}
}
}
// Include project specific settings
tasks.register('eclipseSettings', Copy) {
from rootProject.files(
Binary file not shown.
+3 -1
View File
@@ -1,7 +1,9 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip
networkTimeout=10000
retries=0
retryBackOffMs=500
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
+1 -1
View File
@@ -57,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/b631911858264c0b6e4d6603d677ff5218766cee/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
Vendored
+10 -21
View File
@@ -23,8 +23,8 @@
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Set local scope for the variables, and ensure extensions are enabled
setlocal EnableExtensions
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@@ -51,7 +51,7 @@ echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
"%COMSPEC%" /c exit 1
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
@@ -65,7 +65,7 @@ echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
"%COMSPEC%" /c exit 1
:execute
@rem Setup the command line
@@ -73,21 +73,10 @@ goto fail
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
@rem which allows us to clear the local environment before executing the java command
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
:exitWithErrorLevel
@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
"%COMSPEC%" /c exit %ERRORLEVEL%
@@ -61,10 +61,9 @@ class AopNamespaceHandlerScopeIntegrationTests {
@Test
void testSingletonScoping() throws Exception {
void singletonScoping() throws Exception {
assertThat(AopUtils.isAopProxy(singletonScoped)).as("Should be AOP proxy").isTrue();
boolean condition = singletonScoped instanceof TestBean;
assertThat(condition).as("Should be target class proxy").isTrue();
assertThat(singletonScoped).as("Should be target class proxy").isInstanceOf(TestBean.class);
String rob = "Rob Harrop";
String bram = "Bram Smeets";
assertThat(singletonScoped.getName()).isEqualTo(rob);
@@ -75,19 +74,17 @@ class AopNamespaceHandlerScopeIntegrationTests {
}
@Test
void testRequestScoping() {
void requestScoping() {
MockHttpServletRequest oldRequest = new MockHttpServletRequest();
MockHttpServletRequest newRequest = new MockHttpServletRequest();
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(oldRequest));
assertThat(AopUtils.isAopProxy(requestScoped)).as("Should be AOP proxy").isTrue();
boolean condition = requestScoped instanceof TestBean;
assertThat(condition).as("Should be target class proxy").isTrue();
assertThat(requestScoped).as("Should be target class proxy").isInstanceOf(TestBean.class);
assertThat(AopUtils.isAopProxy(testBean)).as("Should be AOP proxy").isTrue();
boolean condition1 = testBean instanceof TestBean;
assertThat(condition1).as("Regular bean should be JDK proxy").isFalse();
assertThat(testBean).as("Regular bean should be JDK proxy").isNotInstanceOf(TestBean.class);
String rob = "Rob Harrop";
String bram = "Bram Smeets";
@@ -103,7 +100,7 @@ class AopNamespaceHandlerScopeIntegrationTests {
}
@Test
void testSessionScoping() {
void sessionScoping() {
MockHttpSession oldSession = new MockHttpSession();
MockHttpSession newSession = new MockHttpSession();
@@ -112,14 +109,12 @@ class AopNamespaceHandlerScopeIntegrationTests {
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
assertThat(AopUtils.isAopProxy(sessionScoped)).as("Should be AOP proxy").isTrue();
boolean condition1 = sessionScoped instanceof TestBean;
assertThat(condition1).as("Should not be target class proxy").isFalse();
assertThat(sessionScoped).as("Should not be target class proxy").isNotInstanceOf(TestBean.class);
assertThat(sessionScopedAlias).isSameAs(sessionScoped);
assertThat(AopUtils.isAopProxy(testBean)).as("Should be AOP proxy").isTrue();
boolean condition = testBean instanceof TestBean;
assertThat(condition).as("Regular bean should be JDK proxy").isFalse();
assertThat(testBean).as("Regular bean should be JDK proxy").isNotInstanceOf(TestBean.class);
String rob = "Rob Harrop";
String bram = "Bram Smeets";
@@ -65,7 +65,7 @@ class AdvisorAutoProxyCreatorIntegrationTests {
}
@Test
void testDefaultExclusionPrefix() {
void defaultExclusionPrefix() {
DefaultAdvisorAutoProxyCreator aapc = (DefaultAdvisorAutoProxyCreator) getBeanFactory().getBean(ADVISOR_APC_BEAN_NAME);
assertThat(aapc.getAdvisorBeanNamePrefix()).isEqualTo((ADVISOR_APC_BEAN_NAME + DefaultAdvisorAutoProxyCreator.SEPARATOR));
assertThat(aapc.isUsePrefix()).isFalse();
@@ -75,21 +75,21 @@ class AdvisorAutoProxyCreatorIntegrationTests {
* If no pointcuts match (no attrs) there should be proxying.
*/
@Test
void testNoProxy() {
void noProxy() {
BeanFactory bf = getBeanFactory();
Object o = bf.getBean("noSetters");
assertThat(AopUtils.isAopProxy(o)).isFalse();
}
@Test
void testTxIsProxied() {
void txIsProxied() {
BeanFactory bf = getBeanFactory();
ITestBean test = (ITestBean) bf.getBean("test");
assertThat(AopUtils.isAopProxy(test)).isTrue();
}
@Test
void testRegexpApplied() {
void regexpApplied() {
BeanFactory bf = getBeanFactory();
ITestBean test = (ITestBean) bf.getBean("test");
MethodCounter counter = (MethodCounter) bf.getBean("countingAdvice");
@@ -99,7 +99,7 @@ class AdvisorAutoProxyCreatorIntegrationTests {
}
@Test
void testTransactionAttributeOnMethod() {
void transactionAttributeOnMethod() {
BeanFactory bf = getBeanFactory();
ITestBean test = (ITestBean) bf.getBean("test");
@@ -121,7 +121,7 @@ class AdvisorAutoProxyCreatorIntegrationTests {
* Should not roll back on servlet exception.
*/
@Test
void testRollbackRulesOnMethodCauseRollback() throws Exception {
void rollbackRulesOnMethodCauseRollback() throws Exception {
BeanFactory bf = getBeanFactory();
Rollback rb = (Rollback) bf.getBean("rollback");
@@ -147,7 +147,7 @@ class AdvisorAutoProxyCreatorIntegrationTests {
}
@Test
void testRollbackRulesOnMethodPreventRollback() throws Exception {
void rollbackRulesOnMethodPreventRollback() throws Exception {
BeanFactory bf = getBeanFactory();
Rollback rb = (Rollback) bf.getBean("rollback");
@@ -164,12 +164,11 @@ class AdvisorAutoProxyCreatorIntegrationTests {
}
@Test
void testProgrammaticRollback() {
void programmaticRollback() {
BeanFactory bf = getBeanFactory();
Object bean = bf.getBean(TXMANAGER_BEAN_NAME);
boolean condition = bean instanceof CallCountingTransactionManager;
assertThat(condition).isTrue();
assertThat(bean).isInstanceOf(CallCountingTransactionManager.class);
CallCountingTransactionManager txMan = (CallCountingTransactionManager) bf.getBean(TXMANAGER_BEAN_NAME);
Rollback rb = (Rollback) bf.getBean("rollback");
@@ -50,13 +50,13 @@ class ComponentBeanDefinitionParserTests {
}
@Test
void testBionicBasic() {
void bionicBasic() {
Component cp = getBionicFamily();
assertThat(cp.getName()).isEqualTo("Bionic-1");
}
@Test
void testBionicFirstLevelChildren() {
void bionicFirstLevelChildren() {
Component cp = getBionicFamily();
List<Component> components = cp.getComponents();
assertThat(components).hasSize(2);
@@ -65,7 +65,7 @@ class ComponentBeanDefinitionParserTests {
}
@Test
void testBionicSecondLevelChildren() {
void bionicSecondLevelChildren() {
Component cp = getBionicFamily();
List<Component> components = cp.getComponents().get(0).getComponents();
assertThat(components).hasSize(2);
@@ -83,7 +83,7 @@ class ClassPathBeanDefinitionScannerJsr330ScopeIntegrationTests {
@Test
void testPrototype() {
void prototype() {
ApplicationContext context = createContext(ScopedProxyMode.NO);
ScopedTestBean bean = (ScopedTestBean) context.getBean("prototype");
assertThat(bean).isNotNull();
@@ -92,7 +92,7 @@ class ClassPathBeanDefinitionScannerJsr330ScopeIntegrationTests {
}
@Test
void testSingletonScopeWithNoProxy() {
void singletonScopeWithNoProxy() {
RequestContextHolder.setRequestAttributes(oldRequestAttributes);
ApplicationContext context = createContext(ScopedProxyMode.NO);
ScopedTestBean bean = (ScopedTestBean) context.getBean("singleton");
@@ -115,7 +115,7 @@ class ClassPathBeanDefinitionScannerJsr330ScopeIntegrationTests {
}
@Test
void testSingletonScopeIgnoresProxyInterfaces() {
void singletonScopeIgnoresProxyInterfaces() {
RequestContextHolder.setRequestAttributes(oldRequestAttributes);
ApplicationContext context = createContext(ScopedProxyMode.INTERFACES);
ScopedTestBean bean = (ScopedTestBean) context.getBean("singleton");
@@ -136,7 +136,7 @@ class ClassPathBeanDefinitionScannerJsr330ScopeIntegrationTests {
}
@Test
void testSingletonScopeIgnoresProxyTargetClass() {
void singletonScopeIgnoresProxyTargetClass() {
RequestContextHolder.setRequestAttributes(oldRequestAttributes);
ApplicationContext context = createContext(ScopedProxyMode.TARGET_CLASS);
ScopedTestBean bean = (ScopedTestBean) context.getBean("singleton");
@@ -157,7 +157,7 @@ class ClassPathBeanDefinitionScannerJsr330ScopeIntegrationTests {
}
@Test
void testRequestScopeWithNoProxy() {
void requestScopeWithNoProxy() {
RequestContextHolder.setRequestAttributes(oldRequestAttributes);
ApplicationContext context = createContext(ScopedProxyMode.NO);
ScopedTestBean bean = (ScopedTestBean) context.getBean("request");
@@ -178,15 +178,14 @@ class ClassPathBeanDefinitionScannerJsr330ScopeIntegrationTests {
}
@Test
void testRequestScopeWithProxiedInterfaces() {
void requestScopeWithProxiedInterfaces() {
RequestContextHolder.setRequestAttributes(oldRequestAttributes);
ApplicationContext context = createContext(ScopedProxyMode.INTERFACES);
IScopedTestBean bean = (IScopedTestBean) context.getBean("request");
// should be dynamic proxy, implementing both interfaces
assertThat(AopUtils.isJdkDynamicProxy(bean)).isTrue();
boolean condition = bean instanceof AnotherScopeTestInterface;
assertThat(condition).isTrue();
assertThat(bean).isInstanceOf(AnotherScopeTestInterface.class);
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
bean.setName(MODIFIED_NAME);
@@ -200,15 +199,14 @@ class ClassPathBeanDefinitionScannerJsr330ScopeIntegrationTests {
}
@Test
void testRequestScopeWithProxiedTargetClass() {
void requestScopeWithProxiedTargetClass() {
RequestContextHolder.setRequestAttributes(oldRequestAttributes);
ApplicationContext context = createContext(ScopedProxyMode.TARGET_CLASS);
IScopedTestBean bean = (IScopedTestBean) context.getBean("request");
// should be a class-based proxy
assertThat(AopUtils.isCglibProxy(bean)).isTrue();
boolean condition = bean instanceof RequestScopedTestBean;
assertThat(condition).isTrue();
assertThat(bean).isInstanceOf(RequestScopedTestBean.class);
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
bean.setName(MODIFIED_NAME);
@@ -222,7 +220,7 @@ class ClassPathBeanDefinitionScannerJsr330ScopeIntegrationTests {
}
@Test
void testSessionScopeWithNoProxy() {
void sessionScopeWithNoProxy() {
RequestContextHolder.setRequestAttributes(oldRequestAttributesWithSession);
ApplicationContext context = createContext(ScopedProxyMode.NO);
ScopedTestBean bean = (ScopedTestBean) context.getBean("session");
@@ -243,15 +241,14 @@ class ClassPathBeanDefinitionScannerJsr330ScopeIntegrationTests {
}
@Test
void testSessionScopeWithProxiedInterfaces() {
void sessionScopeWithProxiedInterfaces() {
RequestContextHolder.setRequestAttributes(oldRequestAttributesWithSession);
ApplicationContext context = createContext(ScopedProxyMode.INTERFACES);
IScopedTestBean bean = (IScopedTestBean) context.getBean("session");
// should be dynamic proxy, implementing both interfaces
assertThat(AopUtils.isJdkDynamicProxy(bean)).isTrue();
boolean condition = bean instanceof AnotherScopeTestInterface;
assertThat(condition).isTrue();
assertThat(bean).isInstanceOf(AnotherScopeTestInterface.class);
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
bean.setName(MODIFIED_NAME);
@@ -271,17 +268,15 @@ class ClassPathBeanDefinitionScannerJsr330ScopeIntegrationTests {
}
@Test
void testSessionScopeWithProxiedTargetClass() {
void sessionScopeWithProxiedTargetClass() {
RequestContextHolder.setRequestAttributes(oldRequestAttributesWithSession);
ApplicationContext context = createContext(ScopedProxyMode.TARGET_CLASS);
IScopedTestBean bean = (IScopedTestBean) context.getBean("session");
// should be a class-based proxy
assertThat(AopUtils.isCglibProxy(bean)).isTrue();
boolean condition1 = bean instanceof ScopedTestBean;
assertThat(condition1).isTrue();
boolean condition = bean instanceof SessionScopedTestBean;
assertThat(condition).isTrue();
assertThat(bean).isInstanceOf(ScopedTestBean.class);
assertThat(bean).isInstanceOf(SessionScopedTestBean.class);
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
bean.setName(MODIFIED_NAME);
@@ -166,8 +166,7 @@ class ClassPathBeanDefinitionScannerScopeIntegrationTests {
// should be dynamic proxy, implementing both interfaces
assertThat(AopUtils.isJdkDynamicProxy(bean)).isTrue();
boolean condition = bean instanceof AnotherScopeTestInterface;
assertThat(condition).isTrue();
assertThat(bean).isInstanceOf(AnotherScopeTestInterface.class);
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
bean.setName(MODIFIED_NAME);
@@ -188,8 +187,7 @@ class ClassPathBeanDefinitionScannerScopeIntegrationTests {
// should be a class-based proxy
assertThat(AopUtils.isCglibProxy(bean)).isTrue();
boolean condition = bean instanceof RequestScopedTestBean;
assertThat(condition).isTrue();
assertThat(bean).isInstanceOf(RequestScopedTestBean.class);
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
bean.setName(MODIFIED_NAME);
@@ -231,8 +229,7 @@ class ClassPathBeanDefinitionScannerScopeIntegrationTests {
// should be dynamic proxy, implementing both interfaces
assertThat(AopUtils.isJdkDynamicProxy(bean)).isTrue();
boolean condition = bean instanceof AnotherScopeTestInterface;
assertThat(condition).isTrue();
assertThat(bean).isInstanceOf(AnotherScopeTestInterface.class);
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
bean.setName(MODIFIED_NAME);
@@ -259,10 +256,8 @@ class ClassPathBeanDefinitionScannerScopeIntegrationTests {
// should be a class-based proxy
assertThat(AopUtils.isCglibProxy(bean)).isTrue();
boolean condition1 = bean instanceof ScopedTestBean;
assertThat(condition1).isTrue();
boolean condition = bean instanceof SessionScopedTestBean;
assertThat(condition).isTrue();
assertThat(bean).isInstanceOf(ScopedTestBean.class);
assertThat(bean).isInstanceOf(SessionScopedTestBean.class);
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
bean.setName(MODIFIED_NAME);
@@ -0,0 +1,36 @@
/*
* Copyright 2002-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core.env;
/**
* Constants used both locally and in scan* sub-packages
*/
public class Constants {
public static final String XML_PATH = "org/springframework/core/env/EnvironmentSystemIntegrationTests-context.xml";
public static final String ENVIRONMENT_AWARE_BEAN_NAME = "envAwareBean";
public static final String PROD_BEAN_NAME = "prodBean";
public static final String DEV_BEAN_NAME = "devBean";
public static final String DERIVED_DEV_BEAN_NAME = "derivedDevBean";
public static final String TRANSITIVE_BEAN_NAME = "transitiveBean";
public static final String PROD_ENV_NAME = "prod";
public static final String DEV_ENV_NAME = "dev";
public static final String DERIVED_DEV_ENV_NAME = "derivedDev";
}
@@ -58,15 +58,15 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.rootBeanDefinition;
import static org.springframework.context.ConfigurableApplicationContext.ENVIRONMENT_BEAN_NAME;
import static org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.DERIVED_DEV_BEAN_NAME;
import static org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.DERIVED_DEV_ENV_NAME;
import static org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.DEV_BEAN_NAME;
import static org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.DEV_ENV_NAME;
import static org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.ENVIRONMENT_AWARE_BEAN_NAME;
import static org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.PROD_BEAN_NAME;
import static org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.PROD_ENV_NAME;
import static org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.TRANSITIVE_BEAN_NAME;
import static org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.XML_PATH;
import static org.springframework.core.env.Constants.DERIVED_DEV_BEAN_NAME;
import static org.springframework.core.env.Constants.DERIVED_DEV_ENV_NAME;
import static org.springframework.core.env.Constants.DEV_BEAN_NAME;
import static org.springframework.core.env.Constants.DEV_ENV_NAME;
import static org.springframework.core.env.Constants.ENVIRONMENT_AWARE_BEAN_NAME;
import static org.springframework.core.env.Constants.PROD_BEAN_NAME;
import static org.springframework.core.env.Constants.PROD_ENV_NAME;
import static org.springframework.core.env.Constants.TRANSITIVE_BEAN_NAME;
import static org.springframework.core.env.Constants.XML_PATH;
/**
* System integration tests for container support of the {@link Environment} API.
@@ -87,7 +87,7 @@ import static org.springframework.core.env.EnvironmentSystemIntegrationTests.Con
* @author Sam Brannen
* @see org.springframework.context.support.EnvironmentIntegrationTests
*/
public class EnvironmentSystemIntegrationTests {
class EnvironmentSystemIntegrationTests {
private final ConfigurableEnvironment prodEnv = new StandardEnvironment();
@@ -648,7 +648,7 @@ public class EnvironmentSystemIntegrationTests {
}
}
@Profile(DERIVED_DEV_ENV_NAME)
@Profile(Constants.DERIVED_DEV_ENV_NAME)
@Configuration
static class DerivedDevConfig extends DevConfig {
@Bean
@@ -666,24 +666,4 @@ public class EnvironmentSystemIntegrationTests {
}
}
/**
* Constants used both locally and in scan* sub-packages
*/
public static class Constants {
public static final String XML_PATH = "org/springframework/core/env/EnvironmentSystemIntegrationTests-context.xml";
public static final String ENVIRONMENT_AWARE_BEAN_NAME = "envAwareBean";
public static final String PROD_BEAN_NAME = "prodBean";
public static final String DEV_BEAN_NAME = "devBean";
public static final String DERIVED_DEV_BEAN_NAME = "derivedDevBean";
public static final String TRANSITIVE_BEAN_NAME = "transitiveBean";
public static final String PROD_ENV_NAME = "prod";
public static final String DEV_ENV_NAME = "dev";
public static final String DERIVED_DEV_ENV_NAME = "derivedDev";
}
}
@@ -20,7 +20,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
@Profile(org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.DEV_ENV_NAME)
@Profile(org.springframework.core.env.Constants.DEV_ENV_NAME)
@Configuration
class DevConfig {
@@ -20,7 +20,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
@Profile(org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.PROD_ENV_NAME)
@Profile(org.springframework.core.env.Constants.PROD_ENV_NAME)
@Configuration
class ProdConfig {
@@ -19,7 +19,7 @@ package org.springframework.core.env.scan2;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
@Profile(org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.DEV_ENV_NAME)
@Component(org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.DEV_BEAN_NAME)
@Profile(org.springframework.core.env.Constants.DEV_ENV_NAME)
@Component(org.springframework.core.env.Constants.DEV_BEAN_NAME)
class DevBean {
}
@@ -19,8 +19,8 @@ package org.springframework.core.env.scan2;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
@Profile(org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.PROD_ENV_NAME)
@Component(org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.PROD_BEAN_NAME)
@Profile(org.springframework.core.env.Constants.PROD_ENV_NAME)
@Component(org.springframework.core.env.Constants.PROD_BEAN_NAME)
class ProdBean {
}
@@ -163,7 +163,7 @@ public class MethodInvocationProceedingJoinPoint implements ProceedingJoinPoint,
@Override
public String toString() {
return "execution(" + getSignature().toString() + ")";
return "execution(" + getSignature() + ")";
}
@@ -83,7 +83,7 @@ public class AspectJProxyFactory extends ProxyCreatorSupport {
/**
* Add the supplied aspect instance to the chain. The type of the aspect instance
* supplied must be a singleton aspect. True singleton lifecycle is not honoured when
* supplied must be a singleton aspect. True singleton lifecycle is not honored when
* using this method - the caller is responsible for managing the lifecycle of any
* aspects added in this way.
* @param aspectInstance the AspectJ aspect instance
@@ -119,7 +119,7 @@ final class InstantiationModelAwarePointcutAdvisorImpl
/**
* The pointcut for Spring AOP to use.
* Actual behaviour of the pointcut will change depending on the state of the advice.
* Actual behavior of the pointcut will change depending on the state of the advice.
*/
@Override
public Pointcut getPointcut() {
@@ -258,7 +258,7 @@ final class InstantiationModelAwarePointcutAdvisorImpl
/**
* Pointcut implementation that changes its behaviour when the advice is instantiated.
* Pointcut implementation that changes its behavior when the advice is instantiated.
* Note that this is a <i>dynamic</i> pointcut; otherwise it might be optimized out
* if it does not at first match statically.
*/
@@ -82,7 +82,7 @@ public class DelegatePerTargetObjectIntroductionInterceptor extends Introduction
/**
* Subclasses may need to override this if they want to perform custom
* behaviour in around advice. However, subclasses should invoke this
* behavior in around advice. However, subclasses should invoke this
* method, which handles introduced interfaces and forwarding to the target.
*/
@Override
@@ -97,7 +97,7 @@ public class DelegatingIntroductionInterceptor extends IntroductionInfoSupport
/**
* Subclasses may need to override this if they want to perform custom
* behaviour in around advice. However, subclasses should invoke this
* behavior in around advice. However, subclasses should invoke this
* method, which handles introduced interfaces and forwarding to the target.
*/
@Override
@@ -24,7 +24,7 @@ import org.springframework.aop.Pointcut;
* Convenient superclass when we want to force subclasses to
* implement MethodMatcher interface, but subclasses
* will want to be pointcuts. The getClassFilter() method can
* be overridden to customize ClassFilter behaviour as well.
* be overridden to customize ClassFilter behavior as well.
*
* @author Rod Johnson
*/
@@ -23,7 +23,6 @@ import java.util.Map;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import test.annotation.EmptySpringAnnotation;
import test.annotation.transaction.Tx;
@@ -37,6 +36,7 @@ import org.springframework.beans.testfixture.beans.IOther;
import org.springframework.beans.testfixture.beans.ITestBean;
import org.springframework.beans.testfixture.beans.TestBean;
import org.springframework.beans.testfixture.beans.subpkg.DeepBean;
import org.springframework.util.ClassUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
@@ -50,30 +50,22 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
*/
class AspectJExpressionPointcutTests {
private Method getAge;
private Method setAge;
private Method setSomeNumber;
private final Method getAge = ClassUtils.getMethod(TestBean.class, "getAge");
private final Method setAge = ClassUtils.getMethod(TestBean.class, "setAge", int.class);
private final Method setSomeNumber = ClassUtils.getMethod(TestBean.class, "setSomeNumber", Number.class);
private final Map<String, Method> methodsOnHasGeneric = new HashMap<>();
@BeforeEach
void setup() throws NoSuchMethodException {
getAge = TestBean.class.getMethod("getAge");
setAge = TestBean.class.getMethod("setAge", int.class);
setSomeNumber = TestBean.class.getMethod("setSomeNumber", Number.class);
AspectJExpressionPointcutTests() throws NoSuchMethodException {
// Assumes no overloading
for (Method method : HasGeneric.class.getMethods()) {
methodsOnHasGeneric.put(method.getName(), method);
this.methodsOnHasGeneric.put(method.getName(), method);
}
}
@Test
void testMatchExplicit() {
void matchExplicit() {
String expression = "execution(int org.springframework.beans.testfixture.beans.TestBean.getAge())";
Pointcut pointcut = getPointcut(expression);
@@ -91,7 +83,7 @@ class AspectJExpressionPointcutTests {
}
@Test
void testMatchWithTypePattern() {
void matchWithTypePattern() {
String expression = "execution(* *..TestBean.*Age(..))";
Pointcut pointcut = getPointcut(expression);
@@ -110,12 +102,12 @@ class AspectJExpressionPointcutTests {
@Test
void testThis() throws SecurityException, NoSuchMethodException{
void thisCase() throws SecurityException, NoSuchMethodException{
testThisOrTarget("this");
}
@Test
void testTarget() throws SecurityException, NoSuchMethodException {
void target() throws SecurityException, NoSuchMethodException {
testThisOrTarget("target");
}
@@ -139,12 +131,12 @@ class AspectJExpressionPointcutTests {
}
@Test
void testWithinRootPackage() throws SecurityException, NoSuchMethodException {
void withinRootPackage() throws SecurityException, NoSuchMethodException {
testWithinPackage(false);
}
@Test
void testWithinRootAndSubpackages() throws SecurityException, NoSuchMethodException {
void withinRootAndSubpackages() throws SecurityException, NoSuchMethodException {
testWithinPackage(true);
}
@@ -168,7 +160,7 @@ class AspectJExpressionPointcutTests {
}
@Test
void testFriendlyErrorOnNoLocationClassMatching() {
void friendlyErrorOnNoLocationClassMatching() {
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
assertThatIllegalStateException()
.isThrownBy(() -> pc.getClassFilter().matches(ITestBean.class))
@@ -176,7 +168,7 @@ class AspectJExpressionPointcutTests {
}
@Test
void testFriendlyErrorOnNoLocation2ArgMatching() {
void friendlyErrorOnNoLocation2ArgMatching() {
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
assertThatIllegalStateException()
.isThrownBy(() -> pc.getMethodMatcher().matches(getAge, ITestBean.class))
@@ -184,7 +176,7 @@ class AspectJExpressionPointcutTests {
}
@Test
void testFriendlyErrorOnNoLocation3ArgMatching() {
void friendlyErrorOnNoLocation3ArgMatching() {
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
assertThatIllegalStateException()
.isThrownBy(() -> pc.getMethodMatcher().matches(getAge, ITestBean.class, (Object[]) null))
@@ -193,7 +185,7 @@ class AspectJExpressionPointcutTests {
@Test
void testMatchWithArgs() {
void matchWithArgs() {
String expression = "execution(void org.springframework.beans.testfixture.beans.TestBean.setSomeNumber(Number)) && args(Double)";
Pointcut pointcut = getPointcut(expression);
@@ -214,7 +206,7 @@ class AspectJExpressionPointcutTests {
}
@Test
void testSimpleAdvice() {
void simpleAdvice() {
String expression = "execution(int org.springframework.beans.testfixture.beans.TestBean.getAge())";
CallCountingInterceptor interceptor = new CallCountingInterceptor();
TestBean testBean = getAdvisedProxy(expression, interceptor);
@@ -227,7 +219,7 @@ class AspectJExpressionPointcutTests {
}
@Test
void testDynamicMatchingProxy() {
void dynamicMatchingProxy() {
String expression = "execution(void org.springframework.beans.testfixture.beans.TestBean.setSomeNumber(Number)) && args(Double)";
CallCountingInterceptor interceptor = new CallCountingInterceptor();
TestBean testBean = getAdvisedProxy(expression, interceptor);
@@ -241,7 +233,7 @@ class AspectJExpressionPointcutTests {
}
@Test
void testInvalidExpression() {
void invalidExpression() {
String expression = "execution(void org.springframework.beans.testfixture.beans.TestBean.setSomeNumber(Number) && args(Double)";
assertThat(getPointcut(expression).getClassFilter().matches(Object.class)).isFalse();
}
@@ -271,20 +263,20 @@ class AspectJExpressionPointcutTests {
}
@Test
void testWithUnsupportedPointcutPrimitive() {
void withUnsupportedPointcutPrimitive() {
String expression = "call(int org.springframework.beans.testfixture.beans.TestBean.getAge())";
assertThat(getPointcut(expression).getClassFilter().matches(Object.class)).isFalse();
}
@Test
void testAndSubstitution() {
void andSubstitution() {
AspectJExpressionPointcut pc = getPointcut("execution(* *(..)) and args(String)");
String expr = pc.getPointcutExpression().getPointcutExpression();
assertThat(expr).isEqualTo("execution(* *(..)) && args(String)");
}
@Test
void testMultipleAndSubstitutions() {
void multipleAndSubstitutions() {
AspectJExpressionPointcut pc = getPointcut("execution(* *(..)) and args(String) and this(Object)");
String expr = pc.getPointcutExpression().getPointcutExpression();
assertThat(expr).isEqualTo("execution(* *(..)) && args(String) && this(Object)");
@@ -297,7 +289,7 @@ class AspectJExpressionPointcutTests {
}
@Test
void testMatchGenericArgument() {
void matchGenericArgument() {
String expression = "execution(* set*(java.util.List<org.springframework.beans.testfixture.beans.TestBean>) )";
AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut();
ajexp.setExpression(expression);
@@ -316,7 +308,7 @@ class AspectJExpressionPointcutTests {
}
@Test
void testMatchVarargs() throws Exception {
void matchVarargs() throws Exception {
@SuppressWarnings("unused")
class MyTemplate {
@@ -342,19 +334,19 @@ class AspectJExpressionPointcutTests {
}
@Test
void testMatchAnnotationOnClassWithAtWithin() throws Exception {
void matchAnnotationOnClassWithAtWithin() throws Exception {
String expression = "@within(test.annotation.transaction.Tx)";
testMatchAnnotationOnClass(expression);
}
@Test
void testMatchAnnotationOnClassWithoutBinding() throws Exception {
void matchAnnotationOnClassWithoutBinding() throws Exception {
String expression = "within(@test.annotation.transaction.Tx *)";
testMatchAnnotationOnClass(expression);
}
@Test
void testMatchAnnotationOnClassWithSubpackageWildcard() throws Exception {
void matchAnnotationOnClassWithSubpackageWildcard() throws Exception {
String expression = "within(@(test.annotation..*) *)";
AspectJExpressionPointcut springAnnotatedPc = testMatchAnnotationOnClass(expression);
assertThat(springAnnotatedPc.matches(TestBean.class.getMethod("setName", String.class), TestBean.class)).isFalse();
@@ -366,7 +358,7 @@ class AspectJExpressionPointcutTests {
}
@Test
void testMatchAnnotationOnClassWithExactPackageWildcard() throws Exception {
void matchAnnotationOnClassWithExactPackageWildcard() throws Exception {
String expression = "within(@(test.annotation.transaction.*) *)";
testMatchAnnotationOnClass(expression);
}
@@ -384,7 +376,7 @@ class AspectJExpressionPointcutTests {
}
@Test
void testAnnotationOnMethodWithFQN() throws Exception {
void annotationOnMethodWithFQN() throws Exception {
String expression = "@annotation(test.annotation.transaction.Tx)";
AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut();
ajexp.setExpression(expression);
@@ -398,7 +390,7 @@ class AspectJExpressionPointcutTests {
}
@Test
void testAnnotationOnCglibProxyMethod() throws Exception {
void annotationOnCglibProxyMethod() throws Exception {
String expression = "@annotation(test.annotation.transaction.Tx)";
AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut();
ajexp.setExpression(expression);
@@ -410,7 +402,7 @@ class AspectJExpressionPointcutTests {
}
@Test
void testNotAnnotationOnCglibProxyMethod() throws Exception {
void notAnnotationOnCglibProxyMethod() throws Exception {
String expression = "!@annotation(test.annotation.transaction.Tx)";
AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut();
ajexp.setExpression(expression);
@@ -422,7 +414,7 @@ class AspectJExpressionPointcutTests {
}
@Test
void testAnnotationOnDynamicProxyMethod() throws Exception {
void annotationOnDynamicProxyMethod() throws Exception {
String expression = "@annotation(test.annotation.transaction.Tx)";
AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut();
ajexp.setExpression(expression);
@@ -434,7 +426,7 @@ class AspectJExpressionPointcutTests {
}
@Test
void testNotAnnotationOnDynamicProxyMethod() throws Exception {
void notAnnotationOnDynamicProxyMethod() throws Exception {
String expression = "!@annotation(test.annotation.transaction.Tx)";
AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut();
ajexp.setExpression(expression);
@@ -446,7 +438,7 @@ class AspectJExpressionPointcutTests {
}
@Test
void testAnnotationOnMethodWithWildcard() throws Exception {
void annotationOnMethodWithWildcard() throws Exception {
String expression = "execution(@(test.annotation..*) * *(..))";
AspectJExpressionPointcut anySpringMethodAnnotation = new AspectJExpressionPointcut();
anySpringMethodAnnotation.setExpression(expression);
@@ -462,7 +454,7 @@ class AspectJExpressionPointcutTests {
}
@Test
void testAnnotationOnMethodArgumentsWithFQN() throws Exception {
void annotationOnMethodArgumentsWithFQN() throws Exception {
String expression = "@args(*, test.annotation.EmptySpringAnnotation))";
AspectJExpressionPointcut takesSpringAnnotatedArgument2 = new AspectJExpressionPointcut();
takesSpringAnnotatedArgument2.setExpression(expression);
@@ -491,7 +483,7 @@ class AspectJExpressionPointcutTests {
}
@Test
void testAnnotationOnMethodArgumentsWithWildcards() throws Exception {
void annotationOnMethodArgumentsWithWildcards() throws Exception {
String expression = "execution(* *(*, @(test..*) *))";
AspectJExpressionPointcut takesSpringAnnotatedArgument2 = new AspectJExpressionPointcut();
takesSpringAnnotatedArgument2.setExpression(expression);

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