Compare commits

...

233 Commits

Author SHA1 Message Date
Stéphane Nicoll 6b117247d3 Release v6.2.18 2026-04-17 08:26:24 +02:00
Juergen Hoeller f6671e77e2 Upgrade to Reactor 2024.0.17 and Micrometer 1.15.11
Closes gh-36660
Closes gh-36661
2026-04-16 23:47:49 +02:00
rstoyanchev b338fdd99d Add doOnDiscard in MultipartHttpMessageReader
Closes gh-36563
2026-04-16 21:19:13 +01:00
Sam Brannen 4e3f264f34 Add missing tests for WebRequestDataBinder
See gh-36625

(cherry picked from commit 63817ce202)
2026-04-16 16:36:27 +02:00
Sam Brannen 9e0b83ead3 Polish WebRequestDataBinderTests
(cherry picked from commit 61bd79017f)
2026-04-16 16:36:18 +02:00
Sam Brannen af4b1229a5 Extract ServletRequestParameterPropertyValuesTests
(cherry picked from commit c9b88b4ebd)
2026-04-16 15:08:23 +02:00
Sam Brannen 623ccd1a4f 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.

See gh-36625
Fixes gh-36627

(cherry picked from commit 68c575ab14)
2026-04-16 15:08:09 +02:00
Sam Brannen 69068ba33d Further clarify semantics of HttpMethod.valueOf()
See gh-36652

(cherry picked from commit cb320468db)
2026-04-14 16:19:43 +02:00
angry2.k f182f9a76b 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>

(cherry picked from commit df828458fa)
2026-04-14 16:19:35 +02:00
Sam Brannen 9d144487e7 Improve SpEL tests for Elvis and Ternary operators
(cherry picked from commit 21f3b964fe)
2026-04-12 14:31:15 +02:00
Sébastien Deleuze b07bc2cb99 Apply nullable value class fix to InvocableHandlerMethod
See gh-36643
2026-04-10 16:24:36 +02:00
T45K 57d4765131 Fix nullable value class handling in CoroutinesUtils
Closes gh-36643
Signed-off-by: T45K <tasktas9@gmail.com>
2026-04-10 16:22:55 +02:00
Brian Clozel cbdec804a5 Allow null id/event in ServerSentEvent
The builder itself does not allow `null` values so this shouldn't be
necessary, but because the offending change was introduced late in the
6.2.x line, we'll be more flexible here.

Fixes gh-36634
2026-04-10 14:43:29 +02:00
Sam Brannen 438b1011fe Revise documentation for @⁠ActiveProfiles and ActiveProfilesResolver
See gh-36269
See gh-36600

(cherry picked from commit 99b78adce3)
2026-04-09 12:59:42 +02:00
Mohak-Nagaraju c5e560574c 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>
(cherry picked from commit 644731c9f6)
2026-04-09 12:59:34 +02:00
Brian Clozel 589e58e400 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-36627
2026-04-09 11:06:46 +02:00
Sam Brannen 6101419d80 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

(cherry picked from commit 6251b2c0c9)
2026-04-09 10:36:55 +02:00
Juergen Hoeller 63cd96fa5f Revise target bean exception with consistent message formatting 2026-04-08 15:48:17 +02:00
Juergen Hoeller 923ec6a8e1 Upgrade to Groovy 4.0.31 and Mockito 5.23 2026-04-08 15:09:57 +02:00
Juergen Hoeller 241a7dca02 Polishing
(cherry picked from commit 1687d90a8c)
2026-04-08 15:09:33 +02:00
Juergen Hoeller d675132ac0 Improve SpringValidatorAdapter and MethodValidationAdapter performance
Closes gh-36621

(cherry picked from commit b8f1005897)
2026-04-08 13:57:27 +02:00
Juergen Hoeller 2801c7e23e Avoid MessageFormat rendering for exception messages in binding errors
Closes gh-36609

(cherry picked from commit 0150c4ba06)
2026-04-08 13:57:22 +02:00
Sam Brannen d356e7a238 Improve Javadoc for MergedAnnotations
(cherry picked from commit b560c7b85d)
2026-04-08 12:36:44 +02:00
Brian Clozel df198987e0 Allow unlimited caching in ContentCachingRequestWrapper
Prior to this commit the `ContentCachingRequestWrapper(HttpServletRequest)`
constructor was deprecated; this variant caches by default an unlimited
amount of data. The replacement
`ContentCachingRequestWrapper(HttpServletRequest, int)` allows such
behavior in 7.0 but that change has not been bacported to 6.2.x.

This commit ensures that the replacement constructor can be safely used
in 6.2.x, preparing for the 7.0.x upgrade.

Fixes gh-36620
2026-04-08 11:29:27 +02:00
Sam Brannen b6a246989f 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

(cherry picked from commit f3b6c222f9)
2026-04-07 18:24:04 +02:00
Brian Clozel 2a9afd6ede Document valid SpEL separators for tokenizer
(cherry picked from commit 14466eef4c)
(cherry picked from commit 8d390f4e5a)
2026-04-07 18:23:46 +02:00
Sam Brannen 9c82638820 Fix typo
(cherry picked from commit 9d365906b5)
2026-04-06 17:02:31 +02:00
Sam Brannen b2069e4c37 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.

(cherry picked from commit 97e10a5948)
2026-04-06 17:02:14 +02:00
Sam Brannen 659271cf61 Fix BridgeMethodResolverTests.findBridgedMethodInHierarchy() in Eclipse
(cherry picked from commit bfbfe4a572)
2026-04-06 17:00:57 +02:00
Brian Clozel 39a6d4709f Deprecate types removed in 7.0
This commit `@Deprecate` for removal the following because support was
removed in 7.0:

* Netty 5 and Reactor Netty2 experimental support is abandonned
* Undertow does not support the Framework 7.0 baseline, developers
  should consider another Servlet container at this point

See gh-36591
2026-04-03 17:50:38 +02:00
Sam Brannen d78d80b02a 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

(cherry picked from commit 00fbd91cca)
2026-04-02 16:57:09 +02:00
Juergen Hoeller 684b1e8a4b Enforce strict implementation of Resource#isReadable() contract
Closes gh-36584

(cherry picked from commit e977bd7bf0)
2026-04-02 14:45:05 +02:00
Sam Brannen 6f204bf4a3 Polishing
(cherry picked from commit 24c7d31ba7)
2026-04-02 13:24:03 +02:00
Sam Brannen 5873e40782 Enforce use of AssertJ assumptions via Checkstyle
Closes gh-36582

(cherry picked from commit b6fc3a1b6f)
2026-04-02 13:24:03 +02:00
Sam Brannen 153e553675 Ignore .cursor directory
(cherry picked from commit d767806977)
2026-04-02 12:48:21 +02:00
Juergen Hoeller 9ceb9efb2c Polishing 2026-04-01 21:25:11 +02:00
Sam Brannen 9af3961b9b 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.

See gh-36576
Closes gh-36576

(cherry picked from commit 22adccd190)
2026-03-31 17:51:17 +02:00
rstoyanchev fa04db3b03 Consistently log request details in ExchangeFunctions
Closes gh-36502
2026-03-31 16:26:37 +01:00
Sam Brannen 1a05ed236e Correct Javadoc for MergedAnnotation.asAnnotationAttributes()
Closes gh-36567

(cherry picked from commit b7a1157e66)
2026-03-30 17:03:14 +02:00
Sam Brannen 5a4a14c9c2 Polishing
(cherry picked from commit 27bc60e077)
2026-03-30 17:03:05 +02:00
Juergen Hoeller aff0ce1a00 Polishing 2026-03-28 20:53:26 +01:00
Juergen Hoeller cdfb77d5b0 Upgrade to Netty 4.1.132 and Protobuf 4.34.1 2026-03-28 11:47:13 +01:00
Juergen Hoeller 41f4072670 Fix ApplicationListenerMethodAdapter name in error message 2026-03-28 11:42:46 +01:00
Juergen Hoeller 49bb287800 Polishing
(cherry picked from commit b81a63fb5c)
2026-03-28 11:24:25 +01:00
Juergen Hoeller db6111fabe Add documentation notes on error handling with sync=true
See gh-36531

(cherry picked from commit 529a6fc932)
2026-03-28 11:23:59 +01:00
Sam Brannen 522a2e2e80 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

(cherry picked from commit 6d3d3d11c4)
2026-03-26 14:30:17 +01:00
Juergen Hoeller 0c2375a08c Consistent handling of early setCatalog/setSchema/setHoldability calls
Closes gh-36527
Closes gh-36528
2026-03-24 23:48:25 +01:00
Junseo Bae b7137c5baf Fix incorrect cross reference in AbstractEnvironment Javadoc
Closes gh-36516

Signed-off-by: Junseo Bae <ferrater1013@gmail.com>

(cherry picked from commit f92e810831)
2026-03-23 11:51:37 +01:00
Sam Brannen 315591f18e Sync changes to antora-playbook.yml from docs-build branch
(cherry picked from commit 9fba394b8a)
2026-03-23 11:37:25 +01:00
Juergen Hoeller 96a028a028 Consistent IOException spelling in class/method names 2026-03-21 12:19:07 +01:00
Juergen Hoeller 8b67472e35 Avoid endless re-interruption loop on shutdown
Closes gh-36506
2026-03-21 12:19:01 +01:00
Juergen Hoeller 3997551ba5 Recognize error code 149 as deadlock loser (MySQL Galera)
Closes gh-36499
2026-03-21 12:18:58 +01:00
Brian Clozel e607f1c30f 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 16:13:47 +01:00
Sam Brannen 506c6f1777 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

(cherry picked from commit e1e4d52b61)
2026-03-20 11:38:33 +01:00
Sam Brannen 6d6b788b25 Enable SpringJUnit5 Checkstyle rule
See gh-36496
Closes gh-36496

(cherry picked from commit 1256307c83)
2026-03-18 19:07:27 +01:00
Sam Brannen 01248c7753 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

(cherry picked from commit 4c14abf0cd)
2026-03-18 18:27:53 +01:00
github-actions[bot] 40562e2485 Update Antora Spring UI to v0.4.26 2026-03-17 09:21:37 +01:00
jun 52345c56a7 Fix typo in StompSession Javadoc
Closes gh-36469

Signed-off-by: jun <ryuu.public@gmail.com>
(cherry picked from commit c2c6130da5)
2026-03-16 13:40:12 +01:00
Sam Brannen e598df8c60 Fix common typos and grammatical mistakes
Closes gh-36471

(cherry picked from commit 5eb0e99d58)
2026-03-15 17:19:28 +01:00
Sam Brannen 0c4ed55fd2 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

(cherry picked from commit 17699103dc)
2026-03-15 17:16:39 +01:00
Sam Brannen 10faa7fdff Remove obsolete, custom Eclipse configuration for spring-oxm
See gh-36195

(cherry picked from commit 9b366b28b7)
2026-03-15 14:59:32 +01:00
Sam Brannen 2269c56d61 Polish contribution
See gh-36195

(cherry picked from commit 0f990e44a8)
2026-03-15 14:59:32 +01:00
Clayton Walker 15c82d558d Improve XJC configuration for spring-oxm in the Gradle build
Closes gh-36195

Signed-off-by: Clayton Walker <clayton.m.walker@gmail.com>

(cherry picked from commit e102bc4933)
2026-03-15 14:59:25 +01:00
Brian Clozel b4646ce9b4 Next development version (v6.2.18-SNAPSHOT) 2026-03-13 09:16:10 +01:00
Sébastien Deleuze 317a1f9909 Leverage ResourceHandlerUtils in ScriptTemplateView
This commit apply extra checks to ScriptTemplateView resource handling
with ResourceHandlerUtils, consistently with what is done with static
resource handling.

Closes gh-36459
2026-03-12 20:02:59 +01:00
Sébastien Deleuze de6601fdac Restore ScriptTemplateViewTests
Restore both WebMVC and WebFlux variants that were deleted
by mistake in commit 4db2f8ea1b.

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

Closes gh-36457
2026-03-12 19:53:26 +01:00
Sam Brannen 47dc1c4d93 Fix log message in ConfigurationClassBeanDefinitionReader
The log message now properly generates the fully-qualified method name
and includes the resolved bean name as well.

Closes gh-36453

(cherry picked from commit e634ced56b)
2026-03-12 16:25:03 +01:00
Juergen Hoeller d8c77934ff Upgrade to SnakeYAML 2.6, Protobuf 4.34, H2 2.4.240
(cherry picked from commit d8216d719b)
2026-03-11 15:55:42 +01:00
Juergen Hoeller 99fbce1254 Polishing (aligned with main) 2026-03-11 15:08:12 +01:00
Juergen Hoeller d1e69a9677 Upgrade to Reactor 2024.0.16 and Micrometer 1.15.10
Includes Jetty 12.0.33, Selenium 4.41, Mockito 5.22

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

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

Fixes gh-36440
2026-03-10 17:56:39 +01:00
Sam Brannen 131f94fbc5 Use link for first reference to @⁠Fallback in @⁠Bean Javadoc
See gh-36439

(cherry picked from commit 37e8aa76e9)
2026-03-10 17:54:05 +01:00
Sam Brannen d4f4c69318 Document @Fallback alongside Primary in the reference docs and @Bean Javadoc
Closes gh-36439

(cherry picked from commit 27686dc2e2)
2026-03-10 17:28:49 +01:00
Sam Brannen 74ab6625ac Document registration recommendations for BeanPostProcessor and BeanFactoryPostProcessor
Closes gh-34964

(cherry picked from commit 3b8efbe5a6)
2026-03-10 13:09:35 +01:00
Sam Brannen 7c27183a4f Polishing
(cherry picked from commit 6f2e59a995)
2026-03-10 13:09:26 +01:00
Sam Brannen 526f67cd83 Polish @⁠Bean Javadoc and reference docs
(cherry picked from commit 644a20ae2a)
2026-03-10 13:09:17 +01:00
Sam Brannen 88f081b1cd Resolve context initializers only once in AbstractTestContextBootstrapper
The internal buildMergedContextConfiguration() method in
AbstractTestContextBootstrapper originally resolved the
ApplicationContextInitializer set only once. However, the changes made
in commit 2244461778 introduced a regression resulting in the
initializers being resolved twice: once for validation and once for
actually building the merged context configuration. In addition, the
resolution for validation does not honor the inheritInitializers flag
in ContextConfigurationAttributes.

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

See gh-18528
Closes gh-36430

(cherry picked from commit 463138acbc)
2026-03-08 14:50:42 +01:00
Sam Brannen 5806a23bcf Address Checkstyle violation
(cherry picked from commit bbe733def7)
2026-03-05 11:44:13 +01:00
Sam Brannen 507399d9a8 Polish documentation for FrameworkServlet and HttpServletBean
(cherry picked from commit 50c29e64f8)
2026-03-05 11:25:11 +01:00
Juergen Hoeller cd9a430869 Polishing
(cherry picked from commit 4734c15d81)
2026-03-03 23:38:55 +01:00
Sam Brannen 0a933787a6 Exclude legacy @⁠javax.validation.Constraint from attribute override check
Prior to this commit, we only excluded @⁠jakarta.validation.Constraint
from the convention-based annotation attribute override check.

This commit additionally excludes the legacy
@⁠javax.validation.Constraint annotation from the convention-based
annotation attribute override check.

See gh-28760
See gh-28761
Closes gh-36412
2026-03-03 17:02:13 +01:00
cetf 2bbea84830 Fix format string argument count
Fixes: gh-36410
Signed-off-by: cetf <cetf9h@163.com>
2026-03-03 12:15:12 +01:00
dependabot[bot] b9517b8ed7 Upgrade fast-xml-parser to 5.3.8
Closes gh-36402

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

(cherry picked from commit a87b2e0146)
2026-03-01 13:14:49 +01:00
Sam Brannen f41786b6af Fix typo
(cherry picked from commit bd40f0e6bb)
2026-03-01 13:06:56 +01:00
Sam Brannen 2d10d513e1 Consistently refer to URLs and URIs in documentation
(cherry picked from commit 04186fdf0e)
2026-03-01 13:06:56 +01:00
Tran Ngoc Nhan c83fb69d9b Fix links to UriComponentsBuilder and polish examples
Closes gh-36403

Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>

(cherry picked from commit b2b731b0ba)
2026-03-01 13:06:51 +01:00
Juergen Hoeller 566a42bec9 Remove prefixed FactoryBean name in ApplicationListenerDetector
Closes gh-36404

(cherry picked from commit a3b9098850)
2026-03-01 12:10:44 +01:00
Juergen Hoeller b426ef2d1b Polishing 2026-02-28 19:45:22 +01:00
Juergen Hoeller 728466dce0 Cancel late-executing tasks within revised closed handling
Closes gh-36362

(cherry picked from commit b6833ff31f)
2026-02-28 13:56:34 +01:00
Sam Brannen 455cb766a9 Further emphasize @⁠Configuration classes over XML and Groovy in testing chapter
See gh-36393

(cherry picked from commit 9eea227ab9)
2026-02-25 15:49:19 +01:00
Sam Brannen c77993acac Emphasize @⁠Configuration classes over XML and Groovy in testing chapter
Closes gh-36393

(cherry picked from commit 706c98228d)
2026-02-25 15:27:25 +01:00
Sam Brannen f3d0f9427a Upgrade to JUnit 5.14.3
Closes gh-36388
2026-02-25 11:00:24 +01:00
dependabot[bot] 688975e327 Upgrade fast-xml-parser to 5.3.6
Closes gh-36348

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
(cherry picked from commit ee6a115a18)
2026-02-25 10:29:02 +01:00
Juergen Hoeller b619e69130 Polishing (aligned with main) 2026-02-24 18:04:40 +01:00
Juergen Hoeller 972ba739aa Reject late-executing tasks after termination waiting
Closes gh-36362

(cherry picked from commit cff48fff2d)
2026-02-24 17:58:18 +01:00
Sam Brannen f87b5ee326 Polish contribution
See gh-36367

(cherry picked from commit b1cb9c5052)
2026-02-23 17:33:15 +01:00
Tran Ngoc Nhan 7768cee7a0 Polish SpEL operator examples in reference docs
Closes gh-36367

Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>
(cherry picked from commit 057633edb5)
2026-02-23 17:33:07 +01:00
rstoyanchev 9042682b56 Optimal charset handling in AbstractHttpMessageConverter
Closes gh-36320
2026-02-23 15:44:26 +00:00
Brian Clozel 2d81a9fe9b Optimize MediaType(MediaType, Charset) constructor
Prior to this commit, the `MediaType` and `MimeType` "copy" constructors
would not leverage the fact that the existing instance has been
validated already (types, subtype and parameters have been checked
already for errors) and the entire validation would be performed again.
This would also allocate map instances in the process.

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

Closes gh-36318
2026-02-23 15:40:18 +00:00
Brian Clozel a3f7179ab3 Fix InvalidMimeTypeException for compatible media types
The `AbstractMessageConverterMethodProcessor` is in charge of handling
controller method return values and to write those as HTTP response
messages. The content negotiation process is an important part.

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

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

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

Fixes gh-36300
2026-02-20 18:43:02 +01:00
rstoyanchev 50ef3b0a29 Update Javadoc of RestClient.Builder defaultStatusHandler
See gh-36248
2026-02-19 11:45:33 +00:00
Juergen Hoeller 1c7e79026f Skip serialization of potentially non-serializable cached state
Closes gh-36346

(cherry picked from commit 22bd8bd704)
2026-02-17 22:19:43 +01:00
Juergen Hoeller c8735efb9d Polishing (aligned with main) 2026-02-17 19:08:15 +01:00
Juergen Hoeller 999bbe3959 Polishing 2026-02-17 18:41:07 +01:00
Juergen Hoeller acab61f9a7 Consistent adaptation of HTTP headers on Servlet responses
Closes gh-36345
2026-02-17 18:40:50 +01:00
Juergen Hoeller bfa81290b3 Polishing 2026-02-17 12:18:31 +01:00
Juergen Hoeller 2c7ed5e5f1 Align validation groups check with InvocableHandlerMethod in spring-web
Closes gh-36337
2026-02-17 12:15:07 +01:00
Brian Clozel 474d520182 Fix MultipartParser & PartGenerator memory leak
Prior to this commit, the reactive `MultipartParser` and `PartGenerator`
types were leaking memory at runtime in specific cases:

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

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

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

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

Fixes gh-36262
2026-02-13 18:45:05 +01:00
Juergen Hoeller 50bffe7ddc Detect all common size exceptions from Tomcat and Commons FileUpload 2.x
Closes gh-36317

(cherry picked from commit e8e24e65d2)
2026-02-13 16:32:56 +01:00
Brian Clozel 6d849bd9b0 Next development version (v6.2.17-SNAPSHOT) 2026-02-12 10:03:33 +01:00
Christian Schuster 8334388e20 avoid unnecessary locking in ConcurrentReferenceHashMap's implementation of computeIfAbsent and computeIfPresent
Signed-off-by: Christian Schuster <christian@dnup.de>

Closes gh-36308

(cherry picked from commit a9b1d6335e)
2026-02-11 18:08:59 +01:00
Brian Clozel 757b713f22 Use updated message in HttpEntityMethodProcessor
Prior to this commit, the `HttpEntityMethodProcessor` would create a new
`ServletServerHttpRequest` input message to parse the native Servlet
request, but would not reuse it for reading the request body using the
message converters.

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

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

Fixes gh-36298
2026-02-11 14:07:21 +01:00
rstoyanchev a065563484 Optimize RequestMappingInfo hashcode calculation
Precalculated hashcode makes sense for infos in the registry, but
matched infos created on the fly don't need it.

Closes gh-36279
2026-02-11 09:42:01 +00:00
rstoyanchev 6162d89042 Cache HandlerMethod with resolved bean if singleton
See gh-36278
2026-02-11 09:12:08 +00:00
rstoyanchev 5c537db2cc Optimize single PathPattern match
Closes gh-36275
2026-02-11 09:10:54 +00:00
rstoyanchev 849553dc8e Avoid determineValidationGroups not necessary
There is no need to call determineValidationGroups if the method
itself doesn't require method validation.

See gh-36274
2026-02-11 09:05:19 +00:00
Brian Clozel 7240a5a669 Upgrade to Reactor 2024.0.15
Fixes gh-36289
2026-02-10 13:54:51 +01:00
Brian Clozel b00c387775 Upgrade to Micrometer 1.15.9
Closes gh-36290
2026-02-10 13:38:39 +01:00
Brian Clozel 8396c071af Fix wildcard MIME type support in messaging converters
Prior to this commit, the "application/*+json" wildcard MIME type was
added to the list of supported MIME types in the JSON messaging
converter. This change wasn't fully reflected in the
`AbstractMessageConverter`, because only strict matching of type and
subtybe were considered.

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

Fixes gh-36285
2026-02-10 10:42:51 +01:00
Juergen Hoeller bb35e9f11c Make LocalEntityManagerFactoryBean#setDataSource work on Hibernate and EclipseLink
Includes fix for consistent PersistenceException in case of no unit found for name.
Includes proper tests for LocalContainerEntityManagerFactoryBean with scan setup.

Closes gh-36272
2026-02-08 18:06:58 +01:00
Juergen Hoeller a0319b1f91 Repeatedly check status while trying to lock for shutdown
Closes gh-36260

(cherry picked from commit 20970a4a37)
2026-02-06 19:04:55 +01:00
Juergen Hoeller 5973a17253 Polishing
(cherry picked from commit 153c378bb2)
2026-02-05 20:30:36 +01:00
Juergen Hoeller ed7243259b Upgrade to Groovy 4.0.30, Netty 4.1.130, Mockito 5.21 2026-02-02 12:58:03 +01:00
Juergen Hoeller a391db2ef5 Upgrade to ASM 9.9.1 and Objenesis 3.5
Closes gh-36244

(cherry picked from commit 40350653e1)
2026-02-02 12:42:53 +01:00
dependabot[bot] 957202523c Upgrade fast-xml-parser from 4.5.2 to 5.3.4 in /framework-docs
Closes gh-36234

(cherry picked from commit 6fd84e4c2f)
2026-01-31 15:33:14 +01:00
qwding a3c9166da7 Optimize NamedParameterUtils#buildValueArray by lazily fetching SqlParameter
This PR optimizes the performance of NamedParameterUtils#buildValueArray by deferring the call to findParameter(declaredParams, paramName, i).

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

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

Signed-off-by: qwding <761945125@qq.com>
(cherry picked from commit 149397ed10)
2026-01-30 14:10:46 +01:00
Brian Clozel 0c3dd8cb00 Fix "remove" implementation in netty headers adapter
Prior to this commit, the `Netty4HeadersAdapter` `MultiValueMapi#remove`
implementation would return an empty list if no value was present. This
is not consistent with other implementations.

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

Fixes gh-36226
2026-01-29 11:31:50 +01:00
Juergen Hoeller 56d8ec25e8 Consistently close streams through try-with-resources
Also aligns byte array and Reader copying with 7.0.4.

Closes gh-36223

(cherry picked from commit 16f4b23c32)
2026-01-28 18:17:20 +01:00
Sam Brannen fb9db146f2 Consistently refer to "an SQL" statement, type, etc.
(cherry picked from commit 799a520c35)
2026-01-28 18:11:11 +01:00
Juergen Hoeller dcf98bc55c Polishing
(cherry picked from commit 5d33de943b)
2026-01-28 17:19:22 +01:00
Juergen Hoeller b9cd06a487 Support -1 for undetermined length in SqlBinaryValue/SqlCharacterValue
Closes gh-36219

(cherry picked from commit 7da65fe4fc)
2026-01-28 17:18:17 +01:00
Sam Brannen 91ff706e8c Fix formatting and spelling
(cherry picked from commit 4dacc64a30)
2026-01-28 14:31:44 +01:00
Sam Brannen 103057de2a Fix links to JUnit User Guide
Closes gh-36217

(cherry picked from commit ba47dd0714)
2026-01-28 14:31:34 +01:00
rstoyanchev b6d1e88563 Fix unfinished edit from previous commit
See gh-36198
2026-01-28 12:27:30 +00:00
rstoyanchev af1e9da3d7 Update docs on trailing slash handling
Closes gh-36198
2026-01-28 12:23:47 +00:00
rstoyanchev bc5e395a42 Update docs content types for ProblemDetail
Closes gh-36192
2026-01-28 12:21:56 +00:00
Juergen Hoeller 1ec3cb4d5f Polishing (aligned with main) 2026-01-27 21:25:23 +01:00
rstoyanchev 7b7126ae3d Polishing in ReactorClientHttpConnector 2026-01-26 10:46:59 +00:00
rstoyanchev 9f1332c716 Refine solution to clear Netty channel attribute
Closes gh-36158
2026-01-26 10:45:33 +00:00
Sam Brannen 2ff93f4207 Consistently indent with tabs instead of spaces 2026-01-25 17:57:36 +01:00
Sam Brannen 3a6c7786a1 Upgrade to AssertJ 3.27.7
(cherry picked from commit 801035bea7)
2026-01-25 17:57:36 +01:00
Padraic Slattery 539a098f8c Update GitHub upload-artifact action to version 6
Closes gh-36199

Signed-off-by: Padraic Slattery <pgoslatara@gmail.com>

(cherry picked from commit 65bdc78a1e)
2026-01-25 17:57:28 +01:00
Juergen Hoeller ecfd78ff93 Polishing (aligned with main) 2026-01-25 10:52:37 +01:00
Juergen Hoeller 1e0a85368e Revise setPersistenceUnitName javadoc
Closes gh-36205

(cherry picked from commit 22cf7958a5)
2026-01-25 10:47:56 +01:00
rstoyanchev c49d5dc860 Handle early exception from AsynchronousFileChannel#write
Closes gh-36184
2026-01-22 14:17:52 +00:00
Juergen Hoeller 58af70f2e3 Upgrade to Selenium 4.40, HtmlUnit 4.21, Protobuf 4.33.4 2026-01-21 14:59:57 +01:00
Juergen Hoeller c6e73b5d1d Fix JMSReplyTo references in javadoc (backported from main) 2026-01-21 14:52:16 +01:00
Juergen Hoeller f531cc9fdf Polishing
(cherry picked from commit 1c56ec3f7e)
2026-01-20 19:34:17 +01:00
Juergen Hoeller 1977f31821 Prevent accidental printStackTrace() usage in main codebase
Closes gh-36185

(cherry picked from commit 65565de1a8)
2026-01-20 19:33:30 +01:00
Sam Brannen cf3ed229de Fix test parameterization in CaffeineReactiveCachingTests
Prior to this commit, test methods in CaffeineReactiveCachingTests
were parameterized twice with the same configuration class.

See gh-31637
See gh-35833

(cherry picked from commit 9ebfdb8b6a)
2026-01-20 11:44:06 +01:00
Sam Brannen 154dad6587 Revise contribution
See gh-36170

(cherry picked from commit b164db35c1)
2026-01-18 17:02:09 +01:00
Tran Ngoc Nhan fd8b4d5936 Replace getErrors() with getBindingResult() in examples
DataBinder#getErrors was removed in v4.0.0.M1.

Closes gh-36170

Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>

(cherry picked from commit 385e62dbf0)
2026-01-18 17:02:01 +01:00
Juergen Hoeller 13defc09f9 Fix setBody call in reactorNettyAttributes test
See gh-36158
2026-01-17 00:05:15 +01:00
Juergen Hoeller 9e512315ab Avoid javadoc build failure against HttpClient 5.5.x 2026-01-16 23:38:32 +01:00
Juergen Hoeller c750efb0a6 Bring back WebLogicJtaTransactionManager for WebLogic 15.1.1
Closes gh-36152
2026-01-16 23:19:48 +01:00
Juergen Hoeller 0033edad45 Apply transactionIsolationLock in EclipseLinkConnectionHandle as well
Closes gh-36165

(cherry picked from commit 2b96a61063)
2026-01-16 23:15:56 +01:00
rstoyanchev 183cd4c8cd Clear Netty channel attribute
Closes gh-36158
2026-01-16 17:22:11 +00:00
Juergen Hoeller 6bd0ed8e79 Polishing
(cherry picked from commit 62fd09dfa5)
2026-01-14 22:13:07 +01:00
rstoyanchev dcb7922a24 Exclude DataAccessException and MessagingException in DisconnectedClientHelper
Closes gh-36135
2026-01-14 11:55:51 +00:00
Yanming Zhou 05a9cc26ae Improve DisconnectedClientHelper to better guard ClassNotFoundException
Before this commit, WebClientException is ignored if RestClientException is not present.

Closes gh-36150

Signed-off-by: Yanming Zhou <zhouyanming@gmail.com>
2026-01-14 11:55:51 +00:00
Sam Brannen 05beb4ad4d Upgrade to JUnit 5.14.2
Closes gh-36148
2026-01-14 12:39:33 +01:00
rstoyanchev 5236b35687 Update Principal check in TransportHandlingSockJsService
Closes gh-35753
2026-01-12 15:21:59 +00:00
Sébastien Deleuze 7482ccc18f Upgrade Antora dependencies
Closes gh-36106
2026-01-07 09:57:09 +01:00
Sam Brannen b3fb9f4e31 Reliably resolve generic read/write methods in PropertyDescriptorUtils
Prior to this commit, the determineBasicProperties() method in
PropertyDescriptorUtils did not reliably resolve read/write methods in
type hierarchies with generics. This utility method is used by
SimpleBeanInfoFactory which is used by BeanUtils and BeanWrapperImpl.
Thus, failure to reliably resolve read/write JavaBeans methods resulted
in bugs in certain scenarios.

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

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

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

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

Closes gh-36019

(cherry picked from commit 4b07edbaeb)
2025-12-29 12:41:46 +02:00
Sam Brannen add3b1a7f4 Extract CopyPropertiesTests as nested test class in BeanUtilsTests 2025-12-29 12:38:01 +02:00
Sam Brannen 476aae5ce8 Polish integration tests 2025-12-29 12:32:42 +02:00
Brian Clozel be68a777b6 Fix memory leak in WiretapConnector
Prior to this commit, we found in gh-35953 that using the `WebTestClient`
the following way leaks data buffers:

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

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

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

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

Fixes gh-36050
2025-12-19 16:20:10 +01:00
Juergen Hoeller 9a0bfd7306 Do not attempt nested PropertyHandler resolution for argument conversion
This is not actually triggered on 6.2.x but nevertheless worth aligning.
Includes fix for return type declaration in PropertyAccessor subclasses.
Includes related polishing from main commits.

See gh-36024
2025-12-17 14:56:12 +01:00
Brian Clozel 91a0c28fa9 Fix RfcUriParser parsing for single char fragments
Prior to this commit, the `RfcUriParser` would ignore URI fragments if
their length is < 2. This commit fixes the length check to allow for
single char fragments when parsing URIs.

Fixes gh-36029
2025-12-17 09:54:08 +01:00
Sam Brannen c0e8a36592 Polishing
(cherry picked from commit ed451c107f)
2025-12-13 17:17:46 +01:00
Sam Brannen 836289315d Revise contribution
See gh-36022

(cherry picked from commit 452257eb96)
2025-12-13 17:16:49 +01:00
Tran Ngoc Nhan 08b77dd0ad Fix typos and grammar in reference manual
Closes gh-36022

Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>

(cherry picked from commit a7863a0877)
2025-12-13 17:16:04 +01:00
Stéphane Nicoll 5eb16a6a17 Refresh GitHub Actions 2025-12-12 11:58:22 +01:00
Brian Clozel d4705804cc Next development version (v6.2.16-SNAPSHOT) 2025-12-11 11:34:10 +01:00
Brian Clozel 23625ee698 Do not send null HTTP header value in JdkClientHttpRequest
Prior to this commit, the `JdkClientHttpRequest` would add all values
from `HttpHeaders` to the native request builder. This could cause
`NullPointerException` being thrown at runtime because the `HttpClient`
does not support that.

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

Fixes gh-35996
2025-12-11 09:57:55 +01:00
Brian Clozel c89c4ac614 Upgrade to Reactor 2024.0.13
Closes gh-35987
2025-12-09 22:26:46 +01:00
Brian Clozel 2155e9fb25 Upgrade to Micrometer 1.14.14
Closes gh-35986
2025-12-09 22:26:10 +01:00
Juergen Hoeller 24df35c55a Do not keep target connection after failed settings
Includes aligned setReadOnly exception suppression.

Closes gh-35980

(cherry picked from commit ab33000750)
2025-12-09 13:01:36 +01:00
Brian Clozel 3b1fa369b4 Polishing contribution
Closes gh-35978
2025-12-08 16:11:38 +01:00
Johnny Lim 221adf14a4 Fix SubscriberInputStream.resume()
See gh-35978

Signed-off-by: Johnny Lim <izeye@naver.com>
2025-12-08 16:11:31 +01:00
Juergen Hoeller ad849fb3a2 Accept assignable match for covariant return type
See gh-35936

(cherry picked from commit df27627516)
2025-12-08 12:43:35 +01:00
Juergen Hoeller 8041a09268 Polishing 2025-12-05 16:23:12 +01:00
Juergen Hoeller 97b9517918 Handle absolute file URLs in getClassPathManifestEntriesFromJar
Closes gh-35682

(cherry picked from commit 196c1dd51c)
2025-12-05 16:16:44 +01:00
Sam Brannen e79d8e35a2 Document that annotations which reference types not present are ignored
Add warnings to the class-level Javadoc for MergedAnnotations,
AnnotatedTypeMetadata, AnnotationMetadata, and MethodMetadata to point
out that annotations may be ignored if their attributes reference types
that are not present in the classpath.

Closes gh-35959

(cherry picked from commit b916dc962e)
2025-12-05 16:02:14 +01:00
rstoyanchev 96503a2ea6 Polishing 2025-12-04 09:31:20 +00:00
rstoyanchev 8803d3c5dc Lower bad requests to DEBUG level in AbstractHandshakeHandler
Closes gh-35930
2025-12-04 09:24:11 +00:00
rstoyanchev 5b4728f0bf Avoid http - web.utils package cycle
Closes gh-35952
2025-12-04 09:23:45 +00:00
Juergen Hoeller 1fd1d8d629 Polishing
(cherry picked from commit 07c0213f20)
2025-12-04 00:40:02 +01:00
Juergen Hoeller afc40d7b93 Avoid computeIfAbsent for createMappings which calls back into same map
Closes gh-35944

(cherry picked from commit 667851c0fa)
2025-12-04 00:15:36 +01:00
Sam Brannen 3eb1df0ac0 Update antora-extensions to 1.14.7
Closes gh-35949

(cherry picked from commit c7e24a5c26)
2025-12-02 15:36:45 +01:00
Juergen Hoeller 2aa3b0a77e Select method with resolved return type match (among multiple candidates)
Removes unnecessary array type check for parameters of candidate methods.

Closes gh-35936

(cherry picked from commit 92e9543ad4)
2025-12-01 23:28:09 +01:00
ivonaest 0008106adb Upgrade json-path to 2.10.0
Closes gh-35924
Signed-off-by: ivonaest <ivona.cvija@est.tech>
2025-12-01 15:36:44 +01:00
Juergen Hoeller 8f6d44a86b Improve debug log for received message
(cherry picked from commit 71d18ebabc)
2025-12-01 15:04:42 +01:00
Juergen Hoeller 874498272f Clear remaining invoker resources when releasing shared Connection
Closes gh-35932

(cherry picked from commit 3ccb0786db)
2025-12-01 15:04:39 +01:00
rstoyanchev 2144813bad FragmentsRendering exposes its fragments
Closes gh-35775
2025-12-01 11:20:43 +00:00
John Niang 67a92306f7 BindingContext constructor uses given ReactiveAdapterRegistry arg
Closes gh-35771

Signed-off-by: John Niang <johnniang@foxmail.com>
2025-12-01 11:20:29 +00:00
rstoyanchev bc0731891b Use channelId for ReactorNettyWebSocketSession's id
Closes gh-35883
2025-12-01 11:20:13 +00:00
Juergen Hoeller 3faa7cac4d Polishing 2025-11-30 11:41:24 +01:00
Sam Brannen 3121daf553 Stop linking to 6.1.22-SNAPSHOT reference docs
With this commit, we now include snapshots for main (which currently
correlates to 7.0.x), 6.2.x, and 7.0.x to 9.*.x.

Closes gh-35923

(cherry picked from commit 305a512a55)
2025-11-28 14:06:48 +01:00
Juergen Hoeller 821165488e Use concurrent set for reactive transaction synchronizations
Closes gh-35921

(cherry picked from commit fd25e2f468)
2025-11-28 10:25:54 +01:00
Juergen Hoeller 3b556ba0c0 Polishing 2025-11-27 16:10:56 +01:00
Juergen Hoeller b25f98374b Tighten cacheable decision behind @Lazy injection point
Closes gh-35917

(cherry picked from commit 61d5413c23)
2025-11-27 16:03:27 +01:00
Sam Brannen c2f7cd3401 Convert sentence to tip in Kotlin testing chapter
This commit also moves the text to a more appropriate section of the
chapter.

(cherry picked from commit 24d152cdab)
2025-11-27 12:38:55 +01:00
Juergen Hoeller 22d2810ed0 Narrow method annotation check in hasQualifier to setter methods
Closes gh-35908

(cherry picked from commit 6c3132cb8c)
2025-11-26 23:02:58 +01:00
Tran Ngoc Nhan b39055f293 Fix broken Javadoc links to methods
Closes gh-35904
Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>
2025-11-26 17:12:18 +01:00
Sam Brannen 140933400d Refer to "Spring Tools" instead of "Spring Tools for Eclipse"
Closes gh-35901

(cherry picked from commit 6504177e7b)
2025-11-26 16:12:27 +01:00
Juergen Hoeller e7a5452a14 Consistent namespace element declarations
(cherry picked from commit f58d0f6aae)
2025-11-26 15:14:31 +01:00
Juergen Hoeller ab96576e67 Expose non-existent resources at the end of the sorted result
Closes gh-35895

(cherry picked from commit c1b6bfb681)
2025-11-26 15:14:28 +01:00
Juergen Hoeller 19b080b73f Clarify JMS sessionTransacted flag for local versus global transaction
Closes gh-35897

(cherry picked from commit 9d4abb63d8)
2025-11-26 15:14:24 +01:00
rstoyanchev f9b4fba97a Add required type to TypeMismatchException message args
Closes gh-35837
2025-11-26 12:50:26 +00:00
Sam Brannen 37e26e0377 Use current links to JUnit documentation
Closes gh-35892

(cherry picked from commit f4ee120a42)
2025-11-26 13:25:54 +01:00
Sam Brannen 85c47a73dd Fix formatting for backticks in Kotlin docs
(cherry picked from commit e625a28f6d)
2025-11-26 13:24:27 +01:00
Sam Brannen ab93020263 Link to Spring Framework Artifacts wiki page
This commit revises the Integration Testing chapter to reference the
"Spring Framework Artifacts" wiki page instead of the nonexistent
"Dependency Management" section of the reference manual.

Closes gh-35890

(cherry picked from commit 45c1cd9295)
2025-11-26 12:55:46 +01:00
github-actions[bot] a4134663a6 Update Antora Spring UI to v0.4.25
Closes gh-35877

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-11-24 14:44:14 +01:00
github-actions[bot] a4b1155ca6 Update Antora Spring UI to v0.4.22
Closes gh-35859
2025-11-24 10:14:50 +01:00
Sébastien Deleuze 1bdd8337c6 Update outdated comments in JdbcOperationsExtensions.kt 2025-11-20 09:55:10 +01:00
Brian Clozel 7a0ea14452 Next development version (v6.2.15-SNAPSHOT) 2025-11-20 09:48:06 +01:00
Juergen Hoeller 59025c5b96 Lazily initialize ProblemDetail for picking up actual status code
Closes gh-35829

(cherry picked from commit 3026f0a49b)
2025-11-19 17:31:27 +01:00
Sam Brannen e5de8b9bc6 Fix link to MockMvc test in HtmlUnit section
See gh-35853

(cherry picked from commit 9fe4e7798d)
2025-11-19 17:19:34 +01:00
Sam Brannen e146e809e5 Polishing
(cherry picked from commit d178930186)
2025-11-19 17:19:34 +01:00
Tran Ngoc Nhan ff62e7355f Fix cross-reference links in HtmlUnit sections
Closes gh-35853

Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>

(cherry picked from commit 91d2a51f3f)
2025-11-19 17:19:30 +01:00
Juergen Hoeller 4e97013595 Polishing
(cherry picked from commit f456674529)
2025-11-19 16:21:38 +01:00
Juergen Hoeller bd10b7ae06 Remove javadoc references to deprecated PropertiesBeanDefinitionReader
Closes gh-35836

(cherry picked from commit 35b8fbf901)
2025-11-19 16:21:33 +01:00
Juergen Hoeller e4288170c8 Fix getCacheNames() concurrent access in NoOpCacheManager
Closes gh-35842

(cherry picked from commit 57a1d4007b)
2025-11-18 13:50:13 +01:00
Juergen Hoeller 8545a759a7 Add resetCaches() method to Caffeine/ConcurrentMapCacheManager
Closes gh-35840

(cherry picked from commit bc3431f435)
2025-11-18 13:49:20 +01:00
Juergen Hoeller f94645de17 Narrow Aware interface exclusion check to BeanFactoryAware only
Closes gh-35835

(cherry picked from commit de5b9aab55)
2025-11-18 13:48:32 +01:00
Sam Brannen 8553f97df1 Merge HtmlCharacterEntityDecoderTests into HtmlUtilsTests
See gh-35711

(cherry picked from commit 0342cd0904)
2025-11-17 15:29:52 +01:00
Brian Clozel 030dace2be Polishing contribution
Closes gh-35477
2025-11-17 15:15:39 +01:00
potato 5af1c9b487 Fix HtmlUtils unescape for supplementary chars
See gh-35477

Signed-off-by: potato <65760583+juntae6942@users.noreply.github.com>
2025-11-17 15:15:39 +01:00
Patrick Strawderman f3ed04c9d7 Fix single-check idiom in UnmodifiableMultiValueMap
Read the respective fields only once in the values(), entrySet(), and
keySet() methods.

Closes gh-35822

Signed-off-by: Patrick Strawderman <pstrawderman@netflix.com>

(cherry picked from commit 3b6be3d4d3)
2025-11-17 15:04:49 +01:00
github-actions[bot] 46ee944acc Update Antora Spring UI to v0.4.20
Closes gh-35814
2025-11-17 14:22:35 +01:00
Sam Brannen 6be1c29fda Polish contribution
See gh-35817

(cherry picked from commit 09a8bbc0c7)
2025-11-17 12:28:03 +01:00
Patrick Strawderman 3fa56db88b Fix Spliterator characteristics in ConcurrentReferenceHashMap
The Spliterators returned by values, entrySet, and keySet incorrectly
reported the SIZED characteristic, instead of CONCURRENT. This could
lead to bugs when the map is concurrently modified during a stream
operation.

For keySet and values, the incorrect characteristics are inherited from
AbstractMap, so to rectify that the respective methods are overridden,
and custom collections are provided that report the correct Spliterator
characteristics.

Closes gh-35817

Signed-off-by: Patrick Strawderman <pstrawderman@netflix.com>

(cherry picked from commit ed75906834)
2025-11-17 12:27:43 +01:00
1199 changed files with 10571 additions and 6667 deletions
@@ -24,14 +24,14 @@ runs:
using: composite
steps:
- name: Set Up Java
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
distribution: ${{ inputs.java-early-access == 'true' && 'temurin' || (inputs.java-distribution || 'liberica') }}
java-version: |
${{ inputs.java-early-access == 'true' && format('{0}-ea', inputs.java-version) || inputs.java-version }}
${{ inputs.java-toolchain == 'true' && '17' || '' }}
- name: Set Up Gradle
uses: gradle/actions/setup-gradle@ac638b010cf58a27ee6c972d7336334ccaf61c96 # v4.4.1
uses: gradle/actions/setup-gradle@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # v5.0.0
with:
cache-read-only: false
develocity-access-key: ${{ inputs.develocity-access-key }}
@@ -17,14 +17,14 @@ runs:
using: composite
steps:
- name: Set Up JFrog CLI
uses: jfrog/setup-jfrog-cli@f748a0599171a192a2668afee8d0497f7c1069df # v4.5.6
uses: jfrog/setup-jfrog-cli@5b06f730cc5a6f55d78b30753f8583454b08c0aa # v4.8.1
env:
JF_ENV_SPRING: ${{ inputs.jfrog-cli-config-token }}
- name: Download Release Artifacts
shell: bash
run: jf rt download --spec ${{ format('{0}/artifacts.spec', github.action_path) }} --spec-vars 'buildName=${{ format('spring-framework-{0}', inputs.spring-framework-version) }};buildNumber=${{ github.run_number }}'
- name: Sync
uses: spring-io/central-publish-action@0cdd90d12e6876341e82860d951e1bcddc1e51b6 # v0.2.0
uses: spring-io/central-publish-action@0c03960e9b16fdfe70e2443e1d5393cbc3a35622 # v0.3.0
with:
token: ${{ inputs.central-token-password }}
token-name: ${{ inputs.central-token-username }}
+2 -2
View File
@@ -18,9 +18,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Set up Java
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
distribution: 'liberica'
java-version: 17
@@ -13,7 +13,7 @@ jobs:
timeout-minutes: 60
steps:
- name: Check Out Code
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Build and Publish
id: build-and-publish
uses: ./.github/actions/build
@@ -21,7 +21,7 @@ jobs:
develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
publish: true
- name: Deploy
uses: spring-io/artifactory-deploy-action@dc1913008c0599f0c4b1fdafb6ff3c502b3565ea # v0.0.2
uses: spring-io/artifactory-deploy-action@926d7f7cc810569395346bf3a4d91b380b3e355b # v0.0.4
with:
artifact-properties: |
/**/framework-api-*.zip::zip.name=spring-framework,zip.deployed=false
+2 -2
View File
@@ -10,7 +10,7 @@ jobs:
timeout-minutes: 60
steps:
- name: Check Out Code
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Build
id: build
uses: ./.github/actions/build
@@ -19,7 +19,7 @@ jobs:
uses: ./.github/actions/print-jvm-thread-dumps
- name: Upload Build Reports
if: failure()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: build-reports
path: '**/build/reports/'
+1 -1
View File
@@ -36,7 +36,7 @@ jobs:
git config --global core.longPaths true
Stop-Service -name Docker
- name: Check Out Code
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Build
id: build
uses: ./.github/actions/build
+1 -1
View File
@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 1
ref: docs-build
+5 -5
View File
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check Out Code
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Build and Publish
id: build-and-publish
uses: ./.github/actions/build
@@ -20,7 +20,7 @@ jobs:
develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
publish: true
- name: Stage Release
uses: spring-io/artifactory-deploy-action@dc1913008c0599f0c4b1fdafb6ff3c502b3565ea # v0.0.2
uses: spring-io/artifactory-deploy-action@926d7f7cc810569395346bf3a4d91b380b3e355b # v0.0.4
with:
artifact-properties: |
/**/framework-api-*.zip::zip.name=spring-framework,zip.deployed=false
@@ -56,7 +56,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check Out Code
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@v6
- name: Sync to Maven Central
uses: ./.github/actions/sync-to-maven-central
with:
@@ -72,7 +72,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Set up JFrog CLI
uses: jfrog/setup-jfrog-cli@dff217c085c17666e8849ebdbf29c8fe5e3995e6 # v4.5.2
uses: jfrog/setup-jfrog-cli@5b06f730cc5a6f55d78b30753f8583454b08c0aa # v4.8.1
env:
JF_ENV_SPRING: ${{ secrets.JF_ARTIFACTORY_SPRING }}
- name: Promote build
@@ -85,7 +85,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check Out Code
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@v6
- name: Create GitHub Release
uses: ./.github/actions/create-github-release
with:
+5 -5
View File
@@ -30,23 +30,23 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check Out Release Verification Tests
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
ref: 'v0.0.2'
repository: spring-projects/spring-framework-release-verification
token: ${{ secrets.token }}
- name: Check Out Send Notification Action
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
path: send-notification
sparse-checkout: .github/actions/send-notification
- name: Set Up Java
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
distribution: 'liberica'
java-version: 17
- name: Set Up Gradle
uses: gradle/actions/setup-gradle@ac638b010cf58a27ee6c972d7336334ccaf61c96 # v4.4.1
uses: gradle/actions/setup-gradle@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # v5.0.0
with:
cache-read-only: false
- name: Configure Gradle Properties
@@ -64,7 +64,7 @@ jobs:
run: ./gradlew spring-framework-release-verification-tests:test
- name: Upload Build Reports on Failure
if: failure()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: build-reports
path: '**/build/reports/'
+1
View File
@@ -9,6 +9,7 @@ ivy-cache
argfile*
activemq-data/
classes/
.cursor/
# Log files
jxl.log
+2 -6
View File
@@ -91,14 +91,10 @@ configure([rootProject] + javaProjects) { project ->
"https://fasterxml.github.io/jackson-core/javadoc/2.14/",
"https://fasterxml.github.io/jackson-databind/javadoc/2.14/",
"https://fasterxml.github.io/jackson-dataformat-xml/javadoc/2.14/",
"https://hc.apache.org/httpcomponents-client-5.5.x/current/httpclient5/apidocs/",
//"https://hc.apache.org/httpcomponents-client-5.5.x/current/httpclient5/apidocs/",
"https://projectreactor.io/docs/test/release/api/",
"https://junit.org/junit4/javadoc/4.13.2/",
// TODO Uncomment link to JUnit 5 docs once we execute Gradle with Java 18+.
// See https://github.com/spring-projects/spring-framework/issues/27497
//
// "https://junit.org/junit5/docs/5.14.1/api/",
"https://www.reactive-streams.org/reactive-streams-1.0.3-javadoc/",
"https://www.reactive-streams.org/reactive-streams-1.0.4-javadoc/",
//"https://javadoc.io/static/io.rsocket/rsocket-core/1.1.1/",
"https://r2dbc.io/spec/1.0.0.RELEASE/api/",
// Previously there could be a split-package issue between JSR250 and JSR305 javax.annotation packages,
+5 -3
View File
@@ -13,8 +13,10 @@ content:
- url: https://github.com/spring-projects/spring-framework
# Refname matching:
# https://docs.antora.org/antora/latest/playbook/content-refname-matching/
branches: ['main', '{6..9}.+({1..9}).x']
tags: ['v{6..9}.+({0..9}).+({0..9})?(-{RC,M}*)', '!(v6.0.{0..8})', '!(v6.0.0-{RC,M}{0..9})']
# branches: We include snapshots for main, 6.2.x, and 7.0.x to 9.*.x.
branches: ['main', '6.2.x', '{7..9}.+({0..9}).x']
# tags: include all releases from 6.2.0 to 9.*.*.
tags: ['v6.2.+({0..9})', 'v{7..9}.+({0..9}).+({0..9})?(-{RC,M}*)']
start_path: framework-docs
asciidoc:
extensions:
@@ -36,4 +38,4 @@ runtime:
failure_level: warn
ui:
bundle:
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.18/ui-bundle.zip
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.26/ui-bundle.zip
+1 -1
View File
@@ -326,9 +326,9 @@
*** xref:testing/testcontext-framework/application-events.adoc[]
*** xref:testing/testcontext-framework/test-execution-events.adoc[]
*** xref:testing/testcontext-framework/ctx-management.adoc[]
**** xref:testing/testcontext-framework/ctx-management/javaconfig.adoc[]
**** xref:testing/testcontext-framework/ctx-management/xml.adoc[]
**** xref:testing/testcontext-framework/ctx-management/groovy.adoc[]
**** xref:testing/testcontext-framework/ctx-management/javaconfig.adoc[]
**** xref:testing/testcontext-framework/ctx-management/mixed-config.adoc[]
**** xref:testing/testcontext-framework/ctx-management/context-customizers.adoc[]
**** xref:testing/testcontext-framework/ctx-management/initializers.adoc[]
@@ -104,7 +104,7 @@ Note that pointcut definitions are generally matched against any intercepted met
If a pointcut is strictly meant to be public-only, even in a CGLIB proxy scenario with
potential non-public interactions through proxies, it needs to be defined accordingly.
If your interception needs include method calls or even constructors within the target
If your interception needs to include method calls or even constructors within the target
class, consider the use of Spring-driven xref:core/aop/using-aspectj.adoc#aop-aj-ltw[native AspectJ weaving] instead
of Spring's proxy-based AOP framework. This constitutes a different mode of AOP usage
with different characteristics, so be sure to make yourself familiar with weaving
@@ -378,7 +378,7 @@ The container also supports creating a bean with {spring-framework-api}++/beans/
. The custom arguments require dynamic introspection of a matching constructor or factory method.
Those arguments cannot be detected by AOT, so the necessary reflection hints will have to be provided manually.
. By-passing the instance supplier means that all other optimizations after creation are skipped as well.
. Bypassing the instance supplier means that all other optimizations after creation are skipped as well.
For instance, autowiring on fields and methods will be skipped as they are handled in the instance supplier.
Rather than having prototype-scoped beans created with custom arguments, we recommend a manual factory pattern where a bean is responsible for the creation of the instance.
@@ -151,17 +151,17 @@ injected into a `Set<MovieCatalog>` annotated with `@Qualifier("action")`.
[TIP]
====
Letting qualifier values select against target bean names, within the type-matching
candidates, does not require a `@Qualifier` annotation at the injection point.
If there is no other resolution indicator (such as a qualifier or a primary marker),
for a non-unique dependency situation, Spring matches the injection point name
(that is, the field name or parameter name) against the target bean names and chooses
the same-named candidate, if any (either by bean name or by associated alias).
candidates, does not require a `@Qualifier` annotation at the injection point. If there
is no other resolution indicator (such as a qualifier, a primary marker, or a fallback
marker), for a non-unique dependency situation, Spring matches the injection point name
(that is, the field name or parameter name) against the target bean names and chooses the
same-named candidate, if any (either by bean name or by associated alias).
Since version 6.1, this requires the `-parameters` Java compiler flag to be present.
As of 6.2, the container applies fast shortcut resolution for bean name matches,
bypassing the full type matching algorithm when the parameter name matches the
bean name and no type, qualifier or primary conditions override the match. It is
therefore recommendable for your parameter names to match the target bean names.
Since version 6.1, this requires the `-parameters` Java compiler flag to be present. As
of 6.2, the container applies fast shortcut resolution for bean name matches, bypassing
the full type matching algorithm when the parameter name matches the bean name and no
type, qualifier, primary, or fallback conditions override the match. It is therefore
recommendable for your parameter names to match the target bean names.
====
As an alternative for injection by name, consider the JSR-250 `@Resource` annotation
@@ -274,7 +274,7 @@ Kotlin::
Next, you can provide the information for the candidate bean definitions. You can add
`<qualifier/>` tags as sub-elements of the `<bean/>` tag and then specify the `type` and
`value` to match your custom qualifier annotations. The type is matched against the
fully-qualified class name of the annotation. Alternately, as a convenience if no risk of
fully-qualified class name of the annotation. Alternatively, as a convenience if no risk of
conflicting names exists, you can use the short class name. The following example
demonstrates both approaches:
@@ -308,7 +308,8 @@ set of multiple matches for the specific bean type (as returned by the factory m
Note that the standard `jakarta.annotation.Priority` annotation is not available at the
`@Bean` level, since it cannot be declared on methods. Its semantics can be modeled
through `@Order` values in combination with `@Primary` on a single bean for each type.
through `@Order` values in combination with `@Primary` or `@Fallback` on a single bean
for each type.
====
Even typed `Map` instances can be autowired as long as the expected key type is `String`.
@@ -27,4 +27,5 @@ with the `CustomAutowireConfigurer`
When multiple beans qualify as autowire candidates, the determination of a "`primary`" is
as follows: If exactly one bean definition among the candidates has a `primary`
attribute set to `true`, it is selected.
attribute set to `true`, it is selected. For annotation-based configuration, see
xref:core/beans/annotation-config/autowired-primary.adoc[Fine-tuning with `@Primary` or `@Fallback`].
@@ -575,7 +575,7 @@ Kotlin::
----
======
NOTE: Do not define such beans to be lazy as the `ApplicationContext` will honour that and will not register the method to listen to events.
NOTE: Do not define such beans to be lazy as the `ApplicationContext` will honor that and will not register the method to listen to events.
The method signature once again declares the event type to which it listens,
but, this time, with a flexible name and without implementing a specific listener interface.
@@ -50,9 +50,8 @@ XML configuration:
The preceding XML is more succinct. However, typos are discovered at runtime rather than
design time, unless you use an IDE (such as https://www.jetbrains.com/idea/[IntelliJ
IDEA] or the {spring-site-tools}[Spring Tools for Eclipse])
that supports automatic property completion when you create bean definitions. Such IDE
assistance is highly recommended.
IDEA] or the {spring-site-tools}[Spring Tools]) that supports automatic property
completion when you create bean definitions. Such IDE assistance is highly recommended.
You can also configure a `java.util.Properties` instance, as follows:
@@ -67,6 +67,13 @@ interface, clearly indicating the post-processor nature of that bean. Otherwise,
Since a `BeanPostProcessor` needs to be instantiated early in order to apply to the
initialization of other beans in the context, this early type detection is critical.
Furthermore, when registering a `BeanPostProcessor` via an `@Bean` factory method,
declare the method as `static` and ideally with no dependencies. Doing so avoids eager
initialization of the configuration class and other beans, which would make them
ineligible for full post-processing (such as auto-proxying). See the
"BeanPostProcessor-returning `@Bean` methods" section in the
{spring-framework-api}/context/annotation/Bean.html[`@Bean`] javadoc for details.
[[beans-factory-programmatically-registering-beanpostprocessors]]
.Programmatically registering `BeanPostProcessor` instances
NOTE: While the recommended approach for `BeanPostProcessor` registration is through
@@ -80,7 +87,7 @@ of execution. Note also that `BeanPostProcessor` instances registered programmat
are always processed before those registered through auto-detection, regardless of any
explicit ordering.
.`BeanPostProcessor` instances and AOP auto-proxying
.`BeanPostProcessor` instances and early initialization
[NOTE]
====
Classes that implement the `BeanPostProcessor` interface are special and are treated
@@ -90,17 +97,23 @@ of the `ApplicationContext`. Next, all `BeanPostProcessor` instances are registe
in a sorted fashion and applied to all further beans in the container. Because AOP
auto-proxying is implemented as a `BeanPostProcessor` itself, neither `BeanPostProcessor`
instances nor the beans they directly reference are eligible for auto-proxying and,
thus, do not have aspects woven into them.
thus, do not have aspects woven into them. More generally, any bean that is instantiated
during this early phase is not eligible for full post-processing by all
`BeanPostProcessor` instances.
For any such bean, you should see an informational log message: `Bean someBean is not
eligible for getting processed by all BeanPostProcessor interfaces (for example: not
eligible for auto-proxying)`.
For any such bean, you should see a WARN-level log message similar to the following.
If you have beans wired into your `BeanPostProcessor` by using autowiring or
`@Resource` (which may fall back to autowiring), Spring might access unexpected beans
when searching for type-matching dependency candidates and, therefore, make them
ineligible for auto-proxying or other kinds of bean post-processing. For example, if you
have a dependency annotated with `@Resource` where the field or setter name does not
[quote]
Bean 'someBean' of type [org.example.SomeType] is not eligible for getting processed by
all BeanPostProcessors (for example: not eligible for auto-proxying).
To minimize the number of beans affected, register a `BeanPostProcessor` with a
`static` `@Bean` method that has no dependencies (see the note above). If you have
beans wired into your `BeanPostProcessor` by using autowiring or `@Resource` (which
may fall back to autowiring), Spring might access unexpected beans when searching
for type-matching dependency candidates and, therefore, make them ineligible for
auto-proxying or other kinds of bean post-processing. For example, if you have a
dependency annotated with `@Resource` where the field or setter name does not
directly correspond to the declared name of a bean and no name attribute is used,
Spring accesses other beans for matching them by type.
====
@@ -135,7 +148,7 @@ Java::
}
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("Bean '" + beanName + "' created : " + bean.toString());
System.out.println("Bean '" + beanName + "' created : " + bean);
return bean;
}
}
@@ -164,7 +177,48 @@ Kotlin::
----
======
The following `beans` element uses the `InstantiationTracingBeanPostProcessor`:
You can register the `InstantiationTracingBeanPostProcessor` with Java configuration
by using a `static` `@Bean` method (recommended to avoid eager initialization of the
configuration class and other beans):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
----
@Configuration
public class AppConfig {
@Bean
public static InstantiationTracingBeanPostProcessor instantiationTracingBeanPostProcessor() {
return new InstantiationTracingBeanPostProcessor();
}
// ... other bean definitions
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",chomp="-packages"]
----
@Configuration
class AppConfig {
@Bean
companion object {
@JvmStatic
fun instantiationTracingBeanPostProcessor() = InstantiationTracingBeanPostProcessor()
}
// ... other bean definitions
}
----
======
Alternatively, the `InstantiationTracingBeanPostProcessor` can be registered via the
`bean` element with XML configuration:
[source,xml,indent=0,subs="verbatim,quotes"]
----
@@ -192,7 +246,7 @@ The following `beans` element uses the `InstantiationTracingBeanPostProcessor`:
----
Notice how the `InstantiationTracingBeanPostProcessor` is merely defined. It does not
even have a name, and, because it is a bean, it can be dependency-injected as you would any
even have a name, and, because it is a bean, it can be dependency-injected as with any
other bean. (The preceding configuration also defines a bean that is backed by a Groovy
script. The Spring dynamic language support is detailed in the chapter entitled
xref:languages/dynamic.adoc[Dynamic Language Support].)
@@ -301,6 +355,23 @@ implement the `BeanFactoryPostProcessor` interface. It uses these beans as bean
post-processors, at the appropriate time. You can deploy these post-processor beans as
you would any other bean.
When registering a `BeanFactoryPostProcessor` via an `@Bean` factory method in a
`@Configuration` class, declare the method as `static` to avoid lifecycle conflicts
with annotation processing (such as `@Autowired`, `@Value`, and `@PostConstruct`) in the
configuration class. See the "BeanFactoryPostProcessor-returning `@Bean` methods"
section in the {spring-framework-api}/context/annotation/Bean.html[`@Bean`] javadoc
for details and an example.
For any non-static `@Bean` factory method with a `BeanFactoryPostProcessor` return type,
you should see an INFO-level log message similar to the following.
[quote]
@Bean method MyConfig.myBfpp is non-static and returns an object assignable to Spring's
BeanFactoryPostProcessor interface. This will result in a failure to process annotations
such as @Autowired, @Resource, and @PostConstruct within the method's declaring
@Configuration class. Add the 'static' modifier to this method to avoid these container
lifecycle issues; see @Bean javadoc for complete details.
NOTE: As with ``BeanPostProcessor``s , you typically do not want to configure
``BeanFactoryPostProcessor``s for lazy initialization. If no other bean references a
`Bean(Factory)PostProcessor`, that post-processor will not get instantiated at all.
@@ -339,11 +339,11 @@ In the preceding scenario, using `@Autowired` works well and provides the desire
modularity, but determining exactly where the autowired bean definitions are declared is
still somewhat ambiguous. For example, as a developer looking at `ServiceConfig`, how do
you know exactly where the `@Autowired AccountRepository` bean is declared? It is not
explicit in the code, and this may be just fine. Remember that the
{spring-site-tools}[Spring Tools for Eclipse] provides tooling that
can render graphs showing how everything is wired, which may be all you need. Also,
your Java IDE can easily find all declarations and uses of the `AccountRepository` type
and quickly show you the location of `@Bean` methods that return that type.
explicit in the code, and this may be just fine. Note that the
{spring-site-tools}[Spring Tools] IDE support provides tooling that can render graphs
showing how everything is wired, which may be all you need. Also, your Java IDE can
easily find all declarations and uses of the `AccountRepository` type and quickly show
you the location of `@Bean` methods that return that type.
In cases where this ambiguity is not acceptable and you wish to have direct navigation
from within your IDE from one `@Configuration` class to another, consider autowiring the
@@ -7,14 +7,14 @@ similar to the https://jakarta.ee/specifications/expression-language/[Jakarta Ex
Language] but offers additional features, most notably method invocation and basic string
templating functionality.
While there are several other Java expression languages available -- OGNL, MVEL, and JBoss
EL, to name a few -- the Spring Expression Language was created to provide the Spring
community with a single well supported expression language that can be used across all
the products in the Spring portfolio. Its language features are driven by the
requirements of the projects in the Spring portfolio, including tooling requirements
for code completion support within the {spring-site-tools}[Spring Tools for Eclipse].
That said, SpEL is based on a technology-agnostic API that lets other expression language
implementations be integrated, should the need arise.
While there are several other Java expression languages available -- OGNL, MVEL, and
JBoss EL, to name a few -- the Spring Expression Language was created to provide the
Spring community with a single well supported expression language that can be used across
all the products in the Spring portfolio. Its language features are driven by the
requirements of the projects in the Spring portfolio, including tooling requirements for
code completion within the {spring-site-tools}[Spring Tools] IDE support. That said, SpEL
is based on a technology-agnostic API that lets other expression language implementations
be integrated, should the need arise.
While SpEL serves as the foundation for expression evaluation within the Spring
portfolio, it is not directly tied to Spring and can be used independently. To
@@ -2,4 +2,12 @@
= Language Reference
:page-section-summary-toc: 1
This section describes how the Spring Expression Language works.
Spring Expression Language (SpEL) expressions are composed of a sequence of tokens such
as literals, operators, method invocations, and so forth.
Whitespace can be used freely between tokens to format and improve the readability of
expressions. Specifically, the `\s` (space), `\t` (tab), `\r` (carriage return), and `\n`
(newline) characters are all valid separators between tokens. However, whitespace is
ignored by the expression parser unless it is part of a string literal.
The following sections describe the features and syntax of SpEL.
@@ -53,7 +53,7 @@ Kotlin::
val trueValue = parser.parseExpression("'black' < 'block'").getValue(Boolean::class.java)
// uses CustomValue:::compareTo
val trueValue = parser.parseExpression("new CustomValue(1) < new CustomValue(2)").getValue(Boolean::class.java);
val trueValue = parser.parseExpression("new CustomValue(1) < new CustomValue(2)").getValue(Boolean::class.java)
----
======
@@ -167,7 +167,7 @@ Kotlin::
[CAUTION]
====
The syntax for the `between` operator is `<input> between {<range_begin>, <range_end>}`,
which is effectively a shortcut for `<input> >= <range_begin> && <input> \<= <range_end>}`.
which is effectively a shortcut for `<input> >= <range_begin> && <input> \<= <range_end>`.
Consequently, `1 between {1, 5}` evaluates to `true`, while `1 between {5, 1}` evaluates
to `false`.
@@ -312,13 +312,13 @@ Kotlin::
// evaluates to 'a'
val ch = parser.parseExpression("'d' - 3")
.getValue(Character::class.java);
.getValue(Char::class.java)
// -- Repeat --
// evaluates to "abcabc"
val repeated = parser.parseExpression("'abc' * 2")
.getValue(String::class.java);
.getValue(String::class.java)
----
======
@@ -485,7 +485,7 @@ Kotlin::
// -- Operator precedence --
val minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Int::class.java) // -21
val minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Int::class.java) // -21
----
======
@@ -541,32 +541,7 @@ For example, if we want to overload the `ADD` operator to allow two lists to be
concatenated using the `+` sign, we can implement a custom `OperatorOverloader` as
follows.
[source,java,indent=0,subs="verbatim,quotes"]
----
pubic class ListConcatenation implements OperatorOverloader {
@Override
public boolean overridesOperation(Operation operation, Object left, Object right) {
return (operation == Operation.ADD &&
left instanceof List && right instanceof List);
}
@Override
@SuppressWarnings("unchecked")
public Object operate(Operation operation, Object left, Object right) {
if (operation == Operation.ADD &&
left instanceof List list1 && right instanceof List list2) {
List result = new ArrayList(list1);
result.addAll(list2);
return result;
}
throw new UnsupportedOperationException(
"No overload for operation %s and operands [%s] and [%s]"
.formatted(operation, left, right));
}
}
----
include-code::./ListConcatenation[]
If we register `ListConcatenation` as the `OperatorOverloader` in a
`StandardEvaluationContext`, we can then evaluate expressions like `{1, 2, 3} + {4, 5}`
@@ -589,8 +564,8 @@ Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
StandardEvaluationContext context = StandardEvaluationContext()
context.setOperatorOverloader(ListConcatenation())
val context = StandardEvaluationContext()
context.operatorOverloader = ListConcatenation()
// evaluates to a new list: [1, 2, 3, 4, 5]
parser.parseExpression("{1, 2, 3} + {2 + 2, 5}").getValue(context, List::class.java)
@@ -270,7 +270,7 @@ is applicable for typical implementations of indexed structures.
NOTE: `ReflectiveIndexAccessor` also implements `CompilableIndexAccessor` in order to
support xref:core/expressions/evaluation.adoc#expressions-spel-compilation[compilation]
to bytecode for read access. Note, however, that the configured read-method must be
invokable via a `public` class or `public` interface for compilation to succeed.
invocable via a `public` class or `public` interface for compilation to succeed.
The following code listings define a `Color` enum and `FruitMap` type that behaves like a
map but does not implement the `java.util.Map` interface. Thus, if you want to index into
@@ -6,7 +6,7 @@ are set by using the `setVariable()` method in `EvaluationContext` implementatio
[NOTE]
====
Variable names must be begin with a letter (as defined below), an underscore, or a dollar
Variable names must begin with a letter (as defined below), an underscore, or a dollar
sign.
Variable names must be composed of one or more of the following supported types of
@@ -351,10 +351,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
@@ -366,10 +366,10 @@ following example uses the `BeanInfo` mechanism to explicitly register one or mo
[literal,subs="verbatim,quotes"]
----
com
chank
pop
Something
SomethingBeanInfo // the BeanInfo for the Something class
└── example
└── things
├── *Something*
└── *SomethingBeanInfo* // the BeanInfo for the Something class
----
The following Java source code for the referenced `SomethingBeanInfo` class
@@ -15,7 +15,7 @@ configuration options. You should instantiate the `SimpleJdbcInsert` in the data
layer's initialization method. For this example, the initializing method is the
`setDataSource` method. You do not need to subclass the `SimpleJdbcInsert` class. Instead,
you can create a new instance and set the table name by using the `withTableName` method.
Configuration methods for this class follow the `fluid` style that returns the instance
Configuration methods for this class follow the `fluent` style that returns the instance
of the `SimpleJdbcInsert`, which lets you chain all configuration methods. The following
example uses only one configuration method (we show examples of multiple methods later):
@@ -349,11 +349,11 @@ parameters return the data read from the table.
You can declare `SimpleJdbcCall` in a manner similar to declaring `SimpleJdbcInsert`. You
should instantiate and configure the class in the initialization method of your data-access
layer. Compared to the `StoredProcedure` class, you need not create a subclass
and you need not to declare parameters that can be looked up in the database metadata.
The following example of a `SimpleJdbcCall` configuration uses the preceding stored
procedure (the only configuration option, in addition to the `DataSource`, is the name
of the stored procedure):
layer. In contrast to the `StoredProcedure` class, you do not need to create a subclass,
and you do not need to declare parameters that can be looked up in the database metadata.
The following `SimpleJdbcCall` configuration example uses the preceding stored procedure.
The only configuration option (other than the `DataSource`) is the name of the stored
procedure.
[tabs]
======
@@ -1,16 +1,11 @@
[[appendix]]
= Appendix
[[appendix.xsd-schemas]]
== XML Schemas
This part of the appendix lists XML schemas related to integration technologies.
[[appendix.xsd-schemas-jee]]
=== The `jee` Schema
@@ -172,7 +167,7 @@ different properties with `jee`:
The `<jee:local-slsb/>` element configures a reference to a local EJB Stateless Session Bean.
The following example shows how to configures a reference to a local EJB Stateless Session Bean
The following example shows how to configure a reference to a local EJB Stateless Session Bean
without `jee`:
[source,xml,indent=0,subs="verbatim,quotes"]
@@ -184,7 +179,7 @@ without `jee`:
</bean>
----
The following example shows how to configures a reference to a local EJB Stateless Session Bean
The following example shows how to configure a reference to a local EJB Stateless Session Bean
with `jee`:
[source,xml,indent=0,subs="verbatim,quotes"]
@@ -200,7 +195,7 @@ with `jee`:
The `<jee:local-slsb/>` element configures a reference to a local EJB Stateless Session Bean.
The following example shows how to configures a reference to a local EJB Stateless Session Bean
The following example shows how to configure a reference to a local EJB Stateless Session Bean
and a number of properties without `jee`:
[source,xml,indent=0,subs="verbatim,quotes"]
@@ -215,7 +210,7 @@ and a number of properties without `jee`:
</bean>
----
The following example shows how to configures a reference to a local EJB Stateless Session Bean
The following example shows how to configure a reference to a local EJB Stateless Session Bean
and a number of properties with `jee`:
[source,xml,indent=0,subs="verbatim,quotes"]
@@ -234,7 +229,7 @@ and a number of properties with `jee`:
The `<jee:remote-slsb/>` element configures a reference to a `remote` EJB Stateless Session Bean.
The following example shows how to configures a reference to a remote EJB Stateless Session Bean
The following example shows how to configure a reference to a remote EJB Stateless Session Bean
without `jee`:
[source,xml,indent=0,subs="verbatim,quotes"]
@@ -251,7 +246,7 @@ without `jee`:
</bean>
----
The following example shows how to configures a reference to a remote EJB Stateless Session Bean
The following example shows how to configure a reference to a remote EJB Stateless Session Bean
with `jee`:
[source,xml,indent=0,subs="verbatim,quotes"]
@@ -313,7 +308,7 @@ xref:integration/jmx/naming.adoc#jmx-context-mbeanexport[Configuring Annotation-
=== The `cache` Schema
You can use the `cache` elements to enable support for Spring's `@CacheEvict`, `@CachePut`,
and `@Caching` annotations. It it also supports declarative XML-based caching. See
and `@Caching` annotations. The `cache` schema also supports declarative XML-based caching. See
xref:integration/cache/annotations.adoc#cache-annotation-enable[Enabling Caching Annotations] and
xref:integration/cache/declarative-xml.adoc[Declarative XML-based Caching] for details.
@@ -13,4 +13,4 @@ running the Kotlin compiler with its `-java-parameters` flag for standard Java p
You can declare configuration classes as
{kotlin-docs}/nested-classes.html[top level or nested but not inner],
since the later requires a reference to the outer class.
since the latter requires a reference to the outer class.
@@ -319,9 +319,17 @@ progresses.
== Testing
This section addresses testing with the combination of Kotlin and Spring Framework.
The recommended testing framework is https://junit.org/junit5/[JUnit 5] along with
The recommended testing framework is https://junit.org/[JUnit Jupiter] along with
https://mockk.io/[Mockk] for mocking.
[TIP]
====
Kotlin lets you specify meaningful test function names between backticks (```).
For a concrete example, see the `+++`Find all users on HTML page`()+++` test function later
in this section.
====
NOTE: If you are using Spring Boot, see
{spring-boot-docs-ref}/features/kotlin.html#features.kotlin.testing[this related documentation].
@@ -352,7 +360,6 @@ file with a `spring.test.constructor.autowire.mode = all` property.
[[per_class-lifecycle]]
=== `PER_CLASS` Lifecycle
Kotlin lets you specify meaningful test function names between backticks (```).
With JUnit Jupiter (JUnit 5), Kotlin test classes can use the `@TestInstance(TestInstance.Lifecycle.PER_CLASS)`
annotation to enable single instantiation of test classes, which allows the use of `@BeforeAll`
and `@AfterAll` annotations on non-static methods, which is a good fit for Kotlin.
@@ -187,7 +187,7 @@ default mode may be set via the
xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism.
The default mode may also be configured as a
https://junit.org/junit5/docs/current/user-guide/#running-tests-config-params[JUnit Platform configuration parameter].
https://docs.junit.org/current/running-tests/configuration-parameters.html[JUnit Platform configuration parameter].
If the `spring.test.constructor.autowire.mode` property is not set, test class
constructors will not be automatically autowired.
@@ -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
@@ -130,6 +130,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
@@ -158,6 +173,22 @@ Java::
}
----
<1> Replace the bean named `service` with a Mockito mock.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@SpringJUnitConfig(TestConfig::class)
class BeanOverrideTests {
@MockitoBean("service") // <1>
lateinit var customService: CustomService
// tests...
}
----
<1> Replace the bean named `service` with a Mockito mock.
======
The following `@SharedMocks` annotation registers two mocks by-type and one mock by-name.
@@ -177,6 +208,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.
@@ -207,6 +251,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
@@ -236,6 +308,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
@@ -261,6 +348,21 @@ Java::
}
----
<1> Wrap the bean named `service` with a Mockito spy.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@SpringJUnitConfig(TestConfig::class)
class BeanOverrideTests {
@MockitoSpyBean("service") // <1>
lateinit var customService: CustomService
// tests...
}
----
<1> Wrap the bean named `service` with a Mockito spy.
======
The following `@SharedSpies` annotation registers two spies by-type and one spy by-name.
@@ -280,6 +382,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.
@@ -310,6 +425,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]
@@ -5,24 +5,25 @@ It is important to be able to perform some integration testing without requiring
deployment to your application server or connecting to other enterprise infrastructure.
Doing so lets you test things such as:
* The correct wiring of your Spring IoC container contexts.
* Data access using JDBC or an ORM tool. This can include such things as the correctness
of SQL statements, Hibernate queries, JPA entity mappings, and so forth.
* The correct wiring of your Spring components.
* Data access using JDBC or an ORM tool.
** This can include such things as the correctness of SQL statements, Hibernate queries,
JPA entity mappings, and so forth.
The Spring Framework provides first-class support for integration testing in the
`spring-test` module. The name of the actual JAR file might include the release version
and might also be in the long `org.springframework.test` form, depending on where you get
it from (see the xref:core/beans/dependencies.adoc[section on Dependency Management]
for an explanation). This library includes the `org.springframework.test` package, which
`spring-test` module. The name of the actual JAR file might include the release version,
depending on where you get it from (see the
{spring-framework-wiki}/Spring-Framework-Artifacts[Spring Framework Artifacts] wiki page
for details). This library includes the `org.springframework.test` package, which
contains valuable classes for integration testing with a Spring container. This testing
does not rely on an application server or other deployment environment. Such tests are
slower to run than unit tests but much faster than the equivalent Selenium tests or
remote tests that rely on deployment to an application server.
Unit and integration testing support is provided in the form of the annotation-driven
xref:testing/testcontext-framework.adoc[Spring TestContext Framework]. The TestContext framework is
agnostic of the actual testing framework in use, which allows instrumentation of tests
in various environments, including JUnit, TestNG, and others.
xref:testing/testcontext-framework.adoc[Spring TestContext Framework]. The TestContext
framework is agnostic of the actual testing framework in use, which allows
instrumentation of tests in various environments, including JUnit, TestNG, and others.
The following section provides an overview of the high-level goals of Spring's
integration support, and the rest of this chapter then focuses on dedicated topics:
@@ -8,9 +8,9 @@ use https://www.gebish.org/[Geb] to make our tests even Groovy-er.
== Why Geb and MockMvc?
Geb is backed by WebDriver, so it offers many of the
xref:testing/mockmvc/htmlunit/webdriver.adoc#spring-mvc-test-server-htmlunit-webdriver-why[same benefits] that we get from
WebDriver. However, Geb makes things even easier by taking care of some of the
boilerplate code for us.
xref:testing/mockmvc/htmlunit/webdriver.adoc#mockmvc-server-htmlunit-webdriver-why[same benefits]
that we get from WebDriver. However, Geb makes things even easier by taking care of some
of the boilerplate code for us.
[[mockmvc-server-htmlunit-geb-setup]]
== MockMvc and Geb Setup
@@ -28,7 +28,8 @@ def setup() {
----
NOTE: This is a simple example of using `MockMvcHtmlUnitDriverBuilder`. For more advanced
usage, see xref:testing/mockmvc/htmlunit/webdriver.adoc#spring-mvc-test-server-htmlunit-webdriver-advanced-builder[Advanced `MockMvcHtmlUnitDriverBuilder`].
usage, see
xref:testing/mockmvc/htmlunit/webdriver.adoc#mockmvc-server-htmlunit-webdriver-advanced-builder[Advanced `MockMvcHtmlUnitDriverBuilder`].
This ensures that any URL referencing `localhost` as the server is directed to our
`MockMvc` instance without the need for a real HTTP connection. Any other URL is
@@ -62,10 +63,10 @@ forwarded to the current page object. This removes a lot of the boilerplate code
needed when using WebDriver directly.
As with direct WebDriver usage, this improves on the design of our
xref:testing/mockmvc/htmlunit/mah.adoc#spring-mvc-test-server-htmlunit-mah-usage[HtmlUnit test] by using the Page Object
Pattern. As mentioned previously, we can use the Page Object Pattern with HtmlUnit and
WebDriver, but it is even easier with Geb. Consider our new Groovy-based
`CreateMessagePage` implementation:
xref:testing/mockmvc/htmlunit/mah.adoc#mockmvc-server-htmlunit-mah-usage[HtmlUnit test]
by using the Page Object Pattern. As mentioned previously, we can use the Page Object
Pattern with HtmlUnit and WebDriver, but it is even easier with Geb. Consider our new
Groovy-based `CreateMessagePage` implementation:
[source,groovy]
----
@@ -7,8 +7,7 @@ to use the raw HtmlUnit libraries.
[[mockmvc-server-htmlunit-mah-setup]]
== MockMvc and HtmlUnit Setup
First, make sure that you have included a test dependency on
`org.htmlunit:htmlunit`.
First, make sure that you have included a test dependency on `org.htmlunit:htmlunit`.
We can easily create an HtmlUnit `WebClient` that integrates with MockMvc by using the
`MockMvcWebClientBuilder`, as follows:
@@ -45,7 +44,7 @@ Kotlin::
======
NOTE: This is a simple example of using `MockMvcWebClientBuilder`. For advanced usage,
see xref:testing/mockmvc/htmlunit/mah.adoc#spring-mvc-test-server-htmlunit-mah-advanced-builder[Advanced `MockMvcWebClientBuilder`].
see <<mockmvc-server-htmlunit-mah-advanced-builder>>.
This ensures that any URL that references `localhost` as the server is directed to our
`MockMvc` instance without the need for a real HTTP connection. Any other URL is
@@ -77,7 +76,7 @@ Kotlin::
======
NOTE: The default context path is `""`. Alternatively, we can specify the context path,
as described in xref:testing/mockmvc/htmlunit/mah.adoc#spring-mvc-test-server-htmlunit-mah-advanced-builder[Advanced `MockMvcWebClientBuilder`].
as described in <<mockmvc-server-htmlunit-mah-advanced-builder>>.
Once we have a reference to the `HtmlPage`, we can then fill out the form and submit it
to create a message, as the following example shows:
@@ -144,10 +143,10 @@ Kotlin::
======
The preceding code improves on our
xref:testing/mockmvc/htmlunit/why.adoc#spring-mvc-test-server-htmlunit-mock-mvc-test[MockMvc test] in a number of ways.
First, we no longer have to explicitly verify our form and then create a request that
looks like the form. Instead, we request the form, fill it out, and submit it, thereby
significantly reducing the overhead.
xref:testing/mockmvc/htmlunit/why.adoc#mockmvc-server-htmlunit-why[MockMvc test] in a
number of ways. First, we no longer have to explicitly verify our form and then create a
request that looks like the form. Instead, we request the form, fill it out, and submit
it, thereby significantly reducing the overhead.
Another important factor is that https://htmlunit.sourceforge.io/javascript.html[HtmlUnit
uses the Mozilla Rhino engine] to evaluate JavaScript. This means that we can also test
@@ -203,7 +203,7 @@ Kotlin::
======
NOTE: This is a simple example of using `MockMvcHtmlUnitDriverBuilder`. For more advanced
usage, see xref:testing/mockmvc/htmlunit/webdriver.adoc#spring-mvc-test-server-htmlunit-webdriver-advanced-builder[Advanced `MockMvcHtmlUnitDriverBuilder`].
usage, see <<mockmvc-server-htmlunit-webdriver-advanced-builder>>.
The preceding example ensures that any URL that references `localhost` as the server is
directed to our `MockMvc` instance without the need for a real HTTP connection. Any other
@@ -259,10 +259,11 @@ Kotlin::
======
--
This improves on the design of our xref:testing/mockmvc/htmlunit/mah.adoc#spring-mvc-test-server-htmlunit-mah-usage[HtmlUnit test]
This improves on the design of our
xref:testing/mockmvc/htmlunit/mah.adoc#mockmvc-server-htmlunit-mah-usage[HtmlUnit test]
by leveraging the Page Object Pattern. As we mentioned in
xref:testing/mockmvc/htmlunit/webdriver.adoc#mockmvc-server-htmlunit-webdriver-why[Why WebDriver and MockMvc?], we can use the Page Object Pattern
with HtmlUnit, but it is much easier with WebDriver. Consider the following
<<mockmvc-server-htmlunit-webdriver-why>>, we can use the Page Object Pattern with
HtmlUnit, but it is much easier with WebDriver. Consider the following
`CreateMessagePage` implementation:
--
@@ -60,7 +60,7 @@ assume our form looks like the following snippet:
</form>
----
How do we ensure that our form produce the correct request to create a new message? A
How do we ensure that our form produces the correct request to create a new message? A
naive attempt might resemble the following:
[tabs]
@@ -154,7 +154,7 @@ validation.
[[mockmvc-server-htmlunit-why-integration]]
== Integration Testing to the Rescue?
To resolve the issues mentioned earlier, we could perform end-to-end integration testing,
To resolve the issues mentioned above, we could perform end-to-end integration testing,
but this has some drawbacks. Consider testing the view that lets us page through the
messages. We might need the following tests:
@@ -171,7 +171,7 @@ leads to a number of additional challenges:
* Testing can become slow, since each test would need to ensure that the database is in
the correct state.
* Since our database needs to be in a specific state, we cannot run tests in parallel.
* Performing assertions on such items as auto-generated IDs, timestamps, and others can
* Performing assertions on items such as auto-generated IDs, timestamps, and others can
be difficult.
These challenges do not mean that we should abandon end-to-end integration testing
@@ -19,6 +19,6 @@ meta-annotation. If a bootstrapper is not explicitly configured by using
`WebTestContextBootstrapper` is used, depending on the presence of `@WebAppConfiguration`.
Since the `TestContextBootstrapper` SPI is likely to change in the future (to accommodate
new requirements), we strongly encourage implementers not to implement this interface
new requirements), we strongly encourage implementors not to implement this interface
directly but rather to extend `AbstractTestContextBootstrapper` or one of its concrete
subclasses instead.
@@ -49,7 +49,6 @@ Kotlin::
<1> Injecting the `ApplicationContext`.
======
Similarly, if your test is configured to load a `WebApplicationContext`, you can inject
the web application context into your test, as follows:
@@ -87,7 +86,6 @@ Kotlin::
<2> Injecting the `WebApplicationContext`.
======
Dependency injection by using `@Autowired` is provided by the
`DependencyInjectionTestExecutionListener`, which is configured by default
(see xref:testing/testcontext-framework/fixture-di.adoc[Dependency Injection of Test Fixtures]).
@@ -96,22 +94,23 @@ Dependency injection by using `@Autowired` is provided by the
Test classes that use the TestContext framework do not need to extend any particular
class or implement a specific interface to configure their application context. Instead,
configuration is achieved by declaring the `@ContextConfiguration` annotation at the
class level. If your test class does not explicitly declare application context resource
locations or component classes, the configured `ContextLoader` determines how to load a
context from a default location or default configuration classes. In addition to context
resource locations and component classes, an application context can also be configured
through application context initializers.
class level. If your test class does not explicitly declare component classes or resource
locations, the configured `ContextLoader` determines how to load a context from _default_
configuration classes or a _default_ location. In addition to component classes and
context resource locations, an application context can also be configured through
xref:testing/testcontext-framework/ctx-management/context-customizers.adoc[context customizers]
or xref:testing/testcontext-framework/ctx-management/initializers.adoc[context initializers].
The following sections explain how to use Spring's `@ContextConfiguration` annotation to
configure a test `ApplicationContext` by using XML configuration files, Groovy scripts,
component classes (typically `@Configuration` classes), or context initializers.
Alternatively, you can implement and configure your own custom `SmartContextLoader` for
advanced use cases.
The following sections explain how to use `@ContextConfiguration` and related annotations
to configure a test `ApplicationContext` by using component classes (typically
`@Configuration` classes), XML configuration files, Groovy scripts, context customizers,
or context initializers. Alternatively, you can implement and configure your own custom
`SmartContextLoader` for advanced use cases.
* xref:testing/testcontext-framework/ctx-management/javaconfig.adoc[Context Configuration with Component Classes]
* xref:testing/testcontext-framework/ctx-management/xml.adoc[Context Configuration with XML resources]
* xref:testing/testcontext-framework/ctx-management/groovy.adoc[Context Configuration with Groovy Scripts]
* xref:testing/testcontext-framework/ctx-management/javaconfig.adoc[Context Configuration with Component Classes]
* xref:testing/testcontext-framework/ctx-management/mixed-config.adoc[Mixing XML, Groovy Scripts, and Component Classes]
* xref:testing/testcontext-framework/ctx-management/mixed-config.adoc[Mixing Component Classes, XML, and Groovy Scripts]
* xref:testing/testcontext-framework/ctx-management/context-customizers.adoc[Context Configuration with Context Customizers]
* xref:testing/testcontext-framework/ctx-management/initializers.adoc[Context Configuration with Context Initializers]
* xref:testing/testcontext-framework/ctx-management/inheritance.adoc[Context Configuration Inheritance]
@@ -543,3 +543,85 @@ Kotlin::
----
======
The following example demonstrates how to implement and register a custom
`SystemPropertyOverrideActiveProfilesResolver` that allows the `spring.profiles.active`
property (when configured as a JVM system property) to override profiles configured via
`@ActiveProfiles`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
// profiles resolved programmatically via a custom resolver that
// allows "spring.profiles.active" to override @ActiveProfiles
@ActiveProfiles(
resolver = SystemPropertyOverrideActiveProfilesResolver.class,
inheritProfiles = false)
class TransferServiceTest extends AbstractIntegrationTest {
// test body
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
// profiles resolved programmatically via a custom resolver that
// allows "spring.profiles.active" to override @ActiveProfiles
@ActiveProfiles(
resolver = SystemPropertyOverrideActiveProfilesResolver::class,
inheritProfiles = false)
class TransferServiceTest : AbstractIntegrationTest() {
// test body
}
----
======
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",fold="-imports"]
----
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.test.context.support.DefaultActiveProfilesResolver;
import org.springframework.util.StringUtils;
public class SystemPropertyOverrideActiveProfilesResolver extends DefaultActiveProfilesResolver {
@Override
public String[] resolve(Class<?> testClass) {
String profiles = System.getProperty(AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME);
if (StringUtils.hasText(profiles)) {
return StringUtils.commaDelimitedListToStringArray(
StringUtils.trimAllWhitespace(profiles));
}
return super.resolve(testClass);
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",fold="-imports"]
----
import org.springframework.core.env.AbstractEnvironment
import org.springframework.test.context.support.DefaultActiveProfilesResolver
import org.springframework.util.StringUtils
class SystemPropertyOverrideActiveProfilesResolver : DefaultActiveProfilesResolver() {
override fun resolve(testClass: Class<*>): Array<String> {
val profiles = System.getProperty(AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME)
if (StringUtils.hasText(profiles)) {
return StringUtils.commaDelimitedListToStringArray(
StringUtils.trimAllWhitespace(profiles)
)
}
return super.resolve(testClass)
}
}
----
======
@@ -1,33 +1,32 @@
[[testcontext-ctx-management-mixed-config]]
= Mixing XML, Groovy Scripts, and Component Classes
= Mixing Component Classes, XML, and Groovy Scripts
It may sometimes be desirable to mix XML configuration files, Groovy scripts, and
component classes (typically `@Configuration` classes) to configure an
`ApplicationContext` for your tests. For example, if you use XML configuration in
production, you may decide that you want to use `@Configuration` classes to configure
It may sometimes be desirable to mix component classes (typically `@Configuration`
classes), XML configuration files, or Groovy scripts to configure an `ApplicationContext`
for your tests. For example, if you use XML configuration in production for legacy
reasons, you may decide that you want to use `@Configuration` classes to configure
specific Spring-managed components for your tests, or vice versa.
Furthermore, some third-party frameworks (such as Spring Boot) provide first-class
support for loading an `ApplicationContext` from different types of resources
simultaneously (for example, XML configuration files, Groovy scripts, and
`@Configuration` classes). The Spring Framework, historically, has not supported this for
standard deployments. Consequently, most of the `SmartContextLoader` implementations that
the Spring Framework delivers in the `spring-test` module support only one resource type
for each test context. However, this does not mean that you cannot use both. One
exception to the general rule is that the `GenericGroovyXmlContextLoader` and
simultaneously (for example, `@Configuration` classes, XML configuration files, and
Groovy scripts). The Spring Framework, historically, has not supported this for standard
deployments. Consequently, most of the `SmartContextLoader` implementations that the
Spring Framework delivers in the `spring-test` module support only one resource type for
each test context. However, this does not mean that you cannot use a mixture of resource
types. One exception to the general rule is that the `GenericGroovyXmlContextLoader` and
`GenericGroovyXmlWebContextLoader` support both XML configuration files and Groovy
scripts simultaneously. Furthermore, third-party frameworks may choose to support the
declaration of both `locations` and `classes` through `@ContextConfiguration`, and, with
declaration of both `classes` and `locations` through `@ContextConfiguration`, and, with
the standard testing support in the TestContext framework, you have the following options.
If you want to use resource locations (for example, XML or Groovy) and `@Configuration`
classes to configure your tests, you must pick one as the entry point, and that one must
include or import the other. For example, in XML or Groovy scripts, you can include
`@Configuration` classes by using component scanning or defining them as normal Spring
beans, whereas, in a `@Configuration` class, you can use `@ImportResource` to import XML
configuration files or Groovy scripts. Note that this behavior is semantically equivalent
If you want to use `@Configuration` classes and resource locations (for example, XML or
Groovy) to configure your tests, you must pick one as the entry point, and that one must
import or include the other. For example, in a `@Configuration` class, you can use
`@ImportResource` to import XML configuration files or Groovy scripts; whereas, in XML or
Groovy scripts, you can include `@Configuration` classes by using component scanning or
defining them as normal Spring beans. Note that this behavior is semantically equivalent
to how you configure your application in production: In production configuration, you
define either a set of XML or Groovy resource locations or a set of `@Configuration`
classes from which your production `ApplicationContext` is loaded, but you still have the
freedom to include or import the other type of configuration.
define either a set of `@Configuration` classes or a set of XML or Groovy resource
locations from which your production `ApplicationContext` is loaded, but you still have
the freedom to import or include the other type of configuration.
@@ -22,7 +22,7 @@ TestNG:
* Dependency injection for test constructors, test methods, and test lifecycle callback
methods. See xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit-jupiter-di[Dependency
Injection with the `SpringExtension`] for further details.
* Powerful support for link:https://junit.org/junit5/docs/current/user-guide/#extensions-conditions[conditional
* Powerful support for link:https://docs.junit.org/current/extensions/conditional-test-execution.html[conditional
test execution] based on SpEL expressions, environment variables, system properties,
and so on. See the documentation for `@EnabledIf` and `@DisabledIf` in
xref:testing/annotations/integration-junit-jupiter.adoc[Spring JUnit Jupiter Testing Annotations]
@@ -160,7 +160,7 @@ for further details.
=== Dependency Injection with the `SpringExtension`
The `SpringExtension` implements the
link:https://junit.org/junit5/docs/current/user-guide/#extensions-parameter-resolution[`ParameterResolver`]
link:https://docs.junit.org/current/extensions/parameter-resolution.html[`ParameterResolver`]
extension API from JUnit Jupiter, which lets Spring provide dependency injection for test
constructors, test methods, and test lifecycle callback methods.
@@ -362,7 +362,7 @@ of `PlatformTransactionManager` within the test's `ApplicationContext`, you can
qualifier by using `@Transactional("myTxMgr")` or `@Transactional(transactionManager =
"myTxMgr")`, or `TransactionManagementConfigurer` can be implemented by an
`@Configuration` class. Consult the
{spring-framework-api}/test/context/transaction/TestContextTransactionUtils.html#retrieveTransactionManager-org.springframework.test.context.TestContext-java.lang.String-[javadoc
{spring-framework-api}/test/context/transaction/TestContextTransactionUtils.html#retrieveTransactionManager(org.springframework.test.context.TestContext,java.lang.String)[javadoc
for `TestContextTransactionUtils.retrieveTransactionManager()`] for details on the
algorithm used to look up a transaction manager in the test's `ApplicationContext`.
@@ -75,7 +75,7 @@ infrastructure and controller declarations and use it to handle requests via moc
and response objects, without a running server.
For WebFlux, use the following where the Spring `ApplicationContext` is passed to
{spring-framework-api}/web/server/adapter/WebHttpHandlerBuilder.html#applicationContext-org.springframework.context.ApplicationContext-[WebHttpHandlerBuilder]
{spring-framework-api}/web/server/adapter/WebHttpHandlerBuilder.html#applicationContext(org.springframework.context.ApplicationContext)[WebHttpHandlerBuilder]
to create the xref:web/webflux/reactive-spring.adoc#webflux-web-handler-api[WebHandler chain] to handle
requests:
@@ -44,7 +44,7 @@ rejected. No CORS headers are added to the responses of simple and actual CORS r
and, consequently, browsers reject them.
Each `HandlerMapping` can be
{spring-framework-api}/web/reactive/handler/AbstractHandlerMapping.html#setCorsConfigurations-java.util.Map-[configured]
{spring-framework-api}/web/reactive/handler/AbstractHandlerMapping.html#setCorsConfigurations(java.util.Map)[configured]
individually with URL pattern-based `CorsConfiguration` mappings. In most cases, applications
use the WebFlux Java configuration to declare such mappings, which results in a single,
global map passed to all `HandlerMapping` implementations.
@@ -57,7 +57,7 @@ class- or method-level `@CrossOrigin` annotations (other handlers can implement
The rules for combining global and local configuration are generally additive -- for example,
all global and all local origins. For those attributes where only a single value can be
accepted, such as `allowCredentials` and `maxAge`, the local overrides the global value. See
{spring-framework-api}/web/cors/CorsConfiguration.html#combine-org.springframework.web.cors.CorsConfiguration-[`CorsConfiguration#combine(CorsConfiguration)`]
{spring-framework-api}/web/cors/CorsConfiguration.html#combine(org.springframework.web.cors.CorsConfiguration)[`CorsConfiguration#combine(CorsConfiguration)`]
for more details.
[TIP]
@@ -32,9 +32,9 @@ any `@RequestMapping` method to render an RFC 9457 response. This is processed a
- The `status` property of `ProblemDetail` determines the HTTP status.
- The `instance` property of `ProblemDetail` is set from the current URL path, if not
already set.
- For content negotiation, the Jackson `HttpMessageConverter` prefers
"application/problem+json" over "application/json" when rendering a `ProblemDetail`,
and also falls back on it if no compatible media type is found.
- The Jackson JSON and XML message converters use "application/problem+json" or
"application/problem+xml" respectively as the producible media types for `ProblemDetail`
to ensure they are favored for content negotiation.
To enable RFC 9457 responses for Spring WebFlux exceptions and for any
`ErrorResponseException`, extend `ResponseEntityExceptionHandler` and declare it as an
@@ -251,7 +251,8 @@ Kotlin::
======
TIP: If you need to have a `LocalValidatorFactoryBean` injected somewhere, create a bean and
mark it with `@Primary` in order to avoid conflict with the one declared in the MVC config.
mark it with `@Primary`, or mark the one declared in the MVC config with `@Fallback`, in
order to avoid conflict.
[[webflux-config-content-negotiation]]
@@ -114,6 +114,6 @@ and others) and is equivalent to `required=false`.
| Any other argument
| If a method argument is not matched to any of the above, it is, by default, resolved as
a `@RequestParam` if it is a simple type, as determined by
{spring-framework-api}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty],
{spring-framework-api}/beans/BeanUtils.html#isSimpleProperty(java.lang.Class)[BeanUtils#isSimpleProperty],
or as a `@ModelAttribute`, otherwise.
|===
@@ -205,7 +205,7 @@ controller method xref:web/webmvc/mvc-controller/ann-validation.adoc[Validation]
TIP: Using `@ModelAttribute` is optional. By default, any argument that is not a simple
value type as determined by
{spring-framework-api}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty]
{spring-framework-api}/beans/BeanUtils.html#isSimpleProperty(java.lang.Class)[BeanUtils#isSimpleProperty]
_AND_ that is not resolved by any other argument resolver is treated as an implicit `@ModelAttribute`.
WARNING: When compiling to a native image with GraalVM, the implicit `@ModelAttribute`
@@ -74,6 +74,6 @@ When a `@RequestParam` annotation is declared on a `Map<String, String>` or
Note that use of `@RequestParam` is optional -- for example, to set its attributes. By
default, any argument that is a simple value type (as determined by
{spring-framework-api}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty])
{spring-framework-api}/beans/BeanUtils.html#isSimpleProperty(java.lang.Class)[BeanUtils#isSimpleProperty])
and is not resolved by any other argument resolver is treated as if it were annotated
with `@RequestParam`.
@@ -87,6 +87,6 @@ Reactor provides a dedicated operator for that, `Flux#collectList()`.
| Other return values
| If a return value remains unresolved in any other way, it is treated as a model
attribute, unless it is a simple type as determined by
{spring-framework-api}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty],
{spring-framework-api}/beans/BeanUtils.html#isSimpleProperty(java.lang.Class)[BeanUtils#isSimpleProperty],
in which case it remains unresolved.
|===
@@ -207,7 +207,7 @@ was not provided (for example, model attribute was returned) or an async return
view resolution scenarios. Explore the options in your IDE with code completion.
* `Model`, `Map`: Extra model attributes to be added to the model for the request.
* Any other: Any other return value (except for simple types, as determined by
{spring-framework-api}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty])
{spring-framework-api}/beans/BeanUtils.html#isSimpleProperty(java.lang.Class)[BeanUtils#isSimpleProperty])
is treated as a model attribute to be added to the model. The attribute name is derived
from the class name by using {spring-framework-api}/core/Conventions.html[conventions],
unless a handler method `@ModelAttribute` annotation is present.
@@ -418,16 +418,23 @@ See the section on xref:web/webflux-cors.adoc[CORS] and the xref:web/webflux-cor
You may want your controller endpoints to match routes with or without a trailing slash in the URL path.
For example, both "GET /home" and "GET /home/" should be handled by a controller method annotated with `@GetMapping("/home")`.
Adding trailing slash variants to all mapping declarations is not the best way to handle this use case.
The `UrlHandlerFilter` web filter has been designed for this purpose. It can be configured to:
Spring provides `UrlHandlerFilter` that removes the trailing slash from URL paths to ensure a consistent view of paths with or without a trailing slash.
This is important to avoid a mismatch between URL-based authorization decisions and web framework request mappings.
The filter can remove the trailing slash in one of a couple of ways:
* respond with an HTTP redirect status when receiving URLs with trailing slashes, sending browsers to the non-trailing slash URL variant.
* mutate the request to act as if the request was sent without a trailing slash and continue the processing of the request.
* respond with an HTTP redirect status that sends clients to the same path without a trailing slash.
* mutate the request to remove the trailing slash.
Here is how you can instantiate and configure a `UrlHandlerFilter` for a blog application:
include-code::./UrlHandlerFilterConfiguration[tag=config,indent=0]
Keep in mind the following:
- the root path `"/"` is excluded from trailing slash handling.
- `@RequestMapping("/")` adds a trailing slash to a type-level mapping, and therefore will
not map when trailing slash handling applies; use `@RequestMapping` (no path attribute) instead.
[[webflux-exception-handler]]
== Exceptions
@@ -71,7 +71,7 @@ rejected. No CORS headers are added to the responses of simple and actual CORS r
and, consequently, browsers reject them.
Each `HandlerMapping` can be
{spring-framework-api}/web/servlet/handler/AbstractHandlerMapping.html#setCorsConfigurations-java.util.Map-[configured]
{spring-framework-api}/web/servlet/handler/AbstractHandlerMapping.html#setCorsConfigurations(java.util.Map)[configured]
individually with URL pattern-based `CorsConfiguration` mappings. In most cases, applications
use the MVC Java configuration or the XML namespace to declare such mappings, which results
in a single global map being passed to all `HandlerMapping` instances.
@@ -84,7 +84,7 @@ class- or method-level `@CrossOrigin` annotations (other handlers can implement
The rules for combining global and local configuration are generally additive -- for example,
all global and all local origins. For those attributes where only a single value can be
accepted, for example, `allowCredentials` and `maxAge`, the local overrides the global value. See
{spring-framework-api}/web/cors/CorsConfiguration.html#combine-org.springframework.web.cors.CorsConfiguration-[`CorsConfiguration#combine(CorsConfiguration)`]
{spring-framework-api}/web/cors/CorsConfiguration.html#combine(org.springframework.web.cors.CorsConfiguration)[`CorsConfiguration#combine(CorsConfiguration)`]
for more details.
[TIP]
@@ -269,7 +269,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
@@ -120,17 +120,26 @@ See the sections on xref:web/webmvc-cors.adoc[CORS] and the xref:web/webmvc-cors
== URL Handler
[.small]#xref:web/webflux/reactive-spring.adoc#filters.url-handler[See equivalent in the Reactive stack]#
In previous Spring Framework versions, Spring MVC could be configured to ignore trailing slashes in URL paths
when mapping incoming requests on controller methods. This could be done by enabling the `setUseTrailingSlashMatch`
option on the `PathMatchConfigurer`. This means that sending a "GET /home/" request would be handled by a controller
method annotated with `@GetMapping("/home")`.
You may want your controller endpoints to match routes with or without a trailing slash in the URL path.
For example, both "GET /home" and "GET /home/" should be handled by a controller method annotated with `@GetMapping("/home")`.
This option has been retired, but applications are still expected to handle such requests in a safe way.
The `UrlHandlerFilter` Servlet filter has been designed for this purpose. It can be configured to:
Spring provides `UrlHandlerFilter` that removes the trailing slash from URL paths to ensure a consistent view of paths with or without a trailing slash.
This is important to avoid a mismatch between URL-based authorization decisions and web framework request mappings.
The filter can remove the trailing slash in one of a couple of ways:
* respond with an HTTP redirect status when receiving URLs with trailing slashes, sending browsers to the non-trailing slash URL variant.
* wrap the request to act as if the request was sent without a trailing slash and continue the processing of the request.
* respond with an HTTP redirect status that sends clients to the same path without a trailing slash.
* wrap the request to remove the trailing slash.
NOTE: Historically Spring MVC supported trailing slash matching of URL paths.
This capability was deprecated in 6.0 for security reasons and removed in 7.0 with
`UrlHandlerFilter` providing a safer alternative.
Here is how you can instantiate and configure a `UrlHandlerFilter` for a blog application:
include-code::./UrlHandlerFilterConfiguration[tag=config,indent=0]
Keep in mind the following:
- the root path `"/"` is excluded from trailing slash handling.
- `@RequestMapping("/")` adds a trailing slash to a type-level mapping, and therefore will
not map when trailing slash handling applies; use `@RequestMapping` (no path attribute) instead.
@@ -32,9 +32,9 @@ any `@RequestMapping` method to render an RFC 9457 response. This is processed a
- The `status` property of `ProblemDetail` determines the HTTP status.
- The `instance` property of `ProblemDetail` is set from the current URL path, if not
already set.
- For content negotiation, the Jackson `HttpMessageConverter` prefers
"application/problem+json" over "application/json" when rendering a `ProblemDetail`,
and also falls back on it if no compatible media type is found.
- The Jackson JSON and XML codecs use "application/problem+json" or
"application/problem+xml" respectively as the producible media types for `ProblemDetail`
to ensure they are favored for content negotiation.
To enable RFC 9457 responses for Spring MVC exceptions and for any
`ErrorResponseException`, extend `ResponseEntityExceptionHandler` and declare it as an
@@ -175,7 +175,7 @@ Message codes and arguments for each error are also resolved via `MessageSource`
| `TypeMismatchException`
| (default)
| `+{0}+` property name, `+{1}+` property value
| `+{0}+` property name, `+{1}+` property value, `+{2}+` simple name of required type
| `UnsatisfiedServletRequestParameterException`
| (default)
@@ -19,4 +19,5 @@ example shows:
include-code::./MyController[tag=snippet,indent=0]
TIP: If you need to have a `LocalValidatorFactoryBean` injected somewhere, create a bean and
mark it with `@Primary` in order to avoid conflict with the one declared in the MVC configuration.
mark it with `@Primary`, or mark the one declared in the MVC configuration with
`@Fallback`, in order to avoid conflict.
@@ -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
@@ -215,7 +215,7 @@ the content negotiation during the error handling phase will decide which conten
| Any other return value
| If a return value is not matched to any of the above and is not a simple type (as determined by
{spring-framework-api}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty]),
{spring-framework-api}/beans/BeanUtils.html#isSimpleProperty(java.lang.Class)[BeanUtils#isSimpleProperty]),
by default, it is treated as a model attribute to be added to the model. If it is a simple type,
it remains unresolved.
|===
@@ -135,6 +135,6 @@ and others) and is equivalent to `required=false`.
| Any other argument
| If a method argument is not matched to any of the earlier values in this table and it is
a simple type (as determined by
{spring-framework-api}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty]),
{spring-framework-api}/beans/BeanUtils.html#isSimpleProperty(java.lang.Class)[BeanUtils#isSimpleProperty]),
it is resolved as a `@RequestParam`. Otherwise, it is resolved as a `@ModelAttribute`.
|===
@@ -250,7 +250,7 @@ xref:web/webmvc/mvc-controller/ann-validation.adoc[Validation].
TIP: Using `@ModelAttribute` is optional. By default, any parameter that is not a simple
value type as determined by
{spring-framework-api}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty]
{spring-framework-api}/beans/BeanUtils.html#isSimpleProperty(java.lang.Class)[BeanUtils#isSimpleProperty]
_AND_ that is not resolved by any other argument resolver is treated as an implicit `@ModelAttribute`.
WARNING: When compiling to a native image with GraalVM, the implicit `@ModelAttribute`
@@ -117,6 +117,6 @@ Kotlin::
Note that use of `@RequestParam` is optional (for example, to set its attributes).
By default, any argument that is a simple value type (as determined by
{spring-framework-api}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty])
{spring-framework-api}/beans/BeanUtils.html#isSimpleProperty(java.lang.Class)[BeanUtils#isSimpleProperty])
and is not resolved by any other argument resolver, is treated as if it were annotated
with `@RequestParam`.
@@ -98,6 +98,6 @@ supported for all return values.
| Other return values
| If a return value remains unresolved in any other way, it is treated as a model
attribute, unless it is a simple type as determined by
{spring-framework-api}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty],
{spring-framework-api}/beans/BeanUtils.html#isSimpleProperty(java.lang.Class)[BeanUtils#isSimpleProperty],
in which case it remains unresolved.
|===
@@ -221,7 +221,7 @@ When multiple patterns match a URL, the best match must be selected. This is don
one of the following depending on whether use of parsed `PathPattern` is enabled for use or not:
* {spring-framework-api}/web/util/pattern/PathPattern.html#SPECIFICITY_COMPARATOR[`PathPattern.SPECIFICITY_COMPARATOR`]
* {spring-framework-api}/util/AntPathMatcher.html#getPatternComparator-java.lang.String-[`AntPathMatcher.getPatternComparator(String path)`]
* {spring-framework-api}/util/AntPathMatcher.html#getPatternComparator(java.lang.String)[`AntPathMatcher.getPatternComparator(String path)`]
Both help to sort patterns with more specific ones on top. A pattern is more specific if
it has a lower count of URI variables (counted as 1), single wildcards (counted as 1),
@@ -3,7 +3,7 @@
[.small]#xref:web/webflux/uri-building.adoc[See equivalent in the Reactive stack]#
This section describes various options available in the Spring Framework to work with URI's.
This section describes various options available in the Spring Framework to work with URIs.
include::partial$web/web-uris.adoc[leveloffset=+1]
@@ -2,7 +2,7 @@
= UriComponents
[.small]#Spring MVC and Spring WebFlux#
`UriComponentsBuilder` helps to build URI's from URI templates with variables, as the following example shows:
`UriComponentsBuilder` helps to build URIs from URI templates with variables, as the following example shows:
[tabs]
======
@@ -128,7 +128,7 @@ Kotlin::
= UriBuilder
[.small]#Spring MVC and Spring WebFlux#
<<web-uricomponents, `UriComponentsBuilder`>> implements `UriBuilder`. You can create a
<<uricomponents, `UriComponentsBuilder`>> implements `UriBuilder`. You can create a
`UriBuilder`, in turn, with a `UriBuilderFactory`. Together, `UriBuilderFactory` and
`UriBuilder` provide a pluggable mechanism to build URIs from URI templates, based on
shared configuration, such as a base URL, encoding preferences, and other details.
@@ -247,7 +247,7 @@ and treats deviations from the syntax as illegal.
https://github.com/web-platform-tests/wpt/tree/master/url[URL parsing algorithm] in the
https://url.spec.whatwg.org[WhatWG URL Living standard]. It provides lenient handling of
a wide range of cases of unexpected input. Browsers implement this in order to handle
leniently user typed URL's. For more details, see the URL Living Standard and URL parsing
leniently user typed URLs. For more details, see the URL Living Standard and URL parsing
https://github.com/web-platform-tests/wpt/tree/master/url[test cases].
By default, `RestClient`, `WebClient`, and `RestTemplate` use the RFC parser type, and
@@ -255,10 +255,10 @@ expect applications to provide with URL templates that conform to RFC syntax. To
that you can customize the `UriBuilderFactory` on any of the clients.
Applications and frameworks may further rely on `UriComponentsBuilder` for their own needs
to parse user provided URL's in order to inspect and possibly validated URI components
to parse user provided URLs in order to inspect and possibly validated URI components
such as the scheme, host, port, path, and query. Such components can decide to use the
WhatWG parser type in order to handle URL's more leniently, and to align with the way
browsers parse URI's, in case of a redirect to the input URL or if it is included in a
WhatWG parser type in order to handle URLs more leniently, and to align with the way
browsers parse URIs, in case of a redirect to the input URL or if it is included in a
response to a browser.
@@ -373,14 +373,14 @@ Java::
[source,java,indent=0,subs="verbatim,quotes"]
----
String baseUrl = "https://example.com";
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl)
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl);
factory.setEncodingMode(EncodingMode.TEMPLATE_AND_VALUES);
// Customize the RestTemplate..
// Customize the RestTemplate.
RestTemplate restTemplate = new RestTemplate();
restTemplate.setUriTemplateHandler(factory);
// Customize the WebClient..
// Customize the WebClient.
WebClient client = WebClient.builder().uriBuilderFactory(factory).build();
----
@@ -393,12 +393,12 @@ Kotlin::
encodingMode = EncodingMode.TEMPLATE_AND_VALUES
}
// Customize the RestTemplate..
// Customize the RestTemplate.
val restTemplate = RestTemplate().apply {
uriTemplateHandler = factory
}
// Customize the WebClient..
// Customize the WebClient.
val client = WebClient.builder().uriBuilderFactory(factory).build()
----
======
+6 -6
View File
@@ -1,11 +1,11 @@
{
"dependencies": {
"antora": "3.2.0-alpha.4",
"@antora/atlas-extension": "1.0.0-alpha.2",
"@antora/collector-extension": "1.0.0-alpha.3",
"antora": "3.2.0-alpha.11",
"@antora/atlas-extension": "1.0.0-alpha.5",
"@antora/collector-extension": "1.0.2",
"@asciidoctor/tabs": "1.0.0-beta.6",
"@springio/antora-extensions": "1.14.2",
"fast-xml-parser": "4.5.2",
"@springio/asciidoctor-extensions": "1.0.0-alpha.10"
"@springio/antora-extensions": "1.14.7",
"fast-xml-parser": "5.3.8",
"@springio/asciidoctor-extensions": "1.0.0-alpha.17"
}
}
+9 -10
View File
@@ -200,6 +200,7 @@
See the License for the specific language governing permissions and
limitations under the License.
=======================================================================
SPRING FRAMEWORK ${version} SUBCOMPONENTS:
@@ -212,7 +213,7 @@ code for these subcomponents is subject to the terms and
conditions of the following licenses.
>>> ASM 9.1 (org.ow2.asm:asm:9.1, org.ow2.asm:asm-commons:9.1):
>>> ASM 9.9.1 (org.ow2.asm:asm:9.9.1):
Copyright (c) 2000-2011 INRIA, France Telecom
All rights reserved.
@@ -249,10 +250,8 @@ Copyright (c) 1999-2009, OW2 Consortium <https://www.ow2.org/>
>>> CGLIB 3.3 (cglib:cglib:3.3):
Per the LICENSE file in the CGLIB JAR distribution downloaded from
https://github.com/cglib/cglib/releases/download/RELEASE_3_3_0/cglib-3.3.0.jar,
CGLIB 3.3 is licensed under the Apache License, version 2.0, the text of which
is included above.
Per the LICENSE file in the CGLIB distribution, CGLIB 3.3 is licensed
under the Apache License, version 2.0, the text of which is included above.
>>> JavaPoet 1.13.0 (com.squareup:javapoet:1.13.0):
@@ -263,18 +262,18 @@ JavaPoet 1.13.0 is licensed under the Apache License, version 2.0, the text of
which is included above.
>>> Objenesis 3.4 (org.objenesis:objenesis:3.4):
>>> Objenesis 3.5 (org.objenesis:objenesis:3.5):
Per the LICENSE file in the Objenesis ZIP distribution downloaded from
http://objenesis.org/download.html, Objenesis 3.4 is licensed under the
Per the LICENSE file in the Objenesis distribution downloaded from
http://objenesis.org/download.html, Objenesis 3.5 is licensed under the
Apache License, version 2.0, the text of which is included above.
Per the NOTICE file in the Objenesis ZIP distribution downloaded from
Per the NOTICE file in the Objenesis distribution downloaded from
http://objenesis.org/download.html and corresponding to section 4d of the
Apache License, Version 2.0, in this case for Objenesis:
Objenesis
Copyright 2006-2019 Joe Walnes, Henri Tremblay, Leonardo Mesquita
Copyright 2006-2026 Joe Walnes, Henri Tremblay, Leonardo Mesquita
===============================================================================
@@ -0,0 +1,47 @@
/*
* 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.docs.core.expressions.languageref.expressionsoperatorsoverloaded;
import org.springframework.expression.Operation;
import org.springframework.expression.OperatorOverloader;
import java.util.ArrayList;
import java.util.List;
public class ListConcatenation implements OperatorOverloader {
@Override
public boolean overridesOperation(Operation operation, Object left, Object right) {
return (operation == Operation.ADD && left instanceof List && right instanceof List);
}
@Override
@SuppressWarnings({"rawtypes", "unchecked"})
public Object operate(Operation operation, Object left, Object right) {
if (operation == Operation.ADD &&
left instanceof List list1 && right instanceof List list2) {
List result = new ArrayList(list1);
result.addAll(list2);
return result;
}
throw new UnsupportedOperationException(
"No overload for operation %s and operands [%s] and [%s]"
.formatted(operation, left, right));
}
}
@@ -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[]
@@ -0,0 +1,37 @@
/*
* 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.docs.core.expressions.languageref.expressionsoperatorsoverloaded
import org.springframework.expression.Operation
import org.springframework.expression.OperatorOverloader
class ListConcatenation: OperatorOverloader {
override fun overridesOperation(operation: Operation, left: Any?, right: Any?): Boolean {
return operation == Operation.ADD && left is List<*> && right is List<*>
}
override fun operate(operation: Operation, left: Any?, right: Any?): Any {
if (operation == Operation.ADD && left is List<*> && right is List<*>) {
return left + right
}
throw UnsupportedOperationException(
"No overload for operation $operation and operands [$left] and [$right]")
}
}
@@ -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[]
+22 -22
View File
@@ -8,20 +8,20 @@ javaPlatform {
dependencies {
api(platform("com.fasterxml.jackson:jackson-bom:2.18.5"))
api(platform("io.micrometer:micrometer-bom:1.14.13"))
api(platform("io.netty:netty-bom:4.1.128.Final"))
api(platform("io.micrometer:micrometer-bom:1.15.11"))
api(platform("io.netty:netty-bom:4.1.132.Final"))
api(platform("io.netty:netty5-bom:5.0.0.Alpha5"))
api(platform("io.projectreactor:reactor-bom:2024.0.12"))
api(platform("io.projectreactor:reactor-bom:2024.0.17"))
api(platform("io.rsocket:rsocket-bom:1.1.5"))
api(platform("org.apache.groovy:groovy-bom:4.0.29"))
api(platform("org.apache.groovy:groovy-bom:4.0.31"))
api(platform("org.apache.logging.log4j:log4j-bom:2.21.1"))
api(platform("org.assertj:assertj-bom:3.27.6"))
api(platform("org.eclipse.jetty:jetty-bom:12.0.30"))
api(platform("org.eclipse.jetty.ee10:jetty-ee10-bom:12.0.30"))
api(platform("org.assertj:assertj-bom:3.27.7"))
api(platform("org.eclipse.jetty:jetty-bom:12.0.33"))
api(platform("org.eclipse.jetty.ee10:jetty-ee10-bom:12.0.33"))
api(platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.8.1"))
api(platform("org.jetbrains.kotlinx:kotlinx-serialization-bom:1.6.3"))
api(platform("org.junit:junit-bom:5.14.1"))
api(platform("org.mockito:mockito-bom:5.20.0"))
api(platform("org.junit:junit-bom:5.14.3"))
api(platform("org.mockito:mockito-bom:5.23.0"))
constraints {
api("com.fasterxml:aalto-xml:1.3.4")
@@ -31,9 +31,9 @@ dependencies {
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.32.1")
api("com.h2database:h2:2.3.232")
api("com.jayway.jsonpath:json-path:2.9.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.oracle.database.jdbc:ojdbc11:21.9.0.0")
api("com.rometools:rome:1.19.0")
api("com.squareup.okhttp3:mockwebserver:3.14.9")
@@ -50,7 +50,7 @@ dependencies {
api("io.mockk:mockk:1.13.17")
api("io.projectreactor.netty:reactor-netty5-http:2.0.0-M3")
api("io.projectreactor.tools:blockhound:1.0.8.RELEASE")
api("io.r2dbc:r2dbc-h2:1.0.0.RELEASE")
api("io.r2dbc:r2dbc-h2:1.1.0.RELEASE")
api("io.r2dbc:r2dbc-spi-test:1.0.0.RELEASE")
api("io.r2dbc:r2dbc-spi:1.0.0.RELEASE")
api("io.reactivex.rxjava3:rxjava:3.1.12")
@@ -90,11 +90,11 @@ dependencies {
api("junit:junit:4.13.2")
api("net.sf.jopt-simple:jopt-simple:5.0.4")
api("org.apache-extras.beanshell:bsh:2.0b6")
api("org.apache.activemq:activemq-broker:5.17.7")
api("org.apache.activemq:activemq-kahadb-store:5.17.7")
api("org.apache.activemq:activemq-stomp:5.17.7")
api("org.apache.activemq:artemis-jakarta-client:2.42.0")
api("org.apache.activemq:artemis-junit-5:2.42.0")
api("org.apache.activemq:activemq-broker:5.17.7")
api("org.apache.activemq:activemq-kahadb-store:5.17.7")
api("org.apache.activemq:activemq-stomp:5.17.7")
api("org.apache.activemq:artemis-jakarta-client:2.42.0")
api("org.apache.activemq:artemis-junit-5:2.42.0")
api("org.apache.commons:commons-pool2:2.9.0")
api("org.apache.derby:derby:10.16.1.1")
api("org.apache.derby:derbyclient:10.16.1.1")
@@ -129,7 +129,7 @@ dependencies {
api("org.hibernate:hibernate-core-jakarta:5.6.15.Final")
api("org.hibernate:hibernate-validator:7.0.5.Final")
api("org.hsqldb:hsqldb:2.7.4")
api("org.htmlunit:htmlunit:4.18.0")
api("org.htmlunit:htmlunit:4.21.0")
api("org.javamoney:moneta:1.4.4")
api("org.jruby:jruby:9.4.13.0")
api("org.junit.support:testng-engine:1.0.5")
@@ -137,8 +137,8 @@ dependencies {
api("org.ogce:xpp3:1.1.6")
api("org.python:jython-standalone:2.7.4")
api("org.quartz-scheduler:quartz:2.3.2")
api("org.seleniumhq.selenium:htmlunit3-driver:4.38.0")
api("org.seleniumhq.selenium:selenium-java:4.38.0")
api("org.seleniumhq.selenium:htmlunit3-driver:4.41.0")
api("org.seleniumhq.selenium:selenium-java:4.41.0")
api("org.skyscreamer:jsonassert:1.5.3")
api("org.slf4j:slf4j-api:2.0.17")
api("org.testng:testng:7.11.0")
@@ -147,6 +147,6 @@ dependencies {
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")
api("org.yaml:snakeyaml:2.5")
api("org.yaml:snakeyaml:2.6")
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
version=6.2.14-SNAPSHOT
version=6.2.18
org.gradle.caching=true
org.gradle.jvmargs=-Xmx2048m
-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(
@@ -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");
@@ -165,12 +165,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 {
}
@@ -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
@@ -120,7 +120,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() {
@@ -261,7 +261,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
@@ -98,7 +98,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);
@@ -31,7 +31,7 @@ import static org.assertj.core.api.Assertions.assertThat;
class BeanNamePointcutMatchingTests {
@Test
void testMatchingPointcuts() {
void matchingPointcuts() {
assertMatch("someName", "bean(someName)");
// Spring bean names are less restrictive compared to AspectJ names (methods, types etc.)
@@ -66,7 +66,7 @@ class BeanNamePointcutMatchingTests {
}
@Test
void testNonMatchingPointcuts() {
void nonMatchingPointcuts() {
assertMisMatch("someName", "bean(someNamex)");
assertMisMatch("someName", "bean(someX*Name)");
@@ -17,7 +17,6 @@
package org.springframework.aop.aspectj;
import java.io.IOException;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
import org.aspectj.lang.JoinPoint;
@@ -49,17 +48,17 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
class MethodInvocationProceedingJoinPointTests {
@Test
void testingBindingWithJoinPoint() {
void bindingWithJoinPoint() {
assertThatIllegalStateException().isThrownBy(AbstractAspectJAdvice::currentJoinPoint);
}
@Test
void testingBindingWithProceedingJoinPoint() {
void bindingWithProceedingJoinPoint() {
assertThatIllegalStateException().isThrownBy(AbstractAspectJAdvice::currentJoinPoint);
}
@Test
void testCanGetMethodSignatureFromJoinPoint() {
void canGetMethodSignatureFromJoinPoint() {
final Object raw = new TestBean();
// Will be set by advice during a method call
final int newAge = 23;
@@ -106,9 +105,9 @@ class MethodInvocationProceedingJoinPointTests {
assertThat(AbstractAspectJAdvice.currentJoinPoint().getSignature()).as("Return same MethodSignature repeatedly").isSameAs(msig);
assertThat(AbstractAspectJAdvice.currentJoinPoint()).as("Return same JoinPoint repeatedly").isSameAs(AbstractAspectJAdvice.currentJoinPoint());
assertThat(msig.getDeclaringType()).isEqualTo(method.getDeclaringClass());
assertThat(Arrays.equals(method.getParameterTypes(), msig.getParameterTypes())).isTrue();
assertThat(method.getParameterTypes()).isEqualTo(msig.getParameterTypes());
assertThat(msig.getReturnType()).isEqualTo(method.getReturnType());
assertThat(Arrays.equals(method.getExceptionTypes(), msig.getExceptionTypes())).isTrue();
assertThat(method.getExceptionTypes()).isEqualTo(msig.getExceptionTypes());
msig.toLongString();
msig.toShortString();
});
@@ -118,7 +117,7 @@ class MethodInvocationProceedingJoinPointTests {
}
@Test
void testCanGetSourceLocationFromJoinPoint() {
void canGetSourceLocationFromJoinPoint() {
final Object raw = new TestBean();
ProxyFactory pf = new ProxyFactory(raw);
pf.addAdvisor(ExposeInvocationInterceptor.ADVISOR);
@@ -135,7 +134,7 @@ class MethodInvocationProceedingJoinPointTests {
}
@Test
void testCanGetStaticPartFromJoinPoint() {
void canGetStaticPartFromJoinPoint() {
final Object raw = new TestBean();
ProxyFactory pf = new ProxyFactory(raw);
pf.addAdvisor(ExposeInvocationInterceptor.ADVISOR);
@@ -43,14 +43,14 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
class TrickyAspectJPointcutExpressionTests {
@Test
void testManualProxyJavaWithUnconditionalPointcut() {
void manualProxyJavaWithUnconditionalPointcut() {
TestService target = new TestServiceImpl();
LogUserAdvice logAdvice = new LogUserAdvice();
testAdvice(new DefaultPointcutAdvisor(logAdvice), logAdvice, target, "TestServiceImpl");
}
@Test
void testManualProxyJavaWithStaticPointcut() {
void manualProxyJavaWithStaticPointcut() {
TestService target = new TestServiceImpl();
LogUserAdvice logAdvice = new LogUserAdvice();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
@@ -59,7 +59,7 @@ class TrickyAspectJPointcutExpressionTests {
}
@Test
void testManualProxyJavaWithDynamicPointcut() {
void manualProxyJavaWithDynamicPointcut() {
TestService target = new TestServiceImpl();
LogUserAdvice logAdvice = new LogUserAdvice();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
@@ -68,7 +68,7 @@ class TrickyAspectJPointcutExpressionTests {
}
@Test
void testManualProxyJavaWithDynamicPointcutAndProxyTargetClass() {
void manualProxyJavaWithDynamicPointcutAndProxyTargetClass() {
TestService target = new TestServiceImpl();
LogUserAdvice logAdvice = new LogUserAdvice();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
@@ -77,7 +77,7 @@ class TrickyAspectJPointcutExpressionTests {
}
@Test
void testManualProxyJavaWithStaticPointcutAndTwoClassLoaders() throws Exception {
void manualProxyJavaWithStaticPointcutAndTwoClassLoaders() throws Exception {
LogUserAdvice logAdvice = new LogUserAdvice();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
@@ -97,7 +97,7 @@ class TypePatternClassFilterTests {
}
@Test
void testEquals() {
void equals() {
TypePatternClassFilter filter1 = new TypePatternClassFilter("org.springframework.beans.testfixture.beans.*");
TypePatternClassFilter filter2 = new TypePatternClassFilter("org.springframework.beans.testfixture.beans.*");
TypePatternClassFilter filter3 = new TypePatternClassFilter("org.springframework.tests.*");
@@ -107,7 +107,7 @@ class TypePatternClassFilterTests {
}
@Test
void testHashCode() {
void hashCodeBehavior() {
TypePatternClassFilter filter1 = new TypePatternClassFilter("org.springframework.beans.testfixture.beans.*");
TypePatternClassFilter filter2 = new TypePatternClassFilter("org.springframework.beans.testfixture.beans.*");
TypePatternClassFilter filter3 = new TypePatternClassFilter("org.springframework.tests.*");
@@ -117,7 +117,7 @@ class TypePatternClassFilterTests {
}
@Test
void testToString() {
void toStringOutput() {
TypePatternClassFilter filter1 = new TypePatternClassFilter("org.springframework.beans.testfixture.beans.*");
TypePatternClassFilter filter2 = new TypePatternClassFilter("org.springframework.beans.testfixture.beans.*");

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