Compare commits

...

1046 Commits

Author SHA1 Message Date
Spring Builds 71db005f81 Release v6.1.0-M5 2023-09-14 09:20:07 +00:00
Juergen Hoeller 59961960dd Merge branch '6.0.x' 2023-09-14 09:30:40 +02:00
Stephane Nicoll 727f69ae70 Merge branch '6.0.x' 2023-09-13 21:25:14 +02:00
Stephane Nicoll 92b6c5df4f Upgrade to Context Propagation 1.1.0-M4
Closes gh-31220
2023-09-13 21:10:46 +02:00
Stephane Nicoll e109c55192 Upgrade to Reactor 2023.0.0-M3
Closes gh-31219
2023-09-13 21:10:46 +02:00
Stephane Nicoll 8f21c228b8 Upgrade to Micrometer 1.12.0-M3
Closes gh-31218
2023-09-13 21:10:46 +02:00
Sam Brannen a220c545fc Support declarative ContextCustomizerFactory registration in the TCF
Prior to this commit, it was only possible to register a
ContextCustomizerFactory in the TestContext framework (TCF) via the
SpringFactoriesLoader mechanism.

This commit introduces support for declarative registration of a
ContextCustomizerFactory local to a test class via a new
@ContextCustomizerFactories annotation.

Closes gh-26148
2023-09-13 19:59:50 +02:00
Sam Brannen 99a50e7cea Polishing 2023-09-13 19:52:17 +02:00
Sébastien Deleuze 68ba55bea9 Fix ModelInitializerKotlinTests
See gh-30894
2023-09-13 18:30:01 +02:00
Sébastien Deleuze bb85d9b5e4 Merge branch '6.0.x' 2023-09-13 18:07:59 +02:00
Sam Brannen a678be80ea Merge branch '6.0.x' 2023-09-13 17:51:02 +02:00
Juergen Hoeller 550f05c9dc Merge branch '6.0.x'
# Conflicts:
#	spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java
2023-09-13 17:28:54 +02:00
Sam Brannen 5bcbcb3049 Simplify SynthesizedMergedAnnotationInvocationHandler.invoke() 2023-09-13 17:14:03 +02:00
Sam Brannen fe7355d5b0 Polishing 2023-09-13 17:04:39 +02:00
Sam Brannen a271d5ec15 Use nullSafeHashCode() in SynthesizedMergedAnnotationInvocationHandler
In light of the refinements to ObjectUtils, this commit updates
SynthesizedMergedAnnotationInvocationHandler to use
ObjectUtils.nullSafeHashCode() and removes the now obsolete code in
SynthesizedMergedAnnotationInvocationHandler.

See gh-29051
2023-09-13 16:50:58 +02:00
Sam Brannen af13967e2a Polishing 2023-09-13 16:50:58 +02:00
Juergen Hoeller ae8a353041 Reject iterable/collection value for positional parameter
Closes gh-31215
2023-09-13 16:50:01 +02:00
Juergen Hoeller 8f6c56fe9a Support for one-time tasks with just @Scheduled(initialDelay=...)
Closes gh-31211
2023-09-13 16:48:54 +02:00
Sam Brannen e63e3a6d28 Delete obsolete constants 2023-09-13 16:13:09 +02:00
Sam Brannen fa1dd74aff Merge branch '6.0.x' 2023-09-13 16:10:29 +02:00
Sam Brannen 5dacf688b2 Polishing 2023-09-13 16:01:11 +02:00
Sam Brannen e5f18798ca Revert deprecation of [Simple]CommandLinePropertySource
This commit reverts the deprecation of CommandLinePropertySource and
SimpleCommandLinePropertySource, since we have discovered that Spring
Boot actively uses SimpleCommandLinePropertySource in
org.springframework.boot.SpringApplication.

Closes gh-31207
2023-09-13 15:58:00 +02:00
Stephane Nicoll 01f717375b Introduce ObjectUtils#nullSafeHash(Object... element)
This commit deprecates the various nullSafeHashCode methods taking array
types as they are superseded by Arrays.hashCode now. This means that
the now only remaining nullSafeHashCode method does not trigger a
warning only if the target type is not an array. At the same time, there
are multiple use of this method on several elements, handling the
accumulation of hash codes.

For that reason, this commit also introduces a nullSafeHash that takes
an array of elements. The only difference between Objects.hash is that
this method handles arrays.

The codebase has been reviewed to use any of those two methods when it
is possible.

Closes gh-29051
2023-09-13 15:14:34 +02:00
Stephane Nicoll f2e898d35d Polish 2023-09-13 14:30:01 +02:00
rstoyanchev bd4b3866fc Revise solution for WebFlux JettyRequestUpgradeStrategy
See gh-30344
2023-09-13 13:25:13 +01:00
Sam Brannen c23795a8a6 Update copyright headers 2023-09-13 13:57:51 +02:00
Sam Brannen c598f0565a Deprecate CommandLinePropertySource and associated implementations
Closes gh-31207
2023-09-13 13:56:24 +02:00
Sam Brannen e7cf54d4e2 Revise SpEL inline collection caching improvements
This commit revises the contribution for gh-25921 in the following ways.

- Use instanceof pattern matching

- Use List.of() and Map.of()

- Add missing @⁠since tags

- Polish Javadoc

- Rename isNegativeNumber() to isNegativeNumberLiteral()

- Restructure InlineCollectionTests using @⁠Nested, etc.

- Fix testListWithVariableNotCached() test: it previously set a SpEL
  "variable" but tested a "property" in the root context object, which
  effectively did not test anything.

- Introduce additional tests: listWithPropertyAccessIsNotCached(),
  mapWithVariableIsNotCached(), and mapWithPropertyAccessIsNotCached().
2023-09-13 13:35:41 +02:00
Sam Brannen 2cc1ee78c1 Polishing 2023-09-13 13:33:21 +02:00
rstoyanchev 1f1f222b8b Partial revert of 1f8913a9
For WebFlux we can't use ServletContextAware.

See gh-30344
2023-09-13 12:05:09 +01:00
rstoyanchev 74182cd3d6 Ensure correct detection of Jetty WebSocket server 2023-09-13 09:34:07 +01:00
rstoyanchev 1f8913a96c Support Jetty WebSocket server parameters
Closes gh-30344
2023-09-13 09:34:07 +01:00
rstoyanchev 29e3acc887 Re-enable Jetty in spring-websocket tests 2023-09-13 09:34:07 +01:00
Sébastien Deleuze 5d454390f0 Polishing
See gh-25921
2023-09-13 10:29:32 +02:00
Semyon Danilov 6f9546722a Improve SpEL inline collection caching
This commit fixes SpEL inline collection caching with
with negative keys or values.

Closes gh-25921
2023-09-13 10:29:01 +02:00
Sébastien Deleuze 6548cee20a Document CoWebFilter.COROUTINE_CONTEXT_ATTRIBUTE
See gh-27522
2023-09-12 19:27:49 +02:00
Sébastien Deleuze abb296c7d2 Remove Java code dependency on CoWebFilter
To avoid compilation errors in Eclipse which does
not support Java code dependency on Kotlin code.

See gh-27522
2023-09-12 19:27:49 +02:00
Sam Brannen b6fdfe8ee3 Remove duplicated terms in documentation
... using the following RegEx to find them:

(?<!\S)(\w+)(?:\s+\1)+(?!\S)
2023-09-12 19:00:28 +02:00
Sam Brannen 345910591a Polishing 2023-09-12 15:08:11 +02:00
Arjen Poutsma 9df735b3ab Merge pull request #30028 from yuzawa-san:request-predicate-commit
* gh-30028:
  Polishing external contribution
  Improve attribute handling in RequestPredicates
2023-09-12 12:36:23 +02:00
Arjen Poutsma c5c843696b Polishing external contribution 2023-09-12 12:34:44 +02:00
James Yuzawa 39786e4790 Improve attribute handling in RequestPredicates
This commit changes the way request attributes are handled in
RequestPredicates. Previously, the AND/OR/NOT predicates copied all
attributes in a map, and restored that when the delegate predicate(s)
failed.
Now, we only set the attributes when all delegates have succeeded.

Closes gh-30028
2023-09-12 12:34:44 +02:00
Rossen Stoyanchev ed172d6269 ByteBuffer handling for Jetty WebSocket messages
Closes gh-31182
2023-09-12 10:08:57 +01:00
rstoyanchev f51838b6ba Polishing spring-websocket tests 2023-09-12 10:08:57 +01:00
Brian Clozel 19588d413d Auto-detect observability beans in WebHttpHandlerBuilder
This commit ensures that both `ObservationRegsitry` and
`ServerRequestObservationConvention` beans are automatically detected in
the application context if they are unique. This aligns with the
existing behavior for all other builder methods.

Closes gh-31205
2023-09-11 21:17:14 +02:00
Sébastien Deleuze dfd631e662 Merge branch '6.0.x' 2023-09-11 18:10:13 +02:00
Juergen Hoeller 3099710087 Merge branch '6.0.x'
# Conflicts:
#	spring-r2dbc/src/main/java/org/springframework/r2dbc/connection/R2dbcTransactionManager.java
#	spring-r2dbc/src/test/java/org/springframework/r2dbc/connection/R2dbcTransactionManagerUnitTests.java
2023-09-11 17:40:11 +02:00
Sam Brannen a199654a62 Use MergedAnnotations API in AnnotationBeanNameGenerator where feasible
AnnotationBeanNameGenerator was written before the introduction of the
MergedAnnotations API and therefore heavily relies on the
AnnotationMetadata abstraction and various helper methods for ASM
compatibility.

However, recent work on determineBeanNameFromAnnotation() has made it
apparent that we should use the MergedAnnotations API directly in
AnnotationBeanNameGenerator where feasible in order to avoid
unnecessary, repeated iterations/streams over the same annotation
metadata.

Closes gh-31203
2023-09-11 17:20:01 +02:00
Stephane Nicoll bf5ce82254 Move custom code fragments requirements at the right place
This commit moves the check on bean definitions having an instance
supplier where they are actually used.

Closes gh-31200
2023-09-11 17:13:04 +02:00
Stephane Nicoll 66a571fe27 Make constructorOrFactory method resolution optional
This commit allows a custom code fragment to provide the code to
create a bean without relying on ConstructorResolver. This is especially
important for use cases that derive from the default behaviour and
provide an instance supplier with the regular runtime scenario.

This is a breaking change for code fragments providing a custom
implementation of the related methods. As it turns out, almost all of
them did not need the Executable argument. Configuration class parsing
is the exception, where it needs to provide a different constructor in
the case of the proxy. To make this use case possible,
InstanceSupplierCodeGenerator has been made public.

Closes gh-31117
2023-09-11 15:55:22 +02:00
Sam Brannen fceed9f3e5 Remove @Nullable on attributes parameter in isStereotypeWithNameValue() 2023-09-11 14:54:55 +02:00
christophejan 8d4ea72e4e Skip release connection after nested with existing transaction 2023-09-11 14:20:37 +02:00
Sam Brannen 5c7bf0c2fe Add tests for MethodInvoker behavior in the TestContext framework
See gh-31199
2023-09-11 13:15:21 +02:00
Sam Brannen 4aeeacb4c2 Simplify and rename TestContextManager[ListenerExecutionOrder]Tests 2023-09-11 13:11:16 +02:00
rstoyanchev 38a9c2ceeb Polishing contribution
See gh-31164
2023-09-11 11:27:25 +01:00
Dmitrii Bocharov fe8e6d3d5a Merge MultipartFileArgumentResolver into RequestPartArgumentResolver
Also add test with Optional parameter for a RequestPart argument.

See gh-31164

Signed-off-by: Dmitrii Bocharov <bdshadow@gmail.com>
2023-09-11 11:27:25 +01:00
Brian Clozel f83c609436 Polish
This commit replaces the initial allocation size for the content caching
buffer by a `FastByteArrayOutputStream`. With this variant, allocations
are cheap and we don't need to apply heuristics anymore to guess the
best initial buffer size.

See gh-29775
2023-09-11 10:58:01 +02:00
Ryan Rupp 6de0be16c2 Optimize initial buffer size in ContentCachingRequestWrapper
Prior to this commit, the initial buffer size for content caching
allocated in `ContentCachingRequestWrapper` would be:

* the request content length, if available in request headers
* the cache limit size as configured on the wrapper

The latter is really an upper bound and should not be considered as a
good default in most cases. This commit ensures that the request content
length is still used if available, but uses a default 1024 size if it's
not.

While this change will probably cause more reallocations as the buffer
grows, this will avoid large allocations in many cases and should
overall help with GC.

Closes gh-29775
2023-09-11 10:36:40 +02:00
Sam Brannen ed83461021 Support parameter injection in @[Before|After]Transaction methods
Prior to this commit, @​BeforeTransaction and @​AfterTransaction
methods could not accept any arguments. This is acceptable with testing
frameworks such as JUnit 4 and TestNG that do not provide support for
parameter injection. However, users of JUnit Jupiter have become
accustomed to being able to accept arguments in lifecycle methods
annotated with JUnit's @​BeforeEach, @​AfterEach, etc.

As a follow up to the previous commit (see gh-31199), this commit
introduces first-class support for parameter injection in
@​BeforeTransaction and @​AfterTransaction methods, as demonstrated in
the following example.

@​BeforeTransaction
void verifyInitialDatabaseState(@Autowired DataSource dataSource) {
	// Use the DataSource to verify the initial DB state
}

Closes gh-30736
2023-09-10 20:15:26 +02:00
Sam Brannen 41904d46ad Introduce MethodInvoker API for TestExecutionListeners
In order to be able to support parameter injection in
@​BeforeTransaction and @​AfterTransaction methods (see gh-30736), this
commit introduces a MethodInvoker API for TestExecutionListeners as a
generic mechanism for delegating to the underlying testing framework to
invoke methods.

The default implementation simply invokes the method without arguments,
which allows TestExecutionListeners using this mechanism to operate
correctly when the underlying testing framework is JUnit 4, TestNG, etc.

A JUnit Jupiter specific implementation is registered in the
SpringExtension which delegates to the
ExtensionContext.getExecutableInvoker() mechanism introduced in JUnit
Jupiter 5.9. This allows a TestExecutionListener to transparently
benefit from registered ParameterResolvers in JUnit Jupiter (including
the SpringExtension) when invoking user methods, effectively providing
support for parameter injection for arbitrary methods.

Closes gh-31199
2023-09-10 20:15:12 +02:00
Sam Brannen 0ebdd8cb98 Polishing 2023-09-10 20:15:04 +02:00
Sam Brannen b082f546ec Polishing 2023-09-10 14:59:21 +02:00
liaozan 9728b8cefd Use Method#isDefault to find default methods in interfaces
Closes gh-31197
2023-09-10 14:53:05 +02:00
Sam Brannen 6ca01e15cf Polishing 2023-09-10 14:50:47 +02:00
Toshiaki Maki 588b5a45f8 Pass correct SqlParameterSource to NamedParameterJdbcTemplate in DefaultJdbcClient
Prior to this commit, when using RowCallbackHandler or ResultSetExtractor in JdbcClient
and passing a parameter to paramSource(), an exception was thrown stating "No value
supplied for the SQL parameter 'xxxxxx'" because the SqlParameterSource used internally
was the wrong one.

Closes gh-31195
2023-09-10 14:42:54 +02:00
Sam Brannen 436c7136e9 Polish TransactionAssert 2023-09-10 14:37:28 +02:00
Sam Brannen 7305692005 Merge branch '6.0.x' 2023-09-10 14:37:01 +02:00
Sam Brannen 0f36c5fd6a Polishing 2023-09-08 20:12:44 +02:00
Sam Brannen 60d05d4204 Polishing 2023-09-08 19:29:28 +02:00
Sam Brannen dfea3d05aa Revise support for JSR-330 and Jakarta @Inject for autowiring test constructors
Closes gh-29851
2023-09-08 19:29:28 +02:00
Florian Lehmann 8dd857a84d Support @Inject from JSR-330 & Jakarta for autowiring test constructors
See gh-29851
2023-09-08 19:26:39 +02:00
Brian Clozel 0f945873a3 Only reset response buffer for error handling
Prior to this commit, `DispatcherServlet` would completely reset the
response (status, headers and body) before handling errors within Spring
MVC. This can cause unintended consequences when Servlet Filters added
response headers before the error happened. Such response headers might
be still required in case of error handling.

This commit changes the complete reset of the response to only resetting
the response buffer, if possible.

Closes gh-31154
See gh-31104
2023-09-08 18:47:21 +02:00
Sébastien Deleuze 88ee8fc92f Upgrade to CRaC 1.4.0
Closes gh-31190
2023-09-08 17:53:08 +02:00
Sam Brannen 154aec7d75 Merge branch '6.0.x' 2023-09-08 17:22:16 +02:00
Sam Brannen a11b8039c5 Merge branch '6.0.x' 2023-09-08 16:31:11 +02:00
Sam Brannen f0b1133b12 Merge branch '6.0.x' 2023-09-08 16:12:29 +02:00
Stephane Nicoll 91f65e63b4 Merge pull request #30068 from enimiste
* pr/30068:
  Polish "Add return type check on GetterPointcut"
  Add return type check on GetterPointcut

Closes gh-30068
2023-09-08 15:37:07 +02:00
Stephane Nicoll 3e424e3ce7 Polish "Add return type check on GetterPointcut"
See gh-30068
2023-09-08 15:30:52 +02:00
Enimiste d1dfac4e5d Add return type check on GetterPointcut
See gh-30068
2023-09-08 15:20:53 +02:00
Sam Brannen c9e13575ca Use switch expression and constants in CallMetaDataProviderFactory 2023-09-08 14:05:03 +02:00
Sam Brannen 762d903aad Convert TableMetaData to a record
This simplifies the code and also improves diagnostics while debugging.
2023-09-08 14:04:47 +02:00
Sam Brannen 47d760845b Add missing @Nullable declaration 2023-09-08 14:00:52 +02:00
Sam Brannen ab5d46411a Introduce identifierNameToUse() in GenericCallMetaDataProvider 2023-09-08 13:59:46 +02:00
Sam Brannen 4df3eb4670 Polish Javadoc 2023-09-08 13:59:10 +02:00
Sam Brannen 5a87f555f8 Update copyright headers 2023-09-08 13:57:41 +02:00
Sébastien Deleuze 5fd356e44f Remove outdated @Suppress("DEPRECATION") annotations
See gh-31127
2023-09-08 13:18:16 +02:00
Sébastien Deleuze 6f38e90048 Merge branch '6.0.x' 2023-09-08 13:08:14 +02:00
rstoyanchev b0d4931d5d Merge branch '6.0.x' 2023-09-08 10:07:35 +01:00
Sébastien Deleuze 4c621826bd Merge branch '6.0.x' 2023-09-08 10:51:36 +02:00
rstoyanchev e0c4347cc6 Merge branch '6.0.x' 2023-09-08 09:18:24 +01:00
rstoyanchev 53133d949d Polishing contribution
Closes gh-29509
2023-09-08 08:28:26 +01:00
tianshuang a1ce5dac0b Execute preflight checks before interceptor chain
See gh-29509
2023-09-08 08:28:26 +01:00
Sébastien Deleuze da7ad71b7f Refine ReactorResourceFactory Javadoc
Provides more details about stop/restart
scenario.

See gh-31178
2023-09-08 09:12:35 +02:00
Yanming Zhou 94a49c4ed3 Log warning if ResponseStatus 'reason' is set and handler returns value
When a handler method is annotated with `@ResponseStatus(reason="...")`,
Spring MVC will use `HttpServletResponse#sendError` to complete the
response. As a result, the Servlet container will send an HTML error
page and any existing data in the response buffer will be cleared.

This commit clarifies the `@ResponseStatus` Javadoc and ensures that a
message is logged at the WARN level if a handler method is annotated
like this and still returns a non-Void value. In this case, the return
value will be ignored and developers should be aware of this fact.

See gh-31113
Closes gh-31121
2023-09-07 19:00:26 +02:00
Brian Clozel bc6954255b Merge branch '6.0.x' 2023-09-07 17:28:08 +02:00
Sam Brannen c3a0022c9e Polish DefaultClientResponseTests and suppress "unchecked" warnings 2023-09-07 17:09:31 +02:00
Brian Clozel b02e1bb935 Update CI pipeline for api docs publication
See gh-31049
2023-09-07 17:00:20 +02:00
Sam Brannen ac3a466607 Merge branch '6.0.x' 2023-09-07 16:50:50 +02:00
rstoyanchev fefbeeeb18 Merge branch '6.0.x' 2023-09-07 15:27:56 +01:00
Brian Clozel 244c069ea8 Polish
See gh-31173
2023-09-07 16:07:52 +02:00
Brian Clozel 3686c457bb Polish Aggregate Javadoc task
Closes gh-31173
2023-09-07 15:29:25 +02:00
Arjen Poutsma 7b5effecf3 Reference documentation for RestClient
In addition to providing reference documentation for the `RestClient`,
this commit also shortens the `RestTemplate` section.

Closes gh-30826
2023-09-07 14:28:56 +02:00
Sam Brannen e42902b742 Stop referring to JDO and outdated JDBC versions in documentation
Closes gh-31183
2023-09-07 13:26:38 +02:00
Sam Brannen 05790e36db Polish StandardTypeLocatorTests 2023-09-07 13:26:38 +02:00
Sébastien Deleuze 578fdbe97a Merge branch '6.0.x' 2023-09-07 13:19:32 +02:00
Sébastien Deleuze ada76bd7bf Add an assertion to AopUtils.KotlinDelegate#invokeSuspendingFunction
See gh-22462
2023-09-07 12:30:47 +02:00
Sébastien Deleuze b0aa004d9d Propagate CoroutineContext in CoWebFilter
This provides an elegant and dynamic way to customize the
CoroutineContext in WebFlux with the annotation programming
model.

Closes gh-27522
2023-09-07 12:08:12 +02:00
Sébastien Deleuze 9d768a89d2 Make ReactorClientHttpConnector lifecycle-aware
With this commit, ReactorClientHttpConnector now implements
SmartLifecycle which optionally allows recreating the HttpClient
after ReactorResourceFactory has been updated.

Closes gh-31180
2023-09-06 17:48:22 +02:00
Sébastien Deleuze ab2ad746b5 Polish ReactorResourceFactoryTests
See gh-31178
2023-09-06 16:01:06 +02:00
Sébastien Deleuze 125b8e7418 Make ReactorResourceFactory lifecycle-aware
With this commit, ReactorResourceFactory now implements
Lifecycle which allows supporting JVM Checkpoint Restore
in Spring Boot with Reactor Netty server, and helps
to support Reactor Netty client as well.

Closes gh-31178
2023-09-06 14:14:07 +02:00
Sébastien Deleuze 2a916a3869 Polish ReactorResourceFactoryTests 2023-09-06 14:14:07 +02:00
rstoyanchev 2bc93710f3 Reactive support in MethodValidationInterceptor
Closes gh-20781
2023-09-06 12:54:06 +01:00
rstoyanchev b110a39e88 Polishing MethodValidator contract 2023-09-06 12:54:06 +01:00
Sam Brannen 734d113b11 Ensure configuration methods in SimpleJdbcInsert support method chaining
Prior to this commit, the withoutTableColumnMetaDataAccess() and
includeSynonymsForTableColumnMetaData() methods in SimpleJdbcInsert
incorrectly declared a SimpleJdbcInsertOperations return type, which
prevented method chaining when the instance was declared to be of type
SimpleJdbcInsert.

This commit changes the return types of those methods to
SimpleJdbcInsert to benefit from covariant return types like the rest
of the configuration methods in SimpleJdbcInsert.

Closes gh-31177
2023-09-06 11:37:50 +02:00
Sam Brannen 0a7166234d Polishing 2023-09-06 11:37:50 +02:00
Sam Brannen 552e7fb1d5 Test another scenario 2023-09-06 11:10:44 +02:00
Johnny Lim 884975e094 Polish JMS observation instrumentation
See gh-30335
Closes gh-31172
2023-09-05 17:18:02 +02:00
Sébastien Deleuze adb73389a8 Merge branch '6.0.x' 2023-09-05 16:32:15 +02:00
Sam Brannen 7dc0653f38 Revise quoted identifier support in SimpleJdbcInsert
Prior to this commit and the previous commit, SimpleJdbcInsert did not
provide built-in support for "quoted identifiers". Consequently, if any
column names conflicted with keywords or functions from the underlying
database, you had to manually quote the column names when specifying
them via `usingColumns(...)`, and there was unfortunately no way to
quote schema and table names.

The previous commit provided rudimentary support for quoted SQL
identifiers (schema, table, and column names) by querying
java.sql.DatabaseMetaData.getIdentifierQuoteString() to determine the
quote string. It also introduced `usingEscaping(boolean)` in
`SimpleJdbcInsertOperations` to enable the feature. However, it
incorrectly quoted the schema and table names together, and it did not
take into account the fact that a quoted identifier should respect the
casing (uppercase vs. lowercase) of the underlying database's metadata.

This commit revises quoted identifier support in `SimpleJdbcInsert` by:

- renaming `usingEscaping(boolean)` to `usingQuotedIdentifiers()`

- quoting schema and table names separately

- respecting the casing (uppercase vs. lowercase) of the underlying
  database's metadata when quoting identifiers

- introducing integration tests against an in-memory H2 database

See gh-13874
Closes gh-24013
2023-09-05 13:29:40 +02:00
Kita, Maksim d39034754f Support quoted identifiers in SimpleJdbcInsert
See gh-24013
2023-09-05 13:29:40 +02:00
Sam Brannen 070590cb11 Introduce TableMetaDataProvider.columnNameToUse(String) 2023-09-05 13:29:40 +02:00
Sam Brannen 4c45f37166 Polish simple JDBC support classes 2023-09-05 11:36:48 +02:00
Sam Brannen fad70aa252 Update copyright headers 2023-09-04 14:30:44 +02:00
rstoyanchev 0c714e3a78 Fix documentation syntax issue
See gh-16917
2023-09-04 13:25:19 +01:00
rstoyanchev b068742ec8 Support method validation for Lists in WebMvc and WebFlux
Closes gh-31120
2023-09-04 13:22:09 +01:00
Arjen Poutsma 6597727c86 Upgrade to Jetty 12
This commit upgrades Spring Framework to Jetty 12.0.1, and Reactive HTTP
 Client 4.0.0.

Closes gh-30698
2023-09-04 14:03:29 +02:00
Sam Brannen 210b42b7d8 Polishing 2023-09-04 12:42:16 +02:00
Brian Clozel 5fb567a37e Fix missing URI template from client observations in ResTemplateAdapter
Prior to this commit, the `RestTemplateAdapter` would manually expand
templated URIs. This means that the `RestTemplate` instance itself would
never see the templated URIs and could not record it in the client
observations at runtime.

This commit ensures that when URI templates are available, the adapter
uses the correct `exchange` method variant.

Fixes gh-31144
2023-09-04 11:45:35 +02:00
Sam Brannen e8fd29091c Register hints for @TestPropertySource factory
This commit updates MergedContextConfigurationRuntimeHints so that it
registers hints for a custom PropertySourceFactory.

Closes gh-31160
2023-09-03 16:59:58 +02:00
Sam Brannen 7882d265c6 Polishing 2023-09-03 16:59:58 +02:00
Juergen Hoeller 759e3d4aa6 Adapt to PreparedStatementCreator for JdbcClient update operation
See gh-31122
2023-09-03 02:32:01 +02:00
Juergen Hoeller 9b50e7ded6 Merge branch '6.0.x'
# Conflicts:
#	framework-platform/framework-platform.gradle
2023-09-03 01:27:18 +02:00
Juergen Hoeller 855fe39b7f Use PreparedStatementCreator for query/update with indexed params
Closes gh-31122
2023-09-03 00:44:11 +02:00
Juergen Hoeller 7595465c21 Lazily obtain Validator from original Supplier
Closes gh-31137
2023-09-03 00:42:52 +02:00
Juergen Hoeller d6e167d16e Raise log level to warn when enhancement cannot apply
Closes gh-31102
2023-09-03 00:42:38 +02:00
Sam Brannen 23a0903b5c Add missing comma and polishing 2023-09-02 19:34:20 +02:00
Sam Brannen a96aeb69c1 Merge branch '6.0.x' 2023-09-02 19:08:36 +02:00
Sébastien Deleuze 03650e7d58 Merge branch '6.0.x' 2023-09-01 12:52:59 +02:00
Sébastien Deleuze 0aa4dc11f1 Add missing graal-sdk dependency to spring-orm
See gh-29549
2023-09-01 12:47:47 +02:00
Sébastien Deleuze 81607238ce Use consistently @since 6.1 2023-09-01 12:33:21 +02:00
Sébastien Deleuze 63a20be502 Optimize Hibernate native footprint by making ByteBuddy unreachable
As a workaround before a proper solution the Spring team is trying to
find with the Hibernate team in a future version of Hibernate, this
commit introduces 2 GraalVM substitutions that should allow to remove
ByteBuddy reachability with Hibernate 6.3.

Closes gh-29549
2023-09-01 12:29:16 +02:00
Marten Deinum acd34df0d5 Fix WebFlux blocking configuration documentation
BlockingExecutionConfigurer#setExecutor takes an AsyncTaskExecutor
not an Executor as the documentation states. With this commit,
this has been corrected.

Closes gh-31150
2023-09-01 10:29:47 +02:00
Sam Brannen 093d6a1bec Merge branch '6.0.x' 2023-08-31 13:40:31 +02:00
Anton-Vasilev 1ee18e74fc Fix typo in RestClient Javadoc
Closes gh-31145
2023-08-31 09:43:06 +02:00
Brian Clozel 0d7c9b7c93 Attempt to reset Servlet response in DispatcherServlet
This is a follow-up change related to gh-31104.
This change reverts the changes previously made in
`ExceptionHandlerExceptionResolver` and instead attempts to reset the
response directly in `DispatcherServlet` in order to cover all types or
exception handling.

Unlike the previous change, we decided to continue even if the response
was already committed: exception handlers will have a chance to be
called, even if it means they'll have to operate on a garbled response.
This change will cause less disruption, in case existing exception
handlers were relying on this behavior.

See gh-31104
2023-08-30 19:41:46 +02:00
rstoyanchev 32f128b6ba Add create static factory method to WebClientAdapter
Closes gh-31120
2023-08-30 16:37:28 +01:00
Alec Musasa dc7179c528 Fix typo in reference documentation intro
Closes gh-31135
2023-08-30 14:39:42 +02:00
Brian Clozel da1d00be7a Fix dependency management in framework-docs module
See gh-31049
2023-08-30 13:19:11 +02:00
Brian Clozel c93ae250f9 Stop publishing distribution zip artifact
Prior to this commit, the Spring Framework build would publish several
zip artifacts:

* a "*-schema.zip" containing all the XSD schemas produced
* a "*-docs.zip" containing the API docs
* a "*-dist.zip" containing all of the above, plus module jars

Since the reference docs are now produced by Antora in a separate
process, the "*-docs.zip" does not contain the reference docs anymore.
But it still contains the API docs which are automatically fetched from
the artifact repository and published on the docs.spring.io website.

This commit intends to update the current arrangement and optimize the
build.

First, the "*-dist.zip" is not published anymore, since it cannot be
consumed anyway by the community: repo.spring.io does not distribute
release artifacts publicly, developers are expected to get them from
Maven Central. This arrangement is quite dated anyway and is not really
useful in current application build setups.

The generation of API docs is moved to a new "framework-api" module,
separating it from the reference docs module ("framework-docs") which
contains Java, Kotlin and Asciidoctor sources. This removes the custom
javadoc aggregation task and instead uses a dedicated Gradle plugin.

This change also adds a new `-PskipDocs` Gradle project property that
skips entirely the documentation tasks (javadoc, kdocs) as well as the
"distrbution" tasks managed in the framework-api module.
This allows developers to publish locally a SNAPSHOT of Spring Framework
without creating the entire documentation distribution. This is
particularly useful for local testing.

For example, `$ ./gradlew pTML -PskipDocs`.

Closes gh-31049
2023-08-30 12:15:23 +02:00
Brian Clozel 9133e61687 Polish
See gh-31104
2023-08-29 16:55:38 +02:00
lihan 1d7cbfa415 Attempt to reset Servlet response before calling ExceptionHandlers
Prior to this commit, the `ExceptionHandlerExceptionResolver` would
resolve exceptions and handle them by writing to the HTTP response body,
even if the request was already partially handled and content was
written to the response body.

This could result in HTTP responses with some content for the intended
application response, then other content for the handled exception.
This would happen especially when the error would be raised while
writing to the response (for example when serializing content).

This commit attempts to reset the HTTP response before handling the
exception. This effectively resets the response buffer for the body as
well as response headers. If the response is already committed, the
Servlet container raises an exception and the exception handling is
skipped altogether in order to avoid garbled responses.

Closes gh-31104
2023-08-29 16:50:38 +02:00
Anna Buyevich 376a8989f5 Add more tests to DefaultClientRequestObservationConvention
Closes gh-31125
2023-08-29 14:09:46 +02:00
Sébastien Deleuze 38392233ba Add context function to CoRouterFunctionDsl
This new function allows to customize the CoroutineContext
potentially dynamically based on the incoming
ServerRequest.

Closes gh-27010
2023-08-29 12:59:35 +02:00
Brian Clozel 64ff37f42c Update JDK21 release candidate download URL 2023-08-29 11:28:47 +02:00
Brian Clozel f5a356c9c6 Add ContextPropagatingTaskDecorator
Prior to this commit, `@Async` and `@EventListener` annotated methods
would lose the the logging and observation contexts whenever their
execution was scheduled on a different Thread.

The Context Propagation library supports this use case and can propagate
context values in ThreadLocals, Reactor Context and more.

This commit introduces a new `TaskDecorator` implementation that
leverages the Context Propagation library. When configured on a
`TaskExecutor`, this allows to properly propagate context value through
the execution of the task.

This implementation is completely optional and requires the
"io.micrometer:context-propagation" library on the classpath. Enabling
this feature must be done consciously and sometimes selectively, as
context propagation introduces some overhead.

Closes gh-31130
2023-08-29 11:21:47 +02:00
Sébastien Deleuze d47c7f9552 Propagate CoroutineContext in coRouter filters
Closes gh-26977
2023-08-29 10:40:32 +02:00
Sébastien Deleuze bcf11e8919 Replace Reactive awaitSingle() usages by the Mono variant
Closes gh-31127
2023-08-29 09:49:44 +02:00
Sam Brannen af03fa7657 Reinstate support for javax.* annotations in component indexer
- for consistency with the changes made in e1826d2322

See gh-31090
2023-08-28 18:22:21 +02:00
Sam Brannen bfd918d16c Deprecate convention-based @Component stereotype names in favor of @AliasFor
When use of the deprecated feature is detected, a WARNING log message
will be generated analogous to the following.

WARN o.s.c.a.AnnotationBeanNameGenerator - Support for convention-based
stereotype names is deprecated and will be removed in a future version
of the framework. Please annotate the 'value' attribute in
@org.springframework.context.annotation.AnnotationBeanNameGeneratorTests$ConventionBasedComponent1
with @AliasFor(annotation=Component.class) to declare an explicit alias
for @Component's 'value' attribute.

See gh-31089
Closes gh-31093
2023-08-28 18:02:15 +02:00
Stephane Nicoll 2d377155ab Merge pull request #26725 from Jinhui-Z
* pr/26725:
  Polish "Introduce reverse on ClassFilter and MethodFilter"
  Introduce reverse on ClassFilter and MethodFilter

Closes gh-26725
2023-08-28 15:07:10 +02:00
Stephane Nicoll 1f6fd16dae Polish "Introduce reverse on ClassFilter and MethodFilter"
See gh-26725
2023-08-28 15:05:27 +02:00
Jinhui-Z 13e62d0b8e Introduce reverse on ClassFilter and MethodFilter
See gh-26725
2023-08-28 15:05:27 +02:00
Sam Brannen c91841883c Upgrade to Checkstyle 10.12.3 2023-08-28 14:52:49 +02:00
Sam Brannen 9e4b95ef3c Upgrade to spring-javaformat-checkstyle 0.0.39 2023-08-28 14:50:28 +02:00
Sam Brannen 9b44413f2d Merge branch '6.0.x' 2023-08-28 14:48:45 +02:00
Arjen Poutsma 76c032cc11 Schedule blocking ResourceHttpMessageWriter operations on bounded elastic
This commit schedules blocking I/O operations on the bounded elastic
scheduler, which includes retrieving the content length and writing
the resource (region).

Closes gh-30928
2023-08-28 12:54:52 +02:00
Sébastien Deleuze 3ccbe3d1cb Refine documentation contribution
Closes gh-28758
2023-08-28 12:14:35 +02:00
Nheyll 411b355f2c Document buffering with a WebClient filter
See gh-28758
2023-08-28 12:14:28 +02:00
Sébastien Deleuze 499f57ebc5 Fix formatting errors 2023-08-28 10:12:45 +02:00
Sébastien Deleuze 349674ef5e Adapt tests for Jackson ParameterNamesModule
Closes gh-27511
2023-08-28 09:39:53 +02:00
Sam Kruglov 2f43f77dd1 Add ParameterNamesModule to "well known" jackson modules
See gh-27511
2023-08-28 09:36:36 +02:00
Sam Brannen f054c2e804 Polishing 2023-08-27 19:29:21 +02:00
Sam Brannen 95453a4427 Merge branch '6.0.x' 2023-08-27 19:03:16 +02:00
Sam Brannen 4e2d9252e5 Allow component name to be specified in @[Rest]ControllerAdvice
This commit builds on the recently added support for using @AliasFor to
override the `value` attribute in `@Component, and allows a custom
component name to be specified in both @ControllerAdvice and
@RestControllerAdvice via new `name` attributes.

See gh-31089
Closes gh-21108
2023-08-27 18:06:08 +02:00
Stephane Nicoll e239753a1b Merge branch '6.0.x' 2023-08-27 18:03:59 +02:00
Sam Brannen ff104b6de0 Look up @Component stereotype names using @AliasFor semantics
Although gh-20615 introduced the use of @AliasFor for @Component(value) in the built-in
stereotype annotations (@Service, @Controller, @Repository, @Configuration, and
@RestController), prior to this commit the framework did not actually rely on @AliasFor
support when looking up a component name via stereotype annotations. Rather, the
framework had custom annotation parsing logic in
AnnotationBeanNameGenerator#determineBeanNameFromAnnotation() which effectively ignored
explicit annotation attribute overrides configured via @AliasFor.

This commit revises AnnotationBeanNameGenerator#determineBeanNameFromAnnotation() so that
it first looks up @Component stereotype names using @AliasFor semantics before falling
back to the "convention-based" component name lookup strategy.

Consequently, the name of the annotation attribute that is used to specify the bean name
is no longer required to be `value`, and custom stereotype annotations can now declare an
attribute with a different name (such as `name`) and annotate that attribute with
`@AliasFor(annotation = Component.class, attribute = "value")`.

Closes gh-31089
2023-08-27 17:17:52 +02:00
Sam Brannen d189e169cc Polish CompositePropertySource[Tests] 2023-08-27 14:08:39 +02:00
Stephane Nicoll 7c508a4cd3 Merge pull request #27670 from asgh
* pr/27670:
  Use Arrays.deepToString for toString in SimpleKey

Closes gh-27670
2023-08-26 18:14:35 +02:00
asgh 9d7bd9520c Use Arrays.deepToString for toString in SimpleKey
See gh-27670
2023-08-26 18:10:42 +02:00
Stephane Nicoll ce4a5b1f06 Merge pull request #27236 from Shawyeok
* pr/27236:
  Polish "Improve performance of CompositePropertySource#getPropertyNames"
  Improve performance of CompositePropertySource#getPropertyNames

Closes gh-27236
2023-08-26 17:49:32 +02:00
Stephane Nicoll 5c691960a2 Polish "Improve performance of CompositePropertySource#getPropertyNames"
See gh-27236
2023-08-26 17:46:19 +02:00
shawyeok b67a381fbe Improve performance of CompositePropertySource#getPropertyNames
Create LinkedHashSet with a initialCapacity, prevent under the hood
table resize cost in continuous add operations. Reduce bootstrap time
in the case of large properties.

See gh-27236
2023-08-26 17:46:19 +02:00
Sam Brannen e1826d2322 Reinstate support for @javax.annotation.ManagedBean & @javax.inject.Named
This commit reinstates support for the legacy JSR-250
@javax.annotation.ManagedBean and JSR-330 @javax.inject.Named
annotations with regard to component name lookups and component
scanning.

Closes gh-31090
2023-08-26 17:16:00 +02:00
Sam Brannen aaa0a2be63 Test status quo for @Components with multiple declared names 2023-08-26 17:15:47 +02:00
Sam Brannen 71ba7bc5e0 Polishing 2023-08-26 17:15:39 +02:00
Stephane Nicoll e18f1f5d9c Merge pull request #27154 from Li0n13
* pr/27154:
  Polish "Avoid calling executeBatch() with an empty batch"
  Avoid calling executeBatch() with an empty batch

Closes gh-27154
2023-08-26 17:09:39 +02:00
Stephane Nicoll f9cffcce05 Polish "Avoid calling executeBatch() with an empty batch"
See gh-27154
2023-08-26 17:05:39 +02:00
Li0n13 2d7bf83d8d Avoid calling executeBatch() with an empty batch
See gh-27154
2023-08-26 17:05:20 +02:00
Stephane Nicoll a6b0c75cae Merge branch '6.0.x' 2023-08-26 16:41:21 +02:00
Sam Brannen e0c3b1ae1d Merge branch '6.0.x' 2023-08-26 15:28:50 +02:00
Sam Brannen dac481da43 Test CollectionUtils.unmodifiableMultiValueMap only once
No need to test that feature as a parameterized test.
2023-08-26 14:56:17 +02:00
Sam Brannen 31a07e6fbf User AssertJ assertions 2023-08-26 14:56:16 +02:00
Sam Brannen ef01a078d3 Polishing 2023-08-26 14:55:15 +02:00
Sam Brannen 44167ecea4 Polish BackOff support 2023-08-26 14:44:46 +02:00
Stephane Nicoll 962f4d20b4 Merge pull request #27071 from garyrussell
* pr/27071:
  Polish "Add maxAttempts to ExponentialBackOff"
  Add maxAttempts to ExponentialBackOff

Closes gh-27071
2023-08-26 12:55:10 +02:00
Stephane Nicoll 609580bfb9 Polish "Add maxAttempts to ExponentialBackOff"
See gh-27071
2023-08-26 12:54:24 +02:00
Gary Russell f6a09f3fad Add maxAttempts to ExponentialBackOff
If you wish to stop after a certain number of attempts with an
`ExponentialBackOff` you have to calculate the `maxElapsedTime`
corresponding to the number of attempts.

Add a new property to make it more convenient to stop after a
certain number of attempts.

See gh-27071
2023-08-26 12:45:32 +02:00
Stephane Nicoll 48878619d2 Merge branch '6.0.x' 2023-08-26 10:18:04 +02:00
Stephane Nicoll 16cb296b35 Merge pull request #27335 from marschall
* pr/27335:
  Optimize for single TransactionSynchronization

Closes gh-27335
2023-08-26 09:56:47 +02:00
Philippe Marschall fd18c00e50 Optimize for single TransactionSynchronization
Optimize TransactionSynchronizationManager.getSynchronizations() to
avoid sorting and ArrayList creation in case of a single
TransactionSynchronization.

See gh-27335
2023-08-26 09:52:17 +02:00
Sam Brannen e737e6d9ba Update copyright headers 2023-08-25 17:07:17 +02:00
Sam Brannen 78d8fac05b Polishing 2023-08-25 17:06:57 +02:00
Sam Brannen 3efbbc5994 Merge branch '6.0.x' 2023-08-25 16:56:52 +02:00
Brian Clozel fe2ef2ea6e Upgrade Java versions in CI build 2023-08-25 16:25:24 +02:00
Brian Clozel 35fc2df948 Instrument RestClient for Observability
This commit instruments the new `RestClient` HTTP client for
observability. Since this client is sharing its HTTP infrastructure with
`RestTemplate` and operates on the same request/response types, this
instrumentation reuses the Observation convention and context.
This choice makes sense since one can build a new `RestClient` instance
using a `RestTemplate` instance, effectively reusing the underlying
configuration.

Closes gh-31114
2023-08-25 16:23:04 +02:00
Stephane Nicoll 22fd6df711 Merge pull request #26028 from hzmpay
* pr/26028:
  Update copyright header of changed file
  Use computeIfAbsent in SpelCompiler and AdvisedSupport

Closes gh-26028
2023-08-25 16:17:49 +02:00
Stephane Nicoll 8432f777aa Update copyright header of changed file
See gh-26028
2023-08-25 16:13:27 +02:00
hzmpay cde476f90e Use computeIfAbsent in SpelCompiler and AdvisedSupport
See gh-26028
2023-08-25 16:12:08 +02:00
Stephane Nicoll 178cb429a8 Merge pull request #25478 from wind57
* pr/25478:
  Polish "Use Comparable instead of dedicated implementations"
  Use Comparable instead of dedicated implementations

Closes gh-25478
2023-08-25 14:47:53 +02:00
Stephane Nicoll 49cafe07ed Polish "Use Comparable instead of dedicated implementations"
See gh-25478
2023-08-25 14:44:57 +02:00
Eugene 33454a4007 Use Comparable instead of dedicated implementations
This commit deprecates ComparableComparator and NullSafeComparator in
favor of the available convenient implementation in the JDK.

See gh-25478
2023-08-25 14:41:27 +02:00
Stephane Nicoll d0fc6dd06d Merge pull request #17215 from chenqimiao
* pr/24773:
  Polish "Improve @Autowired method injection on mixed nullability args"
  Improve @Autowired method injection on mixed nullability args

Closes gh-17215
2023-08-25 12:52:40 +02:00
Stephane Nicoll 8efc7a958e Polish "Improve @Autowired method injection on mixed nullability args"
See gh-17215
2023-08-25 12:47:05 +02:00
陈其苗 32c0540424 Improve @Autowired method injection on mixed nullability args
See gh-17215
2023-08-25 12:44:16 +02:00
Sébastien Deleuze c8169e5cad Add Coroutines support to Spring AOP
This commit adds support for Kotlin Coroutines to Spring AOP
by leveraging CoroutinesUtils#invokeSuspendingFunction in
AopUtils#invokeJoinpointUsingReflection to convert it to the
equivalent Publisher return value, like in other parts of Spring
Framework.

That allows method interceptors with Reactive support to process
related return values.

CglibAopProxy#processReturnType and JdkDynamicAopProxy#invoke
take care of the conversion from the Publisher return value
to Kotlin Coroutines.

Reactive transactional and HTTP service interface support
have been refined to leverage those new generic capabilities.

Closes gh-22462
2023-08-25 11:53:18 +02:00
Stephane Nicoll 9b3f4567ee Merge pull request #25160 from midumitrescu
* pr/25160:
  Polish "Add additional tests for MultiValueMap"
  Add additional tests for MultiValueMap

Closes gh-25160
2023-08-25 11:50:10 +02:00
Stephane Nicoll 352c7cd8b3 Polish "Add additional tests for MultiValueMap"
See gh-25160
2023-08-25 11:46:54 +02:00
Mihai Dumitrescu 325edbec7d Add additional tests for MultiValueMap
See gh-25140
See gh-25160
2023-08-25 11:27:52 +02:00
Stephane Nicoll 2a37284c86 Merge pull request #24649 from stsypanov
* pr/24649:
  Polish "Skip non-overridden methods of Object.class"
  Skip non-overridden methods of Object.class

Closes gh-24649
2023-08-24 11:46:00 +02:00
Stephane Nicoll 95c43bb0ae Polish "Skip non-overridden methods of Object.class"
See gh-24649
2023-08-24 11:33:34 +02:00
stsypanov b91179d1b8 Skip non-overridden methods of Object.class
See gh-24649
2023-08-24 11:18:49 +02:00
Sébastien Deleuze 21613eabf1 Properly use Reactor Netty 2 in AbstractHttpHandlerIntegrationTests
Closes gh-31095
2023-08-24 10:46:43 +02:00
Sébastien Deleuze c564f27647 Upgrade to Kotlin Coroutines 1.7.3
Closes gh-31108
2023-08-24 09:17:22 +02:00
Sébastien Deleuze cf0a48692c Upgrade to Kotlin Serialization 1.6.0
Closes gh-31107
2023-08-24 09:17:22 +02:00
Sébastien Deleuze 1f437add0b Upgrade to Kotlin 1.9.10
Closes gh-31106
2023-08-24 09:17:22 +02:00
Stephane Nicoll 86e02a47fb Polish 2023-08-24 09:04:14 +02:00
Stephane Nicoll c809fda4ed Merge pull request #24633 from chenqimiao
* pr/24633:
  Add a unit test for getMostSpecificMethod of ClassUtils

Closes gh-24633
2023-08-23 20:50:49 +02:00
陈其苗 e7071c329a Add a unit test for getMostSpecificMethod of ClassUtils
See gh-24633
2023-08-23 20:45:02 +02:00
Juergen Hoeller 3c5dc19da3 Polishing 2023-08-23 19:09:46 +02:00
Juergen Hoeller ecc0a6d2db Merge branch '6.0.x' 2023-08-23 19:02:44 +02:00
Stephane Nicoll 5878a0741e Polish "Add factory to create a NamedThreadLocal with an initial value"
See gh-24705
2023-08-23 18:58:12 +02:00
陈其苗 e1d0176faa Add factory to create a NamedThreadLocal with an initial value
See gh-24705
2023-08-23 18:58:12 +02:00
Sébastien Deleuze b2a86cc42d Polishing
See gh-24311
2023-08-23 18:56:14 +02:00
Sébastien Deleuze f161bc798e Implement StringToRegexConverter in Java
This commit implements StringToRegexConverter in Java
in order to avoid circular dependencies between Java
and Kotlin codes that can break IDE support, and for
consistency with the rest of the codebase.

See gh-24311
2023-08-23 18:53:32 +02:00
Stephane Nicoll 471e4d2b20 Merge pull request #24311 from valfirst
* pr/24311:
  Polish "Add support for converting String to Pattern"
  Add support for converting String to Pattern

Closes gh-24311
2023-08-23 18:13:54 +02:00
Stephane Nicoll a20a74817e Polish "Add support for converting String to Pattern"
See gh-24311
2023-08-23 18:13:41 +02:00
Valery Yatsynovich 06f6b45248 Add support for converting String to Pattern
See gh-24311
2023-08-23 18:13:41 +02:00
Sébastien Deleuze bb14dfa747 Merge branch '6.0.x' 2023-08-23 18:11:14 +02:00
Stephane Nicoll 8695fadbc0 Merge pull request #23692 from ttddyy
* pr/23692:
  Polish "Add a composite for TaskDecorator"
  Add a composite for TaskDecorator

Closes gh-23692
2023-08-23 16:05:01 +02:00
Stephane Nicoll 6e141cc23a Polish "Add a composite for TaskDecorator"
See gh-23692
2023-08-23 16:02:54 +02:00
Tadaya Tsuyukubo 5f581a9a68 Add a composite for TaskDecorator
See gh-23692
2023-08-23 16:02:41 +02:00
Stephane Nicoll 8f6d24efde Merge pull request #23914 from chenqimiao
* pr/23914:
  Polish "Rename earlyProxyReferences in AbstractAutoProxyCreator"
  Rename earlyProxyReferences in AbstractAutoProxyCreator

Closes gh-23914
2023-08-23 15:35:38 +02:00
Stephane Nicoll 5d5cd17d33 Polish "Rename earlyProxyReferences in AbstractAutoProxyCreator"
See gh-23914
2023-08-23 15:34:22 +02:00
陈其苗 67e3b1b531 Rename earlyProxyReferences in AbstractAutoProxyCreator
See gh-23914
2023-08-23 15:33:33 +02:00
rstoyanchev 35ba53e918 Unwrap validator if necessary in HandlerMethodValidator
See gh-31082
2023-08-23 14:55:59 +03:00
rstoyanchev 942800e8ec Refactor MethodValidationAdapter constructors
See gh-31082
2023-08-23 14:55:59 +03:00
rstoyanchev ff069de6d7 Expose unwrap method on SmartValidator
See gh-31082
2023-08-23 14:55:59 +03:00
Sébastien Deleuze c6c61d6485 Refine "checkpoint on refresh" error handling
Closes gh-31096
2023-08-23 12:33:13 +02:00
Stephane Nicoll bf2cdcb1f2 Merge branch '6.0.x' 2023-08-23 10:47:32 +02:00
Stephane Nicoll 74175c1117 Merge pull request #30709 from kilink
* pr/30709:
  Polish "Get content as String for ContentCachingRequestWrapper"
  Get content as String for ContentCachingRequestWrapper

Closes gh-30709
2023-08-22 19:13:55 +02:00
Stephane Nicoll 4695bd332a Polish "Get content as String for ContentCachingRequestWrapper"
See gh-30709
2023-08-22 19:11:26 +02:00
Patrick Strawderman d9b8826142 Get content as String for ContentCachingRequestWrapper
This commits adds a getContentAsString method to
ContentCachingRequestWrapper that uses the configured charset without
copying the underlying byte array.

See gh-30709
2023-08-22 18:51:39 +02:00
Stephane Nicoll d529eee889 Merge branch '6.0.x' 2023-08-22 17:02:00 +02:00
Stephane Nicoll 26485b0e7d Merge branch '6.0.x' 2023-08-22 16:57:37 +02:00
Stephane Nicoll 18813de69f Merge pull request #1822 from x-x-z
* pr/1822:
  Polish "Add Basic Authorization for UrlResource"
  Add Basic Authorization for UrlResource

Closes gh-1822
2023-08-22 16:43:37 +02:00
Stephane Nicoll f95a1f49df Polish "Add Basic Authorization for UrlResource"
See gh-1822
2023-08-22 16:38:00 +02:00
Denis Kostin ac9ca412c8 Add Basic Authorization for UrlResource
See gh-1822
2023-08-22 16:12:31 +02:00
Stephane Nicoll 9c175608e5 Merge pull request #31076 from 70825
* pr/31076:
  Polish "Wrap ternary operator within parentheses"
  Wrap ternary operator within parentheses

Closes gh-31076
2023-08-22 15:55:53 +02:00
Stephane Nicoll 2b76c4d847 Polish "Wrap ternary operator within parentheses"
See gh-31076
2023-08-22 15:40:16 +02:00
70825 6712c044b1 Wrap ternary operator within parentheses
See gh-31076
2023-08-22 15:15:05 +02:00
Sam Brannen a55b50b512 Simplify implementation of AnnotationMetadata.getMetaAnnotationTypes()
Since an annotation cannot be extended in Java, there is no need to use
the INHERITED_ANNOTATIONS SearchStrategy to search for meta-annotations
on an annotation.
2023-08-22 14:58:19 +02:00
Sam Brannen 2935ff8f97 Fix Javadoc for AnnotationMetadata.getMetaAnnotationTypes() 2023-08-22 14:50:58 +02:00
Sam Brannen aedd909ef6 Test status quo for component name lookups for Jakarta annotations 2023-08-22 11:49:08 +02:00
Sam Brannen d8523cb033 Polishing 2023-08-22 11:49:08 +02:00
Sam Brannen 5e171d3cf5 Polish reference documentation for JdbcClient 2023-08-21 16:51:14 +02:00
Sam Brannen 5f893cadc5 Merge branch '6.0.x' 2023-08-21 16:03:52 +02:00
Toshiaki Maki 8f7d61650d Add @Nullable to value in param() methods in JdbcClient
Closes gh-31084
2023-08-21 15:50:38 +02:00
Juergen Hoeller c2bdc23b5e Merge branch '6.0.x' 2023-08-21 15:45:22 +02:00
Juergen Hoeller 26da0e49e4 Show example for CaffeineCacheManager async cache setup
See gh-28250
2023-08-21 15:39:02 +02:00
Sam Brannen 583d92e69a Merge branch '6.0.x' 2023-08-21 15:19:27 +02:00
Sam Brannen 758dddce3b Merge branch '6.0.x' 2023-08-21 14:18:38 +02:00
Juergen Hoeller 430cc2edc0 Merge branch '6.0.x' 2023-08-21 12:38:43 +02:00
Sam Brannen 1e3099759e Polishing 2023-08-20 19:32:07 +02:00
Harry Yang 0e0c298dcf Optimize InlineMap and InlineList in SpEL
- Make InlineList#constant and InlineMap#constant final.

- Add more assertions for InlineMap tests.

Closes gh-30251
2023-08-20 17:52:27 +02:00
Sam Brannen baf367831f Add @AliasFor tip to Javadoc for @TestPropertySource 2023-08-19 15:40:31 +02:00
Sam Brannen 8f71bea553 Avoid unnecessary synchronization in SequencedProperties 2023-08-19 15:32:37 +02:00
Sam Brannen 94d9a71bb5 Polishing 2023-08-19 15:21:55 +02:00
Sébastien Deleuze 11abd8b04f Merge branch '6.0.x' 2023-08-18 17:25:50 +02:00
Sam Brannen 74130d007b Ensure direct @PropertySource annotations override meta-annotations
Prior to this commit, there was an issue with the semantics of property
source overrides. Specifically, a @PropertySource annotation present as
a meta-annotation on a @Configuration class was registered with higher
precedence than a @PropertySource annotation declared closer to (or
directly on) the @Configuration class. Consequently, there was no way
for a "local" @PropertySource annotation to override properties
registered via @PropertySource as a meta-annotation.

This commit addresses this issue by introducing a new overloaded
getMergedRepeatableAnnotationAttributes() variant in
AnnotatedTypeMetadata that allows the caller to supply a
sortByReversedMetaDistance flag. When set to `true`, the annotation
search results will be sorted in reversed order based on each
annotation's meta distance, which effectively orders meta-annotations
before annotations that are declared directly on the underlying element.

ConfigurationClassParser and AnnotationConfigUtils have been updated to
use this new repeatable annotation search method for @PropertySource.

Closes gh-31074
2023-08-18 16:43:44 +02:00
Sam Brannen 285c92bb03 Store reversedMetaDistance Comparator in static field 2023-08-18 16:33:51 +02:00
Sam Brannen ee6998ba52 Polishing 2023-08-18 16:21:50 +02:00
Stephane Nicoll 9583d18896 Merge branch '6.0.x' 2023-08-18 15:39:49 +02:00
Sébastien Deleuze 334b1548b7 Merge branch '6.0.x'
Adapt gh-31045 fix to the main branch, and throw a
WebExchangeBindException instead of a ServerWebInputException.
2023-08-18 15:11:16 +02:00
Sam Brannen 3dcac0cf80 Clean up / Suppress warnings 2023-08-18 12:46:17 +02:00
Sam Brannen ca118ca4a0 Introduce overloaded methods in JdbcTestUtils that accept JdbcClient
Closes gh-31066
2023-08-18 12:20:57 +02:00
Sam Brannen e0fb777325 Introduce var-args params() method in JdbcClient's StatementSpec
Closes gh-31070
2023-08-18 12:00:28 +02:00
Sam Brannen 48e94f535c Polish JdbcClient 2023-08-18 12:00:28 +02:00
Sam Brannen 77067d0e6b Revise JdbcTestUtils method signatures to accept JdbcOperations
Closes gh-31065
2023-08-18 12:00:28 +02:00
Juergen Hoeller 1a137c2e61 Merge branch '6.0.x'
# Conflicts:
#	spring-core/src/main/java/org/springframework/util/StringUtils.java
2023-08-18 11:27:03 +02:00
Sam Brannen f43f1e12f1 Merge branch '6.0.x' 2023-08-17 17:59:30 +02:00
Arjen Poutsma 34477b4f03 Schedule FreeMarker template lookup on bounded elastic scheduler
This commit makes sure that FreeMarker template lookups, which
potentially block, are scheduled on the bounded elastic scheduler.

Closes gh-30903
2023-08-17 16:42:44 +02:00
Juergen Hoeller d41f546c43 Merge branch '6.0.x' 2023-08-17 10:24:34 +02:00
Juergen Hoeller e685ff0416 Always accept existing explicit definition for same class name
See gh-25952
2023-08-16 18:46:32 +02:00
Juergen Hoeller 4bfbf7d3c8 Merge branch '6.0.x' 2023-08-16 17:56:18 +02:00
Sam Brannen 279e6eb423 Document best practices for inlined properties in @TestPropertySource 2023-08-16 17:27:18 +02:00
Juergen Hoeller eb65939341 Add examples for new bind/map methods on DatabaseClient
See gh-27282
See gh-26021
2023-08-16 17:02:41 +02:00
Juergen Hoeller 3f79b267b1 Merge branch '6.0.x' 2023-08-16 16:55:01 +02:00
Sébastien Deleuze 5641d87ce8 Merge branch '6.0.x' 2023-08-16 16:06:55 +02:00
Sam Brannen 24dd484471 Polish internal SequencedProperties implementation 2023-08-16 14:11:02 +02:00
Sam Brannen b43972bb54 Preserve property ordering for text blocks in @TestPropertySource
See gh-31053
2023-08-16 13:48:25 +02:00
Brian Clozel 17109b2402 Upgrade to Reactor 2023.0.0-M2
Closes gh-31014
2023-08-16 13:02:05 +02:00
Brian Clozel dbfece6c31 Upgrade to Micrometer 1.12.0-M2
Closes gh-30997
2023-08-16 13:01:54 +02:00
Juergen Hoeller 86a101ac2b Merge branch '6.0.x' 2023-08-16 12:48:43 +02:00
Sam Brannen 1f544f113a Support resource patterns in @TestPropertySource locations
Inspired by the recently added support for resource patterns in
@PropertySource locations, this commit adds the same support for
resource locations in @TestPropertySource.

For example, assuming the `config` folder in the classpath contains
only 3 files matching the pattern `file?.properties`,

... the following:

@TestPropertySource("classpath:/config/file1.properties")
@TestPropertySource("classpath:/config/file2.properties")
@TestPropertySource("classpath:/config/file3.properties")

... or:

@TestPropertySource({
    "classpath:/config/file1.properties",
    "classpath:/config/file2.properties",
    "classpath:/config/file3.properties"
})

... can now be replaced by:

@TestPropertySource("classpath*:/config/file?.properties")

See gh-21325
Closes gh-31055
2023-08-16 12:34:20 +02:00
Sam Brannen 3a38bb48b5 Polishing 2023-08-16 12:17:58 +02:00
Sam Brannen 02d3269dbb Polishing 2023-08-16 11:44:55 +02:00
Sam Brannen d81ddcef34 Update documentation regarding repeatable @PropertySource support
See gh-30941
2023-08-16 11:37:12 +02:00
Sam Brannen 701c39a325 Update @PropertySource Javadoc regarding resource patterns
See gh-21325
2023-08-16 11:26:59 +02:00
Juergen Hoeller d77595bf2c Merge branch '6.0.x' 2023-08-16 11:00:58 +02:00
Juergen Hoeller 0b99872704 Support for binding values from a source Map
See gh-27282
2023-08-16 10:55:26 +02:00
Juergen Hoeller b8b3e6d20c Merge branch '6.0.x'
# Conflicts:
#	spring-r2dbc/src/main/java/org/springframework/r2dbc/core/DatabaseClient.java
2023-08-15 23:52:44 +02:00
Juergen Hoeller ae3bc378d6 Support for parameter/result records and beans on DatabaseClient
Includes a revision of BeanProperty/DataClassRowMapper with exclusively constructor-based configuration and without JDBC-inherited legacy settings.

Closes gh-27282
Closes gh-26021
2023-08-15 22:42:45 +02:00
Juergen Hoeller 2ab1c5b387 JdbcClient documentation update
See gh-30931
2023-08-15 22:42:25 +02:00
Sam Brannen a2f52db452 Support text blocks for inlined properties in @TestPropertySource
Prior to this commit, inlined properties could only be supplied as an
array of Strings as follows.

@TestPropertySource(properties = {
    "key1 = value1",
    "key2 = value2"
})

Although a user could supply a text block, it was previously rejected
due to a "single key-value pair per string" check in
TestPropertySourceUtils.convertInlinedPropertiesToMap(String...).

This commit removes that restriction and allows the above example to be
refactored to use a text block as follows.

@TestPropertySource(properties = """
    key1 = value1
    key2 = value2
    """
)

Closes gh-31053
2023-08-15 18:10:01 +02:00
Sam Brannen 8c2a39b5af Simplify Javadoc 2023-08-15 18:10:01 +02:00
Sébastien Deleuze 02d003127f Move ServerWebExchange Kotlin extensions
This commit moves ServerWebExchange Kotlin extensions
where they belong: in the spring-web module with the
org.springframework.web.server package, like
ServerWebExchange itself.

The extensions in the wrong location are deprecated
and semi-automated migration to the new variants is
made possible via @Deprecated + ReplaceWith(...).

Some tests have been added as well.

Closes gh-31046
2023-08-15 18:03:41 +02:00
Juergen Hoeller 57f675c537 Allow @Bean method to override scanned class matching its return type
Closes gh-31052
2023-08-15 13:55:57 +02:00
Juergen Hoeller d89e305c87 Introduce SimplePropertyRowMapper with flexible constructor/property/field mapping
Includes query(Class) method with value and property mapping support on JdbcClient.
JdbcClient's singleColumn/singleValue are declared without a Class parameter now.

Closes gh-26594
See gh-30931
2023-08-15 13:14:52 +02:00
Sam Brannen 443e3d5fa6 Polishing 2023-08-14 19:48:32 +02:00
Juergen Hoeller cf75a09011 Polishing 2023-08-14 19:39:19 +02:00
Juergen Hoeller 45c20e34e4 Merge branch '6.0.x'
# Conflicts:
#	spring-context/src/test/java/org/springframework/context/annotation/PropertySourceAnnotationTests.java
2023-08-14 19:29:14 +02:00
Juergen Hoeller 9c74c25961 Support for resource patterns in @PropertySource locations
Closes gh-21325
2023-08-14 19:16:52 +02:00
Sam Brannen 3228502108 Polish MessageListenerContainerObservationTests 2023-08-14 17:08:56 +02:00
Brian Clozel 94fe1f4c63 Guard for Micrometer Core usage in JMS instrumentation
This commit ensures that the Observability instrumentation for
`@JmsListener` does not assume that micrometer-core is on the classpath
and guards against direct usage.

Closes gh-30335
2023-08-14 16:27:45 +02:00
Juergen Hoeller 20d27e4fb6 Merge branch '6.0.x' 2023-08-14 15:14:58 +02:00
Juergen Hoeller fd5b0e144d Reuse Reactive Streams 1.0.3 FlowAdapters for Flow.Publisher bridge
See gh-31000
2023-08-14 14:25:03 +02:00
Brian Clozel 2c895974b2 Add observability support for JMS
This commit adds observability support for Jakarta JMS support in
spring-jms support. This feature leverages the `JmsInstrumentation`
infrastructure in `io.micrometer:micrometer-core` library.

This instruments the `JmsTemplate` and the `@JmsListener` support to
record observations:

* "jms.message.publish" when the `JmsTemplate` sends a message
* "jms.message.process" when a message is processed by a `@JmsListener`
  annotated method

The observation `Convention` and `Context` implementations are shipped
with "micrometer-core".

Closes gh-30335
2023-08-13 18:24:51 +02:00
Sam Brannen bfeca4a0bf Polish contribution
See gh-30744
2023-08-13 17:03:12 +02:00
Adilson A 4979d8fded Increase code coverage in spring-core serializer package
Closes gh-30744
2023-08-13 16:26:02 +02:00
Sam Brannen f9b6aed6b6 Polishing
... for consistency with StandardAnnotationMetadata.
2023-08-13 16:21:18 +02:00
Sam Brannen 156546ad05 Make AnnotationAttributes.validated field immutable 2023-08-13 13:43:27 +02:00
Sam Brannen 1a05ba3215 Polishing 2023-08-13 12:18:26 +02:00
Sam Brannen 3bda2b7124 Find all @ComponentScan and @PropertySource annotations
Prior to this commit, Spring failed to find multiple composed
@ComponentScan and @PropertySource annotations or multiple
@ComponentScans and @PropertySources container annotations. The reason
was due to lacking support in the AnnotatedTypeMetadata API.

This commit introduces support for finding all @ComponentScan and
@PropertySource annotations by making use of the new
getMergedRepeatableAnnotationAttributes() method in
AnnotatedTypeMetadata.

Closes gh-30941
See gh-31041
2023-08-13 11:33:32 +02:00
Sam Brannen 0b902f32f6 Support finding repeatable annotations in AnnotatedTypeMetadata
AnnotatedTypeMetadata has various methods for finding annotations;
however, prior to this commit it did not provide explicit support for
repeatable annotations.

Although it is possible to craft a search "query" for repeatable
annotations using the MergedAnnotations API via getAnnotations(), that
requires intimate knowledge of the MergedAnnotations API as well as the
structure of repeatable annotations.

Furthermore, the bugs reported in gh-30941 result from the fact that
AnnotationConfigUtils attempts to use the existing functionality in
AnnotatedTypeMetadata to find repeatable annotations without success.

This commit introduces a getMergedRepeatableAnnotationAttributes()
method in AnnotatedTypeMetadata that provides dedicated support for
finding merged repeatable annotation attributes with full @AliasFor
semantics.

Closes gh-31041
2023-08-12 18:49:08 +02:00
Sam Brannen fb6c325cc0 Document @AliasFor support in AnnotatedTypeMetadata.getAnnotationAttributes()
The Javadoc for getAnnotationAttributes() states that it supports
"attribute overrides on composed annotations"; however, it actually
supports @AliasFor in general, including attribute aliases within a
given annotation.

This commit updates the Javadoc and corresponding tests to reflect that.

Closes gh-31042
2023-08-12 18:09:37 +02:00
Sam Brannen c52bfc0586 Update test names to reflect status quo
AnnotationMetadataReadingVisitor was replaced by
SimpleAnnotationMetadata in Spring Framework 5.2.
2023-08-12 18:09:37 +02:00
Sam Brannen a33b14338f Polishing annotation processing internals 2023-08-12 15:41:48 +02:00
Sam Brannen 2ede74fa9c Merge branch '6.0.x' 2023-08-12 15:14:26 +02:00
Juergen Hoeller 86ca6fee16 Merge branch '6.0.x' 2023-08-12 14:52:03 +02:00
Juergen Hoeller 9ede1d07a0 Revise multiple beans resolution for custom collection types
Closes gh-30022
2023-08-12 12:48:14 +02:00
Juergen Hoeller c65b0a199e Merge branch '6.0.x'
# Conflicts:
#	spring-tx/src/main/java/org/springframework/transaction/event/TransactionalApplicationListenerMethodAdapter.java
2023-08-12 11:35:24 +02:00
Sébastien Deleuze 1e73439955 Support Kotlin value class properties in SpEL
This commit adds support for inlined Kotlin value
class properties in SpEL by leveraging Kotlin
reflection instead of Java reflection broken
by the mangled method name.

Closes gh-30468
2023-08-11 16:54:34 +02:00
Sébastien Deleuze 566621f7e3 Merge branch '6.0.x' 2023-08-10 19:13:19 +02:00
Juergen Hoeller f516431260 Merge branch '6.0.x' 2023-08-09 23:54:30 +02:00
Juergen Hoeller b4c61f20e7 Mention JdbcClient in package documentation
Closes gh-31017
2023-08-09 23:18:55 +02:00
Sébastien Deleuze 0c477f14cc Polishing
See gh-28638
2023-08-09 11:14:42 +02:00
Sébastien Deleuze c38f053905 Add Kotlin value classes tests with primitive types
This commit adds more Kotlin value classes tests with
primitive types to ensure testing unwrapped use case.

See gh-28638
2023-08-09 11:12:56 +02:00
Sébastien Deleuze 88c2a25f12 Add support for Kotlin value classes in BeanUtils
This commit adds support for Kotlin value classes annotated
with @JvmInline to BeanUtils#findPrimaryConstructor.

This is only the first step, more refinements are expected
to be needed to achieve a comprehensive support of Kotlin
values classes in Spring Framework.

Closes gh-28638
2023-08-09 10:31:49 +02:00
Juergen Hoeller dd76ed7a0a Merge branch '6.0.x'
# Conflicts:
#	spring-core/src/main/java/org/springframework/core/ReactiveAdapterRegistry.java
#	spring-core/src/main/java/org/springframework/util/StopWatch.java
2023-08-08 20:12:08 +02:00
Sam Brannen 837e8960c2 Make addPropertySourcesToEnvironment() public
This commit makes the recently introduced
addPropertySourcesToEnvironment(ConfigurableEnvironment, ResourceLoader,
List<PropertySourceDescriptor>) method in TestPropertySourceUtils public
so that it can be used by third parties (for example, Spring Boot).

Closes gh-30981
2023-08-08 11:47:15 +03:00
Stephane Nicoll 8a6c52b018 Merge pull request #30904 from izeye
* pr/30904:
  Polish AbstractReflectionParameterNameDiscovererKotlinTests

Closes gh-30904
2023-08-08 10:26:30 +02:00
Johnny Lim 7f561fb53d Polish AbstractReflectionParameterNameDiscovererKotlinTests
See gh-30904
2023-08-08 10:23:07 +02:00
Juergen Hoeller c4896aca9b Defensively register Flow bridge in delegate class
See gh-31000
2023-08-08 09:52:42 +02:00
Juergen Hoeller 3b093754c8 Rename getLastTaskInfo to lastTaskInfo (aligned with currentTaskName)
See gh-25803
2023-08-08 09:41:00 +02:00
Juergen Hoeller 8e16e5ea35 Revise StopWatch for flexible time units in nanosecond precision
Closes gh-25803
2023-08-08 01:48:58 +02:00
Juergen Hoeller dedb58f7ed Defensively register default adapters behind Reactive Streams guard
See gh-31000
2023-08-07 20:48:10 +02:00
Sam Brannen f4b5738869 Polishing 2023-08-07 17:51:37 +03:00
xumengqi 21a007bb15 Fix misspelling of applicationListenerDectector variable
Closes gh-31006
2023-08-07 16:14:55 +02:00
Juergen Hoeller d3d414c3c7 Reject @Bean method with void return type
Closes gh-31007
2023-08-07 15:00:08 +02:00
Juergen Hoeller 3c34e69cc2 Merge branch '6.0.x' 2023-08-07 14:53:01 +02:00
Sam Brannen 526fc391ee Use Class#componentType() for consistency with arrayType()
Java 12 introduced java.lang.Class#componentType() as a shortcut for
getComponentType().

Since we started using arrayType() in fe5560400c, this commit switches
to componentType() for consistent API usage style.
2023-08-07 12:43:40 +03:00
Sam Brannen 96fd3c10fb Update copyright headers 2023-08-07 12:08:04 +03:00
Sam Brannen fe5560400c Use Class#arrayType() where feasible
Closes gh-31002
2023-08-07 12:07:46 +03:00
Juergen Hoeller 6090eb0b42 Merge branch '6.0.x' 2023-08-06 15:00:42 +02:00
Juergen Hoeller 3804b1c602 Merge branch '6.0.x'
# Conflicts:
#	spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java
#	spring-core/src/main/java/org/springframework/core/ReactiveAdapterRegistry.java
#	spring-r2dbc/src/test/java/org/springframework/r2dbc/connection/R2dbcTransactionManagerUnitTests.java
2023-08-06 14:26:55 +02:00
Juergen Hoeller 40b33bca59 Compatibility with Flow-based SmallRye Mutiny 2 at runtime
Includes simple Flow.Publisher bridge without Reactor.

Closes gh-31000
2023-08-06 14:04:24 +02:00
Juergen Hoeller 3253d2de89 Introduce TransactionExecutionListener with begin/commit/rollback notifications
Includes ConfigurableTransactionManager interface for listener registration.
Includes additional introspection methods on TransactionExecution interface.
Includes default method declarations on TransactionStatus/SmartTransactionObject.

Closes gh-27479
2023-08-06 14:03:44 +02:00
Juergen Hoeller eaf54b54c3 Detect illegal bean definition override during classpath scanning
Closes gh-25952
2023-08-06 14:03:29 +02:00
Juergen Hoeller c596ff5c38 Log warn message with specific guidance in BeanPostProcessorChecker
Closes gh-24092
2023-08-06 14:03:12 +02:00
Juergen Hoeller cc90a956f7 Reject invalid afterThrowing signature on ThrowsAdvice
Closes gh-1896
2023-08-06 14:02:57 +02:00
ali dandach 6dbd684279 Optimize collection usage in CGLIB fork
Closes gh-30993
2023-08-05 16:18:09 +03:00
Sam Brannen faf3c7831f Add support for property file encoding in @TestPropertySource
Prior to this commit, property files configured (or inferred) via
@TestPropertySource were read using the default encoding of the JVM.

This commit introduces an `encoding` attribute in @TestPropertySource
which allows developers to specify an explicit encoding for test
property files.

Closes gh-30982
2023-08-05 13:47:46 +03:00
Sam Brannen 59b78cc513 Polishing 2023-08-05 10:49:39 +03:00
Sam Brannen 14911fb32f Merge branch '6.0.x' 2023-08-05 10:28:32 +03:00
rstoyanchev 5115684baf Revise docs for server use of @HttpExchange
Closes gh-30980
2023-08-04 18:21:42 +03:00
rstoyanchev 6630b16771 Polishing
See gh-30980
2023-08-04 18:21:42 +03:00
Olga MaciaszekSharma d1d5b54f12 Support @HttpExchange for server-side handling
See gh-30980
2023-08-04 18:21:42 +03:00
Brian Clozel 646fd3edcc Avoid thread pinning in SseEmitter write operations
This commit changes the `synchronized` usage into a `ReentrantLock`, in
order to guard write operations with a construct that does not pin
virtual threads to the current platform thread on JDK21.

Closes gh-30996
2023-08-04 14:59:15 +02:00
Sam Brannen 9908967954 Merge branch '6.0.x' 2023-08-04 15:55:59 +03:00
Sam Brannen 5fd3456f1e Merge branch '6.0.x' 2023-08-04 15:42:25 +03:00
Sam Brannen 3758e5155c Merge branch '6.0.x' 2023-08-04 15:35:16 +03:00
Sam Brannen 376f13f8ef Update copyright headers 2023-08-04 15:02:41 +03:00
Sam Brannen 04cce0bafd Support custom properties file formats in @TestPropertySource
Spring Framework 4.3 introduced the `PropertySourceFactory` SPI for use
with `@PropertySource` on `@Configuration` classes; however, prior to
this commit there was no mechanism to support custom properties file
formats in `@TestPropertySource` for integration tests.

This commit introduces support for configuring a custom
`PropertySourceFactory` via a new `factory` attribute in
`@TestPropertySource` in order to support custom file formats such as
JSON, YAML, etc.

For example, if you create a YamlPropertySourceFactory, you can use it
in integration tests as follows.

@SpringJUnitConfig
@TestPropertySource(locations = "/test.yaml", factory = YamlPropertySourceFactory.class)
class MyTestClass { /* ... /* }

If a custom factory is not specified, traditional `*.properties` and
`*.xml` based `java.util.Properties` file formats are supported, which
was the existing behavior.

Closes gh-30981
2023-08-04 14:57:22 +03:00
Sam Brannen b80872b762 Polishing 2023-08-04 13:52:14 +03:00
Sébastien Deleuze 8feb8198fe Merge branch '6.0.x' 2023-08-04 11:30:29 +02:00
Brian Clozel c97def0b98 Merge branch '6.0.x' 2023-08-04 10:32:00 +02:00
Juergen Hoeller 4e863c5a75 Merge branch '6.0.x' 2023-08-04 02:40:17 +02:00
Juergen Hoeller a6ff95a69c Restore restrictive getTypeForFactoryBeanFromAttributes check
See gh-29799
See gh-30987
2023-08-04 00:56:01 +02:00
Juergen Hoeller f7c3e6480a Merge branch '6.0.x'
# Conflicts:
#	spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java
2023-08-04 00:49:20 +02:00
Juergen Hoeller d868f58e6e Merge branch '6.0.x'
# Conflicts:
#	spring-beans/src/test/java/org/springframework/beans/factory/annotation/LookupAnnotationTests.java
2023-08-03 18:11:08 +02:00
Juergen Hoeller 84b3335e71 Apply array editor to collection of same element type as well
Closes gh-24845
2023-08-03 17:55:54 +02:00
Juergen Hoeller c3e18bc173 Retain metadata during bean creation even with cacheBeanMetadata=false
Closes gh-23795
Closes gh-25749
2023-08-03 17:55:47 +02:00
Sébastien Deleuze c942c04aa0 Support resource bundle custom file extensions
This commit allows to configure custom file
extensions in ReloadableResourceBundleMessageSource
thanks to a new setFileExtensions setter.

Combined with setPropertiesPersister, it allows
custom implementations supporting any kind of
property file.

Closes gh-18990
2023-08-03 14:29:29 +02:00
rstoyanchev ad80b94e14 Allow BindParam on a field
See gh-30947
2023-08-03 15:11:08 +03:00
rstoyanchev 8513ec7440 Update documentation for data binding improvements
Closes gh-30952
2023-08-02 17:21:33 +03:00
Rossen Stoyanchev 667eb42a63 Polishing
See gh-30952
2023-08-01 07:50:06 +03:00
Sam Brannen bd23798323 Polish Javadoc for PropertySourceDescriptor 2023-08-03 14:33:53 +03:00
Sam Brannen 961084dfe0 Merge branch '6.0.x' 2023-08-03 11:27:37 +03:00
Sam Brannen 3e5aa8d734 Fail on error by default during test AOT processing
Prior to this commit, if an error was encountered during build-time AOT
processing, the error was logged at WARN/DEBUG level, and processing
continued.

With this commit, test AOT processing now fails on error by default. In
addition, the `failOnError` mode can be disabled by setting the
`spring.test.aot.processing.failOnError` Spring/System property to
`false`.

Closes gh-30977
2023-08-03 10:59:46 +03:00
Sam Brannen 1bfcaecc9b Polishing 2023-08-03 10:42:34 +03:00
Stephane Nicoll eed14214b5 Document 'preferredConstructors' attribute escape hatch with AOT
See gh-30917
2023-08-02 16:40:13 +02:00
Sam Brannen 4b7d1e3a2c Delete duplicate DummyFactory 2023-08-02 11:29:15 +03:00
Sam Brannen 08a99e275e Merge branch '6.0.x' 2023-08-02 11:26:43 +03:00
Sam Brannen 44d14811d3 Suppress deprecation warnings in test fixtures 2023-08-02 10:51:03 +03:00
Sam Brannen 900ee11f3b Merge branch '6.0.x' 2023-08-02 10:35:54 +03:00
Sam Brannen 89b85c81a7 Polishing 2023-08-02 10:21:06 +03:00
Sam Brannen 51aaaae94e Merge branch '6.0.x'
# Conflicts:
#	framework-platform/framework-platform.gradle
2023-08-02 09:56:47 +03:00
Sam Brannen aa10d23de4 Merge branch '6.0.x' 2023-08-02 09:48:42 +03:00
Juergen Hoeller 10610a6f54 Merge branch '6.0.x'
# Conflicts:
#	framework-platform/framework-platform.gradle
2023-08-02 01:42:27 +02:00
Juergen Hoeller 5e26786bab Fix malformed javadoc 2023-08-02 01:13:27 +02:00
Juergen Hoeller 3b1af692cc Merge branch '6.0.x'
# Conflicts:
#	spring-beans/spring-beans.gradle
#	spring-context/spring-context.gradle
#	spring-orm/spring-orm.gradle
#	spring-test/spring-test.gradle
#	spring-web/spring-web.gradle
#	spring-webflux/spring-webflux.gradle
2023-08-02 01:04:31 +02:00
Juergen Hoeller b9ae996dfc Merge branch '6.0.x'
# Conflicts:
#	spring-context/spring-context.gradle
#	spring-context/src/main/java/org/springframework/context/event/SimpleApplicationEventMulticaster.java
#	spring-context/src/test/java/org/springframework/scheduling/annotation/EnableSchedulingTests.java
#	spring-orm/src/main/java/org/springframework/orm/hibernate5/SpringBeanContainer.java
#	spring-test/spring-test.gradle
#	spring-webmvc/spring-webmvc.gradle
2023-08-02 00:16:55 +02:00
Juergen Hoeller 2d50b758c4 Deprecate applyBeanPostProcessorsBefore/AfterInitialization
Closes gh-30974
2023-08-01 23:28:06 +02:00
Juergen Hoeller 450cc212a2 Support for transactional listeners with reactive transactions
TransactionalApplicationListener and TransactionalEventListener automatically detect a reactive TransactionContext as the event source and register the synchronization accordingly. TransactionalEventPublisher is a convenient delegate for publishing corresponding events with the current TransactionContext as event source. This can also serve as a guideline for similar reactive event purposes.

Closes gh-27515
Closes gh-21025
Closes gh-30244
2023-08-01 23:27:38 +02:00
Juergen Hoeller a9d100eeee Support for always executing specific listeners in original thread
See gh-30244
2023-08-01 23:26:35 +02:00
Sam Brannen dde8f4489f Polish MappedInterceptor 2023-08-01 16:19:26 +03:00
Sam Brannen f9f7a7cd78 Polish contribution
See gh-30971
2023-08-01 16:19:26 +03:00
graceyu 6de95a2b37 Provide methods for obtaining include/exclude patterns in MappedInterceptor
Prior to this commit, MappedInterceptor had a getPathPatterns() method
that returned the include patterns.

This commit introduces getIncludePathPatterns() (which effectively
replaces getPathPatterns()) and getExcludePathPatterns(). In addition,
this commit deprecates getPathPatterns().

Closes gh-30971
2023-08-01 15:57:43 +03:00
Sam Brannen f00756bc7c Update copyright headers 2023-08-01 14:55:04 +03:00
Sam Brannen b00d120514 Merge branch '6.0.x' 2023-08-01 14:54:41 +03:00
Sam Brannen 2dc4eea62f Remove native image hints for TransactionDefinition constants
Closes gh-30854
2023-08-01 14:46:07 +03:00
Sam Brannen db8fa4d505 Polish DataSize 2023-08-01 11:20:40 +03:00
Sam Brannen 525621c4d8 Polish contribution
See gh-30710
2023-08-01 11:19:54 +03:00
Patrick Strawderman 01e90bbd0e Use Long.parseLong(CharSequence,...) to avoid intermediate String creation
Where possible, switch to the Long.parseLong variant that accepts a
start and end index for the supplied CharSequence, thus avoiding making
unnecessary copies of the String input.

Closes gh-30710
2023-08-01 10:53:30 +03:00
Sam Brannen af1c06917d Upgrade to JUnit 5.10
Closes gh-30970
2023-08-01 10:28:50 +03:00
Sam Brannen bcff7d74cd Replace map entry traversal with switch expression
See gh-30851
2023-07-31 16:23:47 +03:00
Sam Brannen 170d6bfdad Clean up warnings in tests 2023-07-31 16:17:48 +03:00
Sam Brannen 2f7046f572 Deprecate org.springframework.core.Constants utility
Closes gh-30851
2023-07-31 16:08:47 +03:00
Sam Brannen 3b8dd0a5ac Assert JmsAccessor.setSessionAcknowledgeMode() values
See gh-30851
2023-07-31 16:08:47 +03:00
Sam Brannen 9da318af96 Assert JmsActivationSpecConfig.setAcknowledgeMode() values
See gh-30851
2023-07-31 16:08:47 +03:00
Sam Brannen b45bfcafc2 Assert DefaultMessageListenerContainer.setCacheLevel() values
See gh-30851
2023-07-31 16:08:47 +03:00
Sam Brannen 81181c346a Assert SimpleTriggerFactoryBean.setMisfireInstruction() values
See gh-30851
2023-07-31 16:08:47 +03:00
Sam Brannen 1378cce9fb Assert CronTriggerFactoryBean.setMisfireInstruction() values
See gh-30851
2023-07-31 16:08:47 +03:00
Sam Brannen 12f765c133 Assert XmlBeanDefinitionReader.setValidationMode() values
See gh-30851
2023-07-31 16:08:47 +03:00
Sam Brannen 06c6af9b0d Stop using Constants utility in IsolationLevelDataSourceRouter
See gh-30851
2023-07-31 16:08:47 +03:00
Sam Brannen 181c814e69 Polish IsolationLevelDataSourceRouter 2023-07-31 16:08:47 +03:00
Sam Brannen 0eda44186a Polish tests for configuration constants
See gh-30851
2023-07-31 16:08:47 +03:00
Sam Brannen 48eb477755 Assert LazyConnectionDataSourceProxy.setDefaultTransactionIsolation() values
See gh-30851
2023-07-31 15:11:52 +03:00
rstoyanchev c43d2e2edc Polishing
Closes gh-30959
2023-07-31 14:52:58 +03:00
Olga MaciaszekSharma 0d4010841e Update docs for HTTP interface clients return values
See gh-30959
2023-07-31 14:52:58 +03:00
Sam Brannen cc9278666d Stop using Constants utility in LazyConnectionDataSourceProxy
See gh-30851
2023-07-31 13:56:49 +03:00
Sam Brannen c87925cee7 Polish LazyConnectionDataSourceProxy 2023-07-31 13:56:49 +03:00
rstoyanchev 5b6c127283 Polishing
Closes gh-30936
2023-07-28 12:46:58 +03:00
Olga MaciaszekSharma 4cd9e2e9b0 Support @RSocketExchange for annotated responders
See gh-30936
2023-07-28 04:23:00 +03:00
Juergen Hoeller 376223c87d Merge branch '6.0.x'
# Conflicts:
#	spring-core/src/main/java/org/springframework/core/task/SimpleAsyncTaskExecutor.java
2023-07-27 21:48:42 +02:00
Juergen Hoeller ce80637891 Add option for graceful shutdown (setTaskTerminationTimeout)
See gh-30956
2023-07-27 21:39:58 +02:00
rstoyanchev 78d0dbb519 Ensure handling of 404 errors for static resources
Closes gh-30930
2023-07-27 22:13:10 +03:00
Rossen Stoyanchev 85704c890e Use DispatchExceptionHandler for handler errors
See gh-30930
2023-07-27 22:13:10 +03:00
Juergen Hoeller 7681200ee7 Introduce SimpleAsyncTaskScheduler (extending SimpleAsyncTaskExecutor)
Closes gh-30956
2023-07-26 23:56:59 +02:00
Juergen Hoeller 5e4ed68fdd Merge branch '6.0.x'
# Conflicts:
#	framework-docs/modules/ROOT/pages/data-access/jdbc/core.adoc
2023-07-26 14:16:43 +02:00
Juergen Hoeller 49c4b205e4 Documentation for common JdbcClient query/update usage
See gh-30931
2023-07-26 13:47:52 +02:00
Juergen Hoeller cf1ba98d01 Documentation for Validator invocation via validateObject
See gh-19877
2023-07-26 13:09:26 +02:00
Juergen Hoeller cb4222d2c2 Documentation for caching with CompletableFuture and reactive types
See gh-17559
See gh-17920
2023-07-26 13:07:22 +02:00
Juergen Hoeller 3437e61f98 Merge branch '6.0.x' 2023-07-26 12:08:20 +02:00
rstoyanchev 67e3d86bd8 Support declarativeBinding mode in DataBinder
Closes gh-30948
2023-07-25 18:04:21 +03:00
rstoyanchev 37eaded63d Support BindParam annotation
Allows customizing the name of the request parameter to bind a
constructor parameter to.

Closes gh-30947
2023-07-25 16:15:55 +03:00
Juergen Hoeller ccaccda6ca Polishing 2023-07-25 19:22:02 +02:00
Juergen Hoeller 5ebbb3ff3e Merge branch '6.0.x'
# Conflicts:
#	spring-jdbc/src/main/java/org/springframework/jdbc/core/JdbcOperations.java
#	spring-jdbc/src/main/java/org/springframework/jdbc/core/JdbcTemplate.java
#	spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplate.java
2023-07-25 19:13:33 +02:00
Juergen Hoeller 3c02ab83ed Polishing 2023-07-25 18:11:00 +02:00
Juergen Hoeller 021161ea38 Make bean property method accessible as well (for non-public types)
See gh-19877
2023-07-25 18:10:43 +02:00
Juergen Hoeller 019c34f480 Introduce JdbcClient as a fluent facade for query/update execution
Delegates to JdbcTemplate/NamedParameterJdbcTemplate underneath the covers.
Supports parameter objects/records through SimplePropertySqlParameterSource.

Closes gh-30931
2023-07-25 18:10:30 +02:00
Juergen Hoeller b9ba0fc572 Polishing 2023-07-24 11:26:48 +02:00
Juergen Hoeller 4a319c3c33 Merge branch '6.0.x' 2023-07-24 11:23:04 +02:00
Arjen Poutsma 52c77d89e9 Reverse order of RestClient and RestTemplate adapters in HTTP interface section 2023-07-24 09:46:05 +02:00
Juergen Hoeller 4786e2bf53 Introduce PREFERRED_CONSTRUCTORS_ATTRIBUTE on AbstractBeanDefinition
Closes gh-30917
2023-07-22 16:06:14 +02:00
Juergen Hoeller b53034fe62 Merge branch '6.0.x'
# Conflicts:
#	framework-platform/framework-platform.gradle
2023-07-22 00:33:12 +02:00
Juergen Hoeller ba46d2bf21 Polishing 2023-07-21 23:57:20 +02:00
Juergen Hoeller 27f9473422 Merge branch '6.0.x'
# Conflicts:
#	spring-context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java
2023-07-21 20:42:56 +02:00
Juergen Hoeller f99faac073 Add caching annotation support for CompletableFuture and reactive return values
Includes CompletableFuture-based retrieve operations on Spring's Cache interface.
Includes support for retrieve operations on CaffeineCache and ConcurrentMapCache.
Includes async cache mode option on CaffeineCacheManager.

Closes gh-17559
Closes gh-17920
Closes gh-30122
2023-07-21 20:27:23 +02:00
Arjen Poutsma d65d285378 Cleanup after deprecation of OkHttp3ClientHttpRequestFactory
See gh-30919
2023-07-21 10:07:05 +02:00
Arjen Poutsma efb6abc43f Deprecate OkHttp3ClientHttpRequestFactory
Closes gh-30919
2023-07-20 13:48:52 +02:00
Juergen Hoeller 3d57425dcb Return rejected value from getFieldValue in case of error
See gh-19877
2023-07-19 23:08:42 +02:00
Juergen Hoeller d4caaebab0 Merge branch '6.0.x'
# Conflicts:
#	spring-context/src/main/java/org/springframework/validation/Errors.java
#	spring-context/src/test/java/org/springframework/validation/DataBinderTests.java
2023-07-19 23:01:02 +02:00
Juergen Hoeller 10cb2322e9 Introduce Validator.validateObject(Object) with returned Errors
Includes failOnError method on Errors interface for use with validateObject.
Declares many Errors methods as default methods for lean SimpleErrors class.

Closes gh-19877
2023-07-19 21:56:44 +02:00
Sam Brannen 9571aa1c68 Stop using Constants utility in IsolationLevelDataSourceAdapter
See gh-30851
2023-07-19 18:05:41 +03:00
Sam Brannen 97810c84a2 Stop using Constants utility in AbstractPlatformTransactionManager
See gh-30851
2023-07-19 17:29:36 +03:00
Sam Brannen d0076f5c14 Stop using Constants utility in DefaultTransactionDefinition
See gh-30851
2023-07-19 16:35:15 +03:00
Sam Brannen c110644107 Polish TransactionSupportTests 2023-07-19 14:33:40 +03:00
Sam Brannen 05956d4028 Change MvcSimpleAsyncTaskExecutor.taskExecutorWarning to primitive boolean 2023-07-19 13:21:57 +03:00
Sam Brannen b2ca36f098 Stop using Constants utility in JmsAccessor
See gh-30851
2023-07-19 12:16:26 +03:00
Sam Brannen 15253f3448 Polish JmsAccessor[Tests] 2023-07-19 12:16:14 +03:00
Sam Brannen 4e00c988c1 Stop using Constants utility in JmsActivationSpecConfig
See gh-30851
2023-07-19 11:48:28 +03:00
Brian Clozel c88c9620b3 Upgrade to Java 21 EA 31 in CI build 2023-07-19 09:51:28 +02:00
Juergen Hoeller 2ac55659c8 Merge branch '6.0.x' 2023-07-19 01:26:05 +02:00
Juergen Hoeller 25ea1f4c0f Merge branch '6.0.x'
# Conflicts:
#	spring-context/src/main/java/org/springframework/scheduling/support/CronSequenceGenerator.java
2023-07-19 00:37:06 +02:00
Juergen Hoeller 33862d98ea Merge branch '6.0.x' 2023-07-18 22:03:03 +02:00
Juergen Hoeller c504ac5a47 Merge branch '6.0.x' 2023-07-18 12:57:18 +02:00
rstoyanchev 73c06347be Update WebMvc docs on use of AsyncTaskExecutor
Closes gh-30905
2023-07-18 10:42:27 +01:00
rstoyanchev 4becce1c2b Consolidate default WebMvc executor log warnings
Closes gh-30902
2023-07-18 10:22:45 +01:00
rstoyanchev 8a283e39ff Update DefaultHandlerExceptionResolver logging
Reduce logging for NoResourceFoundException and NoHandlerFoundException.

Closes gh-30884
2023-07-14 15:11:19 +01:00
Juergen Hoeller 0d8a8432d1 Upgrade to ASM master (including early support for Java 22 bytecode)
Closes gh-30845
2023-07-17 21:17:15 +02:00
Sam Brannen 3a8b5111d9 Stop using Constants utility in DefaultMessageListenerContainer
See gh-30851
2023-07-16 19:14:09 +02:00
Sam Brannen b375a061df Polish DefaultMessageListenerContainer[Tests] 2023-07-16 19:13:27 +02:00
Juergen Hoeller 0e9cd2a35f Merge branch '6.0.x' 2023-07-16 16:23:15 +02:00
Sam Brannen d6230824cb Stop using Constants utility in SimpleTriggerFactoryBean
See gh-30851
2023-07-16 15:00:11 +02:00
Sam Brannen edba535c8b Stop using Constants utility in CronTriggerFactoryBean
See gh-30851
2023-07-16 15:00:11 +02:00
Sam Brannen 79cf532c2f Merge branch '6.0.x' 2023-07-16 14:36:09 +02:00
Sam Brannen f6b8ee76cd Merge branch '6.0.x' 2023-07-16 14:05:46 +02:00
Sam Brannen 635cd29599 Remove cross reference to Constants utility
See gh-30851
2023-07-16 13:50:26 +02:00
Sam Brannen 6f733512b5 Stop using Constants utility in XmlBeanDefinitionReader
See gh-30851
2023-07-16 13:29:44 +02:00
Sam Brannen d6e05ddf2d Polish XmlBeanDefinitionReaderTests 2023-07-16 13:22:48 +02:00
Sam Brannen a92bd42236 Stop using Constants utility in PropertyPlaceholderConfigurer
See gh-30851
2023-07-16 12:56:03 +02:00
Sam Brannen cebda46469 Polish PropertyPlaceholderConfigurerTests 2023-07-16 12:56:03 +02:00
Sam Brannen f01fb19318 Stop using Constants utility in CustomizableTraceInterceptor
See gh-30851
2023-07-16 12:56:03 +02:00
Sam Brannen 5e31856aaa Polish CustomizableTraceInterceptor[Tests] 2023-07-16 12:12:10 +02:00
Sam Brannen 1058fed339 Merge branch '6.0.x' 2023-07-15 18:24:00 +02:00
Sam Brannen bcd09d7ad8 Merge branch '6.0.x' 2023-07-15 16:01:47 +02:00
Sam Brannen abc56cde3d Merge branch '6.0.x' 2023-07-15 14:56:47 +02:00
Sam Brannen 889fca98ac Merge branch '6.0.x' 2023-07-15 14:45:52 +02:00
Juergen Hoeller 351a200747 Merge branch '6.0.x' 2023-07-15 14:21:05 +02:00
Sam Brannen 01ceee8de5 Polishing 2023-07-15 13:36:11 +02:00
Sam Brannen 0611192dac Enable KotlinScriptTemplateTests in Spring MVC on Java 20
See gh-29249
Closes gh-30896
2023-07-15 13:36:11 +02:00
Johnny Lim be8bfad3af Enable KotlinScriptTemplateTests in Spring WebFlux on Java 20
See gh-29249
Closes gh-30896
2023-07-15 13:35:59 +02:00
Sam Brannen 63fe45d92a Update copyright headers 2023-07-15 13:11:29 +02:00
Sam Brannen 5ce8ffd197 Merge branch '6.0.x' 2023-07-15 13:11:03 +02:00
Sam Brannen 56c7b4065d Merge branch '6.0.x' 2023-07-15 13:09:12 +02:00
Sam Brannen 680769d770 Remove obsolete private constant 2023-07-15 13:04:50 +02:00
Sam Brannen 70cf754a2f Suppress warnings in tests 2023-07-15 13:02:16 +02:00
Sam Brannen e6d360c1c6 Polishing 2023-07-15 12:58:18 +02:00
Sam Brannen 8629182822 Suppress deprecation warnings in tests 2023-07-15 12:16:38 +02:00
Juergen Hoeller 37ac3d8764 Merge branch '6.0.x' 2023-07-14 16:44:26 +02:00
Stephane Nicoll d61b9c6294 Merge pull request #22769 from lgxbslgx
* pr/22769:
  Polish "Evaluate key only if necessary"
  Evaluate key only if necessary

Closes gh-22769
2023-07-14 14:43:18 +02:00
Stephane Nicoll d8854a2f3f Polish "Evaluate key only if necessary"
See gh-22769
2023-07-14 14:43:04 +02:00
liguoxiong 806c83591c Evaluate key only if necessary
Prior to this commit a @CachePut operation would fail if the key
expression is invalid, but guarded with an unless condition as the
former was evaluated too early. This commit makes sure that key for
a put is only evaluated if the put operation is active.

Note that this does not apply for @Cacheable as the key needs to be
computed early to determine if a matching entry exists in the cache.

See gh-22769
2023-07-14 14:43:04 +02:00
Juergen Hoeller fd17df91fd Merge branch '6.0.x'
# Conflicts:
#	spring-jdbc/src/main/java/org/springframework/jdbc/support/AbstractFallbackSQLExceptionTranslator.java
2023-07-14 14:38:24 +02:00
Juergen Hoeller 519927421e Add support for configurable custom translator
Closes gh-24634
2023-07-14 14:17:20 +02:00
Juergen Hoeller 52c19272c6 Deprecate MBeanExporter's AUTODETECT constants
Closes gh-30874
2023-07-14 14:12:39 +02:00
Juergen Hoeller 0d5a7db238 Remove unused import
See gh-30886
2023-07-14 12:24:55 +02:00
Juergen Hoeller 064cd3b7af Merge branch '6.0.x'
# Conflicts:
#	gradle.properties
#	spring-core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java
2023-07-14 12:18:41 +02:00
Juergen Hoeller 6957ac5324 Trigger late commit in JmsResourceSynchronization.afterCompletion if necessary
Closes gh-20817
2023-07-14 11:50:31 +02:00
Juergen Hoeller d24c131130 Expose convert(Object, TypeDescriptor) in ConversionService interface
Closes gh-25394
2023-07-14 11:49:26 +02:00
Juergen Hoeller 0ab94478fe Add since tags and code style refinements
See gh-27735
2023-07-14 11:48:37 +02:00
René Wittner 4b80b0f490 DataAccessUtils result accessors with Optional return value (#27735) 2023-07-14 11:37:09 +02:00
rstoyanchev 793581ebde Add ForwardedHeaderUtils
Closes gh-30886
2023-07-13 18:12:30 +01:00
Brian Clozel cc7f3101b7 Upgrade to Context Propagation 1.1.0-M3
Closes gh-30882
2023-07-13 11:21:30 +02:00
Arjen Poutsma 49c463b1d2 Polish RestClient request factories
This commit changes the default request factory from the
SimpleClientHttpRequestFactory to the JdkClientHttpRequestFactory if
available. It also adds detection logic for OkHttp and Jetty.
2023-07-13 09:24:21 +02:00
Juergen Hoeller 094eb3e236 Use varargs for array elements
See gh-1850
2023-07-13 00:55:35 +02:00
Juergen Hoeller 3fed2ec3a1 Add common SqlValue implementation for JDBC Array creation
Closes gh-1850
2023-07-13 00:28:32 +02:00
Juergen Hoeller 357d5b4e6e Merge branch '6.0.x' 2023-07-12 19:23:26 +02:00
Juergen Hoeller 4152034799 Move transaction configuration check to transaction.annotation package
AbstractTransactionManagementConfiguration.transactionalEventListenerFactory() creating a RestrictedTransactionalEventListenerFactory now.

See gh-30679
2023-07-12 19:09:48 +02:00
rstoyanchev 3c9cfa8a0f Revise order of method declarations in WebFluxConfigurer
See gh-30678
2023-07-12 17:09:57 +01:00
rstoyanchev b016f385e1 Add BlockingExecutionConfigurer to WebFlux config
Closes gh-30678
2023-07-12 16:55:02 +01:00
Stephane Nicoll f40d1f2329 Merge branch '6.0.x' 2023-07-12 17:23:22 +02:00
Stephane Nicoll f73f1e0687 Merge branch '6.0.x' 2023-07-12 17:22:44 +02:00
Stephane Nicoll 1b09ef5051 Avoid ambiguous call with BeanInstanceSupplier#withGenerator
Previously, BeanInstanceSupplier had three variants of the
`withGenerator` callback, one with a bi function, one with a function,
and with a supplier. This could lead to compilation failure when the
target type has a method with the same name and a number of arguments
that match another variant.

It turns out the supplier-based variant is only used a shortcut. This
commit deprecates it and update ghe code generation to use the function
instead.

Closes gh-29278
2023-07-12 16:40:22 +02:00
Juergen Hoeller c1932dd307 Support for MultiValueMap elements in bean property paths
Closes gh-26297
2023-07-12 16:23:39 +02:00
Sébastien Deleuze 490ff0af5e Refine the log message printed after restoration
Closes gh-30876
2023-07-12 15:20:33 +02:00
Sam Brannen 16b9640af2 Merge branch '6.0.x' 2023-07-12 11:50:11 +02:00
Sam Brannen 2e3fbac9a0 Merge branch '6.0.x' 2023-07-12 10:36:21 +02:00
Sam Brannen 18c11e84f3 Merge branch '6.0.x' 2023-07-12 10:34:43 +02:00
Brian Clozel a2da97bc4c Upgrade to Micrometer 1.12.0-M1
Closes gh-30872
2023-07-12 09:17:09 +02:00
Brian Clozel 2e809903ec Upgrade to Reactor 2023.0.0-M1
Closes gh-30856
2023-07-12 08:56:28 +02:00
Brian Clozel 6075629015 Merge branch '6.0.x' 2023-07-12 08:55:53 +02:00
Juergen Hoeller c20a2e4763 Support for indexing into any Collection/Iterable
Closes gh-907
2023-07-11 23:07:51 +02:00
Juergen Hoeller e048b093b5 ContextClosedEvent triggers early cancelling of scheduled tasks
Closes gh-24629
See gh-27090
2023-07-11 22:11:13 +02:00
rstoyanchev 57ed5bf34b Polishing contribution
Closes gh-30869
2023-07-11 19:13:22 +01:00
Olga MaciaszekSharma 8b77ed164d Add RestClientAdapter
See gh-30869
2023-07-11 19:13:22 +01:00
Sébastien Deleuze 3a8c40fd2f Merge branch '6.0.x' 2023-07-11 19:58:44 +02:00
Juergen Hoeller 3a481a7d7f Merge branch '6.0.x' 2023-07-11 18:02:25 +02:00
rstoyanchev 74972fb751 Add updateModel to BindingContext
The method includes logic that is currently in
ViewResolutionResultHandler but fits well in BindingContext and also
includes the call to saveModel method from the InitBinderBindingContext
subclass, which was called too early until now from
RequestMappingHandlerAdapter before the model has been fully updated.

This mirrors a similar method in ModelFactory on the Spring MVC side
which also combines those two tasks.

Closes gh-30821
2023-07-11 16:34:03 +01:00
Sam Brannen 15b6626a4c Merge branch '6.0.x' 2023-07-11 16:36:32 +02:00
rstoyanchev c61d011076 Support RestClient in MockRestServiceServer
Closes gh-30821
2023-07-11 14:46:02 +01:00
Sam Brannen 8448f597b1 Merge branch '6.0.x' 2023-07-11 15:41:51 +02:00
Sébastien Deleuze 950c43b4e7 Merge branch '6.0.x' 2023-07-11 14:07:34 +02:00
rstoyanchev 39e74d89e1 Merge branch '6.0.x' 2023-07-11 11:17:41 +01:00
Sam Brannen dc4fcef884 Merge branch '6.0.x'
# Conflicts:
#	framework-platform/framework-platform.gradle
2023-07-11 11:08:59 +02:00
Arjen Poutsma d04d7b2e57 Add request binding to functional endpoints
This commit introduces form binding to ServerRequest in both WebMVC.fn
and WebFlux.fn's, in the form of a bind(Class) method.

Closes gh-25943
2023-07-11 09:52:39 +02:00
rstoyanchev a3e37597aa Add ReactiveHttpRequestValues
Separate collection and handling of reactive request values into a
subclass of HttpRequestValues.

Closes gh-30117
2023-07-11 07:42:04 +01:00
rstoyanchev 3209cf5c7a Add Reactor classpath checks in argument resolvers
HTTP interface client argument resolvers for RequestBody and
RequestPart now handle reactive input conditionally.

See gh-30117
2023-07-10 16:03:11 +01:00
Rossen Stoyanchev 22376c2efa Polishing
See gh-30117
2023-07-10 11:24:30 +01:00
rstoyanchev 47667ab990 Collapse hierarchy under HttpServiceMethodTests
See gh-30117
2023-07-10 10:56:32 +01:00
Rossen Stoyanchev 068dc7db28 Remove use of TestHttpClientAdapter
Now that HttpClientAdapter is deprecated and replaced by HttpExchangeAdapter
and ReactorHttpExchangeAdapter, our tests should use the new contracts.

See gh-30117
2023-07-10 10:36:16 +01:00
rstoyanchev 3be4c0a893 Replace Void with void on exchange method
See gh-30117
2023-07-10 09:10:14 +01:00
Olga MaciaszekSharma 268f3c853e Add RestTemplate support for HTTP interface client
See gh-30117
2023-07-06 19:07:40 +02:00
Rossen Stoyanchev bf82ed7186 Add HttpExchangeAdapter
See gh-30117
2023-06-23 11:49:29 +01:00
Eric Haag 1b27a7baab Remove explicit cross project task dependency
Previously, the runtimeHintsTest task configuration made a cross
project task reference to obtain the jar path of spring-core-test
to use in the -javaagent argument.

With this change, a dedicated configuration exists to resolve the
project artifact without the need to reference the jar task of
spring-core-test.

Closes gh-30838
2023-07-10 22:42:37 +02:00
Eric Haag d40c10e00f Evaluate runtime hints agent parameter lazily
Previously, an afterEvaluate call was used to read the values of the
runtimeHintsAgent extension and the -javaagent parameter supplied to
the runtimeHintsTest task was evaluated eagerly.

With this change the construction of the -javaagent parameter is
deferred until task execution and the extension properties are
evaluated lazily.

See gh-30838
2023-07-10 22:42:37 +02:00
Eric Haag 5d022d2a24 Fix build cache miss in runtimeHintsTest task
Prior to this change, a build cache miss would occur in
runtimeHintsTest tasks when executing the build from two different
locations, causing the tasks to re-execute. This was due to an
absolute path to the agent jar being included in the -javaagent
argument supplied to the test JVM process.

This change introduces a RuntimeHintsAgentArgumentProvider allowing
finer grained control over the way Gradle computes the build cache
key. With this change, the contents of the agent jar file are
considered, rather than the absolute file location.

See gh-30838
2023-07-10 22:42:37 +02:00
Johnny Lim 433b72d493 Polish gh-29883 2023-07-10 22:13:12 +02:00
Sam Brannen 7bc731dfa6 Merge branch '6.0.x' 2023-07-10 19:07:09 +02:00
Brian Clozel 5cb6454824 Switch to Reactor 2023.0.0-SNAPSHOT
See gh-30856
2023-07-10 18:13:40 +02:00
Juergen Hoeller 9a68b3e910 Merge branch '6.0.x' 2023-07-10 17:18:16 +02:00
Sébastien Deleuze 38f9cbe3de Use latest version of sockjs-client in AbstractSockJsService
Closes gh-30848
2023-07-10 15:27:53 +02:00
Sébastien Deleuze 76f94e998e Merge branch '6.0.x' 2023-07-09 18:41:58 +02:00
Juergen Hoeller 71bb45c87b Merge branch '6.0.x' 2023-07-09 16:56:45 +02:00
Sam Brannen 0521cdda88 Merge branch '6.0.x' 2023-07-09 15:38:55 +02:00
Juergen Hoeller 6b1fbc9fe1 Revise ResolvableType equality
ResolvableTypes are only considered equal if of the very same class now.
As a consequence, a forRawClass result is not equal to forClass anymore.
The new equalsType method is available for plain type equality checks.

Closes gh-28608
Closes gh-27748
2023-07-09 12:35:37 +02:00
Juergen Hoeller 5243c2262a Support scheduler qualifier for reactive SubscribingRunnable as well
See gh-20818
2023-07-08 17:18:17 +02:00
Juergen Hoeller a0c80ffc06 Destroy local TaskSchedulerRouter which may contain local executor
See gh-20818
2023-07-08 15:55:25 +02:00
Juergen Hoeller a8614531ab Support for determining a target scheduler for a specific task
Introduces "scheduler" attribute on @Scheduled annotation.
TaskSchedulerRouter delegates to qualified/default scheduler.
ScheduledMethodRunnable exposes qualifier through SchedulingAwareRunnable.

Closes gh-20818
2023-07-08 15:37:04 +02:00
Juergen Hoeller f0fe58f0ec Move observation support classes to scheduling.support package
Avoids a package cycle between config and support (only config->support allowed).

See gh-29883
2023-07-08 14:57:02 +02:00
Sébastien Deleuze 8fb412ea74 Merge branch '6.0.x' 2023-07-08 11:11:37 +02:00
Juergen Hoeller bf99361abb Merge branch '6.0.x' 2023-07-08 00:58:58 +02:00
Juergen Hoeller 464b676ec5 Expose shutdown state in TaskRejectedException message
See gh-27090
2023-07-07 23:59:10 +02:00
rstoyanchev ac11b03cd3 Document customization of method validation errors
Closes gh-30653
2023-07-07 21:31:58 +01:00
rstoyanchev df2617d575 Document method validation improvements
Closes gh-30643
2023-07-05 17:07:30 +01:00
Sébastien Deleuze a03a14797f Add AOT support for Kotlin constructors with optional parameters
This commit leverages Kotlin reflection to instantiate classes
with constructors using optional parameters in the code
generated AOT.

Closes gh-29820
2023-07-07 17:40:31 +02:00
Arjen Poutsma 20dd66cd5a Introduce ReactorNettyClientRequestFactory
This commit introduces an implementation of ClientHttpRequestFactory
based on Reactor Netty's HttpClient.

Closes gh-30835
2023-07-07 15:30:53 +02:00
Sam Brannen d720d6be6b Merge branch '6.0.x' 2023-07-07 15:05:01 +02:00
Arjen Poutsma cb0c5f5a7b Allow RestClient::exchange to keep connection open
This commit introduces an overloaded version of RestClient::exchange,
adding a boolean parameter that indicates whether the connection is
closed after the exchange function is executed.

See gh-29552
2023-07-07 14:24:44 +02:00
Arjen Poutsma 8691173fd8 Create RestClient documentation placeholder
This commit creates a placeholder for future RestClient reference
documentation. It also creats a link to RestClient from the RestTemplate
javadoc.

See gh-30826
2023-07-07 14:14:59 +02:00
Juergen Hoeller ddc3cf301a Merge branch '6.0.x'
# Conflicts:
#	spring-context/src/main/java/org/springframework/scheduling/concurrent/ExecutorConfigurationSupport.java
2023-07-07 13:51:08 +02:00
Juergen Hoeller b12115b61f Sophisticated lifecycle support for ThreadPoolTaskExecutor/Scheduler
Pause/resume capability through SmartLifecycle implementation.
ContextClosedEvent triggers early executor/scheduler shutdown.
Programmatic initiateShutdown() option for custom early shutdown.

Closes gh-30831
Closes gh-27090
Closes gh-24497
2023-07-07 13:04:52 +02:00
Sébastien Deleuze 029a69f5c1 Upgrade to Kotlin Coroutines 1.7.2
Closes gh-30833
2023-07-07 11:44:03 +02:00
Sébastien Deleuze 80d1f12324 Upgrade to Kotlin 1.9.0
Closes gh-30719
2023-07-07 11:43:02 +02:00
Sam Brannen e906eac049 Update regarding Antora, etc. 2023-07-07 11:18:43 +02:00
Sam Brannen 32aa9e4b63 Update links 2023-07-07 11:00:53 +02:00
Sam Brannen 7162faaa4d Merge branch '6.0.x' 2023-07-06 17:30:02 +02:00
Sam Brannen 1901b708b2 Merge branch '6.0.x' 2023-07-06 17:21:49 +02:00
Sam Brannen d86750372a Improve Javadoc for @Configuration 2023-07-06 17:13:37 +02:00
Brian Clozel 3a0e5c8894 Merge branch '6.0.x' 2023-07-06 17:12:05 +02:00
Arjen Poutsma cf008eb9b1 Revert "Follow redirects in JdkClientHttpRequestFactory"
This reverts commit 7c37f4bc51.
2023-07-06 15:53:35 +02:00
Sam Brannen 0bf85af8e9 Merge branch '6.0.x'
# Conflicts:
#	framework-docs/modules/ROOT/pages/integration/observability.adoc
#	spring-core/src/test/java/org/springframework/core/convert/converter/DefaultConversionServiceTests.java
2023-07-06 14:11:24 +02:00
Arjen Poutsma 78bb66b0f2 Reduce flushes in FormHttpMessageConverter
Before this commit, each part written by the FormHttpMessageConverter
would typically end with a flush, which reduced performance.
2023-07-06 13:49:57 +02:00
Brian Clozel 246135364c Merge branch '6.0.x' 2023-07-06 13:09:58 +02:00
Sam Brannen 468ef7a618 Merge branch '6.0.x' 2023-07-06 12:27:47 +02:00
Arjen Poutsma 68b5eedde1 Change OutputStreamPublisher default chunk size
This commit set the default chunk size to 1024 (from 8192).
2023-07-06 12:01:24 +02:00
Arjen Poutsma 3d2befc84a Rearrange HttpHeaders adapters
This commit moves HttpHeaders that are used in multiple places (client
and server, reactive and non-reactive) to a new, separate http.support
package.

Closes gh-30823
2023-07-06 11:00:26 +02:00
Brian Clozel 8e9528de13 Merge branch '6.0.x' 2023-07-06 09:40:13 +02:00
Juergen Hoeller 0c8d3e70cf Prefer ArrayList for Collection when source is an array
Closes gh-28048
2023-07-05 20:23:17 +02:00
Juergen Hoeller 47e631d5ff Merge branch '6.0.x' 2023-07-05 20:15:53 +02:00
Sam Brannen e86003b692 Merge branch '6.0.x' 2023-07-05 17:02:35 +02:00
Sam Brannen f6218cadd7 Merge branch '6.0.x' 2023-07-05 15:33:26 +02:00
Sam Brannen 368a917466 Polish RestClient, etc. 2023-07-05 15:22:27 +02:00
Arjen Poutsma a6c5692586 Make OutputStreamPublisher more generic
This commit improves the OutputStreamPublisher so that it is capable
of publishing other types that ByteBuffers.
2023-07-05 15:20:43 +02:00
Sam Brannen d0a2820af4 Polish AnnotatedMethod 2023-07-05 14:46:19 +02:00
Sam Brannen 299b86bae3 Merge branch '6.0.x' 2023-07-05 14:02:49 +02:00
Juergen Hoeller 1218e65ca1 Merge branch '6.0.x' 2023-07-05 12:53:07 +02:00
Arjen Poutsma 496155525c Allow built ServerResponse to throw exception
This commit ensures that ServerResponse.HeadersBuilder::build can throw
an exception, by introducing a separate functional interface that does
allow for exceptions to be thrown.

Closes gh-30818
2023-07-05 12:01:55 +02:00
Arjen Poutsma 7c37f4bc51 Follow redirects in JdkClientHttpRequestFactory
Make sure we follow redirects in the default HttpClient created by
JdkClientHttpRequestFactory, similar toSimpleClientHttpRequestFactory.
2023-07-05 11:08:41 +02:00
Arjen Poutsma 6793edc349 Increase Jetty read timeout to 10 seconds 2023-07-05 10:34:20 +02:00
Juergen Hoeller 1777e7f3b7 Merge branch '6.0.x'
# Conflicts:
#	spring-messaging/src/main/java/org/springframework/messaging/handler/HandlerMethod.java
#	spring-web/src/main/java/org/springframework/web/method/HandlerMethod.java
2023-07-04 21:40:11 +02:00
Juergen Hoeller 6fa09e1783 Extract AnnotatedMethod base class for consistent annotation exposure
As a consequence, the spring-messaging HandlerMethod detects interface parameter annotations as well, and the same is available for other HandlerMethod variants.

Closes gh-30801
2023-07-04 20:42:30 +02:00
rstoyanchev ae23f5a594 Merge branch '6.0.x' 2023-07-04 18:10:19 +01:00
rstoyanchev deaa493644 Add Visitor to HandlerMethodValidationException
Closes gh-30813
2023-07-04 17:19:58 +01:00
Sébastien Deleuze 2a126faae7 Upgrade to Project CRaC 1.3.0
Closes gh-30812
2023-07-04 17:49:00 +02:00
Sébastien Deleuze 35304435d0 Add RestClient Kotlin extensions
Closes gh-30807
2023-07-04 17:33:39 +02:00
Juergen Hoeller 420255373d Merge branch '6.0.x' 2023-07-04 16:53:17 +02:00
Juergen Hoeller 834d22f866 Polishing 2023-07-04 16:08:24 +02:00
Juergen Hoeller 2a77665be7 Merge branch '6.0.x' 2023-07-04 15:59:38 +02:00
Brian Clozel 14857ae0da Upgrade to JDK 21ea29 in CI build 2023-07-04 15:23:51 +02:00
Sam Brannen 7156ea016e Merge branch '6.0.x'
# Conflicts:
#	spring-core/src/main/java/org/springframework/util/ObjectUtils.java
2023-07-04 13:44:19 +02:00
Arjen Poutsma 0820210c7c Introduce RestClient
This commit introduces the RestClient, a synchronous HTTP client that
offers an API similar to WebClient, using the same infrastructure (i.e.
request factory, error handler, interceptors, etc) as RestTemplate.

Closes gh-29552
2023-07-04 10:27:58 +02:00
Sam Brannen 9eb1fbd5c3 Merge branch '6.0.x' 2023-07-03 17:18:27 +02:00
Sam Brannen ea9c217827 Merge branch '6.0.x' 2023-07-03 16:37:35 +02:00
rstoyanchev 1fc020cf92 Fix checkstyle violations 2023-07-03 15:31:35 +01:00
rstoyanchev 592ab0f350 Add ~.validation.method package
Extract classes from ~.validation.beanvalidation without a direct
dependency on beanvalidation.

See gh-30644
2023-07-03 15:05:51 +01:00
Sam Brannen 0da2241367 Merge branch '6.0.x' 2023-07-03 15:54:13 +02:00
Sam Brannen 37e8ef1542 Merge branch '6.0.x' 2023-07-03 15:50:17 +02:00
Sam Brannen e995033811 Polishing 2023-07-03 15:00:02 +02:00
Sam Brannen 4c0329014f Merge branch '6.0.x' 2023-07-03 14:49:23 +02:00
Arjen Poutsma 72301dc861 Add Duration variants for ClientHttpRequestFactory timeouts
This commit adds overloaded variants of timeout setters that take a
Duration instead of a number.
2023-07-03 13:35:26 +02:00
rstoyanchev 84e863f803 Refactoring in MethodValidationAdapter
Extract the default logic for resolving the name of an @Valid
parameter into an ObjectNameResolver, and use it when there isn't
one configured.

See gh-30644
2023-07-03 12:08:11 +01:00
rstoyanchev 7a79da589a Add default web handling of method validation errors
Closes gh-30644
2023-07-03 12:08:11 +01:00
rstoyanchev a481c7649f Refactor to prepare for method validation handling
To handle method validation errors in ResponseEntityExceptionHandler,
MethodValidationException and associated types should not depend on
Bean Validation. To that effect:

1. MethodValidationResult and ParameterValidationResult no longer make
the underlying ConstraintViolation set available, and instead expose
only the adapted validation errors (MessageSourceResolvable, Errors),
analogous to what SpringValidatorAdapter does. And likewise
MethodValidationException no longer extends ConstraintViolationException.

2. MethodValidationPostProcessor has a new property
adaptConstraintViolations to decide whether to simply raise
ConstraintViolationException, or otherwise to adapt the ConstraintViolations
and raise MethodValidationException instead, with the former is the default
for compatibility.

3. As a result, the MethodValidator contract can now expose methods that
return MethodValidationResult, which provided more flexibility for handling,
and it allows MethodValidationAdapter to implement MethodValidator directly.

4. Update Javadoc in method validation classes to reflect this shift, and
use terminology consistent with Spring validation in classes without an
explicit dependency on Bean Validation.

See gh-30644
2023-07-03 12:08:11 +01:00
Rossen Stoyanchev ba4d9a5230 Add BindErrorUtils
This deprecates static methods in MethodArgumentNotValidException
which is not a great vehicle for such methods.

See gh-30644
2023-07-03 12:08:11 +01:00
rstoyanchev e83594a2a3 Polishing in Web validation exceptions
- Update method order
- Do not automatically create MessageSource arguments in
WebExchangeBindException constructor as they're more likely to be
created via getDetailMessageArguments with MessageSource passed in.

See gh-30644
2023-07-03 12:08:11 +01:00
rstoyanchev a8ea472121 Refactoring in MethodValidationResult
Remove throwIfViolationsPresent and replace with static factory
methods on MethodValidationException taking MethodValidationResult,
which makes handling more explicit and allows choice of what
exception to raise.

Update MethodValidationResult to expose the target, the method, and
forReturnValue flag, so the code handling an exception will have
access to all details.

See gh-30644
2023-07-03 12:08:11 +01:00
Arjen Poutsma 1393fac402 Merge pull request #30788 from spencergibb:customize-disallowed-headers
* gh-30788:
  Polishing external contribution
  Allow customization of disallowed JdkClientHttpRequest headers
2023-07-03 12:42:47 +02:00
Arjen Poutsma 46f1849c2f Polishing external contribution
See gh-30787
Closes gh-30788
2023-07-03 12:36:25 +02:00
spencergibb 9900575f9c Allow customization of disallowed JdkClientHttpRequest headers
By default, the JDK HttpClient's HttpRequest does not allow Connection,
Content-Length, Expect, Host, or Upgrade headers to be set, but this can
be overriden with the `jdk.httpclient.allowRestrictedHeaders` system
property.

See https://bugs.openjdk.org/browse/JDK-8213696

Closes gh-30787
2023-07-03 12:35:44 +02:00
Sam Brannen 2ed10f13e9 Merge branch '6.0.x' 2023-07-02 16:47:25 +02:00
Sam Brannen a23c9cd53d Merge branch '6.0.x' 2023-07-01 17:42:28 +02:00
Sam Brannen 9ac2443b78 Polish ModuleResource[Tests] 2023-07-01 16:49:32 +02:00
Sébastien Deleuze f075120675 Support WildcardType resolution in GenericTypeResolver
This commit adds support for WildcardType bounds resolution,
commonly seen in Kotlin due to declaration-site variance,
but also possible in Java even if less common.

Closes gh-22313
2023-06-30 15:26:32 +02:00
Sam Brannen b8f091e2f6 Merge branch '6.0.x' 2023-06-30 14:17:49 +02:00
Johnny Lim 41f8b6926f Polish gh-30013
See gh-30013
2023-06-30 14:02:15 +02:00
Juergen Hoeller b4ed3fbcd0 Upgrade to Checkstyle 10.12.1 2023-06-30 13:02:38 +02:00
Juergen Hoeller 83ec9f4fe7 Merge branch '6.0.x'
# Conflicts:
#	build.gradle
2023-06-30 13:00:04 +02:00
Seonghoon Kim 3f65b8506b Increase log level to error 2023-06-30 12:38:28 +02:00
Juergen Hoeller 50074f0e96 Merge branch '6.0.x' 2023-06-30 10:40:37 +02:00
Juergen Hoeller 31a51cca4f Support for getFilename() in ModuleResource
See gh-28507
2023-06-30 00:35:14 +02:00
Juergen Hoeller 654dee8cd6 Additional notes on module access via ClassPathResource
See gh-28507
2023-06-29 18:15:38 +02:00
Juergen Hoeller 7028de9dbd Merge branch '6.0.x' 2023-06-29 18:04:40 +02:00
Juergen Hoeller e4751513a4 Additional notes on module access via ClassPathResource
See gh-28507
2023-06-29 17:54:53 +02:00
Sébastien Deleuze bd7e2d2875 Change restore logging to info level
Closes gh-30775
2023-06-29 17:20:51 +02:00
Juergen Hoeller 6061fdf231 Introduce ModuleResource for resources loaded from a given Module
Closes gh-28507
2023-06-29 17:18:46 +02:00
Arjen Poutsma 9b662e8244 Typo 2023-06-29 15:36:31 +02:00
Arjen Poutsma f51a640309 Polishing JdkClientHttpRequestFactory 2023-06-29 14:40:25 +02:00
Arjen Poutsma 48906afaf4 Polishing JettyClientHttpRequestFactory 2023-06-29 12:32:20 +02:00
Juergen Hoeller ef4d93507d Merge branch '6.0.x' 2023-06-29 12:05:57 +02:00
Arjen Poutsma d1d79babe7 Polishing JettyClientHttpRequestFactory 2023-06-28 16:19:13 +02:00
Juergen Hoeller 2eb8efe83b Merge branch '6.0.x' 2023-06-28 15:46:29 +02:00
Arjen Poutsma dd57ec9fa0 Merge pull request #30478 from mdeinum:http-client
* gh-30478:
  Polishing external contribution
  HttpClient based ClientHttpRequestFactory
2023-06-28 12:28:24 +02:00
Arjen Poutsma 0033eb4ed6 Polishing external contribution
- Removed duplicate Client in types names.
- Removed buffering in favor of OutputStream to
  Flow.Publisher<ByteBuffer> bridge.
- Made request and types package private.
- Various other small improvements.

Closes gh-30478
2023-06-28 12:23:29 +02:00
Marten Deinum 2ca8dd2faa HttpClient based ClientHttpRequestFactory
As JDK17 is now the baseline it is possible to use the
HttpClient provided by Java.
2023-06-28 10:43:44 +02:00
Brian Clozel c2e3fed8dc Align observations of @Scheduled with OTel conventions
This commit updates the `ScheduledTaskObservationDocumentation` to
better align the contributed KeyValues with OpenTelemetry conventions
for observations of code executions.

Instead of a "target.type" key with the bean class simple name, this
is now contributing the canonical class name of the bean under the
"code.namespace" key.
The "method.name" key is renamed to "code.function" and its values
remain unchanged.

Closes gh-30721
2023-06-27 22:20:06 +02:00
rstoyanchev 91eb2be44c Remove no longer supported protected methods
Closes gh-26721
2023-06-27 16:34:52 +01:00
rstoyanchev d4ac90dae0 Support nested constructors in DataBinder
Closes gh-20806
2023-06-26 17:01:44 +01:00
rstoyanchev 11a416156b Use ResolvableType to create WebDataBinder
This provides more flexibility to pass a targetType even if
a MethodParameter is not available.

See gh-26721
2023-06-26 13:11:05 +01:00
rstoyanchev d37d6688d8 WebFlux constructs model attribute via DataBinder
See gh-26721
2023-06-23 11:15:00 +01:00
rstoyanchev 801f01e23f Polishing in ModelAttributeMethodArgumentResolver
See gh-26721
2023-06-22 20:59:05 +01:00
rstoyanchev ea398d7b7e Support constructing target object in DataBinder
See gh-26721
2023-06-22 20:36:28 +01:00
rstoyanchev 40bf923d7d Polishing in MultipartFileArgumentResolver
Closes gh-30728
2023-06-27 10:51:42 +01:00
Olga Maciaszek-Sharma e69a1d22f9 Add MultipartFile support to HTTP interface client
See gh-30728
2023-06-27 09:55:24 +01:00
Juergen Hoeller 3f40452511 Merge branch '6.0.x' 2023-06-26 19:36:20 +02:00
Juergen Hoeller 2ccf4cab8b Polishing 2023-06-26 12:37:33 +02:00
Juergen Hoeller feac983869 Merge branch '6.0.x' 2023-06-26 12:35:56 +02:00
Sébastien Deleuze 66b27e6dc8 Merge branch '6.0.x' 2023-06-26 12:03:40 +02:00
Sam Brannen 6072a537d9 Polish formatting 2023-06-25 19:13:27 +02:00
Sam Brannen aecebf7981 Ensure MethodValidationAdapterTests pass with default Locale other than English
Closes gh-30734
2023-06-25 19:12:41 +02:00
Sam Brannen 4cbf47834d Merge branch '6.0.x' 2023-06-25 18:50:23 +02:00
Sam Brannen 854715d9a7 Update copyright headers 2023-06-25 15:39:16 +02:00
Sam Brannen 9127777c32 Merge branch '6.0.x'
# Conflicts:
#	spring-context/src/test/java/org/springframework/jmx/export/MBeanExporterTests.java
2023-06-25 15:34:10 +02:00
Johnny Lim 6c816c55d1 Fix incomplete AssertJ assertions
Closes gh-30743
2023-06-25 15:10:11 +02:00
Sam Brannen 209e02cf29 Merge branch '6.0.x' 2023-06-24 14:27:13 +02:00
Sam Brannen 19686adc01 Merge branch '6.0.x' 2023-06-24 14:15:05 +02:00
Sébastien Deleuze 35667e81ea Merge branch '6.0.x' 2023-06-23 14:20:13 +02:00
Juergen Hoeller d881cd3346 Accept REQUIRES_NEW on non-async transactional event listeners
See gh-30679
2023-06-23 14:03:11 +02:00
Sam Brannen ca51b1422a Merge branch '6.0.x' 2023-06-23 13:53:01 +02:00
Sam Brannen da00bbebbf Merge branch '6.0.x' 2023-06-23 12:46:30 +02:00
Sam Brannen 171535f680 Merge branch '6.0.x' 2023-06-23 12:07:37 +02:00
Sébastien Deleuze f06cf21341 Support Kotlin parameter default values in handler methods
This commit adds support for Kotlin parameter default values
in handler methods. It allows to write:
@RequestParam value: String = "default"
as an alternative to:
@RequestParam(defaultValue = "default") value: String

Both Spring MVC and WebFlux are supported, including on
suspending functions.

Closes gh-21139
2023-06-22 16:08:48 +02:00
Sam Brannen 254fb39567 Merge branch '6.0.x' 2023-06-22 16:01:22 +02:00
Sam Brannen 40596433f1 Merge branch '6.0.x' 2023-06-22 15:28:34 +02:00
Johnny Lim 3be1216897 Polish
This commit polishes a bit.

Closes gh-30691
2023-06-22 15:12:58 +02:00
Sam Brannen b8a713fde3 Merge branch '6.0.x' 2023-06-22 15:12:25 +02:00
Sam Brannen 06b6c4bcf9 Update copyright headers 2023-06-22 14:56:09 +02:00
Sam Brannen cd4ab23fa9 Merge branch '6.0.x' 2023-06-22 14:55:43 +02:00
Sam Brannen 5598dd2c34 Merge branch '6.0.x' 2023-06-22 13:54:04 +02:00
Sam Brannen f12d81f791 Merge branch '6.0.x' 2023-06-22 11:36:10 +02:00
Sam Brannen 49547fc710 Merge branch '6.0.x'
# Conflicts:
#	build.gradle
2023-06-22 11:32:39 +02:00
Brian Clozel ea8fdd17c8 Upgrade to JDK21 EA build 27 2023-06-22 08:43:03 +02:00
Juergen Hoeller a6566bfd5f Merge branch '6.0.x' 2023-06-21 18:53:15 +02:00
Sam Brannen abefc0aba0 Merge branch '6.0.x' 2023-06-21 17:54:16 +02:00
Sam Brannen 03b304f05c Merge branch '6.0.x' 2023-06-21 17:37:04 +02:00
Juergen Hoeller 54e25e2fa6 Merge branch '6.0.x' 2023-06-21 17:18:55 +02:00
Sam Brannen 83447663b4 Merge branch '6.0.x' 2023-06-21 16:21:34 +02:00
Stephane Nicoll b581a79eed Merge branch '6.0.x' 2023-06-21 14:51:00 +02:00
Sam Brannen 6535614d78 Merge branch '6.0.x' 2023-06-21 14:22:11 +02:00
Juergen Hoeller fa82683ce2 Merge branch '6.0.x' 2023-06-21 13:16:47 +02:00
Juergen Hoeller 1a201cd180 Merge branch '6.0.x'
# Conflicts:
#	spring-jcl/src/main/java/org/apache/commons/logging/LogFactory.java
2023-06-21 09:51:11 +02:00
Maarten Coene 24457660e3 Ensure Spring LogFactory contains all public methods from Apache LogFactory (#30668) 2023-06-21 09:29:58 +02:00
Juergen Hoeller f1fb8cf03e Polishing 2023-06-20 22:53:12 +02:00
Juergen Hoeller 18c6aceee7 Consistent guard for Reactive Streams presence
Closes gh-30707
2023-06-20 22:51:23 +02:00
Sébastien Deleuze 24ddceea47 Merge branch '6.0.x' 2023-06-20 20:57:33 +02:00
Sébastien Deleuze 82ec28b369 Add a CoExchangeFilterFunction for WebClient
This commit introduces a Kotlin-specific implementation
of the ExchangeFilterFunction interface that allows for
using coroutines, similar to what CoWebFilter does on
server-side.

Closes gh-30650
2023-06-20 20:54:38 +02:00
rstoyanchev db19f6395d Polishing
Closes gh-29768
2023-06-20 17:18:40 +01:00
Atsushi Yoshikawa b677ff20fe AbstractMessageConverter delegates null headers
See gh-29768
2023-06-20 17:18:40 +01:00
rstoyanchev b98c1ec36a Apply default value in case of null after conversion
Closes gh-29550
2023-06-20 17:18:40 +01:00
Stephane Nicoll 37ff9792be Merge branch '6.0.x' 2023-06-20 17:42:40 +02:00
Sam Brannen 3344ab909b Merge branch '6.0.x' 2023-06-20 15:49:13 +02:00
Stephane Nicoll 828b39587f Merge branch '6.0.x' 2023-06-20 15:21:37 +02:00
Sam Brannen 0605172d4d Merge branch '6.0.x'
# Conflicts:
#	spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolver.java
2023-06-20 13:29:06 +02:00
rstoyanchev 089d938e15 Set throwExceptionIfNoHandlerFound=true and deprecate
Closes gh-29491
2023-06-19 17:18:09 +01:00
rstoyanchev c00508d6cf Raise and handle NoResourceFoundException
See gh-29491
2023-06-19 17:18:08 +01:00
Stephane Nicoll 83b0f4f394 Merge branch '6.0.x' 2023-06-19 15:47:26 +02:00
Brian Clozel 09cb844421 Instrument Scheduled methods for observability
This commit enhances the `ScheduledAnnotationBeanPostProcessor` to
instrument `@Scheduled` methods declared on beans. This will create
`"tasks.scheduled.execution"` observations for each execution of a
scheduled method. This supports both blocking and reactive variants.

By default, observations are no-ops; developers must configure the
current `ObservationRegistry` on the `ScheduledTaskRegistrar` by using a
`SchedulingConfigurer`.

Closes gh-29883
2023-06-19 08:55:08 +02:00
Juergen Hoeller 842569c9e5 Detect invalid transaction configuration for transactional event listeners
Closes gh-30679
2023-06-17 16:13:59 +02:00
Sam Brannen 3568f6c61a Merge branch '6.0.x' 2023-06-17 13:36:57 +02:00
Juergen Hoeller 5bf213948c Merge branch '6.0.x'
# Conflicts:
#	spring-orm/src/main/java/org/springframework/orm/hibernate5/SpringBeanContainer.java
2023-06-17 11:41:07 +02:00
Sam Brannen 92c2fb45c7 Merge branch '6.0.x' 2023-06-16 13:40:18 +02:00
Sébastien Deleuze cd9c0e03e7 Merge branch '6.0.x' 2023-06-16 11:35:31 +02:00
Sam Brannen 2dd34cf967 Clean up warnings in tests 2023-06-15 16:26:34 +02:00
Sam Brannen a2072de391 Update copyright headers 2023-06-15 16:21:13 +02:00
Sam Brannen 526d9eae7f Merge branch '6.0.x' 2023-06-15 16:20:19 +02:00
Sam Brannen 7681d12eb0 Merge branch '6.0.x'
# Conflicts:
#	gradle.properties
2023-06-15 16:15:17 +02:00
Brian Clozel b077ae9259 Configure local JDK toolchains in CI staging job 2023-06-15 14:01:06 +02:00
Juergen Hoeller 362e189aa5 Adapt UserController example to current ServerRequestObservationContext
See gh-30013
2023-06-14 22:48:40 +02:00
Juergen Hoeller 96ae03b48f Merge branch '6.0.x' 2023-06-14 22:27:55 +02:00
Juergen Hoeller c276e5b679 Consistent use of 6.1 as generational version number 2023-06-14 21:58:45 +02:00
Juergen Hoeller f00a8cb3a3 Remove ServerWebExchange dependency in ServerRequestObservationContext
Avoiding cycle between http.server and web.server packages.

See gh-30013
2023-06-14 21:57:27 +02:00
Juergen Hoeller 220995b830 Move HandlerMethodValidator to web.method.annotation package
Avoiding cycle between web.method.support and web.method.annotation packages.

See gh-29825
2023-06-14 21:52:55 +02:00
Juergen Hoeller e344f3f869 Consistent treatment of new Spring system properties
See gh-30606
See gh-30571
2023-06-14 18:27:06 +02:00
Sébastien Deleuze 517a073f33 Use spring.native.precompute.log system property
This commit changes the Java system property used to
control PreComputeFieldFeature verbose logging from
spring.aot.precompute to spring.native.precompute.log
in order to clarify its purpose and avoid confusion
with AOT processing of JVM bytecode or Java sources.

See gh-30571
2023-06-14 17:58:08 +02:00
rstoyanchev dcba9475ba Allow custom ProblemDetail in ErrorResponse.Builder
Closes gh-30568
2023-06-14 16:48:35 +01:00
rstoyanchev 2e43412a82 Polishing in DefaultErrorResponseBuilder
Closes gh-30566
2023-06-14 16:48:35 +01:00
rstoyanchev 48861b67dd Add "title" message code to ErrorResponse.Builder
See gh-30566
2023-06-14 16:30:15 +01:00
rstoyanchev 61eaa9333b Polishing in ErrorResponse
See gh-30566
2023-06-14 16:30:15 +01:00
rstoyanchev 9c7b5cb3f5 Add ProblemDetail "type" message code
See gh-30566
2023-06-14 16:30:15 +01:00
Sam Brannen 53828cbfad Merge branch '6.0.x' 2023-06-14 14:39:06 +02:00
Arjen Poutsma e3c602c43a Polishing
Restore thread interrupted status in JettyClientHttpRequest
2023-06-14 14:09:25 +02:00
Arjen Poutsma 02e9209e5c Merge pull request #30433 from srivatsa-cfp:main
* gh-30433:
  Polishing external contribution
  Add non-null assertions in DefaultEntityResponseBuilder
2023-06-14 13:49:20 +02:00
Arjen Poutsma 8a29bfba3f Polishing external contribution 2023-06-14 13:44:50 +02:00
Vatsa 0c817f0441 Add non-null assertions in DefaultEntityResponseBuilder
This commit adds various non-null assertions to
DefaultEntityResponseBuilder, in Spring MVC.

Closes gh-30433
2023-06-14 13:44:50 +02:00
Juergen Hoeller 326e27eab2 Merge branch '6.0.x' 2023-06-14 12:57:59 +02:00
Arjen Poutsma 5547361d18 Polishing
Store status code as HttpStatusCode instead of Object.
2023-06-14 12:05:49 +02:00
Arjen Poutsma 805d643347 Merge branch '6.0.x' 2023-06-14 11:45:04 +02:00
Juergen Hoeller b20a71a0d3 Merge branch '6.0.x'
# Conflicts:
#	spring-core/src/main/java/org/springframework/util/ObjectUtils.java
2023-06-14 11:19:56 +02:00
Juergen Hoeller 3de4e931c7 Javadoc notes on isSimpleValueType in BeanUtils vs ClassUtils
See gh-30664
2023-06-14 10:40:14 +02:00
Juergen Hoeller 93345de687 Consistent Locale exposure for Bean Validation message assertions
See gh-29825
See gh-30198
2023-06-14 10:39:19 +02:00
Juergen Hoeller bbf3c6ecac Upgrade to Jackson 2.15
Closes gh-30665
2023-06-14 10:39:11 +02:00
Juergen Hoeller ca4de8f191 Consistent simple value type check
Includes UUID treatment for data binding and bean dependency checks.

Closes gh-30664
2023-06-14 09:37:39 +02:00
Juergen Hoeller 858ea1a8c5 Merge branch '6.0.x' 2023-06-14 09:32:30 +02:00
Sébastien Deleuze dd50da314d Update reference documentation index
Add "JVM Checkpoint Restore" to the Integration section.

See gh-30647
2023-06-14 08:49:52 +02:00
Juergen Hoeller 1f068fcdb8 Rename checkpoint property to "spring.context.checkpoint"
See gh-30606
2023-06-13 19:06:46 +02:00
rstoyanchev 85d81024a4 Add MethodParameter[] input to MethodValidationAdapter
This allows re-use of existing MethodParameter instances from controller
methods with cached metadata, and also ensures additional capabilities
such as looking up parameter annotations on interfaces.

See gh-29825
2023-06-13 17:40:57 +01:00
Sam Brannen e7c3e1c516 Merge branch '6.0.x' 2023-06-13 17:14:03 +02:00
Sam Brannen a268203832 Merge branch '6.0.x' 2023-06-13 16:27:20 +02:00
Sébastien Deleuze 9536dd14ef Polish PreComputeFieldFeature 2023-06-13 14:25:56 +02:00
Sébastien Deleuze c565df96f5 Remove System.out calls in PreComputeFieldFeature
Closes gh-30571
2023-06-13 14:25:32 +02:00
Juergen Hoeller 4b4a8af893 Merge branch '6.0.x' 2023-06-13 12:41:35 +02:00
rstoyanchev 1e3161b161 Expose MethodParameter in MissingServletRequestParameterException
See gh-26219
2023-06-13 11:29:35 +01:00
rstoyanchev 6b50b7b72a Initialize propertyName in TypeMismatchException
See gh-26219
2023-06-13 11:29:35 +01:00
rstoyanchev 96895c7759 Polishing in MethodArgumentNotValidException
See gh-30198
2023-06-13 11:29:35 +01:00
rstoyanchev 96c494c6ad Update error format in MethodArgumentNotValidException
1. Remove list markers (those can be provided in message).
2. Use ", and " between errors for readability.
3. Remove single quotes around errors.
4. If MessageSource is provided, use resolved message as is since in
that case applications have full control over each message.

Closes gh-30198
2023-06-13 11:29:35 +01:00
Sam Brannen 48548b29d6 Merge branch '6.0.x' 2023-06-13 11:58:37 +02:00
Sébastien Deleuze 55b3b28aec Introduce JVM checkpoint/restore support documentation
Closes gh-30647
2023-06-13 10:52:12 +02:00
Sébastien Deleuze 6a26db80b6 Refine RouterFunctionMapping ordering consistency
This commit changes the order of RouterFunctionMapping defined
in WebMvcConfigurationSupport from 3 to -1 in order to achieve
better consistency between WebMVC and WebFlux. It also reduces
the surprise factor. That way, functional routes are always
before annotation-based ones.

Closes gh-30278
2023-06-13 08:17:54 +02:00
rstoyanchev ccf68878c7 Merge branch '6.0.x' 2023-06-12 16:12:03 +01:00
Sam Brannen f7367ceae5 Merge branch '6.0.x' 2023-06-12 16:44:48 +02:00
Stephane Nicoll 61c9cbc3f5 Retain active profiles used during AOT processing
This commit makes sure that profiles that have been explicitly enabled
during AOT optimizations are automatically enabled when using those
optimizations.

If other profiles are set at runtime, they take precedence over the ones
defined during AOT processing.

Closes gh-30421
2023-06-12 16:33:35 +02:00
Sébastien Deleuze 446b90172b Merge branch '6.0.x' 2023-06-12 16:01:35 +02:00
Juergen Hoeller 6501176081 Merge branch '6.0.x' 2023-06-12 12:59:55 +02:00
Sébastien Deleuze ac35ffef9e Merge branch '6.0.x' 2023-06-12 12:48:42 +02:00
Rossen Stoyanchev 6b89cf94a3 Add method validation to WebFlux
See gh-29825
2023-06-12 11:37:55 +01:00
Rossen Stoyanchev bd054a4918 Add method validation to Spring MVC
See gh-29825
2023-06-12 11:37:55 +01:00
rstoyanchev cb04c3b335 Expose isReturnValue from MethodValidationException
See gh-29825
2023-06-12 11:37:55 +01:00
rstoyanchev 5c5d8e61ae Add BindingResultNameResolver option
See gh-29825
2023-06-12 11:37:55 +01:00
rstoyanchev 0fef380202 Add MethodValidator
See gh-29825
2023-06-12 11:37:55 +01:00
Rossen Stoyanchev cc8361cd93 Add MethodValidationResult
See gh-29825
2023-06-12 11:37:55 +01:00
rstoyanchev 8b53fecc06 Use List for violations in ParameterErrors
See gh-29825
2023-06-12 11:37:55 +01:00
Rossen Stoyanchev 425d5a94cb Adapt ConstraintViolation's from method validation
See gh-29825
2023-06-12 11:37:55 +01:00
rstoyanchev 38abee00e2 Rename MethodValidationTests and polishing
See gh-29825
2023-06-12 11:37:55 +01:00
rstoyanchev 082784e023 Extract delegate from MethodValidationInterceptor
See gh-29825
2023-06-12 11:37:55 +01:00
Juergen Hoeller 155a37d3a6 Merge branch '6.0.x' 2023-06-12 10:50:40 +02:00
Sam Brannen f8a3253d12 Polishing 2023-06-11 16:32:16 +02:00
Sam Brannen b4ba80b09e Merge branch '6.0.x'
# Conflicts:
#	spring-test/src/main/java/org/springframework/test/context/cache/DefaultCacheAwareContextLoaderDelegate.java
2023-06-11 15:56:23 +02:00
Sam Brannen f6045e8e11 Move context failure tracking to the ContextCache
In the previous commit which introduced the new context failure threshold
support in the TestContext framework, the context failure tracking was
tied to an instance of DefaultCacheAwareContextLoaderDelegate.
Consequently, the feature was only supported within a given test class.

This commit therefore moves context failure tracking to the ContextCache
SPI (and DefaultContextCache) so that the feature applies to all test
classes within the current test suite (i.e., JVM).

This commit also includes the total failure count in the statistics
logged by the DefaultContextCache.

See gh-14182
2023-06-11 15:35:10 +02:00
Sam Brannen f0a3f776c2 Avoid duplication of constant in Javadoc 2023-06-11 15:20:23 +02:00
Sam Brannen 74dc07e736 Improve ContextCacheTestUtils 2023-06-11 15:15:35 +02:00
Sam Brannen c5eb4ed978 Introduce context failure threshold support in the TestContext framework
This set of commits introduces ApplicationContext "failure threshold"
support in the Spring TestContext Framework (TCF).

Specifically, this new feature avoids repeated attempts to load a
failing ApplicationContext in the TCF, based on a failure threshold
which defaults to 1 but can be configured via a system property.

See individual commits for details.

Closes gh-14182
2023-06-09 17:50:48 +02:00
Sam Brannen bff81d3056 Document test context failure threshold support in reference manual
See gh-14182
2023-06-09 17:45:12 +02:00
Sam Brannen c8bb7ded5d Improve Javadoc for DefaultCacheAwareContextLoaderDelegate
See gh-14182
2023-06-09 17:45:06 +02:00
Sam Brannen 56f3bd86cf Add integration tests for context failure threshold support
See gh-14182
2023-06-09 17:44:44 +02:00
Sam Brannen 52ae97cffc Make test context failure threshold configurable
This commit makes the failure threshold value configurable via a JVM
system property or Spring property named
"spring.test.context.failure.threshold".

See gh-14182
2023-06-09 17:43:29 +02:00
Sam Brannen c7ca5c81c3 Avoid repeated attempts to load failing ApplicationContext in the TCF
This commit introduces initial support for a new "context failure
threshold" feature in the Spring TestContext Framework (TCF).

Specifically, DefaultCacheAwareContextLoaderDelegate now tracks how
many times a failure occurs when attempting to load an
ApplicationContext and preemptively throws an IllegalStateException for
subsequent attempts to load the same context if the configured failure
threshold has been exceeded.

See gh-14182
2023-06-09 17:40:28 +02:00
Sébastien Deleuze ccb8db41f0 Merge branch '6.0.x' 2023-06-09 15:21:54 +02:00
Sébastien Deleuze 46fe7f5bea Merge branch '6.0.x' 2023-06-09 14:17:45 +02:00
Sam Brannen 54904de77d Merge branch '6.0.x' 2023-06-09 11:52:20 +02:00
Sam Brannen dbd32f8f11 Merge branch '6.0.x' 2023-06-09 10:30:16 +02:00
Juergen Hoeller ef2b47e202 Merge branch '6.0.x' 2023-06-08 17:46:19 +02:00
Sam Brannen 60e8dbf11d Polishing 2023-06-08 17:41:22 +02:00
Sébastien Deleuze 01414d0b3d Merge branch '6.0.x' 2023-06-08 17:33:50 +02:00
Arjen Poutsma ad5bf2fac8 Merge branch '6.0.x' 2023-06-08 16:40:44 +02:00
Sam Brannen a28ef22113 Merge branch '6.0.x' 2023-06-08 16:06:31 +02:00
Sam Brannen fe55b49c7d Consistently use mergedConfig as variable name for MergedContextConfiguration 2023-06-08 15:53:38 +02:00
Sam Brannen e32a92daa7 Polishing 2023-06-08 15:44:57 +02:00
Arjen Poutsma d7970e4ab8 Support JAXBElement in Jaxb2XmlEncoder
This commit introduces support for JAXBElements in the Jaxb2XmlEncoder.

Closes gh-30552
2023-06-08 15:30:41 +02:00
Sébastien Deleuze 2f78b42133 Refine Kotlin ParameterNameDiscoverer tests
Related to gh-30052, we should improve ParameterNameDiscoverer
Kotlin tests to make sure DefaultParameterNameDiscoverer behaves
as expected and is consistent with
KotlinReflectionParameterNameDiscoverer behavior.

Closes gh-30618
2023-06-08 15:27:06 +02:00
Arjen Poutsma f4ef057e9e Merge branch '6.0.x' 2023-06-08 14:42:24 +02:00
Sébastien Deleuze 448786bfd0 Upgrade to Kotlin 1.8.22
See gh-29754
2023-06-08 09:29:06 +02:00
Sam Brannen 0093d6e34d Polish contribution
See gh-30587
2023-06-07 19:13:05 +02:00
luohongtu 324c6057fd Remove duplicate code in RequestResponseBodyMethodProcessor
Closes gh-30587
2023-06-07 19:13:05 +02:00
Juergen Hoeller 759ab23c45 Release shared JMS Connection on Single/CachingConnectionFactory.stop()
See gh-30612
2023-06-07 18:40:56 +02:00
Juergen Hoeller 1b62b6dd89 Merge branch '6.0.x' 2023-06-07 17:23:09 +02:00
Juergen Hoeller b3597f3d99 Polishing 2023-06-07 17:11:46 +02:00
Juergen Hoeller 06bb6dbcff Release shared JMS Connection before stop completion callback
Closes gh-30612
2023-06-07 17:11:35 +02:00
Sam Brannen 73e9de7b5f Simplify Javadoc 2023-06-07 17:08:11 +02:00
Sam Brannen e535ef37c7 Merge branch '6.0.x' 2023-06-07 16:02:04 +02:00
Johnny Lim dd926af5a8 This PR adds Javadoc for RegisteredBean.resolveAutowiredArgument()
See gh-30401
Closes gh-30609
2023-06-07 16:01:48 +02:00
Johnny Lim a726b5109a This PR adds Javadoc for RegisteredBean.resolveAutowiredArgument()
See gh-30401
Closes gh-30609
2023-06-07 16:00:30 +02:00
Johnny Lim 7c817dd34b Remove LocalVariableTableParameterNameDiscoverer reference in doc
See gh-29559
Closes gh-30611
2023-06-07 15:49:56 +02:00
Sam Brannen c9165470f2 Update test
See gh-30610
2023-06-07 15:04:28 +02:00
Sam Brannen 61816d0076 Merge branch '6.0.x' 2023-06-07 14:59:14 +02:00
Sam Brannen aa2a067489 Merge branch '6.0.x' 2023-06-07 13:36:05 +02:00
Sam Brannen 714d380ec0 Merge branch '6.0.x' 2023-06-07 12:01:54 +02:00
Arjen Poutsma df7223f39c Remove APIs marked for removal
See gh-30604
2023-06-07 10:20:27 +02:00
Juergen Hoeller 7f6f47b97b Property-driven onRefresh checkpoint during application context bootstrap
Closes gh-30606
2023-06-06 19:18:34 +02:00
Sam Brannen b9e972c248 Polishing 2023-06-06 16:11:40 +02:00
Sébastien Deleuze 66a1be2d86 Enable KotlinScriptTemplateTests with Java 19
No support yet for Java 21.

Closes gh-29249
2023-06-06 15:49:04 +02:00
Sébastien Deleuze 717915e6ac Upgrade to Dokka 1.8.20
Closes gh-30605
2023-06-06 15:24:12 +02:00
Sam Brannen c8c8f5722b Support letters other than A-Z in identifiers in SpEL
Prior to this commit, only the letters 'A' - 'Z' (ignoring case) were
supported in identifiers (i.e., property, field, and variable names).

This known (yet undocumented) limitation prevented the use of characters
such as 'ü', 'ñ', 'é' as well as letters from other character sets such
as Chinese, Japanese, Cyrillic, etc.

This commit lifts that restriction by delegating to Character.isLetter()
to determine if a character in a SpEL expression is a letter.

Closes gh-30580
2023-06-06 14:14:15 +02:00
Sam Brannen cd610b3ed1 Merge branch '6.0.x' 2023-06-06 12:46:15 +02:00
Sam Brannen 93deb0a584 Polishing 2023-06-06 11:54:38 +02:00
Sam Brannen 05eab703cc Polishing 2023-06-06 11:29:25 +02:00
Brian Clozel c0cd55a8fa Clarify error handling for reactive scheduled methods
This commit adds a note in the reference documentation stating that
`ErrorHandler` infrastructure is not involved when reactive methods send
an error signal: the exception is sent as a message in the pipeline and
is not thrown from the task `Runnable`.

See gh-23533
2023-06-05 14:13:44 +02:00
Simon Baslé 35052f2113 Support @Scheduled fixedDelay/fixedRate on Publisher-returning methods
This commit adds support for `@Scheduled` annotation on reactive
methods and Kotlin suspending functions.

Reactive methods are methods that return a `Publisher` or a subclass
of `Publisher`. The `ReactiveAdapterRegistry` is used to support many
implementations, such as `Flux`, `Mono`, `Flow`, `Single`, etc.
Methods should not take any argument and published values will be
ignored, as they are already with synchronous support.

This is implemented in `ScheduledAnnotationReactiveSupport`, which
"converts" Publishers to `Runnable`. This strategy keeps track of
active Subscriptions in the `ScheduledAnnotationBeanPostProcessor`,
in order to cancel them all in case of shutdown.
The existing scheduling support for tasks is reused, aligning the
triggering behavior with the existing support: cron, fixedDelay and
fixedRate are all supported strategies.

If the `Publisher` errors, the exception is logged at warn level and
otherwise ignored. As a result new `Runnable` instances will be
created for each execution and scheduling will continue.
The only difference with synchronous support is that error signals
will not be thrown by those `Runnable` tasks and will not be made
available to the `org.springframework.util.ErrorHandler` contract.
This is due to the asynchronous and lazy nature of Publishers.

Closes gh-23533
Closes gh-28515
2023-06-05 14:13:44 +02:00
Juergen Hoeller 53f891226e Merge branch '6.0.x'
# Conflicts:
#	spring-core/src/main/java/org/springframework/core/CollectionFactory.java
2023-06-05 13:59:37 +02:00
Juergen Hoeller 4fb4c95220 Merge branch '6.0.x' 2023-06-05 10:53:29 +02:00
Juergen Hoeller b8b88e638b Upgrade to Checkstyle 10.12 2023-06-04 18:54:30 +02:00
Juergen Hoeller 837327f530 Merge branch '6.0.x'
# Conflicts:
#	build.gradle
2023-06-04 18:43:46 +02:00
Juergen Hoeller e224c71119 Merge branch '6.0.x' 2023-06-04 17:25:35 +02:00
Sam Brannen 5bc80fc094 Disable SpEL selector support in WebSocket messaging by default
This commit disables support for evaluating SpEL expressions from
untrusted sources by default. Specifically, this applies to the
SpEL-based 'selector' header support in WebSocket messaging, which
includes the DefaultSubscriptionRegistry and the classes used to
configure the 'selector' header name (SimpleBrokerMessageHandler and
SimpleBrokerRegistration).

The selector header support remains in place but will have to be
explicitly enabled beginning with Spring Framework 6.1.

For example, a custom implementation of WebSocketMessageBrokerConfigurer
can override the configureMessageBroker() method and configure the
selector header name as follows.

  registry.enableSimpleBroker().setSelectorHeaderName("selector");

Closes gh-30550
2023-06-04 17:02:02 +02:00
Sam Brannen 75466fee8d Merge branch '6.0.x' 2023-06-04 16:40:23 +02:00
Sam Brannen bedf5f26fe Merge branch '6.0.x' 2023-06-04 16:27:23 +02:00
Juergen Hoeller 2689bab652 Merge branch '6.0.x' 2023-06-03 17:19:06 +02:00
Juergen Hoeller e82dd14f0f Merge branch '6.0.x'
# Conflicts:
#	spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java
2023-06-02 23:30:04 +02:00
Juergen Hoeller 1dba8f6add Polishing 2023-06-02 20:45:26 +02:00
Juergen Hoeller 25a1e8d414 Reset common caches before LifecycleProcessor-initiated checkpoint
See gh-29921
2023-06-02 20:42:37 +02:00
Juergen Hoeller 8c6287ef7b Expose parameter/field name for non-JavaBeans type conversion
Supports name-bound PropertyEditor registrations on data classes.
Includes consistent support for field-aware method parameters.

Closes gh-28284
2023-06-02 20:42:05 +02:00
Juergen Hoeller 322cbca0dc Support for async/reactive close methods (e.g. R2DBC)
Closes gh-26991
2023-06-02 20:40:55 +02:00
Sam Brannen 2685a35c3a Merge branch '6.0.x' 2023-06-01 15:02:42 +02:00
Stephane Nicoll 8606d9b581 Merge branch '6.0.x' 2023-05-31 12:27:02 +02:00
Arjen Poutsma 3d63cbf076 Introduce JettyClientHttpRequestFactory
This commit introduces an implementation of ClientHttpRequestFactory
based on Jetty's HttpClient.

Closes gh-30564
2023-05-31 10:36:57 +02:00
rstoyanchev 67f88482e6 Merge branch '6.0.x' 2023-05-30 17:36:08 +01:00
rstoyanchev b3f5d20ad8 Merge branch '6.0.x' 2023-05-30 17:18:01 +01:00
Arjen Poutsma 49d688f99c Mark ClientHttpResponse::getRawStatusCode for removal 2023-05-30 15:37:58 +02:00
Arjen Poutsma 033bebf8cd Remove buffering from ClientHttpRequest implementations
This commit ensures that ClientHttpRequest implementations implement
StreamingHttpOutputMessage, so that they do not expose an OutputStream,
but store a handle capable of writing to a stream instead.

Closes gh-30557
2023-05-30 15:37:58 +02:00
Sébastien Deleuze 1ce22bdcc1 Remove MethodArgumentNotValidException(Executable, BindingResult)
Closes gh-30559
2023-05-30 12:45:01 +02:00
Sébastien Deleuze 4e678479b9 Merge branch '6.0.x' 2023-05-30 12:44:47 +02:00
Brian Clozel f69db61147 Fix JDK21 EA build URL 2023-05-30 11:51:39 +02:00
Brian Clozel 655f5eb1e2 Upgrade to JDK21 EA 24 in CI build 2023-05-30 10:49:42 +02:00
Brian Clozel e45edff4a4 Merge branch '6.0.x' 2023-05-30 10:37:50 +02:00
Stephane Nicoll fc309ee314 Merge branch '6.0.x' 2023-05-30 09:15:55 +02:00
Sébastien Deleuze 8a5f655a5c Merge branch '6.0.x' 2023-05-26 19:15:03 +02:00
Simon Baslé 3f14ebc4cf Polish ReactiveTypeHandler when checking wildcard ndjson subtype
This commit polishes the previous change, ensuring that if the request
uses a wildcard subtype with an ndjson suffix then the wildcard isn't
present in the response's Content-Type.

Insteand, in that case we would fall back to `applicatin/x-ndjson` (the
base ndjson media type), assuming that the requester is only interested
in the ndjson nature of the underlying representation and not in the
specific semantics of any subtype.

See commit 9332b3f
See gh-26817
2023-05-26 15:23:09 +02:00
Remus Richard Dumitrache 4d8f6c1b41 Support parsing long millisecond timestamps in InstantFormatter
This commit adds support of parsing a simple long from a String and
turning it to an `Instant` by considering it represents a timestamp in
milliseconds (see `Instant.ofEpochMilli`). Failing to parse a long from
the String, the previous algorithm is used: first check for an RFC-1123
representation then an ISO_INSTANT representation.

See gh-30312
Closes gh-30546
2023-05-26 12:08:26 +02:00
Juergen Hoeller 7150c23e93 Merge branch '6.0.x' 2023-05-26 11:03:48 +02:00
Brian Clozel 009179fed7 Merge branch '6.0.x' 2023-05-25 20:18:10 +02:00
Brian Clozel 6dbd446fc8 Update CI pipeline 2023-05-25 19:17:29 +02:00
Juergen Hoeller a45d438f05 Enforce JPA bootstrap failure for incomplete Hibernate Validator setup
Closes gh-30549
2023-05-25 18:50:57 +02:00
Juergen Hoeller cabc41bcd6 Merge branch '6.0.x' 2023-05-25 18:46:50 +02:00
Simon Baslé 5977bb05ca Port BeanPropertyRowMapper and DataClassRowMapper for r2dbc
This commit ports and adapts spring-jdbc's `BeanPropertyRowMapper` and
`DataClassRowMapper` to spring-r2dbc, allowing to `map` rows or
outParameters to object instances, data classes or records.

See gh-26021
Closes gh-30530
2023-05-25 17:40:52 +02:00
Simon Baslé 9332b3f690 Have ReactiveTypeHandler consider +x-ndjson suffix as streaming
This commit adds `application/*+x-ndjson`, a wildcard media type which
covers all types that can be parsed as nd-json, to the list of media
types the ReactiveTypeHandler considers for a streaming response in
WebMVC.

As a result, a request which for example `Accept` the
`application/vnd.myapp.v1+x-ndjson` media type will generate a response
with the same `Content-Type`, with newline-delimited json objects being
streamed in the response body.

Closes gh-26817
2023-05-25 16:00:34 +02:00
Sam Brannen 5f13b2b35b Merge branch '6.0.x' 2023-05-25 14:03:01 +02:00
Sam Brannen 01abd521e2 Merge branch '6.0.x' 2023-05-25 13:48:15 +02:00
Sam Brannen f91fae5dfb Avoid deprecation warnings for imports 2023-05-25 13:30:54 +02:00
Sam Brannen 7169577f9c Merge branch '6.0.x' 2023-05-24 16:57:30 +02:00
Sam Brannen 72152ff1f3 Merge branch '6.0.x' 2023-05-24 14:02:51 +02:00
Juergen Hoeller 1d6246d700 Merge branch '6.0.x' 2023-05-23 20:34:58 +02:00
Juergen Hoeller 5441796675 Polishing 2023-05-23 20:01:28 +02:00
Juergen Hoeller 876e417eb4 Javadoc for setVariables and registerFunction
See gh-30045
2023-05-23 20:01:02 +02:00
rstoyanchev e6c812e2ab Merge branch '6.0.x' 2023-05-23 15:13:04 +01:00
rstoyanchev 0e5e2f5d2e Merge branch '6.0.x' 2023-05-23 15:10:10 +01:00
rstoyanchev 5bca25fe44 Remove commented out check->antora task dependency
Instead of having antora run always, which happens regardless of
whether anything has changed, we'll have it invoked as part of the CI
build, and we'll have to run it locally ourselves when necessary.

See gh-30481
2023-05-23 14:55:39 +01:00
Sam Brannen ab253470f0 Update copyright headers 2023-05-23 15:12:32 +02:00
Sam Brannen 02f152c6e1 Merge branch '6.0.x' 2023-05-23 15:11:19 +02:00
Sam Brannen 791802fdf7 Merge branch '6.0.x' 2023-05-23 12:41:26 +02:00
Sam Brannen f4f8066877 Merge branch '6.0.x' 2023-05-23 11:36:40 +02:00
Sam Brannen d95ecbe0fd Merge branch '6.0.x'
# Conflicts:
#	spring-test/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java
2023-05-23 11:29:10 +02:00
Sébastien Deleuze cba77c6909 Merge branch '6.0.x' 2023-05-23 11:21:03 +02:00
Sébastien Deleuze b4eb352e1d Merge branch '6.0.x' 2023-05-23 09:11:07 +02:00
rstoyanchev 5fc937d558 Remove asciidoctor build plugins after move to antora 2023-05-23 07:19:52 +01:00
rstoyanchev f926deda89 Fix link to code in documentation
See gh-30013
2023-05-23 07:18:16 +01:00
Sébastien Deleuze c3e28728ce Upgrade to Kotlin Serialization 1.5
Closes gh-30515
2023-05-22 18:07:32 +02:00
Sébastien Deleuze f956d399ae Upgrade to Kotlin Coroutines 1.7
Leverages Kotlin/kotlinx.coroutines#3487.

Closes gh-30514
2023-05-22 18:07:21 +02:00
Sébastien Deleuze 7efd347da6 Upgrade to Kotlin 1.8
This commit also disables the JVM target validation
introduced in Kotlin 1.8 since Kotlin is not yet
compatible with Java 21.

Closes gh-29754
2023-05-22 15:43:37 +02:00
Stephane Nicoll 5e07141846 Merge branch '6.0.x' 2023-05-22 15:30:47 +02:00
Brian Clozel 40543e0a58 Polish SnakeYAML 2.0 upgrade
See gh-30048
2023-05-22 14:52:59 +02:00
Andrey Somov 097758baf3 Upgrade to SnakeYAML 2.0
This commit raises the SnakeYAML baseline version to 2.0.
While most Spring applications are not affected by CVE-2022-1471,
upgrading this version should prevent automated tools from raising this
as a security issue. Such tools usually do not understand that YAML
parsing in Spring is about reading configuration, not parsing untrusted
content.

Closes gh-30048
2023-05-22 14:31:27 +02:00
Brian Clozel 96a429a561 Move reactive server instrumentation out of WebFilter
Prior to this commit, the Observation instrumentation for Reactive
server applications was implemented with a `WebFilter`. This allowed to
record observations and set up a tracing context for the controller
handlers.

The limitation of this approach is that all processing happening at a
lower level is not aware of any observation. Here, the
`HttpWebHandlerAdapter` handles several interesting aspects:

* logging of HTTP requests and responses at the TRACE level
* logging of client disconnect errors
* handling of unresolved errors

With the current instrumentation, these logging statements will miss the
tracing context information. As a result, this commit deprecates the
`ServerHttpObservationFilter` in favor of a more direct instrumentation
of the `HttpWebHandlerAdapter`. This enables a more precise
instrumentattion and allows to set up the current observation earlier in
the reactor context: log statements will now contain the relevant
information.

Fixes gh-30013
2023-05-22 11:03:02 +02:00
greg.lee e5ee369e70 Introduce method in MockHttpServletRequestBuilder to set remote address
Co-authored-by: Sam Brannen <sam@sambrannen.com>
Closes gh-30497
2023-05-16 17:30:48 +02:00
Simon Baslé f5bc084ce2 Merge branch '6.0.x' (javadoc changes) 2023-05-16 15:05:42 +02:00
Sam Brannen d86cfc949d Polishing 2023-05-15 16:07:15 +02:00
Sam Brannen 1d708510df Merge branch '6.0.x' 2023-05-15 15:23:16 +02:00
Sam Brannen 1275b10d9c Merge branch '6.0.x' 2023-05-15 15:09:56 +02:00
Sébastien Deleuze 1f3dd3e2ee Merge branch '6.0.x' 2023-05-15 11:49:31 +02:00
Sam Brannen c227fbfdf2 Reorganize helper methods to align with first usage principle 2023-05-13 19:29:48 +02:00
Sam Brannen f3f3dc693a Fix logic for validating @RecordApplicationEvents config
Prior to this commit, the SpringExtension looked up the
TestInstance.Lifecycle and ExecutionMode using
TestContextAnnotationUtils; however, using TestContextAnnotationUtils is
problematic since the TestInstance.Lifecycle and ExecutionMode can be
configured globally via configuration parameters instead of locally via
the @TestInstance and @Execution annotations.

This commit addresses these issues by looking up the
TestInstance.Lifecycle and ExecutionMode via JUnit Jupiter's
ExtensionContext which takes into account both global and local
configuration.

See gh-30020
2023-05-13 19:27:46 +02:00
Sam Brannen 5e1a4740d7 Suppress unused warning in test 2023-05-13 18:49:44 +02:00
Brian Clozel b4866e51b2 Configure toolchain locations when building PRs
This commit updates the script that builds PRs to add locations to the
installed JDK within the CI image. Without this change, PRs fail to
build because of the recently introduced JDK21 toolchain requirement in
the Spring Framework build.

Closes gh-30472
2023-05-12 11:33:17 +02:00
rstoyanchev a9fa813a75 Merge branch '6.0.x' 2023-05-12 10:20:11 +01:00
Sébastien Deleuze d99c2cf31e Refine JVM checkpoint/restore support
This commit:
 - Refine the wording used in logs and Javadoc
 - Avoid calling awaitPreventShutdownBarrier() in afterRestore()
 - Add logs to print the restart duration

See gh-29921
2023-05-12 09:29:35 +02:00
Stephane Nicoll ffa431da7b Merge branch '6.0.x' 2023-05-12 08:59:57 +02:00
Brian Clozel a91effcd86 Ignore some FreeMarker tests for JDK21
This reverts commit 07a5d8c91 and instead disables the relevant
FreeMarker tests for JDK21+ runs.
2023-05-11 18:00:23 +02:00
Brian Clozel 07a5d8c91c Fix FreeMarker tests for JDK21 build
Prior to this commit, some FreeMarker tests would fail when involving
property lookup in a template file, where this bean property is linked
with a method implemented as a default method on an interface.

While I did not manage to reproduce this behavior in an independent test
case, this is most likely related to a change in JDK 21:
https://bugs.openjdk.org/browse/JDK-8071693

This commit changes the template expression to using the default method
directly.
2023-05-11 17:10:26 +02:00
Stephane Nicoll 0bd0578ff9 Merge branch '6.0.x' 2023-05-11 16:36:38 +02:00
Brian Clozel 2f3a73a59e Merge branch '6.0.x' 2023-05-11 15:28:30 +02:00
Juergen Hoeller 1e74d793a2 Merge branch '6.0.x' 2023-05-10 18:51:40 +02:00
rstoyanchev 3d96e3f8c8 Merge branch '6.0.x' 2023-05-10 17:39:56 +01:00
Sam Brannen d8215a62f6 Merge branch '6.0.x' 2023-05-10 17:40:42 +02:00
Juergen Hoeller 841124af75 Merge branch '6.0.x' 2023-05-10 17:19:39 +02:00
Simon Baslé dbc42f56b3 Merge branch '6.0.x' 2023-05-10 15:18:57 +02:00
Sam Brannen 96fbcb26c9 Merge branch '6.0.x' 2023-05-10 14:36:09 +02:00
Sam Brannen 6a6dd599a1 Remove duplicate Checkstyle suppressions 2023-05-10 14:31:24 +02:00
Sam Brannen 5e52259fb3 Suppress warnings in tests 2023-05-10 14:27:43 +02:00
Sam Brannen e51c71bcd6 Delete unused code 2023-05-10 14:26:41 +02:00
Stephane Nicoll 4d5e235166 Merge branch '6.0.x' 2023-05-10 13:55:36 +02:00
rstoyanchev 5236d17ac4 Re-enable flaky tests after bd66c70b
See gh-29287
2023-05-10 11:44:32 +01:00
rstoyanchev d9f659171b Merge branch '6.0.x' 2023-05-10 11:43:38 +01:00
Stephane Nicoll 993a69d3a9 Deprecate spring context indexer
This commit deprecates the context indexer as our efforts on the AOT
engine cover the indexer in a much broader fashion.

Closes gh-30431
2023-05-10 09:13:57 +02:00
rstoyanchev 944305b9f1 Merge branch '6.0.x' 2023-05-09 19:59:11 +01:00
Stephane Nicoll 6eb63f778e Merge branch '6.0.x' 2023-05-09 16:27:23 +02:00
Stephane Nicoll eb240016ed Merge branch '6.0.x' 2023-05-09 14:52:49 +02:00
Arjen Poutsma b674906e3a Document @ResponseStatus behavior
This commit documents that the ResponseStatus annotation does not
override the status set through other means.

Closes gh-30305
See gh-18019
2023-05-09 14:08:04 +02:00
rstoyanchev bc7ba8cf2b Merge branch '6.0.x'
Closes gh-30435
2023-05-09 12:16:13 +01:00
Brian Clozel b4649137fc Merge branch '6.0.x' 2023-05-09 09:34:33 +02:00
Arjen Poutsma c8b920a05d Fix SimpleAsyncTaskExecutor virtual thread path
This commit makes sure that the SimpleAsyncTaskExecutor only creates a
virtual thread when the virtualThreadDelegate is set, and not an
additional kernel thread as well.

See gh-30241
2023-05-08 16:59:35 +02:00
Brian Clozel c564725f46 Upgrade to Java 21 EA build 21 2023-05-08 15:42:35 +02:00
Brian Clozel e599fec685 Make JDK21 available as toolchain in main build
See gh-30339
See gh-30241
2023-05-08 15:27:30 +02:00
Juergen Hoeller 657fe1fa43 Merge branch '6.0.x' 2023-05-08 14:52:26 +02:00
Juergen Hoeller 697d5e6247 Configuration options for virtual threads (on JDK 21)
VirtualThreadDelegate built on JDK 21 for multi-release jar.
Includes dedicated VirtualThreadTaskExecutor as lean option.
Includes setVirtualThreads flag on SimpleAsyncTaskExecutor.
Includes additional default methods on AsyncTaskExecutor.

Closes gh-30241
2023-05-08 11:22:47 +02:00
Juergen Hoeller d8d7e0a762 Deprecate default constructor on ConcurrentTaskExecutor/Scheduler
Includes revision of null Executor configuration.
Respects TaskDecorator configuration on DefaultManagedTaskExecutor.

Closes gh-27914
2023-05-08 11:21:29 +02:00
rstoyanchev 73d30dd875 Polishing contribution
Closes gh-30403
2023-05-05 20:53:52 +01:00
Olga MaciaszekSharma 033548a760 Remove default blockTimeout on interface clients
See gh-30403
2023-05-05 20:53:52 +01:00
Sébastien Deleuze e416dfdbc0 Merge branch '6.0.x' 2023-05-05 18:20:43 +02:00
rstoyanchev 592a3074e1 Merge branch '6.0.x'
Closes gh-30430
2023-05-05 16:57:08 +01:00
Simon Baslé b39e93d0d1 Add test support to record async events, with Junit5 caveat
This commit modifies the way the `@RecordApplicationEvents` annotation
works in tests, allowing for capture of events from threads other than
the main test thread (async events) and for the assertion of captured
event from a separate thread (e.g. when using `Awaitility`).

This is done by switching the `ApplicationEventsHolder` to use an
`InheritedThreadLocal`.

There is a mutual exclusion between support of asynchronous events vs
support of JUnit5 parallel tests with the `@TestInstance(PER_CLASS)`
mode. As a result, we favor the former and now `SpringExtension` will
invalidate a test class that is annotated (or meta-annotated, or
enclosed-annotated) with `@RecordApplicationEvents` AND
`@TestInstance(PER_CLASS)` AND `@Execution(CONCURRENT)`.

See gh-29827
Closes gh-30020
2023-05-05 17:10:41 +02:00
Simon Baslé 906c54faff Add SpEL support for registered MethodHandles
This commit adds support for MethodHandles in SpEL, using the same
syntax as user-defined functions (which also covers reflective Methods).

The most benefit is expected with handles that capture a static method
with no arguments, or with fully bound handles (where all the arguments
have been bound, including a target instance as first bound argument
if necessary). Partially bound MethodHandle should also be supported.

A best effort approach is taken to detect varargs as there is no API
support to determine if an argument is a vararg or an explicit array,
unlike with Method. Argument conversions are also applied. Finally,
array repacking is not always necessary with varargs so it is only
performed when the vararg is the sole argument to the invoked method.

See gh-27099
Closes gh-30045
2023-05-05 16:18:25 +02:00
Simon Baslé d3c3088c6b Clarify behavior of the Elvis SpEL operator in documentation (#30352)
This commit improves both the javadoc and the reference guide section on
the Elvis SpEL operator to clarify that in addition to `null` objects,
empty Strings also lead the operator to evaluate to its second operand.

The reference guide's advanced snippet is modified to use such an empty
String instead of `null` to make that behavior prominent with some code.

See gh-30318
Closes gh-30352
2023-05-05 16:13:06 +02:00
Stephane Nicoll f2c0b30641 Merge branch '6.0.x' 2023-05-05 15:04:15 +02:00
Rob Winch 601de66c03 nohttp exclude **/.gradle/** 2023-05-04 17:47:37 +01:00
rstoyanchev 91ea2d4c27 Merge branch '6.0.x'
Closes gh-30414
2023-05-04 15:46:54 +01:00
Sébastien Deleuze b7884ddd02 Disable flaky ReactorNetty2StompBrokerRelayIntegrationTests
See gh-29287
2023-05-04 16:37:17 +02:00
Sébastien Deleuze 9fec516560 Skip CRaC support with native images
Skip CRaC support with native images to avoid compiling related
infrastructure to the native image and to fix tests via
gh-30422.

See gh-30242
2023-05-04 15:36:29 +02:00
Sébastien Deleuze f469eff97b Update RuntimeHintsAgentPlugin to use the native image code path
RuntimeHintsAgentPlugin purpose is to verify that the reflective
invocations expected match the ones performed.

gh-30242 introduces a code path where the reflective invocation
is skipped when running on native.

This issue set the system property "org.graalvm.nativeimage.imagecode"
to "runtime" when running runtime tests with the agent in order to
make sure the code path tested for those tests is the native one.

Closes gh-30422
2023-05-04 15:31:31 +02:00
Stephane Nicoll 69cde11a51 Merge branch '6.0.x' 2023-05-04 12:23:46 +02:00
Juergen Hoeller 7d2047cdbd Prevent JVM shutdown before checkpoint
See gh-30242
2023-05-03 18:16:32 +02:00
Juergen Hoeller c76116b970 Keep CracResourceAdapter strongly reachable
See gh-30242
2023-05-03 17:50:36 +02:00
Juergen Hoeller 8d604350e4 Support for Project CRaC in DefaultLifecycleProcessor
Includes stopForRestart/restartAfterStop functionality.

Closes gh-30242
2023-05-03 16:01:41 +02:00
Juergen Hoeller 4db724984e Suppress deprecations for compiling on JDK 19/20 2023-05-03 10:17:12 +02:00
Juergen Hoeller 87942ed71d Support for SequencedCollection/Set/Map (on JDK 21)
Includes consistent support for MultiValueMap.

Closes gh-30239
2023-05-03 10:16:25 +02:00
Juergen Hoeller dd871e0d8c Trigger rollback for (Completable)Future exception on method return
Closes gh-30018
2023-05-03 10:03:31 +02:00
Juergen Hoeller c1014f5989 Relax relative path existence assertion for parsed URIs
See gh-29481
See gh-28522
2023-05-01 00:01:49 +02:00
Juergen Hoeller 6a2262969e Upgrade to Tomcat 10.1.8, Jetty 11.0.15, Netty 4.1.92, Mockito 5.3.1 2023-04-30 23:08:28 +02:00
Juergen Hoeller d39e44c209 Deprecate AutowireCapableBeanFactory.createBean(Class, int, boolean)
Includes consistent usage of createBean(Class) in SpringBeanJobFactory and SpringBeanContainer.

Closes gh-30345
See gh-29855
See gh-30041
2023-04-30 23:07:29 +02:00
Juergen Hoeller 4d15b58ca4 Remove LocalVariableTableParameterNameDiscoverer
Closes gh-29559
2023-04-30 23:05:09 +02:00
Juergen Hoeller 9342317291 Avoid use of java.net.URL constructors (for JDK 20 compatibility)
Explicit path computation also leads to consistent relative path semantics for resource URLs.

Closes gh-29481
Closes gh-28522
2023-04-30 23:03:39 +02:00
Sam Brannen dfd74495bd Merge branch '6.0.x' 2023-04-28 16:09:15 +02:00
Sébastien Deleuze 90ffa95b6a Merge branch '6.0.x' 2023-04-28 10:00:59 +02:00
Stephane Nicoll 825f160369 Merge branch '6.0.x' 2023-04-26 16:55:10 +02:00
Stephane Nicoll 834e694e94 Merge branch '6.0.x' 2023-04-26 14:39:58 +02:00
Sam Brannen ce3e9b0c29 Polishing 2023-04-26 11:21:34 +02:00
Sam Brannen 1c7ceaa2ca Improve Javadoc for AbstractEnvironment and Profiles 2023-04-26 11:09:38 +02:00
Sam Brannen 7e0620a143 Use new Environment.matchesProfiles() method 2023-04-26 11:09:38 +02:00
Sam Brannen fb039a3e0c Merge branch '6.0.x' 2023-04-26 10:37:35 +02:00
Sam Brannen 05e4234525 Merge branch '6.0.x' 2023-04-25 19:52:06 +02:00
Sam Brannen e19472568f Merge branch '6.0.x' 2023-04-25 18:11:35 +02:00
Sam Brannen 845488af8d Improve toString() for Profiles.of() implementation
Prior to this commit, the toString() implementation in ProfilesParser
(which is the internal implementation of Profiles.of()) concatenated
profile expressions with " or ".

For example, the string representation of
Profiles.of("spring & framework", "java | kotlin") was
"spring & framework or java | kotlin".

This commit improves the toString() implementation by wrapping
individual profile expressions in parentheses and concatenating them
with " | ".

For example, the string representation from the previous example is now
"(spring & framework) | (java | kotlin)".

This makes it easier to read (for example, when debugging) and
comprehend.

As an additional benefit, the result of invoking toString() can even be
used as a logically equivalent composite profile expression, though
that is not the primary goal of this change.

Closes gh-30374
2023-04-25 16:49:34 +02:00
Sam Brannen b924b7b4c6 Merge branch '6.0.x' 2023-04-25 15:15:45 +02:00
Sam Brannen b368fc03ea Merge branch '6.0.x' 2023-04-25 14:09:30 +02:00
Sam Brannen 982a094646 Rename determineResource() to resolveResource()
See gh-30369
2023-04-25 11:35:00 +02:00
Sam Brannen dfe320f172 Reorganize methods in ReloadableResourceBundleMessageSource
See gh-30369
2023-04-25 11:08:56 +02:00
Sam Brannen 2537c7c735 Polish Javadoc in ReloadableResourceBundleMessageSource
See gh-30369
2023-04-25 11:08:56 +02:00
Arjen Poutsma 54853ee239 Support setCharacterEncoding(null) in MockHttpServletResponse
Closes gh-30341
2023-04-25 10:53:44 +02:00
Brian Clozel b408cee29f Merge branch '6.0.x' 2023-04-25 10:26:40 +02:00
Simon Baslé e63d89973a Add ReloadableResourceBundleMessageSource Resource selection hook
This commit enables sub-classes to better customize resource selection
and resource loading in `ReloadableResourceBundleMessageSource`, without
a need to duplicate the caching logic of `refreshProperties`.

See gh-30334
Closes gh-30369
2023-04-24 17:57:45 +02:00
Han Li b311088f5a Optimize DefaultLifecycleProcessor::stopBeans
This commit optimizes `DefaultLifecycleProcessor::stopBeans` by using a
`TreeMap` when gathering the `LifecycleGroup`s during stopBeans. It also
switches to a functional style using `computeIfAbsent`.

Finally, it further optimizes `LifecycleGroup` by removing sorting of
`LifecycleGroupMember` members list entirely, turning the class into a
simple record.
This is possible because the members list is already comprised of
members which all share the same phase value, so sorting according to
each member's phase is redundant.

Closes gh-30361

Co-authored-by: Simon Baslé <sbasle@vmware.com>
2023-04-24 16:45:24 +02:00
Arjen Poutsma d9c534b13d Merge branch '6.0.x' 2023-04-24 12:56:37 +02:00
Arjen Poutsma 95e311fd02 Merge branch '6.0.x' 2023-04-24 12:30:16 +02:00
Kai Zander 5b2aa0be81 Fix link in Javadoc of PassThroughFilterChain
Closes gh-30365
2023-04-23 15:50:14 +02:00
Simon Baslé 33b4995547 Fix glob pattern in CheckstyleConventions to be Windows-compatible
On windows, the `*` character is not allowed in a directory's name.
When trying to append a glob pattern to a `Path` (`path.resolve("**")`),
if the underlying `Path.getFileSystem()` is windows then an
`InvalidPathException` is thrown.

The NoHttp plugin doesn't really need a `Path` but a glob `String`, so
this commit uses a simple String concatenation in order to append the
wildcard part (with the canonical path separator for good measure).

Closes gh-30355
2023-04-20 16:44:58 +02:00
Sam Brannen c16dab236a Merge branch '6.0.x' 2023-04-20 16:21:16 +02:00
Stephane Nicoll 202c46035a Merge branch '6.0.x' 2023-04-20 15:49:01 +02:00
Stephane Nicoll ea83d66fb5 Merge branch '6.0.x' 2023-04-20 15:43:19 +02:00
Arjen Poutsma 9f85e397d4 Support XmlSeeAlso in Jaxb2XmlDecoder
This commit adds support for the @XmlSeeAlso annotation in the
Jaxb2XmlDecoder. This includes

- Finding the set of possible qualified names given a class name, rather
  than a single name.
- Splitting the XMLEvent stream when coming across one of the names in
  this set.

Closes gh-30167
2023-04-20 10:46:27 +02:00
Sam Brannen 7df2e2a8d2 Remove APIs deprecated for removal in 6.1
This is the first commit that removes deprecated APIs.

Subsequent commits will remove additional deprecated APIs.

See gh-29449
2023-04-19 17:23:49 +02:00
Sam Brannen d446de62a4 Update copyright headers 2023-04-19 15:55:11 +02:00
Arjen Poutsma dd9f03d9b9 Merge pull request #29890 from making:support-biconsumer-validator
* gh-29890:
  Polish contribution
  Introduce functional factory methods in Validator
2023-04-19 15:19:45 +02:00
Arjen Poutsma 3c57d5518a Polish contribution
- Split Validator::of into two factory methods:
  - forInstanceOf using Class::isAssignableFrom
  - forType using Class::equals
- Moved anonymous implementation into TypedValidator with default access

Closes gh-30341
2023-04-19 15:17:31 +02:00
Toshiaki Maki 5f98afc180 Introduce functional factory methods in Validator
This commit introduces `of` method in `Validator` to provide a way to
create a validator for the specific type `<T>` using `BiConsumer<T, Errors>`
and define the validator in a functional way.
This also eliminates the boilerplate for implementing the `supports` method.
2023-04-19 13:54:51 +02:00
Arjen Poutsma 7492c0ea03 Merge branch '6.0.x' 2023-04-18 15:35:36 +02:00
Sam Brannen 65a430bb42 Upgrade to Checkstyle 10.9.3 again
Commit ed4404f350 inadvertently switched back to 10.9.1.
2023-04-18 14:09:43 +02:00
Stephane Nicoll bd62f47afa Merge pull request #30340 from jstansel
* pr/30340:
  Fix link to releases feed

Closes gh-30340
2023-04-17 18:02:25 +02:00
James Stansell 3e0aa4c603 Fix link to releases feed
See gh-30340
2023-04-17 18:02:01 +02:00
Brian Clozel 8b5bfdfa44 Fix CI image reference to JDK21
See gh-30339
2023-04-15 10:06:09 +02:00
Brian Clozel da88ac8cca Move NoHttp config to Gradle conventions
See gh-30339
2023-04-14 12:31:43 +02:00
Brian Clozel a66f3e02f4 Upgrade CI Concourse resources 2023-04-14 10:39:39 +02:00
Brian Clozel 7f44861f32 Configure CI pipeline for 6.1.x milestone 2023-04-14 10:39:03 +02:00
Brian Clozel 63e20404a2 Configure JDK 21 CI build
This commit replaces the JDK 20 compatibility build with a JDK 21 (early
access) variant.

See gh-30339
2023-04-14 10:35:51 +02:00
Brian Clozel 5c574b9878 Refactor toolchain support in Gradle build
This commit removes the `-PmainToolchain` option from our build, since
it was not broadly used. Instead, the language level is now configured
in the `JavaConventions` for JDK 17.

The `-PtestToolchain` option is still available for testing Spring
Framework with other JDKs (i.e., compiling and running tests with a JDK
that's not the baseline).

See gh-30339
2023-04-14 10:30:12 +02:00
Brian Clozel 9562a1c146 Move build test configuration to Gradle convention
This commit moves the Gradle test configuration to a convention so it
can be applied to all test tasks, including from other source sets.

See gh-30339
2023-04-14 10:27:56 +02:00
Brian Clozel ed4404f350 Move checkstyle config to Gradle convention
This commit moves the checkstyle conventions from the build.gradle
script to a buildSrc convention, ensuring that the same configuration is
applied to all checkstyle tasks.

See gh-30339
2023-04-14 10:27:52 +02:00
Brian Clozel bd55b647c7 Switch to 6.1.0-SNAPSHOT version 2023-04-14 10:17:09 +02:00
Spring Builds 19c7527376 Next development version (v6.0.9-SNAPSHOT) 2023-04-13 08:30:07 +00:00
1174 changed files with 53275 additions and 15462 deletions
+6 -6
View File
@@ -123,13 +123,13 @@ define the source file coding standards we use along with some IDEA editor setti
### Reference Docs
The reference documentation is in the [framework-docs/src/docs/asciidoc](framework-docs/src/docs/asciidoc) directory, in
[Asciidoctor](https://asciidoctor.org/) format. For trivial changes, you may be able to browse,
edit source files, and submit directly from GitHub.
The reference documentation is authored in [Asciidoctor](https://asciidoctor.org/) format
using [Antora](https://docs.antora.org/antora/latest/). The source files for the documentation
reside in the [framework-docs/modules/ROOT](framework-docs/modules/ROOT) directory. For
trivial changes, you may be able to browse, edit source files, and submit directly from GitHub.
When making changes locally, execute `./gradlew :framework-docs:asciidoctor` and then browse the result under
`framework-docs/build/docs/ref-docs/html5/index.html`.
When making changes locally, execute `./gradlew antora` and then browse the results under
`framework-docs/build/site/index.html`.
Asciidoctor also supports live editing. For more details see
[AsciiDoc Tooling](https://docs.asciidoctor.org/asciidoctor/latest/tooling/).
+3 -3
View File
@@ -2,7 +2,7 @@
This is the home of the Spring Framework: the foundation for all [Spring projects](https://spring.io/projects). Collectively the Spring Framework and the family of Spring projects are often referred to simply as "Spring".
Spring provides everything required beyond the Java programming language for creating enterprise applications for a wide range of scenarios and architectures. Please read the [Overview](https://docs.spring.io/spring/docs/current/spring-framework-reference/overview.html#spring-introduction) section as reference for a more complete introduction.
Spring provides everything required beyond the Java programming language for creating enterprise applications for a wide range of scenarios and architectures. Please read the [Overview](https://docs.spring.io/spring-framework/reference/overview.html) section of the reference documentation for a more complete introduction.
## Code of Conduct
@@ -14,7 +14,7 @@ For access to artifacts or a distribution zip, see the [Spring Framework Artifac
## Documentation
The Spring Framework maintains reference documentation ([published](https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/) and [source](framework-docs/src/docs/asciidoc)), GitHub [wiki pages](https://github.com/spring-projects/spring-framework/wiki), and an
The Spring Framework maintains reference documentation ([published](https://docs.spring.io/spring-framework/reference/) and [source](framework-docs/modules/ROOT)), GitHub [wiki pages](https://github.com/spring-projects/spring-framework/wiki), and an
[API reference](https://docs.spring.io/spring-framework/docs/current/javadoc-api/). There are also [guides and tutorials](https://spring.io/guides) across Spring projects.
## Micro-Benchmarks
@@ -31,7 +31,7 @@ Information regarding CI builds can be found in the [Spring Framework Concourse
## Stay in Touch
Follow [@SpringCentral](https://twitter.com/springcentral), [@SpringFramework](https://twitter.com/springframework), and its [team members](https://twitter.com/springframework/lists/team/members) on Twitter. In-depth articles can be found at [The Spring Blog](https://spring.io/blog/), and releases are announced via our [news feed](https://spring.io/blog/category/news).
Follow [@SpringCentral](https://twitter.com/springcentral), [@SpringFramework](https://twitter.com/springframework), and its [team members](https://twitter.com/springframework/lists/team/members) on Twitter. In-depth articles can be found at [The Spring Blog](https://spring.io/blog/), and releases are announced via our [releases feed](https://spring.io/blog/category/releases).
## License
+19 -67
View File
@@ -1,22 +1,24 @@
plugins {
id 'io.spring.nohttp' version '0.0.11'
id 'io.freefair.aspectj' version '8.0.1' apply false
id 'io.freefair.aspectj' version '8.3' apply false
// kotlinVersion is managed in gradle.properties
id 'org.jetbrains.kotlin.plugin.serialization' version "${kotlinVersion}" apply false
id 'org.jetbrains.dokka' version '1.8.10'
id 'org.jetbrains.dokka' version '1.8.20'
id 'org.unbroken-dome.xjc' version '2.0.0' apply false
id 'com.github.ben-manes.versions' version '0.46.0'
id 'com.github.johnrengelman.shadow' version '8.1.1' apply false
id 'de.undercouch.download' version '5.4.0'
id 'me.champeau.jmh' version '0.7.1' apply false
id 'me.champeau.mrjar' version '0.1.1'
}
ext {
moduleProjects = subprojects.findAll { it.name.startsWith("spring-") }
javaProjects = subprojects - project(":framework-bom") - project(":framework-platform")
javaProjects = subprojects.findAll { !it.name.startsWith("framework-") }
}
configure(allprojects) { project ->
apply plugin: "org.springframework.build.localdev"
group = "org.springframework"
repositories {
mavenCentral()
maven {
@@ -41,16 +43,7 @@ configure(allprojects) { project ->
}
}
configure([rootProject] + javaProjects) { project ->
group = "org.springframework"
apply plugin: "java"
apply plugin: "java-test-fixtures"
apply plugin: "checkstyle"
apply plugin: 'org.springframework.build.conventions'
apply from: "${rootDir}/gradle/toolchains.gradle"
apply from: "${rootDir}/gradle/ide.gradle"
configure(allprojects - project(":framework-platform")) {
configurations {
dependencyManagement {
canBeConsumed = false
@@ -59,36 +52,19 @@ configure([rootProject] + javaProjects) { project ->
}
matching { it.name.endsWith("Classpath") }.all { it.extendsFrom(dependencyManagement) }
}
test {
useJUnitPlatform()
include(["**/*Tests.class", "**/*Test.class"])
systemProperty("java.awt.headless", "true")
systemProperty("testGroups", project.properties.get("testGroups"))
systemProperty("io.netty.leakDetection.level", "paranoid")
systemProperty("io.netty5.leakDetectionLevel", "paranoid")
systemProperty("io.netty5.leakDetection.targetRecords", "32")
systemProperty("io.netty5.buffer.lifecycleTracingEnabled", "true")
systemProperty("io.netty5.buffer.leakDetectionEnabled", "true")
jvmArgs(["--add-opens=java.base/java.lang=ALL-UNNAMED",
"--add-opens=java.base/java.util=ALL-UNNAMED"])
}
checkstyle {
toolVersion = "10.12.3"
configDirectory.set(rootProject.file("src/checkstyle"))
}
tasks.named("checkstyleMain").configure {
maxHeapSize = "1g"
}
tasks.named("checkstyleTest").configure {
maxHeapSize = "1g"
}
dependencies {
dependencyManagement(enforcedPlatform(dependencies.project(path: ":framework-platform")))
}
}
configure([rootProject] + javaProjects) { project ->
apply plugin: "java"
apply plugin: "java-test-fixtures"
apply plugin: 'org.springframework.build.conventions'
apply from: "${rootDir}/gradle/toolchains.gradle"
apply from: "${rootDir}/gradle/ide.gradle"
dependencies {
testImplementation("org.junit.jupiter:junit-jupiter-api")
testImplementation("org.junit.jupiter:junit-jupiter-params")
testImplementation("org.junit.platform:junit-platform-suite-api")
@@ -106,7 +82,6 @@ configure([rootProject] + javaProjects) { project ->
// JSR-305 only used for non-required meta-annotations
compileOnly("com.google.code.findbugs:jsr305")
testCompileOnly("com.google.code.findbugs:jsr305")
checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:0.0.39")
}
ext.javadocLinks = [
@@ -127,7 +102,7 @@ configure([rootProject] + javaProjects) { project ->
// 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.9.3/api/",
// "https://junit.org/junit5/docs/5.10.0/api/",
"https://www.reactive-streams.org/reactive-streams-1.0.3-javadoc/",
"https://javadoc.io/static/io.rsocket/rsocket-core/1.1.1/",
"https://r2dbc.io/spec/1.0.0.RELEASE/api/",
@@ -144,28 +119,5 @@ configure(moduleProjects) { project ->
configure(rootProject) {
description = "Spring Framework"
apply plugin: "io.spring.nohttp"
apply plugin: 'org.springframework.build.api-diff'
nohttp {
source.exclude "**/test-output/**"
source.exclude "**/.gradle/**"
allowlistFile = project.file("src/nohttp/allowlist.lines")
def rootPath = file(rootDir).toPath()
def projectDirs = allprojects.collect { it.projectDir } + "${rootDir}/buildSrc"
projectDirs.forEach { dir ->
[ 'bin', 'build', 'out', '.settings' ]
.collect { rootPath.relativize(new File(dir, it).toPath()) }
.forEach { source.exclude "$it/**" }
[ '.classpath', '.project' ]
.collect { rootPath.relativize(new File(dir, it).toPath()) }
.forEach { source.exclude "$it" }
}
}
tasks.named("checkstyleNohttp").configure {
maxHeapSize = "1g"
}
}
+11 -2
View File
@@ -1,5 +1,7 @@
plugins {
id 'java-gradle-plugin'
id 'checkstyle'
id 'io.spring.javaformat' version "${javaFormatVersion}"
}
repositories {
@@ -17,10 +19,13 @@ ext {
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}")
implementation("org.jetbrains.kotlin:kotlin-compiler-embeddable:${kotlinVersion}")
checkstyle "io.spring.javaformat:spring-javaformat-checkstyle:${javaFormatVersion}"
implementation "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}"
implementation "org.jetbrains.kotlin:kotlin-compiler-embeddable:${kotlinVersion}"
implementation "me.champeau.gradle:japicmp-gradle-plugin:0.3.0"
implementation "org.gradle:test-retry-gradle-plugin:1.4.1"
implementation "io.spring.javaformat:spring-javaformat-gradle-plugin:${javaFormatVersion}"
implementation "io.spring.nohttp:nohttp-gradle:0.0.11"
}
gradlePlugin {
@@ -33,6 +38,10 @@ gradlePlugin {
id = "org.springframework.build.conventions"
implementationClass = "org.springframework.build.ConventionsPlugin"
}
localDevPlugin {
id = "org.springframework.build.localdev"
implementationClass = "org.springframework.build.dev.LocalDevelopmentPlugin"
}
optionalDependenciesPlugin {
id = "org.springframework.build.optional-dependencies"
implementationClass = "org.springframework.build.optional.OptionalDependenciesPlugin"
+1
View File
@@ -1 +1,2 @@
org.gradle.caching=true
javaFormatVersion=0.0.39
@@ -0,0 +1,79 @@
/*
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.build;
import java.io.File;
import java.nio.file.Path;
import java.util.List;
import io.spring.javaformat.gradle.SpringJavaFormatPlugin;
import io.spring.nohttp.gradle.NoHttpExtension;
import io.spring.nohttp.gradle.NoHttpPlugin;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.artifacts.DependencySet;
import org.gradle.api.plugins.JavaBasePlugin;
import org.gradle.api.plugins.quality.Checkstyle;
import org.gradle.api.plugins.quality.CheckstyleExtension;
import org.gradle.api.plugins.quality.CheckstylePlugin;
/**
* {@link Plugin} that applies conventions for checkstyle.
*
* @author Brian Clozel
*/
public class CheckstyleConventions {
/**
* Applies the Spring Java Format and Checkstyle plugins with the project conventions.
* @param project the current project
*/
public void apply(Project project) {
project.getPlugins().withType(JavaBasePlugin.class, (java) -> {
if (project.getRootProject() == project) {
configureNoHttpPlugin(project);
}
project.getPlugins().apply(CheckstylePlugin.class);
project.getTasks().withType(Checkstyle.class).forEach(checkstyle -> checkstyle.getMaxHeapSize().set("1g"));
CheckstyleExtension checkstyle = project.getExtensions().getByType(CheckstyleExtension.class);
checkstyle.setToolVersion("10.12.3");
checkstyle.getConfigDirectory().set(project.getRootProject().file("src/checkstyle"));
String version = SpringJavaFormatPlugin.class.getPackage().getImplementationVersion();
DependencySet checkstyleDependencies = project.getConfigurations().getByName("checkstyle").getDependencies();
checkstyleDependencies
.add(project.getDependencies().create("io.spring.javaformat:spring-javaformat-checkstyle:" + version));
});
}
private static void configureNoHttpPlugin(Project project) {
project.getPlugins().apply(NoHttpPlugin.class);
NoHttpExtension noHttp = project.getExtensions().getByType(NoHttpExtension.class);
noHttp.setAllowlistFile(project.file("src/nohttp/allowlist.lines"));
noHttp.getSource().exclude("**/test-output/**", "**/.settings/**",
"**/.classpath", "**/.project", "**/.gradle/**");
List<String> buildFolders = List.of("bin", "build", "out");
project.allprojects(subproject -> {
Path rootPath = project.getRootDir().toPath();
Path projectPath = rootPath.relativize(subproject.getProjectDir().toPath());
for (String buildFolder : buildFolders) {
Path innerBuildDir = projectPath.resolve(buildFolder);
noHttp.getSource().exclude(innerBuildDir + File.separator + "**");
}
});
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
@@ -25,10 +25,8 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinBasePlugin;
* Plugin to apply conventions to projects that are part of Spring Framework's build.
* Conventions are applied in response to various plugins being applied.
*
* When the {@link JavaBasePlugin} is applied, the conventions in {@link TestConventions}
* are applied.
* When the {@link JavaBasePlugin} is applied, the conventions in {@link JavaConventions}
* are applied.
* <p>When the {@link JavaBasePlugin} is applied, the conventions in {@link CheckstyleConventions},
* {@link TestConventions} and {@link JavaConventions} are applied.
* When the {@link KotlinBasePlugin} is applied, the conventions in {@link KotlinConventions}
* are applied.
*
@@ -38,8 +36,10 @@ public class ConventionsPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
new CheckstyleConventions().apply(project);
new JavaConventions().apply(project);
new KotlinConventions().apply(project);
new TestConventions().apply(project);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
@@ -24,7 +24,9 @@ import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.plugins.JavaBasePlugin;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.tasks.compile.JavaCompile;
import org.gradle.jvm.toolchain.JavaLanguageVersion;
/**
* {@link Plugin} that applies conventions for compiling Java sources in Spring Framework.
@@ -68,6 +70,8 @@ public class JavaConventions {
* @param project the current project
*/
private void applyJavaCompileConventions(Project project) {
project.getExtensions().getByType(JavaPluginExtension.class)
.getToolchain().getLanguageVersion().set(JavaLanguageVersion.of(17));
project.getTasks().withType(JavaCompile.class)
.matching(compileTask -> compileTask.getName().equals(JavaPlugin.COMPILE_JAVA_TASK_NAME))
.forEach(compileTask -> {
@@ -16,6 +16,8 @@
package org.springframework.build;
import java.util.Map;
import org.gradle.api.Project;
import org.gradle.api.plugins.JavaBasePlugin;
import org.gradle.api.tasks.testing.Test;
@@ -41,11 +43,36 @@ class TestConventions {
private void configureTestConventions(Project project) {
project.getTasks().withType(Test.class,
test -> project.getPlugins().withType(TestRetryPlugin.class, testRetryPlugin -> {
TestRetryTaskExtension testRetry = test.getExtensions().getByType(TestRetryTaskExtension.class);
testRetry.getFailOnPassedAfterRetry().set(true);
testRetry.getMaxRetries().set(isCi() ? 3 : 0);
}));
test -> {
configureTests(project, test);
configureTestRetryPlugin(project, test);
});
}
private void configureTests(Project project, Test test) {
test.useJUnitPlatform();
test.include("**/*Tests.class", "**/*Test.class");
test.setSystemProperties(Map.of(
"java.awt.headless", "true",
"io.netty.leakDetection.level", "paranoid",
"io.netty5.leakDetectionLevel", "paranoid",
"io.netty5.leakDetection.targetRecords", "32",
"io.netty5.buffer.lifecycleTracingEnabled", "true"
));
if (project.hasProperty("testGroups")) {
test.systemProperty("testGroups", project.getProperties().get("testGroups"));
}
test.jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED",
"--add-opens=java.base/java.util=ALL-UNNAMED",
"-Djava.locale.providers=COMPAT");
}
private void configureTestRetryPlugin(Project project, Test test) {
project.getPlugins().withType(TestRetryPlugin.class, testRetryPlugin -> {
TestRetryTaskExtension testRetry = test.getExtensions().getByType(TestRetryTaskExtension.class);
testRetry.getFailOnPassedAfterRetry().set(true);
testRetry.getMaxRetries().set(isCi() ? 3 : 0);
});
}
private boolean isCi() {
@@ -0,0 +1,49 @@
/*
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.build.dev;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.plugins.JavaBasePlugin;
/**
* {@link Plugin} that skips documentation tasks when the {@code "-PskipDocs"} property is defined.
*
* @author Brian Clozel
*/
public class LocalDevelopmentPlugin implements Plugin<Project> {
private static final String SKIP_DOCS_PROPERTY = "skipDocs";
@Override
public void apply(Project target) {
if (target.hasProperty(SKIP_DOCS_PROPERTY)) {
skipDocumentationTasks(target);
target.subprojects(this::skipDocumentationTasks);
}
}
private void skipDocumentationTasks(Project project) {
project.afterEvaluate(p -> {
p.getTasks().matching(task -> {
return JavaBasePlugin.DOCUMENTATION_GROUP.equals(task.getGroup())
|| "distribution".equals(task.getGroup());
})
.forEach(task -> task.setEnabled(false));
});
}
}
@@ -0,0 +1,48 @@
/*
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.build.hint;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.provider.SetProperty;
import org.gradle.api.tasks.Classpath;
import org.gradle.api.tasks.Input;
import org.gradle.process.CommandLineArgumentProvider;
import java.util.Collections;
/**
* Argument provider for registering the runtime hints agent with a Java process.
*/
public interface RuntimeHintsAgentArgumentProvider extends CommandLineArgumentProvider {
@Classpath
ConfigurableFileCollection getAgentJar();
@Input
SetProperty<String> getIncludedPackages();
@Input
SetProperty<String> getExcludedPackages();
@Override
default Iterable<String> asArguments() {
StringBuilder packages = new StringBuilder();
getIncludedPackages().get().forEach(packageName -> packages.append('+').append(packageName).append(','));
getExcludedPackages().get().forEach(packageName -> packages.append('-').append(packageName).append(','));
return Collections.singleton("-javaagent:" + getAgentJar().getSingleFile() + "=" + packages);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
@@ -16,38 +16,15 @@
package org.springframework.build.hint;
import java.util.Collections;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.provider.SetProperty;
/**
* Entry point to the DSL extension for the {@link RuntimeHintsAgentPlugin} Gradle plugin.
* @author Brian Clozel
*/
public class RuntimeHintsAgentExtension {
public interface RuntimeHintsAgentExtension {
private final SetProperty<String> includedPackages;
SetProperty<String> getIncludedPackages();
private final SetProperty<String> excludedPackages;
public RuntimeHintsAgentExtension(ObjectFactory objectFactory) {
this.includedPackages = objectFactory.setProperty(String.class).convention(Collections.singleton("org.springframework"));
this.excludedPackages = objectFactory.setProperty(String.class).convention(Collections.emptySet());
}
public SetProperty<String> getIncludedPackages() {
return this.includedPackages;
}
public SetProperty<String> getExcludedPackages() {
return this.excludedPackages;
}
String asJavaAgentArgument() {
StringBuilder builder = new StringBuilder();
this.includedPackages.get().forEach(packageName -> builder.append('+').append(packageName).append(','));
this.excludedPackages.get().forEach(packageName -> builder.append('-').append(packageName).append(','));
return builder.toString();
}
SetProperty<String> getExcludedPackages();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
@@ -16,41 +16,79 @@
package org.springframework.build.hint;
import org.gradle.api.JavaVersion;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.attributes.Bundling;
import org.gradle.api.attributes.Category;
import org.gradle.api.attributes.LibraryElements;
import org.gradle.api.attributes.Usage;
import org.gradle.api.attributes.java.TargetJvmVersion;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.tasks.bundling.Jar;
import org.gradle.api.tasks.testing.Test;
import java.util.Collections;
/**
* {@link Plugin} that configures the {@code RuntimeHints} Java agent to test tasks.
*
* @author Brian Clozel
* @author Sebastien Deleuze
*/
public class RuntimeHintsAgentPlugin implements Plugin<Project> {
public static final String RUNTIMEHINTS_TEST_TASK = "runtimeHintsTest";
private static final String EXTENSION_NAME = "runtimeHintsAgent";
private static final String CONFIGURATION_NAME = "testRuntimeHintsAgentJar";
@Override
public void apply(Project project) {
project.getPlugins().withType(JavaPlugin.class, javaPlugin -> {
RuntimeHintsAgentExtension agentExtension = project.getExtensions().create(EXTENSION_NAME,
RuntimeHintsAgentExtension.class, project.getObjects());
RuntimeHintsAgentExtension agentExtension = createRuntimeHintsAgentExtension(project);
Test agentTest = project.getTasks().create(RUNTIMEHINTS_TEST_TASK, Test.class, test -> {
test.useJUnitPlatform(options -> {
options.includeTags("RuntimeHintsTests");
});
test.include("**/*Tests.class", "**/*Test.class");
test.systemProperty("java.awt.headless", "true");
});
project.afterEvaluate(p -> {
Jar jar = project.getRootProject().project("spring-core-test").getTasks().withType(Jar.class).named("jar").get();
agentTest.jvmArgs("-javaagent:" + jar.getArchiveFile().get().getAsFile() + "=" + agentExtension.asJavaAgentArgument());
test.systemProperty("org.graalvm.nativeimage.imagecode", "runtime");
test.getJvmArgumentProviders().add(createRuntimeHintsAgentArgumentProvider(project, agentExtension));
});
project.getTasks().getByName("check", task -> task.dependsOn(agentTest));
project.getDependencies().add(CONFIGURATION_NAME, project.project(":spring-core-test"));
});
}
private static RuntimeHintsAgentExtension createRuntimeHintsAgentExtension(Project project) {
RuntimeHintsAgentExtension agentExtension = project.getExtensions().create(EXTENSION_NAME, RuntimeHintsAgentExtension.class);
agentExtension.getIncludedPackages().convention(Collections.singleton("org.springframework"));
agentExtension.getExcludedPackages().convention(Collections.emptySet());
return agentExtension;
}
private static RuntimeHintsAgentArgumentProvider createRuntimeHintsAgentArgumentProvider(
Project project, RuntimeHintsAgentExtension agentExtension) {
RuntimeHintsAgentArgumentProvider agentArgumentProvider = project.getObjects().newInstance(RuntimeHintsAgentArgumentProvider.class);
agentArgumentProvider.getAgentJar().from(createRuntimeHintsAgentConfiguration(project));
agentArgumentProvider.getIncludedPackages().set(agentExtension.getIncludedPackages());
agentArgumentProvider.getExcludedPackages().set(agentExtension.getExcludedPackages());
return agentArgumentProvider;
}
private static Configuration createRuntimeHintsAgentConfiguration(Project project) {
return project.getConfigurations().create(CONFIGURATION_NAME, configuration -> {
configuration.setCanBeConsumed(false);
configuration.setTransitive(false); // Only the built artifact is required
configuration.attributes(attributes -> {
attributes.attribute(Bundling.BUNDLING_ATTRIBUTE, project.getObjects().named(Bundling.class, Bundling.EXTERNAL));
attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.getObjects().named(Category.class, Category.LIBRARY));
attributes.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, project.getObjects().named(LibraryElements.class, LibraryElements.JAR));
attributes.attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, Integer.valueOf(JavaVersion.current().getMajorVersion()));
attributes.attribute(Usage.USAGE_ATTRIBUTE, project.getObjects().named(Usage.class, Usage.JAVA_RUNTIME));
});
});
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 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.
@@ -19,6 +19,7 @@ package org.springframework.build.optional;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.plugins.JavaBasePlugin;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.tasks.SourceSetContainer;
@@ -40,10 +41,10 @@ public class OptionalDependenciesPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
Configuration optional = project.getConfigurations().create("optional");
Configuration optional = project.getConfigurations().create(OPTIONAL_CONFIGURATION_NAME);
optional.setCanBeConsumed(false);
optional.setCanBeResolved(false);
project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> {
project.getPlugins().withType(JavaBasePlugin.class, (javaBasePlugin) -> {
SourceSetContainer sourceSets = project.getExtensions().getByType(JavaPluginExtension.class)
.getSourceSets();
sourceSets.all((sourceSet) -> {
+1 -1
View File
@@ -6,6 +6,6 @@ RUN ./setup.sh
ENV JAVA_HOME /opt/openjdk/java17
ENV JDK17 /opt/openjdk/java17
ENV JDK20 /opt/openjdk/java20
ENV JDK21 /opt/openjdk/java21
ENV PATH $JAVA_HOME/bin:$PATH
+3 -3
View File
@@ -3,10 +3,10 @@ set -e
case "$1" in
java17)
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.7+7/bellsoft-jdk17.0.7+7-linux-amd64.tar.gz"
echo "https://download.bell-sw.com/java/17.0.8.1+1/bellsoft-jdk17.0.8.1+1-linux-amd64.tar.gz"
;;
java20)
echo "https://github.com/bell-sw/Liberica/releases/download/20.0.1+10/bellsoft-jdk20.0.1+10-linux-amd64.tar.gz"
java21)
echo "https://download.java.net/java/GA/jdk21/fd2272bbf8e04c3dbaee13770090416c/35/GPL/openjdk-21_linux-x64_bin.tar.gz"
;;
*)
echo $"Unknown java version"
+1 -1
View File
@@ -20,7 +20,7 @@ curl https://raw.githubusercontent.com/spring-io/concourse-java-scripts/v0.0.4/c
mkdir -p /opt/openjdk
pushd /opt/openjdk > /dev/null
for jdk in java17 java20
for jdk in java17 java21
do
JDK_URL=$( /get-jdk-url.sh $jdk )
mkdir $jdk
+2 -2
View File
@@ -3,8 +3,8 @@ github-repo-name: "spring-projects/spring-framework"
sonatype-staging-profile: "org.springframework"
docker-hub-organization: "springci"
artifactory-server: "https://repo.spring.io"
branch: "6.0.x"
milestone: "6.0.x"
branch: "main"
milestone: "6.1.x"
build-name: "spring-framework"
pipeline-name: "spring-framework"
concourse-url: "https://ci.spring.io"
+13 -17
View File
@@ -45,7 +45,7 @@ resource_types:
source:
<<: *docker-resource-source
repository: concourse/registry-image-resource
tag: 1.5.0
tag: 1.8.0
- name: artifactory-resource
type: registry-image
source:
@@ -57,7 +57,7 @@ resource_types:
source:
<<: *docker-resource-source
repository: concourse/github-release-resource
tag: 1.5.5
tag: 1.8.0
- name: github-status-resource
type: registry-image
source:
@@ -113,14 +113,14 @@ resources:
access_token: ((github-ci-status-token))
branch: ((branch))
context: build
- name: repo-status-jdk20-build
- name: repo-status-jdk21-build
type: github-status-resource
icon: eye-check-outline
source:
repository: ((github-repo-name))
access_token: ((github-ci-status-token))
branch: ((branch))
context: jdk20-build
context: jdk21-build
- name: slack-alert
type: slack-notification
icon: slack
@@ -198,26 +198,22 @@ jobs:
threads: 8
artifact_set:
- include:
- "/**/framework-docs-*.zip"
- "/**/framework-api-*.zip"
properties:
"zip.name": "spring-framework"
"zip.displayname": "Spring Framework"
"zip.deployed": "false"
- include:
- "/**/framework-docs-*-docs.zip"
- "/**/framework-api-*-docs.zip"
properties:
"zip.type": "docs"
- include:
- "/**/framework-docs-*-dist.zip"
properties:
"zip.type": "dist"
- include:
- "/**/framework-docs-*-schema.zip"
- "/**/framework-api-*-schema.zip"
properties:
"zip.type": "schema"
get_params:
threads: 8
- name: jdk20-build
- name: jdk21-build
serial: true
public: true
plan:
@@ -225,7 +221,7 @@ jobs:
- get: git-repo
- get: every-morning
trigger: true
- put: repo-status-jdk20-build
- put: repo-status-jdk21-build
params: { state: "pending", commit: "git-repo" }
- do:
- task: check-project
@@ -234,16 +230,16 @@ jobs:
privileged: true
timeout: ((task-timeout))
params:
TEST_TOOLCHAIN: 20
TEST_TOOLCHAIN: 21
<<: *build-project-task-params
on_failure:
do:
- put: repo-status-jdk20-build
- put: repo-status-jdk21-build
params: { state: "failure", commit: "git-repo" }
- put: slack-alert
params:
<<: *slack-fail-params
- put: repo-status-jdk20-build
- put: repo-status-jdk21-build
params: { state: "success", commit: "git-repo" }
- name: stage-milestone
serial: true
@@ -396,7 +392,7 @@ jobs:
groups:
- name: "builds"
jobs: ["build", "jdk20-build"]
jobs: ["build", "jdk21-build"]
- name: "releases"
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release"]
- name: "ci-images"
+2 -1
View File
@@ -4,5 +4,6 @@ set -e
source $(dirname $0)/common.sh
pushd git-repo > /dev/null
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --max-workers=4 check
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK17,JDK21 \
--no-daemon --max-workers=4 check
popd > /dev/null
+2 -1
View File
@@ -5,5 +5,6 @@ source $(dirname $0)/common.sh
repository=$(pwd)/distribution-repository
pushd git-repo > /dev/null
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --max-workers=4 -PdeploymentRepository=${repository} build publishAllPublicationsToDeploymentRepository
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK17,JDK21 \
--no-daemon --max-workers=4 -PdeploymentRepository=${repository} build publishAllPublicationsToDeploymentRepository
popd > /dev/null
+1 -1
View File
@@ -4,6 +4,6 @@ set -e
source $(dirname $0)/common.sh
pushd git-repo > /dev/null
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK17,JDK20 \
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK17,JDK21 \
-PmainToolchain=${MAIN_TOOLCHAIN} -PtestToolchain=${TEST_TOOLCHAIN} --no-daemon --max-workers=4 check antora
popd > /dev/null
+2 -1
View File
@@ -35,7 +35,8 @@ git add gradle.properties > /dev/null
git commit -m"Release v$stageVersion" > /dev/null
git tag -a "v$stageVersion" -m"Release v$stageVersion" > /dev/null
./gradlew --no-daemon --max-workers=4 -PdeploymentRepository=${repository} build publishAllPublicationsToDeploymentRepository
./gradlew --no-daemon --max-workers=4 -PdeploymentRepository=${repository} -Porg.gradle.java.installations.fromEnv=JDK17,JDK21 \
build publishAllPublicationsToDeploymentRepository
git reset --hard HEAD^ > /dev/null
if [[ $nextVersion != $snapshotVersion ]]; then
+117
View File
@@ -0,0 +1,117 @@
plugins {
id 'java-platform'
id 'io.freefair.aggregate-javadoc' version '8.3'
}
description = "Spring Framework API Docs"
apply from: "${rootDir}/gradle/publications.gradle"
repositories {
maven {
url "https://repo.spring.io/release"
}
}
dependencies {
moduleProjects.each { moduleProject ->
javadoc moduleProject
}
}
javadoc {
title = "${rootProject.description} ${version} API"
options {
encoding = "UTF-8"
memberLevel = JavadocMemberLevel.PROTECTED
author = true
header = rootProject.description
use = true
overview = "framework-docs/src/docs/api/overview.html"
splitIndex = true
links(rootProject.ext.javadocLinks)
addBooleanOption('Xdoclint:syntax,reference', true) // only check syntax and reference with doclint
addBooleanOption('Werror', true) // fail build on Javadoc warnings
}
maxMemory = "1024m"
doFirst {
classpath += files(
// ensure the javadoc process can resolve types compiled from .aj sources
project(":spring-aspects").sourceSets.main.output
)
classpath += files(moduleProjects.collect { it.sourceSets.main.compileClasspath })
}
}
/**
* Produce KDoc for all Spring Framework modules in "build/docs/kdoc"
*/
rootProject.tasks.dokkaHtmlMultiModule.configure {
dependsOn {
tasks.named("javadoc")
}
moduleName.set("spring-framework")
outputDirectory.set(project.file("$buildDir/docs/kdoc"))
}
/**
* Zip all Java docs (javadoc & kdoc) into a single archive
*/
tasks.register('docsZip', Zip) {
dependsOn = ['javadoc', rootProject.tasks.dokkaHtmlMultiModule]
group = "distribution"
description = "Builds -${archiveClassifier} archive containing api and reference " +
"for deployment at https://docs.spring.io/spring-framework/docs/."
archiveBaseName.set("spring-framework")
archiveClassifier.set("docs")
from("src/dist") {
include "changelog.txt"
}
from(javadoc) {
into "javadoc-api"
}
from(rootProject.tasks.dokkaHtmlMultiModule.outputDirectory) {
into "kdoc-api"
}
}
/**
* Zip all Spring Framework schemas into a single archive
*/
tasks.register('schemaZip', Zip) {
group = "distribution"
archiveBaseName.set("spring-framework")
archiveClassifier.set("schema")
description = "Builds -${archiveClassifier} archive containing all " +
"XSDs for deployment at https://springframework.org/schema."
duplicatesStrategy DuplicatesStrategy.EXCLUDE
moduleProjects.each { module ->
def Properties schemas = new Properties();
module.sourceSets.main.resources.find {
(it.path.endsWith("META-INF/spring.schemas") || it.path.endsWith("META-INF\\spring.schemas"))
}?.withInputStream { schemas.load(it) }
for (def key : schemas.keySet()) {
def shortName = key.replaceAll(/http.*schema.(.*).spring-.*/, '$1')
assert shortName != key
File xsdFile = module.sourceSets.main.resources.find {
(it.path.endsWith(schemas.get(key)) || it.path.endsWith(schemas.get(key).replaceAll('\\/', '\\\\')))
}
assert xsdFile != null
into(shortName) {
from xsdFile.path
}
}
}
}
publishing {
publications {
mavenJava(MavenPublication) {
artifact docsZip
artifact schemaZip
}
}
}
+3 -166
View File
@@ -8,7 +8,6 @@ description = "Spring Framework Docs"
apply from: "${rootDir}/gradle/publications.gradle"
antora {
version = '3.2.0-alpha.2'
playbook = 'cached-antora-playbook.yml'
@@ -34,23 +33,16 @@ antora {
]
}
tasks.named("generateAntoraYml") {
asciidocAttributes = project.provider( {
return ["spring-version": project.version ]
} )
}
tasks.create("generateAntoraResources") {
tasks.register("generateAntoraResources") {
dependsOn 'generateAntoraYml'
}
// Commented out for now:
// https://github.com/spring-projects/spring-framework/issues/30481
// tasks.named("check") {
// dependsOn 'antora'
// }
jar {
enabled = false
}
@@ -67,166 +59,11 @@ repositories {
dependencies {
api(project(":spring-context"))
api(project(":spring-jms"))
api(project(":spring-web"))
api("jakarta.jms:jakarta.jms-api")
api("jakarta.servlet:jakarta.servlet-api")
implementation(project(":spring-core-test"))
implementation("org.assertj:assertj-core")
}
/**
* Produce Javadoc for all Spring Framework modules in "build/docs/javadoc"
*/
task api(type: Javadoc) {
group = "Documentation"
description = "Generates aggregated Javadoc API documentation."
title = "${rootProject.description} ${version} API"
dependsOn {
moduleProjects.collect {
it.tasks.getByName("jar")
}
}
doFirst {
classpath = files(
// ensure the javadoc process can resolve types compiled from .aj sources
project(":spring-aspects").sourceSets.main.output
)
classpath += files(moduleProjects.collect { it.sourceSets.main.compileClasspath })
}
options {
encoding = "UTF-8"
memberLevel = JavadocMemberLevel.PROTECTED
author = true
header = rootProject.description
use = true
overview = "framework-docs/src/docs/api/overview.html"
splitIndex = true
links(project.ext.javadocLinks)
addBooleanOption('Xdoclint:syntax,reference', true) // only check syntax and reference with doclint
addBooleanOption('Werror', true) // fail build on Javadoc warnings
}
source moduleProjects.collect { project ->
project.sourceSets.main.allJava
}
maxMemory = "1024m"
destinationDir = file("$buildDir/docs/javadoc")
}
/**
* Produce KDoc for all Spring Framework modules in "build/docs/kdoc"
*/
rootProject.tasks.dokkaHtmlMultiModule.configure {
dependsOn {
tasks.getByName("api")
}
moduleName.set("spring-framework")
outputDirectory.set(project.file("$buildDir/docs/kdoc"))
}
/**
* Zip all Java docs (javadoc & kdoc) into a single archive
*/
task docsZip(type: Zip, dependsOn: ['api', rootProject.tasks.dokkaHtmlMultiModule]) {
group = "Distribution"
description = "Builds -${archiveClassifier} archive containing api and reference " +
"for deployment at https://docs.spring.io/spring-framework/docs/."
archiveBaseName.set("spring-framework")
archiveClassifier.set("docs")
from("src/dist") {
include "changelog.txt"
}
from (api) {
into "javadoc-api"
}
from (rootProject.tasks.dokkaHtmlMultiModule.outputDirectory) {
into "kdoc-api"
}
}
/**
* Zip all Spring Framework schemas into a single archive
*/
task schemaZip(type: Zip) {
group = "Distribution"
archiveBaseName.set("spring-framework")
archiveClassifier.set("schema")
description = "Builds -${archiveClassifier} archive containing all " +
"XSDs for deployment at https://springframework.org/schema."
duplicatesStrategy DuplicatesStrategy.EXCLUDE
moduleProjects.each { module ->
def Properties schemas = new Properties();
module.sourceSets.main.resources.find {
(it.path.endsWith("META-INF/spring.schemas") || it.path.endsWith("META-INF\\spring.schemas"))
}?.withInputStream { schemas.load(it) }
for (def key : schemas.keySet()) {
def shortName = key.replaceAll(/http.*schema.(.*).spring-.*/, '$1')
assert shortName != key
File xsdFile = module.sourceSets.main.resources.find {
(it.path.endsWith(schemas.get(key)) || it.path.endsWith(schemas.get(key).replaceAll('\\/','\\\\')))
}
assert xsdFile != null
into (shortName) {
from xsdFile.path
}
}
}
}
/**
* Create a distribution zip with everything:
* docs, schemas, jars, source jars, javadoc jars
*/
task distZip(type: Zip, dependsOn: [docsZip, schemaZip]) {
group = "Distribution"
archiveBaseName.set("spring-framework")
archiveClassifier.set("dist")
description = "Builds -${archiveClassifier} archive, containing all jars and docs, " +
"suitable for community download page."
ext.baseDir = "spring-framework-${project.version}";
from("src/docs/dist") {
include "readme.txt"
include "license.txt"
include "notice.txt"
into "${baseDir}"
expand(copyright: new Date().format("yyyy"), version: project.version)
}
from(zipTree(docsZip.archiveFile)) {
into "${baseDir}/docs"
}
from(zipTree(schemaZip.archiveFile)) {
into "${baseDir}/schema"
}
moduleProjects.each { module ->
into ("${baseDir}/libs") {
from module.jar
if (module.tasks.findByPath("sourcesJar")) {
from module.sourcesJar
}
if (module.tasks.findByPath("javadocJar")) {
from module.javadocJar
}
}
}
}
distZip.mustRunAfter moduleProjects.check
publishing {
publications {
mavenJava(MavenPublication) {
artifact docsZip
artifact schemaZip
artifact distZip
}
}
}
+6
View File
@@ -122,6 +122,7 @@
**** 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[]
**** xref:testing/testcontext-framework/ctx-management/inheritance.adoc[]
**** xref:testing/testcontext-framework/ctx-management/env-profiles.adoc[]
@@ -130,6 +131,7 @@
**** xref:testing/testcontext-framework/ctx-management/web.adoc[]
**** xref:testing/testcontext-framework/ctx-management/web-mocks.adoc[]
**** xref:testing/testcontext-framework/ctx-management/caching.adoc[]
**** xref:testing/testcontext-framework/ctx-management/failure-threshold.adoc[]
**** xref:testing/testcontext-framework/ctx-management/hierarchies.adoc[]
*** xref:testing/testcontext-framework/fixture-di.adoc[]
*** xref:testing/testcontext-framework/web-scoped-beans.adoc[]
@@ -165,6 +167,7 @@
***** xref:testing/annotations/integration-spring/annotation-contextconfiguration.adoc[]
***** xref:testing/annotations/integration-spring/annotation-webappconfiguration.adoc[]
***** xref:testing/annotations/integration-spring/annotation-contexthierarchy.adoc[]
***** xref:testing/annotations/integration-spring/annotation-contextcustomizerfactories.adoc[]
***** xref:testing/annotations/integration-spring/annotation-activeprofiles.adoc[]
***** xref:testing/annotations/integration-spring/annotation-testpropertysource.adoc[]
***** xref:testing/annotations/integration-spring/annotation-dynamicpropertysource.adoc[]
@@ -266,6 +269,7 @@
***** xref:web/webmvc/mvc-controller/ann-methods/jackson.adoc[]
**** xref:web/webmvc/mvc-controller/ann-modelattrib-methods.adoc[]
**** xref:web/webmvc/mvc-controller/ann-initbinder.adoc[]
**** xref:web/webmvc/mvc-controller/ann-validation.adoc[]
**** xref:web/webmvc/mvc-controller/ann-exceptionhandler.adoc[]
**** xref:web/webmvc/mvc-controller/ann-advice.adoc[]
*** xref:web/webmvc-functional.adoc[]
@@ -360,6 +364,7 @@
***** xref:web/webflux/controller/ann-methods/jackson.adoc[]
**** xref:web/webflux/controller/ann-modelattrib-methods.adoc[]
**** xref:web/webflux/controller/ann-initbinder.adoc[]
**** xref:web/webflux/controller/ann-validation.adoc[]
**** xref:web/webflux/controller/ann-exceptions.adoc[]
**** xref:web/webflux/controller/ann-advice.adoc[]
*** xref:web/webflux-functional.adoc[]
@@ -414,6 +419,7 @@
*** xref:integration/cache/plug.adoc[]
*** xref:integration/cache/specific-config.adoc[]
** xref:integration/observability.adoc[]
** xref:integration/checkpoint-restore.adoc[]
** xref:integration/appendix.adoc[]
* xref:languages.adoc[]
** xref:languages/kotlin.adoc[]
@@ -39,11 +39,6 @@ resolvable otherwise. See
{api-spring-framework}++/core/env/AbstractEnvironment.html#IGNORE_GETENV_PROPERTY_NAME++[`AbstractEnvironment`]
for details.
| `spring.index.ignore`
| Instructs Spring to ignore the components index located in
`META-INF/spring.components`. See xref:core/beans/classpath-scanning.adoc#beans-scanning-index[Generating an Index of Candidate Components]
.
| `spring.jdbc.getParameterType.ignore`
| Instructs Spring to ignore `java.sql.ParameterMetaData.getParameterType` completely.
See the note in xref:data-access/jdbc/advanced.adoc#jdbc-batch-list[Batch Operations with a List of Objects].
@@ -60,19 +55,27 @@ for details.
{api-spring-framework}++/objenesis/SpringObjenesis.html#IGNORE_OBJENESIS_PROPERTY_NAME++[`SpringObjenesis`]
for details.
| `spring.test.aot.processing.failOnError`
| A boolean flag that controls whether errors encountered during AOT processing in the
_Spring TestContext Framework_ should result in an exception that fails the overall process.
See xref:testing/testcontext-framework/aot.adoc[Ahead of Time Support for Tests].
| `spring.test.constructor.autowire.mode`
| The default _test constructor autowire mode_ to use if `@TestConstructor` is not present
on a test class. See xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-testconstructor[Changing the default test constructor autowire mode]
.
on a test class. See xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-testconstructor[Changing the default test constructor autowire mode].
| `spring.test.context.cache.maxSize`
| The maximum size of the context cache in the _Spring TestContext Framework_. See
xref:testing/testcontext-framework/ctx-management/caching.adoc[Context Caching].
| `spring.test.context.failure.threshold`
| The failure threshold for errors encountered while attempting to load an `ApplicationContext`
in the _Spring TestContext Framework_. See
xref:testing/testcontext-framework/ctx-management/failure-threshold.adoc[Context Failure Threshold].
| `spring.test.enclosing.configuration`
| The default _enclosing configuration inheritance mode_ to use if
`@NestedTestConfiguration` is not present on a test class. See
xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-nestedtestconfiguration[Changing the default enclosing configuration inheritance mode]
.
xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-nestedtestconfiguration[Changing the default enclosing configuration inheritance mode].
|===
@@ -728,11 +728,6 @@ of determining parameter names, an exception will be thrown.
`StandardReflectionParameterNameDiscoverer` :: Uses the standard `java.lang.reflect.Parameter`
API to determine parameter names. Requires that code be compiled with the `-parameters`
flag for `javac`. Recommended approach on Java 8+.
`LocalVariableTableParameterNameDiscoverer` :: Analyzes the local variable table available
in the byte code of the advice class to determine parameter names from debug information.
Requires that code be compiled with debug symbols (`-g:vars` at a minimum). Deprecated
as of Spring Framework 6.0 for removal in Spring Framework 6.1 in favor of compiling
code with `-parameters`. Not supported in a GraalVM native image.
`AspectJAdviceParameterNameDiscoverer` :: Deduces parameter names from the pointcut
expression, `returning`, and `throwing` clauses. See the
{api-spring-framework}/aop/aspectj/AspectJAdviceParameterNameDiscoverer.html[javadoc]
@@ -31,7 +31,7 @@ However, it would be even more confusing if Spring used its own terminology.
the "advised object". Since Spring AOP is implemented by using runtime proxies, this
object is always a proxied object.
* AOP proxy: An object created by the AOP framework in order to implement the aspect
contracts (advise method executions and so on). In the Spring Framework, an AOP proxy
contracts (advice method executions and so on). In the Spring Framework, an AOP proxy
is a JDK dynamic proxy or a CGLIB proxy.
* Weaving: linking aspects with other application types or objects to create an
advised object. This can be done at compile time (using the AspectJ compiler, for
@@ -256,6 +256,13 @@ Java::
If you are registering bean definitions programmatically, consider using `RootBeanBefinition` as it allows to specify a `ResolvableType` that handles generics.
[[aot.bestpractices.constructors]]
=== Avoid Multiple Constructors
The container is able to choose the most appropriate constructor to use based on several candidates.
However, this is not a best practice and flagging the preferred constructor with `@Autowired` if necessary is preferred.
In case you are working on a code base that you can't modify, you can set the {api-spring-framework}/beans/factory/support/AbstractBeanDefinition.html#PREFERRED_CONSTRUCTORS_ATTRIBUTE[`preferredConstructors` attribute] on the related bean definition to indicate which constructor should be used.
[[aot.bestpractices.factory-bean]]
=== FactoryBean
@@ -27,7 +27,7 @@ use these features.
== `@Component` and Further Stereotype Annotations
The `@Repository` annotation is a marker for any class that fulfills the role or
stereotype of a repository (also known as Data Access Object or DAO). Among the uses
_stereotype_ of a repository (also known as Data Access Object or DAO). Among the uses
of this marker is the automatic translation of exceptions, as described in
xref:data-access/orm/general.adoc#orm-exception-translation[Exception Translation].
@@ -39,7 +39,7 @@ layers, respectively). Therefore, you can annotate your component classes with
`@Component`, but, by annotating them with `@Repository`, `@Service`, or `@Controller`
instead, your classes are more properly suited for processing by tools or associating
with aspects. For example, these stereotype annotations make ideal targets for
pointcuts. `@Repository`, `@Service`, and `@Controller` can also
pointcuts. `@Repository`, `@Service`, and `@Controller` may also
carry additional semantics in future releases of the Spring Framework. Thus, if you are
choosing between using `@Component` or `@Service` for your service layer, `@Service` is
clearly the better choice. Similarly, as stated earlier, `@Repository` is already
@@ -664,15 +664,36 @@ analogous to how the container selects between multiple `@Autowired` constructor
== Naming Autodetected Components
When a component is autodetected as part of the scanning process, its bean name is
generated by the `BeanNameGenerator` strategy known to that scanner. By default, any
Spring stereotype annotation (`@Component`, `@Repository`, `@Service`, and
`@Controller`) that contains a name `value` thereby provides that name to the
corresponding bean definition.
generated by the `BeanNameGenerator` strategy known to that scanner.
If such an annotation contains no name `value` or for any other detected component
(such as those discovered by custom filters), the default bean name generator returns
the uncapitalized non-qualified class name. For example, if the following component
classes were detected, the names would be `myMovieLister` and `movieFinderImpl`:
By default, the `AnnotationBeanNameGenerator` is used. For Spring
xref:core/beans/classpath-scanning.adoc#beans-stereotype-annotations[stereotype annotations],
if you supply a name via the annotation's `value` attribute that name will be used as
the name in the corresponding bean definition. This convention also applies when the
following JSR-250 and JSR-330 annotations are used instead of Spring stereotype
annotations: `@jakarta.annotation.ManagedBean`, `@javax.annotation.ManagedBean`,
`@jakarta.inject.Named`, and `@javax.inject.Named`.
As of Spring Framework 6.1, the name of the annotation attribute that is used to specify
the bean name is no longer required to be `value`. Custom stereotype annotations can
declare an attribute with a different name (such as `name`) and annotate that attribute
with `@AliasFor(annotation = Component.class, attribute = "value")`. See the source code
declaration of `ControllerAdvice#name()` for a concrete example.
[WARNING]
====
As of Spring Framework 6.1, support for convention-based stereotype names is deprecated
and will be removed in a future version of the framework. Consequently, custom stereotype
annotations must use `@AliasFor` to declare an explicit alias for the `value` attribute
in `@Component`. See the source code declaration of `Repository#value()` and
`ControllerAdvice#name()` for concrete examples.
====
If an explicit bean name cannot be derived from such an annotation or for any other
detected component (such as those discovered by custom filters), the default bean name
generator returns the uncapitalized non-qualified class name. For example, if the
following component classes were detected, the names would be `myMovieLister` and
`movieFinderImpl`.
[tabs]
======
@@ -989,68 +1010,4 @@ metadata is provided per-instance rather than per-class.
[[beans-scanning-index]]
== Generating an Index of Candidate Components
While classpath scanning is very fast, it is possible to improve the startup performance
of large applications by creating a static list of candidates at compilation time. In this
mode, all modules that are targets of component scanning must use this mechanism.
NOTE: Your existing `@ComponentScan` or `<context:component-scan/>` directives must remain
unchanged to request the context to scan candidates in certain packages. When the
`ApplicationContext` detects such an index, it automatically uses it rather than scanning
the classpath.
To generate the index, add an additional dependency to each module that contains
components that are targets for component scan directives. The following example shows
how to do so with Maven:
[source,xml,indent=0,subs="verbatim,quotes,attributes"]
----
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-indexer</artifactId>
<version>{spring-version}</version>
<optional>true</optional>
</dependency>
</dependencies>
----
With Gradle 4.5 and earlier, the dependency should be declared in the `compileOnly`
configuration, as shown in the following example:
[source,groovy,indent=0,subs="verbatim,quotes,attributes"]
----
dependencies {
compileOnly "org.springframework:spring-context-indexer:{spring-version}"
}
----
With Gradle 4.6 and later, the dependency should be declared in the `annotationProcessor`
configuration, as shown in the following example:
[source,groovy,indent=0,subs="verbatim,quotes,attributes"]
----
dependencies {
annotationProcessor "org.springframework:spring-context-indexer:{spring-version}"
}
----
The `spring-context-indexer` artifact generates a `META-INF/spring.components` file that
is included in the jar file.
NOTE: When working with this mode in your IDE, the `spring-context-indexer` must be
registered as an annotation processor to make sure the index is up-to-date when
candidate components are updated.
TIP: The index is enabled automatically when a `META-INF/spring.components` file is found
on the classpath. If an index is partially available for some libraries (or use cases)
but could not be built for the whole application, you can fall back to a regular classpath
arrangement (as though no index were present at all) by setting `spring.index.ignore` to
`true`, either as a JVM system property or via the
xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism.
@@ -478,20 +478,21 @@ Kotlin::
----
======
Notice that `ApplicationListener` is generically parameterized with the type of your
custom event (`BlockedListEvent` in the preceding example). This means that the
`onApplicationEvent()` method can remain type-safe, avoiding any need for downcasting.
You can register as many event listeners as you wish, but note that, by default, event
listeners receive events synchronously. This means that the `publishEvent()` method
blocks until all listeners have finished processing the event. One advantage of this
synchronous and single-threaded approach is that, when a listener receives an event,
it operates inside the transaction context of the publisher if a transaction context
is available. If another strategy for event publication becomes necessary, e.g.
asynchronous event processing by default, see the javadoc for Spring's
{api-spring-framework}/context/event/ApplicationEventMulticaster.html[`ApplicationEventMulticaster`] interface
and {api-spring-framework}/context/event/SimpleApplicationEventMulticaster.html[`SimpleApplicationEventMulticaster`]
implementation for configuration options which can be applied to a custom
"applicationEventMulticaster" bean definition.
Notice that `ApplicationListener` is generically parameterized with the type of your custom event (`BlockedListEvent` in the preceding example).
This means that the `onApplicationEvent()` method can remain type-safe, avoiding any need for downcasting.
You can register as many event listeners as you wish, but note that, by default, event listeners receive events synchronously.
This means that the `publishEvent()` method blocks until all listeners have finished processing the event.
One advantage of this synchronous and single-threaded approach is that, when a listener receives an event,
it operates inside the transaction context of the publisher if a transaction context is available.
If another strategy for event publication becomes necessary, e.g. asynchronous event processing by default,
see the javadoc for Spring's {api-spring-framework}/context/event/ApplicationEventMulticaster.html[`ApplicationEventMulticaster`] interface
and {api-spring-framework}/context/event/SimpleApplicationEventMulticaster.html[`SimpleApplicationEventMulticaster`] implementation
for configuration options which can be applied to a custom "applicationEventMulticaster" bean definition.
In these cases, ThreadLocals and logging context are not propagated for the event processing.
See xref:integration/observability.adoc#observability.application-events[the `@EventListener` Observability section]
for more information on Observability concerns.
The following example shows the bean definitions used to register and configure each of
the classes above:
@@ -747,6 +748,9 @@ Be aware of the following limitations when using asynchronous events:
value. If you need to publish another event as the result of the processing, inject an
{api-spring-framework}/context/ApplicationEventPublisher.html[`ApplicationEventPublisher`]
to publish the event manually.
* ThreadLocals and logging context are not propagated by default for the event processing.
See xref:integration/observability.adoc#observability.application-events[the `@EventListener` Observability section]
for more information on Observability concerns.
[[context-functionality-events-order]]
@@ -771,11 +771,9 @@ resolved to the corresponding value. If not, then `default/path` is used
as a default. If no default is specified and a property cannot be resolved, an
`IllegalArgumentException` is thrown.
NOTE: The `@PropertySource` annotation is repeatable, according to Java 8 conventions.
However, all such `@PropertySource` annotations need to be declared at the same
level, either directly on the configuration class or as meta-annotations within the
same custom annotation. Mixing direct annotations and meta-annotations is not
recommended, since direct annotations effectively override meta-annotations.
NOTE: `@PropertySource` can be used as a repeatable annotation. `@PropertySource`
may also be used as a meta-annotation to create custom composed annotations with
attribute overrides.
@@ -46,7 +46,9 @@ The expression language supports the following functionality:
* Inline maps
* Ternary operator
* Variables
* User-defined functions
* User-defined functions added to the context
* reflective invocation of `Method`
* various cases of `MethodHandle`
* Collection projection
* Collection selection
* Templated expressions
@@ -15,7 +15,7 @@ topics:
* xref:core/expressions/language-ref/types.adoc[Types]
* xref:core/expressions/language-ref/constructors.adoc[Constructors]
* xref:core/expressions/language-ref/variables.adoc[Variables]
* xref:core/expressions/language-ref/functions.adoc[Functions]
* xref:core/expressions/language-ref/functions.adoc[User-Defined Functions]
* xref:core/expressions/language-ref/bean-references.adoc[Bean References]
* xref:core/expressions/language-ref/operator-ternary.adoc[Ternary Operator (If-Then-Else)]
* xref:core/expressions/language-ref/operator-elvis.adoc[The Elvis Operator]
@@ -3,7 +3,8 @@
You can extend SpEL by registering user-defined functions that can be called within the
expression string. The function is registered through the `EvaluationContext`. The
following example shows how to register a user-defined function:
following example shows how to register a user-defined function to be invoked via reflection
(i.e. a `Method`):
[tabs]
======
@@ -94,5 +95,97 @@ Kotlin::
----
======
The use of `MethodHandle` is also supported. This enables potentially more efficient use
cases if the `MethodHandle` target and parameters have been fully bound prior to
registration, but partially bound handles are also supported.
Consider the `String#formatted(String, Object...)` instance method, which produces a
message according to a template and a variable number of arguments.
You can register and use the `formatted` method as a `MethodHandle`, as the following
example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
MethodHandle mh = MethodHandles.lookup().findVirtual(String.class, "formatted",
MethodType.methodType(String.class, Object[].class));
context.setVariable("message", mh);
String message = parser.parseExpression("#message('Simple message: <%s>', 'Hello World', 'ignored')")
.getValue(context, String.class);
//returns "Simple message: <Hello World>"
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val parser = SpelExpressionParser()
val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()
val mh = MethodHandles.lookup().findVirtual(String::class.java, "formatted",
MethodType.methodType(String::class.java, Array<Any>::class.java))
context.setVariable("message", mh)
val message = parser.parseExpression("#message('Simple message: <%s>', 'Hello World', 'ignored')")
.getValue(context, String::class.java)
----
======
As hinted above, binding a `MethodHandle` and registering the bound `MethodHandle` is also
supported. This is likely to be more performant if both the target and all the arguments
are bound. In that case no arguments are necessary in the SpEL expression, as the
following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
String template = "This is a %s message with %s words: <%s>";
Object varargs = new Object[] { "prerecorded", 3, "Oh Hello World!", "ignored" };
MethodHandle mh = MethodHandles.lookup().findVirtual(String.class, "formatted",
MethodType.methodType(String.class, Object[].class))
.bindTo(template)
.bindTo(varargs); //here we have to provide arguments in a single array binding
context.setVariable("message", mh);
String message = parser.parseExpression("#message()")
.getValue(context, String.class);
//returns "This is a prerecorded message with 3 words: <Oh Hello World!>"
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val parser = SpelExpressionParser()
val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()
val template = "This is a %s message with %s words: <%s>"
val varargs = arrayOf("prerecorded", 3, "Oh Hello World!", "ignored")
val mh = MethodHandles.lookup().findVirtual(String::class.java, "formatted",
MethodType.methodType(String::class.java, Array<Any>::class.java))
.bindTo(template)
.bindTo(varargs) //here we have to provide arguments in a single array binding
context.setVariable("message", mh)
val message = parser.parseExpression("#message()")
.getValue(context, String::class.java)
----
======
@@ -38,6 +38,10 @@ Kotlin::
----
======
NOTE: The SpEL Elvis operator also checks for _empty_ Strings in addition to `null` objects.
The original snippet is thus only close to emulating the semantics of the operator (it would need an
additional `!name.isEmpty()` check).
The following listing shows a more complex example:
[tabs]
@@ -53,7 +57,7 @@ Java::
String name = parser.parseExpression("name?:'Elvis Presley'").getValue(context, tesla, String.class);
System.out.println(name); // Nikola Tesla
tesla.setName(null);
tesla.setName("");
name = parser.parseExpression("name?:'Elvis Presley'").getValue(context, tesla, String.class);
System.out.println(name); // Elvis Presley
----
@@ -69,7 +73,7 @@ Kotlin::
var name = parser.parseExpression("name?:'Elvis Presley'").getValue(context, tesla, String::class.java)
println(name) // Nikola Tesla
tesla.setName(null)
tesla.setName("")
name = parser.parseExpression("name?:'Elvis Presley'").getValue(context, tesla, String::class.java)
println(name) // Elvis Presley
----
@@ -1,5 +1,50 @@
[[beans-binding]]
= Data Binding
Data binding is useful for binding user input to a target object where user input is a map
with property paths as keys, following xref:beans-beans-conventions[JavaBeans conventions].
`DataBinder` is the main class that supports this, and it provides two ways to bind user
input:
- xref:beans-constructor-binding[Constructor binding] - bind user input to a public data
constructor, looking up constructor argument values in the user input.
- xref:beans-beans[Property binding] - bind user input to setters, matching keys from the
user input to properties of the target object structure.
You can apply both constructor and property binding or only one.
[[beans-constructor-binding]]
== Constructor Binding
To use constructor binding:
1. Create a `DataBinder` with `null` as the target object.
2. Set `targetType` to the target class.
3. Call `construct`.
The target class should have a single public constructor or a single non-public constructor
with arguments. If there are multiple constructors, then a default constructor if present
is used.
By default, constructor parameter names are used to look up argument values, but you can
configure a `NameResolver`. Spring MVC and WebFlux both rely to allow customizing the name
of the value to bind through an `@BindParam` annotation on constructor parameters.
xref:beans-beans-conventions[Type conversion] is applied as needed to convert user input.
If the constructor parameter is an object, it is constructed recursively in the same
manner, but through a nested property path. That means constructor binding creates both
the target object and any objects it contains.
Binding and conversion errors are reflected in the `BindingResult` of the `DataBinder`.
If the target is created successfully, then `target` is set to the created instance
after the call to `construct`.
[[beans-beans]]
= Bean Manipulation and the `BeanWrapper`
== Property Binding with `BeanWrapper`
The `org.springframework.beans` package adheres to the JavaBeans standard.
A JavaBean is a class with a default no-argument constructor and that follows
@@ -26,7 +71,7 @@ perform actions on that bean, such as setting and retrieving properties.
[[beans-beans-conventions]]
== Setting and Getting Basic and Nested Properties
=== Setting and Getting Basic and Nested Properties
Setting and getting properties is done through the `setPropertyValue` and
`getPropertyValue` overloaded method variants of `BeanWrapper`. See their Javadoc for
@@ -192,7 +237,7 @@ Kotlin::
[[beans-beans-conversion]]
== Built-in `PropertyEditor` Implementations
== ``PropertyEditor``'s
Spring uses the concept of a `PropertyEditor` to effect the conversion between an
`Object` and a `String`. It can be handy
@@ -378,7 +423,7 @@ Kotlin::
[[beans-beans-conversion-customeditor-registration]]
=== Registering Additional Custom `PropertyEditor` Implementations
=== Custom ``PropertyEditor``'s
When setting bean properties as string values, a Spring IoC container ultimately uses
standard JavaBeans `PropertyEditor` implementations to convert these strings to the complex type of the
@@ -521,7 +566,7 @@ Finally, the following example shows how to use `CustomEditorConfigurer` to regi
----
[[beans-beans-conversion-customeditor-registration-per]]
==== Using `PropertyEditorRegistrar`
=== `PropertyEditorRegistrar`
Another mechanism for registering property editors with the Spring container is to
create and use a `PropertyEditorRegistrar`. This interface is particularly useful when
@@ -123,15 +123,12 @@ Validator, is expected to be present in the classpath and is automatically detec
[[validation-beanvalidation-spring-inject]]
=== Injecting a Validator
=== Inject Jakarta Validator
`LocalValidatorFactoryBean` implements both `jakarta.validation.ValidatorFactory` and
`jakarta.validation.Validator`, as well as Spring's `org.springframework.validation.Validator`.
You can inject a reference to either of these interfaces into beans that need to invoke
validation logic.
You can inject a reference to `jakarta.validation.Validator` if you prefer to work with the Bean
Validation API directly, as the following example shows:
`jakarta.validation.Validator`, so you can inject a reference to the latter to
apply validation logic if you prefer to work with the Bean Validation API directly,
as the following example shows:
[tabs]
======
@@ -160,8 +157,15 @@ Kotlin::
----
======
You can inject a reference to `org.springframework.validation.Validator` if your bean
requires the Spring Validation API, as the following example shows:
[[validation-beanvalidation-spring-inject-adapter]]
=== Inject Spring Validator
In addition to implementing `jakarta.validation.Validator`, `LocalValidatorFactoryBean`
also adapts to `org.springframework.validation.Validator`, so you can inject a reference
to the latter if your bean requires the Spring Validation API.
For example:
[tabs]
======
@@ -190,9 +194,15 @@ Kotlin::
----
======
When used as `org.springframework.validation.Validator`, `LocalValidatorFactoryBean`
invokes the underlying `jakarta.validation.Validator`, and then adapts
``ContraintViolation``s to ``FieldError``s, and registers them with the `Errors` object
passed into the `validate` method.
[[validation-beanvalidation-spring-constraints]]
=== Configuring Custom Constraints
=== Configure Custom Constraints
Each bean validation constraint consists of two parts:
@@ -272,11 +282,10 @@ As the preceding example shows, a `ConstraintValidator` implementation can have
[[validation-beanvalidation-spring-method]]
=== Spring-driven Method Validation
== Spring-driven Method Validation
You can integrate the method validation feature supported by Bean Validation 1.1 (and, as
a custom extension, also by Hibernate Validator 4.3) into a Spring context through a
`MethodValidationPostProcessor` bean definition:
You can integrate the method validation feature of Bean Validation into a
Spring context through a `MethodValidationPostProcessor` bean definition:
[tabs]
======
@@ -305,11 +314,11 @@ XML::
----
======
To be eligible for Spring-driven method validation, all target classes need to be annotated
To be eligible for Spring-driven method validation, target classes need to be annotated
with Spring's `@Validated` annotation, which can optionally also declare the validation
groups to use. See
{api-spring-framework}/validation/beanvalidation/MethodValidationPostProcessor.html[`MethodValidationPostProcessor`]
for setup details with the Hibernate Validator and Bean Validation 1.1 providers.
for setup details with the Hibernate Validator and Bean Validation providers.
[TIP]
====
@@ -320,8 +329,142 @@ xref:core/aop/proxying.adoc#aop-understanding-aop-proxies[Understanding AOP Prox
to always use methods and accessors on proxied classes; direct field access will not work.
====
Spring MVC and WebFlux have built-in support for the same underlying method validation but without
the need for AOP. Therefore, do check the rest of this section, and also see the Spring MVC
xref:web/webmvc/mvc-controller/ann-validation.adoc[Validation] and
xref:web/webmvc/mvc-ann-rest-exceptions.adoc[Error Responses] sections, and the WebFlux
xref:web/webflux/controller/ann-validation.adoc[Validation] and
xref:web/webflux/ann-rest-exceptions.adoc[Error Responses] sections.
[[validation-beanvalidation-spring-method-exceptions]]
=== Method Validation Exceptions
By default, `jakarta.validation.ConstraintViolationException` is raised with the set of
``ConstraintViolation``s returned by `jakarata.validation.Validator`. As an alternative,
you can have `MethodValidationException` raised instead with ``ConstraintViolation``s
adapted to `MessageSourceResolvable` errors. To enable set the following flag:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
@Configuration
public class AppConfig {
@Bean
public MethodValidationPostProcessor validationPostProcessor() {
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
processor.setAdaptConstraintViolations(true);
return processor;
}
}
----
XML::
+
[source,xml,indent=0,subs="verbatim,quotes",role="secondary"]
----
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor">
<property name="adaptConstraintViolations" value="true"/>
</bean>
----
======
`MethodValidationException` contains a list of ``ParameterValidationResult``s which
group errors by method parameter, and each exposes a `MethodParameter`, the argument
value, and a list of `MessageSourceResolvable` errors adapted from
``ConstraintViolation``s. For `@Valid` method parameters with cascaded violations on
fields and properties, the `ParameterValidationResult` is `ParameterErrors` which
implements `org.springframework.validation.Errors` and exposes validation errors as
``FieldError``s.
[[validation-beanvalidation-spring-method-i18n]]
=== Customizing Validation Errors
The adapted `MessageSourceResolvable` errors can be turned into error messages to
display to users through the configured `MessageSource` with locale and language specific
resource bundles. This section provides an example for illustration.
Given the following class declarations:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
record Person(@Size(min = 1, max = 10) String name) {
}
@Validated
public class MyService {
void addStudent(@Valid Person person, @Max(2) int degrees) {
// ...
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@JvmRecord
internal data class Person(@Size(min = 1, max = 10) val name: String)
@Validated
class MyService {
fun addStudent(person: @Valid Person?, degrees: @Max(2) Int) {
// ...
}
}
----
======
A `ConstraintViolation` on `Person.name()` is adapted to a `FieldErrro` with the following:
- Error codes `"Size.student.name"`, `"Size.name"`, `"Size.java.lang.String"`, and `"Size"`
- Message arguments `"name"`, `10`, and `1` (the field name and the constraint attributes)
- Default message "size must be between 1 and 10"
To customize the default message, you can add properties to
xref:core/beans/context-introduction.adoc#context-functionality-messagesource[MessageSource]
resource bundles using any of the above errors codes and message arguments. Note also that the
message argument `"name"` is itself a `MessagreSourceResolvable` with error codes
`"student.name"` and `"name"` and can customized too. For example:
Properties::
+
[source,properties,indent=0,subs="verbatim,quotes",role="secondary"]
----
Size.student.name=Please, provide a {0} that is between {2} and {1} characters long
student.name=username
----
A `ConstraintViolation` on the `degrees` method parameter is adapted to a
`MessageSourceResolvable` with the following:
- Error codes `"Max.myService#addStudent.degrees"`, `"Max.degrees"`, `"Max.int"`, `"Max"`
- Message arguments "degrees2 and 2 (the field name and the constraint attribute)
- Default message "must be less than or equal to 2"
To customize the above default message, you can add a property such as:
Properties::
+
[source,properties,indent=0,subs="verbatim,quotes",role="secondary"]
----
Max.degrees=You cannot provide more than {1} {0}
----
[[validation-beanvalidation-spring-other]]
=== Additional Configuration Options
@@ -195,6 +195,12 @@ of Spring Web MVC, you can use the `<spring:bind/>` tag to inspect the error mes
you can also inspect the `Errors` object yourself. More information about the
methods it offers can be found in the {api-spring-framework}/validation/Errors.html[javadoc].
Validators may also get locally invoked for the immediate validation of a given object,
not involving a binding process. As of 6.1, this has been simplified through a new
`Validator.validateObject(Object)` method which is available by default now, returning
a simple ´Errors` representation which can be inspected: typically calling `hasErrors()`
or the new `failOnError` method for turning the error summary message into an exception
(e.g. `validator.validateObject(myObject).failOnError(IllegalArgumentException::new)`).
@@ -2,30 +2,26 @@
= Choosing an Approach for JDBC Database Access
You can choose among several approaches to form the basis for your JDBC database access.
In addition to three flavors of `JdbcTemplate`, a new `SimpleJdbcInsert` and
`SimpleJdbcCall` approach optimizes database metadata, and the RDBMS Object style takes a
more object-oriented approach similar to that of JDO Query design. Once you start using
one of these approaches, you can still mix and match to include a feature from a
different approach. All approaches require a JDBC 2.0-compliant driver, and some
advanced features require a JDBC 3.0 driver.
In addition to three flavors of `JdbcTemplate`, a `SimpleJdbcInsert` and `SimpleJdbcCall`
approach optimizes database metadata, and the RDBMS Object style results in a more
object-oriented approach. Once you start using one of these approaches, you can still mix
and match to include a feature from a different approach.
* `JdbcTemplate` is the classic and most popular Spring JDBC approach. This
"`lowest-level`" approach and all others use a JdbcTemplate under the covers.
"`lowest-level`" approach and all others use a `JdbcTemplate` under the covers.
* `NamedParameterJdbcTemplate` wraps a `JdbcTemplate` to provide named parameters
instead of the traditional JDBC `?` placeholders. This approach provides better
documentation and ease of use when you have multiple parameters for an SQL statement.
* `SimpleJdbcInsert` and `SimpleJdbcCall` optimize database metadata to limit the amount
of necessary configuration. This approach simplifies coding so that you need to
provide only the name of the table or procedure and provide a map of parameters matching
the column names. This works only if the database provides adequate metadata. If the
database does not provide this metadata, you have to provide explicit
configuration of the parameters.
of necessary configuration. This approach simplifies coding so that you only need to
provide the name of the table or procedure and a map of parameters matching the column
names. This works only if the database provides adequate metadata. If the database does
not provide this metadata, you have to provide explicit configuration of the parameters.
* RDBMS objects — including `MappingSqlQuery`, `SqlUpdate`, and `StoredProcedure` —
require you to create reusable and thread-safe objects during initialization of your
data-access layer. This approach is modeled after JDO Query, wherein you define your
query string, declare parameters, and compile the query. Once you do that,
`execute(...)`, `update(...)`, and `findObject(...)` methods can be called multiple
times with various parameter values.
data-access layer. This approach allows you to define your query string, declare
parameters, and compile the query. Once you do that, `execute(...)`, `update(...)`, and
`findObject(...)` methods can be called multiple times with various parameter values.
@@ -6,6 +6,7 @@ including error handling. It includes the following topics:
* xref:data-access/jdbc/core.adoc#jdbc-JdbcTemplate[Using `JdbcTemplate`]
* xref:data-access/jdbc/core.adoc#jdbc-NamedParameterJdbcTemplate[Using `NamedParameterJdbcTemplate`]
* xref:data-access/jdbc/core.adoc#jdbc-JdbcClient[Unified JDBC Query/Update Operations: `JdbcClient`]
* xref:data-access/jdbc/core.adoc#jdbc-SQLExceptionTranslator[Using `SQLExceptionTranslator`]
* xref:data-access/jdbc/core.adoc#jdbc-statements-executing[Running Statements]
* xref:data-access/jdbc/core.adoc#jdbc-statements-querying[Running Queries]
@@ -701,6 +702,120 @@ See also xref:data-access/jdbc/core.adoc#jdbc-JdbcTemplate-idioms[`JdbcTemplate`
for guidelines on using the `NamedParameterJdbcTemplate` class in the context of an application.
[[jdbc-JdbcClient]]
== Unified JDBC Query/Update Operations: `JdbcClient`
As of 6.1, the named parameter statements of `NamedParameterJdbcTemplate` and the positional
parameter statements of a regular `JdbcTemplate` are available through a unified client API
with a fluent interaction model.
For example, with positional parameters:
[source,java,indent=0,subs="verbatim,quotes"]
----
private JdbcClient jdbcClient = JdbcClient.create(dataSource);
public int countOfActorsByFirstName(String firstName) {
return this.jdbcClient.sql("select count(*) from t_actor where first_name = ?")
.param(firstName);
.query(Integer.class).single();
}
----
For example, with named parameters:
[source,java,indent=0,subs="verbatim,quotes"]
----
private JdbcClient jdbcClient = JdbcClient.create(dataSource);
public int countOfActorsByFirstName(String firstName) {
return this.jdbcClient.sql("select count(*) from t_actor where first_name = :firstName")
.param("firstName", firstName);
.query(Integer.class).single();
}
----
`RowMapper` capabilities are available as well, with flexible result resolution:
[source,java,indent=0,subs="verbatim,quotes"]
----
List<Actor> actors = this.jdbcClient.sql("select first_name, last_name from t_actor")
.query((rs, rowNum) -> new Actor(rs.getString("first_name"), rs.getString("last_name")))
.list();
----
Instead of a custom `RowMapper`, you may also specify a class to map to.
For example, assuming that `Actor` has `firstName` and `lastName` properties
as a record class, a custom constructor, bean properties, or plain fields:
[source,java,indent=0,subs="verbatim,quotes"]
----
List<Actor> actors = this.jdbcClient.sql("select first_name, last_name from t_actor")
.query(Actor.class)
.list();
----
With a required single object result:
[source,java,indent=0,subs="verbatim,quotes"]
----
Actor actor = this.jdbcClient.sql("select first_name, last_name from t_actor where id = ?",
.param(1212L);
.query(Actor.class)
.single();
----
With a `java.util.Optional` result:
[source,java,indent=0,subs="verbatim,quotes"]
----
Optional<Actor> actor = this.jdbcClient.sql("select first_name, last_name from t_actor where id = ?",
.param(1212L);
.query(Actor.class)
.optional();
----
And for an update statement:
[source,java,indent=0,subs="verbatim,quotes"]
----
this.jdbcClient.sql("insert into t_actor (first_name, last_name) values (?, ?)")
.param("Leonor").param("Watling");
.update();
----
Or an update statement with named parameters:
[source,java,indent=0,subs="verbatim,quotes"]
----
this.jdbcClient.sql("insert into t_actor (first_name, last_name) values (:firstName, :lastName)")
.param("firstName", "Leonor").param("lastName", "Watling");
.update();
----
Instead of individual named parameters, you may also specify a parameter source object
for example, a record class, a class with bean properties, or a plain field holder which
provides `firstName` and `lastName` properties, such as the `Actor` class from above:
[source,java,indent=0,subs="verbatim,quotes"]
----
this.jdbcClient.sql("insert into t_actor (first_name, last_name) values (:firstName, :lastName)")
.paramSource(new Actor("Leonor", "Watling");
.update();
----
The automatic `Actor` class mapping for parameters as well as the query results above is
provided through implicit `SimplePropertySqlParameterSource` and `SimplePropertyRowMapper`
strategies which are also available for direct use. They can serve as a common replacement
for `BeanPropertySqlParameterSource` and `BeanPropertyRowMapper`/`DataClassRowMapper`,
also with `JdbcTemplate` and `NamedParameterJdbcTemplate` themselves.
NOTE: `JdbcClient` is a flexible but simplified facade for JDBC query/update statements.
Advanced capabilities such as batch inserts and stored procedure calls typically require
extra customization: consider Spring's `SimpleJdbcInsert` and `SimpleJdbcCall` classes or
plain direct `JdbcTemplate` usage for any such capabilities not available in `JdbcClient`.
[[jdbc-SQLExceptionTranslator]]
== Using `SQLExceptionTranslator`
@@ -709,7 +824,7 @@ between ``SQLException``s and Spring's own `org.springframework.dao.DataAccessEx
which is agnostic in regard to data access strategy. Implementations can be generic (for
example, using SQLState codes for JDBC) or proprietary (for example, using Oracle error
codes) for greater precision. This exception translation mechanism is used behind the
the common `JdbcTemplate` and `JdbcTransactionManager` entry points which do not
common `JdbcTemplate` and `JdbcTransactionManager` entry points which do not
propagate `SQLException` but rather `DataAccessException`.
NOTE: As of 6.0, the default exception translator is `SQLExceptionSubclassTranslator`,
@@ -20,9 +20,10 @@ xref:data-access/jdbc/connections.adoc[Controlling Database Connections] and xre
* `object`: The `org.springframework.jdbc.object` package contains classes that represent
RDBMS queries, updates, and stored procedures as thread-safe, reusable objects. See
xref:data-access/jdbc/object.adoc[Modeling JDBC Operations as Java Objects]. This approach is modeled by JDO, although objects returned by queries
are naturally disconnected from the database. This higher-level of JDBC abstraction
depends on the lower-level abstraction in the `org.springframework.jdbc.core` package.
xref:data-access/jdbc/object.adoc[Modeling JDBC Operations as Java Objects]. This style
results in a more object-oriented approach, although objects returned by queries are
naturally disconnected from the database. This higher-level of JDBC abstraction depends
on the lower-level abstraction in the `org.springframework.jdbc.core` package.
* `support`: The `org.springframework.jdbc.support` package provides `SQLException`
translation functionality and some utility classes. Exceptions thrown during JDBC processing
@@ -254,6 +254,25 @@ Kotlin::
----
======
Alternatively, there is a shortcut for mapping to a single value:
[source,java]
----
Flux<String> names = client.sql("SELECT name FROM person")
.mapValue(String.class)
.all();
----
Or you may map to a result object with bean properties or record components:
[source,java]
----
// assuming a name property on Person
Flux<Person> persons = client.sql("SELECT name FROM person")
.mapProperties(Person.class)
.all();
----
[[r2dbc-DatabaseClient-mapping-null]]
.What about `null`?
****
@@ -324,6 +343,27 @@ The following example shows parameter binding for a query:
.bind("age", 34);
----
Alternatively, you may pass in a map of names and values:
[source,java]
----
Map<String, Object> params = new LinkedHashMap<>();
params.put("id", "joe");
params.put("name", "Joe");
params.put("age", 34);
db.sql("INSERT INTO person (id, name, age) VALUES(:id, :name, :age)")
.bindValues(params);
----
Or you may pass in a parameter object with bean properties or record components:
[source,java]
----
// assuming id, name, age properties on Person
db.sql("INSERT INTO person (id, name, age) VALUES(:id, :name, :age)")
.bindProperties(new Person("joe", "Joe", 34);
----
.R2DBC Native Bind Markers
****
R2DBC uses database-native bind markers that depend on the actual database vendor.
@@ -57,10 +57,14 @@ attribute of the annotation to `true`.
[NOTE]
====
`@TransactionalEventListener` only works with thread-bound transactions managed by
`PlatformTransactionManager`. A reactive transaction managed by `ReactiveTransactionManager`
uses the Reactor context instead of thread-local attributes, so from the perspective of
an event listener, there is no compatible active transaction that it can participate in.
As of 6.1, `@TransactionalEventListener` can work with thread-bound transactions managed by
`PlatformTransactionManager` as well as reactive transactions managed by `ReactiveTransactionManager`.
For the former, listeners are guaranteed to see the current thread-bound transaction.
Since the latter uses the Reactor context instead of thread-local variables, the transaction
context needs to be included in the published event instance as the event source.
See the
{api-spring-framework}/transaction/reactive/TransactionalEventPublisher.html[`TransactionalEventPublisher`]
javadoc for details.
====
+1 -1
View File
@@ -16,7 +16,7 @@ STOMP Messaging.
xref:web-reactive.adoc[Web Reactive] :: Spring WebFlux, WebClient,
WebSocket, RSocket.
xref:integration.adoc[Integration] :: REST Clients, JMS, JCA, JMX,
Email, Tasks, Scheduling, Caching, Observability.
Email, Tasks, Scheduling, Caching, Observability, JVM Checkpoint Restore.
xref:languages.adoc[Languages] :: Kotlin, Groovy, Dynamic Languages.
xref:testing/appendix.adoc[Appendix] :: Spring properties.
https://github.com/spring-projects/spring-framework/wiki[Wiki] :: What's New,
@@ -208,6 +208,76 @@ NOTE: This is an optional feature, and your favorite cache library may not suppo
All `CacheManager` implementations provided by the core framework support it. See the
documentation of your cache provider for more details.
[[cache-annotations-cacheable-reactive]]
=== Caching with CompletableFuture and Reactive Return Types
As of 6.1, cache annotations take `CompletableFuture` and reactive return types
into account, automatically adapting the cache interaction accordingly.
For a method returning a `CompletableFuture`, the object produced by that future
will be cached whenever it is complete, and the cache lookup for a cache hit will
be retrieved via a `CompletableFuture`:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Cacheable("books")
public CompletableFuture<Book> findBook(ISBN isbn) {...}
----
For a method returning a Reactor `Mono`, the object emitted by that Reactive Streams
publisher will be cached whenever it is available, and the cache lookup for a cache
hit will be retrieved as a `Mono` (backed by a `CompletableFuture`):
[source,java,indent=0,subs="verbatim,quotes"]
----
@Cacheable("books")
public Mono<Book> findBook(ISBN isbn) {...}
----
For a method returning a Reactor `Flux`, the objects emitted by that Reactive Streams
publisher will be collected into a `List` and cached whenever that list is complete,
and the cache lookup for a cache hit will be retrieved as a `Flux` (backed by a
`CompletableFuture` for the cached `List` value):
[source,java,indent=0,subs="verbatim,quotes"]
----
@Cacheable("books")
public Flux<Book> findBooks(String author) {...}
----
Such `CompletableFuture` and reactive adaptation also works for synchronized caching,
computing the value only once in case of a concurrent cache miss:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Cacheable(cacheNames="foos", sync=true) <1>
public CompletableFuture<Foo> executeExpensiveOperation(String id) {...}
----
<1> Using the `sync` attribute.
NOTE: In order for such an arrangement to work at runtime, the configured cache
needs to be capable of `CompletableFuture`-based retrieval. The Spring-provided
`ConcurrentMapCacheManager` automatically adapts to that retrieval style, and
`CaffeineCacheManager` natively supports it when its asynchronous cache mode is
enabled: set `setAsyncCacheMode(true)` on your `CaffeineCacheManager` instance.
[source,java,indent=0,subs="verbatim,quotes"]
----
@Bean
CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCacheSpecification(...);
cacheManager.setAsyncCacheMode(true);
return cacheManager;
}
----
Last but not least, be aware that annotation-driven caching is not appropriate
for sophisticated reactive interactions involving composition and back pressure.
If you choose to declare `@Cacheable` on specific reactive methods, consider the
impact of the rather coarse-granular cache interaction which simply stores the
emitted object for a `Mono` or even a pre-collected list of objects for a `Flux`.
[[cache-annotations-cacheable-condition]]
=== Conditional Caching
@@ -337,6 +407,9 @@ other), such declarations should be avoided. Note also that such conditions shou
on the result object (that is, the `#result` variable), as these are validated up-front to
confirm the exclusion.
As of 6.1, `@CachePut` takes `CompletableFuture` and reactive return types into account,
performing the put operation whenever the produced object is available.
[[cache-annotations-evict]]
== The `@CacheEvict` Annotation
@@ -379,6 +452,9 @@ trigger, the return values are ignored (as they do not interact with the cache).
not the case with `@Cacheable` which adds data to the cache or updates data in the cache
and, thus, requires a result.
As of 6.1, `@CacheEvict` takes `CompletableFuture` and reactive return types into account,
performing an after-invocation evict operation whenever processing has completed.
[[cache-annotations-caching]]
== The `@Caching` Annotation
@@ -0,0 +1,31 @@
[[checkpoint-restore]]
= JVM Checkpoint Restore
The Spring Framework integrates with checkpoint/restore as implemented by https://github.com/CRaC/docs[Project CRaC] in order to allow implementing systems capable to reduce the startup and warmup times of Spring-based Java applications with the JVM.
Using this feature requires:
* A checkpoint/restore enabled JVM (Linux only for now).
* The presence in the classpath of the https://github.com/CRaC/org.crac[`org.crac:crac`] library.
* Specifying the required `java` command line parameters like `-XX:CRaCCheckpointTo=PATH` or `-XX:CRaCRestoreFrom=PATH`.
WARNING: The files generated in the path specified by `-XX:CRaCCheckpointTo=PATH` when a checkpoint is requested contain a representation of the memory of the running JVM, which may contain secrets and other sensitive data. Using this feature should be done with the assumption that any value "seen" by the JVM, such as configuration properties coming from the environment, will be stored in those CRaC files. As a consequence, the security implications of where and how those files are generated, stored and accessed should be carefully assessed.
Conceptually, checkpoint and restore match with xref:core/beans/factory-nature.adoc#beans-factory-lifecycle-processor[Spring `Lifecycle` contract] for individual beans.
== On demand checkpoint/restore of a running application
A checkpoint can be created on demand, for example using a command like `jcmd application.jar JDK.checkpoint`. Before the creation of the checkpoint, Spring Framework
stops all the running beans, giving them a chance to close resources if needed by implementing `Lifecycle.stop`. After restore, the same beans are restarted, with `Lifecycle.start` allowing to reopen resources when relevant. For libraries not depending on Spring, checkpoint/restore custom integration can be provided by implementing `org.crac.Resource` and registering the related instance.
WARNING: Leveraging checkpoint/restore of a running application typically requires additional lifecycle management to gracefully stop and start using resources like files or sockets and stop active threads.
NOTE: If the checkpoint is created on a warmed-up JVM, the restored JVM will be equally warmed-up, allowing potentially peak performance immediately. This method typically requires access to remote services, and thus requires some level of platform integration.
== Automatic checkpoint/restore at startup
When the `-Dspring.context.checkpoint=onRefresh` Java system property is set, a checkpoint is created automatically during the startup at `LifecycleProcessor.onRefresh` level. At this phase, all non-lazy initialized singletons are instantiated, `InitializingBean.afterPropertiesSet` callbacks have been invoked, but not `Lifecycle.start` ones and `ContextRefreshedEvent` has not yet been published.
WARNING: As mentioned above, and especially in use cases where the CRaC files are shipped as part of a deployable artifact (a container image for example), operate with the assumption that any sensitive data "seen" by the JVM ends up in the CRaC files, and assess carefully the related security implications.
NOTE: Here checkpoint/restore is a way to "fast-forward" the startup of the application to a phase where the application context is about to start, but does not allow to have a fully warmed-up JVM.
@@ -21,11 +21,20 @@ As outlined xref:integration/observability.adoc[at the beginning of this section
|===
|Observation name |Description
|xref:integration/observability.adoc#http-client[`"http.client.requests"`]
|xref:integration/observability.adoc#observability.http-client[`"http.client.requests"`]
|Time spent for HTTP client exchanges
|xref:integration/observability.adoc#http-server[`"http.server.requests"`]
|xref:integration/observability.adoc#observability.http-server[`"http.server.requests"`]
|Processing time for HTTP server exchanges at the Framework level
|xref:integration/observability.adoc#observability.jms.publish[`"jms.message.publish"`]
|Time spent sending a JMS message to a destination by a message producer.
|xref:integration/observability.adoc#observability.jms.process[`"jms.message.process"`]
|Processing time for a JMS message that was previously received by a message consumer.
|xref:integration/observability.adoc#observability.tasks-scheduled[`"tasks.scheduled.execution"`]
|Processing time for an execution of a `@Scheduled` task
|===
NOTE: Observations are using Micrometer's official naming convention, but Metrics names will be automatically converted
@@ -79,6 +88,97 @@ include-code::./ServerRequestObservationFilter[]
You can configure `ObservationFilter` instances on the `ObservationRegistry`.
[[observability.tasks-scheduled]]
== @Scheduled tasks instrumentation
An Observation is created for xref:integration/scheduling.adoc#scheduling-enable-annotation-support[each execution of an `@Scheduled` task].
Applications need to configure the `ObservationRegistry` on the `ScheduledTaskRegistrar` to enable the recording of observations.
This can be done by declaring a `SchedulingConfigurer` bean that sets the observation registry:
include-code::./ObservationSchedulingConfigurer[]
It is using the `org.springframework.scheduling.support.DefaultScheduledTaskObservationConvention` by default, backed by the `ScheduledTaskObservationContext`.
You can configure a custom implementation on the `ObservationRegistry` directly.
During the execution of the scheduled method, the current observation is restored in the `ThreadLocal` context or the Reactor context (if the scheduled method returns a `Mono` or `Flux` type).
By default, the following `KeyValues` are created:
.Low cardinality Keys
[cols="a,a"]
|===
|Name | Description
|`code.function` _(required)_|Name of Java `Method` that is scheduled for execution.
|`code.namespace` _(required)_|Canonical name of the class of the bean instance that holds the scheduled method.
|`exception` _(required)_|Name of the exception thrown during the execution, or `"none"` if no exception happened.
|`outcome` _(required)_|Outcome of the method execution. Can be `"SUCCESS"`, `"ERROR"` or `"UNKNOWN"` (if for example the operation was cancelled during execution).
|===
[[observability.jms]]
== JMS messaging instrumentation
Spring Framework uses the Jakarta JMS instrumentation provided by Micrometer if the `io.micrometer:micrometer-core` dependency is on the classpath.
The `io.micrometer.core.instrument.binder.jms.JmsInstrumentation` instruments `jakarta.jms.Session` and records the relevant observations.
This instrumentation will create 2 types of observations:
* `"jms.message.publish"` when a JMS message is sent to the broker, typically with `JmsTemplate`.
* `"jms.message.process"` when a JMS message is processed by the application, typically with a `MessageListener` or a `@JmsListener` annotated method.
NOTE: currently there is no instrumentation for `"jms.message.receive"` observations as there is little value in measuring the time spent waiting for the reception of a message.
Such an integration would typically instrument `MessageConsumer#receive` method calls. But once those return, the processing time is not measured and the trace scope cannot be propagated to the application.
By default, both observations share the same set of possible `KeyValues`:
.Low cardinality Keys
[cols="a,a"]
|===
|Name | Description
|`exception` |Class name of the exception thrown during the messaging operation (or "none").
|`messaging.destination.temporary` _(required)_|Whether the destination is a `TemporaryQueue` or `TemporaryTopic` (values: `"true"` or `"false"`).
|`messaging.operation` _(required)_|Name of JMS operation being performed (values: `"publish"` or `"process"`).
|===
.High cardinality Keys
[cols="a,a"]
|===
|Name | Description
|`messaging.message.conversation_id` |The correlation ID of the JMS message.
|`messaging.destination.name` |The name of destination the current message was sent to.
|`messaging.message.id` |Value used by the messaging system as an identifier for the message.
|===
[[observability.jms.publish]]
=== JMS message Publication instrumentation
`"jms.message.publish"` observations are recorded when a JMS message is sent to the broker.
They measure the time spent sending the message and propagate the tracing information with outgoing JMS message headers.
You will need to configure the `ObservationRegistry` on the `JmsTemplate` to enable observations:
include-code::./JmsTemplatePublish[]
It uses the `io.micrometer.core.instrument.binder.jms.DefaultJmsPublishObservationConvention` by default, backed by the `io.micrometer.core.instrument.binder.jms.JmsPublishObservationContext`.
[[observability.jms.process]]
=== JMS message Processing instrumentation
`"jms.message.process"` observations are recorded when a JMS message is processed by the application.
They measure the time spent processing the message and propagate the tracing context with incoming JMS message headers.
Most applications will use the xref:integration/jms/annotated.adoc#jms-annotated[`@JmsListener` annotated methods] mechanism to process incoming messages.
You will need to ensure that the `ObservationRegistry` is configured on the dedicated `JmsListenerContainerFactory`:
include-code::./JmsConfiguration[]
A xref:integration/jms/annotated.adoc#jms-annotated-support[default container factory is required to enable the annotation support],
but note that `@JmsListener` annotations can refer to specific container factory beans for specific purposes.
In all cases, Observations are only recorded if the observation registry is configured on the container factory.
Similar observations are recorded with `JmsTemplate` when messages are processed by a `MessageListener`.
Such listeners are set on a `MessageConsumer` within a session callback (see `JmsTemplate.execute(SessionCallback<T>)`).
This observation uses the `io.micrometer.core.instrument.binder.jms.DefaultJmsProcessObservationConvention` by default, backed by the `io.micrometer.core.instrument.binder.jms.JmsProcessObservationContext`.
[[observability.http-server]]
== HTTP Server instrumentation
@@ -107,7 +207,7 @@ By default, the following `KeyValues` are created:
[cols="a,a"]
|===
|Name | Description
|`exception` _(required)_|Name of the exception thrown during the exchange, or `KeyValue#NONE_VALUE`} if no exception happened.
|`exception` _(required)_|Name of the exception thrown during the exchange, or `"none"` if no exception happened.
|`method` _(required)_|Name of HTTP request method or `"none"` if the request was not received properly.
|`outcome` _(required)_|Outcome of the HTTP server exchange.
|`status` _(required)_|HTTP response raw status code, or `"UNKNOWN"` if no response was created.
@@ -125,11 +225,15 @@ By default, the following `KeyValues` are created:
[[observability.http-server.reactive]]
=== Reactive applications
Applications need to configure the `org.springframework.web.filter.reactive.ServerHttpObservationFilter` reactive `WebFilter` in their application.
It uses the `org.springframework.http.server.reactive.observation.DefaultServerRequestObservationConvention` by default, backed by the `ServerRequestObservationContext`.
Applications need to configure the `WebHttpHandlerBuilder` with a `MeterRegistry` to enable server instrumentation.
This can be done on the `WebHttpHandlerBuilder`, as follows:
This will only record an observation as an error if the `Exception` has not been handled by the web framework and has bubbled up to the `WebFilter`.
Typically, all exceptions handled by Spring WebFlux's `@ExceptionHandler` and xref:web/webflux/ann-rest-exceptions.adoc[`ProblemDetail` support] will not be recorded with the observation.
include-code::./HttpHandlerConfiguration[]
It is using the `org.springframework.http.server.reactive.observation.DefaultServerRequestObservationConvention` by default, backed by the `ServerRequestObservationContext`.
This will only record an observation as an error if the `Exception` has not been handled by an application Controller.
Typically, all exceptions handled by Spring WebFlux's `@ExceptionHandler` and <<web.adoc#webflux-ann-rest-exceptions,`ProblemDetail` support>> will not be recorded with the observation.
You can, at any point during request processing, set the error field on the `ObservationContext` yourself:
include-code::./UserController[]
@@ -190,6 +294,32 @@ Instrumentation uses the `org.springframework.http.client.observation.ClientRequ
|===
[[observability.http-client.restclient]]
=== RestClient
Applications must configure an `ObservationRegistry` on the `RestClient.Builder` to enable the instrumentation; without that, observations are "no-ops".
Instrumentation uses the `org.springframework.http.client.observation.ClientRequestObservationConvention` by default, backed by the `ClientRequestObservationContext`.
.Low cardinality Keys
[cols="a,a"]
|===
|Name | Description
|`method` _(required)_|Name of HTTP request method or `"none"` if the request could not be created.
|`uri` _(required)_|URI template used for HTTP request, or `"none"` if none was provided. Only the path part of the URI is considered.
|`client.name` _(required)_|Client name derived from the request URI host.
|`status` _(required)_|HTTP response raw status code, or `"IO_ERROR"` in case of `IOException`, or `"CLIENT_ERROR"` if no response was received.
|`outcome` _(required)_|Outcome of the HTTP client exchange.
|`exception` _(required)_|Name of the exception thrown during the exchange, or `"none"` if no exception happened.
|===
.High cardinality Keys
[cols="a,a"]
|===
|Name | Description
|`http.url` _(required)_|HTTP request URI.
|===
[[observability.http-client.webclient]]
=== WebClient
@@ -219,3 +349,28 @@ Instrumentation uses the `org.springframework.web.reactive.function.client.Clien
|===
[[observability.application-events]]
== Application Events and `@EventListener`
Spring Framework does not contribute Observations for xref:core/beans/context-introduction.adoc#context-functionality-events-annotation[`@EventListener` calls],
as they don't have the right semantics for such instrumentation.
By default, event publication and processing is done synchronously and on the same Thread.
This means that during the execution of that task, the ThreadLocals and logging context will be the same as the event publisher.
If the application configures globally a custom `ApplicationEventMulticaster` with a strategy that schedules event processing on different threads, this is no longer true.
All `@EventListener` methods will be processed on a different thread, outstide of the main event publication thread.
In these cases, the https://micrometer.io/docs/contextPropagation[Micrometer Context Propagation library] can help propagating such values and better correlate the processing of the events.
The application can configure the chosen `TaskExecutor` to use a `ContextPropagatingTaskDecorator` that decorates tasks and propagates context.
For this to work, the `io.micrometer:context-propagation` library must be present on the classpath:
include-code::./ApplicationEventsConfiguration[]
Similarly, if that asynchronous choice is made locally for each `@EventListener` annotated method, by adding an `@Async` method to it,
you can choose a `TaskExecutor` that propagates context by referring to it by its qualifier.
Given the following `TaskExecutor` bean definition, configured with the dedicated task decorator:
include-code::./EventAsyncExecutionConfiguration[]
Annotating event listeners with `@Async` and the relevant qualifier will achieve similar context propagation results:
include-code::./EmailNotificationListener[]
@@ -3,11 +3,467 @@
The Spring Framework provides the following choices for making calls to REST endpoints:
* xref:integration/rest-clients.adoc#rest-restclient[`RestClient`] - synchronous client with a fluent API.
* xref:integration/rest-clients.adoc#rest-webclient[`WebClient`] - non-blocking, reactive client with fluent API.
* xref:integration/rest-clients.adoc#rest-resttemplate[`RestTemplate`] - synchronous client with template method API.
* xref:integration/rest-clients.adoc#rest-http-interface[HTTP Interface] - annotated interface with generated, dynamic proxy implementation.
[[rest-restclient]]
== `RestClient`
The `RestClient` is a synchronous HTTP client that offers a modern, fluent API.
It offers an abstraction over HTTP libraries that allows for convenient conversion from Java object to HTTP request, and creation of objects from the HTTP response.
=== Creating a `RestClient`
The `RestClient` is created using one of the static `create` methods.
You can also use `builder` to get a builder with further options, such as specifying which HTTP library to use (see <<rest-request-factories>>) and which message converters to use (see <<rest-message-conversion>>), setting a default URI, default path variables, a default request headers, or `uriBuilderFactory`, or registering interceptors and initializers.
Once created (or built), the `RestClient` can be used safely by multiple threads.
The following sample shows how to create a default `RestClient`, and how to build a custom one.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
RestClient defaultClient = RestClient.create();
RestClient customClient = RestClient.builder()
.requestFactory(new HttpComponentsClientHttpRequestFactory())
.messageConverters(converters -> converters.add(new MyCustomMessageConverter()))
.baseUrl("https://example.com")
.defaultUriVariables(Map.of("variable", "foo"))
.defaultHeader("My-Header", "Foo")
.requestInterceptor(myCustomInterceptor)
.requestInitializer(myCustomInitializer)
.build();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
val defaultClient = RestClient.create()
val customClient = RestClient.builder()
.requestFactory(HttpComponentsClientHttpRequestFactory())
.messageConverters(converters -> converters.add(MyCustomMessageConverter()))
.baseUrl("https://example.com")
.defaultUriVariables(Map.of("variable", "foo"))
.defaultHeader("My-Header", "Foo")
.requestInterceptor(myCustomInterceptor)
.requestInitializer(myCustomInitializer)
.build()
----
======
=== Using the `RestClient`
When making an HTTP request with the `RestClient`, the first thing to specify is which HTTP method to use.
This can be done with `method(HttpMethod)`, or with the convenience methods `get()`, `head()`, `post()`, and so on.
==== Request URL
Next, the request URI can be specified with the `uri` methods
This step is optional, and can be skipped if the `RestClient` is configured with a default URI.
The URL is typically specified as `String`, with optional URI template variables.
String URLs are encoded by default, but this can be changed by building a client with a custom `uriBuilderFactory`.
The URL can also be provided with a function, or as `java.net.URI`, both of which are not encoded.
For more details on working with and encoding URIs, see xref:web/webmvc/mvc-uri-building.adoc[URI Links].
==== Request headers and body
If necessary, the HTTP request can be manipulated, by adding request headers with `header(String, String)`, `headers(Consumer<HttpHeaders>`, or with the convenience methods `accept(MediaType...)`, `acceptCharset(Charset...)` and so on.
For HTTP request that can contain a body (`POST`, `PUT`, and `PATCH`), additional methods are available: `contentType(MediaType)`, and `contentLength(long)`.
The request body itself can be set by `body(Object)`, which internally uses <<rest-message-conversion>>.
Alternatively, the request body can be set using a `ParameterizedTypeReference`, allowing you to use generics.
Finally, the body can be set to a callback function that writes to an `OutputStream`.
==== Retrieving the response
Once the request has been set up, the HTTP response is accessed by invoking `retrieve()`.
The response body can be accessed by using `body(Class)`, or `body(ParameterizedTypeReference)` for parameterized types like lists.
The `body` method converts the response contents into various types, for instance bytes can be converted into a `String`, JSON into objects using Jackson, and so on (see <<rest-message-conversion>>).
The response can also be converted into a `ResponseEntity`, giving access to the response headers as well as the body.
This sample shows how `RestClient` can be used to perform a simple GET request.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
String result = restClient.get() <1>
.uri("https://example.com") <2>
.retrieve() <3>
.body(String.class); <4>
System.out.println(result); <5>
----
<1> Set up a GET request
<2> Specify the URL to connect to
<3> Retrieve the response
<4> Convert the response into a string
<5> Print the result
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val result= restClient.get() <1>
.uri("https://example.com") <2>
.retrieve() <3>
.body<String>() <4>
println(result) <5>
----
<1> Set up a GET request
<2> Specify the URL to connect to
<3> Retrieve the response
<4> Convert the response into a string
<5> Print the result
======
Access to the response status code and headers is provided through `ResponseEntity`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
ResponseEntity<String> result = restClient.get() <1>
.uri("https://example.com") <1>
.retrieve()
.toEntity(String.class); <2>
System.out.println("Response status: " + result.getStatusCode()); <3>
System.out.println("Response headers: " + result.getHeaders()); <3>
System.out.println("Contents: " + result.getBody()); <3>
----
<1> Set up a GET request for the specified URL
<2> Convert the response into a `ResponseEntity`
<3> Print the result
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val result = restClient.get() <1>
.uri("https://example.com") <1>
.retrieve()
.toEntity<String>() <2>
println("Response status: " + result.statusCode) <3>
println("Response headers: " + result.headers) <3>
println("Contents: " + result.body) <3>
----
<1> Set up a GET request for the specified URL
<2> Convert the response into a `ResponseEntity`
<3> Print the result
======
`RestClient` can convert JSON to objects, using the Jackson library.
Note the usage of uri variables in this sample, and that the `Accept` header is set to JSON.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
int id = ...;
Pet pet = restClient.get()
.uri("https://petclinic.example.com/pets/{id}", id) <1>
.accept(APPLICATION_JSON) <2>
.retrieve()
.body(Pet.class); <3>
----
<1> Using URI variables
<2> Set the `Accept` header to `application/json`
<3> Convert the JSON response into a `Pet` domain object
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val id = ...
val pet = restClient.get()
.uri("https://petclinic.example.com/pets/{id}", id) <1>
.accept(APPLICATION_JSON) <2>
.retrieve()
.body<Pet>() <3>
----
<1> Using URI variables
<2> Set the `Accept` header to `application/json`
<3> Convert the JSON response into a `Pet` domain object
======
In the next sample, `RestClient` is used to perform a POST request that contains JSON, which again is converted using Jackson.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
Pet pet = ... <1>
ResponseEntity<Void> response = restClient.post() <2>
.uri("https://petclinic.example.com/pets/new") <2>
.contentType(APPLICATION_JSON) <3>
.body(pet) <4>
.retrieve()
.toBodilessEntity(); <5>
----
<1> Create a `Pet` domain object
<2> Set up a POST request, and the URL to connect to
<3> Set the `Content-Type` header to `application/json`
<4> Use `pet` as the request body
<5> Convert the response into a response entity with no body.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val pet: Pet = ... <1>
val response = restClient.post() <2>
.uri("https://petclinic.example.com/pets/new") <2>
.contentType(APPLICATION_JSON) <3>
.body(pet) <4>
.retrieve()
.toBodilessEntity() <5>
----
<1> Create a `Pet` domain object
<2> Set up a POST request, and the URL to connect to
<3> Set the `Content-Type` header to `application/json`
<4> Use `pet` as the request body
<5> Convert the response into a response entity with no body.
======
==== Error handling
By default, `RestClient` throws a subclass of `RestClientException` when retrieving a response with a 4xx or 5xx status code.
This behavior can be overriden using `onStatus`.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
String result = restClient.get() <1>
.uri("https://example.com/this-url-does-not-exist") <1>
.retrieve()
.onStatus(HttpStatusCode::is4xxClientError, (request, response) -> { <2>
throw new MyCustomRuntimeException(response.getStatusCode(), response.getHeaders()) <3>
})
.body(String.class);
----
<1> Create a GET request for a URL that returns a 404 status code
<2> Set up a status handler for all 4xx status codes
<3> Throw a custom exception
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val result = restClient.get() <1>
.uri("https://example.com/this-url-does-not-exist") <1>
.retrieve()
.onStatus(HttpStatusCode::is4xxClientError) { _, response -> <2>
throw MyCustomRuntimeException(response.getStatusCode(), response.getHeaders()) } <3>
.body<String>()
----
<1> Create a GET request for a URL that returns a 404 status code
<2> Set up a status handler for all 4xx status codes
<3> Throw a custom exception
======
==== Exchange
For more advanced scenarios, the `RestClient` gives access to the underlying HTTP request and response through the `exchange` method, which can be used instead of `retrieve()`.
Status handlers are not applied when you exchange, because the exchange function already provides access to the full response, allowing you to perform any error handling necessary.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
Pet result = restClient.get()
.uri("https://petclinic.example.com/pets/{id}", id)
.accept(APPLICATION_JSON)
.exchange((request, response) -> { <1>
if (response.getStatusCode().is4xxClientError()) { <2>
throw new MyCustomRuntimeException(response.getStatusCode(), response.getHeaders()); <2>
}
else {
Pet pet = convertResponse(response); <3>
return pet;
}
});
----
<1> `exchange` provides the request and response
<2> Throw an exception when the response has a 4xx status code
<3> Convert the response into a Pet domain object
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val result = restClient.get()
.uri("https://petclinic.example.com/pets/{id}", id)
.accept(MediaType.APPLICATION_JSON)
.exchange { request, response -> <1>
if (response.getStatusCode().is4xxClientError()) { <2>
throw MyCustomRuntimeException(response.getStatusCode(), response.getHeaders()) <2>
} else {
val pet: Pet = convertResponse(response) <3>
pet
}
}
----
<1> `exchange` provides the request and response
<2> Throw an exception when the response has a 4xx status code
<3> Convert the response into a Pet domain object
======
[[rest-message-conversion]]
=== HTTP Message Conversion
[.small]#xref:web/webflux/reactive-spring.adoc#webflux-codecs[See equivalent in the Reactive stack]#
The `spring-web` module contains the `HttpMessageConverter` interface for reading and writing the body of HTTP requests and responses through `InputStream` and `OutputStream`.
`HttpMessageConverter` instances are used on the client side (for example, in the `RestClient`) and on the server side (for example, in Spring MVC REST controllers).
Concrete implementations for the main media (MIME) types are provided in the framework and are, by default, registered with the `RestClient` and `RestTemplate` on the client side and with `RequestMappingHandlerAdapter` on the server side (see xref:web/webmvc/mvc-config/message-converters.adoc[Configuring Message Converters]).
Several implementations of `HttpMessageConverter` are described below.
Refer to the {api-spring-framework}/http/converter/HttpMessageConverter.html[`HttpMessageConverter` Javadoc] for the complete list.
For all converters, a default media type is used, but you can override it by setting the `supportedMediaTypes` property.
[[rest-message-converters-tbl]]
.HttpMessageConverter Implementations
[cols="1,3"]
|===
| MessageConverter | Description
| `StringHttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write `String` instances from the HTTP request and response.
By default, this converter supports all text media types(`text/{asterisk}`) and writes with a `Content-Type` of `text/plain`.
| `FormHttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write form data from the HTTP request and response.
By default, this converter reads and writes the `application/x-www-form-urlencoded` media type.
Form data is read from and written into a `MultiValueMap<String, String>`.
The converter can also write (but not read) multipart data read from a `MultiValueMap<String, Object>`.
By default, `multipart/form-data` is supported.
Additional multipart subtypes can be supported for writing form data.
Consult the javadoc for `FormHttpMessageConverter` for further details.
| `ByteArrayHttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write byte arrays from the HTTP request and response.
By default, this converter supports all media types (`{asterisk}/{asterisk}`) and writes with a `Content-Type` of `application/octet-stream`.
You can override this by setting the `supportedMediaTypes` property and overriding `getContentType(byte[])`.
| `MarshallingHttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write XML by using Spring's `Marshaller` and `Unmarshaller` abstractions from the `org.springframework.oxm` package.
This converter requires a `Marshaller` and `Unmarshaller` before it can be used.
You can inject these through constructor or bean properties.
By default, this converter supports `text/xml` and `application/xml`.
| `MappingJackson2HttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write JSON by using Jackson's `ObjectMapper`.
You can customize JSON mapping as needed through the use of Jackson's provided annotations.
When you need further control (for cases where custom JSON serializers/deserializers need to be provided for specific types), you can inject a custom `ObjectMapper` through the `ObjectMapper` property.
By default, this converter supports `application/json`.
| `MappingJackson2XmlHttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write XML by using https://github.com/FasterXML/jackson-dataformat-xml[Jackson XML] extension's `XmlMapper`.
You can customize XML mapping as needed through the use of JAXB or Jackson's provided annotations.
When you need further control (for cases where custom XML serializers/deserializers need to be provided for specific types), you can inject a custom `XmlMapper` through the `ObjectMapper` property.
By default, this converter supports `application/xml`.
| `SourceHttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write `javax.xml.transform.Source` from the HTTP request and response.
Only `DOMSource`, `SAXSource`, and `StreamSource` are supported.
By default, this converter supports `text/xml` and `application/xml`.
|===
By default, `RestClient` and `RestTemplate` register all built-in message converters, depending on the availability of underlying libraries on the classpath.
You can also set the message converters to use explicitly, by using `messageConverters` on the `RestClient` builder, or via the `messageConverters` property of `RestTemplate`.
==== Jackson JSON Views
To serialize only a subset of the object properties, you can specify a https://www.baeldung.com/jackson-json-view-annotation[Jackson JSON View], as the following example shows:
[source,java,indent=0,subs="verbatim"]
----
MappingJacksonValue value = new MappingJacksonValue(new User("eric", "7!jd#h23"));
value.setSerializationView(User.WithoutPasswordView.class);
ResponseEntity<Void> response = restClient.post() // or RestTemplate.postForEntity
.contentType(APPLICATION_JSON)
.body(value)
.retrieve()
.toBodilessEntity();
----
==== Multipart
To send multipart data, you need to provide a `MultiValueMap<String, Object>` whose values may be an `Object` for part content, a `Resource` for a file part, or an `HttpEntity` for part content with headers.
For example:
[source,java,indent=0,subs="verbatim"]
----
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>();
parts.add("fieldPart", "fieldValue");
parts.add("filePart", new FileSystemResource("...logo.png"));
parts.add("jsonPart", new Person("Jason"));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_XML);
parts.add("xmlPart", new HttpEntity<>(myBean, headers));
// send using RestClient.post or RestTemplate.postForEntity
----
In most cases, you do not have to specify the `Content-Type` for each part.
The content type is determined automatically based on the `HttpMessageConverter` chosen to serialize it or, in the case of a `Resource` based on the file extension.
If necessary, you can explicitly provide the `MediaType` with an `HttpEntity` wrapper.
Once the `MultiValueMap` is ready, you can use it as the body of a POST request, using `RestClient.post().body(parts)` (or `RestTemplate.postForObject`).
If the `MultiValueMap` contains at least one non-`String` value, the `Content-Type` is set to `multipart/form-data` by the `FormHttpMessageConverter`.
If the `MultiValueMap` has `String` values the `Content-Type` defaults to `application/x-www-form-urlencoded`.
If necessary the `Content-Type` may also be set explicitly.
[[rest-request-factories]]
=== Client Request Factories
To execute the HTTP request, `RestClient` uses a client HTTP library.
These libraries are adapted via the `ClientRequestFactory` interface.
Various implementations are available:
* `JdkClientHttpRequestFactory` for Java's `HttpClient`,
* `HttpComponentsClientHttpRequestFactory` for use with Apache HTTP Components `HttpClient`,
* `JettyClientHttpRequestFactory` for Jetty's `HttpClient`,
* `ReactorNettyClientRequestFactory` for Reactor Netty's `HttpClient`,
* `SimpleClientHttpRequestFactory` as a simple default.
If no request factory is specified when the `RestClient` was built, it will use the Apache or Jetty `HttpClient` if they are available on the classpath.
Otherwise, if the `java.net.http` module is loaded, it will use Java's `HttpClient`.
Finally, it will resort to the simple default.
[[rest-webclient]]
== `WebClient`
@@ -32,13 +488,11 @@ See xref:web/webflux-webclient.adoc[WebClient] for more details.
[[rest-resttemplate]]
== `RestTemplate`
The `RestTemplate` provides a higher level API over HTTP client libraries. It makes it
easy to invoke REST endpoints in a single line. It exposes the following groups of
overloaded methods:
The `RestTemplate` provides a high-level API over HTTP client libraries in the form of a classic Spring Template class.
It exposes the following groups of overloaded methods:
NOTE: `RestTemplate` is in maintenance mode, with only requests for minor
changes and bugs to be accepted. Please, consider using the
xref:web/webflux-webclient.adoc[WebClient] instead.
NOTE: The xref:integration/rest-clients.adoc#rest-restclient[`RestClient`] offers a more modern API for synchronous HTTP access.
For asynchronous and streaming scenarios, consider the reactive xref:web/webflux-webclient.adoc[WebClient].
[[rest-overview-of-resttemplate-methods-tbl]]
.RestTemplate methods
@@ -69,8 +523,7 @@ xref:web/webflux-webclient.adoc[WebClient] instead.
| `patchForObject`
| Updates a resource by using PATCH and returns the representation from the response.
Note that the JDK `HttpURLConnection` does not support `PATCH`, but Apache
HttpComponents and others do.
Note that the JDK `HttpURLConnection` does not support `PATCH`, but Apache HttpComponents and others do.
| `delete`
| Deletes the resources at the specified URI by using DELETE.
@@ -79,9 +532,8 @@ HttpComponents and others do.
| Retrieves allowed HTTP methods for a resource by using ALLOW.
| `exchange`
| More generalized (and less opinionated) version of the preceding methods that provides extra
flexibility when needed. It accepts a `RequestEntity` (including HTTP method, URL, headers,
and body as input) and returns a `ResponseEntity`.
| More generalized (and less opinionated) version of the preceding methods that provides extra flexibility when needed.
It accepts a `RequestEntity` (including HTTP method, URL, headers, and body as input) and returns a `ResponseEntity`.
These methods allow the use of `ParameterizedTypeReference` instead of `Class` to specify
a response type with generics.
@@ -92,276 +544,30 @@ preparation and response extraction through callback interfaces.
|===
[[rest-resttemplate-create]]
=== Initialization
The default constructor uses `java.net.HttpURLConnection` to perform requests. You can
switch to a different HTTP library with an implementation of `ClientHttpRequestFactory`.
There is built-in support for the following:
* Apache HttpComponents
* Netty
* OkHttp
For example, to switch to Apache HttpComponents, you can use the following:
[source,java,indent=0,subs="verbatim,quotes"]
----
RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
----
Each `ClientHttpRequestFactory` exposes configuration options specific to the underlying
HTTP client library -- for example, for credentials, connection pooling, and other details.
TIP: Note that the `java.net` implementation for HTTP requests can raise an exception when
accessing the status of a response that represents an error (such as 401). If this is an
issue, switch to another HTTP client library.
`RestTemplate` uses the same HTTP library abstraction as `RestClient`.
By default, it uses the `SimpleClientHttpRequestFactory`, but this can be changed via the constructor.
See <<rest-request-factories>>.
NOTE: `RestTemplate` can be instrumented for observability, in order to produce metrics and traces.
See the xref:integration/observability.adoc#http-client.resttemplate[RestTemplate Observability support] section.
[[rest-resttemplate-uri]]
==== URIs
Many of the `RestTemplate` methods accept a URI template and URI template variables,
either as a `String` variable argument, or as `Map<String,String>`.
The following example uses a `String` variable argument:
[source,java,indent=0,subs="verbatim,quotes"]
----
String result = restTemplate.getForObject(
"https://example.com/hotels/{hotel}/bookings/{booking}", String.class, "42", "21");
----
The following example uses a `Map<String, String>`:
[source,java,indent=0,subs="verbatim,quotes"]
----
Map<String, String> vars = Collections.singletonMap("hotel", "42");
String result = restTemplate.getForObject(
"https://example.com/hotels/{hotel}/rooms/{hotel}", String.class, vars);
----
Keep in mind URI templates are automatically encoded, as the following example shows:
[source,java,indent=0,subs="verbatim,quotes"]
----
restTemplate.getForObject("https://example.com/hotel list", String.class);
// Results in request to "https://example.com/hotel%20list"
----
You can use the `uriTemplateHandler` property of `RestTemplate` to customize how URIs
are encoded. Alternatively, you can prepare a `java.net.URI` and pass it into one of
the `RestTemplate` methods that accepts a `URI`.
For more details on working with and encoding URIs, see xref:web/webmvc/mvc-uri-building.adoc[URI Links].
[[rest-template-headers]]
==== Headers
You can use the `exchange()` methods to specify request headers, as the following example shows:
[source,java,indent=0,subs="verbatim,quotes"]
----
String uriTemplate = "https://example.com/hotels/{hotel}";
URI uri = UriComponentsBuilder.fromUriString(uriTemplate).build(42);
RequestEntity<Void> requestEntity = RequestEntity.get(uri)
.header("MyRequestHeader", "MyValue")
.build();
ResponseEntity<String> response = template.exchange(requestEntity, String.class);
String responseHeader = response.getHeaders().getFirst("MyResponseHeader");
String body = response.getBody();
----
You can obtain response headers through many `RestTemplate` method variants that return
`ResponseEntity`.
[[rest-template-body]]
=== Body
Objects passed into and returned from `RestTemplate` methods are converted to and from raw
content with the help of an `HttpMessageConverter`.
On a POST, an input object is serialized to the request body, as the following example shows:
----
URI location = template.postForLocation("https://example.com/people", person);
----
You need not explicitly set the Content-Type header of the request. In most cases,
you can find a compatible message converter based on the source `Object` type, and the chosen
message converter sets the content type accordingly. If necessary, you can use the
`exchange` methods to explicitly provide the `Content-Type` request header, and that, in
turn, influences what message converter is selected.
On a GET, the body of the response is deserialized to an output `Object`, as the following example shows:
----
Person person = restTemplate.getForObject("https://example.com/people/{id}", Person.class, 42);
----
The `Accept` header of the request does not need to be explicitly set. In most cases,
a compatible message converter can be found based on the expected response type, which
then helps to populate the `Accept` header. If necessary, you can use the `exchange`
methods to provide the `Accept` header explicitly.
By default, `RestTemplate` registers all built-in
xref:integration/rest-clients.adoc#rest-message-conversion[message converters], depending on classpath checks that help
to determine what optional conversion libraries are present. You can also set the message
converters to use explicitly.
[[rest-message-conversion]]
==== Message Conversion
[.small]#xref:web/webflux/reactive-spring.adoc#webflux-codecs[See equivalent in the Reactive stack]#
The `spring-web` module contains the `HttpMessageConverter` contract for reading and
writing the body of HTTP requests and responses through `InputStream` and `OutputStream`.
`HttpMessageConverter` instances are used on the client side (for example, in the `RestTemplate`) and
on the server side (for example, in Spring MVC REST controllers).
Concrete implementations for the main media (MIME) types are provided in the framework
and are, by default, registered with the `RestTemplate` on the client side and with
`RequestMappingHandlerAdapter` on the server side (see
xref:web/webmvc/mvc-config/message-converters.adoc[Configuring Message Converters]).
The implementations of `HttpMessageConverter` are described in the following sections.
For all converters, a default media type is used, but you can override it by setting the
`supportedMediaTypes` bean property. The following table describes each implementation:
[[rest-message-converters-tbl]]
.HttpMessageConverter Implementations
[cols="1,3"]
|===
| MessageConverter | Description
| `StringHttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write `String` instances from the HTTP
request and response. By default, this converter supports all text media types
(`text/{asterisk}`) and writes with a `Content-Type` of `text/plain`.
| `FormHttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write form data from the HTTP
request and response. By default, this converter reads and writes the
`application/x-www-form-urlencoded` media type. Form data is read from and written into a
`MultiValueMap<String, String>`. The converter can also write (but not read) multipart
data read from a `MultiValueMap<String, Object>`. By default, `multipart/form-data` is
supported. As of Spring Framework 5.2, additional multipart subtypes can be supported for
writing form data. Consult the javadoc for `FormHttpMessageConverter` for further details.
| `ByteArrayHttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write byte arrays from the
HTTP request and response. By default, this converter supports all media types (`{asterisk}/{asterisk}`)
and writes with a `Content-Type` of `application/octet-stream`. You can override this
by setting the `supportedMediaTypes` property and overriding `getContentType(byte[])`.
| `MarshallingHttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write XML by using Spring's
`Marshaller` and `Unmarshaller` abstractions from the `org.springframework.oxm` package.
This converter requires a `Marshaller` and `Unmarshaller` before it can be used. You can inject these
through constructor or bean properties. By default, this converter supports
`text/xml` and `application/xml`.
| `MappingJackson2HttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write JSON by using Jackson's
`ObjectMapper`. You can customize JSON mapping as needed through the use of Jackson's
provided annotations. When you need further control (for cases where custom JSON
serializers/deserializers need to be provided for specific types), you can inject a custom `ObjectMapper`
through the `ObjectMapper` property. By default, this
converter supports `application/json`.
| `MappingJackson2XmlHttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write XML by using
https://github.com/FasterXML/jackson-dataformat-xml[Jackson XML] extension's
`XmlMapper`. You can customize XML mapping as needed through the use of JAXB
or Jackson's provided annotations. When you need further control (for cases where custom XML
serializers/deserializers need to be provided for specific types), you can inject a custom `XmlMapper`
through the `ObjectMapper` property. By default, this
converter supports `application/xml`.
| `SourceHttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write
`javax.xml.transform.Source` from the HTTP request and response. Only `DOMSource`,
`SAXSource`, and `StreamSource` are supported. By default, this converter supports
`text/xml` and `application/xml`.
| `BufferedImageHttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write
`java.awt.image.BufferedImage` from the HTTP request and response. This converter reads
and writes the media type supported by the Java I/O API.
|===
[[rest-template-jsonview]]
=== Jackson JSON Views
You can specify a https://www.baeldung.com/jackson-json-view-annotation[Jackson JSON View]
to serialize only a subset of the object properties, as the following example shows:
[source,java,indent=0,subs="verbatim,quotes"]
----
MappingJacksonValue value = new MappingJacksonValue(new User("eric", "7!jd#h23"));
value.setSerializationView(User.WithoutPasswordView.class);
RequestEntity<MappingJacksonValue> requestEntity =
RequestEntity.post(new URI("https://example.com/user")).body(value);
ResponseEntity<String> response = template.exchange(requestEntity, String.class);
----
[[rest-template-multipart]]
=== Multipart
To send multipart data, you need to provide a `MultiValueMap<String, Object>` whose values
may be an `Object` for part content, a `Resource` for a file part, or an `HttpEntity` for
part content with headers. For example:
[source,java,indent=0,subs="verbatim,quotes"]
----
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>();
parts.add("fieldPart", "fieldValue");
parts.add("filePart", new FileSystemResource("...logo.png"));
parts.add("jsonPart", new Person("Jason"));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_XML);
parts.add("xmlPart", new HttpEntity<>(myBean, headers));
----
In most cases, you do not have to specify the `Content-Type` for each part. The content
type is determined automatically based on the `HttpMessageConverter` chosen to serialize
it or, in the case of a `Resource` based on the file extension. If necessary, you can
explicitly provide the `MediaType` with an `HttpEntity` wrapper.
Once the `MultiValueMap` is ready, you can pass it to the `RestTemplate`, as show below:
[source,java,indent=0,subs="verbatim,quotes"]
----
MultiValueMap<String, Object> parts = ...;
template.postForObject("https://example.com/upload", parts, Void.class);
----
If the `MultiValueMap` contains at least one non-`String` value, the `Content-Type` is set
to `multipart/form-data` by the `FormHttpMessageConverter`. If the `MultiValueMap` has
`String` values the `Content-Type` is defaulted to `application/x-www-form-urlencoded`.
If necessary the `Content-Type` may also be set explicitly.
Objects passed into and returned from `RestTemplate` methods are converted to and from HTTP messages with the help of an `HttpMessageConverter`, see <<rest-message-conversion>>.
[[rest-http-interface]]
== HTTP Interface
The Spring Framework lets you define an HTTP service as a Java interface with annotated
methods for HTTP exchanges. You can then generate a proxy that implements this interface
and performs the exchanges. This helps to simplify HTTP remote access which often
involves a facade that wraps the details of using the underlying HTTP client.
The Spring Framework lets you define an HTTP service as a Java interface with
`@HttpExchange` methods. You can pass such an interface to `HttpServiceProxyFactory`
to create a proxy which performs requests through an HTTP client such as `RestClient`
or `WebClient`. You can also implement the interface from an `@Controller` for server
request handling.
One, declare an interface with `@HttpExchange` methods:
Start by creating the interface with `@HttpExchange` methods:
[source,java,indent=0,subs="verbatim,quotes"]
----
@@ -375,12 +581,38 @@ One, declare an interface with `@HttpExchange` methods:
}
----
Two, create a proxy that will perform the declared HTTP exchanges:
Now you can create a proxy that performs requests when methods are called.
For `RestClient`:
[source,java,indent=0,subs="verbatim,quotes"]
----
RestClient restClient = RestClient.builder().baseUrl("https://api.github.com/").build();
RestClientAdapter adapter = RestClientAdapter.create(restClient);
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build();
RepositoryService service = factory.createClient(RepositoryService.class);
----
For `WebClient`:
[source,java,indent=0,subs="verbatim,quotes"]
----
WebClient client = WebClient.builder().baseUrl("https://api.github.com/").build();
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(WebClientAdapter.forClient(client)).build();
WebClientAdapter adapter = WebClientAdapter.forClient(webClient)
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build();
RepositoryService service = factory.createClient(RepositoryService.class);
----
For `RestTemplate`:
[source,java,indent=0,subs="verbatim,quotes"]
----
RestTemplate restTemplate = new RestTemplate();
restTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory("https://api.github.com/"));
RestTemplateAdapter adapter = RestTemplateAdapter.create(restTemplate);
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build();
RepositoryService service = factory.createClient(RepositoryService.class);
----
@@ -448,6 +680,10 @@ method parameters:
Object (entity to be encoded, e.g. as JSON), `HttpEntity` (part content and headers),
a Spring `Part`, or Reactive Streams `Publisher` of any of the above.
| `MultipartFile`
| Add a request part from a `MultipartFile`, typically used in a Spring MVC controller
where it represents an uploaded file.
| `@CookieValue`
| Add a cookie or multiple cookies. The argument may be a `Map<String, ?>` or
`MultiValueMap<String, ?>` with multiple cookies, a `Collection<?>` of values, or an
@@ -459,43 +695,74 @@ method parameters:
[[rest-http-interface-return-values]]
=== Return Values
Annotated, HTTP exchange methods support the following return values:
The supported return values depend on the underlying client.
Clients adapted to `HttpExchangeAdapter` such as `RestClient` and `RestTemplate`
support synchronous return values:
[cols="1,2", options="header"]
|===
| Method return value | Description
| `void`, `Mono<Void>`
| Perform the given request, and release the response content, if any.
| `void`
| Perform the given request.
| `HttpHeaders`, `Mono<HttpHeaders>`
| Perform the given request, release the response content, if any, and return the
response headers.
| `HttpHeaders`
| Perform the given request and return the response headers.
| `<T>`, `Mono<T>`
| `<T>`
| Perform the given request and decode the response content to the declared return type.
| `<T>`, `Flux<T>`
| Perform the given request and decode the response content to a stream of the declared
element type.
| `ResponseEntity<Void>`
| Perform the given request and return a `ResponseEntity` with the status and headers.
| `ResponseEntity<Void>`, `Mono<ResponseEntity<Void>>`
| Perform the given request, and release the response content, if any, and return a
`ResponseEntity` with the status and headers.
| `ResponseEntity<T>`, `Mono<ResponseEntity<T>>`
| `ResponseEntity<T>`
| Perform the given request, decode the response content to the declared return type, and
return a `ResponseEntity` with the status, headers, and the decoded body.
|===
Clients adapted to `ReactorHttpExchangeAdapter` such as `WebClient`, support all of above
as well as reactive variants. The table below shows Reactor types, but you can also use
other reactive types that are supported through the `ReactiveAdapterRegistry`:
[cols="1,2", options="header"]
|===
| Method return value | Description
| `Mono<Void>`
| Perform the given request, and release the response content, if any.
| `Mono<HttpHeaders>`
| Perform the given request, release the response content, if any, and return the
response headers.
| `Mono<T>`
| Perform the given request and decode the response content to the declared return type.
| `Flux<T>`
| Perform the given request and decode the response content to a stream of the declared
element type.
| `Mono<ResponseEntity<Void>>`
| Perform the given request, and release the response content, if any, and return a
`ResponseEntity` with the status and headers.
| `Mono<ResponseEntity<T>>`
| Perform the given request, decode the response content to the declared return type, and
return a `ResponseEntity` with the status, headers, and the decoded body.
| `Mono<ResponseEntity<Flux<T>>`
| Perform the given request, decode the response content to a stream of the declared
element type, and return a `ResponseEntity` with the status, headers, and the decoded
response body stream.
element type, and return a `ResponseEntity` with the status, headers, and the decoded
response body stream.
|===
TIP: You can also use any other async or reactive types registered in the
`ReactiveAdapterRegistry`.
By default, the timeout for synchronous return values with `ReactorHttpExchangeAdapter`
depends on how the underlying HTTP client is configured. You can set a `blockTimeout`
value on the adapter level as well, but we recommend relying on timeout settings of the
underlying HTTP client, which operates at a lower level and provides more control.
[[rest-http-interface-exceptions]]
@@ -66,6 +66,11 @@ The variants that Spring provides are as follows:
compatible runtime environment (such as a Jakarta EE application server),
replacing a CommonJ WorkManager for that purpose.
As of 6.1, `ThreadPoolTaskExecutor` provides a pause/resume capability and graceful
shutdown through Spring's lifecycle management. There is also a new "virtualThreads"
option on `SimpleAsyncTaskExecutor` which is aligned with JDK 21's Virtual Threads,
as well as a graceful shutdown capability for `SimpleAsyncTaskExecutor` as well.
[[scheduling-task-executor-usage]]
=== Using a `TaskExecutor`
@@ -244,6 +249,11 @@ to provide common bean-style configuration along the lines of `ThreadPoolTaskExe
These variants work perfectly fine for locally embedded thread pool setups in lenient
application server environments, as well -- in particular on Tomcat and Jetty.
As of 6.1, `ThreadPoolTaskScheduler` provides a pause/resume capability and graceful
shutdown through Spring's lifecycle management. There is also a new option called
`SimpleAsyncTaskScheduler` which is aligned with JDK 21's Virtual Threads, using a
single scheduler thread but firing up a new thread for every scheduled task execution.
[[scheduling-annotation-support]]
@@ -353,6 +363,17 @@ the amount of time to wait before the first execution of the method, as the foll
}
----
For one-time tasks, you can just specify an initial delay by indicating the amount
of time to wait before the intended execution of the method:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Scheduled(initialDelay = 1000)
public void doSomething() {
// something that should run only once
}
----
If simple periodic scheduling is not expressive enough, you can provide a
xref:integration/scheduling.adoc#scheduling-cron-expression[cron expression].
The following example runs only on weekdays:
@@ -392,6 +413,120 @@ container and once through the `@Configurable` aspect), with the consequence of
`@Scheduled` method being invoked twice.
====
[[scheduling-annotation-support-scheduled-reactive]]
=== The `@Scheduled` annotation on Reactive methods or Kotlin suspending functions
As of Spring Framework 6.1, `@Scheduled` methods are also supported on several types
of reactive methods:
- methods with a `Publisher` return type (or any concrete implementation of `Publisher`)
like in the following example:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Scheduled(fixedDelay = 500)
public Publisher<Void> reactiveSomething() {
// return an instance of Publisher
}
----
- methods with a return type that can be adapted to `Publisher` via the shared instance
of the `ReactiveAdapterRegistry`, provided the type supports _deferred subscription_ like
in the following example:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Scheduled(fixedDelay = 500)
public Single<String> rxjavaNonPublisher() {
return Single.just("example");
}
----
[NOTE]
====
The `CompletableFuture` class is an example of a type that can typically be adapted
to `Publisher` but doesn't support deferred subscription. Its `ReactiveAdapter` in the
registry denotes that by having the `getDescriptor().isDeferred()` method return `false`.
====
- Kotlin suspending functions, like in the following example:
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@Scheduled(fixedDelay = 500)
suspend fun something() {
// do something asynchronous
}
----
- methods that return a Kotlin `Flow` or `Deferred` instance, like in the following example:
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@Scheduled(fixedDelay = 500)
fun something(): Flow<Void> {
flow {
// do something asynchronous
}
}
----
All these types of methods must be declared without any arguments. In the case of Kotlin
suspending functions, the `kotlinx.coroutines.reactor` bridge must also be present to allow
the framework to invoke a suspending function as a `Publisher`.
The Spring Framework will obtain a `Publisher` for the annotated method once and will
schedule a `Runnable` in which it subscribes to said `Publisher`. These inner regular
subscriptions occur according to the corresponding `cron`/fixedDelay`/`fixedRate` configuration.
If the `Publisher` emits `onNext` signal(s), these are ignored and discarded (the same way
return values from synchronous `@Scheduled` methods are ignored).
In the following example, the `Flux` emits `onNext("Hello"), onNext("World")` every 5
seconds, but these values are unused:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Scheduled(initialDelay = 5000, fixedRate = 5000)
public Flux<String> reactiveSomething() {
return Flux.just("Hello", "World");
}
----
If the `Publisher` emits an `onError` signal, it is logged at `WARN` level and recovered.
Because of the asynchronous and lazy nature of `Publisher` instances, exceptions are
not thrown from the `Runnable` task: this means that the `ErrorHandler` contract is not
involved for reactive methods.
As a result, further scheduled subscription occurs despite the error.
In the following example, the `Mono` subscription fails twice in the first five seconds.
Then subscriptions start succeeding, printing a message to the standard output every five
seconds:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Scheduled(initialDelay = 0, fixedRate = 5000)
public Mono<Void> reactiveSomething() {
AtomicInteger countdown = new AtomicInteger(2);
return Mono.defer(() -> {
if (countDown.get() == 0 || countDown.decrementAndGet() == 0) {
return Mono.fromRunnable(() -> System.out.println("Message"));
}
return Mono.error(new IllegalStateException("Cannot deliver message"));
})
}
----
[NOTE]
====
When destroying the annotated bean or closing the application context, Spring Framework cancels
scheduled tasks, which includes the next scheduled subscription to the `Publisher` as well
as any past subscription that is still currently active (e.g. for long-running publishers
or even infinite publishers).
====
[[scheduling-annotation-support-async]]
=== The `@Async` annotation
@@ -251,11 +251,14 @@ https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-featu
=== Constructor injection
As described in the xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit-jupiter-di[dedicated section],
JUnit 5 allows constructor injection of beans which is pretty useful with Kotlin
JUnit Jupiter (JUnit 5) allows constructor injection of beans which is pretty useful with Kotlin
in order to use `val` instead of `lateinit var`. You can use
{api-spring-framework}/test/context/TestConstructor.html[`@TestConstructor(autowireMode = AutowireMode.ALL)`]
to enable autowiring for all parameters.
NOTE: You can also change the default behavior to `ALL` in a `junit-platform.properties`
file with a `spring.test.constructor.autowire.mode = all` property.
[source,kotlin,indent=0]
----
@SpringJUnitConfig(TestConfig::class)
@@ -272,11 +275,11 @@ class OrderServiceIntegrationTests(val orderService: OrderService,
=== `PER_CLASS` Lifecycle
Kotlin lets you specify meaningful test function names between backticks (```).
As of JUnit 5, Kotlin test classes can use the `@TestInstance(TestInstance.Lifecycle.PER_CLASS)`
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.
You can also change the default behavior to `PER_CLASS` thanks to a `junit-platform.properties`
NOTE: You can also change the default behavior to `PER_CLASS` in a `junit-platform.properties`
file with a `junit.jupiter.testinstance.lifecycle.default = per_class` property.
The following example demonstrates `@BeforeAll` and `@AfterAll` annotations on non-static methods:
+87 -10
View File
@@ -137,10 +137,12 @@ demonstrate its API and protocol features.
The `spring-messaging` module contains the following:
* xref:rsocket.adoc#rsocket-requester[RSocketRequester] -- fluent API to make requests through an `io.rsocket.RSocket`
with data and metadata encoding/decoding.
* xref:rsocket.adoc#rsocket-annot-responders[Annotated Responders] -- `@MessageMapping` annotated handler methods for
responding.
* xref:rsocket.adoc#rsocket-requester[RSocketRequester] -- fluent API to make requests
through an `io.rsocket.RSocket` with data and metadata encoding/decoding.
* xref:rsocket.adoc#rsocket-annot-responders[Annotated Responders] -- `@MessageMapping`
and `@RSocketExchange` annotated handler methods for responding.
* xref:rsocket.adoc#rsocket-interface[RSocket Interface] -- RSocket service declaration
as Java interface with `@RSocketExchange` methods, for use as requester or responder.
The `spring-web` module contains `Encoder` and `Decoder` implementations such as Jackson
CBOR/JSON, and Protobuf that RSocket applications will likely need. It also contains the
@@ -863,6 +865,69 @@ interaction type(s):
[[rsocket-annot-rsocketexchange]]
=== @RSocketExchange
As an alternative to `@MessageMapping`, you can also handle requests with
`@RSocketExchange` methods. Such methods are declared on an
xref:rsocket-interface[RSocket Interface] and can be used as a requester via
`RSocketServiceProxyFactory` or implemented by a responder.
For example, to handle requests as a responder:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public interface RadarsService {
@RSocketExchange("locate.radars.within")
Flux<AirportLocation> radars(MapRequest request);
}
@Controller
public class RadarsController implements RadarsService {
public Flux<AirportLocation> radars(MapRequest request) {
// ...
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
interface RadarsService {
@RSocketExchange("locate.radars.within")
fun radars(request: MapRequest): Flow<AirportLocation>
}
@Controller
class RadarsController : RadarsService {
override fun radars(request: MapRequest): Flow<AirportLocation> {
// ...
}
}
----
======
There some differences between `@RSocketExhange` and `@MessageMapping` since the
former needs to remain suitable for requester and responder use. For example, while
`@MessageMapping` can be declared to handle any number of routes and each route can
be a pattern, `@RSocketExchange` must be declared with a single, concrete route. There are
also small differences in the supported method parameters related to metadata, see
xref:rsocket-annot-messagemapping[@MessageMapping] and
xref:rsocket-interface[RSocket Interface] for a list of supported parameters.
`@RSocketExchange` can be used at the type level to specify a common prefix for all routes
for a given RSocket service interface.
[[rsocket-annot-connectmapping]]
=== @ConnectMapping
@@ -997,12 +1062,13 @@ Kotlin::
[[rsocket-interface]]
== RSocket Interface
The Spring Framework lets you define an RSocket service as a Java interface with annotated
methods for RSocket exchanges. You can then generate a proxy that implements this interface
and performs the exchanges. This helps to simplify RSocket remote access by wrapping the
use of the underlying xref:rsocket.adoc#rsocket-requester[RSocketRequester].
The Spring Framework lets you define an RSocket service as a Java interface with
`@RSocketExchange` methods. You can pass such an interface to `RSocketServiceProxyFactory`
to create a proxy which performs requests through an
xref:rsocket.adoc#rsocket-requester[RSocketRequester]. You can also implement the
interface as a responder that handles requests.
One, declare an interface with `@RSocketExchange` methods:
Start by creating the interface with `@RSocketExchange` methods:
[source,java,indent=0,subs="verbatim,quotes"]
----
@@ -1016,7 +1082,7 @@ One, declare an interface with `@RSocketExchange` methods:
}
----
Two, create a proxy that will perform the declared RSocket exchanges:
Now you can create a proxy that performs requests when methods are called:
[source,java,indent=0,subs="verbatim,quotes"]
----
@@ -1026,6 +1092,10 @@ Two, create a proxy that will perform the declared RSocket exchanges:
RadarService service = factory.createClient(RadarService.class);
----
You can also implement the interface to handle requests as a responder.
See xref:rsocket.adoc#rsocket-annot-rsocketexchange[Annotated Responders].
[[rsocket-interface-method-parameters]]
=== Method Parameters
@@ -1066,3 +1136,10 @@ method parameters:
Annotated, RSocket exchange methods support return values that are concrete value(s), or
any producer of value(s) that can be adapted to a Reactive Streams `Publisher` via
`ReactiveAdapterRegistry`.
By default, the behavior of RSocket service methods with synchronous (blocking) method
signature depends on response timeout settings of the underlying RSocket `ClientTransport`
as well as RSocket keep-alive settings. `RSocketServiceProxyFactory.Builder` does expose a
`blockTimeout` option that also lets you configure the maximum time to block for a response,
but we recommend configuring timeout values at the RSocket level for more control.
@@ -170,8 +170,9 @@ of a test class constructor are autowired from components in the test's
If `@TestConstructor` is not present or meta-present on a test class, the default _test
constructor autowire mode_ will be used. See the tip below for details on how to change
the default mode. Note, however, that a local declaration of `@Autowired` on a
constructor takes precedence over both `@TestConstructor` and the default mode.
the default mode. Note, however, that a local declaration of `@Autowired`,
`@jakarta.inject.Inject`, or `@javax.inject.Inject` on a constructor takes precedence
over both `@TestConstructor` and the default mode.
.Changing the default test constructor autowire mode
[TIP]
@@ -222,6 +223,7 @@ following annotations.
* xref:testing/annotations/integration-spring/annotation-contextconfiguration.adoc[`@ContextConfiguration`]
* xref:testing/annotations/integration-spring/annotation-webappconfiguration.adoc[`@WebAppConfiguration`]
* xref:testing/annotations/integration-spring/annotation-contexthierarchy.adoc[`@ContextHierarchy`]
* xref:testing/annotations/integration-spring/annotation-contextcustomizerfactories.adoc[`@ContextCustomizerFactories`]
* xref:testing/annotations/integration-spring/annotation-activeprofiles.adoc[`@ActiveProfiles`]
* xref:testing/annotations/integration-spring/annotation-testpropertysource.adoc[`@TestPropertySource`]
* xref:testing/annotations/integration-spring/annotation-dynamicpropertysource.adoc[`@DynamicPropertySource`]
@@ -11,6 +11,7 @@ xref:testing/testcontext-framework.adoc[TestContext framework].
* `@BootstrapWith`
* `@ContextConfiguration`
* `@ContextHierarchy`
* `@ContextCustomizerFactories`
* `@ActiveProfiles`
* `@TestPropertySource`
* `@DirtiesContext`
@@ -12,6 +12,7 @@ Spring's testing annotations include the following:
* xref:testing/annotations/integration-spring/annotation-contextconfiguration.adoc[`@ContextConfiguration`]
* xref:testing/annotations/integration-spring/annotation-webappconfiguration.adoc[`@WebAppConfiguration`]
* xref:testing/annotations/integration-spring/annotation-contexthierarchy.adoc[`@ContextHierarchy`]
* xref:testing/annotations/integration-spring/annotation-contextcustomizerfactories.adoc[`@ContextCustomizerFactories`]
* xref:testing/annotations/integration-spring/annotation-activeprofiles.adoc[`@ActiveProfiles`]
* xref:testing/annotations/integration-spring/annotation-testpropertysource.adoc[`@TestPropertySource`]
* xref:testing/annotations/integration-spring/annotation-dynamicpropertysource.adoc[`@DynamicPropertySource`]
@@ -0,0 +1,45 @@
[[spring-testing-annotation-contextcustomizerfactories]]
= `@ContextCustomizerFactories`
`@ContextCustomizerFactories` is used to register `ContextCustomizerFactory`
implementations for a particular test class, its subclasses, and its nested classes. If
you wish to register a factory globally, you should register it via the automatic
discovery mechanism described in
xref:testing/testcontext-framework/ctx-management/context-customizers.adoc[`ContextCustomizerFactory` Configuration].
The following example shows how to register two `ContextCustomizerFactory` implementations:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@ContextConfiguration
@ContextCustomizerFactories({CustomContextCustomizerFactory.class, AnotherContextCustomizerFactory.class}) // <1>
class CustomContextCustomizerFactoryTests {
// class body...
}
----
<1> Register two `ContextCustomizerFactory` implementations.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@ContextConfiguration
@ContextCustomizerFactories([CustomContextCustomizerFactory::class, AnotherContextCustomizerFactory::class]) // <1>
class CustomContextCustomizerFactoryTests {
// class body...
}
----
<1> Register two `ContextCustomizerFactory` implementations.
======
By default, `@ContextCustomizerFactories` provides support for inheriting factories from
superclasses or enclosing classes. See
xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit-jupiter-nested-test-configuration[`@Nested` test class configuration] and the
{api-spring-framework}/test/context/ContextCustomizerFactories.html[`@ContextCustomizerFactories`
javadoc] for an example and further details.
@@ -19,7 +19,22 @@ following features.
use an AOT-optimized `ApplicationContext` that participates transparently with the
xref:testing/testcontext-framework/ctx-management/caching.adoc[context cache].
[WARNING]
[TIP]
====
By default, if an error is encountered during build-time AOT processing, an exception
will be thrown, and the overall process will fail immediately.
If you would prefer that build-time AOT processing continue after errors are encountered,
you can disable the `failOnError` mode which results in errors being logged at `WARN`
level or with greater detail at `DEBUG` level.
The `failOnError` mode can be disabled from the command line or a build script by setting
a JVM system property named `spring.test.aot.processing.failOnError` to `false`. As an
alternative, you can set the same property via the
xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism.
====
[NOTE]
====
The `@ContextHierarchy` annotation is currently not supported in AOT mode.
====
@@ -112,6 +112,7 @@ advanced use cases.
* 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/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]
* xref:testing/testcontext-framework/ctx-management/env-profiles.adoc[Context Configuration with Environment Profiles]
@@ -119,5 +120,6 @@ advanced use cases.
* xref:testing/testcontext-framework/ctx-management/dynamic-property-sources.adoc[Context Configuration with Dynamic Property Sources]
* xref:testing/testcontext-framework/ctx-management/web.adoc[Loading a `WebApplicationContext`]
* xref:testing/testcontext-framework/ctx-management/caching.adoc[Context Caching]
* xref:testing/testcontext-framework/ctx-management/failure-threshold.adoc[Context Failure Threshold]
* xref:testing/testcontext-framework/ctx-management/hierarchies.adoc[Context Hierarchies]
@@ -20,7 +20,7 @@ framework uses the following configuration parameters to build the context cache
* `contextLoader` (from `@ContextConfiguration`)
* `parent` (from `@ContextHierarchy`)
* `activeProfiles` (from `@ActiveProfiles`)
* `propertySourceLocations` (from `@TestPropertySource`)
* `propertySourceDescriptors` (from `@TestPropertySource`)
* `propertySourceProperties` (from `@TestPropertySource`)
* `resourceBasePath` (from `@WebAppConfiguration`)
@@ -0,0 +1,69 @@
[[testcontext-context-customizers]]
= Configuration Configuration with Context Customizers
A `ContextCustomizer` is responsible for customizing the supplied
`ConfigurableApplicationContext` after bean definitions have been loaded into the context
but before the context has been refreshed.
A `ContextCustomizerFactory` is responsible for creating a `ContextCustomizer`, based on
some custom logic which determines if the `ContextCustomizer` is necessary for a given
test class -- for example, based on the presence of a certain annotation. Factories are
invoked after `ContextLoaders` have processed context configuration attributes for a test
class but before the `MergedContextConfiguration` is created.
For example, Spring Framework provides the following `ContextCustomizerFactory`
implementation which is registered by default:
`MockServerContainerContextCustomizerFactory`:: Creates a
`MockServerContainerContextCustomizer` if WebSocket support is present in the classpath
and the test class or one of its enclosing classes is annotated or meta-annotated with
`@WebAppConfiguration`. `MockServerContainerContextCustomizer` instantiates a new
`MockServerContainer` and stores it in the `ServletContext` under the attribute named
`jakarta.websocket.server.ServerContainer`.
[[testcontext-context-customizers-registration]]
== Registering `ContextCustomizerFactory` Implementations
You can register `ContextCustomizerFactory` implementations explicitly for a test class, its
subclasses, and its nested classes by using the `@ContextCustomizerFactories` annotation. See
xref:testing/annotations/integration-spring/annotation-contextcustomizerfactories.adoc[annotation support]
and the javadoc for
{api-spring-framework}/test/context/ContextCustomizerFactories.html[`@ContextCustomizerFactories`]
for details and examples.
[[testcontext-context-customizers-automatic-discovery]]
== Automatic Discovery of Default `ContextCustomizerFactory` Implementations
Registering `ContextCustomizerFactory` implementations by using `@ContextCustomizerFactories` is
suitable for custom factories that are used in limited testing scenarios. However, it can
become cumbersome if a custom factory needs to be used across an entire test suite. This
issue is addressed through support for automatic discovery of default
`ContextCustomizerFactory` implementations through the `SpringFactoriesLoader` mechanism.
Specifically, the modules that make up the testing support in Spring Framework and Spring
Boot declare all core default `ContextCustomizerFactory` implementations under the
`org.springframework.test.context.ContextCustomizerFactory` key in their
`META-INF/spring.factories` properties files. Third-party frameworks and developers can
contribute their own `ContextCustomizerFactory` implementations to the list of default
factories in the same manner through their own `META-INF/spring.factories` properties
files.
[[testcontext-context-customizers-merging]]
== Merging `ContextCustomizerFactory` Implementations
If a custom `ContextCustomizerFactory` is registered via `@ContextCustomizerFactories`, it
will be _merged_ with the default factories that have been registered using the aforementioned
xref:testing/testcontext-framework/ctx-management/context-customizers.adoc#testcontext-context-customizers-automatic-discovery[automatic discovery mechanism].
The merging algorithm ensures that duplicates are removed from the list and that locally
declared factories are appended to the list of default factories when merged.
[TIP]
====
To replace the default factories for a test class, its subclasses, and its nested
classes, you can set the `mergeMode` attribute of `@ContextCustomizerFactories` to
`MergeMode.REPLACE_DEFAULTS`.
====
@@ -0,0 +1,23 @@
[[testcontext-ctx-management-failure-threshold]]
= Context Failure Threshold
As of Spring Framework 6.1, a context _failure threshold_ policy is in place which helps
avoid repeated attempts to load a failing `ApplicationContext`. By default, the failure
threshold is set to `1` which means that only one attempt will be made to load an
`ApplicationContext` for a given context cache key (see
xref:testing/testcontext-framework/ctx-management/caching.adoc[Context Caching]). Any
subsequent attempt to load the `ApplicationContext` for the same context cache key will
result in an immediate `IllegalStateException` with an error message which explains that
the attempt was preemptively skipped. This behavior allows individual test classes and
test suites to fail faster by avoiding repeated attempts to load an `ApplicationContext`
that will never successfully load -- for example, due to a configuration error or a missing
external resource that prevents the context from loading in the current environment.
You can configure the context failure threshold from the command line or a build script
by setting a JVM system property named `spring.test.context.failure.threshold` with a
positive integer value. As an alternative, you can set the same property via the
xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism.
NOTE: If you wish to effectively disable the context failure threshold, you can set the
property to a very large value. For example, from the command line you could set the
system property via `-Dspring.test.context.failure.threshold=1000000`.
@@ -16,7 +16,7 @@ SPI, but `@TestPropertySource` is not supported with implementations of the olde
`ContextLoader` SPI.
Implementations of `SmartContextLoader` gain access to merged test property source values
through the `getPropertySourceLocations()` and `getPropertySourceProperties()` methods in
through the `getPropertySourceDescriptors()` and `getPropertySourceProperties()` methods in
`MergedContextConfiguration`.
====
@@ -26,17 +26,23 @@ through the `getPropertySourceLocations()` and `getPropertySourceProperties()` m
You can configure test properties files by using the `locations` or `value` attribute of
`@TestPropertySource`.
Both traditional and XML-based properties file formats are supported -- for example,
`"classpath:/com/example/test.properties"` or `"file:///path/to/file.xml"`.
By default, both traditional and XML-based `java.util.Properties` file formats are
supported -- for example, `"classpath:/com/example/test.properties"` or
`"file:///path/to/file.xml"`. As of Spring Framework 6.1, you can configure a custom
`PropertySourceFactory` via the `factory` attribute in `@TestPropertySource` in order to
support a different file format such as JSON, YAML, etc.
Each path is interpreted as a Spring `Resource`. A plain path (for example,
`"test.properties"`) is treated as a classpath resource that is relative to the package
in which the test class is defined. A path starting with a slash is treated as an
absolute classpath resource (for example: `"/org/example/test.xml"`). A path that
references a URL (for example, a path prefixed with `classpath:`, `file:`, or `http:`) is
loaded by using the specified resource protocol. Resource location wildcards (such as
`{asterisk}{asterisk}/{asterisk}.properties`) are not permitted: Each location must
evaluate to exactly one `.properties` or `.xml` resource.
loaded by using the specified resource protocol.
Property placeholders in paths (such as `${...}`) will be resolved against the `Environment`.
As of Spring Framework 6.1, resource location patterns are also supported — for
example, `"classpath*:/config/*.properties"`.
The following example uses a test properties file:
@@ -80,6 +86,20 @@ a Java properties file:
* `key:value`
* `key value`
[TIP]
====
Although properties can be defined using any of the above syntax variants and any number
of spaces between the key and the value, it is recommended that you use one syntax
variant and consistent spacing within your test suite — for example, consider always
using `key = value` instead of `key= value`, `key=value`, etc. Similarly, if you define
inlined properties using text blocks you should consistently use text blocks for inlined
properties throughout your test suite.
The reason is that the exact strings you provide will be used to determine the key for
the context cache. Consequently, to benefit from the context cache you must ensure that
you define inlined properties consistently.
====
The following example sets two inlined properties:
[tabs]
@@ -89,24 +109,61 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@ContextConfiguration
@TestPropertySource(properties = {"timezone = GMT", "port: 4242"}) // <1>
@TestPropertySource(properties = {"timezone = GMT", "port = 4242"}) // <1>
class MyIntegrationTests {
// class body...
}
----
<1> Setting two properties by using two variations of the key-value syntax.
<1> Setting two properties via an array of strings.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@ContextConfiguration
@TestPropertySource(properties = ["timezone = GMT", "port: 4242"]) // <1>
@TestPropertySource(properties = ["timezone = GMT", "port = 4242"]) // <1>
class MyIntegrationTests {
// class body...
}
----
<1> Setting two properties by using two variations of the key-value syntax.
<1> Setting two properties via an array of strings.
======
As of Spring Framework 6.1, you can use _text blocks_ to define multiple inlined
properties in a single `String`. The following example sets two inlined properties using
a text block:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@ContextConfiguration
@TestPropertySource(properties = """
timezone = GMT
port = 4242
""") // <1>
class MyIntegrationTests {
// class body...
}
----
<1> Setting two properties via a text block.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@ContextConfiguration
@TestPropertySource(properties = ["""
timezone = GMT
port = 4242
"""]) // <1>
class MyIntegrationTests {
// class body...
}
----
<1> Setting two properties via a text block.
======
[NOTE]
@@ -166,7 +223,7 @@ Java::
@ContextConfiguration
@TestPropertySource(
locations = "/test.properties",
properties = {"timezone = GMT", "port: 4242"}
properties = {"timezone = GMT", "port = 4242"}
)
class MyIntegrationTests {
// class body...
@@ -179,7 +236,7 @@ Kotlin::
----
@ContextConfiguration
@TestPropertySource("/test.properties",
properties = ["timezone = GMT", "port: 4242"]
properties = ["timezone = GMT", "port = 4242"]
)
class MyIntegrationTests {
// class body...
@@ -177,7 +177,7 @@ following features above and beyond the feature set that Spring supports for JUn
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 `SpringExtension`] for further details.
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
test execution] based on SpEL expressions, environment variables, system properties,
and so on. See the documentation for `@EnabledIf` and `@DisabledIf` in
@@ -310,17 +310,19 @@ See the documentation for `@SpringJUnitConfig` and `@SpringJUnitWebConfig` in
xref:testing/annotations/integration-junit-jupiter.adoc[Spring JUnit Jupiter Testing Annotations] for further details.
[[testcontext-junit-jupiter-di]]
=== Dependency Injection with `SpringExtension`
=== Dependency Injection with the `SpringExtension`
`SpringExtension` implements the
The `SpringExtension` implements the
link:https://junit.org/junit5/docs/current/user-guide/#extensions-parameter-resolution[`ParameterResolver`]
extension API from JUnit Jupiter, which lets Spring provide dependency injection for test
constructors, test methods, and test lifecycle callback methods.
Specifically, `SpringExtension` can inject dependencies from the test's
Specifically, the `SpringExtension` can inject dependencies from the test's
`ApplicationContext` into test constructors and methods that are annotated with
`@BeforeAll`, `@AfterAll`, `@BeforeEach`, `@AfterEach`, `@Test`, `@RepeatedTest`,
`@ParameterizedTest`, and others.
Spring's `@BeforeTransaction` and `@AfterTransaction` or JUnit's `@BeforeAll`,
`@AfterAll`, `@BeforeEach`, `@AfterEach`, `@Test`, `@RepeatedTest`, `@ParameterizedTest`,
and others.
[[testcontext-junit-jupiter-di-constructor]]
==== Constructor Injection
@@ -534,7 +536,7 @@ The _Spring TestContext Framework_ has supported the use of test-related annotat
Framework 5.3 class-level test configuration annotations were not _inherited_ from
enclosing classes like they are from superclasses.
Spring Framework 5.3 introduces first-class support for inheriting test class
Spring Framework 5.3 introduced first-class support for inheriting test class
configuration from enclosing classes, and such configuration will be inherited by
default. To change from the default `INHERIT` mode to `OVERRIDE` mode, you may annotate
an individual `@Nested` test class with
@@ -295,14 +295,56 @@ behavior after your test runs (if the test was configured to commit the transact
`TransactionalTestExecutionListener` supports the `@BeforeTransaction` and
`@AfterTransaction` annotations for exactly such scenarios. You can annotate any `void`
method in a test class or any `void` default method in a test interface with one of these
annotations, and the `TransactionalTestExecutionListener` ensures that your before
transaction method or after transaction method runs at the appropriate time.
annotations, and the `TransactionalTestExecutionListener` ensures that your
before-transaction method or after-transaction method runs at the appropriate time.
TIP: Any before methods (such as methods annotated with JUnit Jupiter's `@BeforeEach`)
and any after methods (such as methods annotated with JUnit Jupiter's `@AfterEach`) are
run within a transaction. In addition, methods annotated with `@BeforeTransaction` or
`@AfterTransaction` are not run for test methods that are not configured to run within a
transaction.
[NOTE]
====
Generally speaking, `@BeforeTransaction` and `@AfterTransaction` methods must not accept
any arguments.
However, as of Spring Framework 6.1, for tests using the
xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit-jupiter-extension[`SpringExtension`]
with JUnit Jupiter, `@BeforeTransaction` and `@AfterTransaction` methods may optionally
accept arguments which will be resolved by any registered JUnit `ParameterResolver`
extension such as the `SpringExtension`. This means that JUnit-specific arguments like
`TestInfo` or beans from the test's `ApplicationContext` may be provided to
`@BeforeTransaction` and `@AfterTransaction` methods, as demonstrated in the following
example.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@BeforeTransaction
void verifyInitialDatabaseState(@Autowired DataSource dataSource) {
// Use the DataSource to verify the initial state before a transaction is started
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@BeforeTransaction
fun verifyInitialDatabaseState(@Autowired dataSource: DataSource) {
// Use the DataSource to verify the initial state before a transaction is started
}
----
======
====
[TIP]
====
Any before methods (such as methods annotated with JUnit Jupiter's `@BeforeEach`) and any
after methods (such as methods annotated with JUnit Jupiter's `@AfterEach`) are run
within the test-managed transaction for a transactional test method.
Similarly, methods annotated with `@BeforeTransaction` or `@AfterTransaction` are only
run for transactional test methods.
====
[[testcontext-tx-mgr-config]]
== Configuring a Transaction Manager
@@ -148,5 +148,75 @@ Kotlin::
----
======
The example below demonstrates how to use the `ExchangeFilterFunction` interface to create
a custom filter class that helps with computing a `Content-Length` header for `PUT` and `POST`
`multipart/form-data` requests using buffering.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class MultipartExchangeFilterFunction implements ExchangeFilterFunction {
@Override
public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
if (MediaType.MULTIPART_FORM_DATA.includes(request.headers().getContentType())
&& (request.method() == HttpMethod.PUT || request.method() == HttpMethod.POST)) {
return next.exchange(ClientRequest.from(request).body((outputMessage, context) ->
request.body().insert(new BufferingDecorator(outputMessage), context)).build()
);
} else {
return next.exchange(request);
}
}
private static final class BufferingDecorator extends ClientHttpRequestDecorator {
private BufferingDecorator(ClientHttpRequest delegate) {
super(delegate);
}
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
return DataBufferUtils.join(body).flatMap(buffer -> {
getHeaders().setContentLength(buffer.readableByteCount());
return super.writeWith(Mono.just(buffer));
});
}
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class MultipartExchangeFilterFunction : ExchangeFilterFunction {
override fun filter(request: ClientRequest, next: ExchangeFunction): Mono<ClientResponse> {
return if (MediaType.MULTIPART_FORM_DATA.includes(request.headers().getContentType())
&& (request.method() == HttpMethod.PUT || request.method() == HttpMethod.POST)) {
next.exchange(ClientRequest.from(request)
.body { message, context -> request.body().insert(BufferingDecorator(message), context) }
.build())
}
else {
next.exchange(request)
}
}
private class BufferingDecorator(delegate: ClientHttpRequest) : ClientHttpRequestDecorator(delegate) {
override fun writeWith(body: Publisher<out DataBuffer>): Mono<Void> {
return DataBufferUtils.join(body)
.flatMap {
headers.contentLength = it.readableByteCount().toLong()
super.writeWith(Mono.just(it))
}
}
}
}
----
======
@@ -67,52 +67,44 @@ from an existing `ProblemDetail`. This could be done centrally, e.g. from an
[[webflux-ann-rest-exceptions-i18n]]
== Internationalization
== Customization and i18n
[.small]#xref:web/webmvc/mvc-ann-rest-exceptions.adoc#mvc-ann-rest-exceptions-i18n[See equivalent in the Servlet stack]#
It is a common requirement to internationalize error response details, and good practice
to customize the problem details for Spring WebFlux exceptions. This is supported as follows:
It is a common requirement to customize and internationalize error response details.
It is also good practice to customize the problem details for Spring WebFlux exceptions
to avoid revealing implementation details. This section describes the support for that.
- Each `ErrorResponse` exposes a message code and arguments to resolve the "detail" field
through a xref:core/beans/context-introduction.adoc#context-functionality-messagesource[MessageSource].
The actual message code value is parameterized with placeholders, e.g.
`+"HTTP method {0} not supported"+` to be expanded from the arguments.
- Each `ErrorResponse` also exposes a message code to resolve the "title" field.
- `ResponseEntityExceptionHandler` uses the message code and arguments to resolve the
"detail" and the "title" fields.
An `ErrorResponse` exposes message codes for "type", "title", and "detail", as well as
message code arguments for the "detail" field. `ResponseEntityExceptionHandler` resolves
these through a xref:core/beans/context-introduction.adoc#context-functionality-messagesource[MessageSource]
and updates the corresponding `ProblemDetail` fields accordingly.
By default, the message code for the "detail" field is "problemDetail." + the fully
qualified exception class name. Some exceptions may expose additional message codes in
which case a suffix is added to the default message code. The table below lists message
arguments and codes for Spring WebFlux exceptions:
The default strategy for message codes follows the pattern:
`problemDetail.[type|title|detail].[fully qualified exception class name]`
An `ErrorResponse` may expose more than one message code, typically adding a suffix
to the default message code. The table below lists message codes, and arguments for
Spring WebFlux exceptions:
[[webflux-ann-rest-exceptions-codes]]
[cols="1,1,2", options="header"]
|===
| Exception | Message Code | Message Code Arguments
| `UnsupportedMediaTypeStatusException`
| `HandlerMethodValidationException`
| (default)
| `+{0}+` the media type that is not supported, `+{1}+` list of supported media types
| `+{0}+` list all validation errors.
Message codes and arguments for each error are also resolved via `MessageSource`.
| `UnsupportedMediaTypeStatusException`
| (default) + ".parseError"
|
| `MethodNotAllowedException`
| (default)
| `+{0}+` the current HTTP method, `+{1}+` the list of supported HTTP methods
| `MissingRequestValueException`
| (default)
| `+{0}+` a label for the value (e.g. "request header", "cookie value", ...), `+{1}+` the value name
| `UnsatisfiedRequestParameterException`
| (default)
| `+{0}+` the list of parameter conditions
| `WebExchangeBindException`
| (default)
| `+{0}+` the list of global errors, `+{1}+` the list of field errors.
Message codes and arguments for each error within the `BindingResult` are also resolved
via `MessageSource`.
| `NotAcceptableStatusException`
| (default)
| `+{0}+` list of supported media types
@@ -125,14 +117,32 @@ via `MessageSource`.
| (default)
| `+{0}+` the failure reason provided to the class constructor
| `MethodNotAllowedException`
| `UnsupportedMediaTypeStatusException`
| (default)
| `+{0}+` the current HTTP method, `+{1}+` the list of supported HTTP methods
| `+{0}+` the media type that is not supported, `+{1}+` list of supported media types
| `UnsupportedMediaTypeStatusException`
| (default) + ".parseError"
|
| `UnsatisfiedRequestParameterException`
| (default)
| `+{0}+` the list of parameter conditions
| `WebExchangeBindException`
| (default)
| `+{0}+` the list of global errors, `+{1}+` the list of field errors.
Message codes and arguments for each error are also resolved via `MessageSource`.
|===
By default, the message code for the "title" field is "problemDetail.title." + the fully
qualified exception class name.
NOTE: Unlike other exceptions, the message arguments for
`WebExchangeBindException` and `HandlerMethodValidationException` are based on a list of
`MessageSourceResolvable` errors that can also be customized through a
xref:core/beans/context-introduction.adoc#context-functionality-messagesource[MessageSource]
resource bundle. See
xref:core/validation/beanvalidation.adoc#validation-beanvalidation-spring-method-i18n[Customizing Validation Errors]
for more details.
@@ -700,9 +700,8 @@ Java::
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer
.setUseCaseSensitiveMatch(true)
.addPathPrefix("/api", HandlerTypePredicate.forAnnotation(RestController.class));
configurer.addPathPrefix(
"/api", HandlerTypePredicate.forAnnotation(RestController.class));
}
}
----
@@ -717,9 +716,8 @@ Kotlin::
@Override
fun configurePathMatch(configurer: PathMatchConfigurer) {
configurer
.setUseCaseSensitiveMatch(true)
.addPathPrefix("/api", HandlerTypePredicate.forAnnotation(RestController::class.java))
configurer.addPathPrefix(
"/api", HandlerTypePredicate.forAnnotation(RestController::class.java))
}
}
----
@@ -740,6 +738,59 @@ reliance on it.
[[webflux-config-blocking-execution]]
== Blocking Execution
The WebFlux Java config allows you to customize blocking execution in WebFlux.
You can have blocking controller methods called on a separate thread by providing
an `AsyncTaskExecutor` such as the
{api-spring-framework}/core/task/VirtualThreadTaskExecutor.html[`VirtualThreadTaskExecutor`]
as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
public void configureBlockingExecution(BlockingExecutionConfigurer configurer) {
AsyncTaskExecutor executor = ...
configurer.setExecutor(executor);
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
@Override
fun configureBlockingExecution(configurer: BlockingExecutionConfigurer) {
val executor = ...
configurer.setExecutor(executor)
}
}
----
======
By default, controller methods whose return type is not recognized by the configured
`ReactiveAdapterRegistry` are considered blocking, but you can set a custom controller
method predicate via `BlockingExecutionConfigurer`.
[[webflux-config-websocket-service]]
== WebSocketService
@@ -3,23 +3,21 @@
[.small]#xref:web/webmvc/mvc-controller/ann-initbinder.adoc[See equivalent in the Servlet stack]#
`@Controller` or `@ControllerAdvice` classes can have `@InitBinder` methods, to
initialize instances of `WebDataBinder`. Those, in turn, are used to:
`@Controller` or `@ControllerAdvice` classes can have `@InitBinder` methods to
initialize `WebDataBinder` instances that in turn can:
* Bind request parameters (that is, form data or query) to a model object.
* Convert `String`-based request values (such as request parameters, path variables,
headers, cookies, and others) to the target type of controller method arguments.
* Format model object values as `String` values when rendering HTML forms.
* Bind request parameters to a model object.
* Convert request values from string to object property types.
* Format model object properties as strings when rendering HTML forms.
`@InitBinder` methods can register controller-specific `java.beans.PropertyEditor` or
Spring `Converter` and `Formatter` components. In addition, you can use the
xref:web/webflux/config.adoc#webflux-config-conversion[WebFlux Java configuration] to register `Converter` and
`Formatter` types in a globally shared `FormattingConversionService`.
In an `@Controller`, `DataBinder` customizations apply locally within the controller,
or even to a specific model attribute referenced by name through the annotation.
In an `@ControllerAdvice` customizations can apply to all or a subset of controllers.
`@InitBinder` methods support many of the same arguments that `@RequestMapping` methods
do, except for `@ModelAttribute` (command object) arguments. Typically, they are declared
with a `WebDataBinder` argument, for registrations, and a `void` return value.
The following example uses the `@InitBinder` annotation:
You can register `PropertyEditor`, `Converter`, and `Formatter` components in the
`DataBinder` for type conversion. Alternatively, you can use the
xref:web/webflux/config.adoc#webflux-config-conversion[WebFlux config] to register
`Converter` and `Formatter` components in a globally shared `FormattingConversionService`.
--
[tabs]
@@ -112,4 +110,5 @@ Kotlin::
== Model Design
[.small]#xref:web/webmvc/mvc-controller/ann-initbinder.adoc#mvc-ann-initbinder-model-design[See equivalent in the Servlet stack]#
include::partial$web/web-data-binding-model-design.adoc[]
@@ -3,11 +3,8 @@
[.small]#xref:web/webmvc/mvc-controller/ann-methods/modelattrib-method-args.adoc[See equivalent in the Servlet stack]#
You can use the `@ModelAttribute` annotation on a method argument to access an attribute from the
model or have it instantiated if not present. The model attribute is also overlaid with
the values of query parameters and form fields whose names match to field names. This is
referred to as data binding, and it saves you from having to deal with parsing and
converting individual query parameters and form fields. The following example binds an instance of `Pet`:
The `@ModelAttribute` method parameter annotation binds request parameters onto a model
object. For example:
[tabs]
======
@@ -18,7 +15,7 @@ Java::
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@ModelAttribute Pet pet) { } // <1>
----
<1> Bind an instance of `Pet`.
<1> Bind to an instance of `Pet`.
Kotlin::
+
@@ -27,28 +24,66 @@ Kotlin::
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@ModelAttribute pet: Pet): String { } // <1>
----
<1> Bind an instance of `Pet`.
<1> Bind to an instance of `Pet`.
======
The `Pet` instance in the preceding example is resolved as follows:
The `Pet` instance may be:
* From the model if already added through xref:web/webflux/controller/ann-modelattrib-methods.adoc[`Model`].
* From the HTTP session through xref:web/webflux/controller/ann-methods/sessionattributes.adoc[`@SessionAttributes`].
* From the invocation of a default constructor.
* From the invocation of a "`primary constructor`" with arguments that match query
parameters or form fields. Argument names are determined through JavaBeans
`@ConstructorProperties` or through runtime-retained parameter names in the bytecode.
* Accessed from the model where it could have been added by a
xref:web/webflux/controller/ann-modelattrib-methods.adoc[`Model`].
* Accessed from the HTTP session if the model attribute was listed in
the class-level xref:web/webflux/controller/ann-methods/sessionattributes.adoc[`@SessionAttributes`].
* Instantiated through a default constructor.
* Instantiated through a "`primary constructor`" with arguments that match to Servlet
request parameters. Argument names are determined through runtime-retained parameter
names in the bytecode.
After the model attribute instance is obtained, data binding is applied. The
`WebExchangeDataBinder` class matches names of query parameters and form fields to field
names on the target `Object`. Matching fields are populated after type conversion is applied
where necessary. For more on data binding (and validation), see
xref:web/webmvc/mvc-config/validation.adoc[Validation]. For more on customizing data binding, see
xref:web/webflux/controller/ann-initbinder.adoc[`DataBinder`].
By default, both constructor and property
xref:core/validation/beans-beans.adoc#beans-binding[data binding] are applied. However,
model object design requires careful consideration, and for security reasons it is
recommended either to use an object tailored specifically for web binding, or to apply
constructor binding only. If property binding must still be used, then _allowedFields_
patterns should be set to limit which properties can be set. For further details on this
and example configuration, see
xref:web/webflux/controller/ann-initbinder.adoc#webflux-ann-initbinder-model-design[model design].
Data binding can result in errors. By default, a `WebExchangeBindException` is raised, but,
to check for such errors in the controller method, you can add a `BindingResult` argument
immediately next to the `@ModelAttribute`, as the following example shows:
When using constructor binding, you can customize request parameter names through an
`@BindParam` annotation. For example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
class Account {
private final String firstName;
public Account(@BindParam("first-name") String firstName) {
this.firstName = firstName;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class Account(@BindParam("first-name") val firstName: String)
----
======
NOTE: The `@BindParam` may also be placed on the fields that correspond to constructor
parameters. While `@BindParam` is supported out of the box, you can also use a
different annotation by setting a `DataBinder.NameResolver` on `DataBinder`
WebFlux, unlike Spring MVC, supports reactive types in the model, e.g. `Mono<Account>`.
You can declare a `@ModelAttribute` argument with or without a reactive type wrapper, and
it will be resolved accordingly to the actual value.
If data binding results in errors, by default a `WebExchangeBindException` is raised,
but you can also add a `BindingResult` argument immediately next to the `@ModelAttribute`
in order to handle such errors in the controller method. For example:
[tabs]
======
@@ -81,49 +116,9 @@ Kotlin::
<1> Adding a `BindingResult`.
======
You can automatically apply validation after data binding by adding the
`jakarta.validation.Valid` annotation or Spring's `@Validated` annotation (see also
xref:core/validation/beanvalidation.adoc[Bean Validation] and
xref:web/webmvc/mvc-config/validation.adoc[Spring validation]). The following example uses the `@Valid` annotation:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@Valid @ModelAttribute("pet") Pet pet, BindingResult result) { // <1>
if (result.hasErrors()) {
return "petForm";
}
// ...
}
----
<1> Using `@Valid` on a model attribute argument.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@Valid @ModelAttribute("pet") pet: Pet, result: BindingResult): String { // <1>
if (result.hasErrors()) {
return "petForm"
}
// ...
}
----
<1> Using `@Valid` on a model attribute argument.
======
Spring WebFlux, unlike Spring MVC, supports reactive types in the model -- for example,
`Mono<Account>` or `io.reactivex.Single<Account>`. You can declare a `@ModelAttribute` argument
with or without a reactive type wrapper, and it will be resolved accordingly,
to the actual value if necessary. However, note that, to use a `BindingResult`
argument, you must declare the `@ModelAttribute` argument before it without a reactive
type wrapper, as shown earlier. Alternatively, you can handle any errors through the
reactive type, as the following example shows:
To use a `BindingResult` argument, you must declare the `@ModelAttribute` argument before
it without a reactive type wrapper. If you want to use the reactive, you can handle errors
directly through it. For example:
[tabs]
======
@@ -160,10 +155,49 @@ Kotlin::
----
======
Note that use of `@ModelAttribute` is optional -- for example, to set its attributes.
By default, any argument that is not a simple value type (as determined by
{api-spring-framework}/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 `@ModelAttribute`.
You can automatically apply validation after data binding by adding the
`jakarta.validation.Valid` annotation or Spring's `@Validated` annotation (see
xref:core/validation/beanvalidation.adoc[Bean Validation] and
xref:web/webmvc/mvc-config/validation.adoc[Spring validation]). For example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@Valid @ModelAttribute("pet") Pet pet, BindingResult result) { // <1>
if (result.hasErrors()) {
return "petForm";
}
// ...
}
----
<1> Using `@Valid` on a model attribute argument.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@Valid @ModelAttribute("pet") pet: Pet, result: BindingResult): String { // <1>
if (result.hasErrors()) {
return "petForm"
}
// ...
}
----
<1> Using `@Valid` on a model attribute argument.
======
If method validation applies because other parameters have `@Constraint` annotations,
then `HandlerMethodValidationException` would be raised instead. See the section on
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
{api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty]
_AND_ that is not resolved by any other argument resolver is treated as an `@ModelAttribute`.
@@ -176,6 +176,10 @@ Kotlin::
======
--
If method validation applies because other parameters have `@Constraint` annotations,
then `HandlerMethodValidationException` is raised instead. See the section on
xref:web/webflux/controller/ann-validation.adoc[Validation].
To access all multipart data as a `MultiValueMap`, you can use `@RequestBody`,
as the following example shows:
@@ -306,5 +310,3 @@ file upload.
Received part events can also be relayed to another service by using the `WebClient`.
See xref:web/webflux-webclient/client-body.adoc#webflux-client-body-multipart[Multipart Data].
@@ -89,4 +89,33 @@ Kotlin::
----
======
You can also declare an `Errors` parameter for access to validation errors, but in
that case the request body must not be a `Mono`, and will be resolved first:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@PostMapping("/accounts")
public void handle(@Valid @RequestBody Account account, Errors errors) {
// use one of the onError* operators...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@PostMapping("/accounts")
fun handle(@Valid @RequestBody account: Mono<Account>) {
// ...
}
----
======
If method validation applies because other parameters have `@Constraint` annotations,
then `HandlerMethodValidationException` is raised instead. For more details, see the
section on xref:web/webflux/controller/ann-validation.adoc[Validation].
@@ -1,8 +1,15 @@
[[webflux-ann-requestmapping]]
= Request Mapping
= Mapping Requests
[.small]#xref:web/webmvc/mvc-controller/ann-requestmapping.adoc[See equivalent in the Servlet stack]#
This section discusses request mapping for annotated controllers.
[[webflux-ann-requestmapping-annotation]]
== `@RequestMapping`
[.small]#xref:web/webmvc/mvc-controller/ann-requestmapping.adoc#mvc-ann-requestmapping-annotation[See equivalent in the Servlet stack]#
The `@RequestMapping` annotation is used to map requests to controllers methods. It has
various attributes to match by URL, HTTP method, request parameters, headers, and media
types. You can use it at the class level to express shared mappings or at the method level
@@ -500,3 +507,68 @@ Kotlin::
[[webflux-ann-httpexchange-annotation]]
== `@HttpExchange`
[.small]#xref:web/webmvc/mvc-controller/ann-requestmapping.adoc#mvc-ann-httpexchange-annotation[See equivalent in the Reactive stack]#
As an alternative to `@RequestMapping`, you can also handle requests with `@HttpExchange`
methods. Such methods are declared on an
xref:integration/rest-clients.adoc#rest-http-interface[HTTP Interface] and can be used as
a client via `HttpServiceProxyFactory` or implemented by a server `@Controller`.
For example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@RestController
@HttpExchange("/persons")
class PersonController {
@GetExchange("/{id}")
public Person getPerson(@PathVariable Long id) {
// ...
}
@PostExchange
@ResponseStatus(HttpStatus.CREATED)
public void add(@RequestBody Person person) {
// ...
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@RestController
@HttpExchange("/persons")
class PersonController {
@GetExchange("/{id}")
fun getPerson(@PathVariable id: Long): Person {
// ...
}
@PostExchange
@ResponseStatus(HttpStatus.CREATED)
fun add(@RequestBody person: Person) {
// ...
}
}
----
======
There some differences between `@HttpExchange` and `@RequestMapping` since the
former needs to remain suitable for client and server use. For example, while
`@RequestMapping` can be declared to handle any number of paths and each path can
be a pattern, `@HttpExchange` must be declared with a single, concrete path. There are
also differences in the supported method parameters. Generally, `@HttpExchange` supports
a subset of method parameters that `@RequestMapping` does, excluding any parameters that
are server side only. For details see the list of supported method parameters for
xref:integration/rest-clients.adoc#rest-http-interface-method-parameters[HTTP interface] and for
xref:web/webflux/controller/ann-methods/arguments.adoc[@RequestMapping].
@@ -0,0 +1,111 @@
[[mvc-ann-validation]]
= Validation
[.small]#xref:web/webmvc/mvc-controller/ann-validation.adoc[See equivalent in the Servlet stack]#
Spring WebFlux has built-in xref:core/validation/validator.adoc[Validation] support for
`@RequestMapping` methods, including the option to use
xref:core/validation/beanvalidation.adoc[Java Bean Validation].
The validation support works on two levels.
First, method parameters such as
xref:web/webflux/controller/ann-methods/modelattrib-method-args.adoc[@ModelAttribute],
xref:web/webflux/controller/ann-methods/requestbody.adoc[@RequestBody], and
xref:web/webflux/controller/ann-methods/multipart-forms.adoc[@RequestPart] do perform
validation if annotated with Jakarta's `@Valid` or Spring's `@Validated` annotation, and
raise `MethodArgumentNotValidException` in case of validation errors. If you want to handle
the errors in the controller method instead, you can declare an `Errors` or `BindingResult`
method parameter immediately after the validated parameter.
Second, if https://beanvalidation.org/[Java Bean Validation] is present _AND_ other method
parameters, e.g. `@RequestHeader`, `@RequestParam`, `@PathVariable` have `@Constraint`
annotations, then method validation is applied to all method arguments, raising
`HandlerMethodValidationException` in case of validation errors. You can still declare an
`Errors` or `BindingResult` after an `@Valid` method parameter, and handle validation
errors within the controller method, as long as there are no validation errors on other
method arguments.
You can configure a `Validator` globally through the
xref:web/webflux/config.adoc#webflux-config-validation[WebMvc config], or locally
through an xref:web/webflux/controller/ann-initbinder.adoc[@InitBinder] method in an
`@Controller` or `@ControllerAdvice`. You can also use multiple validators.
NOTE: If a controller has a class level `@Validated`, then
xref:core/validation/beanvalidation.adoc#validation-beanvalidation-spring-method[method validation is applied]
through an AOP proxy. In order to take advantage of the Spring MVC built-in support for
method validation added in Spring Framework 6.1, you need to remove the class level
`@Validated` annotation from the controller.
The xref:web/webmvc/mvc-ann-rest-exceptions.adoc[Error Responses] section provides further
details on how `MethodArgumentNotValidException` and `HandlerMethodValidationException`
are handled, and also how their rendering can be customized through a `MessageSource` and
locale and language specific resource bundles.
For further custom handling of method validation errors, you can extend
`ResponseEntityExceptionHandler` or use an `@ExceptionHandler` method in a controller
or in a `@ControllerAdvice`, and handle `HandlerMethodValidationException` directly.
The exception contains a list of``ParameterValidationResult``s that group validation errors
by method parameter. You can either iterate over those, or provide a visitor with callback
methods by controller method parameter type:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
HandlerMethodValidationException ex = ... ;
ex.visitResults(new HandlerMethodValidationException.Visitor() {
@Override
public void requestHeader(RequestHeader requestHeader, ParameterValidationResult result) {
// ...
}
@Override
public void requestParam(@Nullable RequestParam requestParam, ParameterValidationResult result) {
// ...
}
@Override
public void modelAttribute(@Nullable ModelAttribute modelAttribute, ParameterErrors errors) {
// ...
@Override
public void other(ParameterValidationResult result) {
// ...
}
});
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
// HandlerMethodValidationException
val ex
ex.visitResults(object : HandlerMethodValidationException.Visitor {
override fun requestHeader(requestHeader: RequestHeader, result: ParameterValidationResult) {
// ...
}
override fun requestParam(requestParam: RequestParam?, result: ParameterValidationResult) {
// ...
}
override fun modelAttribute(modelAttribute: ModelAttribute?, errors: ParameterErrors) {
// ...
}
// ...
override fun other(result: ParameterValidationResult) {
// ...
}
})
----
======
@@ -6,6 +6,26 @@ This section describes options for client-side access to REST endpoints.
[[webmvc-restclient]]
== `RestClient`
`RestClient` is a synchronous HTTP client that exposes a modern, fluent API.
See xref:integration/rest-clients.adoc#rest-restclient[`RestClient`] for more details.
[[webmvc-webclient]]
== `WebClient`
`WebClient` is a reactive client to perform HTTP requests with a fluent API.
See xref:web/webflux-webclient.adoc[WebClient] for more details.
[[webmvc-resttemplate]]
== `RestTemplate`
@@ -13,37 +33,8 @@ This section describes options for client-side access to REST endpoints.
Spring REST client and exposes a simple, template-method API over underlying HTTP client
libraries.
NOTE: As of 5.0 the `RestTemplate` is in maintenance mode, with only requests for minor
changes and bugs to be accepted. Please, consider using the
xref:web/webflux-webclient.adoc[WebClient] which offers a more modern API and
supports sync, async, and streaming scenarios.
See xref:integration/rest-clients.adoc[REST Endpoints] for details.
[[webmvc-webclient]]
== `WebClient`
`WebClient` is a non-blocking, reactive client to perform HTTP requests. It was
introduced in 5.0 and offers a modern alternative to the `RestTemplate`, with efficient
support for both synchronous and asynchronous, as well as streaming scenarios.
In contrast to `RestTemplate`, `WebClient` supports the following:
* Non-blocking I/O.
* Reactive Streams back pressure.
* High concurrency with fewer hardware resources.
* Functional-style, fluent API that takes advantage of Java 8 lambdas.
* Synchronous and asynchronous interactions.
* Streaming up to or streaming down from a server.
See xref:web/webflux-webclient.adoc[WebClient] for more details.
[[webmvc-http-interface]]
== HTTP Interface
@@ -92,7 +92,7 @@ Kotlin::
======
The return value can then be obtained by running the given task through the
xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-configuration-spring-mvc[configured] `TaskExecutor`.
xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-configuration-spring-mvc[configured] `AsyncTaskExecutor`.
@@ -128,7 +128,7 @@ Here is a very concise overview of Servlet asynchronous request processing:
* The controller returns a `Callable`.
* Spring MVC calls `request.startAsync()` and submits the `Callable` to
a `TaskExecutor` for processing in a separate thread.
an `AsyncTaskExecutor` for processing in a separate thread.
* Meanwhile, the `DispatcherServlet` and all filters exit the Servlet container thread,
but the response remains open.
* Eventually the `Callable` produces a result, and Spring MVC dispatches the request back
@@ -404,11 +404,10 @@ TIP: Spring MVC supports Reactor and RxJava through the
For streaming to the response, reactive back pressure is supported, but writes to the
response are still blocking and are run on a separate thread through the
xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-configuration-spring-mvc[configured] `TaskExecutor`, to avoid
blocking the upstream source (such as a `Flux` returned from `WebClient`).
By default, `SimpleAsyncTaskExecutor` is used for the blocking writes, but that is not
suitable under load. If you plan to stream with a reactive type, you should use the
xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-configuration-spring-mvc[MVC configuration] to configure a task executor.
xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-configuration-spring-mvc[configured]
`AsyncTaskExecutor`, to avoid blocking the upstream source such as a `Flux` returned
from `WebClient`.
@@ -494,7 +493,7 @@ In `web.xml` configuration, you can add `<async-supported>true</async-supported>
[[mvc-ann-async-configuration-spring-mvc]]
=== Spring MVC
The MVC configuration exposes the following options related to asynchronous request processing:
The MVC configuration exposes the following options for asynchronous request processing:
* Java configuration: Use the `configureAsyncSupport` callback on `WebMvcConfigurer`.
* XML namespace: Use the `<async-support>` element under `<mvc:annotation-driven>`.
@@ -504,10 +503,9 @@ You can configure the following:
* Default timeout value for async requests, which if not set, depends
on the underlying Servlet container.
* `AsyncTaskExecutor` to use for blocking writes when streaming with
xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-reactive-types[Reactive Types] and for executing `Callable` instances returned from
controller methods. We highly recommended configuring this property if you
stream with reactive types or have controller methods that return `Callable`, since
by default, it is a `SimpleAsyncTaskExecutor`.
xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-reactive-types[Reactive Types] and for
executing `Callable` instances returned from controller methods.
The one used by default is not suitable for production under load.
* `DeferredResultProcessingInterceptor` implementations and `CallableProcessingInterceptor` implementations.
Note that you can also set the default timeout value on a `DeferredResult`,
@@ -67,24 +67,25 @@ from an existing `ProblemDetail`. This could be done centrally, e.g. from an
[[mvc-ann-rest-exceptions-i18n]]
== Internationalization
== Customization and i18n
[.small]#xref:web/webflux/ann-rest-exceptions.adoc#webflux-ann-rest-exceptions-i18n[See equivalent in the Reactive stack]#
It is a common requirement to internationalize error response details, and good practice
to customize the problem details for Spring MVC exceptions. This is supported as follows:
It is a common requirement to customize and internationalize error response details.
It is also good practice to customize the problem details for Spring MVC exceptions
to avoid revealing implementation details. This section describes the support for that.
- Each `ErrorResponse` exposes a message code and arguments to resolve the "detail" field
through a xref:core/beans/context-introduction.adoc#context-functionality-messagesource[MessageSource].
The actual message code value is parameterized with placeholders, e.g.
`+"HTTP method {0} not supported"+` to be expanded from the arguments.
- Each `ErrorResponse` also exposes a message code to resolve the "title" field.
- `ResponseEntityExceptionHandler` uses the message code and arguments to resolve the
"detail" and the "title" fields.
An `ErrorResponse` exposes message codes for "type", "title", and "detail", as well as
message code arguments for the "detail" field. `ResponseEntityExceptionHandler` resolves
these through a xref:core/beans/context-introduction.adoc#context-functionality-messagesource[MessageSource]
and updates the corresponding `ProblemDetail` fields accordingly.
By default, the message code for the "detail" field is "problemDetail." + the fully
qualified exception class name. Some exceptions may expose additional message codes in
which case a suffix is added to the default message code. The table below lists message
arguments and codes for Spring MVC exceptions:
The default strategy for message codes follows the pattern:
`problemDetail.[type|title|detail].[fully qualified exception class name]`
An `ErrorResponse` may expose more than one message code, typically adding a suffix
to the default message code. The table below lists message codes, and arguments for
Spring MVC exceptions:
[[mvc-ann-rest-exceptions-codes]]
[cols="1,1,2", options="header"]
@@ -99,6 +100,11 @@ arguments and codes for Spring MVC exceptions:
| (default)
| `+{0}+` property name, `+{1}+` property value
| `HandlerMethodValidationException`
| (default)
| `+{0}+` list all validation errors.
Message codes and arguments for each error are also resolved via `MessageSource`.
| `HttpMediaTypeNotAcceptableException`
| (default)
| `+{0}+` list of supported media types
@@ -130,8 +136,7 @@ arguments and codes for Spring MVC exceptions:
| `MethodArgumentNotValidException`
| (default)
| `+{0}+` the list of global errors, `+{1}+` the list of field errors.
Message codes and arguments for each error within the `BindingResult` are also resolved
via `MessageSource`.
Message codes and arguments for each error are also resolvedvia `MessageSource`.
| `MissingRequestHeaderException`
| (default)
@@ -161,6 +166,10 @@ arguments and codes for Spring MVC exceptions:
| (default)
|
| `NoResourceFoundException`
| (default)
|
| `TypeMismatchException`
| (default)
| `+{0}+` property name, `+{1}+` property value
@@ -171,8 +180,13 @@ arguments and codes for Spring MVC exceptions:
|===
By default, the message code for the "title" field is "problemDetail.title." + the fully
qualified exception class name.
NOTE: Unlike other exceptions, the message arguments for
`MethodArgumentValidException` and `HandlerMethodValidationException` are baed on a list of
`MessageSourceResolvable` errors that can also be customized through a
xref:core/beans/context-introduction.adoc#context-functionality-messagesource[MessageSource]
resource bundle. See
xref:core/validation/beanvalidation.adoc#validation-beanvalidation-spring-method-i18n[Customizing Validation Errors]
for more details.
@@ -6,7 +6,7 @@
By default, if xref:core/validation/beanvalidation.adoc#validation-beanvalidation-overview[Bean Validation] is present
on the classpath (for example, Hibernate Validator), the `LocalValidatorFactoryBean` is
registered as a global xref:core/validation/validator.adoc[Validator] for use with `@Valid` and
`Validated` on controller method arguments.
`@Validated` on controller method arguments.
In Java configuration, you can customize the global `Validator` instance, as the
following example shows:
@@ -1,25 +1,27 @@
[[mvc-ann-initbinder]]
= `DataBinder`
= `@InitBinder`
[.small]#xref:web/webflux/controller/ann-initbinder.adoc[See equivalent in the Reactive stack]#
`@Controller` or `@ControllerAdvice` classes can have `@InitBinder` methods that
initialize instances of `WebDataBinder`, and those, in turn, can:
`@Controller` or `@ControllerAdvice` classes can have `@InitBinder` methods to
initialize `WebDataBinder` instances that in turn can:
* Bind request parameters (that is, form or query data) to a model object.
* Convert String-based request values (such as request parameters, path variables,
headers, cookies, and others) to the target type of controller method arguments.
* Format model object values as `String` values when rendering HTML forms.
* Bind request parameters to a model object.
* Convert request values from string to object property types.
* Format model object properties as strings when rendering HTML forms.
`@InitBinder` methods can register controller-specific `java.beans.PropertyEditor` or
Spring `Converter` and `Formatter` components. In addition, you can use the
xref:web/webmvc/mvc-config/conversion.adoc[MVC config] to register `Converter` and `Formatter`
types in a globally shared `FormattingConversionService`.
In an `@Controller`, `DataBinder` customizations apply locally within the controller,
or even to a specific model attribute referenced by name through the annotation.
In an `@ControllerAdvice` customizations can apply to all or a subset of controllers.
`@InitBinder` methods support many of the same arguments that `@RequestMapping` methods
do, except for `@ModelAttribute` (command object) arguments. Typically, they are declared
with a `WebDataBinder` argument (for registrations) and a `void` return value.
The following listing shows an example:
You can register `PropertyEditor`, `Converter`, and `Formatter` components in the
`DataBinder` for type conversion. Alternatively, you can use the
xref:web/webmvc/mvc-config/conversion.adoc[MVC config] to register `Converter` and
`Formatter` components in a globally shared `FormattingConversionService`.
`@InitBinder` methods can have many of the same arguments that `@RequestMapping` methods
have, with the notable exception of `@ModelAttribute`. Typically, such methods have a
`WebDataBinder` argument (for registrations) and a `void` return value, for example:
[tabs]
======
@@ -3,11 +3,8 @@
[.small]#xref:web/webflux/controller/ann-methods/modelattrib-method-args.adoc[See equivalent in the Reactive stack]#
You can use the `@ModelAttribute` annotation on a method argument to access an attribute from
the model or have it be instantiated if not present. The model attribute is also overlain with
values from HTTP Servlet request parameters whose names match to field names. This is referred
to as data binding, and it saves you from having to deal with parsing and converting individual
query parameters and form fields. The following example shows how to do so:
The `@ModelAttribute` method parameter annotation binds request parameters onto a model
object. For example:
[tabs]
======
@@ -20,7 +17,7 @@ Java::
// method logic...
}
----
<1> Bind an instance of `Pet`.
<1> Bind to an instance of `Pet`.
Kotlin::
+
@@ -31,30 +28,27 @@ fun processSubmit(@ModelAttribute pet: Pet): String { // <1>
// method logic...
}
----
<1> Bind an instance of `Pet`.
<1> Bind to an instance of `Pet`.
======
The `Pet` instance above is sourced in one of the following ways:
The `Pet` instance may be:
* Retrieved from the model where it may have been added by a
* Accessed from the model where it could have been added by a
xref:web/webmvc/mvc-controller/ann-modelattrib-methods.adoc[@ModelAttribute method].
* Retrieved from the HTTP session if the model attribute was listed in
* Accessed from the HTTP session if the model attribute was listed in
the class-level xref:web/webmvc/mvc-controller/ann-methods/sessionattributes.adoc[`@SessionAttributes`] annotation.
* Obtained through a `Converter` where the model attribute name matches the name of a
request value such as a path variable or a request parameter (see next example).
* Instantiated using its default constructor.
* Obtained through a `Converter` if the model attribute name matches the name of a
request value such as a path variable or a request parameter (example follows).
* Instantiated through a default constructor.
* Instantiated through a "`primary constructor`" with arguments that match to Servlet
request parameters. Argument names are determined through JavaBeans
`@ConstructorProperties` or through runtime-retained parameter names in the bytecode.
request parameters. Argument names are determined through runtime-retained parameter
names in the bytecode.
One alternative to using a xref:web/webmvc/mvc-controller/ann-modelattrib-methods.adoc[@ModelAttribute method] to
supply it or relying on the framework to create the model attribute, is to have a
`Converter<String, T>` to provide the instance. This is applied when the model attribute
name matches to the name of a request value such as a path variable or a request
parameter, and there is a `Converter` from `String` to the model attribute type.
In the following example, the model attribute name is `account` which matches the URI
path variable `account`, and there is a registered `Converter<String, Account>` which
could load the `Account` from a data store:
As mentioned above, a `Converter<String, T>` may be used to obtain the model object if
the model attribute name matches to the name of a request value such as a path variable or a
request parameter, _and_ there is a compatible `Converter<String, T>`. In the below example,
the model attribute name `account` matches URI path variable `account`, and there is a
registered `Converter<String, Account>` that perhaps retrieves it from a persistence store:
[tabs]
======
@@ -67,7 +61,6 @@ Java::
// ...
}
----
<1> Bind an instance of `Account` using an explicit attribute name.
Kotlin::
+
@@ -78,19 +71,101 @@ Kotlin::
// ...
}
----
<1> Bind an instance of `Account` using an explicit attribute name.
======
After the model attribute instance is obtained, data binding is applied. The
`WebDataBinder` class matches Servlet request parameter names (query parameters and form
fields) to field names on the target `Object`. Matching fields are populated after type
conversion is applied, where necessary. For more on data binding (and validation), see
xref:web/webmvc/mvc-config/validation.adoc[Validation]. For more on customizing data binding, see
xref:web/webmvc/mvc-controller/ann-initbinder.adoc[`DataBinder`].
By default, both constructor and property
xref:core/validation/beans-beans.adoc#beans-binding[data binding] are applied. However,
model object design requires careful consideration, and for security reasons it is
recommended either to use an object tailored specifically for web binding, or to apply
constructor binding only. If property binding must still be used, then _allowedFields_
patterns should be set to limit which properties can be set. For further details on this
and example configuration, see
xref:web/webmvc/mvc-controller/ann-initbinder.adoc#mvc-ann-initbinder-model-design[model design].
Data binding can result in errors. By default, a `BindException` is raised. However, to check
for such errors in the controller method, you can add a `BindingResult` argument immediately next
to the `@ModelAttribute`, as the following example shows:
When using constructor binding, you can customize request parameter names through an
`@BindParam` annotation. For example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
class Account {
private final String firstName;
public Account(@BindParam("first-name") String firstName) {
this.firstName = firstName;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class Account(@BindParam("first-name") val firstName: String)
----
======
NOTE: The `@BindParam` may also be placed on the fields that correspond to constructor
parameters. While `@BindParam` is supported out of the box, you can also use a
different annotation by setting a `DataBinder.NameResolver` on `DataBinder`
In some cases, you may want access to a model attribute without data binding. For such
cases, you can inject the `Model` into the controller and access it directly or,
alternatively, set `@ModelAttribute(binding=false)`, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@ModelAttribute
public AccountForm setUpForm() {
return new AccountForm();
}
@ModelAttribute
public Account findAccount(@PathVariable String accountId) {
return accountRepository.findOne(accountId);
}
@PostMapping("update")
public String update(AccountForm form, BindingResult result,
@ModelAttribute(binding=false) Account account) { // <1>
// ...
}
----
<1> Setting `@ModelAttribute(binding=false)`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@ModelAttribute
fun setUpForm(): AccountForm {
return AccountForm()
}
@ModelAttribute
fun findAccount(@PathVariable accountId: String): Account {
return accountRepository.findOne(accountId)
}
@PostMapping("update")
fun update(form: AccountForm, result: BindingResult,
@ModelAttribute(binding = false) account: Account): String { // <1>
// ...
}
----
<1> Setting `@ModelAt\tribute(binding=false)`.
======
If data binding results in errors, by default a `MethodArgumentNotValidException` is raised,
but you can also add a `BindingResult` argument immediately next to the `@ModelAttribute`
in order to handle such errors in the controller method. For example:
[tabs]
======
@@ -123,61 +198,10 @@ Kotlin::
<1> Adding a `BindingResult` next to the `@ModelAttribute`.
======
In some cases, you may want access to a model attribute without data binding. For such
cases, you can inject the `Model` into the controller and access it directly or,
alternatively, set `@ModelAttribute(binding=false)`, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@ModelAttribute
public AccountForm setUpForm() {
return new AccountForm();
}
@ModelAttribute
public Account findAccount(@PathVariable String accountId) {
return accountRepository.findOne(accountId);
}
@PostMapping("update")
public String update(@Valid AccountForm form, BindingResult result,
@ModelAttribute(binding=false) Account account) { // <1>
// ...
}
----
<1> Setting `@ModelAttribute(binding=false)`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@ModelAttribute
fun setUpForm(): AccountForm {
return AccountForm()
}
@ModelAttribute
fun findAccount(@PathVariable accountId: String): Account {
return accountRepository.findOne(accountId)
}
@PostMapping("update")
fun update(@Valid form: AccountForm, result: BindingResult,
@ModelAttribute(binding = false) account: Account): String { // <1>
// ...
}
----
<1> Setting `@ModelAttribute(binding=false)`.
======
You can automatically apply validation after data binding by adding the
`jakarta.validation.Valid` annotation or Spring's `@Validated` annotation
(xref:core/validation/beanvalidation.adoc[Bean Validation] and
xref:web/webmvc/mvc-config/validation.adoc[Spring validation]). The following example shows how to do so:
`jakarta.validation.Valid` annotation or Spring's `@Validated` annotation.
See xref:core/validation/beanvalidation.adoc[Bean Validation] and
xref:web/webmvc/mvc-config/validation.adoc[Spring validation]. For example:
[tabs]
======
@@ -210,10 +234,13 @@ Kotlin::
<1> Validate the `Pet` instance.
======
Note that using `@ModelAttribute` is optional (for example, to set its attributes).
By default, any argument that is not a simple value type (as determined by
{api-spring-framework}/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 `@ModelAttribute`.
If there is no `BindingResult` parameter after the `@ModelAttribute`, then
`MethodArgumentNotValueException` is raised with the validation errors. However, if method
validation applies because other parameters have `@jakarta.validation.Constraint` annotations,
then `HandlerMethodValidationException` is raised instead. For more details, see the section
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
{api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty]
_AND_ that is not resolved by any other argument resolver is treated as an `@ModelAttribute`.
@@ -188,8 +188,7 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@PostMapping("/")
public String handle(@Valid @RequestPart("meta-data") MetaData metadata,
BindingResult result) {
public String handle(@Valid @RequestPart("meta-data") MetaData metadata, Errors errors) {
// ...
}
----
@@ -199,12 +198,14 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@PostMapping("/")
fun handle(@Valid @RequestPart("meta-data") metadata: MetaData,
result: BindingResult): String {
fun handle(@Valid @RequestPart("meta-data") metadata: MetaData, errors: Errors): String {
// ...
}
----
======
If method validation applies because other parameters have `@Constraint` annotations,
then `HandlerMethodValidationException` is raised instead. For more details, see the
section on xref:web/webmvc/mvc-controller/ann-validation.adoc[Validation].
@@ -48,7 +48,7 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@PostMapping("/accounts")
public void handle(@Valid @RequestBody Account account, BindingResult result) {
public void handle(@Valid @RequestBody Account account, Errors errors) {
// ...
}
----
@@ -58,10 +58,13 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@PostMapping("/accounts")
fun handle(@Valid @RequestBody account: Account, result: BindingResult) {
fun handle(@Valid @RequestBody account: Account, errors: Errors) {
// ...
}
----
======
If method validation applies because other parameters have `@Constraint` annotations,
then `HandlerMethodValidationException` is raised instead. For more details, see the
section on xref:web/webmvc/mvc-controller/ann-validation.adoc[Validation].
@@ -1,8 +1,17 @@
[[mvc-ann-requestmapping]]
= Request Mapping
= Mapping Requests
[.small]#xref:web/webflux/controller/ann-requestmapping.adoc[See equivalent in the Reactive stack]#
This section discusses request mapping for annotated controllers.
[[mvc-ann-requestmapping-annotation]]
== `@RequestMapping`
[.small]#xref:web/webflux/controller/ann-requestmapping.adoc#webflux-ann-requestmapping-annotation[See equivalent in the Reactive stack]#
You can use the `@RequestMapping` annotation to map requests to controllers methods. It has
various attributes to match by URL, HTTP method, request parameters, headers, and media
types. You can use it at the class level to express shared mappings or at the method level
@@ -549,3 +558,68 @@ Kotlin::
[[mvc-ann-httpexchange-annotation]]
== `@HttpExchange`
[.small]#xref:web/webflux/controller/ann-requestmapping.adoc#webflux-ann-httpexchange-annotation[See equivalent in the Reactive stack]#
As an alternative to `@RequestMapping`, you can also handle requests with `@HttpExchange`
methods. Such methods are declared on an
xref:integration/rest-clients.adoc#rest-http-interface[HTTP Interface] and can be used as
a client via `HttpServiceProxyFactory` or implemented by a server `@Controller`.
For example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@RestController
@HttpExchange("/persons")
class PersonController {
@GetExchange("/{id}")
public Person getPerson(@PathVariable Long id) {
// ...
}
@PostExchange
@ResponseStatus(HttpStatus.CREATED)
public void add(@RequestBody Person person) {
// ...
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@RestController
@HttpExchange("/persons")
class PersonController {
@GetExchange("/{id}")
fun getPerson(@PathVariable id: Long): Person {
// ...
}
@PostExchange
@ResponseStatus(HttpStatus.CREATED)
fun add(@RequestBody person: Person) {
// ...
}
}
----
======
There some differences between `@HttpExchange` and `@RequestMapping` since the
former needs to remain suitable for client and server use. For example, while
`@RequestMapping` can be declared to handle any number of paths and each path can
be a pattern, `@HttpExchange` must be declared with a single, concrete path. There are
also differences in the supported method parameters. Generally, `@HttpExchange` supports
a subset of method parameters that `@RequestMapping` does, excluding any parameters that
are server side only. For details see the list of supported method parameters for
xref:integration/rest-clients.adoc#rest-http-interface-method-parameters[HTTP interface] and for
xref:web/webmvc/mvc-controller/ann-methods/arguments.adoc[@RequestMapping].
@@ -0,0 +1,112 @@
[[mvc-ann-validation]]
= Validation
[.small]#xref:web/webflux/controller/ann-validation.adoc[See equivalent in the Reactive stack]#
Spring MVC has built-in xref:core/validation/validator.adoc[Validation] support for
`@RequestMapping` methods, including the option to use
xref:core/validation/beanvalidation.adoc[Java Bean Validation].
The validation support works on two levels.
First, method parameters such as
xref:web/webmvc/mvc-controller/ann-methods/modelattrib-method-args.adoc[@ModelAttribute],
xref:web/webmvc/mvc-controller/ann-methods/requestbody.adoc[@RequestBody], and
xref:web/webmvc/mvc-controller/ann-methods/multipart-forms.adoc[@RequestPart] do perform
validation if annotated with Jakarta's `@Valid` or Spring's `@Validated` annotation, and
raise `MethodArgumentNotValidException` in case of validation errors. If you want to handle
the errors in the controller method instead, you can declare an `Errors` or `BindingResult`
method parameter immediately after the validated parameter.
Second, if https://beanvalidation.org/[Java Bean Validation] is present _AND_ other method
parameters, e.g. `@RequestHeader`, `@RequestParam`, `@PathVariable` have `@Constraint`
annotations, then method validation is applied to all method arguments, raising
`HandlerMethodValidationException` in case of validation errors. You can still declare an
`Errors` or `BindingResult` after an `@Valid` method parameter, and handle validation
errors within the controller method, as long as there are no validation errors on other
method arguments. Method validation is also applied to the return value if the method
is annotated with `@Valid` or has other `@Constraint` annotations.
You can configure a `Validator` globally through the
xref:web/webmvc/mvc-config/validation.adoc[WebMvc config], or locally through an
xref:web/webmvc/mvc-controller/ann-initbinder.adoc[@InitBinder] method in an
`@Controller` or `@ControllerAdvice`. You can also use multiple validators.
NOTE: If a controller has a class level `@Validated`, then
xref:core/validation/beanvalidation.adoc#validation-beanvalidation-spring-method[method validation is applied]
through an AOP proxy. In order to take advantage of the Spring MVC built-in support for
method validation added in Spring Framework 6.1, you need to remove the class level
`@Validated` annotation from the controller.
The xref:web/webmvc/mvc-ann-rest-exceptions.adoc[Error Responses] section provides further
details on how `MethodArgumentNotValidException` and `HandlerMethodValidationException`
are handled, and also how their rendering can be customized through a `MessageSource` and
locale and language specific resource bundles.
For further custom handling of method validation errors, you can extend
`ResponseEntityExceptionHandler` or use an `@ExceptionHandler` method in a controller
or in a `@ControllerAdvice`, and handle `HandlerMethodValidationException` directly.
The exception contains a list of``ParameterValidationResult``s that group validation errors
by method parameter. You can either iterate over those, or provide a visitor with callback
methods by controller method parameter type:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
HandlerMethodValidationException ex = ... ;
ex.visitResults(new HandlerMethodValidationException.Visitor() {
@Override
public void requestHeader(RequestHeader requestHeader, ParameterValidationResult result) {
// ...
}
@Override
public void requestParam(@Nullable RequestParam requestParam, ParameterValidationResult result) {
// ...
}
@Override
public void modelAttribute(@Nullable ModelAttribute modelAttribute, ParameterErrors errors) {
// ...
@Override
public void other(ParameterValidationResult result) {
// ...
}
});
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
// HandlerMethodValidationException
val ex
ex.visitResults(object : HandlerMethodValidationException.Visitor {
override fun requestHeader(requestHeader: RequestHeader, result: ParameterValidationResult) {
// ...
}
override fun requestParam(requestParam: RequestParam?, result: ParameterValidationResult) {
// ...
}
override fun modelAttribute(modelAttribute: ModelAttribute?, errors: ParameterErrors) {
// ...
}
// ...
override fun other(result: ParameterValidationResult) {
// ...
}
})
----
======
@@ -62,8 +62,7 @@ initialization parameters (`init-param` elements) to the Servlet declaration in
The exception can then be caught with a `HandlerExceptionResolver` (for example, by using an
`@ExceptionHandler` controller method) and handled as any others.
By default, this is set to `false`, in which case the `DispatcherServlet` sets the
response status to 404 (NOT_FOUND) without raising an exception.
As of 6.1, this property is set to `true` and deprecated.
Note that, if xref:web/webmvc/mvc-config/default-servlet-handler.adoc[default servlet handling] is
also configured, unresolved requests are always forwarded to the default servlet
@@ -280,9 +280,7 @@ The following example shows the XML configuration equivalent of the preceding ex
NOTE: For client-side WebSocket configuration, you should use `WebSocketContainerFactoryBean`
(XML) or `ContainerProvider.getWebSocketContainer()` (Java configuration).
For Jetty, you need to supply a pre-configured Jetty `WebSocketServerFactory` and plug
that into Spring's `DefaultHandshakeHandler` through your WebSocket Java config.
The following example shows how to do so:
For Jetty, you need to supply a `Consumer` callback to configure the WebSocket server. For example:
[source,java,indent=0,subs="verbatim,quotes"]
----
@@ -292,62 +290,20 @@ The following example shows how to do so:
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
JettyRequestUpgradeStrategy upgradeStrategy = new JettyRequestUpgradeStrategy();
upgradeStrategy.addWebSocketConfigurer(configurable -> {
policy.setInputBufferSize(8192);
policy.setIdleTimeout(600000);
});
registry.addHandler(echoWebSocketHandler(),
"/echo").setHandshakeHandler(handshakeHandler());
}
@Bean
public DefaultHandshakeHandler handshakeHandler() {
WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
policy.setInputBufferSize(8192);
policy.setIdleTimeout(600000);
return new DefaultHandshakeHandler(
new JettyRequestUpgradeStrategy(new WebSocketServerFactory(policy)));
"/echo").setHandshakeHandler(new DefaultHandshakeHandler(upgradeStrategy));
}
}
----
The following example shows the XML configuration equivalent of the preceding example:
[source,xml,indent=0,subs="verbatim,quotes,attributes"]
----
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
https://www.springframework.org/schema/websocket/spring-websocket.xsd">
<websocket:handlers>
<websocket:mapping path="/echo" handler="echoHandler"/>
<websocket:handshake-handler ref="handshakeHandler"/>
</websocket:handlers>
<bean id="handshakeHandler" class="org.springframework...DefaultHandshakeHandler">
<constructor-arg ref="upgradeStrategy"/>
</bean>
<bean id="upgradeStrategy" class="org.springframework...JettyRequestUpgradeStrategy">
<constructor-arg ref="serverFactory"/>
</bean>
<bean id="serverFactory" class="org.eclipse.jetty...WebSocketServerFactory">
<constructor-arg>
<bean class="org.eclipse.jetty...WebSocketPolicy">
<constructor-arg value="SERVER"/>
<property name="inputBufferSize" value="8092"/>
<property name="idleTimeout" value="600000"/>
</bean>
</constructor-arg>
</bean>
</beans>
----
@@ -1,29 +1,16 @@
In the context of web applications, _data binding_ involves the binding of HTTP request
parameters (that is, form data or query parameters) to properties in a model object and
its nested objects.
xref:core/validation/beans-beans.adoc#beans-binding[Data binding] for web requests involves
binding request parameters to a model object. By default, request parameters can be bound
to any public property of the model object, which means malicious clients can provide
extra values for properties that exist in the model object graph, but are not expected to
be set. This is why model object design requires careful consideration.
Only `public` properties following the
https://www.oracle.com/java/technologies/javase/javabeans-spec.html[JavaBeans naming conventions]
are exposed for data binding — for example, `public String getFirstName()` and
`public void setFirstName(String)` methods for a `firstName` property.
TIP: The model object, and its nested object graph, is also sometimes referred to as a
TIP: The model object, and its nested object graph is also sometimes referred to as a
_command object_, _form-backing object_, or _POJO_ (Plain Old Java Object).
By default, Spring permits binding to all public properties in the model object graph.
This means you need to carefully consider what public properties the model has, since a
client could target any public property path, even some that are not expected to be
targeted for a given use case.
For example, given an HTTP form data endpoint, a malicious client could supply values for
properties that exist in the model object graph but are not part of the HTML form
presented in the browser. This could lead to data being set on the model object and any
of its nested objects, that is not expected to be updated.
The recommended approach is to use a _dedicated model object_ that exposes only
properties that are relevant for the form submission. For example, on a form for changing
a user's email address, the model object should declare a minimum set of properties such
as in the following `ChangeEmailForm`.
A good practice is to use a _dedicated model object_ rather than exposing your domain
model such as JPA or Hibernate entities for web data binding. For example, on a form to
change an email address, create a `ChangeEmailForm` model object that declares only
the properties required for the input:
[source,java,indent=0,subs="verbatim,quotes"]
----
@@ -51,13 +38,16 @@ as in the following `ChangeEmailForm`.
}
----
If you cannot or do not want to use a _dedicated model object_ for each data
binding use case, you **must** limit the properties that are allowed for data binding.
Ideally, you can achieve this by registering _allowed field patterns_ via the
`setAllowedFields()` method on `WebDataBinder`.
Another good practice is to apply
xref:core/validation/beans-beans.adoc#beans-constructor-binding[constructor binding],
which uses only the request parameters it needs for constructor arguments, and any other
input is ignored. This is in contrast to property binding which by default binds every
request parameter for which there is a matching property.
For example, to register allowed field patterns in your application, you can implement an
`@InitBinder` method in a `@Controller` or `@ControllerAdvice` component as shown below:
If neither a dedicated model object nor constructor binding is sufficient, and you must
use property binding, we strongy recommend registering `allowedFields` patterns (case
sensitive) on `WebDataBinder` in order to prevent unexpected properties from being set.
For example:
[source,java,indent=0,subs="verbatim,quotes"]
----
@@ -74,22 +64,28 @@ For example, to register allowed field patterns in your application, you can imp
}
----
In addition to registering allowed patterns, it is also possible to register _disallowed
field patterns_ via the `setDisallowedFields()` method in `DataBinder` and its subclasses.
Please note, however, that an "allow list" is safer than a "deny list". Consequently,
`setAllowedFields()` should be favored over `setDisallowedFields()`.
You can also register `disallowedFields` patterns (case insensitive). However,
"allowed" configuration is preferred over "disallowed" as it is more explicit and less
prone to mistakes.
Note that matching against allowed field patterns is case-sensitive; whereas, matching
against disallowed field patterns is case-insensitive. In addition, a field matching a
disallowed pattern will not be accepted even if it also happens to match a pattern in the
allowed list.
By default, constructor and property binding are both used. If you want to use
constructor binding only, you can set the `declarativeBinding` flag on `WebDataBinder`
through an `@InitBinder` method either locally within a controller or globally through an
`@ControllerAdvice`. Turning this flag on ensures that only constructor binding is used
and that property binding is not used unless `allowedFields` patterns are configured.
For example:
[WARNING]
====
It is extremely important to properly configure allowed and disallowed field patterns
when exposing your domain model directly for data binding purposes. Otherwise, it is a
big security risk.
[source,java,indent=0,subs="verbatim,quotes"]
----
@Controller
public class MyController {
Furthermore, it is strongly recommended that you do **not** use types from your domain
model such as JPA or Hibernate entities as the model object in data binding scenarios.
====
@InitBinder
void initBinder(WebDataBinder binder) {
binder.setDeclarativeBinding(true);
}
// @RequestMapping methods, etc.
}
----
@@ -0,0 +1,38 @@
/*
* Copyright 2002-2023 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.integration.observability.applicationevents;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.support.ContextPropagatingTaskDecorator;
@Configuration
public class ApplicationEventsConfiguration {
@Bean(name = "applicationEventMulticaster")
public SimpleApplicationEventMulticaster simpleApplicationEventMulticaster() {
SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster();
SimpleAsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();
// decorate task execution with a decorator that supports context propagation
taskExecutor.setTaskDecorator(new ContextPropagatingTaskDecorator());
eventMulticaster.setTaskExecutor(taskExecutor);
return eventMulticaster;
}
}
@@ -0,0 +1,39 @@
/*
* Copyright 2002-2023 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.integration.observability.applicationevents;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
public class EmailNotificationListener {
private final Log logger = LogFactory.getLog(EmailNotificationListener.class);
@EventListener(EmailReceivedEvent.class)
@Async("propagatingContextExecutor")
public void emailReceived(EmailReceivedEvent event) {
// asynchronously process the received event
// this logging statement will contain the expected MDC entries from the propagated context
logger.info("email has been received");
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2023 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.
@@ -14,16 +14,15 @@
* limitations under the License.
*/
package org.springframework.http.client;
package org.springframework.docs.integration.observability.applicationevents;
import org.springframework.context.ApplicationEvent;
public class NoOutputStreamingStreamingSimpleHttpRequestFactoryTests extends AbstractHttpRequestFactoryTests {
@SuppressWarnings("serial")
public class EmailReceivedEvent extends ApplicationEvent {
@Override
protected ClientHttpRequestFactory createRequestFactory() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setBufferRequestBody(false);
factory.setOutputStreaming(false);
return factory;
public EmailReceivedEvent(Object source) {
super(source);
}
}
@@ -0,0 +1,36 @@
/*
* Copyright 2002-2023 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.integration.observability.applicationevents;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.TaskExecutor;
import org.springframework.core.task.support.ContextPropagatingTaskDecorator;
@Configuration
public class EventAsyncExecutionConfiguration {
@Bean(name = "propagatingContextExecutor")
public TaskExecutor propagatingContextExecutor() {
SimpleAsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();
// decorate task execution with a decorator that supports context propagation
taskExecutor.setTaskDecorator(new ContextPropagatingTaskDecorator());
return taskExecutor;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
@@ -59,7 +59,7 @@ public class CustomServerRequestObservationConvention implements ServerRequestOb
}
private KeyValue exception(ServerRequestObservationContext context) {
String exception = (context.getError() != null) ? context.getError().getClass().getSimpleName() : KeyValue.NONE_VALUE;
String exception = (context.getError() != null ? context.getError().getClass().getSimpleName() : KeyValue.NONE_VALUE);
return KeyValue.of(ServerHttpObservationDocumentation.LowCardinalityKeyNames.EXCEPTION, exception);
}
@@ -0,0 +1,42 @@
/*
* Copyright 2002-2023 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.integration.observability.httpserver.reactive;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
@Configuration(proxyBeanMethods = false)
public class HttpHandlerConfiguration {
private final ApplicationContext applicationContext;
public HttpHandlerConfiguration(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Bean
public HttpHandler httpHandler(ObservationRegistry registry) {
return WebHttpHandlerBuilder.applicationContext(this.applicationContext)
.observationRegistry(registry)
.build();
}
}
@@ -17,9 +17,9 @@
package org.springframework.docs.integration.observability.httpserver.reactive;
import org.springframework.http.ResponseEntity;
import org.springframework.http.server.reactive.observation.ServerRequestObservationContext;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.filter.reactive.ServerHttpObservationFilter;
import org.springframework.web.server.ServerWebExchange;
@Controller
@@ -28,7 +28,7 @@ public class UserController {
@ExceptionHandler(MissingUserException.class)
ResponseEntity<Void> handleMissingUser(ServerWebExchange exchange, MissingUserException exception) {
// We want to record this exception with the observation
ServerHttpObservationFilter.findObservationContext(exchange)
ServerRequestObservationContext.findCurrent(exchange.getAttributes())
.ifPresent(context -> context.setError(exception));
return ResponseEntity.notFound().build();
}
@@ -0,0 +1,39 @@
/*
* Copyright 2002-2023 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.integration.observability.jms.process;
import io.micrometer.observation.ObservationRegistry;
import jakarta.jms.ConnectionFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
@Configuration
@EnableJms
public class JmsConfiguration {
@Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(ConnectionFactory connectionFactory, ObservationRegistry observationRegistry) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setObservationRegistry(observationRegistry);
return factory;
}
}
@@ -0,0 +1,44 @@
/*
* Copyright 2002-2023 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.integration.observability.jms.publish;
import io.micrometer.observation.ObservationRegistry;
import jakarta.jms.ConnectionFactory;
import org.springframework.jms.core.JmsMessagingTemplate;
import org.springframework.jms.core.JmsTemplate;
public class JmsTemplatePublish {
private final JmsTemplate jmsTemplate;
private final JmsMessagingTemplate jmsMessagingTemplate;
public JmsTemplatePublish(ObservationRegistry observationRegistry, ConnectionFactory connectionFactory) {
this.jmsTemplate = new JmsTemplate(connectionFactory);
// configure the observation registry
this.jmsTemplate.setObservationRegistry(observationRegistry);
// For JmsMessagingTemplate, instantiate it with a JMS template that has a configured registry
this.jmsMessagingTemplate = new JmsMessagingTemplate(this.jmsTemplate);
}
public void sendMessages() {
this.jmsTemplate.convertAndSend("spring.observation.test", "test message");
}
}
@@ -0,0 +1,38 @@
/*
* Copyright 2002-2023 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.integration.observability.tasksscheduled;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
public class ObservationSchedulingConfigurer implements SchedulingConfigurer {
private final ObservationRegistry observationRegistry;
public ObservationSchedulingConfigurer(ObservationRegistry observationRegistry) {
this.observationRegistry = observationRegistry;
}
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setObservationRegistry(this.observationRegistry);
}
}
+15 -10
View File
@@ -7,18 +7,19 @@ javaPlatform {
}
dependencies {
api(platform("com.fasterxml.jackson:jackson-bom:2.14.3"))
api(platform("io.micrometer:micrometer-bom:1.10.11"))
api(platform("com.fasterxml.jackson:jackson-bom:2.15.2"))
api(platform("io.micrometer:micrometer-bom:1.12.0-M3"))
api(platform("io.netty:netty-bom:4.1.97.Final"))
api(platform("io.netty:netty5-bom:5.0.0.Alpha5"))
api(platform("io.projectreactor:reactor-bom:2022.0.11"))
api(platform("io.projectreactor:reactor-bom:2023.0.0-M3"))
api(platform("io.rsocket:rsocket-bom:1.1.3"))
api(platform("org.apache.groovy:groovy-bom:4.0.14"))
api(platform("org.apache.logging.log4j:log4j-bom:2.20.0"))
api(platform("org.eclipse.jetty:jetty-bom:11.0.16"))
api(platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.6.4"))
api(platform("org.jetbrains.kotlinx:kotlinx-serialization-bom:1.4.0"))
api(platform("org.junit:junit-bom:5.9.3"))
api(platform("org.eclipse.jetty:jetty-bom:12.0.1"))
api(platform("org.eclipse.jetty.ee10:jetty-ee10-bom:12.0.1"))
api(platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.3"))
api(platform("org.jetbrains.kotlinx:kotlinx-serialization-bom:1.6.0"))
api(platform("org.junit:junit-bom:5.10.0"))
api(platform("org.mockito:mockito-bom:5.5.0"))
constraints {
@@ -45,7 +46,7 @@ dependencies {
api("com.thoughtworks.xstream:xstream:1.4.20")
api("commons-io:commons-io:2.11.0")
api("de.bechte.junit:junit-hierarchicalcontextrunner:4.12.2")
api("io.micrometer:context-propagation:1.0.5")
api("io.micrometer:context-propagation:1.1.0-M4")
api("io.mockk:mockk:1.13.4")
api("io.projectreactor.netty:reactor-netty5-http:2.0.0-M3")
api("io.projectreactor.tools:blockhound:1.0.8.RELEASE")
@@ -83,6 +84,7 @@ dependencies {
api("jakarta.xml.bind:jakarta.xml.bind-api:3.0.1")
api("javax.annotation:javax.annotation-api:1.3.2")
api("javax.cache:cache-api:1.1.1")
api("javax.inject:javax.inject:1")
api("javax.money:money-api:1.1")
api("jaxen:jaxen:1.2.0")
api("junit:junit:4.13.2")
@@ -92,6 +94,8 @@ dependencies {
api("org.apache.activemq:activemq-broker:5.17.4")
api("org.apache.activemq:activemq-kahadb-store:5.17.4")
api("org.apache.activemq:activemq-stomp:5.17.4")
api("org.apache.activemq:artemis-junit-5:2.29.0")
api("org.apache.activemq:artemis-jakarta-client:2.29.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")
@@ -110,8 +114,9 @@ dependencies {
api("org.awaitility:awaitility:4.2.0")
api("org.bouncycastle:bcpkix-jdk18on:1.72")
api("org.codehaus.jettison:jettison:1.5.4")
api("org.crac:crac:1.4.0")
api("org.dom4j:dom4j:2.1.4")
api("org.eclipse.jetty:jetty-reactive-httpclient:3.0.8")
api("org.eclipse.jetty:jetty-reactive-httpclient:4.0.0")
api("org.eclipse.persistence:org.eclipse.persistence.jpa:3.0.3")
api("org.eclipse:yasson:2.0.4")
api("org.ehcache:ehcache:3.10.8")
@@ -143,6 +148,6 @@ dependencies {
api("org.webjars:webjars-locator-core:0.53")
api("org.xmlunit:xmlunit-assertj:2.9.1")
api("org.xmlunit:xmlunit-matchers:2.9.1")
api("org.yaml:snakeyaml:1.33")
api("org.yaml:snakeyaml:2.0")
}
}
+3 -2
View File
@@ -1,9 +1,10 @@
version=6.0.12-SNAPSHOT
version=6.1.0-M5
org.gradle.caching=true
org.gradle.jvmargs=-Xmx2048m
org.gradle.parallel=true
kotlinVersion=1.7.21
kotlinVersion=1.9.10
kotlin.jvm.target.validation.mode=ignore
kotlin.stdlib.default.dependency=false
+14 -65
View File
@@ -2,16 +2,14 @@
* Apply the JVM Toolchain conventions
* See https://docs.gradle.org/current/userguide/toolchains.html
*
* One can choose the toolchain to use for compiling the MAIN sources and/or compiling
* and running the TEST sources. These options apply to Java, Kotlin and Groovy sources
* when available.
* {@code "./gradlew check -PmainToolchain=17 -PtestToolchain=20"} will use:
* <ul>
* <li>a JDK17 toolchain for compiling the main SourceSet
* <li>a JDK20 toolchain for compiling and running the test SourceSet
* </ul>
* One can choose the toolchain to use for compiling and running the TEST sources.
* These options apply to Java, Kotlin and Groovy test sources when available.
* {@code "./gradlew check -PtestToolchain=22"} will use a JDK22
* toolchain for compiling and running the test SourceSet.
*
* By default, the build will fall back to using the current JDK and 17 language level for all sourceSets.
* By default, the main build will fall back to using the a JDK 17
* toolchain (and 17 language level) for all main sourceSets.
* See {@link org.springframework.build.JavaConventions}.
*
* Gradle will automatically detect JDK distributions in well-known locations.
* The following command will list the detected JDKs on the host.
@@ -23,52 +21,27 @@
* {@code
* $ echo JDK17
* /opt/openjdk/java17
* $ echo JDK20
* /opt/openjdk/java20
* $ ./gradlew -Porg.gradle.java.installations.fromEnv=JDK17,JDK20 check
* $ echo JDK22
* /opt/openjdk/java22
* $ ./gradlew -Porg.gradle.java.installations.fromEnv=JDK17,JDK22 check
* }
*
* @author Brian Clozel
* @author Sam Brannen
*/
def mainToolchainConfigured() {
return project.hasProperty('mainToolchain') && project.mainToolchain
}
def testToolchainConfigured() {
return project.hasProperty('testToolchain') && project.testToolchain
}
def mainToolchainLanguageVersion() {
if (mainToolchainConfigured()) {
return JavaLanguageVersion.of(project.mainToolchain.toString())
}
return JavaLanguageVersion.of(17)
}
def testToolchainLanguageVersion() {
if (testToolchainConfigured()) {
return JavaLanguageVersion.of(project.testToolchain.toString())
}
return mainToolchainLanguageVersion()
return JavaLanguageVersion.of(17)
}
plugins.withType(JavaPlugin) {
// Configure the Java Toolchain if the 'mainToolchain' is configured
if (mainToolchainConfigured()) {
java {
toolchain {
languageVersion = mainToolchainLanguageVersion()
}
}
}
else {
// Fallback to JDK17
java {
sourceCompatibility = JavaVersion.VERSION_17
}
}
plugins.withType(JavaPlugin).configureEach {
// Configure a specific Java Toolchain for compiling and running tests if the 'testToolchain' property is defined
if (testToolchainConfigured()) {
def testLanguageVersion = testToolchainLanguageVersion()
@@ -81,37 +54,13 @@ plugins.withType(JavaPlugin) {
javaLauncher = javaToolchains.launcherFor {
languageVersion = testLanguageVersion
}
jvmArgs += ['-Djava.locale.providers=COMPAT']
}
}
}
plugins.withType(GroovyPlugin) {
// Fallback to JDK17
if (!mainToolchainConfigured()) {
compileGroovy {
sourceCompatibility = JavaVersion.VERSION_17
}
}
}
pluginManager.withPlugin("kotlin") {
// Fallback to JDK17
compileKotlin {
kotlinOptions {
jvmTarget = '17'
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = '17'
}
}
}
// Configure the JMH plugin to use the toolchain for generating and running JMH bytecode
pluginManager.withPlugin("me.champeau.jmh") {
if (mainToolchainConfigured() || testToolchainConfigured()) {
if (testToolchainConfigured()) {
tasks.matching { it.name.contains('jmh') && it.hasProperty('javaLauncher') }.configureEach {
javaLauncher.set(javaToolchains.launcherFor {
languageVersion.set(testToolchainLanguageVersion())
@@ -131,7 +80,7 @@ rootProject.ext {
resolvedTestToolchain = false
}
gradle.taskGraph.afterTask { Task task, TaskState state ->
if (mainToolchainConfigured() && !resolvedMainToolchain && task instanceof JavaCompile && task.javaCompiler.isPresent()) {
if (!resolvedMainToolchain && task instanceof JavaCompile && task.javaCompiler.isPresent()) {
def metadata = task.javaCompiler.get().metadata
task.project.buildScan.value('Main toolchain', "$metadata.vendor $metadata.languageVersion ($metadata.installationPath)")
resolvedMainToolchain = true

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