Commit Graph
100 Commits
Author SHA1 Message Date
Sam Brannen 90ad7f947d Increase heap size for checkstyleNohttp task
On a developer machine, the nohttp check scans the whole project
directory, and things like local git worktrees can add enough extra
content on disk to push the task past its previous 1g heap limit,
causing an OutOfMemoryError. This goes beyond what was addressed by
excluding the .claude folder from nohttp scanning in 48971139c0.

This commit raises the heap size for checkstyleNohttp specifically
to 1536m, leaving the limit for other Checkstyle tasks unchanged so
as not to increase memory pressure on CI.
2026-08-20 12:43:02 +02:00
Sam Brannen 49d2a202da Add .claude folder contents to .gitignore
If we later wish to share certain settings, we can introduce
exclusions like: !.claude/commands/
2026-08-20 12:43:02 +02:00
Sam Brannen df10251539 Upgrade to Gradle 9.7.1
Closes gh-37160
2026-08-20 10:57:33 +02:00
Sam Brannen a4720ccf77 Guard all AsynchronousFileChannel#write call sites in DataBufferUtils
Prior to this commit, only the very first AsynchronousFileChannel#write
call in DataBufferUtils$WriteCompletionHandler#hookOnNext(DataBuffer)
was guarded against exceptions escaping synchronously, and even then
only via `catch (RuntimeException ex)`, per the original fix for
gh-36184.

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

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

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

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

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

See gh-36184
Closes gh-37143
2026-08-18 16:32:34 +02:00
Sam Brannen 48971139c0 Exclude the .claude folder from nohttp scanning
Without this exclusion, the Gradle build will fail (due to an
OutOfMemoryError) for temporary git work trees residing in the .claude
folder.
2026-08-18 16:32:24 +02:00
Sam Brannen f8a2bdad87 Clarify Bean Overrides and Spring AOP Proxies documentation
A user reported confusion between two distinct uses of "wraps" in the
AOP proxy documentation for Bean Overrides: the sense in which a
Mockito spy wraps the original bean instance it was created from, and
the sense in which a Spring AOP proxy wraps the spy in the actual
object graph.

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

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

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

See gh-37121
2026-08-13 18:57:57 +02:00
Sam Brannen 8df51ad6cc Document AOP proxy semantics for Bean Overrides in tests
This commit documents how the Bean Override support in the TestContext
framework interacts with Spring AOP proxies created for annotations
such as @⁠Transactional, @⁠Cacheable, and @⁠Retryable.

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

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

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

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

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

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

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

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

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

Closes gh-37102
2026-08-03 11:35:05 +03:00
Sam Brannen 0f5bd82c5d Centralize SpEL security documentation in the reference manual
The Javadoc for EvaluationContext, StandardEvaluationContext, and
SimpleEvaluationContext previously repeated the same detailed
explanation of trusted sources and best-effort restrictions in four
places, making it hard to maintain and to digest.

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

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

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

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

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

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

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

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

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

Closes gh-36985
2026-07-25 16:05:23 +03:00
Sam Brannen b90624472e Polish Javadoc for ProtobufDecoder 2026-07-21 11:57:20 +03:00
Sam Brannen f0e69a702c Ensure SpEL's InlineList is immutable in compiled mode
Prior to this commit, SpEL's InlineList was cached as a mutable list in
compiled mode (i.e., in a static field in the generated byte code).

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

Closes gh-37001
2026-07-06 10:52:54 +02:00
Sam Brannen 78f05d8f8e Address deprecation warnings
This commit addresses warnings across the code base related to:

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

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

To avoid the warnings, this commit modifies:

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

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

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

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

See gh-36952
2026-06-27 17:02:40 +02:00
Sam Brannen ade96d275d Upgrade to Gradle 9.6.1
Closes gh-36952
2026-06-27 16:16:59 +02:00
Sam Brannen 072fa3f43d Polishing 2026-06-27 16:11:42 +02:00
Sam Brannen 78dcdab3fc Polishing
See gh-36972
2026-06-27 15:19:42 +02:00
Sam Brannen 4d6e88dc98 Fix off-by-one error in MimeTypeUtils.parseMimeType()
Due to changes made in commit 41cd6879bd, MimeTypeUtils now raises a
StringIndexOutOfBoundsException instead of an InvalidMimeTypeException
when parsing certain invalid mime types -- for example, for a value
wrapped in double quotes which does not contain a ";" character.

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

See gh-36730
Closes gh-36971
2026-06-26 17:40:53 +02:00
Sam Brannen b00f691655 Preserve parameter order in DefaultServerRequest's ServletParametersMap
Prior to this commit, DefaultServerRequest's ServletParametersMap lost
the original parameter order when entrySet() was invoked.

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

Closes gh-36966
2026-06-25 13:57:34 +02:00
Sam Brannen 4074155d76 Polish contribution
See gh-36948
2026-06-25 13:23:49 +02:00
Sam Brannen ee81785afc Upgrade to Gradle 9.6
Closes gh-36952
2026-06-19 16:31:38 +02:00
Sam Brannen 846a6a8f7c Document behavior for 0 delay combined with jitter
Closes gh-36946
2026-06-17 12:41:03 +02:00
Sam Brannen 0d706f8da6 Polish contribution
See gh-36932
2026-06-17 12:38:31 +02:00
Sam Brannen 292959e2ca Improve nullability for getSession(*) in MockHttpServletRequest
Closes gh-36926
2026-06-15 15:35:17 +02:00
Sam Brannen 5383520388 Sync changes in MockHttpServletRequest to spring-web test fixture 2026-06-15 15:34:32 +02:00
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
Sam Brannen 6ca66afc7b Polish contribution
See gh-36871
2026-06-05 15:13:25 +02:00
Sam Brannen b4a378186f Fix additional links to Selenium documentation
See gh-36875
2026-06-05 14:56:51 +02:00
Sam Brannen 6985d00fce Update antora-extensions to 1.14.12
Closes gh-36851
2026-05-28 10:57:11 +02:00
Sam Brannen b95caa8331 Upgrade Antora dependencies 2026-05-27 12:15:25 +02:00
Sam Brannen c17939ed5c Polish contribution
See gh-36831
2026-05-27 12:02:50 +02:00
Sam Brannen 7651d5841f Pin Node.js version to 24.15.0
Prior to this commit, the `antora` Gradle task silently failed to build
the reference documentation, since Antora uses the latest LTS release
for Node.js by default, and the latest LTS apparently does not work for
us.
2026-05-27 12:01:09 +02:00
Sam Brannen 6e122d3aaa Polish contribution
See gh-36833
2026-05-26 16:39:30 +02:00
Sam Brannen 1e843fd3ec Polish contribution
See gh-36777
2026-05-20 17:45:24 +02:00
Sam Brannen d5e85bd95e Remove obsolete code 2026-05-17 15:30:04 +02:00
Sam Brannen c3c96b9f32 Upgrade to Gradle 9.5.1
Closes gh-36744
2026-05-17 13:47:00 +02:00
Sam Brannen 987d6cca6d Fix error message for invalid regex in SpEL
Closes gh-36756
2026-05-06 13:53:37 +02:00
Sam Brannen bb5142164e Upgrade to Gradle 9.5
Closes gh-36744
2026-05-02 18:38:09 +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 ab6637c670 Completely extract ServletRequestParameterPropertyValuesTests
This aligns with changes to ServletRequestParameterPropertyValuesTests
on 6.2.x.
2026-04-16 15:11:39 +02:00
Sam Brannen c9b88b4ebd Extract ServletRequestParameterPropertyValuesTests 2026-04-16 14:38:17 +02:00
Sam Brannen 68c575ab14 Revise "Skip binding entirely when field is not allowed"
This commit reverts the changes made to WebDataBinder's doBind()
implementation in e4d03f6625 and instead implements the skipping logic
directly in checkFieldDefaults(), checkFieldMarkers(), and
adaptEmptyArrayIndices() by preemptively checking if the corresponding
field is allowed.

This commit also improves the Javadoc and adds missing tests.

Fixes gh-36625
2026-04-16 14:33:50 +02:00
Sam Brannen cb320468db Further clarify semantics of HttpMethod.valueOf()
See gh-36652
2026-04-14 16:17:20 +02:00
Sam Brannen ff3f2937ab Restructure SpelCompilationCoverageTests using @⁠Nested test classes 2026-04-13 17:42:18 +02:00
Sam Brannen d7450ce8e4 Polish SpelCompilationCoverageTests 2026-04-13 12:58:36 +02:00
Sam Brannen 28f78f435e Introduce missing tests for immediate SpEL compilation for Elvis operator 2026-04-12 17:11:07 +02:00
Sam Brannen a3705632c1 Polish SpEL Ternary and Elvis operators 2026-04-12 17:09:28 +02:00
Sam Brannen 84221064c8 Polish SpEl Ternary operator and compilation tests 2026-04-12 16:41:49 +02:00
Sam Brannen 21f3b964fe Improve SpEL tests for Elvis and Ternary operators 2026-04-12 14:28:33 +02:00
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 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
Sam Brannen 18b8f871aa Prevent mockk from transitively pulling in JUnit 4 2026-04-08 14:13:59 +02:00
Sam Brannen b560c7b85d Improve Javadoc for MergedAnnotations 2026-04-08 12:35:24 +02:00
Sam Brannen 0f05a2e153 Polish ClassFileMethodMetadata 2026-04-08 11:38:07 +02:00
Sam Brannen d4cc273c31 Avoid recursion in ClassFileAnnotationMetadata.resolveTypeName()
See gh-36577
2026-04-08 11:13:51 +02:00
Sam Brannen f3b6c222f9 Use ClassLoader for method or field in MergedAnnotation
Prior to this commit, the `return` keyword was missing in
TypeMappedAnnotation's getClassLoader() implementation, which prevented
the ClassLoader of the Member (Method or Field) from being used.

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

Closes gh-36606
2026-04-07 18:16:09 +02:00
Sam Brannen 8d390f4e5a Polish SpEL documentation 2026-04-07 17:54:43 +02:00
Sam Brannen 9d365906b5 Fix typo 2026-04-06 16:55:32 +02:00
Sam Brannen 97e10a5948 Fix flaky SpEL tests
This commit fixes SpEL related tests that failed if the test methods
were executed in a different order than in the Gradle build.
2026-04-06 16:55:32 +02:00
Sam Brannen bfbfe4a572 Fix BridgeMethodResolverTests.findBridgedMethodInHierarchy() in Eclipse 2026-04-06 16:54:34 +02:00
Sam Brannen 38464a15dc Retain source declaration order in AnnotatedTypeMetadata on Java 24+
Prior to this commit, ClassFileAnnotationDelegate created
MergedAnnotations from a HashSet, which resulted in a non-deterministic
iteration order and lost the original source declaration order of the
annotations.

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

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

Closes gh-36598
2026-04-04 17:19:25 +02:00
Sam Brannen c04b502866 Polishing 2026-04-04 17:17:38 +02:00
Sam Brannen d10460d775 Track class loading exceptions in MergedAnnotation.asMap()
Spring Framework 5.2 introduced a regression in our annotation
processing support when the MergedAnnotations API was introduced.
Consequently, prior to this commit, our "annotation attributes as a Map
or AnnotationAttributes instance" support no longer stored exceptions
thrown while attempting to load a type referenced by an annotation
attribute. Instead, the exception was thrown immediately.

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

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

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

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

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

See gh-31203
Closes gh-36524
2026-04-02 16:51:17 +02:00
Sam Brannen 24c7d31ba7 Polishing 2026-04-02 12:43:28 +02:00
Sam Brannen b6fc3a1b6f Enforce use of AssertJ assumptions via Checkstyle
Closes gh-36582
2026-04-02 12:43:12 +02:00
Sam Brannen d767806977 Ignore .cursor directory 2026-04-02 12:11:58 +02:00
Sam Brannen c5b9c3d604 Add 2D array tests, use AssertJ assumptions, and polish
See gh-36577
2026-04-02 11:05:59 +02:00
Sam Brannen 7b8d8693dd Fix AnnotationMetadata tests in Eclipse IDE
Since commit 89391fd94c, the getClassAttributeWhenUnknownClass() methods
in SimpleAnnotationMetadataTests and DefaultAnnotationMetadataTests
have failed in Eclipse, since the JSR-305 JAR is always on the test
runtime classpath in Eclipse.

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

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

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

Closes gh-36576
2026-03-31 17:49:21 +02:00
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 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
Sam Brannen 7f7f7c5f5b Invoke assertIsMock() before configuring mocks 2026-03-24 11:07:38 +01:00
Sam Brannen 9fba394b8a Sync changes to antora-playbook.yml from docs-build branch 2026-03-23 11:36:26 +01:00
Sam Brannen 0005ec09c2 Fix link to SpringProperties
See gh-36460
2026-03-22 18:04:33 +01:00
Sam Brannen 0e800538c0 Fix warning in ConcurrentOperationExecutor.kt
Prior to this commit, the following warning was emitted in the build.

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

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

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

See gh-35697
Closes gh-36460
2026-03-22 16:48:41 +01:00
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